Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0 2 : /* 3 : * This file contains functions which manage high resolution tick 4 : * related events. 5 : * 6 : * Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de> 7 : * Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar 8 : * Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner 9 : */ 10 : #include <linux/cpu.h> 11 : #include <linux/err.h> 12 : #include <linux/hrtimer.h> 13 : #include <linux/interrupt.h> 14 : #include <linux/percpu.h> 15 : #include <linux/profile.h> 16 : #include <linux/sched.h> 17 : 18 : #include "tick-internal.h" 19 : 20 : /** 21 : * tick_program_event 22 : */ 23 31083 : int tick_program_event(ktime_t expires, int force) 24 : { 25 31083 : struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); 26 : 27 31083 : if (unlikely(expires == KTIME_MAX)) { 28 : /* 29 : * We don't need the clock event device any more, stop it. 30 : */ 31 0 : clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED); 32 0 : dev->next_event = KTIME_MAX; 33 0 : return 0; 34 : } 35 : 36 31083 : if (unlikely(clockevent_state_oneshot_stopped(dev))) { 37 : /* 38 : * We need the clock event again, configure it in ONESHOT mode 39 : * before using it. 40 : */ 41 0 : clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); 42 : } 43 : 44 31083 : return clockevents_program_event(dev, expires, force); 45 : } 46 : 47 : /** 48 : * tick_resume_onshot - resume oneshot mode 49 : */ 50 0 : void tick_resume_oneshot(void) 51 : { 52 0 : struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); 53 : 54 0 : clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); 55 0 : clockevents_program_event(dev, ktime_get(), true); 56 0 : } 57 : 58 : /** 59 : * tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz) 60 : */ 61 0 : void tick_setup_oneshot(struct clock_event_device *newdev, 62 : void (*handler)(struct clock_event_device *), 63 : ktime_t next_event) 64 : { 65 0 : newdev->event_handler = handler; 66 0 : clockevents_switch_state(newdev, CLOCK_EVT_STATE_ONESHOT); 67 0 : clockevents_program_event(newdev, next_event, true); 68 0 : } 69 : 70 : /** 71 : * tick_switch_to_oneshot - switch to oneshot mode 72 : */ 73 4 : int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)) 74 : { 75 4 : struct tick_device *td = this_cpu_ptr(&tick_cpu_device); 76 4 : struct clock_event_device *dev = td->evtdev; 77 : 78 4 : if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) || 79 4 : !tick_device_is_functional(dev)) { 80 : 81 0 : pr_info("Clockevents: could not switch to one-shot mode:"); 82 0 : if (!dev) { 83 0 : pr_cont(" no tick device\n"); 84 : } else { 85 0 : if (!tick_device_is_functional(dev)) 86 0 : pr_cont(" %s is not functional.\n", dev->name); 87 : else 88 0 : pr_cont(" %s does not support one-shot mode.\n", 89 : dev->name); 90 : } 91 0 : return -EINVAL; 92 : } 93 : 94 4 : td->mode = TICKDEV_MODE_ONESHOT; 95 4 : dev->event_handler = handler; 96 4 : clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT); 97 4 : tick_broadcast_switch_to_oneshot(); 98 4 : return 0; 99 : } 100 : 101 : /** 102 : * tick_check_oneshot_mode - check whether the system is in oneshot mode 103 : * 104 : * returns 1 when either nohz or highres are enabled. otherwise 0. 105 : */ 106 6 : int tick_oneshot_mode_active(void) 107 : { 108 6 : unsigned long flags; 109 6 : int ret; 110 : 111 12 : local_irq_save(flags); 112 6 : ret = __this_cpu_read(tick_cpu_device.mode) == TICKDEV_MODE_ONESHOT; 113 6 : local_irq_restore(flags); 114 : 115 6 : return ret; 116 : } 117 : 118 : #ifdef CONFIG_HIGH_RES_TIMERS 119 : /** 120 : * tick_init_highres - switch to high resolution mode 121 : * 122 : * Called with interrupts disabled. 123 : */ 124 : int tick_init_highres(void) 125 : { 126 : return tick_switch_to_oneshot(hrtimer_interrupt); 127 : } 128 : #endif