Message ID | 20250205-clk-ssc-v2-1-fa73083caa92@nxp.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | clk: Support spread spectrum and use it in clk-pll144x and clk-scmi | expand |
Hi Peng, On 25-02-05, Peng Fan (OSS) wrote: > From: Peng Fan <peng.fan@nxp.com> > > Add clk_hw_set_spread_spectrum to configure a clock to enable spread > spectrum feature. set_spread_spectrum ops is added for clk drivers to > have their own hardware specific implementation. > > Signed-off-by: Peng Fan <peng.fan@nxp.com> > --- > drivers/clk/clk.c | 34 ++++++++++++++++++++++++++++++++++ > include/linux/clk-provider.h | 32 ++++++++++++++++++++++++++++++++ > include/linux/clk.h | 22 ++++++++++++++++++++++ > 3 files changed, 88 insertions(+) > > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c > index cf7720b9172ff223d86227aad144e15375ddfd86..e11f9615e683af52c719d4c8419bd30f369f301b 100644 > --- a/drivers/clk/clk.c > +++ b/drivers/clk/clk.c > @@ -2790,6 +2790,40 @@ int clk_set_max_rate(struct clk *clk, unsigned long rate) > } > EXPORT_SYMBOL_GPL(clk_set_max_rate); > > +int clk_hw_set_spread_spectrum(struct clk_hw *hw, unsigned int modfreq, > + unsigned int spreaddepth, enum clk_ssc_method method, > + bool enable) > +{ > + struct clk_spread_spectrum clk_ss; > + struct clk_core *core; > + int ret; > + > + if (!hw) > + return 0; > + > + core = hw->core; > + > + clk_ss.modfreq = modfreq; > + clk_ss.spreaddepth = spreaddepth; > + clk_ss.method = method; > + clk_ss.enable = enable; > + > + clk_prepare_lock(); > + > + ret = clk_pm_runtime_get(core); > + if (ret) > + goto fail; > + > + if (core->ops->set_spread_spectrum) > + ret = core->ops->set_spread_spectrum(hw, &clk_ss); > + > + clk_pm_runtime_put(core); > + > +fail: > + clk_prepare_unlock(); > + return ret; > +} > + > /** > * clk_get_parent - return the parent of a clk > * @clk: the clk whose parent gets returned > diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h > index 2e6e603b749342931c0d0693c3e72b62c000791b..ac0270cc9ec133954b1f8dcffed015723bd1ff5d 100644 > --- a/include/linux/clk-provider.h > +++ b/include/linux/clk-provider.h > @@ -84,6 +84,28 @@ struct clk_duty { > unsigned int den; > }; > > +/* Aligned with dtschema/schemas/clock/clock.yaml */ > +enum clk_ssc_method { > + CLK_SSC_CENTER_SPREAD, > + CLK_SSC_UP_SPREAD, > + CLK_SSC_DOWN_SPREAD, > +}; > + > +/** > + * struct clk_spread_spectrum - Structure encoding spread spectrum of a clock > + * > + * @modfreq: Modulation frequency > + * @spreadpercent: Modulation percent > + * @method: Modulation method > + * @enable: Modulation enable or disable > + */ > +struct clk_spread_spectrum { > + unsigned int modfreq; > + unsigned int spreaddepth; Please use per mil as unit since I noticed that 0.x% is a common value too. Regards, Marco > + enum clk_ssc_method method; > + bool enable; > +}; > + > /** > * struct clk_ops - Callback operations for hardware clocks; these are to > * be provided by the clock implementation, and will be called by drivers > @@ -178,6 +200,11 @@ struct clk_duty { > * separately via calls to .set_parent and .set_rate. > * Returns 0 on success, -EERROR otherwise. > * > + * @set_spread_spectrum: Configure the modulation frequency, modulation percentage > + * and method. This callback is optional for clocks that does not > + * support spread spectrum feature or no need to enable this feature. > + * Returns 0 on success, -EERROR otherwise. > + * > * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy > * is expressed in ppb (parts per billion). The parent accuracy is > * an input parameter. > @@ -255,6 +282,8 @@ struct clk_ops { > int (*set_rate_and_parent)(struct clk_hw *hw, > unsigned long rate, > unsigned long parent_rate, u8 index); > + int (*set_spread_spectrum)(struct clk_hw *hw, > + struct clk_spread_spectrum *clk_ss); > unsigned long (*recalc_accuracy)(struct clk_hw *hw, > unsigned long parent_accuracy); > int (*get_phase)(struct clk_hw *hw); > @@ -1404,6 +1433,9 @@ void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate, > unsigned long *max_rate); > void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, > unsigned long max_rate); > +int clk_hw_set_spread_spectrum(struct clk_hw *hw, unsigned int modfreq, > + unsigned int spreaddepth, enum clk_ssc_method method, > + bool enable); > > static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) > { > diff --git a/include/linux/clk.h b/include/linux/clk.h > index b607482ca77e987b9344c38f25ebb5c8d35c1d39..49a7f7eb8b03233e11cd3b92768896c4e45c4e7c 100644 > --- a/include/linux/clk.h > +++ b/include/linux/clk.h > @@ -858,6 +858,21 @@ int clk_set_rate(struct clk *clk, unsigned long rate); > */ > int clk_set_rate_exclusive(struct clk *clk, unsigned long rate); > > +/** > + * clk_set_spread_spectrum - set the spread spectrum for a clock > + * @clk: clock source > + * @modfreq: modulation freq > + * @spreadpercent: modulation percentage > + * @method: down spread, up spread, center spread or else > + * @enable: enable or disable > + * > + * Configure the spread spectrum parameters for a clock. > + * > + * Returns success (0) or negative errno. > + */ > +int clk_set_spread_spectrum(struct clk *clk, unsigned int modfreq, > + unsigned int spreadpercent, unsigned int method, > + bool enable); > /** > * clk_has_parent - check if a clock is a possible parent for another > * @clk: clock source > @@ -1088,6 +1103,13 @@ static inline int clk_set_rate_exclusive(struct clk *clk, unsigned long rate) > return 0; > } > > +static inline int clk_set_spread_spectrum(struct clk *clk, unsigned int modfreq, > + unsigned int spreadpercent, > + unsigned int method, bool enable) > +{ > + return 0; > +} > + > static inline long clk_round_rate(struct clk *clk, unsigned long rate) > { > return 0; > > -- > 2.37.1 > > >
> Subject: Re: [PATCH v2 1/4] clk: Introduce > clk_hw_set_spread_spectrum > > Hi Peng, > > On 25-02-05, Peng Fan (OSS) wrote: > > From: Peng Fan <peng.fan@nxp.com> > > > > Add clk_hw_set_spread_spectrum to configure a clock to enable > spread > > spectrum feature. set_spread_spectrum ops is added for clk drivers to > > have their own hardware specific implementation. > > > > Signed-off-by: Peng Fan <peng.fan@nxp.com> > > --- > > drivers/clk/clk.c | 34 ++++++++++++++++++++++++++++++++++ > > include/linux/clk-provider.h | 32 > ++++++++++++++++++++++++++++++++ > > include/linux/clk.h | 22 ++++++++++++++++++++++ > > 3 files changed, 88 insertions(+) > > > > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index > > > cf7720b9172ff223d86227aad144e15375ddfd86..e11f9615e683af52c7 > 19d4c8419b > > d30f369f301b 100644 > > --- a/drivers/clk/clk.c > > +++ b/drivers/clk/clk.c > > @@ -2790,6 +2790,40 @@ int clk_set_max_rate(struct clk *clk, > unsigned > > long rate) } EXPORT_SYMBOL_GPL(clk_set_max_rate); > > > > +int clk_hw_set_spread_spectrum(struct clk_hw *hw, unsigned int > modfreq, > > + unsigned int spreaddepth, enum > clk_ssc_method method, > > + bool enable) > > +{ > > + struct clk_spread_spectrum clk_ss; > > + struct clk_core *core; > > + int ret; > > + > > + if (!hw) > > + return 0; > > + > > + core = hw->core; > > + > > + clk_ss.modfreq = modfreq; > > + clk_ss.spreaddepth = spreaddepth; > > + clk_ss.method = method; > > + clk_ss.enable = enable; > > + > > + clk_prepare_lock(); > > + > > + ret = clk_pm_runtime_get(core); > > + if (ret) > > + goto fail; > > + > > + if (core->ops->set_spread_spectrum) > > + ret = core->ops->set_spread_spectrum(hw, &clk_ss); > > + > > + clk_pm_runtime_put(core); > > + > > +fail: > > + clk_prepare_unlock(); > > + return ret; > > +} > > + > > /** > > * clk_get_parent - return the parent of a clk > > * @clk: the clk whose parent gets returned diff --git > > a/include/linux/clk-provider.h b/include/linux/clk-provider.h index > > > 2e6e603b749342931c0d0693c3e72b62c000791b..ac0270cc9ec13395 > 4b1f8dcffed0 > > 15723bd1ff5d 100644 > > --- a/include/linux/clk-provider.h > > +++ b/include/linux/clk-provider.h > > @@ -84,6 +84,28 @@ struct clk_duty { > > unsigned int den; > > }; > > > > +/* Aligned with dtschema/schemas/clock/clock.yaml */ enum > > +clk_ssc_method { > > + CLK_SSC_CENTER_SPREAD, > > + CLK_SSC_UP_SPREAD, > > + CLK_SSC_DOWN_SPREAD, > > +}; > > + > > +/** > > + * struct clk_spread_spectrum - Structure encoding spread spectrum > of > > +a clock > > + * > > + * @modfreq: Modulation frequency > > + * @spreadpercent: Modulation percent > > + * @method: Modulation method > > + * @enable: Modulation enable or disable > > + */ > > +struct clk_spread_spectrum { > > + unsigned int modfreq; > > + unsigned int spreaddepth; > > Please use per mil as unit since I noticed that 0.x% is a common value > too. I use " The modulation depth in permyriad " in dt-schema, since Dario said ST chips has permyriad. So change it to moddepth_per_myriad? Thanks, Peng. > > Regards, > Marco > > > > + enum clk_ssc_method method; > > + bool enable; > > +}; > > + > > /** > > * struct clk_ops - Callback operations for hardware clocks; these are > to > > * be provided by the clock implementation, and will be called by > > drivers @@ -178,6 +200,11 @@ struct clk_duty { > > * separately via calls to .set_parent and .set_rate. > > * Returns 0 on success, -EERROR otherwise. > > * > > + * @set_spread_spectrum: Configure the modulation frequency, > modulation percentage > > + * and method. This callback is optional for clocks that > does not > > + * support spread spectrum feature or no need to enable > this feature. > > + * Returns 0 on success, -EERROR otherwise. > > + * > > * @recalc_accuracy: Recalculate the accuracy of this clock. The > clock accuracy > > * is expressed in ppb (parts per billion). The parent > accuracy is > > * an input parameter. > > @@ -255,6 +282,8 @@ struct clk_ops { > > int (*set_rate_and_parent)(struct clk_hw *hw, > > unsigned long rate, > > unsigned long parent_rate, u8 > index); > > + int (*set_spread_spectrum)(struct clk_hw *hw, > > + struct > clk_spread_spectrum *clk_ss); > > unsigned long (*recalc_accuracy)(struct clk_hw *hw, > > unsigned long > parent_accuracy); > > int (*get_phase)(struct clk_hw *hw); > > @@ -1404,6 +1433,9 @@ void clk_hw_get_rate_range(struct clk_hw > *hw, unsigned long *min_rate, > > unsigned long *max_rate); > > void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long > min_rate, > > unsigned long max_rate); > > +int clk_hw_set_spread_spectrum(struct clk_hw *hw, unsigned int > modfreq, > > + unsigned int spreaddepth, enum > clk_ssc_method method, > > + bool enable); > > > > static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw > > *src) { diff --git a/include/linux/clk.h b/include/linux/clk.h index > > > b607482ca77e987b9344c38f25ebb5c8d35c1d39..49a7f7eb8b03233e > 11cd3b927688 > > 96c4e45c4e7c 100644 > > --- a/include/linux/clk.h > > +++ b/include/linux/clk.h > > @@ -858,6 +858,21 @@ int clk_set_rate(struct clk *clk, unsigned > long rate); > > */ > > int clk_set_rate_exclusive(struct clk *clk, unsigned long rate); > > > > +/** > > + * clk_set_spread_spectrum - set the spread spectrum for a clock > > + * @clk: clock source > > + * @modfreq: modulation freq > > + * @spreadpercent: modulation percentage > > + * @method: down spread, up spread, center spread or else > > + * @enable: enable or disable > > + * > > + * Configure the spread spectrum parameters for a clock. > > + * > > + * Returns success (0) or negative errno. > > + */ > > +int clk_set_spread_spectrum(struct clk *clk, unsigned int modfreq, > > + unsigned int spreadpercent, unsigned int > method, > > + bool enable); > > /** > > * clk_has_parent - check if a clock is a possible parent for another > > * @clk: clock source > > @@ -1088,6 +1103,13 @@ static inline int > clk_set_rate_exclusive(struct clk *clk, unsigned long rate) > > return 0; > > } > > > > +static inline int clk_set_spread_spectrum(struct clk *clk, unsigned > int modfreq, > > + unsigned int spreadpercent, > > + unsigned int method, bool > enable) { > > + return 0; > > +} > > + > > static inline long clk_round_rate(struct clk *clk, unsigned long > > rate) { > > return 0; > > > > -- > > 2.37.1 > > > > > >
On 25-02-06, Peng Fan wrote: > > Subject: Re: [PATCH v2 1/4] clk: Introduce > > clk_hw_set_spread_spectrum > > > > Hi Peng, > > > > On 25-02-05, Peng Fan (OSS) wrote: > > > From: Peng Fan <peng.fan@nxp.com> > > > > > > Add clk_hw_set_spread_spectrum to configure a clock to enable > > spread > > > spectrum feature. set_spread_spectrum ops is added for clk drivers to > > > have their own hardware specific implementation. > > > > > > Signed-off-by: Peng Fan <peng.fan@nxp.com> > > > --- > > > drivers/clk/clk.c | 34 ++++++++++++++++++++++++++++++++++ > > > include/linux/clk-provider.h | 32 > > ++++++++++++++++++++++++++++++++ > > > include/linux/clk.h | 22 ++++++++++++++++++++++ > > > 3 files changed, 88 insertions(+) > > > > > > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index > > > > > cf7720b9172ff223d86227aad144e15375ddfd86..e11f9615e683af52c7 > > 19d4c8419b > > > d30f369f301b 100644 > > > --- a/drivers/clk/clk.c > > > +++ b/drivers/clk/clk.c > > > @@ -2790,6 +2790,40 @@ int clk_set_max_rate(struct clk *clk, > > unsigned > > > long rate) } EXPORT_SYMBOL_GPL(clk_set_max_rate); > > > > > > +int clk_hw_set_spread_spectrum(struct clk_hw *hw, unsigned int > > modfreq, > > > + unsigned int spreaddepth, enum > > clk_ssc_method method, > > > + bool enable) > > > +{ > > > + struct clk_spread_spectrum clk_ss; > > > + struct clk_core *core; > > > + int ret; > > > + > > > + if (!hw) > > > + return 0; > > > + > > > + core = hw->core; > > > + > > > + clk_ss.modfreq = modfreq; > > > + clk_ss.spreaddepth = spreaddepth; > > > + clk_ss.method = method; > > > + clk_ss.enable = enable; > > > + > > > + clk_prepare_lock(); > > > + > > > + ret = clk_pm_runtime_get(core); > > > + if (ret) > > > + goto fail; > > > + > > > + if (core->ops->set_spread_spectrum) > > > + ret = core->ops->set_spread_spectrum(hw, &clk_ss); > > > + > > > + clk_pm_runtime_put(core); > > > + > > > +fail: > > > + clk_prepare_unlock(); > > > + return ret; > > > +} > > > + > > > /** > > > * clk_get_parent - return the parent of a clk > > > * @clk: the clk whose parent gets returned diff --git > > > a/include/linux/clk-provider.h b/include/linux/clk-provider.h index > > > > > 2e6e603b749342931c0d0693c3e72b62c000791b..ac0270cc9ec13395 > > 4b1f8dcffed0 > > > 15723bd1ff5d 100644 > > > --- a/include/linux/clk-provider.h > > > +++ b/include/linux/clk-provider.h > > > @@ -84,6 +84,28 @@ struct clk_duty { > > > unsigned int den; > > > }; > > > > > > +/* Aligned with dtschema/schemas/clock/clock.yaml */ enum > > > +clk_ssc_method { > > > + CLK_SSC_CENTER_SPREAD, > > > + CLK_SSC_UP_SPREAD, > > > + CLK_SSC_DOWN_SPREAD, > > > +}; > > > + > > > +/** > > > + * struct clk_spread_spectrum - Structure encoding spread spectrum > > of > > > +a clock > > > + * > > > + * @modfreq: Modulation frequency > > > + * @spreadpercent: Modulation percent > > > + * @method: Modulation method > > > + * @enable: Modulation enable or disable > > > + */ > > > +struct clk_spread_spectrum { > > > + unsigned int modfreq; > > > + unsigned int spreaddepth; > > > > Please use per mil as unit since I noticed that 0.x% is a common value > > too. > > I use " The modulation depth in permyriad " in dt-schema, since > Dario said ST chips has permyriad. Ah okay, thanks for the explanation, this wasn't clear since also the comment says: "Modulation percent". I wouldn't call it permyriad or per_myriad since this unit is rather rare. Instead I would call it "basis point" so the variable name could be 'spread_bp'. While on it, we could rename modfreq to modfreq_hz to make it clear. Regards, Marco > > So change it to moddepth_per_myriad? > > Thanks, > Peng. > > > > > Regards, > > Marco > > > > > > > + enum clk_ssc_method method; > > > + bool enable; > > > +}; > > > + > > > /** > > > * struct clk_ops - Callback operations for hardware clocks; these are > > to > > > * be provided by the clock implementation, and will be called by > > > drivers @@ -178,6 +200,11 @@ struct clk_duty { > > > * separately via calls to .set_parent and .set_rate. > > > * Returns 0 on success, -EERROR otherwise. > > > * > > > + * @set_spread_spectrum: Configure the modulation frequency, > > modulation percentage > > > + * and method. This callback is optional for clocks that > > does not > > > + * support spread spectrum feature or no need to enable > > this feature. > > > + * Returns 0 on success, -EERROR otherwise. > > > + * > > > * @recalc_accuracy: Recalculate the accuracy of this clock. The > > clock accuracy > > > * is expressed in ppb (parts per billion). The parent > > accuracy is > > > * an input parameter. > > > @@ -255,6 +282,8 @@ struct clk_ops { > > > int (*set_rate_and_parent)(struct clk_hw *hw, > > > unsigned long rate, > > > unsigned long parent_rate, u8 > > index); > > > + int (*set_spread_spectrum)(struct clk_hw *hw, > > > + struct > > clk_spread_spectrum *clk_ss); > > > unsigned long (*recalc_accuracy)(struct clk_hw *hw, > > > unsigned long > > parent_accuracy); > > > int (*get_phase)(struct clk_hw *hw); > > > @@ -1404,6 +1433,9 @@ void clk_hw_get_rate_range(struct clk_hw > > *hw, unsigned long *min_rate, > > > unsigned long *max_rate); > > > void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long > > min_rate, > > > unsigned long max_rate); > > > +int clk_hw_set_spread_spectrum(struct clk_hw *hw, unsigned int > > modfreq, > > > + unsigned int spreaddepth, enum > > clk_ssc_method method, > > > + bool enable); > > > > > > static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw > > > *src) { diff --git a/include/linux/clk.h b/include/linux/clk.h index > > > > > b607482ca77e987b9344c38f25ebb5c8d35c1d39..49a7f7eb8b03233e > > 11cd3b927688 > > > 96c4e45c4e7c 100644 > > > --- a/include/linux/clk.h > > > +++ b/include/linux/clk.h > > > @@ -858,6 +858,21 @@ int clk_set_rate(struct clk *clk, unsigned > > long rate); > > > */ > > > int clk_set_rate_exclusive(struct clk *clk, unsigned long rate); > > > > > > +/** > > > + * clk_set_spread_spectrum - set the spread spectrum for a clock > > > + * @clk: clock source > > > + * @modfreq: modulation freq > > > + * @spreadpercent: modulation percentage > > > + * @method: down spread, up spread, center spread or else > > > + * @enable: enable or disable > > > + * > > > + * Configure the spread spectrum parameters for a clock. > > > + * > > > + * Returns success (0) or negative errno. > > > + */ > > > +int clk_set_spread_spectrum(struct clk *clk, unsigned int modfreq, > > > + unsigned int spreadpercent, unsigned int > > method, > > > + bool enable); > > > /** > > > * clk_has_parent - check if a clock is a possible parent for another > > > * @clk: clock source > > > @@ -1088,6 +1103,13 @@ static inline int > > clk_set_rate_exclusive(struct clk *clk, unsigned long rate) > > > return 0; > > > } > > > > > > +static inline int clk_set_spread_spectrum(struct clk *clk, unsigned > > int modfreq, > > > + unsigned int spreadpercent, > > > + unsigned int method, bool > > enable) { > > > + return 0; > > > +} > > > + > > > static inline long clk_round_rate(struct clk *clk, unsigned long > > > rate) { > > > return 0; > > > > > > -- > > > 2.37.1 > > > > > > > > > >
Hi Peng, On Wed, 5 Feb 2025 at 10:51, Peng Fan (OSS) <peng.fan@oss.nxp.com> wrote: > From: Peng Fan <peng.fan@nxp.com> > > Add clk_hw_set_spread_spectrum to configure a clock to enable spread > spectrum feature. set_spread_spectrum ops is added for clk drivers to > have their own hardware specific implementation. > > Signed-off-by: Peng Fan <peng.fan@nxp.com> Thanks for your patch! > --- a/include/linux/clk-provider.h > +++ b/include/linux/clk-provider.h > @@ -84,6 +84,28 @@ struct clk_duty { > unsigned int den; > }; > > +/* Aligned with dtschema/schemas/clock/clock.yaml */ > +enum clk_ssc_method { > + CLK_SSC_CENTER_SPREAD, > + CLK_SSC_UP_SPREAD, > + CLK_SSC_DOWN_SPREAD, > +}; > + > +/** > + * struct clk_spread_spectrum - Structure encoding spread spectrum of a clock > + * > + * @modfreq: Modulation frequency > + * @spreadpercent: Modulation percent E.g. Renesas R-Car V4M also supports 0.5%, 1.5%, and 2.5%. > + * @method: Modulation method > + * @enable: Modulation enable or disable > + */ > +struct clk_spread_spectrum { > + unsigned int modfreq; > + unsigned int spreaddepth; > + enum clk_ssc_method method; > + bool enable; Do you envision a use case for having a separate enable flag? The alternative would be to add an extra enum value above: CLK_SSC_NO_SPREAD = 0, Gr{oetje,eeting}s, Geert
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index cf7720b9172ff223d86227aad144e15375ddfd86..e11f9615e683af52c719d4c8419bd30f369f301b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2790,6 +2790,40 @@ int clk_set_max_rate(struct clk *clk, unsigned long rate) } EXPORT_SYMBOL_GPL(clk_set_max_rate); +int clk_hw_set_spread_spectrum(struct clk_hw *hw, unsigned int modfreq, + unsigned int spreaddepth, enum clk_ssc_method method, + bool enable) +{ + struct clk_spread_spectrum clk_ss; + struct clk_core *core; + int ret; + + if (!hw) + return 0; + + core = hw->core; + + clk_ss.modfreq = modfreq; + clk_ss.spreaddepth = spreaddepth; + clk_ss.method = method; + clk_ss.enable = enable; + + clk_prepare_lock(); + + ret = clk_pm_runtime_get(core); + if (ret) + goto fail; + + if (core->ops->set_spread_spectrum) + ret = core->ops->set_spread_spectrum(hw, &clk_ss); + + clk_pm_runtime_put(core); + +fail: + clk_prepare_unlock(); + return ret; +} + /** * clk_get_parent - return the parent of a clk * @clk: the clk whose parent gets returned diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 2e6e603b749342931c0d0693c3e72b62c000791b..ac0270cc9ec133954b1f8dcffed015723bd1ff5d 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -84,6 +84,28 @@ struct clk_duty { unsigned int den; }; +/* Aligned with dtschema/schemas/clock/clock.yaml */ +enum clk_ssc_method { + CLK_SSC_CENTER_SPREAD, + CLK_SSC_UP_SPREAD, + CLK_SSC_DOWN_SPREAD, +}; + +/** + * struct clk_spread_spectrum - Structure encoding spread spectrum of a clock + * + * @modfreq: Modulation frequency + * @spreadpercent: Modulation percent + * @method: Modulation method + * @enable: Modulation enable or disable + */ +struct clk_spread_spectrum { + unsigned int modfreq; + unsigned int spreaddepth; + enum clk_ssc_method method; + bool enable; +}; + /** * struct clk_ops - Callback operations for hardware clocks; these are to * be provided by the clock implementation, and will be called by drivers @@ -178,6 +200,11 @@ struct clk_duty { * separately via calls to .set_parent and .set_rate. * Returns 0 on success, -EERROR otherwise. * + * @set_spread_spectrum: Configure the modulation frequency, modulation percentage + * and method. This callback is optional for clocks that does not + * support spread spectrum feature or no need to enable this feature. + * Returns 0 on success, -EERROR otherwise. + * * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy * is expressed in ppb (parts per billion). The parent accuracy is * an input parameter. @@ -255,6 +282,8 @@ struct clk_ops { int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); + int (*set_spread_spectrum)(struct clk_hw *hw, + struct clk_spread_spectrum *clk_ss); unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy); int (*get_phase)(struct clk_hw *hw); @@ -1404,6 +1433,9 @@ void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate, unsigned long *max_rate); void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, unsigned long max_rate); +int clk_hw_set_spread_spectrum(struct clk_hw *hw, unsigned int modfreq, + unsigned int spreaddepth, enum clk_ssc_method method, + bool enable); static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) { diff --git a/include/linux/clk.h b/include/linux/clk.h index b607482ca77e987b9344c38f25ebb5c8d35c1d39..49a7f7eb8b03233e11cd3b92768896c4e45c4e7c 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -858,6 +858,21 @@ int clk_set_rate(struct clk *clk, unsigned long rate); */ int clk_set_rate_exclusive(struct clk *clk, unsigned long rate); +/** + * clk_set_spread_spectrum - set the spread spectrum for a clock + * @clk: clock source + * @modfreq: modulation freq + * @spreadpercent: modulation percentage + * @method: down spread, up spread, center spread or else + * @enable: enable or disable + * + * Configure the spread spectrum parameters for a clock. + * + * Returns success (0) or negative errno. + */ +int clk_set_spread_spectrum(struct clk *clk, unsigned int modfreq, + unsigned int spreadpercent, unsigned int method, + bool enable); /** * clk_has_parent - check if a clock is a possible parent for another * @clk: clock source @@ -1088,6 +1103,13 @@ static inline int clk_set_rate_exclusive(struct clk *clk, unsigned long rate) return 0; } +static inline int clk_set_spread_spectrum(struct clk *clk, unsigned int modfreq, + unsigned int spreadpercent, + unsigned int method, bool enable) +{ + return 0; +} + static inline long clk_round_rate(struct clk *clk, unsigned long rate) { return 0;