Re: [PATCH v4 2/3] irqchip/gic-pm: update driver to use clk_bulk APIs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 22/03/2019 10:53, Sameer Pujar wrote:
> 
> On 3/22/2019 4:11 PM, Jon Hunter wrote:
>> On 22/03/2019 10:25, Sameer Pujar wrote:
>>> gic-pm driver is using pm-clk framework to manage clock resources, where
>>> clocks remain always ON. This happens on Tegra devices which use BPMP
>>> co-processor to manage the clocks. Calls to BPMP are always blocking and
>>> hence it is necessary to enable/disable clocks during prepare/unprepare
>>> phase respectively. When pm-clk is used, prepare count of clock is not
>>> balanced until pm_clk_remove() happens. Clock is prepared in the driver
>>> probe() and thus prepare count of clock remains non-zero, which in turn
>>> keeps clock ON always.
>>>
>>> Please note that above mentioned behavior is specific to Tegra devices
>>> using BPMP for clock management and this should not be seen on other
>>> devices. Though this patch uses clk_bulk APIs to address the mentioned
>>> behavior, this works fine for all devices.
>>>
>>> Suggested-by: Mohan Kumar D <mkumard@xxxxxxxxxx>
>>> Signed-off-by: Sameer Pujar <spujar@xxxxxxxxxx>
>>> ---
>>>   drivers/irqchip/irq-gic-pm.c | 78
>>> +++++++++++++++++++++++++-------------------
>>>   1 file changed, 45 insertions(+), 33 deletions(-)
>>>
>>> diff --git a/drivers/irqchip/irq-gic-pm.c b/drivers/irqchip/irq-gic-pm.c
>>> index ecafd29..a2ef954 100644
>>> --- a/drivers/irqchip/irq-gic-pm.c
>>> +++ b/drivers/irqchip/irq-gic-pm.c
>>> @@ -19,7 +19,6 @@
>>>   #include <linux/of_irq.h>
>>>   #include <linux/irqchip/arm-gic.h>
>>>   #include <linux/platform_device.h>
>>> -#include <linux/pm_clock.h>
>>>   #include <linux/pm_runtime.h>
>>>   #include <linux/slab.h>
>>>   @@ -28,17 +27,27 @@ struct gic_clk_data {
>>>       const char *const *clocks;
>>>   };
>>>   +struct gic_chip_pm {
>>> +    struct gic_chip_data *chip_data;
>>> +    const struct gic_clk_data *clk_data;
>>> +    struct clk_bulk_data *clks;
>>> +};
>>> +
>>>   static int gic_runtime_resume(struct device *dev)
>>>   {
>>> -    struct gic_chip_data *gic = dev_get_drvdata(dev);
>>> +    struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
>>> +    struct gic_chip_data *gic = chip_pm->chip_data;
>>> +    const struct gic_clk_data *data = chip_pm->clk_data;
>>>       int ret;
>>>   -    ret = pm_clk_resume(dev);
>>> -    if (ret)
>>> +    ret = clk_bulk_prepare_enable(data->num_clocks, chip_pm->clks);
>>> +    if (ret) {
>>> +        dev_err(dev, "clk_enable failed: %d\n", ret);
>>>           return ret;
>>> +    }
>>>         /*
>>> -     * On the very first resume, the pointer to the driver data
>>> +     * On the very first resume, the pointer to chip_pm->chip_data
>>>        * will be NULL and this is intentional, because we do not
>>>        * want to restore the GIC on the very first resume. So if
>>>        * the pointer is not valid just return.
>>> @@ -54,51 +63,55 @@ static int gic_runtime_resume(struct device *dev)
>>>     static int gic_runtime_suspend(struct device *dev)
>>>   {
>>> -    struct gic_chip_data *gic = dev_get_drvdata(dev);
>>> +    struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
>>> +    struct gic_chip_data *gic = chip_pm->chip_data;
>>> +    const struct gic_clk_data *data = chip_pm->clk_data;
>>>         gic_dist_save(gic);
>>>       gic_cpu_save(gic);
>>>   -    return pm_clk_suspend(dev);
>>> +    clk_bulk_disable_unprepare(data->num_clocks, chip_pm->clks);
>>> +
>>> +    return 0;
>>>   }
>>>   -static int gic_get_clocks(struct device *dev, const struct
>>> gic_clk_data *data)
>>> +static int gic_get_clocks(struct device *dev)
>>>   {
>>> +    struct gic_chip_pm *chip_pm = dev_get_drvdata(dev);
>>> +    const struct gic_clk_data *data = chip_pm->clk_data;
>>>       unsigned int i;
>>> -    int ret;
>>> -
>>> -    if (!dev || !data)
>>> -        return -EINVAL;
>>>   -    ret = pm_clk_create(dev);
>>> -    if (ret)
>>> -        return ret;
>>> +    chip_pm->clks = devm_kcalloc(dev, data->num_clocks,
>>> +                     sizeof(*chip_pm->clks), GFP_KERNEL);
>>> +    if (!chip_pm->clks)
>>> +        return -ENOMEM;
>>>   -    for (i = 0; i < data->num_clocks; i++) {
>>> -        ret = of_pm_clk_add_clk(dev, data->clocks[i]);
>>> -        if (ret) {
>>> -            dev_err(dev, "failed to add clock %s\n",
>>> -                data->clocks[i]);
>>> -            pm_clk_destroy(dev);
>>> -            return ret;
>>> -        }
>>> -    }
>>> +    for (i = 0; i < data->num_clocks; i++)
>>> +        chip_pm->clks[i].id = data->clocks[i];
>>>   -    return 0;
>>> +    return devm_clk_bulk_get(dev, data->num_clocks, chip_pm->clks);
>>>   }
>>>     static int gic_probe(struct platform_device *pdev)
>>>   {
>>>       struct device *dev = &pdev->dev;
>>>       const struct gic_clk_data *data;
>>> -    struct gic_chip_data *gic;
>>> +    struct gic_chip_pm *chip_pm;
>>>       int ret, irq;
>>>   -    data = of_device_get_match_data(&pdev->dev);
>>> +    if (!dev)
>>> +        return -EINVAL;
>> This test is really not necessary.
> done
>>
>>> +
>>> +    chip_pm = devm_kzalloc(dev, sizeof(*chip_pm), GFP_KERNEL);
>>> +    if (!chip_pm)
>>> +        return -ENOMEM;
>> Why not allocate this after the match? I don't think it is necessary to
>> change the sequence here and we should just allocate this once we have
>> found a match.
> done
>>> +
>>> +    data = of_device_get_match_data(dev);
>>>       if (!data) {
>>> -        dev_err(&pdev->dev, "no device match found\n");
>>> +        dev_err(dev, "no device match found\n");
>>>           return -ENODEV;
>>>       }
>>> +    chip_pm->clk_data = data;
>>>         irq = irq_of_parse_and_map(dev->of_node, 0);
>>>       if (!irq) {
>>> @@ -106,7 +119,9 @@ static int gic_probe(struct platform_device *pdev)
>>>           return -EINVAL;
>>>       }
>>>   -    ret = gic_get_clocks(dev, data);
>>> +    dev_set_drvdata(dev, chip_pm);
>>> +
>>> +    ret = gic_get_clocks(dev);
>> Sorry to be pedantic, but I don't think that there is any value in
>> updating this function in terms of the number of parameters passed. We
>> have to the chip data so just pass it, it is simpler.
> I guess no need to pass a parameter, when it can be derived and for me it
> looked nicer this way. If you really don't like it, I can remove.

I would be tempted to get rid of gic_get_clocks altogether and just
place the code directly in probe seeing as it is simpler now.

Cheers
Jon

-- 
nvpublic




[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux