Linux clock framework development
 help / color / mirror / Atom feed
* [PATCH 0/4] clk: Add the Siflower SF19A2890 clock manager and peripheral clock and reset managers
@ 2026-11-04  7:20 Joseph Telaak via B4 Relay
  2026-11-04  7:20 ` [PATCH 1/4] dt-bindings: clock: Add Siflower SF19A2890 clock manager Joseph Telaak via B4 Relay
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Joseph Telaak via B4 Relay @ 2026-11-04  7:20 UTC (permalink / raw)
  To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
  Cc: Chuanhong Guo, Yao Zi, linux-mips, linux-clk, devicetree,
	linux-kernel, Joseph Telaak

The Siflower SF19A2890, a MIPS SoC for home routers, has a clock manager
with its PLLs and the muxes and dividers that feed the CPU, the buses
and the peripherals, and for each peripheral block a small clock and
reset manager with its gates, its resets and a bus output enable. This
series adds bindings and drivers for both.

Both drivers are Chuanhong Guo's, from his SF19A2890 branch [1], with
his authorship kept and my changes noted in each. The clock manager's
driver is unchanged apart from fixes. The peripheral managers' binding
is rewritten: Chuanhong's described every block with one compatible and
its bit layout in properties (siflower,valid-gates, siflower,reset-masks
and so on); here each block has its own compatible and the layouts are
tables in the driver. Clock N of a block is its input clock N, gated.

Following Conor's review of Chuanhong's SF21 clock binding, the header
has no count at its end.

The clock manager registers from CLK_OF_DECLARE because the GIC timer
runs from the CPU clock. The SoC support itself (MACH_SIFLOWER_MIPS)
goes through the MIPS tree; until then the drivers build with
COMPILE_TEST.

The MAINTAINERS entry that covers these files comes with the MIPS series
"MIPS: Add support for the Siflower SF19A2890"; this series applies on
its own. That entry is also why checkpatch's question about updating
MAINTAINERS for the new files is left unanswered here.

Tested on a GL.iNet GL-SFT1200: the rates in clk_summary match the
vendor firmware's (CMN PLL 1.5 GHz, buses 300, 300 and 375 MHz, PBUS
100 MHz, UART 125 MHz, GMAC reference 125 MHz, USB PHY reference
50 MHz), and the CPU clock reads 800 MHz, the rate the GIC timer was
checked at against a reference clock with the MIPS series; the console,
DDR and buses survive the unused clock sweep; the UART, SPI NAND, I2C,
GPIO, GMAC, USB and watchdog drivers probe with these clocks and resets;
and the I2C controller works again after an unbind and bind, which
cycles its reset.

[1] https://github.com/981213/linux/commits/siflower-7.1

---
Chuanhong Guo (2):
      clk: siflower: Add SF19A2890 clock manager driver
      clk: siflower: Add SF19A2890 peripheral clock and reset manager driver

Joseph Telaak (2):
      dt-bindings: clock: Add Siflower SF19A2890 clock manager
      dt-bindings: clock: Add Siflower SF19A2890 peripheral clock/reset managers

 .../bindings/clock/siflower,sf19a2890-clk.yaml     |  57 +++
 .../clock/siflower,sf19a2890-periph-crm.yaml       | 169 +++++++++
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/siflower/Kconfig                       |  22 ++
 drivers/clk/siflower/Makefile                      |   2 +
 drivers/clk/siflower/clk-sf19a2890-periphcrm.c     | 360 ++++++++++++++++++
 drivers/clk/siflower/clk-sf19a2890.c               | 406 +++++++++++++++++++++
 include/dt-bindings/clock/siflower,sf19a2890-clk.h |  27 ++
 9 files changed, 1045 insertions(+)
---
base-commit: fc1f8f0ceb3b949b58c2f7819976644f5cf84249
change-id: 20261102-sf19a2890-sysm-ac1fbcb21dd5

Best regards,
--  
Joseph Telaak <joseph@telaak.dev>



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/4] dt-bindings: clock: Add Siflower SF19A2890 clock manager
  2026-11-04  7:20 [PATCH 0/4] clk: Add the Siflower SF19A2890 clock manager and peripheral clock and reset managers Joseph Telaak via B4 Relay
@ 2026-11-04  7:20 ` Joseph Telaak via B4 Relay
  2026-11-04  7:20 ` [PATCH 2/4] clk: siflower: Add SF19A2890 clock manager driver Joseph Telaak via B4 Relay
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 10+ messages in thread
From: Joseph Telaak via B4 Relay @ 2026-11-04  7:20 UTC (permalink / raw)
  To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
  Cc: Chuanhong Guo, Yao Zi, linux-mips, linux-clk, devicetree,
	linux-kernel, Joseph Telaak

From: Joseph Telaak <joseph@telaak.dev>

The clock manager of the Siflower SF19A2890 holds the SoC's PLLs and
the muxes and dividers that derive the bus, CPU and peripheral clocks
from them. The peripherals' own gates and resets are in separate
blocks.

The header is Chuanhong Guo's, from his SF19A2890 branch, without the
count at its end.

Signed-off-by: Joseph Telaak <joseph@telaak.dev>
---
 .../bindings/clock/siflower,sf19a2890-clk.yaml     | 57 ++++++++++++++++++++++
 include/dt-bindings/clock/siflower,sf19a2890-clk.h | 27 ++++++++++
 2 files changed, 84 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/siflower,sf19a2890-clk.yaml b/Documentation/devicetree/bindings/clock/siflower,sf19a2890-clk.yaml
new file mode 100644
index 000000000..9b72f14c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/siflower,sf19a2890-clk.yaml
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/siflower,sf19a2890-clk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Siflower SF19A2890 clock manager
+
+maintainers:
+  - Joseph Telaak <joseph@telaak.dev>
+
+description: |
+  The clock manager of the Siflower SF19A2890 holds the SoC's three PLLs
+  (CPU, DDR and common) and the clock muxes with dividers that derive the
+  bus, CPU and peripheral clocks from them. The peripherals' own clock
+  gates and resets are in separate peripheral clock and reset managers.
+
+  The clocks are defined in include/dt-bindings/clock/siflower,sf19a2890-clk.h.
+
+properties:
+  compatible:
+    const: siflower,sf19a2890-clk
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: 12 MHz oscillator
+      - description: 40 MHz oscillator
+
+  clock-names:
+    items:
+      - const: osc12m
+      - const: osc40m
+
+  "#clock-cells":
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - "#clock-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@19e01000 {
+        compatible = "siflower,sf19a2890-clk";
+        reg = <0x19e01000 0x800>;
+        clocks = <&osc12m>, <&osc40m>;
+        clock-names = "osc12m", "osc40m";
+        #clock-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/siflower,sf19a2890-clk.h b/include/dt-bindings/clock/siflower,sf19a2890-clk.h
new file mode 100644
index 000000000..74b9d67ea
--- /dev/null
+++ b/include/dt-bindings/clock/siflower,sf19a2890-clk.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+#ifndef __DT_BINDINGS_CLOCK_SIFLOWER_SF19A2890_CLK_H
+#define __DT_BINDINGS_CLOCK_SIFLOWER_SF19A2890_CLK_H
+
+#define CLK_PLL_CPU		0
+#define CLK_PLL_DDR		1
+#define CLK_PLL_CMN		2
+#define CLK_MUXDIV_BUS1		3
+#define CLK_MUXDIV_BUS2		4
+#define CLK_MUXDIV_BUS3		5
+#define CLK_MUXDIV_CPU		6
+#define CLK_MUXDIV_PBUS		7
+#define CLK_MUXDIV_MEM_PHY	8
+#define CLK_MUXDIV_UART		9
+#define CLK_MUXDIV_ETH_REF	10
+#define CLK_MUXDIV_ETH_BYP_REF	11
+#define CLK_MUXDIV_ETH_TSU	12
+#define CLK_MUXDIV_GMAC_BYP_REF	13
+#define CLK_MUXDIV_M6250_0	14
+#define CLK_MUXDIV_M6250_1	15
+#define CLK_MUXDIV_WLAN24_PLF	16
+#define CLK_MUXDIV_WLAN5_PLF	17
+#define CLK_MUXDIV_USBPHY_REF	18
+#define CLK_MUXDIV_TCLK		19
+#define CLK_MUXDIV_NPU_PE_CLK	20
+
+#endif /* __DT_BINDINGS_CLOCK_SIFLOWER_SF19A2890_CLK_H */

-- 
Git-155)



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/4] clk: siflower: Add SF19A2890 clock manager driver
  2026-11-04  7:20 [PATCH 0/4] clk: Add the Siflower SF19A2890 clock manager and peripheral clock and reset managers Joseph Telaak via B4 Relay
  2026-11-04  7:20 ` [PATCH 1/4] dt-bindings: clock: Add Siflower SF19A2890 clock manager Joseph Telaak via B4 Relay
