diff mbox series

[v4,65/68] clk: tegra: super: Switch to determine_rate

Message ID 20221018-clk-range-checks-fixes-v4-65-971d5077e7d2@cerno.tech
State Handled Elsewhere
Headers show
Series clk: Make determine_rate mandatory for muxes | expand

Commit Message

Maxime Ripard May 5, 2023, 11:26 a.m. UTC
The Tegra super clocks implements a mux with a set_parent hook, but
doesn't provide a determine_rate implementation.

This is a bit odd, since set_parent() is there to, as its name implies,
change the parent of a clock. However, the most likely candidate to
trigger that parent change is a call to clk_set_rate(), with
determine_rate() figuring out which parent is the best suited for a
given rate.

The other trigger would be a call to clk_set_parent(), but it's far less
used, and it doesn't look like there's any obvious user for that clock.

So, the set_parent hook is effectively unused, possibly because of an
oversight. However, it could also be an explicit decision by the
original author to avoid any reparenting but through an explicit call to
clk_set_parent().

The driver does implement round_rate() though, which means that we can
change the rate of the clock, but we will never get to change the
parent.

However, It's hard to tell whether it's been done on purpose or not.

Since we'll start mandating a determine_rate() implementation, let's
convert the round_rate() implementation to a determine_rate(), which
will also make the current behavior explicit. And if it was an
oversight, the clock behaviour can be adjusted later on.

Cc: Jonathan Hunter <jonathanh@nvidia.com>
Cc: Peter De Schrijver <pdeschrijver@nvidia.com>
Cc: Prashant Gaikwad <pgaikwad@nvidia.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: linux-tegra@vger.kernel.org
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/clk/tegra/clk-super.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

Comments

Dmitry Osipenko June 18, 2023, 11:38 p.m. UTC | #1
05.05.2023 14:26, Maxime Ripard пишет:
> The Tegra super clocks implements a mux with a set_parent hook, but
> doesn't provide a determine_rate implementation.
> 
> This is a bit odd, since set_parent() is there to, as its name implies,
> change the parent of a clock. However, the most likely candidate to
> trigger that parent change is a call to clk_set_rate(), with
> determine_rate() figuring out which parent is the best suited for a
> given rate.
> 
> The other trigger would be a call to clk_set_parent(), but it's far less
> used, and it doesn't look like there's any obvious user for that clock.
> 
> So, the set_parent hook is effectively unused, possibly because of an
> oversight. However, it could also be an explicit decision by the
> original author to avoid any reparenting but through an explicit call to
> clk_set_parent().
> 
> The driver does implement round_rate() though, which means that we can
> change the rate of the clock, but we will never get to change the
> parent.
> 
> However, It's hard to tell whether it's been done on purpose or not.
> 
> Since we'll start mandating a determine_rate() implementation, let's
> convert the round_rate() implementation to a determine_rate(), which
> will also make the current behavior explicit. And if it was an
> oversight, the clock behaviour can be adjusted later on.
> 
> Cc: Jonathan Hunter <jonathanh@nvidia.com>
> Cc: Peter De Schrijver <pdeschrijver@nvidia.com>
> Cc: Prashant Gaikwad <pgaikwad@nvidia.com>
> Cc: Thierry Reding <thierry.reding@gmail.com>
> Cc: linux-tegra@vger.kernel.org
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>  drivers/clk/tegra/clk-super.c | 15 +++++++++++----
>  1 file changed, 11 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> index 3f3a7a203c5f..7ec47942720c 100644
> --- a/drivers/clk/tegra/clk-super.c
> +++ b/drivers/clk/tegra/clk-super.c
> @@ -142,15 +142,22 @@ static const struct clk_ops tegra_clk_super_mux_ops = {
>  	.restore_context = clk_super_mux_restore_context,
>  };
>  
> -static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
> -				 unsigned long *parent_rate)
> +static int clk_super_determine_rate(struct clk_hw *hw,
> +				    struct clk_rate_request *req)
>  {
>  	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
>  	struct clk_hw *div_hw = &super->frac_div.hw;
> +	unsigned long rate;
>  
>  	__clk_hw_set_clk(div_hw, hw);
>  
> -	return super->div_ops->round_rate(div_hw, rate, parent_rate);
> +	rate = super->div_ops->round_rate(div_hw, req->rate,
> +					  &req->best_parent_rate);
> +	if (rate < 0)
> +		return rate;
> +
> +	req->rate = rate;
> +	return 0;
>  }
>  
>  static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
> @@ -193,7 +200,7 @@ const struct clk_ops tegra_clk_super_ops = {
>  	.get_parent = clk_super_get_parent,
>  	.set_parent = clk_super_set_parent,
>  	.set_rate = clk_super_set_rate,
> -	.round_rate = clk_super_round_rate,
> +	.determine_rate = clk_super_determine_rate,
>  	.recalc_rate = clk_super_recalc_rate,
>  	.restore_context = clk_super_restore_context,
>  };
> 

