]> git.karo-electronics.de Git - linux-beck.git/commitdiff
ARM: OMAP2+: clockdomain/hwmod: add workaround for EMU clockdomain idle problems
authorPaul Walmsley <paul@pwsan.com>
Sun, 23 Sep 2012 23:28:28 +0000 (17:28 -0600)
committerPaul Walmsley <paul@pwsan.com>
Sun, 23 Sep 2012 23:28:28 +0000 (17:28 -0600)
The idle status of the IP blocks and clocks inside the EMU clockdomain
isn't taken into account by the PRCM hardware when deciding whether
the clockdomain is idle.  Add a workaround flag in the clockdomain
code, CLKDM_MISSING_IDLE_REPORTING, to deal with this problem, and add
the code necessary to support it.

If CLKDM_MISSING_IDLE_REPORTING is set on a clockdomain, the
clockdomain will be forced active whenever an IP block inside that
clockdomain is in use, even if the clockdomain supports
hardware-supervised idle.  When the kernel indicates that the last
active IP block inside the clockdomain is no longer used, the
clockdomain will be forced idle, or, if that mode is not supported in
the hardware, it will be placed into hardware-supervised idle.

This patch is an equal collaboration with Jon Hunter
<jon-hunter@ti.com>.  Ming Lei <ming.lei@canonical.com>, Will Deacon
<will.deacon@arm.com>, Madhav Vij <mvij@ti.com>, Kevin Hilman
<khilman@ti.com>, Benoît Cousson <b-cousson@ti.com>, and Santosh
Shilimkar <santosh.shilimkar@ti.com> all made essential contributions
to the understanding of EMU clockdomain power management on OMAP.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Jon Hunter <jon-hunter@ti.com>
Cc: Ming Lei <ming.lei@canonical.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Madhav Vij <mvij@ti.com>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Benoît Cousson <b-cousson@ti.com>
Cc: Santosh Shilimkar <santosh.shilimkar@ti.com>
Tested-by: Jon Hunter <jon-hunter@ti.com>
arch/arm/mach-omap2/clockdomain.c
arch/arm/mach-omap2/clockdomain.h
arch/arm/mach-omap2/clockdomain2xxx_3xxx.c
arch/arm/mach-omap2/clockdomain44xx.c
arch/arm/mach-omap2/clockdomains3xxx_data.c
arch/arm/mach-omap2/clockdomains44xx_data.c
arch/arm/mach-omap2/omap_hwmod.c
arch/arm/mach-omap2/pm.c

index a1555627ad973048ea36f9dca72baa235dae52a3..cbb879139c51fd915c7ea50d00a6e91ad730d8b3 100644 (file)
@@ -899,6 +899,23 @@ bool clkdm_in_hwsup(struct clockdomain *clkdm)
        return ret;
 }
 
+/**
+ * clkdm_missing_idle_reporting - can @clkdm enter autoidle even if in use?
+ * @clkdm: struct clockdomain *
+ *
+ * Returns true if clockdomain @clkdm has the
+ * CLKDM_MISSING_IDLE_REPORTING flag set, or false if not or @clkdm is
+ * null.  More information is available in the documentation for the
+ * CLKDM_MISSING_IDLE_REPORTING macro.
+ */
+bool clkdm_missing_idle_reporting(struct clockdomain *clkdm)
+{
+       if (!clkdm)
+               return false;
+
+       return (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) ? true : false;
+}
+
 /* Clockdomain-to-clock/hwmod framework interface code */
 
 static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
index 5601dc13785ed606f0ce1bdf562472270489811a..629576be74445419fbc815fdfd8861cd2190943a 100644 (file)
@@ -1,9 +1,7 @@
 /*
- * arch/arm/plat-omap/include/mach/clockdomain.h
- *
  * OMAP2/3 clockdomain framework functions
  *
- * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008, 2012 Texas Instruments, Inc.
  * Copyright (C) 2008-2011 Nokia Corporation
  *
  * Paul Walmsley
  * CLKDM_ACTIVE_WITH_MPU: The PRCM guarantees that this clockdomain is
  *     active whenever the MPU is active.  True for interconnects and
  *     the WKUP clockdomains.
+ * CLKDM_MISSING_IDLE_REPORTING: The idle status of the IP blocks and
+ *     clocks inside this clockdomain are not taken into account by
+ *     the PRCM when determining whether the clockdomain is idle.
+ *     Without this flag, if the clockdomain is set to
+ *     hardware-supervised idle mode, the PRCM may transition the
+ *     enclosing powerdomain to a low power state, even when devices
+ *     inside the clockdomain and powerdomain are in use.  (An example
+ *     of such a clockdomain is the EMU clockdomain on OMAP3/4.)  If
+ *     this flag is set, and the clockdomain does not support the
+ *     force-sleep mode, then the HW_AUTO mode will be used to put the
+ *     clockdomain to sleep.  Similarly, if the clockdomain supports
+ *     the force-wakeup mode, then it will be used whenever a clock or
+ *     IP block inside the clockdomain is active, rather than the
+ *     HW_AUTO mode.
  */
 #define CLKDM_CAN_FORCE_SLEEP                  (1 << 0)
 #define CLKDM_CAN_FORCE_WAKEUP                 (1 << 1)
