public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [PATCH 00/10] rockchip: Add initial support for RK3506
@ 2026-01-09  0:49 Jonas Karlman
  2026-01-09  0:49 ` [PATCH 01/10] rockchip: mkimage: Add " Jonas Karlman
                   ` (10 more replies)
  0 siblings, 11 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini
  Cc: Aaron Griffith, u-boot, Jonas Karlman

This series add initial support for the Rockchip RK3506 SoC.

Clk and pinctrl drivers have been imported from vendor U-Boot with
some adjustments and fixes.

Upstream DT is currently not existing for RK3506, so this series does
not add support for any new boards, it only add initial arch support.

Please see my U-Boot rk3506 branch at [1] for a few more commits that
add DTs and defconfig for e.g. Luckfox Lyra variants and ArmSoM Forge1.

With this series, board DTs and defconfigs it should be possible to boot
into U-Boot proper (without OP-TEE) and still have support for MMC,
Ethernet, OTP, RNG, LEDs, buttons and USB gadget/host.

[1] https://source.denx.de/u-boot/contributors/kwiboo/u-boot/-/commits/rk3506

Finley Xiao (2):
  dt-bindings: clock: rockchip: Add RK3506 clock and reset unit
  clk: rockchip: Add support for RK3506

Jonas Karlman (6):
  ram: rockchip: Add basic support for RK3506
  pinctrl: rockchip: Use syscon_regmap_lookup_by_phandle()
  rockchip: otp: Add support for RK3506
  phy: rockchip-inno-usb2: Add support for RK3506
  net: dwc_eth_qos_rockchip: Add support for RK3506
  arch: arm: rockchip: Add initial support for RK3506

Xuhui Lin (1):
  rockchip: mkimage: Add support for RK3506

Ye Zhang (1):
  pinctrl: rockchip: Add support for RK3506

 arch/arm/include/asm/arch-rk3506/boot0.h      |    9 +
 arch/arm/include/asm/arch-rk3506/gpio.h       |    9 +
 arch/arm/include/asm/arch-rockchip/clock.h    |   10 +
 .../include/asm/arch-rockchip/cru_rk3506.h    |  220 +++
 arch/arm/mach-rockchip/Kconfig                |   43 +
 arch/arm/mach-rockchip/Makefile               |    1 +
 arch/arm/mach-rockchip/rk3506/Kconfig         |   15 +
 arch/arm/mach-rockchip/rk3506/Makefile        |    5 +
 arch/arm/mach-rockchip/rk3506/clk_rk3506.c    |   16 +
 arch/arm/mach-rockchip/rk3506/rk3506.c        |  130 ++
 arch/arm/mach-rockchip/rk3506/syscon_rk3506.c |   19 +
 drivers/clk/rockchip/Makefile                 |    1 +
 drivers/clk/rockchip/clk_rk3506.c             | 1212 +++++++++++++++++
 drivers/misc/rockchip-otp.c                   |    4 +
 drivers/net/dwc_eth_qos.c                     |    4 +
 drivers/net/dwc_eth_qos_rockchip.c            |   86 ++
 drivers/phy/rockchip/phy-rockchip-inno-usb2.c |   20 +
 drivers/pinctrl/rockchip/Makefile             |    1 +
 drivers/pinctrl/rockchip/pinctrl-rk3506.c     |  462 +++++++
 .../pinctrl/rockchip/pinctrl-rockchip-core.c  |   46 +-
 drivers/pinctrl/rockchip/pinctrl-rockchip.h   |    1 +
 drivers/ram/rockchip/Makefile                 |    1 +
 drivers/ram/rockchip/sdram_rk3506.c           |   33 +
 drivers/reset/Makefile                        |    2 +-
 drivers/reset/rst-rk3506.c                    |  222 +++
 drivers/usb/gadget/Kconfig                    |    1 +
 .../Bindings/clock/rockchip,rk3506-cru.yaml   |   55 +
 .../dt-bindings/clock/rockchip,rk3506-cru.h   |  285 ++++
 .../dt-bindings/reset/rockchip,rk3506-cru.h   |  211 +++
 include/configs/rk3506_common.h               |   38 +
 tools/rkcommon.c                              |    1 +
 31 files changed, 3136 insertions(+), 27 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-rk3506/boot0.h
 create mode 100644 arch/arm/include/asm/arch-rk3506/gpio.h
 create mode 100644 arch/arm/include/asm/arch-rockchip/cru_rk3506.h
 create mode 100644 arch/arm/mach-rockchip/rk3506/Kconfig
 create mode 100644 arch/arm/mach-rockchip/rk3506/Makefile
 create mode 100644 arch/arm/mach-rockchip/rk3506/clk_rk3506.c
 create mode 100644 arch/arm/mach-rockchip/rk3506/rk3506.c
 create mode 100644 arch/arm/mach-rockchip/rk3506/syscon_rk3506.c
 create mode 100644 drivers/clk/rockchip/clk_rk3506.c
 create mode 100644 drivers/pinctrl/rockchip/pinctrl-rk3506.c
 create mode 100644 drivers/ram/rockchip/sdram_rk3506.c
 create mode 100644 drivers/reset/rst-rk3506.c
 create mode 100644 dts/upstream/Bindings/clock/rockchip,rk3506-cru.yaml
 create mode 100644 dts/upstream/include/dt-bindings/clock/rockchip,rk3506-cru.h
 create mode 100644 dts/upstream/include/dt-bindings/reset/rockchip,rk3506-cru.h
 create mode 100644 include/configs/rk3506_common.h

-- 
2.52.0


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

* [PATCH 01/10] rockchip: mkimage: Add support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 02/10] ram: rockchip: Add basic " Jonas Karlman
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini
  Cc: Aaron Griffith, u-boot, Jonas Karlman, Xuhui Lin

From: Xuhui Lin <xuhui.lin@rock-chips.com>

Add support for generating Rockchip Boot Image for RK3506.

The RK3506 has 48 KiB SRAM and 4 KiB is reserved for BootROM.

