]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ARM: OMAP2+: clockdomain: add usecounting support to autoidle APIs
authorTero Kristo <t-kristo@ti.com>
Thu, 30 Jun 2016 13:15:02 +0000 (16:15 +0300)
committerTony Lindgren <tony@atomide.com>
Mon, 4 Jul 2016 14:15:38 +0000 (07:15 -0700)
The previous implementation was racy in many locations, where the current
status of the clockdomain was read out, some operations were executed,
and the previous status info was used afterwards to decide next state
for the clockdomain. Instead, fix the implementation of the allow_idle /
deny_idle APIs to properly have usecounting support. This allows clean
handling internally within the clockdomain core, and simplifies the
usage also within hwmod.

Signed-off-by: Tero Kristo <t-kristo@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
arch/arm/mach-omap2/clockdomain.c
arch/arm/mach-omap2/clockdomain.h
arch/arm/mach-omap2/cpuidle44xx.c
arch/arm/mach-omap2/omap-smp.c
arch/arm/mach-omap2/omap_hwmod.c
arch/arm/mach-omap2/pm.c
arch/arm/mach-omap2/powerdomain.c

index 2da3b5ec010c97764da805368900841f83175744..b79b1ca9aee9edbd96099495e0c6165089c0c01e 100644 (file)
@@ -465,10 +465,7 @@ int clkdm_complete_init(void)
                return -EACCES;
 
        list_for_each_entry(clkdm, &clkdm_list, node) {
-               if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
-                       clkdm_wakeup(clkdm);
-               else if (clkdm->flags & CLKDM_CAN_DISABLE_AUTO)
-                       clkdm_deny_idle(clkdm);
+               clkdm_deny_idle(clkdm);
 
                _resolve_clkdm_deps(clkdm, clkdm->wkdep_srcs);
                clkdm_clear_all_wkdeps(clkdm);
@@ -925,11 +922,20 @@ void clkdm_allow_idle_nolock(struct clockdomain *clkdm)
        if (!clkdm)
                return;
 
-       if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
-               pr_debug("clock: %s: automatic idle transitions cannot be enabled\n",
-                        clkdm->name);
+       if (!WARN_ON(!clkdm->forcewake_count))
+               clkdm->forcewake_count--;
+
+       if (clkdm->forcewake_count)
+               return;
+
+       if (!clkdm->usecount && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
+               clkdm_sleep_nolock(clkdm);
+
+       if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO))
+               return;
+
+       if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
                return;
-       }
 
        if (!arch_clkdm || !arch_clkdm->clkdm_allow_idle)
                return;
@@ -974,11 +980,17 @@ void clkdm_deny_idle_nolock(struct clockdomain *clkdm)
        if (!clkdm)
                return;
 
-       if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
-               pr_debug("clockdomain: %s: automatic idle transitions cannot be disabled\n",
-                        clkdm->name);
+       if (clkdm->forcewake_count++)
+               return;
+
+       if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
+               clkdm_wakeup_nolock(clkdm);
+
+       if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO))
+               return;
+
+       if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
                return;
-       }
 
        if (!arch_clkdm || !arch_clkdm->clkdm_deny_idle)
                return;
index 2c398ce1a0f27a628ee882f0cda0f96d66458c3b..24667a5a9dc0f487759b3635eff014517e8533c8 100644 (file)
@@ -114,6 +114,7 @@ struct omap_hwmod;
  * @wkdep_srcs: Clockdomains that can be told to wake this powerdomain up
  * @sleepdep_srcs: Clockdomains that can be told to keep this clkdm from inact
  * @usecount: Usecount tracking
+ * @forcewake_count: Usecount for forcing the domain active
  * @node: list_head to link all clockdomains together
  *
  * @prcm_partition should be a macro from mach-omap2/prcm44xx.h (OMAP4 only)
@@ -138,6 +139,7 @@ struct clockdomain {
        struct clkdm_dep *wkdep_srcs;
        struct clkdm_dep *sleepdep_srcs;
        int usecount;
+       int forcewake_count;
        struct list_head node;
 };
 
