diff mbox

[v2,03/12] clk: Add a function to retrieve phase

Message ID 1409428991-2442-4-git-send-email-maxime.ripard@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Maxime Ripard Aug. 30, 2014, 8:03 p.m. UTC
The current phase API doesn't look into the actual hardware to get the phase
value, but will rather get it from a variable only set by the set_phase
function.

This will cause issue when the client driver will never call the set_phase
function, where we can end up having a reported phase that will not match what
the hardware has been programmed to by the bootloader or what phase is
programmed out of reset.

Add a new get_phase function for the drivers to implement so that we can get
this value.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/clk.c            | 17 ++++++++++++++---
 include/linux/clk-provider.h |  5 +++++
 2 files changed, 19 insertions(+), 3 deletions(-)

Comments

Hans de Goede Aug. 31, 2014, 10:15 a.m. UTC | #1
Hi,

On 08/30/2014 10:03 PM, Maxime Ripard wrote:
> The current phase API doesn't look into the actual hardware to get the phase
> value, but will rather get it from a variable only set by the set_phase
> function.
> 
> This will cause issue when the client driver will never call the set_phase
> function, where we can end up having a reported phase that will not match what
> the hardware has been programmed to by the bootloader or what phase is
> programmed out of reset.
> 
> Add a new get_phase function for the drivers to implement so that we can get
> this value.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/clk.c            | 17 ++++++++++++++---
>  include/linux/clk-provider.h |  5 +++++
>  2 files changed, 19 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index d87661af0c72..7dbceca694f1 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1797,8 +1797,8 @@ out:
>   * clk_get_phase - return the phase shift of a clock signal
>   * @clk: clock signal source
>   *
> - * Returns the phase shift of a clock node in degrees, otherwise returns
> - * -EERROR.
> + * Returns the phase shift of a clock node in degrees. Any negative
> + * values are errors.
>   */
>  int clk_get_phase(struct clk *clk)
>  {
> @@ -1808,7 +1808,18 @@ int clk_get_phase(struct clk *clk)
>  		goto out;
>  
>  	clk_prepare_lock();
> -	ret = clk->phase;
> +
> +	if (clk->phase) {
> +		ret = clk->phase;
> +		goto out_unlock;
> +	}

0 is a valid phase, so this will cause the phase to
be read from the hardware each time if the phase is 0.

Perhaps make clk->phase signed (if it is not already), init it
to -1, and check for it not being -1 ?

Regards,

Hans


> +
> +	if (!clk->ops->get_phase)
> +		goto out_unlock;
> +
> +	ret = clk->ops->get_phase(clk->hw);
> +
> +out_unlock:
>  	clk_prepare_unlock();
>  
>  out:
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 69b20d4c1e1a..abec961092a7 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -130,6 +130,10 @@ struct dentry;
>   *		set then clock accuracy will be initialized to parent accuracy
>   *		or 0 (perfect clock) if clock has no parent.
>   *
> + * @get_phase:	Queries the hardware to get the current phase of a clock.
> + *		Returned values are 0-359 degrees on success, negative
> + *		error codes on failure.
> + *
>   * @set_phase:	Shift the phase this clock signal in degrees specified
>   *		by the second argument. Valid values for degrees are
>   *		0-359. Return 0 on success, otherwise -EERROR.
> @@ -182,6 +186,7 @@ struct clk_ops {
>  				    unsigned long parent_rate, u8 index);
>  	unsigned long	(*recalc_accuracy)(struct clk_hw *hw,
>  					   unsigned long parent_accuracy);
> +	int		(*get_phase)(struct clk_hw *hw);
>  	int		(*set_phase)(struct clk_hw *hw, int degrees);
>  	void		(*init)(struct clk_hw *hw);
>  	int		(*debug_init)(struct clk_hw *hw, struct dentry *dentry);
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Maxime Ripard Sept. 1, 2014, 10:20 a.m. UTC | #2
Hi,

On Sun, Aug 31, 2014 at 12:15:50PM +0200, Hans de Goede wrote:
> Hi,
> 
> On 08/30/2014 10:03 PM, Maxime Ripard wrote:
> > The current phase API doesn't look into the actual hardware to get the phase
> > value, but will rather get it from a variable only set by the set_phase
> > function.
> > 
> > This will cause issue when the client driver will never call the set_phase
> > function, where we can end up having a reported phase that will not match what
> > the hardware has been programmed to by the bootloader or what phase is
> > programmed out of reset.
> > 
> > Add a new get_phase function for the drivers to implement so that we can get
> > this value.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> >  drivers/clk/clk.c            | 17 ++++++++++++++---
> >  include/linux/clk-provider.h |  5 +++++
> >  2 files changed, 19 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> > index d87661af0c72..7dbceca694f1 100644
> > --- a/drivers/clk/clk.c
> > +++ b/drivers/clk/clk.c
> > @@ -1797,8 +1797,8 @@ out:
> >   * clk_get_phase - return the phase shift of a clock signal
> >   * @clk: clock signal source
> >   *
> > - * Returns the phase shift of a clock node in degrees, otherwise returns
> > - * -EERROR.
> > + * Returns the phase shift of a clock node in degrees. Any negative
> > + * values are errors.
> >   */
> >  int clk_get_phase(struct clk *clk)
> >  {
> > @@ -1808,7 +1808,18 @@ int clk_get_phase(struct clk *clk)
> >  		goto out;
> >  
> >  	clk_prepare_lock();
> > -	ret = clk->phase;
> > +
> > +	if (clk->phase) {
> > +		ret = clk->phase;
> > +		goto out_unlock;
> > +	}
> 
> 0 is a valid phase, so this will cause the phase to
> be read from the hardware each time if the phase is 0.
> 
> Perhaps make clk->phase signed (if it is not already), init it
> to -1, and check for it not being -1 ?

Yeah, I'm not really proud of this code either, but yours would expose
this -1 into debugfs, so I'm not sure it's really better :) 

(with -1 being a valid phase too)

Maxime
Hans de Goede Sept. 1, 2014, 11:27 a.m. UTC | #3
Hi,

On 09/01/2014 12:20 PM, Maxime Ripard wrote:
> Hi,
> 
> On Sun, Aug 31, 2014 at 12:15:50PM +0200, Hans de Goede wrote:
>> Hi,
>>
>> On 08/30/2014 10:03 PM, Maxime Ripard wrote:
>>> The current phase API doesn't look into the actual hardware to get the phase
>>> value, but will rather get it from a variable only set by the set_phase
>>> function.
>>>
>>> This will cause issue when the client driver will never call the set_phase
>>> function, where we can end up having a reported phase that will not match what
>>> the hardware has been programmed to by the bootloader or what phase is
>>> programmed out of reset.
>>>
>>> Add a new get_phase function for the drivers to implement so that we can get
>>> this value.
>>>
>>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>>> ---
>>>  drivers/clk/clk.c            | 17 ++++++++++++++---
>>>  include/linux/clk-provider.h |  5 +++++
>>>  2 files changed, 19 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
>>> index d87661af0c72..7dbceca694f1 100644
>>> --- a/drivers/clk/clk.c
>>> +++ b/drivers/clk/clk.c
>>> @@ -1797,8 +1797,8 @@ out:
>>>   * clk_get_phase - return the phase shift of a clock signal
>>>   * @clk: clock signal source
>>>   *
>>> - * Returns the phase shift of a clock node in degrees, otherwise returns
>>> - * -EERROR.
>>> + * Returns the phase shift of a clock node in degrees. Any negative
>>> + * values are errors.
>>>   */
>>>  int clk_get_phase(struct clk *clk)
>>>  {
>>> @@ -1808,7 +1808,18 @@ int clk_get_phase(struct clk *clk)
>>>  		goto out;
>>>  
>>>  	clk_prepare_lock();
>>> -	ret = clk->phase;
>>> +
>>> +	if (clk->phase) {
>>> +		ret = clk->phase;
>>> +		goto out_unlock;
>>> +	}
>>
>> 0 is a valid phase, so this will cause the phase to
>> be read from the hardware each time if the phase is 0.
>>
>> Perhaps make clk->phase signed (if it is not already), init it
>> to -1, and check for it not being -1 ?
> 
> Yeah, I'm not really proud of this code either, but yours would expose
> this -1 into debugfs, so I'm not sure it's really better :) 
> 
> (with -1 being a valid phase too)

According to the comments in the patch adding the original phase functions
valid values are 0 - 359. And you could use clk_get_phase from the debugfs
code.

Regards,

Hans
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mike Turquette Sept. 1, 2014, 7 p.m. UTC | #4
Quoting Maxime Ripard (2014-08-30 13:03:02)
> The current phase API doesn't look into the actual hardware to get the phase
> value, but will rather get it from a variable only set by the set_phase
> function.
> 
> This will cause issue when the client driver will never call the set_phase
> function, where we can end up having a reported phase that will not match what
> the hardware has been programmed to by the bootloader or what phase is
> programmed out of reset.
> 
> Add a new get_phase function for the drivers to implement so that we can get
> this value.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/clk/clk.c            | 17 ++++++++++++++---
>  include/linux/clk-provider.h |  5 +++++
>  2 files changed, 19 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index d87661af0c72..7dbceca694f1 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1797,8 +1797,8 @@ out:
>   * clk_get_phase - return the phase shift of a clock signal
>   * @clk: clock signal source
>   *
> - * Returns the phase shift of a clock node in degrees, otherwise returns
> - * -EERROR.
> + * Returns the phase shift of a clock node in degrees. Any negative
> + * values are errors.
>   */
>  int clk_get_phase(struct clk *clk)
>  {
> @@ -1808,7 +1808,18 @@ int clk_get_phase(struct clk *clk)
>                 goto out;
>  
>         clk_prepare_lock();
> -       ret = clk->phase;
> +
> +       if (clk->phase) {

So if a phase is zero, then we fall through and query the hardware?
Seems like this case might be hit a lot for clocks that implement
.get_phase but never have their phase shifted, and accesses to the
registers may be much slower than to memory.

Do you expect the phase to be changed behind our backs? E.g. firmware
changes it, or coming in and out of idle state, etc. If not then we can
store the phase at clock registration time and use a cached value.

See how the CLK_GET_RATE_NOCACHE and CLK_GET_ACCURACY_NOCACHE flags are
used for details on how we can handle the case where the phase might
change behind our back.

Regards,
Mike

> +               ret = clk->phase;
> +               goto out_unlock;
> +       }
> +
> +       if (!clk->ops->get_phase)
> +               goto out_unlock;
> +
> +       ret = clk->ops->get_phase(clk->hw);
> +
> +out_unlock:
>         clk_prepare_unlock();
>  
>  out:
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 69b20d4c1e1a..abec961092a7 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -130,6 +130,10 @@ struct dentry;
>   *             set then clock accuracy will be initialized to parent accuracy
>   *             or 0 (perfect clock) if clock has no parent.
>   *
> + * @get_phase: Queries the hardware to get the current phase of a clock.
> + *             Returned values are 0-359 degrees on success, negative
> + *             error codes on failure.
> + *
>   * @set_phase: Shift the phase this clock signal in degrees specified
>   *             by the second argument. Valid values for degrees are
>   *             0-359. Return 0 on success, otherwise -EERROR.
> @@ -182,6 +186,7 @@ struct clk_ops {
>                                     unsigned long parent_rate, u8 index);
>         unsigned long   (*recalc_accuracy)(struct clk_hw *hw,
>                                            unsigned long parent_accuracy);
> +       int             (*get_phase)(struct clk_hw *hw);
>         int             (*set_phase)(struct clk_hw *hw, int degrees);
>         void            (*init)(struct clk_hw *hw);
>         int             (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
> -- 
> 2.0.2
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Maxime Ripard Sept. 2, 2014, 9:34 a.m. UTC | #5
Hi,

On Mon, Sep 01, 2014 at 12:00:54PM -0700, Mike Turquette wrote:
> Quoting Maxime Ripard (2014-08-30 13:03:02)
> > The current phase API doesn't look into the actual hardware to get the phase
> > value, but will rather get it from a variable only set by the set_phase
> > function.
> > 
> > This will cause issue when the client driver will never call the set_phase
> > function, where we can end up having a reported phase that will not match what
> > the hardware has been programmed to by the bootloader or what phase is
> > programmed out of reset.
> > 
> > Add a new get_phase function for the drivers to implement so that we can get
> > this value.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> >  drivers/clk/clk.c            | 17 ++++++++++++++---
> >  include/linux/clk-provider.h |  5 +++++
> >  2 files changed, 19 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> > index d87661af0c72..7dbceca694f1 100644
> > --- a/drivers/clk/clk.c
> > +++ b/drivers/clk/clk.c
> > @@ -1797,8 +1797,8 @@ out:
> >   * clk_get_phase - return the phase shift of a clock signal
> >   * @clk: clock signal source
> >   *
> > - * Returns the phase shift of a clock node in degrees, otherwise returns
> > - * -EERROR.
> > + * Returns the phase shift of a clock node in degrees. Any negative
> > + * values are errors.
> >   */
> >  int clk_get_phase(struct clk *clk)
> >  {
> > @@ -1808,7 +1808,18 @@ int clk_get_phase(struct clk *clk)
> >                 goto out;
> >  
> >         clk_prepare_lock();
> > -       ret = clk->phase;
> > +
> > +       if (clk->phase) {
> 
> So if a phase is zero, then we fall through and query the hardware?
> Seems like this case might be hit a lot for clocks that implement
> .get_phase but never have their phase shifted, and accesses to the
> registers may be much slower than to memory.

Yep, which is exactly why I'm not that proud of it :)

> Do you expect the phase to be changed behind our backs? E.g. firmware
> changes it, or coming in and out of idle state, etc. If not then we can
> store the phase at clock registration time and use a cached value.

This is exactly what happens in our case. Since in order to use the
MMC, you have to change the phase of the output and sample clocks,
every time the firmware will have to use the MMC, it will change these
clocks phase. Which happens pretty much all the time.

> See how the CLK_GET_RATE_NOCACHE and CLK_GET_ACCURACY_NOCACHE flags are
> used for details on how we can handle the case where the phase might
> change behind our back.

Ah, yes, of course. Thanks!

Maxime
Maxime Ripard Sept. 2, 2014, 9:50 a.m. UTC | #6
On Mon, Sep 01, 2014 at 01:27:28PM +0200, Hans de Goede wrote:
> Hi,
> 
> On 09/01/2014 12:20 PM, Maxime Ripard wrote:
> > Hi,
> > 
> > On Sun, Aug 31, 2014 at 12:15:50PM +0200, Hans de Goede wrote:
> >> Hi,
> >>
> >> On 08/30/2014 10:03 PM, Maxime Ripard wrote:
> >>> The current phase API doesn't look into the actual hardware to get the phase
> >>> value, but will rather get it from a variable only set by the set_phase
> >>> function.
> >>>
> >>> This will cause issue when the client driver will never call the set_phase
> >>> function, where we can end up having a reported phase that will not match what
> >>> the hardware has been programmed to by the bootloader or what phase is
> >>> programmed out of reset.
> >>>
> >>> Add a new get_phase function for the drivers to implement so that we can get
> >>> this value.
> >>>
> >>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> >>> ---
> >>>  drivers/clk/clk.c            | 17 ++++++++++++++---
> >>>  include/linux/clk-provider.h |  5 +++++
> >>>  2 files changed, 19 insertions(+), 3 deletions(-)
> >>>
> >>> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> >>> index d87661af0c72..7dbceca694f1 100644
> >>> --- a/drivers/clk/clk.c
> >>> +++ b/drivers/clk/clk.c
> >>> @@ -1797,8 +1797,8 @@ out:
> >>>   * clk_get_phase - return the phase shift of a clock signal
> >>>   * @clk: clock signal source
> >>>   *
> >>> - * Returns the phase shift of a clock node in degrees, otherwise returns
> >>> - * -EERROR.
> >>> + * Returns the phase shift of a clock node in degrees. Any negative
> >>> + * values are errors.
> >>>   */
> >>>  int clk_get_phase(struct clk *clk)
> >>>  {
> >>> @@ -1808,7 +1808,18 @@ int clk_get_phase(struct clk *clk)
> >>>  		goto out;
> >>>  
> >>>  	clk_prepare_lock();
> >>> -	ret = clk->phase;
> >>> +
> >>> +	if (clk->phase) {
> >>> +		ret = clk->phase;
> >>> +		goto out_unlock;
> >>> +	}
> >>
> >> 0 is a valid phase, so this will cause the phase to
> >> be read from the hardware each time if the phase is 0.
> >>
> >> Perhaps make clk->phase signed (if it is not already), init it
> >> to -1, and check for it not being -1 ?
> > 
> > Yeah, I'm not really proud of this code either, but yours would expose
> > this -1 into debugfs, so I'm not sure it's really better :) 
> > 
> > (with -1 being a valid phase too)
> 
> According to the comments in the patch adding the original phase functions
> valid values are 0 - 359. And you could use clk_get_phase from the debugfs
> code.

Yep, except that if I see a value -1 as a phase, I would assume that
it's 359, but maybe it's just me :)

Mike's solution seem great, I'll go for that.

Maxime
David Lanzendörfer Sept. 2, 2014, 11:03 a.m. UTC | #7
Hi
> Yep, except that if I see a value -1 as a phase, I would assume that
> it's 359, but maybe it's just me :)
-1 looks suspiciously like an error return value from somewhere...
diff mbox

Patch

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index d87661af0c72..7dbceca694f1 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1797,8 +1797,8 @@  out:
  * clk_get_phase - return the phase shift of a clock signal
  * @clk: clock signal source
  *
- * Returns the phase shift of a clock node in degrees, otherwise returns
- * -EERROR.
+ * Returns the phase shift of a clock node in degrees. Any negative
+ * values are errors.
  */
 int clk_get_phase(struct clk *clk)
 {
@@ -1808,7 +1808,18 @@  int clk_get_phase(struct clk *clk)
 		goto out;
 
 	clk_prepare_lock();
-	ret = clk->phase;
+
+	if (clk->phase) {
+		ret = clk->phase;
+		goto out_unlock;
+	}
+
+	if (!clk->ops->get_phase)
+		goto out_unlock;
+
+	ret = clk->ops->get_phase(clk->hw);
+
+out_unlock:
 	clk_prepare_unlock();
 
 out:
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 69b20d4c1e1a..abec961092a7 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -130,6 +130,10 @@  struct dentry;
  *		set then clock accuracy will be initialized to parent accuracy
  *		or 0 (perfect clock) if clock has no parent.
  *
+ * @get_phase:	Queries the hardware to get the current phase of a clock.
+ *		Returned values are 0-359 degrees on success, negative
+ *		error codes on failure.
+ *
  * @set_phase:	Shift the phase this clock signal in degrees specified
  *		by the second argument. Valid values for degrees are
  *		0-359. Return 0 on success, otherwise -EERROR.
@@ -182,6 +186,7 @@  struct clk_ops {
 				    unsigned long parent_rate, u8 index);
 	unsigned long	(*recalc_accuracy)(struct clk_hw *hw,
 					   unsigned long parent_accuracy);
+	int		(*get_phase)(struct clk_hw *hw);
 	int		(*set_phase)(struct clk_hw *hw, int degrees);
 	void		(*init)(struct clk_hw *hw);
 	int		(*debug_init)(struct clk_hw *hw, struct dentry *dentry);