@ 2026-11-04  7:20 ` Joseph Telaak via B4 Relay
  2026-11-04  7:34   ` sashiko-bot
  2026-11-04  7:21 ` [PATCH 3/4] dt-bindings: clock: Add Siflower SF19A2890 peripheral clock/reset managers Joseph Telaak via B4 Relay
  2026-11-04  7:21 ` [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver Joseph Telaak via B4 Relay
  3 siblings, 1 reply; 10+ messages in thread
From: Joseph Telaak via B4 Relay @ 2026-11-04  7:20 UTC (permalink / raw)
  To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
  Cc: Chuanhong Guo, Yao Zi, linux-mips, linux-clk, devicetree,
	linux-kernel, Joseph Telaak

From: Chuanhong Guo <gch981213@gmail.com>

Add a driver for the clock manager of the Siflower SF19A2890: the CPU,
DDR and common PLLs, and the muxes with dividers that derive the bus,
CPU and peripheral clocks from them. The GIC timer needs the CPU clock,
so the driver registers early, from CLK_OF_DECLARE.

Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
[jt: from Chuanhong's siflower-7.1 branch; read the PLL power-down register
 at the PLL's offset instead of at offset 1, take the oscillators from the
 node's clocks and the PLLs by pointer instead of by global name, count the
 clocks in the driver instead of the binding header, drop the spinlock and
 sf_rmw(), which nothing used, unregister the clocks and unmap on errors,
 return 0 for a zero divider and compute the VCO rate in 64 bits, pr_fmt,
 u32 offsets, sorted includes, one Kconfig symbol without the menu, depend
 on MACH_SIFLOWER_MIPS, commit message, checkpatch --strict cleanups,
 subject]
Signed-off-by: Joseph Telaak <joseph@telaak.dev>
---
 drivers/clk/Kconfig                  |   1 +
 drivers/clk/Makefile                 |   1 +
 drivers/clk/siflower/Kconfig         |  11 +
 drivers/clk/siflower/Makefile        |   1 +
 drivers/clk/siflower/clk-sf19a2890.c | 406 +++++++++++++++++++++++++++++++++++
 5 files changed, 420 insertions(+)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 37a8e35d4..8e73490cb 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -536,6 +536,7 @@ source "drivers/clk/renesas/Kconfig"
 source "drivers/clk/rockchip/Kconfig"
 source "drivers/clk/samsung/Kconfig"
 source "drivers/clk/sifive/Kconfig"
+source "drivers/clk/siflower/Kconfig"
 source "drivers/clk/socfpga/Kconfig"
 source "drivers/clk/sophgo/Kconfig"
 source "drivers/clk/spacemit/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 52d138d4f..d2f075847 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -160,6 +160,7 @@ obj-y					+= renesas/
 obj-$(CONFIG_COMMON_CLK_ROCKCHIP)	+= rockchip/
 obj-$(CONFIG_COMMON_CLK_SAMSUNG)	+= samsung/
 obj-$(CONFIG_CLK_SIFIVE)		+= sifive/
+obj-y					+= siflower/
 obj-y					+= socfpga/
 obj-y					+= sophgo/
 obj-y					+= spacemit/
diff --git a/drivers/clk/siflower/Kconfig b/drivers/clk/siflower/Kconfig
new file mode 100644
index 000000000..a8812fab2
--- /dev/null
+++ b/drivers/clk/siflower/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config CLK_SF19A2890
+	bool "Clock driver for Siflower SF19A2890"
+	depends on MACH_SIFLOWER_MIPS || COMPILE_TEST
+	default MACH_SIFLOWER_MIPS
+	help
+	  Supports the clock manager of the Siflower SF19A2890: its PLLs
+	  and the muxes and dividers that feed the CPU, the buses and the
+	  peripherals. The GIC timer runs from the CPU clock, so a kernel
+	  for this SoC needs it built in.
diff --git a/drivers/clk/siflower/Makefile b/drivers/clk/siflower/Makefile
new file mode 100644
index 000000000..9f3df2e1e
--- /dev/null
+++ b/drivers/clk/siflower/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CLK_SF19A2890) += clk-sf19a2890.o
diff --git a/drivers/clk/siflower/clk-sf19a2890.c b/drivers/clk/siflower/clk-sf19a2890.c
new file mode 100644
index 000000000..f7553ac49
--- /dev/null
+++ b/drivers/clk/siflower/clk-sf19a2890.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) "sf19a2890-clk: " fmt
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/of_address.h>
+
+#include <dt-bindings/clock/siflower,sf19a2890-clk.h>
+
+#define CLK_SF19A2890_NR_CLKS	(CLK_MUXDIV_NPU_PE_CLK + 1)
+
+#define REG_PLL_BASE		0x0
+#define REG_PLL_PD		0x0
+#define  PLL_PD			BIT(0)
+#define  PLL_PD_VCO		BIT(1)
+#define  PLL_PD_POSTDIV		BIT(2)
+#define  PLL_PD_4PHASE		BIT(3)
+#define  PLL_PD_DAC		BIT(4)
+#define  PLL_PD_DSM		BIT(5)
+
+/*
+ * PLL_PARAM is a 48-bit value put into 6 registers, 8-bit per register:
+ * REFDIV = PLL_PARAM[47:42]
+ * POSTDIV2 = PLL_PARAM[41:39]
+ * POSTDIV1 = PLL_PARAM[38:36]
+ * FRAC = PLL_PARAM[35:12]
+ * FBDIV = PLL_PARAM[11:0]
+ */
+
+#define REG_PLL_PARAM(_x)	(0x4 + (_x) * 4)
+#define  PLL_REFDIV_HI		47
+#define  PLL_REFDIV_LO		42
+#define  PLL_POSTDIV2_HI	41
+#define  PLL_POSTDIV2_LO	39
+#define  PLL_POSTDIV1_HI	38
+#define  PLL_POSTDIV1_LO	36
+#define  PLL_FRAC_HI		35
+#define  PLL_FRAC_LO		12
+#define  PLL_FRAC_BITS		(PLL_FRAC_HI - PLL_FRAC_LO + 1)
+#define  PLL_FBDIV_HI		11
+#define  PLL_FBDIV_LO		0
+
+#define REG_PLL_CFG 0x1c
+#define  PLL_CFG_BYPASS		BIT(0)
+#define  PLL_CFG_SRC		GENMASK(2, 1)
+#define  PLL_CFG_OCLK_SEL	BIT(3)
+#define  PLL_CFG_OCLK_GATE	BIT(4)
+#define  PLL_CFG_LOAD_DIVS	BIT(5)
+
+#define REG_PLL_LOCK 0x20
+
+/*
+ * Read-only register indicating the value of the hardware clock source
+ * override pin. When the first bit of this register is set, PLL clock
+ * source is forced to the 40M oscillator regardless of PLL_CFG_SRC
+ * value.
+ */
+#define REG_PLL_SRC_OVERRIDE 0x24
+
+struct sf_clk_common {
+	void __iomem *base;
+	struct clk_hw hw;
+};
+
+struct sf19a2890_clk {
+	struct sf_clk_common common;
+	u32 offset;
+};
+
+#define SF_CLK_COMMON(_name, _parents, _op, _flags)				\
+	{								\
+		.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parents, _op,	\
+						    _flags),		\
+	}
+
+static inline struct sf_clk_common *hw_to_sf_clk_common(struct clk_hw *hw)
+{
+	return container_of(hw, struct sf_clk_common, hw);
+}
+
+static inline struct sf19a2890_clk *cmn_to_clk(struct sf_clk_common *cmn_priv)
+{
+	return container_of(cmn_priv, struct sf19a2890_clk, common);
+}
+
+static inline u32 sf_readl(struct sf_clk_common *priv, u32 reg)
+{
+	return readl(priv->base + reg);
+}
+
+static inline void sf_writel(struct sf_clk_common *priv, u32 reg, u32 val)
+{
+	writel(val, priv->base + reg);
+}
+
+static u32 sf_pll_param_get(struct sf19a2890_clk *priv, u32 hi, u32 lo)
+{
+	struct sf_clk_common *cmn = &priv->common;
+	u32 ret = 0;
+	int reg_hi = hi / 8;
+	int reg_lo = lo / 8;
+	u32 reg_hi_pos = hi % 8;
+	u32 reg_lo_pos = lo % 8;
+	int i;
+
+	if (reg_hi == reg_lo) {
+		u32 mask = (BIT(reg_hi_pos - reg_lo_pos + 1)) - 1;
+		u32 reg_val =
+			sf_readl(cmn, priv->offset + REG_PLL_PARAM(reg_hi));
+		return (reg_val >> reg_lo_pos) & mask;
+	}
+
+	ret = sf_readl(cmn, priv->offset + REG_PLL_PARAM(reg_hi)) &
+	      (BIT(reg_hi_pos + 1) - 1);
+	for (i = reg_hi - 1; i > reg_lo; i--)
+		ret = (ret << 8) |
+		      sf_readl(cmn, priv->offset + REG_PLL_PARAM(i));
+	ret = (ret << (8 - reg_lo_pos)) |
+	      (sf_readl(cmn, priv->offset + REG_PLL_PARAM(reg_lo)) >>
+	       reg_lo_pos);
+
+	return ret;
+}
+
+static unsigned long sf19a28_pll_recalc_rate(struct clk_hw *hw,
+					     unsigned long parent_rate)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+	u32 refdiv = sf_pll_param_get(priv, PLL_REFDIV_HI, PLL_REFDIV_LO);
+	u32 fbdiv = sf_pll_param_get(priv, PLL_FBDIV_HI, PLL_FBDIV_LO);
+	u32 postdiv1 = sf_pll_param_get(priv, PLL_POSTDIV1_HI, PLL_POSTDIV1_LO);
+	u32 postdiv2 = sf_pll_param_get(priv, PLL_POSTDIV2_HI, PLL_POSTDIV2_LO);
+	u32 pll_pd = sf_readl(cmn_priv, priv->offset + REG_PLL_PD);
+	u64 rate;
+	u32 frac;
+
+	if (!refdiv || !postdiv1 || !postdiv2)
+		return 0;
+
+	rate = (u64)(parent_rate / refdiv) * fbdiv;
+
+	if (!(pll_pd & PLL_PD_DSM)) {
+		frac = sf_pll_param_get(priv, PLL_FRAC_HI, PLL_FRAC_LO);
+		rate += (rate * frac) >> PLL_FRAC_BITS;
+	}
+
+	return div_u64(rate, postdiv1 * postdiv2);
+}
+
+static u8 sf19a28_pll_get_parent(struct clk_hw *hw)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+	u32 cfg;
+
+	if (sf_readl(cmn_priv, priv->offset + REG_PLL_SRC_OVERRIDE))
+		return 1;
+	cfg = sf_readl(cmn_priv, priv->offset + REG_PLL_CFG);
+	return (FIELD_GET(PLL_CFG_SRC, cfg) == 1);
+}
+
+static const struct clk_ops sf19a28_pll_ops = {
+	.recalc_rate = sf19a28_pll_recalc_rate,
+	.get_parent = sf19a28_pll_get_parent,
+};
+
+static const struct clk_parent_data clk_pll_parents[] = {
+	{ .fw_name = "osc12m" },
+	{ .fw_name = "osc40m" },
+};
+
+#define SF19A28_PLL(_name, _offset, _flags)				\
+	struct sf19a2890_clk _name = {					\
+		.common = SF_CLK_COMMON(#_name, clk_pll_parents,	\
+					&sf19a28_pll_ops, _flags),	\
+		.offset = REG_PLL_BASE + (_offset),			\
+	}
+
+static SF19A28_PLL(pll_cpu, 0x0, 0);
+static SF19A28_PLL(pll_ddr, 0x40, 0);
+static SF19A28_PLL(pll_cmn, 0x80, 0);
+
+#define REG_MUXDIV_BASE		0x400
+#define REG_MUXDIV_CFG		0x0
+#define  MUXDIV_USE_NCO		BIT(3)
+#define  MUXDIV_SRC_SEL		GENMASK(2, 0)
+#define REG_MUXDIV_RATIO	0x4
+#define  MUXDIV_RATIO_MAX	0xff
+#define REG_MUXDIV_NCO_V	0x8
+#define REG_MUXDIV_EN		0xc
+#define REG_MUXDIV_XN_DIV_RATIO	0x10
+#define  MUXDIV_XN_DIV_MAX	3
+
+static unsigned long sf19a28_muxdiv_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+	u32 div;
+
+	div = sf_readl(cmn_priv, priv->offset + REG_MUXDIV_RATIO) + 1;
+	return parent_rate / div;
+}
+
+static int sf19a28_muxdiv_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+	unsigned int div;
+
+	div = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
+	if (!div)
+		div = 1;
+	else if (div > MUXDIV_RATIO_MAX + 1)
+		div = MUXDIV_RATIO_MAX + 1;
+
+	req->rate = req->best_parent_rate / div;
+	return 0;
+}
+
+static int sf19a28_muxdiv_set_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long parent_rate)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+	unsigned int div;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+	if (div < 1)
+		div = 1;
+	else if (div > MUXDIV_RATIO_MAX + 1)
+		div = MUXDIV_RATIO_MAX + 1;
+	div -= 1;
+
+	sf_writel(cmn_priv, priv->offset + REG_MUXDIV_RATIO, div);
+
+	return 0;
+}
+
+static int sf19a28_muxdiv_enable(struct clk_hw *hw)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+
+	sf_writel(cmn_priv, priv->offset + REG_MUXDIV_EN, 1);
+	return 0;
+}
+
+static void sf19a28_muxdiv_disable(struct clk_hw *hw)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+
+	sf_writel(cmn_priv, priv->offset + REG_MUXDIV_EN, 0);
+}
+
+static int sf19a28_muxdiv_is_enabled(struct clk_hw *hw)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+
+	return !!sf_readl(cmn_priv, priv->offset + REG_MUXDIV_EN);
+}
+
+static u8 sf19a28_muxdiv_get_parent(struct clk_hw *hw)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+	u32 cfg = sf_readl(cmn_priv, priv->offset + REG_MUXDIV_CFG);
+	u32 src = FIELD_GET(MUXDIV_SRC_SEL, cfg);
+
+	if (src <= 2)
+		return src;
+	if (src == 4)
+		return 3;
+	return 4;
+}
+
+static int sf19a28_muxdiv_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
+	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
+	u32 src;
+
+	if (index <= 2)
+		src = index;
+	else if (index == 3)
+		src = 4;
+	else
+		src = 6;
+	sf_writel(cmn_priv, priv->offset + REG_MUXDIV_CFG, src);
+	return 0;
+}
+
+static const struct clk_parent_data clk_muxdiv_parents[] = {
+	{ .hw = &pll_cpu.common.hw },
+	{ .hw = &pll_ddr.common.hw },
+	{ .hw = &pll_cmn.common.hw },
+	{ .fw_name = "osc12m" },
+	{ .fw_name = "osc40m" },
+};
+
+static const struct clk_ops sf19a28_muxdiv_ops = {
+	.recalc_rate = sf19a28_muxdiv_recalc_rate,
+	.determine_rate = sf19a28_muxdiv_determine_rate,
+	.set_rate = sf19a28_muxdiv_set_rate,
+	.enable = sf19a28_muxdiv_enable,
+	.disable = sf19a28_muxdiv_disable,
+	.is_enabled = sf19a28_muxdiv_is_enabled,
+	.get_parent = sf19a28_muxdiv_get_parent,
+	.set_parent = sf19a28_muxdiv_set_parent,
+};
+
+#define SF19A28_MUXDIV(_name, _offset, _flags)				\
+	struct sf19a2890_clk _name = {					\
+		.common = SF_CLK_COMMON(#_name, clk_muxdiv_parents,	\
+					&sf19a28_muxdiv_ops, _flags),	\
+		.offset = REG_MUXDIV_BASE + (_offset),			\
+	}
+
+static SF19A28_MUXDIV(muxdiv_bus1, 0x0, CLK_IS_CRITICAL);
+static SF19A28_MUXDIV(muxdiv_bus2, 0x20, CLK_IS_CRITICAL);
+static SF19A28_MUXDIV(muxdiv_bus3, 0x40, CLK_IS_CRITICAL);
+static SF19A28_MUXDIV(muxdiv_cpu, 0x100, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT);
+static SF19A28_MUXDIV(muxdiv_pbus, 0x120, CLK_IS_CRITICAL);
+static SF19A28_MUXDIV(muxdiv_mem_phy, 0x140, CLK_IS_CRITICAL);
+static SF19A28_MUXDIV(muxdiv_uart, 0x180, 0);
+static SF19A28_MUXDIV(muxdiv_eth_ref, 0x200, 0);
+static SF19A28_MUXDIV(muxdiv_eth_byp_ref, 0x220, 0);
+static SF19A28_MUXDIV(muxdiv_eth_tsu, 0x240, 0);
+static SF19A28_MUXDIV(muxdiv_gmac_byp_ref, 0x260, 0);
+static SF19A28_MUXDIV(muxdiv_m6250_0, 0x280, 0);
+static SF19A28_MUXDIV(muxdiv_m6250_1, 0x2a0, 0);
+static SF19A28_MUXDIV(muxdiv_wlan24_plf, 0x2c0, 0);
+static SF19A28_MUXDIV(muxdiv_wlan5_plf, 0x2e0, 0);
+static SF19A28_MUXDIV(muxdiv_usbphy_ref, 0x300, 0);
+static SF19A28_MUXDIV(muxdiv_tclk, 0x320, 0);
+static SF19A28_MUXDIV(muxdiv_npu_pe, 0x340, 0);
+
+static struct clk_hw_onecell_data sf19a2890_hw_clks = {
+	.num = CLK_SF19A2890_NR_CLKS,
+	.hws = {
+		[CLK_PLL_CPU] = &pll_cpu.common.hw,
+		[CLK_PLL_DDR] = &pll_ddr.common.hw,
+		[CLK_PLL_CMN] = &pll_cmn.common.hw,
+		[CLK_MUXDIV_BUS1] = &muxdiv_bus1.common.hw,
+		[CLK_MUXDIV_BUS2] = &muxdiv_bus2.common.hw,
+		[CLK_MUXDIV_BUS3] = &muxdiv_bus3.common.hw,
+		[CLK_MUXDIV_CPU] = &muxdiv_cpu.common.hw,
+		[CLK_MUXDIV_PBUS] = &muxdiv_pbus.common.hw,
+		[CLK_MUXDIV_MEM_PHY] = &muxdiv_mem_phy.common.hw,
+		[CLK_MUXDIV_UART] = &muxdiv_uart.common.hw,
+		[CLK_MUXDIV_ETH_REF] = &muxdiv_eth_ref.common.hw,
+		[CLK_MUXDIV_ETH_BYP_REF] = &muxdiv_eth_byp_ref.common.hw,
+		[CLK_MUXDIV_ETH_TSU] = &muxdiv_eth_tsu.common.hw,
+		[CLK_MUXDIV_GMAC_BYP_REF] = &muxdiv_gmac_byp_ref.common.hw,
+		[CLK_MUXDIV_M6250_0] = &muxdiv_m6250_0.common.hw,
+		[CLK_MUXDIV_M6250_1] = &muxdiv_m6250_1.common.hw,
+		[CLK_MUXDIV_WLAN24_PLF] = &muxdiv_wlan24_plf.common.hw,
+		[CLK_MUXDIV_WLAN5_PLF] = &muxdiv_wlan5_plf.common.hw,
+		[CLK_MUXDIV_USBPHY_REF] = &muxdiv_usbphy_ref.common.hw,
+		[CLK_MUXDIV_TCLK] = &muxdiv_tclk.common.hw,
+		[CLK_MUXDIV_NPU_PE_CLK] = &muxdiv_npu_pe.common.hw,
+	},
+};
+
+static void __init sf19a2890_clk_init(struct device_node *node)
+{
+	void __iomem *base;
+	int i, ret;
+
+	base = of_iomap(node, 0);
+	if (!base) {
+		pr_err("failed to map resources.\n");
+		return;
+	}
+
+	for (i = 0; i < sf19a2890_hw_clks.num; i++) {
+		struct clk_hw *hw = sf19a2890_hw_clks.hws[i];
+
+		if (!hw)
+			continue;
+		hw_to_sf_clk_common(hw)->base = base;
+		ret = of_clk_hw_register(node, hw);
+		if (ret) {
+			pr_err("Couldn't register clock %d: %d\n", i, ret);
+			goto err;
+		}
+	}
+
+	ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, &sf19a2890_hw_clks);
+	if (ret) {
+		pr_err("failed to add hw provider.\n");
+		goto err;
+	}
+	return;
+err:
+	while (--i >= 0)
+		if (sf19a2890_hw_clks.hws[i])
+			clk_hw_unregister(sf19a2890_hw_clks.hws[i]);
+	iounmap(base);
+}
+
+CLK_OF_DECLARE(sf19a2890_clk, "siflower,sf19a2890-clk", sf19a2890_clk_init);

