From patchwork Wed Aug 15 10:02:42 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean Pihet X-Patchwork-Id: 1325181 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 0433C3FC81 for ; Wed, 15 Aug 2012 10:06:52 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1T1aS0-0006bN-9Z; Wed, 15 Aug 2012 10:03:44 +0000 Received: from mail-we0-f177.google.com ([74.125.82.177]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1T1aRX-0006Uf-D8 for linux-arm-kernel@lists.infradead.org; Wed, 15 Aug 2012 10:03:17 +0000 Received: by weyr3 with SMTP id r3so962120wey.36 for ; Wed, 15 Aug 2012 03:03:12 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=G8ECVaAcV8aoWPid0FK9F/FCS6H9z8/NQFWmKX943HA=; b=Ketoa9aLCDoCy7Zw6U384eSc02blHnZ+vZ87aO7oKksLvwLLCO36c/P0SEDNmBjhU5 ru3ypDVOkS/CJzL0fCh4+uyKvM66Mav8BmiBHKUrR6ftNw+OeQLWtm8Z7pqzsfjrTP/q cdY/QUfxAJqjd/fGWyAkpMzUcLOv2EI8BL0nV1ZnV26tCv40WkaK2ok9969COHZZSu8z 12gJ/lPfyR0rHd7B05A83uKGrveDljehsYiZg+LNhywdCzcIZcDIFSDxEX96s2V9XCxg I3Yymbik6PQyXujBaovF3tKgac3bilGAYT/J40CkG4r6BQlXWqj3JELzLTTfib1wa4wk Y2dQ== Received: by 10.180.98.138 with SMTP id ei10mr35803455wib.1.1345024991941; Wed, 15 Aug 2012 03:03:11 -0700 (PDT) Received: from localhost.localdomain (49.68-66-87.adsl-dyn.isp.belgacom.be. [87.66.68.49]) by mx.google.com with ESMTPS id q4sm27679093wix.9.2012.08.15.03.03.10 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 15 Aug 2012 03:03:11 -0700 (PDT) From: Jean Pihet To: linux-omap@vger.kernel.org, paul@pwsan.com, linux-arm-kernel@lists.infradead.org, khilman@ti.com, Rajendra Nayak , Santosh Shilimkar , Nishanth Menon Subject: [PATCH 2/8] ARM: OMAP2+: PM: introduce power domains achievable functional states Date: Wed, 15 Aug 2012 12:02:42 +0200 Message-Id: <1345024968-28951-3-git-send-email-j-pihet@ti.com> X-Mailer: git-send-email 1.7.7.6 In-Reply-To: <1345024968-28951-1-git-send-email-j-pihet@ti.com> References: <1345024968-28951-1-git-send-email-j-pihet@ti.com> X-Gm-Message-State: ALoCoQnxDC651FUbx1oMuT6tFpLcnnwhl2n37iw0vznyvi5zgJi3Aib3uhV/OxWHl0/VZrgiIOah X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [74.125.82.177 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: Tero Kristo , Jean Pihet X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org 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 pwrdm_set_next_fpwrst. 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, generalized to the functional power states and reworked as per Nishant's suggestions. Signed-off-by: Jean Pihet Cc: Tero Kristo Cc: Nishanth Menon --- arch/arm/mach-omap2/powerdomain.c | 152 +++++++++++++++++++++++++++++++------ arch/arm/mach-omap2/powerdomain.h | 1 + 2 files changed, 130 insertions(+), 23 deletions(-) diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index 6fc3c84..3a1f56c 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c @@ -199,6 +199,53 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused) return 0; } +/** + * Search down then up for a valid state from a list of allowed states. + * Used by pwrdm_get_achievable_fpwrst to look for allowed power and + * logic states for a powerdomain. + * + * @pwrsts: list of allowed states, defined as a bitmask + * @pwrst: initial state to be used as a starting point + * @min: minimum allowed state + * @max: maximum allowed state + * + * Returns the matching allowed state. + */ +static inline int _match_pwrst(u32 pwrsts, int pwrst, int min, int max) +{ + int found = 1, new_pwrst = pwrst; + + /* + * Search lower: if the requested state is not supported + * try the lower states, stopping at the minimum allowed + * state + */ + while (!(pwrsts & (1 << new_pwrst))) { + if (new_pwrst <= min) { + found = 0; + break; + } + new_pwrst--; + } + + /* + * Search higher: if no lower state found fallback to the higher + * states, stopping at the maximum allowed state + */ + if (!found) { + new_pwrst = pwrst; + while (!(pwrsts & (1 << new_pwrst))) { + if (new_pwrst >= max) { + new_pwrst = max; + break; + } + new_pwrst++; + } + } + + return new_pwrst; +} + /* Public functions */ /** @@ -553,6 +600,57 @@ int pwrdm_pwrst_to_fpwrst(struct powerdomain *pwrdm, u8 pwrst, u8 logic) return ret; } +/** + * pwrdm_get_achievable_fpwrst() - Provide achievable functional state + * @pwrdm: struct powerdomain * to set + * @fpwrst: 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_fpwrst(struct powerdomain *pwrdm, u8 fpwrst) +{ + int pwrst = pwrdm_fpwrst_to_pwrst(pwrdm, fpwrst); + int logic = pwrdm_fpwrst_to_logic_pwrst(pwrdm, fpwrst); + int new_fpwrst, new_pwrst, new_logic; + + if (!pwrdm || IS_ERR(pwrdm)) { + pr_debug("%s: invalid params: pwrdm=%p, fpwrst=%0x\n", + __func__, pwrdm, fpwrst); + return -EINVAL; + } + + if ((pwrst < 0) || (logic < 0)) { + pr_debug("%s: invalid params for pwrdm %s, fpwrst=%0x\n", + __func__, pwrdm->name, fpwrst); + return PWRDM_FUNC_PWRST_ON; + } + + new_pwrst = _match_pwrst(pwrdm->pwrsts, pwrst, PWRDM_POWER_OFF, + PWRDM_POWER_ON); + new_logic = _match_pwrst(pwrdm->pwrsts_logic_ret, logic, + PWRDM_LOGIC_MEM_PWRST_OFF, + PWRDM_LOGIC_MEM_PWRST_RET); + + new_fpwrst = pwrdm_pwrst_to_fpwrst(pwrdm, new_pwrst, new_logic); + + pr_debug("%s(%s, fpwrst=%0x) returns %0x\n", __func__, + pwrdm->name, fpwrst, new_fpwrst); + + return new_fpwrst; +} + /* Types of sleep_switch used in pwrdm_set_next_fpwrst */ #define FORCEWAKEUP_SWITCH 0 #define LOWPOWERSTATE_SWITCH 1 @@ -562,34 +660,30 @@ int pwrdm_pwrst_to_fpwrst(struct powerdomain *pwrdm, u8 pwrst, u8 logic) * @pwrdm: struct powerdomain * to set * @fpwrst: 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 pwrdm_set_next_fpwrst(struct powerdomain *pwrdm, enum pwrdm_func_state fpwrst) { - u8 curr_pwrst, next_pwrst; - int pwrst = pwrdm_fpwrst_to_pwrst(pwrdm, fpwrst); - int logic = pwrdm_fpwrst_to_logic_pwrst(pwrdm, fpwrst); int sleep_switch = -1, ret = 0, hwsup = 0; + int new_fpwrst, next_fpwrst, pwrst, logic; + u8 curr_pwrst; - if (!pwrdm || IS_ERR(pwrdm) || (pwrst < 0) || (logic < 0)) { - pr_debug("%s: invalid params: pwrdm=%p, fpwrst=%0x\n", - __func__, pwrdm, fpwrst); + if (!pwrdm || IS_ERR(pwrdm)) { + pr_debug("%s: invalid params: pwrdm=%p\n", __func__, pwrdm); return -EINVAL; } - pr_debug("%s: set fpwrst %0x to pwrdm %s\n", - __func__, fpwrst, pwrdm->name); + pr_debug("%s(%s, fpwrst=%0x)\n", __func__, pwrdm->name, fpwrst); - while (!(pwrdm->pwrsts & (1 << pwrst))) { - if (pwrst == PWRDM_POWER_OFF) - return ret; - pwrst--; - } + new_fpwrst = pwrdm_get_achievable_fpwrst(pwrdm, fpwrst); + pwrst = pwrdm_fpwrst_to_pwrst(pwrdm, new_fpwrst); + logic = pwrdm_fpwrst_to_logic_pwrst(pwrdm, new_fpwrst); - next_pwrst = pwrdm_read_next_pwrst(pwrdm); - if (next_pwrst == pwrst) + next_fpwrst = pwrdm_read_next_fpwrst(pwrdm); + if (new_fpwrst == next_fpwrst) return ret; curr_pwrst = pwrdm_read_pwrst(pwrdm); @@ -604,13 +698,25 @@ int pwrdm_set_next_fpwrst(struct powerdomain *pwrdm, } } - if (logic != pwrdm_read_logic_retst(pwrdm)) - pwrdm_set_logic_retst(pwrdm, logic); + pr_debug("%s: set fpwrst %0x (%0x,%0x) to pwrdm %s\n", + __func__, new_fpwrst, pwrst, logic, pwrdm->name); + + /* Trace the pwrdm desired target state */ + trace_power_domain_target(pwrdm->name, new_fpwrst, + smp_processor_id()); + + /* Program next power state */ + if (pwrst != pwrdm_fpwrst_to_pwrst(pwrdm, next_fpwrst)) { + 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_fpwrst_to_logic_pwrst(pwrdm, next_fpwrst))) + 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 aa9f9b9..45c449d 100644 --- a/arch/arm/mach-omap2/powerdomain.h +++ b/arch/arm/mach-omap2/powerdomain.h @@ -239,6 +239,7 @@ int pwrdm_set_next_fpwrst(struct powerdomain *pwrdm, int pwrdm_read_prev_fpwrst(struct powerdomain *pwrdm); int pwrdm_read_fpwrst(struct powerdomain *pwrdm); int pwrdm_read_next_fpwrst(struct powerdomain *pwrdm); +int pwrdm_get_achievable_fpwrst(struct powerdomain *pwrdm, u8 fpwrst); int omap2_pwrdm_fpwrst_to_pwrst(struct powerdomain *pwrdm, u8 fpwrst); int omap2_pwrdm_fpwrst_to_logic_pwrst(struct powerdomain *pwrdm, u8 fpwrst); int omap2_pwrdm_pwrst_to_fpwrst(struct powerdomain *pwrdm, u8 pwrst, u8 logic);