OMAP2+: Fix clockdomain state programming
Subsystems (5)
cover OMAP2+: Fix clockdomain state programming
This series fixes a few issues in the clockdomain state programming done as part of the omap_set_pwrdm_state() function.
More details on the issues can be found in the discussions below
http://marc.info/?l=linux-arm-kernel&m=130189971726452&w=2
Doing this requires adding additional api's in the clockdomain framework to identify what idle mode a clockdomain is currently programmed in. Hence a new api clkdm_is_idle() is also added as part of this series.
The series is tested on OMAP3-beagle for all low power states in idle and suspend and also boot tested on a OMAP4430sdp. Applies on 2.6.39-rc1.
Rajendra Nayak (4):
OMAP2+: clockdomain: Add an api to read idle mode OMAP2+: clockdomain: Add SoC support for clkdm_is_idle OMAP2+: PM: Initialise sleep_switch to a non-valid value OMAP2+: PM: idle clkdms only if already in idle
arch/arm/mach-omap2/clockdomain.c | 21 +++++++++++++++++++++ arch/arm/mach-omap2/clockdomain.h | 3 +++ arch/arm/mach-omap2/clockdomain2xxx_3xxx.c | 12 ++++++++++++ arch/arm/mach-omap2/clockdomain44xx.c | 7 +++++++ arch/arm/mach-omap2/pm.c | 6 ++++-- 5 files changed, 47 insertions(+), 2 deletions(-)
PATCH 1/4 OMAP2+: clockdomain: Add an api to read idle mode
Add a clockdomain api to check if hardware supervised idle transitions are enabled on a clockdomain.
--- arch/arm/mach-omap2/clockdomain.c | 21 +++++++++++++++++++++ arch/arm/mach-omap2/clockdomain.h | 3 +++ 2 files changed, 24 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/clockdomain.c +21 -0
@@ -795,6 +795,27 @@ void clkdm_deny_idle(struct clockdomain *clkdm) arch_clkdm->clkdm_deny_idle (clkdm); } +/** +* clkdm_is_idle - Check if the clkdm hwsup/autoidle is enabled +* @clkdm: struct clockdomain * +* +* Returns true if the clockdomain is in hardware-supervised +* idle mode, or 0 otherwise. +* +*/ +int clkdm_is_idle (struct clockdomain *clkdm) +{ +if (!clkdm) +return -EINVAL ; + +if (!arch_clkdm || !arch_clkdm->clkdm_is_idle) +return -EINVAL ; + +pr_debug ("clockdomain: reading idle state for %s\n" , clkdm->name); + +return arch_clkdm->clkdm_is_idle (clkdm); +} +/* Clockdomain-to-clock framework interface code */
arch/arm/mach-omap2/clockdomain.h +3 -0
@@ -138,6 +138,7 @@ struct clockdomain {* @clkdm_wakeup: Force a clockdomain to wakeup * @clkdm_allow_idle: Enable hw supervised idle transitions for clock domain * @clkdm_deny_idle: Disable hw supervised idle transitions for clock domain +* @clkdm_is_idle: Check if hw supervised idle transitions are enabled * @clkdm_clk_enable: Put the clkdm in right state for a clock enable * @clkdm_clk_disable: Put the clkdm in right state for a clock disable */
@@ -154,6 +155,7 @@ struct clkdm_ops {int (*clkdm_wakeup)(struct clockdomain *clkdm);void (*clkdm_allow_idle)(struct clockdomain *clkdm);void (*clkdm_deny_idle)(struct clockdomain *clkdm); +int (*clkdm_is_idle)(struct clockdomain *clkdm);int (*clkdm_clk_enable)(struct clockdomain *clkdm);int (*clkdm_clk_disable)(struct clockdomain *clkdm); };
@@ -177,6 +179,7 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm);void clkdm_allow_idle (struct clockdomain *clkdm);void clkdm_deny_idle (struct clockdomain *clkdm); +int clkdm_is_idle (struct clockdomain *clkdm);int clkdm_wakeup (struct clockdomain *clkdm);int clkdm_sleep (struct clockdomain *clkdm);
Signature · 1 line
1.7.0.4
PATCH 2/4 OMAP2+: clockdomain: Add SoC support for clkdm_is_idle
Add the SoC specific implemention for clkdm_is_idle for OMAP2/3 and OMAP4.
--- arch/arm/mach-omap2/clockdomain2xxx_3xxx.c | 12 ++++++++++++ arch/arm/mach-omap2/clockdomain44xx.c | 7 +++++++ 2 files changed, 19 insertions(+), 0 deletions(-)
arch/arm/mach-omap2/clockdomain2xxx_3xxx.c +12 -0
@@ -13,6 +13,7 @@*/ #include <linux/types.h> +#include <linux/errno.h> #include <plat/prcm.h> #include "prm.h" #include "prm2xxx_3xxx.h"
@@ -146,6 +147,15 @@ static void omap2_clkdm_deny_idle(struct clockdomain *clkdm)_clkdm_del_autodeps (clkdm); } +static int omap2_clkdm_is_idle (struct clockdomain *clkdm) +{ +if (!clkdm->clktrctrl_mask) +return -EINVAL ; + +return omap2_cm_is_clkdm_in_hwsup (clkdm->pwrdm.ptr->prcm_offs, + clkdm->clktrctrl_mask); +} +static void _enable_hwsup (struct clockdomain *clkdm) {if (cpu_is_omap24xx ())
@@ -252,6 +262,7 @@ struct clkdm_ops omap2_clkdm_operations = { .clkdm_wakeup = omap2_clkdm_wakeup, .clkdm_allow_idle = omap2_clkdm_allow_idle, .clkdm_deny_idle = omap2_clkdm_deny_idle, + .clkdm_is_idle = omap2_clkdm_is_idle, .clkdm_clk_enable = omap2_clkdm_clk_enable, .clkdm_clk_disable = omap2_clkdm_clk_disable, };
@@ -269,6 +280,7 @@ 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_is_idle = omap2_clkdm_is_idle, .clkdm_clk_enable = omap2_clkdm_clk_enable, .clkdm_clk_disable = omap2_clkdm_clk_disable, };
arch/arm/mach-omap2/clockdomain44xx.c +7 -0
@@ -93,6 +93,12 @@ static void omap4_clkdm_deny_idle(struct clockdomain *clkdm) clkdm->cm_inst, clkdm->clkdm_offs); } +static int omap4_clkdm_is_idle (struct clockdomain *clkdm) +{ +return omap4_cminst_is_clkdm_in_hwsup (clkdm->prcm_partition, + clkdm->cm_inst, clkdm->clkdm_offs); +} +static int omap4_clkdm_clk_enable (struct clockdomain *clkdm) {bool hwsup = false;
@@ -132,6 +138,7 @@ struct clkdm_ops omap4_clkdm_operations = { .clkdm_wakeup = omap4_clkdm_wakeup, .clkdm_allow_idle = omap4_clkdm_allow_idle, .clkdm_deny_idle = omap4_clkdm_deny_idle, + .clkdm_is_idle = omap4_clkdm_is_idle, .clkdm_clk_enable = omap4_clkdm_clk_enable, .clkdm_clk_disable = omap4_clkdm_clk_disable, };
Signature · 1 line
1.7.0.4
PATCH 3/4 OMAP2+: PM: Initialise sleep_switch to a non-valid value
sleep_switch which is initialised to 0 in omap_set_pwrdm_state happens to be a valid sleep_switch type (FORCEWAKEUP_SWITCH) which are defined as
#define FORCEWAKEUP_SWITCH 0
#define LOWPOWERSTATE_SWITCH 1
This causes the function to wrongly program some clock domains even when the Powerdomain is in ON state.
--- arch/arm/mach-omap2/pm.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/pm.c +1 -1
@@ -105,7 +105,7 @@ static void omap2_init_processor_devices(void)int omap_set_pwrdm_state (struct powerdomain *pwrdm,u32 state) {u32 cur_state; -int sleep_switch =0 ; +int sleep_switch =-1 ;int ret =0 ;if (pwrdm ==NULL ||IS_ERR (pwrdm))
Signature · 1 line
1.7.0.4
PATCH 4/4 OMAP2+: PM: idle clkdms only if already in idle
The omap_set_pwrdm_state function forces clockdomains to idle, without checking the existing idle state programmed, instead based solely on the HW capability of the clockdomain to support idle.
This is wrong and the clockdomains should be idled post a state_switch *only* if idle transitions on the clockdomain were already enabled.
--- arch/arm/mach-omap2/pm.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)
arch/arm/mach-omap2/pm.c +3 -1
@@ -107,6 +107,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)u32 cur_state;int sleep_switch =-1 ;int ret =0 ; +int hwsup =0 ;if (pwrdm ==NULL ||IS_ERR (pwrdm))return -EINVAL ;
@@ -126,6 +127,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) { sleep_switch = LOWPOWERSTATE_SWITCH; } else { + hwsup =clkdm_is_idle (pwrdm->pwrdm_clkdms[0 ]);clkdm_wakeup (pwrdm->pwrdm_clkdms[0 ]);pwrdm_wait_transition (pwrdm); sleep_switch =FORCEWAKEUP_SWITCH ;
@@ -141,7 +143,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)switch (sleep_switch) {case FORCEWAKEUP_SWITCH : -if (pwrdm->pwrdm_clkdms[0 ]->flags &CLKDM_CAN_ENABLE_AUTO ) +if (hwsup)clkdm_allow_idle (pwrdm->pwrdm_clkdms[0 ]);else clkdm_sleep (pwrdm->pwrdm_clkdms[0 ]);
Signature · 1 line
1.7.0.4
PATCH 1/4 OMAP2+: clockdomain: Add an api to read idle mode
In reply to Rajendra NayakQuoted 17 lines · Rajendra Nayak
Add a clockdomain api to check if hardware supervised idle transitions are enabled on a clockdomain.
Signed-off-by Rajendra Nayak<[email protected]>--- arch/arm/mach-omap2/clockdomain.c | 21 +++++++++++++++++++++ arch/arm/mach-omap2/clockdomain.h | 3 +++ 2 files changed, 24 insertions(+), 0 deletions(-)arch/arm/mach-omap2/clockdomain.c +0
-0@@ -795,6 +795,27 @@ void clkdm_deny_idle(struct clockdomain *clkdm) arch_clkdm->clkdm_deny_idle (clkdm); }
arch/arm/mach-omap2/clockdomain.c void clkdm_deny_idle(struct clockdomain *clkdm) · line 807
Quoted 10 lines
arch/arm/mach-omap2/clockdomain.c +9
-0+/** +* clkdm_is_idle - Check if the clkdm hwsup/autoidle is enabled +* @clkdm: struct clockdomain * +* +* Returns true if the clockdomain is in hardware-supervised +* idle mode, or 0 otherwise. +* +*/ +int clkdm_is_idle (struct clockdomain *clkdm)
Name of the API not seems to fit correctly.
Shoule it be something like "clkdm_is_in_hwsup"?