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> --- 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); 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); - 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]; + + 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), + 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); irq = irq_of_parse_and_map(dev->of_node, 0); if (!irq) { @@ -106,7 +123,7 @@ static int gic_probe(struct platform_device *pdev) return -EINVAL; } - ret = gic_get_clocks(dev, data); + ret = gic_get_clocks(dev, chip_pm); if (ret) goto irq_dispose; @@ -116,12 +133,10 @@ static int gic_probe(struct platform_device *pdev) if (ret < 0) goto rpm_disable; - ret = gic_of_init_child(dev, &gic, irq); + ret = gic_of_init_child(dev, &chip_pm->chip_data, irq); if (ret) goto rpm_put; - platform_set_drvdata(pdev, gic); - pm_runtime_put(dev); dev_info(dev, "GIC IRQ controller registered\n"); @@ -132,7 +147,6 @@ static int gic_probe(struct platform_device *pdev) pm_runtime_put_sync(dev); rpm_disable: pm_runtime_disable(dev); - pm_clk_destroy(dev); irq_dispose: irq_dispose_mapping(irq); -- 2.7.4