diff options
-rw-r--r-- | arch/arm/boot/dts/zynq-7000.dtsi | 45 | ||||
-rw-r--r-- | arch/arm/boot/dts/zynq-zc702.dts | 10 | ||||
-rw-r--r-- | arch/arm/mach-zynq/common.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-zynq/timer.c | 261 |
4 files changed, 195 insertions, 122 deletions
diff --git a/arch/arm/boot/dts/zynq-7000.dtsi b/arch/arm/boot/dts/zynq-7000.dtsi index 5914b5654591..51243db2e9e4 100644 --- a/arch/arm/boot/dts/zynq-7000.dtsi +++ b/arch/arm/boot/dts/zynq-7000.dtsi | |||
@@ -111,56 +111,23 @@ | |||
111 | }; | 111 | }; |
112 | 112 | ||
113 | ttc0: ttc0@f8001000 { | 113 | ttc0: ttc0@f8001000 { |
114 | #address-cells = <1>; | 114 | interrupt-parent = <&intc>; |
115 | #size-cells = <0>; | 115 | interrupts = < 0 10 4 0 11 4 0 12 4 >; |
116 | compatible = "xlnx,ttc"; | 116 | compatible = "cdns,ttc"; |
117 | reg = <0xF8001000 0x1000>; | 117 | reg = <0xF8001000 0x1000>; |
118 | clocks = <&cpu_clk 3>; | 118 | clocks = <&cpu_clk 3>; |
119 | clock-names = "cpu_1x"; | 119 | clock-names = "cpu_1x"; |
120 | clock-ranges; | 120 | clock-ranges; |
121 | |||
122 | ttc0_0: ttc0.0 { | ||
123 | status = "disabled"; | ||
124 | reg = <0>; | ||
125 | interrupts = <0 10 4>; | ||
126 | }; | ||
127 | ttc0_1: ttc0.1 { | ||
128 | status = "disabled"; | ||
129 | reg = <1>; | ||
130 | interrupts = <0 11 4>; | ||
131 | }; | ||
132 | ttc0_2: ttc0.2 { | ||
133 | status = "disabled"; | ||
134 | reg = <2>; | ||
135 | interrupts = <0 12 4>; | ||
136 | }; | ||
137 | }; | 121 | }; |
138 | 122 | ||
139 | ttc1: ttc1@f8002000 { | 123 | ttc1: ttc1@f8002000 { |
140 | #interrupt-parent = <&intc>; | 124 | interrupt-parent = <&intc>; |
141 | #address-cells = <1>; | 125 | interrupts = < 0 37 4 0 38 4 0 39 4 >; |
142 | #size-cells = <0>; | 126 | compatible = "cdns,ttc"; |
143 | compatible = "xlnx,ttc"; | ||
144 | reg = <0xF8002000 0x1000>; | 127 | reg = <0xF8002000 0x1000>; |
145 | clocks = <&cpu_clk 3>; | 128 | clocks = <&cpu_clk 3>; |
146 | clock-names = "cpu_1x"; | 129 | clock-names = "cpu_1x"; |
147 | clock-ranges; | 130 | clock-ranges; |
148 | |||
149 | ttc1_0: ttc1.0 { | ||
150 | status = "disabled"; | ||
151 | reg = <0>; | ||
152 | interrupts = <0 37 4>; | ||
153 | }; | ||
154 | ttc1_1: ttc1.1 { | ||
155 | status = "disabled"; | ||
156 | reg = <1>; | ||
157 | interrupts = <0 38 4>; | ||
158 | }; | ||
159 | ttc1_2: ttc1.2 { | ||
160 | status = "disabled"; | ||
161 | reg = <2>; | ||
162 | interrupts = <0 39 4>; | ||
163 | }; | ||
164 | }; | 131 | }; |
165 | }; | 132 | }; |
166 | }; | 133 | }; |
diff --git a/arch/arm/boot/dts/zynq-zc702.dts b/arch/arm/boot/dts/zynq-zc702.dts index c772942a399a..86f44d5b0265 100644 --- a/arch/arm/boot/dts/zynq-zc702.dts +++ b/arch/arm/boot/dts/zynq-zc702.dts | |||
@@ -32,13 +32,3 @@ | |||
32 | &ps_clk { | 32 | &ps_clk { |
33 | clock-frequency = <33333330>; | 33 | clock-frequency = <33333330>; |
34 | }; | 34 | }; |
35 | |||
36 | &ttc0_0 { | ||
37 | status = "ok"; | ||
38 | compatible = "xlnx,ttc-counter-clocksource"; | ||
39 | }; | ||
40 | |||
41 | &ttc0_1 { | ||
42 | status = "ok"; | ||
43 | compatible = "xlnx,ttc-counter-clockevent"; | ||
44 | }; | ||
diff --git a/arch/arm/mach-zynq/common.c b/arch/arm/mach-zynq/common.c index 5c8983218183..76493b050beb 100644 --- a/arch/arm/mach-zynq/common.c +++ b/arch/arm/mach-zynq/common.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
22 | #include <linux/clk/zynq.h> | 22 | #include <linux/clk/zynq.h> |
23 | #include <linux/clocksource.h> | ||
23 | #include <linux/of_address.h> | 24 | #include <linux/of_address.h> |
24 | #include <linux/of_irq.h> | 25 | #include <linux/of_irq.h> |
25 | #include <linux/of_platform.h> | 26 | #include <linux/of_platform.h> |
diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c index f9fbc9c1e7a6..82357d94a0e0 100644 --- a/arch/arm/mach-zynq/timer.c +++ b/arch/arm/mach-zynq/timer.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * This file contains driver for the Xilinx PS Timer Counter IP. | 2 | * This file contains driver for the Xilinx PS Timer Counter IP. |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Xilinx | 4 | * Copyright (C) 2011-2013 Xilinx |
5 | * | 5 | * |
6 | * based on arch/mips/kernel/time.c timer driver | 6 | * based on arch/mips/kernel/time.c timer driver |
7 | * | 7 | * |
@@ -15,6 +15,7 @@ | |||
15 | * GNU General Public License for more details. | 15 | * GNU General Public License for more details. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/clk.h> | ||
18 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
19 | #include <linux/clockchips.h> | 20 | #include <linux/clockchips.h> |
20 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
@@ -24,6 +25,21 @@ | |||
24 | #include "common.h" | 25 | #include "common.h" |
25 | 26 | ||
26 | /* | 27 | /* |
28 | * This driver configures the 2 16-bit count-up timers as follows: | ||
29 | * | ||
30 | * T1: Timer 1, clocksource for generic timekeeping | ||
31 | * T2: Timer 2, clockevent source for hrtimers | ||
32 | * T3: Timer 3, <unused> | ||
33 | * | ||
34 | * The input frequency to the timer module for emulation is 2.5MHz which is | ||
35 | * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32, | ||
36 | * the timers are clocked at 78.125KHz (12.8 us resolution). | ||
37 | |||
38 | * The input frequency to the timer module in silicon is configurable and | ||
39 | * obtained from device tree. The pre-scaler of 32 is used. | ||
40 | */ | ||
41 | |||
42 | /* | ||
27 | * Timer Register Offset Definitions of Timer 1, Increment base address by 4 | 43 | * Timer Register Offset Definitions of Timer 1, Increment base address by 4 |
28 | * and use same offsets for Timer 2 | 44 | * and use same offsets for Timer 2 |
29 | */ | 45 | */ |
@@ -44,17 +60,24 @@ | |||
44 | #define PRESCALE 2048 /* The exponent must match this */ | 60 | #define PRESCALE 2048 /* The exponent must match this */ |
45 | #define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1) | 61 | #define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1) |
46 | #define CLK_CNTRL_PRESCALE_EN 1 | 62 | #define CLK_CNTRL_PRESCALE_EN 1 |
47 | #define CNT_CNTRL_RESET (1<<4) | 63 | #define CNT_CNTRL_RESET (1 << 4) |
48 | 64 | ||
49 | /** | 65 | /** |
50 | * struct xttcps_timer - This definition defines local timer structure | 66 | * struct xttcps_timer - This definition defines local timer structure |
51 | * | 67 | * |
52 | * @base_addr: Base address of timer | 68 | * @base_addr: Base address of timer |
53 | **/ | 69 | * @clk: Associated clock source |
70 | * @clk_rate_change_nb Notifier block for clock rate changes | ||
71 | */ | ||
54 | struct xttcps_timer { | 72 | struct xttcps_timer { |
55 | void __iomem *base_addr; | 73 | void __iomem *base_addr; |
74 | struct clk *clk; | ||
75 | struct notifier_block clk_rate_change_nb; | ||
56 | }; | 76 | }; |
57 | 77 | ||
78 | #define to_xttcps_timer(x) \ | ||
79 | container_of(x, struct xttcps_timer, clk_rate_change_nb) | ||
80 | |||
58 | struct xttcps_timer_clocksource { | 81 | struct xttcps_timer_clocksource { |
59 | struct xttcps_timer xttc; | 82 | struct xttcps_timer xttc; |
60 | struct clocksource cs; | 83 | struct clocksource cs; |
@@ -66,7 +89,6 @@ struct xttcps_timer_clocksource { | |||
66 | struct xttcps_timer_clockevent { | 89 | struct xttcps_timer_clockevent { |
67 | struct xttcps_timer xttc; | 90 | struct xttcps_timer xttc; |
68 | struct clock_event_device ce; | 91 | struct clock_event_device ce; |
69 | struct clk *clk; | ||
70 | }; | 92 | }; |
71 | 93 | ||
72 | #define to_xttcps_timer_clkevent(x) \ | 94 | #define to_xttcps_timer_clkevent(x) \ |
@@ -167,8 +189,8 @@ static void xttcps_set_mode(enum clock_event_mode mode, | |||
167 | switch (mode) { | 189 | switch (mode) { |
168 | case CLOCK_EVT_MODE_PERIODIC: | 190 | case CLOCK_EVT_MODE_PERIODIC: |
169 | xttcps_set_interval(timer, | 191 | xttcps_set_interval(timer, |
170 | DIV_ROUND_CLOSEST(clk_get_rate(xttce->clk), | 192 | DIV_ROUND_CLOSEST(clk_get_rate(xttce->xttc.clk), |
171 | PRESCALE * HZ)); | 193 | PRESCALE * HZ)); |
172 | break; | 194 | break; |
173 | case CLOCK_EVT_MODE_ONESHOT: | 195 | case CLOCK_EVT_MODE_ONESHOT: |
174 | case CLOCK_EVT_MODE_UNUSED: | 196 | case CLOCK_EVT_MODE_UNUSED: |
@@ -189,79 +211,148 @@ static void xttcps_set_mode(enum clock_event_mode mode, | |||
189 | } | 211 | } |
190 | } | 212 | } |
191 | 213 | ||
192 | static void __init zynq_ttc_setup_clocksource(struct device_node *np, | 214 | static int xttcps_rate_change_clocksource_cb(struct notifier_block *nb, |
193 | void __iomem *base) | 215 | unsigned long event, void *data) |
216 | { | ||
217 | struct clk_notifier_data *ndata = data; | ||
218 | struct xttcps_timer *xttcps = to_xttcps_timer(nb); | ||
219 | struct xttcps_timer_clocksource *xttccs = container_of(xttcps, | ||
220 | struct xttcps_timer_clocksource, xttc); | ||
221 | |||
222 | switch (event) { | ||
223 | case POST_RATE_CHANGE: | ||
224 | /* | ||
225 | * Do whatever is necessary to maintain a proper time base | ||
226 | * | ||
227 | * I cannot find a way to adjust the currently used clocksource | ||
228 | * to the new frequency. __clocksource_updatefreq_hz() sounds | ||
229 | * good, but does not work. Not sure what's that missing. | ||
230 | * | ||
231 | * This approach works, but triggers two clocksource switches. | ||
232 | * The first after unregister to clocksource jiffies. And | ||
233 | * another one after the register to the newly registered timer. | ||
234 | * | ||
235 | * Alternatively we could 'waste' another HW timer to ping pong | ||
236 | * between clock sources. That would also use one register and | ||
237 | * one unregister call, but only trigger one clocksource switch | ||
238 | * for the cost of another HW timer used by the OS. | ||
239 | */ | ||
240 | clocksource_unregister(&xttccs->cs); | ||
241 | clocksource_register_hz(&xttccs->cs, | ||
242 | ndata->new_rate / PRESCALE); | ||
243 | /* fall through */ | ||
244 | case PRE_RATE_CHANGE: | ||
245 | case ABORT_RATE_CHANGE: | ||
246 | default: | ||
247 | return NOTIFY_DONE; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | static void __init xttc_setup_clocksource(struct clk *clk, void __iomem *base) | ||
194 | { | 252 | { |
195 | struct xttcps_timer_clocksource *ttccs; | 253 | struct xttcps_timer_clocksource *ttccs; |
196 | struct clk *clk; | ||
197 | int err; | 254 | int err; |
198 | u32 reg; | ||
199 | 255 | ||
200 | ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL); | 256 | ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL); |
201 | if (WARN_ON(!ttccs)) | 257 | if (WARN_ON(!ttccs)) |
202 | return; | 258 | return; |
203 | 259 | ||
204 | err = of_property_read_u32(np, "reg", ®); | 260 | ttccs->xttc.clk = clk; |
205 | if (WARN_ON(err)) | ||
206 | return; | ||
207 | 261 | ||
208 | clk = of_clk_get_by_name(np, "cpu_1x"); | 262 | err = clk_prepare_enable(ttccs->xttc.clk); |
209 | if (WARN_ON(IS_ERR(clk))) | ||
210 | return; | ||
211 | |||
212 | err = clk_prepare_enable(clk); | ||
213 | if (WARN_ON(err)) | 263 | if (WARN_ON(err)) |
214 | return; | 264 | return; |
215 | 265 | ||
216 | ttccs->xttc.base_addr = base + reg * 4; | 266 | ttccs->xttc.clk_rate_change_nb.notifier_call = |
267 | xttcps_rate_change_clocksource_cb; | ||
268 | ttccs->xttc.clk_rate_change_nb.next = NULL; | ||
269 | if (clk_notifier_register(ttccs->xttc.clk, | ||
270 | &ttccs->xttc.clk_rate_change_nb)) | ||
271 | pr_warn("Unable to register clock notifier.\n"); | ||
217 | 272 | ||
218 | ttccs->cs.name = np->name; | 273 | ttccs->xttc.base_addr = base; |
274 | ttccs->cs.name = "xttcps_clocksource"; | ||
219 | ttccs->cs.rating = 200; | 275 | ttccs->cs.rating = 200; |
220 | ttccs->cs.read = __xttc_clocksource_read; | 276 | ttccs->cs.read = __xttc_clocksource_read; |
221 | ttccs->cs.mask = CLOCKSOURCE_MASK(16); | 277 | ttccs->cs.mask = CLOCKSOURCE_MASK(16); |
222 | ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; | 278 | ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; |
223 | 279 | ||
280 | /* | ||
281 | * Setup the clock source counter to be an incrementing counter | ||
282 | * with no interrupt and it rolls over at 0xFFFF. Pre-scale | ||
283 | * it by 32 also. Let it start running now. | ||
284 | */ | ||
224 | __raw_writel(0x0, ttccs->xttc.base_addr + XTTCPS_IER_OFFSET); | 285 | __raw_writel(0x0, ttccs->xttc.base_addr + XTTCPS_IER_OFFSET); |
225 | __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, | 286 | __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, |
226 | ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET); | 287 | ttccs->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET); |
227 | __raw_writel(CNT_CNTRL_RESET, | 288 | __raw_writel(CNT_CNTRL_RESET, |
228 | ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET); | 289 | ttccs->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET); |
229 | 290 | ||
230 | err = clocksource_register_hz(&ttccs->cs, clk_get_rate(clk) / PRESCALE); | 291 | err = clocksource_register_hz(&ttccs->cs, |
292 | clk_get_rate(ttccs->xttc.clk) / PRESCALE); | ||
231 | if (WARN_ON(err)) | 293 | if (WARN_ON(err)) |
232 | return; | 294 | return; |
295 | |||
233 | } | 296 | } |
234 | 297 | ||
235 | static void __init zynq_ttc_setup_clockevent(struct device_node *np, | 298 | static int xttcps_rate_change_clockevent_cb(struct notifier_block *nb, |
236 | void __iomem *base) | 299 | unsigned long event, void *data) |
300 | { | ||
301 | struct clk_notifier_data *ndata = data; | ||
302 | struct xttcps_timer *xttcps = to_xttcps_timer(nb); | ||
303 | struct xttcps_timer_clockevent *xttcce = container_of(xttcps, | ||
304 | struct xttcps_timer_clockevent, xttc); | ||
305 | |||
306 | switch (event) { | ||
307 | case POST_RATE_CHANGE: | ||
308 | { | ||
309 | unsigned long flags; | ||
310 | |||
311 | /* | ||
312 | * clockevents_update_freq should be called with IRQ disabled on | ||
313 | * the CPU the timer provides events for. The timer we use is | ||
314 | * common to both CPUs, not sure if we need to run on both | ||
315 | * cores. | ||
316 | */ | ||
317 | local_irq_save(flags); | ||
318 | clockevents_update_freq(&xttcce->ce, | ||
319 | ndata->new_rate / PRESCALE); | ||
320 | local_irq_restore(flags); | ||
321 | |||
322 | /* fall through */ | ||
323 | } | ||
324 | case PRE_RATE_CHANGE: | ||
325 | case ABORT_RATE_CHANGE: | ||
326 | default: | ||
327 | return NOTIFY_DONE; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | static void __init xttc_setup_clockevent(struct clk *clk, | ||
332 | void __iomem *base, u32 irq) | ||
237 | { | 333 | { |
238 | struct xttcps_timer_clockevent *ttcce; | 334 | struct xttcps_timer_clockevent *ttcce; |
239 | int err, irq; | 335 | int err; |
240 | u32 reg; | ||
241 | 336 | ||
242 | ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL); | 337 | ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL); |
243 | if (WARN_ON(!ttcce)) | 338 | if (WARN_ON(!ttcce)) |
244 | return; | 339 | return; |
245 | 340 | ||
246 | err = of_property_read_u32(np, "reg", ®); | 341 | ttcce->xttc.clk = clk; |
247 | if (WARN_ON(err)) | ||
248 | return; | ||
249 | 342 | ||
250 | ttcce->xttc.base_addr = base + reg * 4; | 343 | err = clk_prepare_enable(ttcce->xttc.clk); |
251 | |||
252 | ttcce->clk = of_clk_get_by_name(np, "cpu_1x"); | ||
253 | if (WARN_ON(IS_ERR(ttcce->clk))) | ||
254 | return; | ||
255 | |||
256 | err = clk_prepare_enable(ttcce->clk); | ||
257 | if (WARN_ON(err)) | 344 | if (WARN_ON(err)) |
258 | return; | 345 | return; |
259 | 346 | ||
260 | irq = irq_of_parse_and_map(np, 0); | 347 | ttcce->xttc.clk_rate_change_nb.notifier_call = |
261 | if (WARN_ON(!irq)) | 348 | xttcps_rate_change_clockevent_cb; |
262 | return; | 349 | ttcce->xttc.clk_rate_change_nb.next = NULL; |
350 | if (clk_notifier_register(ttcce->xttc.clk, | ||
351 | &ttcce->xttc.clk_rate_change_nb)) | ||
352 | pr_warn("Unable to register clock notifier.\n"); | ||
263 | 353 | ||
264 | ttcce->ce.name = np->name; | 354 | ttcce->xttc.base_addr = base; |
355 | ttcce->ce.name = "xttcps_clockevent"; | ||
265 | ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; | 356 | ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; |
266 | ttcce->ce.set_next_event = xttcps_set_next_event; | 357 | ttcce->ce.set_next_event = xttcps_set_next_event; |
267 | ttcce->ce.set_mode = xttcps_set_mode; | 358 | ttcce->ce.set_mode = xttcps_set_mode; |
@@ -269,56 +360,80 @@ static void __init zynq_ttc_setup_clockevent(struct device_node *np, | |||
269 | ttcce->ce.irq = irq; | 360 | ttcce->ce.irq = irq; |
270 | ttcce->ce.cpumask = cpu_possible_mask; | 361 | ttcce->ce.cpumask = cpu_possible_mask; |
271 | 362 | ||
363 | /* | ||
364 | * Setup the clock event timer to be an interval timer which | ||
365 | * is prescaled by 32 using the interval interrupt. Leave it | ||
366 | * disabled for now. | ||
367 | */ | ||
272 | __raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET); | 368 | __raw_writel(0x23, ttcce->xttc.base_addr + XTTCPS_CNT_CNTRL_OFFSET); |
273 | __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, | 369 | __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, |
274 | ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET); | 370 | ttcce->xttc.base_addr + XTTCPS_CLK_CNTRL_OFFSET); |
275 | __raw_writel(0x1, ttcce->xttc.base_addr + XTTCPS_IER_OFFSET); | 371 | __raw_writel(0x1, ttcce->xttc.base_addr + XTTCPS_IER_OFFSET); |
276 | 372 | ||
277 | err = request_irq(irq, xttcps_clock_event_interrupt, IRQF_TIMER, | 373 | err = request_irq(irq, xttcps_clock_event_interrupt, |
278 | np->name, ttcce); | 374 | IRQF_DISABLED | IRQF_TIMER, |
375 | ttcce->ce.name, ttcce); | ||
279 | if (WARN_ON(err)) | 376 | if (WARN_ON(err)) |
280 | return; | 377 | return; |
281 | 378 | ||
282 | clockevents_config_and_register(&ttcce->ce, | 379 | clockevents_config_and_register(&ttcce->ce, |
283 | clk_get_rate(ttcce->clk) / PRESCALE, | 380 | clk_get_rate(ttcce->xttc.clk) / PRESCALE, 1, 0xfffe); |
284 | 1, 0xfffe); | ||
285 | } | 381 | } |
286 | 382 | ||
287 | static const __initconst struct of_device_id zynq_ttc_match[] = { | ||
288 | { .compatible = "xlnx,ttc-counter-clocksource", | ||
289 | .data = zynq_ttc_setup_clocksource, }, | ||
290 | { .compatible = "xlnx,ttc-counter-clockevent", | ||
291 | .data = zynq_ttc_setup_clockevent, }, | ||
292 | {} | ||
293 | }; | ||
294 | |||
295 | /** | 383 | /** |
296 | * xttcps_timer_init - Initialize the timer | 384 | * xttcps_timer_init - Initialize the timer |
297 | * | 385 | * |
298 | * Initializes the timer hardware and register the clock source and clock event | 386 | * Initializes the timer hardware and register the clock source and clock event |
299 | * timers with Linux kernal timer framework | 387 | * timers with Linux kernal timer framework |
300 | **/ | 388 | */ |
389 | static void __init xttcps_timer_init_of(struct device_node *timer) | ||
390 | { | ||
391 | unsigned int irq; | ||
392 | void __iomem *timer_baseaddr; | ||
393 | struct clk *clk; | ||
394 | |||
395 | /* | ||
396 | * Get the 1st Triple Timer Counter (TTC) block from the device tree | ||
397 | * and use it. Note that the event timer uses the interrupt and it's the | ||
398 | * 2nd TTC hence the irq_of_parse_and_map(,1) | ||
399 | */ | ||
400 | timer_baseaddr = of_iomap(timer, 0); | ||
401 | if (!timer_baseaddr) { | ||
402 | pr_err("ERROR: invalid timer base address\n"); | ||
403 | BUG(); | ||
404 | } | ||
405 | |||
406 | irq = irq_of_parse_and_map(timer, 1); | ||
407 | if (irq <= 0) { | ||
408 | pr_err("ERROR: invalid interrupt number\n"); | ||
409 | BUG(); | ||
410 | } | ||
411 | |||
412 | clk = of_clk_get_by_name(timer, "cpu_1x"); | ||
413 | if (IS_ERR(clk)) { | ||
414 | pr_err("ERROR: timer input clock not found\n"); | ||
415 | BUG(); | ||
416 | } | ||
417 | |||
418 | xttc_setup_clocksource(clk, timer_baseaddr); | ||
419 | xttc_setup_clockevent(clk, timer_baseaddr + 4, irq); | ||
420 | |||
421 | pr_info("%s #0 at %p, irq=%d\n", timer->name, timer_baseaddr, irq); | ||
422 | } | ||
423 | |||
301 | void __init xttcps_timer_init(void) | 424 | void __init xttcps_timer_init(void) |
302 | { | 425 | { |
303 | struct device_node *np; | 426 | const char * const timer_list[] = { |
304 | 427 | "cdns,ttc", | |
305 | for_each_compatible_node(np, NULL, "xlnx,ttc") { | 428 | NULL |
306 | struct device_node *np_chld; | 429 | }; |
307 | void __iomem *base; | 430 | struct device_node *timer; |
308 | 431 | ||
309 | base = of_iomap(np, 0); | 432 | timer = of_find_compatible_node(NULL, NULL, timer_list[0]); |
310 | if (WARN_ON(!base)) | 433 | if (!timer) { |
311 | return; | 434 | pr_err("ERROR: no compatible timer found\n"); |
312 | 435 | BUG(); | |
313 | for_each_available_child_of_node(np, np_chld) { | ||
314 | int (*cb)(struct device_node *np, void __iomem *base); | ||
315 | const struct of_device_id *match; | ||
316 | |||
317 | match = of_match_node(zynq_ttc_match, np_chld); | ||
318 | if (match) { | ||
319 | cb = match->data; | ||
320 | cb(np_chld, base); | ||
321 | } | ||
322 | } | ||
323 | } | 436 | } |
437 | |||
438 | xttcps_timer_init_of(timer); | ||
324 | } | 439 | } |