index 4b8e9f4d59eaddff4aebc18c4b404e9206bd41db..fa138d4032b6c2ed16389ece0e83c7a4e0b2b696 100644 (file)
@@ -140,7 +140,7 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
                    mpuss_can_lose_context)
                        gic_dist_disable();
 
-               clkdm_wakeup(cpu_clkdm[1]);
+               clkdm_deny_idle(cpu_clkdm[1]);
                omap_set_pwrdm_state(cpu_pd[1], PWRDM_POWER_ON);
                clkdm_allow_idle(cpu_clkdm[1]);
 
index d53a0def2d6c8517caadd1cc81258a8adf8cd520..b4de3da6dffa5e0593dad0ca305895fc9c341e6a 100644 (file)
@@ -200,7 +200,7 @@ static int omap4_boot_secondary(unsigned int cpu, struct task_struct *idle)
                 * Ensure that CPU power state is set to ON to avoid CPU
                 * powerdomain transition on wfi
                 */
-               clkdm_wakeup_nolock(cpu1_clkdm);
+               clkdm_deny_idle_nolock(cpu1_clkdm);
                pwrdm_set_next_pwrst(cpu1_pwrdm, PWRDM_POWER_ON);
                clkdm_allow_idle_nolock(cpu1_clkdm);
 
