aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorMichal Simek <michal.simek@xilinx.com>2013-03-20 05:15:28 -0400
committerMichal Simek <michal.simek@xilinx.com>2013-04-04 03:09:08 -0400
commite932900a3279b5dbb6d8f43c7b369003620e137c (patch)
tree9278ee3e2b6f951a72ef5479029f836403950a11 /arch/arm
parent3d5a96582303e28c48699f3faaf920ef7d43e6f2 (diff)
arm: zynq: Use standard timer binding
Use cdns,ttc because this driver is Cadence Rev06 Triple Timer Counter and everybody can use it without xilinx specific function name or probing. Also use standard dt description for timer and also prepare for moving to clocksource initialization. Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/boot/dts/zynq-7000.dtsi45
-rw-r--r--arch/arm/boot/dts/zynq-zc702.dts10
-rw-r--r--arch/arm/mach-zynq/common.c1
-rw-r--r--arch/arm/mach-zynq/timer.c261
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 */
54struct xttcps_timer { 72struct 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
58struct xttcps_timer_clocksource { 81struct 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 {
66struct xttcps_timer_clockevent { 89struct 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
192static void __init zynq_ttc_setup_clocksource(struct device_node *np, 214static 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
251static 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", &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
235static void __init zynq_ttc_setup_clockevent(struct device_node *np, 298static 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
331static 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", &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
287static 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 */
389static 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
301void __init xttcps_timer_init(void) 424void __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}