[PATCH 5/8] ARM: OMAP2+: PM: introduce power domains achievable functional states

Jean Pihet jean.pihet at newoldbits.com
Fri Jun 15 07:28:30 EDT 2012


Hi,

Here are some remarks I got after an internal review. I think those
points need to be discussed with a broader audience.

On Thu, Jun 14, 2012 at 4:53 PM, Jean Pihet <jean.pihet at newoldbits.com> wrote:
> Note: the patch is in RFC state because the state machine for setting
> the next power domain states needs more discussion. Validated on OMAP3&4
> with cpuidle and suspend/resume, though.
>
> Power domains have varied capabilities. When attempting a low power
> state such as OFF/RET, a specific min requested state may not be
> supported on the power domain. This is because a combination
> of system power states where the parent PD's state is not in line
> with expectation can result in system instabilities.
>
> This patch provides a function that returns the achievable functional
> power state for a power domain and its use by omap_set_pwrdm_state.
> The achievable power state is first looked for in the lower power
> states in order to maximize the power savings, then if not found
> in the higher power states.
>
> Inspired from Tero's work on OMAP4 device OFF support and generalized
> to the functional power states.
>
> Signed-off-by: Jean Pihet <j-pihet at ti.com>
> Cc: Tero Kristo <t-kristo at ti.com>
> ---
>  arch/arm/mach-omap2/powerdomain.c |  156 +++++++++++++++++++++++++++++++------
>  arch/arm/mach-omap2/powerdomain.h |    1 +
>  2 files changed, 134 insertions(+), 23 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
> index ce2685a..f94174b 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -554,6 +554,108 @@ int pwrdm_pwrst_to_func(struct powerdomain *pwrdm, u8 pwrst, u8 logic)
>        return ret;
>  }
>
> +/**
> + * pwrdm_get_achievable_func_pwrst() - Provide achievable functional state
> + * @pwrdm: struct powerdomain * to set
> + * @func_pwrst: minimum functional state we would like to hit
> + * (one of the PWRDM_FUNC_* macros)
> + *
> + * Power domains have varied capabilities. When attempting a low power
> + * state such as OFF/RET, a specific min requested state may not be
> + * supported on the power domain. This is because a combination
> + * of system power states where the parent PD's state is not in line
> + * with expectation can result in system instabilities.
> + *
> + * The achievable power state is first looked for in the lower power
> + * states in order to maximize the power savings, then if not found
> + * in the higher power states.
> + *
> + * Returns the achievable functional power state, or -EINVAL in case of
> + * invalid parameters.
> + */
> +int pwrdm_get_achievable_func_pwrst(struct powerdomain *pwrdm, u8 func_pwrst)
> +{
> +       int pwrst = pwrdm_func_to_pwrst(pwrdm, func_pwrst);
> +       int logic = pwrdm_func_to_logic_pwrst(pwrdm, func_pwrst);
> +       int new_func_pwrst, new_pwrst, new_logic;
> +       int found;
> +
> +       if (!pwrdm || IS_ERR(pwrdm)) {
> +               pr_debug("%s: invalid params: pwrdm=%p, func_pwrst=%0x\n",
> +                        __func__, pwrdm, func_pwrst);
> +               return -EINVAL;
> +       }
> +
> +       if ((pwrst < 0) || (logic < 0)) {
> +               pr_debug("%s: invalid params for pwrdm %s, func_pwrst=%0x\n",
> +                        __func__, pwrdm->name, func_pwrst);
> +               return PWRDM_FUNC_PWRST_ON;
> +       }
> +
> +       /*
> +        * Power state: if the requested state is not supported,
> +        * try the lower power states.
> +        */
> +       found = 1;
> +       new_pwrst = pwrst;
> +       while (!(pwrdm->pwrsts & (1 << new_pwrst))) {
> +               if (new_pwrst == PWRDM_POWER_OFF) {
> +                       found = 0;
> +                       break;
> +               }
> +               new_pwrst--;
> +       }
> +
> +       /*
> +        * If no lower power state found, fallback to the higher
> +        * power states.
> +        * PWRDM_POWER_ON is always valid.
> +        */
> +       if (!found) {
> +               new_pwrst = pwrst;
> +               while (!(pwrdm->pwrsts & (1 << new_pwrst))) {
> +                       if (new_pwrst == PWRDM_POWER_ON)
> +                               break;
> +                       new_pwrst++;
> +               }
> +       }
> +
> +       /*
> +        * Logic RET state: if the requested state is not supported,
> +        * try the lower logic states.
> +        */
> +       found = 1;
> +       new_logic = logic;
> +       while (!(pwrdm->pwrsts_logic_ret & (1 << new_logic))) {
> +               if (new_logic == PWRDM_LOGIC_MEM_PWRST_OFF) {
> +                       found = 0;
> +                       break;
> +               }
> +               new_logic--;
> +       }
> +
> +       /*
> +        * If no lower logic state found, fallback to the higher
> +        * logic states.
> +        * PWRDM_LOGIC_MEM_PWRST_RET is always valid.
> +        */
> +       if (!found) {
> +               new_logic = logic;
> +               while (!(pwrdm->pwrsts_logic_ret & (1 << new_logic))) {
> +                       if (new_logic == PWRDM_LOGIC_MEM_PWRST_RET)
> +                               break;
> +                       new_logic++;
> +               }
> +       }
> +

"
1. The while loops need optimization. - This can definitely be
simplified and made non-risky; this also needs some more error
handling
2. About the power domains state machine: you have only one power
state to move in and out of: it is called ON. If you do ON->CSWR->ON
etc. attempting to program ON->CSWR->OSWR is NOT supported in OMAP and
is an invitation for production team to sit and debug for a while
before finding the use of invalid power states
"

Point 2 is a good point. The solution would be to implement a state
machine in this function and/or omap_set_pwrdm_state, possibly using a
platform specific handling function.

Any thought?

Regards,
Jean

> +       new_func_pwrst = pwrdm_pwrst_to_func(pwrdm, new_pwrst, new_logic);
> +
> +       pr_debug("%s(%s, func_pwrst=%0x) returns %0x\n", __func__,
> +                pwrdm->name, func_pwrst, new_func_pwrst);
> +
> +       return new_func_pwrst;
> +}
> +
>  /* Types of sleep_switch used in omap_set_pwrdm_state */
>  #define FORCEWAKEUP_SWITCH     0
>  #define LOWPOWERSTATE_SWITCH   1
> @@ -563,36 +665,32 @@ int pwrdm_pwrst_to_func(struct powerdomain *pwrdm, u8 pwrst, u8 logic)
>  * @pwrdm: struct powerdomain * to set
>  * @func_pwrst: power domain functional state, one of the PWRDM_FUNC_* macros
>  *
> - * This programs the pwrdm next functional state, sets the dependencies
> - * and waits for the state to be applied.
> + * This looks for the more suited (or achievable) next functional power
> + * state, programs it, sets the dependencies and waits for the state to
> + * be applied to the power domain.
>  */
>  int omap_set_pwrdm_state(struct powerdomain *pwrdm,
>                         enum pwrdm_func_state func_pwrst)
>  {
> -       u8 curr_pwrst, next_pwrst;
> -       int pwrst = pwrdm_func_to_pwrst(pwrdm, func_pwrst);
> -       int logic = pwrdm_func_to_logic_pwrst(pwrdm, func_pwrst);
>        int sleep_switch = -1, ret = 0, hwsup = 0;
> +       int new_func_pwrst, next_func_pwrst, pwrst, logic;
> +       u8 curr_pwrst;
>
> -       if (!pwrdm || IS_ERR(pwrdm) || (pwrst < 0) || (logic < 0)) {
> -               pr_debug("%s: invalid params: pwrdm=%p, func_pwrst=%0x\n",
> -                        __func__, pwrdm, func_pwrst);
> +       if (!pwrdm || IS_ERR(pwrdm)) {
> +               pr_debug("%s: invalid params: pwrdm=%p\n", __func__, pwrdm);
>                return -EINVAL;
>        }
>
> -       pr_debug("%s: set func_pwrst %0x to pwrdm %s\n",
> -                __func__, func_pwrst, pwrdm->name);
> +       pr_debug("%s(%s, func_pwrst=%0x)\n", __func__, pwrdm->name, func_pwrst);
>
>        mutex_lock(&pwrdm->lock);
>
> -       while (!(pwrdm->pwrsts & (1 << pwrst))) {
> -               if (pwrst == PWRDM_POWER_OFF)
> -                       goto out;
> -               pwrst--;
> -       }
> +       new_func_pwrst = pwrdm_get_achievable_func_pwrst(pwrdm, func_pwrst);
> +       pwrst = pwrdm_func_to_pwrst(pwrdm, new_func_pwrst);
> +       logic = pwrdm_func_to_logic_pwrst(pwrdm, new_func_pwrst);
>
> -       next_pwrst = pwrdm_read_next_pwrst(pwrdm);
> -       if (next_pwrst == pwrst)
> +       next_func_pwrst = pwrdm_read_next_func_pwrst(pwrdm);
> +       if (new_func_pwrst == next_func_pwrst)
>                goto out;
>
>        curr_pwrst = pwrdm_read_pwrst(pwrdm);
> @@ -607,13 +705,25 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm,
>                }
>        }
>
> -       if (logic != pwrdm_read_logic_retst(pwrdm))
> -               pwrdm_set_logic_retst(pwrdm, logic);
> +       pr_debug("%s: set func_pwrst %0x (%0x,%0x) to pwrdm %s\n",
> +                __func__, new_func_pwrst, pwrst, logic, pwrdm->name);
> +
> +       /* Trace the pwrdm desired target state */
> +       trace_power_domain_target(pwrdm->name, new_func_pwrst,
> +                                 smp_processor_id());
> +
> +       /* Program next power state */
> +       if (pwrst != pwrdm_func_to_pwrst(pwrdm, next_func_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);
> +       }
>
> -       ret = pwrdm_set_next_pwrst(pwrdm, pwrst);
> -       if (ret)
> -               pr_err("%s: unable to set power state of powerdomain: %s\n",
> -                      __func__, pwrdm->name);
> +       /* Program RET logic state */
> +       if ((pwrst == PWRDM_POWER_RET) &&
> +           (logic != pwrdm_func_to_logic_pwrst(pwrdm, next_func_pwrst)))
> +               pwrdm_set_logic_retst(pwrdm, logic);
>
>        switch (sleep_switch) {
>        case FORCEWAKEUP_SWITCH:
> diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
> index d08c25d..9dd68ab 100644
> --- a/arch/arm/mach-omap2/powerdomain.h
> +++ b/arch/arm/mach-omap2/powerdomain.h
> @@ -247,6 +247,7 @@ int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm);
>  int omap2_pwrdm_func_to_pwrst(struct powerdomain *pwrdm, u8 func_pwrst);
>  int omap2_pwrdm_func_to_logic_pwrst(struct powerdomain *pwrdm, u8 func_pwrst);
>  int omap2_pwrdm_pwrst_to_func(struct powerdomain *pwrdm, u8 pwrst, u8 logic);
> +int pwrdm_get_achievable_func_pwrst(struct powerdomain *pwrdm, u8 func_pwrst);
>
>  int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst);
>  int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst);
> --
> 1.7.7.6
>



More information about the linux-arm-kernel mailing list