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/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.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, \ 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 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)