regulator: pm8xxx-regulator: Add slew_rate platform data parameter

Add a slew_rate platform data parameter which can be used to
specify the worst case board dependent voltage slew rate of a
given regulator.

regulator_set_voltage calls will be delayed based on the
slew_rate parameter when voltage is stepping up.  The delay is
calculated as (new_uV - old_uV) / slew_rate.

If no slew_rate is specified for a given regulator (i.e.
slew_rate == 0), then slew_rate will default to
max_uV / enable_time.

Change-Id: I7a9be300945d90941040b3a26d8a4199338bf9c4
Signed-off-by: David Collins <collinsd@codeaurora.org>
diff --git a/drivers/regulator/pm8xxx-regulator.c b/drivers/regulator/pm8xxx-regulator.c
index 833c513..dfdbb44 100644
--- a/drivers/regulator/pm8xxx-regulator.c
+++ b/drivers/regulator/pm8xxx-regulator.c
@@ -595,6 +595,29 @@
 	return enabled;
 }
 
+/*
+ * Adds delay when increasing in voltage to account for the slew rate of
+ * the regulator.
+ */
+static void pm8xxx_vreg_delay_for_slew(struct pm8xxx_vreg *vreg, int prev_uV,
+					int new_uV)
+{
+	int delay;
+
+	if (vreg->pdata.slew_rate == 0 || new_uV <= prev_uV ||
+	    !_pm8xxx_vreg_is_enabled(vreg))
+		return;
+
+	delay = DIV_ROUND_UP(new_uV - prev_uV, vreg->pdata.slew_rate);
+
+	if (delay >= 1000) {
+		mdelay(delay / 1000);
+		udelay(delay % 1000);
+	} else {
+		udelay(delay);
+	}
+}
+
 static int pm8xxx_pldo_get_voltage(struct regulator_dev *rdev)
 {
 	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
@@ -655,7 +678,7 @@
 {
 	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
 	int rc = 0, uV = min_uV;
-	int vmin;
+	int vmin, prev_uV;
 	unsigned vprog, fine_step;
 	u8 range_ext, range_sel, fine_step_reg, prev_reg;
 	bool reg_changed = false;
@@ -699,6 +722,8 @@
 		return -EINVAL;
 	}
 
+	prev_uV = pm8xxx_pldo_get_voltage(rdev);
+
 	mutex_lock(&vreg->pc_lock);
 
 	/* Write fine step, range select and program voltage update. */
@@ -746,8 +771,10 @@
 
 	if (rc)
 		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
-	else
+	else {
+		pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
 		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+	}
 
 	return rc;
 }
@@ -783,7 +810,7 @@
 {
 	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
 	unsigned vprog, fine_step_reg, prev_reg;
-	int rc;
+	int rc, prev_uV;
 	int uV = min_uV;
 
 	if (uV < NLDO_UV_MIN && max_uV >= NLDO_UV_MIN)
@@ -808,6 +835,8 @@
 		return -EINVAL;
 	}
 
+	prev_uV = pm8xxx_nldo_get_voltage(rdev);
+
 	mutex_lock(&vreg->pc_lock);
 
 	/* Write fine step. */
@@ -840,8 +869,10 @@
 
 	if (rc)
 		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
-	else
+	else {
+		pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
 		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+	}
 
 	return rc;
 }
@@ -897,7 +928,7 @@
 		int max_uV)
 {
 	u8 vprog, range;
-	int rc;
+	int rc, prev_uV;
 	int uV = min_uV;
 
 	if (uV < NLDO1200_LOW_UV_MIN && max_uV >= NLDO1200_LOW_UV_MIN)
@@ -931,6 +962,8 @@
 		return -EINVAL;
 	}
 
+	prev_uV = _pm8xxx_nldo1200_get_voltage(vreg);
+
 	/* Set to advanced mode */
 	rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
 		NLDO1200_ADVANCED_MODE | REGULATOR_BANK_SEL(2)
@@ -948,6 +981,7 @@
 
 	vreg->save_uV = uV;
 
+	pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
 bail:
 	if (rc)
 		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
