diff mbox series

[2/2] soc: imx: gpcv2: prepare bus clocks early

Message ID 20230602185417.4098937-2-l.stach@pengutronix.de (mailing list archive)
State New, archived
Headers show
Series [1/2] soc: imx: gpcv2: rename keep_clocks to bus_clocks | expand

Commit Message

Lucas Stach June 2, 2023, 6:54 p.m. UTC
Prepare the bus clocks during PGC domain driver probe. This avoids a
potential deadlock when there a clock providers inside the domain,
as this might end up trying to take the CCF prepare_lock from two
different contexts, when runtime PM is trying to resume the PGC domain
for the clock provider. By keeping the bus clocks prepared as long as
there is a PGC domain driver attached, we don't need to take the
prepare_lock in the domain power up/down paths.

We don't want to do this for regular reset clocks, as this might lead
to some PLLs being kept prepared that could otherwise be shut down.
For the bus clocks this isn't a concern, as all the bus clocks are
derived from always-on system PLLs.

Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
---
 drivers/soc/imx/gpcv2.c | 41 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 7 deletions(-)

Comments

Marek Vasut June 2, 2023, 7:12 p.m. UTC | #1
On 6/2/23 20:54, Lucas Stach wrote:
> Prepare the bus clocks during PGC domain driver probe. This avoids a
> potential deadlock when there a clock providers inside the domain,
> as this might end up trying to take the CCF prepare_lock from two
> different contexts, when runtime PM is trying to resume the PGC domain
> for the clock provider. By keeping the bus clocks prepared as long as
> there is a PGC domain driver attached, we don't need to take the
> prepare_lock in the domain power up/down paths.
> 
> We don't want to do this for regular reset clocks, as this might lead
> to some PLLs being kept prepared that could otherwise be shut down.
> For the bus clocks this isn't a concern, as all the bus clocks are
> derived from always-on system PLLs.
> 
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
> ---
>   drivers/soc/imx/gpcv2.c | 41 ++++++++++++++++++++++++++++++++++-------
>   1 file changed, 34 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c
> index 706f852e5d87..428e2fd82f26 100644
> --- a/drivers/soc/imx/gpcv2.c
> +++ b/drivers/soc/imx/gpcv2.c
> @@ -337,10 +337,14 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
>   
>   	reset_control_assert(domain->reset);
>   
> -	/* Enable reset clocks for all devices in the domain */
> -	ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
> +	if (!domain->bus_clocks)
> +		/* Enable reset clocks for all devices in the domain */
> +		ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
> +	else
> +		/* Enable bus clocks for this domain */
> +		ret = clk_bulk_enable(domain->num_clks, domain->clks);
>   	if (ret) {
> -		dev_err(domain->dev, "failed to enable reset clocks\n");
> +		dev_err(domain->dev, "failed to enable clocks\n");
>   		goto out_regulator_disable;
>   	}
>   
> @@ -402,7 +406,10 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
>   	return 0;
>   
>   out_clk_disable:
> -	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	if (!domain->bus_clocks)
> +		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	else
> +		clk_bulk_disable(domain->num_clks, domain->clks);
>   out_regulator_disable:
>   	if (!IS_ERR(domain->regulator))
>   		regulator_disable(domain->regulator);
> @@ -466,8 +473,11 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
>   		}
>   	}
>   
> -	/* Disable reset clocks for all devices in the domain */
> -	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	/* Disable bus or reset clocks for all devices in the domain */
> +	if (!domain->bus_clocks)
> +		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	else
> +		clk_bulk_disable(domain->num_clks, domain->clks);
>   
>   	if (!IS_ERR(domain->regulator)) {
>   		ret = regulator_disable(domain->regulator);
> @@ -486,6 +496,8 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
>   out_clk_disable:
>   	if (!domain->bus_clocks)
>   		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	else
> +		clk_bulk_disable(domain->num_clks, domain->clks);
>   
>   	return ret;
>   }
> @@ -1343,10 +1355,19 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
>   		regmap_update_bits(domain->regmap, domain->regs->map,
>   				   domain->bits.map, domain->bits.map);
>   
> +	if (domain->bus_clocks) {
> +		ret = clk_bulk_prepare(domain->num_clks, domain->clks);
> +		if (ret) {
> +			dev_err(domain->dev,
> +				"Failed to prepare domain's clocks\n");
> +			goto out_domain_unmap;
> +		}
> +	}
> +
>   	ret = pm_genpd_init(&domain->genpd, NULL, true);
>   	if (ret) {
>   		dev_err(domain->dev, "Failed to init power domain\n");
> -		goto out_domain_unmap;
> +		goto out_disable_clocks;
>   	}
>   
>   	if (IS_ENABLED(CONFIG_LOCKDEP) &&
> @@ -1364,6 +1385,9 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
>   
>   out_genpd_remove:
>   	pm_genpd_remove(&domain->genpd);
> +out_disable_clocks:
> +	if (domain->bus_clocks)
> +		clk_bulk_unprepare(domain->num_clks, domain->clks);
>   out_domain_unmap:
>   	if (domain->bits.map)
>   		regmap_update_bits(domain->regmap, domain->regs->map,
> @@ -1380,6 +1404,9 @@ static int imx_pgc_domain_remove(struct platform_device *pdev)
>   	of_genpd_del_provider(domain->dev->of_node);
>   	pm_genpd_remove(&domain->genpd);
>   
> +	if (domain->bus_clocks)
> +		clk_bulk_unprepare(domain->num_clks, domain->clks);
> +
>   	if (domain->bits.map)
>   		regmap_update_bits(domain->regmap, domain->regs->map,
>   				   domain->bits.map, 0);

Isn't this similar approach to

[PATCH] [RFC] soc: imx: gpcv2: Split clock prepare from clock enable in 
the domain

where Laurent (now on CC) reported that this still causes issues ?

If not, then please ignore my comment here.
Lucas Stach June 2, 2023, 8:06 p.m. UTC | #2
Hi Marek,

Am Freitag, dem 02.06.2023 um 21:12 +0200 schrieb Marek Vasut:
> On 6/2/23 20:54, Lucas Stach wrote:
> > Prepare the bus clocks during PGC domain driver probe. This avoids a
> > potential deadlock when there a clock providers inside the domain,
> > as this might end up trying to take the CCF prepare_lock from two
> > different contexts, when runtime PM is trying to resume the PGC domain
> > for the clock provider. By keeping the bus clocks prepared as long as
> > there is a PGC domain driver attached, we don't need to take the
> > prepare_lock in the domain power up/down paths.
> > 
> > We don't want to do this for regular reset clocks, as this might lead
> > to some PLLs being kept prepared that could otherwise be shut down.
> > For the bus clocks this isn't a concern, as all the bus clocks are
> > derived from always-on system PLLs.
> > 
> > Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
> > ---
> 
[...]
> Isn't this similar approach to
> 
> [PATCH] [RFC] soc: imx: gpcv2: Split clock prepare from clock enable in 
> the domain
> 
> where Laurent (now on CC) reported that this still causes issues ?
> 
> If not, then please ignore my comment here.

Yes, that's right. It doesn't solve the clock provider (HDMI PHY) is
inside a power domain itself issue.
However it resolves the simple cases where we would deadlock due to the
needed bus clocks, which is what happens when audiomix adds runtime PM
support. So I still think it's a good idea in general, even if it
doesn't solve all the problems. 

Regards,
Lucas
Marco Felsch June 5, 2023, 7:15 a.m. UTC | #3
Hi Lucas,

thanks for posting.

On 23-06-02, Lucas Stach wrote:
> Prepare the bus clocks during PGC domain driver probe. This avoids a
> potential deadlock when there a clock providers inside the domain,
> as this might end up trying to take the CCF prepare_lock from two
> different contexts, when runtime PM is trying to resume the PGC domain
> for the clock provider. By keeping the bus clocks prepared as long as
> there is a PGC domain driver attached, we don't need to take the
> prepare_lock in the domain power up/down paths.
> 
> We don't want to do this for regular reset clocks, as this might lead
> to some PLLs being kept prepared that could otherwise be shut down.
> For the bus clocks this isn't a concern, as all the bus clocks are
> derived from always-on system PLLs.
> 
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>

Feel free to add my rb for both patches.

Reviewed-by: Marco Felsch <m.felsch@pengutronix.de>

> ---
>  drivers/soc/imx/gpcv2.c | 41 ++++++++++++++++++++++++++++++++++-------
>  1 file changed, 34 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c
> index 706f852e5d87..428e2fd82f26 100644
> --- a/drivers/soc/imx/gpcv2.c
> +++ b/drivers/soc/imx/gpcv2.c
> @@ -337,10 +337,14 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
>  
>  	reset_control_assert(domain->reset);
>  
> -	/* Enable reset clocks for all devices in the domain */
> -	ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
> +	if (!domain->bus_clocks)
> +		/* Enable reset clocks for all devices in the domain */
> +		ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
> +	else
> +		/* Enable bus clocks for this domain */
> +		ret = clk_bulk_enable(domain->num_clks, domain->clks);
>  	if (ret) {
> -		dev_err(domain->dev, "failed to enable reset clocks\n");
> +		dev_err(domain->dev, "failed to enable clocks\n");
>  		goto out_regulator_disable;
>  	}
>  
> @@ -402,7 +406,10 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
>  	return 0;
>  
>  out_clk_disable:
> -	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	if (!domain->bus_clocks)
> +		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	else
> +		clk_bulk_disable(domain->num_clks, domain->clks);
>  out_regulator_disable:
>  	if (!IS_ERR(domain->regulator))
>  		regulator_disable(domain->regulator);
> @@ -466,8 +473,11 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
>  		}
>  	}
>  
> -	/* Disable reset clocks for all devices in the domain */
> -	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	/* Disable bus or reset clocks for all devices in the domain */
> +	if (!domain->bus_clocks)
> +		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	else
> +		clk_bulk_disable(domain->num_clks, domain->clks);
>  
>  	if (!IS_ERR(domain->regulator)) {
>  		ret = regulator_disable(domain->regulator);
> @@ -486,6 +496,8 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
>  out_clk_disable:
>  	if (!domain->bus_clocks)
>  		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
> +	else
> +		clk_bulk_disable(domain->num_clks, domain->clks);
>  
>  	return ret;
>  }
> @@ -1343,10 +1355,19 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
>  		regmap_update_bits(domain->regmap, domain->regs->map,
>  				   domain->bits.map, domain->bits.map);
>  
> +	if (domain->bus_clocks) {
> +		ret = clk_bulk_prepare(domain->num_clks, domain->clks);
> +		if (ret) {
> +			dev_err(domain->dev,
> +				"Failed to prepare domain's clocks\n");
> +			goto out_domain_unmap;
> +		}
> +	}
> +
>  	ret = pm_genpd_init(&domain->genpd, NULL, true);
>  	if (ret) {
>  		dev_err(domain->dev, "Failed to init power domain\n");
> -		goto out_domain_unmap;
> +		goto out_disable_clocks;
>  	}
>  
>  	if (IS_ENABLED(CONFIG_LOCKDEP) &&
> @@ -1364,6 +1385,9 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
>  
>  out_genpd_remove:
>  	pm_genpd_remove(&domain->genpd);
> +out_disable_clocks:
> +	if (domain->bus_clocks)
> +		clk_bulk_unprepare(domain->num_clks, domain->clks);
>  out_domain_unmap:
>  	if (domain->bits.map)
>  		regmap_update_bits(domain->regmap, domain->regs->map,
> @@ -1380,6 +1404,9 @@ static int imx_pgc_domain_remove(struct platform_device *pdev)
>  	of_genpd_del_provider(domain->dev->of_node);
>  	pm_genpd_remove(&domain->genpd);
>  
> +	if (domain->bus_clocks)
> +		clk_bulk_unprepare(domain->num_clks, domain->clks);
> +
>  	if (domain->bits.map)
>  		regmap_update_bits(domain->regmap, domain->regs->map,
>  				   domain->bits.map, 0);
> -- 
> 2.39.2
> 
> 
>
Luca Ceresoli June 5, 2023, 8:06 a.m. UTC | #4
Hi Lucas,

On Fri,  2 Jun 2023 20:54:17 +0200
Lucas Stach <l.stach@pengutronix.de> wrote:

> Prepare the bus clocks during PGC domain driver probe. This avoids a
> potential deadlock when there a clock providers inside the domain,
> as this might end up trying to take the CCF prepare_lock from two
> different contexts, when runtime PM is trying to resume the PGC domain
> for the clock provider. By keeping the bus clocks prepared as long as
> there is a PGC domain driver attached, we don't need to take the
> prepare_lock in the domain power up/down paths.
> 
> We don't want to do this for regular reset clocks, as this might lead
> to some PLLs being kept prepared that could otherwise be shut down.
> For the bus clocks this isn't a concern, as all the bus clocks are
> derived from always-on system PLLs.
> 
> Signed-off-by: Lucas Stach <l.stach@pengutronix.de>

...

> @@ -1343,10 +1355,19 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
>  		regmap_update_bits(domain->regmap, domain->regs->map,
>  				   domain->bits.map, domain->bits.map);
>  
> +	if (domain->bus_clocks) {
> +		ret = clk_bulk_prepare(domain->num_clks, domain->clks);
> +		if (ret) {
> +			dev_err(domain->dev,
> +				"Failed to prepare domain's clocks\n");
> +			goto out_domain_unmap;
> +		}
> +	}
> +
>  	ret = pm_genpd_init(&domain->genpd, NULL, true);
>  	if (ret) {
>  		dev_err(domain->dev, "Failed to init power domain\n");
> -		goto out_domain_unmap;
> +		goto out_disable_clocks;
>  	}
>  
>  	if (IS_ENABLED(CONFIG_LOCKDEP) &&
> @@ -1364,6 +1385,9 @@ static int imx_pgc_domain_probe(struct platform_device *pdev)
>  
>  out_genpd_remove:
>  	pm_genpd_remove(&domain->genpd);
> +out_disable_clocks:
> +	if (domain->bus_clocks)
> +		clk_bulk_unprepare(domain->num_clks, domain->clks);

Shouldn't the label be "out_unprepare_clocks"?

Luca
diff mbox series

Patch

diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c
index 706f852e5d87..428e2fd82f26 100644
--- a/drivers/soc/imx/gpcv2.c
+++ b/drivers/soc/imx/gpcv2.c
@@ -337,10 +337,14 @@  static int imx_pgc_power_up(struct generic_pm_domain *genpd)
 
 	reset_control_assert(domain->reset);
 
-	/* Enable reset clocks for all devices in the domain */
-	ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
+	if (!domain->bus_clocks)
+		/* Enable reset clocks for all devices in the domain */
+		ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks);
+	else
+		/* Enable bus clocks for this domain */
+		ret = clk_bulk_enable(domain->num_clks, domain->clks);
 	if (ret) {
-		dev_err(domain->dev, "failed to enable reset clocks\n");
+		dev_err(domain->dev, "failed to enable clocks\n");
 		goto out_regulator_disable;
 	}
 
@@ -402,7 +406,10 @@  static int imx_pgc_power_up(struct generic_pm_domain *genpd)
 	return 0;
 
 out_clk_disable:
-	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
+	if (!domain->bus_clocks)
+		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
+	else
+		clk_bulk_disable(domain->num_clks, domain->clks);
 out_regulator_disable:
 	if (!IS_ERR(domain->regulator))
 		regulator_disable(domain->regulator);
@@ -466,8 +473,11 @@  static int imx_pgc_power_down(struct generic_pm_domain *genpd)
 		}
 	}
 