Signed-off-by: Xuhui Lin <xuhui.lin@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 tools/rkcommon.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/rkcommon.c b/tools/rkcommon.c
index e7e78ef7e5b7..784e542ea27c 100644
--- a/tools/rkcommon.c
+++ b/tools/rkcommon.c
@@ -150,6 +150,7 @@ static struct spl_info spl_infos[] = {
 	{ "rk3399", "RK33", 0x30000 - 0x2000, false, RK_HEADER_V1 },
 	{ "rv1108", "RK11", 0x1800, false, RK_HEADER_V1 },
 	{ "rv1126", "110B", 0x10000 - 0x1000, false, RK_HEADER_V1 },
+	{ "rk3506", "RK35", 0xC000 - 0x1000, false, RK_HEADER_V2 },
 	{ "rk3528", "RK35", 0x10000 - 0x1000, false, RK_HEADER_V2 },
 	{ "rk3568", "RK35", 0x10000 - 0x1000, false, RK_HEADER_V2 },
 	{ "rk3576", "RK35", 0x80000 - 0x1000, false, RK_HEADER_V2 },
-- 
2.52.0


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

* [PATCH 02/10] ram: rockchip: Add basic support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
  2026-01-09  0:49 ` [PATCH 01/10] rockchip: mkimage: Add " Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 03/10] dt-bindings: clock: rockchip: Add RK3506 clock and reset unit Jonas Karlman
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini
  Cc: Aaron Griffith, u-boot, Jonas Karlman

Add support for reading DRAM size information from PMUGRF os_reg2 reg.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/ram/rockchip/Makefile       |  1 +
 drivers/ram/rockchip/sdram_rk3506.c | 33 +++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)
 create mode 100644 drivers/ram/rockchip/sdram_rk3506.c

diff --git a/drivers/ram/rockchip/Makefile b/drivers/ram/rockchip/Makefile
index fd94aad0cd49..27921ae4921a 100644
--- a/drivers/ram/rockchip/Makefile
+++ b/drivers/ram/rockchip/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_ROCKCHIP_RK3288) = sdram_rk3288.o
 obj-$(CONFIG_ROCKCHIP_RK3308) = sdram_rk3308.o
 obj-$(CONFIG_ROCKCHIP_RK3328) = sdram_rk3328.o sdram_pctl_px30.o sdram_phy_px30.o
 obj-$(CONFIG_ROCKCHIP_RK3399) += sdram_rk3399.o
+obj-$(CONFIG_ROCKCHIP_RK3506) += sdram_rk3506.o
 obj-$(CONFIG_ROCKCHIP_RK3528) += sdram_rk3528.o
 obj-$(CONFIG_ROCKCHIP_RK3568) += sdram_rk3568.o
 obj-$(CONFIG_ROCKCHIP_RK3576) += sdram_rk3576.o
diff --git a/drivers/ram/rockchip/sdram_rk3506.c b/drivers/ram/rockchip/sdram_rk3506.c
new file mode 100644
index 000000000000..a8396ea88883
--- /dev/null
+++ b/drivers/ram/rockchip/sdram_rk3506.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright Contributors to the U-Boot project.
+
+#include <dm.h>
+#include <ram.h>
+#include <asm/arch-rockchip/sdram.h>
+
+#define PMUGRF_BASE			0xff910000
+#define OS_REG2_REG			0x208
+
+static int rk3506_dmc_get_info(struct udevice *dev, struct ram_info *info)
+{
+	info->base = CFG_SYS_SDRAM_BASE;
+	info->size = rockchip_sdram_size(PMUGRF_BASE + OS_REG2_REG);
+
+	return 0;
+}
+
+static struct ram_ops rk3506_dmc_ops = {
+	.get_info = rk3506_dmc_get_info,
+};
+
+static const struct udevice_id rk3506_dmc_ids[] = {
+	{ .compatible = "rockchip,rk3506-dmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_rk3506_dmc) = {
+	.name = "rockchip_rk3506_dmc",
+	.id = UCLASS_RAM,
+	.of_match = rk3506_dmc_ids,
+	.ops = &rk3506_dmc_ops,
+};
-- 
2.52.0


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

* [PATCH 03/10] dt-bindings: clock: rockchip: Add RK3506 clock and reset unit
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
  2026-01-09  0:49 ` [PATCH 01/10] rockchip: mkimage: Add " Jonas Karlman
  2026-01-09  0:49 ` [PATCH 02/10] ram: rockchip: Add basic " Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 04/10] clk: rockchip: Add support for RK3506 Jonas Karlman
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini, Sumit Garg,
	Finley Xiao, Heiko Stuebner
  Cc: Aaron Griffith, u-boot, Jonas Karlman, Elaine Zhang, Conor Dooley

From: Finley Xiao <finley.xiao@rock-chips.com>

Add device tree bindings for clock and reset unit on RK3506 SoC.
Add clock and reset IDs for RK3506 SoC.

Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Link: https://patch.msgid.link/20251121075350.2564860-2-zhangqing@rock-chips.com
Signed-off-by: Heiko Stuebner <heiko@sntech.de>

[ upstream commit: 84898f8e9cea06f8178fc5ca53f068180f7bfba0 ]

(cherry picked from commit 516951213a82094f7f49f149cbf3c66dfb14c65d)
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 .../Bindings/clock/rockchip,rk3506-cru.yaml   |  55 ++++
 .../dt-bindings/clock/rockchip,rk3506-cru.h   | 285 ++++++++++++++++++
 .../dt-bindings/reset/rockchip,rk3506-cru.h   | 211 +++++++++++++
 3 files changed, 551 insertions(+)
 create mode 100644 dts/upstream/Bindings/clock/rockchip,rk3506-cru.yaml
 create mode 100644 dts/upstream/include/dt-bindings/clock/rockchip,rk3506-cru.h
 create mode 100644 dts/upstream/include/dt-bindings/reset/rockchip,rk3506-cru.h

diff --git a/dts/upstream/Bindings/clock/rockchip,rk3506-cru.yaml b/dts/upstream/Bindings/clock/rockchip,rk3506-cru.yaml
new file mode 100644
index 000000000000..ca940475336c
--- /dev/null
+++ b/dts/upstream/Bindings/clock/rockchip,rk3506-cru.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/rockchip,rk3506-cru.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip RK3506 Clock and Reset Unit (CRU)
+
+maintainers:
+  - Finley Xiao <finley.xiao@rock-chips.com>
+  - Heiko Stuebner <heiko@sntech.de>
+
+description:
+  The RK3506 CRU generates the clock and also implements reset for SoC
+  peripherals.
+
+properties:
+  compatible:
+    const: rockchip,rk3506-cru
+
+  reg:
+    maxItems: 1
+
+  "#clock-cells":
+    const: 1
+
+  "#reset-cells":
+    const: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    const: xin
+
+required:
+  - compatible
+  - reg
+  - "#clock-cells"
+  - "#reset-cells"
+  - clocks
+  - clock-names
+
+additionalProperties: false
+
+examples:
+  - |
+    clock-controller@ff9a0000 {
+      compatible = "rockchip,rk3506-cru";
+      reg = <0xff9a0000 0x20000>;
+      #clock-cells = <1>;
+      #reset-cells = <1>;
+      clocks = <&xin24m>;
+      clock-names = "xin";
+    };
diff --git a/dts/upstream/include/dt-bindings/clock/rockchip,rk3506-cru.h b/dts/upstream/include/dt-bindings/clock/rockchip,rk3506-cru.h
new file mode 100644
index 000000000000..71d7dda23cc9
--- /dev/null
+++ b/dts/upstream/include/dt-bindings/clock/rockchip,rk3506-cru.h
@@ -0,0 +1,285 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2023-2025 Rockchip Electronics Co., Ltd.
+ * Author: Finley Xiao <finley.xiao@rock-chips.com>
+ */
+
+#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RK3506_H
+#define _DT_BINDINGS_CLK_ROCKCHIP_RK3506_H
+
+/* cru plls */
+#define PLL_GPLL			0
+#define PLL_V0PLL			1
+#define PLL_V1PLL			2
+
+/* cru-clocks indices */
+#define ARMCLK				3
+#define CLK_DDR				4
+#define XIN24M_GATE			5
+#define CLK_GPLL_GATE			6
+#define CLK_V0PLL_GATE			7
+#define CLK_V1PLL_GATE			8
+#define CLK_GPLL_DIV			9
+#define CLK_GPLL_DIV_100M		10
+#define CLK_V0PLL_DIV			11
+#define CLK_V1PLL_DIV			12
+#define CLK_INT_VOICE_MATRIX0		13
+#define CLK_INT_VOICE_MATRIX1		14
+#define CLK_INT_VOICE_MATRIX2		15
+#define CLK_FRAC_UART_MATRIX0_MUX	16
+#define CLK_FRAC_UART_MATRIX1_MUX	17
+#define CLK_FRAC_VOICE_MATRIX0_MUX	18
+#define CLK_FRAC_VOICE_MATRIX1_MUX	19
+#define CLK_FRAC_COMMON_MATRIX0_MUX	20
+#define CLK_FRAC_COMMON_MATRIX1_MUX	21
+#define CLK_FRAC_COMMON_MATRIX2_MUX	22
+#define CLK_FRAC_UART_MATRIX0		23
+#define CLK_FRAC_UART_MATRIX1		24
+#define CLK_FRAC_VOICE_MATRIX0		25
+#define CLK_FRAC_VOICE_MATRIX1		26
+#define CLK_FRAC_COMMON_MATRIX0		27
+#define CLK_FRAC_COMMON_MATRIX1		28
+#define CLK_FRAC_COMMON_MATRIX2		29
+#define CLK_REF_USBPHY_TOP		30
+#define CLK_REF_DPHY_TOP		31
+#define ACLK_CORE_ROOT			32
+#define PCLK_CORE_ROOT			33
+#define PCLK_DBG			34
+#define PCLK_CORE_GRF			35
+#define PCLK_CORE_CRU			36
+#define CLK_CORE_EMA_DETECT		37
+#define CLK_REF_PVTPLL_CORE		38
+#define PCLK_GPIO1			39
+#define DBCLK_GPIO1			40
+#define ACLK_CORE_PERI_ROOT		41
+#define HCLK_CORE_PERI_ROOT		42
+#define PCLK_CORE_PERI_ROOT		43
+#define CLK_DSMC			44
+#define ACLK_DSMC			45
+#define PCLK_DSMC			46
+#define CLK_FLEXBUS_TX			47
+#define CLK_FLEXBUS_RX			48
+#define ACLK_FLEXBUS			49
+#define HCLK_FLEXBUS			50
+#define ACLK_DSMC_SLV			51
+#define HCLK_DSMC_SLV			52
+#define ACLK_BUS_ROOT			53
+#define HCLK_BUS_ROOT			54
+#define PCLK_BUS_ROOT			55
+#define ACLK_SYSRAM			56
+#define HCLK_SYSRAM			57
+#define ACLK_DMAC0			58
+#define ACLK_DMAC1			59
+#define HCLK_M0				60
+#define PCLK_BUS_GRF			61
+#define PCLK_TIMER			62
+#define CLK_TIMER0_CH0			63
+#define CLK_TIMER0_CH1			64
+#define CLK_TIMER0_CH2			65
+#define CLK_TIMER0_CH3			66
+#define CLK_TIMER0_CH4			67
+#define CLK_TIMER0_CH5			68
+#define PCLK_WDT0			69
+#define TCLK_WDT0			70
+#define PCLK_WDT1			71
+#define TCLK_WDT1			72
+#define PCLK_MAILBOX			73
+#define PCLK_INTMUX			74
+#define PCLK_SPINLOCK			75
+#define PCLK_DDRC			76
+#define HCLK_DDRPHY			77
+#define PCLK_DDRMON			78
+#define CLK_DDRMON_OSC			79
+#define PCLK_STDBY			80
+#define HCLK_USBOTG0			81
+#define HCLK_USBOTG0_PMU		82
+#define CLK_USBOTG0_ADP			83
+#define HCLK_USBOTG1			84
+#define HCLK_USBOTG1_PMU		85
+#define CLK_USBOTG1_ADP			86
+#define PCLK_USBPHY			87
+#define ACLK_DMA2DDR			88
+#define PCLK_DMA2DDR			89
+#define STCLK_M0			90
+#define CLK_DDRPHY			91
+#define CLK_DDRC_SRC			92
+#define ACLK_DDRC_0			93
+#define ACLK_DDRC_1			94
+#define CLK_DDRC			95
+#define CLK_DDRMON			96
+#define HCLK_LSPERI_ROOT		97
+#define PCLK_LSPERI_ROOT		98
+#define PCLK_UART0			99
+#define PCLK_UART1			100
+#define PCLK_UART2			101
+#define PCLK_UART3			102
+#define PCLK_UART4			103
+#define SCLK_UART0			104
+#define SCLK_UART1			105
+#define SCLK_UART2			106
+#define SCLK_UART3			107
+#define SCLK_UART4			108
+#define PCLK_I2C0			109
+#define CLK_I2C0			110
+#define PCLK_I2C1			111
+#define CLK_I2C1			112
+#define PCLK_I2C2			113
+#define CLK_I2C2			114
+#define PCLK_PWM1			115
+#define CLK_PWM1			116
+#define CLK_OSC_PWM1			117
+#define CLK_RC_PWM1			118
+#define CLK_FREQ_PWM1			119
+#define CLK_COUNTER_PWM1		120
+#define PCLK_SPI0			121
+#define CLK_SPI0			122
+#define PCLK_SPI1			123
+#define CLK_SPI1			124
+#define PCLK_GPIO2			125
+#define DBCLK_GPIO2			126
+#define PCLK_GPIO3			127
+#define DBCLK_GPIO3			128
+#define PCLK_GPIO4			129
+#define DBCLK_GPIO4			130
+#define HCLK_CAN0			131
+#define CLK_CAN0			132
+#define HCLK_CAN1			133
+#define CLK_CAN1			134
+#define HCLK_PDM			135
+#define MCLK_PDM			136
+#define CLKOUT_PDM			137
+#define MCLK_SPDIFTX			138
+#define HCLK_SPDIFTX			139
+#define HCLK_SPDIFRX			140
+#define MCLK_SPDIFRX			141
+#define MCLK_SAI0			142
+#define HCLK_SAI0			143
+#define MCLK_OUT_SAI0			144
+#define MCLK_SAI1			145
+#define HCLK_SAI1			146
+#define MCLK_OUT_SAI1			147
+#define HCLK_ASRC0			148
+#define CLK_ASRC0			149
+#define HCLK_ASRC1			150
+#define CLK_ASRC1			151
+#define PCLK_CRU			152
+#define PCLK_PMU_ROOT			153
+#define MCLK_ASRC0			154
+#define MCLK_ASRC1			155
+#define MCLK_ASRC2			156
+#define MCLK_ASRC3			157
+#define LRCK_ASRC0_SRC			158
+#define LRCK_ASRC0_DST			159
+#define LRCK_ASRC1_SRC			160
+#define LRCK_ASRC1_DST			161
+#define ACLK_HSPERI_ROOT		162
+#define HCLK_HSPERI_ROOT		163
+#define PCLK_HSPERI_ROOT		164
+#define CCLK_SRC_SDMMC			165
+#define HCLK_SDMMC			166
+#define HCLK_FSPI			167
+#define SCLK_FSPI			168
+#define PCLK_SPI2			169
+#define ACLK_MAC0			170
+#define ACLK_MAC1			171
+#define PCLK_MAC0			172
+#define PCLK_MAC1			173
+#define CLK_MAC_ROOT			174
+#define CLK_MAC0			175
+#define CLK_MAC1			176
+#define MCLK_SAI2			177
+#define HCLK_SAI2			178
+#define MCLK_OUT_SAI2			179
+#define MCLK_SAI3_SRC			180
+#define HCLK_SAI3			181
+#define MCLK_SAI3			182
+#define MCLK_OUT_SAI3			183
+#define MCLK_SAI4_SRC			184
+#define HCLK_SAI4			185
+#define MCLK_SAI4			186
+#define HCLK_DSM			187
+#define MCLK_DSM			188
+#define PCLK_AUDIO_ADC			189
+#define MCLK_AUDIO_ADC			190
+#define MCLK_AUDIO_ADC_DIV4		191
+#define PCLK_SARADC			192
+#define CLK_SARADC			193
+#define PCLK_OTPC_NS			194
+#define CLK_SBPI_OTPC_NS		195
+#define CLK_USER_OTPC_NS		196
+#define PCLK_UART5			197
+#define SCLK_UART5			198
+#define PCLK_GPIO234_IOC		199
+#define CLK_MAC_PTP_ROOT		200
+#define CLK_MAC0_PTP			201
+#define CLK_MAC1_PTP			202
+#define CLK_SPI2			203
+#define ACLK_VIO_ROOT			204
+#define HCLK_VIO_ROOT			205
+#define PCLK_VIO_ROOT			206
+#define HCLK_RGA			207
+#define ACLK_RGA			208
+#define CLK_CORE_RGA			209
+#define ACLK_VOP			210
+#define HCLK_VOP			211
+#define DCLK_VOP			212
+#define PCLK_DPHY			213
+#define PCLK_DSI_HOST			214
+#define PCLK_TSADC			215
+#define CLK_TSADC			216
+#define CLK_TSADC_TSEN			217
+#define PCLK_GPIO1_IOC			218
+#define PCLK_OTPC_S			219
+#define CLK_SBPI_OTPC_S			220
+#define CLK_USER_OTPC_S			221
+#define PCLK_OTP_MASK			222
+#define PCLK_KEYREADER			223
+#define HCLK_BOOTROM			224
+#define PCLK_DDR_SERVICE		225
+#define HCLK_CRYPTO_S			226
+#define HCLK_KEYLAD			227
+#define CLK_CORE_CRYPTO			228
+#define CLK_PKA_CRYPTO			229
+#define CLK_CORE_CRYPTO_S		230
+#define CLK_PKA_CRYPTO_S		231
+#define ACLK_CRYPTO_S			232
+#define HCLK_RNG_S			233
+#define CLK_CORE_CRYPTO_NS		234
+#define CLK_PKA_CRYPTO_NS		235
+#define ACLK_CRYPTO_NS			236
+#define HCLK_CRYPTO_NS			237
+#define HCLK_RNG			238
+#define CLK_PMU				239
+#define PCLK_PMU			240
+#define CLK_PMU_32K			241
+#define PCLK_PMU_CRU			242
+#define PCLK_PMU_GRF			243
+#define PCLK_GPIO0_IOC			244
+#define PCLK_GPIO0			245
+#define DBCLK_GPIO0			246
+#define PCLK_GPIO1_SHADOW		247
+#define DBCLK_GPIO1_SHADOW		248
+#define PCLK_PMU_HP_TIMER		249
+#define CLK_PMU_HP_TIMER		250
+#define CLK_PMU_HP_TIMER_32K		251
+#define PCLK_PWM0			252
+#define CLK_PWM0			253
+#define CLK_OSC_PWM0			254
+#define CLK_RC_PWM0			255
+#define CLK_MAC_OUT			256
+#define CLK_REF_OUT0			257
+#define CLK_REF_OUT1			258
+#define CLK_32K_FRAC			259
+#define CLK_32K_RC			260
+#define CLK_32K				261
+#define CLK_32K_PMU			262
+#define PCLK_TOUCH_KEY			263
+#define CLK_TOUCH_KEY			264
+#define CLK_REF_PHY_PLL			265
+#define CLK_REF_PHY_PMU_MUX		266
+#define CLK_WIFI_OUT			267
+#define CLK_V0PLL_REF			268
+#define CLK_V1PLL_REF			269
+#define CLK_32K_FRAC_MUX		270
+
+#endif
diff --git a/dts/upstream/include/dt-bindings/reset/rockchip,rk3506-cru.h b/dts/upstream/include/dt-bindings/reset/rockchip,rk3506-cru.h
new file mode 100644
index 000000000000..31c0d4aa410f
--- /dev/null
+++ b/dts/upstream/include/dt-bindings/reset/rockchip,rk3506-cru.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2023-2025 Rockchip Electronics Co., Ltd.
+ * Author: Finley Xiao <finley.xiao@rock-chips.com>
+ */
+
+#ifndef _DT_BINDINGS_REST_ROCKCHIP_RK3506_H
+#define _DT_BINDINGS_REST_ROCKCHIP_RK3506_H
+
+/* CRU-->SOFTRST_CON00 */
+#define SRST_NCOREPORESET0_AC	0
+#define SRST_NCOREPORESET1_AC	1
+#define SRST_NCOREPORESET2_AC	2
+#define SRST_NCORESET0_AC	3
+#define SRST_NCORESET1_AC	4
+#define SRST_NCORESET2_AC	5
+#define SRST_NL2RESET_AC	6
+#define SRST_A_CORE_BIU_AC	7
+#define SRST_H_M0_AC		8
+
+/* CRU-->SOFTRST_CON02 */
+#define SRST_NDBGRESET		9
+#define SRST_P_CORE_BIU		10
+#define SRST_PMU		11
+
+/* CRU-->SOFTRST_CON03 */
+#define SRST_P_DBG		12
+#define SRST_POT_DBG		13
+#define SRST_P_CORE_GRF		14
+#define SRST_CORE_EMA_DETECT	15
+#define SRST_REF_PVTPLL_CORE	16
+#define SRST_P_GPIO1		17
+#define SRST_DB_GPIO1		18
+
+/* CRU-->SOFTRST_CON04 */
+#define SRST_A_CORE_PERI_BIU	19
+#define SRST_A_DSMC		20
+#define SRST_P_DSMC		21
+#define SRST_FLEXBUS		22
+#define SRST_A_FLEXBUS		23
+#define SRST_H_FLEXBUS		24
+#define SRST_A_DSMC_SLV		25
+#define SRST_H_DSMC_SLV		26
+#define SRST_DSMC_SLV		27
+
+/* CRU-->SOFTRST_CON05 */
+#define SRST_A_BUS_BIU		28
+#define SRST_H_BUS_BIU		29
+#define SRST_P_BUS_BIU		30
+#define SRST_A_SYSRAM		31
+#define SRST_H_SYSRAM		32
+#define SRST_A_DMAC0		33
+#define SRST_A_DMAC1		34
+#define SRST_H_M0		35
+#define SRST_M0_JTAG		36
+#define SRST_H_CRYPTO		37
+
+/* CRU-->SOFTRST_CON06 */
+#define SRST_H_RNG		38
+#define SRST_P_BUS_GRF		39
+#define SRST_P_TIMER0		40
+#define SRST_TIMER0_CH0		41
+#define SRST_TIMER0_CH1		42
+#define SRST_TIMER0_CH2		43
+#define SRST_TIMER0_CH3		44
+#define SRST_TIMER0_CH4		45
+#define SRST_TIMER0_CH5		46
+#define SRST_P_WDT0		47
+#define SRST_T_WDT0		48
+#define SRST_P_WDT1		49
+#define SRST_T_WDT1		50
+#define SRST_P_MAILBOX		51
+#define SRST_P_INTMUX		52
+#define SRST_P_SPINLOCK		53
+
+/* CRU-->SOFTRST_CON07 */
+#define SRST_P_DDRC		54
+#define SRST_H_DDRPHY		55
+#define SRST_P_DDRMON		56
+#define SRST_DDRMON_OSC		57
+#define SRST_P_DDR_LPC		58
+#define SRST_H_USBOTG0		59
+#define SRST_USBOTG0_ADP	60
+#define SRST_H_USBOTG1		61
+#define SRST_USBOTG1_ADP	62
+#define SRST_P_USBPHY		63
+#define SRST_USBPHY_POR		64
+#define SRST_USBPHY_OTG0	65
+#define SRST_USBPHY_OTG1	66
+
+/* CRU-->SOFTRST_CON08 */
+#define SRST_A_DMA2DDR		67
+#define SRST_P_DMA2DDR		68
+
+/* CRU-->SOFTRST_CON09 */
+#define SRST_USBOTG0_UTMI	69
+#define SRST_USBOTG1_UTMI	70
+
+/* CRU-->SOFTRST_CON10 */
+#define SRST_A_DDRC_0		71
+#define SRST_A_DDRC_1		72
+#define SRST_A_DDR_BIU		73
+#define SRST_DDRC		74
+#define SRST_DDRMON		75
+
+/* CRU-->SOFTRST_CON11 */
+#define SRST_H_LSPERI_BIU	76
+#define SRST_P_UART0		77
+#define SRST_P_UART1		78
+#define SRST_P_UART2		79
+#define SRST_P_UART3		80
+#define SRST_P_UART4		81
+#define SRST_UART0		82
+#define SRST_UART1		83
+#define SRST_UART2		84
+#define SRST_UART3		85
+#define SRST_UART4		86
+#define SRST_P_I2C0		87
+#define SRST_I2C0		88
+
+/* CRU-->SOFTRST_CON12 */
+#define SRST_P_I2C1		89
+#define SRST_I2C1		90
+#define SRST_P_I2C2		91
+#define SRST_I2C2		92
+#define SRST_P_PWM1		93
+#define SRST_PWM1		94
+#define SRST_P_SPI0		95
+#define SRST_SPI0		96
+#define SRST_P_SPI1		97
+#define SRST_SPI1		98
+#define SRST_P_GPIO2		99
+#define SRST_DB_GPIO2		100
+
+/* CRU-->SOFTRST_CON13 */
+#define SRST_P_GPIO3		101
+#define SRST_DB_GPIO3		102
+#define SRST_P_GPIO4		103
+#define SRST_DB_GPIO4		104
+#define SRST_H_CAN0		105
+#define SRST_CAN0		106
+#define SRST_H_CAN1		107
+#define SRST_CAN1		108
+#define SRST_H_PDM		109
+#define SRST_M_PDM		110
+#define SRST_PDM		111
+#define SRST_SPDIFTX		112
+#define SRST_H_SPDIFTX		113
+#define SRST_H_SPDIFRX		114
+#define SRST_SPDIFRX		115
+#define SRST_M_SAI0		116
+
+/* CRU-->SOFTRST_CON14 */
+#define SRST_H_SAI0		117
+#define SRST_M_SAI1		118
+#define SRST_H_SAI1		119
+#define SRST_H_ASRC0		120
+#define SRST_ASRC0		121
+#define SRST_H_ASRC1		122
+#define SRST_ASRC1		123
+
+/* CRU-->SOFTRST_CON17 */
+#define SRST_H_HSPERI_BIU	124
+#define SRST_H_SDMMC		125
+#define SRST_H_FSPI		126
+#define SRST_S_FSPI		127
+#define SRST_P_SPI2		128
+#define SRST_A_MAC0		129
+#define SRST_A_MAC1		130
+
+/* CRU-->SOFTRST_CON18 */
+#define SRST_M_SAI2		131
+#define SRST_H_SAI2		132
+#define SRST_H_SAI3		133
+#define SRST_M_SAI3		134
+#define SRST_H_SAI4		135
+#define SRST_M_SAI4		136
+#define SRST_H_DSM		137
+#define SRST_M_DSM		138
+#define SRST_P_AUDIO_ADC	139
+#define SRST_M_AUDIO_ADC	140
+
+/* CRU-->SOFTRST_CON19 */
+#define SRST_P_SARADC		141
+#define SRST_SARADC		142
+#define SRST_SARADC_PHY		143
+#define SRST_P_OTPC_NS		144
+#define SRST_SBPI_OTPC_NS	145
+#define SRST_USER_OTPC_NS	146
+#define SRST_P_UART5		147
+#define SRST_UART5		148
+#define SRST_P_GPIO234_IOC	149
+
+/* CRU-->SOFTRST_CON21 */
+#define SRST_A_VIO_BIU		150
+#define SRST_H_VIO_BIU		151
+#define SRST_H_RGA		152
+#define SRST_A_RGA		153
+#define SRST_CORE_RGA		154
+#define SRST_A_VOP		155
+#define SRST_H_VOP		156
+#define SRST_VOP		157
+#define SRST_P_DPHY		158
+#define SRST_P_DSI_HOST		159
+#define SRST_P_TSADC		160
+#define SRST_TSADC		161
+
+/* CRU-->SOFTRST_CON22 */
+#define SRST_P_GPIO1_IOC	162
+
+#endif
-- 
2.52.0


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

* [PATCH 04/10] clk: rockchip: Add support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
                   ` (2 preceding siblings ...)
  2026-01-09  0:49 ` [PATCH 03/10] dt-bindings: clock: rockchip: Add RK3506 clock and reset unit Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 05/10] pinctrl: rockchip: Use syscon_regmap_lookup_by_phandle() Jonas Karlman
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini,
	Lukasz Majewski
  Cc: Aaron Griffith, u-boot, Jonas Karlman, Finley Xiao

From: Finley Xiao <finley.xiao@rock-chips.com>

Add clock driver for RK3506.

Imported from vendor U-Boot linux-6.1-stan-rkr6 tag with minor
adjustments and fixes for mainline.

Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 arch/arm/include/asm/arch-rockchip/clock.h    |   10 +
 .../include/asm/arch-rockchip/cru_rk3506.h    |  220 +++
 drivers/clk/rockchip/Makefile                 |    1 +
 drivers/clk/rockchip/clk_rk3506.c             | 1212 +++++++++++++++++
 drivers/reset/Makefile                        |    2 +-
 drivers/reset/rst-rk3506.c                    |  222 +++
 6 files changed, 1666 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/asm/arch-rockchip/cru_rk3506.h
 create mode 100644 drivers/clk/rockchip/clk_rk3506.c
 create mode 100644 drivers/reset/rst-rk3506.c

diff --git a/arch/arm/include/asm/arch-rockchip/clock.h b/arch/arm/include/asm/arch-rockchip/clock.h
index 3c204501f709..95b08bfd046f 100644
--- a/arch/arm/include/asm/arch-rockchip/clock.h
+++ b/arch/arm/include/asm/arch-rockchip/clock.h
@@ -214,6 +214,16 @@ int rockchip_reset_bind(struct udevice *pdev, u32 reg_offset, u32 reg_number);
  */
 int rockchip_reset_bind_lut(struct udevice *pdev, const int *lookup_table,
 			    u32 reg_offset, u32 reg_number);
+/*
+ * rk3506_reset_bind_lut() - Bind soft reset device as child of clock device
+ *			     using dedicated RK3506 lookup table
+ *
+ * @pdev: clock udevice
+ * @reg_offset: the first offset in cru for softreset registers
+ * @reg_number: the reg numbers of softreset registers
+ * Return: 0 success, or error value
+ */
+int rk3506_reset_bind_lut(struct udevice *pdev, u32 reg_offset, u32 reg_number);
 /*
  * rk3528_reset_bind_lut() - Bind soft reset device as child of clock device
  *			     using dedicated RK3528 lookup table
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3506.h b/arch/arm/include/asm/arch-rockchip/cru_rk3506.h
new file mode 100644
index 000000000000..38c061104ecd
--- /dev/null
+++ b/arch/arm/include/asm/arch-rockchip/cru_rk3506.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ * Author: Finley Xiao <finley.xiao@rock-chips.com>
+ */
+
+#ifndef _ASM_ARCH_CRU_RK3506_H
+#define _ASM_ARCH_CRU_RK3506_H
+
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+
+#define MHz		1000000
+#define KHz		1000
+#define OSC_HZ		(24 * MHz)
+
+/* RK3506 pll id */
+enum rk3506_pll_id {
+	GPLL,
+	V0PLL,
+	V1PLL,
+	PLL_COUNT,
+};
+
+struct rk3506_clk_priv {
+	struct rk3506_cru *cru;
+	unsigned long gpll_hz;
+	unsigned long gpll_div_hz;
+	unsigned long gpll_div_100mhz;
+	unsigned long v0pll_hz;
+	unsigned long v0pll_div_hz;
+	unsigned long v1pll_hz;
+	unsigned long v1pll_div_hz;
+	unsigned long armclk_hz;
+	unsigned long armclk_enter_hz;
+	unsigned long armclk_init_hz;
+	bool sync_kernel;
+};
+
+#define RK3506_CRU_BASE			((struct rk3506_cru *)0xff9a0000)
+
+struct rk3506_cru {
+	/* cru */
+	unsigned int reserved0000[160];   /* offset 0x0   */
+	unsigned int mode_con;            /* offset 0x280 */
+	unsigned int reserved0284[31];    /* offset 0x284 */
+	unsigned int clksel_con[62];      /* offset 0x300 */
+	unsigned int reserved03f8[258];   /* offset 0x3F8 */
+	unsigned int gate_con[23];        /* offset 0x800 */
+	unsigned int reserved085c[105];   /* offset 0x85C */
+	unsigned int softrst_con[23];     /* offset 0xA00 */
+	unsigned int reserved0a5c[105];   /* offset 0xA5C */
+	unsigned int glb_cnt_th;          /* offset 0xC00 */
+	unsigned int glb_rst_st;          /* offset 0xC04 */
+	unsigned int glb_srst_fst;        /* offset 0xC08 */
+	unsigned int glb_srst_snd;        /* offset 0xC0C */
+	unsigned int glb_rst_con;         /* offset 0xC10 */
+	unsigned int reserved0c14[6];     /* offset 0xC14 */
+	unsigned int corewfi_con;         /* offset 0xC2C */
+	unsigned int reserved0c30[15604]; /* offset 0xC30 */
+
+	/* pmu cru */
+	unsigned int gpll_con[5];         /* offset 0x10000 */
+	unsigned int reserved10014[3];    /* offset 0x10014 */
+	unsigned int v0pll_con[5];        /* offset 0x10020 */
+	unsigned int reserved10034[3];    /* offset 0x10034 */
+	unsigned int v1pll_con[5];        /* offset 0x10040 */
+	unsigned int reserved10074[171];  /* offset 0x10054 */
+	unsigned int pmuclksel_con[7];    /* offset 0x10300 */
+	unsigned int reserved1031c[313];  /* offset 0x1031C */
+	unsigned int pmugate_con[3];      /* offset 0x10800 */
+	unsigned int reserved1080c[125];  /* offset 0x1080C */
+	unsigned int pmusoftrst_con[2];   /* offset 0x10A00 */
+	unsigned int reserved10a08[7583]; /* offset 0x10A08 */
+};
+
+check_member(rk3506_cru, reserved0c30[0], 0x0c30);
+check_member(rk3506_cru, reserved10a08[0], 0x10a08);
+
+struct pll_rate_table {
+	unsigned long rate;
+	unsigned int fbdiv;
+	unsigned int postdiv1;
+	unsigned int refdiv;
+	unsigned int postdiv2;
+	unsigned int dsmpd;
+	unsigned int frac;
+};
+
+#define RK3506_PMU_CRU_BASE		0x10000
+#define RK3506_PLL_CON(x)		((x) * 0x4 + RK3506_PMU_CRU_BASE)
+#define RK3506_CLKSEL_CON(x)		((x) * 0x4 + 0x300)
+#define RK3506_MODE_CON			0x280
+
+enum {
+	/* CRU_CLKSEL_CON00 */
+	CLK_GPLL_DIV_MASK		= GENMASK(9, 6),
+	CLK_GPLL_DIV_100M_MASK		= GENMASK(13, 10),
+
+	/* CRU_CLKSEL_CON01 */
+	CLK_V0PLL_DIV_MASK		= GENMASK(3, 0),
+	CLK_V1PLL_DIV_MASK		= GENMASK(7, 4),
+
+	/* CRU_CLKSEL_CON15 */
+	CLK_CORE_SRC_DIV_MASK		= GENMASK(4, 0),
+	CLK_CORE_SRC_SEL_MASK		= GENMASK(6, 5),
+	CLK_CORE_SEL_GPLL		= 0,
+	CLK_CORE_SEL_V0PLL,
+	CLK_CORE_SEL_V1PLL,
+
+	ACLK_CORE_DIV_MASK		= GENMASK(12, 9),
+
+	/* CRU_CLKSEL_CON16 */
+	PCLK_CORE_DIV_MASK		= GENMASK(3, 0),
+
+	/* CRU_CLKSEL_CON21 */
+	ACLK_BUS_DIV_MASK		= GENMASK(4, 0),
+	ACLK_BUS_SEL_MASK		= GENMASK(6, 5),
+	ACLK_BUS_SEL_GPLL_DIV		= 0,
+	ACLK_BUS_SEL_V0PLL_DIV,
+	ACLK_BUS_SEL_V1PLL_DIV,
+
+	HCLK_BUS_DIV_MASK		= GENMASK(11, 7),
+	HCLK_BUS_SEL_MASK		= GENMASK(13, 12),
+
+	/* CRU_CLKSEL_CON22 */
+	PCLK_BUS_DIV_MASK		= GENMASK(4, 0),
+	PCLK_BUS_SEL_MASK		= GENMASK(6, 5),
+
+	/* CRU_CLKSEL_CON29 */
+	HCLK_LSPERI_DIV_MASK		= GENMASK(4, 0),
+	HCLK_LSPERI_SEL_MASK		= GENMASK(6, 5),
+
+	/* CRU_CLKSEL_CON32 */
+	CLK_I2C0_DIV_MASK		= GENMASK(3, 0),
+	CLK_I2C0_SEL_MASK		= GENMASK(5, 4),
+	CLK_I2C_SEL_GPLL		= 0,
+	CLK_I2C_SEL_V0PLL,
+	CLK_I2C_SEL_V1PLL,
+	CLK_I2C1_DIV_MASK		= GENMASK(9, 6),
+	CLK_I2C1_SEL_MASK		= GENMASK(11, 10),
+
+	/* CRU_CLKSEL_CON33 */
+	CLK_I2C2_DIV_MASK		= GENMASK(3, 0),
+	CLK_I2C2_SEL_MASK		= GENMASK(5, 4),
+	CLK_PWM1_DIV_MASK		= GENMASK(9, 6),
+	CLK_PWM1_SEL_MASK		= GENMASK(11, 10),
+	CLK_PWM1_SEL_GPLL_DIV		= 0,
+	CLK_PWM1_SEL_V0PLL_DIV,
+	CLK_PWM1_SEL_V1PLL_DIV,
+
+	/* CRU_CLKSEL_CON34 */
+	CLK_SPI0_DIV_MASK		= GENMASK(7, 4),
+	CLK_SPI0_SEL_MASK		= GENMASK(9, 8),
+	CLK_SPI_SEL_24M			= 0,
+	CLK_SPI_SEL_GPLL_DIV,
+	CLK_SPI_SEL_V0PLL_DIV,
+	CLK_SPI_SEL_V1PLL_DIV,
+	CLK_SPI1_DIV_MASK		= GENMASK(13, 10),
+	CLK_SPI1_SEL_MASK		= GENMASK(15, 14),
+
+	/* CRU_CLKSEL_CON49 */
+	ACLK_HSPERI_DIV_MASK		= GENMASK(4, 0),
+	ACLK_HSPERI_SEL_MASK		= GENMASK(6, 5),
+	ACLK_HSPERI_SEL_GPLL_DIV	= 0,
+	ACLK_HSPERI_SEL_V0PLL_DIV,
+	ACLK_HSPERI_SEL_V1PLL_DIV,
+
+	CCLK_SDMMC_DIV_MASK		= GENMASK(12, 7),
+	CCLK_SDMMC_SEL_MASK		= GENMASK(14, 13),
+	CCLK_SDMMC_SEL_24M		= 0,
+	CCLK_SDMMC_SEL_GPLL,
+	CCLK_SDMMC_SEL_V0PLL,
+	CCLK_SDMMC_SEL_V1PLL,
+
+	/* CRU_CLKSEL_CON50 */
+	SCLK_FSPI_DIV_MASK		= GENMASK(4, 0),
+	SCLK_FSPI_SEL_MASK		= GENMASK(6, 5),
+	SCLK_FSPI_SEL_24M		= 0,
+	SCLK_FSPI_SEL_GPLL,
+	SCLK_FSPI_SEL_V0PLL,
+	SCLK_FSPI_SEL_V1PLL,
+	CLK_MAC_DIV_MASK		= GENMASK(11, 7),
+
+	/* CRU_CLKSEL_CON54 */
+	CLK_SARADC_DIV_MASK		= GENMASK(3, 0),
+	CLK_SARADC_SEL_MASK		= GENMASK(5, 4),
+	CLK_SARADC_SEL_24M		= 0,
+	CLK_SARADC_SEL_400K,
+	CLK_SARADC_SEL_32K,
+
+	/* CRU_CLKSEL_CON60 */
+	DCLK_VOP_DIV_MASK		= GENMASK(7, 0),
+	DCLK_VOP_SEL_MASK		= GENMASK(10, 8),
+	DCLK_VOP_SEL_24M		= 0,
+	DCLK_VOP_SEL_GPLL,
+	DCLK_VOP_SEL_V0PLL,
+	DCLK_VOP_SEL_V1PLL,
+	DCLK_VOP_SEL_FRAC_VOIC1,
+	DCLK_VOP_SEL_FRAC_COMMON0,
+	DCLK_VOP_SEL_FRAC_COMMON1,
+	DCLK_VOP_SEL_FRAC_COMMON2,
+
+	/* CRU_CLKSEL_CON61 */
+	CLK_TSADC_DIV_MASK		= GENMASK(7, 0),
+	CLK_TSADC_TSEN_DIV_MASK		= GENMASK(10, 8),
+
+	/* PMUCRU_CLKSEL_CON00 */
+	CLK_PWM0_DIV_MASK		= GENMASK(9, 6),
+	CLK_MAC_OUT_DIV_MASK		= GENMASK(15, 10),
+
+	/* SCRU_CLKSEL_CON104 */
+	CLK_PKA_CRYPTO_DIV_MASK		= GENMASK(11, 7),
+	CLK_PKA_CRYPTO_SEL_MASK		= GENMASK(13, 12),
+	CLK_PKA_CRYPTO_SEL_GPLL		= 0,
+	CLK_PKA_CRYPTO_SEL_V0PLL,
+	CLK_PKA_CRYPTO_SEL_V1PLL,
+};
+
+#endif /* _ASM_ARCH_CRU_RK3506_H */
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 34b63d4df34a..07525c364326 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += clk_rk3308.o
 obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
 obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o
 obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
+obj-$(CONFIG_ROCKCHIP_RK3506) += clk_rk3506.o
 obj-$(CONFIG_ROCKCHIP_RK3528) += clk_rk3528.o
 obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o
 obj-$(CONFIG_ROCKCHIP_RK3576) += clk_rk3576.o
diff --git a/drivers/clk/rockchip/clk_rk3506.c b/drivers/clk/rockchip/clk_rk3506.c
new file mode 100644
index 000000000000..36a80d7dfbde
--- /dev/null
+++ b/drivers/clk/rockchip/clk_rk3506.c
@@ -0,0 +1,1212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ * Author: Finley Xiao <finley.xiao@rock-chips.com>
+ */
+
+#include <bitfield.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/cru_rk3506.h>
+#include <asm/arch-rockchip/hardware.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dt-bindings/clock/rockchip,rk3506-cru.h>
+#include <linux/delay.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define RK3506_SCRU_BASE 0xff9a8000
+
+#define DIV_TO_RATE(input_rate, div)    ((input_rate) / ((div) + 1))
+
+/*
+ * [FRAC PLL]: GPLL, V0PLL, V1PLL
+ *   - VCO Frequency: 950MHz to 3800MHZ
+ *   - Output Frequency: 19MHz to 3800MHZ
+ *   - refdiv: 1 to 63 (Int Mode), 1 to 2 (Frac Mode)
+ *   - fbdiv: 16 to 3800 (Int Mode), 20 to 380 (Frac Mode)
+ *   - post1div: 1 to 7
+ *   - post2div: 1 to 7
+ */
+static struct rockchip_pll_rate_table rk3506_pll_rates[] = {
+	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
+	RK3036_PLL_RATE(1896000000, 1, 79, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1800000000, 1, 75, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1704000000, 1, 71, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1512000000, 1, 63, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1500000000, 1, 125, 2, 1, 1, 0),
+	RK3036_PLL_RATE(1416000000, 1, 59, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1350000000, 4, 225, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1296000000, 1, 54, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
+	RK3036_PLL_RATE(1188000000, 1, 99, 2, 1, 1, 0),
+	RK3036_PLL_RATE(1179648000, 1, 49, 1, 1, 0, 2550137),
+	RK3036_PLL_RATE(1008000000, 1, 84, 2, 1, 1, 0),
+	RK3036_PLL_RATE(1000000000, 3, 125, 1, 1, 1, 0),
+	RK3036_PLL_RATE(993484800, 1, 41, 1, 1, 0, 6630355),
+	RK3036_PLL_RATE(983040000, 1, 40, 1, 1, 0, 16106127),
+	RK3036_PLL_RATE(960000000, 1, 80, 2, 1, 1, 0),
+	RK3036_PLL_RATE(912000000, 1, 76, 2, 1, 1, 0),
+	RK3036_PLL_RATE(903168000, 1, 75, 2, 1, 0, 4429185),
+	RK3036_PLL_RATE(816000000, 1, 68, 2, 1, 1, 0),
+	RK3036_PLL_RATE(800000000, 3, 200, 2, 1, 1, 0),
+	RK3036_PLL_RATE(600000000, 1, 50, 2, 1, 1, 0),
+	RK3036_PLL_RATE(594000000, 2, 99, 2, 1, 1, 0),
+	RK3036_PLL_RATE(408000000, 1, 68, 2, 2, 1, 0),
+	RK3036_PLL_RATE(312000000, 1, 78, 6, 1, 1, 0),
+	RK3036_PLL_RATE(216000000, 1, 72, 4, 2, 1, 0),
+	RK3036_PLL_RATE(96000000, 1, 48, 6, 2, 1, 0),
+	{ /* sentinel */ },
+};
+
+static struct rockchip_pll_clock rk3506_pll_clks[] = {
+	[GPLL] = PLL(pll_rk3328, PLL_GPLL, RK3506_PLL_CON(0),
+		     RK3506_MODE_CON, 0, 10, 0, rk3506_pll_rates),
+	[V0PLL] = PLL(pll_rk3328, PLL_V0PLL, RK3506_PLL_CON(8),
+		     RK3506_MODE_CON, 2, 10, 0, rk3506_pll_rates),
+	[V1PLL] = PLL(pll_rk3328, PLL_V1PLL, RK3506_PLL_CON(16),
+		     RK3506_MODE_CON, 4, 10, 0, rk3506_pll_rates),
+};
+
+#define RK3506_CPUCLK_RATE(_rate, _aclk_m_core, _pclk_dbg)	\
+{								\
+	.rate = _rate##U,					\
+	.aclk_div = (_aclk_m_core),				\
+	.pclk_div = (_pclk_dbg),				\
+}
+
+/* SIGN-OFF: aclk_core: 500M, pclk_core: 125M, */
+static struct rockchip_cpu_rate_table rk3506_cpu_rates[] = {
+	RK3506_CPUCLK_RATE(1179648000, 1, 6),
+	RK3506_CPUCLK_RATE(903168000, 1, 5),
+	RK3506_CPUCLK_RATE(800000000, 1, 4),
+	RK3506_CPUCLK_RATE(589824000, 1, 3),
+	RK3506_CPUCLK_RATE(400000000, 1, 2),
+	RK3506_CPUCLK_RATE(200000000, 1, 1),
+	{ /* sentinel */ },
+};
+
+static int rk3506_armclk_get_rate(struct rk3506_clk_priv *priv)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	con = readl(&cru->clksel_con[15]);
+	sel = FIELD_GET(CLK_CORE_SRC_SEL_MASK, con);
+	div = FIELD_GET(CLK_CORE_SRC_DIV_MASK, con);
+
+	if (sel == CLK_CORE_SEL_GPLL)
+		prate = priv->gpll_hz;
+	else if (sel ==  CLK_CORE_SEL_V0PLL)
+		prate = priv->v0pll_hz;
+	else if (sel ==  CLK_CORE_SEL_V1PLL)
+		prate = priv->v1pll_hz;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static int rk3506_armclk_set_rate(struct rk3506_clk_priv *priv, ulong new_rate)
+{
+	const struct rockchip_cpu_rate_table *rate;
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, old_div, sel;
+	ulong old_rate, prate;
+
+	rate = rockchip_get_cpu_settings(rk3506_cpu_rates, new_rate);
+	if (!rate) {
+		printf("%s unsupported rate\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * set up dependent divisors for PCLK and ACLK clocks.
+	 */
+	old_rate = rk3506_armclk_get_rate(priv);
+	if (new_rate >= old_rate) {
+		rk_clrsetreg(&cru->clksel_con[15], ACLK_CORE_DIV_MASK,
+			     FIELD_PREP(ACLK_CORE_DIV_MASK, rate->aclk_div));
+		rk_clrsetreg(&cru->clksel_con[16], PCLK_CORE_DIV_MASK,
+			     FIELD_PREP(PCLK_CORE_DIV_MASK, rate->pclk_div));
+	}
+
+	if (new_rate == 589824000 || new_rate == 1179648000) {
+		sel = CLK_CORE_SEL_V0PLL;
+		div = DIV_ROUND_UP(priv->v0pll_hz, new_rate);
+		prate = priv->v0pll_hz;
+	} else if (new_rate == 903168000) {
+		sel = CLK_CORE_SEL_V1PLL;
+		div = DIV_ROUND_UP(priv->v1pll_hz, new_rate);
+		prate = priv->v1pll_hz;
+	} else {
+		sel = CLK_CORE_SEL_GPLL;
+		div = DIV_ROUND_UP(priv->gpll_hz, new_rate);
+		prate = priv->gpll_hz;
+	}
+	assert(div - 1 <= 31);
+
+	con = readl(&cru->clksel_con[15]);
+	old_div = FIELD_GET(CLK_CORE_SRC_DIV_MASK, con);
+	if (DIV_TO_RATE(prate, old_div) > new_rate) {
+		rk_clrsetreg(&cru->clksel_con[15], CLK_CORE_SRC_DIV_MASK,
+			     FIELD_PREP(CLK_CORE_SRC_DIV_MASK, div - 1));
+		rk_clrsetreg(&cru->clksel_con[15], CLK_CORE_SRC_SEL_MASK,
+			     FIELD_PREP(CLK_CORE_SRC_SEL_MASK, sel));
+	} else {
+		rk_clrsetreg(&cru->clksel_con[15], CLK_CORE_SRC_SEL_MASK,
+			     FIELD_PREP(CLK_CORE_SRC_SEL_MASK, sel));
+		rk_clrsetreg(&cru->clksel_con[15], CLK_CORE_SRC_DIV_MASK,
+			     FIELD_PREP(CLK_CORE_SRC_DIV_MASK, div - 1));
+	}
+
+	if (new_rate < old_rate) {
+		rk_clrsetreg(&cru->clksel_con[15], ACLK_CORE_DIV_MASK,
+			     FIELD_PREP(ACLK_CORE_DIV_MASK, rate->aclk_div));
+		rk_clrsetreg(&cru->clksel_con[16], PCLK_CORE_DIV_MASK,
+			     FIELD_PREP(PCLK_CORE_DIV_MASK, rate->pclk_div));
+	}
+
+	return 0;
+}
+
+static ulong rk3506_pll_div_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div;
+	ulong prate;
+
+	switch (clk_id) {
+	case CLK_GPLL_DIV:
+		con = readl(&cru->clksel_con[0]);
+		div = FIELD_GET(CLK_GPLL_DIV_MASK, con);
+		prate = priv->gpll_hz;
+		break;
+	case CLK_GPLL_DIV_100M:
+		con = readl(&cru->clksel_con[0]);
+		div = FIELD_GET(CLK_GPLL_DIV_100M_MASK, con);
+		prate = priv->gpll_div_hz;
+		break;
+	case CLK_V0PLL_DIV:
+		con = readl(&cru->clksel_con[1]);
+		div = FIELD_GET(CLK_V0PLL_DIV_MASK, con);
+		prate = priv->v0pll_hz;
+		break;
+	case CLK_V1PLL_DIV:
+		con = readl(&cru->clksel_con[1]);
+		div = FIELD_GET(CLK_V1PLL_DIV_MASK, con);
+		prate = priv->v1pll_hz;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_pll_div_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				     ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div;
+
+	switch (clk_id) {
+	case CLK_GPLL_DIV:
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		assert(div - 1 <= 15);
+		rk_clrsetreg(&cru->clksel_con[0], CLK_GPLL_DIV_MASK,
+			     FIELD_PREP(CLK_GPLL_DIV_MASK, div - 1));
+		break;
+	case CLK_GPLL_DIV_100M:
+		div = DIV_ROUND_UP(priv->gpll_div_hz, rate);
+		assert(div - 1 <= 15);
+		rk_clrsetreg(&cru->clksel_con[0], CLK_GPLL_DIV_100M_MASK,
+			     FIELD_PREP(CLK_GPLL_DIV_100M_MASK, div - 1));
+		break;
+	case CLK_V0PLL_DIV:
+		div = DIV_ROUND_UP(priv->v0pll_hz, rate);
+		assert(div - 1 <= 15);
+		rk_clrsetreg(&cru->clksel_con[1], CLK_V0PLL_DIV_MASK,
+			     FIELD_PREP(CLK_V0PLL_DIV_MASK, div - 1));
+		break;
+	case CLK_V1PLL_DIV:
+		div = DIV_ROUND_UP(priv->v1pll_hz, rate);
+		assert(div - 1 <= 15);
+		rk_clrsetreg(&cru->clksel_con[1], CLK_V1PLL_DIV_MASK,
+			     FIELD_PREP(CLK_V1PLL_DIV_MASK, div - 1));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3506_pll_div_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_bus_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	switch (clk_id) {
+	case ACLK_BUS_ROOT:
+		con = readl(&cru->clksel_con[21]);
+		sel = FIELD_GET(ACLK_BUS_SEL_MASK, con);
+		div = FIELD_GET(ACLK_BUS_DIV_MASK, con);
+		break;
+	case HCLK_BUS_ROOT:
+		con = readl(&cru->clksel_con[21]);
+		sel = FIELD_GET(HCLK_BUS_SEL_MASK, con);
+		div = FIELD_GET(HCLK_BUS_DIV_MASK, con);
+		break;
+	case PCLK_BUS_ROOT:
+		con = readl(&cru->clksel_con[22]);
+		sel = FIELD_GET(PCLK_BUS_SEL_MASK, con);
+		div = FIELD_GET(PCLK_BUS_DIV_MASK, con);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (sel == ACLK_BUS_SEL_GPLL_DIV)
+		prate = priv->gpll_div_hz;
+	else if (sel == ACLK_BUS_SEL_V0PLL_DIV)
+		prate = priv->v0pll_div_hz;
+	else if (sel == ACLK_BUS_SEL_V1PLL_DIV)
+		prate = priv->v1pll_div_hz;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_bus_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				 ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div, sel;
+
+	if (priv->v0pll_div_hz % rate == 0) {
+		sel = ACLK_BUS_SEL_V0PLL_DIV;
+		div = DIV_ROUND_UP(priv->v0pll_div_hz, rate);
+	} else if (priv->v1pll_div_hz % rate == 0) {
+		sel = ACLK_BUS_SEL_V1PLL_DIV;
+		div = DIV_ROUND_UP(priv->v1pll_div_hz, rate);
+	} else {
+		sel = ACLK_BUS_SEL_GPLL_DIV;
+		div = DIV_ROUND_UP(priv->gpll_div_hz, rate);
+	}
+	assert(div - 1 <= 31);
+
+	switch (clk_id) {
+	case ACLK_BUS_ROOT:
+		rk_clrsetreg(&cru->clksel_con[21],
+			     ACLK_BUS_SEL_MASK | ACLK_BUS_DIV_MASK,
+			     FIELD_PREP(ACLK_BUS_SEL_MASK, sel) |
+			     FIELD_PREP(ACLK_BUS_DIV_MASK, div - 1));
+		break;
+	case HCLK_BUS_ROOT:
+		rk_clrsetreg(&cru->clksel_con[21],
+			     HCLK_BUS_SEL_MASK | HCLK_BUS_DIV_MASK,
+			     FIELD_PREP(HCLK_BUS_SEL_MASK, sel) |
+			     FIELD_PREP(HCLK_BUS_DIV_MASK, div - 1));
+		break;
+	case PCLK_BUS_ROOT:
+		rk_clrsetreg(&cru->clksel_con[22],
+			     PCLK_BUS_SEL_MASK | PCLK_BUS_DIV_MASK,
+			     FIELD_PREP(PCLK_BUS_SEL_MASK, sel) |
+			     FIELD_PREP(PCLK_BUS_DIV_MASK, div - 1));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3506_bus_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_peri_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	switch (clk_id) {
+	case ACLK_HSPERI_ROOT:
+		con = readl(&cru->clksel_con[49]);
+		sel = FIELD_GET(ACLK_HSPERI_SEL_MASK, con);
+		div = FIELD_GET(ACLK_HSPERI_DIV_MASK, con);
+		break;
+	case HCLK_LSPERI_ROOT:
+		con = readl(&cru->clksel_con[29]);
+		sel = FIELD_GET(HCLK_LSPERI_SEL_MASK, con);
+		div = FIELD_GET(HCLK_LSPERI_DIV_MASK, con);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (sel == ACLK_HSPERI_SEL_GPLL_DIV)
+		prate = priv->gpll_div_hz;
+	else if (sel == ACLK_HSPERI_SEL_V0PLL_DIV)
+		prate = priv->v0pll_div_hz;
+	else if (sel == ACLK_HSPERI_SEL_V1PLL_DIV)
+		prate = priv->v1pll_div_hz;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_peri_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				  ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div, sel;
+
+	if (priv->v0pll_div_hz % rate == 0) {
+		sel = ACLK_BUS_SEL_V0PLL_DIV;
+		div = DIV_ROUND_UP(priv->v0pll_div_hz, rate);
+	} else if (priv->v1pll_div_hz % rate == 0) {
+		sel = ACLK_BUS_SEL_V1PLL_DIV;
+		div = DIV_ROUND_UP(priv->v1pll_div_hz, rate);
+	} else {
+		sel = ACLK_BUS_SEL_GPLL_DIV;
+		div = DIV_ROUND_UP(priv->gpll_div_hz, rate);
+	}
+	assert(div - 1 <= 31);
+
+	switch (clk_id) {
+	case ACLK_HSPERI_ROOT:
+		rk_clrsetreg(&cru->clksel_con[49],
+			     ACLK_HSPERI_SEL_MASK | ACLK_HSPERI_DIV_MASK,
+			     FIELD_PREP(ACLK_HSPERI_SEL_MASK, sel) |
+			     FIELD_PREP(ACLK_HSPERI_DIV_MASK, div - 1));
+		break;
+	case HCLK_LSPERI_ROOT:
+		rk_clrsetreg(&cru->clksel_con[29],
+			     HCLK_LSPERI_SEL_MASK | HCLK_LSPERI_DIV_MASK,
+			     FIELD_PREP(HCLK_LSPERI_SEL_MASK, sel) |
+			     FIELD_PREP(HCLK_LSPERI_DIV_MASK, div - 1));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3506_peri_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_sdmmc_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	con = readl(&cru->clksel_con[49]);
+	sel = FIELD_GET(CCLK_SDMMC_SEL_MASK, con);
+	div = FIELD_GET(CCLK_SDMMC_DIV_MASK, con);
+
+	if (sel == CCLK_SDMMC_SEL_24M)
+		prate = OSC_HZ;
+	else if (sel == CCLK_SDMMC_SEL_GPLL)
+		prate = priv->gpll_hz;
+	else if (sel == CCLK_SDMMC_SEL_V0PLL)
+		prate = priv->v0pll_hz;
+	else if (sel == CCLK_SDMMC_SEL_V1PLL)
+		prate = priv->v1pll_hz;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_sdmmc_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				   ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div, sel;
+
+	if (OSC_HZ % rate == 0) {
+		sel = CCLK_SDMMC_SEL_24M;
+		div = DIV_ROUND_UP(OSC_HZ, rate);
+	} else if (priv->v0pll_hz % rate == 0) {
+		sel = CCLK_SDMMC_SEL_V0PLL;
+		div = DIV_ROUND_UP(priv->v0pll_hz, rate);
+	} else if (priv->v1pll_hz % rate == 0) {
+		sel = CCLK_SDMMC_SEL_V1PLL;
+		div = DIV_ROUND_UP(priv->v1pll_hz, rate);
+	} else {
+		sel = CCLK_SDMMC_SEL_GPLL;
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+	}
+	assert(div - 1 <= 63);
+
+	rk_clrsetreg(&cru->clksel_con[49],
+		     CCLK_SDMMC_SEL_MASK | CCLK_SDMMC_DIV_MASK,
+		     FIELD_PREP(CCLK_SDMMC_SEL_MASK, sel) |
+		     FIELD_PREP(CCLK_SDMMC_DIV_MASK, div - 1));
+
+	return rk3506_sdmmc_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_saradc_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	con = readl(&cru->clksel_con[54]);
+	sel = FIELD_GET(CLK_SARADC_SEL_MASK, con);
+	div = FIELD_GET(CLK_SARADC_DIV_MASK, con);
+
+	if (sel == CLK_SARADC_SEL_24M)
+		prate = OSC_HZ;
+	else if (sel == CLK_SARADC_SEL_400K)
+		prate = 400000;
+	else if (sel == CLK_SARADC_SEL_32K)
+		prate = 32000;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_saradc_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				    ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div, sel;
+
+	if (32000 % rate == 0) {
+		sel = CLK_SARADC_SEL_32K;
+		div = 1;
+	} else if (400000 % rate == 0) {
+		sel = CLK_SARADC_SEL_400K;
+		div = 1;
+	} else {
+		sel = CLK_SARADC_SEL_24M;
+		div = DIV_ROUND_UP(OSC_HZ, rate);
+	}
+	assert(div - 1 <= 15);
+
+	rk_clrsetreg(&cru->clksel_con[54],
+		     CLK_SARADC_SEL_MASK | CLK_SARADC_DIV_MASK,
+		     FIELD_PREP(CLK_SARADC_SEL_MASK, sel) |
+		     FIELD_PREP(CLK_SARADC_DIV_MASK, div - 1));
+
+	return rk3506_saradc_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_tsadc_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div;
+
+	con = readl(&cru->clksel_con[61]);
+	switch (clk_id) {
+	case CLK_TSADC_TSEN:
+		div = FIELD_GET(CLK_TSADC_TSEN_DIV_MASK, con);
+		break;
+	case CLK_TSADC:
+		div = FIELD_GET(CLK_TSADC_DIV_MASK, con);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return DIV_TO_RATE(OSC_HZ, div);
+}
+
+static ulong rk3506_tsadc_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				   ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div;
+
+	switch (clk_id) {
+	case CLK_TSADC_TSEN:
+		div = DIV_ROUND_UP(OSC_HZ, rate);
+		assert(div - 1 <= 7);
+		rk_clrsetreg(&cru->clksel_con[61], CLK_TSADC_TSEN_DIV_MASK,
+			     FIELD_PREP(CLK_TSADC_TSEN_DIV_MASK, div - 1));
+		break;
+	case CLK_TSADC:
+		div = DIV_ROUND_UP(OSC_HZ, rate);
+		assert(div - 1 <= 255);
+		rk_clrsetreg(&cru->clksel_con[61], CLK_TSADC_DIV_MASK,
+			     FIELD_PREP(CLK_TSADC_DIV_MASK, div - 1));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3506_tsadc_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_i2c_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	switch (clk_id) {
+	case CLK_I2C0:
+		con = readl(&cru->clksel_con[32]);
+		sel = FIELD_GET(CLK_I2C0_SEL_MASK, con);
+		div = FIELD_GET(CLK_I2C0_DIV_MASK, con);
+	case CLK_I2C1:
+		con = readl(&cru->clksel_con[32]);
+		sel = FIELD_GET(CLK_I2C1_SEL_MASK, con);
+		div = FIELD_GET(CLK_I2C1_DIV_MASK, con);
+	case CLK_I2C2:
+		con = readl(&cru->clksel_con[33]);
+		sel = FIELD_GET(CLK_I2C2_SEL_MASK, con);
+		div = FIELD_GET(CLK_I2C2_DIV_MASK, con);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (sel == CLK_I2C_SEL_GPLL)
+		prate = priv->gpll_hz;
+	else if (sel == CLK_I2C_SEL_V0PLL)
+		prate = priv->v0pll_hz;
+	else if (sel == CLK_I2C_SEL_V1PLL)
+		prate = priv->v1pll_hz;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_i2c_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				 ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div, sel;
+
+	if (priv->v0pll_hz % rate == 0) {
+		sel = CLK_I2C_SEL_V0PLL;
+		div = DIV_ROUND_UP(priv->v0pll_hz, rate);
+	} else if (priv->v1pll_hz % rate == 0) {
+		sel = CLK_I2C_SEL_V1PLL;
+		div = DIV_ROUND_UP(priv->v1pll_hz, rate);
+	} else {
+		sel = CLK_I2C_SEL_GPLL;
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+	}
+	assert(div - 1 <= 15);
+
+	switch (clk_id) {
+	case CLK_I2C0:
+		rk_clrsetreg(&cru->clksel_con[32],
+			     CLK_I2C0_SEL_MASK | CLK_I2C0_DIV_MASK,
+			     FIELD_PREP(CLK_I2C0_SEL_MASK, sel) |
+			     FIELD_PREP(CLK_I2C0_DIV_MASK, div - 1));
+		break;
+	case CLK_I2C1:
+		rk_clrsetreg(&cru->clksel_con[32],
+			     CLK_I2C1_SEL_MASK | CLK_I2C1_DIV_MASK,
+			     FIELD_PREP(CLK_I2C1_SEL_MASK, sel) |
+			     FIELD_PREP(CLK_I2C1_DIV_MASK, div - 1));
+		break;
+	case CLK_I2C2:
+		rk_clrsetreg(&cru->clksel_con[33],
+			     CLK_I2C2_SEL_MASK | CLK_I2C2_DIV_MASK,
+			     FIELD_PREP(CLK_I2C2_SEL_MASK, sel) |
+			     FIELD_PREP(CLK_I2C2_DIV_MASK, div - 1));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3506_i2c_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_pwm_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	switch (clk_id) {
+	case CLK_PWM0:
+		con = readl(&cru->pmuclksel_con[0]);
+		div = FIELD_GET(CLK_PWM0_DIV_MASK, con);
+		prate = priv->gpll_div_100mhz;
+		break;
+	case CLK_PWM1:
+		con = readl(&cru->clksel_con[33]);
+		sel = FIELD_GET(CLK_PWM1_SEL_MASK, con);
+		div = FIELD_GET(CLK_PWM1_DIV_MASK, con);
+		if (sel == CLK_PWM1_SEL_GPLL_DIV)
+			prate = priv->gpll_div_hz;
+		else if (sel == CLK_PWM1_SEL_V0PLL_DIV)
+			prate = priv->v0pll_div_hz;
+		else if (sel == CLK_PWM1_SEL_V1PLL_DIV)
+			prate = priv->v1pll_div_hz;
+		else
+			return -EINVAL;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_pwm_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				 ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div, sel;
+
+	switch (clk_id) {
+	case CLK_PWM0:
+		div = DIV_ROUND_UP(priv->gpll_div_100mhz, rate);
+		assert(div - 1 <= 15);
+		rk_clrsetreg(&cru->pmuclksel_con[0], CLK_PWM0_DIV_MASK,
+			     FIELD_PREP(CLK_PWM0_DIV_MASK, div - 1));
+		break;
+	case CLK_PWM1:
+		if (priv->v0pll_hz % rate == 0) {
+			sel = CLK_PWM1_SEL_V0PLL_DIV;
+			div = DIV_ROUND_UP(priv->v0pll_div_hz, rate);
+		} else if (priv->v1pll_hz % rate == 0) {
+			sel = CLK_PWM1_SEL_V1PLL_DIV;
+			div = DIV_ROUND_UP(priv->v1pll_div_hz, rate);
+		} else {
+			sel = CLK_PWM1_SEL_GPLL_DIV;
+			div = DIV_ROUND_UP(priv->gpll_div_hz, rate);
+		}
+		assert(div - 1 <= 15);
+		rk_clrsetreg(&cru->clksel_con[33],
+			     CLK_PWM1_SEL_MASK | CLK_PWM1_DIV_MASK,
+			     FIELD_PREP(CLK_PWM1_SEL_MASK, sel) |
+			     FIELD_PREP(CLK_PWM1_DIV_MASK, div - 1));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3506_pwm_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_spi_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	switch (clk_id) {
+	case CLK_SPI0:
+		con = readl(&cru->clksel_con[34]);
+		sel = FIELD_GET(CLK_SPI0_SEL_MASK, con);
+		div = FIELD_GET(CLK_SPI0_DIV_MASK, con);
+		break;
+	case CLK_SPI1:
+		con = readl(&cru->clksel_con[34]);
+		sel = FIELD_GET(CLK_SPI1_SEL_MASK, con);
+		div = FIELD_GET(CLK_SPI1_DIV_MASK, con);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (sel == CLK_SPI_SEL_24M)
+		prate = OSC_HZ;
+	else if (sel == CLK_SPI_SEL_GPLL_DIV)
+		prate = priv->gpll_div_hz;
+	else if (sel == CLK_SPI_SEL_V0PLL_DIV)
+		prate = priv->v0pll_div_hz;
+	else if (sel == CLK_SPI_SEL_V1PLL_DIV)
+		prate = priv->v1pll_div_hz;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_spi_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				 ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div, sel;
+
+	if (OSC_HZ % rate == 0) {
+		sel = CLK_SPI_SEL_24M;
+		div = DIV_ROUND_UP(OSC_HZ, rate);
+	} else if (priv->v0pll_div_hz % rate == 0) {
+		sel = CLK_SPI_SEL_V0PLL_DIV;
+		div = DIV_ROUND_UP(priv->v0pll_div_hz, rate);
+	} else if (priv->v1pll_div_hz % rate == 0) {
+		sel = CLK_SPI_SEL_V1PLL_DIV;
+		div = DIV_ROUND_UP(priv->v1pll_div_hz, rate);
+	} else {
+		sel = CLK_SPI_SEL_GPLL_DIV;
+		div = DIV_ROUND_UP(priv->gpll_div_hz, rate);
+	}
+	assert(div - 1 <= 15);
+
+	switch (clk_id) {
+	case CLK_SPI0:
+		rk_clrsetreg(&cru->clksel_con[34],
+			     CLK_SPI0_SEL_MASK | CLK_SPI0_DIV_MASK,
+			     FIELD_PREP(CLK_SPI0_SEL_MASK, sel) |
+			     FIELD_PREP(CLK_SPI0_DIV_MASK, div - 1));
+		break;
+	case CLK_SPI1:
+		rk_clrsetreg(&cru->clksel_con[34],
+			     CLK_SPI1_SEL_MASK | CLK_SPI1_DIV_MASK,
+			     FIELD_PREP(CLK_SPI1_SEL_MASK, sel) |
+			     FIELD_PREP(CLK_SPI1_DIV_MASK, div - 1));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3506_spi_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_fspi_get_rate(struct rk3506_clk_priv *priv)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	con = readl(&cru->clksel_con[50]);
+	sel = FIELD_GET(SCLK_FSPI_SEL_MASK, con);
+	div = FIELD_GET(SCLK_FSPI_DIV_MASK, con);
+
+	if (sel == SCLK_FSPI_SEL_24M)
+		prate = OSC_HZ;
+	else if (sel == SCLK_FSPI_SEL_GPLL)
+		prate = priv->gpll_hz;
+	else if (sel == SCLK_FSPI_SEL_V0PLL)
+		prate = priv->v0pll_hz;
+	else if (sel == SCLK_FSPI_SEL_V1PLL)
+		prate = priv->v1pll_hz;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_fspi_set_rate(struct rk3506_clk_priv *priv, ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	int div, sel;
+
+	if (OSC_HZ % rate == 0) {
+		sel = SCLK_FSPI_SEL_24M;
+		div = DIV_ROUND_UP(OSC_HZ, rate);
+	} else if (priv->v0pll_hz % rate == 0) {
+		sel = SCLK_FSPI_SEL_V0PLL;
+		div = DIV_ROUND_UP(priv->v0pll_hz, rate);
+	} else if (priv->v1pll_hz % rate == 0) {
+		sel = SCLK_FSPI_SEL_V1PLL;
+		div = DIV_ROUND_UP(priv->v1pll_hz, rate);
+	} else {
+		sel = SCLK_FSPI_SEL_GPLL;
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+	}
+	assert(div - 1 <= 31);
+
+	rk_clrsetreg(&cru->clksel_con[50],
+		     SCLK_FSPI_SEL_MASK | SCLK_FSPI_DIV_MASK,
+		     FIELD_PREP(SCLK_FSPI_SEL_MASK, sel) |
+		     FIELD_PREP(SCLK_FSPI_DIV_MASK, div - 1));
+
+	return rk3506_fspi_get_rate(priv);
+}
+
+static ulong rk3506_vop_dclk_get_rate(struct rk3506_clk_priv *priv)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div, sel;
+	ulong prate;
+
+	con = readl(&cru->clksel_con[60]);
+	sel = FIELD_GET(DCLK_VOP_SEL_MASK, con);
+	div = FIELD_GET(DCLK_VOP_DIV_MASK, con);
+
+	if (sel == DCLK_VOP_SEL_24M)
+		prate = OSC_HZ;
+	else if (sel == DCLK_VOP_SEL_GPLL)
+		prate = priv->gpll_hz;
+	else if (sel == DCLK_VOP_SEL_V0PLL)
+		prate = priv->v0pll_hz;
+	else if (sel == DCLK_VOP_SEL_V1PLL)
+		prate = priv->v1pll_hz;
+	else
+		return -EINVAL;
+
+	return DIV_TO_RATE(prate, div);
+}
+
+static ulong rk3506_vop_dclk_set_rate(struct rk3506_clk_priv *priv, ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	int div, sel;
+
+	if (OSC_HZ % rate == 0) {
+		sel = DCLK_VOP_SEL_24M;
+		div = DIV_ROUND_UP(OSC_HZ, rate);
+	} else if (priv->v0pll_hz % rate == 0) {
+		sel = DCLK_VOP_SEL_V0PLL;
+		div = DIV_ROUND_UP(priv->v0pll_hz, rate);
+	} else if (priv->v1pll_hz % rate == 0) {
+		sel = DCLK_VOP_SEL_V1PLL;
+		div = DIV_ROUND_UP(priv->v1pll_hz, rate);
+	} else {
+		sel = DCLK_VOP_SEL_GPLL;
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+	}
+	assert(div - 1 <= 255);
+
+	rk_clrsetreg(&cru->clksel_con[60],
+		     DCLK_VOP_SEL_MASK | DCLK_VOP_DIV_MASK,
+		     FIELD_PREP(DCLK_VOP_SEL_MASK, sel) |
+		     FIELD_PREP(DCLK_VOP_DIV_MASK, div - 1));
+
+	return rk3506_vop_dclk_get_rate(priv);
+}
+
+static ulong rk3506_mac_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 con, div;
+
+	switch (clk_id) {
+	case CLK_MAC0:
+	case CLK_MAC1:
+		con = readl(&cru->clksel_con[50]);
+		div = FIELD_GET(CLK_MAC_DIV_MASK, con);
+		break;
+	case CLK_MAC_OUT:
+		con = readl(&cru->pmuclksel_con[0]);
+		div = FIELD_GET(CLK_MAC_OUT_DIV_MASK, con);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return DIV_TO_RATE(priv->gpll_hz, div);
+}
+
+static ulong rk3506_mac_set_rate(struct rk3506_clk_priv *priv, ulong clk_id,
+				 ulong rate)
+{
+	struct rk3506_cru *cru = priv->cru;
+	u32 div;
+
+	switch (clk_id) {
+	case CLK_MAC0:
+	case CLK_MAC1:
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		rk_clrsetreg(&cru->clksel_con[50], CLK_MAC_DIV_MASK,
+			     FIELD_PREP(CLK_MAC_DIV_MASK, div - 1));
+		break;
+	case CLK_MAC_OUT:
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		rk_clrsetreg(&cru->pmuclksel_con[0], CLK_MAC_OUT_DIV_MASK,
+			     FIELD_PREP(CLK_MAC_OUT_DIV_MASK, div - 1));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3506_mac_get_rate(priv, clk_id);
+}
+
+static ulong rk3506_clk_get_rate(struct clk *clk)
+{
+	struct rk3506_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong rate = 0;
+
+	if (!priv->gpll_hz || !priv->v0pll_hz || !priv->v1pll_hz) {
+		printf("%s: gpll=%lu, v0pll=%lu, v1pll=%lu\n",
+		       __func__, priv->gpll_hz, priv->v0pll_hz, priv->v1pll_hz);
+		return -ENOENT;
+	}
+
+	switch (clk->id) {
+	case PLL_GPLL:
+		rate = priv->gpll_hz;
+		break;
+	case PLL_V0PLL:
+		rate = priv->v0pll_hz;
+		break;
+	case PLL_V1PLL:
+		rate = priv->v1pll_hz;
+		break;
+	case ARMCLK:
+		rate = rk3506_armclk_get_rate(priv);
+		break;
+	case CLK_GPLL_DIV:
+	case CLK_GPLL_DIV_100M:
+	case CLK_V0PLL_DIV:
+	case CLK_V1PLL_DIV:
+		rate = rk3506_pll_div_get_rate(priv, clk->id);
+		break;
+	case ACLK_BUS_ROOT:
+	case HCLK_BUS_ROOT:
+	case PCLK_BUS_ROOT:
+		rate = rk3506_bus_get_rate(priv, clk->id);
+		break;
+	case ACLK_HSPERI_ROOT:
+	case HCLK_LSPERI_ROOT:
+		rate = rk3506_peri_get_rate(priv, clk->id);
+		break;
+	case HCLK_SDMMC:
+	case CCLK_SRC_SDMMC:
+		rate = rk3506_sdmmc_get_rate(priv, clk->id);
+		break;
+	case CLK_SARADC:
+		rate = rk3506_saradc_get_rate(priv, clk->id);
+		break;
+	case CLK_TSADC:
+	case CLK_TSADC_TSEN:
+		rate = rk3506_tsadc_get_rate(priv, clk->id);
+		break;
+	case CLK_I2C0:
+	case CLK_I2C1:
+	case CLK_I2C2:
+		rate = rk3506_i2c_get_rate(priv, clk->id);
+		break;
+	case CLK_PWM0:
+	case CLK_PWM1:
+		rate = rk3506_pwm_get_rate(priv, clk->id);
+		break;
+	case CLK_SPI0:
+	case CLK_SPI1:
+		rate = rk3506_spi_get_rate(priv, clk->id);
+		break;
+	case SCLK_FSPI:
+		rate = rk3506_fspi_get_rate(priv);
+		break;
+	case DCLK_VOP:
+		rate = rk3506_vop_dclk_get_rate(priv);
+		break;
+	case CLK_MAC0:
+	case CLK_MAC1:
+	case CLK_MAC_OUT:
+		rate = rk3506_mac_get_rate(priv, clk->id);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rate;
+};
+
+static ulong rk3506_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct rk3506_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong ret = 0;
+
+	if (!priv->gpll_hz || !priv->v0pll_hz || !priv->v1pll_hz) {
+		printf("%s: gpll=%lu, v0pll=%lu, v1pll=%lu\n",
+		       __func__, priv->gpll_hz, priv->v0pll_hz, priv->v1pll_hz);
+		return -ENOENT;
+	}
+
+	switch (clk->id) {
+	case ARMCLK:
+		if (priv->armclk_hz)
+			rk3506_armclk_set_rate(priv, rate);
+		priv->armclk_hz = rate;
+		break;
+	case CLK_GPLL_DIV:
+	case CLK_GPLL_DIV_100M:
+	case CLK_V0PLL_DIV:
+	case CLK_V1PLL_DIV:
+		ret = rk3506_pll_div_set_rate(priv, clk->id, rate);
+		break;
+	case ACLK_BUS_ROOT:
+	case HCLK_BUS_ROOT:
+	case PCLK_BUS_ROOT:
+		ret = rk3506_bus_set_rate(priv, clk->id, rate);
+		break;
+	case ACLK_HSPERI_ROOT:
+	case HCLK_LSPERI_ROOT:
+		ret = rk3506_peri_set_rate(priv, clk->id, rate);
+		break;
+	case HCLK_SDMMC:
+	case CCLK_SRC_SDMMC:
+		ret = rk3506_sdmmc_set_rate(priv, clk->id, rate);
+		break;
+	case CLK_SARADC:
+		ret = rk3506_saradc_set_rate(priv, clk->id, rate);
+		break;
+	case CLK_TSADC:
+	case CLK_TSADC_TSEN:
+		ret = rk3506_tsadc_set_rate(priv, clk->id, rate);
+		break;
+	case CLK_I2C0:
+	case CLK_I2C1:
+	case CLK_I2C2:
+		ret = rk3506_i2c_set_rate(priv, clk->id, rate);
+		break;
+	case CLK_PWM0:
+	case CLK_PWM1:
+		ret = rk3506_pwm_set_rate(priv, clk->id, rate);
+		break;
+	case CLK_SPI0:
+	case CLK_SPI1:
+		ret = rk3506_spi_set_rate(priv, clk->id, rate);
+		break;
+	case SCLK_FSPI:
+		ret = rk3506_fspi_set_rate(priv, rate);
+		break;
+	case DCLK_VOP:
+		ret = rk3506_vop_dclk_set_rate(priv, rate);
+		break;
+	case CLK_MAC0:
+	case CLK_MAC1:
+	case CLK_MAC_OUT:
+		ret = rk3506_mac_set_rate(priv, clk->id, rate);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return ret;
+};
+
+static struct clk_ops rk3506_clk_ops = {
+	.get_rate = rk3506_clk_get_rate,
+	.set_rate = rk3506_clk_set_rate,
+};
+
+static void rk3506_clk_init(struct rk3506_clk_priv *priv)
+{
+	priv->sync_kernel = false;
+
+	if (!priv->gpll_hz) {
+		priv->gpll_hz = rockchip_pll_get_rate(&rk3506_pll_clks[GPLL],
+						      priv->cru, GPLL);
+		priv->gpll_hz = roundup(priv->gpll_hz, 1000);
+	}
+	if (!priv->v0pll_hz) {
+		priv->v0pll_hz = rockchip_pll_get_rate(&rk3506_pll_clks[V0PLL],
+						       priv->cru, V0PLL);
+		priv->v0pll_hz = roundup(priv->v0pll_hz, 1000);
+	}
+	if (!priv->v1pll_hz) {
+		priv->v1pll_hz = rockchip_pll_get_rate(&rk3506_pll_clks[V1PLL],
+						       priv->cru, V1PLL);
+		priv->v1pll_hz = roundup(priv->v1pll_hz, 1000);
+	}
+	if (!priv->gpll_div_hz) {
+		priv->gpll_div_hz = rk3506_pll_div_get_rate(priv, CLK_GPLL_DIV);
+		priv->gpll_div_hz = roundup(priv->gpll_div_hz, 1000);
+	}
+	if (!priv->gpll_div_100mhz) {
+		priv->gpll_div_100mhz = rk3506_pll_div_get_rate(priv, CLK_GPLL_DIV_100M);
+		priv->gpll_div_100mhz = roundup(priv->gpll_div_100mhz, 1000);
+	}
+	if (!priv->v0pll_div_hz) {
+		priv->v0pll_div_hz = rk3506_pll_div_get_rate(priv, CLK_V0PLL_DIV);
+		priv->v0pll_div_hz = roundup(priv->v0pll_div_hz, 1000);
+	}
+	if (!priv->v1pll_div_hz) {
+		priv->v1pll_div_hz = rk3506_pll_div_get_rate(priv, CLK_V1PLL_DIV);
+		priv->v1pll_div_hz = roundup(priv->v1pll_div_hz, 1000);
+	}
+	if (!priv->armclk_enter_hz) {
+		priv->armclk_enter_hz = rk3506_armclk_get_rate(priv);
+		priv->armclk_init_hz = priv->armclk_enter_hz;
+	}
+}
+
+static int rk3506_clk_probe(struct udevice *dev)
+{
+	struct rk3506_clk_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	rk3506_clk_init(priv);
+
+	/* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */
+	ret = clk_set_defaults(dev, 1);
+	if (ret)
+		debug("%s clk_set_defaults failed %d\n", __func__, ret);
+	else
+		priv->sync_kernel = true;
+
+	return 0;
+}
+
+static int rk3506_clk_ofdata_to_platdata(struct udevice *dev)
+{
+	struct rk3506_clk_priv *priv = dev_get_priv(dev);
+
+	priv->cru = dev_read_addr_ptr(dev);
+
+	return 0;
+}
+
+static int rk3506_clk_bind(struct udevice *dev)
+{
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
+	int ret;
+
+#if IS_ENABLED(CONFIG_XPL_BUILD)
+	/* Init pka crypto rate, sel=v0pll, div=3 */
+	rk_clrsetreg(RK3506_SCRU_BASE + 0x0010,
+		     CLK_PKA_CRYPTO_SEL_MASK | CLK_PKA_CRYPTO_DIV_MASK,
+		     FIELD_PREP(CLK_PKA_CRYPTO_SEL_MASK, CLK_PKA_CRYPTO_SEL_V0PLL) |
+		     FIELD_PREP(CLK_PKA_CRYPTO_DIV_MASK, 3));
+
+	/* Change clk core src rate, sel=gpll, div=3 */
+	rk_clrsetreg((ulong)RK3506_CRU_BASE + RK3506_CLKSEL_CON(15),
+		     CLK_CORE_SRC_SEL_MASK | CLK_CORE_SRC_DIV_MASK,
+		     FIELD_PREP(CLK_CORE_SRC_SEL_MASK, CLK_CORE_SEL_GPLL) |
+		     FIELD_PREP(CLK_CORE_SRC_DIV_MASK, 3));
+#endif
+
+	/* The reset driver does not have a device node, so bind it here */
+	ret = device_bind_driver(dev, "rockchip_sysreset", "sysreset",
+				 &sys_child);
+	if (ret) {
+		debug("Warning: No sysreset driver: ret=%d\n", ret);
+	} else {
+		priv = malloc(sizeof(struct sysreset_reg));
+		priv->glb_srst_fst_value = offsetof(struct rk3506_cru,
+						    glb_srst_fst);
+		priv->glb_srst_snd_value = offsetof(struct rk3506_cru,
+						    glb_srst_snd);
+		dev_set_priv(sys_child, priv);
+	}
+
+#if CONFIG_IS_ENABLED(RESET_ROCKCHIP)
+	ret = offsetof(struct rk3506_cru, softrst_con[0]);
+	ret = rk3506_reset_bind_lut(dev, ret, 23);
+	if (ret)
+		debug("Warning: software reset driver bind failed\n");
+#endif
+
+	return 0;
+}
+
+static const struct udevice_id rk3506_clk_ids[] = {
+	{ .compatible = "rockchip,rk3506-cru" },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_rk3506_cru) = {
+	.name		= "rockchip_rk3506_cru",
+	.id		= UCLASS_CLK,
+	.of_match	= rk3506_clk_ids,
+	.priv_auto	= sizeof(struct rk3506_clk_priv),
+	.of_to_plat	= rk3506_clk_ofdata_to_platdata,
+	.ops		= &rk3506_clk_ops,
+	.bind		= rk3506_clk_bind,
+	.probe		= rk3506_clk_probe,
+};
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index ee5b009d1341..088545c64733 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -16,7 +16,7 @@ obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o
 obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
 obj-$(CONFIG_RESET_AST2500) += reset-ast2500.o
 obj-$(CONFIG_RESET_AST2600) += reset-ast2600.o
-obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o rst-rk3528.o rst-rk3576.o rst-rk3588.o
+obj-$(CONFIG_RESET_ROCKCHIP) += reset-rockchip.o rst-rk3506.o rst-rk3528.o rst-rk3576.o rst-rk3588.o
 obj-$(CONFIG_RESET_MESON) += reset-meson.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
 obj-$(CONFIG_RESET_MEDIATEK) += reset-mediatek.o
diff --git a/drivers/reset/rst-rk3506.c b/drivers/reset/rst-rk3506.c
new file mode 100644
index 000000000000..9c384db0589a
--- /dev/null
+++ b/drivers/reset/rst-rk3506.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Rockchip Electronics Co., Ltd.
+ * Author: Finley Xiao <finley.xiao@rock-chips.com>
+ */
+
+#include <dm.h>
+#include <asm/arch-rockchip/clock.h>
+#include <dt-bindings/reset/rockchip,rk3506-cru.h>
+
+/* 0xFF9A0000 + 0x0A00 */
+#define RK3506_CRU_RESET_OFFSET(id, reg, bit) [id] = (0 + reg * 16 + bit)
+
+/* mapping table for reset ID to register offset */
+static const int rk3506_register_offset[] = {
+	/* CRU-->SOFTRST_CON00 */
+	RK3506_CRU_RESET_OFFSET(SRST_NCOREPORESET0_AC, 0, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_NCOREPORESET1_AC, 0, 1),
+	RK3506_CRU_RESET_OFFSET(SRST_NCOREPORESET2_AC, 0, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_NCORESET0_AC, 0, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_NCORESET1_AC, 0, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_NCORESET2_AC, 0, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_NL2RESET_AC, 0, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_A_CORE_BIU_AC, 0, 9),
+	RK3506_CRU_RESET_OFFSET(SRST_H_M0_AC, 0, 10),
+
+	/* CRU-->SOFTRST_CON02 */
+	RK3506_CRU_RESET_OFFSET(SRST_NDBGRESET, 2, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_P_CORE_BIU, 2, 14),
+	RK3506_CRU_RESET_OFFSET(SRST_PMU, 2, 15),
+
+	/* CRU-->SOFTRST_CON03 */
+	RK3506_CRU_RESET_OFFSET(SRST_P_DBG, 3, 1),
+	RK3506_CRU_RESET_OFFSET(SRST_POT_DBG, 3, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_P_CORE_GRF, 3, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_CORE_EMA_DETECT, 3, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_REF_PVTPLL_CORE, 3, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_P_GPIO1, 3, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_DB_GPIO1, 3, 9),
+
+	/* CRU-->SOFTRST_CON04 */
+	RK3506_CRU_RESET_OFFSET(SRST_A_CORE_PERI_BIU, 4, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_A_DSMC, 4, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_P_DSMC, 4, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_FLEXBUS, 4, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_A_FLEXBUS, 4, 9),
+	RK3506_CRU_RESET_OFFSET(SRST_H_FLEXBUS, 4, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_A_DSMC_SLV, 4, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_H_DSMC_SLV, 4, 12),
+	RK3506_CRU_RESET_OFFSET(SRST_DSMC_SLV, 4, 13),
+
+	/* CRU-->SOFTRST_CON05 */
+	RK3506_CRU_RESET_OFFSET(SRST_A_BUS_BIU, 5, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_H_BUS_BIU, 5, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_P_BUS_BIU, 5, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_A_SYSRAM, 5, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_H_SYSRAM, 5, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_A_DMAC0, 5, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_A_DMAC1, 5, 9),
+	RK3506_CRU_RESET_OFFSET(SRST_H_M0, 5, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_M0_JTAG, 5, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_H_CRYPTO, 5, 15),
+
+	/* CRU-->SOFTRST_CON06 */
+	RK3506_CRU_RESET_OFFSET(SRST_H_RNG, 6, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_P_BUS_GRF, 6, 1),
+	RK3506_CRU_RESET_OFFSET(SRST_P_TIMER0, 6, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_TIMER0_CH0, 6, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_TIMER0_CH1, 6, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_TIMER0_CH2, 6, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_TIMER0_CH3, 6, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_TIMER0_CH4, 6, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_TIMER0_CH5, 6, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_P_WDT0, 6, 9),
+	RK3506_CRU_RESET_OFFSET(SRST_T_WDT0, 6, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_P_WDT1, 6, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_T_WDT1, 6, 12),
+	RK3506_CRU_RESET_OFFSET(SRST_P_MAILBOX, 6, 13),
+	RK3506_CRU_RESET_OFFSET(SRST_P_INTMUX, 6, 14),
+	RK3506_CRU_RESET_OFFSET(SRST_P_SPINLOCK, 6, 15),
+
+	/* CRU-->SOFTRST_CON07 */
+	RK3506_CRU_RESET_OFFSET(SRST_P_DDRC, 7, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_H_DDRPHY, 7, 1),
+	RK3506_CRU_RESET_OFFSET(SRST_P_DDRMON, 7, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_DDRMON_OSC, 7, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_P_DDR_LPC, 7, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_H_USBOTG0, 7, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_USBOTG0_ADP, 7, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_H_USBOTG1, 7, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_USBOTG1_ADP, 7, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_P_USBPHY, 7, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_USBPHY_POR, 7, 12),
+	RK3506_CRU_RESET_OFFSET(SRST_USBPHY_OTG0, 7, 13),
+	RK3506_CRU_RESET_OFFSET(SRST_USBPHY_OTG1, 7, 14),
+
+	/* CRU-->SOFTRST_CON08 */
+	RK3506_CRU_RESET_OFFSET(SRST_A_DMA2DDR, 8, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_P_DMA2DDR, 8, 1),
+
+	/* CRU-->SOFTRST_CON09 */
+	RK3506_CRU_RESET_OFFSET(SRST_USBOTG0_UTMI, 9, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_USBOTG1_UTMI, 9, 1),
+
+	/* CRU-->SOFTRST_CON10 */
+	RK3506_CRU_RESET_OFFSET(SRST_A_DDRC_0, 10, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_A_DDRC_1, 10, 1),
+	RK3506_CRU_RESET_OFFSET(SRST_A_DDR_BIU, 10, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_DDRC, 10, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_DDRMON, 10, 4),
+
+	/* CRU-->SOFTRST_CON11 */
+	RK3506_CRU_RESET_OFFSET(SRST_H_LSPERI_BIU, 11, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_P_UART0, 11, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_P_UART1, 11, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_P_UART2, 11, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_P_UART3, 11, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_P_UART4, 11, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_UART0, 11, 9),
+	RK3506_CRU_RESET_OFFSET(SRST_UART1, 11, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_UART2, 11, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_UART3, 11, 12),
+	RK3506_CRU_RESET_OFFSET(SRST_UART4, 11, 13),
+	RK3506_CRU_RESET_OFFSET(SRST_P_I2C0, 11, 14),
+	RK3506_CRU_RESET_OFFSET(SRST_I2C0, 11, 15),
+
+	/* CRU-->SOFTRST_CON12 */
+	RK3506_CRU_RESET_OFFSET(SRST_P_I2C1, 12, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_I2C1, 12, 1),
+	RK3506_CRU_RESET_OFFSET(SRST_P_I2C2, 12, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_I2C2, 12, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_P_PWM1, 12, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_PWM1, 12, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_P_SPI0, 12, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_SPI0, 12, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_P_SPI1, 12, 12),
+	RK3506_CRU_RESET_OFFSET(SRST_SPI1, 12, 13),
+	RK3506_CRU_RESET_OFFSET(SRST_P_GPIO2, 12, 14),
+	RK3506_CRU_RESET_OFFSET(SRST_DB_GPIO2, 12, 15),
+
+	/* CRU-->SOFTRST_CON13 */
+	RK3506_CRU_RESET_OFFSET(SRST_P_GPIO3, 13, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_DB_GPIO3, 13, 1),
+	RK3506_CRU_RESET_OFFSET(SRST_P_GPIO4, 13, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_DB_GPIO4, 13, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_H_CAN0, 13, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_CAN0, 13, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_H_CAN1, 13, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_CAN1, 13, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_H_PDM, 13, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_M_PDM, 13, 9),
+	RK3506_CRU_RESET_OFFSET(SRST_PDM, 13, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_SPDIFTX, 13, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_H_SPDIFTX, 13, 12),
+	RK3506_CRU_RESET_OFFSET(SRST_H_SPDIFRX, 13, 13),
+	RK3506_CRU_RESET_OFFSET(SRST_SPDIFRX, 13, 14),
+	RK3506_CRU_RESET_OFFSET(SRST_M_SAI0, 13, 15),
+
+	/* CRU-->SOFTRST_CON14 */
+	RK3506_CRU_RESET_OFFSET(SRST_H_SAI0, 14, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_M_SAI1, 14, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_H_SAI1, 14, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_H_ASRC0, 14, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_ASRC0, 14, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_H_ASRC1, 14, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_ASRC1, 14, 8),
+
+	/* CRU-->SOFTRST_CON17 */
+	RK3506_CRU_RESET_OFFSET(SRST_H_HSPERI_BIU, 17, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_H_SDMMC, 17, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_H_FSPI, 17, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_S_FSPI, 17, 9),
+	RK3506_CRU_RESET_OFFSET(SRST_P_SPI2, 17, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_A_MAC0, 17, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_A_MAC1, 17, 12),
+
+	/* CRU-->SOFTRST_CON18 */
+	RK3506_CRU_RESET_OFFSET(SRST_M_SAI2, 18, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_H_SAI2, 18, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_H_SAI3, 18, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_M_SAI3, 18, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_H_SAI4, 18, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_M_SAI4, 18, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_H_DSM, 18, 12),
+	RK3506_CRU_RESET_OFFSET(SRST_M_DSM, 18, 13),
+	RK3506_CRU_RESET_OFFSET(SRST_P_AUDIO_ADC, 18, 14),
+	RK3506_CRU_RESET_OFFSET(SRST_M_AUDIO_ADC, 18, 15),
+
+	/* CRU-->SOFTRST_CON19 */
+	RK3506_CRU_RESET_OFFSET(SRST_P_SARADC, 19, 0),
+	RK3506_CRU_RESET_OFFSET(SRST_SARADC, 19, 1),
+	RK3506_CRU_RESET_OFFSET(SRST_SARADC_PHY, 19, 2),
+	RK3506_CRU_RESET_OFFSET(SRST_P_OTPC_NS, 19, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_SBPI_OTPC_NS, 19, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_USER_OTPC_NS, 19, 5),
+	RK3506_CRU_RESET_OFFSET(SRST_P_UART5, 19, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_UART5, 19, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_P_GPIO234_IOC, 19, 8),
+
+	/* CRU-->SOFTRST_CON21 */
+	RK3506_CRU_RESET_OFFSET(SRST_A_VIO_BIU, 21, 3),
+	RK3506_CRU_RESET_OFFSET(SRST_H_VIO_BIU, 21, 4),
+	RK3506_CRU_RESET_OFFSET(SRST_H_RGA, 21, 6),
+	RK3506_CRU_RESET_OFFSET(SRST_A_RGA, 21, 7),
+	RK3506_CRU_RESET_OFFSET(SRST_CORE_RGA, 21, 8),
+	RK3506_CRU_RESET_OFFSET(SRST_A_VOP, 21, 9),
+	RK3506_CRU_RESET_OFFSET(SRST_H_VOP, 21, 10),
+	RK3506_CRU_RESET_OFFSET(SRST_VOP, 21, 11),
+	RK3506_CRU_RESET_OFFSET(SRST_P_DPHY, 21, 12),
+	RK3506_CRU_RESET_OFFSET(SRST_P_DSI_HOST, 21, 13),
+	RK3506_CRU_RESET_OFFSET(SRST_P_TSADC, 21, 14),
+	RK3506_CRU_RESET_OFFSET(SRST_TSADC, 21, 15),
+
+	/* CRU-->SOFTRST_CON22 */
+	RK3506_CRU_RESET_OFFSET(SRST_P_GPIO1_IOC, 22, 1),
+};
+
+int rk3506_reset_bind_lut(struct udevice *pdev, u32 reg_offset, u32 reg_number)
+{
+	return rockchip_reset_bind_lut(pdev, rk3506_register_offset,
+				       reg_offset, reg_number);
+}
-- 
2.52.0


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

* [PATCH 05/10] pinctrl: rockchip: Use syscon_regmap_lookup_by_phandle()
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
                   ` (3 preceding siblings ...)
  2026-01-09  0:49 ` [PATCH 04/10] clk: rockchip: Add support for RK3506 Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 06/10] pinctrl: rockchip: Add support for RK3506 Jonas Karlman
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini
  Cc: Aaron Griffith, u-boot, Jonas Karlman, Quentin Schulz

Use syscon_regmap_lookup_by_phandle() to simplify the code.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
Reviewed-by: Kever Yang <kever.yang@rock-chips.com>
Reviewed-by: Quentin Schulz <quentin.schulz@cherry.de>
---
 .../pinctrl/rockchip/pinctrl-rockchip-core.c  | 39 ++++++-------------
 1 file changed, 12 insertions(+), 27 deletions(-)

diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c b/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
index 4de67aba1c34..273e356e7c02 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
@@ -10,6 +10,7 @@
 #include <syscon.h>
 #include <fdtdec.h>
 #include <linux/bitops.h>
+#include <linux/err.h>
 #include <linux/libfdt.h>
 
 #include "pinctrl-rockchip.h"
@@ -641,37 +642,21 @@ int rockchip_pinctrl_probe(struct udevice *dev)
 {
 	struct rockchip_pinctrl_priv *priv = dev_get_priv(dev);
 	struct rockchip_pin_ctrl *ctrl;
-	struct udevice *syscon;
-	struct regmap *regmap;
-	int ret = 0;
 
-	/* get rockchip grf syscon phandle */
-	ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "rockchip,grf",
-					   &syscon);
-	if (ret) {
-		debug("unable to find rockchip,grf syscon device (%d)\n", ret);
-		return ret;
+	priv->regmap_base =
+			syscon_regmap_lookup_by_phandle(dev, "rockchip,grf");
+	if (IS_ERR(priv->regmap_base)) {
+		debug("unable to find rockchip,grf regmap\n");
+		return PTR_ERR(priv->regmap_base);
 	}
 
-	/* get grf-reg base address */
-	regmap = syscon_get_regmap(syscon);
-	if (!regmap) {
-		debug("unable to find rockchip grf regmap\n");
-		return -ENODEV;
-	}
-	priv->regmap_base = regmap;
-
-	/* option: get pmu-reg base address */
-	ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "rockchip,pmu",
-					   &syscon);
-	if (!ret) {
-		/* get pmugrf-reg base address */
-		regmap = syscon_get_regmap(syscon);
-		if (!regmap) {
-			debug("unable to find rockchip pmu regmap\n");
-			return -ENODEV;
+	if (dev_read_bool(dev, "rockchip,pmu")) {
+		priv->regmap_pmu =
+			syscon_regmap_lookup_by_phandle(dev, "rockchip,pmu");
+		if (IS_ERR(priv->regmap_pmu)) {
+			debug("unable to find rockchip,pmu regmap\n");
+			return PTR_ERR(priv->regmap_pmu);
 		}
-		priv->regmap_pmu = regmap;
 	}
 
 	ctrl = rockchip_pinctrl_get_soc_data(dev);
-- 
2.52.0


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

* [PATCH 06/10] pinctrl: rockchip: Add support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
                   ` (4 preceding siblings ...)
  2026-01-09  0:49 ` [PATCH 05/10] pinctrl: rockchip: Use syscon_regmap_lookup_by_phandle() Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 07/10] rockchip: otp: " Jonas Karlman
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini
  Cc: Aaron Griffith, u-boot, Jonas Karlman, Ye Zhang

From: Ye Zhang <ye.zhang@rock-chips.com>

Add pinctrl driver for RK3506.

Imported from vendor U-Boot linux-6.1-stan-rkr6 tag with adjustments
to use regmap_update_bits().

Signed-off-by: Ye Zhang <ye.zhang@rock-chips.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/pinctrl/rockchip/Makefile             |   1 +
 drivers/pinctrl/rockchip/pinctrl-rk3506.c     | 462 ++++++++++++++++++
 .../pinctrl/rockchip/pinctrl-rockchip-core.c  |   9 +
 drivers/pinctrl/rockchip/pinctrl-rockchip.h   |   1 +
 4 files changed, 473 insertions(+)
 create mode 100644 drivers/pinctrl/rockchip/pinctrl-rk3506.c

diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile
index e17415e1ca68..0405df128dfa 100644
--- a/drivers/pinctrl/rockchip/Makefile
+++ b/drivers/pinctrl/rockchip/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += pinctrl-rk3308.o
 obj-$(CONFIG_ROCKCHIP_RK3328) += pinctrl-rk3328.o
 obj-$(CONFIG_ROCKCHIP_RK3368) += pinctrl-rk3368.o
 obj-$(CONFIG_ROCKCHIP_RK3399) += pinctrl-rk3399.o
+obj-$(CONFIG_ROCKCHIP_RK3506) += pinctrl-rk3506.o
 obj-$(CONFIG_ROCKCHIP_RK3528) += pinctrl-rk3528.o
 obj-$(CONFIG_ROCKCHIP_RK3568) += pinctrl-rk3568.o
 obj-$(CONFIG_ROCKCHIP_RK3576) += pinctrl-rk3576.o
diff --git a/drivers/pinctrl/rockchip/pinctrl-rk3506.c b/drivers/pinctrl/rockchip/pinctrl-rk3506.c
new file mode 100644
index 000000000000..969acb66f157
--- /dev/null
+++ b/drivers/pinctrl/rockchip/pinctrl-rk3506.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2024 Rockchip Electronics Co., Ltd.
+ */
+
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include "pinctrl-rockchip.h"
+#include <dt-bindings/pinctrl/rockchip.h>
+
+static int rk3506_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
+{
+	struct rockchip_pinctrl_priv *priv = bank->priv;
+	int iomux_num = (pin / 8);
+	struct regmap *regmap;
+	int reg, mask;
+	u8 bit;
+	u32 data, rmask;
+
+	if (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
+		regmap = priv->regmap_pmu;
+	else
+		regmap = priv->regmap_base;
+
+	if (bank->bank_num == 1)
+		regmap = priv->regmap_ioc1;
+	else if (bank->bank_num == 4)
+		return 0;
+
+	reg = bank->iomux[iomux_num].offset;
+	if ((pin % 8) >= 4)
+		reg += 0x4;
+	bit = (pin % 4) * 4;
+	mask = 0xf;
+
+	if (bank->recalced_mask & BIT(pin))
+		rockchip_get_recalced_mux(bank, pin, &reg, &bit, &mask);
+
+	data = (mask << (bit + 16));
+	rmask = data | (data >> 16);
+	data |= (mux & mask) << bit;
+
+	return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3506_DRV_BITS_PER_PIN		8
+#define RK3506_DRV_PINS_PER_REG		2
+#define RK3506_DRV_GPIO0_A_OFFSET	0x100
+#define RK3506_DRV_GPIO0_D_OFFSET	0x830
+#define RK3506_DRV_GPIO1_OFFSET		0x140
+#define RK3506_DRV_GPIO2_OFFSET		0x180
+#define RK3506_DRV_GPIO3_OFFSET		0x1c0
+#define RK3506_DRV_GPIO4_OFFSET		0x840
+
+static int rk3506_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+				       int pin_num, struct regmap **regmap,
+				       int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl_priv *priv = bank->priv;
+	int ret = 0;
+
+	switch (bank->bank_num) {
+	case 0:
+		*regmap = priv->regmap_pmu;
+		if (pin_num > 24) {
+			ret = -EINVAL;
+		} else if (pin_num < 24) {
+			*reg = RK3506_DRV_GPIO0_A_OFFSET;
+		} else {
+			*reg = RK3506_DRV_GPIO0_D_OFFSET;
+			*bit = 3;
+
+			return 0;
+		}
+		break;
+
+	case 1:
+		*regmap = priv->regmap_ioc1;
+		if (pin_num < 28)
+			*reg = RK3506_DRV_GPIO1_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 2:
+		*regmap = priv->regmap_base;
+		if (pin_num < 17)
+			*reg = RK3506_DRV_GPIO2_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 3:
+		*regmap = priv->regmap_base;
+		if (pin_num < 15)
+			*reg = RK3506_DRV_GPIO3_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 4:
+		*regmap = priv->regmap_base;
+		if (pin_num < 8 || pin_num > 11) {
+			ret = -EINVAL;
+		} else {
+			*reg = RK3506_DRV_GPIO4_OFFSET;
+			*bit = 10;
+
+			return 0;
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret) {
+		debug("unsupported bank_num %d pin_num %d\n", bank->bank_num, pin_num);
+		return ret;
+	}
+
+	*reg += ((pin_num / RK3506_DRV_PINS_PER_REG) * 4);
+	*bit = pin_num % RK3506_DRV_PINS_PER_REG;
+	*bit *= RK3506_DRV_BITS_PER_PIN;
+
+	return 0;
+}
+
+static int rk3506_set_drive(struct rockchip_pin_bank *bank,
+			    int pin_num, int strength)
+{
+	struct regmap *regmap;
+	int reg, ret, i;
+	u32 data, rmask;
+	u8 bit;
+	int rmask_bits = RK3506_DRV_BITS_PER_PIN;
+
+	ret = rk3506_calc_drv_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+	if (ret)
+		return ret;
+
+	for (i = 0, ret = 1; i < strength; i++)
+		ret = (ret << 1) | 1;
+
+	if ((bank->bank_num == 0 && pin_num == 24) || bank->bank_num == 4) {
+		rmask_bits = 2;
+		ret = strength;
+	}
+
+	/* enable the write to the equivalent lower bits */
+	data = ((1 << rmask_bits) - 1) << (bit + 16);
+	rmask = data | (data >> 16);
+	data |= (ret << bit);
+
+	return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3506_PULL_BITS_PER_PIN	2
+#define RK3506_PULL_PINS_PER_REG	8
+#define RK3506_PULL_GPIO0_A_OFFSET	0x200
+#define RK3506_PULL_GPIO0_D_OFFSET	0x830
+#define RK3506_PULL_GPIO1_OFFSET	0x210
+#define RK3506_PULL_GPIO2_OFFSET	0x220
+#define RK3506_PULL_GPIO3_OFFSET	0x230
+#define RK3506_PULL_GPIO4_OFFSET	0x840
+
+static int rk3506_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl_priv *priv = bank->priv;
+	int ret = 0;
+
+	switch (bank->bank_num) {
+	case 0:
+		*regmap = priv->regmap_pmu;
+		if (pin_num > 24) {
+			ret = -EINVAL;
+		} else if (pin_num < 24) {
+			*reg = RK3506_PULL_GPIO0_A_OFFSET;
+		} else {
+			*reg = RK3506_PULL_GPIO0_D_OFFSET;
+			*bit = 5;
+
+			return 0;
+		}
+		break;
+
+	case 1:
+		*regmap = priv->regmap_ioc1;
+		if (pin_num < 28)
+			*reg = RK3506_PULL_GPIO1_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 2:
+		*regmap = priv->regmap_base;
+		if (pin_num < 17)
+			*reg = RK3506_PULL_GPIO2_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 3:
+		*regmap = priv->regmap_base;
+		if (pin_num < 15)
+			*reg = RK3506_PULL_GPIO3_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 4:
+		*regmap = priv->regmap_base;
+		if (pin_num < 8 || pin_num > 11) {
+			ret = -EINVAL;
+		} else {
+			*reg = RK3506_PULL_GPIO4_OFFSET;
+			*bit = 13;
+
+			return 0;
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret) {
+		debug("unsupported bank_num %d pin_num %d\n", bank->bank_num, pin_num);
+		return ret;
+	}
+
+	*reg += ((pin_num / RK3506_PULL_PINS_PER_REG) * 4);
+	*bit = pin_num % RK3506_PULL_PINS_PER_REG;
+	*bit *= RK3506_PULL_BITS_PER_PIN;
+
+	return 0;
+}
+
+static int rk3506_set_pull(struct rockchip_pin_bank *bank,
+			   int pin_num, int pull)
+{
+	struct regmap *regmap;
+	int reg, ret;
+	u8 bit, type;
+	u32 data, rmask;
+
+	if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT)
+		return -EOPNOTSUPP;
+
+	ret = rk3506_calc_pull_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+	if (ret)
+		return ret;
+	type = bank->pull_type[pin_num / 8];
+
+	if ((bank->bank_num == 0 && pin_num == 24) || bank->bank_num == 4)
+		type = 1;
+
+	ret = rockchip_translate_pull_value(type, pull);
+	if (ret < 0) {
+		debug("unsupported pull setting %d\n", pull);
+		return ret;
+	}
+
+	/* enable the write to the equivalent lower bits */
+	data = ((1 << RK3506_PULL_BITS_PER_PIN) - 1) << (bit + 16);
+	rmask = data | (data >> 16);
+	data |= (ret << bit);
+
+	return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define RK3506_SMT_BITS_PER_PIN		1
+#define RK3506_SMT_PINS_PER_REG		8
+#define RK3506_SMT_GPIO0_A_OFFSET	0x400
+#define RK3506_SMT_GPIO0_D_OFFSET	0x830
+#define RK3506_SMT_GPIO1_OFFSET		0x410
+#define RK3506_SMT_GPIO2_OFFSET		0x420
+#define RK3506_SMT_GPIO3_OFFSET		0x430
+#define RK3506_SMT_GPIO4_OFFSET		0x840
+
+static int rk3506_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+					   int pin_num,
+					   struct regmap **regmap,
+					   int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl_priv *priv = bank->priv;
+	int ret = 0;
+
+	switch (bank->bank_num) {
+	case 0:
+		*regmap = priv->regmap_pmu;
+		if (pin_num > 24) {
+			ret = -EINVAL;
+		} else if (pin_num < 24) {
+			*reg = RK3506_SMT_GPIO0_A_OFFSET;
+		} else {
+			*reg = RK3506_SMT_GPIO0_D_OFFSET;
+			*bit = 9;
+
+			return 0;
+		}
+		break;
+
+	case 1:
+		*regmap = priv->regmap_ioc1;
+		if (pin_num < 28)
+			*reg = RK3506_SMT_GPIO1_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 2:
+		*regmap = priv->regmap_base;
+		if (pin_num < 17)
+			*reg = RK3506_SMT_GPIO2_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 3:
+		*regmap = priv->regmap_base;
+		if (pin_num < 15)
+			*reg = RK3506_SMT_GPIO3_OFFSET;
+		else
+			ret = -EINVAL;
+		break;
+
+	case 4:
+		*regmap = priv->regmap_base;
+		if (pin_num < 8 || pin_num > 11) {
+			ret = -EINVAL;
+		} else {
+			*reg = RK3506_SMT_GPIO4_OFFSET;
+			*bit = 8;
+
+			return 0;
+		}
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret) {
+		debug("unsupported bank_num %d pin_num %d\n", bank->bank_num, pin_num);
+		return ret;
+	}
+
+	*reg += ((pin_num / RK3506_SMT_PINS_PER_REG) * 4);
+	*bit = pin_num % RK3506_SMT_PINS_PER_REG;
+	*bit *= RK3506_SMT_BITS_PER_PIN;
+
+	return 0;
+}
+
+static int rk3506_set_schmitt(struct rockchip_pin_bank *bank,
+			      int pin_num, int enable)
+{
+	struct regmap *regmap;
+	int reg, ret;
+	u32 data, rmask;
+	u8 bit;
+
+	ret = rk3506_calc_schmitt_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+	if (ret)
+		return ret;
+
+	/* enable the write to the equivalent lower bits */
+	data = ((1 << RK3506_SMT_BITS_PER_PIN) - 1) << (bit + 16);
+	rmask = data | (data >> 16);
+	data |= (enable << bit);
+
+	if ((bank->bank_num == 0 && pin_num == 24) || bank->bank_num == 4) {
+		data = 0x3 << (bit + 16);
+		rmask = data | (data >> 16);
+		data |= ((enable ? 0x3 : 0) << bit);
+	}
+
+	return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+static struct rockchip_mux_recalced_data rk3506_mux_recalced_data[] = {
+	{
+		.num = 0,
+		.pin = 24,
+		.reg = 0x830,
+		.bit = 0,
+		.mask = 0x3
+	},
+};
+
+static struct rockchip_pin_bank rk3506_pin_banks[] = {
+	PIN_BANK_IOMUX_FLAGS_OFFSET(0, 32, "gpio0",
+				    IOMUX_WIDTH_4BIT | IOMUX_SOURCE_PMU,
+				    IOMUX_WIDTH_4BIT | IOMUX_SOURCE_PMU,
+				    IOMUX_WIDTH_4BIT | IOMUX_SOURCE_PMU,
+				    IOMUX_8WIDTH_2BIT | IOMUX_SOURCE_PMU,
+				    0x0, 0x8, 0x10, 0x830),
+	PIN_BANK_IOMUX_FLAGS_OFFSET(1, 32, "gpio1",
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    0x20, 0x28, 0x30, 0x38),
+	PIN_BANK_IOMUX_FLAGS_OFFSET(2, 32, "gpio2",
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    0x40, 0x48, 0x50, 0x58),
+	PIN_BANK_IOMUX_FLAGS_OFFSET(3, 32, "gpio3",
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    0x60, 0x68, 0x70, 0x78),
+	PIN_BANK_IOMUX_FLAGS_OFFSET(4, 32, "gpio4",
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    IOMUX_WIDTH_4BIT,
+				    0x80, 0x88, 0x90, 0x98),
+};
+
+static const struct rockchip_pin_ctrl rk3506_pin_ctrl = {
+	.pin_banks		= rk3506_pin_banks,
+	.nr_banks		= ARRAY_SIZE(rk3506_pin_banks),
+	.iomux_recalced		= rk3506_mux_recalced_data,
+	.niomux_recalced	= ARRAY_SIZE(rk3506_mux_recalced_data),
+	.set_mux		= rk3506_set_mux,
+	.set_pull		= rk3506_set_pull,
+	.set_drive		= rk3506_set_drive,
+	.set_schmitt		= rk3506_set_schmitt,
+};
+
+static const struct udevice_id rk3506_pinctrl_ids[] = {
+	{
+		.compatible = "rockchip,rk3506-pinctrl",
+		.data = (ulong)&rk3506_pin_ctrl
+	},
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_rk3506_pinctrl) = {
+	.name		= "rockchip_rk3506_pinctrl",
+	.id		= UCLASS_PINCTRL,
+	.of_match	= rk3506_pinctrl_ids,
+	.priv_auto	= sizeof(struct rockchip_pinctrl_priv),
+	.ops		= &rockchip_pinctrl_ops,
+#if CONFIG_IS_ENABLED(OF_REAL)
+	.bind		= dm_scan_fdt_dev,
+#endif
+	.probe		= rockchip_pinctrl_probe,
+};
diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c b/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
index 273e356e7c02..957dcb520591 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
+++ b/drivers/pinctrl/rockchip/pinctrl-rockchip-core.c
@@ -659,6 +659,15 @@ int rockchip_pinctrl_probe(struct udevice *dev)
 		}
 	}
 
+	if (dev_read_bool(dev, "rockchip,ioc1")) {
+		priv->regmap_ioc1 =
+			syscon_regmap_lookup_by_phandle(dev, "rockchip,ioc1");
+		if (IS_ERR(priv->regmap_ioc1)) {
+			debug("unable to find rockchip,ioc1 regmap\n");
+			return PTR_ERR(priv->regmap_ioc1);
+		}
+	}
+
 	ctrl = rockchip_pinctrl_get_soc_data(dev);
 	if (!ctrl) {
 		debug("driver data not available\n");
diff --git a/drivers/pinctrl/rockchip/pinctrl-rockchip.h b/drivers/pinctrl/rockchip/pinctrl-rockchip.h
index ba684baed24d..568e6024b78c 100644
--- a/drivers/pinctrl/rockchip/pinctrl-rockchip.h
+++ b/drivers/pinctrl/rockchip/pinctrl-rockchip.h
@@ -528,6 +528,7 @@ struct rockchip_pinctrl_priv {
 	struct rockchip_pin_ctrl	*ctrl;
 	struct regmap			*regmap_base;
 	struct regmap			*regmap_pmu;
+	struct regmap			*regmap_ioc1;
 };
 
 extern const struct pinctrl_ops rockchip_pinctrl_ops;
-- 
2.52.0


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

* [PATCH 07/10] rockchip: otp: Add support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
                   ` (5 preceding siblings ...)
  2026-01-09  0:49 ` [PATCH 06/10] pinctrl: rockchip: Add support for RK3506 Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 08/10] phy: rockchip-inno-usb2: " Jonas Karlman
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini
  Cc: Aaron Griffith, u-boot, Jonas Karlman

Add support for the OTP controller in RK3506. The OTPC is similar to the
OTPC in RK3568 and can use the same ops for reading OTP data.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/misc/rockchip-otp.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/misc/rockchip-otp.c b/drivers/misc/rockchip-otp.c
index 46820425a84d..64b6238981b8 100644
--- a/drivers/misc/rockchip-otp.c
+++ b/drivers/misc/rockchip-otp.c
@@ -390,6 +390,10 @@ static const struct udevice_id rockchip_otp_ids[] = {
 		.compatible = "rockchip,rk3308-otp",
 		.data = (ulong)&px30_data,
 	},
+	{
+		.compatible = "rockchip,rk3506-otp",
+		.data = (ulong)&rk3568_data,
+	},
 	{
 		.compatible = "rockchip,rk3528-otp",
 		.data = (ulong)&rk3568_data,
-- 
2.52.0


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

* [PATCH 08/10] phy: rockchip-inno-usb2: Add support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
                   ` (6 preceding siblings ...)
  2026-01-09  0:49 ` [PATCH 07/10] rockchip: otp: " Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 09/10] net: dwc_eth_qos_rockchip: " Jonas Karlman
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini
  Cc: Aaron Griffith, u-boot, Jonas Karlman

Add support for the two USB2.0 PHYs use in the RK3506 SoC.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 4ea6600ce7f5..f80b2789333d 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -421,6 +421,22 @@ static const struct rockchip_usb2phy_cfg rk3399_usb2phy_cfgs[] = {
 	{ /* sentinel */ }
 };
 
+static const struct rockchip_usb2phy_cfg rk3506_phy_cfgs[] = {
+	{
+		.reg		= 0xff2b0000,
+		.clkout_ctl_phy = { 0x041c, 7, 2, 0, 0x27 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x0060, 1, 0, 2, 1 },
+			},
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x0070, 1, 0, 2, 1 },
+			}
+		},
+	},
+	{ /* sentinel */ }
+};
+
 static const struct rockchip_usb2phy_cfg rk3528_phy_cfgs[] = {
 	{
 		.reg		= 0xffdf0000,
@@ -540,6 +556,10 @@ static const struct udevice_id rockchip_usb2phy_ids[] = {
 		.compatible = "rockchip,rk3399-usb2phy",
 		.data = (ulong)&rk3399_usb2phy_cfgs,
 	},
+	{
+		.compatible = "rockchip,rk3506-usb2phy",
+		.data = (ulong)&rk3506_phy_cfgs,
+	},
 	{
 		.compatible = "rockchip,rk3528-usb2phy",
 		.data = (ulong)&rk3528_phy_cfgs,
-- 
2.52.0


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

* [PATCH 09/10] net: dwc_eth_qos_rockchip: Add support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
                   ` (7 preceding siblings ...)
  2026-01-09  0:49 ` [PATCH 08/10] phy: rockchip-inno-usb2: " Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  0:49 ` [PATCH 10/10] arch: arm: rockchip: Add initial " Jonas Karlman
  2026-01-09 20:23 ` [PATCH 00/10] " Aaron Griffith
  10 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini,
	Joe Hershberger, Ramon Fried, Jerome Forissier
  Cc: Aaron Griffith, u-boot, Jonas Karlman

Rockchip RK3506 has two Ethernet controllers based on Synopsys DWC
Ethernet QoS IP.

Add initial support for the RK3506 GMAC variant.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 drivers/net/dwc_eth_qos.c          |  4 ++
 drivers/net/dwc_eth_qos_rockchip.c | 86 ++++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+)

diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c
index 551ee0ea6a02..d320646b57f5 100644
--- a/drivers/net/dwc_eth_qos.c
+++ b/drivers/net/dwc_eth_qos.c
@@ -1615,6 +1615,10 @@ static const struct udevice_id eqos_ids[] = {
 	},
 #endif
 #if IS_ENABLED(CONFIG_DWC_ETH_QOS_ROCKCHIP)
+	{
+		.compatible = "rockchip,rk3506-gmac",
+		.data = (ulong)&eqos_rockchip_config
+	},
 	{
 		.compatible = "rockchip,rk3528-gmac",
 		.data = (ulong)&eqos_rockchip_config
diff --git a/drivers/net/dwc_eth_qos_rockchip.c b/drivers/net/dwc_eth_qos_rockchip.c
index d646d3ebac87..4ccefda0ef5e 100644
--- a/drivers/net/dwc_eth_qos_rockchip.c
+++ b/drivers/net/dwc_eth_qos_rockchip.c
@@ -50,6 +50,80 @@ struct rockchip_platform_data {
 	(((tx) ? soc##_GMAC_TXCLK_DLY_ENABLE : soc##_GMAC_TXCLK_DLY_DISABLE) | \
 	 ((rx) ? soc##_GMAC_RXCLK_DLY_ENABLE : soc##_GMAC_RXCLK_DLY_DISABLE))
 
+#define RK3506_GRF_SOC_CON8		0x0020
+#define RK3506_GRF_SOC_CON11		0x002c
+
+#define RK3506_GMAC_RMII_MODE		GRF_BIT(1)
+
+#define RK3506_GMAC_CLK_RMII_DIV2	GRF_BIT(3)
+#define RK3506_GMAC_CLK_RMII_DIV20	GRF_CLR_BIT(3)
+
+#define RK3506_GMAC_CLK_SELECT_CRU	GRF_CLR_BIT(5)
+#define RK3506_GMAC_CLK_SELECT_IO	GRF_BIT(5)
+
+#define RK3506_GMAC_CLK_RMII_GATE	GRF_BIT(2)
+#define RK3506_GMAC_CLK_RMII_NOGATE	GRF_CLR_BIT(2)
+
+static int rk3506_set_to_rgmii(struct udevice *dev,
+			       int tx_delay, int rx_delay)
+{
+	return -EINVAL;
+}
+
+static int rk3506_set_to_rmii(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct rockchip_platform_data *data = pdata->priv_pdata;
+	u32 reg;
+
+	reg = data->id == 1 ? RK3506_GRF_SOC_CON11 :
+			      RK3506_GRF_SOC_CON8;
+	regmap_write(data->grf, reg, RK3506_GMAC_RMII_MODE);
+
+	return 0;
+}
+
+static int rk3506_set_gmac_speed(struct udevice *dev)
+{
+	struct eqos_priv *eqos = dev_get_priv(dev);
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct rockchip_platform_data *data = pdata->priv_pdata;
+	u32 val, reg;
+
+	switch (eqos->phy->speed) {
+	case SPEED_10:
+		val = RK3506_GMAC_CLK_RMII_DIV20;
+		break;
+	case SPEED_100:
+		val = RK3506_GMAC_CLK_RMII_DIV2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	reg = data->id == 1 ? RK3506_GRF_SOC_CON11 :
+			      RK3506_GRF_SOC_CON8;
+	regmap_write(data->grf, reg, val);
+
+	return 0;
+}
+
+static void rk3506_set_clock_selection(struct udevice *dev, bool enable)
+{
+	struct eth_pdata *pdata = dev_get_plat(dev);
+	struct rockchip_platform_data *data = pdata->priv_pdata;
+	u32 val, reg;
+
+	val = data->clock_input ? RK3506_GMAC_CLK_SELECT_IO :
+				  RK3506_GMAC_CLK_SELECT_CRU;
+	val |= enable ? RK3506_GMAC_CLK_RMII_NOGATE :
+			RK3506_GMAC_CLK_RMII_GATE;
+
+	reg = data->id == 1 ? RK3506_GRF_SOC_CON11 :
+			      RK3506_GRF_SOC_CON8;
+	regmap_write(data->grf, reg, val);
+}
+
 #define RK3528_VO_GRF_GMAC_CON		0x0018
 #define RK3528_VPU_GRF_GMAC_CON5	0x0018
 #define RK3528_VPU_GRF_GMAC_CON6	0x001c
@@ -534,6 +608,18 @@ static void rk3588_set_clock_selection(struct udevice *dev, bool enable)
 }
 
 static const struct rk_gmac_ops rk_gmac_ops[] = {
+	{
+		.compatible = "rockchip,rk3506-gmac",
+		.set_to_rgmii = rk3506_set_to_rgmii,
+		.set_to_rmii = rk3506_set_to_rmii,
+		.set_gmac_speed = rk3506_set_gmac_speed,
+		.set_clock_selection = rk3506_set_clock_selection,
+		.regs = {
+			0xff4c8000, /* gmac0 */
+			0xff4d0000, /* gmac1 */
+			0x0, /* sentinel */
+		},
+	},
 	{
 		.compatible = "rockchip,rk3528-gmac",
 		.set_to_rgmii = rk3528_set_to_rgmii,
-- 
2.52.0


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

* [PATCH 10/10] arch: arm: rockchip: Add initial support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
                   ` (8 preceding siblings ...)
  2026-01-09  0:49 ` [PATCH 09/10] net: dwc_eth_qos_rockchip: " Jonas Karlman
@ 2026-01-09  0:49 ` Jonas Karlman
  2026-01-09  9:37   ` Mattijs Korpershoek
  2026-01-09 20:23 ` [PATCH 00/10] " Aaron Griffith
  10 siblings, 1 reply; 14+ messages in thread
From: Jonas Karlman @ 2026-01-09  0:49 UTC (permalink / raw)
  To: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini,
	Lukasz Majewski, Mattijs Korpershoek, Marek Vasut
  Cc: Aaron Griffith, u-boot, Jonas Karlman

Rockchip RK3506 is a ARM-based SoC with tri-core Cortex-A7.

Add initial arch support for the RK3506 SoC.

Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
 arch/arm/include/asm/arch-rk3506/boot0.h      |   9 ++
 arch/arm/include/asm/arch-rk3506/gpio.h       |   9 ++
 arch/arm/mach-rockchip/Kconfig                |  43 ++++++
 arch/arm/mach-rockchip/Makefile               |   1 +
 arch/arm/mach-rockchip/rk3506/Kconfig         |  15 ++
 arch/arm/mach-rockchip/rk3506/Makefile        |   5 +
 arch/arm/mach-rockchip/rk3506/clk_rk3506.c    |  16 +++
 arch/arm/mach-rockchip/rk3506/rk3506.c        | 130 ++++++++++++++++++
 arch/arm/mach-rockchip/rk3506/syscon_rk3506.c |  19 +++
 drivers/usb/gadget/Kconfig                    |   1 +
 include/configs/rk3506_common.h               |  38 +++++
 11 files changed, 286 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-rk3506/boot0.h
 create mode 100644 arch/arm/include/asm/arch-rk3506/gpio.h
 create mode 100644 arch/arm/mach-rockchip/rk3506/Kconfig
 create mode 100644 arch/arm/mach-rockchip/rk3506/Makefile
 create mode 100644 arch/arm/mach-rockchip/rk3506/clk_rk3506.c
 create mode 100644 arch/arm/mach-rockchip/rk3506/rk3506.c
 create mode 100644 arch/arm/mach-rockchip/rk3506/syscon_rk3506.c
 create mode 100644 include/configs/rk3506_common.h

diff --git a/arch/arm/include/asm/arch-rk3506/boot0.h b/arch/arm/include/asm/arch-rk3506/boot0.h
new file mode 100644
index 000000000000..8ae46f25a87a
--- /dev/null
+++ b/arch/arm/include/asm/arch-rk3506/boot0.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright Contributors to the U-Boot project. */
+
+#ifndef __ASM_ARCH_BOOT0_H__
+#define __ASM_ARCH_BOOT0_H__
+
+#include <asm/arch-rockchip/boot0.h>
+
+#endif
diff --git a/arch/arm/include/asm/arch-rk3506/gpio.h b/arch/arm/include/asm/arch-rk3506/gpio.h
new file mode 100644
index 000000000000..5516e649b80b
--- /dev/null
+++ b/arch/arm/include/asm/arch-rk3506/gpio.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright Contributors to the U-Boot project. */
+
+#ifndef __ASM_ARCH_GPIO_H__
+#define __ASM_ARCH_GPIO_H__
+
+#include <asm/arch-rockchip/gpio.h>
+
+#endif
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
index 92bb4aa62f11..7d5372899225 100644
--- a/arch/arm/mach-rockchip/Kconfig
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -317,6 +317,48 @@ config ROCKCHIP_RK3399
 	  and video codec support. Peripherals include Gigabit Ethernet,
 	  USB2 host and OTG, SDIO, I2S, UARTs, SPI, I2C and PWMs.
 
+config ROCKCHIP_RK3506
+	bool "Support Rockchip RK3506"
+	select CPU_V7A
+	select SUPPORT_SPL
+	select SPL
+	select CLK
+	select PINCTRL
+	select RAM
+	select REGMAP
+	select SYSCON
+	select BOARD_LATE_INIT
+	select DM_REGULATOR_FIXED
+	select DM_RESET
+	imply BOOTSTD_FULL
+	imply DM_RNG
+	imply ENV_RELOC_GD_ENV_ADDR
+	imply FIT
+	imply LEGACY_IMAGE_FORMAT
+	imply MISC
+	imply MISC_INIT_R
+	imply OF_LIBFDT_OVERLAY
+	imply OF_LIVE
+	imply RNG_ROCKCHIP
+	imply ROCKCHIP_COMMON_BOARD
+	imply ROCKCHIP_COMMON_STACK_ADDR
+	imply ROCKCHIP_EXTERNAL_TPL
+	imply ROCKCHIP_OTP
+	imply SPL_CLK
+	imply SPL_DM_SEQ_ALIAS
+	imply SPL_FIT_SIGNATURE
+	imply SPL_LOAD_FIT
+	imply SPL_OF_CONTROL
+	imply SPL_PINCTRL
+	imply SPL_RAM
+	imply SPL_REGMAP
+	imply SPL_SERIAL
+	imply SPL_SYSCON
+	imply SYS_ARCH_TIMER
+	imply SYSRESET
+	help
+	  The Rockchip RK3506 is a ARM-based SoC with a tri-core Cortex-A7.
+
 config ROCKCHIP_RK3528
 	bool "Support Rockchip RK3528"
 	select ARM64
@@ -745,6 +787,7 @@ source "arch/arm/mach-rockchip/rk3308/Kconfig"
 source "arch/arm/mach-rockchip/rk3328/Kconfig"
 source "arch/arm/mach-rockchip/rk3368/Kconfig"
 source "arch/arm/mach-rockchip/rk3399/Kconfig"
+source "arch/arm/mach-rockchip/rk3506/Kconfig"
 source "arch/arm/mach-rockchip/rk3528/Kconfig"
 source "arch/arm/mach-rockchip/rk3568/Kconfig"
 source "arch/arm/mach-rockchip/rk3576/Kconfig"
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index 06fb527b21a0..d3bc0689f893 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_ROCKCHIP_RK3308) += rk3308/
 obj-$(CONFIG_ROCKCHIP_RK3328) += rk3328/
 obj-$(CONFIG_ROCKCHIP_RK3368) += rk3368/
 obj-$(CONFIG_ROCKCHIP_RK3399) += rk3399/