index 201da9158d7b60ff1d0f9b201784a492b8b19071..5b709383381c8ae0bdafe014253537804043d97d 100644 (file)
@@ -1702,7 +1702,6 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
 {
        struct omap_hwmod_rst_info ohri;
        int ret = -EINVAL;
-       int hwsup = 0;
 
        if (!oh)
                return -EINVAL;
@@ -1720,7 +1719,7 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
                 * might not be completed. The clockdomain can be set
                 * in HW_AUTO only when the module become ready.
                 */
-               hwsup = clkdm_in_hwsup(oh->clkdm);
+               clkdm_deny_idle(oh->clkdm);
                ret = clkdm_hwmod_enable(oh->clkdm, oh);
                if (ret) {
                        WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
@@ -1747,8 +1746,7 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
                 * Set the clockdomain to HW_AUTO, assuming that the
                 * previous state was HW_AUTO.
                 */
-               if (hwsup)
-                       clkdm_allow_idle(oh->clkdm);
+               clkdm_allow_idle(oh->clkdm);
 
                clkdm_hwmod_disable(oh->clkdm, oh);
        }
@@ -2102,7 +2100,6 @@ static int _enable_preprogram(struct omap_hwmod *oh)
 static int _enable(struct omap_hwmod *oh)
 {
        int r;
-       int hwsup = 0;
 
        pr_debug("omap_hwmod: %s: enabling\n", oh->name);
 
@@ -2162,8 +2159,7 @@ 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) &&
-                       !clkdm_missing_idle_reporting(oh->clkdm);
+               clkdm_deny_idle(oh->clkdm);
                r = clkdm_hwmod_enable(oh->clkdm, oh);
                if (r) {
                        WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
@@ -2183,14 +2179,10 @@ static int _enable(struct omap_hwmod *oh)
 
        r = (soc_ops.wait_target_ready) ? soc_ops.wait_target_ready(oh) :
                -EINVAL;
-       if (!r) {
-               /*
-                * Set the clockdomain to HW_AUTO only if the target is ready,
-                * assuming that the previous state was HW_AUTO
-                */
-               if (oh->clkdm && hwsup)
-                       clkdm_allow_idle(oh->clkdm);
+       if (oh->clkdm)
+               clkdm_allow_idle(oh->clkdm);
 
+       if (!r) {
                oh->_state = _HWMOD_STATE_ENABLED;
 
                /* Access the sysconfig only if the target is ready */
@@ -2244,6 +2236,9 @@ static int _idle(struct omap_hwmod *oh)
                _idle_sysc(oh);
        _del_initiator_dep(oh, mpu_oh);
 
+       if (oh->clkdm)
+               clkdm_deny_idle(oh->clkdm);
+
        if (oh->flags & HWMOD_BLOCK_WFI)
                cpu_idle_poll_ctrl(false);
        if (soc_ops.disable_module)
@@ -2256,8 +2251,10 @@ static int _idle(struct omap_hwmod *oh)
         * transition to complete properly.
         */
        _disable_clocks(oh);
-       if (oh->clkdm)
+       if (oh->clkdm) {
+               clkdm_allow_idle(oh->clkdm);
                clkdm_hwmod_disable(oh->clkdm, oh);
+       }
 
        /* Mux pins for device idle if populated */
        if (oh->mux && oh->mux->pads_dynamic) {
index 2f7b11da7d5d9ce23e0399aaf240d4ef73edacf1..678d2a31dcb89d0b409e4a08039e391cb59179a8 100644 (file)
@@ -110,13 +110,7 @@ static void __init omap2_init_processor_devices(void)
 
 int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused)
 {
-       /* XXX The usecount test is racy */
-       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 &&
-                clkdm->usecount == 0)
-               clkdm_sleep(clkdm);
+       clkdm_allow_idle(clkdm);
        return 0;
 }
 
index 78af6d8cf2e208811f2d6b31e1aa837c3b585565..be7a9761ab3f8fb1f8046bbc12f9f46d05e42a90 100644 (file)
@@ -222,7 +222,6 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
  * @pwrdm: struct powerdomain * to operate on
  * @curr_pwrst: current power state of @pwrdm
  * @pwrst: power state to switch to
- * @hwsup: ptr to a bool to return whether the clkdm is hardware-supervised
  *
  * Determine whether the powerdomain needs to be turned on before
  * attempting to switch power states.  Called by
@@ -233,8 +232,7 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
  * "Types of sleep_switch" comment above).
  */
 static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
-                                              u8 curr_pwrst, u8 pwrst,
-                                              bool *hwsup)
+                                              u8 curr_pwrst, u8 pwrst)
 {
        u8 sleep_switch;
 
@@ -244,8 +242,7 @@ static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
                    arch_pwrdm->pwrdm_set_lowpwrstchange) {
                        sleep_switch = LOWPOWERSTATE_SWITCH;
                } else {
-                       *hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
-                       clkdm_wakeup_nolock(pwrdm->pwrdm_clkdms[0]);
+                       clkdm_deny_idle_nolock(pwrdm->pwrdm_clkdms[0]);
                        sleep_switch = FORCEWAKEUP_SWITCH;
                }
        } else {
@@ -259,7 +256,6 @@ static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
  * _pwrdm_restore_clkdm_state - restore the clkdm hwsup state after pwrst change
  * @pwrdm: struct powerdomain * to operate on
  * @sleep_switch: return value from _pwrdm_save_clkdm_state_and_activate()
- * @hwsup: should @pwrdm's first clockdomain be set to hardware-supervised mode?
  *
  * Restore the clockdomain state perturbed by
  * _pwrdm_save_clkdm_state_and_activate(), and call the power state
@@ -270,14 +266,11 @@ static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
  * software-supervised sleep.  No return value.
  */
 static void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm,
-                                      u8 sleep_switch, bool hwsup)
+                                      u8 sleep_switch)
 {
        switch (sleep_switch) {
        case FORCEWAKEUP_SWITCH:
-               if (hwsup)
-                       clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]);
-               else
-                       clkdm_sleep_nolock(pwrdm->pwrdm_clkdms[0]);
+               clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]);
                break;
        case LOWPOWERSTATE_SWITCH:
                if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
@@ -1092,7 +1085,6 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)
        u8 next_pwrst, sleep_switch;
        int curr_pwrst;
        int ret = 0;
-       bool hwsup = false;
 
        if (!pwrdm || IS_ERR(pwrdm))
                return -EINVAL;
@@ -1116,14 +1108,14 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)
                goto osps_out;
 
        sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, curr_pwrst,
-                                                           pwrst, &hwsup);
+                                                           pwrst);
 
        ret = pwrdm_set_next_pwrst(pwrdm, pwrst);
        if (ret)
                pr_err("%s: unable to set power state of powerdomain: %s\n",
                       __func__, pwrdm->name);
 
-       _pwrdm_restore_clkdm_state(pwrdm, sleep_switch, hwsup);
+       _pwrdm_restore_clkdm_state(pwrdm, sleep_switch);
 
 osps_out:
        pwrdm_unlock(pwrdm);