xref: /openbmc/linux/kernel/time/tick-common.c (revision 79bf2bb335b85db25d27421c798595a2fa2a0e82)
1906568c9SThomas Gleixner /*
2906568c9SThomas Gleixner  * linux/kernel/time/tick-common.c
3906568c9SThomas Gleixner  *
4906568c9SThomas Gleixner  * This file contains the base functions to manage periodic tick
5906568c9SThomas Gleixner  * related events.
6906568c9SThomas Gleixner  *
7906568c9SThomas Gleixner  * Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de>
8906568c9SThomas Gleixner  * Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar
9906568c9SThomas Gleixner  * Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner
10906568c9SThomas Gleixner  *
11906568c9SThomas Gleixner  * This code is licenced under the GPL version 2. For details see
12906568c9SThomas Gleixner  * kernel-base/COPYING.
13906568c9SThomas Gleixner  */
14906568c9SThomas Gleixner #include <linux/cpu.h>
15906568c9SThomas Gleixner #include <linux/err.h>
16906568c9SThomas Gleixner #include <linux/hrtimer.h>
17906568c9SThomas Gleixner #include <linux/irq.h>
18906568c9SThomas Gleixner #include <linux/percpu.h>
19906568c9SThomas Gleixner #include <linux/profile.h>
20906568c9SThomas Gleixner #include <linux/sched.h>
21906568c9SThomas Gleixner #include <linux/tick.h>
22906568c9SThomas Gleixner 
23f8381cbaSThomas Gleixner #include "tick-internal.h"
24f8381cbaSThomas Gleixner 
25906568c9SThomas Gleixner /*
26906568c9SThomas Gleixner  * Tick devices
27906568c9SThomas Gleixner  */
28f8381cbaSThomas Gleixner DEFINE_PER_CPU(struct tick_device, tick_cpu_device);
29906568c9SThomas Gleixner /*
30906568c9SThomas Gleixner  * Tick next event: keeps track of the tick time
31906568c9SThomas Gleixner  */
32f8381cbaSThomas Gleixner ktime_t tick_next_period;
33f8381cbaSThomas Gleixner ktime_t tick_period;
34906568c9SThomas Gleixner static int tick_do_timer_cpu = -1;
35f8381cbaSThomas Gleixner DEFINE_SPINLOCK(tick_device_lock);
36906568c9SThomas Gleixner 
37*79bf2bb3SThomas Gleixner /**
38*79bf2bb3SThomas Gleixner  * tick_is_oneshot_available - check for a oneshot capable event device
39*79bf2bb3SThomas Gleixner  */
40*79bf2bb3SThomas Gleixner int tick_is_oneshot_available(void)
41*79bf2bb3SThomas Gleixner {
42*79bf2bb3SThomas Gleixner 	struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
43*79bf2bb3SThomas Gleixner 
44*79bf2bb3SThomas Gleixner 	return dev && (dev->features & CLOCK_EVT_FEAT_ONESHOT);
45*79bf2bb3SThomas Gleixner }
46*79bf2bb3SThomas Gleixner 
47906568c9SThomas Gleixner /*
48906568c9SThomas Gleixner  * Periodic tick
49906568c9SThomas Gleixner  */
50906568c9SThomas Gleixner static void tick_periodic(int cpu)
51906568c9SThomas Gleixner {
52906568c9SThomas Gleixner 	if (tick_do_timer_cpu == cpu) {
53906568c9SThomas Gleixner 		write_seqlock(&xtime_lock);
54906568c9SThomas Gleixner 
55906568c9SThomas Gleixner 		/* Keep track of the next tick event */
56906568c9SThomas Gleixner 		tick_next_period = ktime_add(tick_next_period, tick_period);
57906568c9SThomas Gleixner 
58906568c9SThomas Gleixner 		do_timer(1);
59906568c9SThomas Gleixner 		write_sequnlock(&xtime_lock);
60906568c9SThomas Gleixner 	}
61906568c9SThomas Gleixner 
62906568c9SThomas Gleixner 	update_process_times(user_mode(get_irq_regs()));
63906568c9SThomas Gleixner 	profile_tick(CPU_PROFILING);
64906568c9SThomas Gleixner }
65906568c9SThomas Gleixner 
66906568c9SThomas Gleixner /*
67906568c9SThomas Gleixner  * Event handler for periodic ticks
68906568c9SThomas Gleixner  */
69906568c9SThomas Gleixner void tick_handle_periodic(struct clock_event_device *dev)
70906568c9SThomas Gleixner {
71906568c9SThomas Gleixner 	int cpu = smp_processor_id();
72906568c9SThomas Gleixner 
73906568c9SThomas Gleixner 	tick_periodic(cpu);
74906568c9SThomas Gleixner 
75906568c9SThomas Gleixner 	if (dev->mode != CLOCK_EVT_MODE_ONESHOT)
76906568c9SThomas Gleixner 		return;
77906568c9SThomas Gleixner 	/*
78906568c9SThomas Gleixner 	 * Setup the next period for devices, which do not have
79906568c9SThomas Gleixner 	 * periodic mode:
80906568c9SThomas Gleixner 	 */
81906568c9SThomas Gleixner 	for (;;) {
82906568c9SThomas Gleixner 		ktime_t next = ktime_add(dev->next_event, tick_period);
83906568c9SThomas Gleixner 
84906568c9SThomas Gleixner 		if (!clockevents_program_event(dev, next, ktime_get()))
85906568c9SThomas Gleixner 			return;
86906568c9SThomas Gleixner 		tick_periodic(cpu);
87906568c9SThomas Gleixner 	}
88906568c9SThomas Gleixner }
89906568c9SThomas Gleixner 
90906568c9SThomas Gleixner /*
91906568c9SThomas Gleixner  * Setup the device for a periodic tick
92906568c9SThomas Gleixner  */
93f8381cbaSThomas Gleixner void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
94906568c9SThomas Gleixner {
95f8381cbaSThomas Gleixner 	tick_set_periodic_handler(dev, broadcast);
96f8381cbaSThomas Gleixner 
97f8381cbaSThomas Gleixner 	/* Broadcast setup ? */
98f8381cbaSThomas Gleixner 	if (!tick_device_is_functional(dev))
99f8381cbaSThomas Gleixner 		return;
100906568c9SThomas Gleixner 
101906568c9SThomas Gleixner 	if (dev->features & CLOCK_EVT_FEAT_PERIODIC) {
102906568c9SThomas Gleixner 		clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
103906568c9SThomas Gleixner 	} else {
104906568c9SThomas Gleixner 		unsigned long seq;
105906568c9SThomas Gleixner 		ktime_t next;
106906568c9SThomas Gleixner 
107906568c9SThomas Gleixner 		do {
108906568c9SThomas Gleixner 			seq = read_seqbegin(&xtime_lock);
109906568c9SThomas Gleixner 			next = tick_next_period;
110906568c9SThomas Gleixner 		} while (read_seqretry(&xtime_lock, seq));
111906568c9SThomas Gleixner 
112906568c9SThomas Gleixner 		clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
113906568c9SThomas Gleixner 
114906568c9SThomas Gleixner 		for (;;) {
115906568c9SThomas Gleixner 			if (!clockevents_program_event(dev, next, ktime_get()))
116906568c9SThomas Gleixner 				return;
117906568c9SThomas Gleixner 			next = ktime_add(next, tick_period);
118906568c9SThomas Gleixner 		}
119906568c9SThomas Gleixner 	}
120906568c9SThomas Gleixner }
121906568c9SThomas Gleixner 
122906568c9SThomas Gleixner /*
123906568c9SThomas Gleixner  * Setup the tick device
124906568c9SThomas Gleixner  */
125906568c9SThomas Gleixner static void tick_setup_device(struct tick_device *td,
126906568c9SThomas Gleixner 			      struct clock_event_device *newdev, int cpu,
127906568c9SThomas Gleixner 			      cpumask_t cpumask)
128906568c9SThomas Gleixner {
129906568c9SThomas Gleixner 	ktime_t next_event;
130906568c9SThomas Gleixner 	void (*handler)(struct clock_event_device *) = NULL;
131906568c9SThomas Gleixner 
132906568c9SThomas Gleixner 	/*
133906568c9SThomas Gleixner 	 * First device setup ?
134906568c9SThomas Gleixner 	 */
135906568c9SThomas Gleixner 	if (!td->evtdev) {
136906568c9SThomas Gleixner 		/*
137906568c9SThomas Gleixner 		 * If no cpu took the do_timer update, assign it to
138906568c9SThomas Gleixner 		 * this cpu:
139906568c9SThomas Gleixner 		 */
140906568c9SThomas Gleixner 		if (tick_do_timer_cpu == -1) {
141906568c9SThomas Gleixner 			tick_do_timer_cpu = cpu;
142906568c9SThomas Gleixner 			tick_next_period = ktime_get();
143906568c9SThomas Gleixner 			tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
144906568c9SThomas Gleixner 		}
145906568c9SThomas Gleixner 
146906568c9SThomas Gleixner 		/*
147906568c9SThomas Gleixner 		 * Startup in periodic mode first.
148906568c9SThomas Gleixner 		 */
149906568c9SThomas Gleixner 		td->mode = TICKDEV_MODE_PERIODIC;
150906568c9SThomas Gleixner 	} else {
151906568c9SThomas Gleixner 		handler = td->evtdev->event_handler;
152906568c9SThomas Gleixner 		next_event = td->evtdev->next_event;
153906568c9SThomas Gleixner 	}
154906568c9SThomas Gleixner 
155906568c9SThomas Gleixner 	td->evtdev = newdev;
156906568c9SThomas Gleixner 
157906568c9SThomas Gleixner 	/*
158906568c9SThomas Gleixner 	 * When the device is not per cpu, pin the interrupt to the
159906568c9SThomas Gleixner 	 * current cpu:
160906568c9SThomas Gleixner 	 */
161906568c9SThomas Gleixner 	if (!cpus_equal(newdev->cpumask, cpumask))
162906568c9SThomas Gleixner 		irq_set_affinity(newdev->irq, cpumask);
163906568c9SThomas Gleixner 
164f8381cbaSThomas Gleixner 	/*
165f8381cbaSThomas Gleixner 	 * When global broadcasting is active, check if the current
166f8381cbaSThomas Gleixner 	 * device is registered as a placeholder for broadcast mode.
167f8381cbaSThomas Gleixner 	 * This allows us to handle this x86 misfeature in a generic
168f8381cbaSThomas Gleixner 	 * way.
169f8381cbaSThomas Gleixner 	 */
170f8381cbaSThomas Gleixner 	if (tick_device_uses_broadcast(newdev, cpu))
171f8381cbaSThomas Gleixner 		return;
172f8381cbaSThomas Gleixner 
173906568c9SThomas Gleixner 	if (td->mode == TICKDEV_MODE_PERIODIC)
174906568c9SThomas Gleixner 		tick_setup_periodic(newdev, 0);
175*79bf2bb3SThomas Gleixner 	else
176*79bf2bb3SThomas Gleixner 		tick_setup_oneshot(newdev, handler, next_event);
177906568c9SThomas Gleixner }
178906568c9SThomas Gleixner 
179906568c9SThomas Gleixner /*
180906568c9SThomas Gleixner  * Check, if the new registered device should be used.
181906568c9SThomas Gleixner  */
182906568c9SThomas Gleixner static int tick_check_new_device(struct clock_event_device *newdev)
183906568c9SThomas Gleixner {
184906568c9SThomas Gleixner 	struct clock_event_device *curdev;
185906568c9SThomas Gleixner 	struct tick_device *td;
186906568c9SThomas Gleixner 	int cpu, ret = NOTIFY_OK;
187906568c9SThomas Gleixner 	unsigned long flags;
188906568c9SThomas Gleixner 	cpumask_t cpumask;
189906568c9SThomas Gleixner 
190906568c9SThomas Gleixner 	spin_lock_irqsave(&tick_device_lock, flags);
191906568c9SThomas Gleixner 
192906568c9SThomas Gleixner 	cpu = smp_processor_id();
193906568c9SThomas Gleixner 	if (!cpu_isset(cpu, newdev->cpumask))
194906568c9SThomas Gleixner 		goto out;
195906568c9SThomas Gleixner 
196906568c9SThomas Gleixner 	td = &per_cpu(tick_cpu_device, cpu);
197906568c9SThomas Gleixner 	curdev = td->evtdev;
198906568c9SThomas Gleixner 	cpumask = cpumask_of_cpu(cpu);
199906568c9SThomas Gleixner 
200906568c9SThomas Gleixner 	/* cpu local device ? */
201906568c9SThomas Gleixner 	if (!cpus_equal(newdev->cpumask, cpumask)) {
202906568c9SThomas Gleixner 
203906568c9SThomas Gleixner 		/*
204906568c9SThomas Gleixner 		 * If the cpu affinity of the device interrupt can not
205906568c9SThomas Gleixner 		 * be set, ignore it.
206906568c9SThomas Gleixner 		 */
207906568c9SThomas Gleixner 		if (!irq_can_set_affinity(newdev->irq))
208906568c9SThomas Gleixner 			goto out_bc;
209906568c9SThomas Gleixner 
210906568c9SThomas Gleixner 		/*
211906568c9SThomas Gleixner 		 * If we have a cpu local device already, do not replace it
212906568c9SThomas Gleixner 		 * by a non cpu local device
213906568c9SThomas Gleixner 		 */
214906568c9SThomas Gleixner 		if (curdev && cpus_equal(curdev->cpumask, cpumask))
215906568c9SThomas Gleixner 			goto out_bc;
216906568c9SThomas Gleixner 	}
217906568c9SThomas Gleixner 
218906568c9SThomas Gleixner 	/*
219906568c9SThomas Gleixner 	 * If we have an active device, then check the rating and the oneshot
220906568c9SThomas Gleixner 	 * feature.
221906568c9SThomas Gleixner 	 */
222906568c9SThomas Gleixner 	if (curdev) {
223906568c9SThomas Gleixner 		/*
224*79bf2bb3SThomas Gleixner 		 * Prefer one shot capable devices !
225*79bf2bb3SThomas Gleixner 		 */
226*79bf2bb3SThomas Gleixner 		if ((curdev->features & CLOCK_EVT_FEAT_ONESHOT) &&
227*79bf2bb3SThomas Gleixner 		    !(newdev->features & CLOCK_EVT_FEAT_ONESHOT))
228*79bf2bb3SThomas Gleixner 			goto out_bc;
229*79bf2bb3SThomas Gleixner 		/*
230906568c9SThomas Gleixner 		 * Check the rating
231906568c9SThomas Gleixner 		 */
232906568c9SThomas Gleixner 		if (curdev->rating >= newdev->rating)
233f8381cbaSThomas Gleixner 			goto out_bc;
234906568c9SThomas Gleixner 	}
235906568c9SThomas Gleixner 
236906568c9SThomas Gleixner 	/*
237906568c9SThomas Gleixner 	 * Replace the eventually existing device by the new
238f8381cbaSThomas Gleixner 	 * device. If the current device is the broadcast device, do
239f8381cbaSThomas Gleixner 	 * not give it back to the clockevents layer !
240906568c9SThomas Gleixner 	 */
241f8381cbaSThomas Gleixner 	if (tick_is_broadcast_device(curdev)) {
242f8381cbaSThomas Gleixner 		clockevents_set_mode(curdev, CLOCK_EVT_MODE_SHUTDOWN);
243f8381cbaSThomas Gleixner 		curdev = NULL;
244f8381cbaSThomas Gleixner 	}
245906568c9SThomas Gleixner 	clockevents_exchange_device(curdev, newdev);
246906568c9SThomas Gleixner 	tick_setup_device(td, newdev, cpu, cpumask);
247*79bf2bb3SThomas Gleixner 	if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
248*79bf2bb3SThomas Gleixner 		tick_oneshot_notify();
249906568c9SThomas Gleixner 
250f8381cbaSThomas Gleixner 	spin_unlock_irqrestore(&tick_device_lock, flags);
251f8381cbaSThomas Gleixner 	return NOTIFY_STOP;
252f8381cbaSThomas Gleixner 
253f8381cbaSThomas Gleixner out_bc:
254f8381cbaSThomas Gleixner 	/*
255f8381cbaSThomas Gleixner 	 * Can the new device be used as a broadcast device ?
256f8381cbaSThomas Gleixner 	 */
257f8381cbaSThomas Gleixner 	if (tick_check_broadcast_device(newdev))
258f8381cbaSThomas Gleixner 		ret = NOTIFY_STOP;
259906568c9SThomas Gleixner out:
260906568c9SThomas Gleixner 	spin_unlock_irqrestore(&tick_device_lock, flags);
261f8381cbaSThomas Gleixner 
262906568c9SThomas Gleixner 	return ret;
263906568c9SThomas Gleixner }
264906568c9SThomas Gleixner 
265906568c9SThomas Gleixner /*
266906568c9SThomas Gleixner  * Shutdown an event device on a given cpu:
267906568c9SThomas Gleixner  *
268906568c9SThomas Gleixner  * This is called on a life CPU, when a CPU is dead. So we cannot
269906568c9SThomas Gleixner  * access the hardware device itself.
270906568c9SThomas Gleixner  * We just set the mode and remove it from the lists.
271906568c9SThomas Gleixner  */
272906568c9SThomas Gleixner static void tick_shutdown(unsigned int *cpup)
273906568c9SThomas Gleixner {
274906568c9SThomas Gleixner 	struct tick_device *td = &per_cpu(tick_cpu_device, *cpup);
275906568c9SThomas Gleixner 	struct clock_event_device *dev = td->evtdev;
276906568c9SThomas Gleixner 	unsigned long flags;
277906568c9SThomas Gleixner 
278906568c9SThomas Gleixner 	spin_lock_irqsave(&tick_device_lock, flags);
279906568c9SThomas Gleixner 	td->mode = TICKDEV_MODE_PERIODIC;
280906568c9SThomas Gleixner 	if (dev) {
281906568c9SThomas Gleixner 		/*
282906568c9SThomas Gleixner 		 * Prevent that the clock events layer tries to call
283906568c9SThomas Gleixner 		 * the set mode function!
284906568c9SThomas Gleixner 		 */
285906568c9SThomas Gleixner 		dev->mode = CLOCK_EVT_MODE_UNUSED;
286906568c9SThomas Gleixner 		clockevents_exchange_device(dev, NULL);
287906568c9SThomas Gleixner 		td->evtdev = NULL;
288906568c9SThomas Gleixner 	}
289906568c9SThomas Gleixner 	spin_unlock_irqrestore(&tick_device_lock, flags);
290906568c9SThomas Gleixner }
291906568c9SThomas Gleixner 
292906568c9SThomas Gleixner /*
293906568c9SThomas Gleixner  * Notification about clock event devices
294906568c9SThomas Gleixner  */
295906568c9SThomas Gleixner static int tick_notify(struct notifier_block *nb, unsigned long reason,
296906568c9SThomas Gleixner 			       void *dev)
297906568c9SThomas Gleixner {
298906568c9SThomas Gleixner 	switch (reason) {
299906568c9SThomas Gleixner 
300906568c9SThomas Gleixner 	case CLOCK_EVT_NOTIFY_ADD:
301906568c9SThomas Gleixner 		return tick_check_new_device(dev);
302906568c9SThomas Gleixner 
303f8381cbaSThomas Gleixner 	case CLOCK_EVT_NOTIFY_BROADCAST_ON:
304f8381cbaSThomas Gleixner 	case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
305f8381cbaSThomas Gleixner 		tick_broadcast_on_off(reason, dev);
306f8381cbaSThomas Gleixner 		break;
307f8381cbaSThomas Gleixner 
308*79bf2bb3SThomas Gleixner 	case CLOCK_EVT_NOTIFY_BROADCAST_ENTER:
309*79bf2bb3SThomas Gleixner 	case CLOCK_EVT_NOTIFY_BROADCAST_EXIT:
310*79bf2bb3SThomas Gleixner 		tick_broadcast_oneshot_control(reason);
311*79bf2bb3SThomas Gleixner 		break;
312*79bf2bb3SThomas Gleixner 
313906568c9SThomas Gleixner 	case CLOCK_EVT_NOTIFY_CPU_DEAD:
314*79bf2bb3SThomas Gleixner 		tick_shutdown_broadcast_oneshot(dev);
315f8381cbaSThomas Gleixner 		tick_shutdown_broadcast(dev);
316906568c9SThomas Gleixner 		tick_shutdown(dev);
317906568c9SThomas Gleixner 		break;
318906568c9SThomas Gleixner 
319906568c9SThomas Gleixner 	default:
320906568c9SThomas Gleixner 		break;
321906568c9SThomas Gleixner 	}
322906568c9SThomas Gleixner 
323906568c9SThomas Gleixner 	return NOTIFY_OK;
324906568c9SThomas Gleixner }
325906568c9SThomas Gleixner 
326906568c9SThomas Gleixner static struct notifier_block tick_notifier = {
327906568c9SThomas Gleixner 	.notifier_call = tick_notify,
328906568c9SThomas Gleixner };
329906568c9SThomas Gleixner 
330906568c9SThomas Gleixner /**
331906568c9SThomas Gleixner  * tick_init - initialize the tick control
332906568c9SThomas Gleixner  *
333906568c9SThomas Gleixner  * Register the notifier with the clockevents framework
334906568c9SThomas Gleixner  */
335906568c9SThomas Gleixner void __init tick_init(void)
336906568c9SThomas Gleixner {
337906568c9SThomas Gleixner 	clockevents_register_notifier(&tick_notifier);
338906568c9SThomas Gleixner }
339