From patchwork Mon Nov 10 22:52:46 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bjorn Andersson X-Patchwork-Id: 5269841 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 9746CC11AC for ; Mon, 10 Nov 2014 22:55:05 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 48FF12012F for ; Mon, 10 Nov 2014 22:55:04 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 0376D2011D for ; Mon, 10 Nov 2014 22:55:03 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XnxpT-0007m9-Ue; Mon, 10 Nov 2014 22:52:59 +0000 Received: from seldrel01.sonyericsson.com ([212.209.106.2]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XnxpF-0007ix-K7 for linux-arm-kernel@lists.infradead.org; Mon, 10 Nov 2014 22:52:47 +0000 From: Bjorn Andersson To: Stephen Boyd , David Collins , Lina Iyer , Mark Brown Subject: [RFC 2/2] regulator: qcom-rpm: Implement RPM assisted disable Date: Mon, 10 Nov 2014 14:52:46 -0800 Message-ID: <1415659966-16200-3-git-send-email-bjorn.andersson@sonymobile.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1415659966-16200-1-git-send-email-bjorn.andersson@sonymobile.com> References: <1415659966-16200-1-git-send-email-bjorn.andersson@sonymobile.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20141110_145245_899339_E0A98CD3 X-CRM114-Status: GOOD ( 17.55 ) X-Spam-Score: -5.0 (-----) Cc: linux-arm-msm@vger.kernel.org, Lee Jones , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-3.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Some regulators are used to power e.g. PLLs that clocks the core we're running on, so we can't turn them off directly; but we do want them off when the core is powered down. To handle this the Qualcomm SoC provides a means to specify a "sleep state" for RPM resources that can be triggered by the hardware when the cores are brought down. Resources of this type should be considered always-on while the CPU(s) are running and we can utilize the "sleep state" functionality to defer disabling them until the CPU(s) go to sleep, if not used by other peripherals at that time. Other properties are kept in sync between the states, so that if the CPU(s) go to sleep with a particular regulator still enabled there will be no change. Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/mfd/qcom-rpm.txt | 28 ++++++++ drivers/regulator/qcom_rpm-regulator.c | 68 +++++++++++++++----- 2 files changed, 79 insertions(+), 17 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt index 4264021..4671eef 100644 --- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt +++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt @@ -112,6 +112,13 @@ of valid subnodes that can operate on these resources. Definition: select that the power supply should operate in hysteretic mode, instead of the default pwm mode +- qcom,rpm-assisted-disable: + Usage: optional + Value type: + Definition: select that the regulator is supplying the active CPU(s) + and can only be disabled with the assistans from the RPM + after going to sleep + Standard regulator bindings are used inside switch mode power supply subnodes. Check Documentation/devicetree/bindings/regulator/regulator.txt for more details. @@ -160,6 +167,13 @@ details. qcom,rpm-pm8921-nldo, qcom,rpm-pm8921-nldo1200 +- qcom,rpm-assisted-disable: + Usage: optional + Value type: + Definition: select that the regulator is supplying the active CPU(s) + and can only be disabled with the assistans from the RPM + after going to sleep + Standard regulator bindings are used inside switch low-dropout regulator subnodes. Check Documentation/devicetree/bindings/regulator/regulator.txt for more details. @@ -190,6 +204,13 @@ more details. 2740000, 2400000, 2130000, 1920000, 1750000, 1600000, 1480000, 1370000, 1280000, 1200000 +- qcom,rpm-assisted-disable: + Usage: optional + Value type: + Definition: select that the regulator is supplying the active CPU(s) + and can only be disabled with the assistans from the RPM + after going to sleep + Standard regulator bindings are used inside negative charge pump regulator subnodes. Check Documentation/devicetree/bindings/regulator/regulator.txt for more details. @@ -215,6 +236,13 @@ more details. QCOM_RPM_PM8921_LVS1 - QCOM_RPM_PM8921_LVS7, QCOM_RPM_PM8921_MVS +- qcom,rpm-assisted-disable: + Usage: optional + Value type: + Definition: select that the regulator is supplying the active CPU(s) + and can only be disabled with the assistans from the RPM + after going to sleep + = EXAMPLE #include diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c index 4fc1c7e..2a78f59 100644 --- a/drivers/regulator/qcom_rpm-regulator.c +++ b/drivers/regulator/qcom_rpm-regulator.c @@ -24,6 +24,7 @@ #include #define MAX_REQUEST_LEN 2 +#define RPM_NUM_STATES 2 struct request_member { int word; @@ -61,13 +62,15 @@ struct qcom_rpm_reg { const struct rpm_reg_parts *parts; int resource; - u32 val[MAX_REQUEST_LEN]; + u32 val[RPM_NUM_STATES][MAX_REQUEST_LEN]; int uV; int is_enabled; bool supports_force_mode_auto; bool supports_force_mode_bypass; + + bool rpm_assist; }; static const struct rpm_reg_parts rpm8660_ldo_parts = { @@ -188,19 +191,20 @@ static const struct regulator_linear_range ncp_ranges[] = { }; static int rpm_reg_write(struct qcom_rpm_reg *vreg, + int state, const struct request_member *req, const int value) { if (WARN_ON((value << req->shift) & ~req->mask)) return -EINVAL; - vreg->val[req->word] &= ~req->mask; - vreg->val[req->word] |= value << req->shift; + vreg->val[state][req->word] &= ~req->mask; + vreg->val[state][req->word] |= value << req->shift; return qcom_rpm_write(vreg->rpm, - RPM_ACTIVE_STATE, + state, vreg->resource, - vreg->val, + vreg->val[state], vreg->parts->request_len); } @@ -222,8 +226,11 @@ static int rpm_reg_set_mV_sel(struct regulator_dev *rdev, mutex_lock(&vreg->lock); vreg->uV = uV; - if (vreg->is_enabled) - ret = rpm_reg_write(vreg, req, vreg->uV / 1000); + if (vreg->is_enabled) { + ret = rpm_reg_write(vreg, RPM_ACTIVE_STATE, req, vreg->uV / 1000); + if (!ret && vreg->rpm_assist) + ret = rpm_reg_write(vreg, RPM_SLEEP_STATE, req, vreg->uV / 1000); + } mutex_unlock(&vreg->lock); return ret; @@ -247,8 +254,11 @@ static int rpm_reg_set_uV_sel(struct regulator_dev *rdev, mutex_lock(&vreg->lock); vreg->uV = uV; - if (vreg->is_enabled) - ret = rpm_reg_write(vreg, req, vreg->uV); + if (vreg->is_enabled) { + ret = rpm_reg_write(vreg, RPM_ACTIVE_STATE, req, vreg->uV); + if (!ret && vreg->rpm_assist) + ret = rpm_reg_write(vreg, RPM_SLEEP_STATE, req, vreg->uV); + } mutex_unlock(&vreg->lock); return ret; @@ -266,13 +276,16 @@ static int rpm_reg_mV_enable(struct regulator_dev *rdev) struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); const struct rpm_reg_parts *parts = vreg->parts; const struct request_member *req = &parts->mV; + int state; int ret; if (req->mask == 0) return -EINVAL; + state = vreg->rpm_assist ? RPM_SLEEP_STATE : RPM_ACTIVE_STATE; + mutex_lock(&vreg->lock); - ret = rpm_reg_write(vreg, req, vreg->uV / 1000); + ret = rpm_reg_write(vreg, state, req, vreg->uV / 1000); if (!ret) vreg->is_enabled = 1; mutex_unlock(&vreg->lock); @@ -285,13 +298,16 @@ static int rpm_reg_uV_enable(struct regulator_dev *rdev) struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); const struct rpm_reg_parts *parts = vreg->parts; const struct request_member *req = &parts->uV; + int state; int ret; if (req->mask == 0) return -EINVAL; + state = vreg->rpm_assist ? RPM_SLEEP_STATE : RPM_ACTIVE_STATE; + mutex_lock(&vreg->lock); - ret = rpm_reg_write(vreg, req, vreg->uV); + ret = rpm_reg_write(vreg, state, req, vreg->uV); if (!ret) vreg->is_enabled = 1; mutex_unlock(&vreg->lock); @@ -304,13 +320,16 @@ static int rpm_reg_switch_enable(struct regulator_dev *rdev) struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); const struct rpm_reg_parts *parts = vreg->parts; const struct request_member *req = &parts->enable_state; + int state; int ret; if (req->mask == 0) return -EINVAL; + state = vreg->rpm_assist ? RPM_SLEEP_STATE : RPM_ACTIVE_STATE; + mutex_lock(&vreg->lock); - ret = rpm_reg_write(vreg, req, 1); + ret = rpm_reg_write(vreg, state, req, 1); if (!ret) vreg->is_enabled = 1; mutex_unlock(&vreg->lock); @@ -323,13 +342,16 @@ static int rpm_reg_mV_disable(struct regulator_dev *rdev) struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); const struct rpm_reg_parts *parts = vreg->parts; const struct request_member *req = &parts->mV; + int state; int ret; if (req->mask == 0) return -EINVAL; + state = vreg->rpm_assist ? RPM_SLEEP_STATE : RPM_ACTIVE_STATE; + mutex_lock(&vreg->lock); - ret = rpm_reg_write(vreg, req, 0); + ret = rpm_reg_write(vreg, state, req, 0); if (!ret) vreg->is_enabled = 0; mutex_unlock(&vreg->lock); @@ -342,13 +364,16 @@ static int rpm_reg_uV_disable(struct regulator_dev *rdev) struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); const struct rpm_reg_parts *parts = vreg->parts; const struct request_member *req = &parts->uV; + int state; int ret; if (req->mask == 0) return -EINVAL; + state = vreg->rpm_assist ? RPM_SLEEP_STATE : RPM_ACTIVE_STATE; + mutex_lock(&vreg->lock); - ret = rpm_reg_write(vreg, req, 0); + ret = rpm_reg_write(vreg, state, req, 0); if (!ret) vreg->is_enabled = 0; mutex_unlock(&vreg->lock); @@ -361,13 +386,16 @@ static int rpm_reg_switch_disable(struct regulator_dev *rdev) struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev); const struct rpm_reg_parts *parts = vreg->parts; const struct request_member *req = &parts->enable_state; + int state; int ret; if (req->mask == 0) return -EINVAL; + state = vreg->rpm_assist ? RPM_SLEEP_STATE : RPM_ACTIVE_STATE; + mutex_lock(&vreg->lock); - ret = rpm_reg_write(vreg, req, 0); + ret = rpm_reg_write(vreg, state, req, 0); if (!ret) vreg->is_enabled = 0; mutex_unlock(&vreg->lock); @@ -590,8 +618,11 @@ static int rpm_reg_set(struct qcom_rpm_reg *vreg, if (req->mask == 0 || (value << req->shift) & ~req->mask) return -EINVAL; - vreg->val[req->word] &= ~req->mask; - vreg->val[req->word] |= value << req->shift; + vreg->val[RPM_ACTIVE_STATE][req->word] &= ~req->mask; + vreg->val[RPM_ACTIVE_STATE][req->word] |= value << req->shift; + + vreg->val[RPM_SLEEP_STATE][req->word] &= ~req->mask; + vreg->val[RPM_SLEEP_STATE][req->word] |= value << req->shift; return 0; } @@ -690,6 +721,9 @@ static int rpm_reg_probe(struct platform_device *pdev) } } + key = "qcom,rpm-assisted-disable"; + vreg->rpm_assist = of_property_read_bool(pdev->dev.of_node, key); + if (vreg->parts->freq.mask) { ret = rpm_reg_of_parse_freq(&pdev->dev, vreg); if (ret < 0)