xref: /openbmc/linux/arch/arm/kernel/smp_twd.c (revision 7490ca1e)
1 /*
2  *  linux/arch/arm/kernel/smp_twd.c
3  *
4  *  Copyright (C) 2002 ARM Ltd.
5  *  All Rights Reserved
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/clk.h>
14 #include <linux/cpufreq.h>
15 #include <linux/delay.h>
16 #include <linux/device.h>
17 #include <linux/err.h>
18 #include <linux/smp.h>
19 #include <linux/jiffies.h>
20 #include <linux/clockchips.h>
21 #include <linux/irq.h>
22 #include <linux/io.h>
23 
24 #include <asm/smp_twd.h>
25 #include <asm/localtimer.h>
26 #include <asm/hardware/gic.h>
27 
28 /* set up by the platform code */
29 void __iomem *twd_base;
30 
31 static struct clk *twd_clk;
32 static unsigned long twd_timer_rate;
33 
34 static struct clock_event_device __percpu **twd_evt;
35 
36 static void twd_set_mode(enum clock_event_mode mode,
37 			struct clock_event_device *clk)
38 {
39 	unsigned long ctrl;
40 
41 	switch (mode) {
42 	case CLOCK_EVT_MODE_PERIODIC:
43 		/* timer load already set up */
44 		ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
45 			| TWD_TIMER_CONTROL_PERIODIC;
46 		__raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD);
47 		break;
48 	case CLOCK_EVT_MODE_ONESHOT:
49 		/* period set, and timer enabled in 'next_event' hook */
50 		ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
51 		break;
52 	case CLOCK_EVT_MODE_UNUSED:
53 	case CLOCK_EVT_MODE_SHUTDOWN:
54 	default:
55 		ctrl = 0;
56 	}
57 
58 	__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
59 }
60 
61 static int twd_set_next_event(unsigned long evt,
62 			struct clock_event_device *unused)
63 {
64 	unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
65 
66 	ctrl |= TWD_TIMER_CONTROL_ENABLE;
67 
68 	__raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
69 	__raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
70 
71 	return 0;
72 }
73 
74 /*
75  * local_timer_ack: checks for a local timer interrupt.
76  *
77  * If a local timer interrupt has occurred, acknowledge and return 1.
78  * Otherwise, return 0.
79  */
80 int twd_timer_ack(void)
81 {
82 	if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
83 		__raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
84 		return 1;
85 	}
86 
87 	return 0;
88 }
89 
90 void twd_timer_stop(struct clock_event_device *clk)
91 {
92 	twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
93 	disable_percpu_irq(clk->irq);
94 }
95 
96 #ifdef CONFIG_CPU_FREQ
97 
98 /*
99  * Updates clockevent frequency when the cpu frequency changes.
100  * Called on the cpu that is changing frequency with interrupts disabled.
101  */
102 static void twd_update_frequency(void *data)
103 {
104 	twd_timer_rate = clk_get_rate(twd_clk);
105 
106 	clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
107 }
108 
109 static int twd_cpufreq_transition(struct notifier_block *nb,
110 	unsigned long state, void *data)
111 {
112 	struct cpufreq_freqs *freqs = data;
113 
114 	/*
115 	 * The twd clock events must be reprogrammed to account for the new
116 	 * frequency.  The timer is local to a cpu, so cross-call to the
117 	 * changing cpu.
118 	 */
119 	if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE)
120 		smp_call_function_single(freqs->cpu, twd_update_frequency,
121 			NULL, 1);
122 
123 	return NOTIFY_OK;
124 }
125 
126 static struct notifier_block twd_cpufreq_nb = {
127 	.notifier_call = twd_cpufreq_transition,
128 };
129 
130 static int twd_cpufreq_init(void)
131 {
132 	if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
133 		return cpufreq_register_notifier(&twd_cpufreq_nb,
134 			CPUFREQ_TRANSITION_NOTIFIER);
135 
136 	return 0;
137 }
138 core_initcall(twd_cpufreq_init);
139 
140 #endif
141 
142 static void __cpuinit twd_calibrate_rate(void)
143 {
144 	unsigned long count;
145 	u64 waitjiffies;
146 
147 	/*
148 	 * If this is the first time round, we need to work out how fast
149 	 * the timer ticks
150 	 */
151 	if (twd_timer_rate == 0) {
152 		printk(KERN_INFO "Calibrating local timer... ");
153 
154 		/* Wait for a tick to start */
155 		waitjiffies = get_jiffies_64() + 1;
156 
157 		while (get_jiffies_64() < waitjiffies)
158 			udelay(10);
159 
160 		/* OK, now the tick has started, let's get the timer going */
161 		waitjiffies += 5;
162 
163 				 /* enable, no interrupt or reload */
164 		__raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
165 
166 				 /* maximum value */
167 		__raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
168 
169 		while (get_jiffies_64() < waitjiffies)
170 			udelay(10);
171 
172 		count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
173 
174 		twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
175 
176 		printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
177 			(twd_timer_rate / 10000) % 100);
178 	}
179 }
180 
181 static irqreturn_t twd_handler(int irq, void *dev_id)
182 {
183 	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
184 
185 	if (twd_timer_ack()) {
186 		evt->event_handler(evt);
187 		return IRQ_HANDLED;
188 	}
189 
190 	return IRQ_NONE;
191 }
192 
193 static struct clk *twd_get_clock(void)
194 {
195 	struct clk *clk;
196 	int err;
197 
198 	clk = clk_get_sys("smp_twd", NULL);
199 	if (IS_ERR(clk)) {
200 		pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk));
201 		return clk;
202 	}
203 
204 	err = clk_prepare(clk);
205 	if (err) {
206 		pr_err("smp_twd: clock failed to prepare: %d\n", err);
207 		clk_put(clk);
208 		return ERR_PTR(err);
209 	}
210 
211 	err = clk_enable(clk);
212 	if (err) {
213 		pr_err("smp_twd: clock failed to enable: %d\n", err);
214 		clk_unprepare(clk);
215 		clk_put(clk);
216 		return ERR_PTR(err);
217 	}
218 
219 	return clk;
220 }
221 
222 /*
223  * Setup the local clock events for a CPU.
224  */
225 void __cpuinit twd_timer_setup(struct clock_event_device *clk)
226 {
227 	struct clock_event_device **this_cpu_clk;
228 
229 	if (!twd_evt) {
230 		int err;
231 
232 		twd_evt = alloc_percpu(struct clock_event_device *);
233 		if (!twd_evt) {
234 			pr_err("twd: can't allocate memory\n");
235 			return;
236 		}
237 
238 		err = request_percpu_irq(clk->irq, twd_handler,
239 					 "twd", twd_evt);
240 		if (err) {
241 			pr_err("twd: can't register interrupt %d (%d)\n",
242 			       clk->irq, err);
243 			return;
244 		}
245 	}
246 
247 	if (!twd_clk)
248 		twd_clk = twd_get_clock();
249 
250 	if (!IS_ERR_OR_NULL(twd_clk))
251 		twd_timer_rate = clk_get_rate(twd_clk);
252 	else
253 		twd_calibrate_rate();
254 
255 	__raw_writel(0, twd_base + TWD_TIMER_CONTROL);
256 
257 	clk->name = "local_timer";
258 	clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
259 			CLOCK_EVT_FEAT_C3STOP;
260 	clk->rating = 350;
261 	clk->set_mode = twd_set_mode;
262 	clk->set_next_event = twd_set_next_event;
263 
264 	this_cpu_clk = __this_cpu_ptr(twd_evt);
265 	*this_cpu_clk = clk;
266 
267 	clockevents_config_and_register(clk, twd_timer_rate,
268 					0xf, 0xffffffff);
269 	enable_percpu_irq(clk->irq, 0);
270 }
271