diff options
author | Arnd Bergmann <arnd@arndb.de> | 2013-03-28 07:25:51 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2013-03-28 07:25:51 -0400 |
commit | 063ab6daeb2ba5ef8f47c3fc04a749936c62d5bb (patch) | |
tree | 43532e85397b7105999851a2875db9bbf043080e /drivers/clocksource/timer-marco.c | |
parent | cde35bd027023b052316c14ae3fc01e2f487a6ab (diff) | |
parent | dbaf6a8d5de7b63f85eea10a47681f920cbf7385 (diff) |
Merge branch 'prima2/multiplatform' into next/multiplatform
This series enables multiplatform support on the SIRF prima2/marco/atlas6
platform. The code was already quite tidy, so this is a relatively simple
change, and it follows similar changes we made to other ARMv7 based
platforms recently.
* prima2/multiplatform:
ARM: sirf: enable support in multi_v7_defconfig
ARM: sirf: enable multiplatform support
ARM: sirf: use clocksource_of infrastructure
ARM: sirf: move debug-macro.S to include/debug/sirf.S
ARM: sirf: enable sparse IRQ
ARM: sirf: move irq driver to drivers/irqchip
ARM: sirf: fix prima2 interrupt lookup
pinctrl: sirf: convert to linear irq domain
clocksource: make CLOCKSOURCE_OF_DECLARE type safe
ARM/dts: prima2: add .dtsi for atlas6 and .dts for atla6-evb board
arm: prima2: add new SiRFatlas6 machine in common board
ARM: smp_twd: convert to use CLKSRC_OF init
clocksource: tegra20: use the device_node pointer passed to init
clocksource: pass DT node pointer to init functions
clocksource: add empty version of clocksource_of_init
Conflicts:
arch/arm/configs/multi_v7_defconfig
arch/arm/mach-spear/spear13xx.c
Tested-by: Barry Song <Barry.Song@csr.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'drivers/clocksource/timer-marco.c')
-rw-r--r-- | drivers/clocksource/timer-marco.c | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c new file mode 100644 index 000000000000..97738dbf3e3b --- /dev/null +++ b/drivers/clocksource/timer-marco.c | |||
@@ -0,0 +1,299 @@ | |||
1 | /* | ||
2 | * System timer for CSR SiRFprimaII | ||
3 | * | ||
4 | * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. | ||
5 | * | ||
6 | * Licensed under GPLv2 or later. | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/clockchips.h> | ||
12 | #include <linux/clocksource.h> | ||
13 | #include <linux/bitops.h> | ||
14 | #include <linux/irq.h> | ||
15 | #include <linux/clk.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_irq.h> | ||
19 | #include <linux/of_address.h> | ||
20 | #include <asm/sched_clock.h> | ||
21 | #include <asm/localtimer.h> | ||
22 | #include <asm/mach/time.h> | ||
23 | |||
24 | #define SIRFSOC_TIMER_32COUNTER_0_CTRL 0x0000 | ||
25 | #define SIRFSOC_TIMER_32COUNTER_1_CTRL 0x0004 | ||
26 | #define SIRFSOC_TIMER_MATCH_0 0x0018 | ||
27 | #define SIRFSOC_TIMER_MATCH_1 0x001c | ||
28 | #define SIRFSOC_TIMER_COUNTER_0 0x0048 | ||
29 | #define SIRFSOC_TIMER_COUNTER_1 0x004c | ||
30 | #define SIRFSOC_TIMER_INTR_STATUS 0x0060 | ||
31 | #define SIRFSOC_TIMER_WATCHDOG_EN 0x0064 | ||
32 | #define SIRFSOC_TIMER_64COUNTER_CTRL 0x0068 | ||
33 | #define SIRFSOC_TIMER_64COUNTER_LO 0x006c | ||
34 | #define SIRFSOC_TIMER_64COUNTER_HI 0x0070 | ||
35 | #define SIRFSOC_TIMER_64COUNTER_LOAD_LO 0x0074 | ||
36 | #define SIRFSOC_TIMER_64COUNTER_LOAD_HI 0x0078 | ||
37 | #define SIRFSOC_TIMER_64COUNTER_RLATCHED_LO 0x007c | ||
38 | #define SIRFSOC_TIMER_64COUNTER_RLATCHED_HI 0x0080 | ||
39 | |||
40 | #define SIRFSOC_TIMER_REG_CNT 6 | ||
41 | |||
42 | static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = { | ||
43 | SIRFSOC_TIMER_WATCHDOG_EN, | ||
44 | SIRFSOC_TIMER_32COUNTER_0_CTRL, | ||
45 | SIRFSOC_TIMER_32COUNTER_1_CTRL, | ||
46 | SIRFSOC_TIMER_64COUNTER_CTRL, | ||
47 | SIRFSOC_TIMER_64COUNTER_RLATCHED_LO, | ||
48 | SIRFSOC_TIMER_64COUNTER_RLATCHED_HI, | ||
49 | }; | ||
50 | |||
51 | static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT]; | ||
52 | |||
53 | static void __iomem *sirfsoc_timer_base; | ||
54 | |||
55 | /* disable count and interrupt */ | ||
56 | static inline void sirfsoc_timer_count_disable(int idx) | ||
57 | { | ||
58 | writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) & ~0x7, | ||
59 | sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx); | ||
60 | } | ||
61 | |||
62 | /* enable count and interrupt */ | ||
63 | static inline void sirfsoc_timer_count_enable(int idx) | ||
64 | { | ||
65 | writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx) | 0x7, | ||
66 | sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL + 4 * idx); | ||
67 | } | ||
68 | |||
69 | /* timer interrupt handler */ | ||
70 | static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id) | ||
71 | { | ||
72 | struct clock_event_device *ce = dev_id; | ||
73 | int cpu = smp_processor_id(); | ||
74 | |||
75 | /* clear timer interrupt */ | ||
76 | writel_relaxed(BIT(cpu), sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); | ||
77 | |||
78 | if (ce->mode == CLOCK_EVT_MODE_ONESHOT) | ||
79 | sirfsoc_timer_count_disable(cpu); | ||
80 | |||
81 | ce->event_handler(ce); | ||
82 | |||
83 | return IRQ_HANDLED; | ||
84 | } | ||
85 | |||
86 | /* read 64-bit timer counter */ | ||
87 | static cycle_t sirfsoc_timer_read(struct clocksource *cs) | ||
88 | { | ||
89 | u64 cycles; | ||
90 | |||
91 | writel_relaxed((readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | | ||
92 | BIT(0)) & ~BIT(1), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | ||
93 | |||
94 | cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_HI); | ||
95 | cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_RLATCHED_LO); | ||
96 | |||
97 | return cycles; | ||
98 | } | ||
99 | |||
100 | static int sirfsoc_timer_set_next_event(unsigned long delta, | ||
101 | struct clock_event_device *ce) | ||
102 | { | ||
103 | int cpu = smp_processor_id(); | ||
104 | |||
105 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0 + | ||
106 | 4 * cpu); | ||
107 | writel_relaxed(delta, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0 + | ||
108 | 4 * cpu); | ||
109 | |||
110 | /* enable the tick */ | ||
111 | sirfsoc_timer_count_enable(cpu); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static void sirfsoc_timer_set_mode(enum clock_event_mode mode, | ||
117 | struct clock_event_device *ce) | ||
118 | { | ||
119 | switch (mode) { | ||
120 | case CLOCK_EVT_MODE_ONESHOT: | ||
121 | /* enable in set_next_event */ | ||
122 | break; | ||
123 | default: | ||
124 | break; | ||
125 | } | ||
126 | |||
127 | sirfsoc_timer_count_disable(smp_processor_id()); | ||
128 | } | ||
129 | |||
130 | static void sirfsoc_clocksource_suspend(struct clocksource *cs) | ||
131 | { | ||
132 | int i; | ||
133 | |||
134 | for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++) | ||
135 | sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); | ||
136 | } | ||
137 | |||
138 | static void sirfsoc_clocksource_resume(struct clocksource *cs) | ||
139 | { | ||
140 | int i; | ||
141 | |||
142 | for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++) | ||
143 | writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]); | ||
144 | |||
145 | writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], | ||
146 | sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO); | ||
147 | writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], | ||
148 | sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI); | ||
149 | |||
150 | writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | | ||
151 | BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | ||
152 | } | ||
153 | |||
154 | static struct clock_event_device sirfsoc_clockevent = { | ||
155 | .name = "sirfsoc_clockevent", | ||
156 | .rating = 200, | ||
157 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
158 | .set_mode = sirfsoc_timer_set_mode, | ||
159 | .set_next_event = sirfsoc_timer_set_next_event, | ||
160 | }; | ||
161 | |||
162 | static struct clocksource sirfsoc_clocksource = { | ||
163 | .name = "sirfsoc_clocksource", | ||
164 | .rating = 200, | ||
165 | .mask = CLOCKSOURCE_MASK(64), | ||
166 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
167 | .read = sirfsoc_timer_read, | ||
168 | .suspend = sirfsoc_clocksource_suspend, | ||
169 | .resume = sirfsoc_clocksource_resume, | ||
170 | }; | ||
171 | |||
172 | static struct irqaction sirfsoc_timer_irq = { | ||
173 | .name = "sirfsoc_timer0", | ||
174 | .flags = IRQF_TIMER | IRQF_NOBALANCING, | ||
175 | .handler = sirfsoc_timer_interrupt, | ||
176 | .dev_id = &sirfsoc_clockevent, | ||
177 | }; | ||
178 | |||
179 | #ifdef CONFIG_LOCAL_TIMERS | ||
180 | |||
181 | static struct irqaction sirfsoc_timer1_irq = { | ||
182 | .name = "sirfsoc_timer1", | ||
183 | .flags = IRQF_TIMER | IRQF_NOBALANCING, | ||
184 | .handler = sirfsoc_timer_interrupt, | ||
185 | }; | ||
186 | |||
187 | static int __cpuinit sirfsoc_local_timer_setup(struct clock_event_device *ce) | ||
188 | { | ||
189 | /* Use existing clock_event for cpu 0 */ | ||
190 | if (!smp_processor_id()) | ||
191 | return 0; | ||
192 | |||
193 | ce->irq = sirfsoc_timer1_irq.irq; | ||
194 | ce->name = "local_timer"; | ||
195 | ce->features = sirfsoc_clockevent.features; | ||
196 | ce->rating = sirfsoc_clockevent.rating; | ||
197 | ce->set_mode = sirfsoc_timer_set_mode; | ||
198 | ce->set_next_event = sirfsoc_timer_set_next_event; | ||
199 | ce->shift = sirfsoc_clockevent.shift; | ||
200 | ce->mult = sirfsoc_clockevent.mult; | ||
201 | ce->max_delta_ns = sirfsoc_clockevent.max_delta_ns; | ||
202 | ce->min_delta_ns = sirfsoc_clockevent.min_delta_ns; | ||
203 | |||
204 | sirfsoc_timer1_irq.dev_id = ce; | ||
205 | BUG_ON(setup_irq(ce->irq, &sirfsoc_timer1_irq)); | ||
206 | irq_set_affinity(sirfsoc_timer1_irq.irq, cpumask_of(1)); | ||
207 | |||
208 | clockevents_register_device(ce); | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static void sirfsoc_local_timer_stop(struct clock_event_device *ce) | ||
213 | { | ||
214 | sirfsoc_timer_count_disable(1); | ||
215 | |||
216 | remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq); | ||
217 | } | ||
218 | |||
219 | static struct local_timer_ops sirfsoc_local_timer_ops __cpuinitdata = { | ||
220 | .setup = sirfsoc_local_timer_setup, | ||
221 | .stop = sirfsoc_local_timer_stop, | ||
222 | }; | ||
223 | #endif /* CONFIG_LOCAL_TIMERS */ | ||
224 | |||
225 | static void __init sirfsoc_clockevent_init(void) | ||
226 | { | ||
227 | clockevents_calc_mult_shift(&sirfsoc_clockevent, CLOCK_TICK_RATE, 60); | ||
228 | |||
229 | sirfsoc_clockevent.max_delta_ns = | ||
230 | clockevent_delta2ns(-2, &sirfsoc_clockevent); | ||
231 | sirfsoc_clockevent.min_delta_ns = | ||
232 | clockevent_delta2ns(2, &sirfsoc_clockevent); | ||
233 | |||
234 | sirfsoc_clockevent.cpumask = cpumask_of(0); | ||
235 | clockevents_register_device(&sirfsoc_clockevent); | ||
236 | #ifdef CONFIG_LOCAL_TIMERS | ||
237 | local_timer_register(&sirfsoc_local_timer_ops); | ||
238 | #endif | ||
239 | } | ||
240 | |||
241 | /* initialize the kernel jiffy timer source */ | ||
242 | static void __init sirfsoc_marco_timer_init(void) | ||
243 | { | ||
244 | unsigned long rate; | ||
245 | u32 timer_div; | ||
246 | struct clk *clk; | ||
247 | |||
248 | /* timer's input clock is io clock */ | ||
249 | clk = clk_get_sys("io", NULL); | ||
250 | |||
251 | BUG_ON(IS_ERR(clk)); | ||
252 | rate = clk_get_rate(clk); | ||
253 | |||
254 | BUG_ON(rate < CLOCK_TICK_RATE); | ||
255 | BUG_ON(rate % CLOCK_TICK_RATE); | ||
256 | |||
257 | /* Initialize the timer dividers */ | ||
258 | timer_div = rate / CLOCK_TICK_RATE - 1; | ||
259 | writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | ||
260 | writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_0_CTRL); | ||
261 | writel_relaxed(timer_div << 16, sirfsoc_timer_base + SIRFSOC_TIMER_32COUNTER_1_CTRL); | ||
262 | |||
263 | /* Initialize timer counters to 0 */ | ||
264 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_LO); | ||
265 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_LOAD_HI); | ||
266 | writel_relaxed(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL) | | ||
267 | BIT(1) | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_64COUNTER_CTRL); | ||
268 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_0); | ||
269 | writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_1); | ||
270 | |||
271 | /* Clear all interrupts */ | ||
272 | writel_relaxed(0xFFFF, sirfsoc_timer_base + SIRFSOC_TIMER_INTR_STATUS); | ||
273 | |||
274 | BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE)); | ||
275 | |||
276 | BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq)); | ||
277 | |||
278 | sirfsoc_clockevent_init(); | ||
279 | } | ||
280 | |||
281 | static void __init sirfsoc_of_timer_init(struct device_node *np) | ||
282 | { | ||
283 | sirfsoc_timer_base = of_iomap(np, 0); | ||
284 | if (!sirfsoc_timer_base) | ||
285 | panic("unable to map timer cpu registers\n"); | ||
286 | |||
287 | sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); | ||
288 | if (!sirfsoc_timer_irq.irq) | ||
289 | panic("No irq passed for timer0 via DT\n"); | ||
290 | |||
291 | #ifdef CONFIG_LOCAL_TIMERS | ||
292 | sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1); | ||
293 | if (!sirfsoc_timer1_irq.irq) | ||
294 | panic("No irq passed for timer1 via DT\n"); | ||
295 | #endif | ||
296 | |||
297 | sirfsoc_marco_timer_init(); | ||
298 | } | ||
299 | CLOCKSOURCE_OF_DECLARE(sirfsoc_marco_timer, "sirf,marco-tick", sirfsoc_of_timer_init ); | ||