Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * This file contains functions which manage high resolution tick
0004  * related events.
0005  *
0006  * Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de>
0007  * Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar
0008  * Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner
0009  */
0010 #include <linux/cpu.h>
0011 #include <linux/err.h>
0012 #include <linux/hrtimer.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/percpu.h>
0015 #include <linux/profile.h>
0016 #include <linux/sched.h>
0017 
0018 #include "tick-internal.h"
0019 
0020 /**
0021  * tick_program_event
0022  */
0023 int tick_program_event(ktime_t expires, int force)
0024 {
0025     struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
0026 
0027     if (unlikely(expires == KTIME_MAX)) {
0028         /*
0029          * We don't need the clock event device any more, stop it.
0030          */
0031         clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED);
0032         dev->next_event = KTIME_MAX;
0033         return 0;
0034     }
0035 
0036     if (unlikely(clockevent_state_oneshot_stopped(dev))) {
0037         /*
0038          * We need the clock event again, configure it in ONESHOT mode
0039          * before using it.
0040          */
0041         clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
0042     }
0043 
0044     return clockevents_program_event(dev, expires, force);
0045 }
0046 
0047 /**
0048  * tick_resume_oneshot - resume oneshot mode
0049  */
0050 void tick_resume_oneshot(void)
0051 {
0052     struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev);
0053 
0054     clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
0055     clockevents_program_event(dev, ktime_get(), true);
0056 }
0057 
0058 /**
0059  * tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz)
0060  */
0061 void tick_setup_oneshot(struct clock_event_device *newdev,
0062             void (*handler)(struct clock_event_device *),
0063             ktime_t next_event)
0064 {
0065     newdev->event_handler = handler;
0066     clockevents_switch_state(newdev, CLOCK_EVT_STATE_ONESHOT);
0067     clockevents_program_event(newdev, next_event, true);
0068 }
0069 
0070 /**
0071  * tick_switch_to_oneshot - switch to oneshot mode
0072  */
0073 int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
0074 {
0075     struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
0076     struct clock_event_device *dev = td->evtdev;
0077 
0078     if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) ||
0079             !tick_device_is_functional(dev)) {
0080 
0081         pr_info("Clockevents: could not switch to one-shot mode:");
0082         if (!dev) {
0083             pr_cont(" no tick device\n");
0084         } else {
0085             if (!tick_device_is_functional(dev))
0086                 pr_cont(" %s is not functional.\n", dev->name);
0087             else
0088                 pr_cont(" %s does not support one-shot mode.\n",
0089                     dev->name);
0090         }
0091         return -EINVAL;
0092     }
0093 
0094     td->mode = TICKDEV_MODE_ONESHOT;
0095     dev->event_handler = handler;
0096     clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT);
0097     tick_broadcast_switch_to_oneshot();
0098     return 0;
0099 }
0100 
0101 /**
0102  * tick_check_oneshot_mode - check whether the system is in oneshot mode
0103  *
0104  * returns 1 when either nohz or highres are enabled. otherwise 0.
0105  */
0106 int tick_oneshot_mode_active(void)
0107 {
0108     unsigned long flags;
0109     int ret;
0110 
0111     local_irq_save(flags);
0112     ret = __this_cpu_read(tick_cpu_device.mode) == TICKDEV_MODE_ONESHOT;
0113     local_irq_restore(flags);
0114 
0115     return ret;
0116 }
0117 
0118 #ifdef CONFIG_HIGH_RES_TIMERS
0119 /**
0120  * tick_init_highres - switch to high resolution mode
0121  *
0122  * Called with interrupts disabled.
0123  */
0124 int tick_init_highres(void)
0125 {
0126     return tick_switch_to_oneshot(hrtimer_interrupt);
0127 }
0128 #endif