+obj-$(CONFIG_ROCKCHIP_RK3506) += rk3506/
 obj-$(CONFIG_ROCKCHIP_RK3528) += rk3528/
 obj-$(CONFIG_ROCKCHIP_RK3568) += rk3568/
 obj-$(CONFIG_ROCKCHIP_RK3576) += rk3576/
diff --git a/arch/arm/mach-rockchip/rk3506/Kconfig b/arch/arm/mach-rockchip/rk3506/Kconfig
new file mode 100644
index 000000000000..92f187458c66
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3506/Kconfig
@@ -0,0 +1,15 @@
+if ROCKCHIP_RK3506
+
+config ROCKCHIP_BOOT_MODE_REG
+	default 0xff910200
+
+config ROCKCHIP_STIMER_BASE
+	default 0xff980000
+
+config SYS_SOC
+	default "rk3506"
+
+config SYS_CONFIG_NAME
+	default "rk3506_common"
+
+endif
diff --git a/arch/arm/mach-rockchip/rk3506/Makefile b/arch/arm/mach-rockchip/rk3506/Makefile
new file mode 100644
index 000000000000..a1760bd0f0a3
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3506/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+obj-y += rk3506.o
+obj-y += clk_rk3506.o
+obj-y += syscon_rk3506.o
diff --git a/arch/arm/mach-rockchip/rk3506/clk_rk3506.c b/arch/arm/mach-rockchip/rk3506/clk_rk3506.c
new file mode 100644
index 000000000000..ee40e108886c
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3506/clk_rk3506.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright Contributors to the U-Boot project.
+
+#include <dm.h>
+#include <asm/arch-rockchip/cru_rk3506.h>
+
+int rockchip_get_clk(struct udevice **devp)
+{
+	return uclass_get_device_by_driver(UCLASS_CLK,
+				DM_DRIVER_GET(rockchip_rk3506_cru), devp);
+}
+
+void *rockchip_get_cru(void)
+{
+	return RK3506_CRU_BASE;
+}
diff --git a/arch/arm/mach-rockchip/rk3506/rk3506.c b/arch/arm/mach-rockchip/rk3506/rk3506.c
new file mode 100644
index 000000000000..51b1513d6505
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3506/rk3506.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright Contributors to the U-Boot project.
+
+#define LOG_CATEGORY LOGC_ARCH
+
+#include <dm.h>
+#include <misc.h>
+#include <asm/arch-rockchip/bootrom.h>
+#include <asm/arch-rockchip/hardware.h>
+
+#define SGRF_BASE			0xff210000
+
+#define FIREWALL_DDR_BASE		0xff5f0000
+#define FW_DDR_MST1_REG			0x24
+
+const char * const boot_devices[BROM_LAST_BOOTSOURCE + 1] = {
+	[BROM_BOOTSOURCE_EMMC] = "/soc/mmc@ff480000",
+	[BROM_BOOTSOURCE_SD] = "/soc/mmc@ff480000",
+};
+
+void board_debug_uart_init(void)
+{
+}
+
+u32 read_brom_bootsource_id(void)
+{
+	u32 bootsource_id = readl(BROM_BOOTSOURCE_ID_ADDR);
+
+	/* Re-map the raw value read from reg to an existing BROM_BOOTSOURCE
+	 * enum value to avoid having to create a larger boot_devices table.
+	 */
+	if (bootsource_id == 0x81)
+		return BROM_BOOTSOURCE_USB;
+	else if (bootsource_id > BROM_LAST_BOOTSOURCE)
+		log_debug("Unknown bootsource %x\n", bootsource_id);
+
+	return bootsource_id;
+}
+
+int arch_cpu_init(void)
+{
+	u32 val;
+
+	if (!IS_ENABLED(CONFIG_SPL_BUILD))
+		return 0;
+
+	/* Select non-secure OTPC */
+	rk_clrreg(SGRF_BASE + 0x100, BIT(1));
+
+	/* Set the sdmmc/emmc to access ddr memory */
+	val = readl(FIREWALL_DDR_BASE + FW_DDR_MST1_REG);
+	writel(val & 0xffff00ff, FIREWALL_DDR_BASE + FW_DDR_MST1_REG);
+
+	/* Set the fspi to access ddr memory */
+	val = readl(FIREWALL_DDR_BASE + FW_DDR_MST1_REG);
+	writel(val & 0xff00ffff, FIREWALL_DDR_BASE + FW_DDR_MST1_REG);
+
+	/* Set the mac0/mac1 to access ddr memory */
+	val = readl(FIREWALL_DDR_BASE + FW_DDR_MST1_REG);
+	writel(val & 0x00ffffff, FIREWALL_DDR_BASE + FW_DDR_MST1_REG);
+
+	return 0;
+}
+
+#define HP_TIMER_BASE			CONFIG_ROCKCHIP_STIMER_BASE
+#define HP_CTRL_REG			0x04
+#define TIMER_EN			BIT(0)
+#define HP_LOAD_COUNT0_REG		0x14
+#define HP_LOAD_COUNT1_REG		0x18
+
+void rockchip_stimer_init(void)
+{
+	u32 reg;
+
+	if (!IS_ENABLED(CONFIG_XPL_BUILD))
+		return;
+
+	reg = readl(HP_TIMER_BASE + HP_CTRL_REG);
+	if (reg & TIMER_EN)
+		return;
+
+	asm volatile("mcr p15, 0, %0, c14, c0, 0" : : "r" (CONFIG_COUNTER_FREQUENCY));
+	writel(0xffffffff, HP_TIMER_BASE + HP_LOAD_COUNT0_REG);
+	writel(0xffffffff, HP_TIMER_BASE + HP_LOAD_COUNT1_REG);
+	writel((TIMER_EN << 16) | TIMER_EN, HP_TIMER_BASE + HP_CTRL_REG);
+}
+
+#define RK3506_OTP_CPU_CODE_OFFSET		0x02
+#define RK3506_OTP_SPECIFICATION_OFFSET		0x08
+
+int checkboard(void)
+{
+	u8 cpu_code[2], specification;
+	struct udevice *dev;
+	char suffix[2];
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_ROCKCHIP_OTP) || !CONFIG_IS_ENABLED(MISC))
+		return 0;
+
+	ret = uclass_get_device_by_driver(UCLASS_MISC,
+					  DM_DRIVER_GET(rockchip_otp), &dev);
+	if (ret) {
+		log_debug("Could not find otp device, ret=%d\n", ret);
+		return 0;
+	}
+
+	/* cpu-code: SoC model, e.g. 0x35 0x06 */
+	ret = misc_read(dev, RK3506_OTP_CPU_CODE_OFFSET, cpu_code, 2);
+	if (ret < 0) {
+		log_debug("Could not read cpu-code, ret=%d\n", ret);
+		return 0;
+	}
+
+	/* specification: SoC variant, e.g. 0xA for RK3506J */
+	ret = misc_read(dev, RK3506_OTP_SPECIFICATION_OFFSET, &specification, 1);
+	if (ret < 0) {
+		log_debug("Could not read specification, ret=%d\n", ret);
+		return 0;
+	}
+	specification &= 0x1f;
+
+	/* for RK3506J i.e. '@' + 0xA = 'J' */
+	suffix[0] = specification > 1 ? '@' + specification : '\0';
+	suffix[1] = '\0';
+
+	printf("SoC:   RK%02x%02x%s\n", cpu_code[0], cpu_code[1], suffix);
+
+	return 0;
+}
diff --git a/arch/arm/mach-rockchip/rk3506/syscon_rk3506.c b/arch/arm/mach-rockchip/rk3506/syscon_rk3506.c
new file mode 100644
index 000000000000..2548b0fa2d3a
--- /dev/null
+++ b/arch/arm/mach-rockchip/rk3506/syscon_rk3506.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright Contributors to the U-Boot project.
+
+#include <dm.h>
+#include <asm/arch-rockchip/clock.h>
+
+static const struct udevice_id rk3506_syscon_ids[] = {
+	{ .compatible = "rockchip,rk3506-grf", .data = ROCKCHIP_SYSCON_GRF },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_rk3506_syscon) = {
+	.name = "rockchip_rk3506_syscon",
+	.id = UCLASS_SYSCON,
+	.of_match = rk3506_syscon_ids,
+#if CONFIG_IS_ENABLED(OF_REAL)
+	.bind = dm_scan_fdt_dev,
+#endif
+};
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 7e08aeab9043..82a784d2fb00 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -89,6 +89,7 @@ config USB_GADGET_PRODUCT_NUM
 	default 0x350b if ROCKCHIP_RK3588
 	default 0x350c if ROCKCHIP_RK3528
 	default 0x350e if ROCKCHIP_RK3576
