From mboxrd@z Thu Jan 1 00:00:00 1970 From: Lina Iyer Subject: Re: [PATCH v4] regulator: qcom-saw: Add support for SAW regulators Date: Tue, 9 Feb 2016 15:21:54 -0700 Message-ID: <20160209222154.GB1646@linaro.org> References: <1455023549-30836-1-git-send-email-georgi.djakov@linaro.org> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii; format=flowed Return-path: Received: from mail-pf0-f179.google.com ([209.85.192.179]:34671 "EHLO mail-pf0-f179.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756020AbcBIWV7 (ORCPT ); Tue, 9 Feb 2016 17:21:59 -0500 Received: by mail-pf0-f179.google.com with SMTP id x65so769223pfb.1 for ; Tue, 09 Feb 2016 14:21:58 -0800 (PST) Content-Disposition: inline In-Reply-To: <1455023549-30836-1-git-send-email-georgi.djakov@linaro.org> Sender: linux-arm-msm-owner@vger.kernel.org List-Id: linux-arm-msm@vger.kernel.org To: Georgi Djakov Cc: broonie@kernel.org, lgirdwood@gmail.com, andy.gross@linaro.org, sboyd@codeaurora.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org On Tue, Feb 09 2016 at 06:13 -0700, Georgi Djakov wrote: >The SAW (Subsystem Power Manager and Adaptive Voltage Scaling Wrapper) >is part of the SPM subsystem. It is a hardware block in the Qualcomm >chipsets that regulates the power to the CPU cores on platform such as >apq8064, msm8974, apq8084 and others. > >Signed-off-by: Georgi Djakov >--- > >Changes since v3 (https://lkml.org/lkml/2016/2/3/829) > * Add MFD dependency to Kconfig > * Rename SAW2 to SAW as we may support other SAW generations than just 2 > * Increase timeout to 100us > >Changes since v2 (https://lkml.org/lkml/2016/1/28/481) > * Address the comments from Mark. Thanks! > - Implement regulator_get_voltage_sel() instead of regulator_get_voltage() > - Add cpu_relax() in the loop > - Specify ramp_delay > - Drop the legacy "regulator-name" property > >Changes since v1 (https://lkml.org/lkml/2015/12/18/629) > * Move into a separate regulator driver > > .../bindings/regulator/qcom,saw-regulator.txt | 31 +++ > drivers/regulator/Kconfig | 12 + > drivers/regulator/Makefile | 1 + > drivers/regulator/qcom_saw-regulator.c | 229 ++++++++++++++++++++ > 4 files changed, 273 insertions(+) > create mode 100644 Documentation/devicetree/bindings/regulator/qcom,saw-regulator.txt > create mode 100644 drivers/regulator/qcom_saw-regulator.c > >diff --git a/Documentation/devicetree/bindings/regulator/qcom,saw-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,saw-regulator.txt >new file mode 100644 >index 000000000000..977fec08b2ae >--- /dev/null >+++ b/Documentation/devicetree/bindings/regulator/qcom,saw-regulator.txt >@@ -0,0 +1,31 @@ >+Qualcomm SAW Regulators >+ >+SAW (Subsystem Power Manager and Adaptive Voltage Scaling Wrapper) is a hardware >+block in the Qualcomm chipsets that regulates the power to the CPU cores on devices >+such as APQ8064, MSM8974, APQ8084 and others. >+ >+- compatible: >+ Usage: required >+ Value type: >+ Definition: must be one of: >+ "qcom,apq8064-saw2-v1.1-regulator" >+ >+Example: >+ saw0: power-controller@2089000 { >+ compatible = "qcom,apq8064-saw2-v1.1-cpu", "qcom,saw2", "syscon", "simple-mfd"; >+ reg = <0x02089000 0x1000>, <0x02009000 0x1000>; >+ #address-cells = <1>; >+ #size-cells = <1>; >+ >+ saw0_regulator: regulator@2089000 { >+ compatible = "qcom,apq8064-saw2-v1.1-regulator"; >+ regulator-always-on; >+ regulator-min-microvolt = <825000>; >+ regulator-max-microvolt = <1250000>; >+ }; >+ }; >+ >+ >+ &CPU0 { >+ cpu-supply = <&saw0_regulator>; >+ }; >diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig >index 74a6354eaefa..6d5f4fce1d75 100644 >--- a/drivers/regulator/Kconfig >+++ b/drivers/regulator/Kconfig >@@ -558,6 +558,18 @@ config REGULATOR_QCOM_RPM > Qualcomm RPM as a module. The module will be named > "qcom_rpm-regulator". > >+config REGULATOR_QCOM_SAW >+ tristate "Qualcomm SAW regulator driver" >+ depends on (ARCH_QCOM || COMPILE_TEST) && MFD_SYSCON >+ help >+ If you say yes to this option, support will be included for the >+ regulators providing power to the CPU cores on devices such as >+ APQ8064. >+ >+ Say M here if you want to include support for the CPU core voltage >+ regulators as a module. The module will be named >+ "qcom_saw-regulator". >+ > config REGULATOR_QCOM_SMD_RPM > tristate "Qualcomm SMD based RPM regulator driver" > depends on QCOM_SMD_RPM >diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile >index 348cfd727350..75a0b4a8f1b2 100644 >--- a/drivers/regulator/Makefile >+++ b/drivers/regulator/Makefile >@@ -64,6 +64,7 @@ obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o > obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o > obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o > obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o >+obj-$(CONFIG_REGULATOR_QCOM_SAW)+= qcom_saw-regulator.o > obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o > obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o > obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o >diff --git a/drivers/regulator/qcom_saw-regulator.c b/drivers/regulator/qcom_saw-regulator.c >new file mode 100644 >index 000000000000..c800f16adaf0 >--- /dev/null >+++ b/drivers/regulator/qcom_saw-regulator.c >@@ -0,0 +1,229 @@ >+/* >+ * Copyright (c) 2016, Linaro Limited. All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License version 2 and >+ * only version 2 as published by the Free Software Foundation. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ */ >+ >+#include >+#include >+#include >+#include >+#include >+#include >+#include >+#include >+#include >+#include >+ >+#define SPM_REG_STS_1 0x10 >+#define SPM_REG_VCTL 0x14 >+#define SPM_REG_PMIC_DATA_0 0x28 >+#define SPM_REG_PMIC_DATA_1 0x2c >+#define SPM_REG_RST 0x30 >+ These register offsets are SoC specific. You may want to follow the model of drivers/soc/qcom/spm.c in getting register offsets. While I see that you are only supporting APQ8064 with this patch, you probably would want to think a bit far ahead. To support any other QCOM SoC, you would need extensive changes. >+struct saw_vreg { >+ struct device *dev; >+ struct regmap *regmap; >+ struct regulator_desc rdesc; >+ struct regulator_dev *rdev; >+ unsigned int sel; >+}; >+ >+struct spm_vlevel_data { >+ struct saw_vreg *vreg; >+ unsigned int sel; >+}; >+ >+static int saw_regulator_get_voltage_sel(struct regulator_dev *rdev) >+{ >+ struct saw_vreg *vreg = rdev_get_drvdata(rdev); >+ >+ return vreg->sel; >+} >+ >+static void smp_set_vdd(void *data) >+{ >+ struct spm_vlevel_data *vdata = (struct spm_vlevel_data *)data; >+ struct saw_vreg *vreg = vdata->vreg; >+ unsigned long new_sel = vdata->sel; >+ u32 val, new_val; >+ u32 vctl, data0, data1; >+ unsigned long timeout; >+ >+ if (vreg->sel == new_sel) >+ return; >+ >+ regmap_read(vreg->regmap, SPM_REG_VCTL, &vctl); >+ regmap_read(vreg->regmap, SPM_REG_PMIC_DATA_0, &data0); >+ regmap_read(vreg->regmap, SPM_REG_PMIC_DATA_1, &data1); >+ >+ /* select the band */ >+ val = 0x80 | new_sel; >+ >+ vctl &= ~0xff; >+ vctl |= val; >+ >+ data0 &= ~0xff; >+ data0 |= val; >+ >+ data1 &= ~0x3f; >+ data1 |= val & 0x3f; >+ data1 &= ~0x3f0000; >+ data1 |= ((val & 0x3f) << 16); >+ >+ regmap_write(vreg->regmap, SPM_REG_RST, 1); >+ regmap_write(vreg->regmap, SPM_REG_VCTL, vctl); >+ regmap_write(vreg->regmap, SPM_REG_PMIC_DATA_0, data0); >+ regmap_write(vreg->regmap, SPM_REG_PMIC_DATA_1, data1); >+ >+ timeout = jiffies + usecs_to_jiffies(100); >+ do { >+ regmap_read(vreg->regmap, SPM_REG_STS_1, &new_val); >+ new_val &= 0xff; >+ if (new_val == val) { >+ vreg->sel = new_sel; >+ return; >+ } >+ >+ cpu_relax(); >+ >+ } while (time_before(jiffies, timeout)); >+ >+ pr_err("%s: Voltage not changed: %#x\n", __func__, new_val); >+} >+ >+static int saw_regulator_set_voltage_sel(struct regulator_dev *rdev, >+ unsigned selector) >+{ >+ struct saw_vreg *vreg = rdev_get_drvdata(rdev); >+ struct spm_vlevel_data data; >+ int cpu = rdev_get_id(rdev); >+ >+ data.vreg = vreg; >+ data.sel = selector; >+ >+ return smp_call_function_single(cpu, smp_set_vdd, &data, true); >+} >+ >+static struct regulator_ops saw_regulator_ops = { >+ .list_voltage = regulator_list_voltage_linear_range, >+ .set_voltage_sel = saw_regulator_set_voltage_sel, >+ .get_voltage_sel = saw_regulator_get_voltage_sel, >+ .set_voltage_time_sel = regulator_set_voltage_time_sel, >+}; >+ >+static struct regulator_desc saw_regulator = { >+ .owner = THIS_MODULE, >+ .type = REGULATOR_VOLTAGE, >+ .ops = &saw_regulator_ops, >+ .linear_ranges = (struct regulator_linear_range[]) { >+ REGULATOR_LINEAR_RANGE(700000, 0, 56, 12500), >+ }, >+ .n_linear_ranges = 1, >+ .n_voltages = 57, >+ .ramp_delay = 1250, >+}; >+ >+static struct saw_vreg *saw_get_drv(struct platform_device *pdev, >+ int *vreg_cpu) >+{ >+ struct saw_vreg *vreg = NULL; >+ struct device_node *cpu_node, *saw_node; >+ int cpu; >+ bool found; >+ >+ for_each_possible_cpu(cpu) { >+ cpu_node = of_cpu_device_node_get(cpu); >+ if (!cpu_node) >+ continue; >+ saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); >+ found = (saw_node == pdev->dev.of_node->parent); >+ of_node_put(saw_node); >+ of_node_put(cpu_node); >+ if (found) >+ break; >+ } >+ >+ if (found) { >+ vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL); >+ if (vreg) >+ *vreg_cpu = cpu; >+ } >+ >+ return vreg; >+} >+ >+static const struct of_device_id qcom_saw_regulator_match[] = { >+ { .compatible = "qcom,apq8064-saw2-v1.1-regulator" }, >+ { } >+}; >+MODULE_DEVICE_TABLE(of, qcom_saw_regulator_match); >+ >+static int qcom_saw_regulator_probe(struct platform_device *pdev) >+{ >+ struct device *dev = &pdev->dev; >+ struct device_node *np = dev->of_node; >+ struct device_node *saw_np; >+ struct saw_vreg *vreg; >+ struct regulator_config config = { }; >+ int ret = 0, cpu = 0; >+ char name[] = "kraitXX"; >+ >+ vreg = saw_get_drv(pdev, &cpu); >+ if (!vreg) >+ return -EINVAL; >+ >+ saw_np = of_get_parent(np); >+ if (!saw_np) >+ return -ENODEV; >+ >+ vreg->regmap = syscon_node_to_regmap(saw_np); >+ of_node_put(saw_np); >+ if (IS_ERR(config.regmap)) >+ return PTR_ERR(config.regmap); >+ >+ snprintf(name, sizeof(name), "krait%d", cpu); >+ >+ config.regmap = vreg->regmap; >+ config.dev = &pdev->dev; >+ config.of_node = np; >+ config.driver_data = vreg; >+ >+ vreg->rdesc = saw_regulator; >+ vreg->rdesc.id = cpu; >+ vreg->rdesc.name = name; >+ 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); >+ if (IS_ERR(vreg->rdev)) { >+ ret = PTR_ERR(vreg->rdev); >+ dev_err(dev, "failed to register SAW regulator: %d\n", ret); >+ return ret; >+ } >+ >+ return 0; >+} >+ >+static struct platform_driver qcom_saw_regulator_driver = { >+ .driver = { >+ .name = "qcom-saw-regulator", >+ .of_match_table = qcom_saw_regulator_match, >+ }, >+ .probe = qcom_saw_regulator_probe, >+}; >+ >+module_platform_driver(qcom_saw_regulator_driver); >+ builtin_platform_driver() perhaps ? >+MODULE_ALIAS("platform:qcom-saw-regulator"); >+MODULE_DESCRIPTION("Qualcomm SAW regulator driver"); >+MODULE_AUTHOR("Georgi Djakov "); >+MODULE_LICENSE("GPL v2");