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 > > >
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;