-- 
Git-155)



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/4] dt-bindings: clock: Add Siflower SF19A2890 peripheral clock/reset managers
  2026-11-04  7:20 [PATCH 0/4] clk: Add the Siflower SF19A2890 clock manager and peripheral clock and reset managers Joseph Telaak via B4 Relay
  2026-11-04  7:20 ` [PATCH 1/4] dt-bindings: clock: Add Siflower SF19A2890 clock manager Joseph Telaak via B4 Relay
  2026-11-04  7:20 ` [PATCH 2/4] clk: siflower: Add SF19A2890 clock manager driver Joseph Telaak via B4 Relay
@ 2026-11-04  7:21 ` Joseph Telaak via B4 Relay
  2026-11-04  7:27   ` sashiko-bot
  2026-11-04  7:21 ` [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver Joseph Telaak via B4 Relay
  3 siblings, 1 reply; 10+ messages in thread
From: Joseph Telaak via B4 Relay @ 2026-11-04  7:21 UTC (permalink / raw)
  To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
  Cc: Chuanhong Guo, Yao Zi, linux-mips, linux-clk, devicetree,
	linux-kernel, Joseph Telaak

From: Joseph Telaak <joseph@telaak.dev>

Each peripheral block of the Siflower SF19A2890 has a clock and reset
manager of its own, with a reset register, a clock gate register and a
bus output enable. The blocks differ in which gate and reset bits they
use, so each has its own compatible, and clock N of a manager is its
input clock N, gated.

Based on Chuanhong Guo's binding, which described every block with one
compatible and its bit layout in properties.

Signed-off-by: Joseph Telaak <joseph@telaak.dev>
---
 .../clock/siflower,sf19a2890-periph-crm.yaml       | 169 +++++++++++++++++++++
 1 file changed, 169 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/siflower,sf19a2890-periph-crm.yaml b/Documentation/devicetree/bindings/clock/siflower,sf19a2890-periph-crm.yaml
new file mode 100644
index 000000000..7f0bce603
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/siflower,sf19a2890-periph-crm.yaml
@@ -0,0 +1,169 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/siflower,sf19a2890-periph-crm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Siflower SF19A2890 peripheral clock and reset manager
+
+maintainers:
+  - Joseph Telaak <joseph@telaak.dev>
+
+description: |
+  Each peripheral, or group of peripherals, of the Siflower SF19A2890 has
+  a clock and reset manager of its own: a reset register, a clock gate
+  register, and a bus output enable that the peripherals need as well as
+  their clocks to answer bus requests.
+
+  Clock N of the manager is input clock N, gated. The reset lines are:
+
+    gmac-crm:  0 GMAC
+    usb-crm:   0 USB controller, 1 USB PHY power-on, 2 USB PHY
+    uart-crm:  0 UART0, 1 UART1, 2 UART2
+    the others: 0 the block
+
+properties:
+  compatible:
+    enum:
+      - siflower,sf19a2890-gmac-crm
+      - siflower,sf19a2890-gpio-crm
+      - siflower,sf19a2890-i2c-crm
+      - siflower,sf19a2890-pwm-crm
+      - siflower,sf19a2890-rf-crm
+      - siflower,sf19a2890-spi-crm
+      - siflower,sf19a2890-timer-crm
+      - siflower,sf19a2890-uart-crm
+      - siflower,sf19a2890-usb-crm
+      - siflower,sf19a2890-wdt-crm
+      - siflower,sf19a2890-wlan24-crm
+      - siflower,sf19a2890-wlan5-crm
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 6
+
+  "#clock-cells":
+    const: 1
+
+  "#reset-cells":
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - "#clock-cells"
+  - "#reset-cells"
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: siflower,sf19a2890-gmac-crm
+    then:
+      properties:
+        clocks:
+          items:
+            - description: GMAC bus clock
+            - description: GMAC register clock
+            - description: RGMII transmit reference clock
+            - description: Timestamp unit clock
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - siflower,sf19a2890-wlan24-crm
+              - siflower,sf19a2890-wlan5-crm
+    then:
+      properties:
+        clocks:
+          items:
+            - description: AXI slave clock
+            - description: AXI master clock
+            - description: Radio platform clock
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: siflower,sf19a2890-rf-crm
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Test clock
+            - description: AXI slave clock
+            - description: Boot clock
+            - description: Low-power clock
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: siflower,sf19a2890-usb-crm
+    then:
+      properties:
+        clocks:
+          items:
+            - description: USB controller clock
+            - description: AXI master clock
+            - description: USB PHY reference clock
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: siflower,sf19a2890-spi-crm
+    then:
+      properties:
+        clocks:
+          items:
+            - description: APB clock
+            - description: SSP clock
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: siflower,sf19a2890-uart-crm
+    then:
+      properties:
+        clocks:
+          items:
+            - description: UART0 APB clock
+            - description: UART1 APB clock
+            - description: UART2 APB clock
+            - description: UART0 clock
+            - description: UART1 clock
+            - description: UART2 clock
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - siflower,sf19a2890-gpio-crm
+              - siflower,sf19a2890-i2c-crm
+              - siflower,sf19a2890-pwm-crm
+              - siflower,sf19a2890-timer-crm
+              - siflower,sf19a2890-wdt-crm
+    then:
+      properties:
+        clocks:
+          maxItems: 1
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/siflower,sf19a2890-clk.h>
+
+    clock-controller@19e24c00 {
+        compatible = "siflower,sf19a2890-uart-crm";
+        reg = <0x19e24c00 0x10>;
+        clocks = <&clk CLK_MUXDIV_PBUS>, <&clk CLK_MUXDIV_PBUS>,
+                 <&clk CLK_MUXDIV_PBUS>, <&clk CLK_MUXDIV_UART>,
+                 <&clk CLK_MUXDIV_UART>, <&clk CLK_MUXDIV_UART>;
+        #clock-cells = <1>;
+        #reset-cells = <1>;
+    };

-- 
Git-155)



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver
  2026-11-04  7:20 [PATCH 0/4] clk: Add the Siflower SF19A2890 clock manager and peripheral clock and reset managers Joseph Telaak via B4 Relay
                   ` (2 preceding siblings ...)
  2026-11-04  7:21 ` [PATCH 3/4] dt-bindings: clock: Add Siflower SF19A2890 peripheral clock/reset managers Joseph Telaak via B4 Relay
@ 2026-11-04  7:21 ` Joseph Telaak via B4 Relay
  2026-11-04  9:25   ` Philipp Zabel
  2026-11-04 13:32   ` Chuanhong Guo
  3 siblings, 2 replies; 10+ messages in thread