@@ -1208,6 +1242,9 @@
 {
 	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
 	int rc = 0;
+	int prev_uV, new_uV;
+
+	prev_uV = pm8xxx_smps_get_voltage(rdev);
 
 	mutex_lock(&vreg->pc_lock);
 
@@ -1218,8 +1255,12 @@
 
 	mutex_unlock(&vreg->pc_lock);
 
-	if (!rc)
+	new_uV = pm8xxx_smps_get_voltage(rdev);
+
+	if (!rc) {
+		pm8xxx_vreg_delay_for_slew(vreg, prev_uV, new_uV);
 		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+	}
 
 	return rc;
 }
@@ -1291,6 +1332,7 @@
 	int rc = 0;
 	u8 vprog, band;
 	int uV = min_uV;
+	int prev_uV;
 
 	if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN)
 		uV = FTSMPS_BAND1_UV_MIN;
@@ -1336,6 +1378,8 @@
 		return -EINVAL;
 	}
 
+	prev_uV = _pm8xxx_ftsmps_get_voltage(vreg);
+
 	/*
 	 * Do not set voltage if regulator is currently disabled because doing
 	 * so will enable it.
@@ -1358,6 +1402,8 @@
 
 	vreg->save_uV = uV;
 
+	pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
+
 bail:
 	if (rc)
 		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
@@ -1402,7 +1448,7 @@
 				  int max_uV, unsigned *selector)
 {
 	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
-	int rc;
+	int rc, prev_uV;
 	int uV = min_uV;
 	u8 val;
 
@@ -1426,13 +1472,17 @@
 		return -EINVAL;
 	}
 
+	prev_uV = pm8xxx_ncp_get_voltage(rdev);
+
 	/* voltage setting */
 	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
 			NCP_VPROG_MASK, &vreg->ctrl_reg);
 	if (rc)
 		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
-	else
+	else {
+		pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
 		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+	}
 
 	return rc;
 }
@@ -1460,7 +1510,7 @@
 				   int max_uV, unsigned *selector)
 {
 	struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
-	int rc;
+	int rc, prev_uV;
 	int uV = min_uV;
 	u8 val;
 
@@ -1484,13 +1534,17 @@
 		return -EINVAL;
 	}
 
+	prev_uV = pm8xxx_boost_get_voltage(rdev);
+
 	/* voltage setting */
 	rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
 			BOOST_VPROG_MASK, &vreg->ctrl_reg);
 	if (rc)
 		vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
-	else
+	else {
+		pm8xxx_vreg_delay_for_slew(vreg, prev_uV, uV);
 		pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+	}
 
 	return rc;
 }
@@ -3151,6 +3205,16 @@
 			sizeof(struct pm8xxx_regulator_platform_data));
 		vreg->pdata.pin_ctrl = pin_ctrl;
 		vreg->pdata.pin_fn = pin_fn;
+		/*
+		 * If slew_rate isn't specified but enable_time is, then set
+		 * slew_rate = max_uV / enable_time.
+		 */
+		if (vreg->pdata.enable_time > 0
+		    && vreg->pdata.init_data.constraints.max_uV > 0
+		    && vreg->pdata.slew_rate <= 0)
+			vreg->pdata.slew_rate =
+			  DIV_ROUND_UP(vreg->pdata.init_data.constraints.max_uV,
+					vreg->pdata.enable_time);
 		vreg->dev = &pdev->dev;
 	} else {
 		/* Pin control regulator */
diff --git a/include/linux/regulator/pm8xxx-regulator.h b/include/linux/regulator/pm8xxx-regulator.h
index ddf1901..6b42263 100644
--- a/include/linux/regulator/pm8xxx-regulator.h
+++ b/include/linux/regulator/pm8xxx-regulator.h
@@ -66,6 +66,9 @@
  * @enable_time:	time in us taken to enable a regulator to the maximum
  *			allowed voltage for the system.  This is dependent upon
  *			the load and capacitance for a regulator on the board.
+ * @slew_rate:		worst case rate of change of regulator output voltage
+ *			in units of uV/us (V/s).  This is dependent upon the
+ *			load and capacitance for a regulator on the board.
  * @ocp_enable:		enable over current protection logic (available for
  *			LVS and MVS type switches)
  * @ocp_enable_time:	time in us to delay between enabling the switch and then
@@ -80,6 +83,7 @@
 	enum pm8xxx_vreg_pin_function	pin_fn;
 	int				system_uA;
 	int				enable_time;
+	int				slew_rate;
 	unsigned			ocp_enable;
 	int				ocp_enable_time;
 };