xref: /openbmc/linux/arch/arm/kernel/smp_twd.c (revision d8e03643)
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>
21f32f4ce2SRussell King #include <linux/irq.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 */
31f32f4ce2SRussell King 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  */
83f32f4ce2SRussell King 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 
99abde710cSMarc Zyngier /* Temporary hack to be removed when all TWD users are converted to
100abde710cSMarc Zyngier    the new registration interface */
101abde710cSMarc Zyngier void local_timer_stop(struct clock_event_device *clk)
102abde710cSMarc Zyngier 	__attribute__ ((alias ("twd_timer_stop")));
103abde710cSMarc Zyngier 
1044fd7f9b1SLinus Walleij #ifdef CONFIG_CPU_FREQ
1054fd7f9b1SLinus Walleij 
1064fd7f9b1SLinus Walleij /*
1074fd7f9b1SLinus Walleij  * Updates clockevent frequency when the cpu frequency changes.
1084fd7f9b1SLinus Walleij  * Called on the cpu that is changing frequency with interrupts disabled.
1094fd7f9b1SLinus Walleij  */
1104fd7f9b1SLinus Walleij static void twd_update_frequency(void *data)
1114fd7f9b1SLinus Walleij {
1124fd7f9b1SLinus Walleij 	twd_timer_rate = clk_get_rate(twd_clk);
1134fd7f9b1SLinus Walleij 
1144fd7f9b1SLinus Walleij 	clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
1154fd7f9b1SLinus Walleij }
1164fd7f9b1SLinus Walleij 
1174fd7f9b1SLinus Walleij static int twd_cpufreq_transition(struct notifier_block *nb,
1184fd7f9b1SLinus Walleij 	unsigned long state, void *data)
1194fd7f9b1SLinus Walleij {
1204fd7f9b1SLinus Walleij 	struct cpufreq_freqs *freqs = data;
1214fd7f9b1SLinus Walleij 
1224fd7f9b1SLinus Walleij 	/*
1234fd7f9b1SLinus Walleij 	 * The twd clock events must be reprogrammed to account for the new
1244fd7f9b1SLinus Walleij 	 * frequency.  The timer is local to a cpu, so cross-call to the
1254fd7f9b1SLinus Walleij 	 * changing cpu.
1264fd7f9b1SLinus Walleij 	 */
1274fd7f9b1SLinus Walleij 	if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE)
1284fd7f9b1SLinus Walleij 		smp_call_function_single(freqs->cpu, twd_update_frequency,
1294fd7f9b1SLinus Walleij 			NULL, 1);
1304fd7f9b1SLinus Walleij 
1314fd7f9b1SLinus Walleij 	return NOTIFY_OK;
1324fd7f9b1SLinus Walleij }
1334fd7f9b1SLinus Walleij 
1344fd7f9b1SLinus Walleij static struct notifier_block twd_cpufreq_nb = {
1354fd7f9b1SLinus Walleij 	.notifier_call = twd_cpufreq_transition,
1364fd7f9b1SLinus Walleij };
1374fd7f9b1SLinus Walleij 
1384fd7f9b1SLinus Walleij static int twd_cpufreq_init(void)
1394fd7f9b1SLinus Walleij {
1404fd7f9b1SLinus Walleij 	if (!IS_ERR(twd_clk))
1414fd7f9b1SLinus Walleij 		return cpufreq_register_notifier(&twd_cpufreq_nb,
1424fd7f9b1SLinus Walleij 			CPUFREQ_TRANSITION_NOTIFIER);
1434fd7f9b1SLinus Walleij 
1444fd7f9b1SLinus Walleij 	return 0;
1454fd7f9b1SLinus Walleij }
1464fd7f9b1SLinus Walleij core_initcall(twd_cpufreq_init);
1474fd7f9b1SLinus Walleij 
1484fd7f9b1SLinus Walleij #endif
1494fd7f9b1SLinus Walleij 
150f32f4ce2SRussell King static void __cpuinit twd_calibrate_rate(void)
151f32f4ce2SRussell King {
15203399c1cSRussell King 	unsigned long count;
153f32f4ce2SRussell King 	u64 waitjiffies;
154f32f4ce2SRussell King 
155f32f4ce2SRussell King 	/*
156f32f4ce2SRussell King 	 * If this is the first time round, we need to work out how fast
157f32f4ce2SRussell King 	 * the timer ticks
158f32f4ce2SRussell King 	 */
159f32f4ce2SRussell King 	if (twd_timer_rate == 0) {
1604c5158d4SRussell King 		printk(KERN_INFO "Calibrating local timer... ");
161f32f4ce2SRussell King 
162f32f4ce2SRussell King 		/* Wait for a tick to start */
163f32f4ce2SRussell King 		waitjiffies = get_jiffies_64() + 1;
164f32f4ce2SRussell King 
165f32f4ce2SRussell King 		while (get_jiffies_64() < waitjiffies)
166f32f4ce2SRussell King 			udelay(10);
167f32f4ce2SRussell King 
168f32f4ce2SRussell King 		/* OK, now the tick has started, let's get the timer going */
169f32f4ce2SRussell King 		waitjiffies += 5;
170f32f4ce2SRussell King 
171f32f4ce2SRussell King 				 /* enable, no interrupt or reload */
172f32f4ce2SRussell King 		__raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
173f32f4ce2SRussell King 
174f32f4ce2SRussell King 				 /* maximum value */
175f32f4ce2SRussell King 		__raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
176f32f4ce2SRussell King 
177f32f4ce2SRussell King 		while (get_jiffies_64() < waitjiffies)
178f32f4ce2SRussell King 			udelay(10);
179f32f4ce2SRussell King 
180f32f4ce2SRussell King 		count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
181f32f4ce2SRussell King 
182f32f4ce2SRussell King 		twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
183f32f4ce2SRussell King 
184f32f4ce2SRussell King 		printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
18590c5ffe5SVitaly Kuzmichev 			(twd_timer_rate / 10000) % 100);
186f32f4ce2SRussell King 	}
187f32f4ce2SRussell King }
188f32f4ce2SRussell King 
18928af690aSMarc Zyngier static irqreturn_t twd_handler(int irq, void *dev_id)
19028af690aSMarc Zyngier {
19128af690aSMarc Zyngier 	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
19228af690aSMarc Zyngier 
19328af690aSMarc Zyngier 	if (twd_timer_ack()) {
19428af690aSMarc Zyngier 		evt->event_handler(evt);
19528af690aSMarc Zyngier 		return IRQ_HANDLED;
19628af690aSMarc Zyngier 	}
19728af690aSMarc Zyngier 
19828af690aSMarc Zyngier 	return IRQ_NONE;
19928af690aSMarc Zyngier }
20028af690aSMarc Zyngier 
2015def51b0SLinus Walleij static struct clk *twd_get_clock(void)
2025def51b0SLinus Walleij {
2035def51b0SLinus Walleij 	struct clk *clk;
2045def51b0SLinus Walleij 	int err;
2055def51b0SLinus Walleij 
2065def51b0SLinus Walleij 	clk = clk_get_sys("smp_twd", NULL);
2075def51b0SLinus Walleij 	if (IS_ERR(clk)) {
2085def51b0SLinus Walleij 		pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk));
2095def51b0SLinus Walleij 		return clk;
2105def51b0SLinus Walleij 	}
2115def51b0SLinus Walleij 
2125def51b0SLinus Walleij 	err = clk_prepare(clk);
2135def51b0SLinus Walleij 	if (err) {
2145def51b0SLinus Walleij 		pr_err("smp_twd: clock failed to prepare: %d\n", err);
2155def51b0SLinus Walleij 		clk_put(clk);
2165def51b0SLinus Walleij 		return ERR_PTR(err);
2175def51b0SLinus Walleij 	}
2185def51b0SLinus Walleij 
2195def51b0SLinus Walleij 	err = clk_enable(clk);
2205def51b0SLinus Walleij 	if (err) {
2215def51b0SLinus Walleij 		pr_err("smp_twd: clock failed to enable: %d\n", err);
2225def51b0SLinus Walleij 		clk_unprepare(clk);
2235def51b0SLinus Walleij 		clk_put(clk);
2245def51b0SLinus Walleij 		return ERR_PTR(err);
2255def51b0SLinus Walleij 	}
2265def51b0SLinus Walleij 
2275def51b0SLinus Walleij 	return clk;
2285def51b0SLinus Walleij }
2295def51b0SLinus Walleij 
230f32f4ce2SRussell King /*
231f32f4ce2SRussell King  * Setup the local clock events for a CPU.
232f32f4ce2SRussell King  */
23381e46f7bSMarc Zyngier int __cpuinit twd_timer_setup(struct clock_event_device *clk)
234f32f4ce2SRussell King {
23528af690aSMarc Zyngier 	struct clock_event_device **this_cpu_clk;
23628af690aSMarc Zyngier 
23728af690aSMarc Zyngier 	if (!twd_evt) {
23828af690aSMarc Zyngier 		int err;
23928af690aSMarc Zyngier 
24028af690aSMarc Zyngier 		twd_evt = alloc_percpu(struct clock_event_device *);
24128af690aSMarc Zyngier 		if (!twd_evt) {
24228af690aSMarc Zyngier 			pr_err("twd: can't allocate memory\n");
24381e46f7bSMarc Zyngier 			return -ENOMEM;
24428af690aSMarc Zyngier 		}
24528af690aSMarc Zyngier 
24628af690aSMarc Zyngier 		err = request_percpu_irq(clk->irq, twd_handler,
24728af690aSMarc Zyngier 					 "twd", twd_evt);
24828af690aSMarc Zyngier 		if (err) {
24928af690aSMarc Zyngier 			pr_err("twd: can't register interrupt %d (%d)\n",
25028af690aSMarc Zyngier 			       clk->irq, err);
25181e46f7bSMarc Zyngier 			return err;
25228af690aSMarc Zyngier 		}
25328af690aSMarc Zyngier 	}
25428af690aSMarc Zyngier 
2555def51b0SLinus Walleij 	if (!twd_clk)
2565def51b0SLinus Walleij 		twd_clk = twd_get_clock();
2575def51b0SLinus Walleij 
2585def51b0SLinus Walleij 	if (!IS_ERR_OR_NULL(twd_clk))
2595def51b0SLinus Walleij 		twd_timer_rate = clk_get_rate(twd_clk);
2605def51b0SLinus Walleij 	else
261f32f4ce2SRussell King 		twd_calibrate_rate();
262f32f4ce2SRussell King 
263c214455fSMarc Zyngier 	__raw_writel(0, twd_base + TWD_TIMER_CONTROL);
264c214455fSMarc Zyngier 
265f32f4ce2SRussell King 	clk->name = "local_timer";
2665388a6b2SRussell King 	clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
2675388a6b2SRussell King 			CLOCK_EVT_FEAT_C3STOP;
268f32f4ce2SRussell King 	clk->rating = 350;
269f32f4ce2SRussell King 	clk->set_mode = twd_set_mode;
270f32f4ce2SRussell King 	clk->set_next_event = twd_set_next_event;
27181e46f7bSMarc Zyngier 	if (!clk->irq)
27281e46f7bSMarc Zyngier 		clk->irq = twd_ppi;
273f32f4ce2SRussell King 
27428af690aSMarc Zyngier 	this_cpu_clk = __this_cpu_ptr(twd_evt);
27528af690aSMarc Zyngier 	*this_cpu_clk = clk;
27628af690aSMarc Zyngier 
27754d15b1dSLinus Walleij 	clockevents_config_and_register(clk, twd_timer_rate,
27854d15b1dSLinus Walleij 					0xf, 0xffffffff);
27928af690aSMarc Zyngier 	enable_percpu_irq(clk->irq, 0);
28081e46f7bSMarc Zyngier 
28181e46f7bSMarc Zyngier 	return 0;
28281e46f7bSMarc Zyngier }
28381e46f7bSMarc Zyngier 
28481e46f7bSMarc Zyngier static struct local_timer_ops twd_lt_ops __cpuinitdata = {
28581e46f7bSMarc Zyngier 	.setup	= twd_timer_setup,
28681e46f7bSMarc Zyngier 	.stop	= twd_timer_stop,
28781e46f7bSMarc Zyngier };
28881e46f7bSMarc Zyngier 
289d8e03643SMarc Zyngier static int __init twd_local_timer_common_register(void)
29081e46f7bSMarc Zyngier {
29181e46f7bSMarc Zyngier 	int err;
29281e46f7bSMarc Zyngier 
29381e46f7bSMarc Zyngier 	twd_evt = alloc_percpu(struct clock_event_device *);
294d8e03643SMarc Zyngier 	if (!twd_evt) {
29581e46f7bSMarc Zyngier 		err = -ENOMEM;
296d8e03643SMarc Zyngier 		goto out_free;
29781e46f7bSMarc Zyngier 	}
29881e46f7bSMarc Zyngier 
29981e46f7bSMarc Zyngier 	err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
30081e46f7bSMarc Zyngier 	if (err) {
30181e46f7bSMarc Zyngier 		pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
302d8e03643SMarc Zyngier 		goto out_free;
30381e46f7bSMarc Zyngier 	}
30481e46f7bSMarc Zyngier 
30581e46f7bSMarc Zyngier 	err = local_timer_register(&twd_lt_ops);
30681e46f7bSMarc Zyngier 	if (err)
307d8e03643SMarc Zyngier 		goto out_irq;
30881e46f7bSMarc Zyngier 
30981e46f7bSMarc Zyngier 	return 0;
31081e46f7bSMarc Zyngier 
311d8e03643SMarc Zyngier out_irq:
312d8e03643SMarc Zyngier 	free_percpu_irq(twd_ppi, twd_evt);
313d8e03643SMarc Zyngier out_free:
31481e46f7bSMarc Zyngier 	iounmap(twd_base);
315d8e03643SMarc Zyngier 	twd_base = NULL;
31681e46f7bSMarc Zyngier 	free_percpu(twd_evt);
317d8e03643SMarc Zyngier 
31881e46f7bSMarc Zyngier 	return err;
319f32f4ce2SRussell King }
320d8e03643SMarc Zyngier 
321d8e03643SMarc Zyngier int __init twd_local_timer_register(struct twd_local_timer *tlt)
322d8e03643SMarc Zyngier {
323d8e03643SMarc Zyngier 	if (twd_base || twd_evt)
324d8e03643SMarc Zyngier 		return -EBUSY;
325d8e03643SMarc Zyngier 
326d8e03643SMarc Zyngier 	twd_ppi	= tlt->res[1].start;
327d8e03643SMarc Zyngier 
328d8e03643SMarc Zyngier 	twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0]));
329d8e03643SMarc Zyngier 	if (!twd_base)
330d8e03643SMarc Zyngier 		return -ENOMEM;
331d8e03643SMarc Zyngier 
332d8e03643SMarc Zyngier 	return twd_local_timer_common_register();
333d8e03643SMarc Zyngier }
334d8e03643SMarc Zyngier 
335d8e03643SMarc Zyngier #ifdef CONFIG_OF
336d8e03643SMarc Zyngier const static struct of_device_id twd_of_match[] __initconst = {
337d8e03643SMarc Zyngier 	{ .compatible = "arm,cortex-a9-twd-timer",	},
338d8e03643SMarc Zyngier 	{ .compatible = "arm,cortex-a5-twd-timer",	},
339d8e03643SMarc Zyngier 	{ .compatible = "arm,arm11mp-twd-timer",	},
340d8e03643SMarc Zyngier 	{ },
341d8e03643SMarc Zyngier };
342d8e03643SMarc Zyngier 
343d8e03643SMarc Zyngier void __init twd_local_timer_of_register(void)
344d8e03643SMarc Zyngier {
345d8e03643SMarc Zyngier 	struct device_node *np;
346d8e03643SMarc Zyngier 	int err;
347d8e03643SMarc Zyngier 
348d8e03643SMarc Zyngier 	np = of_find_matching_node(NULL, twd_of_match);
349d8e03643SMarc Zyngier 	if (!np) {
350d8e03643SMarc Zyngier 		err = -ENODEV;
351d8e03643SMarc Zyngier 		goto out;
352d8e03643SMarc Zyngier 	}
353d8e03643SMarc Zyngier 
354d8e03643SMarc Zyngier 	twd_ppi = irq_of_parse_and_map(np, 0);
355d8e03643SMarc Zyngier 	if (!twd_ppi) {
356d8e03643SMarc Zyngier 		err = -EINVAL;
357d8e03643SMarc Zyngier 		goto out;
358d8e03643SMarc Zyngier 	}
359d8e03643SMarc Zyngier 
360d8e03643SMarc Zyngier 	twd_base = of_iomap(np, 0);
361d8e03643SMarc Zyngier 	if (!twd_base) {
362d8e03643SMarc Zyngier 		err = -ENOMEM;
363d8e03643SMarc Zyngier 		goto out;
364d8e03643SMarc Zyngier 	}
365d8e03643SMarc Zyngier 
366d8e03643SMarc Zyngier 	err = twd_local_timer_common_register();
367d8e03643SMarc Zyngier 
368d8e03643SMarc Zyngier out:
369d8e03643SMarc Zyngier 	WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
370d8e03643SMarc Zyngier }
371d8e03643SMarc Zyngier #endif
372