From: Joseph Telaak via B4 Relay @ 2026-11-04  7:21 UTC (permalink / raw)
  To: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel
  Cc: Chuanhong Guo, Yao Zi, linux-mips, linux-clk, devicetree,
	linux-kernel, Joseph Telaak

From: Chuanhong Guo <gch981213@gmail.com>

Add a platform driver for the peripheral clock and reset manager (CRM)
block found on Siflower SF19A2890 and compatible SoCs.

Each CRM block manages a group of APB peripherals through three registers:
a reset register, a clock gate register, and a shared Bus Output Enable
(BOE) register that must be asserted for any peripheral in the block to
respond to bus transactions.

The driver registers a clock gate controller and a reset controller from
a single DT node. A shared spinlock protects all read-modify-write
accesses across both. BOE is managed automatically: asserted when the
first clock gate is enabled and deasserted when the last one is disabled.

Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
[jt: from Chuanhong's siflower-7.1 branch; one compatible per block with
 its gate bits, critical gates and reset masks in tables here instead of
 the siflower,valid-gates, siflower,critical-gates, siflower,reset-masks
 and siflower,num-resets properties, gate N parented to input clock N by
 index instead of by global name, the gate updates under the spinlock too,
 no separator comments, Kconfig dependencies and help, subject]
Signed-off-by: Joseph Telaak <joseph@telaak.dev>
---
 drivers/clk/siflower/Kconfig                   |  11 +
 drivers/clk/siflower/Makefile                  |   1 +
 drivers/clk/siflower/clk-sf19a2890-periphcrm.c | 360 +++++++++++++++++++++++++
 3 files changed, 372 insertions(+)

