All of lore.kernel.org
 help / color / mirror / Atom feed
From: Troy Mitchell <troy.mitchell@linux.spacemit.com>
To: Stephen Boyd <sboyd@kernel.org>,
	Brian Masney <bmasney+clk@redhat.com>,
	 Jerome Brunet <jbrunet+clk@baylibre.com>,
	Yixun Lan <dlan@kernel.org>,  Alex Elder <elder@riscstar.com>,
	Inochi Amaoto <inochiama@outlook.com>,
	 Haylen Chu <heylenay@4d2.org>
Cc: linux-clk@vger.kernel.org, linux-riscv@lists.infradead.org,
	 spacemit@lists.linux.dev, linux-kernel@vger.kernel.org,
	 Troy Mitchell <troy.mitchell@linux.spacemit.com>
Subject: [PATCH 3/5] clk: spacemit: describe CPU clock dividers and shared PLL muxes
Date: Wed, 09 Sep 2026 22:07:03 +0800	[thread overview]
Message-ID: <20260909-spacemit-pll-init-v1-3-b3065ad5a4ac@linux.spacemit.com> (raw)
In-Reply-To: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com>

The K1 CPU clocks have a core divider, while K3 CPU clocks bypass theirs
for some sources. K3 also allows C1/C3 to share the C0/C2 PLLs. Omitting
these details from the clock model can give CCF an incorrect rate or
parent.

Model the dividers, K3 bypass conditions and shared parents. Register
the sharing muxes as internal clocks without adding DT clock IDs.
Exclude K3 selector 4 from the modeled parents to match BSP policy.

Fixes: 1b72c59db0ad ("clk: spacemit: Add clock support for SpacemiT K1 SoC")
Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree")
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
 drivers/clk/spacemit/ccu-k1.c     |  8 +++----
 drivers/clk/spacemit/ccu-k3.c     | 47 +++++++++++++++++++++++++++------------
 drivers/clk/spacemit/ccu_common.c | 12 ++++++++++
 drivers/clk/spacemit/ccu_common.h |  2 ++
 drivers/clk/spacemit/ccu_mix.c    | 10 ++++++++-
 drivers/clk/spacemit/ccu_mix.h    | 18 +++++++++++++++
 6 files changed, 78 insertions(+), 19 deletions(-)

diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c
index dee14d25f75d4..40cc96c4ee876 100644
--- a/drivers/clk/spacemit/ccu-k1.c
+++ b/drivers/clk/spacemit/ccu-k1.c
@@ -391,8 +391,8 @@ static const struct clk_parent_data cpu_c0_clk_parents[] = {
 	CCU_PARENT_HW(pll2_d3),
 	CCU_PARENT_HW(cpu_c0_hi_clk),
 };
-CCU_MUX_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTRL, BIT(12), 0, 3,
-		  CLK_IS_CRITICAL);
+CCU_MUX_DIV_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTRL,
+		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
 CCU_DIV_DEFINE(cpu_c0_ace_clk, CCU_PARENT_HW(cpu_c0_core_clk), APMU_CPU_C0_CLK_CTRL, 6, 3,
 	       CLK_IS_CRITICAL);
 CCU_DIV_DEFINE(cpu_c0_tcm_clk, CCU_PARENT_HW(cpu_c0_core_clk), APMU_CPU_C0_CLK_CTRL, 9, 3,
@@ -413,8 +413,8 @@ static const struct clk_parent_data cpu_c1_clk_parents[] = {
 	CCU_PARENT_HW(pll2_d3),
 	CCU_PARENT_HW(cpu_c1_hi_clk),
 };
-CCU_MUX_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTRL, BIT(12), 0, 3,
-		  CLK_IS_CRITICAL);
+CCU_MUX_DIV_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTRL,
+		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
 CCU_DIV_DEFINE(cpu_c1_ace_clk, CCU_PARENT_HW(cpu_c1_core_clk), APMU_CPU_C1_CLK_CTRL, 6, 3,
 	       CLK_IS_CRITICAL);
 
diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c
index 92b930d5ff305..2f0bd74f7da4d 100644
--- a/drivers/clk/spacemit/ccu-k3.c
+++ b/drivers/clk/spacemit/ccu-k3.c
@@ -587,52 +587,64 @@ static const struct clk_parent_data cpu_c0_clk_parents[] = {
 	CCU_PARENT_HW(pll1_d5_491p52),
 	CCU_PARENT_HW(pll1_d4_614p4),
 	CCU_PARENT_HW(pll2_d3),
-	CCU_PARENT_HW(pll3_d2),
+	{ .index = -1 }, /* Leave selector 4 unmodeled, as in the BSP. */
 	CCU_PARENT_HW(pll1_d2_1228p8),
 	CCU_PARENT_HW(pll2_d2),
 	CCU_PARENT_HW(pll3_d1),
 };
-CCU_MUX_DIV_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTRL,
-		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTRL,
+			     3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4));
+
+static const struct clk_parent_data cpu_c1_pll_src_parents[] = {
+	CCU_PARENT_HW(pll4_d1),
+	CCU_PARENT_HW(pll3_d1),
+};
+CCU_MUX_DEFINE(cpu_c1_pll_src, cpu_c1_pll_src_parents, APMU_CPU_C1_CLK_CTRL, 13, 1, 0);
 
 static const struct clk_parent_data cpu_c1_clk_parents[] = {
 	CCU_PARENT_HW(pll1_d3_819p2),
 	CCU_PARENT_HW(pll1_d5_491p52),
 	CCU_PARENT_HW(pll1_d4_614p4),
 	CCU_PARENT_HW(pll2_d3),
-	CCU_PARENT_HW(pll4_d2),
+	{ .index = -1 }, /* Leave selector 4 unmodeled, as in the BSP. */
 	CCU_PARENT_HW(pll1_d2_1228p8),
 	CCU_PARENT_HW(pll2_d2),
-	CCU_PARENT_HW(pll4_d1),
+	CCU_PARENT_HW(cpu_c1_pll_src),
 };
-CCU_MUX_DIV_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTRL,
-		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTRL,
+			     3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4));
 
 static const struct clk_parent_data cpu_c2_clk_parents[] = {
 	CCU_PARENT_HW(pll1_d3_819p2),
 	CCU_PARENT_HW(pll1_d5_491p52),
 	CCU_PARENT_HW(pll1_d4_614p4),
 	CCU_PARENT_HW(pll2_d3),
-	CCU_PARENT_HW(pll5_d2),
+	{ .index = -1 }, /* Leave selector 4 unmodeled, as in the BSP. */
 	CCU_PARENT_HW(pll1_d2_1228p8),
 	CCU_PARENT_HW(pll2_d2),
 	CCU_PARENT_HW(pll5_d1),
 };
-CCU_MUX_DIV_FC_DEFINE(cpu_c2_core_clk, cpu_c2_clk_parents, APMU_CPU_C2_CLK_CTRL,
-		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c2_core_clk, cpu_c2_clk_parents, APMU_CPU_C2_CLK_CTRL,
+			     3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4));
+
+static const struct clk_parent_data cpu_c3_pll_src_parents[] = {
+	CCU_PARENT_HW(pll8_d1),
+	CCU_PARENT_HW(pll5_d1),
+};
+CCU_MUX_DEFINE(cpu_c3_pll_src, cpu_c3_pll_src_parents, APMU_CPU_C3_CLK_CTRL, 13, 1, 0);
 
 static const struct clk_parent_data cpu_c3_clk_parents[] = {
 	CCU_PARENT_HW(pll1_d3_819p2),
 	CCU_PARENT_HW(pll1_d5_491p52),
 	CCU_PARENT_HW(pll1_d4_614p4),
 	CCU_PARENT_HW(pll2_d3),
-	CCU_PARENT_HW(pll8_d2),
+	{ .index = -1 }, /* Leave selector 4 unmodeled, as in the BSP. */
 	CCU_PARENT_HW(pll1_d2_1228p8),
 	CCU_PARENT_HW(pll2_d2),
-	CCU_PARENT_HW(pll8_d1),
+	CCU_PARENT_HW(cpu_c3_pll_src),
 };