@@ -41,6 +53,7 @@
 #define CLKDM_CAN_DISABLE_AUTO                 (1 << 3)
 #define CLKDM_NO_AUTODEPS                      (1 << 4)
 #define CLKDM_ACTIVE_WITH_MPU                  (1 << 5)
+#define CLKDM_MISSING_IDLE_REPORTING           (1 << 6)
 
 #define CLKDM_CAN_HWSUP                (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_DISABLE_AUTO)
 #define CLKDM_CAN_SWSUP                (CLKDM_CAN_FORCE_SLEEP | CLKDM_CAN_FORCE_WAKEUP)
@@ -187,6 +200,7 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm);
 void clkdm_allow_idle(struct clockdomain *clkdm);
 void clkdm_deny_idle(struct clockdomain *clkdm);
 bool clkdm_in_hwsup(struct clockdomain *clkdm);
+bool clkdm_missing_idle_reporting(struct clockdomain *clkdm);
 
 int clkdm_wakeup(struct clockdomain *clkdm);
 int clkdm_sleep(struct clockdomain *clkdm);
index f99e65cfb86223c77ed544d1a8fbe6a0c4ad4b51..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,20 +244,6 @@ static int omap2_clkdm_clk_disable(struct clockdomain *clkdm)
        return 0;
 }
 
-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 void omap3_clkdm_allow_idle(struct clockdomain *clkdm)
 {
        if (atomic_read(&clkdm->usecount) > 0)
index 762f2cc542cec0badf1316a5ae9144426892a54b..6fc6155625bc2473afbf1f31ad7b922a836019bb 100644 (file)
@@ -113,6 +113,17 @@ static int omap4_clkdm_clk_disable(struct clockdomain *clkdm)
        if (!clkdm->prcm_partition)
                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)) {
+               omap4_clkdm_allow_idle(clkdm);
+               return 0;
+       }
+
        hwsup = omap4_cminst_is_clkdm_in_hwsup(clkdm->prcm_partition,
                                        clkdm->cm_inst, clkdm->clkdm_offs);
 
index 56089c49142a9e810a11ca0e60bbcf62f0fae7da..933a35cd124a33a1184639054afba1d582623fc0 100644 (file)
@@ -387,14 +387,11 @@ static struct clockdomain per_am35x_clkdm = {
        .clktrctrl_mask = OMAP3430_CLKTRCTRL_PER_MASK,
 };
 
-/*
- * Disable hw supervised mode for emu_clkdm, because emu_pwrdm is
- * switched of even if sdti is in use
- */
 static struct clockdomain emu_clkdm = {
        .name           = "emu_clkdm",
        .pwrdm          = { .name = "emu_pwrdm" },
-       .flags          = /* CLKDM_CAN_ENABLE_AUTO |  */CLKDM_CAN_SWSUP,
+       .flags          = (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_SWSUP |
+                          CLKDM_MISSING_IDLE_REPORTING),
        .clktrctrl_mask = OMAP3430_CLKTRCTRL_EMU_MASK,
 };
 
index 63d60a773d3b2e51e7e65bd6131a207871daa0dc..b56d06b48782a8608f77c43f9902972c1cb24bd5 100644 (file)
@@ -390,7 +390,8 @@ static struct clockdomain emu_sys_44xx_clkdm = {
        .prcm_partition   = OMAP4430_PRM_PARTITION,
        .cm_inst          = OMAP4430_PRM_EMU_CM_INST,
        .clkdm_offs       = OMAP4430_PRM_EMU_CM_EMU_CDOFFS,
-       .flags            = CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_FORCE_WAKEUP,
+       .flags            = (CLKDM_CAN_ENABLE_AUTO | CLKDM_CAN_FORCE_WAKEUP |
+                            CLKDM_MISSING_IDLE_REPORTING),
 };
 
 static struct clockdomain l3_dma_44xx_clkdm = {
index 21299879d2d55a540821c92cede8b0111706e986..6af64bbd9e1dd0cebe28f9de78dc7a63da4ef160 100644 (file)
@@ -2007,7 +2007,8 @@ static int _enable(struct omap_hwmod *oh)
                 * completely the module. The clockdomain can be set
                 * in HW_AUTO only when the module become ready.
                 */
-               hwsup = clkdm_in_hwsup(oh->clkdm);
+               hwsup = clkdm_in_hwsup(oh->clkdm) &&
+                       !clkdm_missing_idle_reporting(oh->clkdm);
                r = clkdm_hwmod_enable(oh->clkdm, oh);
                if (r) {
                        WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
index f6f05b67f86a01f1f5510a53c6f48af8221c5f51..abefbc4d8e0b9bd452c54af22d3a09e7cd172af9 100644 (file)
@@ -80,7 +80,8 @@ static void __init omap2_init_processor_devices(void)
 
 int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused)
 {
-       if (clkdm->flags & CLKDM_CAN_ENABLE_AUTO)
+       if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) &&
+           !(clkdm->flags & CLKDM_MISSING_IDLE_REPORTING))
                clkdm_allow_idle(clkdm);
        else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
                 atomic_read(&clkdm->usecount) == 0)