Message ID | 1605280870-32432-2-git-send-email-claudiu.beznea@microchip.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | regulator: mcp16502: add support for ramp delay | expand |
On 13/11/2020 15:21, Claudiu Beznea wrote: > There are regulators who's min selector is not zero. Selectors loops > (looping b/w zero and regulator::desc::n_voltages) might throw errors > because invalid selectors are used (lower than > regulator::desc::linear_min_sel). For this situations validate selectors > against regulator::desc::linear_min_sel. After this commit was merged, I noticed a regression in the DFLL (CPU clock source) on Tegra124. The DFLL driver (drivers/clk/tegra/clk-dfll.c) calls regulator_list_voltage() in a loop to determine the selector for a given voltage (see function find_vdd_map_entry_exact()). Currently, the DFLL driver queries the number of voltages provided by the regulator by calling regulator_count_voltages() and then starting from 0, iterates through the number of voltages to find the selector value for the voltage it is looking for by calling regulator_list_voltage(). It assumes that any negative value returned by calling regulator_list_voltage() is an error and this will cause the loop up to terminate. In this case the regulator in question is the as3722 and the linear_min_sel for this regulator is 1 and so when the DFLL driver calls regulator_list_voltage() with a selector value of 0 it now returns a negative error code, as expected by this change, and this terminates the loop up in the DFLL driver. So I can clearly see why this is happening and I could fix up the DFLL driver to avoid this. Before doing so, I wanted to ask if that is the correct fix here, because it seems a bit odd that regulator_count_voltages() returns N voltages, but if the min selector value is greater than 0, then actually there are less than N. However, changing the number of voltages supported by the regulator to be N - linear_min_sel does not make sense either because then we need to know the linear_min_sel in order to determine the first valid voltage. In case of the as3722, the value 0 means that the regulator is powered down. So it is a valid setting and equates to 0 volts at the output AFAICT. Please let me know your thoughts are the correct way to fix this up. Thanks Jon
Hi Jon, On 24.11.2020 11:36, Jon Hunter wrote: > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe > > On 13/11/2020 15:21, Claudiu Beznea wrote: >> There are regulators who's min selector is not zero. Selectors loops >> (looping b/w zero and regulator::desc::n_voltages) might throw errors >> because invalid selectors are used (lower than >> regulator::desc::linear_min_sel). For this situations validate selectors >> against regulator::desc::linear_min_sel. > > > After this commit was merged, I noticed a regression in the DFLL (CPU > clock source) on Tegra124. The DFLL driver > (drivers/clk/tegra/clk-dfll.c) calls regulator_list_voltage() in a loop > to determine the selector for a given voltage (see function > find_vdd_map_entry_exact()). > > Currently, the DFLL driver queries the number of voltages provided by > the regulator by calling regulator_count_voltages() and then starting > from 0, iterates through the number of voltages to find the selector > value for the voltage it is looking for by calling > regulator_list_voltage(). It assumes that any negative value returned by > calling regulator_list_voltage() is an error and this will cause the > loop up to terminate. > > In this case the regulator in question is the as3722 and the > linear_min_sel for this regulator is 1 and so when the DFLL driver calls > regulator_list_voltage() with a selector value of 0 it now returns a > negative error code, as expected by this change, and this terminates the > loop up in the DFLL driver. So I can clearly see why this is happening > and I could fix up the DFLL driver to avoid this. > > Before doing so, I wanted to ask if that is the correct fix here, > because it seems a bit odd that regulator_count_voltages() returns N > voltages, but if the min selector value is greater than 0, then actually > there are less than N. However, changing the number of voltages > supported by the regulator to be N - linear_min_sel does not make sense > either because then we need to know the linear_min_sel in order to > determine the first valid voltage. > > In case of the as3722, the value 0 means that the regulator is powered > down. So it is a valid setting and equates to 0 volts at the output AFAICT. > > Please let me know your thoughts are the correct way to fix this up. I would say that a solution would be to have a new helper to retrieve the linear_min_sel (e.g. regulator_min_sel()) and use this for all the consumers of regulator_list_voltage() and the other APIs that patch "regulator: core: validate selector against linear_min_sel" has changed (regulator_list_voltage_table(), regulator_set_voltage_time()). With this change the loop in find_vdd_map_entry_exact() should be b/w regulator_min_sel() and regulator_count_voltages(). Maybe Mark has a better solution for this. Thank you, Claudiu > > Thanks > Jon > > -- > nvpublic >
On 24/11/2020 11:14, Claudiu.Beznea@microchip.com wrote: ... > I would say that a solution would be to have a new helper to retrieve the > linear_min_sel (e.g. regulator_min_sel()) and use this for all the > consumers of regulator_list_voltage() and the other APIs that patch > "regulator: core: validate selector against linear_min_sel" has changed > (regulator_list_voltage_table(), regulator_set_voltage_time()). With this > change the loop in find_vdd_map_entry_exact() should be b/w > regulator_min_sel() and regulator_count_voltages(). > > Maybe Mark has a better solution for this. By the way, I don't think that Tegra is alone here. I see some other drivers doing some similar things [0][1][2] and so I am wondering if this is going to be a problem for a few drivers. Jon [0] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/mmc/core/regulator.c#n61 [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/cpufreq/s3c2416-cpufreq.c#n263 [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/leds/leds-regulator.c#n29
On Tue, Nov 24, 2020 at 11:14:54AM +0000, Claudiu.Beznea@microchip.com wrote: > On 24.11.2020 11:36, Jon Hunter wrote: > > Before doing so, I wanted to ask if that is the correct fix here, > > because it seems a bit odd that regulator_count_voltages() returns N > > voltages, but if the min selector value is greater than 0, then actually > > there are less than N. However, changing the number of voltages > > supported by the regulator to be N - linear_min_sel does not make sense > > either because then we need to know the linear_min_sel in order to > > determine the first valid voltage. > I would say that a solution would be to have a new helper to retrieve the > linear_min_sel (e.g. regulator_min_sel()) and use this for all the > consumers of regulator_list_voltage() and the other APIs that patch > "regulator: core: validate selector against linear_min_sel" has changed > (regulator_list_voltage_table(), regulator_set_voltage_time()). With this > change the loop in find_vdd_map_entry_exact() should be b/w > regulator_min_sel() and regulator_count_voltages(). We need an incremental fix to return 0 rather than an error for things below the minimum selector, it's not invalid for there to be holes in the range of selectors and this is just an example of that. Consumers need to be able to cope with skipping over values that can't be mapped.
On 24.11.2020 15:41, Jon Hunter wrote: > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe > > On 24/11/2020 11:14, Claudiu.Beznea@microchip.com wrote: > > ... > >> I would say that a solution would be to have a new helper to retrieve the >> linear_min_sel (e.g. regulator_min_sel()) and use this for all the >> consumers of regulator_list_voltage() and the other APIs that patch >> "regulator: core: validate selector against linear_min_sel" has changed >> (regulator_list_voltage_table(), regulator_set_voltage_time()). With this >> change the loop in find_vdd_map_entry_exact() should be b/w >> regulator_min_sel() and regulator_count_voltages(). >> >> Maybe Mark has a better solution for this. > > > By the way, I don't think that Tegra is alone here. I see some other > drivers doing some similar things [0][1][2] and so I am wondering if > this is going to be a problem for a few drivers. > As far as I can tell most of the regulator_list_voltage() consumers are checking the return value against a [min_uV, max_uV] range or if the return value is a negative error code or zero. The consumers are looking a selector that respect the above rule for the entire [0, regulator_count_voltages()] range (I have to double check for the rest of functions modified by my patch). In case of clk-dfll.c the find_vdd_map_entry_exact() returns if it finds the 1st invalid selector: n_voltages = regulator_count_voltages(td->vdd_reg); for (i = 0; i < n_voltages; i++) { reg_uV = regulator_list_voltage(td->vdd_reg, i); if (reg_uV < 0) break; reg_volt_id = reg_uV / td->soc->alignment.step_uv; if (align_step == reg_volt_id) return i; } Maybe it would be better if the loop continues in case reg_uV is negative or zero? (the zero case is good for this function as it will make the (align_step == reg_volt_id) to be false). But as Mark said in the previous email, there could be regulators with gaps in between min_sel and n_voltages. With the previous code it seems it worked because the regulator_list_voltage() calls the as3722's list_voltage which is regulator_list_voltage_linear() which checks the selector against min_selector and returns zero in case the selector is lower than the min_selector. Please correct me if I'm wrong. Anyway, I will prepare a fix for my previous patch to return zero in case the regulator_count_voltages() receives an invalid selector. That should also fix the case with this driver. Thank you, Claudiu > Jon > > [0] > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/mmc/core/regulator.c#n61 > [1] > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/cpufreq/s3c2416-cpufreq.c#n263 > [2] > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/leds/leds-regulator.c#n29 > > -- > nvpublic >
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2e1ea18221ef..1d0d35f14f37 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2958,7 +2958,8 @@ static int _regulator_list_voltage(struct regulator_dev *rdev, return rdev->desc->fixed_uV; if (ops->list_voltage) { - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; if (lock) regulator_lock(rdev); @@ -3109,7 +3110,8 @@ int regulator_list_hardware_vsel(struct regulator *regulator, struct regulator_dev *rdev = regulator->rdev; const struct regulator_ops *ops = rdev->desc->ops; - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) return -EOPNOTSUPP; @@ -4032,6 +4034,9 @@ int regulator_set_voltage_time(struct regulator *regulator, for (i = 0; i < rdev->desc->n_voltages; i++) { /* We only look for exact voltage matches here */ + if (i < rdev->desc->linear_min_sel) + continue; + voltage = regulator_list_voltage(regulator, i); if (voltage < 0) return -EINVAL; diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index e4bb09bbd3fa..974f1a63993d 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -647,7 +647,8 @@ int regulator_list_voltage_table(struct regulator_dev *rdev, return -EINVAL; } - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; return rdev->desc->volt_table[selector];
There are regulators who's min selector is not zero. Selectors loops (looping b/w zero and regulator::desc::n_voltages) might throw errors because invalid selectors are used (lower than regulator::desc::linear_min_sel). For this situations validate selectors against regulator::desc::linear_min_sel. Fixes: 3a40cfc36bb3d ("regulator: core: create unlocked version of regulator_list_voltage") Fixes: 04eca28cde52c ("regulator: Add helpers for low-level register access") Fixes: 88cd222b259d6 ("regulator: provide consumer interface for fall/rise time") Fixes: d295f7670127e ("regulator: core: Move list_voltage_{linear,linear_range,table} to helpers.c") Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com> --- drivers/regulator/core.c | 9 +++++++-- drivers/regulator/helpers.c | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-)