Merge remote-tracking branches 'regulator/topic/drivers', 'regulator/topic/enable', 'regulator/topic/fan53555', 'regulator/topic/hi6421' and 'regulator/topic/isl9305' into regulator-next
diff --git a/Documentation/devicetree/bindings/regulator/fan53555.txt b/Documentation/devicetree/bindings/regulator/fan53555.txt
new file mode 100644
index 0000000..54a3f2c
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/fan53555.txt
@@ -0,0 +1,23 @@
+Binding for Fairchild FAN53555 regulators
+
+Required properties:
+  - compatible: one of "fcs,fan53555", "silergy,syr827", "silergy,syr828"
+  - reg: I2C address
+
+Optional properties:
+  - fcs,suspend-voltage-selector: declare which of the two available
+		voltage selector registers should be used for the suspend
+		voltage. The other one is used for the runtime voltage setting
+		Possible values are either <0> or <1>
+  - vin-supply: regulator supplying the vin pin
+
+Example:
+
+	regulator@40 {
+		compatible = "fcs,fan53555";
+		regulator-name = "fan53555";
+		regulator-min-microvolt = <1000000>;
+		regulator-max-microvolt = <1800000>;
+		vin-supply = <&parent_reg>;
+		fcs,suspend-voltage-selector = <1>;
+	};
diff --git a/Documentation/devicetree/bindings/regulator/isl9305.txt b/Documentation/devicetree/bindings/regulator/isl9305.txt
new file mode 100644
index 0000000..a626fc1
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/isl9305.txt
@@ -0,0 +1,36 @@
+Intersil ISL9305/ISL9305H voltage regulator
+
+Required properties:
+
+- compatible: "isl,isl9305" or "isl,isl9305h"
+- reg: I2C slave address, usually 0x68.
+- regulators: A node that houses a sub-node for each regulator within the
+  device. Each sub-node is identified using the node's name, with valid
+  values being "dcd1", "dcd2", "ldo1" and "ldo2". The content of each sub-node
+  is defined by the standard binding for regulators; see regulator.txt.
+- VINDCD1-supply: A phandle to a regulator node supplying VINDCD1.
+  VINDCD2-supply: A phandle to a regulator node supplying VINDCD2.
+  VINLDO1-supply: A phandle to a regulator node supplying VINLDO1.
+  VINLDO2-supply: A phandle to a regulator node supplying VINLDO2.
+
+Optional properties:
+- Per-regulator optional properties are defined in regulator.txt
+
+Example
+
+	pmic: isl9305@68 {
+		compatible = "isl,isl9305";
+		reg = <0x68>;
+
+		VINDCD1-supply = <&system_power>;
+		VINDCD2-supply = <&system_power>;
+		VINLDO1-supply = <&system_power>;
+		VINLDO2-supply = <&system_power>;
+
+		regulators {
+			dcd1 {
+			        regulator-name = "VDD_DSP";
+				regulator-always-on;
+                        };
+		};
+	};
diff --git a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
new file mode 100644
index 0000000..ce91f61
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt
@@ -0,0 +1,27 @@
+pwm regulator bindings
+
+Required properties:
+- compatible: Should be "pwm-regulator"
+- pwms: OF device-tree PWM specification (see PWM binding pwm.txt)
+- voltage-table: voltage and duty table, include 2 members in each set of
+  brackets, first one is voltage(unit: uv), the next is duty(unit: percent)
+
+Any property defined as part of the core regulator binding defined in
+regulator.txt can also be used.
+
+Example:
+	pwm_regulator {
+		compatible = "pwm-regulator;
+		pwms = <&pwm1 0 8448 0>;
+
+		voltage-table = <1114000 0>,
+				<1095000 10>,
+				<1076000 20>,
+				<1056000 30>,
+				<1036000 40>,
+				<1016000 50>;
+
+		regulator-min-microvolt = <1016000>;
+		regulator-max-microvolt = <1114000>;
+		regulator-name = "vdd_logic";
+	};
diff --git a/Documentation/devicetree/bindings/regulator/sky81452-regulator.txt b/Documentation/devicetree/bindings/regulator/sky81452-regulator.txt
new file mode 100644
index 0000000..882455e
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/sky81452-regulator.txt
@@ -0,0 +1,16 @@
+SKY81452 voltage regulator
+
+Required properties:
+- any required generic properties defined in regulator.txt
+
+Optional properties:
+- any available generic properties defined in regulator.txt
+
+Example:
+
+	regulator {
+		/* generic regulator properties */
+		regulator-name = "touch_en";
+		regulator-min-microvolt = <4500000>;
+		regulator-max-microvolt = <8000000>;
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index ac7269f..6073e76 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -49,6 +49,7 @@
 est	ESTeem Wireless Modems
 eukrea  Eukréa Electromatique
 excito	Excito
+fcs	Fairchild Semiconductor
 fsl	Freescale Semiconductor
 GEFanuc	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
 gef	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
@@ -124,6 +125,7 @@
 silabs	Silicon Laboratories
 simtek
 sii	Seiko Instruments, Inc.
+silergy	Silergy Corp.
 sirf	SiRF Technology, Inc.
 smsc	Standard Microsystems Corporation
 snps 	Synopsys, Inc.
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 1344aa8..40eab77 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -241,6 +241,23 @@
 	  and the platform has to provide a mapping of GPIO-states
 	  to target volts/amps.
 
+config REGULATOR_HI6421
+	tristate "HiSilicon Hi6421 PMIC voltage regulator support"
+	depends on MFD_HI6421_PMIC && OF
+	help
+	  This driver provides support for the voltage regulators on the
+	  HiSilicon Hi6421 PMU / Codec IC.
+	  Hi6421 is a multi-function device which, on regulator part, provides
+	  21 general purpose LDOs, 3 dedicated LDOs, and 5 BUCKs. All
+	  of them come with support to either ECO (idle) or sleep mode.
+
+config REGULATOR_ISL9305
+	tristate "Intersil ISL9305 regulator"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  This driver supports ISL9305 voltage regulator chip.
+
 config REGULATOR_ISL6271A
 	tristate "Intersil ISL6271A Power regulator"
 	depends on I2C
@@ -450,6 +467,25 @@
 	  Say y here to support the regulators found on the Freescale
 	  PFUZE100/PFUZE200 PMIC.
 
+config REGULATOR_PWM
+	tristate "PWM voltage regulator"
+	depends on PWM
+	help
+	  This driver supports PWM controlled voltage regulators. PWM
+	  duty cycle can increase or decrease the voltage.
+
+config REGULATOR_QCOM_RPM
+	tristate "Qualcomm RPM regulator driver"
+	depends on MFD_QCOM_RPM
+	help
+	  If you say yes to this option, support will be included for the
+	  regulators exposed by the Resource Power Manager found in Qualcomm
+	  8660, 8960 and 8064 based devices.
+
+	  Say M here if you want to include support for the regulators on the
+	  Qualcomm RPM as a module. The module will be named
+	  "qcom_rpm-regulator".
+
 config REGULATOR_RC5T583
 	tristate "RICOH RC5T583 Power regulators"
 	depends on MFD_RC5T583
@@ -484,11 +520,16 @@
 	 via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and
 	 supports DVS mode with 8bits of output voltage control.
 
-config REGULATOR_ST_PWM
-	tristate "STMicroelectronics PWM voltage regulator"
-	depends on ARCH_STI
+config REGULATOR_SKY81452
+	tristate "Skyworks Solutions SKY81452 voltage regulator"
+	depends on SKY81452
 	help
-	 This driver supports ST's PWM controlled voltage regulators.
+	  This driver supports Skyworks SKY81452 voltage output regulator
+	  via I2C bus. SKY81452 has one voltage linear regulator can be
+	  programmed from 4.5V to 20V.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called sky81452-regulator.
 
 config REGULATOR_TI_ABB
 	tristate "TI Adaptive Body Bias on-chip LDO"
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index aa4a6aa..6628348 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -32,7 +32,9 @@
 obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
 obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
 obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
+obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o
 obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
+obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o
 obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
 obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
 obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o
@@ -55,8 +57,10 @@
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
+obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
 obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
+obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
 obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
 obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
@@ -65,7 +69,7 @@
 obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
 obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
-obj-$(CONFIG_REGULATOR_ST_PWM) += st-pwm.o
+obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
 obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index a3c3785..44b4045 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -839,7 +839,7 @@
 static int machine_constraints_voltage(struct regulator_dev *rdev,
 	struct regulation_constraints *constraints)
 {
-	struct regulator_ops *ops = rdev->desc->ops;
+	const struct regulator_ops *ops = rdev->desc->ops;
 	int ret;
 
 	/* do we need to apply the constraint voltage */
@@ -938,7 +938,7 @@
 static int machine_constraints_current(struct regulator_dev *rdev,
 	struct regulation_constraints *constraints)
 {
-	struct regulator_ops *ops = rdev->desc->ops;
+	const struct regulator_ops *ops = rdev->desc->ops;
 	int ret;
 
 	if (!constraints->min_uA && !constraints->max_uA)
@@ -982,7 +982,7 @@
 	const struct regulation_constraints *constraints)
 {
 	int ret = 0;
-	struct regulator_ops *ops = rdev->desc->ops;
+	const struct regulator_ops *ops = rdev->desc->ops;
 
 	if (constraints)
 		rdev->constraints = kmemdup(constraints, sizeof(*constraints),
@@ -1759,6 +1759,45 @@
 	return 0;
 }
 
+/**
+ * _regulator_enable_delay - a delay helper function
+ * @delay: time to delay in microseconds
+ *
+ * Delay for the requested amount of time as per the guidelines in:
+ *
+ *     Documentation/timers/timers-howto.txt
+ *
+ * The assumption here is that regulators will never be enabled in
+ * atomic context and therefore sleeping functions can be used.
+ */
+static void _regulator_enable_delay(unsigned int delay)
+{
+	unsigned int ms = delay / 1000;
+	unsigned int us = delay % 1000;
+
+	if (ms > 0) {
+		/*
+		 * For small enough values, handle super-millisecond
+		 * delays in the usleep_range() call below.
+		 */
+		if (ms < 20)
+			us += ms * 1000;
+		else
+			msleep(ms);
+	}
+
+	/*
+	 * Give the scheduler some room to coalesce with any other
+	 * wakeup sources. For delays shorter than 10 us, don't even
+	 * bother setting up high-resolution timers and just busy-
+	 * loop.
+	 */
+	if (us >= 10)
+		usleep_range(us, us + 100);
+	else
+		udelay(us);
+}
+
 static int _regulator_do_enable(struct regulator_dev *rdev)
 {
 	int ret, delay;
@@ -1774,6 +1813,31 @@
 
 	trace_regulator_enable(rdev_get_name(rdev));
 
+	if (rdev->desc->off_on_delay) {
+		/* if needed, keep a distance of off_on_delay from last time
+		 * this regulator was disabled.
+		 */
+		unsigned long start_jiffy = jiffies;
+		unsigned long intended, max_delay, remaining;
+
+		max_delay = usecs_to_jiffies(rdev->desc->off_on_delay);
+		intended = rdev->last_off_jiffy + max_delay;
+
+		if (time_before(start_jiffy, intended)) {
+			/* calc remaining jiffies to deal with one-time
+			 * timer wrapping.
+			 * in case of multiple timer wrapping, either it can be
+			 * detected by out-of-range remaining, or it cannot be
+			 * detected and we gets a panelty of
+			 * _regulator_enable_delay().
+			 */
+			remaining = intended - start_jiffy;
+			if (remaining <= max_delay)
+				_regulator_enable_delay(
+						jiffies_to_usecs(remaining));
+		}
+	}
+
 	if (rdev->ena_pin) {
 		ret = regulator_ena_gpio_ctrl(rdev, true);
 		if (ret < 0)
@@ -1792,40 +1856,7 @@
 	 * together.  */
 	trace_regulator_enable_delay(rdev_get_name(rdev));
 
-	/*
-	 * Delay for the requested amount of time as per the guidelines in:
-	 *
-	 *     Documentation/timers/timers-howto.txt
-	 *
-	 * The assumption here is that regulators will never be enabled in
-	 * atomic context and therefore sleeping functions can be used.
-	 */
-	if (delay) {
-		unsigned int ms = delay / 1000;
-		unsigned int us = delay % 1000;
-
-		if (ms > 0) {
-			/*
-			 * For small enough values, handle super-millisecond
-			 * delays in the usleep_range() call below.
-			 */
-			if (ms < 20)
-				us += ms * 1000;
-			else
-				msleep(ms);
-		}
-
-		/*
-		 * Give the scheduler some room to coalesce with any other
-		 * wakeup sources. For delays shorter than 10 us, don't even
-		 * bother setting up high-resolution timers and just busy-
-		 * loop.
-		 */
-		if (us >= 10)
-			usleep_range(us, us + 100);
-		else
-			udelay(us);
-	}
+	_regulator_enable_delay(delay);
 
 	trace_regulator_enable_complete(rdev_get_name(rdev));
 
@@ -1919,6 +1950,12 @@
 			return ret;
 	}
 
+	/* cares about last_off_jiffy only if off_on_delay is required by
+	 * device.
+	 */
+	if (rdev->desc->off_on_delay)
+		rdev->last_off_jiffy = jiffies;
+
 	trace_regulator_disable_complete(rdev_get_name(rdev));
 
 	return 0;
@@ -2208,9 +2245,9 @@
  */
 int regulator_list_voltage(struct regulator *regulator, unsigned selector)
 {
-	struct regulator_dev	*rdev = regulator->rdev;
-	struct regulator_ops	*ops = rdev->desc->ops;
-	int			ret;
+	struct regulator_dev *rdev = regulator->rdev;
+	const struct regulator_ops *ops = rdev->desc->ops;
+	int ret;
 
 	if (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1 && !selector)
 		return rdev->desc->fixed_uV;
@@ -2270,8 +2307,8 @@
 					 unsigned *vsel_reg,
 					 unsigned *vsel_mask)
 {
-	struct regulator_dev	*rdev = regulator->rdev;
-	struct regulator_ops	*ops = rdev->desc->ops;
+	struct regulator_dev *rdev = regulator->rdev;
+	const struct regulator_ops *ops = rdev->desc->ops;
 
 	if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap)
 		return -EOPNOTSUPP;
@@ -2297,8 +2334,8 @@
 int regulator_list_hardware_vsel(struct regulator *regulator,
 				 unsigned selector)
 {
-	struct regulator_dev	*rdev = regulator->rdev;
-	struct regulator_ops	*ops = rdev->desc->ops;
+	struct regulator_dev *rdev = regulator->rdev;
+	const struct regulator_ops *ops = rdev->desc->ops;
 
 	if (selector >= rdev->desc->n_voltages)
 		return -EINVAL;
@@ -2572,8 +2609,8 @@
 int regulator_set_voltage_time(struct regulator *regulator,
 			       int old_uV, int new_uV)
 {
-	struct regulator_dev	*rdev = regulator->rdev;
-	struct regulator_ops	*ops = rdev->desc->ops;
+	struct regulator_dev *rdev = regulator->rdev;
+	const struct regulator_ops *ops = rdev->desc->ops;
 	int old_sel = -1;
 	int new_sel = -1;
 	int voltage;
@@ -3336,9 +3373,9 @@
  */
 static int add_regulator_attributes(struct regulator_dev *rdev)
 {
-	struct device		*dev = &rdev->dev;
-	struct regulator_ops	*ops = rdev->desc->ops;
-	int			status = 0;
+	struct device *dev = &rdev->dev;
+	const struct regulator_ops *ops = rdev->desc->ops;
+	int status = 0;
 
 	/* some attributes need specific methods to be displayed */
 	if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
@@ -3516,12 +3553,17 @@
 		return ERR_PTR(-EINVAL);
 	}
 
-	init_data = config->init_data;
-
 	rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
 	if (rdev == NULL)
 		return ERR_PTR(-ENOMEM);
 
+	init_data = regulator_of_get_init_data(dev, regulator_desc,
+					       &rdev->dev.of_node);
+	if (!init_data) {
+		init_data = config->init_data;
+		rdev->dev.of_node = of_node_get(config->of_node);
+	}
+
 	mutex_lock(&regulator_list_mutex);
 
 	mutex_init(&rdev->mutex);
@@ -3548,7 +3590,6 @@
 
 	/* register with sysfs */
 	rdev->dev.class = &regulator_class;
-	rdev->dev.of_node = of_node_get(config->of_node);
 	rdev->dev.parent = dev;
 	dev_set_name(&rdev->dev, "regulator.%d",
 		     atomic_inc_return(&regulator_no) - 1);
@@ -3905,7 +3946,7 @@
 static int __init regulator_init_complete(void)
 {
 	struct regulator_dev *rdev;
-	struct regulator_ops *ops;
+	const struct regulator_ops *ops;
 	struct regulation_constraints *c;
 	int enabled, ret;
 
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index 714fd9a..f8e4257 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -18,6 +18,8 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of_device.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/regmap.h>
@@ -50,6 +52,11 @@
 
 #define FAN53555_NVOLTAGES	64	/* Numbers of voltages */
 
+enum fan53555_vendor {
+	FAN53555_VENDOR_FAIRCHILD = 0,
+	FAN53555_VENDOR_SILERGY,
+};
+
 /* IC Type */
 enum {
 	FAN53555_CHIP_ID_00 = 0,
@@ -60,7 +67,12 @@
 	FAN53555_CHIP_ID_05,
 };
 
+enum {
+	SILERGY_SYR82X = 8,
+};
+
 struct fan53555_device_info {
+	enum fan53555_vendor vendor;
 	struct regmap *regmap;
 	struct device *dev;
 	struct regulator_desc desc;
@@ -135,6 +147,38 @@
 		return REGULATOR_MODE_NORMAL;
 }
 
+static int slew_rates[] = {
+	64000,
+	32000,
+	16000,
+	 8000,
+	 4000,
+	 2000,
+	 1000,
+	  500,
+};
+
+static int fan53555_set_ramp(struct regulator_dev *rdev, int ramp)
+{
+	struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+	int regval = -1, i;
+
+	for (i = 0; i < ARRAY_SIZE(slew_rates); i++) {
+		if (ramp <= slew_rates[i])
+			regval = i;
+		else
+			break;
+	}
+
+	if (regval < 0) {
+		dev_err(di->dev, "unsupported ramp value %d\n", ramp);
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(di->regmap, FAN53555_CONTROL,
+				  CTL_SLEW_MASK, regval << CTL_SLEW_SHIFT);
+}
+
 static struct regulator_ops fan53555_regulator_ops = {
 	.set_voltage_sel = regulator_set_voltage_sel_regmap,
 	.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -146,32 +190,11 @@
 	.is_enabled = regulator_is_enabled_regmap,
 	.set_mode = fan53555_set_mode,
 	.get_mode = fan53555_get_mode,
+	.set_ramp_delay = fan53555_set_ramp,
 };
 
-/* For 00,01,03,05 options:
- * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
- * For 04 option:
- * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
- * */
-static int fan53555_device_setup(struct fan53555_device_info *di,
-				struct fan53555_platform_data *pdata)
+static int fan53555_voltages_setup_fairchild(struct fan53555_device_info *di)
 {
-	unsigned int reg, data, mask;
-
-	/* Setup voltage control register */
-	switch (pdata->sleep_vsel_id) {
-	case FAN53555_VSEL_ID_0:
-		di->sleep_reg = FAN53555_VSEL0;
-		di->vol_reg = FAN53555_VSEL1;
-		break;
-	case FAN53555_VSEL_ID_1:
-		di->sleep_reg = FAN53555_VSEL1;
-		di->vol_reg = FAN53555_VSEL0;
-		break;
-	default:
-		dev_err(di->dev, "Invalid VSEL ID!\n");
-		return -EINVAL;
-	}
 	/* Init voltage range and step */
 	switch (di->chip_id) {
 	case FAN53555_CHIP_ID_00:
@@ -187,18 +210,68 @@
 		break;
 	default:
 		dev_err(di->dev,
-			"Chip ID[%d]\n not supported!\n", di->chip_id);
+			"Chip ID %d not supported!\n", di->chip_id);
 		return -EINVAL;
 	}
-	/* Init slew rate */
-	if (pdata->slew_rate & 0x7)
-		di->slew_rate = pdata->slew_rate;
-	else
-		di->slew_rate = FAN53555_SLEW_RATE_64MV;
-	reg = FAN53555_CONTROL;
-	data = di->slew_rate << CTL_SLEW_SHIFT;
-	mask = CTL_SLEW_MASK;
-	return regmap_update_bits(di->regmap, reg, mask, data);
+
+	return 0;
+}
+
+static int fan53555_voltages_setup_silergy(struct fan53555_device_info *di)
+{
+	/* Init voltage range and step */
+	switch (di->chip_id) {
+	case SILERGY_SYR82X:
+		di->vsel_min = 712500;
+		di->vsel_step = 12500;
+		break;
+	default:
+		dev_err(di->dev,
+			"Chip ID %d not supported!\n", di->chip_id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* For 00,01,03,05 options:
+ * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
+ * For 04 option:
+ * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
+ * */
+static int fan53555_device_setup(struct fan53555_device_info *di,
+				struct fan53555_platform_data *pdata)
+{
+	int ret = 0;
+
+	/* Setup voltage control register */
+	switch (pdata->sleep_vsel_id) {
+	case FAN53555_VSEL_ID_0:
+		di->sleep_reg = FAN53555_VSEL0;
+		di->vol_reg = FAN53555_VSEL1;
+		break;
+	case FAN53555_VSEL_ID_1:
+		di->sleep_reg = FAN53555_VSEL1;
+		di->vol_reg = FAN53555_VSEL0;
+		break;
+	default:
+		dev_err(di->dev, "Invalid VSEL ID!\n");
+		return -EINVAL;
+	}
+
+	switch (di->vendor) {
+	case FAN53555_VENDOR_FAIRCHILD:
+		ret = fan53555_voltages_setup_fairchild(di);
+		break;
+	case FAN53555_VENDOR_SILERGY:
+		ret = fan53555_voltages_setup_silergy(di);
+		break;
+	default:
+		dev_err(di->dev, "vendor %d not supported!\n", di->vendor);
+		return -EINVAL;
+	}
+
+	return ret;
 }
 
 static int fan53555_regulator_register(struct fan53555_device_info *di,
@@ -207,6 +280,7 @@
 	struct regulator_desc *rdesc = &di->desc;
 
 	rdesc->name = "fan53555-reg";
+	rdesc->supply_name = "vin";
 	rdesc->ops = &fan53555_regulator_ops;
 	rdesc->type = REGULATOR_VOLTAGE;
 	rdesc->n_voltages = FAN53555_NVOLTAGES;
@@ -227,9 +301,46 @@
 	.val_bits = 8,
 };
 
+static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev,
+							struct device_node *np)
+{
+	struct fan53555_platform_data *pdata;
+	int ret;
+	u32 tmp;
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return NULL;
+
+	pdata->regulator = of_get_regulator_init_data(dev, np);
+
+	ret = of_property_read_u32(np, "fcs,suspend-voltage-selector",
+				   &tmp);
+	if (!ret)
+		pdata->sleep_vsel_id = tmp;
+
+	return pdata;
+}
+
+static const struct of_device_id fan53555_dt_ids[] = {
+	{
+		.compatible = "fcs,fan53555",
+		.data = (void *)FAN53555_VENDOR_FAIRCHILD
+	}, {
+		.compatible = "silergy,syr827",
+		.data = (void *)FAN53555_VENDOR_SILERGY,
+	}, {
+		.compatible = "silergy,syr828",
+		.data = (void *)FAN53555_VENDOR_SILERGY,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, fan53555_dt_ids);
+
 static int fan53555_regulator_probe(struct i2c_client *client,
 				const struct i2c_device_id *id)
 {
+	struct device_node *np = client->dev.of_node;
 	struct fan53555_device_info *di;
 	struct fan53555_platform_data *pdata;
 	struct regulator_config config = { };
@@ -237,6 +348,9 @@
 	int ret;
 
 	pdata = dev_get_platdata(&client->dev);
+	if (!pdata)
+		pdata = fan53555_parse_dt(&client->dev, np);
+
 	if (!pdata || !pdata->regulator) {
 		dev_err(&client->dev, "Platform data not found!\n");
 		return -ENODEV;
@@ -247,13 +361,35 @@
 	if (!di)
 		return -ENOMEM;
 
+	di->regulator = pdata->regulator;
+	if (client->dev.of_node) {
+		const struct of_device_id *match;
+
+		match = of_match_device(of_match_ptr(fan53555_dt_ids),
+					&client->dev);
+		if (!match)
+			return -ENODEV;
+
+		di->vendor = (unsigned long) match->data;
+	} else {
+		/* if no ramp constraint set, get the pdata ramp_delay */
+		if (!di->regulator->constraints.ramp_delay) {
+			int slew_idx = (pdata->slew_rate & 0x7)
+						? pdata->slew_rate : 0;
+
+			di->regulator->constraints.ramp_delay
+						= slew_rates[slew_idx];
+		}
+
+		di->vendor = id->driver_data;
+	}
+
 	di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config);
 	if (IS_ERR(di->regmap)) {
 		dev_err(&client->dev, "Failed to allocate regmap!\n");
 		return PTR_ERR(di->regmap);
 	}
 	di->dev = &client->dev;
-	di->regulator = pdata->regulator;
 	i2c_set_clientdata(client, di);
 	/* Get chip ID */
 	ret = regmap_read(di->regmap, FAN53555_ID1, &val);
@@ -282,6 +418,8 @@
 	config.init_data = di->regulator;
 	config.regmap = di->regmap;
 	config.driver_data = di;
+	config.of_node = np;
+
 	ret = fan53555_regulator_register(di, &config);
 	if (ret < 0)
 		dev_err(&client->dev, "Failed to register regulator!\n");
@@ -290,13 +428,20 @@
 }
 
 static const struct i2c_device_id fan53555_id[] = {
-	{"fan53555", -1},
+	{
+		.name = "fan53555",
+		.driver_data = FAN53555_VENDOR_FAIRCHILD
+	}, {
+		.name = "syr82x",
+		.driver_data = FAN53555_VENDOR_SILERGY
+	},
 	{ },
 };
 
 static struct i2c_driver fan53555_regulator_driver = {
 	.driver = {
 		.name = "fan53555-regulator",
+		.of_match_table = of_match_ptr(fan53555_dt_ids),
 	},
 	.probe = fan53555_regulator_probe,
 	.id_table = fan53555_id,
diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c
new file mode 100644
index 0000000..156d0d1
--- /dev/null
+++ b/drivers/regulator/hi6421-regulator.c
@@ -0,0 +1,634 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) <2011-2014> HiSilicon Technologies Co., Ltd.
+ *              http://www.hisilicon.com
+ * Copyright (c) <2013-2014> Linaro Ltd.
+ *              http://www.linaro.org
+ *
+ * Author: Guodong Xu <guodong.xu@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/hi6421-pmic.h>
+
+/*
+ * struct hi6421_regulator_pdata - Hi6421 regulator data of platform device
+ * @lock: mutex to serialize regulator enable
+ */
+struct hi6421_regulator_pdata {
+	struct mutex lock;
+};
+
+/*
+ * struct hi6421_regulator_info - hi6421 regulator information
+ * @desc: regulator description
+ * @mode_mask: ECO mode bitmask of LDOs; for BUCKs, this masks sleep
+ * @eco_microamp: eco mode load upper limit (in uA), valid for LDOs only
+ */
+struct hi6421_regulator_info {
+	struct regulator_desc	desc;
+	u8		mode_mask;
+	u32		eco_microamp;
+};
+
+/* HI6421 regulators */
+enum hi6421_regulator_id {
+	HI6421_LDO0,
+	HI6421_LDO1,
+	HI6421_LDO2,
+	HI6421_LDO3,
+	HI6421_LDO4,
+	HI6421_LDO5,
+	HI6421_LDO6,
+	HI6421_LDO7,
+	HI6421_LDO8,
+	HI6421_LDO9,
+	HI6421_LDO10,
+	HI6421_LDO11,
+	HI6421_LDO12,
+	HI6421_LDO13,
+	HI6421_LDO14,
+	HI6421_LDO15,
+	HI6421_LDO16,
+	HI6421_LDO17,
+	HI6421_LDO18,
+	HI6421_LDO19,
+	HI6421_LDO20,
+	HI6421_LDOAUDIO,
+	HI6421_BUCK0,
+	HI6421_BUCK1,
+	HI6421_BUCK2,
+	HI6421_BUCK3,
+	HI6421_BUCK4,
+	HI6421_BUCK5,
+	HI6421_NUM_REGULATORS,
+};
+
+#define HI6421_REGULATOR_OF_MATCH(_name, id)				\
+{									\
+	.name = #_name,							\
+	.driver_data = (void *) HI6421_##id,				\
+}
+
+static struct of_regulator_match hi6421_regulator_match[] = {
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout0, LDO0),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout1, LDO1),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout2, LDO2),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout3, LDO3),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout4, LDO4),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout5, LDO5),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout6, LDO6),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout7, LDO7),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout8, LDO8),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout9, LDO9),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout10, LDO10),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout11, LDO11),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout12, LDO12),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout13, LDO13),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout14, LDO14),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout15, LDO15),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout16, LDO16),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout17, LDO17),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout18, LDO18),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout19, LDO19),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout20, LDO20),
+	HI6421_REGULATOR_OF_MATCH(hi6421_vout_audio, LDOAUDIO),
+	HI6421_REGULATOR_OF_MATCH(hi6421_buck0, BUCK0),
+	HI6421_REGULATOR_OF_MATCH(hi6421_buck1, BUCK1),
+	HI6421_REGULATOR_OF_MATCH(hi6421_buck2, BUCK2),
+	HI6421_REGULATOR_OF_MATCH(hi6421_buck3, BUCK3),
+	HI6421_REGULATOR_OF_MATCH(hi6421_buck4, BUCK4),
+	HI6421_REGULATOR_OF_MATCH(hi6421_buck5, BUCK5),
+};
+
+/* LDO 0, 4~7, 9~14, 16~20 have same voltage table. */
+static const unsigned int ldo_0_voltages[] = {
+	1500000, 1800000, 2400000, 2500000,
+	2600000, 2700000, 2850000, 3000000,
+};
+
+/* LDO 8, 15 have same voltage table. */
+static const unsigned int ldo_8_voltages[] = {
+	1500000, 1800000, 2400000, 2600000,
+	2700000, 2850000, 3000000, 3300000,
+};
+
+/* Ranges are sorted in ascending order. */
+static const struct regulator_linear_range ldo_audio_volt_range[] = {
+	REGULATOR_LINEAR_RANGE(2800000, 0, 3, 50000),
+	REGULATOR_LINEAR_RANGE(3000000, 4, 7, 100000),
+};
+
+static const unsigned int buck_3_voltages[] = {
+	 950000, 1050000, 1100000, 1117000,
+	1134000, 1150000, 1167000, 1200000,
+};
+
+static const unsigned int buck_4_voltages[] = {
+	1150000, 1200000, 1250000, 1350000,
+	1700000, 1800000, 1900000, 2000000,
+};
+
+static const unsigned int buck_5_voltages[] = {
+	1150000, 1200000, 1250000, 1350000,
+	1600000, 1700000, 1800000, 1900000,
+};
+
+static const struct regulator_ops hi6421_ldo_ops;
+static const struct regulator_ops hi6421_ldo_linear_ops;
+static const struct regulator_ops hi6421_ldo_linear_range_ops;
+static const struct regulator_ops hi6421_buck012_ops;
+static const struct regulator_ops hi6421_buck345_ops;
+
+#define HI6421_LDO_ENABLE_TIME (350)
+/*
+ * _id - LDO id name string
+ * v_table - voltage table
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * ecomask - eco mode mask
+ * ecoamp - eco mode load uppler limit in uA
+ */
+#define HI6421_LDO(_id, v_table, vreg, vmask, ereg, emask,		\
+		   odelay, ecomask, ecoamp)				\
+	[HI6421_##_id] = {						\
+		.desc = {						\
+			.name		= #_id,				\
+			.ops		= &hi6421_ldo_ops,		\
+			.type		= REGULATOR_VOLTAGE,		\
+			.id		= HI6421_##_id,			\
+			.owner		= THIS_MODULE,			\
+			.n_voltages	= ARRAY_SIZE(v_table),		\
+			.volt_table	= v_table,			\
+			.vsel_reg	= HI6421_REG_TO_BUS_ADDR(vreg),	\
+			.vsel_mask	= vmask,			\
+			.enable_reg	= HI6421_REG_TO_BUS_ADDR(ereg),	\
+			.enable_mask	= emask,			\
+			.enable_time	= HI6421_LDO_ENABLE_TIME,	\
+			.off_on_delay	= odelay,			\
+		},							\
+		.mode_mask		= ecomask,			\
+		.eco_microamp		= ecoamp,			\
+	}
+
+/* HI6421 LDO1~3 are linear voltage regulators at fixed uV_step
+ *
+ * _id - LDO id name string
+ * _min_uV - minimum voltage supported in uV
+ * n_volt - number of votages available
+ * vstep - voltage increase in each linear step in uV
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * ecomask - eco mode mask
+ * ecoamp - eco mode load uppler limit in uA
+ */
+#define HI6421_LDO_LINEAR(_id, _min_uV, n_volt, vstep, vreg, vmask,	\
+			  ereg, emask, odelay, ecomask, ecoamp)		\
+	[HI6421_##_id] = {						\
+		.desc = {						\
+			.name		= #_id,				\
+			.ops		= &hi6421_ldo_linear_ops,	\
+			.type		= REGULATOR_VOLTAGE,		\
+			.id		= HI6421_##_id,			\
+			.owner		= THIS_MODULE,			\
+			.min_uV		= _min_uV,			\
+			.n_voltages	= n_volt,			\
+			.uV_step	= vstep,			\
+			.vsel_reg	= HI6421_REG_TO_BUS_ADDR(vreg),	\
+			.vsel_mask	= vmask,			\
+			.enable_reg	= HI6421_REG_TO_BUS_ADDR(ereg),	\
+			.enable_mask	= emask,			\
+			.enable_time	= HI6421_LDO_ENABLE_TIME,	\
+			.off_on_delay	= odelay,			\
+		},							\
+		.mode_mask		= ecomask,			\
+		.eco_microamp		= ecoamp,			\
+	}
+
+/* HI6421 LDOAUDIO is a linear voltage regulator with two 4-step ranges
+ *
+ * _id - LDO id name string
+ * n_volt - number of votages available
+ * volt_ranges - array of regulator_linear_range
+ * vstep - voltage increase in each linear step in uV
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * ecomask - eco mode mask
+ * ecoamp - eco mode load uppler limit in uA
+ */
+#define HI6421_LDO_LINEAR_RANGE(_id, n_volt, volt_ranges, vreg, vmask,	\
+				ereg, emask, odelay, ecomask, ecoamp)	\
+	[HI6421_##_id] = {						\
+		.desc = {						\
+			.name		= #_id,				\
+			.ops		= &hi6421_ldo_linear_range_ops,	\
+			.type		= REGULATOR_VOLTAGE,		\
+			.id		= HI6421_##_id,			\
+			.owner		= THIS_MODULE,			\
+			.n_voltages	= n_volt,			\
+			.linear_ranges	= volt_ranges,			\
+			.n_linear_ranges = ARRAY_SIZE(volt_ranges),	\
+			.vsel_reg	= HI6421_REG_TO_BUS_ADDR(vreg),	\
+			.vsel_mask	= vmask,			\
+			.enable_reg	= HI6421_REG_TO_BUS_ADDR(ereg),	\
+			.enable_mask	= emask,			\
+			.enable_time	= HI6421_LDO_ENABLE_TIME,	\
+			.off_on_delay	= odelay,			\
+		},							\
+		.mode_mask		= ecomask,			\
+		.eco_microamp		= ecoamp,			\
+	}
+
+/* HI6421 BUCK0/1/2 are linear voltage regulators at fixed uV_step
+ *
+ * _id - BUCK0/1/2 id name string
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * sleepmask - mask of sleep mode
+ * etime - enable time
+ * odelay - off/on delay time in uS
+ */
+#define HI6421_BUCK012(_id, vreg, vmask, ereg, emask, sleepmask,	\
+			etime, odelay)					\
+	[HI6421_##_id] = {						\
+		.desc = {						\
+			.name		= #_id,				\
+			.ops		= &hi6421_buck012_ops,		\
+			.type		= REGULATOR_VOLTAGE,		\
+			.id		= HI6421_##_id,			\
+			.owner		= THIS_MODULE,			\
+			.min_uV		= 700000,			\
+			.n_voltages	= 128,				\
+			.uV_step	= 7086,				\
+			.vsel_reg	= HI6421_REG_TO_BUS_ADDR(vreg),	\
+			.vsel_mask	= vmask,			\
+			.enable_reg	= HI6421_REG_TO_BUS_ADDR(ereg),	\
+			.enable_mask	= emask,			\
+			.enable_time	= etime,			\
+			.off_on_delay	= odelay,			\
+		},							\
+		.mode_mask		= sleepmask,			\
+	}
+
+/* HI6421 BUCK3/4/5 share similar configurations as LDOs, with exception
+ *  that it supports SLEEP mode, so has different .ops.
+ *
+ * _id - LDO id name string
+ * v_table - voltage table
+ * vreg - voltage select register
+ * vmask - voltage select mask
+ * ereg - enable register
+ * emask - enable mask
+ * odelay - off/on delay time in uS
+ * sleepmask - mask of sleep mode
+ */
+#define HI6421_BUCK345(_id, v_table, vreg, vmask, ereg, emask,		\
+			odelay, sleepmask)				\
+	[HI6421_##_id] = {						\
+		.desc = {						\
+			.name		= #_id,				\
+			.ops		= &hi6421_buck345_ops,		\
+			.type		= REGULATOR_VOLTAGE,		\
+			.id		= HI6421_##_id,			\
+			.owner		= THIS_MODULE,			\
+			.n_voltages	= ARRAY_SIZE(v_table),		\
+			.volt_table	= v_table,			\
+			.vsel_reg	= HI6421_REG_TO_BUS_ADDR(vreg),	\
+			.vsel_mask	= vmask,			\
+			.enable_reg	= HI6421_REG_TO_BUS_ADDR(ereg),	\
+			.enable_mask	= emask,			\
+			.enable_time	= HI6421_LDO_ENABLE_TIME,	\
+			.off_on_delay	= odelay,			\
+		},							\
+		.mode_mask		= sleepmask,			\
+	}
+
+/* HI6421 regulator information */
+static struct hi6421_regulator_info
+		hi6421_regulator_info[HI6421_NUM_REGULATORS] = {
+	HI6421_LDO(LDO0, ldo_0_voltages, 0x20, 0x07, 0x20, 0x10,
+		   10000, 0x20, 8000),
+	HI6421_LDO_LINEAR(LDO1, 1700000, 4, 100000, 0x21, 0x03, 0x21, 0x10,
+			  10000, 0x20, 5000),
+	HI6421_LDO_LINEAR(LDO2, 1050000, 8, 50000, 0x22, 0x07, 0x22, 0x10,
+			  20000, 0x20, 8000),
+	HI6421_LDO_LINEAR(LDO3, 1050000, 8, 50000, 0x23, 0x07, 0x23, 0x10,
+			  20000, 0x20, 8000),
+	HI6421_LDO(LDO4, ldo_0_voltages, 0x24, 0x07, 0x24, 0x10,
+		   20000, 0x20, 8000),
+	HI6421_LDO(LDO5, ldo_0_voltages, 0x25, 0x07, 0x25, 0x10,
+		   20000, 0x20, 8000),
+	HI6421_LDO(LDO6, ldo_0_voltages, 0x26, 0x07, 0x26, 0x10,
+		   20000, 0x20, 8000),
+	HI6421_LDO(LDO7, ldo_0_voltages, 0x27, 0x07, 0x27, 0x10,
+		   20000, 0x20, 5000),
+	HI6421_LDO(LDO8, ldo_8_voltages, 0x28, 0x07, 0x28, 0x10,
+		   20000, 0x20, 8000),
+	HI6421_LDO(LDO9, ldo_0_voltages, 0x29, 0x07, 0x29, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO10, ldo_0_voltages, 0x2a, 0x07, 0x2a, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO11, ldo_0_voltages, 0x2b, 0x07, 0x2b, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO12, ldo_0_voltages, 0x2c, 0x07, 0x2c, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO13, ldo_0_voltages, 0x2d, 0x07, 0x2d, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO14, ldo_0_voltages, 0x2e, 0x07, 0x2e, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO15, ldo_8_voltages, 0x2f, 0x07, 0x2f, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO16, ldo_0_voltages, 0x30, 0x07, 0x30, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO17, ldo_0_voltages, 0x31, 0x07, 0x31, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO18, ldo_0_voltages, 0x32, 0x07, 0x32, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO19, ldo_0_voltages, 0x33, 0x07, 0x33, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO(LDO20, ldo_0_voltages, 0x34, 0x07, 0x34, 0x10,
+		   40000, 0x20, 8000),
+	HI6421_LDO_LINEAR_RANGE(LDOAUDIO, 8, ldo_audio_volt_range, 0x36,
+				0x70, 0x36, 0x01, 40000, 0x02, 5000),
+	HI6421_BUCK012(BUCK0, 0x0d, 0x7f, 0x0c, 0x01, 0x10, 400, 20000),
+	HI6421_BUCK012(BUCK1, 0x0f, 0x7f, 0x0e, 0x01, 0x10, 400, 20000),
+	HI6421_BUCK012(BUCK2, 0x11, 0x7f, 0x10, 0x01, 0x10, 350, 100),
+	HI6421_BUCK345(BUCK3, buck_3_voltages, 0x13, 0x07, 0x12, 0x01,
+		       20000, 0x10),
+	HI6421_BUCK345(BUCK4, buck_4_voltages, 0x15, 0x07, 0x14, 0x01,
+		       20000, 0x10),
+	HI6421_BUCK345(BUCK5, buck_5_voltages, 0x17, 0x07, 0x16, 0x01,
+		       20000, 0x10),
+};
+
+static int hi6421_regulator_enable(struct regulator_dev *rdev)
+{
+	struct hi6421_regulator_pdata *pdata;
+
+	pdata = dev_get_drvdata(rdev->dev.parent);
+	/* hi6421 spec requires regulator enablement must be serialized:
+	 *  - Because when BUCK, LDO switching from off to on, it will have
+	 *    a huge instantaneous current; so you can not turn on two or
+	 *    more LDO or BUCKs simultaneously, or it may burn the chip.
+	 */
+	mutex_lock(&pdata->lock);
+
+	/* call regulator regmap helper */
+	regulator_enable_regmap(rdev);
+
+	mutex_unlock(&pdata->lock);
+	return 0;
+}
+
+static unsigned int hi6421_regulator_ldo_get_mode(struct regulator_dev *rdev)
+{
+	struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+	u32 reg_val;
+
+	regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
+	if (reg_val & info->mode_mask)
+		return REGULATOR_MODE_IDLE;
+
+	return REGULATOR_MODE_NORMAL;
+}
+
+static unsigned int hi6421_regulator_buck_get_mode(struct regulator_dev *rdev)
+{
+	struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+	u32 reg_val;
+
+	regmap_read(rdev->regmap, rdev->desc->enable_reg, &reg_val);
+	if (reg_val & info->mode_mask)
+		return REGULATOR_MODE_STANDBY;
+
+	return REGULATOR_MODE_NORMAL;
+}
+
+static int hi6421_regulator_ldo_set_mode(struct regulator_dev *rdev,
+						unsigned int mode)
+{
+	struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+	u32 new_mode;
+
+	switch (mode) {
+	case REGULATOR_MODE_NORMAL:
+		new_mode = 0;
+		break;
+	case REGULATOR_MODE_IDLE:
+		new_mode = info->mode_mask;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set mode */
+	regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+			   info->mode_mask, new_mode);
+
+	return 0;
+}
+
+static int hi6421_regulator_buck_set_mode(struct regulator_dev *rdev,
+						unsigned int mode)
+{
+	struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+	u32 new_mode;
+
+	switch (mode) {
+	case REGULATOR_MODE_NORMAL:
+		new_mode = 0;
+		break;
+	case REGULATOR_MODE_STANDBY:
+		new_mode = info->mode_mask;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set mode */
+	regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+			   info->mode_mask, new_mode);
+
+	return 0;
+}
+
+unsigned int hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev,
+			int input_uV, int output_uV, int load_uA)
+{
+	struct hi6421_regulator_info *info = rdev_get_drvdata(rdev);
+
+	if (load_uA > info->eco_microamp)
+		return REGULATOR_MODE_NORMAL;
+
+	return REGULATOR_MODE_IDLE;
+}
+
+static const struct regulator_ops hi6421_ldo_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = hi6421_regulator_enable,
+	.disable = regulator_disable_regmap,
+	.list_voltage = regulator_list_voltage_table,
+	.map_voltage = regulator_map_voltage_ascend,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_mode = hi6421_regulator_ldo_get_mode,
+	.set_mode = hi6421_regulator_ldo_set_mode,
+	.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
+};
+
+static const struct regulator_ops hi6421_ldo_linear_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = hi6421_regulator_enable,
+	.disable = regulator_disable_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+	.map_voltage = regulator_map_voltage_linear,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_mode = hi6421_regulator_ldo_get_mode,
+	.set_mode = hi6421_regulator_ldo_set_mode,
+	.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
+};
+
+static const struct regulator_ops hi6421_ldo_linear_range_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = hi6421_regulator_enable,
+	.disable = regulator_disable_regmap,
+	.list_voltage = regulator_list_voltage_linear_range,
+	.map_voltage = regulator_map_voltage_linear_range,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_mode = hi6421_regulator_ldo_get_mode,
+	.set_mode = hi6421_regulator_ldo_set_mode,
+	.get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode,
+};
+
+static const struct regulator_ops hi6421_buck012_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = hi6421_regulator_enable,
+	.disable = regulator_disable_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+	.map_voltage = regulator_map_voltage_linear,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_mode = hi6421_regulator_buck_get_mode,
+	.set_mode = hi6421_regulator_buck_set_mode,
+};
+
+static const struct regulator_ops hi6421_buck345_ops = {
+	.is_enabled = regulator_is_enabled_regmap,
+	.enable = hi6421_regulator_enable,
+	.disable = regulator_disable_regmap,
+	.list_voltage = regulator_list_voltage_table,
+	.map_voltage = regulator_map_voltage_ascend,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_mode = hi6421_regulator_buck_get_mode,
+	.set_mode = hi6421_regulator_buck_set_mode,
+};
+
+static int hi6421_regulator_register(struct platform_device *pdev,
+				     struct regmap *rmap,
+				     struct regulator_init_data *init_data,
+				     int id, struct device_node *np)
+{
+	struct hi6421_regulator_info *info = NULL;
+	struct regulator_config config = { };
+	struct regulator_dev *rdev;
+
+	/* assign per-regulator data */
+	info = &hi6421_regulator_info[id];
+
+	config.dev = &pdev->dev;
+	config.init_data = init_data;
+	config.driver_data = info;
+	config.regmap = rmap;
+	config.of_node = np;
+
+	/* register regulator with framework */
+	rdev = devm_regulator_register(&pdev->dev, &info->desc, &config);
+	if (IS_ERR(rdev)) {
+		dev_err(&pdev->dev, "failed to register regulator %s\n",
+			info->desc.name);
+		return PTR_ERR(rdev);
+	}
+
+	return 0;
+}
+
+static int hi6421_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np;
+	struct hi6421_pmic *pmic;
+	struct hi6421_regulator_pdata *pdata;
+	int i, ret = 0;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+	mutex_init(&pdata->lock);
+	platform_set_drvdata(pdev, pdata);
+
+	np = of_get_child_by_name(dev->parent->of_node, "regulators");
+	if (!np)
+		return -ENODEV;
+
+	ret = of_regulator_match(dev, np,
+				 hi6421_regulator_match,
+				 ARRAY_SIZE(hi6421_regulator_match));
+	of_node_put(np);
+	if (ret < 0) {
+		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+		return ret;
+	}
+
+	pmic = dev_get_drvdata(dev->parent);
+
+	for (i = 0; i < ARRAY_SIZE(hi6421_regulator_info); i++) {
+		ret = hi6421_regulator_register(pdev, pmic->regmap,
+			hi6421_regulator_match[i].init_data, i,
+			hi6421_regulator_match[i].of_node);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static struct platform_driver hi6421_regulator_driver = {
+	.driver = {
+		.name	= "hi6421-regulator",
+		.owner  = THIS_MODULE,
+	},
+	.probe	= hi6421_regulator_probe,
+};
+module_platform_driver(hi6421_regulator_driver);
+
+MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
+MODULE_DESCRIPTION("Hi6421 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h
index 84bbda1..a6043ad 100644
--- a/drivers/regulator/internal.h
+++ b/drivers/regulator/internal.h
@@ -35,4 +35,8 @@
 	struct dentry *debugfs;
 };
 
+struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
+			         const struct regulator_desc *desc,
+				 struct device_node **node);
+
 #endif