diff --git a/drivers/clk/siflower/Kconfig b/drivers/clk/siflower/Kconfig
index a8812fab2..f7c8a5a34 100644
--- a/drivers/clk/siflower/Kconfig
+++ b/drivers/clk/siflower/Kconfig
@@ -9,3 +9,14 @@ config CLK_SF19A2890
 	  and the muxes and dividers that feed the CPU, the buses and the
 	  peripherals. The GIC timer runs from the CPU clock, so a kernel
 	  for this SoC needs it built in.
+
+config CLK_SF19A2890_PERIPHCRM
+	bool "Clock and reset driver for Siflower SF19A2890 peripherals"
+	depends on MACH_SIFLOWER_MIPS || COMPILE_TEST
+	default MACH_SIFLOWER_MIPS
+	select RESET_CONTROLLER
+	help
+	  Supports the peripheral clock and reset managers of the Siflower
+	  SF19A2890. Each peripheral block has one, with its clock gates,
+	  its resets and the bus output enable it needs to answer bus
+	  requests.
diff --git a/drivers/clk/siflower/Makefile b/drivers/clk/siflower/Makefile
index 9f3df2e1e..a328317c4 100644
--- a/drivers/clk/siflower/Makefile
+++ b/drivers/clk/siflower/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_CLK_SF19A2890) += clk-sf19a2890.o
+obj-$(CONFIG_CLK_SF19A2890_PERIPHCRM) += clk-sf19a2890-periphcrm.o
diff --git a/drivers/clk/siflower/clk-sf19a2890-periphcrm.c b/drivers/clk/siflower/clk-sf19a2890-periphcrm.c
new file mode 100644
index 000000000..95d4dd449
--- /dev/null
+++ b/drivers/clk/siflower/clk-sf19a2890-periphcrm.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Siflower SF19A2890 peripheral clock and reset manager driver.
+ *
+ * Each managed block has three registers:
+ *   RESET    at base + 0x0  - per-peripheral reset bitmasks
+ *   CLK_GATE at base + 0x4  - one bit per clock gate
+ *   BOE      at base + 0xc  - shared Bus Output Enable; bits [1:0] must be set
+ *                             for any APB peripheral in the block to respond
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define REG_RESET	0x0
+#define REG_CLK_GATE	0x4
+#define REG_BOE		0xc
+#define BOE_EN		GENMASK(1, 0)
+
+struct sf19a2890_periphcrm_gate {
+	const char *name;
+	u8 bit;
+	unsigned long flags;
+};
+
+struct sf19a2890_periphcrm_data {
+	const struct sf19a2890_periphcrm_gate *gates;
+	unsigned int num_gates;
+	const u32 *reset_masks;
+	unsigned int num_resets;
+};
+
+struct sf19a2890_periphcrm {
+	void __iomem *base;
+	spinlock_t lock;	/* serializes all register updates */
+	struct reset_controller_dev rcdev;
+	const u32 *reset_masks;
+	struct clk_hw_onecell_data clk_data;
+};
+
+struct sf19a2890_periphclk {
+	struct sf19a2890_periphcrm *crm;
+	struct clk_hw hw;
+	u32 idx;
+};
+
+static inline struct sf19a2890_periphclk *hw_to_periphclk(struct clk_hw *hw)
+{
+	return container_of(hw, struct sf19a2890_periphclk, hw);
+}
+
+static int sf19a2890_periphclk_enable(struct clk_hw *hw)
+{
+	struct sf19a2890_periphclk *gate = hw_to_periphclk(hw);
+	struct sf19a2890_periphcrm *crm = gate->crm;
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&crm->lock, flags);
+	reg = readl(crm->base + REG_CLK_GATE);
+	writel(reg | BIT(gate->idx), crm->base + REG_CLK_GATE);
+	writel(BOE_EN, crm->base + REG_BOE);
+	spin_unlock_irqrestore(&crm->lock, flags);
+	return 0;
+}
+
+static void sf19a2890_periphclk_disable(struct clk_hw *hw)
+{
+	struct sf19a2890_periphclk *gate = hw_to_periphclk(hw);
+	struct sf19a2890_periphcrm *crm = gate->crm;
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&crm->lock, flags);
+	reg = readl(crm->base + REG_CLK_GATE);
+	reg &= ~BIT(gate->idx);
+	writel(reg, crm->base + REG_CLK_GATE);
+	if (reg == 0)
+		writel(0, crm->base + REG_BOE);
+	spin_unlock_irqrestore(&crm->lock, flags);
+}
+
+static int sf19a2890_periphclk_is_enabled(struct clk_hw *hw)
+{
+	struct sf19a2890_periphclk *gate = hw_to_periphclk(hw);
+	struct sf19a2890_periphcrm *crm = gate->crm;
+
+	return !!(readl(crm->base + REG_CLK_GATE) & BIT(gate->idx));
+}
+
+static const struct clk_ops sf19a2890_periphclk_ops = {
+	.enable = sf19a2890_periphclk_enable,
+	.disable = sf19a2890_periphclk_disable,
+	.is_enabled = sf19a2890_periphclk_is_enabled,
+};
+
+static inline struct sf19a2890_periphcrm *
+rcdev_to_periphcrm(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct sf19a2890_periphcrm, rcdev);
+}
+
+static int sf19a2890_periphcrm_reset_update(struct reset_controller_dev *rcdev,
+					    unsigned long id, bool assert)
+{
+	struct sf19a2890_periphcrm *crm = rcdev_to_periphcrm(rcdev);
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&crm->lock, flags);
+	reg = readl(crm->base + REG_RESET);
+	if (assert)
+		reg |= crm->reset_masks[id];
+	else
+		reg &= ~crm->reset_masks[id];
+	writel(reg, crm->base + REG_RESET);
+	spin_unlock_irqrestore(&crm->lock, flags);
+	return 0;
+}
+
+static int sf19a2890_periphcrm_reset_assert(struct reset_controller_dev *rcdev,
+					    unsigned long id)
+{
+	return sf19a2890_periphcrm_reset_update(rcdev, id, true);
+}
+
+static int
+sf19a2890_periphcrm_reset_deassert(struct reset_controller_dev *rcdev,
+				   unsigned long id)
+{
+	return sf19a2890_periphcrm_reset_update(rcdev, id, false);
+}
+
+static int sf19a2890_periphcrm_reset_status(struct reset_controller_dev *rcdev,
+					    unsigned long id)
+{
+	struct sf19a2890_periphcrm *crm = rcdev_to_periphcrm(rcdev);
+
+	return !!(readl(crm->base + REG_RESET) & crm->reset_masks[id]);
+}
+
+static const struct reset_control_ops sf19a2890_periphcrm_reset_ops = {
+	.assert = sf19a2890_periphcrm_reset_assert,
+	.deassert = sf19a2890_periphcrm_reset_deassert,
+	.status = sf19a2890_periphcrm_reset_status,
+};
+
+#define GATE(_name, _bit, _flags) { .name = _name, .bit = _bit, .flags = _flags }
+
+#define CRM_DATA(_g, _r)						\
+	{								\
+		.gates = sf19a2890_##_g##_gates,			\
+		.num_gates = ARRAY_SIZE(sf19a2890_##_g##_gates),	\
+		.reset_masks = sf19a2890_##_r##_resets,			\
+		.num_resets = ARRAY_SIZE(sf19a2890_##_r##_resets),	\
+	}
+
+static const u32 sf19a2890_single_resets[] = { BIT(0) };
+
+static const struct sf19a2890_periphcrm_gate sf19a2890_gmac_gates[] = {
+	GATE("gmac", 0, 0),
+	GATE("gmac_pclk", 1, 0),
+	GATE("gmac_byp_ref", 2, 0),
+	GATE("ethtsu", 3, 0),
+};
+
+static const u32 sf19a2890_gmac_resets[] = { GENMASK(1, 0) };
+
+static const struct sf19a2890_periphcrm_data sf19a2890_gmac_crm =
+	CRM_DATA(gmac, gmac);
+
+static const struct sf19a2890_periphcrm_gate sf19a2890_wlan24_gates[] = {
+	GATE("wlan24_axis", 0, 0),
+	GATE("wlan24_axim", 1, 0),
+	GATE("wlan24_plf", 2, 0),
+};
+
+static const struct sf19a2890_periphcrm_gate sf19a2890_wlan5_gates[] = {
+	GATE("wlan5_axis", 0, 0),
+	GATE("wlan5_axim", 1, 0),
+	GATE("wlan5_plf", 2, 0),
+};
+
+static const u32 sf19a2890_wlan_resets[] = { GENMASK(2, 0) };
+
+static const struct sf19a2890_periphcrm_data sf19a2890_wlan24_crm =
+	CRM_DATA(wlan24, wlan);
+
+static const struct sf19a2890_periphcrm_data sf19a2890_wlan5_crm =
+	CRM_DATA(wlan5, wlan);
+
+static const struct sf19a2890_periphcrm_gate sf19a2890_rf_gates[] = {
+	GATE("rf_dft", 0, 0),
+	GATE("rf_axis", 1, 0),
+	GATE("rf_boot", 2, 0),
+	GATE("rf_lp", 3, 0),
+};
+
+static const struct sf19a2890_periphcrm_data sf19a2890_rf_crm =
+	CRM_DATA(rf, wlan);
+
+/* Nothing describes a consumer of usb_axim, and gating it is not safe */
+static const struct sf19a2890_periphcrm_gate sf19a2890_usb_gates[] = {
+	GATE("usb", 0, 0),
+	GATE("usb_axim", 2, CLK_IS_CRITICAL),
+	GATE("usbphy_ref", 3, 0),
+};
+
+static const u32 sf19a2890_usb_resets[] = { GENMASK(2, 0), BIT(3), BIT(4) };
+
+static const struct sf19a2890_periphcrm_data sf19a2890_usb_crm =
+	CRM_DATA(usb, usb);
+
+static const struct sf19a2890_periphcrm_gate sf19a2890_spi_gates[] = {
+	GATE("spi_apb", 4, 0),
+	GATE("spi_ssp", 5, 0),
+};
+
+static const u32 sf19a2890_spi_resets[] = { GENMASK(5, 4) };
+
+static const struct sf19a2890_periphcrm_data sf19a2890_spi_crm =
+	CRM_DATA(spi, spi);
+
+/* UART0 carries the console from the bootloader on */
+static const struct sf19a2890_periphcrm_gate sf19a2890_uart_gates[] = {
+	GATE("uart0_apb", 0, CLK_IS_CRITICAL),
+	GATE("uart1_apb", 1, 0),
+	GATE("uart2_apb", 2, 0),
+	GATE("uart0", 4, CLK_IS_CRITICAL),
+	GATE("uart1", 5, 0),
+	GATE("uart2", 6, 0),
+};
+
+static const u32 sf19a2890_uart_resets[] = {
+	BIT(0) | BIT(4), BIT(1) | BIT(5), BIT(2) | BIT(6),
+};
+
+static const struct sf19a2890_periphcrm_data sf19a2890_uart_crm =
+	CRM_DATA(uart, uart);
+
+#define SINGLE_CRM(_name)						\
+	static const struct sf19a2890_periphcrm_gate sf19a2890_##_name##_gates[] = { \
+		GATE(#_name, 0, 0),					\
+	};								\
+	static const struct sf19a2890_periphcrm_data sf19a2890_##_name##_crm = \
+		CRM_DATA(_name, single)
+
+SINGLE_CRM(i2c0);
+SINGLE_CRM(pwm);
+SINGLE_CRM(timer);
+SINGLE_CRM(wdt);
+SINGLE_CRM(gpio);
+
+static int sf19a2890_periphcrm_probe(struct platform_device *pdev)
+{
+	const struct sf19a2890_periphcrm_data *data;
+	struct device *dev = &pdev->dev;
+	struct sf19a2890_periphclk *gates;
+	struct sf19a2890_periphcrm *crm;
+	u32 valid_gates = 0;
+	unsigned int i;
+	int ret;
+	u32 reg;
+
+	data = device_get_match_data(dev);
+	if (!data)
+		return -EINVAL;
+
+	crm = devm_kzalloc(dev, struct_size(crm, clk_data.hws, data->num_gates),
+			   GFP_KERNEL);
+	if (!crm)
+		return -ENOMEM;
+
+	crm->clk_data.num = data->num_gates;
+
+	gates = devm_kcalloc(dev, data->num_gates, sizeof(*gates), GFP_KERNEL);
+	if (!gates)
+		return -ENOMEM;
+
+	crm->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(crm->base))
+		return PTR_ERR(crm->base);
+
+	spin_lock_init(&crm->lock);
+
+	crm->reset_masks = data->reset_masks;
+	crm->rcdev.owner = THIS_MODULE;
+	crm->rcdev.nr_resets = data->num_resets;
+	crm->rcdev.ops = &sf19a2890_periphcrm_reset_ops;
+	crm->rcdev.of_node = dev->of_node;
+
+	ret = devm_reset_controller_register(dev, &crm->rcdev);
+	if (ret)
+		return ret;
+
+	/*
+	 * Clear any stale bits outside the block's gates so the BOE-disable
+	 * check in periphclk_disable() works correctly.
+	 */
+	for (i = 0; i < data->num_gates; i++)
+		valid_gates |= BIT(data->gates[i].bit);
+	reg = readl(crm->base + REG_CLK_GATE) & valid_gates;
+	writel(reg, crm->base + REG_CLK_GATE);
+
+	for (i = 0; i < data->num_gates; i++) {
+		const struct clk_parent_data parent = { .index = i };
+		struct clk_init_data init = {
+			.name = data->gates[i].name,
+			.ops = &sf19a2890_periphclk_ops,
+			.parent_data = &parent,
+			.num_parents = 1,
+			.flags = data->gates[i].flags,
+		};
+
+		gates[i].crm = crm;
+		gates[i].idx = data->gates[i].bit;
+		gates[i].hw.init = &init;
+
+		ret = devm_clk_hw_register(dev, &gates[i].hw);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "failed to register gate %u\n", i);
+
+		crm->clk_data.hws[i] = &gates[i].hw;
+	}
+
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+					   &crm->clk_data);
+}
+
+static const struct of_device_id sf19a2890_periphcrm_dt_ids[] = {
+	{ .compatible = "siflower,sf19a2890-gmac-crm", .data = &sf19a2890_gmac_crm },
+	{ .compatible = "siflower,sf19a2890-gpio-crm", .data = &sf19a2890_gpio_crm },
+	{ .compatible = "siflower,sf19a2890-i2c-crm", .data = &sf19a2890_i2c0_crm },
+	{ .compatible = "siflower,sf19a2890-pwm-crm", .data = &sf19a2890_pwm_crm },
+	{ .compatible = "siflower,sf19a2890-rf-crm", .data = &sf19a2890_rf_crm },
+	{ .compatible = "siflower,sf19a2890-spi-crm", .data = &sf19a2890_spi_crm },
+	{ .compatible = "siflower,sf19a2890-timer-crm", .data = &sf19a2890_timer_crm },
+	{ .compatible = "siflower,sf19a2890-uart-crm", .data = &sf19a2890_uart_crm },
+	{ .compatible = "siflower,sf19a2890-usb-crm", .data = &sf19a2890_usb_crm },
+	{ .compatible = "siflower,sf19a2890-wdt-crm", .data = &sf19a2890_wdt_crm },
+	{ .compatible = "siflower,sf19a2890-wlan24-crm", .data = &sf19a2890_wlan24_crm },
+	{ .compatible = "siflower,sf19a2890-wlan5-crm", .data = &sf19a2890_wlan5_crm },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver sf19a2890_periphcrm_driver = {
+	.probe	= sf19a2890_periphcrm_probe,
+	.driver = {
+		.name		= "sf19a2890-periph-crm",
+		.of_match_table	= sf19a2890_periphcrm_dt_ids,
+	},
+};
+builtin_platform_driver(sf19a2890_periphcrm_driver);

-- 
Git-155)



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/4] dt-bindings: clock: Add Siflower SF19A2890 peripheral clock/reset managers
  2026-11-04  7:21 ` [PATCH 3/4] dt-bindings: clock: Add Siflower SF19A2890 peripheral clock/reset managers Joseph Telaak via B4 Relay
@ 2026-11-04  7:27   ` sashiko-bot
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-11-04  7:27 UTC (permalink / raw)
  To: Joseph Telaak; +Cc: linux-clk, devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] dt-bindings: clock: unwieldy per-compatible if-then conditionals in schema
