xref: /openbmc/linux/arch/arm/kernel/smp_twd.c (revision 4fd7f9b1)
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>
23f32f4ce2SRussell King 
24f32f4ce2SRussell King #include <asm/smp_twd.h>
2528af690aSMarc Zyngier #include <asm/localtimer.h>
26f32f4ce2SRussell King #include <asm/hardware/gic.h>
27f32f4ce2SRussell King 
28f32f4ce2SRussell King /* set up by the platform code */
29f32f4ce2SRussell King void __iomem *twd_base;
30f32f4ce2SRussell King 
315def51b0SLinus Walleij static struct clk *twd_clk;
32f32f4ce2SRussell King static unsigned long twd_timer_rate;
33f32f4ce2SRussell King 
3428af690aSMarc Zyngier static struct clock_event_device __percpu **twd_evt;
3528af690aSMarc Zyngier 
36f32f4ce2SRussell King static void twd_set_mode(enum clock_event_mode mode,
37f32f4ce2SRussell King 			struct clock_event_device *clk)
38f32f4ce2SRussell King {
39f32f4ce2SRussell King 	unsigned long ctrl;
40f32f4ce2SRussell King 
41f32f4ce2SRussell King 	switch (mode) {
42f32f4ce2SRussell King 	case CLOCK_EVT_MODE_PERIODIC:
43f32f4ce2SRussell King 		/* timer load already set up */
44f32f4ce2SRussell King 		ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
45f32f4ce2SRussell King 			| TWD_TIMER_CONTROL_PERIODIC;
4603399c1cSRussell King 		__raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD);
47f32f4ce2SRussell King 		break;
48f32f4ce2SRussell King 	case CLOCK_EVT_MODE_ONESHOT:
49f32f4ce2SRussell King 		/* period set, and timer enabled in 'next_event' hook */
50f32f4ce2SRussell King 		ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
51f32f4ce2SRussell King 		break;
52f32f4ce2SRussell King 	case CLOCK_EVT_MODE_UNUSED:
53f32f4ce2SRussell King 	case CLOCK_EVT_MODE_SHUTDOWN:
54f32f4ce2SRussell King 	default:
55f32f4ce2SRussell King 		ctrl = 0;
56f32f4ce2SRussell King 	}
57f32f4ce2SRussell King 
58f32f4ce2SRussell King 	__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
59f32f4ce2SRussell King }
60f32f4ce2SRussell King 
61f32f4ce2SRussell King static int twd_set_next_event(unsigned long evt,
62f32f4ce2SRussell King 			struct clock_event_device *unused)
63f32f4ce2SRussell King {
64f32f4ce2SRussell King 	unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
65f32f4ce2SRussell King 
664c5158d4SRussell King 	ctrl |= TWD_TIMER_CONTROL_ENABLE;
674c5158d4SRussell King 
68f32f4ce2SRussell King 	__raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
694c5158d4SRussell King 	__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
70f32f4ce2SRussell King 
71f32f4ce2SRussell King 	return 0;
72f32f4ce2SRussell King }
73f32f4ce2SRussell King 
74f32f4ce2SRussell King /*
75f32f4ce2SRussell King  * local_timer_ack: checks for a local timer interrupt.
76f32f4ce2SRussell King  *
77f32f4ce2SRussell King  * If a local timer interrupt has occurred, acknowledge and return 1.
78f32f4ce2SRussell King  * Otherwise, return 0.
79f32f4ce2SRussell King  */
80f32f4ce2SRussell King int twd_timer_ack(void)
81f32f4ce2SRussell King {
82f32f4ce2SRussell King 	if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
83f32f4ce2SRussell King 		__raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
84f32f4ce2SRussell King 		return 1;
85f32f4ce2SRussell King 	}
86f32f4ce2SRussell King 
87f32f4ce2SRussell King 	return 0;
88f32f4ce2SRussell King }
89f32f4ce2SRussell King 
9028af690aSMarc Zyngier void twd_timer_stop(struct clock_event_device *clk)
9128af690aSMarc Zyngier {
9228af690aSMarc Zyngier 	twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
9328af690aSMarc Zyngier 	disable_percpu_irq(clk->irq);
9428af690aSMarc Zyngier }
9528af690aSMarc Zyngier 
964fd7f9b1SLinus Walleij #ifdef CONFIG_CPU_FREQ
974fd7f9b1SLinus Walleij 
984fd7f9b1SLinus Walleij /*
994fd7f9b1SLinus Walleij  * Updates clockevent frequency when the cpu frequency changes.
1004fd7f9b1SLinus Walleij  * Called on the cpu that is changing frequency with interrupts disabled.
1014fd7f9b1SLinus Walleij  */
1024fd7f9b1SLinus Walleij static void twd_update_frequency(void *data)
1034fd7f9b1SLinus Walleij {
1044fd7f9b1SLinus Walleij 	twd_timer_rate = clk_get_rate(twd_clk);
1054fd7f9b1SLinus Walleij 
1064fd7f9b1SLinus Walleij 	clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
1074fd7f9b1SLinus Walleij }
1084fd7f9b1SLinus Walleij 
1094fd7f9b1SLinus Walleij static int twd_cpufreq_transition(struct notifier_block *nb,
1104fd7f9b1SLinus Walleij 	unsigned long state, void *data)
1114fd7f9b1SLinus Walleij {
1124fd7f9b1SLinus Walleij 	struct cpufreq_freqs *freqs = data;
1134fd7f9b1SLinus Walleij 
1144fd7f9b1SLinus Walleij 	/*
1154fd7f9b1SLinus Walleij 	 * The twd clock events must be reprogrammed to account for the new
1164fd7f9b1SLinus Walleij 	 * frequency.  The timer is local to a cpu, so cross-call to the
1174fd7f9b1SLinus Walleij 	 * changing cpu.
1184fd7f9b1SLinus Walleij 	 */
1194fd7f9b1SLinus Walleij 	if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE)
1204fd7f9b1SLinus Walleij 		smp_call_function_single(freqs->cpu, twd_update_frequency,
1214fd7f9b1SLinus Walleij 			NULL, 1);
1224fd7f9b1SLinus Walleij 
1234fd7f9b1SLinus Walleij 	return NOTIFY_OK;
1244fd7f9b1SLinus Walleij }
1254fd7f9b1SLinus Walleij 
1264fd7f9b1SLinus Walleij static struct notifier_block twd_cpufreq_nb = {
1274fd7f9b1SLinus Walleij 	.notifier_call = twd_cpufreq_transition,
1284fd7f9b1SLinus Walleij };
1294fd7f9b1SLinus Walleij 
1304fd7f9b1SLinus Walleij static int twd_cpufreq_init(void)
1314fd7f9b1SLinus Walleij {
1324fd7f9b1SLinus Walleij 	if (!IS_ERR(twd_clk))
1334fd7f9b1SLinus Walleij 		return cpufreq_register_notifier(&twd_cpufreq_nb,
1344fd7f9b1SLinus Walleij 			CPUFREQ_TRANSITION_NOTIFIER);
1354fd7f9b1SLinus Walleij 
1364fd7f9b1SLinus Walleij 	return 0;
1374fd7f9b1SLinus Walleij }
1384fd7f9b1SLinus Walleij core_initcall(twd_cpufreq_init);
1394fd7f9b1SLinus Walleij 
1404fd7f9b1SLinus Walleij #endif
1414fd7f9b1SLinus Walleij 
142f32f4ce2SRussell King static void __cpuinit twd_calibrate_rate(void)
143f32f4ce2SRussell King {
14403399c1cSRussell King 	unsigned long count;
145f32f4ce2SRussell King 	u64 waitjiffies;
146f32f4ce2SRussell King 
147f32f4ce2SRussell King 	/*
148f32f4ce2SRussell King 	 * If this is the first time round, we need to work out how fast
149f32f4ce2SRussell King 	 * the timer ticks
150f32f4ce2SRussell King 	 */
151f32f4ce2SRussell King 	if (twd_timer_rate == 0) {
1524c5158d4SRussell King 		printk(KERN_INFO "Calibrating local timer... ");
153f32f4ce2SRussell King 
154f32f4ce2SRussell King 		/* Wait for a tick to start */
155f32f4ce2SRussell King 		waitjiffies = get_jiffies_64() + 1;
156f32f4ce2SRussell King 
157f32f4ce2SRussell King 		while (get_jiffies_64() < waitjiffies)
158f32f4ce2SRussell King 			udelay(10);
159f32f4ce2SRussell King 
160f32f4ce2SRussell King 		/* OK, now the tick has started, let's get the timer going */
161f32f4ce2SRussell King 		waitjiffies += 5;
162f32f4ce2SRussell King 
163f32f4ce2SRussell King 				 /* enable, no interrupt or reload */
164f32f4ce2SRussell King 		__raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
165f32f4ce2SRussell King 
166f32f4ce2SRussell King 				 /* maximum value */
167f32f4ce2SRussell King 		__raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
168f32f4ce2SRussell King 
169f32f4ce2SRussell King 		while (get_jiffies_64() < waitjiffies)
170f32f4ce2SRussell King 			udelay(10);
171f32f4ce2SRussell King 
172f32f4ce2SRussell King 		count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
173f32f4ce2SRussell King 
174f32f4ce2SRussell King 		twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
175f32f4ce2SRussell King 
176f32f4ce2SRussell King 		printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
17790c5ffe5SVitaly Kuzmichev 			(twd_timer_rate / 10000) % 100);
178f32f4ce2SRussell King 	}
179f32f4ce2SRussell King }
180f32f4ce2SRussell King 
18128af690aSMarc Zyngier static irqreturn_t twd_handler(int irq, void *dev_id)
18228af690aSMarc Zyngier {
18328af690aSMarc Zyngier 	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
18428af690aSMarc Zyngier 
18528af690aSMarc Zyngier 	if (twd_timer_ack()) {
18628af690aSMarc Zyngier 		evt->event_handler(evt);
18728af690aSMarc Zyngier 		return IRQ_HANDLED;
18828af690aSMarc Zyngier 	}
18928af690aSMarc Zyngier 
19028af690aSMarc Zyngier 	return IRQ_NONE;
19128af690aSMarc Zyngier }
19228af690aSMarc Zyngier 
1935def51b0SLinus Walleij static struct clk *twd_get_clock(void)
1945def51b0SLinus Walleij {
1955def51b0SLinus Walleij 	struct clk *clk;
1965def51b0SLinus Walleij 	int err;
1975def51b0SLinus Walleij 
1985def51b0SLinus Walleij 	clk = clk_get_sys("smp_twd", NULL);
1995def51b0SLinus Walleij 	if (IS_ERR(clk)) {
2005def51b0SLinus Walleij 		pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk));
2015def51b0SLinus Walleij 		return clk;
2025def51b0SLinus Walleij 	}
2035def51b0SLinus Walleij 
2045def51b0SLinus Walleij 	err = clk_prepare(clk);
2055def51b0SLinus Walleij 	if (err) {
2065def51b0SLinus Walleij 		pr_err("smp_twd: clock failed to prepare: %d\n", err);
2075def51b0SLinus Walleij 		clk_put(clk);
2085def51b0SLinus Walleij 		return ERR_PTR(err);
2095def51b0SLinus Walleij 	}
2105def51b0SLinus Walleij 
2115def51b0SLinus Walleij 	err = clk_enable(clk);
2125def51b0SLinus Walleij 	if (err) {
2135def51b0SLinus Walleij 		pr_err("smp_twd: clock failed to enable: %d\n", err);
2145def51b0SLinus Walleij 		clk_unprepare(clk);
2155def51b0SLinus Walleij 		clk_put(clk);
2165def51b0SLinus Walleij 		return ERR_PTR(err);
2175def51b0SLinus Walleij 	}
2185def51b0SLinus Walleij 
2195def51b0SLinus Walleij 	return clk;
2205def51b0SLinus Walleij }
2215def51b0SLinus Walleij 
222f32f4ce2SRussell King /*
223f32f4ce2SRussell King  * Setup the local clock events for a CPU.
224f32f4ce2SRussell King  */
225f32f4ce2SRussell King void __cpuinit twd_timer_setup(struct clock_event_device *clk)
226f32f4ce2SRussell King {
22728af690aSMarc Zyngier 	struct clock_event_device **this_cpu_clk;
22828af690aSMarc Zyngier 
22928af690aSMarc Zyngier 	if (!twd_evt) {
23028af690aSMarc Zyngier 		int err;
23128af690aSMarc Zyngier 
23228af690aSMarc Zyngier 		twd_evt = alloc_percpu(struct clock_event_device *);
23328af690aSMarc Zyngier 		if (!twd_evt) {
23428af690aSMarc Zyngier 			pr_err("twd: can't allocate memory\n");
23528af690aSMarc Zyngier 			return;
23628af690aSMarc Zyngier 		}
23728af690aSMarc Zyngier 
23828af690aSMarc Zyngier 		err = request_percpu_irq(clk->irq, twd_handler,
23928af690aSMarc Zyngier 					 "twd", twd_evt);
24028af690aSMarc Zyngier 		if (err) {
24128af690aSMarc Zyngier 			pr_err("twd: can't register interrupt %d (%d)\n",
24228af690aSMarc Zyngier 			       clk->irq, err);
24328af690aSMarc Zyngier 			return;
24428af690aSMarc Zyngier 		}
24528af690aSMarc Zyngier 	}
24628af690aSMarc Zyngier 
2475def51b0SLinus Walleij 	if (!twd_clk)
2485def51b0SLinus Walleij 		twd_clk = twd_get_clock();
2495def51b0SLinus Walleij 
2505def51b0SLinus Walleij 	if (!IS_ERR_OR_NULL(twd_clk))
2515def51b0SLinus Walleij 		twd_timer_rate = clk_get_rate(twd_clk);
2525def51b0SLinus Walleij 	else
253f32f4ce2SRussell King 		twd_calibrate_rate();
254f32f4ce2SRussell King 
255f32f4ce2SRussell King 	clk->name = "local_timer";
2565388a6b2SRussell King 	clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
2575388a6b2SRussell King 			CLOCK_EVT_FEAT_C3STOP;
258f32f4ce2SRussell King 	clk->rating = 350;
259f32f4ce2SRussell King 	clk->set_mode = twd_set_mode;
260f32f4ce2SRussell King 	clk->set_next_event = twd_set_next_event;
261f32f4ce2SRussell King 
26228af690aSMarc Zyngier 	this_cpu_clk = __this_cpu_ptr(twd_evt);
26328af690aSMarc Zyngier 	*this_cpu_clk = clk;
26428af690aSMarc Zyngier 
26554d15b1dSLinus Walleij 	clockevents_config_and_register(clk, twd_timer_rate,
26654d15b1dSLinus Walleij 					0xf, 0xffffffff);
26728af690aSMarc Zyngier 	enable_percpu_irq(clk->irq, 0);
268f32f4ce2SRussell King }
269