public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/6] clk/reset: anlogic: add support for DR1V90 SoC
@ 2025-12-31  6:40 Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 1/6] clk: correct clk_div_mask() return value for width == 32 Junhui Liu
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Junhui Liu @ 2025-12-31  6:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-clk, linux-kernel, linux-riscv, devicetree, Troy Mitchell,
	Brian Masney, Conor Dooley

Add Clock and Reset Unit (CRU) support for the Anlogic DR1V90 SoC, as
well as corresponding dts bindings and dts integration.

The CRU driver framework is built around the clock controller as the
primary device, with the reset controller implemented as an auxiliary
device. The clock part refers to the vendor's code [1] to determine the
structure of the clock tree.

The Anlogic DR1 series includes not only the DR1V90 (based on the Nuclei
UX900 RISC-V core), but also the DR1M90 (based on the Cortex-A35 ARM64
core). Most of the clock tree and CRU design can be shared between them.
This series only adds CRU support for DR1V90. Nevertheless, the driver
is structured to make future extension to other DR1 variants like
DR1M90.

Link: https://gitee.com/anlogic/linux/blob/anlogic-6.1.54/drivers/clk/anlogic/anl_dr1x90_crp.c [1]

---
Changes in v4:
- Change common cru_dr1 code into a standalone module for future reuse
- Remove redundant .round_rate() in the clock driver
- Use devm_auxiliary_device_create() to simplify auxiliary device
  registration
- Pass register base from clk to reset via platform_data instead of
  performing a second ioremap
- Update clock Kconfig and Makefile licenses to GPL-2.0-only from
  deprecated GPL-2.0
- Add Kconfig dependency for the reset driver on the clock driver
- Link to v3: https://lore.kernel.org/r/20251216-dr1v90-cru-v3-0-52cc938d1db0@pigmoral.tech

Changes in v3:
- Remove incorrect __free(kfree) usage for auxiliary_device in clock
  driver
- Replace __clk_get_enable_count with __clk_is_enabled in clock driver
- Add a lock to protect register read-modify-write in reset driver
- Rebase to v6.19-rc1
- Link to v2: https://lore.kernel.org/r/20251026-dr1v90-cru-v2-0-43b67acd6ddd@pigmoral.tech

Changes in v2:
- Update copyright infomation
- Add the original vendor author's infomation to the clock driver
- Rebase on the v3 basic DT patch, which is based on v6.18-rc1
- Link to v1: https://lore.kernel.org/r/20250922-dr1v90-cru-v1-0-e393d758de4e@pigmoral.tech

---
Junhui Liu (6):
      clk: correct clk_div_mask() return value for width == 32
      dt-bindings: clock: add Anlogic DR1V90 CRU
      clk: anlogic: add cru support for Anlogic DR1V90 SoC
      reset: anlogic: add support for Anlogic DR1V90 resets
      riscv: dts: anlogic: add clocks and CRU for DR1V90
      MAINTAINERS: Add entry for Anlogic DR1V90 SoC drivers

 .../bindings/clock/anlogic,dr1v90-cru.yaml         |  60 ++++++
 MAINTAINERS                                        |   7 +
 arch/riscv/boot/dts/anlogic/dr1v90.dtsi            |  41 +++-
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/anlogic/Kconfig                        |  21 ++
 drivers/clk/anlogic/Makefile                       |   7 +
 drivers/clk/anlogic/cru-dr1v90.c                   | 192 +++++++++++++++++
 drivers/clk/anlogic/cru_dr1.c                      | 226 +++++++++++++++++++++
 drivers/clk/anlogic/cru_dr1.h                      | 117 +++++++++++
 drivers/reset/Kconfig                              |  10 +
 drivers/reset/Makefile                             |   1 +
 drivers/reset/reset-dr1v90.c                       | 141 +++++++++++++
 include/dt-bindings/clock/anlogic,dr1v90-cru.h     |  46 +++++
 include/dt-bindings/reset/anlogic,dr1v90-cru.h     |  41 ++++
 include/linux/clk-provider.h                       |   2 +-
 16 files changed, 911 insertions(+), 3 deletions(-)
---
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
change-id: 20250922-dr1v90-cru-74ab40c7f273

Best regards,
-- 
Junhui Liu <junhui.liu@pigmoral.tech>


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

* [PATCH v4 1/6] clk: correct clk_div_mask() return value for width == 32
  2025-12-31  6:40 [PATCH v4 0/6] clk/reset: anlogic: add support for DR1V90 SoC Junhui Liu
@ 2025-12-31  6:40 ` Junhui Liu
  2025-12-31 10:56   ` David Laight
  2025-12-31  6:40 ` [PATCH v4 2/6] dt-bindings: clock: add Anlogic DR1V90 CRU Junhui Liu
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 9+ messages in thread
From: Junhui Liu @ 2025-12-31  6:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-clk, linux-kernel, linux-riscv, devicetree, Troy Mitchell,
	Brian Masney

The macro clk_div_mask() currently wraps to zero when width is 32 due to
1 << 32 being undefined behavior. This leads to incorrect mask generation
and prevents correct retrieval of register field values for 32-bit-wide
dividers.

Although it is unlikely to exhaust all U32_MAX div, some clock IPs may rely
on a 32-bit val entry in their div_table to match a div, so providing a
full 32-bit mask is necessary.

Fix this by casting 1 to long, ensuring proper behavior for valid widths up
to 32.

Reviewed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
Reviewed-by: Brian Masney <bmasney@redhat.com>
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 include/linux/clk-provider.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 630705a47129..a651ccaf1b44 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -720,7 +720,7 @@ struct clk_divider {
 	spinlock_t	*lock;
 };
 
-#define clk_div_mask(width)	((1 << (width)) - 1)
+#define clk_div_mask(width)	((1L << (width)) - 1)
 #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
 
 #define CLK_DIVIDER_ONE_BASED		BIT(0)

-- 
2.52.0


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

* [PATCH v4 2/6] dt-bindings: clock: add Anlogic DR1V90 CRU
  2025-12-31  6:40 [PATCH v4 0/6] clk/reset: anlogic: add support for DR1V90 SoC Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 1/6] clk: correct clk_div_mask() return value for width == 32 Junhui Liu
@ 2025-12-31  6:40 ` Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 3/6] clk: anlogic: add cru support for Anlogic DR1V90 SoC Junhui Liu
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Junhui Liu @ 2025-12-31  6:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-clk, linux-kernel, linux-riscv, devicetree, Conor Dooley

Add the Clock and Reset Unit (CRU) support for the Anlogic DR1V90 SoC,
which is responsible for clock and reset management.

Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 .../bindings/clock/anlogic,dr1v90-cru.yaml         | 60 ++++++++++++++++++++++
 include/dt-bindings/clock/anlogic,dr1v90-cru.h     | 46 +++++++++++++++++
 include/dt-bindings/reset/anlogic,dr1v90-cru.h     | 41 +++++++++++++++
 3 files changed, 147 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/anlogic,dr1v90-cru.yaml b/Documentation/devicetree/bindings/clock/anlogic,dr1v90-cru.yaml