diff --git a/drivers/regulator/isl9305.c b/drivers/regulator/isl9305.c
new file mode 100644
index 0000000..92fefd9
--- /dev/null
+++ b/drivers/regulator/isl9305.c
@@ -0,0 +1,207 @@
+/*
+ * isl9305 - Intersil ISL9305 DCDC regulator
+ *
+ * Copyright 2014 Linaro Ltd
+ *
+ * Author: Mark Brown <broonie@kernel.org>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/platform_data/isl9305.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+
+/*
+ * Registers
+ */
+#define ISL9305_DCD1OUT          0x0
+#define ISL9305_DCD2OUT          0x1
+#define ISL9305_LDO1OUT          0x2
+#define ISL9305_LDO2OUT          0x3
+#define ISL9305_DCD_PARAMETER    0x4
+#define ISL9305_SYSTEM_PARAMETER 0x5
+#define ISL9305_DCD_SRCTL        0x6
+
+#define ISL9305_MAX_REG ISL9305_DCD_SRCTL
+
+/*
+ * DCD_PARAMETER
+ */
+#define ISL9305_DCD_PHASE   0x40
+#define ISL9305_DCD2_ULTRA  0x20
+#define ISL9305_DCD1_ULTRA  0x10
+#define ISL9305_DCD2_BLD    0x08
+#define ISL9305_DCD1_BLD    0x04
+#define ISL9305_DCD2_MODE   0x02
+#define ISL9305_DCD1_MODE   0x01
+
+/*
+ * SYSTEM_PARAMETER
+ */
+#define ISL9305_I2C_EN      0x40
+#define ISL9305_DCDPOR_MASK 0x30
+#define ISL9305_LDO2_EN     0x08
+#define ISL9305_LDO1_EN     0x04
+#define ISL9305_DCD2_EN     0x02
+#define ISL9305_DCD1_EN     0x01
+
+/*
+ * DCD_SRCTL
+ */
+#define ISL9305_DCD2SR_MASK 0xc0
+#define ISL9305_DCD1SR_MASK 0x07
+
+static const struct regulator_ops isl9305_ops = {
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_linear,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+static const struct regulator_desc isl9305_regulators[] = {
+	[ISL9305_DCD1] = {
+		.name =		"DCD1",
+		.of_match =	of_match_ptr("dcd1"),
+		.regulators_node = of_match_ptr("regulators"),
+		.n_voltages =	0x70,
+		.min_uV =	825000,
+		.uV_step =	25000,
+		.vsel_reg =	ISL9305_DCD1OUT,
+		.vsel_mask =	0x7f,
+		.enable_reg =	ISL9305_SYSTEM_PARAMETER,
+		.enable_mask =	ISL9305_DCD1_EN,
+		.supply_name =	"VINDCD1",
+		.ops =		&isl9305_ops,
+	},
+	[ISL9305_DCD2] = {
+		.name =		"DCD2",
+		.of_match =	of_match_ptr("dcd2"),
+		.regulators_node = of_match_ptr("regulators"),
+		.n_voltages =	0x70,
+		.min_uV =	825000,
+		.uV_step =	25000,
+		.vsel_reg =	ISL9305_DCD2OUT,
+		.vsel_mask =	0x7f,
+		.enable_reg =	ISL9305_SYSTEM_PARAMETER,
+		.enable_mask =	ISL9305_DCD2_EN,
+		.supply_name =	"VINDCD2",
+		.ops =		&isl9305_ops,
+	},
+	[ISL9305_LDO1] = {
+		.name =		"LDO1",
+		.of_match =	of_match_ptr("ldo1"),
+		.regulators_node = of_match_ptr("regulators"),
+		.n_voltages =	0x37,
+		.min_uV =	900000,
+		.uV_step =	50000,
+		.vsel_reg =	ISL9305_LDO1OUT,
+		.vsel_mask =	0x3f,
+		.enable_reg =	ISL9305_SYSTEM_PARAMETER,
+		.enable_mask =	ISL9305_LDO1_EN,
+		.supply_name =	"VINLDO1",
+		.ops =		&isl9305_ops,
+	},
+	[ISL9305_LDO2] = {
+		.name =		"LDO2",
+		.of_match =	of_match_ptr("ldo2"),
+		.regulators_node = of_match_ptr("regulators"),
+		.n_voltages =	0x37,
+		.min_uV =	900000,
+		.uV_step =	50000,
+		.vsel_reg =	ISL9305_LDO2OUT,
+		.vsel_mask =	0x3f,
+		.enable_reg =	ISL9305_SYSTEM_PARAMETER,
+		.enable_mask =	ISL9305_LDO2_EN,
+		.supply_name =	"VINLDO2",
+		.ops =		&isl9305_ops,
+	},
+};
+
+static const struct regmap_config isl9305_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = ISL9305_MAX_REG,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int isl9305_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct regulator_config config = { };
+	struct isl9305_pdata *pdata = i2c->dev.platform_data;
+	struct regulator_dev *rdev;
+	struct regmap *regmap;
+	int i, ret;
+
+	regmap = devm_regmap_init_i2c(i2c, &isl9305_regmap);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
+		return ret;
+	}
+
+	config.dev = &i2c->dev;
+
+	for (i = 0; i < ARRAY_SIZE(isl9305_regulators); i++) {
+		if (pdata)
+			config.init_data = pdata->init_data[i];
+		else
+			config.init_data = NULL;
+
+		rdev = devm_regulator_register(&i2c->dev,
+					       &isl9305_regulators[i],
+					       &config);
+		if (IS_ERR(rdev)) {
+			ret = PTR_ERR(rdev);
+			dev_err(&i2c->dev, "Failed to register %s: %d\n",
+				isl9305_regulators[i].name, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id isl9305_dt_ids[] = {
+	{ .compatible = "isl,isl9305" },
+	{ .compatible = "isl,isl9305h" },
+	{},
+};
+#endif
+
+static const struct i2c_device_id isl9305_i2c_id[] = {
+	{ "isl9305", },
+	{ "isl9305h", },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, isl9305_i2c_id);
+
+static struct i2c_driver isl9305_regulator_driver = {
+	.driver = {
+		.name = "isl9305",
+		.owner = THIS_MODULE,
+		.of_match_table	= of_match_ptr(isl9305_dt_ids),
+	},
+	.probe = isl9305_i2c_probe,
+	.id_table = isl9305_i2c_id,
+};
+
+module_i2c_driver(isl9305_regulator_driver);
+
+MODULE_AUTHOR("Mark Brown");
+MODULE_DESCRIPTION("Intersil ISL9305 DCDC regulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c
index f374fa5..793b662 100644
--- a/drivers/regulator/mc13892-regulator.c
+++ b/drivers/regulator/mc13892-regulator.c
@@ -526,6 +526,7 @@
 	return REGULATOR_MODE_NORMAL;
 }
 
+static struct regulator_ops mc13892_vcam_ops;
 
 static int mc13892_regulator_probe(struct platform_device *pdev)
 {
@@ -582,10 +583,12 @@
 	}
 	mc13xxx_unlock(mc13892);
 
-	mc13892_regulators[MC13892_VCAM].desc.ops->set_mode
-		= mc13892_vcam_set_mode;
-	mc13892_regulators[MC13892_VCAM].desc.ops->get_mode
-		= mc13892_vcam_get_mode;
+	/* update mc13892_vcam ops */
+	memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops,
+						sizeof(struct regulator_ops));
+	mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode,
+	mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode,
+	mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops;
 
 	mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators,
 					ARRAY_SIZE(mc13892_regulators));
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index ee5e67b..7a51814 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -14,8 +14,11 @@
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
 #include <linux/regulator/of_regulator.h>
 
+#include "internal.h"
+
 static void of_get_regulation_constraints(struct device_node *np,
 					struct regulator_init_data **init_data)
 {
@@ -189,3 +192,51 @@
 	return count;
 }
 EXPORT_SYMBOL_GPL(of_regulator_match);
+
+struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
+					    const struct regulator_desc *desc,
+					    struct device_node **node)
+{
+	struct device_node *search, *child;
+	struct regulator_init_data *init_data = NULL;
+	const char *name;
+
+	if (!dev->of_node || !desc->of_match)
+		return NULL;
+
+	if (desc->regulators_node)
+		search = of_get_child_by_name(dev->of_node,
+					      desc->regulators_node);
+	else
+		search = dev->of_node;
+
+	if (!search) {
+		dev_err(dev, "Failed to find regulator container node\n");
+		return NULL;
+	}
+
+	for_each_child_of_node(search, child) {
+		name = of_get_property(child, "regulator-compatible", NULL);
+		if (!name)
+			name = child->name;
+
+		if (strcmp(desc->of_match, name))
+			continue;
+
+		init_data = of_get_regulator_init_data(dev, child);
+		if (!init_data) {
+			dev_err(dev,
+				"failed to parse DT for regulator %s\n",
+				child->name);
+			break;
+		}
+
+		of_node_get(child);
+		*node = child;
+		break;
+	}
+
+	of_node_put(search);
+
+	return init_data;
+}
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
new file mode 100644
index 0000000..d3f55ea
--- /dev/null
+++ b/drivers/regulator/pwm-regulator.c
@@ -0,0 +1,197 @@
+/*
+ * Regulator driver for PWM Regulators
+ *
+ * Copyright (C) 2014 - STMicroelectronics Inc.
+ *
+ * Author: Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pwm.h>
+
+struct pwm_regulator_data {
+	struct regulator_desc desc;
+	struct pwm_voltages *duty_cycle_table;
+	struct pwm_device *pwm;
+	bool enabled;
+	int state;
+};
+
+struct pwm_voltages {
+	unsigned int uV;
+	unsigned int dutycycle;
+};
+
+static int pwm_regulator_get_voltage_sel(struct regulator_dev *dev)
+{
+	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+
+	return drvdata->state;
+}
+
+static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
+					 unsigned selector)
+{
+	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+	unsigned int pwm_reg_period;
+	int dutycycle;
+	int ret;
+
+	pwm_reg_period = pwm_get_period(drvdata->pwm);
+
+	dutycycle = (pwm_reg_period *
+		    drvdata->duty_cycle_table[selector].dutycycle) / 100;
+
+	ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period);
+	if (ret) {
+		dev_err(&dev->dev, "Failed to configure PWM\n");
+		return ret;
+	}
+
+	drvdata->state = selector;
+
+	if (!drvdata->enabled) {
+		ret = pwm_enable(drvdata->pwm);
+		if (ret) {
+			dev_err(&dev->dev, "Failed to enable PWM\n");
+			return ret;
+		}
+		drvdata->enabled = true;
+	}
+
+	return 0;
+}
+
+static int pwm_regulator_list_voltage(struct regulator_dev *dev,
+				      unsigned selector)
+{
+	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+
+	if (selector >= drvdata->desc.n_voltages)
+		return -EINVAL;
+
+	return drvdata->duty_cycle_table[selector].uV;
+}
+
+static struct regulator_ops pwm_regulator_voltage_ops = {
+	.set_voltage_sel = pwm_regulator_set_voltage_sel,
+	.get_voltage_sel = pwm_regulator_get_voltage_sel,
+	.list_voltage    = pwm_regulator_list_voltage,
+	.map_voltage     = regulator_map_voltage_iterate,
+};
+
+static const struct regulator_desc pwm_regulator_desc = {
+	.name		= "pwm-regulator",
+	.ops		= &pwm_regulator_voltage_ops,
+	.type		= REGULATOR_VOLTAGE,
+	.owner		= THIS_MODULE,
+	.supply_name    = "pwm",
+};
+
+static int pwm_regulator_probe(struct platform_device *pdev)
+{
+	struct pwm_regulator_data *drvdata;
+	struct property *prop;
+	struct regulator_dev *regulator;
+	struct regulator_config config = { };
+	struct device_node *np = pdev->dev.of_node;
+	int length, ret;
+
+	if (!np) {
+		dev_err(&pdev->dev, "Device Tree node missing\n");
+		return -EINVAL;
+	}
+
+	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(pwm_regulator_desc));
+
+	/* determine the number of voltage-table */
+	prop = of_find_property(np, "voltage-table", &length);
+	if (!prop) {
+		dev_err(&pdev->dev, "No voltage-table\n");
+		return -EINVAL;
+	}
+
+	if ((length < sizeof(*drvdata->duty_cycle_table)) ||
+	    (length % sizeof(*drvdata->duty_cycle_table))) {
+		dev_err(&pdev->dev, "voltage-table length(%d) is invalid\n",
+			length);
+		return -EINVAL;
+	}
+
+	drvdata->desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
+
+	drvdata->duty_cycle_table = devm_kzalloc(&pdev->dev,
+						 length, GFP_KERNEL);
+	if (!drvdata->duty_cycle_table)
+		return -ENOMEM;
+
+	/* read voltage table from DT property */
+	ret = of_property_read_u32_array(np, "voltage-table",
+					 (u32 *)drvdata->duty_cycle_table,
+					 length / sizeof(u32));
+	if (ret < 0) {
+		dev_err(&pdev->dev, "read voltage-table failed\n");
+		return ret;
+	}
+
+	config.init_data = of_get_regulator_init_data(&pdev->dev, np);
+	if (!config.init_data)
+		return -ENOMEM;
+
+	config.of_node = np;
+	config.dev = &pdev->dev;
+	config.driver_data = drvdata;
+
+	drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(drvdata->pwm)) {
+		dev_err(&pdev->dev, "Failed to get PWM\n");
+		return PTR_ERR(drvdata->pwm);
+	}
+
+	regulator = devm_regulator_register(&pdev->dev,
+					    &drvdata->desc, &config);
+	if (IS_ERR(regulator)) {
+		dev_err(&pdev->dev, "Failed to register regulator %s\n",
+			drvdata->desc.name);
+		return PTR_ERR(regulator);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id pwm_of_match[] = {
+	{ .compatible = "pwm-regulator" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pwm_of_match);
+
+static struct platform_driver pwm_regulator_driver = {
+	.driver = {
+		.name		= "pwm-regulator",
+		.owner		= THIS_MODULE,
+		.of_match_table = of_match_ptr(pwm_of_match),
+	},
+	.probe = pwm_regulator_probe,
+};
+
+module_platform_driver(pwm_regulator_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
+MODULE_DESCRIPTION("PWM Regulator Driver");
+MODULE_ALIAS("platform:pwm-regulator");
diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c
new file mode 100644
index 0000000..b55cd5b
--- /dev/null
+++ b/drivers/regulator/qcom_rpm-regulator.c
@@ -0,0 +1,798 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ * Copyright (c) 2012-2013, 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/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/qcom_rpm.h>
+
+#include <dt-bindings/mfd/qcom-rpm.h>
+
+#define MAX_REQUEST_LEN 2
+
+struct request_member {
+	int		word;
+	unsigned int	mask;
+	int		shift;
+};
+
+struct rpm_reg_parts {
+	struct request_member mV;		/* used if voltage is in mV */
+	struct request_member uV;		/* used if voltage is in uV */
+	struct request_member ip;		/* peak current in mA */
+	struct request_member pd;		/* pull down enable */
+	struct request_member ia;		/* average current in mA */
+	struct request_member fm;		/* force mode */
+	struct request_member pm;		/* power mode */
+	struct request_member pc;		/* pin control */
+	struct request_member pf;		/* pin function */
+	struct request_member enable_state;	/* NCP and switch */
+	struct request_member comp_mode;	/* NCP */
+	struct request_member freq;		/* frequency: NCP and SMPS */
+	struct request_member freq_clk_src;	/* clock source: SMPS */
+	struct request_member hpm;		/* switch: control OCP and SS */
+	int request_len;
+};
+
+#define FORCE_MODE_IS_2_BITS(reg) \
+	(((reg)->parts->fm.mask >> (reg)->parts->fm.shift) == 3)
+
+struct qcom_rpm_reg {
+	struct qcom_rpm *rpm;
+
+	struct mutex lock;
+	struct device *dev;
+	struct regulator_desc desc;
+	const struct rpm_reg_parts *parts;
+
+	int resource;
+	u32 val[MAX_REQUEST_LEN];
+
+	int uV;
+	int is_enabled;
+
+	bool supports_force_mode_auto;
+	bool supports_force_mode_bypass;
+};
+
+static const struct rpm_reg_parts rpm8660_ldo_parts = {
+	.request_len    = 2,
+	.mV             = { 0, 0x00000FFF,  0 },
+	.ip             = { 0, 0x00FFF000, 12 },
+	.fm             = { 0, 0x03000000, 24 },
+	.pc             = { 0, 0x3C000000, 26 },
+	.pf             = { 0, 0xC0000000, 30 },
+	.pd             = { 1, 0x00000001,  0 },
+	.ia             = { 1, 0x00001FFE,  1 },
+};
+
+static const struct rpm_reg_parts rpm8660_smps_parts = {
+	.request_len    = 2,
+	.mV             = { 0, 0x00000FFF,  0 },
+	.ip             = { 0, 0x00FFF000, 12 },
+	.fm             = { 0, 0x03000000, 24 },
+	.pc             = { 0, 0x3C000000, 26 },
+	.pf             = { 0, 0xC0000000, 30 },
+	.pd             = { 1, 0x00000001,  0 },
+	.ia             = { 1, 0x00001FFE,  1 },
+	.freq           = { 1, 0x001FE000, 13 },
+	.freq_clk_src   = { 1, 0x00600000, 21 },
+};
+
+static const struct rpm_reg_parts rpm8660_switch_parts = {
+	.request_len    = 1,
+	.enable_state   = { 0, 0x00000001,  0 },
+	.pd             = { 0, 0x00000002,  1 },
+	.pc             = { 0, 0x0000003C,  2 },
+	.pf             = { 0, 0x000000C0,  6 },
+	.hpm            = { 0, 0x00000300,  8 },
+};
+
+static const struct rpm_reg_parts rpm8660_ncp_parts = {
+	.request_len    = 1,
+	.mV             = { 0, 0x00000FFF,  0 },
+	.enable_state   = { 0, 0x00001000, 12 },
+	.comp_mode      = { 0, 0x00002000, 13 },
+	.freq           = { 0, 0x003FC000, 14 },
+};
+
+static const struct rpm_reg_parts rpm8960_ldo_parts = {
+	.request_len    = 2,
+	.uV             = { 0, 0x007FFFFF,  0 },
+	.pd             = { 0, 0x00800000, 23 },
+	.pc             = { 0, 0x0F000000, 24 },
+	.pf             = { 0, 0xF0000000, 28 },
+	.ip             = { 1, 0x000003FF,  0 },
+	.ia             = { 1, 0x000FFC00, 10 },
+	.fm             = { 1, 0x00700000, 20 },
+};
+
+static const struct rpm_reg_parts rpm8960_smps_parts = {
+	.request_len    = 2,
+	.uV             = { 0, 0x007FFFFF,  0 },
+	.pd             = { 0, 0x00800000, 23 },
+	.pc             = { 0, 0x0F000000, 24 },
+	.pf             = { 0, 0xF0000000, 28 },
+	.ip             = { 1, 0x000003FF,  0 },
+	.ia             = { 1, 0x000FFC00, 10 },
+	.fm             = { 1, 0x00700000, 20 },
+	.pm             = { 1, 0x00800000, 23 },
+	.freq           = { 1, 0x1F000000, 24 },
+	.freq_clk_src   = { 1, 0x60000000, 29 },
+};
+
+static const struct rpm_reg_parts rpm8960_switch_parts = {
+	.request_len    = 1,
+	.enable_state   = { 0, 0x00000001,  0 },
+	.pd             = { 0, 0x00000002,  1 },
+	.pc             = { 0, 0x0000003C,  2 },
+	.pf             = { 0, 0x000003C0,  6 },
+	.hpm            = { 0, 0x00000C00, 10 },
+};
+
+static const struct rpm_reg_parts rpm8960_ncp_parts = {
+	.request_len    = 1,
+	.uV             = { 0, 0x007FFFFF,  0 },
+	.enable_state   = { 0, 0x00800000, 23 },
+	.comp_mode      = { 0, 0x01000000, 24 },
+	.freq           = { 0, 0x3E000000, 25 },
+};
+
+/*
+ * Physically available PMIC regulator voltage ranges
+ */
+static const struct regulator_linear_range pldo_ranges[] = {
+	REGULATOR_LINEAR_RANGE( 750000,   0,  59, 12500),
+	REGULATOR_LINEAR_RANGE(1500000,  60, 123, 25000),
+	REGULATOR_LINEAR_RANGE(3100000, 124, 160, 50000),
+};
+
+static const struct regulator_linear_range nldo_ranges[] = {
+	REGULATOR_LINEAR_RANGE( 750000,   0,  63, 12500),
+};
+
+static const struct regulator_linear_range nldo1200_ranges[] = {
+	REGULATOR_LINEAR_RANGE( 375000,   0,  59,  6250),
+	REGULATOR_LINEAR_RANGE( 750000,  60, 123, 12500),
+};
+
+static const struct regulator_linear_range smps_ranges[] = {
+	REGULATOR_LINEAR_RANGE( 375000,   0,  29, 12500),
+	REGULATOR_LINEAR_RANGE( 750000,  30,  89, 12500),
+	REGULATOR_LINEAR_RANGE(1500000,  90, 153, 25000),
+};
+
+static const struct regulator_linear_range ftsmps_ranges[] = {
+	REGULATOR_LINEAR_RANGE( 350000,   0,   6, 50000),
+	REGULATOR_LINEAR_RANGE( 700000,   7,  63, 12500),
+	REGULATOR_LINEAR_RANGE(1500000,  64, 100, 50000),
+};
+
+static const struct regulator_linear_range ncp_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1500000,   0,  31, 50000),
+};
+
+static int rpm_reg_write(struct qcom_rpm_reg *vreg,
+			 const struct request_member *req,
+			 const int value)
+{
+	if (WARN_ON((value << req->shift) & ~req->mask))
+		return -EINVAL;
+
+	vreg->val[req->word] &= ~req->mask;
+	vreg->val[req->word] |= value << req->shift;
+
+	return qcom_rpm_write(vreg->rpm,
+			      vreg->resource,
+			      vreg->val,
+			      vreg->parts->request_len);
+}
+
+static int rpm_reg_set_mV_sel(struct regulator_dev *rdev,
+			      unsigned selector)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+	const struct rpm_reg_parts *parts = vreg->parts;
+	const struct request_member *req = &parts->mV;
+	int ret = 0;
+	int uV;
+
+	if (req->mask == 0)
+		return -EINVAL;
+
+	uV = regulator_list_voltage_linear_range(rdev, selector);
+	if (uV < 0)
+		return uV;
+
+	mutex_lock(&vreg->lock);
+	vreg->uV = uV;
+	if (vreg->is_enabled)
+		ret = rpm_reg_write(vreg, req, vreg->uV / 1000);
+	mutex_unlock(&vreg->lock);
+
+	return ret;
+}
+
+static int rpm_reg_set_uV_sel(struct regulator_dev *rdev,
+			      unsigned selector)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+	const struct rpm_reg_parts *parts = vreg->parts;
+	const struct request_member *req = &parts->uV;
+	int ret = 0;
+	int uV;
+
+	if (req->mask == 0)
+		return -EINVAL;
+
+	uV = regulator_list_voltage_linear_range(rdev, selector);
+	if (uV < 0)
+		return uV;
+
+	mutex_lock(&vreg->lock);
+	vreg->uV = uV;
+	if (vreg->is_enabled)
+		ret = rpm_reg_write(vreg, req, vreg->uV);
+	mutex_unlock(&vreg->lock);
+
+	return ret;
+}
+
+static int rpm_reg_get_voltage(struct regulator_dev *rdev)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->uV;
+}
+
+static int rpm_reg_mV_enable(struct regulator_dev *rdev)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+	const struct rpm_reg_parts *parts = vreg->parts;
+	const struct request_member *req = &parts->mV;
+	int ret;
+
+	if (req->mask == 0)
+		return -EINVAL;
+
+	mutex_lock(&vreg->lock);
+	ret = rpm_reg_write(vreg, req, vreg->uV / 1000);
+	if (!ret)
+		vreg->is_enabled = 1;
+	mutex_unlock(&vreg->lock);
+
+	return ret;
+}
+
+static int rpm_reg_uV_enable(struct regulator_dev *rdev)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+	const struct rpm_reg_parts *parts = vreg->parts;
+	const struct request_member *req = &parts->uV;
+	int ret;
+
+	if (req->mask == 0)
+		return -EINVAL;
+
+	mutex_lock(&vreg->lock);
+	ret = rpm_reg_write(vreg, req, vreg->uV);
+	if (!ret)
+		vreg->is_enabled = 1;
+	mutex_unlock(&vreg->lock);
+
+	return ret;
+}
+
+static int rpm_reg_switch_enable(struct regulator_dev *rdev)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+	const struct rpm_reg_parts *parts = vreg->parts;
+	const struct request_member *req = &parts->enable_state;
+	int ret;
+
+	if (req->mask == 0)
+		return -EINVAL;
+
+	mutex_lock(&vreg->lock);
+	ret = rpm_reg_write(vreg, req, 1);
+	if (!ret)
+		vreg->is_enabled = 1;
+	mutex_unlock(&vreg->lock);
+
+	return ret;
+}
+
+static int rpm_reg_mV_disable(struct regulator_dev *rdev)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+	const struct rpm_reg_parts *parts = vreg->parts;
+	const struct request_member *req = &parts->mV;
+	int ret;
+
+	if (req->mask == 0)
+		return -EINVAL;
+
+	mutex_lock(&vreg->lock);
+	ret = rpm_reg_write(vreg, req, 0);
+	if (!ret)
+		vreg->is_enabled = 0;
+	mutex_unlock(&vreg->lock);
+
+	return ret;
+}
+
+static int rpm_reg_uV_disable(struct regulator_dev *rdev)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+	const struct rpm_reg_parts *parts = vreg->parts;
+	const struct request_member *req = &parts->uV;
+	int ret;
+
+	if (req->mask == 0)
+		return -EINVAL;
+
+	mutex_lock(&vreg->lock);
+	ret = rpm_reg_write(vreg, req, 0);
+	if (!ret)
+		vreg->is_enabled = 0;
+	mutex_unlock(&vreg->lock);
+
+	return ret;
+}
+
+static int rpm_reg_switch_disable(struct regulator_dev *rdev)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+	const struct rpm_reg_parts *parts = vreg->parts;
+	const struct request_member *req = &parts->enable_state;
+	int ret;
+
+	if (req->mask == 0)
+		return -EINVAL;
+
+	mutex_lock(&vreg->lock);
+	ret = rpm_reg_write(vreg, req, 0);
+	if (!ret)
+		vreg->is_enabled = 0;
+	mutex_unlock(&vreg->lock);
+
+	return ret;
+}
+
+static int rpm_reg_is_enabled(struct regulator_dev *rdev)
+{
+	struct qcom_rpm_reg *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->is_enabled;
+}
+
+static struct regulator_ops uV_ops = {
+	.list_voltage = regulator_list_voltage_linear_range,
+
+	.set_voltage_sel = rpm_reg_set_uV_sel,
+	.get_voltage = rpm_reg_get_voltage,
+
+	.enable = rpm_reg_uV_enable,
+	.disable = rpm_reg_uV_disable,
+	.is_enabled = rpm_reg_is_enabled,
+};
+
+static struct regulator_ops mV_ops = {
+	.list_voltage = regulator_list_voltage_linear_range,
+
+	.set_voltage_sel = rpm_reg_set_mV_sel,
+	.get_voltage = rpm_reg_get_voltage,
+
+	.enable = rpm_reg_mV_enable,
+	.disable = rpm_reg_mV_disable,
+	.is_enabled = rpm_reg_is_enabled,
+};
+
+static struct regulator_ops switch_ops = {
+	.enable = rpm_reg_switch_enable,
+	.disable = rpm_reg_switch_disable,
+	.is_enabled = rpm_reg_is_enabled,
+};
+
+/*
+ * PM8058 regulators
+ */
+static const struct qcom_rpm_reg pm8058_pldo = {
+	.desc.linear_ranges = pldo_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+	.desc.n_voltages = 161,
+	.desc.ops = &mV_ops,
+	.parts = &rpm8660_ldo_parts,
+	.supports_force_mode_auto = false,
+	.supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8058_nldo = {
+	.desc.linear_ranges = nldo_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+	.desc.n_voltages = 64,
+	.desc.ops = &mV_ops,
+	.parts = &rpm8660_ldo_parts,
+	.supports_force_mode_auto = false,
+	.supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8058_smps = {
+	.desc.linear_ranges = smps_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
+	.desc.n_voltages = 154,
+	.desc.ops = &mV_ops,
+	.parts = &rpm8660_smps_parts,
+	.supports_force_mode_auto = false,
+	.supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8058_ncp = {
+	.desc.linear_ranges = ncp_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
+	.desc.n_voltages = 32,
+	.desc.ops = &mV_ops,
+	.parts = &rpm8660_ncp_parts,
+};
+
+static const struct qcom_rpm_reg pm8058_switch = {
+	.desc.ops = &switch_ops,
+	.parts = &rpm8660_switch_parts,
+};
+
+/*
+ * PM8901 regulators
+ */
+static const struct qcom_rpm_reg pm8901_pldo = {
+	.desc.linear_ranges = pldo_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+	.desc.n_voltages = 161,
+	.desc.ops = &mV_ops,
+	.parts = &rpm8660_ldo_parts,
+	.supports_force_mode_auto = false,
+	.supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8901_nldo = {
+	.desc.linear_ranges = nldo_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+	.desc.n_voltages = 64,
+	.desc.ops = &mV_ops,
+	.parts = &rpm8660_ldo_parts,
+	.supports_force_mode_auto = false,
+	.supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8901_ftsmps = {
+	.desc.linear_ranges = ftsmps_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
+	.desc.n_voltages = 101,
+	.desc.ops = &mV_ops,
+	.parts = &rpm8660_smps_parts,
+	.supports_force_mode_auto = true,
+	.supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8901_switch = {
+	.desc.ops = &switch_ops,
+	.parts = &rpm8660_switch_parts,
+};
+
+/*
+ * PM8921 regulators
+ */
+static const struct qcom_rpm_reg pm8921_pldo = {
+	.desc.linear_ranges = pldo_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(pldo_ranges),
+	.desc.n_voltages = 161,
+	.desc.ops = &uV_ops,
+	.parts = &rpm8960_ldo_parts,
+	.supports_force_mode_auto = false,
+	.supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8921_nldo = {
+	.desc.linear_ranges = nldo_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(nldo_ranges),
+	.desc.n_voltages = 64,
+	.desc.ops = &uV_ops,
+	.parts = &rpm8960_ldo_parts,
+	.supports_force_mode_auto = false,
+	.supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8921_nldo1200 = {
+	.desc.linear_ranges = nldo1200_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(nldo1200_ranges),
+	.desc.n_voltages = 124,
+	.desc.ops = &uV_ops,
+	.parts = &rpm8960_ldo_parts,
+	.supports_force_mode_auto = false,
+	.supports_force_mode_bypass = true,
+};
+
+static const struct qcom_rpm_reg pm8921_smps = {
+	.desc.linear_ranges = smps_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(smps_ranges),
+	.desc.n_voltages = 154,
+	.desc.ops = &uV_ops,
+	.parts = &rpm8960_smps_parts,
+	.supports_force_mode_auto = true,
+	.supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8921_ftsmps = {
+	.desc.linear_ranges = ftsmps_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(ftsmps_ranges),
+	.desc.n_voltages = 101,
+	.desc.ops = &uV_ops,
+	.parts = &rpm8960_smps_parts,
+	.supports_force_mode_auto = true,
+	.supports_force_mode_bypass = false,
+};
+
+static const struct qcom_rpm_reg pm8921_ncp = {
+	.desc.linear_ranges = ncp_ranges,
+	.desc.n_linear_ranges = ARRAY_SIZE(ncp_ranges),
+	.desc.n_voltages = 32,
+	.desc.ops = &uV_ops,
+	.parts = &rpm8960_ncp_parts,
+};
+
+static const struct qcom_rpm_reg pm8921_switch = {
+	.desc.ops = &switch_ops,
+	.parts = &rpm8960_switch_parts,
+};
+
+static const struct of_device_id rpm_of_match[] = {
+	{ .compatible = "qcom,rpm-pm8058-pldo",     .data = &pm8058_pldo },
+	{ .compatible = "qcom,rpm-pm8058-nldo",     .data = &pm8058_nldo },
+	{ .compatible = "qcom,rpm-pm8058-smps",     .data = &pm8058_smps },
+	{ .compatible = "qcom,rpm-pm8058-ncp",      .data = &pm8058_ncp },
+	{ .compatible = "qcom,rpm-pm8058-switch",   .data = &pm8058_switch },
+
+	{ .compatible = "qcom,rpm-pm8901-pldo",     .data = &pm8901_pldo },
+	{ .compatible = "qcom,rpm-pm8901-nldo",     .data = &pm8901_nldo },
+	{ .compatible = "qcom,rpm-pm8901-ftsmps",   .data = &pm8901_ftsmps },
+	{ .compatible = "qcom,rpm-pm8901-switch",   .data = &pm8901_switch },
+
+	{ .compatible = "qcom,rpm-pm8921-pldo",     .data = &pm8921_pldo },
+	{ .compatible = "qcom,rpm-pm8921-nldo",     .data = &pm8921_nldo },
+	{ .compatible = "qcom,rpm-pm8921-nldo1200", .data = &pm8921_nldo1200 },
+	{ .compatible = "qcom,rpm-pm8921-smps",     .data = &pm8921_smps },
+	{ .compatible = "qcom,rpm-pm8921-ftsmps",   .data = &pm8921_ftsmps },
+	{ .compatible = "qcom,rpm-pm8921-ncp",      .data = &pm8921_ncp },
+	{ .compatible = "qcom,rpm-pm8921-switch",   .data = &pm8921_switch },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rpm_of_match);
+
+static int rpm_reg_set(struct qcom_rpm_reg *vreg,
+		       const struct request_member *req,
+		       const int value)
+{
+	if (req->mask == 0 || (value << req->shift) & ~req->mask)
+		return -EINVAL;
+
+	vreg->val[req->word] &= ~req->mask;
+	vreg->val[req->word] |= value << req->shift;
+
+	return 0;
+}
+
+static int rpm_reg_of_parse_freq(struct device *dev, struct qcom_rpm_reg *vreg)
+{
+	static const int freq_table[] = {
+		19200000, 9600000, 6400000, 4800000, 3840000, 3200000, 2740000,
+		2400000, 2130000, 1920000, 1750000, 1600000, 1480000, 1370000,
+		1280000, 1200000,
+
+	};
+	const char *key;
+	u32 freq;
+	int ret;
+	int i;
+
+	key = "qcom,switch-mode-frequency";
+	ret = of_property_read_u32(dev->of_node, key, &freq);
+	if (ret) {
+		dev_err(dev, "regulator requires %s property\n", key);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+		if (freq == freq_table[i]) {
+			rpm_reg_set(vreg, &vreg->parts->freq, i + 1);
+			return 0;
+		}
+	}
+
+	dev_err(dev, "invalid frequency %d\n", freq);
+	return -EINVAL;
+}
+
+static int rpm_reg_probe(struct platform_device *pdev)
+{
+	struct regulator_init_data *initdata;
+	const struct qcom_rpm_reg *template;
+	const struct of_device_id *match;
+	struct regulator_config config = { };
+	struct regulator_dev *rdev;
+	struct qcom_rpm_reg *vreg;
+	const char *key;
+	u32 force_mode;
+	bool pwm;
+	u32 val;
+	int ret;
+
+	match = of_match_device(rpm_of_match, &pdev->dev);
+	template = match->data;
+
+	initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
+	if (!initdata)
+		return -EINVAL;
+
+	vreg = devm_kmalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
+	if (!vreg) {
+		dev_err(&pdev->dev, "failed to allocate vreg\n");
+		return -ENOMEM;
+	}
+	memcpy(vreg, template, sizeof(*vreg));
+	mutex_init(&vreg->lock);
+	vreg->dev = &pdev->dev;
+	vreg->desc.id = -1;
+	vreg->desc.owner = THIS_MODULE;
+	vreg->desc.type = REGULATOR_VOLTAGE;
+	vreg->desc.name = pdev->dev.of_node->name;
+
+	vreg->rpm = dev_get_drvdata(pdev->dev.parent);
+	if (!vreg->rpm) {
+		dev_err(&pdev->dev, "unable to retrieve handle to rpm\n");
+		return -ENODEV;
+	}
+
+	key = "reg";
+	ret = of_property_read_u32(pdev->dev.of_node, key, &val);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to read %s\n", key);
+		return ret;
+	}
+	vreg->resource = val;
+
+	if ((vreg->parts->uV.mask || vreg->parts->mV.mask) &&
+	    (!initdata->constraints.min_uV || !initdata->constraints.max_uV)) {
+		dev_err(&pdev->dev, "no voltage specified for regulator\n");
+		return -EINVAL;
+	}
+
+	key = "bias-pull-down";
+	if (of_property_read_bool(pdev->dev.of_node, key)) {
+		ret = rpm_reg_set(vreg, &vreg->parts->pd, 1);
+		if (ret) {
+			dev_err(&pdev->dev, "%s is invalid", key);
+			return ret;
+		}
+	}
+
+	if (vreg->parts->freq.mask) {
+		ret = rpm_reg_of_parse_freq(&pdev->dev, vreg);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (vreg->parts->pm.mask) {
+		key = "qcom,power-mode-hysteretic";
+		pwm = !of_property_read_bool(pdev->dev.of_node, key);
+
+		ret = rpm_reg_set(vreg, &vreg->parts->pm, pwm);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to set power mode\n");
+			return ret;
+		}
+	}
+
+	if (vreg->parts->fm.mask) {
+		force_mode = -1;
+
+		key = "qcom,force-mode";
+		ret = of_property_read_u32(pdev->dev.of_node, key, &val);
+		if (ret == -EINVAL) {
+			val = QCOM_RPM_FORCE_MODE_NONE;
+		} else if (ret < 0) {
+			dev_err(&pdev->dev, "failed to read %s\n", key);
+			return ret;
+		}
+
+		/*
+		 * If force-mode is encoded as 2 bits then the
+		 * possible register values are:
+		 * NONE, LPM, HPM
+		 * otherwise:
+		 * NONE, LPM, AUTO, HPM, BYPASS
+		 */
+		switch (val) {
+		case QCOM_RPM_FORCE_MODE_NONE:
+			force_mode = 0;
+			break;
+		case QCOM_RPM_FORCE_MODE_LPM:
+			force_mode = 1;
+			break;
+		case QCOM_RPM_FORCE_MODE_HPM:
+			if (FORCE_MODE_IS_2_BITS(vreg))
+				force_mode = 2;
+			else
+				force_mode = 3;
+			break;
+		case QCOM_RPM_FORCE_MODE_AUTO:
+			if (vreg->supports_force_mode_auto)
+				force_mode = 2;
+			break;
+		case QCOM_RPM_FORCE_MODE_BYPASS:
+			if (vreg->supports_force_mode_bypass)
+				force_mode = 4;
+			break;
+		}
+
+		if (force_mode < 0) {
+			dev_err(&pdev->dev, "invalid force mode\n");
+			return -EINVAL;
+		}
+
+		ret = rpm_reg_set(vreg, &vreg->parts->fm, force_mode);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to set force mode\n");
+			return ret;
+		}
+	}
+
+	config.dev = &pdev->dev;
+	config.init_data = initdata;
+	config.driver_data = vreg;
+	config.of_node = pdev->dev.of_node;
+	rdev = devm_regulator_register(&pdev->dev, &vreg->desc, &config);
+	if (IS_ERR(rdev)) {
+		dev_err(&pdev->dev, "can't register regulator\n");
+		return PTR_ERR(rdev);
+	}
+
+	return 0;
+}
+
+static struct platform_driver rpm_reg_driver = {
+	.probe          = rpm_reg_probe,
+	.driver  = {
+		.name  = "qcom_rpm_reg",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(rpm_of_match),
+	},
+};
+
+static int __init rpm_reg_init(void)
+{
+	return platform_driver_register(&rpm_reg_driver);
+}
+subsys_initcall(rpm_reg_init);
+
+static void __exit rpm_reg_exit(void)
+{
+	platform_driver_unregister(&rpm_reg_driver);
+}
+module_exit(rpm_reg_exit)
+
+MODULE_DESCRIPTION("Qualcomm RPM regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/sky81452-regulator.c b/drivers/regulator/sky81452-regulator.c
new file mode 100644
index 0000000..97aff0c
--- /dev/null
+++ b/drivers/regulator/sky81452-regulator.c
@@ -0,0 +1,130 @@
+/*
+ * sky81452-regulator.c	SKY81452 regulator driver
+ *
+ * Copyright 2014 Skyworks Solutions Inc.
+ * Author : Gyungoh Yoo <jack.yoo@skyworksinc.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+/* registers */
+#define SKY81452_REG1	0x01
+#define SKY81452_REG3	0x03
+
+/* bit mask */
+#define SKY81452_LEN	0x40
+#define SKY81452_LOUT	0x1F
+
+static struct regulator_ops sky81452_reg_ops = {
+	.list_voltage = regulator_list_voltage_linear_range,
+	.map_voltage = regulator_map_voltage_linear_range,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.enable = regulator_enable_regmap,
+	.disable = regulator_disable_regmap,
+	.is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_linear_range sky81452_reg_ranges[] = {
+	REGULATOR_LINEAR_RANGE(4500000, 0, 14, 250000),
+	REGULATOR_LINEAR_RANGE(9000000, 15, 31, 1000000),
+};
+
+static const struct regulator_desc sky81452_reg = {
+	.name = "LOUT",
+	.ops = &sky81452_reg_ops,
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+	.n_voltages = SKY81452_LOUT + 1,
+	.linear_ranges = sky81452_reg_ranges,
+	.n_linear_ranges = ARRAY_SIZE(sky81452_reg_ranges),
+	.vsel_reg = SKY81452_REG3,
+	.vsel_mask = SKY81452_LOUT,
+	.enable_reg = SKY81452_REG1,
+	.enable_mask = SKY81452_LEN,
+};
+
+#ifdef CONFIG_OF
+static struct regulator_init_data *sky81452_reg_parse_dt(struct device *dev)
+{
+	struct regulator_init_data *init_data;
+	struct device_node *np;
+
+	np = of_get_child_by_name(dev->parent->of_node, "regulator");
+	if (unlikely(!np)) {
+		dev_err(dev, "regulator node not found");
+		return NULL;
+	}
+
+	init_data = of_get_regulator_init_data(dev, np);
+
+	of_node_put(np);
+	return init_data;
+}
+#else
+static struct regulator_init_data *sky81452_reg_parse_dt(struct device *dev)
+{
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
+static int sky81452_reg_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct regulator_init_data *init_data = dev_get_platdata(dev);
+	struct regulator_config config = { };
+	struct regulator_dev *rdev;
+
+	if (!init_data) {
+		init_data = sky81452_reg_parse_dt(dev);
+		if (IS_ERR(init_data))
+			return PTR_ERR(init_data);
+	}
+
+	config.dev = dev;
+	config.init_data = init_data;
+	config.of_node = dev->of_node;
+	config.regmap = dev_get_drvdata(dev->parent);
+
+	rdev = devm_regulator_register(dev, &sky81452_reg, &config);
+	if (IS_ERR(rdev))
+		return PTR_ERR(rdev);
+
+	platform_set_drvdata(pdev, rdev);
+
+	return 0;
+}
+
+static struct platform_driver sky81452_reg_driver = {
+	.driver = {
+		.name = "sky81452-regulator",
+	},
+	.probe = sky81452_reg_probe,
+};
+
+module_platform_driver(sky81452_reg_driver);
+
+MODULE_DESCRIPTION("Skyworks SKY81452 Regulator driver");
+MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
diff --git a/drivers/regulator/st-pwm.c b/drivers/regulator/st-pwm.c
deleted file mode 100644
index 5ea78df..0000000
--- a/drivers/regulator/st-pwm.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Regulator driver for ST's PWM Regulators
- *
- * Copyright (C) 2014 - STMicroelectronics Inc.
- *
- * Author: Lee Jones <lee.jones@linaro.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/regulator/driver.h>
-#include <linux/regulator/machine.h>
-#include <linux/regulator/of_regulator.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/pwm.h>
-
-#define ST_PWM_REG_PERIOD 8448
-
-struct st_pwm_regulator_pdata {
-	const struct regulator_desc *desc;
-	struct st_pwm_voltages *duty_cycle_table;
-};
-
-struct st_pwm_regulator_data {
-	const struct st_pwm_regulator_pdata *pdata;
-	struct pwm_device *pwm;
-	bool enabled;
-	int state;
-};
-
-struct st_pwm_voltages {
-	unsigned int uV;
-	unsigned int dutycycle;
-};
-
-static int st_pwm_regulator_get_voltage_sel(struct regulator_dev *dev)
-{
-	struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
-
-	return drvdata->state;
-}
-
-static int st_pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
-					    unsigned selector)
-{
-	struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
-	int dutycycle;
-	int ret;
-
-	dutycycle = (ST_PWM_REG_PERIOD / 100) *
-		drvdata->pdata->duty_cycle_table[selector].dutycycle;
-
-	ret = pwm_config(drvdata->pwm, dutycycle, ST_PWM_REG_PERIOD);
-	if (ret) {
-		dev_err(&dev->dev, "Failed to configure PWM\n");
-		return ret;
-	}
-
-	drvdata->state = selector;
-
-	if (!drvdata->enabled) {
-		ret = pwm_enable(drvdata->pwm);
-		if (ret) {
-			dev_err(&dev->dev, "Failed to enable PWM\n");
-			return ret;
-		}
-		drvdata->enabled = true;
-	}
-
-	return 0;
-}
-
-static int st_pwm_regulator_list_voltage(struct regulator_dev *dev,
-					 unsigned selector)
-{
-	struct st_pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
-
-	if (selector >= dev->desc->n_voltages)
-		return -EINVAL;
-
-	return drvdata->pdata->duty_cycle_table[selector].uV;
-}
-
-static struct regulator_ops st_pwm_regulator_voltage_ops = {
-	.set_voltage_sel = st_pwm_regulator_set_voltage_sel,
-	.get_voltage_sel = st_pwm_regulator_get_voltage_sel,
-	.list_voltage    = st_pwm_regulator_list_voltage,
-	.map_voltage     = regulator_map_voltage_iterate,
-};
-
-static struct st_pwm_voltages b2105_duty_cycle_table[] = {
-	{ .uV = 1114000, .dutycycle = 0,  },
-	{ .uV = 1095000, .dutycycle = 10, },
-	{ .uV = 1076000, .dutycycle = 20, },
-	{ .uV = 1056000, .dutycycle = 30, },
-	{ .uV = 1036000, .dutycycle = 40, },
-	{ .uV = 1016000, .dutycycle = 50, },
-	/* WARNING: Values above 50% duty-cycle cause boot failures. */
-};
-
-static const struct regulator_desc b2105_desc = {
-	.name		= "b2105-pwm-regulator",
-	.ops		= &st_pwm_regulator_voltage_ops,
-	.type		= REGULATOR_VOLTAGE,
-	.owner		= THIS_MODULE,
-	.n_voltages	= ARRAY_SIZE(b2105_duty_cycle_table),
-	.supply_name    = "pwm",
-};
-
-static const struct st_pwm_regulator_pdata b2105_info = {
-	.desc		  = &b2105_desc,
-	.duty_cycle_table = b2105_duty_cycle_table,
-};
-
-static const struct of_device_id st_pwm_of_match[] = {
-	{ .compatible = "st,b2105-pwm-regulator", .data = &b2105_info, },
-	{ },
-};
-MODULE_DEVICE_TABLE(of, st_pwm_of_match);
-
-static int st_pwm_regulator_probe(struct platform_device *pdev)
-{
-	struct st_pwm_regulator_data *drvdata;
-	struct regulator_dev *regulator;
-	struct regulator_config config = { };
-	struct device_node *np = pdev->dev.of_node;
-	const struct of_device_id *of_match;
-
-	if (!np) {
-		dev_err(&pdev->dev, "Device Tree node missing\n");
-		return -EINVAL;
-	}
-
-	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
-	if (!drvdata)
-		return -ENOMEM;
-
-	of_match = of_match_device(st_pwm_of_match, &pdev->dev);
-	if (!of_match) {
-		dev_err(&pdev->dev, "failed to match of device\n");
-		return -ENODEV;
-	}
-	drvdata->pdata = of_match->data;
-
-	config.init_data = of_get_regulator_init_data(&pdev->dev, np);
-	if (!config.init_data)
-		return -ENOMEM;
-
-	config.of_node = np;
-	config.dev = &pdev->dev;
-	config.driver_data = drvdata;
-
-	drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
-	if (IS_ERR(drvdata->pwm)) {
-		dev_err(&pdev->dev, "Failed to get PWM\n");
-		return PTR_ERR(drvdata->pwm);
-	}
-
-	regulator = devm_regulator_register(&pdev->dev,
-					    drvdata->pdata->desc, &config);
-	if (IS_ERR(regulator)) {
-		dev_err(&pdev->dev, "Failed to register regulator %s\n",
-			drvdata->pdata->desc->name);
-		return PTR_ERR(regulator);
-	}
-
-	return 0;
-}
-
-static struct platform_driver st_pwm_regulator_driver = {
-	.driver = {
-		.name		= "st-pwm-regulator",
-		.owner		= THIS_MODULE,
-		.of_match_table = of_match_ptr(st_pwm_of_match),
-	},
-	.probe = st_pwm_regulator_probe,
-};
-
-module_platform_driver(st_pwm_regulator_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
-MODULE_DESCRIPTION("ST PWM Regulator Driver");
-MODULE_ALIAS("platform:st_pwm-regulator");
diff --git a/include/linux/platform_data/isl9305.h b/include/linux/platform_data/isl9305.h
new file mode 100644
index 0000000..1419133
--- /dev/null
+++ b/include/linux/platform_data/isl9305.h
@@ -0,0 +1,30 @@
+/*
+ * isl9305 - Intersil ISL9305 DCDC regulator
+ *
+ * Copyright 2014 Linaro Ltd
+ *
+ * Author: Mark Brown <broonie@kernel.org>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef __ISL9305_H
+#define __ISL9305_H
+
+#define ISL9305_DCD1 0
+#define ISL9305_DCD2 1
+#define ISL9305_LDO1 2
+#define ISL9305_LDO2 3
+
+#define ISL9305_MAX_REGULATOR ISL9305_LDO2
+
+struct regulator_init_data;
+
+struct isl9305_pdata {
+	struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR];
+};
+
+#endif
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 4efa1ed..fc0ee0c 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -203,6 +203,8 @@
  *
  * @name: Identifying name for the regulator.
  * @supply_name: Identifying the regulator supply
+ * @of_match: Name used to identify regulator in DT.
+ * @regulators_node: Name of node containing regulator definitions in DT.
  * @id: Numerical identifier for the regulator.
  * @ops: Regulator operations table.
  * @irq: Interrupt number for the regulator.
@@ -240,14 +242,17 @@
  * @bypass_val_off: Disabling value for control when using regmap set_bypass
  *
  * @enable_time: Time taken for initial enable of regulator (in uS).
+ * @off_on_delay: guard time (in uS), before re-enabling a regulator
  */
 struct regulator_desc {
 	const char *name;
 	const char *supply_name;
+	const char *of_match;
+	const char *regulators_node;
 	int id;
 	bool continuous_voltage_range;
 	unsigned n_voltages;
-	struct regulator_ops *ops;
+	const struct regulator_ops *ops;
 	int irq;
 	enum regulator_type type;
 	struct module *owner;
@@ -278,6 +283,8 @@
 	unsigned int bypass_val_off;
 
 	unsigned int enable_time;
+
+	unsigned int off_on_delay;
 };
 
 /**
@@ -350,6 +357,9 @@
 
 	struct regulator_enable_gpio *ena_pin;
 	unsigned int ena_gpio_state:1;
+
+	/* time when this regulator was disabled last time */
+	unsigned long last_off_jiffy;
 };
 
 struct regulator_dev *