diff mbox series

[U-Boot,v3,6/9] regulator: Add support for ramp delay

Message ID 20190216094548.911-7-krzk@kernel.org
State Changes Requested
Delegated to: Minkyu Kang
Headers show
Series arm: exynos: Fix reboot on Odroid HC1 | expand

Commit Message

Krzysztof Kozlowski Feb. 16, 2019, 9:45 a.m. UTC
Changing voltage and enabling regulator might require delays so the
regulator stabilizes at expected level.

Add support for "regulator-ramp-delay" binding which can introduce
required time to both enabling the regulator and to changing the
voltage.

Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
---
 .../regulator/regulator.txt                   |  2 +
 drivers/power/regulator/regulator-uclass.c    | 47 ++++++++++++++++++-
 include/power/regulator.h                     |  2 +
 3 files changed, 49 insertions(+), 2 deletions(-)

Comments

Torsten Duwe Feb. 18, 2019, 2:03 p.m. UTC | #1
On Sat, Feb 16, 2019 at 10:45:45AM +0100, Krzysztof Kozlowski wrote:
> Changing voltage and enabling regulator might require delays so the
> regulator stabilizes at expected level.
> 
> Add support for "regulator-ramp-delay" binding which can introduce
> required time to both enabling the regulator and to changing the
> voltage.

I'm surprised that such a thing doesn't exist already.

> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>

> --- a/doc/device-tree-bindings/regulator/regulator.txt
> +++ b/doc/device-tree-bindings/regulator/regulator.txt
> @@ -35,6 +35,7 @@ Optional properties:
>  - regulator-max-microamp: a maximum allowed Current value
>  - regulator-always-on: regulator should never be disabled
>  - regulator-boot-on: enabled by bootloader/firmware
> +- regulator-ramp-delay: ramp delay for regulator (in uV/us)

I guess you mean s/V, not V/s; at least the code suggests so.
But my main point is: is the required delay always a linear
function of the voltage jump? Depending on the dampening and
load on the rail this could be an overshoot and settle, no?

So I suggest to make that an array with 2 elements: a fixed part
and a time per voltage change. Does that make sense?

	Torsten
Krzysztof Kozlowski Feb. 18, 2019, 2:28 p.m. UTC | #2
On Mon, 18 Feb 2019 at 15:03, Torsten Duwe <duwe@lst.de> wrote:
>
> On Sat, Feb 16, 2019 at 10:45:45AM +0100, Krzysztof Kozlowski wrote:
> > Changing voltage and enabling regulator might require delays so the
> > regulator stabilizes at expected level.
> >
> > Add support for "regulator-ramp-delay" binding which can introduce
> > required time to both enabling the regulator and to changing the
> > voltage.
>
> I'm surprised that such a thing doesn't exist already.
>
> > Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
>
> > --- a/doc/device-tree-bindings/regulator/regulator.txt
> > +++ b/doc/device-tree-bindings/regulator/regulator.txt
> > @@ -35,6 +35,7 @@ Optional properties:
> >  - regulator-max-microamp: a maximum allowed Current value
> >  - regulator-always-on: regulator should never be disabled
> >  - regulator-boot-on: enabled by bootloader/firmware
> > +- regulator-ramp-delay: ramp delay for regulator (in uV/us)
>
> I guess you mean s/V, not V/s; at least the code suggests so.

uV/uS. It is correct in the code:
int delay = DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay);
delay = (uV - uV) / (uV / uS) = uS

> But my main point is: is the required delay always a linear
> function of the voltage jump? Depending on the dampening and
> load on the rail this could be an overshoot and settle, no?
>
> So I suggest to make that an array with 2 elements: a fixed part
> and a time per voltage change. Does that make sense?

Just to make it clear - then we do not follow Linux kernel DT bindings.
The voltage change might have exponential characteristic and/or have
additional fixed delay time (see patch 7 here). We could re-use some
properties from Linux bindings for that purpose:
https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/regulator/regulator.txt#L19
https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/regulator/regulator.txt#L24

