From f47c1b77d0a2a9c0d49ec14302e74f933398d1a3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Dec 2025 10:42:26 +0100 Subject: clk: Move clk_{save,restore}_context() to COMMON_CLK section The clk_save_context() and clk_restore_context() helpers are only implemented by the Common Clock Framework. They are not available when using legacy clock frameworks. Dummy implementations are provided, but only if no clock support is available at all. Hence when CONFIG_HAVE_CLK=y, but CONFIG_COMMON_CLK is not enabled: m68k-linux-gnu-ld: drivers/net/phy/air_en8811h.o: in function `en8811h_resume': air_en8811h.c:(.text+0x83e): undefined reference to `clk_restore_context' m68k-linux-gnu-ld: drivers/net/phy/air_en8811h.o: in function `en8811h_suspend': air_en8811h.c:(.text+0x856): undefined reference to `clk_save_context' Fix this by moving forward declarations and dummy implementions from the HAVE_CLK to the COMMON_CLK section. Fixes: 8b95d1ce3300c411 ("clk: Add functions to save/restore clock context en-masse") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202511301553.eaEz1nEW-lkp@intel.com/ Signed-off-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- include/linux/clk.h | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/clk.h b/include/linux/clk.h index b607482ca77e..64ff118ffb1a 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -228,6 +228,23 @@ int devm_clk_rate_exclusive_get(struct device *dev, struct clk *clk); */ void clk_rate_exclusive_put(struct clk *clk); +/** + * clk_save_context - save clock context for poweroff + * + * Saves the context of the clock register for powerstates in which the + * contents of the registers will be lost. Occurs deep within the suspend + * code so locking is not necessary. + */ +int clk_save_context(void); + +/** + * clk_restore_context - restore clock context after poweroff + * + * This occurs with all clocks enabled. Occurs deep within the resume code + * so locking is not necessary. + */ +void clk_restore_context(void); + #else static inline int clk_notifier_register(struct clk *clk, @@ -293,6 +310,13 @@ static inline int devm_clk_rate_exclusive_get(struct device *dev, struct clk *cl static inline void clk_rate_exclusive_put(struct clk *clk) {} +static inline int clk_save_context(void) +{ + return 0; +} + +static inline void clk_restore_context(void) {} + #endif #ifdef CONFIG_HAVE_CLK_PREPARE @@ -933,23 +957,6 @@ struct clk *clk_get_parent(struct clk *clk); */ struct clk *clk_get_sys(const char *dev_id, const char *con_id); -/** - * clk_save_context - save clock context for poweroff - * - * Saves the context of the clock register for powerstates in which the - * contents of the registers will be lost. Occurs deep within the suspend - * code so locking is not necessary. - */ -int clk_save_context(void); - -/** - * clk_restore_context - restore clock context after poweroff - * - * This occurs with all clocks enabled. Occurs deep within the resume code - * so locking is not necessary. - */ -void clk_restore_context(void); - #else /* !CONFIG_HAVE_CLK */ static inline struct clk *clk_get(struct device *dev, const char *id) @@ -1129,13 +1136,6 @@ static inline struct clk *clk_get_sys(const char *dev_id, const char *con_id) return NULL; } -static inline int clk_save_context(void) -{ - return 0; -} - -static inline void clk_restore_context(void) {} - #endif /* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ -- cgit v1.2.3 From d94f0f096ccf83b1a212788c62122c4b97ac8907 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Dec 2025 10:42:27 +0100 Subject: clk: Merge prepare and unprepare sections contains two consecutive #ifdef/#else/#endif sections that check for CONFIG_HAVE_CLK_PREPARE: one for prepare-related functionality, and a second for unprepare-related functionality. Reduce #ifdef clutter by merging them. Signed-off-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- include/linux/clk.h | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/clk.h b/include/linux/clk.h index 64ff118ffb1a..9e5291f37c50 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -329,8 +329,21 @@ static inline void clk_restore_context(void) {} * Must not be called from within atomic context. */ int clk_prepare(struct clk *clk); + +/** + * clk_unprepare - undo preparation of a clock source + * @clk: clock source + * + * This undoes a previously prepared clock. The caller must balance + * the number of prepare and unprepare calls. + * + * Must not be called from within atomic context. + */ +void clk_unprepare(struct clk *clk); + int __must_check clk_bulk_prepare(int num_clks, const struct clk_bulk_data *clks); +void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks); /** * clk_is_enabled_when_prepared - indicate if preparing a clock also enables it. @@ -355,41 +368,28 @@ static inline int clk_prepare(struct clk *clk) return 0; } -static inline int __must_check -clk_bulk_prepare(int num_clks, const struct clk_bulk_data *clks) +static inline void clk_unprepare(struct clk *clk) { might_sleep(); - return 0; } -static inline bool clk_is_enabled_when_prepared(struct clk *clk) -{ - return false; -} -#endif - -/** - * clk_unprepare - undo preparation of a clock source - * @clk: clock source - * - * This undoes a previously prepared clock. The caller must balance - * the number of prepare and unprepare calls. - * - * Must not be called from within atomic context. - */ -#ifdef CONFIG_HAVE_CLK_PREPARE -void clk_unprepare(struct clk *clk); -void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks); -#else -static inline void clk_unprepare(struct clk *clk) +static inline int __must_check +clk_bulk_prepare(int num_clks, const struct clk_bulk_data *clks) { might_sleep(); + return 0; } + static inline void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks) { might_sleep(); } + +static inline bool clk_is_enabled_when_prepared(struct clk *clk) +{ + return false; +} #endif #ifdef CONFIG_HAVE_CLK -- cgit v1.2.3 From abe368db117ea61ace90880c80a12ee3c0d619e6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Dec 2025 10:42:28 +0100 Subject: clk: Annotate #else and #endif Annotate the #else and #endif keywords in large #ifdef/#else/#endif sections, to improve readability. Signed-off-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- include/linux/clk.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/clk.h b/include/linux/clk.h index 9e5291f37c50..efb91604d3f6 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -245,7 +245,7 @@ int clk_save_context(void); */ void clk_restore_context(void); -#else +#else /* !CONFIG_COMMON_CLK */ static inline int clk_notifier_register(struct clk *clk, struct notifier_block *nb) @@ -317,7 +317,7 @@ static inline int clk_save_context(void) static inline void clk_restore_context(void) {} -#endif +#endif /* !CONFIG_COMMON_CLK */ #ifdef CONFIG_HAVE_CLK_PREPARE /** @@ -361,7 +361,7 @@ void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks); * to be right. */ bool clk_is_enabled_when_prepared(struct clk *clk); -#else +#else /* !CONFIG_HAVE_CLK_PREPARE */ static inline int clk_prepare(struct clk *clk) { might_sleep(); @@ -390,7 +390,7 @@ static inline bool clk_is_enabled_when_prepared(struct clk *clk) { return false; } -#endif +#endif /* !CONFIG_HAVE_CLK_PREPARE */ #ifdef CONFIG_HAVE_CLK /** @@ -1136,7 +1136,7 @@ static inline struct clk *clk_get_sys(const char *dev_id, const char *con_id) return NULL; } -#endif +#endif /* !CONFIG_HAVE_CLK */ /* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ static inline int clk_prepare_enable(struct clk *clk) -- cgit v1.2.3 From 5a4326f2e3b1edfb3329c1bee59035dc9f048b59 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Mon, 24 Nov 2025 08:10:02 -0500 Subject: clk: renesas: rzg2l: Remove DSI clock rate restrictions Convert the limited MIPI clock calculations to a full range of settings based on math including H/W limitation validation. Since the required DSI division setting must be specified from external sources before calculations, expose a new API to set it. Signed-off-by: Chris Brandt Reviewed-by: Biju Das Tested-by: Biju Das Reviewed-by: Hugo Villeneuve Tested-by: Hugo Villeneuve Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20251124131003.992554-2-chris.brandt@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rzg2l-cpg.c | 174 +++++++++++++++++++++++++++++++++------- include/linux/clk/renesas.h | 11 +++ 2 files changed, 154 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index f670c6408ea1..3ce28d553e4c 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,17 @@ #define MSTOP_OFF(conf) FIELD_GET(GENMASK(31, 16), (conf)) #define MSTOP_MASK(conf) FIELD_GET(GENMASK(15, 0), (conf)) +#define PLL5_FOUTVCO_MIN 800000000 +#define PLL5_FOUTVCO_MAX 3000000000 +#define PLL5_POSTDIV_MIN 1 +#define PLL5_POSTDIV_MAX 7 +#define PLL5_REFDIV_MIN 1 +#define PLL5_REFDIV_MAX 2 +#define PLL5_INTIN_MIN 20 +#define PLL5_INTIN_MAX 320 +#define PLL5_HSCLK_MIN 10000000 +#define PLL5_HSCLK_MAX 187500000 + /** * struct clk_hw_data - clock hardware data * @hw: clock hw @@ -129,6 +141,12 @@ struct rzg2l_pll5_param { u8 pl5_spread; }; +/* PLL5 output will be used for DPI or MIPI-DSI */ +static int dsi_div_target = PLL5_TARGET_DPI; + +/* Required division ratio for MIPI D-PHY clock depending on number of lanes and bpp. */ +static u8 dsi_div_ab_desired; + struct rzg2l_pll5_mux_dsi_div_param { u8 clksrc; u8 dsi_div_a; @@ -170,6 +188,11 @@ struct rzg2l_cpg_priv { struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params; }; +static inline u8 rzg2l_cpg_div_ab(u8 a, u8 b) +{ + return (b + 1) << a; +} + static void rzg2l_cpg_del_clk_provider(void *data) { of_clk_del_provider(data); @@ -556,24 +579,121 @@ rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk *core, return clk_hw->clk; } +/* + * VCO-->[POSTDIV1,2]--FOUTPOSTDIV--------------->| + * | |-->[1/(DSI DIV A * B)]--> MIPI_DSI_VCLK + * |-->[1/2]--FOUT1PH0-->| + * | + * |------->[1/16]--------------------------------> hsclk (MIPI-PHY) + */ static unsigned long -rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_pll5_param *params, +rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_cpg_priv *priv, + struct rzg2l_pll5_param *params, unsigned long rate) { - unsigned long foutpostdiv_rate, foutvco_rate; + const u32 extal_hz = EXTAL_FREQ_IN_MEGA_HZ * MEGA; + unsigned long foutpostdiv_rate; + unsigned int a, b, odd; + unsigned long hsclk; + u8 dsi_div_ab_calc; + u64 foutvco_rate; + + if (dsi_div_target == PLL5_TARGET_DSI) { + /* Check hsclk */ + hsclk = rate * dsi_div_ab_desired / 16; + if (hsclk < PLL5_HSCLK_MIN || hsclk > PLL5_HSCLK_MAX) { + dev_err(priv->dev, "hsclk out of range\n"); + return 0; + } + + /* Determine the correct clock source based on even/odd of the divider */ + odd = dsi_div_ab_desired & 1; + if (odd) { + priv->mux_dsi_div_params.clksrc = 0; /* FOUTPOSTDIV */ + dsi_div_ab_calc = dsi_div_ab_desired; + } else { + priv->mux_dsi_div_params.clksrc = 1; /* FOUT1PH0 */ + dsi_div_ab_calc = dsi_div_ab_desired / 2; + } + + /* Calculate the DIV_DSI_A and DIV_DSI_B based on the desired divider */ + for (a = 0; a < 4; a++) { + /* FOUT1PH0: Max output of DIV_DSI_A is 750MHz so at least 1/2 to be safe */ + if (!odd && a == 0) + continue; + + /* FOUTPOSTDIV: DIV_DSI_A must always be 1/1 */ + if (odd && a != 0) + break; + + for (b = 0; b < 16; b++) { + /* FOUTPOSTDIV: DIV_DSI_B must always be odd divider 1/(b+1) */ + if (odd && b & 1) + continue; + + if (rzg2l_cpg_div_ab(a, b) == dsi_div_ab_calc) { + priv->mux_dsi_div_params.dsi_div_a = a; + priv->mux_dsi_div_params.dsi_div_b = b; + goto calc_pll_clk; + } + } + } + + dev_err(priv->dev, "Failed to calculate DIV_DSI_A,B\n"); + + return 0; + } else if (dsi_div_target == PLL5_TARGET_DPI) { + /* Fixed settings for DPI */ + priv->mux_dsi_div_params.clksrc = 0; + priv->mux_dsi_div_params.dsi_div_a = 3; /* Divided by 8 */ + priv->mux_dsi_div_params.dsi_div_b = 0; /* Divided by 1 */ + dsi_div_ab_desired = rzg2l_cpg_div_ab(priv->mux_dsi_div_params.dsi_div_a, + priv->mux_dsi_div_params.dsi_div_b); + } - params->pl5_intin = rate / MEGA; - params->pl5_fracin = div_u64(((u64)rate % MEGA) << 24, MEGA); - params->pl5_refdiv = 2; - params->pl5_postdiv1 = 1; - params->pl5_postdiv2 = 1; +calc_pll_clk: + /* PLL5 (MIPI_DSI_PLLCLK) = VCO / POSTDIV1 / POSTDIV2 */ + for (params->pl5_postdiv1 = PLL5_POSTDIV_MIN; + params->pl5_postdiv1 <= PLL5_POSTDIV_MAX; + params->pl5_postdiv1++) { + for (params->pl5_postdiv2 = PLL5_POSTDIV_MIN; + params->pl5_postdiv2 <= PLL5_POSTDIV_MAX; + params->pl5_postdiv2++) { + foutvco_rate = rate * params->pl5_postdiv1 * params->pl5_postdiv2 * + dsi_div_ab_desired; + if (foutvco_rate <= PLL5_FOUTVCO_MIN || foutvco_rate >= PLL5_FOUTVCO_MAX) + continue; + + for (params->pl5_refdiv = PLL5_REFDIV_MIN; + params->pl5_refdiv <= PLL5_REFDIV_MAX; + params->pl5_refdiv++) { + u32 rem; + + params->pl5_intin = div_u64_rem(foutvco_rate * params->pl5_refdiv, + extal_hz, &rem); + + if (params->pl5_intin < PLL5_INTIN_MIN || + params->pl5_intin > PLL5_INTIN_MAX) + continue; + + params->pl5_fracin = div_u64((u64)rem << 24, extal_hz); + + goto clk_valid; + } + } + } + + dev_err(priv->dev, "Failed to calculate PLL5 settings\n"); + return 0; + +clk_valid: params->pl5_spread = 0x16; foutvco_rate = div_u64(mul_u32_u32(EXTAL_FREQ_IN_MEGA_HZ * MEGA, (params->pl5_intin << 24) + params->pl5_fracin), params->pl5_refdiv) >> 24; - foutpostdiv_rate = DIV_ROUND_CLOSEST(foutvco_rate, - params->pl5_postdiv1 * params->pl5_postdiv2); + foutpostdiv_rate = DIV_U64_ROUND_CLOSEST(foutvco_rate, + params->pl5_postdiv1 * params->pl5_postdiv2); return foutpostdiv_rate; } @@ -607,7 +727,7 @@ static unsigned long rzg2l_cpg_get_vclk_parent_rate(struct clk_hw *hw, struct rzg2l_pll5_param params; unsigned long parent_rate; - parent_rate = rzg2l_cpg_get_foutpostdiv_rate(¶ms, rate); + parent_rate = rzg2l_cpg_get_foutpostdiv_rate(priv, ¶ms, rate); if (priv->mux_dsi_div_params.clksrc) parent_rate /= 2; @@ -623,9 +743,19 @@ static int rzg2l_cpg_dsi_div_determine_rate(struct clk_hw *hw, req->best_parent_rate = rzg2l_cpg_get_vclk_parent_rate(hw, req->rate); + if (!req->best_parent_rate) + return -EINVAL; + return 0; } +void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) +{ + dsi_div_ab_desired = divider; + dsi_div_target = target; +} +EXPORT_SYMBOL_GPL(rzg2l_cpg_dsi_div_set_divider); + static int rzg2l_cpg_dsi_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) @@ -796,22 +926,6 @@ struct sipll5 { #define to_sipll5(_hw) container_of(_hw, struct sipll5, hw) -static unsigned long rzg2l_cpg_get_vclk_rate(struct clk_hw *hw, - unsigned long rate) -{ - struct sipll5 *sipll5 = to_sipll5(hw); - struct rzg2l_cpg_priv *priv = sipll5->priv; - unsigned long vclk; - - vclk = rate / ((1 << priv->mux_dsi_div_params.dsi_div_a) * - (priv->mux_dsi_div_params.dsi_div_b + 1)); - - if (priv->mux_dsi_div_params.clksrc) - vclk /= 2; - - return vclk; -} - static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -856,9 +970,9 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw, if (!rate) return -EINVAL; - vclk_rate = rzg2l_cpg_get_vclk_rate(hw, rate); + vclk_rate = rate / dsi_div_ab_desired; sipll5->foutpostdiv_rate = - rzg2l_cpg_get_foutpostdiv_rate(¶ms, vclk_rate); + rzg2l_cpg_get_foutpostdiv_rate(priv, ¶ms, vclk_rate); /* Put PLL5 into standby mode */ writel(CPG_SIPLL5_STBY_RESETB_WEN, priv->base + CPG_SIPLL5_STBY); @@ -945,9 +1059,7 @@ rzg2l_cpg_sipll5_register(const struct cpg_core_clk *core, if (ret) return ERR_PTR(ret); - priv->mux_dsi_div_params.clksrc = 1; /* Use clk src 1 for DSI */ - priv->mux_dsi_div_params.dsi_div_a = 1; /* Divided by 2 */ - priv->mux_dsi_div_params.dsi_div_b = 2; /* Divided by 3 */ + rzg2l_cpg_dsi_div_set_divider(8, PLL5_TARGET_DPI); return clk_hw->clk; } diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index 69d8159deee3..c360df9fa735 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -35,6 +35,17 @@ void cpg_mssr_detach_dev(struct generic_pm_domain *unused, struct device *dev); #define cpg_mssr_detach_dev NULL #endif +enum { + PLL5_TARGET_DPI, + PLL5_TARGET_DSI +}; + +#ifdef CONFIG_CLK_RZG2L +void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target); +#else +static inline void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) { } +#endif + /** * struct rzv2h_pll_limits - PLL parameter constraints * -- cgit v1.2.3