The patch

   regulator: Add QCOM SPMI regulator driver

has been applied to the regulator tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From e92a4047419c805d08ad136fbc72368249d9f091 Mon Sep 17 00:00:00 2001
From: Stephen Boyd <[email protected]>
Date: Fri, 12 Jun 2015 15:47:10 -0700
Subject: [PATCH] regulator: Add QCOM SPMI regulator driver

Add an SPMI regulator driver for Qualcomm's PM8841, PM8941, and
PM8916 PMICs. This driver is based largely on code from
codeaurora.org[1].

[1] 
https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/regulator/qpnp-regulator.c?h=msm-3.10
Cc: David Collins <[email protected]>
Cc: <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
 .../bindings/regulator/qcom,spmi-regulator.txt     |  121 ++
 drivers/regulator/Kconfig                          |   11 +
 drivers/regulator/Makefile                         |    1 +
 drivers/regulator/qcom_spmi-regulator.c            | 1437 ++++++++++++++++++++
 4 files changed, 1570 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
 create mode 100644 drivers/regulator/qcom_spmi-regulator.c

diff --git 
a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt 
b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
new file mode 100644
index 0000000..75b4604
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
@@ -0,0 +1,121 @@
+Qualcomm SPMI Regulators
+
+- compatible:
+       Usage: required
+       Value type: <string>
+       Definition: must be one of:
+                       "qcom,pm8841-regulators"
+                       "qcom,pm8916-regulators"
+                       "qcom,pm8941-regulators"
+
+- interrupts:
+       Usage: optional
+       Value type: <prop-encoded-array>
+       Definition: List of OCP interrupts.
+
+- interrupt-names:
+       Usage: required if 'interrupts' property present
+       Value type: <string-array>
+       Definition: List of strings defining the names of the
+                   interrupts in the 'interrupts' property 1-to-1.
+                   Supported values are "ocp-<regulator_name>", where
+                   <regulator_name> corresponds to a voltage switch
+                   type regulator.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_s4-supply:
+- vdd_s5-supply:
+- vdd_s6-supply:
+- vdd_s7-supply:
+- vdd_s8-supply:
+       Usage: optional (pm8841 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_s4-supply:
+- vdd_l1_l3-supply:
+- vdd_l2-supply:
+- vdd_l4_l5_l6-supply:
+- vdd_l7-supply:
+- vdd_l8_l11_l14_l15_l16-supply:
+- vdd_l9_l10_l12_l13_l17_l18-supply:
+       Usage: optional (pm8916 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_l1_l3-supply:
+- vdd_l2_lvs_1_2_3-supply:
+- vdd_l4_l11-supply:
+- vdd_l5_l7-supply:
+- vdd_l6_l12_l14_l15-supply:
+- vdd_l8_l16_l18_19-supply:
+- vdd_l9_l10_l17_l22-supply:
+- vdd_l13_l20_l23_l24-supply:
+- vdd_l21-supply:
+- vin_5vs-supply:
+       Usage: optional (pm8941 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+
+The regulator node houses sub-nodes for each regulator within the device. Each
+sub-node is identified using the node's name, with valid values listed for each
+of the PMICs below.
+
+pm8841:
+       s1, s2, s3, s4, s5, s6, s7, s8
+
+pm8916:
+       s1, s2, s3, s4, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13,
+       l14, l15, l16, l17, l18
+
+pm8941:
+       s1, s2, s3, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14,
+       l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, lvs1, lvs2, lvs3,
+       mvs1, mvs2
+
+The content of each sub-node is defined by the standard binding for regulators 
-
+see regulator.txt - with additional custom properties described below:
+
+- regulator-initial-mode:
+       Usage: optional
+       Value type: <u32>
+       Descrption: 1 = Set initial mode to high power mode (HPM), also referred
+                   to as NPM.  HPM consumes more ground current than LPM, but
+                   it can source significantly higher load current. HPM is not
+                   available on boost type regulators. For voltage switch type
+                   regulators, HPM implies that over current protection and
+                   soft start are active all the time. 0 = Set initial mode to
+                   low power mode (LPM).
+
+Example:
+
+       regulators {
+               compatible = "qcom,pm8941-regulators";
+               vdd_l1_l3-supply = <&s1>;
+
+               s1: s1 {
+                       regulator-min-microvolt = <1300000>;
+                       regulator-max-microvolt = <1400000>;
+               };
+
+               ...
+
+               l1: l1 {
+                       regulator-min-microvolt = <1225000>;
+                       regulator-max-microvolt = <1300000>;
+               };
+
+               ....
+       };
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a6f116a..53b3e25 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -512,6 +512,17 @@ config REGULATOR_QCOM_RPM
          Qualcomm RPM as a module. The module will be named
          "qcom_rpm-regulator".
 
+config REGULATOR_QCOM_SPMI
+       tristate "Qualcomm SPMI regulator driver"
+       depends on SPMI || COMPILE_TEST
+       help
+         If you say yes to this option, support will be included for the
+         regulators found in Qualcomm SPMI PMICs.
+
+         Say M here if you want to include support for the regulators on the
+         Qualcomm SPMI PMICs as a module. The module will be named
+         "qcom_spmi-regulator".
+
 config REGULATOR_RC5T583
        tristate "RICOH RC5T583 Power regulators"
        depends on MFD_RC5T583
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2c4da15..7152c97 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
 obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
 obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
 obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
diff --git a/drivers/regulator/qcom_spmi-regulator.c 
b/drivers/regulator/qcom_spmi-regulator.c
new file mode 100644
index 0000000..162b865
--- /dev/null
+++ b/drivers/regulator/qcom_spmi-regulator.c
@@ -0,0 +1,1437 @@
+/*
+ * Copyright (c) 2012-2015, The Linux Foundation. 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/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ktime.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+
+/* These types correspond to unique register layouts. */
+enum spmi_regulator_logical_type {
+       SPMI_REGULATOR_LOGICAL_TYPE_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_LDO,
+       SPMI_REGULATOR_LOGICAL_TYPE_VS,
+       SPMI_REGULATOR_LOGICAL_TYPE_BOOST,
+       SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_BOOST_BYP,
+       SPMI_REGULATOR_LOGICAL_TYPE_LN_LDO,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO,
+};
+
+enum spmi_regulator_type {
+       SPMI_REGULATOR_TYPE_BUCK                = 0x03,
+       SPMI_REGULATOR_TYPE_LDO                 = 0x04,
+       SPMI_REGULATOR_TYPE_VS                  = 0x05,
+       SPMI_REGULATOR_TYPE_BOOST               = 0x1b,
+       SPMI_REGULATOR_TYPE_FTS                 = 0x1c,
+       SPMI_REGULATOR_TYPE_BOOST_BYP           = 0x1f,
+       SPMI_REGULATOR_TYPE_ULT_LDO             = 0x21,
+       SPMI_REGULATOR_TYPE_ULT_BUCK            = 0x22,
+};
+
+enum spmi_regulator_subtype {
+       SPMI_REGULATOR_SUBTYPE_GP_CTL           = 0x08,
+       SPMI_REGULATOR_SUBTYPE_RF_CTL           = 0x09,
+       SPMI_REGULATOR_SUBTYPE_N50              = 0x01,
+       SPMI_REGULATOR_SUBTYPE_N150             = 0x02,
+       SPMI_REGULATOR_SUBTYPE_N300             = 0x03,
+       SPMI_REGULATOR_SUBTYPE_N600             = 0x04,
+       SPMI_REGULATOR_SUBTYPE_N1200            = 0x05,
+       SPMI_REGULATOR_SUBTYPE_N600_ST          = 0x06,
+       SPMI_REGULATOR_SUBTYPE_N1200_ST         = 0x07,
+       SPMI_REGULATOR_SUBTYPE_N900_ST          = 0x14,
+       SPMI_REGULATOR_SUBTYPE_N300_ST          = 0x15,
+       SPMI_REGULATOR_SUBTYPE_P50              = 0x08,
+       SPMI_REGULATOR_SUBTYPE_P150             = 0x09,
+       SPMI_REGULATOR_SUBTYPE_P300             = 0x0a,
+       SPMI_REGULATOR_SUBTYPE_P600             = 0x0b,
+       SPMI_REGULATOR_SUBTYPE_P1200            = 0x0c,
+       SPMI_REGULATOR_SUBTYPE_LN               = 0x10,
+       SPMI_REGULATOR_SUBTYPE_LV_P50           = 0x28,
+       SPMI_REGULATOR_SUBTYPE_LV_P150          = 0x29,
+       SPMI_REGULATOR_SUBTYPE_LV_P300          = 0x2a,
+       SPMI_REGULATOR_SUBTYPE_LV_P600          = 0x2b,
+       SPMI_REGULATOR_SUBTYPE_LV_P1200         = 0x2c,
+       SPMI_REGULATOR_SUBTYPE_LV_P450          = 0x2d,
+       SPMI_REGULATOR_SUBTYPE_LV100            = 0x01,
+       SPMI_REGULATOR_SUBTYPE_LV300            = 0x02,
+       SPMI_REGULATOR_SUBTYPE_MV300            = 0x08,
+       SPMI_REGULATOR_SUBTYPE_MV500            = 0x09,
+       SPMI_REGULATOR_SUBTYPE_HDMI             = 0x10,
+       SPMI_REGULATOR_SUBTYPE_OTG              = 0x11,
+       SPMI_REGULATOR_SUBTYPE_5V_BOOST         = 0x01,
+       SPMI_REGULATOR_SUBTYPE_FTS_CTL          = 0x08,
+       SPMI_REGULATOR_SUBTYPE_FTS2p5_CTL       = 0x09,
+       SPMI_REGULATOR_SUBTYPE_BB_2A            = 0x01,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL1      = 0x0d,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL2      = 0x0e,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL3      = 0x0f,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4      = 0x10,
+};
+
+enum spmi_common_regulator_registers {
+       SPMI_COMMON_REG_DIG_MAJOR_REV           = 0x01,
+       SPMI_COMMON_REG_TYPE                    = 0x04,
+       SPMI_COMMON_REG_SUBTYPE                 = 0x05,
+       SPMI_COMMON_REG_VOLTAGE_RANGE           = 0x40,
+       SPMI_COMMON_REG_VOLTAGE_SET             = 0x41,
+       SPMI_COMMON_REG_MODE                    = 0x45,
+       SPMI_COMMON_REG_ENABLE                  = 0x46,
+       SPMI_COMMON_REG_PULL_DOWN               = 0x48,
+       SPMI_COMMON_REG_SOFT_START              = 0x4c,
+       SPMI_COMMON_REG_STEP_CTRL               = 0x61,
+};
+
+enum spmi_vs_registers {
+       SPMI_VS_REG_OCP                         = 0x4a,
+       SPMI_VS_REG_SOFT_START                  = 0x4c,
+};
+
+enum spmi_boost_registers {
+       SPMI_BOOST_REG_CURRENT_LIMIT            = 0x4a,
+};
+
+enum spmi_boost_byp_registers {
+       SPMI_BOOST_BYP_REG_CURRENT_LIMIT        = 0x4b,
+};
+
+/* Used for indexing into ctrl_reg.  These are offets from 0x40 */
+enum spmi_common_control_register_index {
+       SPMI_COMMON_IDX_VOLTAGE_RANGE           = 0,
+       SPMI_COMMON_IDX_VOLTAGE_SET             = 1,
+       SPMI_COMMON_IDX_MODE                    = 5,
+       SPMI_COMMON_IDX_ENABLE                  = 6,
+};
+
+/* Common regulator control register layout */
+#define SPMI_COMMON_ENABLE_MASK                        0x80
+#define SPMI_COMMON_ENABLE                     0x80
+#define SPMI_COMMON_DISABLE                    0x00
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN3_MASK  0x08
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN2_MASK  0x04
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN1_MASK  0x02
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN0_MASK  0x01
+#define SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK     0x0f
+
+/* Common regulator mode register layout */
+#define SPMI_COMMON_MODE_HPM_MASK              0x80
+#define SPMI_COMMON_MODE_AUTO_MASK             0x40
+#define SPMI_COMMON_MODE_BYPASS_MASK           0x20
+#define SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK     0x10
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN3_MASK    0x08
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN2_MASK    0x04
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN1_MASK    0x02
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN0_MASK    0x01
+#define SPMI_COMMON_MODE_FOLLOW_ALL_MASK       0x1f
+
+/* Common regulator pull down control register layout */
+#define SPMI_COMMON_PULL_DOWN_ENABLE_MASK      0x80
+
+/* LDO regulator current limit control register layout */
+#define SPMI_LDO_CURRENT_LIMIT_ENABLE_MASK     0x80
+
+/* LDO regulator soft start control register layout */
+#define SPMI_LDO_SOFT_START_ENABLE_MASK                0x80
+
+/* VS regulator over current protection control register layout */
+#define SPMI_VS_OCP_OVERRIDE                   0x01
+#define SPMI_VS_OCP_NO_OVERRIDE                        0x00
+
+/* VS regulator soft start control register layout */
+#define SPMI_VS_SOFT_START_ENABLE_MASK         0x80
+#define SPMI_VS_SOFT_START_SEL_MASK            0x03
+
+/* Boost regulator current limit control register layout */
+#define SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK   0x80
+#define SPMI_BOOST_CURRENT_LIMIT_MASK          0x07
+
+#define SPMI_VS_OCP_DEFAULT_MAX_RETRIES                10
+#define SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS     30
+#define SPMI_VS_OCP_FALL_DELAY_US              90
+#define SPMI_VS_OCP_FAULT_DELAY_US             20000
+
+#define SPMI_FTSMPS_STEP_CTRL_STEP_MASK                0x18
+#define SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT       3
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_MASK       0x07
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT      0
+
+/* Clock rate in kHz of the FTSMPS regulator reference clock. */
+#define SPMI_FTSMPS_CLOCK_RATE         19200
+
+/* Minimum voltage stepper delay for each step. */
+#define SPMI_FTSMPS_STEP_DELAY         8
+
+/*
+ * The ratio SPMI_FTSMPS_STEP_MARGIN_NUM/SPMI_FTSMPS_STEP_MARGIN_DEN is used to
+ * adjust the step rate in order to account for oscillator variance.
+ */
+#define SPMI_FTSMPS_STEP_MARGIN_NUM    4
+#define SPMI_FTSMPS_STEP_MARGIN_DEN    5
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level.  It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+/* VSET value to decide the range of ULT SMPS */
+#define ULT_SMPS_RANGE_SPLIT 0x60
+
+/**
+ * struct spmi_voltage_range - regulator set point voltage mapping description
+ * @min_uV:            Minimum programmable output voltage resulting from
+ *                     set point register value 0x00
+ * @max_uV:            Maximum programmable output voltage
+ * @step_uV:           Output voltage increase resulting from the set point
+ *                     register value increasing by 1
+ * @set_point_min_uV:  Minimum allowed voltage
+ * @set_point_max_uV:  Maximum allowed voltage.  This may be tweaked in order
+ *                     to pick which range should be used in the case of
+ *                     overlapping set points.
+ * @n_voltages:                Number of preferred voltage set points present 
in this
+ *                     range
+ * @range_sel:         Voltage range register value corresponding to this range
+ *
+ * The following relationships must be true for the values used in this struct:
+ * (max_uV - min_uV) % step_uV == 0
+ * (set_point_min_uV - min_uV) % step_uV == 0*
+ * (set_point_max_uV - min_uV) % step_uV == 0*
+ * n_voltages = (set_point_max_uV - set_point_min_uV) / step_uV + 1
+ *
+ * *Note, set_point_min_uV == set_point_max_uV == 0 is allowed in order to
+ * specify that the voltage range has meaning, but is not preferred.
+ */
+struct spmi_voltage_range {
+       int                                     min_uV;
+       int                                     max_uV;
+       int                                     step_uV;
+       int                                     set_point_min_uV;
+       int                                     set_point_max_uV;
+       unsigned                                n_voltages;
+       u8                                      range_sel;
+};
+
+/*
+ * The ranges specified in the spmi_voltage_set_points struct must be listed
+ * so that range[i].set_point_max_uV < range[i+1].set_point_min_uV.
+ */
+struct spmi_voltage_set_points {
+       struct spmi_voltage_range               *range;
+       int                                     count;
+       unsigned                                n_voltages;
+};
+
+struct spmi_regulator {
+       struct regulator_desc                   desc;
+       struct device                           *dev;
+       struct delayed_work                     ocp_work;
+       struct regmap                           *regmap;
+       struct spmi_voltage_set_points          *set_points;
+       enum spmi_regulator_logical_type        logical_type;
+       int                                     ocp_irq;
+       int                                     ocp_count;
+       int                                     ocp_max_retries;
+       int                                     ocp_retry_delay_ms;
+       int                                     hpm_min_load;
+       int                                     slew_rate;
+       ktime_t                                 vs_enable_time;
+       u16                                     base;
+       struct list_head                        node;
+};
+
+struct spmi_regulator_mapping {
+       enum spmi_regulator_type                type;
+       enum spmi_regulator_subtype             subtype;
+       enum spmi_regulator_logical_type        logical_type;
+       u32                                     revision_min;
+       u32                                     revision_max;
+       struct regulator_ops                    *ops;
+       struct spmi_voltage_set_points          *set_points;
+       int                                     hpm_min_load;
+};
+
+struct spmi_regulator_data {
+       const char                      *name;
+       u16                             base;
+       const char                      *supply;
+       const char                      *ocp;
+       u16                             force_type;
+};
+
+#define SPMI_VREG(_type, _subtype, _dig_major_min, _dig_major_max, \
+                     _logical_type, _ops_val, _set_points_val, _hpm_min_load) \
+       { \
+               .type           = SPMI_REGULATOR_TYPE_##_type, \
+               .subtype        = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+               .revision_min   = _dig_major_min, \
+               .revision_max   = _dig_major_max, \
+               .logical_type   = SPMI_REGULATOR_LOGICAL_TYPE_##_logical_type, \
+               .ops            = &spmi_##_ops_val##_ops, \
+               .set_points     = &_set_points_val##_set_points, \
+               .hpm_min_load   = _hpm_min_load, \
+       }
+
+#define SPMI_VREG_VS(_subtype, _dig_major_min, _dig_major_max) \
+       { \
+               .type           = SPMI_REGULATOR_TYPE_VS, \
+               .subtype        = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+               .revision_min   = _dig_major_min, \
+               .revision_max   = _dig_major_max, \
+               .logical_type   = SPMI_REGULATOR_LOGICAL_TYPE_VS, \
+               .ops            = &spmi_vs_ops, \
+       }
+
+#define SPMI_VOLTAGE_RANGE(_range_sel, _min_uV, _set_point_min_uV, \
+                       _set_point_max_uV, _max_uV, _step_uV) \
+       { \
+               .min_uV                 = _min_uV, \
+               .max_uV                 = _max_uV, \
+               .set_point_min_uV       = _set_point_min_uV, \
+               .set_point_max_uV       = _set_point_max_uV, \
+               .step_uV                = _step_uV, \
+               .range_sel              = _range_sel, \
+       }
+
+#define DEFINE_SPMI_SET_POINTS(name) \
+struct spmi_voltage_set_points name##_set_points = { \
+       .range  = name##_ranges, \
+       .count  = ARRAY_SIZE(name##_ranges), \
+}
+
+/*
+ * These tables contain the physically available PMIC regulator voltage 
setpoint
+ * ranges.  Where two ranges overlap in hardware, one of the ranges is trimmed
+ * to ensure that the setpoints available to software are monotonically
+ * increasing and unique.  The set_voltage callback functions expect these
+ * properties to hold.
+ */
+static struct spmi_voltage_range pldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(2,  750000,  750000, 1537500, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(3, 1500000, 1550000, 3075000, 3075000, 25000),
+       SPMI_VOLTAGE_RANGE(4, 1750000, 3100000, 4900000, 4900000, 50000),
+};
+
+static struct spmi_voltage_range nldo1_ranges[] = {
+       SPMI_VOLTAGE_RANGE(2,  750000,  750000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo2_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,       0,       0, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  375000,  375000,  768750,  768750,  6250),
+       SPMI_VOLTAGE_RANGE(2,  750000,  775000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo3_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1537500, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  375000,       0,       0, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(2,  750000,       0,       0, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ln_ldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(1,  690000,  690000, 1110000, 1110000, 60000),
+       SPMI_VOLTAGE_RANGE(0, 1380000, 1380000, 2220000, 2220000, 120000),
+};
+
+static struct spmi_voltage_range smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1562500, 1562500, 12500),
+       SPMI_VOLTAGE_RANGE(1, 1550000, 1575000, 3125000, 3125000, 25000),
+};
+
+static struct spmi_voltage_range ftsmps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,       0,  350000, 1275000, 1275000,  5000),
+       SPMI_VOLTAGE_RANGE(1,       0, 1280000, 2040000, 2040000, 10000),
+};
+
+static struct spmi_voltage_range ftsmps2p5_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,   80000,  350000, 1355000, 1355000,  5000),
+       SPMI_VOLTAGE_RANGE(1,  160000, 1360000, 2200000, 2200000, 10000),
+};
+
+static struct spmi_voltage_range boost_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 4000000, 4000000, 5550000, 5550000, 50000),
+};
+
+static struct spmi_voltage_range boost_byp_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 2500000, 2500000, 5200000, 5650000, 50000),
+};
+
+static struct spmi_voltage_range ult_lo_smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1562500, 1562500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  750000,       0,       0, 1525000, 25000),
+};
+
+static struct spmi_voltage_range ult_ho_smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 1550000, 1550000, 2325000, 2325000, 25000),
+};
+
+static struct spmi_voltage_range ult_nldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ult_pldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500),
+};
+
+static DEFINE_SPMI_SET_POINTS(pldo);
+static DEFINE_SPMI_SET_POINTS(nldo1);
+static DEFINE_SPMI_SET_POINTS(nldo2);
+static DEFINE_SPMI_SET_POINTS(nldo3);
+static DEFINE_SPMI_SET_POINTS(ln_ldo);
+static DEFINE_SPMI_SET_POINTS(smps);
+static DEFINE_SPMI_SET_POINTS(ftsmps);
+static DEFINE_SPMI_SET_POINTS(ftsmps2p5);
+static DEFINE_SPMI_SET_POINTS(boost);
+static DEFINE_SPMI_SET_POINTS(boost_byp);
+static DEFINE_SPMI_SET_POINTS(ult_lo_smps);
+static DEFINE_SPMI_SET_POINTS(ult_ho_smps);
+static DEFINE_SPMI_SET_POINTS(ult_nldo);
+static DEFINE_SPMI_SET_POINTS(ult_pldo);
+
+static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 
*buf,
+                                int len)
+{
+       return regmap_bulk_read(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static inline int spmi_vreg_write(struct spmi_regulator *vreg, u16 addr,
+                               u8 *buf, int len)
+{
+       return regmap_bulk_write(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static int spmi_vreg_update_bits(struct spmi_regulator *vreg, u16 addr, u8 val,
+               u8 mask)
+{
+       return regmap_update_bits(vreg->regmap, vreg->base + addr, mask, val);
+}
+
+static int spmi_regulator_common_is_enabled(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 reg;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_ENABLE, &reg, 1);
+
+       return (reg & SPMI_COMMON_ENABLE_MASK) == SPMI_COMMON_ENABLE;
+}
+
+static int spmi_regulator_common_enable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       if (vreg->ocp_irq) {
+               vreg->ocp_count = 0;
+               vreg->vs_enable_time = ktime_get();
+       }
+
+       return spmi_regulator_common_enable(rdev);
+}
+
+static int spmi_regulator_common_disable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_select_voltage(struct spmi_regulator *vreg,
+               int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+               unsigned *selector)
+{
+       const struct spmi_voltage_range *range;
+       int uV = min_uV;
+       int lim_min_uV, lim_max_uV, i, range_id, range_max_uV;
+
+       /* Check if request voltage is outside of physically settable range. */
+       lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
+       lim_max_uV =
+         vreg->set_points->range[vreg->set_points->count - 1].set_point_max_uV;
+
+       if (uV < lim_min_uV && max_uV >= lim_min_uV)
+               uV = lim_min_uV;
+
+       if (uV < lim_min_uV || uV > lim_max_uV) {
+               dev_err(vreg->dev,
+                       "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+                        min_uV, max_uV, lim_min_uV, lim_max_uV);
+               return -EINVAL;
+       }
+
+       /* Find the range which uV is inside of. */
+       for (i = vreg->set_points->count - 1; i > 0; i--) {
+               range_max_uV = vreg->set_points->range[i - 1].set_point_max_uV;
+               if (uV > range_max_uV && range_max_uV > 0)
+                       break;
+       }
+
+       range_id = i;
+       range = &vreg->set_points->range[range_id];
+       *range_sel = range->range_sel;
+
+       /*
+        * Force uV to be an allowed set point by applying a ceiling function to
+        * the uV value.
+        */
+       *voltage_sel = (uV - range->min_uV + range->step_uV - 1)
+                       / range->step_uV;
+       uV = *voltage_sel * range->step_uV + range->min_uV;
+
+       if (uV > max_uV) {
+               dev_err(vreg->dev,
+                       "request v=[%d, %d] cannot be met by any set point; "
+                       "next set point: %d\n",
+                       min_uV, max_uV, uV);
+               return -EINVAL;
+       }
+
+       *selector = 0;
+       for (i = 0; i < range_id; i++)
+               *selector += vreg->set_points->range[i].n_voltages;
+       *selector += (uV - range->set_point_min_uV) / range->step_uV;
+
+       return 0;
+}
+
+static const struct spmi_voltage_range *
+spmi_regulator_find_range(struct spmi_regulator *vreg)
+{
+       u8 range_sel;
+       const struct spmi_voltage_range *range, *end;
+
+       range = vreg->set_points->range;
+       end = range + vreg->set_points->count;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, &range_sel, 1);
+
+       for (; range < end; range++)
+               if (range->range_sel == range_sel)
+                       return range;
+
+       return NULL;
+}
+
+static int spmi_regulator_select_voltage_same_range(struct spmi_regulator 
*vreg,
+               int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+               unsigned *selector)
+{
+       const struct spmi_voltage_range *range;
+       int uV = min_uV;
+       int i;
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               goto different_range;
+
+       if (uV < range->min_uV && max_uV >= range->min_uV)
+               uV = range->min_uV;
+
+       if (uV < range->min_uV || uV > range->max_uV) {
+               /* Current range doesn't support the requested voltage. */
+               goto different_range;
+       }
+
+       /*
+        * Force uV to be an allowed set point by applying a ceiling function to
+        * the uV value.
+        */
+       *voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV);
+       uV = *voltage_sel * range->step_uV + range->min_uV;
+
+       if (uV > max_uV) {
+               /*
+                * No set point in the current voltage range is within the
+                * requested min_uV to max_uV range.
+                */
+               goto different_range;
+       }
+
+       *selector = 0;
+       for (i = 0; i < vreg->set_points->count; i++) {
+               if (uV >= vreg->set_points->range[i].set_point_min_uV
+                   && uV <= vreg->set_points->range[i].set_point_max_uV)
+                       *selector +=
+                           (uV - vreg->set_points->range[i].set_point_min_uV)
+                               / vreg->set_points->range[i].step_uV;
+                       break;
+
+               *selector += vreg->set_points->range[i].n_voltages;
+       }
+
+       if (*selector >= vreg->set_points->n_voltages)
+               goto different_range;
+
+       return 0;
+
+different_range:
+       return spmi_regulator_select_voltage(vreg, min_uV, max_uV,
+                       range_sel, voltage_sel, selector);
+}
+
+static int spmi_regulator_common_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 buf[2];
+       u8 range_sel, voltage_sel;
+
+       /*
+        * Favor staying in the current voltage range if possible.  This avoids
+        * voltage spikes that occur when changing the voltage range.
+        */
+       ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+               &range_sel, &voltage_sel, selector);
+       if (ret)
+               return ret;
+
+       buf[0] = range_sel;
+       buf[1] = voltage_sel;
+       return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, buf, 2);
+}
+
+static int spmi_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+               unsigned int old_selector, unsigned int new_selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       int diff_uV;
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return -EINVAL;
+
+       diff_uV = abs(new_selector - old_selector) * range->step_uV;
+
+       return DIV_ROUND_UP(diff_uV, vreg->slew_rate);
+}
+
+static int spmi_regulator_common_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return VOLTAGE_UNKNOWN;
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_single_range_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 range_sel, sel;
+
+       ret = spmi_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+               &sel, selector);
+       if (ret) {
+               dev_err(vreg->dev, "could not set voltage, ret=%d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Certain types of regulators do not have a range select register so
+        * only voltage set register needs to be written.
+        */
+       return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &sel, 1);
+}
+
+static int spmi_regulator_single_range_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range = vreg->set_points->range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_ult_lo_smps_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 range_sel, voltage_sel;
+
+       /*
+        * Favor staying in the current voltage range if possible. This avoids
+        * voltage spikes that occur when changing the voltage range.
+        */
+       ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+               &range_sel, &voltage_sel, selector);
+       if (ret)
+               return ret;
+
+       /*
+        * Calculate VSET based on range
+        * In case of range 0: voltage_sel is a 7 bit value, can be written
+        *                      witout any modification.
+        * In case of range 1: voltage_sel is a 5 bit value, bits[7-5] set to
+        *                      [011].
+        */
+       if (range_sel == 1)
+               voltage_sel |= ULT_SMPS_RANGE_SPLIT;
+
+       ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_VOLTAGE_SET,
+              voltage_sel, 0xff);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int spmi_regulator_ult_lo_smps_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return VOLTAGE_UNKNOWN;
+
+       if (range->range_sel == 1)
+               voltage_sel &= ~ULT_SMPS_RANGE_SPLIT;
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_common_list_voltage(struct regulator_dev *rdev,
+                       unsigned selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int uV = 0;
+       int i;
+
+       if (selector >= vreg->set_points->n_voltages)
+               return 0;
+
+       for (i = 0; i < vreg->set_points->count; i++) {
+               if (selector < vreg->set_points->range[i].n_voltages)
+                       uV = selector * vreg->set_points->range[i].step_uV
+                               + vreg->set_points->range[i].set_point_min_uV;
+                       break;
+
+               selector -= vreg->set_points->range[i].n_voltages;
+       }
+
+       return uV;
+}
+
+static int
+spmi_regulator_common_set_bypass(struct regulator_dev *rdev, bool enable)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 mask = SPMI_COMMON_MODE_BYPASS_MASK;
+       u8 val = 0;
+
+       if (enable)
+               val = mask;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_get_bypass(struct regulator_dev *rdev, bool *enable)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 val;
+       int ret;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &val, 1);
+       *enable = val & SPMI_COMMON_MODE_BYPASS_MASK;
+
+       return ret;
+}
+
+static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 reg;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &reg, 1);
+
+       if (reg & SPMI_COMMON_MODE_HPM_MASK)
+               return REGULATOR_MODE_NORMAL;
+
+       return REGULATOR_MODE_IDLE;
+}
+
+static int
+spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 mask = SPMI_COMMON_MODE_HPM_MASK;
+       u8 val = 0;
+
+       if (mode == REGULATOR_MODE_NORMAL)
+               val = mask;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_set_load(struct regulator_dev *rdev, int load_uA)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mode;
+
+       if (load_uA >= vreg->hpm_min_load)
+               mode = REGULATOR_MODE_NORMAL;
+       else
+               mode = REGULATOR_MODE_IDLE;
+
+       return spmi_regulator_common_set_mode(rdev, mode);
+}
+
+static int spmi_regulator_common_set_pull_down(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mask = SPMI_COMMON_PULL_DOWN_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_PULL_DOWN,
+                                    mask, mask);
+}
+
+static int spmi_regulator_common_set_soft_start(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mask = SPMI_LDO_SOFT_START_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_SOFT_START,
+                                    mask, mask);
+}
+
+static int spmi_regulator_set_ilim(struct regulator_dev *rdev, int ilim_uA)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       enum spmi_regulator_logical_type type = vreg->logical_type;
+       unsigned int current_reg;
+       u8 reg;
+       u8 mask = SPMI_BOOST_CURRENT_LIMIT_MASK |
+                 SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+       int max = (SPMI_BOOST_CURRENT_LIMIT_MASK + 1) * 500;
+
+       if (type == SPMI_REGULATOR_LOGICAL_TYPE_BOOST)
+               current_reg = SPMI_BOOST_REG_CURRENT_LIMIT;
+       else
+               current_reg = SPMI_BOOST_BYP_REG_CURRENT_LIMIT;
+
+       if (ilim_uA > max || ilim_uA <= 0)
+               return -EINVAL;
+
+       reg = (ilim_uA - 1) / 500;
+       reg |= SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, current_reg, reg, mask);
+}
+
+static int spmi_regulator_vs_clear_ocp(struct spmi_regulator *vreg)
+{
+       int ret;
+
+       ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+
+       vreg->vs_enable_time = ktime_get();
+
+       ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+
+       return ret;
+}
+
+static void spmi_regulator_vs_ocp_work(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct spmi_regulator *vreg
+               = container_of(dwork, struct spmi_regulator, ocp_work);
+
+       spmi_regulator_vs_clear_ocp(vreg);
+}
+
+static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)
+{
+       struct spmi_regulator *vreg = data;
+       ktime_t ocp_irq_time;
+       s64 ocp_trigger_delay_us;
+
+       ocp_irq_time = ktime_get();
+       ocp_trigger_delay_us = ktime_us_delta(ocp_irq_time,
+                                               vreg->vs_enable_time);
+
+       /*
+        * Reset the OCP count if there is a large delay between switch enable
+        * and when OCP triggers.  This is indicative of a hotplug event as
+        * opposed to a fault.
+        */
+       if (ocp_trigger_delay_us > SPMI_VS_OCP_FAULT_DELAY_US)
+               vreg->ocp_count = 0;
+
+       /* Wait for switch output to settle back to 0 V after OCP triggered. */
+       udelay(SPMI_VS_OCP_FALL_DELAY_US);
+
+       vreg->ocp_count++;
+
+       if (vreg->ocp_count == 1) {
+               /* Immediately clear the over current condition. */
+               spmi_regulator_vs_clear_ocp(vreg);
+       } else if (vreg->ocp_count <= vreg->ocp_max_retries) {
+               /* Schedule the over current clear task to run later. */
+               schedule_delayed_work(&vreg->ocp_work,
+                       msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1);
+       } else {
+               dev_err(vreg->dev,
+                       "OCP triggered %d times; no further retries\n",
+                       vreg->ocp_count);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct regulator_ops spmi_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_ln_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+};
+
+static struct regulator_ops spmi_vs_ops = {
+       .enable                 = spmi_regulator_vs_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_boost_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_input_current_limit = spmi_regulator_set_ilim,
+};
+
+static struct regulator_ops spmi_ftsmps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .set_voltage_time_sel   = spmi_regulator_set_voltage_time_sel,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_lo_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_ult_lo_smps_set_voltage,
+       .get_voltage            = spmi_regulator_ult_lo_smps_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ho_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+/* Maximum possible digital major revision value */
+#define INF 0xFF
+
+static const struct spmi_regulator_mapping supported_regulators[] = {
+       /*           type subtype dig_min dig_max ltype ops setpoints hpm_min */
+       SPMI_VREG(BUCK,  GP_CTL,   0, INF, SMPS,   smps,   smps,   100000),
+       SPMI_VREG(LDO,   N300,     0, INF, LDO,    ldo,    nldo1,   10000),
+       SPMI_VREG(LDO,   N600,     0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N1200,    0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N600,     1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N1200,    1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N600_ST,  0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N1200_ST, 0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N600_ST,  1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N1200_ST, 1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   P50,      0, INF, LDO,    ldo,    pldo,     5000),
+       SPMI_VREG(LDO,   P150,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P300,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P600,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P1200,    0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LN,       0, INF, LN_LDO, ln_ldo, ln_ldo,      0),
+       SPMI_VREG(LDO,   LV_P50,   0, INF, LDO,    ldo,    pldo,     5000),
+       SPMI_VREG(LDO,   LV_P150,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P300,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P600,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P1200, 0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG_VS(LV100,        0, INF),
+       SPMI_VREG_VS(LV300,        0, INF),
+       SPMI_VREG_VS(MV300,        0, INF),
+       SPMI_VREG_VS(MV500,        0, INF),
+       SPMI_VREG_VS(HDMI,         0, INF),
+       SPMI_VREG_VS(OTG,          0, INF),
+       SPMI_VREG(BOOST, 5V_BOOST, 0, INF, BOOST,  boost,  boost,       0),
+       SPMI_VREG(FTS,   FTS_CTL,  0, INF, FTSMPS, ftsmps, ftsmps, 100000),
+       SPMI_VREG(FTS, FTS2p5_CTL, 0, INF, FTSMPS, ftsmps, ftsmps2p5, 100000),
+       SPMI_VREG(BOOST_BYP, BB_2A, 0, INF, BOOST_BYP, boost, boost_byp, 0),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL1, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL2, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL3, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL4, 0, INF, ULT_HO_SMPS, ult_ho_smps,
+                                               ult_ho_smps,   100000),
+       SPMI_VREG(ULT_LDO, N300_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N600_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N900_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N1200_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P150,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P300,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P450,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P600,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P150,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P50,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 5000),
+};
+
+static void spmi_calculate_num_voltages(struct spmi_voltage_set_points *points)
+{
+       unsigned int n;
+       struct spmi_voltage_range *range = points->range;
+
+       for (; range < points->range + points->count; range++) {
+               n = 0;
+               if (range->set_point_max_uV) {
+                       n = range->set_point_max_uV - range->set_point_min_uV;
+                       n /= range->step_uV + 1;
+               }
+               range->n_voltages = n;
+               points->n_voltages += n;
+       }
+}
+
+static int spmi_regulator_match(struct spmi_regulator *vreg, u16 force_type)
+{
+       const struct spmi_regulator_mapping *mapping;
+       int ret, i;
+       u32 dig_major_rev;
+       u8 version[SPMI_COMMON_REG_SUBTYPE - SPMI_COMMON_REG_DIG_MAJOR_REV + 1];
+       u8 type, subtype;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_DIG_MAJOR_REV, version,
+               ARRAY_SIZE(version));
+       if (ret) {
+               dev_err(vreg->dev, "could not read version registers\n");
+               return ret;
+       }
+       dig_major_rev   = version[SPMI_COMMON_REG_DIG_MAJOR_REV
+                                       - SPMI_COMMON_REG_DIG_MAJOR_REV];
+       if (!force_type) {
+               type            = version[SPMI_COMMON_REG_TYPE -
+                                         SPMI_COMMON_REG_DIG_MAJOR_REV];
+               subtype         = version[SPMI_COMMON_REG_SUBTYPE -
+                                         SPMI_COMMON_REG_DIG_MAJOR_REV];
+       } else {
+               type = force_type >> 8;
+               subtype = force_type;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+               mapping = &supported_regulators[i];
+               if (mapping->type == type && mapping->subtype == subtype
+                   && mapping->revision_min <= dig_major_rev
+                   && mapping->revision_max >= dig_major_rev)
+                       goto found;
+       }
+
+       dev_err(vreg->dev,
+               "unsupported regulator: name=%s type=0x%02X, subtype=0x%02X, 
dig major rev=0x%02X\n",
+               vreg->desc.name, type, subtype, dig_major_rev);
+
+       return -ENODEV;
+
+found:
+       vreg->logical_type      = mapping->logical_type;
+       vreg->set_points        = mapping->set_points;
+       vreg->hpm_min_load      = mapping->hpm_min_load;
+       vreg->desc.ops          = mapping->ops;
+
+       if (mapping->set_points) {
+               if (!mapping->set_points->n_voltages)
+                       spmi_calculate_num_voltages(mapping->set_points);
+               vreg->desc.n_voltages = mapping->set_points->n_voltages;
+       }
+
+       return 0;
+}
+
+static int spmi_regulator_ftsmps_init_slew_rate(struct spmi_regulator *vreg)
+{
+       int ret;
+       u8 reg = 0;
+       int step, delay, slew_rate;
+       const struct spmi_voltage_range *range;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_STEP_CTRL, &reg, 1);
+       if (ret) {
+               dev_err(vreg->dev, "spmi read failed, ret=%d\n", ret);
+               return ret;
+       }
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return -EINVAL;
+
+       step = reg & SPMI_FTSMPS_STEP_CTRL_STEP_MASK;
+       step >>= SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT;
+
+       delay = reg & SPMI_FTSMPS_STEP_CTRL_DELAY_MASK;
+       delay >>= SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT;
+
+       /* slew_rate has units of uV/us */
+       slew_rate = SPMI_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
+       slew_rate /= 1000 * (SPMI_FTSMPS_STEP_DELAY << delay);
+       slew_rate *= SPMI_FTSMPS_STEP_MARGIN_NUM;
+       slew_rate /= SPMI_FTSMPS_STEP_MARGIN_DEN;
+
+       /* Ensure that the slew rate is greater than 0 */
+       vreg->slew_rate = max(slew_rate, 1);
+
+       return ret;
+}
+
+static unsigned int spmi_regulator_of_map_mode(unsigned int mode)
+{
+       if (mode)
+               return REGULATOR_MODE_NORMAL;
+
+       return REGULATOR_MODE_IDLE;
+}
+
+static int spmi_regulator_of_parse(struct device_node *node,
+                           const struct regulator_desc *desc,
+                           struct regulator_config *config)
+{
+       struct spmi_regulator *vreg = config->driver_data;
+       struct device *dev = config->dev;
+       int ret;
+
+       vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES;
+       vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+
+       if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS) {
+               ret = spmi_regulator_ftsmps_init_slew_rate(vreg);
+               if (ret)
+                       return ret;
+       }
+
+       if (vreg->logical_type != SPMI_REGULATOR_LOGICAL_TYPE_VS)
+               vreg->ocp_irq = 0;
+
+       if (vreg->ocp_irq) {
+               ret = devm_request_irq(dev, vreg->ocp_irq,
+                       spmi_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
+                       vreg);
+               if (ret < 0) {
+                       dev_err(dev, "failed to request irq %d, ret=%d\n",
+                               vreg->ocp_irq, ret);
+                       return ret;
+               }
+
+               INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work);
+       }
+
+       return 0;
+}
+
+static const struct spmi_regulator_data pm8941_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "l1", 0x4000, "vdd_l1_l3", },
+       { "l2", 0x4100, "vdd_l2_lvs_1_2_3", },
+       { "l3", 0x4200, "vdd_l1_l3", },
+       { "l4", 0x4300, "vdd_l4_l11", },
+       { "l5", 0x4400, "vdd_l5_l7", NULL, 0x0410 },
+       { "l6", 0x4500, "vdd_l6_l12_l14_l15", },
+       { "l7", 0x4600, "vdd_l5_l7", NULL, 0x0410 },
+       { "l8", 0x4700, "vdd_l8_l16_l18_19", },
+       { "l9", 0x4800, "vdd_l9_l10_l17_l22", },
+       { "l10", 0x4900, "vdd_l9_l10_l17_l22", },
+       { "l11", 0x4a00, "vdd_l4_l11", },
+       { "l12", 0x4b00, "vdd_l6_l12_l14_l15", },
+       { "l13", 0x4c00, "vdd_l13_l20_l23_l24", },
+       { "l14", 0x4d00, "vdd_l6_l12_l14_l15", },
+       { "l15", 0x4e00, "vdd_l6_l12_l14_l15", },
+       { "l16", 0x4f00, "vdd_l8_l16_l18_19", },
+       { "l17", 0x5000, "vdd_l9_l10_l17_l22", },
+       { "l18", 0x5100, "vdd_l8_l16_l18_19", },
+       { "l19", 0x5200, "vdd_l8_l16_l18_19", },
+       { "l20", 0x5300, "vdd_l13_l20_l23_l24", },
+       { "l21", 0x5400, "vdd_l21", },
+       { "l22", 0x5500, "vdd_l9_l10_l17_l22", },
+       { "l23", 0x5600, "vdd_l13_l20_l23_l24", },
+       { "l24", 0x5700, "vdd_l13_l20_l23_l24", },
+       { "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", },
+       { "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", },
+       { "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", },
+       { "mvs1", 0x8300, "vin_5vs", },
+       { "mvs2", 0x8400, "vin_5vs", },
+       { }
+};
+
+static const struct spmi_regulator_data pm8841_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", NULL, 0x1c08 },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "s4", 0x1d00, "vdd_s4", NULL, 0x1c08 },
+       { "s5", 0x2000, "vdd_s5", NULL, 0x1c08 },
+       { "s6", 0x2300, "vdd_s6", NULL, 0x1c08 },
+       { "s7", 0x2600, "vdd_s7", NULL, 0x1c08 },
+       { "s8", 0x2900, "vdd_s8", NULL, 0x1c08 },
+       { }
+};
+
+static const struct spmi_regulator_data pm8916_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "s4", 0x1d00, "vdd_s4", },
+       { "l1", 0x4000, "vdd_l1_l3", },
+       { "l2", 0x4100, "vdd_l2", },
+       { "l3", 0x4200, "vdd_l1_l3", },
+       { "l4", 0x4300, "vdd_l4_l5_l6", },
+       { "l5", 0x4400, "vdd_l4_l5_l6", },
+       { "l6", 0x4500, "vdd_l4_l5_l6", },
+       { "l7", 0x4600, "vdd_l7", },
+       { "l8", 0x4700, "vdd_l8_l11_l14_l15_l16", },
+       { "l9", 0x4800, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l10", 0x4900, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l11", 0x4a00, "vdd_l8_l11_l14_l15_l16", },
+       { "l12", 0x4b00, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l13", 0x4c00, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l14", 0x4d00, "vdd_l8_l11_l14_l15_l16", },
+       { "l15", 0x4e00, "vdd_l8_l11_l14_l15_l16", },
+       { "l16", 0x4f00, "vdd_l8_l11_l14_l15_l16", },
+       { "l17", 0x5000, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l18", 0x5100, "vdd_l9_l10_l12_l13_l17_l18", },
+       { }
+};
+
+static const struct of_device_id qcom_spmi_regulator_match[] = {
+       { .compatible = "qcom,pm8841-regulators", .data = &pm8841_regulators },
+       { .compatible = "qcom,pm8916-regulators", .data = &pm8916_regulators },
+       { .compatible = "qcom,pm8941-regulators", .data = &pm8941_regulators },
+       { }
+};
+MODULE_DEVICE_TABLE(of, qcom_spmi_regulator_match);
+
+static int qcom_spmi_regulator_probe(struct platform_device *pdev)
+{
+       const struct spmi_regulator_data *reg;
+       const struct of_device_id *match;
+       struct regulator_config config = { };
+       struct regulator_dev *rdev;
+       struct spmi_regulator *vreg;
+       struct regmap *regmap;
+       const char *name;
+       struct device *dev = &pdev->dev;
+       int ret;
+       struct list_head *vreg_list;
+
+       vreg_list = devm_kzalloc(dev, sizeof(*vreg_list), GFP_KERNEL);
+       if (!vreg_list)
+               return -ENOMEM;
+       INIT_LIST_HEAD(vreg_list);
+       platform_set_drvdata(pdev, vreg_list);
+
+       regmap = dev_get_regmap(dev->parent, NULL);
+       if (!regmap)
+               return -ENODEV;
+
+       match = of_match_device(qcom_spmi_regulator_match, &pdev->dev);
+       if (!match)
+               return -ENODEV;
+
+       for (reg = match->data; reg->name; reg++) {
+               vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+               if (!vreg)
+                       return -ENOMEM;
+
+               vreg->dev = dev;
+               vreg->base = reg->base;
+               vreg->regmap = regmap;
+
+               if (reg->ocp) {
+                       vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp);
+                       if (vreg->ocp_irq < 0) {
+                               ret = vreg->ocp_irq;
+                               goto err;
+                       }
+               }
+
+               vreg->desc.id = -1;
+               vreg->desc.owner = THIS_MODULE;
+               vreg->desc.type = REGULATOR_VOLTAGE;
+               vreg->desc.name = name = reg->name;
+               vreg->desc.supply_name = reg->supply;
+               vreg->desc.of_match = reg->name;
+               vreg->desc.of_parse_cb = spmi_regulator_of_parse;
+               vreg->desc.of_map_mode = spmi_regulator_of_map_mode;
+
+               ret = spmi_regulator_match(vreg, reg->force_type);
+               if (ret)
+                       goto err;
+
+               config.dev = dev;
+               config.driver_data = vreg;
+               rdev = devm_regulator_register(dev, &vreg->desc, &config);
+               if (IS_ERR(rdev)) {
+                       dev_err(dev, "failed to register %s\n", name);
+                       ret = PTR_ERR(rdev);
+                       goto err;
+               }
+
+               INIT_LIST_HEAD(&vreg->node);
+               list_add(&vreg->node, vreg_list);
+       }
+
+       return 0;
+
+err:
+       list_for_each_entry(vreg, vreg_list, node)
+               if (vreg->ocp_irq)
+                       cancel_delayed_work_sync(&vreg->ocp_work);
+       return ret;
+}
+
+static int qcom_spmi_regulator_remove(struct platform_device *pdev)
+{
+       struct spmi_regulator *vreg;
+       struct list_head *vreg_list = platform_get_drvdata(pdev);
+
+       list_for_each_entry(vreg, vreg_list, node)
+               if (vreg->ocp_irq)
+                       cancel_delayed_work_sync(&vreg->ocp_work);
+
+       return 0;
+}
+
+static struct platform_driver qcom_spmi_regulator_driver = {
+       .driver         = {
+               .name   = "qcom-spmi-regulator",
+               .of_match_table = qcom_spmi_regulator_match,
+       },
+       .probe          = qcom_spmi_regulator_probe,
+       .remove         = qcom_spmi_regulator_remove,
+};
+module_platform_driver(qcom_spmi_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-spmi-regulator");
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to