[PATCH v4 1/4] regulator: Add set_voltage_time op

From: Matthias Kaehlcke
Date: Tue Sep 06 2016 - 17:01:34 EST


The new op is analogous to set_voltage_time_sel. It can be used by
regulators that don't have a table of discrete voltages. The function
returns the time for the regulator voltage output voltage to stabilize
after being set to a new value, in microseconds. The actual calculation
of the stabilization time is done in the same place for both types of
regulators.

Signed-off-by: Matthias Kaehlcke <mka@xxxxxxxxxxxx>
---
Changes in v4:
- This patch is new for v4.

drivers/regulator/core.c | 140 +++++++++++++++++++++++++--------------
include/linux/regulator/driver.h | 8 +++
2 files changed, 97 insertions(+), 51 deletions(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index db320e8..b1cef47 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -2751,6 +2751,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
int best_val = 0;
unsigned int selector;
int old_selector = -1;
+ int old_uV = _regulator_get_voltage(rdev);

trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV);

@@ -2800,27 +2801,38 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
ret = -EINVAL;
}

- /* Call set_voltage_time_sel if successfully obtained old_selector */
- if (ret == 0 && !rdev->constraints->ramp_disable && old_selector >= 0
- && old_selector != selector) {
+ if (ret != 0 || rdev->constraints->ramp_disable)
+ goto no_delay;

- delay = rdev->desc->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 (rdev->desc->ops->set_voltage_time) {
+ int new_uV = _regulator_get_voltage(rdev);

- /* Insert any necessary delays */
- if (delay >= 1000) {
- mdelay(delay / 1000);
- udelay(delay % 1000);
- } else if (delay) {
- udelay(delay);
- }
+ if (old_uV == new_uV)
+ goto no_delay;
+
+ delay = rdev->desc->ops->set_voltage_time(rdev, old_uV, new_uV);
+ } else if (rdev->desc->ops->set_voltage_time_sel) {
+ if (old_selector < 0 || old_selector == selector)
+ goto no_delay;
+
+ delay = rdev->desc->ops->set_voltage_time_sel(
+ rdev, old_selector, selector);
+ }
+
+ 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);
+ }
+
+no_delay:
if (ret == 0 && best_val >= 0) {
unsigned long data = best_val;

@@ -2993,54 +3005,58 @@ int regulator_set_voltage_time(struct regulator *regulator,
{
struct regulator_dev *rdev = regulator->rdev;
const struct regulator_ops *ops = rdev->desc->ops;
- int old_sel = -1;
- int new_sel = -1;
- int voltage;
- int i;

- /* Currently requires operations to do this */
- if (!ops->list_voltage || !ops->set_voltage_time_sel
- || !rdev->desc->n_voltages)
- return -EINVAL;
+ if (ops->set_voltage_time) {
+ return ops->set_voltage_time(rdev, old_uV, new_uV);
+ } else if (ops->set_voltage_time_sel) {
+ int old_sel = -1;
+ int new_sel = -1;
+ int voltage;
+ int i;

- for (i = 0; i < rdev->desc->n_voltages; i++) {
- /* We only look for exact voltage matches here */
- voltage = regulator_list_voltage(regulator, i);
- if (voltage < 0)
+ /* Currently requires operations to do this */
+ if (!ops->list_voltage || !rdev->desc->n_voltages)
return -EINVAL;
- if (voltage == 0)
- continue;
- if (voltage == old_uV)
- old_sel = i;
- if (voltage == new_uV)
- new_sel = i;
- }

- if (old_sel < 0 || new_sel < 0)
- return -EINVAL;
+ for (i = 0; i < rdev->desc->n_voltages; i++) {
+ /* We only look for exact voltage matches here */
+ voltage = regulator_list_voltage(regulator, i);
+ if (voltage < 0)
+ return -EINVAL;
+ if (voltage == 0)
+ continue;
+ if (voltage == old_uV)
+ old_sel = i;
+ if (voltage == new_uV)
+ new_sel = i;
+ }
+
+ if (old_sel < 0 || new_sel < 0)
+ return -EINVAL;
+
+ return ops->set_voltage_time_sel(rdev, old_sel, new_sel);
+ }

- return ops->set_voltage_time_sel(rdev, old_sel, new_sel);
+ return -EINVAL;
}
EXPORT_SYMBOL_GPL(regulator_set_voltage_time);

/**
- * regulator_set_voltage_time_sel - get raise/fall time
- * @rdev: regulator source device
- * @old_selector: selector for starting voltage
- * @new_selector: selector for target voltage
+ * regulator_set_voltage_time_op - get raise/fall time
+ * @regulator: regulator source
+ * @old_uV: starting voltage in microvolts
+ * @new_uV: target voltage in microvolts
*
- * Provided with the starting and target voltage selectors, this function
- * returns time in microseconds required to rise or fall to this new voltage
+ * Provided with the starting and ending voltage, this function calculates
+ * the time in microseconds required to rise or fall to this new voltage.
*
* Drivers providing ramp_delay in regulation_constraints can use this as their
- * set_voltage_time_sel() operation.
+ * set_voltage_time() operation.
*/
-int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
- unsigned int old_selector,
- unsigned int new_selector)
+int regulator_set_voltage_time_op(struct regulator_dev *rdev,
+ int old_uV, int new_uV)
{
unsigned int ramp_delay = 0;
- int old_volt, new_volt;

if (rdev->constraints->ramp_delay)
ramp_delay = rdev->constraints->ramp_delay;
@@ -3052,6 +3068,28 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
return 0;
}

+ return DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay);
+}
+EXPORT_SYMBOL_GPL(regulator_set_voltage_time_op);
+
+/**
+ * regulator_set_voltage_time_sel - get raise/fall time
+ * @rdev: regulator source device
+ * @old_selector: selector for starting voltage
+ * @new_selector: selector for target voltage
+ *
+ * Provided with the starting and target voltage selectors, this function
+ * returns time in microseconds required to rise or fall to this new voltage
+ *
+ * Drivers providing ramp_delay in regulation_constraints can use this as their
+ * set_voltage_time_sel() operation.
+ */
+int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector,
+ unsigned int new_selector)
+{
+ int old_volt, new_volt;
+
/* sanity check */
if (!rdev->desc->ops->list_voltage)
return -EINVAL;
@@ -3059,7 +3097,7 @@ 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);
+ return regulator_set_voltage_time_op(rdev, old_volt, new_volt);
}
EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel);

diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index fcfa40a..537ca7f 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -113,6 +113,10 @@ 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 provides the from and to voltage, the function
+ * 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
@@ -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);
@@ -461,6 +467,8 @@ int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel);
int regulator_is_enabled_regmap(struct regulator_dev *rdev);
int regulator_enable_regmap(struct regulator_dev *rdev);
int regulator_disable_regmap(struct regulator_dev *rdev);
+int regulator_set_voltage_time_op(struct regulator_dev *rdev,
+ int old_uV, int new_uV);
int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int old_selector,
unsigned int new_selector);
--
2.8.0.rc3.226.g39d4020