+	default 0x350f if ROCKCHIP_RK3506
 	default 0x4ee0 if ARCH_QCOM
 	default 0x0
 	help
diff --git a/include/configs/rk3506_common.h b/include/configs/rk3506_common.h
new file mode 100644
index 000000000000..5e4ef67289f0
--- /dev/null
+++ b/include/configs/rk3506_common.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright Contributors to the U-Boot project. */
+
+#ifndef __CONFIG_RK3506_COMMON_H
+#define __CONFIG_RK3506_COMMON_H
+
+#define CFG_CPUID_OFFSET		0xa
+
+#include "rockchip-common.h"
+
+#define CFG_IRAM_BASE			0xfff80000
+
+#define CFG_SYS_SDRAM_BASE		0
+#define SDRAM_MAX_SIZE			0xc0000000
+
+#ifndef ROCKCHIP_DEVICE_SETTINGS
+#define ROCKCHIP_DEVICE_SETTINGS
+#endif
+
+#define ENV_MEM_LAYOUT_SETTINGS			\
+	"scriptaddr=0x00500000\0"		\
+	"script_offset_f=0xffe000\0"		\
+	"script_size_f=0x2000\0"		\
+	"pxefile_addr_r=0x00600000\0"		\
+	"kernel_addr_r=0x02080000\0"		\
+	"kernel_comp_addr_r=0x08000000\0"	\
+	"fdt_addr_r=0x01e00000\0"		\
+	"fdtoverlay_addr_r=0x01f00000\0"	\
+	"ramdisk_addr_r=0x06000000\0"		\
+	"kernel_comp_size=0x2000000\0"
+
+#define CFG_EXTRA_ENV_SETTINGS			\
+	"fdtfile=" CONFIG_DEFAULT_FDT_FILE "\0"	\
+	ENV_MEM_LAYOUT_SETTINGS			\
+	ROCKCHIP_DEVICE_SETTINGS		\
+	"boot_targets=" BOOT_TARGETS "\0"
+
+#endif /* __CONFIG_RK3506_COMMON_H */
-- 
2.52.0


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

