From patchwork Fri May 27 23:18:52 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rafael Wysocki X-Patchwork-Id: 825882 Received: from smtp1.linux-foundation.org (smtp1.linux-foundation.org [140.211.169.13]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p4S0nH0G004046 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) for ; Sat, 28 May 2011 00:49:37 GMT Received: from daredevil.linux-foundation.org (localhost [127.0.0.1]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id p4S0lmFu028031; Fri, 27 May 2011 17:47:49 -0700 Received: from ogre.sisk.pl (ogre.sisk.pl [217.79.144.158]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id p4RNr2L9019214 for ; Fri, 27 May 2011 16:53:03 -0700 Received: from localhost (localhost.localdomain [127.0.0.1]) by ogre.sisk.pl (Postfix) with ESMTP id BC7E61B01D0; Sat, 28 May 2011 01:07:15 +0200 (CEST) Received: from ogre.sisk.pl ([127.0.0.1]) by localhost (ogre.sisk.pl [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 21694-06; Sat, 28 May 2011 01:06:46 +0200 (CEST) Received: from ferrari.rjw.lan (220-bem-13.acn.waw.pl [82.210.184.220]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by ogre.sisk.pl (Postfix) with ESMTP id A94B01B06D2; Sat, 28 May 2011 01:06:39 +0200 (CEST) From: "Rafael J. Wysocki" To: Linux PM mailing list Date: Sat, 28 May 2011 01:18:52 +0200 User-Agent: KMail/1.13.6 (Linux/2.6.39+; KDE/4.6.0; x86_64; ; ) References: <201104290154.12966.rjw@sisk.pl> <201105160117.46927.rjw@sisk.pl> <201105280115.14556.rjw@sisk.pl> In-Reply-To: <201105280115.14556.rjw@sisk.pl> MIME-Version: 1.0 Message-Id: <201105280118.52316.rjw@sisk.pl> X-Virus-Scanned: amavisd-new at ogre.sisk.pl using MkS_Vir for Linux Received-SPF: pass (localhost is always allowed.) X-Spam-Status: No, hits=-3.936 required=5 tests=AWL, BAYES_00, OSDL_HEADER_SUBJECT_BRACKETED X-Spam-Checker-Version: SpamAssassin 3.2.4-osdl_revision__1.47__ X-MIMEDefang-Filter: lf$Revision: 1.188 $ X-Scanned-By: MIMEDefang 2.63 on 140.211.169.21 Cc: linux-sh@vger.kernel.org, Greg KH , LKML , Grant Likely , Guennadi Liakhovetski Subject: [linux-pm] [PATCH 4/5] PM / Domains: Support for multiple generic power domain states X-BeenThere: linux-pm@lists.linux-foundation.org X-Mailman-Version: 2.1.9 Precedence: list List-Id: Linux power management List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-pm-bounces@lists.linux-foundation.org Errors-To: linux-pm-bounces@lists.linux-foundation.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Sat, 28 May 2011 00:49:37 +0000 (UTC) From: Rafael J. Wysocki 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 Reviewed-by: Kevin Hilman --- 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 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,