-	/* Disable reset clocks for all devices in the domain */
-	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
+	/* Disable bus or reset clocks for all devices in the domain */
+	if (!domain->bus_clocks)
+		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
+	else
+		clk_bulk_disable(domain->num_clks, domain->clks);
 
 	if (!IS_ERR(domain->regulator)) {
 		ret = regulator_disable(domain->regulator);
@@ -486,6 +496,8 @@  static int imx_pgc_power_down(struct generic_pm_domain *genpd)
 out_clk_disable:
 	if (!domain->bus_clocks)
 		clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
+	else
+		clk_bulk_disable(domain->num_clks, domain->clks);
 
 	return ret;
 }
@@ -1343,10 +1355,19 @@  static int imx_pgc_domain_probe(struct platform_device *pdev)
 		regmap_update_bits(domain->regmap, domain->regs->map,
 				   domain->bits.map, domain->bits.map);
 
+	if (domain->bus_clocks) {
+		ret = clk_bulk_prepare(domain->num_clks, domain->clks);
+		if (ret) {
+			dev_err(domain->dev,
+				"Failed to prepare domain's clocks\n");
+			goto out_domain_unmap;
+		}
+	}
+
 	ret = pm_genpd_init(&domain->genpd, NULL, true);
 	if (ret) {
 		dev_err(domain->dev, "Failed to init power domain\n");
-		goto out_domain_unmap;
+		goto out_disable_clocks;
 	}
 
 	if (IS_ENABLED(CONFIG_LOCKDEP) &&
@@ -1364,6 +1385,9 @@  static int imx_pgc_domain_probe(struct platform_device *pdev)
 
 out_genpd_remove:
 	pm_genpd_remove(&domain->genpd);
+out_disable_clocks:
+	if (domain->bus_clocks)
+		clk_bulk_unprepare(domain->num_clks, domain->clks);
 out_domain_unmap:
 	if (domain->bits.map)
 		regmap_update_bits(domain->regmap, domain->regs->map,
@@ -1380,6 +1404,9 @@  static int imx_pgc_domain_remove(struct platform_device *pdev)
 	of_genpd_del_provider(domain->dev->of_node);
 	pm_genpd_remove(&domain->genpd);
 
+	if (domain->bus_clocks)
+		clk_bulk_unprepare(domain->num_clks, domain->clks);
+
 	if (domain->bits.map)
 		regmap_update_bits(domain->regmap, domain->regs->map,
 				   domain->bits.map, 0);