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); | ||