* Re: [PATCH 10/10] arch: arm: rockchip: Add initial support for RK3506
  2026-01-09  0:49 ` [PATCH 10/10] arch: arm: rockchip: Add initial " Jonas Karlman
@ 2026-01-09  9:37   ` Mattijs Korpershoek
  0 siblings, 0 replies; 14+ messages in thread
From: Mattijs Korpershoek @ 2026-01-09  9:37 UTC (permalink / raw)
  To: Jonas Karlman, Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini,
	Lukasz Majewski, Mattijs Korpershoek, Marek Vasut
  Cc: Aaron Griffith, u-boot, Jonas Karlman

Hi Jonas,

Thank you for the patch.

On Fri, Jan 09, 2026 at 00:49, Jonas Karlman <jonas@kwiboo.se> wrote:

> Rockchip RK3506 is a ARM-based SoC with tri-core Cortex-A7.
>
> Add initial arch support for the RK3506 SoC.
>
> Signed-off-by: Jonas Karlman <jonas@kwiboo.se>

Acked-by: Mattijs Korpershoek <mkorpershoek@kernel.org> # drivers/usb/gadget

> ---

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

* Re: [PATCH 00/10] rockchip: Add initial support for RK3506
  2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
                   ` (9 preceding siblings ...)
  2026-01-09  0:49 ` [PATCH 10/10] arch: arm: rockchip: Add initial " Jonas Karlman