--

commit a648a6a258536238fc540454eb5ccb65637262cb
Author: Joseph Telaak <joseph@telaak.dev>

dt-bindings: clock: Add Siflower SF19A2890 peripheral clock/reset managers

This commit adds a Devicetree binding schema for the Siflower SF19A2890
peripheral clock and reset managers, defining twelve compatible strings
and their corresponding clock inputs and reset lines.

> diff --git a/Documentation/devicetree/bindings/clock/siflower,sf19a2890-periph-crm.yaml b/Documentation/devicetree/bindings/clock/siflower,sf19a2890-periph-crm.yaml
> new file mode 100644
> index 0000000000000..7f0bce603b423
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/siflower,sf19a2890-periph-crm.yaml
> @@ -0,0 +1,169 @@

[ ... ]

> +properties:
> +  compatible:
> +    enum:
> +      - siflower,sf19a2890-gmac-crm
> +      - siflower,sf19a2890-gpio-crm
> +      - siflower,sf19a2890-i2c-crm
> +      - siflower,sf19a2890-pwm-crm
> +      - siflower,sf19a2890-rf-crm
> +      - siflower,sf19a2890-spi-crm
> +      - siflower,sf19a2890-timer-crm
> +      - siflower,sf19a2890-uart-crm
> +      - siflower,sf19a2890-usb-crm
> +      - siflower,sf19a2890-wdt-crm
> +      - siflower,sf19a2890-wlan24-crm
> +      - siflower,sf19a2890-wlan5-crm

