ARM: OMAP2+: Fix external clock support for dmtimers
[linux-2.6-block.git] / arch / arm / mach-omap2 / timer.c
index 1b7835865c83b59c1e0ca82d7611ef239b3f8211..8c046d9d8ae899374f3dd8d67270f5945fb59631 100644 (file)
 #define OMAP3_SECURE_TIMER     1
 #endif
 
-/* MAX_GPTIMER_ID: number of GPTIMERs on the chip */
-#define MAX_GPTIMER_ID         12
-
-static u32 sys_timer_reserved;
-
 /* Clockevent code */
 
 static struct omap_dm_timer clkev;
@@ -90,7 +85,7 @@ static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id)
 }
 
 static struct irqaction omap2_gp_timer_irq = {
-       .name           = "gp timer",
+       .name           = "gp_timer",
        .flags          = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
        .handler        = omap2_gp_timer_interrupt,
 };
@@ -132,7 +127,7 @@ static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
 }
 
 static struct clock_event_device clockevent_gpt = {
-       .name           = "gp timer",
+       .name           = "gp_timer",
        .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
        .shift          = 32,
        .set_next_event = omap2_gp_timer_set_next_event,
@@ -180,7 +175,8 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
 
        omap_hwmod_enable(oh);
 
-       sys_timer_reserved |= (1 << (gptimer_id - 1));
+       if (omap_dm_timer_reserve_systimer(gptimer_id))
+               return -ENODEV;
 
        if (gptimer_id != 12) {
                struct clk *src;
@@ -236,22 +232,8 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
 }
 
 /* Clocksource code */
-
-#ifdef CONFIG_OMAP_32K_TIMER
-/*
- * When 32k-timer is enabled, don't use GPTimer for clocksource
- * instead, just leave default clocksource which uses the 32k
- * sync counter.  See clocksource setup in plat-omap/counter_32k.c
- */
-
-static void __init omap2_gp_clocksource_init(int unused, const char *dummy)
-{
-       omap_init_clocksource_32k();
-}
-
-#else
-
 static struct omap_dm_timer clksrc;
+static bool use_gptimer_clksrc;
 
 /*
  * clocksource
@@ -262,7 +244,7 @@ static cycle_t clocksource_read_cycles(struct clocksource *cs)
 }
 
 static struct clocksource clocksource_gpt = {
-       .name           = "gp timer",
+       .name           = "gp_timer",
        .rating         = 300,
        .read           = clocksource_read_cycles,
        .mask           = CLOCKSOURCE_MASK(32),
@@ -278,7 +260,46 @@ static u32 notrace dmtimer_read_sched_clock(void)
 }
 
 /* Setup free-running counter for clocksource */
-static void __init omap2_gp_clocksource_init(int gptimer_id,
+static int __init omap2_sync32k_clocksource_init(void)
+{
+       int ret;
+       struct omap_hwmod *oh;
+       void __iomem *vbase;
+       const char *oh_name = "counter_32k";
+
+       /*
+        * First check hwmod data is available for sync32k counter
+        */
+       oh = omap_hwmod_lookup(oh_name);
+       if (!oh || oh->slaves_cnt == 0)
+               return -ENODEV;
+
+       omap_hwmod_setup_one(oh_name);
+
+       vbase = omap_hwmod_get_mpu_rt_va(oh);
+       if (!vbase) {
+               pr_warn("%s: failed to get counter_32k resource\n", __func__);
+               return -ENXIO;
+       }
+
+       ret = omap_hwmod_enable(oh);
+       if (ret) {
+               pr_warn("%s: failed to enable counter_32k module (%d)\n",
+                                                       __func__, ret);
+               return ret;
+       }
+
+       ret = omap_init_clocksource_32k(vbase);
+       if (ret) {
+               pr_warn("%s: failed to initialize counter_32k as a clocksource (%d)\n",
+                                                       __func__, ret);
+               omap_hwmod_idle(oh);
+       }
+
+       return ret;
+}
+
+static void __init omap2_gptimer_clocksource_init(int gptimer_id,
                                                const char *fck_source)
 {
        int res;
@@ -286,9 +307,6 @@ static void __init omap2_gp_clocksource_init(int gptimer_id,
        res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source);
        BUG_ON(res);
 
-       pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n",
-               gptimer_id, clksrc.rate);
-
        __omap_dm_timer_load_start(&clksrc,
                        OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
        setup_sched_clock(dmtimer_read_sched_clock, 32, clksrc.rate);
@@ -296,15 +314,36 @@ static void __init omap2_gp_clocksource_init(int gptimer_id,
        if (clocksource_register_hz(&clocksource_gpt, clksrc.rate))
                pr_err("Could not register clocksource %s\n",
                        clocksource_gpt.name);
+       else
+               pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n",
+                       gptimer_id, clksrc.rate);
+}
+
+static void __init omap2_clocksource_init(int gptimer_id,
+                                               const char *fck_source)
+{
+       /*
+        * First give preference to kernel parameter configuration
+        * by user (clocksource="gp_timer").
+        *
+        * In case of missing kernel parameter for clocksource,
+        * first check for availability for 32k-sync timer, in case
+        * of failure in finding 32k_counter module or registering
+        * it as clocksource, execution will fallback to gp-timer.
+        */
+       if (use_gptimer_clksrc == true)
+               omap2_gptimer_clocksource_init(gptimer_id, fck_source);
+       else if (omap2_sync32k_clocksource_init())
+               /* Fall back to gp-timer code */
+               omap2_gptimer_clocksource_init(gptimer_id, fck_source);
 }
-#endif
 
 #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src,                 \
                                clksrc_nr, clksrc_src)                  \
 static void __init omap##name##_timer_init(void)                       \
 {                                                                      \
        omap2_gp_clockevent_init((clkev_nr), clkev_src);                \
-       omap2_gp_clocksource_init((clksrc_nr), clksrc_src);             \
+       omap2_clocksource_init((clksrc_nr), clksrc_src);                \
 }
 
 #define OMAP_SYS_TIMER(name)                                           \
@@ -335,7 +374,7 @@ static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
 static void __init omap4_timer_init(void)
 {
        omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE);
-       omap2_gp_clocksource_init(2, OMAP4_MPU_SOURCE);
+       omap2_clocksource_init(2, OMAP4_MPU_SOURCE);
 #ifdef CONFIG_LOCAL_TIMERS
        /* Local timers are not supprted on OMAP4430 ES1.0 */
        if (omap_rev() != OMAP4430_REV_ES1_0) {
@@ -358,7 +397,6 @@ OMAP_SYS_TIMER(4)
 static int omap2_dm_timer_set_src(struct platform_device *pdev, int source)
 {
        int ret;
-       struct dmtimer_platform_data *pdata = pdev->dev.platform_data;
        struct clk *fclk, *parent;
        char *parent_name = NULL;
 
@@ -379,14 +417,8 @@ static int omap2_dm_timer_set_src(struct platform_device *pdev, int source)
                break;
 
        case OMAP_TIMER_SRC_EXT_CLK:
-               if (pdata->timer_ip_version == OMAP_TIMER_IP_VERSION_1) {
-                       parent_name = "alt_ck";
-                       break;
-               }
-               dev_err(&pdev->dev, "%s: %d: invalid clk src.\n",
-                       __func__, __LINE__);
-               clk_put(fclk);
-               return -EINVAL;
+               parent_name = "alt_ck";
+               break;
        }
 
        parent = clk_get(&pdev->dev, parent_name);
@@ -459,11 +491,9 @@ static int __init omap_timer_init(struct omap_hwmod *oh, void *unused)
        sscanf(oh->name, "timer%2d", &id);
 
        pdata->set_timer_src = omap2_dm_timer_set_src;
-       pdata->timer_ip_version = oh->class->rev;
 
-       /* Mark clocksource and clockevent timers as reserved */
-       if ((sys_timer_reserved >> (id - 1)) & 0x1)
-               pdata->reserved = 1;
+       if (timer_dev_attr)
+               pdata->timer_capability = timer_dev_attr->timer_capability;
 
        pwrdm = omap_hwmod_get_pwrdm(oh);
        pdata->loses_context = pwrdm_can_ever_lose_context(pwrdm);
@@ -503,3 +533,28 @@ static int __init omap2_dm_timer_init(void)
        return 0;
 }
 arch_initcall(omap2_dm_timer_init);
+
+/**
+ * omap2_override_clocksource - clocksource override with user configuration
+ *
+ * Allows user to override default clocksource, using kernel parameter
+ *   clocksource="gp_timer"    (For all OMAP2PLUS architectures)
+ *
+ * Note that, here we are using same standard kernel parameter "clocksource=",
+ * and not introducing any OMAP specific interface.
+ */
+static int __init omap2_override_clocksource(char *str)
+{
+       if (!str)
+               return 0;
+       /*
+        * For OMAP architecture, we only have two options
+        *    - sync_32k (default)
+        *    - gp_timer (sys_clk based)
+        */
+       if (!strcmp(str, "gp_timer"))
+               use_gptimer_clksrc = true;
+
+       return 0;
+}
+early_param("clocksource", omap2_override_clocksource);