Re: [PATCH v2 2/4] devfreq: exynos-bus: convert to use dev_pm_opp_set_rate()

From: Chanwoo Choi
Date: Tue Jul 16 2019 - 06:30:35 EST


Hi Bartlomiej,

On 19. 7. 16. ìí 7:13, Bartlomiej Zolnierkiewicz wrote:
>
> Hi Chanwoo,
>
> On 7/16/19 5:56 AM, Chanwoo Choi wrote:
>> Hi Kamil,
>>
>> Looks good to me. But, this patch has some issue.
>> I added the detailed reviews.
>>
>> I recommend that you make the separate patches as following
>> in order to clarify the role of which apply the dev_pm_opp_* function.
>>
>> First patch,
>> Need to consolidate the following two function into one function.
>> because the original exynos-bus.c has the problem that the regulator
>> of parent devfreq device have to be enabled before enabling the clock.
>> This issue did not happen because bootloader enables the bus-related
>> regulators before kernel booting.
>> - exynos_bus_parse_of()
>> - exynos_bus_parent_parse_of()
>>> Second patch,
>> Apply dev_pm_opp_set_regulators() and dev_pm_opp_set_rate()
>>
>>
>> On 19. 7. 15. ìí 9:04, Kamil Konieczny wrote:
>>> Reuse opp core code for setting bus clock and voltage. As a side
>>> effect this allow useage of coupled regulators feature (required
>>> for boards using Exynos5422/5800 SoCs) because dev_pm_opp_set_rate()
>>> uses regulator_set_voltage_triplet() for setting regulator voltage
>>> while the old code used regulator_set_voltage_tol() with fixed
>>> tolerance. This patch also removes no longer needed parsing of DT
>>> property "exynos,voltage-tolerance" (no Exynos devfreq DT node uses
>>> it).
>>>
>>> Signed-off-by: Kamil Konieczny <k.konieczny@xxxxxxxxxxxxxxxxxxx>
>>> ---
>>> drivers/devfreq/exynos-bus.c | 172 ++++++++++++++---------------------
>>> 1 file changed, 66 insertions(+), 106 deletions(-)
>>>
>>> diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c
>>> index 486cc5b422f1..7fc4f76bd848 100644
>>> --- a/drivers/devfreq/exynos-bus.c
>>> +++ b/drivers/devfreq/exynos-bus.c
>>> @@ -25,7 +25,6 @@
>>> #include <linux/slab.h>
>>>
>>> #define DEFAULT_SATURATION_RATIO 40
>>> -#define DEFAULT_VOLTAGE_TOLERANCE 2
>>>
>>> struct exynos_bus {
>>> struct device *dev;
>>> @@ -37,9 +36,9 @@ struct exynos_bus {
>>>
>>> unsigned long curr_freq;
>>>
>>> - struct regulator *regulator;
>>> + struct opp_table *opp_table;
>>> +
>>> struct clk *clk;
>>> - unsigned int voltage_tolerance;
>>> unsigned int ratio;
>>> };
>>>
>>> @@ -99,56 +98,25 @@ static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
>>> {
>>> struct exynos_bus *bus = dev_get_drvdata(dev);
>>> struct dev_pm_opp *new_opp;
>>> - unsigned long old_freq, new_freq, new_volt, tol;
>>> int ret = 0;
>>> -
>>> - /* Get new opp-bus instance according to new bus clock */
>>> + /*
>>> + * New frequency for bus may not be exactly matched to opp, adjust
>>> + * *freq to correct value.
>>> + */
>>
>> You better to change this comment with following styles
>> to keep the consistency:
>>
>> /* Get correct frequency for bus ... */
>>
>>> new_opp = devfreq_recommended_opp(dev, freq, flags);
>>> if (IS_ERR(new_opp)) {
>>> dev_err(dev, "failed to get recommended opp instance\n");
>>> return PTR_ERR(new_opp);
>>> }
>>>
>>> - new_freq = dev_pm_opp_get_freq(new_opp);
>>> - new_volt = dev_pm_opp_get_voltage(new_opp);
>>> dev_pm_opp_put(new_opp);
>>>
>>> - old_freq = bus->curr_freq;
>>> -
>>> - if (old_freq == new_freq)
>>> - return 0;
>>> - tol = new_volt * bus->voltage_tolerance / 100;
>>> -
>>> /* Change voltage and frequency according to new OPP level */
>>> mutex_lock(&bus->lock);
>>> + ret = dev_pm_opp_set_rate(dev, *freq);
>>> + if (!ret)
>>> + bus->curr_freq = *freq;
>>
>> Have to print the error log if ret has minus error value.
>
> dev_pm_opp_set_rate() should print the error message on all
> errors so wouldn't printing the error log also here be superfluous?
>
> [ Please also note that the other user of dev_pm_opp_set_rate()
> (cpufreq-dt cpufreq driver) doesn't do this. ]

OK. Thanks for the explanation.

>
>> Modify it as following:
>>
>> if (ret < 0) {
>> dev_err(dev, "failed to set bus rate\n");
>> goto err:
>> }
>> bus->curr_freq = *freq;
>>
>> err:
>> mutex_unlock(&bus->lock);
>>
>> return ret;
>>
>>>
>>> - if (old_freq < new_freq) {
>>> - ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
>>> - if (ret < 0) {
>>> - dev_err(bus->dev, "failed to set voltage\n");
>>> - goto out;
>>> - }
>>> - }
>>> -
>>> - ret = clk_set_rate(bus->clk, new_freq);
>>> - if (ret < 0) {
>>> - dev_err(dev, "failed to change clock of bus\n");
>>> - clk_set_rate(bus->clk, old_freq);
>>> - goto out;
>>> - }
>>> -
>>> - if (old_freq > new_freq) {
>>> - ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
>>> - if (ret < 0) {
>>> - dev_err(bus->dev, "failed to set voltage\n");
>>> - goto out;
>>> - }
>>> - }
>>> - bus->curr_freq = new_freq;
>>> -
>>> - dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
>>> - old_freq, new_freq, clk_get_rate(bus->clk));
>>> -out:
>>> mutex_unlock(&bus->lock);
>>>
>>> return ret;
>>> @@ -194,10 +162,11 @@ static void exynos_bus_exit(struct device *dev)
>>> if (ret < 0)
>>> dev_warn(dev, "failed to disable the devfreq-event devices\n");
>>>
>>> - if (bus->regulator)
>>> - regulator_disable(bus->regulator);
>>> + if (bus->opp_table)
>>> + dev_pm_opp_put_regulators(bus->opp_table);
>>
>> Have to disable regulator after disabling the clock
>> to prevent the h/w fault.
>>
>> I think that you should call them with following sequence:
>>
>> clk_disable_unprepare(bus->clk);
>> if (bus->opp_table)
>> dev_pm_opp_put_regulators(bus->opp_table);
>> dev_pm_opp_of_remove_table(dev);
>>
>>>
>>> dev_pm_opp_of_remove_table(dev);
>>> +
>>> clk_disable_unprepare(bus->clk);
>>> }
>>>
>>> @@ -209,39 +178,26 @@ static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
>>> {
>>> struct exynos_bus *bus = dev_get_drvdata(dev);
>>> struct dev_pm_opp *new_opp;
>>> - unsigned long old_freq, new_freq;
>>> - int ret = 0;
>>> + int ret;
>>>
>>> - /* Get new opp-bus instance according to new bus clock */
>>> + /*
>>> + * New frequency for bus may not be exactly matched to opp, adjust
>>> + * *freq to correct value.
>>> + */
>>
>> You better to change this comment with following styles
>> to keep the consistency:
>>
>> /* Get correct frequency for bus ... */
>>
>>> new_opp = devfreq_recommended_opp(dev, freq, flags);
>>> if (IS_ERR(new_opp)) {
>>> dev_err(dev, "failed to get recommended opp instance\n");
>>> return PTR_ERR(new_opp);
>>> }
>>>
>>> - new_freq = dev_pm_opp_get_freq(new_opp);
>>> dev_pm_opp_put(new_opp);
>>>
>>> - old_freq = bus->curr_freq;
>>> -
>>> - if (old_freq == new_freq)
>>> - return 0;
>>> -
>>> /* Change the frequency according to new OPP level */
>>> mutex_lock(&bus->lock);
>>> + ret = dev_pm_opp_set_rate(dev, *freq);
>>> + if (!ret)
>>> + bus->curr_freq = *freq;
>>
>> ditto. Have to print the error log, check above comment.
>>
>>>
>>> - ret = clk_set_rate(bus->clk, new_freq);
>>> - if (ret < 0) {
>>> - dev_err(dev, "failed to set the clock of bus\n");
>>> - goto out;
>>> - }
>>> -
>>> - *freq = new_freq;
>>> - bus->curr_freq = new_freq;
>>> -
>>> - dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
>>> - old_freq, new_freq, clk_get_rate(bus->clk));
>>> -out:
>>> mutex_unlock(&bus->lock);
>>>
>>> return ret;
>>> @@ -259,20 +215,7 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>>> struct exynos_bus *bus)
>>> {
>>> struct device *dev = bus->dev;
>>> - int i, ret, count, size;
>>> -
>>> - /* Get the regulator to provide each bus with the power */
>>> - bus->regulator = devm_regulator_get(dev, "vdd");
>>> - if (IS_ERR(bus->regulator)) {
>>> - dev_err(dev, "failed to get VDD regulator\n");
>>> - return PTR_ERR(bus->regulator);
>>> - }
>>> -
>>> - ret = regulator_enable(bus->regulator);
>>> - if (ret < 0) {
>>> - dev_err(dev, "failed to enable VDD regulator\n");
>>> - return ret;
>>> - }
>>> + int i, count, size;
>>>
>>> /*
>>> * Get the devfreq-event devices to get the current utilization of
>>> @@ -281,24 +224,20 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>>> count = devfreq_event_get_edev_count(dev);
>>> if (count < 0) {
>>> dev_err(dev, "failed to get the count of devfreq-event dev\n");
>>> - ret = count;
>>> - goto err_regulator;
>>> + return count;
>>> }
>>> +
>>> bus->edev_count = count;
>>>
>>> size = sizeof(*bus->edev) * count;
>>> bus->edev = devm_kzalloc(dev, size, GFP_KERNEL);
>>> - if (!bus->edev) {
>>> - ret = -ENOMEM;
>>> - goto err_regulator;
>>> - }
>>> + if (!bus->edev)
>>> + return -ENOMEM;
>>>
>>> for (i = 0; i < count; i++) {
>>> bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i);
>>> - if (IS_ERR(bus->edev[i])) {
>>> - ret = -EPROBE_DEFER;
>>> - goto err_regulator;
>>> - }
>>> + if (IS_ERR(bus->edev[i]))
>>> + return -EPROBE_DEFER;
>>> }
>>>
>>> /*
>>> @@ -314,22 +253,15 @@ static int exynos_bus_parent_parse_of(struct device_node *np,
>>> if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
>>> bus->ratio = DEFAULT_SATURATION_RATIO;
>>>
>>> - if (of_property_read_u32(np, "exynos,voltage-tolerance",
>>> - &bus->voltage_tolerance))
>>> - bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE;
>>> -
>>> return 0;
>>> -
>>> -err_regulator:
>>> - regulator_disable(bus->regulator);
>>> -
>>> - return ret;
>>> }
>>>
>>> static int exynos_bus_parse_of(struct device_node *np,
>>> - struct exynos_bus *bus)
>>> + struct exynos_bus *bus, bool passive)
>>> {
>>> struct device *dev = bus->dev;
>>> + struct opp_table *opp_table;
>>> + const char *vdd = "vdd";
>>> struct dev_pm_opp *opp;
>>> unsigned long rate;
>>> int ret;
>>> @@ -347,11 +279,22 @@ static int exynos_bus_parse_of(struct device_node *np,
>>> return ret;
>>> }
>>>
>>> + if (!passive) {
>>> + opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1);
>>> + if (IS_ERR(opp_table)) {
>>> + ret = PTR_ERR(opp_table);
>>> + dev_err(dev, "failed to set regulators %d\n", ret);
>>> + goto err_clk;/
>>> + }
>>> +
>>> + bus->opp_table = opp_table;
>>> + }
>>
>> This driver has exynos_bus_parent_parse_of() function for parent devfreq device.
>> dev_pm_opp_set_regulators() have to be called in exynos_bus_parent_parse_of()
>> because the regulator is only used by parent devfreq device.
>
> exynos_bus_parse_of() is called for all devfreq devices (including
> parent) and (as you've noticed) the regulator should be enabled before
> enabling clock (which is done in exynos_bus_parse_of()) so adding
> extra argument to exynos_bus_parse_of() (like it is done currently in
> the patch)

I think that this patch has still the problem about call sequence
between clock and regulator as following:

273 ret = clk_prepare_enable(bus->clk);
274 if (ret < 0) {
275 dev_err(dev, "failed to get enable clock\n");
276 return ret;
277 }
278
279 if (!passive) {
280 opp_table = dev_pm_opp_set_regulators(dev, &vdd, 1);
281 if (IS_ERR(opp_table)) {
282 ret = PTR_ERR(opp_table);
283 dev_err(dev, "failed to set regulators %d\n", ret);
284 goto err_clk;
285 }
286
287 bus->opp_table = opp_table;
288 }

makes it possible to do the setup correctly without the need
> of merging both functions into one huge function (which would be more
> difficult to follow than two simpler functions IMHO). Is that approach
> acceptable or do you prefer one big function?

Actually, I don't force to make one function for both
exynos_bus_parse_of() and exynos_bus_parent_parse_of().

If we just keep this code, dev_pm_opp_set_regulators()
should be handled in exynos_bus_parent_parse_of()
because only parent devfreq device controls the regulator.

In order to keep the two functions, maybe have to change
the call the sequence between exynos_bus_parse_of() and
exynos_bus_parent_parse_of().

Once again, I don't force any fixed method. I want to fix them
with correct way.

>
>>> +
>>> /* Get the freq and voltage from OPP table to scale the bus freq */
>>> ret = dev_pm_opp_of_add_table(dev);
>>> if (ret < 0) {
>>> dev_err(dev, "failed to get OPP table\n");
>>> - goto err_clk;
>>> + goto err_regulator;
>>> }
>>>
>>> rate = clk_get_rate(bus->clk);
>>> @@ -362,6 +305,7 @@ static int exynos_bus_parse_of(struct device_node *np,
>>> ret = PTR_ERR(opp);
>>> goto err_opp;
>>> }
>>> +
>>> bus->curr_freq = dev_pm_opp_get_freq(opp);
>>> dev_pm_opp_put(opp);
>>>
>>> @@ -369,6 +313,13 @@ static int exynos_bus_parse_of(struct device_node *np,
>>>
>>> err_opp:
>>> dev_pm_opp_of_remove_table(dev);
>>> +
>>> +err_regulator:
>>> + if (bus->opp_table) {
>>> + dev_pm_opp_put_regulators(bus->opp_table);
>>> + bus->opp_table = NULL;
>>> + }
>>
>> As I mentioned above, it it wrong to call dev_pm_opp_put_regulators()
>> after removing the opp_table by dev_pm_opp_of_remove_table().
>>
>>> +
>>> err_clk:
>>> clk_disable_unprepare(bus->clk);
>>>
>>> @@ -386,6 +337,7 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>> struct exynos_bus *bus;
>>> int ret, max_state;
>>> unsigned long min_freq, max_freq;
>>> + bool passive = false;
>>>
>>> if (!np) {
>>> dev_err(dev, "failed to find devicetree node\n");
>>> @@ -395,12 +347,18 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>> bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
>>> if (!bus)
>>> return -ENOMEM;
>>> +
>>> mutex_init(&bus->lock);
>>> bus->dev = &pdev->dev;
>>> platform_set_drvdata(pdev, bus);
>>> + node = of_parse_phandle(dev->of_node, "devfreq", 0);
>>> + if (node) {
>>> + of_node_put(node);
>>> + passive = true;
>>> + }
>>>
>>> /* Parse the device-tree to get the resource information */
>>> - ret = exynos_bus_parse_of(np, bus);
>>> + ret = exynos_bus_parse_of(np, bus, passive);
>>> if (ret < 0)
>>> return ret;
>>>
>>> @@ -410,13 +368,10 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>> goto err;
>>> }
>>>
>>> - node = of_parse_phandle(dev->of_node, "devfreq", 0);
>>> - if (node) {
>>> - of_node_put(node);
>>> + if (passive)
>>> goto passive;
>>> - } else {
>>> - ret = exynos_bus_parent_parse_of(np, bus);
>>> - }
>>> +
>>> + ret = exynos_bus_parent_parse_of(np, bus);
>>>
>>
>> Remove unneeded blank line.
>>
>>> if (ret < 0)
>>> goto err;
>>> @@ -509,6 +464,11 @@ static int exynos_bus_probe(struct platform_device *pdev)
>>>
>>> err:
>>> dev_pm_opp_of_remove_table(dev);
>>> + if (bus->opp_table) {
>>> + dev_pm_opp_put_regulators(bus->opp_table);
>>> + bus->opp_table = NULL;
>>> + }
>>> +
>>
>> ditto.
>> Have to disable regulator after disabling the clock
>> to prevent the h/w fault.
>>
>> I think that you should call them with following sequence:
>>
>> clk_disable_unprepare(bus->clk);
>> if (bus->opp_table)
>> dev_pm_opp_put_regulators(bus->opp_table);
>> dev_pm_opp_of_remove_table(dev);
>>
>>> clk_disable_unprepare(bus->clk);
>>>
>>> return ret;
>
> Best regards,
> --
> Bartlomiej Zolnierkiewicz
> Samsung R&D Institute Poland
> Samsung Electronics
>
>


--
Best Regards,
Chanwoo Choi
Samsung Electronics