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;
};