diff options
author | Linaro CI <ci_notify@linaro.org> | 2018-11-08 21:20:22 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2018-11-08 21:20:22 +0000 |
commit | d2b6ef17ae073654a17ca9ebc676c9e7f67f122d (patch) | |
tree | 4ace927a119c58ccce33960459ed970bd18b72e6 | |
parent | 2496bb8a4d12d2923323e0cade7e849fe7edc2d8 (diff) | |
parent | 4aedcff1504e2d4242739c8c2c5fdcb2ec20b7db (diff) |
Merge remote-tracking branch 'db600c/qcomlt-db600c' into integration-linux-qcomlt
# Conflicts:
# drivers/regulator/Kconfig
# drivers/regulator/Makefile
-rw-r--r-- | Documentation/devicetree/bindings/pci/qcom,pcie.txt | 1 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/regulator/qcom,saw-regulator.txt | 31 | ||||
-rw-r--r-- | arch/arm/boot/dts/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom-apq8064-db600c.dts | 572 | ||||
-rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 21 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 12 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/qcom_saw-regulator.c | 229 |
8 files changed, 867 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index 1fd703bd73e0..756cbd69b219 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -88,6 +88,7 @@ Definition: Should contain the following entries - "core" Clocks the pcie hw block - "phy" Clocks the pcie PHY block + - "ref" Clocks the pcie refclk - clock-names: Usage: required for apq8084/ipq4019 Value type: <stringlist> 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: <string> + 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/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index b0e966d625b9..87ad006b729f 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -788,6 +788,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ qcom-apq8064-ifc6410.dtb \ qcom-apq8064-sony-xperia-yuga.dtb \ qcom-apq8064-asus-nexus7-flo.dtb \ + qcom-apq8064-db600c.dtb \ qcom-apq8074-dragonboard.dtb \ qcom-apq8084-ifc6540.dtb \ qcom-apq8084-mtp.dtb \ diff --git a/arch/arm/boot/dts/qcom-apq8064-db600c.dts b/arch/arm/boot/dts/qcom-apq8064-db600c.dts new file mode 100644 index 000000000000..1e3895a98727 --- /dev/null +++ b/arch/arm/boot/dts/qcom-apq8064-db600c.dts @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "qcom-apq8064-v2.0.dtsi" +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/pinctrl/qcom,pmic-gpio.h> +#include <dt-bindings/pinctrl/qcom,pmic-mpp.h> +#include <dt-bindings/mfd/qcom-rpm.h> +#include <dt-bindings/sound/qcom,q6afe.h> +#include <dt-bindings/sound/qcom,q6asm.h> + +/ { + model = "Qualcomm Technologies, Inc. DB600c"; + compatible = "arrow,apq8064-db600c", "qcom,apq8064"; + + aliases { + serial0 = &gsbi7_serial; + serial1 = &gsbi1_serial; + serial2 = &gsbi6_serial; + i2c0 = &gsbi2_i2c; + i2c1 = &gsbi3_i2c; + i2c2 = &gsbi4_i2c; + i2c3 = &gsbi7_i2c; + spi0 = &gsbi5_spi; + }; + + regulators { + compatible = "simple-bus"; + vph: regulator-fixed@1 { + compatible = "regulator-fixed"; + regulator-min-microvolt = <4500000>; + regulator-max-microvolt = <4500000>; + regulator-name = "VPH"; + regulator-type = "voltage"; + regulator-boot-on; + }; + + /* on board fixed 3.3v supply */ + vcc3v3: vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "VCC3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + }; + + clocks { + compatible = "simple-bus"; + pcie_refclk: pcie-refclk { + pinctrl-0 = <&pcie_pins>; + pinctrl-names = "default"; + compatible = "gpio-gate-clock"; + clocks = <&rpmcc RPM_XO_A0>; + #clock-cells = <0>; + enable-gpios = <&pm8921_gpio 22 GPIO_ACTIVE_HIGH>; + }; + }; + + hdmi-out { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <&hdmi_out>; + }; + }; + }; + + soc { + pinctrl@800000 { + card_detect: card_detect { + mux { + pins = "gpio26"; + function = "gpio"; + bias-disable; + }; + }; + + user_leds: user-leds { + mux { + pins = "gpio6", "gpio7", "gpio35", "gpio86"; + function = "gpio"; + }; + + conf { + pins = "gpio6", "gpio7", "gpio35", "gpio86"; + function = "gpio"; + output-low; + }; + }; + + i2c1 { + pinconf { + drive-strength = <8>; + }; + }; + + i2c2 { + pinconf { + drive-strength = <8>; + }; + }; + + i2c3 { + pinconf { + drive-strength = <8>; + }; + }; + + i2c4 { + pinconf { + drive-strength = <8>; + }; + }; + + i2c5 { + pinconf { + drive-strength = <8>; + }; + }; + + i2c6 { + pinconf { + drive-strength = <8>; + }; + }; + + i2c7 { + pinconf { + drive-strength = <8>; + }; + }; + + i2c1 { + pinconf { + drive-strength = <8>; + }; + }; + + spi5_default { + pinconf { + drive-strength = <8>; + }; + + pinconf_cs { + drive-strength = <8>; + }; + }; + }; + + qcom,ssbi@500000 { + pmic@0 { + gpio@150 { + pcie_pins: pcie_pins { + pios { + pins = "gpio22"; + function = "normal"; + power-source = <PM8921_GPIO_VPH>; + }; + }; + }; + + mpps@50 { + pcie_perst: pcie-perst { + pinconf { + pins = "mpp1"; + function = "digital"; + power-source = <PM8921_MPP_S4>; + output-low; + }; + }; + + mpp_leds: mpp-leds { + pinconf { + pins = "mpp7", "mpp8", "mpp9", "mpp10", "mpp11", "mpp12"; + function = "digital"; + power-source = <PM8921_MPP_VPH>; + drive-strength = <12>; + output-high; + }; + }; + }; + }; + }; + + rpm@108000 { + regulators { + vdd_s1-supply = <&vph>; + vdd_s2-supply = <&vph>; + vdd_s3-supply = <&vph>; + vdd_s4-supply = <&vph>; + vdd_s5-supply = <&vph>; + vdd_s6-supply = <&vph>; + vdd_s7-supply = <&vph>; + + vdd_l1_l2_l12_l18-supply = <&pm8921_s4>; + vdd_l3_l15_l17-supply = <&vph>; + vdd_l4_l14-supply = <&vph>; + vdd_l5_l8_l16-supply = <&vph>; + vdd_l6_l7-supply = <&vph>; + vdd_l9_l11-supply = <&vph>; + vdd_l10_l22-supply = <&vph>; + vdd_l21_l23_l29-supply = <&vph>; + + vdd_l24-supply = <&pm8921_s1>; + vdd_l25-supply = <&pm8921_s1>; + vdd_l26-supply = <&pm8921_s7>; + vdd_l27-supply = <&pm8921_s7>; + vdd_l28-supply = <&pm8921_s7>; + + vin_lvs1_3_6-supply = <&pm8921_s4>; + vin_lvs2-supply = <&pm8921_s1>; + vin_lvs4_5_7-supply = <&pm8921_s4>; + + s1 { + regulator-always-on; + regulator-min-microvolt = <1225000>; + regulator-max-microvolt = <1225000>; + qcom,switch-mode-frequency = <3200000>; + bias-pull-down; + }; + + s2 { + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + qcom,switch-mode-frequency = <1600000>; + bias-pull-down; + regulator-always-on; + }; + + s3 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1400000>; + qcom,switch-mode-frequency = <4800000>; + }; + + s4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + qcom,switch-mode-frequency = <1600000>; + qcom,force-mode = <QCOM_RPM_FORCE_MODE_AUTO>; + bias-pull-down; + regulator-always-on; + }; + + s7 { + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1300000>; + qcom,switch-mode-frequency = <3200000>; + }; + + l3 { + regulator-min-microvolt = <3050000>; + regulator-max-microvolt = <3300000>; + bias-pull-down; + }; + + l4 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1800000>; + bias-pull-down; + }; + + l5 { + regulator-min-microvolt = <2750000>; + regulator-max-microvolt = <3000000>; + bias-pull-down; + regulator-boot-on; + regulator-always-on; + }; + + l6 { + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + bias-pull-down; + }; + + l7 { + regulator-min-microvolt = <2950000>; + regulator-max-microvolt = <2950000>; + regulator-boot-on; + regulator-always-on; + }; + + /** + /** + * 1.8v required on LS expansion + * for mezzanine boards + */ + l15 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + l23 { + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <1900000>; + bias-pull-down; + }; + + l26 { + regulator-min-microvolt = < 375000>; + regulator-max-microvolt = <1050000>; + bias-pull-down; + }; + + lvs6 { + bias-pull-down; + }; + + lvs7 { + bias-pull-down; + }; + }; + }; + + gsbi@12440000 { + status = "okay"; + qcom,mode = <GSBI_PROT_UART_W_FC>; + serial@12450000 { + label = "LS-UART1"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&gsbi1_uart_4pins>; + }; + }; + + pil_q6v4: pil@28800000 { + qcom,pll-supply = <&pm8921_l26>; + qcom,pll-uV = <1050000>; + }; + + gsbi@12480000 { + status = "okay"; + qcom,mode = <GSBI_PROT_I2C>; + i2c@124a0000 { + /* On Low speed expansion and Sensors */ + label = "LS-I2C0"; + status = "okay"; + }; + }; + + gsbi@16200000 { + status = "okay"; + qcom,mode = <GSBI_PROT_I2C>; + i2c@16280000 { + /* On Low speed expansion */ + status = "okay"; + label = "LS-I2C1"; + clock-frequency = <200000>; + eeprom@52 { + compatible = "atmel,24c128"; + reg = <0x52>; + pagesize = <64>; + }; + }; + }; + + gsbi@16300000 { + status = "okay"; + qcom,mode = <GSBI_PROT_I2C>; + i2c@16380000 { + /* On High speed expansion */ + label = "HS-CAM-I2C3"; + status = "okay"; + }; + }; + + gsbi@1a200000 { + status = "okay"; + qcom,mode = <GSBI_PROT_SPI>; + spi@1a280000 { + /* On Low speed expansion */ + label = "LS-SPI0"; + status = "okay"; + num-cs = <1>; + cs-gpios = <&tlmm_pinmux 53 0>; + spi-sram@0 { + status = "okay"; + #address-cells = <1>; + #size-cells = <1>; + compatible= "microchip,mchp23lcv1024"; + reg = <0>; + spi-max-frequency = <10000000>; + }; + }; + }; + + gsbi@16500000 { + status = "okay"; + qcom,mode = <GSBI_PROT_UART_W_FC>; + serial@16540000 { + label = "BT-UART"; + status = "okay"; + pinctrl-name = "default"; + pinctrl-0 = <&gsbi6_uart_4pins>; + }; + }; + + + /* DEBUG UART */ + gsbi@16600000 { + status = "okay"; + qcom,mode = <GSBI_PROT_I2C_UART>; + serial@16640000 { + label = "LS-UART0"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&gsbi7_uart_2pins>; + + }; + + i2c@16680000 { + /* On High speed expansion */ + status = "okay"; + label = "HS-CAM-I2C2"; + }; + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&user_leds>, <&mpp_leds>; + + compatible = "gpio-leds"; + + user-led0 { + label = "user0-led"; + gpios = <&pm8921_mpps 9 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + user-led1 { + label = "user1-led"; + gpios = <&pm8921_mpps 10 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + user-led2 { + label = "user2-led"; + gpios = <&pm8921_mpps 11 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + + user-led3 { + label = "user3-led"; + gpios = <&pm8921_mpps 12 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "none"; + default-state = "off"; + }; + + wifi-led { + label = "WiFi-led"; + gpios = <&pm8921_mpps 7 GPIO_ACTIVE_HIGH>; + default-state = "off"; + linux,default-trigger = "none"; + }; + + bt-led { + label = "BT-led"; + gpios = <&pm8921_mpps 8 GPIO_ACTIVE_HIGH>; + default-state = "off"; + linux,default-trigger = "none"; + }; + }; + + pci@1b500000 { + status = "okay"; + vdda-supply = <&pm8921_s3>; + vdda_phy-supply = <&pm8921_lvs6>; + vdda_refclk-supply = <&vcc3v3>; + pinctrl-0 = <&pcie_perst>; + pinctrl-names = "default"; + perst-gpio = <&pm8921_mpps 1 GPIO_ACTIVE_LOW>; + clocks = <&gcc PCIE_A_CLK>, + <&gcc PCIE_H_CLK>, + <&gcc PCIE_PHY_REF_CLK>, + <&pcie_refclk>; + clock-names = "core", "iface", "phy", "ref"; + }; + + /* OTG */ + usb@12500000 { + status = "okay"; + dr_mode = "otg"; + ulpi { + phy { + v3p3-supply = <&pm8921_l3>; + v1p8-supply = <&pm8921_l4>; + }; + }; + }; + + usb@12520000 { + status = "okay"; + dr_mode = "host"; + ulpi { + phy { + v3p3-supply = <&pm8921_l3>; + v1p8-supply = <&pm8921_l23>; + }; + }; + }; + + usb@12530000 { + status = "okay"; + dr_mode = "host"; + ulpi { + phy { + v3p3-supply = <&pm8921_l3>; + v1p8-supply = <&pm8921_l23>; + }; + }; + }; + + amba { + /* eMMC */ + sdcc1: sdcc@12400000 { + status = "okay"; + vmmc-supply = <&pm8921_l5>; + vqmmc-supply = <&pm8921_s4>; + }; + + /* External micro SD card */ + sdcc3: sdcc@12180000 { + status = "okay"; + vmmc-supply = <&pm8921_l6>; + vqmmc-supply = <&pm8921_l7>; + pinctrl-names = "default"; + pinctrl-0 = <&sdcc3_pins &card_detect>; + cd-gpios = <&tlmm_pinmux 26 GPIO_ACTIVE_LOW>; + }; + }; + + hdmi-tx@4a00000 { + status = "okay"; + core-vdda-supply = <&pm8921_hdmi_switch>; + hdmi-mux-supply = <&vcc3v3>; + + hpd-gpio = <&tlmm_pinmux 72 GPIO_ACTIVE_HIGH>; + + ports { + port@1 { + endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + }; + }; + + hdmi-phy@4a00400 { + status = "okay"; + core-vdda-supply = <&pm8921_hdmi_switch>; + }; + + mdp@5100000 { + status = "okay"; + + ports { + port@3 { + endpoint { + remote-endpoint = <&hdmi_in>; + }; + }; + }; + }; + }; +}; diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index e21ea1a2f6d3..b9f60e4ebb96 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -85,6 +85,7 @@ struct qcom_pcie_resources_2_1_0 { struct clk *iface_clk; struct clk *core_clk; + struct clk *ref_clk; struct clk *phy_clk; struct reset_control *pci_reset; struct reset_control *axi_reset; @@ -227,6 +228,15 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) if (IS_ERR(res->iface_clk)) return PTR_ERR(res->iface_clk); + res->ref_clk = devm_clk_get(dev, "ref"); + + if (IS_ERR(res->ref_clk)) { + if (PTR_ERR(res->ref_clk) == -EPROBE_DEFER) + return PTR_ERR(res->ref_clk); + + res->ref_clk = NULL; + } + res->core_clk = devm_clk_get(dev, "core"); if (IS_ERR(res->core_clk)) return PTR_ERR(res->core_clk); @@ -265,6 +275,7 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) reset_control_assert(res->por_reset); reset_control_assert(res->pci_reset); clk_disable_unprepare(res->iface_clk); + clk_disable_unprepare(res->ref_clk); clk_disable_unprepare(res->core_clk); clk_disable_unprepare(res->phy_clk); regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); @@ -290,10 +301,16 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) goto err_assert_ahb; } + ret = clk_prepare_enable(res->ref_clk); + if (ret) { + dev_err(dev, "cannot prepare/enable ref clock\n"); + goto err_assert_ahb; + } + ret = clk_prepare_enable(res->iface_clk); if (ret) { dev_err(dev, "cannot prepare/enable iface clock\n"); - goto err_assert_ahb; + goto err_clk_iface; } ret = clk_prepare_enable(res->phy_clk); @@ -366,6 +383,8 @@ err_clk_core: clk_disable_unprepare(res->phy_clk); err_clk_phy: clk_disable_unprepare(res->iface_clk); +err_clk_iface: + clk_disable_unprepare(res->ref_clk); err_assert_ahb: regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 926cee0d0b5f..cabcbe982a83 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -698,6 +698,18 @@ config REGULATOR_QCOM_RPMH control allows for voting on regulator state between multiple processors within the SoC. +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 72488ef11b8a..499a1620b5e8 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-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..c1411885680e --- /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 <linux/delay.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> + +#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 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 = kstrdup_const(name, GFP_KERNEL); + 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); + +MODULE_ALIAS("platform:qcom-saw-regulator"); +MODULE_DESCRIPTION("Qualcomm SAW regulator driver"); +MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>"); +MODULE_LICENSE("GPL v2"); |