Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753536AbbLRRZD (ORCPT ); Fri, 18 Dec 2015 12:25:03 -0500 Received: from mail-pf0-f182.google.com ([209.85.192.182]:34196 "EHLO mail-pf0-f182.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752388AbbLRRY4 (ORCPT ); Fri, 18 Dec 2015 12:24:56 -0500 Date: Fri, 18 Dec 2015 10:24:53 -0700 From: Lina Iyer To: Georgi Djakov Cc: andy.gross@linaro.org, sboyd@codeaurora.org, broonie@kernel.org, linux-soc@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org Subject: Re: [PATCH] soc: qcom: Add support for SAW2 regulators Message-ID: <20151218172453.GF3342@linaro.org> References: <1450455298-1987-1-git-send-email-georgi.djakov@linaro.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Disposition: inline In-Reply-To: <1450455298-1987-1-git-send-email-georgi.djakov@linaro.org> User-Agent: Mutt/1.5.24 (2015-08-30) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 7099 Lines: 245 On Fri, Dec 18 2015 at 09:15 -0700, Georgi Djakov wrote: >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(-) > >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); > Not all SAWs are regulators. On 8084, only the L2 SAW is a regulator. You may want to parse the device node to see if it has a 'regulator' property and then initialize the regulator aspect of the SAW. L2 SAW nodes are not present in the upstream kernel. You can find a patch that adds cache SPM here in my WIP series at [1]. Sorry, if I have missed this point in the code. Thanks, Lina [1]. https://git.linaro.org/people/lina.iyer/linux-next.git/shortlog/refs/heads/genpd-psci-8084 >+ if (ret) >+ dev_err(&pdev->dev, "error registering SAW2 regulator: %d\n", >+ ret); >+ > return 0; > } > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/