aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinaro CI <ci_notify@linaro.org>2018-05-09 10:24:48 +0000
committerLinaro CI <ci_notify@linaro.org>2018-05-09 10:24:48 +0000
commit4e3d371c588796e60952863feb69ebdafe041599 (patch)
treee55d5c9c25e7882fb7ba6f834d3097a371854d82
parent22ca78c01b3bd4aa27d4861c0bcc087610ac6c11 (diff)
parentb197ec96e0013225de1b6b4a9ba945305be78a50 (diff)
Merge remote-tracking branch 'sdm845-rpmh-regulator/lkml/rpmh-regulator-v1' into integration-linux-qcomlt
-rw-r--r--Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt246
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/qcom_rpmh-regulator.c1124
-rw-r--r--include/dt-bindings/regulator/qcom,rpmh-regulator.h40
5 files changed, 1420 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
new file mode 100644
index 000000000000..2d86306693ac
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
@@ -0,0 +1,246 @@
+Qualcomm Technologies, Inc. RPMh Regulators
+
+rpmh-regulator devices support PMIC regulator management via the VRM and XOB
+RPMh accelerators. The APPS processor communicates with these hardware blocks
+via an RSC using command packets. The VRM allows changing four parameters for a
+given regulator: enable state, output voltage, operating mode, and minimum
+headroom voltage. The XOB allows changing only a single parameter for a given
+regulator: its enable state.
+
+=======================
+Required Node Structure
+=======================
+
+RPMh regulators must be described in two levels of device nodes. The first
+level describes the PMIC containing the regulators and must reside within an
+RPMh device node. The second level describes each regulator within the PMIC
+which is to be used on the board. Each of these regulators maps to a single
+RPMh resource.
+
+The names used for regulator nodes must match those supported by a given PMIC.
+Supported regulator node names:
+ PM8998: smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
+ PMI8998: bob
+ PM8005: smps1 - smps4
+
+==================================
+First Level Nodes - PMIC
+==================================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Must be one of: "qcom,pm8998-rpmh-regulators",
+ "qcom,pmi8998-rpmh-regulators" or
+ "qcom,pm8005-rpmh-regulators".
+
+- qcom,pmic-id
+ Usage: required
+ Value type: <string>
+ Definition: RPMh resource name suffix used for the regulators found on
+ this PMIC. Typical values: "a", "b", "c", "d", "e", "f".
+
+- vdd_s1-supply
+- vdd_s2-supply
+- vdd_s3-supply
+- vdd_s4-supply
+- vdd_s5-supply
+- vdd_s6-supply
+- vdd_s7-supply
+- vdd_s8-supply
+- vdd_s9-supply
+- vdd_s10-supply
+- vdd_s11-supply
+- vdd_s12-supply
+- vdd_s13-supply
+- vdd_l1_l27-supply
+- vdd_l2_l8_l17-supply
+- vdd_l3_l11-supply
+- vdd_l4_l5-supply
+- vdd_l6-supply
+- vdd_l7_l12_l14_l15-supply
+- vdd_l9-supply
+- vdd_l10_l23_l25-supply
+- vdd_l13_l19_l21-supply
+- vdd_l16_l28-supply
+- vdd_l18_l22-supply
+- vdd_l20_l24-supply
+- vdd_l26-supply
+- vdd_lvs1_lvs2-supply
+- vdd_lvs1_lvs2-supply
+ Usage: optional (PM8998 only)
+ Value type: <phandle>
+ Definition: phandle of the parent supply regulator of one or more of the
+ regulators for this PMIC.
+
+- vdd_bob-supply
+ Usage: optional (PMI8998 only)
+ Value type: <phandle>
+ Definition: phandle of the parent supply regulator of one or more of the
+ regulators for this PMIC.
+
+- vdd_s1-supply
+- vdd_s2-supply
+- vdd_s3-supply
+- vdd_s4-supply
+ Usage: optional (PM8005 only)
+ Value type: <phandle>
+ Definition: phandle of the parent supply regulator of one or more of the
+ regulators for this PMIC.
+
+=========================================
+Second Level Nodes - Regulators
+=========================================
+
+- regulator-name
+ Usage: optional
+ Value type: <string>
+ Definition: Specifies the name for this RPMh regulator. If not
+ specified, then the regulator's name is equal to its subnode
+ name.
+
+- regulator-min-microvolt
+ Usage: required
+ Value type: <u32>
+ Definition: For VRM resources, this is the minimum supported voltage in
+ microvolts. For XOB resources, this is the fixed output
+ voltage.
+
+- regulator-max-microvolt
+ Usage: required
+ Value type: <u32>
+ Definition: For VRM resources, this is the maximum supported voltage in
+ microvolts. For XOB resources, this is the fixed output
+ voltage.
+
+- qcom,regulator-initial-voltage
+ Usage: optional; VRM regulators only
+ Value type: <u32>
+ Definition: Specifies the initial voltage in microvolts to request for a
+ VRM regulator. Supported values are 0 to 8191000.
+
+- regulator-initial-mode
+ Usage: optional; VRM regulators only
+ Value type: <u32>
+ Definition: Specifies the initial mode to request for a VRM regulator.
+ Supported values are RPMH_REGULATOR_MODE_* which are defined
+ in [1] (i.e. 0 to 4).
+
+- regulator-allow-set-load
+ Usage: optional
+ Value type: <empty>
+ Definition: Boolean flag indicating that the the mode of this regulator
+ may be configured at runtime based upon consumer load needs.
+
+- qcom,allowed-modes
+ Usage: required if regulator-allow-set-load is specified;
+ VRM regulators only
+ Value type: <prop-encoded-array>
+ Definition: A list of integers specifying the PMIC regulator modes which
+ can be configured at runtime based upon consumer load needs.
+ Supported values are RPMH_REGULATOR_MODE_* which are defined
+ in [1] (i.e. 0 to 4). Elements must be specified in order
+ from lowest to highest value.
+
+- qcom,mode-threshold-currents
+ Usage: required if regulator-allow-set-load is specified;
+ VRM regulators only
+ Value type: <prop-encoded-array>
+ Definition: A list of integers specifying the minimum allowed load
+ current in microamps for each of the modes listed in
+ qcom,allowed-modes. The first element should always be 0.
+ Elements must be specified in order from lowest to highest
+ value.
+
+- qcom,headroom-voltage
+ Usage: optional; VRM regulators only
+ Value type: <u32>
+ Definition: Specifies the headroom voltage in microvolts to request for
+ a VRM regulator. RPMh hardware automatically ensures that
+ the parent of this regulator outputs a voltage high enough
+ to satisfy the requested headroom. Supported values are
+ 0 to 511000.
+
+ - regulator-enable-ramp-delay
+ Usage: optional
+ Value type: <u32>
+ Definition: The time in microseconds to delay after enabling a
+ regulator. Note that RPMh hardware ensures that regulator
+ output has stabilized before acknowledging a given regulator
+ enable request.
+
+- qcom,rpmh-resource-type
+ Usage: optional
+ Value type: <string>
+ Definition: RPMh accelerator type for this regulator. If not specified,
+ then the default type associated with this regulator will be
+ used. Supported values: "vrm" or "xob".
+
+- qcom,always-wait-for-ack
+ Usage: optional
+ Value type: <empty>
+ Definition: Boolean flag which indicates that the application processor
+ must wait for an ACK or a NACK from RPMh for every request
+ sent for this regulator including those which are for a
+ strictly lower power state.
+
+Other properties defined in regulator.txt may also be used.
+
+[1] include/dt-bindings/regulator/qcom,rpmh-regulator.h
+
+========
+Examples
+========
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+&apps_rsc {
+ pm8998-rpmh-regulators {
+ compatible = "qcom,pm8998-rpmh-regulators";
+ qcom,pmic-id = "a";
+
+ vdd_l7_l12_l14_l15-supply = <&pm8998_s5>;
+
+ smps2 {
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ qcom,regulator-initial-voltage = <1100000>;
+ };
+
+ pm8998_s5: smps5 {
+ regulator-min-microvolt = <1904000>;
+ regulator-max-microvolt = <2040000>;
+ qcom,regulator-initial-voltage = <1904000>;
+ };
+
+ ldo7 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,regulator-initial-voltage = <1800000>;
+ qcom,headroom-voltage = <56000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_LPM>;
+ regulator-allow-set-load;
+ qcom,allowed-modes =
+ <RPMH_REGULATOR_MODE_LPM
+ RPMH_REGULATOR_MODE_HPM>;
+ qcom,mode-threshold-currents = <0 10000>;
+ };
+
+ lvs1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+ };
+
+ pmi8998-rpmh-regulators {
+ compatible = "qcom,pmi8998-rpmh-regulators";
+ qcom,pmic-id = "b";
+
+ bob {
+ regulator-min-microvolt = <3312000>;
+ regulator-max-microvolt = <3600000>;
+ qcom,regulator-initial-voltage = <3312000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_PASS>;
+ };
+ };
+};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 097f61784a7d..e0ecd0ad6884 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -671,6 +671,15 @@ config REGULATOR_QCOM_RPM
Qualcomm RPM as a module. The module will be named
"qcom_rpm-regulator".
+config REGULATOR_QCOM_RPMH
+ tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
+ depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
+ help
+ This driver supports control of PMIC regulators via the RPMh hardware
+ block found on Qualcomm Technologies Inc. SoCs. RPMh regulator
+ control allows for voting on regulator state between multiple
+ processors within the SoC.
+
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 590674fbecd7..c2274ddf8e16 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
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_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_rpmh-regulator.c b/drivers/regulator/qcom_rpmh-regulator.c
new file mode 100644
index 000000000000..808f9490cf69
--- /dev/null
+++ b/drivers/regulator/qcom_rpmh-regulator.c
@@ -0,0 +1,1124 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+/**
+ * enum rpmh_regulator_type - supported RPMh accelerator types
+ * %RPMH_REGULATOR_TYPE_VRM: RPMh VRM accelerator which supports voting on
+ * enable, voltage, mode, and headroom voltage of
+ * LDO, SMPS, VS, and BOB type PMIC regulators.
+ * %RPMH_REGULATOR_TYPE_XOB: RPMh XOB accelerator which supports voting on
+ * the enable state of PMIC regulators.
+ */
+enum rpmh_regulator_type {
+ RPMH_REGULATOR_TYPE_VRM,
+ RPMH_REGULATOR_TYPE_XOB,
+};
+
+/* Min and max limits of VRM resource request parameters */
+#define RPMH_VRM_MIN_UV 0
+#define RPMH_VRM_MAX_UV 8191000
+
+#define RPMH_VRM_HEADROOM_MIN_UV 0
+#define RPMH_VRM_HEADROOM_MAX_UV 511000
+
+#define RPMH_VRM_MODE_MIN 0
+#define RPMH_VRM_MODE_MAX 7
+
+/* Register offsets: */
+#define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0
+#define RPMH_REGULATOR_REG_ENABLE 0x4
+#define RPMH_REGULATOR_REG_VRM_MODE 0x8
+#define RPMH_REGULATOR_REG_VRM_HEADROOM 0xC
+
+/* Enable register values: */
+#define RPMH_REGULATOR_DISABLE 0x0
+#define RPMH_REGULATOR_ENABLE 0x1
+
+/* Number of unique hardware modes supported: */
+#define RPMH_REGULATOR_MODE_COUNT 5
+
+/**
+ * struct rpmh_regulator_mode - RPMh VRM mode attributes
+ * @pmic_mode: Raw PMIC mode value written into VRM mode voting
+ * register (i.e. RPMH_REGULATOR_MODE_*)
+ * @framework_mode: Regulator framework mode value
+ * (i.e. REGULATOR_MODE_*)
+ * @min_load_ua: The minimum load current in microamps which
+ * would utilize this mode
+ *
+ * Software selects the lowest mode for which aggr_load_ua >= min_load_ua.
+ */
+struct rpmh_regulator_mode {
+ u32 pmic_mode;
+ u32 framework_mode;
+ int min_load_ua;
+};
+
+/**
+ * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations
+ * @mode_map: Array of size RPMH_REGULATOR_MODE_COUNT which
+ * maps RPMH_REGULATOR_MODE_* indices into PMIC
+ * mode and regulator framework mode that are
+ * supported by this PMIC regulator type
+ * @voltage_range: The single range of voltages supported by this
+ * PMIC regulator type
+ * @n_voltages: The number of unique voltage set points defined
+ * by voltage_range
+ * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined
+ * in device tree to a regulator framework mode
+ */
+struct rpmh_vreg_hw_data {
+ const struct rpmh_regulator_mode *mode_map;
+ const struct regulator_linear_range *voltage_range;
+ int n_voltages;
+ unsigned int (*of_map_mode)(unsigned int mode);
+};
+
+struct rpmh_pmic;
+
+/**
+ * struct rpmh_vreg - individual rpmh regulator data structure encapsulating a
+ * single regulator device
+ * @of_node: Device tree node pointer of the regulator
+ * @pmic: Pointer to the PMIC containing the regulator
+ * @resource_name: Name of the RPMh regulator resource which is
+ * mapped to an RPMh accelerator address via
+ * command DB. This name must match to one that is
+ * defined by the bootloader.
+ * @addr: Base address of the regulator resource within
+ * an RPMh accelerator
+ * @rdesc: Regulator descriptor
+ * @rdev: Regulator device pointer returned by
+ * devm_regulator_register()
+ * @hw_data: PMIC regulator configuration data for this RPMh
+ * regulator
+ * @regulator_type: RPMh accelerator type for this regulator
+ * resource
+ * @always_wait_for_ack: Boolean flag indicating if a request must always
+ * wait for an ACK from RPMh before continuing even
+ * if it corresponds to a strictly lower power
+ * state (e.g. enabled --> disabled).
+ * @mode_map: An array of modes which may be configured at
+ * runtime by setting the load current
+ * @mode_count: The number of entries in the mode_map array.
+ * @enabled: Boolean indicating if the regulator is enabled
+ * or not
+ * @voltage: RPMh VRM regulator voltage in microvolts
+ * @mode: RPMh VRM regulator current framework mode
+ * @headroom_voltage: RPMh VRM regulator minimum headroom voltage
+ * required
+ */
+struct rpmh_vreg {
+ struct device_node *of_node;
+ struct rpmh_pmic *pmic;
+ const char *resource_name;
+ u32 addr;
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+ const struct rpmh_vreg_hw_data *hw_data;
+ enum rpmh_regulator_type regulator_type;
+ bool always_wait_for_ack;
+ struct rpmh_regulator_mode *mode_map;
+ int mode_count;
+
+ bool enabled;
+ int voltage;
+ unsigned int mode;
+ int headroom_voltage;
+};
+
+/**
+ * struct rpmh_vreg_init_data - initialization data for an RPMh regulator
+ * @name: Name for the regulator which also corresponds
+ * to the device tree subnode name of the regulator
+ * @resource_name_base: RPMh regulator resource name prefix. E.g.
+ * "ldo" for RPMh resource "ldoa1".
+ * @supply_name: Parent supply regulator name
+ * @id: Regulator number within the PMIC
+ * @regulator_type: RPMh accelerator type used to manage this
+ * regulator
+ * @hw_data: Configuration data for this PMIC regulator type
+ */
+struct rpmh_vreg_init_data {
+ const char *name;
+ const char *resource_name_base;
+ const char *supply_name;
+ int id;
+ enum rpmh_regulator_type regulator_type;
+ const struct rpmh_vreg_hw_data *hw_data;
+};
+
+/**
+ * struct rpmh_pmic_init_data - initialization data for a PMIC
+ * @name: PMIC name
+ * @vreg_data: Array of data for each regulator in the PMIC
+ * @count: Number of entries in vreg_data
+ */
+struct rpmh_pmic_init_data {
+ const char *name;
+ const struct rpmh_vreg_init_data *vreg_data;
+ int count;
+};
+
+/**
+ * struct rpmh_pmic - top level data structure of all regulators found on a PMIC
+ * @dev: Device pointer of the PMIC device for the
+ * regulators
+ * @rpmh_client: Handle used for rpmh communications
+ * @vreg: Array of rpmh regulator structs representing the
+ * individual regulators found on this PMIC chip
+ * which are configured via device tree.
+ * @vreg_count: The number of entries in the vreg array.
+ * @pmic_id: Letter used to identify this PMIC within the
+ * system. This is dictated by boot loader
+ * specifications on a given target.
+ * @init_data: Pointer to the matched PMIC initialization data
+ */
+struct rpmh_pmic {
+ struct device *dev;
+ struct rpmh_client *rpmh_client;
+ struct rpmh_vreg *vreg;
+ int vreg_count;
+ const char *pmic_id;
+ const struct rpmh_pmic_init_data *init_data;
+};
+
+#define vreg_err(vreg, message, ...) \
+ pr_err("%s %s: " message, (vreg)->pmic->init_data->name, \
+ (vreg)->rdesc.name, ##__VA_ARGS__)
+#define vreg_info(vreg, message, ...) \
+ pr_info("%s %s: " message, (vreg)->pmic->init_data->name, \
+ (vreg)->rdesc.name, ##__VA_ARGS__)
+#define vreg_debug(vreg, message, ...) \
+ pr_debug("%s %s: " message, (vreg)->pmic->init_data->name, \
+ (vreg)->rdesc.name, ##__VA_ARGS__)
+
+/**
+ * rpmh_regulator_send_request() - send the request to RPMh
+ * @vreg: Pointer to the RPMh regulator
+ * @cmd: RPMh commands to send
+ * @count: Size of cmd array
+ * @wait_for_ack: Boolean indicating if execution must wait until the
+ * request has been acknowledged as complete
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_send_request(struct rpmh_vreg *vreg,
+ struct tcs_cmd *cmd, int count, bool wait_for_ack)
+{
+ int ret;
+
+ if (wait_for_ack || vreg->always_wait_for_ack)
+ ret = rpmh_write(vreg->pmic->rpmh_client,
+ RPMH_ACTIVE_ONLY_STATE, cmd, count);
+ else
+ ret = rpmh_write_async(vreg->pmic->rpmh_client,
+ RPMH_ACTIVE_ONLY_STATE, cmd, count);
+ if (ret < 0)
+ vreg_err(vreg, "rpmh_write() failed, ret=%d\n", ret);
+
+ return ret;
+}
+
+static int rpmh_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->enabled;
+}
+
+static int rpmh_regulator_enable(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
+ .data = RPMH_REGULATOR_ENABLE,
+ };
+ int ret;
+
+ if (vreg->enabled)
+ return 0;
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, 1, true);
+ if (ret < 0) {
+ vreg_err(vreg, "enable failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ vreg->enabled = true;
+
+ return 0;
+}
+
+static int rpmh_regulator_disable(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
+ .data = RPMH_REGULATOR_DISABLE,
+ };
+ int ret;
+
+ if (!vreg->enabled)
+ return 0;
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, 1, false);
+ if (ret < 0) {
+ vreg_err(vreg, "disable failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ vreg->enabled = false;
+
+ return 0;
+}
+
+static int rpmh_regulator_vrm_set_voltage(struct regulator_dev *rdev,
+ int min_uv, int max_uv, unsigned int *selector)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE,
+ };
+ const struct regulator_linear_range *range;
+ int mv, uv, ret;
+ bool wait_for_ack;
+
+ mv = DIV_ROUND_UP(min_uv, 1000);
+ uv = mv * 1000;
+ if (uv > max_uv) {
+ vreg_err(vreg, "no set points available in range %d-%d uV\n",
+ min_uv, max_uv);
+ return -EINVAL;
+ }
+
+ range = vreg->hw_data->voltage_range;
+ *selector = DIV_ROUND_UP(uv - range->min_uV, range->uV_step);
+
+ if (uv == vreg->voltage)
+ return 0;
+
+ wait_for_ack = uv > vreg->voltage || max_uv < vreg->voltage;
+ cmd.data = mv;
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, 1, wait_for_ack);
+ if (ret < 0) {
+ vreg_err(vreg, "set voltage=%d uV failed, ret=%d\n", uv, ret);
+ return ret;
+ }
+
+ vreg->voltage = uv;
+
+ return 0;
+}
+
+static int rpmh_regulator_vrm_get_voltage(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->voltage;
+}
+
+static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+ };
+ int i, ret;
+
+ if (mode == vreg->mode)
+ return 0;
+
+ for (i = 0; i < RPMH_REGULATOR_MODE_COUNT; i++)
+ if (vreg->hw_data->mode_map[i].framework_mode == mode)
+ break;
+ if (i >= RPMH_REGULATOR_MODE_COUNT) {
+ vreg_err(vreg, "invalid mode=%u\n", mode);
+ return -EINVAL;
+ }
+
+ cmd.data = vreg->hw_data->mode_map[i].pmic_mode;
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, 1,
+ mode < vreg->mode || !vreg->mode);
+ if (ret < 0) {
+ vreg_err(vreg, "set mode=%u failed, ret=%d\n", cmd.data, ret);
+ return ret;
+ }
+
+ vreg->mode = mode;
+
+ return ret;
+}
+
+static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->mode;
+}
+
+/**
+ * rpmh_regulator_vrm_set_load() - set the PMIC mode based upon the maximum load
+ * required from the VRM rpmh-regulator
+ * @rdev: Regulator device pointer for the rpmh-regulator
+ * @load_ua: Maximum current required from all consumers in microamps
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each VRM rpmh-regulator device.
+ *
+ * This function sets the mode of the regulator to that which has the highest
+ * min support load less than or equal to load_ua. Example:
+ * mode_count = 3
+ * mode_map[].min_load_ua = 0, 100000, 6000000
+ *
+ * load_ua = 10000 --> i = 0
+ * load_ua = 250000 --> i = 1
+ * load_ua = 7000000 --> i = 2
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_ua)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ int i;
+
+ /* No need to check element 0 as it will be the default. */
+ for (i = vreg->mode_count - 1; i > 0; i--)
+ if (vreg->mode_map[i].min_load_ua <= load_ua)
+ break;
+
+ return rpmh_regulator_vrm_set_mode(rdev,
+ vreg->mode_map[i].framework_mode);
+}
+
+static const struct regulator_ops rpmh_regulator_vrm_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+ .set_voltage = rpmh_regulator_vrm_set_voltage,
+ .get_voltage = rpmh_regulator_vrm_get_voltage,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_mode = rpmh_regulator_vrm_set_mode,
+ .get_mode = rpmh_regulator_vrm_get_mode,
+ .set_load = rpmh_regulator_vrm_set_load,
+};
+
+static const struct regulator_ops rpmh_regulator_xob_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+};
+
+static const struct regulator_ops *rpmh_regulator_ops[] = {
+ [RPMH_REGULATOR_TYPE_VRM] = &rpmh_regulator_vrm_ops,
+ [RPMH_REGULATOR_TYPE_XOB] = &rpmh_regulator_xob_ops,
+};
+
+/**
+ * rpmh_regulator_parse_vrm_modes() - parse the supported mode configurations
+ * for a VRM RPMh resource from device tree
+ * vreg: Pointer to the rpmh regulator resource
+ *
+ * This function initializes the mode[] array of vreg based upon the values
+ * of optional device tree properties.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_parse_vrm_modes(struct rpmh_vreg *vreg)
+{
+ struct device_node *node = vreg->of_node;
+ const struct rpmh_regulator_mode *map;
+ const char *prop;
+ int i, len, ret;
+ u32 *buf;
+
+ map = vreg->hw_data->mode_map;
+ if (!map)
+ return 0;
+
+ /* qcom,allowed-modes is optional */
+ prop = "qcom,allowed-modes";
+ len = of_property_count_elems_of_size(node, prop, sizeof(u32));
+ if (len < 0)
+ return 0;
+
+ vreg->mode_map = devm_kcalloc(vreg->pmic->dev, len,
+ sizeof(*vreg->mode_map), GFP_KERNEL);
+ if (!vreg->mode_map)
+ return -ENOMEM;
+ vreg->mode_count = len;
+
+ buf = kcalloc(len, sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(node, prop, buf, len);
+ if (ret < 0) {
+ vreg_err(vreg, "unable to read %s, ret=%d\n",
+ prop, ret);
+ goto done;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (buf[i] >= RPMH_REGULATOR_MODE_COUNT
+ || !map[buf[i]].framework_mode) {
+ vreg_err(vreg, "element %d of %s = %u is invalid for this regulator\n",
+ i, prop, buf[i]);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ vreg->mode_map[i].pmic_mode = map[buf[i]].pmic_mode;
+ vreg->mode_map[i].framework_mode = map[buf[i]].framework_mode;
+
+ if (i > 0 && vreg->mode_map[i].pmic_mode
+ <= vreg->mode_map[i - 1].pmic_mode) {
+ vreg_err(vreg, "%s elements are not in ascending order\n",
+ prop);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+ prop = "qcom,mode-threshold-currents";
+ ret = of_property_read_u32_array(node, prop, buf, len);
+ if (ret < 0) {
+ vreg_err(vreg, "unable to read %s, ret=%d\n",
+ prop, ret);
+ goto done;
+ }
+
+ for (i = 0; i < len; i++) {
+ vreg->mode_map[i].min_load_ua = buf[i];
+
+ if (i > 0 && vreg->mode_map[i].min_load_ua
+ <= vreg->mode_map[i - 1].min_load_ua) {
+ vreg_err(vreg, "%s elements are not in ascending order\n",
+ prop);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(buf);
+ return ret;
+}
+
+/**
+ * rpmh_regulator_allocate_vreg() - allocate space for the regulators associated
+ * with the PMIC and initialize important pointers for each
+ * regulator
+ * @pmic: Pointer to the RPMh regulator PMIC
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_allocate_vreg(struct rpmh_pmic *pmic)
+{
+ struct device_node *node;
+ int i;
+
+ pmic->vreg_count = of_get_available_child_count(pmic->dev->of_node);
+ if (pmic->vreg_count == 0) {
+ dev_err(pmic->dev, "could not find any regulator subnodes\n");
+ return -ENODEV;
+ }
+
+ pmic->vreg = devm_kcalloc(pmic->dev, pmic->vreg_count,
+ sizeof(*pmic->vreg), GFP_KERNEL);
+ if (!pmic->vreg)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_available_child_of_node(pmic->dev->of_node, node) {
+ pmic->vreg[i].of_node = node;
+ pmic->vreg[i].pmic = pmic;
+
+ i++;
+ }
+
+ return 0;
+}
+
+/**
+ * rpmh_regulator_load_default_parameters() - initialize the RPMh resource
+ * request for this regulator based on optional device tree
+ * properties
+ * @vreg: Pointer to the RPMh regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_load_default_parameters(struct rpmh_vreg *vreg)
+{
+ struct tcs_cmd cmd[2] = { };
+ const char *prop;
+ int cmd_count = 0;
+ int ret;
+ u32 temp;
+
+ if (vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM) {
+ prop = "qcom,headroom-voltage";
+ ret = of_property_read_u32(vreg->of_node, prop, &temp);
+ if (!ret) {
+ if (temp < RPMH_VRM_HEADROOM_MIN_UV ||
+ temp > RPMH_VRM_HEADROOM_MAX_UV) {
+ vreg_err(vreg, "%s=%u is invalid\n",
+ prop, temp);
+ return -EINVAL;
+ }
+ vreg->headroom_voltage = temp;
+
+ cmd[cmd_count].addr
+ = vreg->addr + RPMH_REGULATOR_REG_VRM_HEADROOM;
+ cmd[cmd_count++].data
+ = DIV_ROUND_UP(vreg->headroom_voltage, 1000);
+ }
+
+ prop = "qcom,regulator-initial-voltage";
+ ret = of_property_read_u32(vreg->of_node, prop, &temp);
+ if (!ret) {
+ if (temp < RPMH_VRM_MIN_UV || temp > RPMH_VRM_MAX_UV) {
+ vreg_err(vreg, "%s=%u is invalid\n",
+ prop, temp);
+ return -EINVAL;
+ }
+ vreg->voltage = temp;
+
+ cmd[cmd_count].addr
+ = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE;
+ cmd[cmd_count++].data
+ = DIV_ROUND_UP(vreg->voltage, 1000);
+ }
+ }
+
+ if (cmd_count) {
+ ret = rpmh_regulator_send_request(vreg, cmd, cmd_count, true);
+ if (ret < 0) {
+ vreg_err(vreg, "could not send default config, ret=%d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * rpmh_regulator_init_vreg() - initialize all abbributes of an rpmh-regulator
+ * @vreg: Pointer to the RPMh regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg)
+{
+ struct device *dev = vreg->pmic->dev;
+ struct regulator_config reg_config = {};
+ const struct rpmh_vreg_init_data *rpmh_data = NULL;
+ const char *type_name = NULL;
+ enum rpmh_regulator_type type;
+ struct regulator_init_data *init_data;
+ int ret, i;
+
+ for (i = 0; i < vreg->pmic->init_data->count; i++) {
+ if (!strcmp(vreg->pmic->init_data->vreg_data[i].name,
+ vreg->of_node->name)) {
+ rpmh_data = &vreg->pmic->init_data->vreg_data[i];
+ break;
+ }
+ }
+
+ if (!rpmh_data) {
+ dev_err(dev, "Unknown regulator %s for %s RPMh regulator PMIC\n",
+ vreg->of_node->name, vreg->pmic->init_data->name);
+ return -EINVAL;
+ }
+
+ vreg->resource_name = devm_kasprintf(dev, GFP_KERNEL, "%s%s%d",
+ rpmh_data->resource_name_base, vreg->pmic->pmic_id,
+ rpmh_data->id);
+ if (!vreg->resource_name)
+ return -ENOMEM;
+
+ vreg->addr = cmd_db_read_addr(vreg->resource_name);
+ if (!vreg->addr) {
+ vreg_err(vreg, "could not find RPMh address for resource %s\n",
+ vreg->resource_name);
+ return -ENODEV;
+ }
+
+ vreg->rdesc.name = rpmh_data->name;
+ vreg->rdesc.supply_name = rpmh_data->supply_name;
+ vreg->regulator_type = rpmh_data->regulator_type;
+ vreg->hw_data = rpmh_data->hw_data;
+
+ if (rpmh_data->hw_data->voltage_range) {
+ vreg->rdesc.linear_ranges = rpmh_data->hw_data->voltage_range;
+ vreg->rdesc.n_linear_ranges = 1;
+ vreg->rdesc.n_voltages = rpmh_data->hw_data->n_voltages;
+ }
+
+ /* Optional override for the default RPMh accelerator type */
+ ret = of_property_read_string(vreg->of_node, "qcom,rpmh-resource-type",
+ &type_name);
+ if (!ret) {
+ if (!strcmp("vrm", type_name)) {
+ vreg->regulator_type = RPMH_REGULATOR_TYPE_VRM;
+ } else if (!strcmp("xob", type_name)) {
+ vreg->regulator_type = RPMH_REGULATOR_TYPE_XOB;
+ } else {
+ vreg_err(vreg, "Unknown RPMh accelerator type %s\n",
+ type_name);
+ return -EINVAL;
+ }
+ }
+
+ type = vreg->regulator_type;
+
+ if (type == RPMH_REGULATOR_TYPE_VRM) {
+ ret = rpmh_regulator_parse_vrm_modes(vreg);
+ if (ret < 0) {
+ vreg_err(vreg, "could not parse vrm mode mapping, ret=%d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ vreg->always_wait_for_ack = of_property_read_bool(vreg->of_node,
+ "qcom,always-wait-for-ack");
+
+ vreg->rdesc.owner = THIS_MODULE;
+ vreg->rdesc.type = REGULATOR_VOLTAGE;
+ vreg->rdesc.ops = rpmh_regulator_ops[type];
+ vreg->rdesc.of_map_mode = vreg->hw_data->of_map_mode;
+
+ init_data = of_get_regulator_init_data(dev, vreg->of_node,
+ &vreg->rdesc);
+ if (!init_data)
+ return -ENOMEM;
+
+ if (type == RPMH_REGULATOR_TYPE_XOB && init_data->constraints.min_uV) {
+ vreg->rdesc.fixed_uV = init_data->constraints.min_uV;
+ init_data->constraints.apply_uV = 0;
+ vreg->rdesc.n_voltages = 1;
+ }
+
+ if (vreg->hw_data->mode_map) {
+ init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;
+ for (i = 0; i < RPMH_REGULATOR_MODE_COUNT; i++)
+ init_data->constraints.valid_modes_mask
+ |= vreg->hw_data->mode_map[i].framework_mode;
+ }
+
+ reg_config.dev = dev;
+ reg_config.init_data = init_data;
+ reg_config.of_node = vreg->of_node;
+ reg_config.driver_data = vreg;
+
+ ret = rpmh_regulator_load_default_parameters(vreg);
+ if (ret < 0) {
+ vreg_err(vreg, "unable to load default parameters, ret=%d\n",
+ ret);
+ return ret;
+ }
+
+ vreg->rdev = devm_regulator_register(dev, &vreg->rdesc, &reg_config);
+ if (IS_ERR(vreg->rdev)) {
+ ret = PTR_ERR(vreg->rdev);
+ vreg->rdev = NULL;
+ vreg_err(vreg, "devm_regulator_register() failed, ret=%d\n",
+ ret);
+ return ret;
+ }
+
+ vreg_debug(vreg, "registered RPMh resource %s @ 0x%05X\n",
+ vreg->resource_name, vreg->addr);
+
+ return ret;
+}
+
+/*
+ * Mappings from RPMh generic modes to VRM accelerator modes and regulator
+ * framework modes for each regulator type.
+ */
+static const struct rpmh_regulator_mode
+rpmh_regulator_mode_map_pmic4_ldo[RPMH_REGULATOR_MODE_COUNT] = {
+ [RPMH_REGULATOR_MODE_RET] = {
+ .pmic_mode = 4,
+ .framework_mode = REGULATOR_MODE_STANDBY,
+ },
+ [RPMH_REGULATOR_MODE_LPM] = {
+ .pmic_mode = 5,
+ .framework_mode = REGULATOR_MODE_IDLE,
+ },
+ [RPMH_REGULATOR_MODE_HPM] = {
+ .pmic_mode = 7,
+ .framework_mode = REGULATOR_MODE_FAST,
+ },
+};
+
+static const struct rpmh_regulator_mode
+rpmh_regulator_mode_map_pmic4_smps[RPMH_REGULATOR_MODE_COUNT] = {
+ [RPMH_REGULATOR_MODE_RET] = {
+ .pmic_mode = 4,
+ .framework_mode = REGULATOR_MODE_STANDBY,
+ },
+ [RPMH_REGULATOR_MODE_LPM] = {
+ .pmic_mode = 5,
+ .framework_mode = REGULATOR_MODE_IDLE,
+ },
+ [RPMH_REGULATOR_MODE_AUTO] = {
+ .pmic_mode = 6,
+ .framework_mode = REGULATOR_MODE_NORMAL,
+ },
+ [RPMH_REGULATOR_MODE_HPM] = {
+ .pmic_mode = 7,
+ .framework_mode = REGULATOR_MODE_FAST,
+ },
+};
+
+static const struct rpmh_regulator_mode
+rpmh_regulator_mode_map_pmic4_bob[RPMH_REGULATOR_MODE_COUNT] = {
+ [RPMH_REGULATOR_MODE_PASS] = {
+ .pmic_mode = 0,
+ .framework_mode = REGULATOR_MODE_STANDBY,
+ },
+ [RPMH_REGULATOR_MODE_LPM] = {
+ .pmic_mode = 1,
+ .framework_mode = REGULATOR_MODE_IDLE,
+ },
+ [RPMH_REGULATOR_MODE_AUTO] = {
+ .pmic_mode = 2,
+ .framework_mode = REGULATOR_MODE_NORMAL,
+ },
+ [RPMH_REGULATOR_MODE_HPM] = {
+ .pmic_mode = 3,
+ .framework_mode = REGULATOR_MODE_FAST,
+ },
+};
+
+static unsigned int rpmh_regulator_vrm_of_map_mode(unsigned int mode,
+ const struct rpmh_regulator_mode *mode_map)
+{
+ if (mode > RPMH_REGULATOR_MODE_COUNT)
+ return -EINVAL;
+ else if (mode_map[mode].framework_mode == 0)
+ return -EINVAL;
+
+ return mode_map[mode].framework_mode;
+}
+
+static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int mode)
+{
+ return rpmh_regulator_vrm_of_map_mode(mode,
+ rpmh_regulator_mode_map_pmic4_ldo);
+}
+
+static unsigned int rpmh_regulator_pmic4_smps_of_map_mode(unsigned int mode)
+{
+ return rpmh_regulator_vrm_of_map_mode(mode,
+ rpmh_regulator_mode_map_pmic4_smps);
+}
+
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int mode)
+{
+ return rpmh_regulator_vrm_of_map_mode(mode,
+ rpmh_regulator_mode_map_pmic4_bob);
+}
+
+static const struct rpmh_vreg_hw_data pmic4_pldo_hw_data = {
+ .voltage_range = &(const struct regulator_linear_range)
+ REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000),
+ .n_voltages = 256,
+ .mode_map = rpmh_regulator_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_pldo_lv_hw_data = {
+ .voltage_range = &(const struct regulator_linear_range)
+ REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000),
+ .n_voltages = 128,
+ .mode_map = rpmh_regulator_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_nldo_hw_data = {
+ .voltage_range = &(const struct regulator_linear_range)
+ REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
+ .n_voltages = 128,
+ .mode_map = rpmh_regulator_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_hfsmps3_hw_data = {
+ .voltage_range = &(const struct regulator_linear_range)
+ REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000),
+ .n_voltages = 216,
+ .mode_map = rpmh_regulator_mode_map_pmic4_smps,
+ .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_ftsmps426_hw_data = {
+ .voltage_range = &(const struct regulator_linear_range)
+ REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000),
+ .n_voltages = 259,
+ .mode_map = rpmh_regulator_mode_map_pmic4_smps,
+ .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_bob_hw_data = {
+ .voltage_range = &(const struct regulator_linear_range)
+ REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+ .n_voltages = 84,
+ .mode_map = rpmh_regulator_mode_map_pmic4_bob,
+ .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_lvs_hw_data = {
+ /* LVS hardware does not support voltage or mode configuration. */
+};
+
+#define RPMH_VREG(_name, _hw_type, _type, _base_name, _id, _supply_name) \
+{ \
+ .name = #_name, \
+ .hw_data = &_hw_type##_hw_data, \
+ .regulator_type = RPMH_REGULATOR_TYPE_##_type, \
+ .resource_name_base = #_base_name, \
+ .id = _id, \
+ .supply_name = #_supply_name, \
+}
+
+static const struct rpmh_vreg_init_data pm8998_vreg_data[] = {
+ RPMH_VREG(smps1, pmic4_ftsmps426, VRM, smp, 1, vdd_s1),
+ RPMH_VREG(smps2, pmic4_ftsmps426, VRM, smp, 2, vdd_s2),
+ RPMH_VREG(smps3, pmic4_hfsmps3, VRM, smp, 3, vdd_s3),
+ RPMH_VREG(smps4, pmic4_hfsmps3, VRM, smp, 4, vdd_s4),
+ RPMH_VREG(smps5, pmic4_hfsmps3, VRM, smp, 5, vdd_s5),
+ RPMH_VREG(smps6, pmic4_ftsmps426, VRM, smp, 6, vdd_s6),
+ RPMH_VREG(smps7, pmic4_ftsmps426, VRM, smp, 7, vdd_s7),
+ RPMH_VREG(smps8, pmic4_ftsmps426, VRM, smp, 8, vdd_s8),
+ RPMH_VREG(smps9, pmic4_ftsmps426, VRM, smp, 9, vdd_s9),
+ RPMH_VREG(smps10, pmic4_ftsmps426, VRM, smp, 10, vdd_s10),
+ RPMH_VREG(smps11, pmic4_ftsmps426, VRM, smp, 11, vdd_s11),
+ RPMH_VREG(smps12, pmic4_ftsmps426, VRM, smp, 12, vdd_s12),
+ RPMH_VREG(smps13, pmic4_ftsmps426, VRM, smp, 13, vdd_s13),
+ RPMH_VREG(ldo1, pmic4_nldo, VRM, ldo, 1, vdd_l1_l27),
+ RPMH_VREG(ldo2, pmic4_nldo, VRM, ldo, 2, vdd_l2_l8_l17),
+ RPMH_VREG(ldo3, pmic4_nldo, VRM, ldo, 3, vdd_l3_l11),
+ RPMH_VREG(ldo4, pmic4_nldo, VRM, ldo, 4, vdd_l4_l5),
+ RPMH_VREG(ldo5, pmic4_nldo, VRM, ldo, 5, vdd_l4_l5),
+ RPMH_VREG(ldo6, pmic4_pldo, VRM, ldo, 6, vdd_l6),
+ RPMH_VREG(ldo7, pmic4_pldo_lv, VRM, ldo, 7, vdd_l7_l12_l14_l15),
+ RPMH_VREG(ldo8, pmic4_nldo, VRM, ldo, 8, vdd_l2_l8_l17),
+ RPMH_VREG(ldo9, pmic4_pldo, VRM, ldo, 9, vdd_l9),
+ RPMH_VREG(ldo10, pmic4_pldo, VRM, ldo, 10, vdd_l10_l23_l25),
+ RPMH_VREG(ldo11, pmic4_nldo, VRM, ldo, 11, vdd_l3_l11),
+ RPMH_VREG(ldo12, pmic4_pldo_lv, VRM, ldo, 12, vdd_l7_l12_l14_l15),
+ RPMH_VREG(ldo13, pmic4_pldo, VRM, ldo, 13, vdd_l13_l19_l21),
+ RPMH_VREG(ldo14, pmic4_pldo_lv, VRM, ldo, 14, vdd_l7_l12_l14_l15),
+ RPMH_VREG(ldo15, pmic4_pldo_lv, VRM, ldo, 15, vdd_l7_l12_l14_l15),
+ RPMH_VREG(ldo16, pmic4_pldo, VRM, ldo, 16, vdd_l16_l28),
+ RPMH_VREG(ldo17, pmic4_nldo, VRM, ldo, 17, vdd_l2_l8_l17),
+ RPMH_VREG(ldo18, pmic4_pldo, VRM, ldo, 18, vdd_l18_l22),
+ RPMH_VREG(ldo19, pmic4_pldo, VRM, ldo, 19, vdd_l13_l19_l21),
+ RPMH_VREG(ldo20, pmic4_pldo, VRM, ldo, 20, vdd_l20_l24),
+ RPMH_VREG(ldo21, pmic4_pldo, VRM, ldo, 21, vdd_l13_l19_l21),
+ RPMH_VREG(ldo22, pmic4_pldo, VRM, ldo, 22, vdd_l18_l22),
+ RPMH_VREG(ldo23, pmic4_pldo, VRM, ldo, 23, vdd_l10_l23_l25),
+ RPMH_VREG(ldo24, pmic4_pldo, VRM, ldo, 24, vdd_l20_l24),
+ RPMH_VREG(ldo25, pmic4_pldo, VRM, ldo, 25, vdd_l10_l23_l25),
+ RPMH_VREG(ldo26, pmic4_nldo, VRM, ldo, 26, vdd_l26),
+ RPMH_VREG(ldo27, pmic4_nldo, VRM, ldo, 27, vdd_l1_l27),
+ RPMH_VREG(ldo28, pmic4_pldo, VRM, ldo, 28, vdd_l16_l28),
+ RPMH_VREG(lvs1, pmic4_lvs, XOB, vs, 1, vdd_lvs1_lvs2),
+ RPMH_VREG(lvs2, pmic4_lvs, XOB, vs, 2, vdd_lvs1_lvs2),
+};
+
+static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = {
+ RPMH_VREG(bob, pmic4_bob, VRM, bob, 1, vdd_bob),
+};
+
+static const struct rpmh_vreg_init_data pm8005_vreg_data[] = {
+ RPMH_VREG(smps1, pmic4_ftsmps426, VRM, smp, 1, vdd_s1),
+ RPMH_VREG(smps2, pmic4_ftsmps426, VRM, smp, 2, vdd_s2),
+ RPMH_VREG(smps3, pmic4_ftsmps426, VRM, smp, 3, vdd_s3),
+ RPMH_VREG(smps4, pmic4_ftsmps426, VRM, smp, 4, vdd_s4),
+};
+
+static const struct rpmh_pmic_init_data pm8998_pmic_data = {
+ .name = "PM8998",
+ .vreg_data = pm8998_vreg_data,
+ .count = ARRAY_SIZE(pm8998_vreg_data),
+};
+
+static const struct rpmh_pmic_init_data pmi8998_pmic_data = {
+ .name = "PMI8998",
+ .vreg_data = pmi8998_vreg_data,
+ .count = ARRAY_SIZE(pmi8998_vreg_data),
+};
+
+static const struct rpmh_pmic_init_data pm8005_pmic_data = {
+ .name = "PM8005",
+ .vreg_data = pm8005_vreg_data,
+ .count = ARRAY_SIZE(pm8005_vreg_data),
+};
+
+static const struct of_device_id rpmh_regulator_match_table[] = {
+ {
+ .compatible = "qcom,pm8998-rpmh-regulators",
+ .data = &pm8998_pmic_data,
+ },
+ {
+ .compatible = "qcom,pmi8998-rpmh-regulators",
+ .data = &pmi8998_pmic_data,
+ },
+ {
+ .compatible = "qcom,pm8005-rpmh-regulators",
+ .data = &pm8005_pmic_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
+
+/**
+ * rpmh_regulator_probe() - probe an RPMh PMIC and register regulators for each
+ * of the regulator nodes associated with it
+ * @pdev: Pointer to the platform device of the RPMh PMIC
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ struct rpmh_pmic *pmic;
+ struct device_node *node;
+ int ret, i;
+
+ node = dev->of_node;
+
+ if (!node) {
+ dev_err(dev, "Device tree node is missing\n");
+ return -EINVAL;
+ }
+
+ ret = cmd_db_ready();
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Command DB not available, ret=%d\n", ret);
+ return ret;
+ }
+
+ pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ pmic->dev = dev;
+ platform_set_drvdata(pdev, pmic);
+
+ pmic->rpmh_client = rpmh_get_client(pdev);
+ if (IS_ERR(pmic->rpmh_client)) {
+ ret = PTR_ERR(pmic->rpmh_client);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to request RPMh client, ret=%d\n",
+ ret);
+ return ret;
+ }
+
+ match = of_match_node(rpmh_regulator_match_table, node);
+ if (match) {
+ pmic->init_data = match->data;
+ } else {
+ dev_err(dev, "could not find compatible string match\n");
+ ret = -ENODEV;
+ goto cleanup;
+ }
+
+ ret = of_property_read_string(node, "qcom,pmic-id",
+ &pmic->pmic_id);
+ if (ret < 0) {
+ dev_err(dev, "qcom,pmic-id missing in DT node\n");
+ goto cleanup;
+ }
+
+ ret = rpmh_regulator_allocate_vreg(pmic);
+ if (ret < 0) {
+ dev_err(dev, "failed to allocate regulator subnode array, ret=%d\n",
+ ret);
+ goto cleanup;
+ }
+
+ for (i = 0; i < pmic->vreg_count; i++) {
+ ret = rpmh_regulator_init_vreg(&pmic->vreg[i]);
+ if (ret < 0) {
+ dev_err(dev, "unable to initialize rpmh-regulator vreg %s, ret=%d\n",
+ pmic->vreg[i].of_node->name, ret);
+ goto cleanup;
+ }
+ }
+
+ dev_dbg(dev, "successfully probed %d %s regulators\n",
+ pmic->vreg_count, pmic->init_data->name);
+
+ return ret;
+
+cleanup:
+ rpmh_release(pmic->rpmh_client);
+
+ return ret;
+}
+
+static int rpmh_regulator_remove(struct platform_device *pdev)
+{
+ struct rpmh_pmic *pmic = platform_get_drvdata(pdev);
+
+ rpmh_release(pmic->rpmh_client);
+
+ return 0;
+}
+
+static struct platform_driver rpmh_regulator_driver = {
+ .driver = {
+ .name = "qcom-rpmh-regulator",
+ .of_match_table = rpmh_regulator_match_table,
+ .owner = THIS_MODULE,
+ },
+ .probe = rpmh_regulator_probe,
+ .remove = rpmh_regulator_remove,
+};
+
+static int rpmh_regulator_init(void)
+{
+ return platform_driver_register(&rpmh_regulator_driver);
+}
+
+static void rpmh_regulator_exit(void)
+{
+ platform_driver_unregister(&rpmh_regulator_driver);
+}
+
+arch_initcall(rpmh_regulator_init);
+module_exit(rpmh_regulator_exit);
+
+MODULE_DESCRIPTION("Qualcomm RPMh regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/dt-bindings/regulator/qcom,rpmh-regulator.h b/include/dt-bindings/regulator/qcom,rpmh-regulator.h
new file mode 100644
index 000000000000..f854e0efdd7d
--- /dev/null
+++ b/include/dt-bindings/regulator/qcom,rpmh-regulator.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#ifndef __QCOM_RPMH_REGULATOR_H
+#define __QCOM_RPMH_REGULATOR_H
+
+/*
+ * These mode constants may be used for qcom,allowed-modes and qcom,init-mode
+ * properties of an RPMh resource. Each type of regulator supports a subset of
+ * the possible modes.
+ *
+ * %RPMH_REGULATOR_MODE_PASS: Pass-through mode in which output is directly
+ * tied to input. This mode is only supported by
+ * BOB type regulators.
+ * %RPMH_REGULATOR_MODE_RET: Retention mode in which only an extremely small
+ * load current is allowed. This mode is supported
+ * by LDO and SMPS type regulators.
+ * %RPMH_REGULATOR_MODE_LPM: Low power mode in which a small load current is
+ * allowed. This mode corresponds to PFM for SMPS
+ * and BOB type regulators. This mode is supported
+ * by LDO, HFSMPS, BOB, and PMIC4 FTSMPS type
+ * regulators.
+ * %RPMH_REGULATOR_MODE_AUTO: Auto mode in which the regulator hardware
+ * automatically switches between LPM and HPM based
+ * upon the real-time load current. This mode is
+ * supported by HFSMPS, BOB, and PMIC4 FTSMPS type
+ * regulators.
+ * %RPMH_REGULATOR_MODE_HPM: High power mode in which the full rated current
+ * of the regulator is allowed. This mode
+ * corresponds to PWM for SMPS and BOB type
+ * regulators. This mode is supported by all types
+ * of regulators.
+ */
+#define RPMH_REGULATOR_MODE_PASS 0
+#define RPMH_REGULATOR_MODE_RET 1
+#define RPMH_REGULATOR_MODE_LPM 2
+#define RPMH_REGULATOR_MODE_AUTO 3
+#define RPMH_REGULATOR_MODE_HPM 4
+
+#endif