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 |
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
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
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
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 --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;
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(-)