new file mode 100644
index 000000000000..165c71ce333a
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/anlogic,dr1v90-cru.yaml
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/anlogic,dr1v90-cru.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Anlogic DR1V90 Clock and Reset Unit (CRU)
+
+maintainers:
+  - Junhui Liu <junhui.liu@pigmoral.tech>
+
+properties:
+  compatible:
+    const: anlogic,dr1v90-cru
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Main Oscillator (33 MHz)
+      - description: External CAN clock
+      - description: External WDT clock
+
+  clock-names:
+    items:
+      - const: osc_33m
+      - const: can_ext
+      - const: wdt_ext
+
+  "#clock-cells":
+    const: 1
+    description:
+      Refer <dt-bindings/clock/anlogic,dr1v90-cru.h> for valid indices.
+
+  "#reset-cells":
+    const: 1
+    description:
+      Refer <dt-bindings/reset/anlogic,dr1v90-cru.h> for valid indices.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - "#clock-cells"
+  - "#reset-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@f8801000 {
+        compatible = "anlogic,dr1v90-cru";
+        reg = <0xf8801000 0x400>;
+        clocks = <&osc_33m>, <&can_ext>, <&wdt_ext>;
+        clock-names = "osc_33m", "can_ext", "wdt_ext";
+        #clock-cells = <1>;
+        #reset-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/anlogic,dr1v90-cru.h b/include/dt-bindings/clock/anlogic,dr1v90-cru.h
new file mode 100644
index 000000000000..664daf3946a7
--- /dev/null
+++ b/include/dt-bindings/clock/anlogic,dr1v90-cru.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) 2024-2025 Anlogic, Inc.
+ * Copyright (C) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#ifndef _DT_BINDINGS_CLOCK_ANLOGIC_DR1V90_CRU_H_
+#define _DT_BINDINGS_CLOCK_ANLOGIC_DR1V90_CRU_H_
+
+#define CLK_OSC_DIV2		0
+#define CLK_CPU_PLL		1
+#define CLK_CPU_PLL_4X		2
+#define CLK_CPU_4X		3
+#define CLK_CPU_2X		4
+#define CLK_CPU_1X		5
+#define CLK_IO_PLL		6
+#define CLK_IO_1000M		7
+#define CLK_IO_400M		8
+#define CLK_IO_25M		9
+#define CLK_IO_80M		10
+#define CLK_IO_400M_DIV2	11
+#define CLK_IO_400M_DIV4	12
+#define CLK_IO_400M_DIV8	13
+#define CLK_IO_400M_DIV16	14
+#define CLK_QSPI		15
+#define CLK_SPI			16
+#define CLK_SMC			17
+#define CLK_SDIO		18
+#define CLK_GPIO_DB		19
+#define CLK_EFUSE		20
+#define CLK_TVS			21
+#define CLK_TRNG		22
+#define CLK_OSC_DIV		23
+#define CLK_PWM			24
+#define CLK_FCLK0		25
+#define CLK_FCLK1		26
+#define CLK_FCLK2		27
+#define CLK_FCLK3		28
+#define CLK_WDT_SEL		29
+#define CLK_EFUSE_SEL		30
+#define CLK_CAN_SEL		31
+#define CLK_CPU_SEL		32
+#define CLK_CAN0		33
+#define CLK_CAN1		34
+
+#endif /* _DT_BINDINGS_CLOCK_ANLOGIC_DR1V90_CRU_H_ */
diff --git a/include/dt-bindings/reset/anlogic,dr1v90-cru.h b/include/dt-bindings/reset/anlogic,dr1v90-cru.h
new file mode 100644
index 000000000000..c5b7aae1ab6a
--- /dev/null
+++ b/include/dt-bindings/reset/anlogic,dr1v90-cru.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#ifndef _DT_BINDINGS_RESET_ANLOGIC_DR1V90_CRU_H_
+#define _DT_BINDINGS_RESET_ANLOGIC_DR1V90_CRU_H_
+
+#define RESET_OCM		0
+#define RESET_QSPI		1
+#define RESET_SMC		2
+#define RESET_WDT		3
+#define RESET_DMAC_AXI		4
+#define RESET_DMAC_AHB		5
+#define RESET_NPU		6
+#define RESET_JPU		7
+#define RESET_DDRBUS		8
+#define RESET_NIC_HP0		9
+#define RESET_NIC_HP1		10
+#define RESET_NIC_GP0M		11
+#define RESET_NIC_GP1M		12
+#define RESET_GPIO		13
+#define RESET_IPC		14
+#define RESET_USB0		15
+#define RESET_USB1		16
+#define RESET_GBE0		17
+#define RESET_GBE1		18
+#define RESET_SDIO0		19
+#define RESET_SDIO1		20
+#define RESET_UART0		21
+#define RESET_UART1		22
+#define RESET_SPI0		23
+#define RESET_SPI1		24
+#define RESET_CAN0		25
+#define RESET_CAN1		26
+#define RESET_TTC0		27
+#define RESET_TTC1		28
+#define RESET_I2C0		29
+#define RESET_I2C1		30
+
+#endif /* _DT_BINDINGS_RESET_ANLOGIC_DR1V90_CRU_H_ */

-- 
2.52.0


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

* [PATCH v4 3/6] clk: anlogic: add cru support for Anlogic DR1V90 SoC
  2025-12-31  6:40 [PATCH v4 0/6] clk/reset: anlogic: add support for DR1V90 SoC Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 1/6] clk: correct clk_div_mask() return value for width == 32 Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 2/6] dt-bindings: clock: add Anlogic DR1V90 CRU Junhui Liu
