From 5a23f13de04f32c77165d0ac23f26f1237d76d42 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Mon, 11 Aug 2025 22:04:27 +0800 Subject: [PATCH 1/4] dt-bindings: clock: spacemit: introduce i2s pre-clock to fix i2s clock Previously, the K1 clock driver did not include the parent clocks of the I2S sysclk. Otherwise, the I2S clock may not work as expected. This patch adds their definitions to allow proper registration in the driver and usage in the device tree. Fixes: 1b72c59db0add ("clk: spacemit: Add clock support for SpacemiT K1 SoC") Signed-off-by: Troy Mitchell Signed-off-by: Linux RISC-V bot --- include/dt-bindings/clock/spacemit,k1-syscon.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/dt-bindings/clock/spacemit,k1-syscon.h b/include/dt-bindings/clock/spacemit,k1-syscon.h index 2714c3fe66cd5b..524268246fedd3 100644 --- a/include/dt-bindings/clock/spacemit,k1-syscon.h +++ b/include/dt-bindings/clock/spacemit,k1-syscon.h @@ -77,6 +77,9 @@ #define CLK_I2S_BCLK 30 #define CLK_APB 31 #define CLK_WDT_BUS 32 +#define CLK_I2S_153P6 33 +#define CLK_I2S_153P6_BASE 34 +#define CLK_I2S_SYSCLK_SRC 35 /* MPMU resets */ #define RESET_WDT 0 From 953003ed53e99283e2aca8a30ce3ee6c3daa0973 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Mon, 11 Aug 2025 22:04:28 +0800 Subject: [PATCH 2/4] clk: spacemit: introduce pre-div for ddn clock The original DDN operations applied an implicit divide-by-2, which should not be a default behavior. This patch removes that assumption, letting each clock define its actual behavior explicitly. Signed-off-by: Troy Mitchell Signed-off-by: Linux RISC-V bot --- drivers/clk/spacemit/ccu_ddn.c | 12 ++++++------ drivers/clk/spacemit/ccu_ddn.h | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/clk/spacemit/ccu_ddn.c b/drivers/clk/spacemit/ccu_ddn.c index be311b045698e9..06d86748182bd1 100644 --- a/drivers/clk/spacemit/ccu_ddn.c +++ b/drivers/clk/spacemit/ccu_ddn.c @@ -22,21 +22,21 @@ #include "ccu_ddn.h" -static unsigned long ccu_ddn_calc_rate(unsigned long prate, - unsigned long num, unsigned long den) +static unsigned long ccu_ddn_calc_rate(unsigned long prate, unsigned long num, + unsigned long den, unsigned int pre_div) { - return prate * den / 2 / num; + return prate * den / pre_div / num; } static unsigned long ccu_ddn_calc_best_rate(struct ccu_ddn *ddn, unsigned long rate, unsigned long prate, unsigned long *num, unsigned long *den) { - rational_best_approximation(rate, prate / 2, + rational_best_approximation(rate, prate / ddn->pre_div, ddn->den_mask >> ddn->den_shift, ddn->num_mask >> ddn->num_shift, den, num); - return ccu_ddn_calc_rate(prate, *num, *den); + return ccu_ddn_calc_rate(prate, *num, *den, ddn->pre_div); } static long ccu_ddn_round_rate(struct clk_hw *hw, unsigned long rate, @@ -58,7 +58,7 @@ static unsigned long ccu_ddn_recalc_rate(struct clk_hw *hw, unsigned long prate) num = (val & ddn->num_mask) >> ddn->num_shift; den = (val & ddn->den_mask) >> ddn->den_shift; - return ccu_ddn_calc_rate(prate, num, den); + return ccu_ddn_calc_rate(prate, num, den, ddn->pre_div); } static int ccu_ddn_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/spacemit/ccu_ddn.h b/drivers/clk/spacemit/ccu_ddn.h index a52fabe77d62eb..4838414a8e8dc0 100644 --- a/drivers/clk/spacemit/ccu_ddn.h +++ b/drivers/clk/spacemit/ccu_ddn.h @@ -18,13 +18,14 @@ struct ccu_ddn { unsigned int num_shift; unsigned int den_mask; unsigned int den_shift; + unsigned int pre_div; }; #define CCU_DDN_INIT(_name, _parent, _flags) \ CLK_HW_INIT_HW(#_name, &_parent.common.hw, &spacemit_ccu_ddn_ops, _flags) #define CCU_DDN_DEFINE(_name, _parent, _reg_ctrl, _num_shift, _num_width, \ - _den_shift, _den_width, _flags) \ + _den_shift, _den_width, _pre_div, _flags) \ static struct ccu_ddn _name = { \ .common = { \ .reg_ctrl = _reg_ctrl, \ @@ -33,7 +34,8 @@ static struct ccu_ddn _name = { \ .num_mask = GENMASK(_num_shift + _num_width - 1, _num_shift), \ .num_shift = _num_shift, \ .den_mask = GENMASK(_den_shift + _den_width - 1, _den_shift), \ - .den_shift = _den_shift, \ + .den_shift = _den_shift, \ + .pre_div = _pre_div, \ } static inline struct ccu_ddn *hw_to_ccu_ddn(struct clk_hw *hw) From f67bac6fda4a7421c68c94dddf680604c737cede Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Mon, 11 Aug 2025 22:04:29 +0800 Subject: [PATCH 3/4] clk: spacemit: introduce factor for div clock From the definition of register, The i2s_bclk is a non-linear, discrete divider clock. The following table shows the correspondence between index and frequency division coefficients: | index | div | |-------|-------| | 0 | 2 | | 1 | 4 | | 2 | 6 | | 3 | 8 | But from a software perspective and this table, dividing the actual div value by 2 is sufficient to obtain a continuous divider clock. Rather than introducing a new clock type to handle this case, a factor parameter has been added to CCU_DIV_GATE_DEFINE. Suggested-by: Haylen Chu Signed-off-by: Troy Mitchell Signed-off-by: Linux RISC-V bot --- drivers/clk/spacemit/ccu_mix.c | 7 ++++++- drivers/clk/spacemit/ccu_mix.h | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c index 9b852aa61f78ae..dbd2cf234bf81d 100644 --- a/drivers/clk/spacemit/ccu_mix.c +++ b/drivers/clk/spacemit/ccu_mix.c @@ -56,7 +56,10 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, val = ccu_read(&mix->common, ctrl) >> div->shift; val &= (1 << div->width) - 1; - return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width); + if (!div->factor) + return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width); + + return divider_recalc_rate(hw, parent_rate, val, NULL, 0, div->width) / div->factor; } /* @@ -115,6 +118,8 @@ ccu_mix_calc_best_rate(struct clk_hw *hw, unsigned long rate, for (int j = 1; j <= div_max; j++) { unsigned long tmp = DIV_ROUND_CLOSEST_ULL(parent_rate, j); + if (mix->div.factor) + tmp /= mix->div.factor; if (abs(tmp - rate) < abs(best_rate - rate)) { best_rate = tmp; diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h index 54d40cd39b2752..7dd00d24ec4b1d 100644 --- a/drivers/clk/spacemit/ccu_mix.h +++ b/drivers/clk/spacemit/ccu_mix.h @@ -34,6 +34,7 @@ struct ccu_mux_config { struct ccu_div_config { u8 shift; u8 width; + unsigned int factor; }; struct ccu_mix { @@ -130,10 +131,11 @@ static struct ccu_mix _name = { \ } #define CCU_DIV_GATE_DEFINE(_name, _parent, _reg_ctrl, _shift, _width, \ - _mask_gate, _flags) \ + _mask_gate, _factor, _flags) \ static struct ccu_mix _name = { \ .gate = CCU_GATE_INIT(_mask_gate), \ .div = CCU_DIV_INIT(_shift, _width), \ + .div.factor = _factor, \ .common = { \ .reg_ctrl = _reg_ctrl, \ CCU_MIX_INITHW(_name, _parent, spacemit_ccu_div_gate_ops, \ From ffcd34d8193607520a3c0fde2ebb06991436847b Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Mon, 11 Aug 2025 22:04:30 +0800 Subject: [PATCH 4/4] clk: spacemit: fix i2s clock Defining i2s_bclk and i2s_sysclk as fixed-factor clocks is insufficient for real I2S use cases. Moreover, the current I2S clock configuration does not work as expected due to missing parent clocks. This patch adds the missing parent clocks, defines i2s_sysclk as a DDN clock, and i2s_bclk as a DIV clock. The I2S-related clock registers can be found here [1]. Link: https://developer.spacemit.com/documentation?token=LCrKwWDasiJuROkVNusc2pWTnEb [1] Fixes: 1b72c59db0add ("clk: spacemit: Add clock support for SpacemiT K1 SoC") Co-developer: Jinmei Wei Signed-off-by: Jinmei Wei Signed-off-by: Troy Mitchell Signed-off-by: Linux RISC-V bot --- drivers/clk/spacemit/ccu-k1.c | 28 +++++++++++++++++++++++----- include/soc/spacemit/k1-syscon.h | 1 + 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c index 65e6de030717af..3a885d64fb0914 100644 --- a/drivers/clk/spacemit/ccu-k1.c +++ b/drivers/clk/spacemit/ccu-k1.c @@ -136,13 +136,28 @@ CCU_GATE_DEFINE(pll1_d3_819p2, CCU_PARENT_HW(pll1_d3), MPMU_ACGR, BIT(14), 0); CCU_GATE_DEFINE(pll1_d2_1228p8, CCU_PARENT_HW(pll1_d2), MPMU_ACGR, BIT(16), 0); CCU_GATE_DEFINE(slow_uart, CCU_PARENT_NAME(osc), MPMU_ACGR, BIT(1), CLK_IGNORE_UNUSED); -CCU_DDN_DEFINE(slow_uart1_14p74, pll1_d16_153p6, MPMU_SUCCR, 16, 13, 0, 13, 0); -CCU_DDN_DEFINE(slow_uart2_48, pll1_d4_614p4, MPMU_SUCCR_1, 16, 13, 0, 13, 0); +CCU_DDN_DEFINE(slow_uart1_14p74, pll1_d16_153p6, MPMU_SUCCR, 16, 13, 0, 13, 2, 0); +CCU_DDN_DEFINE(slow_uart2_48, pll1_d4_614p4, MPMU_SUCCR_1, 16, 13, 0, 13, 2, 0); CCU_GATE_DEFINE(wdt_clk, CCU_PARENT_HW(pll1_d96_25p6), MPMU_WDTPCR, BIT(1), 0); -CCU_FACTOR_GATE_DEFINE(i2s_sysclk, CCU_PARENT_HW(pll1_d16_153p6), MPMU_ISCCR, BIT(31), 50, 1); -CCU_FACTOR_GATE_DEFINE(i2s_bclk, CCU_PARENT_HW(i2s_sysclk), MPMU_ISCCR, BIT(29), 1, 1); +CCU_FACTOR_DEFINE(i2s_153p6, CCU_PARENT_HW(pll1_d8_307p2), 2, 1); + +static const struct clk_parent_data i2s_153p6_base_parents[] = { + CCU_PARENT_HW(i2s_153p6), + CCU_PARENT_HW(pll1_d8_307p2), +}; +CCU_MUX_DEFINE(i2s_153p6_base, i2s_153p6_base_parents, MPMU_FCCR, 29, 1, 0); + +static const struct clk_parent_data i2s_sysclk_src_parents[] = { + CCU_PARENT_HW(pll1_d96_25p6), + CCU_PARENT_HW(i2s_153p6_base) +}; +CCU_MUX_GATE_DEFINE(i2s_sysclk_src, i2s_sysclk_src_parents, MPMU_ISCCR, 30, 1, BIT(31), 0); + +CCU_DDN_DEFINE(i2s_sysclk, i2s_sysclk_src, MPMU_ISCCR, 0, 15, 15, 12, 1, 0); + +CCU_DIV_GATE_DEFINE(i2s_bclk, CCU_PARENT_HW(i2s_sysclk), MPMU_ISCCR, 27, 2, BIT(29), 2, 0); static const struct clk_parent_data apb_parents[] = { CCU_PARENT_HW(pll1_d96_25p6), @@ -639,7 +654,7 @@ static const struct clk_parent_data emmc_parents[] = { CCU_MUX_DIV_GATE_FC_DEFINE(emmc_clk, emmc_parents, APMU_PMUA_EM_CLK_RES_CTRL, 8, 3, BIT(11), 6, 2, BIT(4), 0); CCU_DIV_GATE_DEFINE(emmc_x_clk, CCU_PARENT_HW(pll1_d2_1228p8), APMU_PMUA_EM_CLK_RES_CTRL, 12, - 3, BIT(15), 0); + 3, BIT(15), 1, 0); static const struct clk_parent_data audio_parents[] = { CCU_PARENT_HW(pll1_aud_245p7), @@ -756,6 +771,9 @@ static struct clk_hw *k1_ccu_mpmu_hws[] = { [CLK_I2S_BCLK] = &i2s_bclk.common.hw, [CLK_APB] = &apb_clk.common.hw, [CLK_WDT_BUS] = &wdt_bus_clk.common.hw, + [CLK_I2S_153P6] = &i2s_153p6.common.hw, + [CLK_I2S_153P6_BASE] = &i2s_153p6_base.common.hw, + [CLK_I2S_SYSCLK_SRC] = &i2s_sysclk_src.common.hw, }; static const struct spacemit_ccu_data k1_ccu_mpmu_data = { diff --git a/include/soc/spacemit/k1-syscon.h b/include/soc/spacemit/k1-syscon.h index c59bd7a38e5b42..354751562c5552 100644 --- a/include/soc/spacemit/k1-syscon.h +++ b/include/soc/spacemit/k1-syscon.h @@ -30,6 +30,7 @@ to_spacemit_ccu_adev(struct auxiliary_device *adev) /* MPMU register offset */ #define MPMU_POSR 0x0010 +#define MPMU_FCCR 0x0008 #define POSR_PLL1_LOCK BIT(27) #define POSR_PLL2_LOCK BIT(28) #define POSR_PLL3_LOCK BIT(29)