blob: 18c55f1e4e48c44f57e36b695908945bdb522ff4 [file] [log] [blame]
Russell Kingf32f4ce2009-05-16 12:14:21 +01001/*
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>
Linus Walleij5def51b2011-12-13 12:47:31 +010013#include <linux/clk.h>
Linus Walleij4fd7f9b2011-12-13 12:48:18 +010014#include <linux/cpufreq.h>
Russell Kingf32f4ce2009-05-16 12:14:21 +010015#include <linux/delay.h>
16#include <linux/device.h>
Linus Walleij5def51b2011-12-13 12:47:31 +010017#include <linux/err.h>
Russell Kingf32f4ce2009-05-16 12:14:21 +010018#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>
Marc Zyngier28af6902011-07-22 12:52:37 +010025#include <asm/localtimer.h>
Russell Kingf32f4ce2009-05-16 12:14:21 +010026#include <asm/hardware/gic.h>
27
Russell Kingf32f4ce2009-05-16 12:14:21 +010028/* set up by the platform code */
29void __iomem *twd_base;
30
Linus Walleij5def51b2011-12-13 12:47:31 +010031static struct clk *twd_clk;
Russell Kingf32f4ce2009-05-16 12:14:21 +010032static unsigned long twd_timer_rate;
33
Marc Zyngier28af6902011-07-22 12:52:37 +010034static struct clock_event_device __percpu **twd_evt;
Marc Zyngier81e46f72012-01-10 19:39:26 +000035static int twd_ppi;
Marc Zyngier28af6902011-07-22 12:52:37 +010036
Russell Kingf32f4ce2009-05-16 12:14:21 +010037static void twd_set_mode(enum clock_event_mode mode,
38 struct clock_event_device *clk)
39{
40 unsigned long ctrl;
41
Russell King4c5158d2009-05-17 10:58:54 +010042 switch (mode) {
Russell Kingf32f4ce2009-05-16 12:14:21 +010043 case CLOCK_EVT_MODE_PERIODIC:
44 /* timer load already set up */
45 ctrl = TWD_TIMER_CONTROL_ENABLE | TWD_TIMER_CONTROL_IT_ENABLE
46 | TWD_TIMER_CONTROL_PERIODIC;
Russell King03399c12011-01-25 10:35:36 +000047 __raw_writel(twd_timer_rate / HZ, twd_base + TWD_TIMER_LOAD);
Russell Kingf32f4ce2009-05-16 12:14:21 +010048 break;
49 case CLOCK_EVT_MODE_ONESHOT:
50 /* period set, and timer enabled in 'next_event' hook */
51 ctrl = TWD_TIMER_CONTROL_IT_ENABLE | TWD_TIMER_CONTROL_ONESHOT;
52 break;
53 case CLOCK_EVT_MODE_UNUSED:
54 case CLOCK_EVT_MODE_SHUTDOWN:
55 default:
56 ctrl = 0;
57 }
58
59 __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
60}
61
62static int twd_set_next_event(unsigned long evt,
63 struct clock_event_device *unused)
64{
65 unsigned long ctrl = __raw_readl(twd_base + TWD_TIMER_CONTROL);
66
Russell King4c5158d2009-05-17 10:58:54 +010067 ctrl |= TWD_TIMER_CONTROL_ENABLE;
68
Russell Kingf32f4ce2009-05-16 12:14:21 +010069 __raw_writel(evt, twd_base + TWD_TIMER_COUNTER);
Russell King4c5158d2009-05-17 10:58:54 +010070 __raw_writel(ctrl, twd_base + TWD_TIMER_CONTROL);
Russell Kingf32f4ce2009-05-16 12:14:21 +010071
72 return 0;
73}
74
75/*
76 * local_timer_ack: checks for a local timer interrupt.
77 *
78 * If a local timer interrupt has occurred, acknowledge and return 1.
79 * Otherwise, return 0.
80 */
81int twd_timer_ack(void)
82{
83 if (__raw_readl(twd_base + TWD_TIMER_INTSTAT)) {
84 __raw_writel(1, twd_base + TWD_TIMER_INTSTAT);
85 return 1;
86 }
87
88 return 0;
89}
90
Marc Zyngierabde7102012-01-10 19:07:28 +000091static void twd_timer_stop(struct clock_event_device *clk)
Marc Zyngier28af6902011-07-22 12:52:37 +010092{
93 twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
94 disable_percpu_irq(clk->irq);
95}
96
Marc Zyngierabde7102012-01-10 19:07:28 +000097/* Temporary hack to be removed when all TWD users are converted to
98 the new registration interface */
99void local_timer_stop(struct clock_event_device *clk)
100 __attribute__ ((alias ("twd_timer_stop")));
101
Linus Walleij4fd7f9b2011-12-13 12:48:18 +0100102#ifdef CONFIG_CPU_FREQ
103
104/*
105 * Updates clockevent frequency when the cpu frequency changes.
106 * Called on the cpu that is changing frequency with interrupts disabled.
107 */
108static void twd_update_frequency(void *data)
109{
110 twd_timer_rate = clk_get_rate(twd_clk);
111
112 clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
113}
114
115static int twd_cpufreq_transition(struct notifier_block *nb,
116 unsigned long state, void *data)
117{
118 struct cpufreq_freqs *freqs = data;
119
120 /*
121 * The twd clock events must be reprogrammed to account for the new
122 * frequency. The timer is local to a cpu, so cross-call to the
123 * changing cpu.
124 */
125 if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE)
126 smp_call_function_single(freqs->cpu, twd_update_frequency,
127 NULL, 1);
128
129 return NOTIFY_OK;
130}
131
132static struct notifier_block twd_cpufreq_nb = {
133 .notifier_call = twd_cpufreq_transition,
134};
135
136static int twd_cpufreq_init(void)
137{
138 if (!IS_ERR(twd_clk))
139 return cpufreq_register_notifier(&twd_cpufreq_nb,
140 CPUFREQ_TRANSITION_NOTIFIER);
141
142 return 0;
143}
144core_initcall(twd_cpufreq_init);
145
146#endif
147
Russell Kingf32f4ce2009-05-16 12:14:21 +0100148static void __cpuinit twd_calibrate_rate(void)
149{
Russell King03399c12011-01-25 10:35:36 +0000150 unsigned long count;
Russell Kingf32f4ce2009-05-16 12:14:21 +0100151 u64 waitjiffies;
152
153 /*
154 * If this is the first time round, we need to work out how fast
155 * the timer ticks
156 */
157 if (twd_timer_rate == 0) {
Russell King4c5158d2009-05-17 10:58:54 +0100158 printk(KERN_INFO "Calibrating local timer... ");
Russell Kingf32f4ce2009-05-16 12:14:21 +0100159
160 /* Wait for a tick to start */
161 waitjiffies = get_jiffies_64() + 1;
162
163 while (get_jiffies_64() < waitjiffies)
164 udelay(10);
165
166 /* OK, now the tick has started, let's get the timer going */
167 waitjiffies += 5;
168
169 /* enable, no interrupt or reload */
170 __raw_writel(0x1, twd_base + TWD_TIMER_CONTROL);
171
172 /* maximum value */
173 __raw_writel(0xFFFFFFFFU, twd_base + TWD_TIMER_COUNTER);
174
175 while (get_jiffies_64() < waitjiffies)
176 udelay(10);
177
178 count = __raw_readl(twd_base + TWD_TIMER_COUNTER);
179
180 twd_timer_rate = (0xFFFFFFFFU - count) * (HZ / 5);
181
182 printk("%lu.%02luMHz.\n", twd_timer_rate / 1000000,
Vitaly Kuzmichev90c5ffe2011-07-07 14:56:05 +0100183 (twd_timer_rate / 10000) % 100);
Russell Kingf32f4ce2009-05-16 12:14:21 +0100184 }
Russell Kingf32f4ce2009-05-16 12:14:21 +0100185}
186
Marc Zyngier28af6902011-07-22 12:52:37 +0100187static irqreturn_t twd_handler(int irq, void *dev_id)
188{
189 struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
190
191 if (twd_timer_ack()) {
192 evt->event_handler(evt);
193 return IRQ_HANDLED;
194 }
195
196 return IRQ_NONE;
197}
198
Linus Walleij5def51b2011-12-13 12:47:31 +0100199static struct clk *twd_get_clock(void)
200{
201 struct clk *clk;
202 int err;
203
204 clk = clk_get_sys("smp_twd", NULL);
205 if (IS_ERR(clk)) {
206 pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk));
207 return clk;
208 }
209
210 err = clk_prepare(clk);
211 if (err) {
212 pr_err("smp_twd: clock failed to prepare: %d\n", err);
213 clk_put(clk);
214 return ERR_PTR(err);
215 }
216
217 err = clk_enable(clk);
218 if (err) {
219 pr_err("smp_twd: clock failed to enable: %d\n", err);
220 clk_unprepare(clk);
221 clk_put(clk);
222 return ERR_PTR(err);
223 }
224
225 return clk;
226}
227
Russell Kingf32f4ce2009-05-16 12:14:21 +0100228/*
229 * Setup the local clock events for a CPU.
230 */
Marc Zyngier81e46f72012-01-10 19:39:26 +0000231int __cpuinit twd_timer_setup(struct clock_event_device *clk)
Russell Kingf32f4ce2009-05-16 12:14:21 +0100232{
Marc Zyngier28af6902011-07-22 12:52:37 +0100233 struct clock_event_device **this_cpu_clk;
234
235 if (!twd_evt) {
236 int err;
237
238 twd_evt = alloc_percpu(struct clock_event_device *);
239 if (!twd_evt) {
240 pr_err("twd: can't allocate memory\n");
Marc Zyngier81e46f72012-01-10 19:39:26 +0000241 return -ENOMEM;
Marc Zyngier28af6902011-07-22 12:52:37 +0100242 }
243
244 err = request_percpu_irq(clk->irq, twd_handler,
245 "twd", twd_evt);
246 if (err) {
247 pr_err("twd: can't register interrupt %d (%d)\n",
248 clk->irq, err);
Marc Zyngier81e46f72012-01-10 19:39:26 +0000249 return err;
Marc Zyngier28af6902011-07-22 12:52:37 +0100250 }
251 }
252
Linus Walleij5def51b2011-12-13 12:47:31 +0100253 if (!twd_clk)
254 twd_clk = twd_get_clock();
255
256 if (!IS_ERR_OR_NULL(twd_clk))
257 twd_timer_rate = clk_get_rate(twd_clk);
258 else
259 twd_calibrate_rate();
Russell Kingf32f4ce2009-05-16 12:14:21 +0100260
Marc Zyngierc2144552012-01-20 12:24:47 +0100261 __raw_writel(0, twd_base + TWD_TIMER_CONTROL);
262
Russell King4c5158d2009-05-17 10:58:54 +0100263 clk->name = "local_timer";
Russell King5388a6b2010-07-26 13:19:43 +0100264 clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
265 CLOCK_EVT_FEAT_C3STOP;
Russell King4c5158d2009-05-17 10:58:54 +0100266 clk->rating = 350;
267 clk->set_mode = twd_set_mode;
268 clk->set_next_event = twd_set_next_event;
Marc Zyngier81e46f72012-01-10 19:39:26 +0000269 if (!clk->irq)
270 clk->irq = twd_ppi;
Russell Kingf32f4ce2009-05-16 12:14:21 +0100271
Marc Zyngier28af6902011-07-22 12:52:37 +0100272 this_cpu_clk = __this_cpu_ptr(twd_evt);
273 *this_cpu_clk = clk;
274
Linus Walleij54d15b12011-12-13 12:46:43 +0100275 clockevents_config_and_register(clk, twd_timer_rate,
276 0xf, 0xffffffff);
Marc Zyngier28af6902011-07-22 12:52:37 +0100277 enable_percpu_irq(clk->irq, 0);
Marc Zyngier81e46f72012-01-10 19:39:26 +0000278
279 return 0;
280}
281
282static struct local_timer_ops twd_lt_ops __cpuinitdata = {
283 .setup = twd_timer_setup,
284 .stop = twd_timer_stop,
285};
286
287int __init twd_local_timer_register(struct twd_local_timer *tlt)
288{
289 int err;
290
291 if (twd_base || twd_evt)
292 return -EBUSY;
293
294 twd_ppi = tlt->res[1].start;
295
296 twd_evt = alloc_percpu(struct clock_event_device *);
297 twd_base = ioremap(tlt->res[0].start, resource_size(&tlt->res[0]));
298 if (!twd_base || !twd_evt) {
299 err = -ENOMEM;
300 goto out;
301 }
302
303 err = request_percpu_irq(twd_ppi, twd_handler, "twd", twd_evt);
304 if (err) {
305 pr_err("twd: can't register interrupt %d (%d)\n", twd_ppi, err);
306 goto out;
307 }
308
309 err = local_timer_register(&twd_lt_ops);
310 if (err)
311 goto out;
312
313 return 0;
314
315out:
316 iounmap(twd_base);
317 free_percpu(twd_evt);
318 twd_base = twd_evt = NULL;
319 return err;
Russell Kingf32f4ce2009-05-16 12:14:21 +0100320}