From 80c439cd1f011e8c6bb4c6c65987e235f686d9ee Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:34 +0200 Subject: [PATCH 01/11] clk: sunxi-ng: nkm: Use correct parameter name for parent HW ccu_nkm_round_rate() takes a clk_hw as parameter "hw". Since "hw" is the nkm clock's parent clk_hw, not the clk_hw of the nkm clock itself, change the parameter name to "parent_hw" to make it more clear what we're dealing with. Acked-by: Maxime Ripard Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-1-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_nkm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index a0978a50edae..f267142e58b3 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -106,7 +106,7 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, } static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, - struct clk_hw *hw, + struct clk_hw *parent_hw, unsigned long *parent_rate, unsigned long rate, void *data) From 3492e4f6db5f6814869a4639d70ea025385f4cb6 Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:35 +0200 Subject: [PATCH 02/11] clk: sunxi-ng: nkm: consider alternative parent rates when determining rate In case the CLK_SET_RATE_PARENT flag is set, consider using a different parent rate when determining a new rate. To find the best match for the requested rate, perform the following steps for each NKM combination: - calculate the optimal parent rate, - find the best parent rate that the parent clock actually supports - use that parent rate to calculate the effective rate. In case the clk does not support setting the parent rate, use the same algorithm as before. Acked-by: Maxime Ripard Signed-off-by: Frank Oltmanns Reviewed-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-2-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_nkm.c | 44 +++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index f267142e58b3..2f60f3c33e30 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -16,6 +16,45 @@ struct _ccu_nkm { unsigned long m, min_m, max_m; }; +static unsigned long ccu_nkm_find_best_with_parent_adj(struct clk_hw *parent_hw, + unsigned long *parent, unsigned long rate, + struct _ccu_nkm *nkm) +{ + unsigned long best_rate = 0, best_parent_rate = *parent, tmp_parent = *parent; + unsigned long best_n = 0, best_k = 0, best_m = 0; + unsigned long _n, _k, _m; + + for (_k = nkm->min_k; _k <= nkm->max_k; _k++) { + for (_n = nkm->min_n; _n <= nkm->max_n; _n++) { + for (_m = nkm->min_m; _m <= nkm->max_m; _m++) { + unsigned long tmp_rate; + + tmp_parent = clk_hw_round_rate(parent_hw, rate * _m / (_n * _k)); + + tmp_rate = tmp_parent * _n * _k / _m; + if (tmp_rate > rate) + continue; + + if ((rate - tmp_rate) < (rate - best_rate)) { + best_rate = tmp_rate; + best_parent_rate = tmp_parent; + best_n = _n; + best_k = _k; + best_m = _m; + } + } + } + } + + nkm->n = best_n; + nkm->k = best_k; + nkm->m = best_m; + + *parent = best_parent_rate; + + return best_rate; +} + static unsigned long ccu_nkm_find_best(unsigned long parent, unsigned long rate, struct _ccu_nkm *nkm) { @@ -124,7 +163,10 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) rate *= nkm->fixed_post_div; - rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm); + if (!clk_hw_can_set_rate_parent(&nkm->common.hw)) + rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm); + else + rate = ccu_nkm_find_best_with_parent_adj(parent_hw, parent_rate, rate, &_nkm); if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) rate /= nkm->fixed_post_div; From b271fc467acd71630d7a2f0f2fcd2a4dbfc22edf Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:36 +0200 Subject: [PATCH 03/11] clk: sunxi-ng: a64: allow pll-mipi to set parent's rate The nkm clock now supports setting the parent's rate. Utilize this option to find the optimal rate for pll-mipi. Acked-by: Maxime Ripard Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-3-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c index eb36f8f77d55..125ae097d96c 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c @@ -179,7 +179,8 @@ static struct ccu_nkm pll_mipi_clk = { .common = { .reg = 0x040, .hw.init = CLK_HW_INIT("pll-mipi", "pll-video0", - &ccu_nkm_ops, CLK_SET_RATE_UNGATE), + &ccu_nkm_ops, + CLK_SET_RATE_UNGATE | CLK_SET_RATE_PARENT), }, }; From 48fb70cdd9e3c896340ab8639f45cf845f9b346f Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:37 +0200 Subject: [PATCH 04/11] clk: sunxi-ng: Add feature to find closest rate The default behaviour of clocks in the sunxi-ng driver is to select a clock rate that is closest to but less than the requested rate. Add the CCU_FEATURE_CLOSEST_RATE flag, which can be used to allow clocks to find the closest rate instead. Acked-by: Maxime Ripard Acked-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-4-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h index fbf16c6b896d..5ad219f041d5 100644 --- a/drivers/clk/sunxi-ng/ccu_common.h +++ b/drivers/clk/sunxi-ng/ccu_common.h @@ -18,6 +18,7 @@ #define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6) #define CCU_FEATURE_SIGMA_DELTA_MOD BIT(7) #define CCU_FEATURE_KEY_FIELD BIT(8) +#define CCU_FEATURE_CLOSEST_RATE BIT(9) /* MMC timing mode switch bit */ #define CCU_MMC_NEW_TIMING_MODE BIT(30) From e373315d8ff839543132d2a0dcea546fda8b5048 Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:38 +0200 Subject: [PATCH 05/11] clk: sunxi-ng: Add helper function to find closest rate The default behaviour of clocks in the sunxi-ng driver is to select a clock rate that is closest to but less than the requested rate. Add the ccu_is_better_rate() helper function that - depending on the fact if thc CCU_FEATURE_CLOSEST_RATE flag is set - decides if a rate is closer than another rate. Acked-by: Maxime Ripard Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-5-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_common.c | 12 ++++++++++++ drivers/clk/sunxi-ng/ccu_common.h | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c index 8d28a7a079d0..8babce55302f 100644 --- a/drivers/clk/sunxi-ng/ccu_common.c +++ b/drivers/clk/sunxi-ng/ccu_common.c @@ -39,6 +39,18 @@ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) } EXPORT_SYMBOL_NS_GPL(ccu_helper_wait_for_lock, SUNXI_CCU); +bool ccu_is_better_rate(struct ccu_common *common, + unsigned long target_rate, + unsigned long current_rate, + unsigned long best_rate) +{ + if (common->features & CCU_FEATURE_CLOSEST_RATE) + return abs(current_rate - target_rate) < abs(best_rate - target_rate); + + return current_rate <= target_rate && current_rate > best_rate; +} +EXPORT_SYMBOL_NS_GPL(ccu_is_better_rate, SUNXI_CCU); + /* * This clock notifier is called when the frequency of a PLL clock is * changed. In common PLL designs, changes to the dividers take effect diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h index 5ad219f041d5..942a72c09437 100644 --- a/drivers/clk/sunxi-ng/ccu_common.h +++ b/drivers/clk/sunxi-ng/ccu_common.h @@ -53,6 +53,11 @@ struct sunxi_ccu_desc { void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock); +bool ccu_is_better_rate(struct ccu_common *common, + unsigned long target_rate, + unsigned long current_rate, + unsigned long best_rate); + struct ccu_pll_nb { struct notifier_block clk_nb; struct ccu_common *common; From c0380d14745194906c378d2494fc494239e16a85 Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:39 +0200 Subject: [PATCH 06/11] clk: sunxi-ng: nm: Support finding closest rate Use the helper function ccu_is_better_rate() to determine the rate that is closest to the requested rate, thereby supporting rates that are higher than the requested rate if the clock uses the CCU_FEATURE_CLOSEST_RATE. Add the macro SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_CLOSEST which sets CCU_FEATURE_CLOSEST_RATE. To avoid code duplication, add the macros SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_FEAT that allows selecting arbitrary features and use it in the original SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX as well as the newly introduced SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_CLOSEST macros. Acked-by: Maxime Ripard Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-6-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_nm.c | 13 ++++------ drivers/clk/sunxi-ng/ccu_nm.h | 48 ++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index c1fd11542c45..ffac3deb89d6 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -27,8 +27,8 @@ static unsigned long ccu_nm_calc_rate(unsigned long parent, return rate; } -static unsigned long ccu_nm_find_best(unsigned long parent, unsigned long rate, - struct _ccu_nm *nm) +static unsigned long ccu_nm_find_best(struct ccu_common *common, unsigned long parent, + unsigned long rate, struct _ccu_nm *nm) { unsigned long best_rate = 0; unsigned long best_n = 0, best_m = 0; @@ -39,10 +39,7 @@ static unsigned long ccu_nm_find_best(unsigned long parent, unsigned long rate, unsigned long tmp_rate = ccu_nm_calc_rate(parent, _n, _m); - if (tmp_rate > rate) - continue; - - if ((rate - tmp_rate) < (rate - best_rate)) { + if (ccu_is_better_rate(common, rate, tmp_rate, best_rate)) { best_rate = tmp_rate; best_n = _n; best_m = _m; @@ -159,7 +156,7 @@ static long ccu_nm_round_rate(struct clk_hw *hw, unsigned long rate, _nm.min_m = 1; _nm.max_m = nm->m.max ?: 1 << nm->m.width; - rate = ccu_nm_find_best(*parent_rate, rate, &_nm); + rate = ccu_nm_find_best(&nm->common, *parent_rate, rate, &_nm); if (nm->common.features & CCU_FEATURE_FIXED_POSTDIV) rate /= nm->fixed_post_div; @@ -210,7 +207,7 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, &_nm.m, &_nm.n); } else { ccu_sdm_helper_disable(&nm->common, &nm->sdm); - ccu_nm_find_best(parent_rate, rate, &_nm); + ccu_nm_find_best(&nm->common, parent_rate, rate, &_nm); } spin_lock_irqsave(nm->common.lock, flags); diff --git a/drivers/clk/sunxi-ng/ccu_nm.h b/drivers/clk/sunxi-ng/ccu_nm.h index 2904e67f05a8..93c11693574f 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.h +++ b/drivers/clk/sunxi-ng/ccu_nm.h @@ -108,7 +108,7 @@ struct ccu_nm { }, \ } -#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX(_struct, _name, \ +#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_FEAT(_struct, _name, \ _parent, _reg, \ _min_rate, _max_rate, \ _nshift, _nwidth, \ @@ -116,7 +116,8 @@ struct ccu_nm { _frac_en, _frac_sel, \ _frac_rate_0, \ _frac_rate_1, \ - _gate, _lock, _flags) \ + _gate, _lock, _flags, \ + _features) \ struct ccu_nm _struct = { \ .enable = _gate, \ .lock = _lock, \ @@ -129,7 +130,7 @@ struct ccu_nm { .max_rate = _max_rate, \ .common = { \ .reg = _reg, \ - .features = CCU_FEATURE_FRACTIONAL, \ + .features = _features, \ .hw.init = CLK_HW_INIT(_name, \ _parent, \ &ccu_nm_ops, \ @@ -137,6 +138,47 @@ struct ccu_nm { }, \ } +#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX(_struct, _name, \ + _parent, _reg, \ + _min_rate, _max_rate, \ + _nshift, _nwidth, \ + _mshift, _mwidth, \ + _frac_en, _frac_sel, \ + _frac_rate_0, \ + _frac_rate_1, \ + _gate, _lock, _flags) \ + SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_FEAT(_struct, _name, \ + _parent, _reg, \ + _min_rate, _max_rate, \ + _nshift, _nwidth, \ + _mshift, _mwidth, \ + _frac_en, _frac_sel, \ + _frac_rate_0, \ + _frac_rate_1, \ + _gate, _lock, _flags, \ + CCU_FEATURE_FRACTIONAL) + +#define SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_CLOSEST(_struct, _name, \ + _parent, _reg, \ + _min_rate, _max_rate, \ + _nshift, _nwidth, \ + _mshift, _mwidth, \ + _frac_en, _frac_sel, \ + _frac_rate_0, \ + _frac_rate_1, \ + _gate, _lock, _flags) \ + SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_FEAT(_struct, _name, \ + _parent, _reg, \ + _min_rate, _max_rate, \ + _nshift, _nwidth, \ + _mshift, _mwidth, \ + _frac_en, _frac_sel, \ + _frac_rate_0, \ + _frac_rate_1, \ + _gate, _lock, _flags, \ + CCU_FEATURE_FRACTIONAL |\ + CCU_FEATURE_CLOSEST_RATE) + #define SUNXI_CCU_NM_WITH_GATE_LOCK(_struct, _name, _parent, _reg, \ _nshift, _nwidth, \ _mshift, _mwidth, \ From 088cefca2418304f457818da0f3c33bf2dac0794 Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:40 +0200 Subject: [PATCH 07/11] clk: sunxi-ng: nkm: Support finding closest rate When finding the best rate for a NKM clock, consider rates that are higher than the requested rate, if the CCU_FEATURE_CLOSEST_RATE flag is set by using the helper function ccu_is_better_rate(). Accommodate ccu_mux_helper_determine_rate to this change. Acked-by: Maxime Ripard Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-7-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_mux.c | 2 +- drivers/clk/sunxi-ng/ccu_nkm.c | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index 1d557e323169..3ca695439620 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -139,7 +139,7 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common, goto out; } - if ((req->rate - tmp_rate) < (req->rate - best_rate)) { + if (ccu_is_better_rate(common, req->rate, tmp_rate, best_rate)) { best_rate = tmp_rate; best_parent_rate = parent_rate; best_parent = parent; diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index 2f60f3c33e30..a714dcf0dfc1 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -16,7 +16,8 @@ struct _ccu_nkm { unsigned long m, min_m, max_m; }; -static unsigned long ccu_nkm_find_best_with_parent_adj(struct clk_hw *parent_hw, +static unsigned long ccu_nkm_find_best_with_parent_adj(struct ccu_common *common, + struct clk_hw *parent_hw, unsigned long *parent, unsigned long rate, struct _ccu_nkm *nkm) { @@ -32,10 +33,8 @@ static unsigned long ccu_nkm_find_best_with_parent_adj(struct clk_hw *parent_hw, tmp_parent = clk_hw_round_rate(parent_hw, rate * _m / (_n * _k)); tmp_rate = tmp_parent * _n * _k / _m; - if (tmp_rate > rate) - continue; - if ((rate - tmp_rate) < (rate - best_rate)) { + if (ccu_is_better_rate(common, rate, tmp_rate, best_rate)) { best_rate = tmp_rate; best_parent_rate = tmp_parent; best_n = _n; @@ -56,7 +55,7 @@ static unsigned long ccu_nkm_find_best_with_parent_adj(struct clk_hw *parent_hw, } static unsigned long ccu_nkm_find_best(unsigned long parent, unsigned long rate, - struct _ccu_nkm *nkm) + struct _ccu_nkm *nkm, struct ccu_common *common) { unsigned long best_rate = 0; unsigned long best_n = 0, best_k = 0, best_m = 0; @@ -69,9 +68,7 @@ static unsigned long ccu_nkm_find_best(unsigned long parent, unsigned long rate, tmp_rate = parent * _n * _k / _m; - if (tmp_rate > rate) - continue; - if ((rate - tmp_rate) < (rate - best_rate)) { + if (ccu_is_better_rate(common, rate, tmp_rate, best_rate)) { best_rate = tmp_rate; best_n = _n; best_k = _k; @@ -164,9 +161,10 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, rate *= nkm->fixed_post_div; if (!clk_hw_can_set_rate_parent(&nkm->common.hw)) - rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm); + rate = ccu_nkm_find_best(*parent_rate, rate, &_nkm, &nkm->common); else - rate = ccu_nkm_find_best_with_parent_adj(parent_hw, parent_rate, rate, &_nkm); + rate = ccu_nkm_find_best_with_parent_adj(&nkm->common, parent_hw, parent_rate, rate, + &_nkm); if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) rate /= nkm->fixed_post_div; @@ -201,7 +199,7 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, _nkm.min_m = 1; _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; - ccu_nkm_find_best(parent_rate, rate, &_nkm); + ccu_nkm_find_best(parent_rate, rate, &_nkm, &nkm->common); spin_lock_irqsave(nkm->common.lock, flags); From 78e7f7da07f9d93288c1804522c696c507787d56 Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:41 +0200 Subject: [PATCH 08/11] clk: sunxi-ng: mux: Support finding closest rate When finding the best rate for a mux clock, consider rates that are higher than the requested rate when CCU_FEATURE_ROUND_CLOSEST is used. Furthermore, introduce an initialization macro that sets this flag. Acked-by: Maxime Ripard Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-8-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_mux.c | 13 +++++++++++- drivers/clk/sunxi-ng/ccu_mux.h | 38 ++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c index 3ca695439620..5edc63b46651 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.c +++ b/drivers/clk/sunxi-ng/ccu_mux.c @@ -242,6 +242,17 @@ static int ccu_mux_set_parent(struct clk_hw *hw, u8 index) return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index); } +static int ccu_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct ccu_mux *cm = hw_to_ccu_mux(hw); + + if (cm->common.features & CCU_FEATURE_CLOSEST_RATE) + return clk_mux_determine_rate_flags(hw, req, CLK_MUX_ROUND_CLOSEST); + + return clk_mux_determine_rate_flags(hw, req, 0); +} + static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -259,7 +270,7 @@ const struct clk_ops ccu_mux_ops = { .get_parent = ccu_mux_get_parent, .set_parent = ccu_mux_set_parent, - .determine_rate = __clk_mux_determine_rate, + .determine_rate = ccu_mux_determine_rate, .recalc_rate = ccu_mux_recalc_rate, }; EXPORT_SYMBOL_NS_GPL(ccu_mux_ops, SUNXI_CCU); diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h index 2c1811a445b0..eb1172ebbd94 100644 --- a/drivers/clk/sunxi-ng/ccu_mux.h +++ b/drivers/clk/sunxi-ng/ccu_mux.h @@ -46,20 +46,36 @@ struct ccu_mux { struct ccu_common common; }; +#define SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents, _table, \ + _reg, _shift, _width, _gate, \ + _flags, _features) \ + struct ccu_mux _struct = { \ + .enable = _gate, \ + .mux = _SUNXI_CCU_MUX_TABLE(_shift, _width, _table), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parents, \ + &ccu_mux_ops, \ + _flags), \ + .features = _features, \ + } \ + } + +#define SUNXI_CCU_MUX_TABLE_WITH_GATE_CLOSEST(_struct, _name, _parents, \ + _table, _reg, _shift, \ + _width, _gate, _flags) \ + SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents, \ + _table, _reg, _shift, \ + _width, _gate, _flags, \ + CCU_FEATURE_CLOSEST_RATE) + #define SUNXI_CCU_MUX_TABLE_WITH_GATE(_struct, _name, _parents, _table, \ _reg, _shift, _width, _gate, \ _flags) \ - struct ccu_mux _struct = { \ - .enable = _gate, \ - .mux = _SUNXI_CCU_MUX_TABLE(_shift, _width, _table), \ - .common = { \ - .reg = _reg, \ - .hw.init = CLK_HW_INIT_PARENTS(_name, \ - _parents, \ - &ccu_mux_ops, \ - _flags), \ - } \ - } + SUNXI_CCU_MUX_TABLE_WITH_GATE_FEAT(_struct, _name, _parents, \ + _table, _reg, _shift, \ + _width, _gate, _flags, 0) #define SUNXI_CCU_MUX_WITH_GATE(_struct, _name, _parents, _reg, \ _shift, _width, _gate, _flags) \ From 253795abdb04bdc762a9c2dd842ad6e549a9e2d7 Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:42 +0200 Subject: [PATCH 09/11] clk: sunxi-ng: div: Support finding closest rate Add initalization macros for divisor clocks with mux (SUNXI_CCU_M_WITH_MUX) to support finding the closest rate. This clock type requires the appropriate flags to be set in the .common structure (for the mux part of the clock) and the .div part. Acked-by: Maxime Ripard Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-9-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_div.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h index 948e2b0c0c3b..90d49ee8e0cc 100644 --- a/drivers/clk/sunxi-ng/ccu_div.h +++ b/drivers/clk/sunxi-ng/ccu_div.h @@ -143,6 +143,26 @@ struct ccu_div { }, \ } +#define SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(_struct, _name, \ + _parents, _table, \ + _reg, \ + _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) \ + struct ccu_div _struct = { \ + .enable = _gate, \ + .div = _SUNXI_CCU_DIV_FLAGS(_mshift, _mwidth, CLK_DIVIDER_ROUND_CLOSEST), \ + .mux = _SUNXI_CCU_MUX_TABLE(_muxshift, _muxwidth, _table), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parents, \ + &ccu_div_ops, \ + _flags), \ + .features = CCU_FEATURE_CLOSEST_RATE, \ + }, \ + } + #define SUNXI_CCU_M_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ _mshift, _mwidth, _muxshift, _muxwidth, \ _gate, _flags) \ @@ -152,6 +172,16 @@ struct ccu_div { _muxshift, _muxwidth, \ _gate, _flags) +#define SUNXI_CCU_M_WITH_MUX_GATE_CLOSEST(_struct, _name, _parents, \ + _reg, _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) \ + SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(_struct, _name, \ + _parents, NULL, \ + _reg, _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) + #define SUNXI_CCU_M_WITH_MUX(_struct, _name, _parents, _reg, \ _mshift, _mwidth, _muxshift, _muxwidth, \ _flags) \ From bf8eb12f52c49e10ca1d86564bfa096e09c51c38 Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:43 +0200 Subject: [PATCH 10/11] clk: sunxi-ng: a64: select closest rate for pll-video0 Selecting the closest rate for pll-video0 instead of the closest rate that is less than the requested rate has no downside for this clock, while allowing for selecting a more suitable rate, e.g. for the connected panels. Furthermore, the algorithm that sets an NKM clock's parent benefits from the closest rate. Without it, the NKM clock's rate might drift away from the requested rate in the multiple successive calls to ccu_nkm_determine_rate that the clk framework performs when setting a clock rate. Therefore, configure pll-video0 and, in consequence, all of its descendents to select the closest rate. Acked-by: Maxime Ripard Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-10-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 38 +++++++++++---------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c index 125ae097d96c..8951ffc14ff5 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c @@ -68,7 +68,7 @@ static SUNXI_CCU_NM_WITH_SDM_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", BIT(28), /* lock */ CLK_SET_RATE_UNGATE); -static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX(pll_video0_clk, "pll-video0", +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK_MIN_MAX_CLOSEST(pll_video0_clk, "pll-video0", "osc24M", 0x010, 192000000, /* Minimum rate */ 1008000000, /* Maximum rate */ @@ -181,6 +181,7 @@ static struct ccu_nkm pll_mipi_clk = { .hw.init = CLK_HW_INIT("pll-mipi", "pll-video0", &ccu_nkm_ops, CLK_SET_RATE_UNGATE | CLK_SET_RATE_PARENT), + .features = CCU_FEATURE_CLOSEST_RATE, }, }; @@ -537,25 +538,18 @@ static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, static const char * const tcon0_parents[] = { "pll-mipi", "pll-video0-2x" }; static const u8 tcon0_table[] = { 0, 2, }; -static SUNXI_CCU_MUX_TABLE_WITH_GATE(tcon0_clk, "tcon0", tcon0_parents, - tcon0_table, 0x118, 24, 3, BIT(31), - CLK_SET_RATE_PARENT | - CLK_SET_RATE_NO_REPARENT); +static SUNXI_CCU_MUX_TABLE_WITH_GATE_CLOSEST(tcon0_clk, "tcon0", tcon0_parents, + tcon0_table, 0x118, 24, 3, BIT(31), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT); static const char * const tcon1_parents[] = { "pll-video0", "pll-video1" }; static const u8 tcon1_table[] = { 0, 2, }; -static struct ccu_div tcon1_clk = { - .enable = BIT(31), - .div = _SUNXI_CCU_DIV(0, 4), - .mux = _SUNXI_CCU_MUX_TABLE(24, 2, tcon1_table), - .common = { - .reg = 0x11c, - .hw.init = CLK_HW_INIT_PARENTS("tcon1", - tcon1_parents, - &ccu_div_ops, - CLK_SET_RATE_PARENT), - }, -}; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(tcon1_clk, "tcon1", tcon1_parents, + tcon1_table, 0x11c, + 0, 4, /* M */ + 24, 2, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); static const char * const deinterlace_parents[] = { "pll-periph0", "pll-periph1" }; static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", deinterlace_parents, @@ -585,8 +579,8 @@ static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", 0x144, BIT(31), 0); static const char * const hdmi_parents[] = { "pll-video0", "pll-video1" }; -static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents, - 0x150, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_MUX_GATE_CLOSEST(hdmi_clk, "hdmi", hdmi_parents, + 0x150, 0, 4, 24, 2, BIT(31), CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(hdmi_ddc_clk, "hdmi-ddc", "osc24M", 0x154, BIT(31), 0); @@ -598,9 +592,9 @@ static SUNXI_CCU_M_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, static const char * const dsi_dphy_parents[] = { "pll-video0", "pll-periph0" }; static const u8 dsi_dphy_table[] = { 0, 2, }; -static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(dsi_dphy_clk, "dsi-dphy", - dsi_dphy_parents, dsi_dphy_table, - 0x168, 0, 4, 8, 2, BIT(15), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE_CLOSEST(dsi_dphy_clk, "dsi-dphy", + dsi_dphy_parents, dsi_dphy_table, + 0x168, 0, 4, 8, 2, BIT(15), CLK_SET_RATE_PARENT); static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", 0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT); From a69f9463397e56666b113eebb2e99956aacf6929 Mon Sep 17 00:00:00 2001 From: Frank Oltmanns Date: Mon, 7 Aug 2023 14:43:44 +0200 Subject: [PATCH 11/11] clk: sunxi-ng: nkm: Prefer current parent rate Similar to ccu_mp, if the current parent rate allows getting the ideal rate, prefer to not change the parent clock's rate. Reviewed-by: Jernej Skrabec Signed-off-by: Frank Oltmanns Link: https://lore.kernel.org/r/20230807-pll-mipi_set_rate_parent-v6-11-f173239a4b59@oltmanns.dev Signed-off-by: Chen-Yu Tsai --- drivers/clk/sunxi-ng/ccu_nkm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index a714dcf0dfc1..eed64547ad42 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -34,7 +34,8 @@ static unsigned long ccu_nkm_find_best_with_parent_adj(struct ccu_common *common tmp_rate = tmp_parent * _n * _k / _m; - if (ccu_is_better_rate(common, rate, tmp_rate, best_rate)) { + if (ccu_is_better_rate(common, rate, tmp_rate, best_rate) || + (tmp_parent == *parent && tmp_rate == best_rate)) { best_rate = tmp_rate; best_parent_rate = tmp_parent; best_n = _n;