aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorStuart Menefy <stuart.menefy@st.com>2013-06-26 07:48:38 -0400
committerDaniel Lezcano <daniel.lezcano@linaro.org>2013-07-03 11:30:57 -0400
commitc1b40e447af8695666d4c11cfec4a7407e6a124d (patch)
tree813182806167d481396812928fec503b84e39cab /drivers/clocksource
parent0c1dcfd53b1066c411d3885cf8156abf59694360 (diff)
clocksource: arm_global_timer: Add ARM global timer support
This is a simple driver for the global timer module found in the Cortex A9-MP cores from revision r1p0 onwards. This should be able to perform the functions of the system timer and the local timer in an SMP system. The global timer has the following features: The global timer is a 64-bit incrementing counter with an auto-incrementing feature. It continues incrementing after sending interrupts. The global timer is memory mapped in the private memory region. The global timer is accessible to all Cortex-A9 processors in the cluster. Each Cortex-A9 processor has a private 64-bit comparator that is used to assert a private interrupt when the global timer has reached the comparator value. All the Cortex-A9 processors in a design use the banked ID, ID27, for this interrupt. ID27 is sent to the Interrupt Controller as a Private Peripheral Interrupt. The global timer is clocked by PERIPHCLK. Signed-off-by: Stuart Menefy <stuart.menefy@st.com> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@st.com> CC: Arnd Bergmann <arnd@arndb.de> CC: Rob Herring <robherring2@gmail.com> CC: Linus Walleij <linus.walleij@linaro.org> CC: Will Deacon <will.deacon@arm.com> CC: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig13
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/arm_global_timer.c321
3 files changed, 335 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index c082e3e4bd0a..42c8eccb1e86 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -72,6 +72,19 @@ config ARM_ARCH_TIMER
72 bool 72 bool
73 select CLKSRC_OF if OF 73 select CLKSRC_OF if OF
74 74
75config ARM_GLOBAL_TIMER
76 bool
77 select CLKSRC_OF if OF
78 help
79 This options enables support for the ARM global timer unit
80
81config CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
82 bool
83 depends on ARM_GLOBAL_TIMER
84 default y
85 help
86 Use ARM global timer clock source as sched_clock
87
75config CLKSRC_METAG_GENERIC 88config CLKSRC_METAG_GENERIC
76 def_bool y if METAG 89 def_bool y if METAG
77 help 90 help
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 49bea7fad85c..8b00c5cebfa4 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -31,5 +31,6 @@ obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
31obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o 31obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
32 32
33obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o 33obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
34obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
34obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o 35obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
35obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o 36obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
new file mode 100644
index 000000000000..db8afc7427a6
--- /dev/null
+++ b/drivers/clocksource/arm_global_timer.c
@@ -0,0 +1,321 @@
1/*
2 * drivers/clocksource/arm_global_timer.c
3 *
4 * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
5 * Author: Stuart Menefy <stuart.menefy@st.com>
6 * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/init.h>
14#include <linux/interrupt.h>
15#include <linux/clocksource.h>
16#include <linux/clockchips.h>
17#include <linux/cpu.h>
18#include <linux/clk.h>
19#include <linux/err.h>
20#include <linux/io.h>
21#include <linux/of.h>
22#include <linux/of_irq.h>
23#include <linux/of_address.h>
24#include <linux/sched_clock.h>
25
26#include <asm/cputype.h>
27
28#define GT_COUNTER0 0x00
29#define GT_COUNTER1 0x04
30
31#define GT_CONTROL 0x08
32#define GT_CONTROL_TIMER_ENABLE BIT(0) /* this bit is NOT banked */
33#define GT_CONTROL_COMP_ENABLE BIT(1) /* banked */
34#define GT_CONTROL_IRQ_ENABLE BIT(2) /* banked */
35#define GT_CONTROL_AUTO_INC BIT(3) /* banked */
36
37#define GT_INT_STATUS 0x0c
38#define GT_INT_STATUS_EVENT_FLAG BIT(0)
39
40#define GT_COMP0 0x10
41#define GT_COMP1 0x14
42#define GT_AUTO_INC 0x18
43
44/*
45 * We are expecting to be clocked by the ARM peripheral clock.
46 *
47 * Note: it is assumed we are using a prescaler value of zero, so this is
48 * the units for all operations.
49 */
50static void __iomem *gt_base;
51static unsigned long gt_clk_rate;
52static int gt_ppi;
53static struct clock_event_device __percpu *gt_evt;
54
55/*
56 * To get the value from the Global Timer Counter register proceed as follows:
57 * 1. Read the upper 32-bit timer counter register
58 * 2. Read the lower 32-bit timer counter register
59 * 3. Read the upper 32-bit timer counter register again. If the value is
60 * different to the 32-bit upper value read previously, go back to step 2.
61 * Otherwise the 64-bit timer counter value is correct.
62 */
63static u64 gt_counter_read(void)
64{
65 u64 counter;
66 u32 lower;
67 u32 upper, old_upper;
68
69 upper = readl_relaxed(gt_base + GT_COUNTER1);
70 do {
71 old_upper = upper;
72 lower = readl_relaxed(gt_base + GT_COUNTER0);
73 upper = readl_relaxed(gt_base + GT_COUNTER1);
74 } while (upper != old_upper);
75
76 counter = upper;
77 counter <<= 32;
78 counter |= lower;
79 return counter;
80}
81
82/**
83 * To ensure that updates to comparator value register do not set the
84 * Interrupt Status Register proceed as follows:
85 * 1. Clear the Comp Enable bit in the Timer Control Register.
86 * 2. Write the lower 32-bit Comparator Value Register.
87 * 3. Write the upper 32-bit Comparator Value Register.
88 * 4. Set the Comp Enable bit and, if necessary, the IRQ enable bit.
89 */
90static void gt_compare_set(unsigned long delta, int periodic)
91{
92 u64 counter = gt_counter_read();
93 unsigned long ctrl;
94
95 counter += delta;
96 ctrl = GT_CONTROL_TIMER_ENABLE;
97 writel(ctrl, gt_base + GT_CONTROL);
98 writel(lower_32_bits(counter), gt_base + GT_COMP0);
99 writel(upper_32_bits(counter), gt_base + GT_COMP1);
100
101 if (periodic) {
102 writel(delta, gt_base + GT_AUTO_INC);
103 ctrl |= GT_CONTROL_AUTO_INC;
104 }
105
106 ctrl |= GT_CONTROL_COMP_ENABLE | GT_CONTROL_IRQ_ENABLE;
107 writel(ctrl, gt_base + GT_CONTROL);
108}
109
110static void gt_clockevent_set_mode(enum clock_event_mode mode,
111 struct clock_event_device *clk)
112{
113 unsigned long ctrl;
114
115 switch (mode) {
116 case CLOCK_EVT_MODE_PERIODIC:
117 gt_compare_set(DIV_ROUND_CLOSEST(gt_clk_rate, HZ), 1);
118 break;
119 case CLOCK_EVT_MODE_ONESHOT:
120 case CLOCK_EVT_MODE_UNUSED:
121 case CLOCK_EVT_MODE_SHUTDOWN:
122 ctrl = readl(gt_base + GT_CONTROL);
123 ctrl &= ~(GT_CONTROL_COMP_ENABLE |
124 GT_CONTROL_IRQ_ENABLE | GT_CONTROL_AUTO_INC);
125 writel(ctrl, gt_base + GT_CONTROL);
126 break;
127 default:
128 break;
129 }
130}
131
132static int gt_clockevent_set_next_event(unsigned long evt,
133 struct clock_event_device *unused)
134{
135 gt_compare_set(evt, 0);
136 return 0;
137}
138
139static irqreturn_t gt_clockevent_interrupt(int irq, void *dev_id)
140{
141 struct clock_event_device *evt = dev_id;
142
143 if (!(readl_relaxed(gt_base + GT_INT_STATUS) &
144 GT_INT_STATUS_EVENT_FLAG))
145 return IRQ_NONE;
146
147 /**
148 * ERRATA 740657( Global Timer can send 2 interrupts for
149 * the same event in single-shot mode)
150 * Workaround:
151 * Either disable single-shot mode.
152 * Or
153 * Modify the Interrupt Handler to avoid the
154 * offending sequence. This is achieved by clearing
155 * the Global Timer flag _after_ having incremented
156 * the Comparator register value to a higher value.
157 */
158 if (evt->mode == CLOCK_EVT_MODE_ONESHOT)
159 gt_compare_set(ULONG_MAX, 0);
160
161 writel_relaxed(GT_INT_STATUS_EVENT_FLAG, gt_base + GT_INT_STATUS);
162 evt->event_handler(evt);
163
164 return IRQ_HANDLED;
165}
166
167static int __cpuinit gt_clockevents_init(struct clock_event_device *clk)
168{
169 int cpu = smp_processor_id();
170
171 clk->name = "arm_global_timer";
172 clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
173 clk->set_mode = gt_clockevent_set_mode;
174 clk->set_next_event = gt_clockevent_set_next_event;
175 clk->cpumask = cpumask_of(cpu);
176 clk->rating = 300;
177 clk->irq = gt_ppi;
178 clockevents_config_and_register(clk, gt_clk_rate,
179 1, 0xffffffff);
180 enable_percpu_irq(clk->irq, IRQ_TYPE_NONE);
181 return 0;
182}
183
184static void gt_clockevents_stop(struct clock_event_device *clk)
185{
186 gt_clockevent_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
187 disable_percpu_irq(clk->irq);
188}
189
190static cycle_t gt_clocksource_read(struct clocksource *cs)
191{
192 return gt_counter_read();
193}
194
195static struct clocksource gt_clocksource = {
196 .name = "arm_global_timer",
197 .rating = 300,
198 .read = gt_clocksource_read,
199 .mask = CLOCKSOURCE_MASK(64),
200 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
201};
202
203#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
204static u32 notrace gt_sched_clock_read(void)
205{
206 return gt_counter_read();
207}
208#endif
209
210static void __init gt_clocksource_init(void)
211{
212 writel(0, gt_base + GT_CONTROL);
213 writel(0, gt_base + GT_COUNTER0);
214 writel(0, gt_base + GT_COUNTER1);
215 /* enables timer on all the cores */
216 writel(GT_CONTROL_TIMER_ENABLE, gt_base + GT_CONTROL);
217
218#ifdef CONFIG_CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
219 setup_sched_clock(gt_sched_clock_read, 32, gt_clk_rate);
220#endif
221 clocksource_register_hz(&gt_clocksource, gt_clk_rate);
222}
223
224static int __cpuinit gt_cpu_notify(struct notifier_block *self,
225 unsigned long action, void *hcpu)
226{
227 switch (action & ~CPU_TASKS_FROZEN) {
228 case CPU_STARTING:
229 gt_clockevents_init(this_cpu_ptr(gt_evt));
230 break;
231 case CPU_DYING:
232 gt_clockevents_stop(this_cpu_ptr(gt_evt));
233 break;
234 }
235
236 return NOTIFY_OK;
237}
238static struct notifier_block gt_cpu_nb __cpuinitdata = {
239 .notifier_call = gt_cpu_notify,
240};
241
242static void __init global_timer_of_register(struct device_node *np)
243{
244 struct clk *gt_clk;
245 int err = 0;
246
247 /*
248 * In r2p0 the comparators for each processor with the global timer
249 * fire when the timer value is greater than or equal to. In previous
250 * revisions the comparators fired when the timer value was equal to.
251 */
252 if ((read_cpuid_id() & 0xf0000f) < 0x200000) {
253 pr_warn("global-timer: non support for this cpu version.\n");
254 return;
255 }
256
257 gt_ppi = irq_of_parse_and_map(np, 0);
258 if (!gt_ppi) {
259 pr_warn("global-timer: unable to parse irq\n");
260 return;
261 }
262
263 gt_base = of_iomap(np, 0);
264 if (!gt_base) {
265 pr_warn("global-timer: invalid base address\n");
266 return;
267 }
268
269 gt_clk = of_clk_get(np, 0);
270 if (!IS_ERR(gt_clk)) {
271 err = clk_prepare_enable(gt_clk);
272 if (err)
273 goto out_unmap;
274 } else {
275 pr_warn("global-timer: clk not found\n");
276 err = -EINVAL;
277 goto out_unmap;
278 }
279
280 gt_clk_rate = clk_get_rate(gt_clk);
281 gt_evt = alloc_percpu(struct clock_event_device);
282 if (!gt_evt) {
283 pr_warn("global-timer: can't allocate memory\n");
284 err = -ENOMEM;
285 goto out_clk;
286 }
287
288 err = request_percpu_irq(gt_ppi, gt_clockevent_interrupt,
289 "gt", gt_evt);
290 if (err) {
291 pr_warn("global-timer: can't register interrupt %d (%d)\n",
292 gt_ppi, err);
293 goto out_free;
294 }
295
296 err = register_cpu_notifier(&gt_cpu_nb);
297 if (err) {
298 pr_warn("global-timer: unable to register cpu notifier.\n");
299 goto out_irq;
300 }
301
302 /* Immediately configure the timer on the boot CPU */
303 gt_clocksource_init();
304 gt_clockevents_init(this_cpu_ptr(gt_evt));
305
306 return;
307
308out_irq:
309 free_percpu_irq(gt_ppi, gt_evt);
310out_free:
311 free_percpu(gt_evt);
312out_clk:
313 clk_disable_unprepare(gt_clk);
314out_unmap:
315 iounmap(gt_base);
316 WARN(err, "ARM Global timer register failed (%d)\n", err);
317}
318
319/* Only tested on r2p2 and r3p0 */
320CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
321 global_timer_of_register);