Best regards,
Krzysztof
Torsten Duwe Feb. 18, 2019, 3:26 p.m. UTC | #3
On Mon, Feb 18, 2019 at 03:28:46PM +0100, Krzysztof Kozlowski wrote:
> On Mon, 18 Feb 2019 at 15:03, Torsten Duwe <duwe@lst.de> wrote:
> >
> > > --- a/doc/device-tree-bindings/regulator/regulator.txt
> > > +++ b/doc/device-tree-bindings/regulator/regulator.txt
> > > @@ -35,6 +35,7 @@ Optional properties:
> > >  - regulator-max-microamp: a maximum allowed Current value
> > >  - regulator-always-on: regulator should never be disabled
> > >  - regulator-boot-on: enabled by bootloader/firmware
> > > +- regulator-ramp-delay: ramp delay for regulator (in uV/us)
> >
> > I guess you mean s/V, not V/s; at least the code suggests so.
> 
> uV/uS. It is correct in the code:
> int delay = DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay);
> delay = (uV - uV) / (uV / uS) = uS

You're right. _divide_ by that value; somhow this has escaped me.
Sorry for the noise.

nit: "s" is for seconds, "S" is for conductance.

> > But my main point is: is the required delay always a linear
> > function of the voltage jump? Depending on the dampening and
> > load on the rail this could be an overshoot and settle, no?
> >
> > So I suggest to make that an array with 2 elements: a fixed part
> > and a time per voltage change. Does that make sense?
> 
> Just to make it clear - then we do not follow Linux kernel DT bindings.
> The voltage change might have exponential characteristic and/or have
> additional fixed delay time (see patch 7 here). We could re-use some
> properties from Linux bindings for that purpose:
> https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/regulator/regulator.txt#L19
> https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/regulator/regulator.txt#L24

I see. But then "static void regulator_set_value_delay(...)" should either
at least have a "ramp" somewhere in its name or it should discover the device
properties on its own, in order to be able to handle regulator-settling-time*
and regulator-enable-ramp-delay as well in the future. (i.e. pass it uc_pdata
instead of uc_pdata->ramp_delay and also let it handle the condition).

	Torsten
Krzysztof Kozlowski Feb. 19, 2019, 12:14 p.m. UTC | #4
On Mon, 18 Feb 2019 at 16:27, Torsten Duwe <duwe@lst.de> wrote:

> > > But my main point is: is the required delay always a linear
> > > function of the voltage jump? Depending on the dampening and
> > > load on the rail this could be an overshoot and settle, no?
> > >
> > > So I suggest to make that an array with 2 elements: a fixed part
> > > and a time per voltage change. Does that make sense?
> >
> > Just to make it clear - then we do not follow Linux kernel DT bindings.
> > The voltage change might have exponential characteristic and/or have
> > additional fixed delay time (see patch 7 here). We could re-use some
> > properties from Linux bindings for that purpose:
> > https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/regulator/regulator.txt#L19
> > https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/regulator/regulator.txt#L24
>
> I see. But then "static void regulator_set_value_delay(...)" should either
> at least have a "ramp" somewhere in its name or it should discover the device
> properties on its own, in order to be able to handle regulator-settling-time*
> and regulator-enable-ramp-delay as well in the future. (i.e. pass it uc_pdata
> instead of uc_pdata->ramp_delay and also let it handle the condition).

Makes sense, so let me add the ramp keyword. I will also mention in
comment that other delays are not yet handled.

Thanks for feedback!

Best regards,
Krzysztof
diff mbox series

Patch

diff --git a/doc/device-tree-bindings/regulator/regulator.txt b/doc/device-tree-bindings/regulator/regulator.txt
index 65b69c427899..4ba642b7c77f 100644
--- a/doc/device-tree-bindings/regulator/regulator.txt
+++ b/doc/device-tree-bindings/regulator/regulator.txt
@@ -35,6 +35,7 @@  Optional properties:
 - regulator-max-microamp: a maximum allowed Current value
 - regulator-always-on: regulator should never be disabled
 - regulator-boot-on: enabled by bootloader/firmware
+- regulator-ramp-delay: ramp delay for regulator (in uV/us)
 
 Note
 The "regulator-name" constraint is used for setting the device's uclass
@@ -60,4 +61,5 @@  ldo0 {
 	regulator-max-microamp = <100000>;
 	regulator-always-on;
 	regulator-boot-on;
+	regulator-ramp-delay = <12000>;
 };
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
index 6f355b969a6d..363c6e6441fa 100644
--- a/drivers/power/regulator/regulator-uclass.c
+++ b/drivers/power/regulator/regulator-uclass.c
@@ -35,10 +35,22 @@  int regulator_get_value(struct udevice *dev)
 	return ops->get_value(dev);
 }
 
