xref: /openbmc/linux/arch/arm/kernel/smp_twd.c (revision 3cd88f99)
1f32f4ce2SRussell King /*
2f32f4ce2SRussell King  *  linux/arch/arm/kernel/smp_twd.c
3f32f4ce2SRussell King  *
4f32f4ce2SRussell King  *  Copyright (C) 2002 ARM Ltd.
5f32f4ce2SRussell King  *  All Rights Reserved
6f32f4ce2SRussell King  *
7f32f4ce2SRussell King  * This program is free software; you can redistribute it and/or modify
8f32f4ce2SRussell King  * it under the terms of the GNU General Public License version 2 as
9f32f4ce2SRussell King  * published by the Free Software Foundation.
10f32f4ce2SRussell King  */
11f32f4ce2SRussell King #include <linux/init.h>
12f32f4ce2SRussell King #include <linux/kernel.h>
135def51b0SLinus Walleij #include <linux/clk.h>
144fd7f9b1SLinus Walleij #include <linux/cpufreq.h>
15f32f4ce2SRussell King #include <linux/delay.h>
16f32f4ce2SRussell King #include <linux/device.h>
175def51b0SLinus Walleij #include <linux/err.h>
18f32f4ce2SRussell King #include <linux/smp.h>
19f32f4ce2SRussell King #include <linux/jiffies.h>
20f32f4ce2SRussell King #include <linux/clockchips.h>
2192485104SMarc Zyngier #include <linux/interrupt.h>
22f32f4ce2SRussell King #include <linux/io.h>
23d8e03643SMarc Zyngier #include <linux/of_irq.h>
24d8e03643SMarc Zyngier #include <linux/of_address.h>
25f32f4ce2SRussell King 
26f32f4ce2SRussell King #include <asm/smp_twd.h>
2728af690aSMarc Zyngier #include <asm/localtimer.h>
28f32f4ce2SRussell King #include <asm/hardware/gic.h>
29f32f4ce2SRussell King 
30f32f4ce2SRussell King /* set up by the platform code */
3192485104SMarc Zyngier static void __iomem *twd_base;
32f32f4ce2SRussell King 
335def51b0SLinus Walleij static struct clk *twd_clk;
34f32f4ce2SRussell King static unsigned long twd_timer_rate;
35f32f4ce2SRussell King 
3628af690aSMarc Zyngier static struct clock_event_device __percpu **twd_evt;
3781e46f7bSMarc Zyngier static int twd_ppi;
3828af690aSMarc Zyngier 
39f32f4ce2SRussell King static void twd_set_mode(enum clock_event_mode mode,
40f32f4ce2SRussell King 			struct clock_event_device *clk)
41f32f4ce2SRussell King {
42f32f4ce2SRussell King 	unsigned long ctrl;
43f32f4ce2SRussell King 
44f32f4ce2SRussell King 	switch (mode) {
45f32f4ce2SRussell King 	case CLOCK_EVT_MODE_PERIODIC:
46f32f4ce2SRussell King 		/* timer load already set up */
47f32f4ce2SRussell King 		ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
48f32f4ce2SRussell King 			| TWD_TIMER_CONTROL_PERIODIC;
4903399c1cSRussell King 		__raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD);
50f32f4ce2SRussell King 		break;
51f32f4ce2SRussell King 	case CLOCK_EVT_MODE_ONESHOT:
52f32f4ce2SRussell King 		/* period set, and timer enabled in 'next_event' hook */
53f32f4ce2SRussell King 		ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
54f32f4ce2SRussell King 		break;
55f32f4ce2SRussell King 	case CLOCK_EVT_MODE_UNUSED:
56f32f4ce2SRussell King 	case CLOCK_EVT_MODE_SHUTDOWN:
57f32f4ce2SRussell King 	default:
58f32f4ce2SRussell King 		ctrl = 0;
59f32f4ce2SRussell King 	}
60f32f4ce2SRussell King 
61f32f4ce2SRussell King 	__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
62f32f4ce2SRussell King }
63f32f4ce2SRussell King 
64f32f4ce2SRussell King static int twd_set_next_event(unsigned long evt,
65f32f4ce2SRussell King 			struct clock_event_device *unused)
66f32f4ce2SRussell King {
67f32f4ce2SRussell King 	unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
68f32f4ce2SRussell King 
694c5158d4SRussell King 	ctrl |= TWD_TIMER_CONTROL_ENABLE;
704c5158d4SRussell King 
71f32f4ce2SRussell King 	__raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
724c5158d4SRussell King 	__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
73f32f4ce2SRussell King 
74f32f4ce2SRussell King 	return 0;
75f32f4ce2SRussell King }
76f32f4ce2SRussell King 
77f32f4ce2SRussell King /*
78f32f4ce2SRussell King  * local_timer_ack: checks for a local timer interrupt.
79f32f4ce2SRussell King  *
80f32f4ce2SRussell King  * If a local timer interrupt has occurred, acknowledge and return 1.
81f32f4ce2SRussell King  * Otherwise, return 0.
82f32f4ce2SRussell King  */
8392485104SMarc Zyngier static int twd_timer_ack(void)
84f32f4ce2SRussell King {
85f32f4ce2SRussell King 	if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
86f32f4ce2SRussell King 		__raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
87f32f4ce2SRussell King 		return 1;
88f32f4ce2SRussell King 	}
89f32f4ce2SRussell King 
90f32f4ce2SRussell King 	return 0;
91f32f4ce2SRussell King }
92f32f4ce2SRussell King 
93abde710cSMarc Zyngier static void twd_timer_stop(struct clock_event_device *clk)
9428af690aSMarc Zyngier {
9528af690aSMarc Zyngier 	twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
9628af690aSMarc Zyngier 	disable_percpu_irq(clk->irq);
9728af690aSMarc Zyngier }
9828af690aSMarc Zyngier 
994fd7f9b1SLinus Walleij #ifdef CONFIG_CPU_FREQ
1004fd7f9b1SLinus Walleij 
1014fd7f9b1SLinus Walleij /*
1024fd7f9b1SLinus Walleij  * Updates clockevent frequency when the cpu frequency changes.
1034fd7f9b1SLinus Walleij  * Called on the cpu that is changing frequency with interrupts disabled.
1044fd7f9b1SLinus Walleij  */
1054fd7f9b1SLinus Walleij static void twd_update_frequency(void *data)
1064fd7f9b1SLinus Walleij {
1074fd7f9b1SLinus Walleij 	twd_timer_rate = clk_get_rate(twd_clk);
1084fd7f9b1SLinus Walleij 
1094fd7f9b1SLinus Walleij 	clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
1104fd7f9b1SLinus Walleij }
1114fd7f9b1SLinus Walleij 
1124fd7f9b1SLinus Walleij static int twd_cpufreq_transition(struct notifier_block *nb,
1134fd7f9b1SLinus Walleij 	unsigned long state, void *data)
1144fd7f9b1SLinus Walleij {
1154fd7f9b1SLinus Walleij 	struct cpufreq_freqs *freqs = data;
1164fd7f9b1SLinus Walleij 
1174fd7f9b1SLinus Walleij 	/*
1184fd7f9b1SLinus Walleij 	 * The twd clock events must be reprogrammed to account for the new
1194fd7f9b1SLinus Walleij 	 * frequency.  The timer is local to a cpu, so cross-call to the
1204fd7f9b1SLinus Walleij 	 * changing cpu.
1214fd7f9b1SLinus Walleij 	 */
1224fd7f9b1SLinus Walleij 	if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE)
1234fd7f9b1SLinus Walleij 		smp_call_function_single(freqs->cpu, twd_update_frequency,
1243cd88f99SRussell King 			NULL, 1);
1254fd7f9b1SLinus Walleij 
1264fd7f9b1SLinus Walleij 	return NOTIFY_OK;
1274fd7f9b1SLinus Walleij }
1284fd7f9b1SLinus Walleij 
1294fd7f9b1SLinus Walleij static struct notifier_block twd_cpufreq_nb = {
1304fd7f9b1SLinus Walleij 	.notifier_call = twd_cpufreq_transition,
1314fd7f9b1SLinus Walleij };
1324fd7f9b1SLinus Walleij 
1334fd7f9b1SLinus Walleij static int twd_cpufreq_init(void)
1344fd7f9b1SLinus Walleij {
135910ba598SSantosh Shilimkar 	if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
1364fd7f9b1SLinus Walleij 		return cpufreq_register_notifier(&twd_cpufreq_nb,
1374fd7f9b1SLinus Walleij 			CPUFREQ_TRANSITION_NOTIFIER);
1384fd7f9b1SLinus Walleij 
1394fd7f9b1SLinus Walleij 	return 0;
1404fd7f9b1SLinus Walleij }
1414fd7f9b1SLinus Walleij core_initcall(twd_cpufreq_init);
1424fd7f9b1SLinus Walleij 
1434fd7f9b1SLinus Walleij #endif
1444fd7f9b1SLinus Walleij 
145f32f4ce2SRussell King static void __cpuinit twd_calibrate_rate(void)
146f32f4ce2SRussell King {
14703399c1cSRussell King 	unsigned long count;
148f32f4ce2SRussell King 	u64 waitjiffies;
149f32f4ce2SRussell King 
150f32f4ce2SRussell King 	/*
151f32f4ce2SRussell King 	 * If this is the first time round, we need to work out how fast
152f32f4ce2SRussell King 	 * the timer ticks
153f32f4ce2SRussell King 	 */
154f32f4ce2SRussell King 	if (twd_timer_rate == 0) {
1554c5158d4SRussell King 		printk(KERN_INFO "Calibrating local timer... ");
156f32f4ce2SRussell King 
157f32f4ce2SRussell King 		/* Wait for a tick to start */
158f32f4ce2SRussell King 		waitjiffies = get_jiffies_64() + 1;
159f32f4ce2SRussell King 
160f32f4ce2SRussell King 		while (get_jiffies_64() < waitjiffies)
161f32f4ce2SRussell King 			udelay(10);
162f32f4ce2SRussell King 
163f32f4ce2SRussell King 		/* OK, now the tick has started, let's get the timer going */
164f32f4ce2SRussell King 		waitjiffies += 5;
165f32f4ce2SRussell King 
166f32f4ce2SRussell King 				 /* enable, no interrupt or reload */
167f32f4ce2SRussell King 		__raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
168f32f4ce2SRussell King 
169f32f4ce2SRussell King 				 /* maximum value */
170f32f4ce2SRussell King 		__raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
171f32f4ce2SRussell King 
172f32f4ce2SRussell King 		while (get_jiffies_64() < waitjiffies)
173f32f4ce2SRussell King 			udelay(10);
174f32f4ce2SRussell King 
175f32f4ce2SRussell King 		count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
176f32f4ce2SRussell King 
177f32f4ce2SRussell King 		twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
178f32f4ce2SRussell King 
179f32f4ce2SRussell King 		printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
18090c5ffe5SVitaly Kuzmichev 			(twd_timer_rate / 10000) % 100);
181f32f4ce2SRussell King 	}
182f32f4ce2SRussell King }
183f32f4ce2SRussell King 
18428af690aSMarc Zyngier static irqreturn_t twd_handler(int irq, void *dev_id)
18528af690aSMarc Zyngier {
18628af690aSMarc Zyngier 	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
18728af690aSMarc Zyngier 
18828af690aSMarc Zyngier 	if (twd_timer_ack()) {
18928af690aSMarc Zyngier 		evt->event_handler(evt);
19028af690aSMarc Zyngier 		return IRQ_HANDLED;
19128af690aSMarc Zyngier 	}
19228af690aSMarc Zyngier 
19328af690aSMarc Zyngier 	return IRQ_NONE;
19428af690aSMarc Zyngier }
19528af690aSMarc Zyngier 
1965def51b0SLinus Walleij static struct clk *twd_get_clock(void)
1975def51b0SLinus Walleij {
1985def51b0SLinus Walleij 	struct clk *clk;
1995def51b0SLinus Walleij 	int err;
2005def51b0SLinus Walleij 
2015def51b0SLinus Walleij 	clk = clk_get_sys("smp_twd", NULL);
2025def51b0SLinus Walleij 	if (IS_ERR(clk)) {
2035def51b0SLinus Walleij 		pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk));
2045def51b0SLinus Walleij 		return clk;
2055def51b0SLinus Walleij 	}
2065def51b0SLinus Walleij 
2075def51b0SLinus Walleij 	err = clk_prepare(clk);
2085def51b0SLinus Walleij 	if (err) {
2095def51b0SLinus Walleij 		pr_err("smp_twd: clock failed to prepare: %d\n", err);
2105def51b0SLinus Walleij 		clk_put(clk);
2115def51b0SLinus Walleij 		return ERR_PTR(err);
2125def51b0SLinus Walleij 	}
2135def51b0SLinus Walleij 
2145def51b0SLinus Walleij 	err = clk_enable(clk);
2155def51b0SLinus Walleij 	if (err) {
2165def51b0SLinus Walleij 		pr_err("smp_twd: clock failed to enable: %d\n", err);
2175def51b0SLinus Walleij 		clk_unprepare(clk);
2185def51b0SLinus Walleij 		clk_put(clk);
2195def51b0SLinus Walleij 		return ERR_PTR(err);
2205def51b0SLinus Walleij 	}
2215def51b0SLinus Walleij 
2225def51b0SLinus Walleij 	return clk;
2235def51b0SLinus Walleij }
2245def51b0SLinus Walleij 
225f32f4ce2SRussell King /*
226f32f4ce2SRussell King  * Setup the local clock events for a CPU.
227f32f4ce2SRussell King  */
22892485104SMarc Zyngier static int __cpuinit twd_timer_setup(struct clock_event_device *clk)
229f32f4ce2SRussell King {
23028af690aSMarc Zyngier 	struct clock_event_device **this_cpu_clk;
23128af690aSMarc Zyngier 
2325def51b0SLinus Walleij 	if (!twd_clk)
2335def51b0SLinus Walleij 		twd_clk = twd_get_clock();
2345def51b0SLinus Walleij 
2355def51b0SLinus Walleij 	if (!IS_ERR_OR_NULL(twd_clk))
2365def51b0SLinus Walleij 		twd_timer_rate = clk_get_rate(twd_clk);
2375def51b0SLinus Walleij 	else
238f32f4ce2SRussell King 		twd_calibrate_rate();
239f32f4ce2SRussell King 
240c214455fSMarc Zyngier 	__raw_writel(0, twd_base + TWD_TIMER_CONTROL);
241c214455fSMarc Zyngier 
242f32f4ce2SRussell King 	clk->name = "local_timer";
2435388a6b2SRussell King 	clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
2445388a6b2SRussell King 			CLOCK_EVT_FEAT_C3STOP;
245f32f4ce2SRussell King 	clk->rating = 350;
246f32f4ce2SRussell King 	clk->set_mode = twd_set_mode;
247f32f4ce2SRussell King 	clk->set_next_event = twd_set_next_event;
24881e46f7bSMarc Zyngier 	clk->irq = twd_ppi;
249f32f4ce2SRussell King 
25028af690aSMarc Zyngier 	this_cpu_clk = __this_cpu_ptr(twd_evt);
25128af690aSMarc Zyngier 	*this_cpu_clk = clk;
25228af690aSMarc Zyngier 
25354d15b1dSLinus Walleij 	clockevents_config_and_register(clk, twd_timer_rate,
25454d15b1dSLinus Walleij 					0xf, 0xffffffff);
25528af690aSMarc Zyngier 	enable_percpu_irq(clk->irq, 0);
25681e46f7bSMarc Zyngier 
25781e46f7bSMarc Zyngier 	return 0;
25881e46f7bSMarc Zyngier }
25981e46f7bSMarc Zyngier 
26081e46f7bSMarc Zyngier static struct local_timer_ops twd_lt_ops __cpuinitdata = {
26181e46f7bSMarc Zyngier 	.setup	= twd_timer_setup,
26281e46f7bSMarc Zyngier 	.stop	= twd_timer_stop,
26381e46f7bSMarc Zyngier };
26481e46f7bSMarc Zyngier 
265d8e03643SMarc Zyngier static int __init twd_local_timer_common_register(void)
26681e46f7bSMarc Zyngier {
26781e46f7bSMarc Zyngier 	int err;
26881e46f7bSMarc Zyngier 
26981e46f7bSMarc Zyngier 	twd_evt = alloc_percpu(struct clock_event_device *);
270d8e03643SMarc Zyngier 	if (!twd_evt) {
27181e46f7bSMarc Zyngier 		err = -ENOMEM;
272d8e03643SMarc Zyngier 		goto out_free;
27381e46f7bSMarc Zyngier 	}
27481e46f7bSMarc Zyngier 
27581e46f7bSMarc Zyngier 	err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
27681e46f7bSMarc Zyngier 	if (err) {
27781e46f7bSMarc Zyngier 		pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
278d8e03643SMarc Zyngier 		goto out_free;
27981e46f7bSMarc Zyngier 	}
28081e46f7bSMarc Zyngier 
28181e46f7bSMarc Zyngier 	err = local_timer_register(&twd_lt_ops);
28281e46f7bSMarc Zyngier 	if (err)
283d8e03643SMarc Zyngier 		goto out_irq;
28481e46f7bSMarc Zyngier 
28581e46f7bSMarc Zyngier 	return 0;
28681e46f7bSMarc Zyngier 
287d8e03643SMarc Zyngier out_irq:
288d8e03643SMarc Zyngier 	free_percpu_irq(twd_ppi, twd_evt);
289d8e03643SMarc Zyngier out_free:
29081e46f7bSMarc Zyngier 	iounmap(twd_base);
291d8e03643SMarc Zyngier 	twd_base = NULL;
29281e46f7bSMarc Zyngier 	free_percpu(twd_evt);
293d8e03643SMarc Zyngier 
29481e46f7bSMarc Zyngier 	return err;
295f32f4ce2SRussell King }
296d8e03643SMarc Zyngier 
297d8e03643SMarc Zyngier int __init twd_local_timer_register(struct twd_local_timer *tlt)
298d8e03643SMarc Zyngier {
299d8e03643SMarc Zyngier 	if (twd_base || twd_evt)
300d8e03643SMarc Zyngier 		return -EBUSY;
301d8e03643SMarc Zyngier 
302d8e03643SMarc Zyngier 	twd_ppi	= tlt->res[1].start;
303d8e03643SMarc Zyngier 
304d8e03643SMarc Zyngier 	twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0]));
305d8e03643SMarc Zyngier 	if (!twd_base)
306d8e03643SMarc Zyngier 		return -ENOMEM;
307d8e03643SMarc Zyngier 
308d8e03643SMarc Zyngier 	return twd_local_timer_common_register();
309d8e03643SMarc Zyngier }
310d8e03643SMarc Zyngier 
311d8e03643SMarc Zyngier #ifdef CONFIG_OF
312d8e03643SMarc Zyngier const static struct of_device_id twd_of_match[] __initconst = {
313d8e03643SMarc Zyngier 	{ .compatible = "arm,cortex-a9-twd-timer",	},
314d8e03643SMarc Zyngier 	{ .compatible = "arm,cortex-a5-twd-timer",	},
315d8e03643SMarc Zyngier 	{ .compatible = "arm,arm11mp-twd-timer",	},
316d8e03643SMarc Zyngier 	{ },
317d8e03643SMarc Zyngier };
318d8e03643SMarc Zyngier 
319d8e03643SMarc Zyngier void __init twd_local_timer_of_register(void)
320d8e03643SMarc Zyngier {
321d8e03643SMarc Zyngier 	struct device_node *np;
322d8e03643SMarc Zyngier 	int err;
323d8e03643SMarc Zyngier 
324d8e03643SMarc Zyngier 	np = of_find_matching_node(NULL, twd_of_match);
325d8e03643SMarc Zyngier 	if (!np) {
326d8e03643SMarc Zyngier 		err = -ENODEV;
327d8e03643SMarc Zyngier 		goto out;
328d8e03643SMarc Zyngier 	}
329d8e03643SMarc Zyngier 
330d8e03643SMarc Zyngier 	twd_ppi = irq_of_parse_and_map(np, 0);
331d8e03643SMarc Zyngier 	if (!twd_ppi) {
332d8e03643SMarc Zyngier 		err = -EINVAL;
333d8e03643SMarc Zyngier 		goto out;
334d8e03643SMarc Zyngier 	}
335d8e03643SMarc Zyngier 
336d8e03643SMarc Zyngier 	twd_base = of_iomap(np, 0);
337d8e03643SMarc Zyngier 	if (!twd_base) {
338d8e03643SMarc Zyngier 		err = -ENOMEM;
339d8e03643SMarc Zyngier 		goto out;
340d8e03643SMarc Zyngier 	}
341d8e03643SMarc Zyngier 
342d8e03643SMarc Zyngier 	err = twd_local_timer_common_register();
343d8e03643SMarc Zyngier 
344d8e03643SMarc Zyngier out:
345d8e03643SMarc Zyngier 	WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
346d8e03643SMarc Zyngier }
347d8e03643SMarc Zyngier #endif
348