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 3/21/2019 10:01 PM, Jon Hunter wrote:
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.
This is moved up to make sure clk_data in chip_pm is populated by the time
gic_get_clocks() is called. The comment in runtime_resume() would still hold
good, gic would be NULL till gic_of_init_child() is called. May be the comment
needs to be slightly updated, because chip_pm is the new driver data now.

Cheers
Jon




[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