From: Rafael J. Wysocki <rjw@xxxxxxx> Allow the generic power domains support code to handle power domains with multiple states. Replace the .power_down_ok() callback in struct dev_power_governor with a new callback .choose_state() that will return the number of the state to put the power domain into. Add new fields nr_states and current_state to struct generic_power_domain with the assumption that state 0 will be the full power state and states 1 through (nr_states - 1) will be low power. Replace power domain callbacks .power_off() and .power_on() with a single .set_state() callback taking the number of the state to put the power domain into as its second argument. Add a new generic power domain callback .power_off_state() (taking a state number as its second argument) allowing the core to check if device runtime PM callbacks need to be executed before putting the given power domain into the given state. Modify the core power domains code and the ARM shmobile platform code to take all of the above changes into account. Signed-off-by: Rafael J. Wysocki <rjw@xxxxxxx> Reviewed-by: Kevin Hilman <khilman@xxxxxx> --- arch/arm/mach-shmobile/pm-sh7372.c | 11 ++++-- drivers/base/power/domain.c | 60 +++++++++++++++++++++++++++---------- include/linux/pm_domain.h | 16 ++++++--- 3 files changed, 63 insertions(+), 24 deletions(-) Index: linux-2.6/include/linux/pm_domain.h =================================================================== --- linux-2.6.orig/include/linux/pm_domain.h +++ linux-2.6/include/linux/pm_domain.h @@ -12,7 +12,7 @@ #include <linux/device.h> struct dev_power_governor { - bool (*power_down_ok)(struct dev_power_domain *domain); + int (*choose_state)(struct dev_power_domain *domain); }; struct generic_power_domain { @@ -23,12 +23,14 @@ struct generic_power_domain { struct list_head device_list; struct mutex lock; struct dev_power_governor *gov; - unsigned int in_progress; + int nr_states; + int current_state; bool power_is_off; + unsigned int in_progress; unsigned int device_count; unsigned int suspended_count; - int (*power_off)(struct dev_power_domain *domain); - int (*power_on)(struct dev_power_domain *domain); + int (*set_state)(struct dev_power_domain *domain, int state); + bool (*power_off_state)(struct dev_power_domain *domain, int state); int (*start_device)(struct device *dev); int (*stop_device)(struct device *dev); }; @@ -48,7 +50,8 @@ extern int pm_genpd_add_subdomain(struct extern int pm_genpd_remove_subdomain(struct generic_power_domain *genpd, struct generic_power_domain *target); extern void pm_genpd_init(struct generic_power_domain *genpd, - struct dev_power_governor *gov, bool is_off); + struct dev_power_governor *gov, int nr_states, + int cur_state); #else static inline int pm_genpd_add_device(struct generic_power_domain *genpd, struct device *dev) @@ -71,7 +74,8 @@ static inline int pm_genpd_remove_subdom return -ENOSYS; } static inline void pm_genpd_init(struct generic_power_domain *genpd, - struct dev_power_governor *gov, bool is_off) {} + struct dev_power_governor *gov, + int nr_states, int cur_state) {} #endif #endif /* _LINUX_PM_DOMAIN_H */ Index: linux-2.6/drivers/base/power/domain.c =================================================================== --- linux-2.6.orig/drivers/base/power/domain.c +++ linux-2.6/drivers/base/power/domain.c @@ -62,6 +62,7 @@ static int __pm_genpd_poweroff(struct ge struct generic_power_domain *subdomain; struct dev_list_entry *dle; unsigned int not_suspended; + int new_state; int ret; if (genpd->power_is_off) @@ -83,9 +84,23 @@ static int __pm_genpd_poweroff(struct ge return ret; } - if (genpd->gov && genpd->gov->power_down_ok) { - if (!genpd->gov->power_down_ok(&genpd->domain)) - return -EAGAIN; + new_state = (genpd->gov && genpd->gov->choose_state) ? + genpd->gov->choose_state(&genpd->domain) : 1; + if (new_state < 0 || new_state >= genpd->nr_states) + return -EAGAIN; + + if (new_state == genpd->current_state) + return 0; + + if (genpd->power_off_state + && !genpd->power_off_state(&genpd->domain, new_state)) { + if (genpd->set_state) { + ret = genpd->set_state(&genpd->domain, new_state); + if (ret) + return ret; + } + genpd->current_state = new_state; + return 0; } list_for_each_entry_reverse(dle, &genpd->device_list, node) { @@ -105,9 +120,13 @@ static int __pm_genpd_poweroff(struct ge goto err_dev; } - if (genpd->power_off) - genpd->power_off(&genpd->domain); + if (genpd->set_state) { + ret = genpd->set_state(&genpd->domain, new_state); + if (ret) + goto err_dev; + } + genpd->current_state = new_state; genpd->power_is_off = true; return 0; @@ -199,14 +218,17 @@ static int __pm_genpd_poweron(struct gen { struct dev_list_entry *dle; - if (!genpd->power_is_off) + if (genpd->current_state == 0) return 0; - if (genpd->power_on) { - int ret = genpd->power_on(&genpd->domain); + if (genpd->set_state) { + int ret = genpd->set_state(&genpd->domain, 0); if (ret) return ret; } + genpd->current_state = 0; + if (!genpd->power_is_off) + return 0; genpd->power_is_off = false; @@ -363,8 +385,8 @@ static int pm_genpd_suspend_noirq(struct mutex_lock(&genpd->lock); if (++genpd->suspended_count == genpd->device_count) { - if (genpd->power_off) - genpd->power_off(&genpd->domain); + if (genpd->set_state) + genpd->set_state(&genpd->domain, genpd->nr_states - 1); } mutex_unlock(&genpd->lock); @@ -395,8 +417,8 @@ static int pm_genpd_resume_noirq(struct mutex_lock(&genpd->lock); if (genpd->suspended_count == genpd->device_count) { - if (genpd->power_on) { - int ret = genpd->power_on(&genpd->domain); + if (genpd->set_state) { + int ret = genpd->set_state(&genpd->domain, 0); if (ret) { mutex_unlock(&genpd->lock); return ret; @@ -713,10 +735,12 @@ int pm_genpd_remove_subdomain(struct gen * pm_genpd_init - Initialize a generic I/O power domain object. * @genpd: Power domain object to initialize. * @gov: Power domain governor to associate with the domain (may be NULL). - * @is_off: Initial value of the domain's power_is_off field. + * @nr_states: Number of power domain states (must be greater than 1). + * @cur_state: Initial state of the power domain. */ void pm_genpd_init(struct generic_power_domain *genpd, - struct dev_power_governor *gov, bool is_off) + struct dev_power_governor *gov, int nr_states, + int cur_state) { if (IS_ERR_OR_NULL(genpd)) return; @@ -727,8 +751,14 @@ void pm_genpd_init(struct generic_power_ INIT_LIST_HEAD(&genpd->subdomain_list); mutex_init(&genpd->lock); genpd->gov = gov; + genpd->nr_states = nr_states > 1 ? nr_states : 2; + if (cur_state < 0 || cur_state >= nr_states) + cur_state = 0; + genpd->current_state = cur_state; + genpd->power_is_off = genpd->power_off_state ? + genpd->power_off_state(&genpd->domain, cur_state) : + (cur_state > 0); genpd->in_progress = 0; - genpd->power_is_off = is_off; genpd->device_count = 0; genpd->suspended_count = 0; genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c =================================================================== --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c @@ -70,15 +70,20 @@ static int pd_power_up(struct dev_power_ return 0; } +static int pd_set_state(struct dev_power_domain *domain, int state) +{ + return state > 0 ? pd_power_down(domain) : pd_power_up(domain); +} + static void sh7372_init_domain(struct generic_power_domain *domain, struct sh7372_domain_data *pdata) { - pm_genpd_init(domain, NULL, false); domain->domain.platform_data = pdata; domain->stop_device = pm_runtime_clk_suspend; domain->start_device = pm_runtime_clk_resume; - domain->power_off = pd_power_down; - domain->power_on = pd_power_up; + domain->set_state = pd_set_state; + domain->power_off_state = NULL; + pm_genpd_init(domain, NULL, 2, 0); } void sh7372_add_device_to_domain(struct generic_power_domain *domain, _______________________________________________ linux-pm mailing list linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/linux-pm