diff options
Diffstat (limited to 'arch/arm/mach-omap2/timer.c')
-rw-r--r-- | arch/arm/mach-omap2/timer.c | 118 |
1 files changed, 93 insertions, 25 deletions
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index 1b7835865c83..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, |
@@ -236,22 +236,8 @@ static void __init omap2_gp_clockevent_init(int gptimer_id, | |||
236 | } | 236 | } |
237 | 237 | ||
238 | /* Clocksource code */ | 238 | /* Clocksource code */ |
239 | |||
240 | #ifdef CONFIG_OMAP_32K_TIMER | ||
241 | /* | ||
242 | * When 32k-timer is enabled, don't use GPTimer for clocksource | ||
243 | * instead, just leave default clocksource which uses the 32k | ||
244 | * sync counter. See clocksource setup in plat-omap/counter_32k.c | ||
245 | */ | ||
246 | |||
247 | static void __init omap2_gp_clocksource_init(int unused, const char *dummy) | ||
248 | { | ||
249 | omap_init_clocksource_32k(); | ||
250 | } | ||
251 | |||
252 | #else | ||
253 | |||
254 | static struct omap_dm_timer clksrc; | 239 | static struct omap_dm_timer clksrc; |
240 | static bool use_gptimer_clksrc; | ||
255 | 241 | ||
256 | /* | 242 | /* |
257 | * clocksource | 243 | * clocksource |
@@ -262,7 +248,7 @@ static cycle_t clocksource_read_cycles(struct clocksource *cs) | |||
262 | } | 248 | } |
263 | 249 | ||
264 | static struct clocksource clocksource_gpt = { | 250 | static struct clocksource clocksource_gpt = { |
265 | .name = "gp timer", | 251 | .name = "gp_timer", |
266 | .rating = 300, | 252 | .rating = 300, |
267 | .read = clocksource_read_cycles, | 253 | .read = clocksource_read_cycles, |
268 | .mask = CLOCKSOURCE_MASK(32), | 254 | .mask = CLOCKSOURCE_MASK(32), |
@@ -278,7 +264,46 @@ static u32 notrace dmtimer_read_sched_clock(void) | |||
278 | } | 264 | } |
279 | 265 | ||
280 | /* Setup free-running counter for clocksource */ | 266 | /* Setup free-running counter for clocksource */ |
281 | 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, | ||
282 | const char *fck_source) | 307 | const char *fck_source) |
283 | { | 308 | { |
284 | int res; | 309 | int res; |
@@ -286,9 +311,6 @@ static void __init omap2_gp_clocksource_init(int gptimer_id, | |||
286 | res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source); | 311 | res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source); |
287 | BUG_ON(res); | 312 | BUG_ON(res); |
288 | 313 | ||
289 | pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n", | ||
290 | gptimer_id, clksrc.rate); | ||
291 | |||
292 | __omap_dm_timer_load_start(&clksrc, | 314 | __omap_dm_timer_load_start(&clksrc, |
293 | OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1); | 315 | OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1); |
294 | setup_sched_clock(dmtimer_read_sched_clock, 32, clksrc.rate); | 316 | setup_sched_clock(dmtimer_read_sched_clock, 32, clksrc.rate); |
@@ -296,15 +318,36 @@ static void __init omap2_gp_clocksource_init(int gptimer_id, | |||
296 | if (clocksource_register_hz(&clocksource_gpt, clksrc.rate)) | 318 | if (clocksource_register_hz(&clocksource_gpt, clksrc.rate)) |
297 | pr_err("Could not register clocksource %s\n", | 319 | pr_err("Could not register clocksource %s\n", |
298 | 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); | ||
299 | } | 343 | } |
300 | #endif | ||
301 | 344 | ||
302 | #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, \ | 345 | #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, \ |
303 | clksrc_nr, clksrc_src) \ | 346 | clksrc_nr, clksrc_src) \ |
304 | static void __init omap##name##_timer_init(void) \ | 347 | static void __init omap##name##_timer_init(void) \ |
305 | { \ | 348 | { \ |
306 | omap2_gp_clockevent_init((clkev_nr), clkev_src); \ | 349 | omap2_gp_clockevent_init((clkev_nr), clkev_src); \ |
307 | omap2_gp_clocksource_init((clksrc_nr), clksrc_src); \ | 350 | omap2_clocksource_init((clksrc_nr), clksrc_src); \ |
308 | } | 351 | } |
309 | 352 | ||
310 | #define OMAP_SYS_TIMER(name) \ | 353 | #define OMAP_SYS_TIMER(name) \ |
@@ -335,7 +378,7 @@ static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, | |||
335 | static void __init omap4_timer_init(void) | 378 | static void __init omap4_timer_init(void) |
336 | { | 379 | { |
337 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); | 380 | omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); |
338 | omap2_gp_clocksource_init(2, OMAP4_MPU_SOURCE); | 381 | omap2_clocksource_init(2, OMAP4_MPU_SOURCE); |
339 | #ifdef CONFIG_LOCAL_TIMERS | 382 | #ifdef CONFIG_LOCAL_TIMERS |
340 | /* Local timers are not supprted on OMAP4430 ES1.0 */ | 383 | /* Local timers are not supprted on OMAP4430 ES1.0 */ |
341 | if (omap_rev() != OMAP4430_REV_ES1_0) { | 384 | if (omap_rev() != OMAP4430_REV_ES1_0) { |
@@ -503,3 +546,28 @@ static int __init omap2_dm_timer_init(void) | |||
503 | return 0; | 546 | return 0; |
504 | } | 547 | } |
505 | 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); | ||