Re: [PATCH v3 2/3] irqchip/gic-pm: use devm_clk to keep clock state balanced

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

 



On 21/03/2019 15:33, Sameer Pujar wrote:
> gic-pm driver is using pm_clk_*() interface to manage clock resources. With
> this, clocks always remain ON. This happens on Tegra devices which use BPMP
> co-processor to manage the clocks, where clocks are enabled during prepare
> phase. This is necessary because calls to BPMP are always blocking. When
> pm_clk_*() interface is used on such devices, clock prepare count is not
> balanced till driver remove() gets executed and hence clocks are seen ON
> always. This patch helps to keep clock ref counts balanced and thus clocks
> are off, when device not in use.
> 
> Please note that this works for any device and the fix is not specific to
> Tegra devices.
> 
> Suggested-by: Mohan Kumar D <mkumard@xxxxxxxxxx>
> Signed-off-by: Sameer Pujar <spujar@xxxxxxxxxx>
> Reviewed-by: Jonathan Hunter <jonathanh@xxxxxxxxxx>

Not yet!

> ---
>  drivers/irqchip/irq-gic-pm.c | 68 ++++++++++++++++++++++++++------------------
>  1 file changed, 41 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-gic-pm.c b/drivers/irqchip/irq-gic-pm.c
> index ecafd29..b557a53 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,14 +27,24 @@ 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);

Unnecessary space here.

>  		return ret;
> +	}
>  
>  	/*
>  	 * On the very first resume, the pointer to the driver data
> @@ -54,51 +63,59 @@ 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)
>  {
> +	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_kzalloc(dev,
> +			     data->num_clocks * sizeof(struct clk_bulk_data),
> +			     GFP_KERNEL);

Ugh, please sort out the formatting here. No reason why devm_kzalloc
cannot start on the same line as chip_pm->clks. You should use
devm_kcalloc here and for sizeof use sizeof(*chip_pm->clks).

>  
> -	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;
> -		}
> -	}
> +	if (!chip_pm->clks)
> +		return -ENOMEM;
>  
> -	return 0;
> +	for (i = 0; i < data->num_clocks; i++)
> +		chip_pm->clks[i].id = data->clocks[i];

Hmm ... that's unfortunate but I don't have a better idea to avoid this :-(

> +
> +	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;
>  
> +	chip_pm = devm_kzalloc(dev, sizeof(struct gic_chip_pm),

sizeof(*chip_pm)

> +			       GFP_KERNEL);
> +	if (!chip_pm)
> +		return -ENOMEM;
> +
>  	data = of_device_get_match_data(&pdev->dev);
>  	if (!data) {
>  		dev_err(&pdev->dev, "no device match found\n");
>  		return -ENODEV;
>  	}
> +	chip_pm->clk_data = data;
> +	platform_set_drvdata(pdev, chip_pm);

So why has this been moved? There is a comment in gic_runtime_resume()
on why this was set after pm_runtime_get_sync() in probe.

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