diff options
Diffstat (limited to 'arch/arm/mach-omap2/timer.c')
-rw-r--r-- | arch/arm/mach-omap2/timer.c | 140 |
1 files changed, 105 insertions, 35 deletions
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index c512bac69ec5..840929bd9dae 100644 --- a/arch/arm/mach-omap2/timer.c +++ b/arch/arm/mach-omap2/timer.c | |||
@@ -90,7 +90,7 @@ static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id) | |||
90 | } | 90 | } |
91 | 91 | ||
92 | static struct irqaction omap2_gp_timer_irq = { | 92 | static struct irqaction omap2_gp_timer_irq = { |
93 | .name = "gp timer", | 93 | .name = "gp_timer", |
94 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | 94 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, |
95 | .handler = omap2_gp_timer_interrupt, | 95 | .handler = omap2_gp_timer_interrupt, |
96 | }; | 96 | }; |
@@ -132,7 +132,7 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode, | |||
132 | } | 132 | } |
133 | 133 | ||
134 | static struct clock_event_device clockevent_gpt = { | 134 | static struct clock_event_device clockevent_gpt = { |
135 | .name = "gp timer", | 135 | .name = "gp_timer", |
136 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | 136 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, |
137 | .shift = 32, | 137 | .shift = 32, |
138 | .set_next_event = omap2_gp_timer_set_next_event, | 138 | .set_next_event = omap2_gp_timer_set_next_event, |
@@ -145,8 +145,10 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, | |||
145 | { | 145 | { |
146 | char name[10]; /* 10 = sizeof("gptXX_Xck0") */ | 146 | char name[10]; /* 10 = sizeof("gptXX_Xck0") */ |
147 | struct omap_hwmod *oh; | 147 | struct omap_hwmod *oh; |
148 | struct resource irq_rsrc, mem_rsrc; | ||
148 | size_t size; | 149 | size_t size; |
149 | int res = 0; | 150 | int res = 0; |
151 | int r; | ||
150 | 152 | ||
151 | sprintf(name, "timer%d", gptimer_id); | 153 | sprintf(name, "timer%d", gptimer_id); |
152 | omap_hwmod_setup_one(name); | 154 | omap_hwmod_setup_one(name); |
@@ -154,9 +156,16 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, | |||
154 | if (!oh) | 156 | if (!oh) |
155 | return -ENODEV; | 157 | return -ENODEV; |
156 | 158 | ||
157 | timer->irq = oh->mpu_irqs[0].irq; | 159 | r = omap_hwmod_get_resource_byname(oh, IORESOURCE_IRQ, NULL, &irq_rsrc); |
158 | timer->phys_base = oh->slaves[0]->addr->pa_start; | 160 | if (r) |
159 | size = oh->slaves[0]->addr->pa_end - timer->phys_base; | 161 | return -ENXIO; |
162 | timer->irq = irq_rsrc.start; | ||
163 | |||
164 | r = omap_hwmod_get_resource_byname(oh, IORESOURCE_MEM, NULL, &mem_rsrc); | ||
165 | if (r) | ||
166 | return -ENXIO; | ||
167 | timer->phys_base = mem_rsrc.start; | ||
168 | size = mem_rsrc.end - mem_rsrc.start; | ||
160 | 169 | ||
161 | /* Static mapping, never released */ | 170 | /* Static mapping, never released */ |
162 | timer->io_base = ioremap(timer->phys_base, size); | 171 | timer->io_base = ioremap(timer->phys_base, size); |
@@ -169,13 +178,6 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, | |||
169 | if (IS_ERR(timer->fclk)) | 178 | if (IS_ERR(timer->fclk)) |
170 | return -ENODEV; | 179 | return -ENODEV; |
171 | 180 | ||
172 | sprintf(name, "gpt%d_ick", gptimer_id); | ||
173 | timer->iclk = clk_get(NULL, name); | ||
174 | if (IS_ERR(timer->iclk)) { | ||
175 | clk_put(timer->fclk); | ||
176 | return -ENODEV; | ||
177 | } | ||
178 | |||
179 | omap_hwmod_enable(oh); | 181 | omap_hwmod_enable(oh); |
180 | 182 | ||
181 | sys_timer_reserved |= (1 << (gptimer_id - 1)); | 183 | sys_timer_reserved |= (1 << (gptimer_id - 1)); |
@@ -234,22 +236,8 @@ static void __init omap2_gp_clockevent_init(int gptimer_id, | |||
234 | } | 236 | } |
235 | 237 | ||
236 | /* Clocksource code */ | 238 | /* Clocksource code */ |
237 | |||
238 | #ifdef CONFIG_OMAP_32K_TIMER | ||
239 | /* | ||
240 | * When 32k-timer is enabled, don't use GPTimer for clocksource | ||
241 | * instead, just leave default clocksource which uses the 32k | ||
242 | * sync counter. See clocksource setup in plat-omap/counter_32k.c | ||
243 | */ | ||
244 | |||
245 | static void __init omap2_gp_clocksource_init(int unused, const char *dummy) | ||
246 | { | ||
247 | omap_init_clocksource_32k(); | ||
248 | } | ||
249 | |||
250 | #else | ||
251 | |||
252 | static struct omap_dm_timer clksrc; | 239 | static struct omap_dm_timer clksrc; |
240 | static bool use_gptimer_clksrc; | ||
253 | 241 | ||
254 | /* | 242 | /* |
255 | * clocksource | 243 | * clocksource |
@@ -260,7 +248,7 @@ static cycle_t clocksource_read_cycles(struct clocksource *cs) | |||
260 | } | 248 | } |
261 | 249 | ||
262 | static struct clocksource clocksource_gpt = { | 250 | static struct clocksource clocksource_gpt = { |
263 | .name = "gp timer", | 251 | .name = "gp_timer", |
264 | .rating = 300, | 252 | .rating = 300, |
265 | .read = clocksource_read_cycles, | 253 | .read = clocksource_read_cycles, |
266 | .mask = CLOCKSOURCE_MASK(32), | 254 | .mask = CLOCKSOURCE_MASK(32), |
@@ -276,7 +264,46 @@ static u32 notrace dmtimer_read_sched_clock(void) | |||
276 | } | 264 | } |
277 | 265 | ||
278 | /* Setup free-running counter for clocksource */ | 266 | /* Setup free-running counter for clocksource */ |
279 | static void __init omap2_gp_clocksource_init(int gptimer_id, | 267 | static int __init omap2_sync32k_clocksource_init(void) |
268 | { | ||
269 | int ret; | ||
270 | struct omap_hwmod *oh; | ||
271 | void __iomem *vbase; | ||
272 | const char *oh_name = "counter_32k"; | ||
273 | |||
274 | /* | ||
275 | * First check hwmod data is available for sync32k counter | ||
276 | */ | ||
277 | oh = omap_hwmod_lookup(oh_name); | ||
278 | if (!oh || oh->slaves_cnt == 0) | ||
279 | return -ENODEV; | ||
280 | |||
281 | omap_hwmod_setup_one(oh_name); | ||
282 | |||
283 | vbase = omap_hwmod_get_mpu_rt_va(oh); | ||
284 | if (!vbase) { | ||
285 | pr_warn("%s: failed to get counter_32k resource\n", __func__); | ||
286 | return -ENXIO; | ||
287 | } | ||
288 | |||
289 | ret = omap_hwmod_enable(oh); | ||
290 | if (ret) { | ||
291 | pr_warn("%s: failed to enable counter_32k module (%d)\n", | ||
292 | __func__, ret); | ||
293 | return ret; | ||
294 | } | ||
295 | |||
296 | ret = omap_init_clocksource_32k(vbase); | ||
297 | if (ret) { | ||
298 | pr_warn("%s: failed to initialize counter_32k as a clocksource (%d)\n", | ||
299 | __func__, ret); | ||
300 | omap_hwmod_idle(oh); | ||
301 | } | ||
302 | |||
303 | return ret; | ||
304 | } | ||
305 | |||
306 | static void __init omap2_gptimer_clocksource_init(int gptimer_id, | ||
280 | const char *fck_source) | 307 | const char *fck_source) |
281 | { | 308 | { |
282 | int res; | 309 | int res; |
@@ -284,9 +311,6 @@ static void __init omap2_gp_clocksource_init(int gptimer_id, | |||
284 | res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source); | 311 | res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source); |
285 | BUG_ON(res); | 312 | BUG_ON(res); |
286 | 313 | ||
287 | pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n", | ||
288 | gptimer_id, clksrc.rate); | ||
289 | |||
290 | __omap_dm_timer_load_start(&clksrc, | 314 | __omap_dm_timer_load_start(&clksrc, |
291 | OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1); | 315 | OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1); |
292 | setup_sched_clock(dmtimer_read_sched_clock, 32, clksrc.rate); | 316 | setup_sched_clock(dmtimer_read_sched_clock, 32, clksrc.rate); |
@@ -294,15 +318,36 @@ static void __init omap2_gp_clocksource_init(int gptimer_id, | |||
294 | if (clocksource_register_hz(&clocksource_gpt, clksrc.rate)) | 318 | if (clocksource_register_hz(&clocksource_gpt, clksrc.rate)) |
295 | pr_err("Could not register clocksource %s\n", | 319 | pr_err("Could not register clocksource %s\n", |
296 | clocksource_gpt.name); | 320 | clocksource_gpt.name); |
321 | else | ||
322 | pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n", | ||
323 | gptimer_id, clksrc.rate); | ||
324 | } | ||
325 | |||
326 | static void __init omap2_clocksource_init(int gptimer_id, | ||
327 | const char *fck_source) | ||
328 | { | ||
329 | /* | ||
330 | * First give preference to kernel parameter configuration | ||
331 | * by user (clocksource="gp_timer"). | ||
332 | * | ||
333 | * In case of missing kernel parameter for clocksource, | ||
334 | * first check for availability for 32k-sync timer, in case | ||
335 | * of failure in finding 32k_counter module or registering | ||
336 | * it as clocksource, execution will fallback to gp-timer. | ||
337 | */ | ||
338 | if (use_gptimer_clksrc == true) | ||
339 | omap2_gptimer_clocksource_init(gptimer_id, fck_source); | ||
340 | else if (omap2_sync32k_clocksource_init()) | ||
341 | /* Fall back to gp-timer code */ | ||
342 | omap2_gptimer_clocksource_init(gptimer_id, fck_source); | ||
297 | } | 343 | } |
298 | #endif | ||
299 | 344 | ||
300 | #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, \ | 345 | #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, \ |
301 | clksrc_nr, clksrc_src) \ | 346 | clksrc_nr, clksrc_src) \ |
302 | static void __init omap##name##_timer_init(void) \ | 347 | static void __init omap##name##_timer_init(void) \ |
303 | { \ | 348 | { \ |
304 | omap2_gp_clockevent_init((clkev_nr), clkev_src); \ | 349 | omap2_gp_clockevent_init((clkev_nr), clkev_src); \ |
305 | omap2_gp_clocksource_init((clksrc_nr), clksrc_src); \ | 350 | omap2_clocksource_init((clksrc_nr), clksrc_src); \ |
306 | } | 351 | } |
307 | 352 | ||
308 | #define OMAP_SYS_TIMER(name) \ | 353 | #define OMAP_SYS_TIMER(name) \ |
@@ -333,7 +378,7 @@ static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, | |||
333 | static void __init omap4_timer_init(void) | 378 | static void __init omap4_timer_init(void) |
334 | { | 379 | { |
335 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); | 380 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); |
336 | omap2_gp_clocksource_init(2, OMAP4_MPU_SOURCE); | 381 | omap2_clocksource_init(2, OMAP4_MPU_SOURCE); |
337 | #ifdef CONFIG_LOCAL_TIMERS | 382 | #ifdef CONFIG_LOCAL_TIMERS |
338 | /* Local timers are not supprted on OMAP4430 ES1.0 */ | 383 | /* Local timers are not supprted on OMAP4430 ES1.0 */ |
339 | if (omap_rev() != OMAP4430_REV_ES1_0) { | 384 | if (omap_rev() != OMAP4430_REV_ES1_0) { |
@@ -501,3 +546,28 @@ static int __init omap2_dm_timer_init(void) | |||
501 | return 0; | 546 | return 0; |
502 | } | 547 | } |
503 | arch_initcall(omap2_dm_timer_init); | 548 | arch_initcall(omap2_dm_timer_init); |
549 | |||
550 | /** | ||
551 | * omap2_override_clocksource - clocksource override with user configuration | ||
552 | * | ||
553 | * Allows user to override default clocksource, using kernel parameter | ||
554 | * clocksource="gp_timer" (For all OMAP2PLUS architectures) | ||
555 | * | ||
556 | * Note that, here we are using same standard kernel parameter "clocksource=", | ||
557 | * and not introducing any OMAP specific interface. | ||
558 | */ | ||
559 | static int __init omap2_override_clocksource(char *str) | ||
560 | { | ||
561 | if (!str) | ||
562 | return 0; | ||
563 | /* | ||
564 | * For OMAP architecture, we only have two options | ||
565 | * - sync_32k (default) | ||
566 | * - gp_timer (sys_clk based) | ||
567 | */ | ||
568 | if (!strcmp(str, "gp_timer")) | ||
569 | use_gptimer_clksrc = true; | ||
570 | |||
571 | return 0; | ||
572 | } | ||
573 | early_param("clocksource", omap2_override_clocksource); | ||