diff mbox series

[V2,3/3] soc/tegra: pmc: Move powergate initialisation to probe

Message ID 1555433288-16967-3-git-send-email-jonathanh@nvidia.com
State Accepted
Headers show
Series [V2,1/3] soc/tegra: pmc: Fix reset sources and levels | expand

Commit Message

Jon Hunter April 16, 2019, 4:48 p.m. UTC
Commit 8df127456f29 ("soc/tegra: pmc: Enable XUSB partitions on boot")
was added as a workaround to ensure that the XUSB powergates or domains
were turned on early during boot because as this time the Tegra XHCI
driver did not handle the power domains at all. Now that the Tegra XHCI
driver has been updated to properly managed the power domains, the
workaround to enable the XUSB power domain early has been removed. This
also means that we can now move the initialisation of the powergates
into the PMC driver probe. Therefore, move the powergate initialisation
into the PMC driver probe and return any errors detected. To handle any
errors, functions to cleanup and remove any power-domains registered
with the generic power-domain framework have been added.

Finally the initialisation of the 'powergates_available' bitmask is kept
in the PMC early init function to allow the legacy PMC powergate APIs to
be called during early boot for enabling secondary CPUs on 32-bit Tegra
devices.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
---
Changes since V1:
- None

drivers/soc/tegra/pmc.c | 83 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 68 insertions(+), 15 deletions(-)

Comments

Thierry Reding April 17, 2019, 8:35 a.m. UTC | #1
On Tue, Apr 16, 2019 at 05:48:08PM +0100, Jon Hunter wrote:
> Commit 8df127456f29 ("soc/tegra: pmc: Enable XUSB partitions on boot")
> was added as a workaround to ensure that the XUSB powergates or domains
> were turned on early during boot because as this time the Tegra XHCI
> driver did not handle the power domains at all. Now that the Tegra XHCI
> driver has been updated to properly managed the power domains, the
> workaround to enable the XUSB power domain early has been removed. This
> also means that we can now move the initialisation of the powergates
> into the PMC driver probe. Therefore, move the powergate initialisation
> into the PMC driver probe and return any errors detected. To handle any
> errors, functions to cleanup and remove any power-domains registered
> with the generic power-domain framework have been added.
> 
> Finally the initialisation of the 'powergates_available' bitmask is kept
> in the PMC early init function to allow the legacy PMC powergate APIs to
> be called during early boot for enabling secondary CPUs on 32-bit Tegra
> devices.
> 
> Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
> ---
> Changes since V1:
> - None
> 
> drivers/soc/tegra/pmc.c | 83 ++++++++++++++++++++++++++++++++++++++++---------
>  1 file changed, 68 insertions(+), 15 deletions(-)

Applied to for-5.2/soc, thanks.

Thierry
diff mbox series

Patch

diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
index 741afb141887..5648e5c09ef5 100644
--- a/drivers/soc/tegra/pmc.c
+++ b/drivers/soc/tegra/pmc.c
@@ -990,20 +990,21 @@  static int tegra_powergate_of_get_resets(struct tegra_powergate *pg,
 	return err;
 }
 
-static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
+static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
 {
 	struct device *dev = pmc->dev;
 	struct tegra_powergate *pg;
-	int id, err;
+	int id, err = 0;
 	bool off;
 
 	pg = kzalloc(sizeof(*pg), GFP_KERNEL);
 	if (!pg)
-		return;
+		return -ENOMEM;
 
 	id = tegra_powergate_lookup(pmc, np->name);
 	if (id < 0) {
 		dev_err(dev, "powergate lookup failed for %pOFn: %d\n", np, id);
+		err = -ENODEV;
 		goto free_mem;
 	}
 
@@ -1056,7 +1057,7 @@  static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
 
 	dev_dbg(dev, "added PM domain %s\n", pg->genpd.name);
 
-	return;
+	return 0;
 
 remove_genpd:
 	pm_genpd_remove(&pg->genpd);
@@ -1075,25 +1076,67 @@  static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np)
 
 free_mem:
 	kfree(pg);
