diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c index 65e6de030717af..b2c426b629a37a 100644 --- a/drivers/clk/spacemit/ccu-k1.c +++ b/drivers/clk/spacemit/ccu-k1.c @@ -136,13 +136,34 @@ 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_FACTOR_DEFINE(i2s_bclk_factor, CCU_PARENT_HW(i2s_sysclk), 2, 1); +/* + * i2s_bclk is a non-linear discrete divider clock. + * Using i2s_bclk_factor as its parent simplifies software handling + * and avoids dealing with the non-linear division directly. + */ +CCU_DIV_GATE_DEFINE(i2s_bclk, CCU_PARENT_HW(i2s_bclk_factor), MPMU_ISCCR, 27, 2, BIT(29), 0); static const struct clk_parent_data apb_parents[] = { CCU_PARENT_HW(pll1_d96_25p6), @@ -756,6 +777,10 @@ 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, + [CLK_I2S_BCLK_FACTOR] = &i2s_bclk_factor.common.hw, }; static const struct spacemit_ccu_data k1_ccu_mpmu_data = { 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) diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h index 54d40cd39b2752..5b5c3bb958167a 100644 --- a/drivers/clk/spacemit/ccu_mix.h +++ b/drivers/clk/spacemit/ccu_mix.h @@ -130,7 +130,7 @@ static struct ccu_mix _name = { \ } #define CCU_DIV_GATE_DEFINE(_name, _parent, _reg_ctrl, _shift, _width, \ - _mask_gate, _flags) \ + _mask_gate, _flags) \ static struct ccu_mix _name = { \ .gate = CCU_GATE_INIT(_mask_gate), \ .div = CCU_DIV_INIT(_shift, _width), \ diff --git a/include/dt-bindings/clock/spacemit,k1-syscon.h b/include/dt-bindings/clock/spacemit,k1-syscon.h index 2714c3fe66cd5b..ad62525be43a90 100644 --- a/include/dt-bindings/clock/spacemit,k1-syscon.h +++ b/include/dt-bindings/clock/spacemit,k1-syscon.h @@ -77,6 +77,10 @@ #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 +#define CLK_I2S_BCLK_FACTOR 36 /* MPMU resets */ #define RESET_WDT 0 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)