diff options
Diffstat (limited to 'arch/arm/mach-zynq')
-rw-r--r-- | arch/arm/mach-zynq/timer.c | 287 |
1 files changed, 165 insertions, 122 deletions
diff --git a/arch/arm/mach-zynq/timer.c b/arch/arm/mach-zynq/timer.c index c93cbe55a495..9662306aa12f 100644 --- a/arch/arm/mach-zynq/timer.c +++ b/arch/arm/mach-zynq/timer.c | |||
@@ -23,31 +23,15 @@ | |||
23 | #include <linux/clocksource.h> | 23 | #include <linux/clocksource.h> |
24 | #include <linux/clockchips.h> | 24 | #include <linux/clockchips.h> |
25 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/of.h> | ||
27 | #include <linux/of_address.h> | ||
28 | #include <linux/of_irq.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/clk-provider.h> | ||
26 | 31 | ||
27 | #include <mach/zynq_soc.h> | 32 | #include <mach/zynq_soc.h> |
28 | #include "common.h" | 33 | #include "common.h" |
29 | 34 | ||
30 | #define IRQ_TIMERCOUNTER0 42 | ||
31 | |||
32 | /* | ||
33 | * This driver configures the 2 16-bit count-up timers as follows: | ||
34 | * | ||
35 | * T1: Timer 1, clocksource for generic timekeeping | ||
36 | * T2: Timer 2, clockevent source for hrtimers | ||
37 | * T3: Timer 3, <unused> | ||
38 | * | ||
39 | * The input frequency to the timer module for emulation is 2.5MHz which is | ||
40 | * common to all the timer channels (T1, T2, and T3). With a pre-scaler of 32, | ||
41 | * the timers are clocked at 78.125KHz (12.8 us resolution). | ||
42 | * | ||
43 | * The input frequency to the timer module in silicon will be 200MHz. With the | ||
44 | * pre-scaler of 32, the timers are clocked at 6.25MHz (160ns resolution). | ||
45 | */ | ||
46 | #define XTTCPSS_CLOCKSOURCE 0 /* Timer 1 as a generic timekeeping */ | ||
47 | #define XTTCPSS_CLOCKEVENT 1 /* Timer 2 as a clock event */ | ||
48 | |||
49 | #define XTTCPSS_TIMER_BASE TTC0_BASE | ||
50 | #define XTTCPCC_EVENT_TIMER_IRQ (IRQ_TIMERCOUNTER0 + 1) | ||
51 | /* | 35 | /* |
52 | * Timer Register Offset Definitions of Timer 1, Increment base address by 4 | 36 | * Timer Register Offset Definitions of Timer 1, Increment base address by 4 |
53 | * and use same offsets for Timer 2 | 37 | * and use same offsets for Timer 2 |
@@ -64,9 +48,14 @@ | |||
64 | 48 | ||
65 | #define XTTCPSS_CNT_CNTRL_DISABLE_MASK 0x1 | 49 | #define XTTCPSS_CNT_CNTRL_DISABLE_MASK 0x1 |
66 | 50 | ||
67 | /* Setup the timers to use pre-scaling */ | 51 | /* Setup the timers to use pre-scaling, using a fixed value for now that will |
68 | 52 | * work across most input frequency, but it may need to be more dynamic | |
69 | #define TIMER_RATE (PERIPHERAL_CLOCK_RATE / 32) | 53 | */ |
54 | #define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */ | ||
55 | #define PRESCALE 2048 /* The exponent must match this */ | ||
56 | #define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1) | ||
57 | #define CLK_CNTRL_PRESCALE_EN 1 | ||
58 | #define CNT_CNTRL_RESET (1<<4) | ||
70 | 59 | ||
71 | /** | 60 | /** |
72 | * struct xttcpss_timer - This definition defines local timer structure | 61 | * struct xttcpss_timer - This definition defines local timer structure |
@@ -74,11 +63,25 @@ | |||
74 | * @base_addr: Base address of timer | 63 | * @base_addr: Base address of timer |
75 | **/ | 64 | **/ |
76 | struct xttcpss_timer { | 65 | struct xttcpss_timer { |
77 | void __iomem *base_addr; | 66 | void __iomem *base_addr; |
67 | }; | ||
68 | |||
69 | struct xttcpss_timer_clocksource { | ||
70 | struct xttcpss_timer xttc; | ||
71 | struct clocksource cs; | ||
78 | }; | 72 | }; |
79 | 73 | ||
80 | static struct xttcpss_timer timers[2]; | 74 | #define to_xttcpss_timer_clksrc(x) \ |
81 | static struct clock_event_device xttcpss_clockevent; | 75 | container_of(x, struct xttcpss_timer_clocksource, cs) |
76 | |||
77 | struct xttcpss_timer_clockevent { | ||
78 | struct xttcpss_timer xttc; | ||
79 | struct clock_event_device ce; | ||
80 | struct clk *clk; | ||
81 | }; | ||
82 | |||
83 | #define to_xttcpss_timer_clkevent(x) \ | ||
84 | container_of(x, struct xttcpss_timer_clockevent, ce) | ||
82 | 85 | ||
83 | /** | 86 | /** |
84 | * xttcpss_set_interval - Set the timer interval value | 87 | * xttcpss_set_interval - Set the timer interval value |
@@ -100,7 +103,7 @@ static void xttcpss_set_interval(struct xttcpss_timer *timer, | |||
100 | 103 | ||
101 | /* Reset the counter (0x10) so that it starts from 0, one-shot | 104 | /* Reset the counter (0x10) so that it starts from 0, one-shot |
102 | mode makes this needed for timing to be right. */ | 105 | mode makes this needed for timing to be right. */ |
103 | ctrl_reg |= 0x10; | 106 | ctrl_reg |= CNT_CNTRL_RESET; |
104 | ctrl_reg &= ~XTTCPSS_CNT_CNTRL_DISABLE_MASK; | 107 | ctrl_reg &= ~XTTCPSS_CNT_CNTRL_DISABLE_MASK; |
105 | __raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET); | 108 | __raw_writel(ctrl_reg, timer->base_addr + XTTCPSS_CNT_CNTRL_OFFSET); |
106 | } | 109 | } |
@@ -115,90 +118,31 @@ static void xttcpss_set_interval(struct xttcpss_timer *timer, | |||
115 | **/ | 118 | **/ |
116 | static irqreturn_t xttcpss_clock_event_interrupt(int irq, void *dev_id) | 119 | static irqreturn_t xttcpss_clock_event_interrupt(int irq, void *dev_id) |
117 | { | 120 | { |
118 | struct clock_event_device *evt = &xttcpss_clockevent; | 121 | struct xttcpss_timer_clockevent *xttce = dev_id; |
119 | struct xttcpss_timer *timer = dev_id; | 122 | struct xttcpss_timer *timer = &xttce->xttc; |
120 | 123 | ||
121 | /* Acknowledge the interrupt and call event handler */ | 124 | /* Acknowledge the interrupt and call event handler */ |
122 | __raw_writel(__raw_readl(timer->base_addr + XTTCPSS_ISR_OFFSET), | 125 | __raw_writel(__raw_readl(timer->base_addr + XTTCPSS_ISR_OFFSET), |
123 | timer->base_addr + XTTCPSS_ISR_OFFSET); | 126 | timer->base_addr + XTTCPSS_ISR_OFFSET); |
124 | 127 | ||
125 | evt->event_handler(evt); | 128 | xttce->ce.event_handler(&xttce->ce); |
126 | 129 | ||
127 | return IRQ_HANDLED; | 130 | return IRQ_HANDLED; |
128 | } | 131 | } |
129 | 132 | ||
130 | static struct irqaction event_timer_irq = { | ||
131 | .name = "xttcpss clockevent", | ||
132 | .flags = IRQF_DISABLED | IRQF_TIMER, | ||
133 | .handler = xttcpss_clock_event_interrupt, | ||
134 | }; | ||
135 | |||
136 | /** | 133 | /** |
137 | * xttcpss_timer_hardware_init - Initialize the timer hardware | 134 | * __xttc_clocksource_read - Reads the timer counter register |
138 | * | ||
139 | * Initialize the hardware to start the clock source, get the clock | ||
140 | * event timer ready to use, and hook up the interrupt. | ||
141 | **/ | ||
142 | static void __init xttcpss_timer_hardware_init(void) | ||
143 | { | ||
144 | /* Setup the clock source counter to be an incrementing counter | ||
145 | * with no interrupt and it rolls over at 0xFFFF. Pre-scale | ||
146 | it by 32 also. Let it start running now. | ||
147 | */ | ||
148 | timers[XTTCPSS_CLOCKSOURCE].base_addr = XTTCPSS_TIMER_BASE; | ||
149 | |||
150 | __raw_writel(0x0, timers[XTTCPSS_CLOCKSOURCE].base_addr + | ||
151 | XTTCPSS_IER_OFFSET); | ||
152 | __raw_writel(0x9, timers[XTTCPSS_CLOCKSOURCE].base_addr + | ||
153 | XTTCPSS_CLK_CNTRL_OFFSET); | ||
154 | __raw_writel(0x10, timers[XTTCPSS_CLOCKSOURCE].base_addr + | ||
155 | XTTCPSS_CNT_CNTRL_OFFSET); | ||
156 | |||
157 | /* Setup the clock event timer to be an interval timer which | ||
158 | * is prescaled by 32 using the interval interrupt. Leave it | ||
159 | * disabled for now. | ||
160 | */ | ||
161 | |||
162 | timers[XTTCPSS_CLOCKEVENT].base_addr = XTTCPSS_TIMER_BASE + 4; | ||
163 | |||
164 | __raw_writel(0x23, timers[XTTCPSS_CLOCKEVENT].base_addr + | ||
165 | XTTCPSS_CNT_CNTRL_OFFSET); | ||
166 | __raw_writel(0x9, timers[XTTCPSS_CLOCKEVENT].base_addr + | ||
167 | XTTCPSS_CLK_CNTRL_OFFSET); | ||
168 | __raw_writel(0x1, timers[XTTCPSS_CLOCKEVENT].base_addr + | ||
169 | XTTCPSS_IER_OFFSET); | ||
170 | |||
171 | /* Setup IRQ the clock event timer */ | ||
172 | event_timer_irq.dev_id = &timers[XTTCPSS_CLOCKEVENT]; | ||
173 | setup_irq(XTTCPCC_EVENT_TIMER_IRQ, &event_timer_irq); | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * __raw_readl_cycles - Reads the timer counter register | ||
178 | * | 135 | * |
179 | * returns: Current timer counter register value | 136 | * returns: Current timer counter register value |
180 | **/ | 137 | **/ |
181 | static cycle_t __raw_readl_cycles(struct clocksource *cs) | 138 | static cycle_t __xttc_clocksource_read(struct clocksource *cs) |
182 | { | 139 | { |
183 | struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKSOURCE]; | 140 | struct xttcpss_timer *timer = &to_xttcpss_timer_clksrc(cs)->xttc; |
184 | 141 | ||
185 | return (cycle_t)__raw_readl(timer->base_addr + | 142 | return (cycle_t)__raw_readl(timer->base_addr + |
186 | XTTCPSS_COUNT_VAL_OFFSET); | 143 | XTTCPSS_COUNT_VAL_OFFSET); |
187 | } | 144 | } |
188 | 145 | ||
189 | |||
190 | /* | ||
191 | * Instantiate and initialize the clock source structure | ||
192 | */ | ||
193 | static struct clocksource clocksource_xttcpss = { | ||
194 | .name = "xttcpss_timer1", | ||
195 | .rating = 200, /* Reasonable clock source */ | ||
196 | .read = __raw_readl_cycles, | ||
197 | .mask = CLOCKSOURCE_MASK(16), | ||
198 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
199 | }; | ||
200 | |||
201 | |||
202 | /** | 146 | /** |
203 | * xttcpss_set_next_event - Sets the time interval for next event | 147 | * xttcpss_set_next_event - Sets the time interval for next event |
204 | * | 148 | * |
@@ -210,7 +154,8 @@ static struct clocksource clocksource_xttcpss = { | |||
210 | static int xttcpss_set_next_event(unsigned long cycles, | 154 | static int xttcpss_set_next_event(unsigned long cycles, |
211 | struct clock_event_device *evt) | 155 | struct clock_event_device *evt) |
212 | { | 156 | { |
213 | struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKEVENT]; | 157 | struct xttcpss_timer_clockevent *xttce = to_xttcpss_timer_clkevent(evt); |
158 | struct xttcpss_timer *timer = &xttce->xttc; | ||
214 | 159 | ||
215 | xttcpss_set_interval(timer, cycles); | 160 | xttcpss_set_interval(timer, cycles); |
216 | return 0; | 161 | return 0; |
@@ -225,12 +170,15 @@ static int xttcpss_set_next_event(unsigned long cycles, | |||
225 | static void xttcpss_set_mode(enum clock_event_mode mode, | 170 | static void xttcpss_set_mode(enum clock_event_mode mode, |
226 | struct clock_event_device *evt) | 171 | struct clock_event_device *evt) |
227 | { | 172 | { |
228 | struct xttcpss_timer *timer = &timers[XTTCPSS_CLOCKEVENT]; | 173 | struct xttcpss_timer_clockevent *xttce = to_xttcpss_timer_clkevent(evt); |
174 | struct xttcpss_timer *timer = &xttce->xttc; | ||
229 | u32 ctrl_reg; | 175 | u32 ctrl_reg; |
230 | 176 | ||
231 | switch (mode) { | 177 | switch (mode) { |
232 | case CLOCK_EVT_MODE_PERIODIC: | 178 | case CLOCK_EVT_MODE_PERIODIC: |
233 | xttcpss_set_interval(timer, TIMER_RATE / HZ); | 179 | xttcpss_set_interval(timer, |
180 | DIV_ROUND_CLOSEST(clk_get_rate(xttce->clk), | ||
181 | PRESCALE * HZ)); | ||
234 | break; | 182 | break; |
235 | case CLOCK_EVT_MODE_ONESHOT: | 183 | case CLOCK_EVT_MODE_ONESHOT: |
236 | case CLOCK_EVT_MODE_UNUSED: | 184 | case CLOCK_EVT_MODE_UNUSED: |
@@ -251,15 +199,106 @@ static void xttcpss_set_mode(enum clock_event_mode mode, | |||
251 | } | 199 | } |
252 | } | 200 | } |
253 | 201 | ||
254 | /* | 202 | static void __init zynq_ttc_setup_clocksource(struct device_node *np, |
255 | * Instantiate and initialize the clock event structure | 203 | void __iomem *base) |
256 | */ | 204 | { |
257 | static struct clock_event_device xttcpss_clockevent = { | 205 | struct xttcpss_timer_clocksource *ttccs; |
258 | .name = "xttcpss_timer2", | 206 | struct clk *clk; |
259 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 207 | int err; |
260 | .set_next_event = xttcpss_set_next_event, | 208 | u32 reg; |
261 | .set_mode = xttcpss_set_mode, | 209 | |
262 | .rating = 200, | 210 | ttccs = kzalloc(sizeof(*ttccs), GFP_KERNEL); |
211 | if (WARN_ON(!ttccs)) | ||
212 | return; | ||
213 | |||
214 | err = of_property_read_u32(np, "reg", ®); | ||
215 | if (WARN_ON(err)) | ||
216 | return; | ||
217 | |||
218 | clk = of_clk_get_by_name(np, "cpu_1x"); | ||
219 | if (WARN_ON(IS_ERR(clk))) | ||
220 | return; | ||
221 | |||
222 | err = clk_prepare_enable(clk); | ||
223 | if (WARN_ON(err)) | ||
224 | return; | ||
225 | |||
226 | ttccs->xttc.base_addr = base + reg * 4; | ||
227 | |||
228 | ttccs->cs.name = np->name; | ||
229 | ttccs->cs.rating = 200; | ||
230 | ttccs->cs.read = __xttc_clocksource_read; | ||
231 | ttccs->cs.mask = CLOCKSOURCE_MASK(16); | ||
232 | ttccs->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS; | ||
233 | |||
234 | __raw_writel(0x0, ttccs->xttc.base_addr + XTTCPSS_IER_OFFSET); | ||
235 | __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, | ||
236 | ttccs->xttc.base_addr + XTTCPSS_CLK_CNTRL_OFFSET); | ||
237 | __raw_writel(CNT_CNTRL_RESET, | ||
238 | ttccs->xttc.base_addr + XTTCPSS_CNT_CNTRL_OFFSET); | ||
239 | |||
240 | err = clocksource_register_hz(&ttccs->cs, clk_get_rate(clk) / PRESCALE); | ||
241 | if (WARN_ON(err)) | ||
242 | return; | ||
243 | } | ||
244 | |||
245 | static void __init zynq_ttc_setup_clockevent(struct device_node *np, | ||
246 | void __iomem *base) | ||
247 | { | ||
248 | struct xttcpss_timer_clockevent *ttcce; | ||
249 | int err, irq; | ||
250 | u32 reg; | ||
251 | |||
252 | ttcce = kzalloc(sizeof(*ttcce), GFP_KERNEL); | ||
253 | if (WARN_ON(!ttcce)) | ||
254 | return; | ||
255 | |||
256 | err = of_property_read_u32(np, "reg", ®); | ||
257 | if (WARN_ON(err)) | ||
258 | return; | ||
259 | |||
260 | ttcce->xttc.base_addr = base + reg * 4; | ||
261 | |||
262 | ttcce->clk = of_clk_get_by_name(np, "cpu_1x"); | ||
263 | if (WARN_ON(IS_ERR(ttcce->clk))) | ||
264 | return; | ||
265 | |||
266 | err = clk_prepare_enable(ttcce->clk); | ||
267 | if (WARN_ON(err)) | ||
268 | return; | ||
269 | |||
270 | irq = irq_of_parse_and_map(np, 0); | ||
271 | if (WARN_ON(!irq)) | ||
272 | return; | ||
273 | |||
274 | ttcce->ce.name = np->name; | ||
275 | ttcce->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; | ||
276 | ttcce->ce.set_next_event = xttcpss_set_next_event; | ||
277 | ttcce->ce.set_mode = xttcpss_set_mode; | ||
278 | ttcce->ce.rating = 200; | ||
279 | ttcce->ce.irq = irq; | ||
280 | |||
281 | __raw_writel(0x23, ttcce->xttc.base_addr + XTTCPSS_CNT_CNTRL_OFFSET); | ||
282 | __raw_writel(CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN, | ||
283 | ttcce->xttc.base_addr + XTTCPSS_CLK_CNTRL_OFFSET); | ||
284 | __raw_writel(0x1, ttcce->xttc.base_addr + XTTCPSS_IER_OFFSET); | ||
285 | |||
286 | err = request_irq(irq, xttcpss_clock_event_interrupt, IRQF_TIMER, | ||
287 | np->name, ttcce); | ||
288 | if (WARN_ON(err)) | ||
289 | return; | ||
290 | |||
291 | clockevents_config_and_register(&ttcce->ce, | ||
292 | clk_get_rate(ttcce->clk) / PRESCALE, | ||
293 | 1, 0xfffe); | ||
294 | } | ||
295 | |||
296 | static const __initconst struct of_device_id zynq_ttc_match[] = { | ||
297 | { .compatible = "xlnx,ttc-counter-clocksource", | ||
298 | .data = zynq_ttc_setup_clocksource, }, | ||
299 | { .compatible = "xlnx,ttc-counter-clockevent", | ||
300 | .data = zynq_ttc_setup_clockevent, }, | ||
301 | {} | ||
263 | }; | 302 | }; |
264 | 303 | ||
265 | /** | 304 | /** |
@@ -270,21 +309,25 @@ static struct clock_event_device xttcpss_clockevent = { | |||
270 | **/ | 309 | **/ |
271 | void __init xttcpss_timer_init(void) | 310 | void __init xttcpss_timer_init(void) |
272 | { | 311 | { |
273 | xttcpss_timer_hardware_init(); | 312 | struct device_node *np; |
274 | clocksource_register_hz(&clocksource_xttcpss, TIMER_RATE); | 313 | |
275 | 314 | for_each_compatible_node(np, NULL, "xlnx,ttc") { | |
276 | /* Calculate the parameters to allow the clockevent to operate using | 315 | struct device_node *np_chld; |
277 | integer math | 316 | void __iomem *base; |
278 | */ | 317 | |
279 | clockevents_calc_mult_shift(&xttcpss_clockevent, TIMER_RATE, 4); | 318 | base = of_iomap(np, 0); |
280 | 319 | if (WARN_ON(!base)) | |
281 | xttcpss_clockevent.max_delta_ns = | 320 | return; |
282 | clockevent_delta2ns(0xfffe, &xttcpss_clockevent); | 321 | |
283 | xttcpss_clockevent.min_delta_ns = | 322 | for_each_available_child_of_node(np, np_chld) { |
284 | clockevent_delta2ns(1, &xttcpss_clockevent); | 323 | int (*cb)(struct device_node *np, void __iomem *base); |
285 | 324 | const struct of_device_id *match; | |
286 | /* Indicate that clock event is on 1st CPU as SMP boot needs it */ | 325 | |
287 | 326 | match = of_match_node(zynq_ttc_match, np_chld); | |
288 | xttcpss_clockevent.cpumask = cpumask_of(0); | 327 | if (match) { |
289 | clockevents_register_device(&xttcpss_clockevent); | 328 | cb = match->data; |
329 | cb(np_chld, base); | ||
330 | } | ||
331 | } | ||
332 | } | ||
290 | } | 333 | } |