@ 2025-12-31  6:40 ` Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 4/6] reset: anlogic: add support for Anlogic DR1V90 resets Junhui Liu
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Junhui Liu @ 2025-12-31  6:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-clk, linux-kernel, linux-riscv, devicetree

The Clock and Reset Unit (CRU) in the Anlogic DR1V90 SoC provides
management for the clock and reset.

The clock driver includes support for:
- Generic clocks: fixed-factor, divider, mux and gate.
- PLL: "nm" type (parent * n / m) and "c" type (parent / c). These PLLs
  are set up by the FSBL and mared as "don't touch" in the datasheet, so
  only the recal_rate() op is provided.
- Divider with gate: support both division and gating (by setting value
  to 0); some of them require a minimum divider value to avoid timing
  issues.

This also prepares the structure for the reset controller support,
registering an auxiliary device for resets.

Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/clk/Kconfig              |   1 +
 drivers/clk/Makefile             |   1 +
 drivers/clk/anlogic/Kconfig      |  21 ++++
 drivers/clk/anlogic/Makefile     |   7 ++
 drivers/clk/anlogic/cru-dr1v90.c | 192 +++++++++++++++++++++++++++++++++
 drivers/clk/anlogic/cru_dr1.c    | 226 +++++++++++++++++++++++++++++++++++++++
 drivers/clk/anlogic/cru_dr1.h    | 117 ++++++++++++++++++++
 7 files changed, 565 insertions(+)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3a1611008e48..b61d6c799bc7 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -513,6 +513,7 @@ config COMMON_CLK_RPMI
 
 source "drivers/clk/actions/Kconfig"
 source "drivers/clk/analogbits/Kconfig"
+source "drivers/clk/anlogic/Kconfig"
 source "drivers/clk/baikal-t1/Kconfig"
 source "drivers/clk/bcm/Kconfig"
 source "drivers/clk/hisilicon/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 61ec08404442..4eea2736f468 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -114,6 +114,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE)		+= clk-xgene.o
 # please keep this section sorted lexicographically by directory path name
 obj-y					+= actions/
 obj-y					+= analogbits/
+obj-y					+= anlogic/
 obj-$(CONFIG_COMMON_CLK_AT91)		+= at91/
 obj-$(CONFIG_ARCH_ARTPEC)		+= axis/
 obj-$(CONFIG_ARC_PLAT_AXS10X)		+= axs10x/
diff --git a/drivers/clk/anlogic/Kconfig b/drivers/clk/anlogic/Kconfig
new file mode 100644
index 000000000000..b3b17540176d
--- /dev/null
+++ b/drivers/clk/anlogic/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config ANLOGIC_DR1_CRU
+	tristate "Clock support for Anlogic DR1 series SoCs"
+	depends on ARCH_ANLOGIC || COMPILE_TEST
+	select AUXILIARY_BUS
+	default ARCH_ANLOGIC
+	help
+	  Say Y to enable clock controller unit support for Anlogic DR1 series
+	  SoCs.
+
+if ANLOGIC_DR1_CRU
+
+config ANLOGIC_DR1V90_CRU
+	tristate "Anlogic DR1V90 clock support"
+	depends on ARCH_ANLOGIC || COMPILE_TEST
+	default ARCH_ANLOGIC
+	help
+	  Support for the Clock and Reset Unit in Anlogic DR1V90 SoCs.
+
+endif
diff --git a/drivers/clk/anlogic/Makefile b/drivers/clk/anlogic/Makefile
new file mode 100644
index 000000000000..fab7f29bb097
--- /dev/null
+++ b/drivers/clk/anlogic/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_ANLOGIC_DR1_CRU)		+= anlogic-dr1-cru.o
+anlogic-dr1-cru-y			+= cru_dr1.o
+
+obj-$(CONFIG_ANLOGIC_DR1V90_CRU)	+= anlogic-dr1v90-cru.o
+anlogic-dr1v90-cru-y			+= cru-dr1v90.o
diff --git a/drivers/clk/anlogic/cru-dr1v90.c b/drivers/clk/anlogic/cru-dr1v90.c
new file mode 100644
index 000000000000..46fc83aa2044
--- /dev/null
+++ b/drivers/clk/anlogic/cru-dr1v90.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 Anlogic, Inc.
+ * Copyright (C) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#include <linux/array_size.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "cru_dr1.h"
+
+#include <dt-bindings/clock/anlogic,dr1v90-cru.h>
+
+static const struct clk_div_table cru_div_table_24[] = {
+	{ 0xFFFFFF, 1 },  { 0x555555, 2 },  { 0x249249, 3 },  { 0x111111, 4 },
+	{ 0x084210, 5 },  { 0x041041, 6 },  { 0x020408, 7 },  { 0x010101, 8 },
+	{ 0x008040, 9 },  { 0x004010, 10 }, { 0x002004, 11 }, { 0x001001, 12 },
+	{ 0x000800, 13 }, { 0x000400, 14 }, { 0x000200, 15 }, { 0x000100, 16 },
+	{ 0x000080, 17 }, { 0x000040, 18 }, { 0x000020, 19 }, { 0x000010, 20 },
+	{ 0x000008, 21 }, { 0x000004, 22 }, { 0x000002, 23 }, { 0x000001, 24 },
+	{ /* sentinel */ }
+};
+
+static const struct clk_div_table cru_div_table_32[] = {
+	{ 0xFFFFFFFF, 1 },  { 0x55555555, 2 },	{ 0x24924924, 3 },
+	{ 0x11111111, 4 },  { 0x08421084, 5 },	{ 0x04104104, 6 },
+	{ 0x02040810, 7 },  { 0x01010101, 8 },	{ 0x00804020, 9 },
+	{ 0x00401004, 10 }, { 0x00200400, 11 }, { 0x00100100, 12 },
+	{ 0x00080040, 13 }, { 0x00040010, 14 }, { 0x00020004, 15 },
+	{ 0x00010001, 16 }, { 0x00008000, 17 }, { 0x00004000, 18 },
+	{ 0x00002000, 19 }, { 0x00001000, 20 }, { 0x00000800, 21 },
+	{ 0x00000400, 22 }, { 0x00000200, 23 }, { 0x00000100, 24 },
+	{ 0x00000080, 25 }, { 0x00000040, 26 }, { 0x00000020, 27 },
+	{ 0x00000010, 28 }, { 0x00000008, 29 }, { 0x00000004, 30 },
+	{ 0x00000002, 31 }, { 0x00000001, 32 }, { /* sentinel */ }
+};
+
+CLK_FIXED_FACTOR_FW_NAME(osc_div2, "osc_div2", "osc_33m", 2, 1, 0);
+
+CRU_PLL_NM_DEFINE(cpu_pll, CRU_PARENT_NAME(osc_33m), 0x120);
+CRU_PLL_C_DEFINE(cpu_pll_4x, CRU_PARENT_HW(cpu_pll), 0x14c);
+
+CRU_DIV_DEFINE(cpu_4x_div1, CRU_PARENT_HW(cpu_pll_4x), 0x010, 0, 24,
+	       cru_div_table_24, CLK_DIVIDER_READ_ONLY);
+CRU_DIV_DEFINE(cpu_4x_div2, CRU_PARENT_HW(cpu_pll_4x), 0x014, 0, 24,
+	       cru_div_table_24, CLK_DIVIDER_READ_ONLY);
+CRU_DIV_DEFINE(cpu_4x_div4, CRU_PARENT_HW(cpu_pll_4x), 0x018, 0, 24,
+	       cru_div_table_24, CLK_DIVIDER_READ_ONLY);
+
+CRU_PLL_NM_DEFINE(io_pll, CRU_PARENT_NAME(osc_33m), 0x220);
+CRU_PLL_C_DEFINE(io_1000m, CRU_PARENT_HW(io_pll), 0x248);
+CRU_PLL_C_DEFINE(io_400m, CRU_PARENT_HW(io_pll), 0x24c);
+CRU_PLL_C_DEFINE(io_25m, CRU_PARENT_HW(io_pll), 0x250);
+CRU_PLL_C_DEFINE(io_80m, CRU_PARENT_HW(io_pll), 0x254);
+
+CRU_DIV_DEFINE(io_400m_div2, CRU_PARENT_HW(io_400m), 0x020, 0, 32,
+	       cru_div_table_32, CLK_DIVIDER_READ_ONLY);
+CRU_DIV_DEFINE(io_400m_div4, CRU_PARENT_HW(io_400m), 0x024, 0, 32,
+	       cru_div_table_32, CLK_DIVIDER_READ_ONLY);
+CRU_DIV_DEFINE(io_400m_div8, CRU_PARENT_HW(io_400m), 0x028, 0, 32,
+	       cru_div_table_32, CLK_DIVIDER_READ_ONLY);
+CRU_DIV_DEFINE(io_400m_div16, CRU_PARENT_HW(io_400m), 0x02c, 0, 32,
+	       cru_div_table_32, CLK_DIVIDER_READ_ONLY);
+
+CRU_DIV_GATE_DEFINE(qspi, CRU_PARENT_HW(io_1000m), 0x030, 0, 6, NULL, 0, 2);
+CRU_DIV_GATE_DEFINE(spi, CRU_PARENT_HW(io_1000m), 0x030, 8, 6, NULL, 0, 4);
+CRU_DIV_GATE_DEFINE(smc, CRU_PARENT_HW(io_1000m), 0x030, 16, 6, NULL, 0, 4);
+CRU_DIV_DEFINE(sdio, CRU_PARENT_HW(io_400m), 0x030, 24, 6, NULL, 0);
+
+CRU_DIV_GATE_DEFINE(gpio_db, CRU_PARENT_HW(io_25m), 0x034, 0, 6, NULL, 0, 1);
+CRU_DIV_GATE_DEFINE(efuse, CRU_PARENT_HW(io_25m), 0x034, 8, 6, NULL, 0, 1);
+CRU_DIV_GATE_DEFINE(tvs, CRU_PARENT_HW(io_25m), 0x034, 16, 6, NULL, 0, 1);
+CRU_DIV_GATE_DEFINE(trng, CRU_PARENT_HW(io_25m), 0x034, 24, 7, NULL, 0, 1);
+
+CRU_DIV_GATE_DEFINE(osc_div, CRU_PARENT_NAME(osc_33m), 0x038, 0, 6, NULL, 0, 1);
+CRU_DIV_GATE_DEFINE(pwm, CRU_PARENT_NAME(osc_33m), 0x038, 8, 12, NULL, 0, 1);
+
+CRU_DIV_GATE_DEFINE(fclk0, CRU_PARENT_HW(io_400m), 0x03c, 0, 6, NULL, 0, 1);
+CRU_DIV_GATE_DEFINE(fclk1, CRU_PARENT_HW(io_400m), 0x03c, 8, 6, NULL, 0, 1);
+CRU_DIV_GATE_DEFINE(fclk2, CRU_PARENT_HW(io_400m), 0x03c, 16, 6, NULL, 0, 1);
+CRU_DIV_GATE_DEFINE(fclk3, CRU_PARENT_HW(io_400m), 0x03c, 24, 6, NULL, 0, 1);
+
+static const struct clk_parent_data wdt_parents[] = {
+	CRU_PARENT_HW(osc_div2),
+	CRU_PARENT_NAME(wdt_ext)
+};
+CRU_MUX_DEFINE(wdt_sel, wdt_parents, 0x040, 1, 1);
+
+static const struct clk_parent_data efuse_parents[] = {
+	CRU_PARENT_NAME(osc_33m),
+	CRU_PARENT_DIV_HW(efuse)
+};
+CRU_MUX_DEFINE(efuse_sel, efuse_parents, 0x040, 2, 1);
+
+static const struct clk_parent_data can_parents[] = {
+	CRU_PARENT_HW(io_80m),
+	CRU_PARENT_NAME(can_ext)
+};
+CRU_MUX_DEFINE(can_sel, can_parents, 0x040, 3, 1);
+
+static const struct clk_parent_data cpu_parents[] = {
+	CRU_PARENT_HW(cpu_4x_div1),
+	CRU_PARENT_HW(cpu_4x_div2)
+};
+CRU_MUX_DEFINE(cpu_sel, cpu_parents, 0x040, 5, 1);
+
+CRU_GATE_DEFINE(can0, CRU_PARENT_HW(can_sel), 0x08c, 20, CLK_GATE_SET_TO_DISABLE);
+CRU_GATE_DEFINE(can1, CRU_PARENT_HW(can_sel), 0x08c, 21, CLK_GATE_SET_TO_DISABLE);
+
+static const struct cru_clk dr1v90_cru_clks[] = {
+	[CLK_OSC_DIV2]		= { &osc_div2.hw,	NULL },
+	[CLK_CPU_PLL]		= { &cpu_pll.hw,	&cpu_pll.reg },
+	[CLK_CPU_PLL_4X]	= { &cpu_pll_4x.hw,	&cpu_pll_4x.reg },
+	[CLK_CPU_4X]		= { &cpu_4x_div1.hw,	&cpu_4x_div1.reg },
+	[CLK_CPU_2X]		= { &cpu_4x_div2.hw,	&cpu_4x_div2.reg },
+	[CLK_CPU_1X]		= { &cpu_4x_div4.hw,	&cpu_4x_div4.reg },
+	[CLK_IO_PLL]		= { &io_pll.hw,		&io_pll.reg },
+	[CLK_IO_1000M]		= { &io_1000m.hw,	&io_1000m.reg },
+	[CLK_IO_400M]		= { &io_400m.hw,	&io_400m.reg },
+	[CLK_IO_25M]		= { &io_25m.hw,		&io_25m.reg },
+	[CLK_IO_80M]		= { &io_80m.hw,		&io_80m.reg },
+	[CLK_IO_400M_DIV2]	= { &io_400m_div2.hw,	&io_400m_div2.reg },
+	[CLK_IO_400M_DIV4]	= { &io_400m_div4.hw,	&io_400m_div4.reg },
+	[CLK_IO_400M_DIV8]	= { &io_400m_div8.hw,	&io_400m_div8.reg },
+	[CLK_IO_400M_DIV16]	= { &io_400m_div16.hw,	&io_400m_div16.reg },
+	[CLK_QSPI]		= { &qspi.divider.hw,	&qspi.divider.reg },
+	[CLK_SPI]		= { &spi.divider.hw,	&spi.divider.reg },
+	[CLK_SMC]		= { &smc.divider.hw,	&smc.divider.reg },
+	[CLK_SDIO]		= { &sdio.hw,		&sdio.reg },
+	[CLK_GPIO_DB]		= { &gpio_db.divider.hw, &gpio_db.divider.reg },
+	[CLK_EFUSE]		= { &efuse.divider.hw,	&efuse.divider.reg },
+	[CLK_TVS]		= { &tvs.divider.hw,	&tvs.divider.reg },
+	[CLK_TRNG]		= { &trng.divider.hw,	&trng.divider.reg },
+	[CLK_OSC_DIV]		= { &osc_div.divider.hw, &osc_div.divider.reg },
+	[CLK_PWM]		= { &pwm.divider.hw,	&pwm.divider.reg },
+	[CLK_FCLK0]		= { &fclk0.divider.hw,	&fclk0.divider.reg },
+	[CLK_FCLK1]		= { &fclk1.divider.hw,	&fclk1.divider.reg },
+	[CLK_FCLK2]		= { &fclk2.divider.hw,	&fclk2.divider.reg },
+	[CLK_FCLK3]		= { &fclk3.divider.hw,	&fclk3.divider.reg },
+	[CLK_WDT_SEL]		= { &wdt_sel.hw,	&wdt_sel.reg },
+	[CLK_EFUSE_SEL]		= { &efuse_sel.hw,	&efuse_sel.reg },
+	[CLK_CAN_SEL]		= { &can_sel.hw,	&can_sel.reg },
+	[CLK_CPU_SEL]		= { &cpu_sel.hw,	&cpu_sel.reg },
+	[CLK_CAN0]		= { &can0.hw,		&can0.reg },
+	[CLK_CAN1]		= { &can1.hw,		&can1.reg }
+};
+
+static int dr1v90_cru_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	void __iomem *base;
+	int ret;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	ret = dr1_cru_clk_register(dev, base, dr1v90_cru_clks,
+				   ARRAY_SIZE(dr1v90_cru_clks));
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register clocks\n");
+
+	ret = dr1_cru_reset_register(dev, base);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register resets\n");
+
+	return 0;
+}
+
+static const struct of_device_id dr1v90_cru_ids[] = {
+	{ .compatible = "anlogic,dr1v90-cru" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dr1v90_cru_ids);
+
+static struct platform_driver dr1v90_cru_driver = {
+	.driver = {
+		.name = "dr1v90-cru",
+		.of_match_table = dr1v90_cru_ids,
+	},
+	.probe = dr1v90_cru_probe,
+};
+module_platform_driver(dr1v90_cru_driver);
+
+MODULE_AUTHOR("Fushan Zeng <fushan.zeng@anlogic.com>");
+MODULE_AUTHOR("Junhui Liu <junhui.liu@pigmoral.tech>");
+MODULE_DESCRIPTION("Anlogic DR1V90 CRU driver");
+MODULE_IMPORT_NS("CLK_ANLOGIC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/anlogic/cru_dr1.c b/drivers/clk/anlogic/cru_dr1.c
new file mode 100644
index 000000000000..2a729957a702
--- /dev/null
+++ b/drivers/clk/anlogic/cru_dr1.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024-2025 Anlogic, Inc.
+ * Copyright (C) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cru_dr1.h"
+
+static unsigned long cru_pll_nm_recalc_rate(struct clk_hw *hw,
+					    unsigned long parent_rate)
+{
+	struct cru_pll *pll = hw_to_cru_pll(hw);
+	u32 mult, div;
+
+	div = FIELD_GET(GENMASK(6, 0), readl(pll->reg)) + 1;
+	mult = FIELD_GET(GENMASK(6, 0), readl(pll->reg + 4)) + 1;
+
+	return parent_rate * mult / div;
+}
+
+const struct clk_ops dr1_cru_pll_nm_ops = {
+	.recalc_rate = cru_pll_nm_recalc_rate,
+};
+EXPORT_SYMBOL_NS_GPL(dr1_cru_pll_nm_ops, "CLK_ANLOGIC");
+
+static unsigned long cru_pll_c_recalc_rate(struct clk_hw *hw,
+					   unsigned long parent_rate)
+{
+	struct cru_pll *pll = hw_to_cru_pll(hw);
+	u32 div;
+
+	div = FIELD_GET(GENMASK(30, 24), readl(pll->reg)) + 1;
+
+	return parent_rate / div;
+}
+
+const struct clk_ops dr1_cru_pll_c_ops = {
+	.recalc_rate = cru_pll_c_recalc_rate,
+};
+EXPORT_SYMBOL_NS_GPL(dr1_cru_pll_c_ops, "CLK_ANLOGIC");
+
+static void cru_div_gate_endisable(struct clk_hw *hw, int enable)
+{
+	struct cru_div_gate *div_gate = hw_to_cru_div_gate(hw);
+	struct clk_divider *divider = &div_gate->divider;
+	u32 reg;
+
+	reg = readl(divider->reg);
+	reg &= ~(clk_div_mask(divider->width) << divider->shift);
+
+	if (enable)
+		reg |= div_gate->val << divider->shift;
+
+	writel(reg, divider->reg);
+}
+
+static int cru_div_gate_enable(struct clk_hw *hw)
+{
+	cru_div_gate_endisable(hw, 1);
+
+	return 0;
+}
+
+static void cru_div_gate_disable(struct clk_hw *hw)
+{
+	cru_div_gate_endisable(hw, 0);
+}
+
+static int cru_div_gate_is_enabled(struct clk_hw *hw)
+{
+	struct cru_div_gate *div_gate = hw_to_cru_div_gate(hw);
+	struct clk_divider *divider = &div_gate->divider;
+	u32 val;
+
+	val = readl(divider->reg) >> divider->shift;
+	val &= clk_div_mask(divider->width);
+
+	return !!val;
+}
+
+static unsigned long cru_div_gate_recalc_rate(struct clk_hw *hw,
+					      unsigned long parent_rate)
+{
+	struct cru_div_gate *div_gate = hw_to_cru_div_gate(hw);
+	struct clk_divider *divider = &div_gate->divider;
+	unsigned int val;
+
+	val = readl(divider->reg) >> divider->shift;
+	val &= clk_div_mask(divider->width);
+
+	if (val < div_gate->min)
+		return 0;
+
+	return divider_recalc_rate(hw, parent_rate, val, divider->table,
+				   divider->flags, divider->width);
+}
+
+static int cru_div_gate_determine_rate(struct clk_hw *hw,
+				       struct clk_rate_request *req)
+{
+	struct cru_div_gate *div_gate = hw_to_cru_div_gate(hw);
+	struct clk_divider *divider = &div_gate->divider;
+	unsigned long maxdiv, mindiv;
+	int div = 0;
+
+	maxdiv = clk_div_mask(divider->width) + 1;
+	mindiv = div_gate->min + 1;
+
+	div = DIV_ROUND_UP_ULL(req->best_parent_rate, req->rate);
+	div = div > maxdiv ? maxdiv : div;
+	div = div < mindiv ? mindiv : div;
+
+	req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div);
+
+	return 0;
+}
+
+static int cru_div_gate_set_rate(struct clk_hw *hw, unsigned long rate,
+				 unsigned long parent_rate)
+{
+	struct cru_div_gate *div_gate = hw_to_cru_div_gate(hw);
+	struct clk_divider *divider = &div_gate->divider;
+	int value;
+	u32 reg;
+
+	if (!__clk_is_enabled(hw->clk))
+		return 0;
+
+	value = divider_get_val(rate, parent_rate, divider->table,
+				divider->width, divider->flags);
+	if (value < 0)
+		return value;
+
+	if (value < div_gate->min)
+		value = div_gate->min;
+
+	reg = readl(divider->reg);
+	reg &= ~(clk_div_mask(divider->width) << divider->shift);
+	reg |= (u32)value << divider->shift;
+	writel(reg, divider->reg);
+
+	div_gate->val = reg;
+
+	return 0;
+}
+
+static int cru_div_gate_init(struct clk_hw *hw)
+{
+	struct cru_div_gate *div_gate = hw_to_cru_div_gate(hw);
+	struct clk_divider *divider = &div_gate->divider;
+	u32 val;
+
+	val = readl(divider->reg) >> divider->shift;
+	val &= clk_div_mask(divider->width);
+	div_gate->val = val;
+
+	return 0;
+}
+
+const struct clk_ops dr1_cru_div_gate_ops = {
+	.enable = cru_div_gate_enable,
+	.disable = cru_div_gate_disable,
+	.is_enabled = cru_div_gate_is_enabled,
+	.recalc_rate = cru_div_gate_recalc_rate,
+	.determine_rate = cru_div_gate_determine_rate,
+	.set_rate = cru_div_gate_set_rate,
+	.init = cru_div_gate_init,
+};
+EXPORT_SYMBOL_NS_GPL(dr1_cru_div_gate_ops, "CLK_ANLOGIC");
+
+int dr1_cru_clk_register(struct device *dev, void __iomem *base,
+			 const struct cru_clk *clks, int nr_clks)
+{
+	struct clk_hw_onecell_data *priv;
+	int i, ret;
+
+	priv = devm_kzalloc(dev, struct_size(priv, hws, nr_clks), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	for (i = 0; i < nr_clks; i++) {
+		const struct cru_clk *clk = &clks[i];
+
+		if (clk->reg)
+			*(clk->reg) += (uintptr_t)base;
+
+		ret = devm_clk_hw_register(dev, clk->hw);
+		if (ret)
+			return ret;
+
+		priv->hws[i] = clk->hw;
+	}
+
+	priv->num = nr_clks;
+
+	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv);
+	if (ret)
+		dev_err(dev, "failed to add clock hardware provider\n");
+
+	return ret;
+}
+EXPORT_SYMBOL_NS_GPL(dr1_cru_clk_register, "CLK_ANLOGIC");
+
+int dr1_cru_reset_register(struct device *dev, void __iomem *base)
+{
+	struct auxiliary_device *adev;
+
+	adev = devm_auxiliary_device_create(dev, "reset", base);
+	if (!adev)
+		return -ENODEV;
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(dr1_cru_reset_register, "CLK_ANLOGIC");
+
+MODULE_DESCRIPTION("Anlogic DR1 CRU driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/anlogic/cru_dr1.h b/drivers/clk/anlogic/cru_dr1.h
new file mode 100644
index 000000000000..d1c5d46dea97
--- /dev/null
+++ b/drivers/clk/anlogic/cru_dr1.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024-2025 Anlogic, Inc.
+ * Copyright (C) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#ifndef _CRU_DR1_H_
+#define _CRU_DR1_H_
+
+#include "linux/clk-provider.h"
+
+struct cru_pll {
+	struct clk_hw hw;
+	void __iomem *reg;
+};
+
+struct cru_div_gate {
+	struct clk_divider divider;
+	u32 val;
+	u8 min; /* Minimum divider value to avoid timing issues */
+};
+
+struct cru_clk {
+	struct clk_hw *hw;
+	void **reg;
+};
+
+#define CRU_PARENT_NAME(_name)		{ .fw_name = #_name }
+#define CRU_PARENT_HW(_parent)		{ .hw = &_parent.hw }
+#define CRU_PARENT_DIV_HW(_parent)	{ .hw = &_parent.divider.hw }
+
+#define CRU_INITHW(_name, _parent, _ops)				\
+	.hw.init = &(struct clk_init_data) {				\
+		.name		= #_name,				\
+		.parent_data	= (const struct clk_parent_data[])	\
+					{ _parent },			\
+		.num_parents	= 1,					\
+		.ops		= &_ops,				\
+	}
+
+#define CRU_INITHW_PARENTS(_name, _parents, _ops)			\
+	.hw.init = CLK_HW_INIT_PARENTS_DATA(#_name, _parents, &_ops, 0)
+
+#define CRU_PLL_NM_DEFINE(_name, _parent, _reg)				\
+static struct cru_pll _name = {						\
+	.reg = (void __iomem *)(_reg),					\
+	CRU_INITHW(_name, _parent, dr1_cru_pll_nm_ops),			\
+}
+
+#define CRU_PLL_C_DEFINE(_name, _parent, _reg)				\
+static struct cru_pll _name = {						\
+	.reg = (void __iomem *)(_reg),					\
+	CRU_INITHW(_name, _parent, dr1_cru_pll_c_ops),			\
+}
+
+#define CRU_DIV_DEFINE(_name, _parent, _reg, _shift, _width, _table,	\
+		       _flags)						\
+static struct clk_divider _name = {					\
+	.shift = _shift,						\
+	.width = _width,						\
+	.flags = _flags,						\
+	.table = _table,						\
+	.reg = (void __iomem *)(_reg),					\
+	CRU_INITHW(_name, _parent, clk_divider_ops),			\
+}
+
+#define CRU_DIV_GATE_DEFINE(_name, _parent, _reg, _shift, _width,	\
+			    _table, _flags, _min)			\
+static struct cru_div_gate _name = {					\
+	.min = _min,							\
+	.divider = {							\
+		.shift = _shift,					\
+		.width = _width,					\
+		.flags = _flags,					\
+		.table = _table,					\
+		.reg = (void __iomem *)(_reg),				\
+		CRU_INITHW(_name, _parent, dr1_cru_div_gate_ops),	\
+	}								\
+}
+
+#define CRU_MUX_DEFINE(_name, _parents, _reg, _shift, _width)		\
+static struct clk_mux _name = {						\
+	.shift = _shift,						\
+	.mask = GENMASK(_width - 1, 0),					\
+	.reg = (void __iomem *)(_reg),					\
+	CRU_INITHW_PARENTS(_name, _parents, clk_mux_ops)		\
+}
+
+#define CRU_GATE_DEFINE(_name, _parent, _reg, _bit_idx, _flags)		\
+static struct clk_gate _name = {					\
+	.bit_idx = _bit_idx,						\
+	.flags = _flags,						\
+	.reg = (void __iomem *)(_reg),					\
+	CRU_INITHW(_name, _parent, clk_gate_ops)			\
+}
+
+static inline struct cru_pll *hw_to_cru_pll(struct clk_hw *hw)
+{
+	return container_of(hw, struct cru_pll, hw);
+}
+
+static inline struct cru_div_gate *hw_to_cru_div_gate(struct clk_hw *hw)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+
+	return container_of(divider, struct cru_div_gate, divider);
+}
+
+extern const struct clk_ops dr1_cru_pll_nm_ops;
+extern const struct clk_ops dr1_cru_pll_c_ops;
+extern const struct clk_ops dr1_cru_div_gate_ops;
+
+int dr1_cru_clk_register(struct device *dev, void __iomem *base,
+			 const struct cru_clk *clks, int nr_clks);
+int dr1_cru_reset_register(struct device *dev, void __iomem *base);
+
+#endif /* _CRU_DR1_H_ */

-- 
2.52.0


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

* [PATCH v4 4/6] reset: anlogic: add support for Anlogic DR1V90 resets
  2025-12-31  6:40 [PATCH v4 0/6] clk/reset: anlogic: add support for DR1V90 SoC Junhui Liu
                   ` (2 preceding siblings ...)
  2025-12-31  6:40 ` [PATCH v4 3/6] clk: anlogic: add cru support for Anlogic DR1V90 SoC Junhui Liu
@ 2025-12-31  6:40 ` Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 5/6] riscv: dts: anlogic: add clocks and CRU for DR1V90 Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 6/6] MAINTAINERS: Add entry for Anlogic DR1V90 SoC drivers Junhui Liu
  5 siblings, 0 replies; 9+ messages in thread
