ARM: OMAP2+: clockdomain/hwmod: add workaround for EMU clockdomain idle problems
[deliverable/linux.git] / arch / arm / mach-omap2 / clockdomain2xxx_3xxx.c
index a0d68dbecfa3bb96cd52226b0f8d7965379d1252..9a7792aec6730f498a642810073888ad6b12df38 100644 (file)
@@ -162,6 +162,19 @@ static void _disable_hwsup(struct clockdomain *clkdm)
                                                clkdm->clktrctrl_mask);
 }
 
+static int omap3_clkdm_sleep(struct clockdomain *clkdm)
+{
+       omap3xxx_cm_clkdm_force_sleep(clkdm->pwrdm.ptr->prcm_offs,
+                               clkdm->clktrctrl_mask);
+       return 0;
+}
+
+static int omap3_clkdm_wakeup(struct clockdomain *clkdm)
+{
+       omap3xxx_cm_clkdm_force_wakeup(clkdm->pwrdm.ptr->prcm_offs,
+                               clkdm->clktrctrl_mask);
+       return 0;
+}
 
 static int omap2_clkdm_clk_enable(struct clockdomain *clkdm)
 {
@@ -170,6 +183,17 @@ static int omap2_clkdm_clk_enable(struct clockdomain *clkdm)
        if (!clkdm->clktrctrl_mask)
                return 0;
 
+       /*
+        * The CLKDM_MISSING_IDLE_REPORTING flag documentation has
+        * more details on the unpleasant problem this is working
+        * around
+        */
+       if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING &&
+           !(clkdm->flags & CLKDM_CAN_FORCE_SLEEP)) {
+               _enable_hwsup(clkdm);
+               return 0;
+       }
+
        hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                clkdm->clktrctrl_mask);
 
@@ -193,6 +217,17 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm)
        if (!clkdm->clktrctrl_mask)
                return 0;
 
+       /*
+        * The CLKDM_MISSING_IDLE_REPORTING flag documentation has
+        * more details on the unpleasant problem this is working
+        * around
+        */
+       if ((clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) &&
+           (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)) {
+               omap3_clkdm_wakeup(clkdm);
+               return 0;
+       }
+
        hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                clkdm->clktrctrl_mask);
 
@@ -209,36 +244,68 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm)
        return 0;
 }
 
-static int omap3_clkdm_sleep(struct clockdomain *clkdm)
+static void omap3_clkdm_allow_idle(struct clockdomain *clkdm)
 {
-       omap3xxx_cm_clkdm_force_sleep(clkdm->pwrdm.ptr->prcm_offs,
+       if (atomic_read(&clkdm->usecount) > 0)
+               _clkdm_add_autodeps(clkdm);
+
+       omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                clkdm->clktrctrl_mask);
-       return 0;
 }
 
-static int omap3_clkdm_wakeup(struct clockdomain *clkdm)
+static void omap3_clkdm_deny_idle(struct clockdomain *clkdm)
 {
-       omap3xxx_cm_clkdm_force_wakeup(clkdm->pwrdm.ptr->prcm_offs,
+       omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                clkdm->clktrctrl_mask);
-       return 0;
+
+       if (atomic_read(&clkdm->usecount) > 0)
+               _clkdm_del_autodeps(clkdm);
 }
 
-static void omap3_clkdm_allow_idle(struct clockdomain *clkdm)
+static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
 {
-       if (atomic_read(&clkdm->usecount) > 0)
-               _clkdm_add_autodeps(clkdm);
+       bool hwsup = false;
 
-       omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
+       if (!clkdm->clktrctrl_mask)
+               return 0;
+
+       hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                clkdm->clktrctrl_mask);
+
+       if (hwsup) {
+               /* Disable HW transitions when we are changing deps */
+               _disable_hwsup(clkdm);
+               _clkdm_add_autodeps(clkdm);
+               _enable_hwsup(clkdm);
+       } else {
+               if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
+                       omap3_clkdm_wakeup(clkdm);
+       }
+
+       return 0;
 }
 
-static void omap3_clkdm_deny_idle(struct clockdomain *clkdm)
+static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm)
 {
-       omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
+       bool hwsup = false;
+
+       if (!clkdm->clktrctrl_mask)
+               return 0;
+
+       hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
                                clkdm->clktrctrl_mask);
 
-       if (atomic_read(&clkdm->usecount) > 0)
+       if (hwsup) {
+               /* Disable HW transitions when we are changing deps */
+               _disable_hwsup(clkdm);
                _clkdm_del_autodeps(clkdm);
+               _enable_hwsup(clkdm);
+       } else {
+               if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
+                       omap3_clkdm_sleep(clkdm);
+       }
+
+       return 0;
 }
 
 struct clkdm_ops omap2_clkdm_operations = {
@@ -267,6 +334,6 @@ struct clkdm_ops omap3_clkdm_operations = {
        .clkdm_wakeup           = omap3_clkdm_wakeup,
        .clkdm_allow_idle       = omap3_clkdm_allow_idle,
        .clkdm_deny_idle        = omap3_clkdm_deny_idle,
-       .clkdm_clk_enable       = omap2_clkdm_clk_enable,
-       .clkdm_clk_disable      = omap2_clkdm_clk_disable,
+       .clkdm_clk_enable       = omap3xxx_clkdm_clk_enable,
+       .clkdm_clk_disable      = omap3xxx_clkdm_clk_disable,
 };
This page took 0.027462 seconds and 5 git commands to generate.