From f11fa1796a4b4f8c6d4ced37e8824276ec57057d Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 10 Aug 2016 17:53:54 +0530 Subject: mfd: tps65218: add version check to the PMIC probe Version information will be needed to handle some error cases under the regulator driver, so store the information once during MFD probe. Signed-off-by: Tero Kristo Signed-off-by: Dave Gerlach Signed-off-by: Keerthy Acked-by: Lee Jones Signed-off-by: Mark Brown --- include/linux/mfd/tps65218.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/mfd/tps65218.h b/include/linux/mfd/tps65218.h index 7fdf5326f34e..85e464e32c43 100644 --- a/include/linux/mfd/tps65218.h +++ b/include/linux/mfd/tps65218.h @@ -267,6 +267,7 @@ struct tps_info { struct tps65218 { struct device *dev; unsigned int id; + u8 rev; struct mutex tps_lock; /* lock guarding the data structure */ /* IRQ Data */ -- cgit v1.2.3 From 23a34f9d03a5d40a6234855bc069da370708cc9e Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 10 Aug 2016 17:53:55 +0530 Subject: regulator: tps65218: do not disable DCDC3 during poweroff on broken PMICs Some versions of tps65218 do not seem to support poweroff modes properly if DCDC3 regulator is shut-down. Thus, keep it enabled even during poweroff if the version info matches the broken silicon revision. Signed-off-by: Tero Kristo Signed-off-by: Dave Gerlach Signed-off-by: Keerthy Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/tps65218-regulator.c | 8 ++++++++ include/linux/mfd/tps65218.h | 5 +++++ 2 files changed, 13 insertions(+) (limited to 'include/linux') diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c index d1e631d64a20..eb0f5b13841a 100644 --- a/drivers/regulator/tps65218-regulator.c +++ b/drivers/regulator/tps65218-regulator.c @@ -180,6 +180,14 @@ static int tps65218_pmic_set_suspend_disable(struct regulator_dev *dev) if (rid < TPS65218_DCDC_1 || rid > TPS65218_LDO_1) return -EINVAL; + /* + * Certain revisions of TPS65218 will need to have DCDC3 regulator + * enabled always, otherwise an immediate system reboot will occur + * during poweroff. + */ + if (rid == TPS65218_DCDC_3 && tps->rev == TPS65218_REV_2_1) + return 0; + if (!tps->info[rid]->strobe) { if (rid == TPS65218_DCDC_3) tps->info[rid]->strobe = 3; diff --git a/include/linux/mfd/tps65218.h b/include/linux/mfd/tps65218.h index 85e464e32c43..d1db9527fab5 100644 --- a/include/linux/mfd/tps65218.h +++ b/include/linux/mfd/tps65218.h @@ -63,6 +63,11 @@ #define TPS65218_CHIPID_CHIP_MASK 0xF8 #define TPS65218_CHIPID_REV_MASK 0x07 +#define TPS65218_REV_1_0 0x0 +#define TPS65218_REV_1_1 0x1 +#define TPS65218_REV_2_0 0x2 +#define TPS65218_REV_2_1 0x3 + #define TPS65218_INT1_VPRG BIT(5) #define TPS65218_INT1_AC BIT(4) #define TPS65218_INT1_PB BIT(3) -- cgit v1.2.3 From 73e705bf81ceb84b39ef9cf6ffb8d12ca0c58a23 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 14 Sep 2016 09:52:08 -0700 Subject: regulator: core: Add set_voltage_time op The new op is analogous to set_voltage_time_sel. It can be used by regulators which don't have a table of discrete voltages. The function returns the time for the regulator output voltage to stabilize after being set to a new value, in microseconds. If the op is not set a default implementation is used to calculate the delay. This change also removes the ramp_delay calculation in the PWM regulator, since the driver now uses the core code for the calculation of the delay. Signed-off-by: Matthias Kaehlcke Signed-off-by: Mark Brown --- drivers/regulator/core.c | 86 +++++++++++++++++++++++++++------------ drivers/regulator/pwm-regulator.c | 10 ----- include/linux/regulator/driver.h | 10 ++++- 3 files changed, 67 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index df3028b2a8e9..c52fc0ce2693 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2743,6 +2743,24 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev, return ret; } +static int _regulator_set_voltage_time(struct regulator_dev *rdev, + int old_uV, int new_uV) +{ + unsigned int ramp_delay = 0; + + if (rdev->constraints->ramp_delay) + ramp_delay = rdev->constraints->ramp_delay; + else if (rdev->desc->ramp_delay) + ramp_delay = rdev->desc->ramp_delay; + + if (ramp_delay == 0) { + rdev_warn(rdev, "ramp_delay not set\n"); + return 0; + } + + return DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay); +} + static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { @@ -2752,6 +2770,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, unsigned int selector; int old_selector = -1; const struct regulator_ops *ops = rdev->desc->ops; + int old_uV = _regulator_get_voltage(rdev); trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV); @@ -2803,23 +2822,37 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, if (ret) goto out; - /* Call set_voltage_time_sel if successfully obtained old_selector */ - if (!old_selector >= 0 && old_selector != selector) { - delay = ops->set_voltage_time_sel(rdev, - old_selector, selector); - if (delay < 0) { - rdev_warn(rdev, "set_voltage_time_sel() failed: %d\n", - delay); - delay = 0; + if (ops->set_voltage_time_sel) { + /* + * Call set_voltage_time_sel if successfully obtained + * old_selector + */ + if (old_selector >= 0 && old_selector != selector) + delay = ops->set_voltage_time_sel(rdev, old_selector, + selector); + } else { + if (old_uV != best_val) { + if (ops->set_voltage_time) + delay = ops->set_voltage_time(rdev, old_uV, + best_val); + else + delay = _regulator_set_voltage_time(rdev, + old_uV, + best_val); } + } - /* Insert any necessary delays */ - if (delay >= 1000) { - mdelay(delay / 1000); - udelay(delay % 1000); - } else if (delay) { - udelay(delay); - } + if (delay < 0) { + rdev_warn(rdev, "failed to get delay: %d\n", delay); + delay = 0; + } + + /* Insert any necessary delays */ + if (delay >= 1000) { + mdelay(delay / 1000); + udelay(delay % 1000); + } else if (delay) { + udelay(delay); } if (best_val >= 0) { @@ -3000,9 +3033,13 @@ int regulator_set_voltage_time(struct regulator *regulator, int voltage; int i; + if (ops->set_voltage_time) + return ops->set_voltage_time(rdev, old_uV, new_uV); + else if (!ops->set_voltage_time_sel) + return _regulator_set_voltage_time(rdev, old_uV, new_uV); + /* Currently requires operations to do this */ - if (!ops->list_voltage || !ops->set_voltage_time_sel - || !rdev->desc->n_voltages) + if (!ops->list_voltage || !rdev->desc->n_voltages) return -EINVAL; for (i = 0; i < rdev->desc->n_voltages; i++) { @@ -3041,17 +3078,8 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { - unsigned int ramp_delay = 0; int old_volt, new_volt; - if (rdev->constraints->ramp_delay) - ramp_delay = rdev->constraints->ramp_delay; - else if (rdev->desc->ramp_delay) - ramp_delay = rdev->desc->ramp_delay; - - if (ramp_delay == 0) - return 0; - /* sanity check */ if (!rdev->desc->ops->list_voltage) return -EINVAL; @@ -3059,7 +3087,11 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, old_volt = rdev->desc->ops->list_voltage(rdev, old_selector); new_volt = rdev->desc->ops->list_voltage(rdev, new_selector); - return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay); + if (rdev->desc->ops->set_voltage_time) + return rdev->desc->ops->set_voltage_time(rdev, old_volt, + new_volt); + else + return _regulator_set_voltage_time(rdev, old_volt, new_volt); } EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel); diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index c24524242da2..1b88e0e15a70 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -10,7 +10,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -194,12 +193,10 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev, unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle; unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle; unsigned int duty_unit = drvdata->continuous.dutycycle_unit; - unsigned int ramp_delay = rdev->constraints->ramp_delay; int min_uV = rdev->constraints->min_uV; int max_uV = rdev->constraints->max_uV; int diff_uV = max_uV - min_uV; struct pwm_state pstate; - int old_uV = pwm_regulator_get_voltage(rdev); unsigned int diff_duty; unsigned int dutycycle; int ret; @@ -233,13 +230,6 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev, return ret; } - if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev)) - return 0; - - /* Ramp delay is in uV/uS. Adjust to uS and delay */ - ramp_delay = DIV_ROUND_UP(abs(req_min_uV - old_uV), ramp_delay); - usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10)); - return 0; } diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index fcfa40a6692c..37b532410528 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -113,10 +113,14 @@ struct regulator_linear_range { * stabilise after being enabled, in microseconds. * @set_ramp_delay: Set the ramp delay for the regulator. The driver should * select ramp delay equal to or less than(closest) ramp_delay. + * @set_voltage_time: Time taken for the regulator voltage output voltage + * to stabilise after being set to a new value, in microseconds. + * The function receives the from and to voltage as input, it + * should return the worst case. * @set_voltage_time_sel: Time taken for the regulator voltage output voltage * to stabilise after being set to a new value, in microseconds. - * The function provides the from and to voltage selector, the - * function should return the worst case. + * The function receives the from and to voltage selector as + * input, it should return the worst case. * @set_soft_start: Enable soft start for the regulator. * * @set_suspend_voltage: Set the voltage for the regulator when the system @@ -168,6 +172,8 @@ struct regulator_ops { /* Time taken to enable or set voltage on the regulator */ int (*enable_time) (struct regulator_dev *); int (*set_ramp_delay) (struct regulator_dev *, int ramp_delay); + int (*set_voltage_time) (struct regulator_dev *, int old_uV, + int new_uV); int (*set_voltage_time_sel) (struct regulator_dev *, unsigned int old_selector, unsigned int new_selector); -- cgit v1.2.3