@ 2026-01-09 20:23 ` Aaron Griffith
  2026-01-12 20:16   ` Jonas Karlman
  10 siblings, 1 reply; 14+ messages in thread
From: Aaron Griffith @ 2026-01-09 20:23 UTC (permalink / raw)
  To: Jonas Karlman; +Cc: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini, u-boot

Hello Jonas,

On Fri, Jan 09, 2026 at 12:49:41AM +0000, Jonas Karlman wrote:
> This series add initial support for the Rockchip RK3506 SoC.
>
> [..]
#
> With this series, board DTs and defconfigs it should be possible to boot
> into U-Boot proper (without OP-TEE) and still have support for MMC,
> Ethernet, OTP, RNG, LEDs, buttons and USB gadget/host.

I have a Luckfox Lyra Pi W board (with RK3506B) and a Luckfox Lyra
(with RK3506G). I've been using the earlier version of this patchset
for about two weeks to successfully boot Linux on both of these
boards. I'm excited to see this support heading upstream.

Using these patches specifically, I can verify that the RNG and LEDs
work, that it is able to read data from the MMC and USB storage, and
that it successfully loads and runs a Linux kernel.

I was not able to get the Ethernet port to work on my Lyra Pi board,
but that uses your device tree information that is not part of these
patches (and may be incomplete). The specific failure U-boot reports is:

    ethernet@ff4d0000 Waiting for PHY auto negotiation to complete.........
    TIMEOUT !

