aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2012-05-04 07:04:11 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-05-04 07:04:11 -0400
commit5693188a6e888603afa9564974145e5e6e2c623c (patch)
treed092580bb003f34fb630d62916214a045ba078f1 /arch/arm
parente423c0c30c76fc850a443102a2965ec397ba6e21 (diff)
parentfb8a99f9f6bdc908cbbd2284cee80c709d9f7c03 (diff)
Merge branch 'timers-v3.4-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into devel-stable
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/Kconfig6
-rw-r--r--arch/arm/include/asm/arch_timer.h19
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/arch_timer.c350
-rw-r--r--arch/arm/kernel/smp.c3
-rw-r--r--arch/arm/mach-vexpress/v2m.c8
6 files changed, 386 insertions, 1 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index cf006d40342c..4faabbade2e9 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1543,6 +1543,12 @@ config HAVE_ARM_SCU
1543 help 1543 help
1544 This option enables support for the ARM system coherency unit 1544 This option enables support for the ARM system coherency unit
1545 1545
1546config ARM_ARCH_TIMER
1547 bool "Architected timer support"
1548 depends on CPU_V7
1549 help
1550 This option enables support for the ARM architected timer
1551
1546config HAVE_ARM_TWD 1552config HAVE_ARM_TWD
1547 bool 1553 bool
1548 depends on SMP 1554 depends on SMP
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
new file mode 100644
index 000000000000..ed2e95d46e29
--- /dev/null
+++ b/arch/arm/include/asm/arch_timer.h
@@ -0,0 +1,19 @@
1#ifndef __ASMARM_ARCH_TIMER_H
2#define __ASMARM_ARCH_TIMER_H
3
4#ifdef CONFIG_ARM_ARCH_TIMER
5int arch_timer_of_register(void);
6int arch_timer_sched_clock_init(void);
7#else
8static inline int arch_timer_of_register(void)
9{
10 return -ENXIO;
11}
12
13static inline int arch_timer_sched_clock_init(void)
14{
15 return -ENXIO;
16}
17#endif
18
19#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 7b787d642af4..22b0f1e255f0 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o
34obj-$(CONFIG_SMP) += smp.o smp_tlb.o 34obj-$(CONFIG_SMP) += smp.o smp_tlb.o
35obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o 35obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
36obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o 36obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
37obj-$(CONFIG_ARM_ARCH_TIMER) += arch_timer.o
37obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o 38obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o
38obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o 39obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o
39obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o 40obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
new file mode 100644
index 000000000000..dd58035621f7
--- /dev/null
+++ b/arch/arm/kernel/arch_timer.c
@@ -0,0 +1,350 @@
1/*
2 * linux/arch/arm/kernel/arch_timer.c
3 *
4 * Copyright (C) 2011 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/delay.h>
14#include <linux/device.h>
15#include <linux/smp.h>
16#include <linux/cpu.h>
17#include <linux/jiffies.h>
18#include <linux/clockchips.h>
19#include <linux/interrupt.h>
20#include <linux/of_irq.h>
21#include <linux/io.h>
22
23#include <asm/cputype.h>
24#include <asm/localtimer.h>
25#include <asm/arch_timer.h>
26#include <asm/system_info.h>
27#include <asm/sched_clock.h>
28
29static unsigned long arch_timer_rate;
30static int arch_timer_ppi;
31static int arch_timer_ppi2;
32
33static struct clock_event_device __percpu **arch_timer_evt;
34
35/*
36 * Architected system timer support.
37 */
38
39#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
40#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
41#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
42
43#define ARCH_TIMER_REG_CTRL 0
44#define ARCH_TIMER_REG_FREQ 1
45#define ARCH_TIMER_REG_TVAL 2
46
47static void arch_timer_reg_write(int reg, u32 val)
48{
49 switch (reg) {
50 case ARCH_TIMER_REG_CTRL:
51 asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
52 break;
53 case ARCH_TIMER_REG_TVAL:
54 asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
55 break;
56 }
57
58 isb();
59}
60
61static u32 arch_timer_reg_read(int reg)
62{
63 u32 val;
64
65 switch (reg) {
66 case ARCH_TIMER_REG_CTRL:
67 asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
68 break;
69 case ARCH_TIMER_REG_FREQ:
70 asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
71 break;
72 case ARCH_TIMER_REG_TVAL:
73 asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
74 break;
75 default:
76 BUG();
77 }
78
79 return val;
80}
81
82static irqreturn_t arch_timer_handler(int irq, void *dev_id)
83{
84 struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
85 unsigned long ctrl;
86
87 ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
88 if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {
89 ctrl |= ARCH_TIMER_CTRL_IT_MASK;
90 arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
91 evt->event_handler(evt);
92 return IRQ_HANDLED;
93 }
94
95 return IRQ_NONE;
96}
97
98static void arch_timer_disable(void)
99{
100 unsigned long ctrl;
101
102 ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
103 ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
104 arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
105}
106
107static void arch_timer_set_mode(enum clock_event_mode mode,
108 struct clock_event_device *clk)
109{
110 switch (mode) {
111 case CLOCK_EVT_MODE_UNUSED:
112 case CLOCK_EVT_MODE_SHUTDOWN:
113 arch_timer_disable();
114 break;
115 default:
116 break;
117 }
118}
119
120static int arch_timer_set_next_event(unsigned long evt,
121 struct clock_event_device *unused)
122{
123 unsigned long ctrl;
124
125 ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
126 ctrl |= ARCH_TIMER_CTRL_ENABLE;
127 ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
128
129 arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
130 arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
131
132 return 0;
133}
134
135static int __cpuinit arch_timer_setup(struct clock_event_device *clk)
136{
137 /* Be safe... */
138 arch_timer_disable();
139
140 clk->features = CLOCK_EVT_FEAT_ONESHOT;
141 clk->name = "arch_sys_timer";
142 clk->rating = 450;
143 clk->set_mode = arch_timer_set_mode;
144 clk->set_next_event = arch_timer_set_next_event;
145 clk->irq = arch_timer_ppi;
146
147 clockevents_config_and_register(clk, arch_timer_rate,
148 0xf, 0x7fffffff);
149
150 *__this_cpu_ptr(arch_timer_evt) = clk;
151
152 enable_percpu_irq(clk->irq, 0);
153 if (arch_timer_ppi2)
154 enable_percpu_irq(arch_timer_ppi2, 0);
155
156 return 0;
157}
158
159/* Is the optional system timer available? */
160static int local_timer_is_architected(void)
161{
162 return (cpu_architecture() >= CPU_ARCH_ARMv7) &&
163 ((read_cpuid_ext(CPUID_EXT_PFR1) >> 16) & 0xf) == 1;
164}
165
166static int arch_timer_available(void)
167{
168 unsigned long freq;
169
170 if (!local_timer_is_architected())
171 return -ENXIO;
172
173 if (arch_timer_rate == 0) {
174 arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
175 freq = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
176
177 /* Check the timer frequency. */
178 if (freq == 0) {
179 pr_warn("Architected timer frequency not available\n");
180 return -EINVAL;
181 }
182
183 arch_timer_rate = freq;
184 }
185
186 pr_info_once("Architected local timer running at %lu.%02luMHz.\n",
187 arch_timer_rate / 1000000, (arch_timer_rate / 10000) % 100);
188 return 0;
189}
190
191static inline cycle_t arch_counter_get_cntpct(void)
192{
193 u32 cvall, cvalh;
194
195 asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
196
197 return ((cycle_t) cvalh << 32) | cvall;
198}
199
200static inline cycle_t arch_counter_get_cntvct(void)
201{
202 u32 cvall, cvalh;
203
204 asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
205
206 return ((cycle_t) cvalh << 32) | cvall;
207}
208
209static u32 notrace arch_counter_get_cntvct32(void)
210{
211 cycle_t cntvct = arch_counter_get_cntvct();
212
213 /*
214 * The sched_clock infrastructure only knows about counters
215 * with at most 32bits. Forget about the upper 24 bits for the
216 * time being...
217 */
218 return (u32)(cntvct & (u32)~0);
219}
220
221static cycle_t arch_counter_read(struct clocksource *cs)
222{
223 return arch_counter_get_cntpct();
224}
225
226static struct clocksource clocksource_counter = {
227 .name = "arch_sys_counter",
228 .rating = 400,
229 .read = arch_counter_read,
230 .mask = CLOCKSOURCE_MASK(56),
231 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
232};
233
234static void __cpuinit arch_timer_stop(struct clock_event_device *clk)
235{
236 pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
237 clk->irq, smp_processor_id());
238 disable_percpu_irq(clk->irq);
239 if (arch_timer_ppi2)
240 disable_percpu_irq(arch_timer_ppi2);
241 arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
242}
243
244static struct local_timer_ops arch_timer_ops __cpuinitdata = {
245 .setup = arch_timer_setup,
246 .stop = arch_timer_stop,
247};
248
249static struct clock_event_device arch_timer_global_evt;
250
251static int __init arch_timer_register(void)
252{
253 int err;
254
255 err = arch_timer_available();
256 if (err)
257 return err;
258
259 arch_timer_evt = alloc_percpu(struct clock_event_device *);
260 if (!arch_timer_evt)
261 return -ENOMEM;
262
263 clocksource_register_hz(&clocksource_counter, arch_timer_rate);
264
265 err = request_percpu_irq(arch_timer_ppi, arch_timer_handler,
266 "arch_timer", arch_timer_evt);
267 if (err) {
268 pr_err("arch_timer: can't register interrupt %d (%d)\n",
269 arch_timer_ppi, err);
270 goto out_free;
271 }
272
273 if (arch_timer_ppi2) {
274 err = request_percpu_irq(arch_timer_ppi2, arch_timer_handler,
275 "arch_timer", arch_timer_evt);
276 if (err) {
277 pr_err("arch_timer: can't register interrupt %d (%d)\n",
278 arch_timer_ppi2, err);
279 arch_timer_ppi2 = 0;
280 goto out_free_irq;
281 }
282 }
283
284 err = local_timer_register(&arch_timer_ops);
285 if (err) {
286 /*
287 * We couldn't register as a local timer (could be
288 * because we're on a UP platform, or because some
289 * other local timer is already present...). Try as a
290 * global timer instead.
291 */
292 arch_timer_global_evt.cpumask = cpumask_of(0);
293 err = arch_timer_setup(&arch_timer_global_evt);
294 }
295
296 if (err)
297 goto out_free_irq;
298
299 return 0;
300
301out_free_irq:
302 free_percpu_irq(arch_timer_ppi, arch_timer_evt);
303 if (arch_timer_ppi2)
304 free_percpu_irq(arch_timer_ppi2, arch_timer_evt);
305
306out_free:
307 free_percpu(arch_timer_evt);
308
309 return err;
310}
311
312static const struct of_device_id arch_timer_of_match[] __initconst = {
313 { .compatible = "arm,armv7-timer", },
314 {},
315};
316
317int __init arch_timer_of_register(void)
318{
319 struct device_node *np;
320 u32 freq;
321
322 np = of_find_matching_node(NULL, arch_timer_of_match);
323 if (!np) {
324 pr_err("arch_timer: can't find DT node\n");
325 return -ENODEV;
326 }
327
328 /* Try to determine the frequency from the device tree or CNTFRQ */
329 if (!of_property_read_u32(np, "clock-frequency", &freq))
330 arch_timer_rate = freq;
331
332 arch_timer_ppi = irq_of_parse_and_map(np, 0);
333 arch_timer_ppi2 = irq_of_parse_and_map(np, 1);
334 pr_info("arch_timer: found %s irqs %d %d\n",
335 np->name, arch_timer_ppi, arch_timer_ppi2);
336
337 return arch_timer_register();
338}
339
340int __init arch_timer_sched_clock_init(void)
341{
342 int err;
343
344 err = arch_timer_available();
345 if (err)
346 return err;
347
348 setup_sched_clock(arch_counter_get_cntvct32, 32, arch_timer_rate);
349 return 0;
350}
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index addbbe8028c2..11c4148b8abb 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -454,6 +454,9 @@ static struct local_timer_ops *lt_ops;
454#ifdef CONFIG_LOCAL_TIMERS 454#ifdef CONFIG_LOCAL_TIMERS
455int local_timer_register(struct local_timer_ops *ops) 455int local_timer_register(struct local_timer_ops *ops)
456{ 456{
457 if (!is_smp() || !setup_max_cpus)
458 return -ENXIO;
459
457 if (lt_ops) 460 if (lt_ops)
458 return -EBUSY; 461 return -EBUSY;
459 462
diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c
index 47cdcca5a7e7..04dd092211b8 100644
--- a/arch/arm/mach-vexpress/v2m.c
+++ b/arch/arm/mach-vexpress/v2m.c
@@ -19,8 +19,10 @@
19#include <linux/clkdev.h> 19#include <linux/clkdev.h>
20#include <linux/mtd/physmap.h> 20#include <linux/mtd/physmap.h>
21 21
22#include <asm/arch_timer.h>
22#include <asm/mach-types.h> 23#include <asm/mach-types.h>
23#include <asm/sizes.h> 24#include <asm/sizes.h>
25#include <asm/smp_twd.h>
24#include <asm/mach/arch.h> 26#include <asm/mach/arch.h>
25#include <asm/mach/map.h> 27#include <asm/mach/map.h>
26#include <asm/mach/time.h> 28#include <asm/mach/time.h>
@@ -616,7 +618,6 @@ void __init v2m_dt_init_early(void)
616 } 618 }
617 619
618 clkdev_add_table(v2m_dt_lookups, ARRAY_SIZE(v2m_dt_lookups)); 620 clkdev_add_table(v2m_dt_lookups, ARRAY_SIZE(v2m_dt_lookups));
619 versatile_sched_clock_init(v2m_sysreg_base + V2M_SYS_24MHZ, 24000000);
620} 621}
621 622
622static struct of_device_id vexpress_irq_match[] __initdata = { 623static struct of_device_id vexpress_irq_match[] __initdata = {
@@ -643,6 +644,11 @@ static void __init v2m_dt_timer_init(void)
643 return; 644 return;
644 node = of_find_node_by_path(path); 645 node = of_find_node_by_path(path);
645 v2m_sp804_init(of_iomap(node, 0), irq_of_parse_and_map(node, 0)); 646 v2m_sp804_init(of_iomap(node, 0), irq_of_parse_and_map(node, 0));
647 if (arch_timer_of_register() != 0)
648 twd_local_timer_of_register();
649
650 if (arch_timer_sched_clock_init() != 0)
651 versatile_sched_clock_init(v2m_sysreg_base + V2M_SYS_24MHZ, 24000000);
646} 652}
647 653
648static struct sys_timer v2m_dt_timer = { 654static struct sys_timer v2m_dt_timer = {