[U-Boot,RFT,4/8] regulator: Add support for ramp delay

Message ID 20190209225411.32756-5-krzk@kernel.org
State RFC
Delegated to: Minkyu Kang
Headers show
Series
  • exynos: Fix reboot on Odroid HC1
Related show

Commit Message

Krzysztof Kozlowski Feb. 9, 2019, 10:54 p.m.
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>
---
 drivers/power/regulator/regulator-uclass.c | 45 +++++++++++++++++++++-
 include/power/regulator.h                  |  2 +
 2 files changed, 45 insertions(+), 2 deletions(-)

Comments

Simon Glass Feb. 10, 2019, 9:49 a.m. | #1
Hi Krzysztof,

On Sat, 9 Feb 2019 at 16:54, Krzysztof Kozlowski <krzk@kernel.org> 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.

Is this binding used in Linux? Can you please add binding
documentation for this?

>
> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
> ---
>  drivers/power/regulator/regulator-uclass.c | 45 +++++++++++++++++++++-
>  include/power/regulator.h                  |  2 +
>  2 files changed, 45 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
> index 39e46279d533..4119f244c74b 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);

So the ramp delay is microseconds per (microvolt delta)?

> +
> +       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;
>
>         uc_pdata = dev_get_uclass_platdata(dev);
>         if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV)
> @@ -49,7 +61,18 @@ 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)
> +               old_uV = regulator_get_value(dev);
> +
> +       ret = ops->set_value(dev, uV);
> +
> +       if (!ret) {
> +               if (uc_pdata->ramp_delay && old_uV > 0)
> +                       regulator_set_value_delay(dev, old_uV, uV,
> +                                                 uc_pdata->ramp_delay);
> +       }
> +
> +       return ret;
>  }
>
>  /*
> @@ -107,6 +130,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 +139,22 @@ int regulator_set_enable(struct udevice *dev, bool enable)
>         if (!enable && uc_pdata->always_on)
>                 return 0;
>
> -       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) {
> +                       int uV = regulator_get_value(dev);
> +
> +                       if (uV > 0) {
> +                               regulator_set_value_delay(dev, 0, uV,
> +                                                         uc_pdata->ramp_delay);
> +                       }

How come there is a delay when enabling it as well as when setting the voltage?

> +               }
> +       }
> +
> +       return ret;
>  }
>
>  int regulator_get_mode(struct udevice *dev)
> @@ -324,6 +363,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 5318ab3acece..c13fa1f336e5 100644
> --- a/include/power/regulator.h
> +++ b/include/power/regulator.h
> @@ -149,6 +149,7 @@ enum regulator_flag {
>   * @max_uA*    - maximum amperage (micro Amps)
>   * @always_on* - bool type, true or false
>   * @boot_on*   - bool type, true or false
> + * @ramp_delay - Time to settle down after voltage change (unit: uV/us)

us/uV isn't it?

>   * TODO(sjg@chromium.org): Consider putting the above two into @flags

This comment needs updating or moving up.

>   * @flags:     - flags value (see REGULATOR_FLAG_...)
>   * @name**     - fdt regulator name - should be taken from the device tree
> @@ -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;
> --
> 2.17.1
>

Regards,
Simon
Krzysztof Kozlowski Feb. 11, 2019, 8:14 a.m. | #2
On Sun, 10 Feb 2019 at 10:49, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Krzysztof,
>
> On Sat, 9 Feb 2019 at 16:54, Krzysztof Kozlowski <krzk@kernel.org> 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.
>
> Is this binding used in Linux? Can you please add binding
> documentation for this?

Yes, these are bindings from the kernel. I will add them.

>
> >
> > Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
> > ---
> >  drivers/power/regulator/regulator-uclass.c | 45 +++++++++++++++++++++-
> >  include/power/regulator.h                  |  2 +
> >  2 files changed, 45 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
> > index 39e46279d533..4119f244c74b 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);
>
> So the ramp delay is microseconds per (microvolt delta)?

The ramp delay is microvolt per microseconds.
int delay = uv / (uV/uS) = uS

>
> > +
> > +       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;
> >
> >         uc_pdata = dev_get_uclass_platdata(dev);
> >         if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV)
> > @@ -49,7 +61,18 @@ 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)
> > +               old_uV = regulator_get_value(dev);
> > +
> > +       ret = ops->set_value(dev, uV);
> > +
> > +       if (!ret) {
> > +               if (uc_pdata->ramp_delay && old_uV > 0)
> > +                       regulator_set_value_delay(dev, old_uV, uV,
> > +                                                 uc_pdata->ramp_delay);
> > +       }
> > +
> > +       return ret;
> >  }
> >
> >  /*
> > @@ -107,6 +130,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 +139,22 @@ int regulator_set_enable(struct udevice *dev, bool enable)
> >         if (!enable && uc_pdata->always_on)
> >                 return 0;
> >
> > -       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) {
> > +                       int uV = regulator_get_value(dev);
> > +
> > +                       if (uV > 0) {
> > +                               regulator_set_value_delay(dev, 0, uV,
> > +                                                         uc_pdata->ramp_delay);
> > +                       }
>
> How come there is a delay when enabling it as well as when setting the voltage?

Enabling a regulator also requires the time, which might be sum of:
1. Initial enable delay (not included here),
2. Rising of voltage delay (ramp delay) from 0 to expected.

In Linux kernel this is separate property regulator-enable-ramp-delay.
Here I reused the ramp delay to make it simpler.

However indeed there is no point to wait with ramp delay if the
regulator is disabled.

>
> > +               }
> > +       }
> > +
> > +       return ret;
> >  }
> >
> >  int regulator_get_mode(struct udevice *dev)
> > @@ -324,6 +363,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 5318ab3acece..c13fa1f336e5 100644
> > --- a/include/power/regulator.h
> > +++ b/include/power/regulator.h
> > @@ -149,6 +149,7 @@ enum regulator_flag {
> >   * @max_uA*    - maximum amperage (micro Amps)
> >   * @always_on* - bool type, true or false
> >   * @boot_on*   - bool type, true or false
> > + * @ramp_delay - Time to settle down after voltage change (unit: uV/us)
>
> us/uV isn't it?

No, opposite.

>
> >   * TODO(sjg@chromium.org): Consider putting the above two into @flags
>
> This comment needs updating or moving up.

Yes, thanks!

Best regards,
Krzysztof

Patch

diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
index 39e46279d533..4119f244c74b 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;
 
 	uc_pdata = dev_get_uclass_platdata(dev);
 	if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV)
@@ -49,7 +61,18 @@  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)
+		old_uV = regulator_get_value(dev);
+
+	ret = ops->set_value(dev, uV);
+
+	if (!ret) {
+		if (uc_pdata->ramp_delay && old_uV > 0)
+			regulator_set_value_delay(dev, old_uV, uV,
+						  uc_pdata->ramp_delay);
+	}
+
+	return ret;
 }
 
 /*
@@ -107,6 +130,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 +139,22 @@  int regulator_set_enable(struct udevice *dev, bool enable)
 	if (!enable && uc_pdata->always_on)
 		return 0;
 
-	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) {
+			int uV = regulator_get_value(dev);
+
+			if (uV > 0) {
+				regulator_set_value_delay(dev, 0, uV,
+							  uc_pdata->ramp_delay);
+			}
+		}
+	}
+
+	return ret;
 }
 
 int regulator_get_mode(struct udevice *dev)
@@ -324,6 +363,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 5318ab3acece..c13fa1f336e5 100644
--- a/include/power/regulator.h
+++ b/include/power/regulator.h
@@ -149,6 +149,7 @@  enum regulator_flag {
  * @max_uA*    - maximum amperage (micro Amps)
  * @always_on* - bool type, true or false
  * @boot_on*   - bool type, true or false
+ * @ramp_delay - Time to settle down after voltage change (unit: uV/us)
  * TODO(sjg@chromium.org): Consider putting the above two into @flags
  * @flags:     - flags value (see REGULATOR_FLAG_...)
  * @name**     - fdt regulator name - should be taken from the device tree
@@ -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;