So, for what it's worth,

Tested-by: Aaron Griffith <aargri@gmail.com>

Thanks,
 -Aaron

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

* Re: [PATCH 00/10] rockchip: Add initial support for RK3506
  2026-01-09 20:23 ` [PATCH 00/10] " Aaron Griffith
@ 2026-01-12 20:16   ` Jonas Karlman
  0 siblings, 0 replies; 14+ messages in thread
From: Jonas Karlman @ 2026-01-12 20:16 UTC (permalink / raw)
  To: Aaron Griffith; +Cc: Kever Yang, Simon Glass, Philipp Tomsich, Tom Rini, u-boot

Hi Aaron,

On 1/9/2026 9:23 PM, Aaron Griffith wrote:
> Hello Jonas,
> 
> On Fri, Jan 09, 2026 at 12:49:41AM +0000, Jonas Karlman wrote:
>> This series add initial support for the Rockchip RK3506 SoC.
>>
>> [..]
> #
>> With this series, board DTs and defconfigs it should be possible to boot
>> into U-Boot proper (without OP-TEE) and still have support for MMC,
>> Ethernet, OTP, RNG, LEDs, buttons and USB gadget/host.
> 
> I have a Luckfox Lyra Pi W board (with RK3506B) and a Luckfox Lyra
> (with RK3506G). I've been using the earlier version of this patchset
> for about two weeks to successfully boot Linux on both of these
> boards. I'm excited to see this support heading upstream.
> 
> Using these patches specifically, I can verify that the RNG and LEDs
> work, that it is able to read data from the MMC and USB storage, and
> that it successfully loads and runs a Linux kernel.
> 
> I was not able to get the Ethernet port to work on my Lyra Pi board,
> but that uses your device tree information that is not part of these
> patches (and may be incomplete). The specific failure U-boot reports is:
> 
>     ethernet@ff4d0000 Waiting for PHY auto negotiation to complete.........
>     TIMEOUT !