From: Junhui Liu @ 2025-12-31  6:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-clk, linux-kernel, linux-riscv, devicetree

Add reset controller support for the Anlogic DR1V90 SoC, which is an
auxiliary device associated with the Clock and Reset Unit (CRU). All
resets are active-low.

Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 drivers/reset/Kconfig        |  10 +++
 drivers/reset/Makefile       |   1 +
 drivers/reset/reset-dr1v90.c | 141 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 152 insertions(+)

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 6e5d6deffa7d..5dd07555557e 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -73,6 +73,16 @@ config RESET_BRCMSTB_RESCAL
 	  This enables the RESCAL reset controller for SATA, PCIe0, or PCIe1 on
 	  BCM7216 or the BCM2712.
 
+config RESET_DR1V90
+	tristate "Anlogic DR1V90 reset controller"
+	depends on ARCH_ANLOGIC || COMPILE_TEST
+	depends on ANLOGIC_DR1V90_CRU
+	select AUXILIARY_BUS
+	default ARCH_ANLOGIC
+	help
+	  This enables the reset controller driver for Anlogic DR1V90 SoCs
+	  provided by the CRU unit.
+
 config RESET_EIC7700
 	bool "Reset controller driver for ESWIN SoCs"
 	depends on ARCH_ESWIN || COMPILE_TEST
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 9c3e484dfd81..2e6804ff463e 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
 obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
 obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o
 obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
