From patchwork Fri Dec 18 16:14:58 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Georgi Djakov X-Patchwork-Id: 58727 Delivered-To: patch@linaro.org Received: by 10.112.89.199 with SMTP id bq7csp1123605lbb; Fri, 18 Dec 2015 08:15:20 -0800 (PST) X-Received: by 10.98.76.136 with SMTP id e8mr3347624pfj.117.1450455320644; Fri, 18 Dec 2015 08:15:20 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 80si20723127pfa.0.2015.12.18.08.15.20; Fri, 18 Dec 2015 08:15:20 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-arm-msm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-arm-msm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-arm-msm-owner@vger.kernel.org; dkim=neutral (body hash did not verify) header.i=@linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754135AbbLRQPT (ORCPT + 6 others); Fri, 18 Dec 2015 11:15:19 -0500 Received: from mail-wm0-f47.google.com ([74.125.82.47]:34602 "EHLO mail-wm0-f47.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752087AbbLRQPR (ORCPT ); Fri, 18 Dec 2015 11:15:17 -0500 Received: by mail-wm0-f47.google.com with SMTP id l126so71971170wml.1 for ; Fri, 18 Dec 2015 08:15:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=CZr5WWbWr8qR2mI7M9p5AffqPSJS4/9NgZj5dSxkBXk=; b=ORh86YOL5XIHEIlB9xr2rDZlTK7DP32HOpUs3lq7bRf8pZ/gDaEQtXKn4JzZ2TQUzb Ryt6Mncg7asQdTg3yQxcfWlyz4kg/uAwQmCW2VUbRLtoHgNXhaiZK6RF1ZerVfBDuCts PHRJrk9K+X1c+k4nVqKGLqB0BogUTp5UIbTJI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=CZr5WWbWr8qR2mI7M9p5AffqPSJS4/9NgZj5dSxkBXk=; b=htnbrxxwNkMDH3T/APVIRL4o8yp/w2dHUz+O/szIL1+hh4J/Qd6q1hTGQl5J1YPIIo wilx6xHe3TsR3p4cnzhdfOScGYyt4XFUZo7L38QFedGRyaw3OWpeQnf3an8C7oJqHgKo gZZOmnNnSAcXMpj7o/1E8EXGm49X93Vwo41GsRPRf+KLZgBAbE1jBsK8VQOOURDvXEAT AFYz7JvL6SEzGVCAqrso9lbStynnKsgLs84UgRZ2SRI/moLSj4DUJXWSOLstosfI53ni q6g+7xscUezMRhhZRQeNcvPA1K2qYBgJqmUq2I+lzY27RkZmbVbQ0F6TD6MM4Wy/jnRD eJ+Q== X-Gm-Message-State: ALoCoQkouiNsKoyzqKM151SBBLbwVqrSz7RdT7HYQ14z3b+dmOrF+bzB8d5A29Tnci0OusbUE18MwuReYk7r2QBLp2w2+WaI5A== X-Received: by 10.194.175.170 with SMTP id cb10mr5872263wjc.36.1450455315851; Fri, 18 Dec 2015 08:15:15 -0800 (PST) Received: from mms.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.googlemail.com with ESMTPSA id l7sm15426889wjx.14.2015.12.18.08.15.14 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 18 Dec 2015 08:15:14 -0800 (PST) From: Georgi Djakov To: andy.gross@linaro.org Cc: lina.iyer@linaro.org, sboyd@codeaurora.org, broonie@kernel.org, linux-soc@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, georgi.djakov@linaro.org Subject: [PATCH] soc: qcom: Add support for SAW2 regulators Date: Fri, 18 Dec 2015 18:14:58 +0200 Message-Id: <1450455298-1987-1-git-send-email-georgi.djakov@linaro.org> X-Mailer: git-send-email 1.7.9.5 Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org The SAW2 (Subsystem Power Manager and Adaptive Voltage Scaling Wrapper) is part of the SPM subsystem. It is a hardware block found on some of the Qualcomm chipsets, which regulates the power to the CPU cores. Add some basic support for it, so that we can do dynamic voltage scaling. Signed-off-by: Georgi Djakov --- drivers/soc/qcom/spm.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index b04b05a0904e..03fcee4b85d9 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -20,11 +20,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include @@ -51,6 +54,8 @@ enum spm_reg { SPM_REG_PMIC_DLY, SPM_REG_PMIC_DATA_0, SPM_REG_PMIC_DATA_1, + SPM_REG_RST, + SPM_REG_STS_1, SPM_REG_VCTL, SPM_REG_SEQ_ENTRY, SPM_REG_SPM_STS, @@ -68,9 +73,22 @@ struct spm_reg_data { u8 start_index[PM_SLEEP_MODE_NR]; }; +struct spm_vlevel_data { + struct spm_driver_data *drv; + unsigned selector; +}; + +struct saw2_vreg { + struct regulator_desc rdesc; + struct regulator_dev *rdev; + struct spm_driver_data *drv; + u32 selector; +}; + struct spm_driver_data { void __iomem *reg_base; const struct spm_reg_data *reg_data; + struct saw2_vreg *vreg; }; static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = { @@ -94,10 +112,13 @@ static const struct spm_reg_data spm_reg_8974_8084_cpu = { static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = { [SPM_REG_CFG] = 0x08, + [SPM_REG_STS_1] = 0x10, + [SPM_REG_VCTL] = 0x14, [SPM_REG_SPM_CTL] = 0x20, [SPM_REG_PMIC_DLY] = 0x24, [SPM_REG_PMIC_DATA_0] = 0x28, [SPM_REG_PMIC_DATA_1] = 0x2C, + [SPM_REG_RST] = 0x30, [SPM_REG_SEQ_ENTRY] = 0x80, }; @@ -282,6 +303,127 @@ static struct cpuidle_ops qcom_cpuidle_ops __initdata = { CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v1, "qcom,kpss-acc-v1", &qcom_cpuidle_ops); CPUIDLE_METHOD_OF_DECLARE(qcom_idle_v2, "qcom,kpss-acc-v2", &qcom_cpuidle_ops); +static int saw2_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct spm_driver_data *drv = rdev_get_drvdata(rdev); + + return regulator_list_voltage_linear_range(rdev, drv->vreg->selector); +} + +static void spm_smp_set_vdd(void *data) +{ + struct spm_vlevel_data *vdata = (struct spm_vlevel_data *)data; + struct spm_driver_data *drv = vdata->drv; + struct saw2_vreg *vreg = drv->vreg; + u32 sel = vdata->selector; + u32 val, new_val; + u32 vctl, data0, data1; + int timeout_us = 50; + + if (vreg->selector == sel) + return; + + vctl = spm_register_read(drv, SPM_REG_VCTL); + data0 = spm_register_read(drv, SPM_REG_PMIC_DATA_0); + data1 = spm_register_read(drv, SPM_REG_PMIC_DATA_1); + + /* select the band */ + val = 0x80 | sel; + + vctl &= ~0xff; + vctl |= val; + + data0 &= ~0xff; + data0 |= val; + + data1 &= ~0x3f; + data1 |= val & 0x3f; + data1 &= ~0x3f0000; + data1 |= (val & 0x3f) << 16; + + spm_register_write(drv, SPM_REG_RST, 1); + spm_register_write(drv, SPM_REG_VCTL, vctl); + spm_register_write(drv, SPM_REG_PMIC_DATA_0, data0); + spm_register_write(drv, SPM_REG_PMIC_DATA_1, data1); + + do { + new_val = spm_register_read(drv, SPM_REG_STS_1) & 0xff; + if (new_val == val) + break; + udelay(1); + } while (--timeout_us); + + if (!timeout_us) { + pr_err("%s: Voltage not changed: %#x\n", __func__, new_val); + return; + } + + if (sel > vreg->selector) { + /* PMIC internal slew rate is 1250 uV per us */ + udelay((sel - vreg->selector) * 10); + } + + vreg->selector = sel; +} + +static int saw2_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct spm_driver_data *drv = rdev_get_drvdata(rdev); + struct spm_vlevel_data data; + int cpu = rdev_get_id(rdev); + + data.drv = drv; + data.selector = selector; + + return smp_call_function_single(cpu, spm_smp_set_vdd, &data, true); +} + +static struct regulator_ops saw2_regulator_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = saw2_regulator_set_voltage_sel, + .get_voltage = saw2_regulator_get_voltage, +}; + +static struct regulator_desc saw2_regulator = { + .owner = THIS_MODULE, + .type = REGULATOR_VOLTAGE, + .ops = &saw2_regulator_ops, + .linear_ranges = (struct regulator_linear_range[]) { + REGULATOR_LINEAR_RANGE(700000, 0, 56, 12500), + }, + .n_linear_ranges = 1, + .n_voltages = 57, +}; + +static int register_saw2_regulator(struct spm_driver_data *drv, + struct platform_device *pdev, int cpu) +{ + struct device_node *np = pdev->dev.of_node; + struct saw2_vreg *vreg; + struct regulator_config config = { }; + + vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) + return -ENOMEM; + + drv->vreg = vreg; + config.driver_data = drv; + config.dev = &pdev->dev; + config.of_node = np; + + vreg->rdesc = saw2_regulator; + vreg->rdesc.id = cpu; + vreg->rdesc.name = of_get_property(np, "regulator-name", NULL); + config.init_data = of_get_regulator_init_data(&pdev->dev, + pdev->dev.of_node, + &vreg->rdesc); + + vreg->rdev = devm_regulator_register(&pdev->dev, &vreg->rdesc, &config); + + return PTR_ERR_OR_ZERO(vreg->rdev); +} + static struct spm_driver_data *spm_get_drv(struct platform_device *pdev, int *spm_cpu) { @@ -327,7 +469,7 @@ static int spm_dev_probe(struct platform_device *pdev) struct resource *res; const struct of_device_id *match_id; void __iomem *addr; - int cpu; + int cpu, ret; drv = spm_get_drv(pdev, &cpu); if (!drv) @@ -368,6 +510,11 @@ static int spm_dev_probe(struct platform_device *pdev) per_cpu(cpu_spm_drv, cpu) = drv; + ret = register_saw2_regulator(drv, pdev, cpu); + if (ret) + dev_err(&pdev->dev, "error registering SAW2 regulator: %d\n", + ret); + return 0; }