Thanks for testing these patches!

I did some testing on my Luckfox Lyra Pi W (eMMC) board and also found
similar issues that you report.

These issues mostly seem to be related to firewall for USB OTG1 and
MAC1 (ff4d0000) or DT issues related to Ethernet clocks and pinctrl.

The firewall values before we update them in arch_cpu_init():

  => md.l ff5f0020 5
  ff5f0020: ffffffff ffffffff ffffffff ffffff00  ................
  ff5f0030: ffffffff                             ....

The following firewall values seem to help fix MAC0/1 and USB OTG1:

  => md.l ff5f0020 5
  ff5f0020: ffffffff f00000ff fffffff0 fff0ff00  ................
  ff5f0030: ffffffff                             ....

We could possible set all these to 0 to allow all masters to access
memory, will send a v2 that configures the firewall like above.

Adding something like following to the ethernet-phy nodes seem to solve
the Ethernet issues in U-Boot on my Lyra Pi:

  assigned-clocks = <&cru CLK_MAC_OUT>;
  assigned-clock-rates = <50000000>;
  clocks = <&cru CLK_MAC_OUT>;
  pinctrl-names = "default";
  pinctrl-0 = <&eth_clk0_25m_out>;
    or
  pinctrl-0 = <&eth_clk1_25m_out>;

Vendor DT for Lyra Pi seem to set CLK_MAC_OUT to 25 MHz, but this does
not seem to work in U-Boot and I can not see anything wrong in the clk
driver, so maybe it is meant to be set to 50 MHz.

Will update my rk3506 branch to include these changes and later also
send out a v2 of this series including the firewall fix.

Regards,
Jonas

> 
> So, for what it's worth,
> 
> Tested-by: Aaron Griffith <aargri@gmail.com>
> 
> Thanks,
>  -Aaron


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

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

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-09  0:49 [PATCH 00/10] rockchip: Add initial support for RK3506 Jonas Karlman
2026-01-09  0:49 ` [PATCH 01/10] rockchip: mkimage: Add " Jonas Karlman
2026-01-09  0:49 ` [PATCH 02/10] ram: rockchip: Add basic " Jonas Karlman
2026-01-09  0:49 ` [PATCH 03/10] dt-bindings: clock: rockchip: Add RK3506 clock and reset unit Jonas Karlman
2026-01-09  0:49 ` [PATCH 04/10] clk: rockchip: Add support for RK3506 Jonas Karlman
2026-01-09  0:49 ` [PATCH 05/10] pinctrl: rockchip: Use syscon_regmap_lookup_by_phandle() Jonas Karlman
2026-01-09  0:49 ` [PATCH 06/10] pinctrl: rockchip: Add support for RK3506 Jonas Karlman
2026-01-09  0:49 ` [PATCH 07/10] rockchip: otp: " Jonas Karlman
2026-01-09  0:49 ` [PATCH 08/10] phy: rockchip-inno-usb2: " Jonas Karlman
2026-01-09  0:49 ` [PATCH 09/10] net: dwc_eth_qos_rockchip: " Jonas Karlman
2026-01-09  0:49 ` [PATCH 10/10] arch: arm: rockchip: Add initial " Jonas Karlman
2026-01-09  9:37   ` Mattijs Korpershoek
2026-01-09 20:23 ` [PATCH 00/10] " Aaron Griffith
2026-01-12 20:16   ` Jonas Karlman

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