[ ... ]

> +allOf:
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: siflower,sf19a2890-gmac-crm
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: GMAC bus clock
> +            - description: GMAC register clock
> +            - description: RGMII transmit reference clock
> +            - description: Timestamp unit clock
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            enum:
> +              - siflower,sf19a2890-wlan24-crm
> +              - siflower,sf19a2890-wlan5-crm
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: AXI slave clock
> +            - description: AXI master clock
> +            - description: Radio platform clock
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: siflower,sf19a2890-rf-crm
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: Test clock
> +            - description: AXI slave clock
> +            - description: Boot clock
> +            - description: Low-power clock
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: siflower,sf19a2890-usb-crm
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: USB controller clock
> +            - description: AXI master clock
> +            - description: USB PHY reference clock
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: siflower,sf19a2890-spi-crm
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: APB clock
> +            - description: SSP clock
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            const: siflower,sf19a2890-uart-crm
> +    then:
> +      properties:
> +        clocks:
> +          items:
> +            - description: UART0 APB clock
> +            - description: UART1 APB clock
> +            - description: UART2 APB clock
> +            - description: UART0 clock
> +            - description: UART1 clock
> +            - description: UART2 clock
> +  - if:
> +      properties:
> +        compatible:
> +          contains:
> +            enum:
> +              - siflower,sf19a2890-gpio-crm
> +              - siflower,sf19a2890-i2c-crm
> +              - siflower,sf19a2890-pwm-crm
> +              - siflower,sf19a2890-timer-crm
> +              - siflower,sf19a2890-wdt-crm
> +    then:
> +      properties:
> +        clocks:
> +          maxItems: 1

[Severity: Low]
This isn't a bug, but properties.compatible in
Documentation/devicetree/bindings/clock/siflower,sf19a2890-periph-crm.yaml
combines twelve peripheral CRM variants that have different clock counts
(from 1 to 6 clocks), clock roles, and reset assignments, requiring seven
separate per-compatible if-then conditionals under allOf to constrain the
clocks property.

Would it be cleaner to split this binding into separate schema documents
rather than piling these per-compatible if-then conditionals into a single
schema?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261104-sf19a2890-sysm-v1-0-a4fac07df823@telaak.dev?part=3

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/4] clk: siflower: Add SF19A2890 clock manager driver
  2026-11-04  7:20 ` [PATCH 2/4] clk: siflower: Add SF19A2890 clock manager driver Joseph Telaak via B4 Relay
@ 2026-11-04  7:34   ` sashiko-bot
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-11-04  7:34 UTC (permalink / raw)
  To: Joseph Telaak; +Cc: linux-clk, devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] clk: siflower: fractional term scaled by fbdiv in sf19a28_pll_recalc_rate()
- [High] clk: siflower: divide by zero on rate 0 in sf19a28_muxdiv_determine_rate()
--

commit d358931350f6daafe39bfeb8c6193c67ccee78b9
Author: Chuanhong Guo <gch981213@gmail.com>

clk: siflower: Add SF19A2890 clock manager driver

This commit adds an early clock driver for the Siflower SF19A2890 SoC,
supporting its CPU, DDR, and common PLLs as well as the downstream mux-divider
clocks.

> diff --git a/drivers/clk/siflower/clk-sf19a2890.c b/drivers/clk/siflower/clk-sf19a2890.c
> new file mode 100644
> index 0000000000000..f7553ac49d66f
> --- /dev/null
> +++ b/drivers/clk/siflower/clk-sf19a2890.c

[ ... ]

