Message ID | 20190209225411.32756-5-krzk@kernel.org |
---|---|
State | RFC |
Delegated to: | Minkyu Kang |
Headers | show |
Series | exynos: Fix reboot on Odroid HC1 | expand |
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
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
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;
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(-)