-CCU_MUX_DIV_FC_DEFINE(cpu_c3_core_clk, cpu_c3_clk_parents, APMU_CPU_C3_CLK_CTRL,
-		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c3_core_clk, cpu_c3_clk_parents, APMU_CPU_C3_CLK_CTRL,
+			     3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4));
 
 static const struct clk_parent_data ccic2phy_parents[] = {
 	CCU_PARENT_HW(pll1_d24_102p4),
@@ -1447,10 +1459,17 @@ static struct clk_hw *k3_ccu_apmu_hws[] = {
 	[CLK_APMU_ISIM_VCLK3]		= &isim_vclk_out3.common.hw,
 };
 
+static struct clk_hw *k3_ccu_apmu_internal_hws[] = {
+	&cpu_c1_pll_src.common.hw,
+	&cpu_c3_pll_src.common.hw,
+};
+
 static const struct spacemit_ccu_data k3_ccu_apmu_data = {
 	.reset_name	= "k3-apmu-reset",
 	.hws		= k3_ccu_apmu_hws,
 	.num		= ARRAY_SIZE(k3_ccu_apmu_hws),
+	.internal_hws	= k3_ccu_apmu_internal_hws,
+	.num_internal	= ARRAY_SIZE(k3_ccu_apmu_internal_hws),
 };
 
 static struct clk_hw *k3_ccu_dciu_hws[] = {
diff --git a/drivers/clk/spacemit/ccu_common.c b/drivers/clk/spacemit/ccu_common.c
index 8696bb9cba2d8..759da6988ca78 100644
--- a/drivers/clk/spacemit/ccu_common.c
+++ b/drivers/clk/spacemit/ccu_common.c
@@ -30,6 +30,18 @@ static int spacemit_ccu_register(struct device *dev,
 
 	clk_data->num = data->num;
 
+	/* Internal muxes have no binding IDs but must precede their children. */
+	for (i = 0; i < data->num_internal; i++) {
+		struct clk_hw *hw = data->internal_hws[i];
+		struct ccu_common *common = hw_to_ccu_common(hw);
+
+		common->regmap = regmap;
+		common->lock_regmap = lock_regmap;
+		ret = devm_clk_hw_register(dev, hw);
+		if (ret)
+			return ret;
+	}
+
 	for (i = 0; i < data->num; i++) {
 		struct clk_hw *hw = data->hws[i];
 		struct ccu_common *common;
diff --git a/drivers/clk/spacemit/ccu_common.h b/drivers/clk/spacemit/ccu_common.h
index 8691698e007d9..659dc68ab902a 100644
--- a/drivers/clk/spacemit/ccu_common.h
+++ b/drivers/clk/spacemit/ccu_common.h
@@ -43,6 +43,8 @@ struct spacemit_ccu_data {
 	const char *reset_name;
 	struct clk_hw **hws;
 	size_t num;
+	struct clk_hw **internal_hws;
+	size_t num_internal;
 };
 
 #define ccu_read(c, reg)						\
diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c
index da3c5685d4f65..68596b9fa5b8f 100644
--- a/drivers/clk/spacemit/ccu_mix.c
+++ b/drivers/clk/spacemit/ccu_mix.c
@@ -13,6 +13,8 @@
 #define MIX_FC_TIMEOUT_US	10000
 #define MIX_FC_DELAY_US		5
 
+static u8 ccu_mux_get_parent(struct clk_hw *hw);
+
 static void ccu_gate_disable(struct clk_hw *hw)
 {
 	struct ccu_mix *mix = hw_to_ccu_mix(hw);
@@ -57,6 +59,9 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
 	struct ccu_div_config *div = &mix->div;
 	unsigned long val;
 
+	if (div->bypass & BIT(ccu_mux_get_parent(hw)))
+		return parent_rate;
+
 	val = ccu_read(&mix->common, ctrl) >> div->shift;
 	val &= (1 << div->width) - 1;
 
@@ -113,7 +118,7 @@ ccu_mix_calc_best_rate(struct clk_hw *hw, unsigned long rate,
 	for (int i = 0; i < parent_num; i++) {
 		struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
 		unsigned long parent_rate;
-		u32 div_max = 1 << div->width;
+		u32 div_max = div->bypass & BIT(i) ? 1 : 1 << div->width;
 
 		if (!parent)
 			continue;
@@ -163,6 +168,9 @@ static int ccu_mix_set_rate(struct clk_hw *hw, unsigned long rate,
 	u32 current_div, target_div = 0, mask;
 	unsigned long best_delta = ULONG_MAX;
 
+	if (div->bypass & BIT(ccu_mux_get_parent(hw)))
+		return rate == parent_rate ? 0 : -EINVAL;
+
 	/* set_rate must use the parent selected by CCF, not search other parents. */
 	for (u32 i = 1; i <= BIT(div->width); i++) {
 		unsigned long divided = DIV_ROUND_UP_ULL(parent_rate, i);
diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h
index dbba9bf49b3bd..ac88c282d660a 100644
--- a/drivers/clk/spacemit/ccu_mix.h
+++ b/drivers/clk/spacemit/ccu_mix.h
@@ -36,6 +36,8 @@ struct ccu_mux_config {
 struct ccu_div_config {
 	u8 shift;
 	u8 width;
+	/* Parent indices which bypass the divider; zero means no bypass. */
+	u32 bypass;
 };
 
 struct ccu_mix {
@@ -203,6 +205,22 @@ static struct ccu_mix _name = {							\
 	},									\
 }
 
+#define CCU_MUX_DIV_BYPASS_FC_DEFINE(_name, _parents, _reg_ctrl,		\
+				     _mshift, _mwidth, _mask_fc,		\
+				     _muxshift, _muxwidth, _flags,		\
+				     _bypass)					\
+static struct ccu_mix _name = {							\
+	.div	= { .shift = _mshift, .width = _mwidth, .bypass = _bypass },	\
+	.mux	= CCU_MUX_INIT(_muxshift, _muxwidth),				\
+	.common = {								\
+		.reg_ctrl	= _reg_ctrl,					\
+		.reg_fc		= _reg_ctrl,					\
+		.mask_fc	= _mask_fc,					\
+		CCU_MIX_INITHW_PARENTS(_name, _parents,				\
+				       spacemit_ccu_mux_div_ops, _flags),	\
+	},									\
+}
+
 #define CCU_MUX_FC_DEFINE(_name, _parents, _reg_ctrl, _mask_fc,	_muxshift,	\
 			  _muxwidth, _flags)					\
 static struct ccu_mix _name = {							\

-- 
2.55.0


WARNING: multiple messages have this Message-ID (diff)
From: Troy Mitchell <troy.mitchell@linux.spacemit.com>
To: Stephen Boyd <sboyd@kernel.org>,
	Brian Masney <bmasney+clk@redhat.com>,
	 Jerome Brunet <jbrunet+clk@baylibre.com>,
	Yixun Lan <dlan@kernel.org>,  Alex Elder <elder@riscstar.com>,
	Inochi Amaoto <inochiama@outlook.com>,
	 Haylen Chu <heylenay@4d2.org>
Cc: linux-clk@vger.kernel.org, linux-riscv@lists.infradead.org,
	 spacemit@lists.linux.dev, linux-kernel@vger.kernel.org,
	 Troy Mitchell <troy.mitchell@linux.spacemit.com>
Subject: [PATCH 3/5] clk: spacemit: describe CPU clock dividers and shared PLL muxes
Date: Wed, 09 Sep 2026 22:07:03 +0800	[thread overview]
Message-ID: <20260909-spacemit-pll-init-v1-3-b3065ad5a4ac@linux.spacemit.com> (raw)
In-Reply-To: <20260909-spacemit-pll-init-v1-0-b3065ad5a4ac@linux.spacemit.com>

The K1 CPU clocks have a core divider, while K3 CPU clocks bypass theirs
for some sources. K3 also allows C1/C3 to share the C0/C2 PLLs. Omitting
these details from the clock model can give CCF an incorrect rate or
parent.

Model the dividers, K3 bypass conditions and shared parents. Register
the sharing muxes as internal clocks without adding DT clock IDs.
Exclude K3 selector 4 from the modeled parents to match BSP policy.

Fixes: 1b72c59db0ad ("clk: spacemit: Add clock support for SpacemiT K1 SoC")
Fixes: e371a77255b8 ("clk: spacemit: k3: add the clock tree")
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
 drivers/clk/spacemit/ccu-k1.c     |  8 +++----
 drivers/clk/spacemit/ccu-k3.c     | 47 +++++++++++++++++++++++++++------------
 drivers/clk/spacemit/ccu_common.c | 12 ++++++++++
 drivers/clk/spacemit/ccu_common.h |  2 ++
 drivers/clk/spacemit/ccu_mix.c    | 10 ++++++++-
 drivers/clk/spacemit/ccu_mix.h    | 18 +++++++++++++++
 6 files changed, 78 insertions(+), 19 deletions(-)

diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c
index dee14d25f75d4..40cc96c4ee876 100644
--- a/drivers/clk/spacemit/ccu-k1.c
+++ b/drivers/clk/spacemit/ccu-k1.c
@@ -391,8 +391,8 @@ static const struct clk_parent_data cpu_c0_clk_parents[] = {
 	CCU_PARENT_HW(pll2_d3),
 	CCU_PARENT_HW(cpu_c0_hi_clk),
 };
-CCU_MUX_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTRL, BIT(12), 0, 3,
-		  CLK_IS_CRITICAL);
+CCU_MUX_DIV_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTRL,
+		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
 CCU_DIV_DEFINE(cpu_c0_ace_clk, CCU_PARENT_HW(cpu_c0_core_clk), APMU_CPU_C0_CLK_CTRL, 6, 3,
 	       CLK_IS_CRITICAL);
 CCU_DIV_DEFINE(cpu_c0_tcm_clk, CCU_PARENT_HW(cpu_c0_core_clk), APMU_CPU_C0_CLK_CTRL, 9, 3,
@@ -413,8 +413,8 @@ static const struct clk_parent_data cpu_c1_clk_parents[] = {
 	CCU_PARENT_HW(pll2_d3),
 	CCU_PARENT_HW(cpu_c1_hi_clk),
 };
-CCU_MUX_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTRL, BIT(12), 0, 3,
-		  CLK_IS_CRITICAL);
+CCU_MUX_DIV_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTRL,
+		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
 CCU_DIV_DEFINE(cpu_c1_ace_clk, CCU_PARENT_HW(cpu_c1_core_clk), APMU_CPU_C1_CLK_CTRL, 6, 3,
 	       CLK_IS_CRITICAL);
 
diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c
index 92b930d5ff305..2f0bd74f7da4d 100644
--- a/drivers/clk/spacemit/ccu-k3.c
+++ b/drivers/clk/spacemit/ccu-k3.c
@@ -587,52 +587,64 @@ static const struct clk_parent_data cpu_c0_clk_parents[] = {
 	CCU_PARENT_HW(pll1_d5_491p52),
 	CCU_PARENT_HW(pll1_d4_614p4),
 	CCU_PARENT_HW(pll2_d3),
-	CCU_PARENT_HW(pll3_d2),
+	{ .index = -1 }, /* Leave selector 4 unmodeled, as in the BSP. */
 	CCU_PARENT_HW(pll1_d2_1228p8),
 	CCU_PARENT_HW(pll2_d2),
 	CCU_PARENT_HW(pll3_d1),
 };
-CCU_MUX_DIV_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTRL,
-		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c0_core_clk, cpu_c0_clk_parents, APMU_CPU_C0_CLK_CTRL,
+			     3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4));
+
+static const struct clk_parent_data cpu_c1_pll_src_parents[] = {
+	CCU_PARENT_HW(pll4_d1),
+	CCU_PARENT_HW(pll3_d1),
+};
+CCU_MUX_DEFINE(cpu_c1_pll_src, cpu_c1_pll_src_parents, APMU_CPU_C1_CLK_CTRL, 13, 1, 0);
 
 static const struct clk_parent_data cpu_c1_clk_parents[] = {
 	CCU_PARENT_HW(pll1_d3_819p2),
 	CCU_PARENT_HW(pll1_d5_491p52),
 	CCU_PARENT_HW(pll1_d4_614p4),
 	CCU_PARENT_HW(pll2_d3),
-	CCU_PARENT_HW(pll4_d2),
+	{ .index = -1 }, /* Leave selector 4 unmodeled, as in the BSP. */
 	CCU_PARENT_HW(pll1_d2_1228p8),
 	CCU_PARENT_HW(pll2_d2),
-	CCU_PARENT_HW(pll4_d1),
+	CCU_PARENT_HW(cpu_c1_pll_src),
 };
-CCU_MUX_DIV_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTRL,
-		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c1_core_clk, cpu_c1_clk_parents, APMU_CPU_C1_CLK_CTRL,
+			     3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4));
 
 static const struct clk_parent_data cpu_c2_clk_parents[] = {
 	CCU_PARENT_HW(pll1_d3_819p2),
 	CCU_PARENT_HW(pll1_d5_491p52),
 	CCU_PARENT_HW(pll1_d4_614p4),
 	CCU_PARENT_HW(pll2_d3),
-	CCU_PARENT_HW(pll5_d2),
+	{ .index = -1 }, /* Leave selector 4 unmodeled, as in the BSP. */
 	CCU_PARENT_HW(pll1_d2_1228p8),
 	CCU_PARENT_HW(pll2_d2),
 	CCU_PARENT_HW(pll5_d1),
 };
-CCU_MUX_DIV_FC_DEFINE(cpu_c2_core_clk, cpu_c2_clk_parents, APMU_CPU_C2_CLK_CTRL,
-		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c2_core_clk, cpu_c2_clk_parents, APMU_CPU_C2_CLK_CTRL,
+			     3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4));
+
+static const struct clk_parent_data cpu_c3_pll_src_parents[] = {
+	CCU_PARENT_HW(pll8_d1),
+	CCU_PARENT_HW(pll5_d1),
+};
+CCU_MUX_DEFINE(cpu_c3_pll_src, cpu_c3_pll_src_parents, APMU_CPU_C3_CLK_CTRL, 13, 1, 0);
 
 static const struct clk_parent_data cpu_c3_clk_parents[] = {
 	CCU_PARENT_HW(pll1_d3_819p2),
 	CCU_PARENT_HW(pll1_d5_491p52),
 	CCU_PARENT_HW(pll1_d4_614p4),
 	CCU_PARENT_HW(pll2_d3),
-	CCU_PARENT_HW(pll8_d2),
+	{ .index = -1 }, /* Leave selector 4 unmodeled, as in the BSP. */
 	CCU_PARENT_HW(pll1_d2_1228p8),
 	CCU_PARENT_HW(pll2_d2),
-	CCU_PARENT_HW(pll8_d1),
+	CCU_PARENT_HW(cpu_c3_pll_src),
 };
-CCU_MUX_DIV_FC_DEFINE(cpu_c3_core_clk, cpu_c3_clk_parents, APMU_CPU_C3_CLK_CTRL,
-		      3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL);
+CCU_MUX_DIV_BYPASS_FC_DEFINE(cpu_c3_core_clk, cpu_c3_clk_parents, APMU_CPU_C3_CLK_CTRL,
+			     3, 3, BIT(12), 0, 3, CLK_IS_CRITICAL, GENMASK(7, 4));
 
 static const struct clk_parent_data ccic2phy_parents[] = {
 	CCU_PARENT_HW(pll1_d24_102p4),
@@ -1447,10 +1459,17 @@ static struct clk_hw *k3_ccu_apmu_hws[] = {
 	[CLK_APMU_ISIM_VCLK3]		= &isim_vclk_out3.common.hw,
 };
 
+static struct clk_hw *k3_ccu_apmu_internal_hws[] = {
+	&cpu_c1_pll_src.common.hw,
+	&cpu_c3_pll_src.common.hw,
+};
+
 static const struct spacemit_ccu_data k3_ccu_apmu_data = {
 	.reset_name	= "k3-apmu-reset",
 	.hws		= k3_ccu_apmu_hws,
 	.num		= ARRAY_SIZE(k3_ccu_apmu_hws),
+	.internal_hws	= k3_ccu_apmu_internal_hws,
+	.num_internal	= ARRAY_SIZE(k3_ccu_apmu_internal_hws),
 };
 
 static struct clk_hw *k3_ccu_dciu_hws[] = {
diff --git a/drivers/clk/spacemit/ccu_common.c b/drivers/clk/spacemit/ccu_common.c
index 8696bb9cba2d8..759da6988ca78 100644
--- a/drivers/clk/spacemit/ccu_common.c
+++ b/drivers/clk/spacemit/ccu_common.c
@@ -30,6 +30,18 @@ static int spacemit_ccu_register(struct device *dev,
 
 	clk_data->num = data->num;
 
+	/* Internal muxes have no binding IDs but must precede their children. */
+	for (i = 0; i < data->num_internal; i++) {
+		struct clk_hw *hw = data->internal_hws[i];
+		struct ccu_common *common = hw_to_ccu_common(hw);
+
+		common->regmap = regmap;
+		common->lock_regmap = lock_regmap;
+		ret = devm_clk_hw_register(dev, hw);
+		if (ret)
+			return ret;
+	}
+
 	for (i = 0; i < data->num; i++) {
 		struct clk_hw *hw = data->hws[i];
 		struct ccu_common *common;
diff --git a/drivers/clk/spacemit/ccu_common.h b/drivers/clk/spacemit/ccu_common.h
index 8691698e007d9..659dc68ab902a 100644
--- a/drivers/clk/spacemit/ccu_common.h
+++ b/drivers/clk/spacemit/ccu_common.h
@@ -43,6 +43,8 @@ struct spacemit_ccu_data {
 	const char *reset_name;
 	struct clk_hw **hws;
 	size_t num;
+	struct clk_hw **internal_hws;
+	size_t num_internal;
 };
 
 #define ccu_read(c, reg)						\
diff --git a/drivers/clk/spacemit/ccu_mix.c b/drivers/clk/spacemit/ccu_mix.c
index da3c5685d4f65..68596b9fa5b8f 100644
--- a/drivers/clk/spacemit/ccu_mix.c
+++ b/drivers/clk/spacemit/ccu_mix.c
@@ -13,6 +13,8 @@
 #define MIX_FC_TIMEOUT_US	10000
 #define MIX_FC_DELAY_US		5
 
+static u8 ccu_mux_get_parent(struct clk_hw *hw);
+
 static void ccu_gate_disable(struct clk_hw *hw)
 {
 	struct ccu_mix *mix = hw_to_ccu_mix(hw);
@@ -57,6 +59,9 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
 	struct ccu_div_config *div = &mix->div;
 	unsigned long val;
 
+	if (div->bypass & BIT(ccu_mux_get_parent(hw)))
+		return parent_rate;
+
 	val = ccu_read(&mix->common, ctrl) >> div->shift;
 	val &= (1 << div->width) - 1;
 
@@ -113,7 +118,7 @@ ccu_mix_calc_best_rate(struct clk_hw *hw, unsigned long rate,
 	for (int i = 0; i < parent_num; i++) {
 		struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
 		unsigned long parent_rate;
-		u32 div_max = 1 << div->width;
+		u32 div_max = div->bypass & BIT(i) ? 1 : 1 << div->width;
 
 		if (!parent)
 			continue;
@@ -163,6 +168,9 @@ static int ccu_mix_set_rate(struct clk_hw *hw, unsigned long rate,
 	u32 current_div, target_div = 0, mask;
 	unsigned long best_delta = ULONG_MAX;
 
+	if (div->bypass & BIT(ccu_mux_get_parent(hw)))
+		return rate == parent_rate ? 0 : -EINVAL;
+
 	/* set_rate must use the parent selected by CCF, not search other parents. */
 	for (u32 i = 1; i <= BIT(div->width); i++) {
 		unsigned long divided = DIV_ROUND_UP_ULL(parent_rate, i);
diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h
index dbba9bf49b3bd..ac88c282d660a 100644
--- a/drivers/clk/spacemit/ccu_mix.h
+++ b/drivers/clk/spacemit/ccu_mix.h
@@ -36,6 +36,8 @@ struct ccu_mux_config {
 struct ccu_div_config {
 	u8 shift;
 	u8 width;
+	/* Parent indices which bypass the divider; zero means no bypass. */
+	u32 bypass;
 };
 
 struct ccu_mix {
@@ -203,6 +205,22 @@ static struct ccu_mix _name = {							\
 	},									\
 }
 
+#define CCU_MUX_DIV_BYPASS_FC_DEFINE(_name, _parents, _reg_ctrl,		\
+				     _mshift, _mwidth, _mask_fc,		\
+				     _muxshift, _muxwidth, _flags,		\
+				     _bypass)					\
+static struct ccu_mix _name = {							\
+	.div	= { .shift = _mshift, .width = _mwidth, .bypass = _bypass },	\
+	.mux	= CCU_MUX_INIT(_muxshift, _muxwidth),				\
+	.common = {								\
+		.reg_ctrl	= _reg_ctrl,					\
+		.reg_fc		= _reg_ctrl,					\
+		.mask_fc	= _mask_fc,					\
+		CCU_MIX_INITHW_PARENTS(_name, _parents,				\
+				       spacemit_ccu_mux_div_ops, _flags),	\
+	},									\
+}
+
 #define CCU_MUX_FC_DEFINE(_name, _parents, _reg_ctrl, _mask_fc,	_muxshift,	\
 			  _muxwidth, _flags)					\
 static struct ccu_mix _name = {							\

-- 
2.55.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

  parent reply	other threads:[~2026-09-09 14:08 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-09 14:07 [PATCH 0/5] clk: spacemit: preserve and safely synchronize firmware PLLs Troy Mitchell
2026-09-09 14:07 ` Troy Mitchell
2026-09-09 14:07 ` [PATCH 1/5] clk: spacemit: derive PLL rates from hardware Troy Mitchell
2026-09-09 14:07   ` Troy Mitchell
2026-09-09 14:07 ` [PATCH 2/5] clk: spacemit: make MIX rate selection consistent Troy Mitchell
2026-09-09 14:07   ` Troy Mitchell
2026-09-10 13:01   ` Yao Zi
2026-09-10 13:01     ` Yao Zi
2026-09-10 14:19     ` Troy Mitchell
2026-09-10 14:19       ` Troy Mitchell
2026-09-09 14:07 ` Troy Mitchell [this message]
2026-09-09 14:07   ` [PATCH 3/5] clk: spacemit: describe CPU clock dividers and shared PLL muxes Troy Mitchell
2026-09-09 14:21   ` sashiko-bot
2026-09-10  3:23     ` Troy Mitchell
2026-09-09 14:07 ` [PATCH 4/5] clk: spacemit: reject rate changes to running firmware PLLs Troy Mitchell
2026-09-09 14:07   ` Troy Mitchell
2026-09-09 14:22   ` sashiko-bot
2026-09-10 13:31   ` Yao Zi
2026-09-10 13:31     ` Yao Zi
2026-09-11  2:02     ` Troy Mitchell
2026-09-11  2:02       ` Troy Mitchell
2026-09-09 14:07 ` [PATCH 5/5] clk: spacemit: safely synchronize PLL parameters during init Troy Mitchell
2026-09-09 14:07   ` Troy Mitchell

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260909-spacemit-pll-init-v1-3-b3065ad5a4ac@linux.spacemit.com \
    --to=troy.mitchell@linux.spacemit.com \
    --cc=bmasney+clk@redhat.com \
    --cc=dlan@kernel.org \
    --cc=elder@riscstar.com \
    --cc=heylenay@4d2.org \
    --cc=inochiama@outlook.com \
    --cc=jbrunet+clk@baylibre.com \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=sboyd@kernel.org \
    --cc=spacemit@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.