[PATCH v2 06/12] clk: rockchip: Add support for RK3506

Kever Yang kever.yang at rock-chips.com
Mon Mar 9 15:38:48 CET 2026


On 2026/2/1 07:38, Jonas Karlman wrote:
> From: Finley Xiao <finley.xiao at 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 at rock-chips.com>
> Signed-off-by: Jonas Karlman <jonas at kwiboo.se>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>

Thanks,
- Kever
> ---
> Changes in v2:
> - Drop use of struct rk3506_cru and of_to_plat() ops
> - Define LOG_CATEGORY and use log_debug()
> - Extract rk3506_clk_init_xpl() from bind() ops
> ---
>   arch/arm/include/asm/arch-rockchip/clock.h    |   10 +
>   .../include/asm/arch-rockchip/cru_rk3506.h    |  181 +++
>   drivers/clk/rockchip/Makefile                 |    1 +
>   drivers/clk/rockchip/clk_rk3506.c             | 1166 +++++++++++++++++
>   drivers/reset/Makefile                        |    2 +-
>   drivers/reset/rst-rk3506.c                    |  222 ++++
>   6 files changed, 1581 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..2f79e5eaf09d
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-rockchip/cru_rk3506.h
> @@ -0,0 +1,181 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
> + * Author: Finley Xiao <finley.xiao at 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 {
> +	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;
> +};
> +
> +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_CRU_BASE			0xff9a0000
> +#define RK3506_MODE_CON			0x0280
> +#define RK3506_CLKSEL_CON(x)		(RK3506_CRU_BASE + 0x0300 + (x) * 0x4)
> +#define RK3506_SOFTRST_CON0		0x0a00
> +#define RK3506_GLB_SRST_FST		0x0c08
> +#define RK3506_GLB_SRST_SND		0x0c0c
> +#define RK3506_PLL_CON(x)		(0x10000 + (x) * 0x4)
> +#define RK3506_SCRU_BASE		0xff9a8000
> +#define RK3506_PMU_CRU_BASE		0xff9b0000
> +#define RK3506_PMU_CLKSEL_CON(x)	(RK3506_PMU_CRU_BASE + 0x0300 + (x) * 0x4)
> +
> +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..38066c5c3e3c
> --- /dev/null
> +++ b/drivers/clk/rockchip/clk_rk3506.c
> @@ -0,0 +1,1166 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
> + * Author: Finley Xiao <finley.xiao at rock-chips.com>
> + */
> +
> +#define LOG_CATEGORY UCLASS_CLK
> +
> +#include <clk-uclass.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>
> +
> +#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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	con = readl(RK3506_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;
> +	u32 con, div, old_div, sel;
> +	ulong old_rate, prate;
> +
> +	rate = rockchip_get_cpu_settings(rk3506_cpu_rates, new_rate);
> +	if (!rate) {
> +		log_debug("unsupported cpu rate %lu\n", new_rate);
> +		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(RK3506_CLKSEL_CON(15), ACLK_CORE_DIV_MASK,
> +			     FIELD_PREP(ACLK_CORE_DIV_MASK, rate->aclk_div));
> +		rk_clrsetreg(RK3506_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(RK3506_CLKSEL_CON(15));
> +	old_div = FIELD_GET(CLK_CORE_SRC_DIV_MASK, con);
> +	if (DIV_TO_RATE(prate, old_div) > new_rate) {
> +		rk_clrsetreg(RK3506_CLKSEL_CON(15), CLK_CORE_SRC_DIV_MASK,
> +			     FIELD_PREP(CLK_CORE_SRC_DIV_MASK, div - 1));
> +		rk_clrsetreg(RK3506_CLKSEL_CON(15), CLK_CORE_SRC_SEL_MASK,
> +			     FIELD_PREP(CLK_CORE_SRC_SEL_MASK, sel));
> +	} else {
> +		rk_clrsetreg(RK3506_CLKSEL_CON(15), CLK_CORE_SRC_SEL_MASK,
> +			     FIELD_PREP(CLK_CORE_SRC_SEL_MASK, sel));
> +		rk_clrsetreg(RK3506_CLKSEL_CON(15), CLK_CORE_SRC_DIV_MASK,
> +			     FIELD_PREP(CLK_CORE_SRC_DIV_MASK, div - 1));
> +	}
> +
> +	if (new_rate < old_rate) {
> +		rk_clrsetreg(RK3506_CLKSEL_CON(15), ACLK_CORE_DIV_MASK,
> +			     FIELD_PREP(ACLK_CORE_DIV_MASK, rate->aclk_div));
> +		rk_clrsetreg(RK3506_CLKSEL_CON(16), PCLK_CORE_DIV_MASK,
> +			     FIELD_PREP(PCLK_CORE_DIV_MASK, rate->pclk_div));
> +	}
> +
> +	return rk3506_armclk_get_rate(priv);
> +}
> +
> +static ulong rk3506_pll_div_get_rate(struct rk3506_clk_priv *priv, ulong clk_id)
> +{
> +	u32 con, div;
> +	ulong prate;
> +
> +	switch (clk_id) {
> +	case CLK_GPLL_DIV:
> +		con = readl(RK3506_CLKSEL_CON(0));
> +		div = FIELD_GET(CLK_GPLL_DIV_MASK, con);
> +		prate = priv->gpll_hz;
> +		break;
> +	case CLK_GPLL_DIV_100M:
> +		con = readl(RK3506_CLKSEL_CON(0));
> +		div = FIELD_GET(CLK_GPLL_DIV_100M_MASK, con);
> +		prate = priv->gpll_div_hz;
> +		break;
> +	case CLK_V0PLL_DIV:
> +		con = readl(RK3506_CLKSEL_CON(1));
> +		div = FIELD_GET(CLK_V0PLL_DIV_MASK, con);
> +		prate = priv->v0pll_hz;
> +		break;
> +	case CLK_V1PLL_DIV:
> +		con = readl(RK3506_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)
> +{
> +	u32 div;
> +
> +	switch (clk_id) {
> +	case CLK_GPLL_DIV:
> +		div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +		assert(div - 1 <= 15);
> +		rk_clrsetreg(RK3506_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(RK3506_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(RK3506_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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	switch (clk_id) {
> +	case ACLK_BUS_ROOT:
> +		con = readl(RK3506_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(RK3506_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(RK3506_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)
> +{
> +	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(RK3506_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(RK3506_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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	switch (clk_id) {
> +	case ACLK_HSPERI_ROOT:
> +		con = readl(RK3506_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(RK3506_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)
> +{
> +	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(RK3506_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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	con = readl(RK3506_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)
> +{
> +	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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	con = readl(RK3506_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)
> +{
> +	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(RK3506_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)
> +{
> +	u32 con, div;
> +
> +	con = readl(RK3506_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)
> +{
> +	u32 div;
> +
> +	switch (clk_id) {
> +	case CLK_TSADC_TSEN:
> +		div = DIV_ROUND_UP(OSC_HZ, rate);
> +		assert(div - 1 <= 7);
> +		rk_clrsetreg(RK3506_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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	switch (clk_id) {
> +	case CLK_I2C0:
> +		con = readl(RK3506_CLKSEL_CON(32));
> +		sel = FIELD_GET(CLK_I2C0_SEL_MASK, con);
> +		div = FIELD_GET(CLK_I2C0_DIV_MASK, con);
> +	case CLK_I2C1:
> +		con = readl(RK3506_CLKSEL_CON(32));
> +		sel = FIELD_GET(CLK_I2C1_SEL_MASK, con);
> +		div = FIELD_GET(CLK_I2C1_DIV_MASK, con);
> +	case CLK_I2C2:
> +		con = readl(RK3506_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)
> +{
> +	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(RK3506_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(RK3506_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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	switch (clk_id) {
> +	case CLK_PWM0:
> +		con = readl(RK3506_PMU_CLKSEL_CON(0));
> +		div = FIELD_GET(CLK_PWM0_DIV_MASK, con);
> +		prate = priv->gpll_div_100mhz;
> +		break;
> +	case CLK_PWM1:
> +		con = readl(RK3506_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)
> +{
> +	u32 div, sel;
> +
> +	switch (clk_id) {
> +	case CLK_PWM0:
> +		div = DIV_ROUND_UP(priv->gpll_div_100mhz, rate);
> +		assert(div - 1 <= 15);
> +		rk_clrsetreg(RK3506_PMU_CLKSEL_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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	switch (clk_id) {
> +	case CLK_SPI0:
> +		con = readl(RK3506_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(RK3506_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)
> +{
> +	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(RK3506_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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	con = readl(RK3506_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)
> +{
> +	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(RK3506_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)
> +{
> +	u32 con, div, sel;
> +	ulong prate;
> +
> +	con = readl(RK3506_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)
> +{
> +	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(RK3506_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)
> +{
> +	u32 con, div;
> +
> +	switch (clk_id) {
> +	case CLK_MAC0:
> +	case CLK_MAC1:
> +		con = readl(RK3506_CLKSEL_CON(50));
> +		div = FIELD_GET(CLK_MAC_DIV_MASK, con);
> +		break;
> +	case CLK_MAC_OUT:
> +		con = readl(RK3506_PMU_CLKSEL_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)
> +{
> +	u32 div;
> +
> +	switch (clk_id) {
> +	case CLK_MAC0:
> +	case CLK_MAC1:
> +		div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +		rk_clrsetreg(RK3506_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(RK3506_PMU_CLKSEL_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) {
> +		log_debug("gpll=%lu, v0pll=%lu, v1pll=%lu\n",
> +			  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:
> +		log_debug("unsupported clk id=%ld\n", clk->id);
> +		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) {
> +		log_debug("gpll=%lu, v0pll=%lu, v1pll=%lu\n",
> +			  priv->gpll_hz, priv->v0pll_hz, priv->v1pll_hz);
> +		return -ENOENT;
> +	}
> +
> +	switch (clk->id) {
> +	case ARMCLK:
> +		ret = rk3506_armclk_set_rate(priv, 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:
> +		log_debug("unsupported clk id=%ld rate=%ld\n", clk->id, rate);
> +		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)
> +{
> +	static void * const cru_base = (void *)RK3506_CRU_BASE;
> +
> +	if (!priv->gpll_hz) {
> +		priv->gpll_hz = rockchip_pll_get_rate(&rk3506_pll_clks[GPLL],
> +						      cru_base, GPLL);
> +		priv->gpll_hz = roundup(priv->gpll_hz, 1000);
> +	}
> +	if (!priv->v0pll_hz) {
> +		priv->v0pll_hz = rockchip_pll_get_rate(&rk3506_pll_clks[V0PLL],
> +						       cru_base, V0PLL);
> +		priv->v0pll_hz = roundup(priv->v0pll_hz, 1000);
> +	}
> +	if (!priv->v1pll_hz) {
> +		priv->v1pll_hz = rockchip_pll_get_rate(&rk3506_pll_clks[V1PLL],
> +						       cru_base, 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);
> +	}
> +}
> +
> +static void rk3506_clk_init_xpl(void)
> +{
> +	/* 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(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));
> +}
> +
> +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)
> +		log_debug("clk_set_defaults failed: ret=%d\n", ret);
> +
> +	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))
> +		rk3506_clk_init_xpl();
> +
> +	/* 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) {
> +		log_debug("Warning: No sysreset driver: ret=%d\n", ret);
> +	} else {
> +		priv = malloc(sizeof(struct sysreset_reg));
> +		priv->glb_srst_fst_value = RK3506_GLB_SRST_FST;
> +		priv->glb_srst_snd_value = RK3506_GLB_SRST_SND;
> +		dev_set_priv(sys_child, priv);
> +	}
> +
> +	if (!CONFIG_IS_ENABLED(RESET_ROCKCHIP))
> +		return 0;
> +
> +	ret = rk3506_reset_bind_lut(dev, RK3506_SOFTRST_CON0, 23);
> +	if (ret)
> +		log_debug("Warning: software reset driver bind failed\n");
> +
> +	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),
> +	.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 at 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);
> +}


More information about the U-Boot mailing list