> +static unsigned long sf19a28_pll_recalc_rate(struct clk_hw *hw,
> +					     unsigned long parent_rate)
> +{
> +	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
> +	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
> +	u32 refdiv = sf_pll_param_get(priv, PLL_REFDIV_HI, PLL_REFDIV_LO);
> +	u32 fbdiv = sf_pll_param_get(priv, PLL_FBDIV_HI, PLL_FBDIV_LO);
> +	u32 postdiv1 = sf_pll_param_get(priv, PLL_POSTDIV1_HI, PLL_POSTDIV1_LO);
> +	u32 postdiv2 = sf_pll_param_get(priv, PLL_POSTDIV2_HI, PLL_POSTDIV2_LO);
> +	u32 pll_pd = sf_readl(cmn_priv, priv->offset + REG_PLL_PD);
> +	u64 rate;
> +	u32 frac;
> +
> +	if (!refdiv || !postdiv1 || !postdiv2)
> +		return 0;
> +
> +	rate = (u64)(parent_rate / refdiv) * fbdiv;
> +
> +	if (!(pll_pd & PLL_PD_DSM)) {
> +		frac = sf_pll_param_get(priv, PLL_FRAC_HI, PLL_FRAC_LO);
> +		rate += (rate * frac) >> PLL_FRAC_BITS;
> +	}

[Severity: High]
In sf19a28_pll_recalc_rate() in drivers/clk/siflower/clk-sf19a2890.c, when the
delta-sigma modulator is enabled (!(pll_pd & PLL_PD_DSM)), does multiplying
rate by frac scale the fractional component by an extra factor of fbdiv?

Because rate is already multiplied by fbdiv before the DSM check, computing
(rate * frac) >> PLL_FRAC_BITS evaluates to:

    ((parent_rate / refdiv) * fbdiv * frac) >> PLL_FRAC_BITS

For a fractional-N PLL, shouldn't the fractional term be scaled only by the
reference frequency (parent_rate / refdiv) rather than by the integer VCO
rate?

[ ... ]

> +static int sf19a28_muxdiv_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
> +{
> +	unsigned int div;
> +
> +	div = DIV_ROUND_CLOSEST(req->best_parent_rate, req->rate);
> +	if (!div)
> +		div = 1;
> +	else if (div > MUXDIV_RATIO_MAX + 1)
> +		div = MUXDIV_RATIO_MAX + 1;
> +
> +	req->rate = req->best_parent_rate / div;
> +	return 0;
> +}
> +
> +static int sf19a28_muxdiv_set_rate(struct clk_hw *hw, unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	struct sf_clk_common *cmn_priv = hw_to_sf_clk_common(hw);
> +	struct sf19a2890_clk *priv = cmn_to_clk(cmn_priv);
> +	unsigned int div;
> +
> +	div = DIV_ROUND_CLOSEST(parent_rate, rate);

[Severity: High]
Can sf19a28_muxdiv_determine_rate() and sf19a28_muxdiv_set_rate() in
drivers/clk/siflower/clk-sf19a2890.c divide by zero if a consumer calls
clk_round_rate(clk, 0) or clk_set_rate(clk, 0)?

In the common clock framework, clk_core_init_rate_req() initializes
req->min_rate to 0 by default, so a requested rate of 0 is passed directly
into sf19a28_muxdiv_determine_rate() as req->rate == 0 without being clamped.
Similarly, if req->rate evaluates to 0, clk_change_rate() can pass rate == 0
to sf19a28_muxdiv_set_rate().

Should both functions guard against a zero target rate before calling
DIV_ROUND_CLOSEST()?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261104-sf19a2890-sysm-v1-0-a4fac07df823@telaak.dev?part=2

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver
  2026-11-04  7:21 ` [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver Joseph Telaak via B4 Relay
@ 2026-11-04  9:25   ` Philipp Zabel
  2026-11-04 13:37     ` Chuanhong Guo
  2026-11-04 13:32   ` Chuanhong Guo
  1 sibling, 1 reply; 10+ messages in thread
From: Philipp Zabel @ 2026-11-04  9:25 UTC (permalink / raw)
  To: joseph, Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: Chuanhong Guo, Yao Zi, linux-mips, linux-clk, devicetree,
	linux-kernel

On Mi, 2026-11-04 at 02:21 -0500, Joseph Telaak via B4 Relay wrote:
> From: Chuanhong Guo <gch981213@gmail.com>
> 
> Add a platform driver for the peripheral clock and reset manager (CRM)
> block found on Siflower SF19A2890 and compatible SoCs.
> 
> Each CRM block manages a group of APB peripherals through three registers:
> a reset register, a clock gate register, and a shared Bus Output Enable
> (BOE) register that must be asserted for any peripheral in the block to
> respond to bus transactions.
> 
> The driver registers a clock gate controller and a reset controller from
> a single DT node. A shared spinlock protects all read-modify-write
> accesses across both. BOE is managed automatically: asserted when the
> first clock gate is enabled and deasserted when the last one is disabled.
> 
> Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
> [jt: from Chuanhong's siflower-7.1 branch; one compatible per block with
>  its gate bits, critical gates and reset masks in tables here instead of
>  the siflower,valid-gates, siflower,critical-gates, siflower,reset-masks
>  and siflower,num-resets properties, gate N parented to input clock N by
>  index instead of by global name, the gate updates under the spinlock too,
>  no separator comments, Kconfig dependencies and help, subject]
> Signed-off-by: Joseph Telaak <joseph@telaak.dev>
> ---
>  drivers/clk/siflower/Kconfig                   |  11 +
>  drivers/clk/siflower/Makefile                  |   1 +
>  drivers/clk/siflower/clk-sf19a2890-periphcrm.c | 360 +++++++++++++++++++++++++
>  3 files changed, 372 insertions(+)
> 
[...]
> diff --git a/drivers/clk/siflower/clk-sf19a2890-periphcrm.c b/drivers/clk/siflower/clk-sf19a2890-periphcrm.c
> new file mode 100644
> index 000000000..95d4dd449
> --- /dev/null
> +++ b/drivers/clk/siflower/clk-sf19a2890-periphcrm.c
> @@ -0,0 +1,360 @@
[...]
> +#define CRM_DATA(_g, _r)						\
> +	{								\
> +		.gates = sf19a2890_##_g##_gates,			\
> +		.num_gates = ARRAY_SIZE(sf19a2890_##_g##_gates),	\
> +		.reset_masks = sf19a2890_##_r##_resets,			\
> +		.num_resets = ARRAY_SIZE(sf19a2890_##_r##_resets),	\
> +	}
> +
> +static const u32 sf19a2890_single_resets[] = { BIT(0) };
> +
[...]
> +
> +static const u32 sf19a2890_gmac_resets[] = { GENMASK(1, 0) };

Are these two bits separate resets? Are they individually controllable?
Why are they bundled into a single reset_control like this? The commit
message doesn't explain.

[...]
> +static const u32 sf19a2890_wlan_resets[] = { GENMASK(2, 0) };
[...]
> +static const u32 sf19a2890_usb_resets[] = { GENMASK(2, 0), BIT(3), BIT(4) };
[...]
> +static const u32 sf19a2890_spi_resets[] = { GENMASK(5, 4) };
[...]
> +static const u32 sf19a2890_uart_resets[] = {
> +	BIT(0) | BIT(4), BIT(1) | BIT(5), BIT(2) | BIT(6),
> +};

Same question for the other multi-bit masks.


regards
Philipp

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver
  2026-11-04  7:21 ` [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver Joseph Telaak via B4 Relay
  2026-11-04  9:25   ` Philipp Zabel
@ 2026-11-04 13:32   ` Chuanhong Guo
  1 sibling, 0 replies; 10+ messages in thread
From: Chuanhong Guo @ 2026-11-04 13:32 UTC (permalink / raw)
  To: joseph
  Cc: Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Yao Zi,
	linux-mips, linux-clk, devicetree, linux-kernel

On Wed, Nov 4, 2026 at 3:21 PM Joseph Telaak via B4 Relay
<devnull+joseph.telaak.dev@kernel.org> wrote:
>
> From: Chuanhong Guo <gch981213@gmail.com>
>
> Add a platform driver for the peripheral clock and reset manager (CRM)
> block found on Siflower SF19A2890 and compatible SoCs.
>
> Each CRM block manages a group of APB peripherals through three registers:
> a reset register, a clock gate register, and a shared Bus Output Enable
> (BOE) register that must be asserted for any peripheral in the block to
> respond to bus transactions.
>
> The driver registers a clock gate controller and a reset controller from
> a single DT node. A shared spinlock protects all read-modify-write
> accesses across both. BOE is managed automatically: asserted when the
> first clock gate is enabled and deasserted when the last one is disabled.
>
> Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
> [jt: from Chuanhong's siflower-7.1 branch; one compatible per block with
>  its gate bits, critical gates and reset masks in tables here instead of
>  the siflower,valid-gates, siflower,critical-gates, siflower,reset-masks
>  and siflower,num-resets properties

Why? This block is one used repeatedly inside this SoC and SF21,
I think a generic binding with clock & reset description in dt makes it
reusable without further changes and also results in a shorter driver
without a table. This table in driver aren't simplifying the dt bindings
anyway since it still needs one block per node.

-- 
Regards,
Chuanhong Guo

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver
  2026-11-04  9:25   ` Philipp Zabel
@ 2026-11-04 13:37     ` Chuanhong Guo
  0 siblings, 0 replies; 10+ messages in thread
From: Chuanhong Guo @ 2026-11-04 13:37 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: joseph, Stephen Boyd, Brian Masney, Jerome Brunet, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Yao Zi, linux-mips, linux-clk,
	devicetree, linux-kernel

Hi!

On Wed, Nov 4, 2026 at 5:25 PM Philipp Zabel <p.zabel@pengutronix.de> wrote:
> [...]
> > +
> > +static const u32 sf19a2890_gmac_resets[] = { GENMASK(1, 0) };
>
> Are these two bits separate resets? Are they individually controllable?
> Why are they bundled into a single reset_control like this? The commit
> message doesn't explain.

These are reset lines from the same IP block for different clock domains.
They have to be asserted together even though there are two lines.
See nUARTRST in [1] for an example of PL011.
The gmac lines are for two sides of the same async axi bridge.
Asserting them separately causes a bus lockup.

[1] https://support.arm.com/documentation/ddi0183/g/signal-descriptions/on-chip-signals
-- 
Regards,
Chuanhong Guo

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-11-04 13:37 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-04  7:20 [PATCH 0/4] clk: Add the Siflower SF19A2890 clock manager and peripheral clock and reset managers Joseph Telaak via B4 Relay
2026-11-04  7:20 ` [PATCH 1/4] dt-bindings: clock: Add Siflower SF19A2890 clock manager Joseph Telaak via B4 Relay
2026-11-04  7:20 ` [PATCH 2/4] clk: siflower: Add SF19A2890 clock manager driver Joseph Telaak via B4 Relay
2026-11-04  7:34   ` sashiko-bot
2026-11-04  7:21 ` [PATCH 3/4] dt-bindings: clock: Add Siflower SF19A2890 peripheral clock/reset managers Joseph Telaak via B4 Relay
2026-11-04  7:27   ` sashiko-bot
2026-11-04  7:21 ` [PATCH 4/4] clk: siflower: Add SF19A2890 peripheral clock and reset manager driver Joseph Telaak via B4 Relay
2026-11-04  9:25   ` Philipp Zabel
2026-11-04 13:37     ` Chuanhong Guo
2026-11-04 13:32   ` Chuanhong Guo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox