* [PATCH RESEND v4 2/3] clk: spacemit: introduce pre-div for ddn clock
2025-09-11 3:34 [PATCH RESEND v4 0/3] clk: spacemit: fix i2s clock Troy Mitchell
2025-09-11 3:34 ` [PATCH RESEND v4 1/3] dt-bindings: clock: spacemit: introduce i2s pre-clock to " Troy Mitchell
@ 2025-09-11 3:34 ` Troy Mitchell
2025-09-20 5:55 ` Stephen Boyd
2025-09-11 3:34 ` [PATCH RESEND v4 3/3] clk: spacemit: fix i2s clock Troy Mitchell
2 siblings, 1 reply; 7+ messages in thread
From: Troy Mitchell @ 2025-09-11 3:34 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yixun Lan, Alex Elder, Haylen Chu, Inochi Amaoto
Cc: linux-clk, devicetree, linux-riscv, spacemit, linux-kernel,
Jinmei Wei, Troy Mitchell
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.
Reviewed-by: Haylen Chu <heylenay@4d2.org>
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
drivers/clk/spacemit/ccu-k1.c | 4 ++--
drivers/clk/spacemit/ccu_ddn.c | 12 ++++++------
drivers/clk/spacemit/ccu_ddn.h | 6 ++++--
3 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c
index 65e6de030717afa60eefab7bda88f9a13b857650..7155824673fb450971439873b6b6163faf48c7e5 100644
--- a/drivers/clk/spacemit/ccu-k1.c
+++ b/drivers/clk/spacemit/ccu-k1.c
@@ -136,8 +136,8 @@ 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);
diff --git a/drivers/clk/spacemit/ccu_ddn.c b/drivers/clk/spacemit/ccu_ddn.c
index be311b045698e95a688a35858a8ac1bcfbffd2c7..06d86748182bd1959cdab5c18d0a882ee25dcade 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 a52fabe77d62eba16426867a9c13481e72f025c0..4838414a8e8dc04af49d3b8d39280efedbd75616 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)
--
2.51.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH RESEND v4 3/3] clk: spacemit: fix i2s clock
2025-09-11 3:34 [PATCH RESEND v4 0/3] clk: spacemit: fix i2s clock Troy Mitchell
2025-09-11 3:34 ` [PATCH RESEND v4 1/3] dt-bindings: clock: spacemit: introduce i2s pre-clock to " Troy Mitchell
2025-09-11 3:34 ` [PATCH RESEND v4 2/3] clk: spacemit: introduce pre-div for ddn clock Troy Mitchell
@ 2025-09-11 3:34 ` Troy Mitchell
2025-09-20 5:55 ` Stephen Boyd
2 siblings, 1 reply; 7+ messages in thread
From: Troy Mitchell @ 2025-09-11 3:34 UTC (permalink / raw)
To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Yixun Lan, Alex Elder, Haylen Chu, Inochi Amaoto
Cc: linux-clk, devicetree, linux-riscv, spacemit, linux-kernel,
Jinmei Wei, Troy Mitchell
Defining i2s_bclk and i2s_sysclk as fixed-rate 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.
A special note for i2s_bclk:
From the register definition, the i2s_bclk divider always implies
an additional 1/2 factor.
The following table shows the correspondence between index
and frequency division coefficients:
| index | div |
|-------|-------|
| 0 | 2 |
| 1 | 4 |
| 2 | 6 |
| 3 | 8 |
From a software perspective, introducing i2s_bclk_factor as the
parent of i2s_bclk is sufficient to address the issue.
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 <weijinmei@linux.spacemit.com>
Suggested-by: Haylen Chu <heylenay@4d2.org>
Signed-off-by: Jinmei Wei <weijinmei@linux.spacemit.com>
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
drivers/clk/spacemit/ccu-k1.c | 28 ++++++++++++++++++++++++++--
include/soc/spacemit/k1-syscon.h | 1 +
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c
index 7155824673fb450971439873b6b6163faf48c7e5..50b472a2721121414f33e9fac6370f544e6b8229 100644
--- a/drivers/clk/spacemit/ccu-k1.c
+++ b/drivers/clk/spacemit/ccu-k1.c
@@ -141,8 +141,28 @@ 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);
+/*
+ * Divider of i2s_bclk always implies a 1/2 factor, which is
+ * described by i2s_bclk_factor.
+ */
+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 +776,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/include/soc/spacemit/k1-syscon.h b/include/soc/spacemit/k1-syscon.h
index c59bd7a38e5b4219121341b9c0d9ffda13a9c3e2..354751562c55523ef8a22be931ddd8aca9651084 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)
--
2.51.0
^ permalink raw reply related [flat|nested] 7+ messages in thread