From patchwork Mon Feb 1 10:31:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Georgi Djakov X-Patchwork-Id: 60895 Delivered-To: patch@linaro.org Received: by 10.112.130.2 with SMTP id oa2csp2956998lbb; Mon, 1 Feb 2016 02:32:48 -0800 (PST) X-Received: by 10.98.65.91 with SMTP id o88mr36694031pfa.114.1454322768639; Mon, 01 Feb 2016 02:32:48 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id un7si23799882pac.228.2016.02.01.02.32.48; Mon, 01 Feb 2016 02:32:48 -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 S1753009AbcBAKcr (ORCPT + 7 others); Mon, 1 Feb 2016 05:32:47 -0500 Received: from mail-wm0-f52.google.com ([74.125.82.52]:35679 "EHLO mail-wm0-f52.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751886AbcBAKcq (ORCPT ); Mon, 1 Feb 2016 05:32:46 -0500 Received: by mail-wm0-f52.google.com with SMTP id r129so63123837wmr.0 for ; Mon, 01 Feb 2016 02:32:45 -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=XSRZ3dhFJN7Dz5Mz+xTu6Xksy1shgf5oQv0u6IMRrwQ=; b=KPe35EV+drwRmzxB32t5fzPauq/1rbfPrmWaSAvMHZ4IYEKd8+iA2/icac10h0AiyO 1CHyQx/6DxIb4ALy80qtNLdoY/ETV8J0gDKTbe/xag5OTRSwHuI6v9tlfzSK7ELbx6LS f+92yRm359VRgHu3sWFoCG/LmE1JnlflNHDc0= 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=XSRZ3dhFJN7Dz5Mz+xTu6Xksy1shgf5oQv0u6IMRrwQ=; b=LsLMkadWFKrhKeToreOdHBn/sIxXBGQsaFkDeUUTfJkK8wlWuPjdHh3q/+yyhvcCUB Qsw1DmFeN0DlUK9xk540mhClYRtwtGV7vV3sToAzhb3hBgGliChd+Z58fHijHK7D/5dq n63ct7+jXxzPn1bvWLMYKuu36JMHcKed9PCHhYW30/E0cRS7Ql7LwfYkkKzweC0Cep7E qh1TSgHWXnsp4vYaN48YubG25XaUe+VVDrQl8ECfdyOSoAbCITY7AiQSrkL/T5UICiKr Apg4ZYMtJJO8DSA0jzZIG0dgiUSvfMANi1gJxDP7GNDaqbZjJDN+1JD1mlCQMy7BqE+R +nvg== X-Gm-Message-State: AG10YOSSTaVrb+MvnR5MThaftieeqjBRyYv1N/4LnWXkyVyF4iyUiDkoBctZkiR1uEyCGIw4 X-Received: by 10.194.93.199 with SMTP id cw7mr25252855wjb.64.1454322764935; Mon, 01 Feb 2016 02:32:44 -0800 (PST) Received: from mms.qualcomm.mm-sol.com ([37.157.136.206]) by smtp.googlemail.com with ESMTPSA id w136sm10755744wmw.0.2016.02.01.02.32.43 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 01 Feb 2016 02:32:43 -0800 (PST) From: Georgi Djakov To: broonie@kernel.org, lgirdwood@gmail.com Cc: andy.gross@linaro.org, lina.iyer@linaro.org, sboyd@codeaurora.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, georgi.djakov@linaro.org Subject: [PATCH v3] regulator: qcom-saw2: Add support for SAW2 regulators Date: Mon, 1 Feb 2016 12:31:53 +0200 Message-Id: <1454322713-18729-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 2) 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 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,saw2-regulator.txt | 31 +++ drivers/regulator/Kconfig | 12 + drivers/regulator/Makefile | 1 + drivers/regulator/qcom_saw2-regulator.c | 229 ++++++++++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/qcom,saw2-regulator.txt create mode 100644 drivers/regulator/qcom_saw2-regulator.c -- 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/Documentation/devicetree/bindings/regulator/qcom,saw2-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,saw2-regulator.txt new file mode 100644 index 000000000000..6df5a32ff9fa --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/qcom,saw2-regulator.txt @@ -0,0 +1,31 @@ +Qualcomm SAW2 Regulators + +SAW2 (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..50f70d94d01b 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_SAW2 + tristate "Qualcomm SAW2 regulator driver" + depends on ARCH_QCOM + 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_saw2-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..371ce2c36fec 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_SAW2)+= qcom_saw2-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_saw2-regulator.c b/drivers/regulator/qcom_saw2-regulator.c new file mode 100644 index 000000000000..5ab74a1745aa --- /dev/null +++ b/drivers/regulator/qcom_saw2-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 + +struct saw2_vreg { + struct device *dev; + struct regmap *regmap; + struct regulator_desc rdesc; + struct regulator_dev *rdev; + unsigned int sel; +}; + +struct spm_vlevel_data { + struct saw2_vreg *vreg; + unsigned int sel; +}; + +static int saw2_regulator_get_voltage_sel(struct regulator_dev *rdev) +{ + struct saw2_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 saw2_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(50); + 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 saw2_regulator_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct saw2_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 saw2_regulator_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_sel = saw2_regulator_set_voltage_sel, + .get_voltage_sel = saw2_regulator_get_voltage_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +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, + .ramp_delay = 1250, /* PMIC internal slew rate is 1250 uV/us */ +}; + +static struct saw2_vreg *saw2_get_drv(struct platform_device *pdev, + int *vreg_cpu) +{ + struct saw2_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_saw2_regulator_match[] = { + { .compatible = "qcom,apq8064-saw2-v1.1-regulator" }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_saw2_regulator_match); + +static int qcom_saw2_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *saw2_np; + struct saw2_vreg *vreg; + struct regulator_config config = { }; + int ret = 0, cpu = 0; + char name[] = "kraitXX"; + + vreg = saw2_get_drv(pdev, &cpu); + if (!vreg) + return -EINVAL; + + saw2_np = of_get_parent(np); + if (!saw2_np) + return -ENODEV; + + vreg->regmap = syscon_node_to_regmap(saw2_np); + of_node_put(saw2_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 = saw2_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, "error registering SAW2 regulator: %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver qcom_saw2_regulator_driver = { + .driver = { + .name = "qcom-saw2-regulator", + .of_match_table = qcom_saw2_regulator_match, + }, + .probe = qcom_saw2_regulator_probe, +}; + +module_platform_driver(qcom_saw2_regulator_driver); + +MODULE_ALIAS("platform:qcom-saw2-regulator"); +MODULE_DESCRIPTION("Qualcomm SAW2 regulator driver"); +MODULE_AUTHOR("Georgi Djakov "); +MODULE_LICENSE("GPL v2");