+
+	return err;
 }
 
-static void tegra_powergate_init(struct tegra_pmc *pmc,
-				 struct device_node *parent)
+static int tegra_powergate_init(struct tegra_pmc *pmc,
+				struct device_node *parent)
 {
 	struct device_node *np, *child;
-	unsigned int i;
+	int err = 0;
+
+	np = of_get_child_by_name(parent, "powergates");
+	if (!np)
+		return 0;
 
-	/* Create a bitmap of the available and valid partitions */
-	for (i = 0; i < pmc->soc->num_powergates; i++)
-		if (pmc->soc->powergates[i])
-			set_bit(i, pmc->powergates_available);
+	for_each_child_of_node(np, child) {
+		err = tegra_powergate_add(pmc, child);
+		if (err < 0) {
+			of_node_put(child);
+			break;
+		}
+	}
+
+	of_node_put(np);
+
+	return err;
+}
+
+static void tegra_powergate_remove(struct generic_pm_domain *genpd)
+{
+	struct tegra_powergate *pg = to_powergate(genpd);
+
+	reset_control_put(pg->reset);
+
+	while (pg->num_clks--)
+		clk_put(pg->clks[pg->num_clks]);
+
+	kfree(pg->clks);
+
+	set_bit(pg->id, pmc->powergates_available);
+
+	kfree(pg);
+}
+
+static void tegra_powergate_remove_all(struct device_node *parent)
+{
+	struct generic_pm_domain *genpd;
+	struct device_node *np, *child;
 
 	np = of_get_child_by_name(parent, "powergates");
 	if (!np)
 		return;
 
-	for_each_child_of_node(np, child)
-		tegra_powergate_add(pmc, child);
+	for_each_child_of_node(np, child) {
+		of_genpd_del_provider(child);
+
+		genpd = of_genpd_remove_last(child);
+		if (IS_ERR(genpd))
+			continue;
+
+		tegra_powergate_remove(genpd);
+	}
 
 	of_node_put(np);
 }
@@ -2054,9 +2097,13 @@  static int tegra_pmc_probe(struct platform_device *pdev)
 	if (err)
 		goto cleanup_restart_handler;
 
+	err = tegra_powergate_init(pmc, pdev->dev.of_node);
+	if (err < 0)
+		goto cleanup_powergates;
+
 	err = tegra_pmc_irq_init(pmc);
 	if (err < 0)
-		goto cleanup_restart_handler;
+		goto cleanup_powergates;
 
 	mutex_lock(&pmc->powergates_lock);
 	iounmap(pmc->base);
@@ -2067,6 +2114,8 @@  static int tegra_pmc_probe(struct platform_device *pdev)
 
 	return 0;
 
+cleanup_powergates:
+	tegra_powergate_remove_all(pdev->dev.of_node);
 cleanup_restart_handler:
 	unregister_restart_handler(&tegra_pmc_restart_handler);
 cleanup_debugfs:
@@ -2763,6 +2812,7 @@  static int __init tegra_pmc_early_init(void)
 	const struct of_device_id *match;
 	struct device_node *np;
 	struct resource regs;
+	unsigned int i;
 	bool invert;
 
 	mutex_init(&pmc->powergates_lock);
@@ -2819,7 +2869,10 @@  static int __init tegra_pmc_early_init(void)
 		if (pmc->soc->maybe_tz_only)
 			pmc->tz_only = tegra_pmc_detect_tz_only(pmc);
 
-		tegra_powergate_init(pmc, np);
+		/* Create a bitmap of the available and valid partitions */
+		for (i = 0; i < pmc->soc->num_powergates; i++)
+			if (pmc->soc->powergates[i])
+				set_bit(i, pmc->powergates_available);
 
 		/*
 		 * Invert the interrupt polarity if a PMC device tree node