+obj-$(CONFIG_RESET_DR1V90) += reset-dr1v90.o
 obj-$(CONFIG_RESET_EIC7700) += reset-eic7700.o
 obj-$(CONFIG_RESET_EYEQ) += reset-eyeq.o
 obj-$(CONFIG_RESET_GPIO) += reset-gpio.o
diff --git a/drivers/reset/reset-dr1v90.c b/drivers/reset/reset-dr1v90.c
new file mode 100644
index 000000000000..b21d28a68677
--- /dev/null
+++ b/drivers/reset/reset-dr1v90.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
+#include "linux/device.h"
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/reset/anlogic,dr1v90-cru.h>
+
+struct dr1v90_reset_map {
+	u32 offset;
+	u32 bit;
+};
+
+struct dr1v90_reset_controller {
+	struct reset_controller_dev rcdev;
+	void __iomem *base;
+	spinlock_t lock; /* protect register read-modify-write */
+};
+
+static inline struct dr1v90_reset_controller *
+to_dr1v90_reset_controller(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct dr1v90_reset_controller, rcdev);
+}
+
+static const struct dr1v90_reset_map dr1v90_resets[] = {
+	[RESET_OCM]		= { 0x74, BIT(4)},
+	[RESET_QSPI]		= { 0x74, BIT(5)},
+	[RESET_SMC]		= { 0x74, BIT(6)},
+	[RESET_WDT]		= { 0x74, BIT(7)},
+	[RESET_DMAC_AXI]	= { 0x74, BIT(8)},
+	[RESET_DMAC_AHB]	= { 0x74, BIT(9)},
+	[RESET_NPU]		= { 0x74, BIT(12)},
+	[RESET_JPU]		= { 0x74, BIT(13)},
+	[RESET_DDRBUS]		= { 0x74, BIT(14)},
+	[RESET_NIC_HP0]		= { 0x78, BIT(0)},
+	[RESET_NIC_HP1]		= { 0x78, BIT(1)},
+	[RESET_NIC_GP0M]	= { 0x78, BIT(4)},
+	[RESET_NIC_GP1M]	= { 0x78, BIT(5)},
+	[RESET_GPIO]		= { 0x78, BIT(8)},
+	[RESET_IPC]		= { 0x78, BIT(12)},
+	[RESET_USB0]		= { 0x7C, BIT(0)},
+	[RESET_USB1]		= { 0x7C, BIT(1)},
+	[RESET_GBE0]		= { 0x7C, BIT(4)},
+	[RESET_GBE1]		= { 0x7C, BIT(5)},
+	[RESET_SDIO0]		= { 0x7C, BIT(8)},
+	[RESET_SDIO1]		= { 0x7C, BIT(9)},
+	[RESET_UART0]		= { 0x7C, BIT(12)},
+	[RESET_UART1]		= { 0x7C, BIT(13)},
+	[RESET_SPI0]		= { 0x7C, BIT(16)},
+	[RESET_SPI1]		= { 0x7C, BIT(17)},
+	[RESET_CAN0]		= { 0x7C, BIT(20)},
+	[RESET_CAN1]		= { 0x7C, BIT(21)},
+	[RESET_TTC0]		= { 0x7C, BIT(24)},
+	[RESET_TTC1]		= { 0x7C, BIT(25)},
+	[RESET_I2C0]		= { 0x7C, BIT(28)},
+	[RESET_I2C1]		= { 0x7C, BIT(29)}
+};
+
+static int dr1v90_reset_control_update(struct reset_controller_dev *rcdev,
+				       unsigned long id, bool assert)
+{
+	struct dr1v90_reset_controller *rstc = to_dr1v90_reset_controller(rcdev);
+	u32 offset = dr1v90_resets[id].offset;
+	u32 bit = dr1v90_resets[id].bit;
+	u32 reg;
+
+	guard(spinlock_irqsave)(&rstc->lock);
+
+	reg = readl(rstc->base + offset);
+	if (assert)
+		reg &= ~bit;
+	else
+		reg |= bit;
+	writel(reg, rstc->base + offset);
+
+	return 0;
+}
+
+static int dr1v90_reset_control_assert(struct reset_controller_dev *rcdev,
+				       unsigned long id)
+{
+	return dr1v90_reset_control_update(rcdev, id, true);
+}
+
+static int dr1v90_reset_control_deassert(struct reset_controller_dev *rcdev,
+					 unsigned long id)
+{
+	return dr1v90_reset_control_update(rcdev, id, false);
+}
+
+static const struct reset_control_ops dr1v90_reset_control_ops = {
+	.assert = dr1v90_reset_control_assert,
+	.deassert = dr1v90_reset_control_deassert,
+};
+
+static int dr1v90_reset_probe(struct auxiliary_device *adev,
+			      const struct auxiliary_device_id *id)
+{
+	struct dr1v90_reset_controller *rstc;
+	struct device *dev = &adev->dev;
+
+	rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL);
+	if (!rstc)
+		return -ENOMEM;
+
+	spin_lock_init(&rstc->lock);
+
+	rstc->base = dev->platform_data;
+	rstc->rcdev.dev = dev;
+	rstc->rcdev.nr_resets = ARRAY_SIZE(dr1v90_resets);
+	rstc->rcdev.of_node = dev->parent->of_node;
+	rstc->rcdev.ops = &dr1v90_reset_control_ops;
+	rstc->rcdev.owner = THIS_MODULE;
+
+	return devm_reset_controller_register(dev, &rstc->rcdev);
+}
+
+static const struct auxiliary_device_id dr1v90_reset_ids[] = {
+	{
+		.name = "anlogic_dr1_cru.reset"
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, dr1v90_reset_ids);
+
+static struct auxiliary_driver dr1v90_reset_driver = {
+	.probe = dr1v90_reset_probe,
+	.id_table = dr1v90_reset_ids,
+};
+module_auxiliary_driver(dr1v90_reset_driver);
+
+MODULE_AUTHOR("Junhui Liu <junhui.liu@pigmoral.tech>");
+MODULE_DESCRIPTION("Anlogic DR1V90 reset controller driver");
+MODULE_LICENSE("GPL");

-- 
2.52.0


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

* [PATCH v4 5/6] riscv: dts: anlogic: add clocks and CRU for DR1V90
  2025-12-31  6:40 [PATCH v4 0/6] clk/reset: anlogic: add support for DR1V90 SoC Junhui Liu
                   ` (3 preceding siblings ...)
  2025-12-31  6:40 ` [PATCH v4 4/6] reset: anlogic: add support for Anlogic DR1V90 resets Junhui Liu
@ 2025-12-31  6:40 ` Junhui Liu
  2025-12-31  6:40 ` [PATCH v4 6/6] MAINTAINERS: Add entry for Anlogic DR1V90 SoC drivers Junhui Liu
  5 siblings, 0 replies; 9+ messages in thread
From: Junhui Liu @ 2025-12-31  6:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-clk, linux-kernel, linux-riscv, devicetree

Add clocks and introduce the CRU (Clock and Reset) unit node
for Anlogic DR1V90 SoC, providing both clock and reset support.

The DR1V90 SoC uses three external clocks:
- A 33 MHz crystal oscillator as the main system clock.
- Two optional external clocks (via IO) for the CAN and WDT modules.

Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 arch/riscv/boot/dts/anlogic/dr1v90.dtsi | 41 +++++++++++++++++++++++++++++++--
 1 file changed, 39 insertions(+), 2 deletions(-)

diff --git a/arch/riscv/boot/dts/anlogic/dr1v90.dtsi b/arch/riscv/boot/dts/anlogic/dr1v90.dtsi
index a5d0765ade32..dc8358b35751 100644
--- a/arch/riscv/boot/dts/anlogic/dr1v90.dtsi
+++ b/arch/riscv/boot/dts/anlogic/dr1v90.dtsi
@@ -3,6 +3,9 @@
  * Copyright (C) 2025 Junhui Liu <junhui.liu@pigmoral.tech>
  */
 
+#include <dt-bindings/clock/anlogic,dr1v90-cru.h>
+#include <dt-bindings/reset/anlogic,dr1v90-cru.h>
+
 /dts-v1/;
 / {
 	#address-cells = <2>;
@@ -39,6 +42,27 @@ cpu0_intc: interrupt-controller {
 		};
 	};
 
+	clocks {
+		can_ext: clock-ext-can {
+			compatible = "fixed-clock";
+			clock-output-names = "can_ext";
+			#clock-cells = <0>;
+		};
+
+		osc_33m: clock-33m {
+			compatible = "fixed-clock";
+			clock-frequency = <33333333>;
+			clock-output-names = "osc_33m";
+			#clock-cells = <0>;
+		};
+
+		wdt_ext: clock-ext-wdt {
+			compatible = "fixed-clock";
+			clock-output-names = "wdt_ext";
+			#clock-cells = <0>;
+		};
+	};
+
 	soc {
 		compatible = "simple-bus";
 		interrupt-parent = <&plic>;
@@ -80,21 +104,34 @@ plic: interrupt-controller@6c000000 {
 		uart0: serial@f8400000 {
 			compatible = "anlogic,dr1v90-uart", "snps,dw-apb-uart";
 			reg = <0x0 0xf8400000 0x0 0x1000>;
-			clock-frequency = <50000000>;
+			clocks = <&cru CLK_IO_400M_DIV8>, <&cru CLK_CPU_1X>;
+			clock-names = "baudclk", "apb_pclk";
 			interrupts = <71>;
 			reg-io-width = <4>;
 			reg-shift = <2>;
+			resets = <&cru RESET_UART0>;
 			status = "disabled";
 		};
 
 		uart1: serial@f8401000 {
 			compatible = "anlogic,dr1v90-uart", "snps,dw-apb-uart";
 			reg = <0x0 0xf8401000 0x0 0x1000>;
-			clock-frequency = <50000000>;
+			clocks = <&cru CLK_IO_400M_DIV8>, <&cru CLK_CPU_1X>;
+			clock-names = "baudclk", "apb_pclk";
 			interrupts = <72>;
 			reg-io-width = <4>;
 			reg-shift = <2>;
+			resets = <&cru RESET_UART1>;
 			status = "disabled";
 		};
+
+		cru: clock-controller@f8801000 {
+			compatible = "anlogic,dr1v90-cru";
+			reg = <0x0 0xf8801000 0 0x400>;
+			clocks = <&osc_33m>, <&can_ext>, <&wdt_ext>;
+			clock-names = "osc_33m", "can_ext", "wdt_ext";
+			#clock-cells = <1>;
+			#reset-cells = <1>;
+		};
 	};
 };

-- 
2.52.0


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

* [PATCH v4 6/6] MAINTAINERS: Add entry for Anlogic DR1V90 SoC drivers
  2025-12-31  6:40 [PATCH v4 0/6] clk/reset: anlogic: add support for DR1V90 SoC Junhui Liu
                   ` (4 preceding siblings ...)
  2025-12-31  6:40 ` [PATCH v4 5/6] riscv: dts: anlogic: add clocks and CRU for DR1V90 Junhui Liu
@ 2025-12-31  6:40 ` Junhui Liu
  5 siblings, 0 replies; 9+ messages in thread
From: Junhui Liu @ 2025-12-31  6:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Junhui Liu, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: linux-clk, linux-kernel, linux-riscv, devicetree

Add myself as maintainer of Anlogic DR1V90 SoC drivers.

Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 5b11839cba9d..3aab1ee3e286 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1841,6 +1841,13 @@ M:	Jiaxun Yang <jiaxun.yang@flygoat.com>
 S:	Supported
 F:	drivers/rtc/rtc-goldfish.c
 
+ANLOGIC DR1V90 SOC DRIVERS
+M:	Junhui Liu <junhui.liu@pigmoral.tech>
+L:	linux-riscv@lists.infradead.org
+S:	Maintained
+N:	dr1v90
+K:	dr1v90
+
 AOA (Apple Onboard Audio) ALSA DRIVER
 M:	Johannes Berg <johannes@sipsolutions.net>
 L:	linuxppc-dev@lists.ozlabs.org

-- 
2.52.0


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

* Re: [PATCH v4 1/6] clk: correct clk_div_mask() return value for width == 32
  2025-12-31  6:40 ` [PATCH v4 1/6] clk: correct clk_div_mask() return value for width == 32 Junhui Liu
@ 2025-12-31 10:56   ` David Laight
  2026-01-04  9:11     ` Junhui Liu
  0 siblings, 1 reply; 9+ messages in thread
From: David Laight @ 2025-12-31 10:56 UTC (permalink / raw)
  To: Junhui Liu
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, linux-clk, linux-kernel, linux-riscv,
	devicetree, Troy Mitchell, Brian Masney

On Wed, 31 Dec 2025 14:40:05 +0800
Junhui Liu <junhui.liu@pigmoral.tech> wrote:

> The macro clk_div_mask() currently wraps to zero when width is 32 due to
> 1 << 32 being undefined behavior. This leads to incorrect mask generation
> and prevents correct retrieval of register field values for 32-bit-wide
> dividers.
> 
> Although it is unlikely to exhaust all U32_MAX div, some clock IPs may rely
> on a 32-bit val entry in their div_table to match a div, so providing a
> full 32-bit mask is necessary.
> 
> Fix this by casting 1 to long, ensuring proper behavior for valid widths up
> to 32.
> 
> Reviewed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
> Reviewed-by: Brian Masney <bmasney@redhat.com>
> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
> ---
>  include/linux/clk-provider.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 630705a47129..a651ccaf1b44 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -720,7 +720,7 @@ struct clk_divider {
>  	spinlock_t	*lock;
>  };
>  
> -#define clk_div_mask(width)	((1 << (width)) - 1)
> +#define clk_div_mask(width)	((1L << (width)) - 1)

That makes no difference on 32bit architectures.
I also suspect you need to ensure the value is 'unsigned int'.
If you can guarantee that width isn't zero (probably true), then:
#define clk_div_mask(width) ((2u << (width) - 1) - 1)
should have the desired value for widths 1..32.
It probably adds an extra instruction.
(OTOH so does passing width as 'u8'.)

	David


>  #define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
>  
>  #define CLK_DIVIDER_ONE_BASED		BIT(0)
> 


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

* Re: [PATCH v4 1/6] clk: correct clk_div_mask() return value for width == 32
  2025-12-31 10:56   ` David Laight
@ 2026-01-04  9:11     ` Junhui Liu
  0 siblings, 0 replies; 9+ messages in thread
From: Junhui Liu @ 2026-01-04  9:11 UTC (permalink / raw)
  To: David Laight, Junhui Liu
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Philipp Zabel, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Alexandre Ghiti, linux-clk, linux-kernel, linux-riscv,
	devicetree, Troy Mitchell, Brian Masney

Hi David,

On Wed Dec 31, 2025 at 6:56 PM CST, David Laight wrote:
> On Wed, 31 Dec 2025 14:40:05 +0800
> Junhui Liu <junhui.liu@pigmoral.tech> wrote:
>
>> The macro clk_div_mask() currently wraps to zero when width is 32 due to
>> 1 << 32 being undefined behavior. This leads to incorrect mask generation
>> and prevents correct retrieval of register field values for 32-bit-wide
>> dividers.
>> 
>> Although it is unlikely to exhaust all U32_MAX div, some clock IPs may rely
>> on a 32-bit val entry in their div_table to match a div, so providing a
>> full 32-bit mask is necessary.
>> 
>> Fix this by casting 1 to long, ensuring proper behavior for valid widths up
>> to 32.
>> 
>> Reviewed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
>> Reviewed-by: Brian Masney <bmasney@redhat.com>
>> Signed-off-by: Junhui Liu <junhui.liu@pigmoral.tech>
>> ---
>>  include/linux/clk-provider.h | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>> 
>> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
>> index 630705a47129..a651ccaf1b44 100644
>> --- a/include/linux/clk-provider.h
>> +++ b/include/linux/clk-provider.h
>> @@ -720,7 +720,7 @@ struct clk_divider {
>>  	spinlock_t	*lock;
>>  };
>>  
>> -#define clk_div_mask(width)	((1 << (width)) - 1)
>> +#define clk_div_mask(width)	((1L << (width)) - 1)
>
> That makes no difference on 32bit architectures.

You're right. Thanks for pointing it out.

> I also suspect you need to ensure the value is 'unsigned int'.
> If you can guarantee that width isn't zero (probably true), then:
> #define clk_div_mask(width) ((2u << (width) - 1) - 1)
> should have the desired value for widths 1..32.
> It probably adds an extra instruction.
> (OTOH so does passing width as 'u8'.)
>

Thanks for your suggestion. After further consideration, I prefer using
the standard GENMASK macro instead:

#define clk_div_mask(width) GENMASK((width) - 1, 0)

Using a unified kernel macro is better for maintenance and it also
benefits from the compile time checks in GENMASK for constant inputs.
This approach also supports a width range of 1..32, and even up to 64 on
64-bit architectures.

-- 
Best regards,
Junhui Liu


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

end of thread, other threads:[~2026-01-04  9:12 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-31  6:40 [PATCH v4 0/6] clk/reset: anlogic: add support for DR1V90 SoC Junhui Liu
2025-12-31  6:40 ` [PATCH v4 1/6] clk: correct clk_div_mask() return value for width == 32 Junhui Liu
2025-12-31 10:56   ` David Laight
2026-01-04  9:11     ` Junhui Liu
2025-12-31  6:40 ` [PATCH v4 2/6] dt-bindings: clock: add Anlogic DR1V90 CRU Junhui Liu
2025-12-31  6:40 ` [PATCH v4 3/6] clk: anlogic: add cru support for Anlogic DR1V90 SoC Junhui Liu
2025-12-31  6:40 ` [PATCH v4 4/6] reset: anlogic: add support for Anlogic DR1V90 resets Junhui Liu
2025-12-31  6:40 ` [PATCH v4 5/6] riscv: dts: anlogic: add clocks and CRU for DR1V90 Junhui Liu
2025-12-31  6:40 ` [PATCH v4 6/6] MAINTAINERS: Add entry for Anlogic DR1V90 SoC drivers Junhui Liu

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