Tegra30 doesn't boot anymore with this change. Best would be to keep the
old behaviour for both sclk and periph tegra clocks.
Maxime Ripard June 19, 2023, 7:26 a.m. UTC | #2
Hi Dmitry,

On Mon, Jun 19, 2023 at 02:38:59AM +0300, Dmitry Osipenko wrote:
> 05.05.2023 14:26, Maxime Ripard пишет:
> > The Tegra super clocks implements a mux with a set_parent hook, but
> > doesn't provide a determine_rate implementation.
> > 
> > This is a bit odd, since set_parent() is there to, as its name implies,
> > change the parent of a clock. However, the most likely candidate to
> > trigger that parent change is a call to clk_set_rate(), with
> > determine_rate() figuring out which parent is the best suited for a
> > given rate.
> > 
> > The other trigger would be a call to clk_set_parent(), but it's far less
> > used, and it doesn't look like there's any obvious user for that clock.
> > 
> > So, the set_parent hook is effectively unused, possibly because of an
> > oversight. However, it could also be an explicit decision by the
> > original author to avoid any reparenting but through an explicit call to
> > clk_set_parent().
> > 
> > The driver does implement round_rate() though, which means that we can
> > change the rate of the clock, but we will never get to change the
> > parent.
> > 
> > However, It's hard to tell whether it's been done on purpose or not.
> > 
> > Since we'll start mandating a determine_rate() implementation, let's
> > convert the round_rate() implementation to a determine_rate(), which
> > will also make the current behavior explicit. And if it was an
> > oversight, the clock behaviour can be adjusted later on.
> > 
> > Cc: Jonathan Hunter <jonathanh@nvidia.com>
> > Cc: Peter De Schrijver <pdeschrijver@nvidia.com>
> > Cc: Prashant Gaikwad <pgaikwad@nvidia.com>
> > Cc: Thierry Reding <thierry.reding@gmail.com>
> > Cc: linux-tegra@vger.kernel.org
> > Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> > ---
> >  drivers/clk/tegra/clk-super.c | 15 +++++++++++----
> >  1 file changed, 11 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> > index 3f3a7a203c5f..7ec47942720c 100644
> > --- a/drivers/clk/tegra/clk-super.c
> > +++ b/drivers/clk/tegra/clk-super.c
> > @@ -142,15 +142,22 @@ static const struct clk_ops tegra_clk_super_mux_ops = {
> >  	.restore_context = clk_super_mux_restore_context,
> >  };
> >  
> > -static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
> > -				 unsigned long *parent_rate)
> > +static int clk_super_determine_rate(struct clk_hw *hw,
> > +				    struct clk_rate_request *req)
> >  {
> >  	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
> >  	struct clk_hw *div_hw = &super->frac_div.hw;
> > +	unsigned long rate;
> >  
> >  	__clk_hw_set_clk(div_hw, hw);
> >  
> > -	return super->div_ops->round_rate(div_hw, rate, parent_rate);
> > +	rate = super->div_ops->round_rate(div_hw, req->rate,
> > +					  &req->best_parent_rate);
> > +	if (rate < 0)
> > +		return rate;
> > +
> > +	req->rate = rate;
> > +	return 0;
> >  }
> >  
> >  static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
> > @@ -193,7 +200,7 @@ const struct clk_ops tegra_clk_super_ops = {
> >  	.get_parent = clk_super_get_parent,
> >  	.set_parent = clk_super_set_parent,
> >  	.set_rate = clk_super_set_rate,
> > -	.round_rate = clk_super_round_rate,
> > +	.determine_rate = clk_super_determine_rate,
> >  	.recalc_rate = clk_super_recalc_rate,
> >  	.restore_context = clk_super_restore_context,
> >  };
> > 
> 
> Tegra30 doesn't boot anymore with this change. Best would be to keep the
> old behaviour for both sclk and periph tegra clocks.

I took a closer look at the patch and can't find anything different to
what the core is doing if there's a round_rate implementation:
https://elixir.bootlin.com/linux/latest/source/drivers/clk/clk.c#L1459

Also, it's not clear to me how that driver is used. It looks like
div_ops is always supposed to be set, and super clocks are registered
with either tegra_clk_register_super_clk() or tegra_clk_register_super_mux()

tegra_clk_register_super_clk() sets the div_ops pointer to
tegra_clk_super_ops, but tegra30 doesn't seem to call it.

tegra_clk_register_super_mux() doesn't set the div_ops pointer, but is
used by tegra30, so I would assume that it's the broken one. But I'm
confused, since div_ops doesn't seem to be set anywhere?

Maxime
Stephen Boyd June 20, 2023, 7:09 p.m. UTC | #3
Quoting Maxime Ripard (2023-06-19 00:26:19)
> On Mon, Jun 19, 2023 at 02:38:59AM +0300, Dmitry Osipenko wrote:
> > 05.05.2023 14:26, Maxime Ripard пишет:
> > > 
> > > diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> > > index 3f3a7a203c5f..7ec47942720c 100644
> > > --- a/drivers/clk/tegra/clk-super.c
> > > +++ b/drivers/clk/tegra/clk-super.c
> > > @@ -142,15 +142,22 @@ static const struct clk_ops tegra_clk_super_mux_ops = {
> > >     .restore_context = clk_super_mux_restore_context,
> > >  };
> > >  
> > > -static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
> > > -                            unsigned long *parent_rate)
> > > +static int clk_super_determine_rate(struct clk_hw *hw,
> > > +                               struct clk_rate_request *req)
> > >  {
> > >     struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
> > >     struct clk_hw *div_hw = &super->frac_div.hw;
> > > +   unsigned long rate;
> > >  
> > >     __clk_hw_set_clk(div_hw, hw);
> > >  
> > > -   return super->div_ops->round_rate(div_hw, rate, parent_rate);
> > > +   rate = super->div_ops->round_rate(div_hw, req->rate,
> > > +                                     &req->best_parent_rate);
> > > +   if (rate < 0)

There's the report that this condition is never possible. Maybe the
previous code was relying on an error value sometimes. Can we add
determine_rate to the div_ops and simplify this code? I asked on the
list for that earlier.
Thierry Reding June 21, 2023, 3:35 p.m. UTC | #4
On Tue, Jun 20, 2023 at 12:09:09PM -0700, Stephen Boyd wrote:
> Quoting Maxime Ripard (2023-06-19 00:26:19)
> > On Mon, Jun 19, 2023 at 02:38:59AM +0300, Dmitry Osipenko wrote:
> > > 05.05.2023 14:26, Maxime Ripard пишет:
> > > > 
> > > > diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> > > > index 3f3a7a203c5f..7ec47942720c 100644
> > > > --- a/drivers/clk/tegra/clk-super.c
> > > > +++ b/drivers/clk/tegra/clk-super.c
> > > > @@ -142,15 +142,22 @@ static const struct clk_ops tegra_clk_super_mux_ops = {
> > > >     .restore_context = clk_super_mux_restore_context,
> > > >  };
> > > >  
> > > > -static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
> > > > -                            unsigned long *parent_rate)
> > > > +static int clk_super_determine_rate(struct clk_hw *hw,
> > > > +                               struct clk_rate_request *req)
> > > >  {
> > > >     struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
> > > >     struct clk_hw *div_hw = &super->frac_div.hw;
> > > > +   unsigned long rate;
> > > >  
> > > >     __clk_hw_set_clk(div_hw, hw);
> > > >  
> > > > -   return super->div_ops->round_rate(div_hw, rate, parent_rate);
> > > > +   rate = super->div_ops->round_rate(div_hw, req->rate,
> > > > +                                     &req->best_parent_rate);
> > > > +   if (rate < 0)
> 
> There's the report that this condition is never possible. Maybe the
> previous code was relying on an error value sometimes. Can we add
> determine_rate to the div_ops and simplify this code? I asked on the
> list for that earlier.

I was able to reproduce this on a Tegra30 Beaver, but the problem is
more straightforward than this. The crash I was seeing during boot was
because cclk_super_determine_rate() was still calling the round_rate()
callback from tegra_clk_super_ops, which this patch removed (and added
determine_rate() instead).

The following fixes the problem for me. It's basically converting the
round_rate() call to an equivalent determine_rate() call.

Dmitry, can you verify that this fixes the issue that you were seeing?

Thierry

--- >8 ---
diff --git a/drivers/clk/tegra/clk-tegra-super-cclk.c b/drivers/clk/tegra/clk-tegra-super-cclk.c
index 68d7bcd5fc8a..8a2bb4ae4fd2 100644
--- a/drivers/clk/tegra/clk-tegra-super-cclk.c
+++ b/drivers/clk/tegra/clk-tegra-super-cclk.c
@@ -86,9 +86,16 @@ static int cclk_super_determine_rate(struct clk_hw *hw,
 	if (rate <= pllp_rate) {
 		if (super->flags & TEGRA20_SUPER_CLK)
 			rate = pllp_rate;
-		else
-			rate = tegra_clk_super_ops.round_rate(hw, rate,
-							      &pllp_rate);
+		else {
+			struct clk_rate_request parent = {
+				.rate = req->rate,
+				.best_parent_rate = pllp_rate,
+			};
+
+			tegra_clk_super_ops.determine_rate(hw, &parent);
+			pllp_rate = parent.best_parent_rate;
+			rate = parent.rate;
+		}
 
 		req->best_parent_rate = pllp_rate;
 		req->best_parent_hw = pllp_hw;
--- >8 ---
Maxime Ripard June 22, 2023, 11:24 a.m. UTC | #5
Hi,

On Wed, Jun 21, 2023 at 05:35:09PM +0200, Thierry Reding wrote:
> On Tue, Jun 20, 2023 at 12:09:09PM -0700, Stephen Boyd wrote:
> > Quoting Maxime Ripard (2023-06-19 00:26:19)
> > > On Mon, Jun 19, 2023 at 02:38:59AM +0300, Dmitry Osipenko wrote:
> > > > 05.05.2023 14:26, Maxime Ripard пишет:
> > > > > 
> > > > > diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> > > > > index 3f3a7a203c5f..7ec47942720c 100644
> > > > > --- a/drivers/clk/tegra/clk-super.c
> > > > > +++ b/drivers/clk/tegra/clk-super.c
> > > > > @@ -142,15 +142,22 @@ static const struct clk_ops tegra_clk_super_mux_ops = {
> > > > >     .restore_context = clk_super_mux_restore_context,
> > > > >  };
> > > > >  
> > > > > -static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
> > > > > -                            unsigned long *parent_rate)
> > > > > +static int clk_super_determine_rate(struct clk_hw *hw,
> > > > > +                               struct clk_rate_request *req)
> > > > >  {
> > > > >     struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
> > > > >     struct clk_hw *div_hw = &super->frac_div.hw;
> > > > > +   unsigned long rate;
> > > > >  
> > > > >     __clk_hw_set_clk(div_hw, hw);
> > > > >  
> > > > > -   return super->div_ops->round_rate(div_hw, rate, parent_rate);
> > > > > +   rate = super->div_ops->round_rate(div_hw, req->rate,
> > > > > +                                     &req->best_parent_rate);
> > > > > +   if (rate < 0)
> > 
> > There's the report that this condition is never possible. Maybe the
> > previous code was relying on an error value sometimes. Can we add
> > determine_rate to the div_ops and simplify this code? I asked on the
> > list for that earlier.
> 
> I was able to reproduce this on a Tegra30 Beaver, but the problem is
> more straightforward than this. The crash I was seeing during boot was
> because cclk_super_determine_rate() was still calling the round_rate()
> callback from tegra_clk_super_ops, which this patch removed (and added
> determine_rate() instead).
> 
> The following fixes the problem for me. It's basically converting the
> round_rate() call to an equivalent determine_rate() call.

Thanks for figuring it out :)

> Dmitry, can you verify that this fixes the issue that you were seeing?
> 
> Thierry
> 
> --- >8 ---
> diff --git a/drivers/clk/tegra/clk-tegra-super-cclk.c b/drivers/clk/tegra/clk-tegra-super-cclk.c
> index 68d7bcd5fc8a..8a2bb4ae4fd2 100644
> --- a/drivers/clk/tegra/clk-tegra-super-cclk.c
> +++ b/drivers/clk/tegra/clk-tegra-super-cclk.c
> @@ -86,9 +86,16 @@ static int cclk_super_determine_rate(struct clk_hw *hw,
>  	if (rate <= pllp_rate) {
>  		if (super->flags & TEGRA20_SUPER_CLK)
>  			rate = pllp_rate;
> -		else
> -			rate = tegra_clk_super_ops.round_rate(hw, rate,
> -							      &pllp_rate);
> +		else {
> +			struct clk_rate_request parent = {
> +				.rate = req->rate,
> +				.best_parent_rate = pllp_rate,
> +			};

If it works and you submit a patch later, this needs to be changed to
clk_hw_init_rate_request()

Maxime
Dmitry Osipenko June 22, 2023, 11:32 a.m. UTC | #6
21.06.2023 18:35, Thierry Reding пишет:
> On Tue, Jun 20, 2023 at 12:09:09PM -0700, Stephen Boyd wrote:
>> Quoting Maxime Ripard (2023-06-19 00:26:19)
>>> On Mon, Jun 19, 2023 at 02:38:59AM +0300, Dmitry Osipenko wrote:
>>>> 05.05.2023 14:26, Maxime Ripard пишет:
>>>>>
>>>>> diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
>>>>> index 3f3a7a203c5f..7ec47942720c 100644
>>>>> --- a/drivers/clk/tegra/clk-super.c
>>>>> +++ b/drivers/clk/tegra/clk-super.c
>>>>> @@ -142,15 +142,22 @@ static const struct clk_ops tegra_clk_super_mux_ops = {
>>>>>     .restore_context = clk_super_mux_restore_context,
>>>>>  };
>>>>>  
>>>>> -static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
>>>>> -                            unsigned long *parent_rate)
>>>>> +static int clk_super_determine_rate(struct clk_hw *hw,
>>>>> +                               struct clk_rate_request *req)
>>>>>  {
>>>>>     struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
>>>>>     struct clk_hw *div_hw = &super->frac_div.hw;
>>>>> +   unsigned long rate;
>>>>>  
>>>>>     __clk_hw_set_clk(div_hw, hw);
>>>>>  
>>>>> -   return super->div_ops->round_rate(div_hw, rate, parent_rate);
>>>>> +   rate = super->div_ops->round_rate(div_hw, req->rate,
>>>>> +                                     &req->best_parent_rate);
>>>>> +   if (rate < 0)
>>
>> There's the report that this condition is never possible. Maybe the
>> previous code was relying on an error value sometimes. Can we add
>> determine_rate to the div_ops and simplify this code? I asked on the
>> list for that earlier.
> 
> I was able to reproduce this on a Tegra30 Beaver, but the problem is
> more straightforward than this. The crash I was seeing during boot was
> because cclk_super_determine_rate() was still calling the round_rate()
> callback from tegra_clk_super_ops, which this patch removed (and added
> determine_rate() instead).
> 
> The following fixes the problem for me. It's basically converting the
> round_rate() call to an equivalent determine_rate() call.
> 
> Dmitry, can you verify that this fixes the issue that you were seeing?
> 
> Thierry
> 
> --- >8 ---
> diff --git a/drivers/clk/tegra/clk-tegra-super-cclk.c b/drivers/clk/tegra/clk-tegra-super-cclk.c
> index 68d7bcd5fc8a..8a2bb4ae4fd2 100644
> --- a/drivers/clk/tegra/clk-tegra-super-cclk.c
> +++ b/drivers/clk/tegra/clk-tegra-super-cclk.c
> @@ -86,9 +86,16 @@ static int cclk_super_determine_rate(struct clk_hw *hw,
>  	if (rate <= pllp_rate) {
>  		if (super->flags & TEGRA20_SUPER_CLK)
>  			rate = pllp_rate;
> -		else
> -			rate = tegra_clk_super_ops.round_rate(hw, rate,
> -							      &pllp_rate);
> +		else {
> +			struct clk_rate_request parent = {
> +				.rate = req->rate,
> +				.best_parent_rate = pllp_rate,
> +			};
> +
> +			tegra_clk_super_ops.determine_rate(hw, &parent);
> +			pllp_rate = parent.best_parent_rate;
> +			rate = parent.rate;
> +		}
>  
>  		req->best_parent_rate = pllp_rate;
>  		req->best_parent_hw = pllp_hw;
> --- >8 ---

Thank you for the fix, it works! Feel free to add my t-b :)
Thierry Reding June 23, 2023, 2:51 p.m. UTC | #7
On Thu, Jun 22, 2023 at 01:24:12PM +0200, Maxime Ripard wrote:
> Hi,
> 
> On Wed, Jun 21, 2023 at 05:35:09PM +0200, Thierry Reding wrote:
> > On Tue, Jun 20, 2023 at 12:09:09PM -0700, Stephen Boyd wrote:
> > > Quoting Maxime Ripard (2023-06-19 00:26:19)
> > > > On Mon, Jun 19, 2023 at 02:38:59AM +0300, Dmitry Osipenko wrote:
> > > > > 05.05.2023 14:26, Maxime Ripard пишет:
> > > > > > 
> > > > > > diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> > > > > > index 3f3a7a203c5f..7ec47942720c 100644
> > > > > > --- a/drivers/clk/tegra/clk-super.c
> > > > > > +++ b/drivers/clk/tegra/clk-super.c
> > > > > > @@ -142,15 +142,22 @@ static const struct clk_ops tegra_clk_super_mux_ops = {
> > > > > >     .restore_context = clk_super_mux_restore_context,
> > > > > >  };
> > > > > >  
> > > > > > -static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
> > > > > > -                            unsigned long *parent_rate)
> > > > > > +static int clk_super_determine_rate(struct clk_hw *hw,
> > > > > > +                               struct clk_rate_request *req)
> > > > > >  {
> > > > > >     struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
> > > > > >     struct clk_hw *div_hw = &super->frac_div.hw;
> > > > > > +   unsigned long rate;
> > > > > >  
> > > > > >     __clk_hw_set_clk(div_hw, hw);
> > > > > >  
> > > > > > -   return super->div_ops->round_rate(div_hw, rate, parent_rate);
> > > > > > +   rate = super->div_ops->round_rate(div_hw, req->rate,
> > > > > > +                                     &req->best_parent_rate);
> > > > > > +   if (rate < 0)
> > > 
> > > There's the report that this condition is never possible. Maybe the
> > > previous code was relying on an error value sometimes. Can we add
> > > determine_rate to the div_ops and simplify this code? I asked on the
> > > list for that earlier.
> > 
> > I was able to reproduce this on a Tegra30 Beaver, but the problem is
> > more straightforward than this. The crash I was seeing during boot was
> > because cclk_super_determine_rate() was still calling the round_rate()
> > callback from tegra_clk_super_ops, which this patch removed (and added
> > determine_rate() instead).
> > 
> > The following fixes the problem for me. It's basically converting the
> > round_rate() call to an equivalent determine_rate() call.
> 
> Thanks for figuring it out :)
> 
> > Dmitry, can you verify that this fixes the issue that you were seeing?
> > 
> > Thierry
> > 
> > --- >8 ---
> > diff --git a/drivers/clk/tegra/clk-tegra-super-cclk.c b/drivers/clk/tegra/clk-tegra-super-cclk.c
> > index 68d7bcd5fc8a..8a2bb4ae4fd2 100644
> > --- a/drivers/clk/tegra/clk-tegra-super-cclk.c
> > +++ b/drivers/clk/tegra/clk-tegra-super-cclk.c
> > @@ -86,9 +86,16 @@ static int cclk_super_determine_rate(struct clk_hw *hw,
> >  	if (rate <= pllp_rate) {
> >  		if (super->flags & TEGRA20_SUPER_CLK)
> >  			rate = pllp_rate;
> > -		else
> > -			rate = tegra_clk_super_ops.round_rate(hw, rate,
> > -							      &pllp_rate);
> > +		else {
> > +			struct clk_rate_request parent = {
> > +				.rate = req->rate,
> > +				.best_parent_rate = pllp_rate,
> > +			};
> 
> If it works and you submit a patch later, this needs to be changed to
> clk_hw_init_rate_request()

I've tried this and while it seems to work, this doesn't seem to be
exactly the same as what the original code does. From what I understand
the parent clock can be either pll-p or pll-x, but what we want to do in
this branch is check what a configuration would look like for pll-p as
the parent. clk_hw_init_rate_request() would initialize the request with
data for the current parent, even if that's not pll-p, so I'm a bit
hesitant to go with that instead of manually hard-coding this to pll-p.

Thierry
Maxime Ripard June 23, 2023, 3:02 p.m. UTC | #8
On Fri, Jun 23, 2023 at 04:51:27PM +0200, Thierry Reding wrote:
> On Thu, Jun 22, 2023 at 01:24:12PM +0200, Maxime Ripard wrote:
> > Hi,
> > 
> > On Wed, Jun 21, 2023 at 05:35:09PM +0200, Thierry Reding wrote:
> > > On Tue, Jun 20, 2023 at 12:09:09PM -0700, Stephen Boyd wrote:
> > > > Quoting Maxime Ripard (2023-06-19 00:26:19)
> > > > > On Mon, Jun 19, 2023 at 02:38:59AM +0300, Dmitry Osipenko wrote:
> > > > > > 05.05.2023 14:26, Maxime Ripard пишет:
> > > > > > > 
> > > > > > > diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
> > > > > > > index 3f3a7a203c5f..7ec47942720c 100644
> > > > > > > --- a/drivers/clk/tegra/clk-super.c
> > > > > > > +++ b/drivers/clk/tegra/clk-super.c
> > > > > > > @@ -142,15 +142,22 @@ static const struct clk_ops tegra_clk_super_mux_ops = {
> > > > > > >     .restore_context = clk_super_mux_restore_context,
> > > > > > >  };
> > > > > > >  
> > > > > > > -static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
> > > > > > > -                            unsigned long *parent_rate)
> > > > > > > +static int clk_super_determine_rate(struct clk_hw *hw,
> > > > > > > +                               struct clk_rate_request *req)
> > > > > > >  {
> > > > > > >     struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
> > > > > > >     struct clk_hw *div_hw = &super->frac_div.hw;
> > > > > > > +   unsigned long rate;
> > > > > > >  
> > > > > > >     __clk_hw_set_clk(div_hw, hw);
> > > > > > >  
> > > > > > > -   return super->div_ops->round_rate(div_hw, rate, parent_rate);
> > > > > > > +   rate = super->div_ops->round_rate(div_hw, req->rate,
> > > > > > > +                                     &req->best_parent_rate);
> > > > > > > +   if (rate < 0)
> > > > 
> > > > There's the report that this condition is never possible. Maybe the
> > > > previous code was relying on an error value sometimes. Can we add
> > > > determine_rate to the div_ops and simplify this code? I asked on the
> > > > list for that earlier.
> > > 
> > > I was able to reproduce this on a Tegra30 Beaver, but the problem is
> > > more straightforward than this. The crash I was seeing during boot was
> > > because cclk_super_determine_rate() was still calling the round_rate()
> > > callback from tegra_clk_super_ops, which this patch removed (and added
> > > determine_rate() instead).
> > > 
> > > The following fixes the problem for me. It's basically converting the
> > > round_rate() call to an equivalent determine_rate() call.
> > 
> > Thanks for figuring it out :)
> > 
> > > Dmitry, can you verify that this fixes the issue that you were seeing?
> > > 
> > > Thierry
> > > 
> > > --- >8 ---
> > > diff --git a/drivers/clk/tegra/clk-tegra-super-cclk.c b/drivers/clk/tegra/clk-tegra-super-cclk.c
> > > index 68d7bcd5fc8a..8a2bb4ae4fd2 100644
> > > --- a/drivers/clk/tegra/clk-tegra-super-cclk.c
> > > +++ b/drivers/clk/tegra/clk-tegra-super-cclk.c
> > > @@ -86,9 +86,16 @@ static int cclk_super_determine_rate(struct clk_hw *hw,
> > >  	if (rate <= pllp_rate) {
> > >  		if (super->flags & TEGRA20_SUPER_CLK)
> > >  			rate = pllp_rate;
> > > -		else
> > > -			rate = tegra_clk_super_ops.round_rate(hw, rate,
> > > -							      &pllp_rate);
> > > +		else {
> > > +			struct clk_rate_request parent = {
> > > +				.rate = req->rate,
> > > +				.best_parent_rate = pllp_rate,
> > > +			};
> > 
> > If it works and you submit a patch later, this needs to be changed to
> > clk_hw_init_rate_request()
> 
> I've tried this and while it seems to work, this doesn't seem to be
> exactly the same as what the original code does. From what I understand
> the parent clock can be either pll-p or pll-x, but what we want to do in
> this branch is check what a configuration would look like for pll-p as
> the parent. clk_hw_init_rate_request() would initialize the request with
> data for the current parent, even if that's not pll-p, so I'm a bit
> hesitant to go with that instead of manually hard-coding this to pll-p.

Ah, yes, sorry.

Maybe we need some kind of variant to address this then, but for the
time being you can at least set req->min_rate and req->max_rate using
clk_hw_get_rate_range.

Maxime
Stephen Boyd June 30, 2023, 4:57 a.m. UTC | #9
Quoting Thierry Reding (2023-06-21 08:35:09)
> 
> I was able to reproduce this on a Tegra30 Beaver, but the problem is
> more straightforward than this. The crash I was seeing during boot was
> because cclk_super_determine_rate() was still calling the round_rate()
> callback from tegra_clk_super_ops, which this patch removed (and added
> determine_rate() instead).
> 
> The following fixes the problem for me. It's basically converting the
> round_rate() call to an equivalent determine_rate() call.
> 
> Dmitry, can you verify that this fixes the issue that you were seeing?
> 

Can you send this as a proper patch? I'd like to send this early next
week.
diff mbox series

Patch

diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c
index 3f3a7a203c5f..7ec47942720c 100644
--- a/drivers/clk/tegra/clk-super.c
+++ b/drivers/clk/tegra/clk-super.c
@@ -142,15 +142,22 @@  static const struct clk_ops tegra_clk_super_mux_ops = {
 	.restore_context = clk_super_mux_restore_context,
 };
 
-static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate,
-				 unsigned long *parent_rate)
+static int clk_super_determine_rate(struct clk_hw *hw,
+				    struct clk_rate_request *req)
 {
 	struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
 	struct clk_hw *div_hw = &super->frac_div.hw;
+	unsigned long rate;
 
 	__clk_hw_set_clk(div_hw, hw);
 
-	return super->div_ops->round_rate(div_hw, rate, parent_rate);
+	rate = super->div_ops->round_rate(div_hw, req->rate,
+					  &req->best_parent_rate);
+	if (rate < 0)
+		return rate;
+
+	req->rate = rate;
+	return 0;
 }
 
 static unsigned long clk_super_recalc_rate(struct clk_hw *hw,
@@ -193,7 +200,7 @@  const struct clk_ops tegra_clk_super_ops = {
 	.get_parent = clk_super_get_parent,
 	.set_parent = clk_super_set_parent,
 	.set_rate = clk_super_set_rate,
-	.round_rate = clk_super_round_rate,
+	.determine_rate = clk_super_determine_rate,
 	.recalc_rate = clk_super_recalc_rate,
 	.restore_context = clk_super_restore_context,
 };