+static void regulator_set_value_delay(struct udevice *dev, int old_uV,
+				      int new_uV, unsigned int ramp_delay)
+{
+	int delay = DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay);
+
+	debug("regulator %s: delay %u us (%d uV -> %d uV)\n", dev->name, delay,
+	      old_uV, new_uV);
+
+	udelay(delay);
+}
+
 int regulator_set_value(struct udevice *dev, int uV)
 {
 	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
 	struct dm_regulator_uclass_platdata *uc_pdata;
+	int ret, old_uV = uV, is_enabled = 0;
 
 	uc_pdata = dev_get_uclass_platdata(dev);
 	if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV)
@@ -49,7 +61,20 @@  int regulator_set_value(struct udevice *dev, int uV)
 	if (!ops || !ops->set_value)
 		return -ENOSYS;
 
-	return ops->set_value(dev, uV);
+	if (uc_pdata->ramp_delay) {
+		is_enabled = regulator_get_enable(dev);
+		old_uV = regulator_get_value(dev);
+	}
+
+	ret = ops->set_value(dev, uV);
+
+	if (!ret) {
+		if (uc_pdata->ramp_delay && old_uV > 0 && is_enabled)
+			regulator_set_value_delay(dev, old_uV, uV,
+						  uc_pdata->ramp_delay);
+	}
+
+	return ret;
 }
 
 /*
@@ -107,6 +132,7 @@  int regulator_set_enable(struct udevice *dev, bool enable)
 {
 	const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
 	struct dm_regulator_uclass_platdata *uc_pdata;
+	int ret, old_enable = 0;
 
 	if (!ops || !ops->set_enable)
 		return -ENOSYS;
@@ -115,7 +141,22 @@  int regulator_set_enable(struct udevice *dev, bool enable)
 	if (!enable && uc_pdata->always_on)
 		return -EACCES;
 
-	return ops->set_enable(dev, enable);
+	if (uc_pdata->ramp_delay)
+		old_enable = regulator_get_enable(dev);
+
+	ret = ops->set_enable(dev, enable);
+	if (!ret) {
+		if (uc_pdata->ramp_delay && !old_enable && enable) {
+			int uV = regulator_get_value(dev);
+
+			if (uV > 0) {
+				regulator_set_value_delay(dev, 0, uV,
+							  uc_pdata->ramp_delay);
+			}
+		}
+	}
+
+	return ret;
 }
 
 int regulator_set_enable_if_allowed(struct udevice *dev, bool enable)
@@ -335,6 +376,8 @@  static int regulator_pre_probe(struct udevice *dev)
 						-ENODATA);
 	uc_pdata->always_on = dev_read_bool(dev, "regulator-always-on");
 	uc_pdata->boot_on = dev_read_bool(dev, "regulator-boot-on");
+	uc_pdata->ramp_delay = dev_read_u32_default(dev, "regulator-ramp-delay",
+						    0);
 
 	/* Those values are optional (-ENODATA if unset) */
 	if ((uc_pdata->min_uV != -ENODATA) &&
diff --git a/include/power/regulator.h b/include/power/regulator.h
index 314160a894b7..6c6e2cd4f996 100644
--- a/include/power/regulator.h
+++ b/include/power/regulator.h
@@ -150,6 +150,7 @@  enum regulator_flag {
  * @always_on* - bool type, true or false
  * @boot_on*   - bool type, true or false
  * TODO(sjg@chromium.org): Consider putting the above two into @flags
+ * @ramp_delay - Time to settle down after voltage change (unit: uV/us)
  * @flags:     - flags value (see REGULATOR_FLAG_...)
  * @name**     - fdt regulator name - should be taken from the device tree
  * ctrl_reg:   - Control register offset used to enable/disable regulator
@@ -169,6 +170,7 @@  struct dm_regulator_uclass_platdata {
 	int max_uV;
 	int min_uA;
 	int max_uA;
+	unsigned int ramp_delay;
 	bool always_on;
 	bool boot_on;
 	const char *name;