diff mbox series

[v1,05/18] clk: rockchip: Add rk3588 clk support

Message ID 20230130145749.177515-6-jagan@edgeble.ai
State Accepted
Delegated to: Kever Yang
Headers show
Series arm: Add Rockchip RK3588 support | expand

Commit Message

Jagan Teki Jan. 30, 2023, 2:57 p.m. UTC
Add clock driver support for Rockchip RK3588 SoC.

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
Signed-off-by: Jagan Teki <jagan@edgeble.ai>
---
 drivers/clk/rockchip/Makefile     |    1 +
 drivers/clk/rockchip/clk_rk3588.c | 1996 +++++++++++++++++++++++++++++
 2 files changed, 1997 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk_rk3588.c

Comments

Jagan Teki Jan. 30, 2023, 3:18 p.m. UTC | #1
On Mon, 30 Jan 2023 at 20:28, Jagan Teki <jagan@edgeble.ai> wrote:
>
> Add clock driver support for Rockchip RK3588 SoC.
>
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> Signed-off-by: Jagan Teki <jagan@edgeble.ai>
> ---

+ clk maintainers
Sean Anderson Feb. 12, 2023, 5:54 p.m. UTC | #2
On 1/30/23 10:18, Jagan Teki wrote:
> On Mon, 30 Jan 2023 at 20:28, Jagan Teki <jagan@edgeble.ai> wrote:
>>
>> Add clock driver support for Rockchip RK3588 SoC.
>>
>> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
>> Signed-off-by: Jagan Teki <jagan@edgeble.ai>
>> ---
> 
> + clk maintainers

Is this patch or [1] the clock driver? Are you working together or are
these independent approaches?

--Sean

[1] https://lore.kernel.org/u-boot/20230124191430.88712-1-fatorangecat@189.cn/
Jagan Teki Feb. 12, 2023, 5:59 p.m. UTC | #3
On Sun, 12 Feb 2023 at 23:24, Sean Anderson <seanga2@gmail.com> wrote:
>
> On 1/30/23 10:18, Jagan Teki wrote:
> > On Mon, 30 Jan 2023 at 20:28, Jagan Teki <jagan@edgeble.ai> wrote:
> >>
> >> Add clock driver support for Rockchip RK3588 SoC.
> >>
> >> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> >> Signed-off-by: Jagan Teki <jagan@edgeble.ai>
> >> ---
> >
> > + clk maintainers
>
> Is this patch or [1] the clock driver? Are you working together or are
> these independent approaches?

I have asked the sender about the test status, but no response
thereafter (added him on TO as well). But this one has been tested on
hardware.

Jagan.
Kever Yang Feb. 16, 2023, 8:43 a.m. UTC | #4
On 2023/1/30 22:57, Jagan Teki wrote:
> Add clock driver support for Rockchip RK3588 SoC.
>
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> Signed-off-by: Jagan Teki <jagan@edgeble.ai>
Reviewed-by: Kever Yang <kever.yang@rock-chips.com>

Thanks,
- Kever
> ---
>   drivers/clk/rockchip/Makefile     |    1 +
>   drivers/clk/rockchip/clk_rk3588.c | 1996 +++++++++++++++++++++++++++++
>   2 files changed, 1997 insertions(+)
>   create mode 100644 drivers/clk/rockchip/clk_rk3588.c
>
> diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
> index f719f4e379..9e379cc2e3 100644
> --- a/drivers/clk/rockchip/Makefile
> +++ b/drivers/clk/rockchip/Makefile
> @@ -16,5 +16,6 @@ obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
>   obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o
>   obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
>   obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o
> +obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o
>   obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o
>   obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o
> diff --git a/drivers/clk/rockchip/clk_rk3588.c b/drivers/clk/rockchip/clk_rk3588.c
> new file mode 100644
> index 0000000000..b87b023bd7
> --- /dev/null
> +++ b/drivers/clk/rockchip/clk_rk3588.c
> @@ -0,0 +1,1996 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Fuzhou Rockchip Electronics Co., Ltd
> + * Author: Elaine Zhang <zhangqing@rock-chips.com>
> + */
> +
> +#include <common.h>
> +#include <bitfield.h>
> +#include <clk-uclass.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <syscon.h>
> +#include <asm/arch-rockchip/cru_rk3588.h>
> +#include <asm/arch-rockchip/clock.h>
> +#include <asm/arch-rockchip/hardware.h>
> +#include <asm/io.h>
> +#include <dm/device-internal.h>
> +#include <dm/lists.h>
> +#include <dt-bindings/clock/rockchip,rk3588-cru.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define DIV_TO_RATE(input_rate, div)	((input_rate) / ((div) + 1))
> +
> +static struct rockchip_pll_rate_table rk3588_pll_rates[] = {
> +	/* _mhz, _p, _m, _s, _k */
> +	RK3588_PLL_RATE(1500000000, 2, 250, 1, 0),
> +	RK3588_PLL_RATE(1200000000, 2, 200, 1, 0),
> +	RK3588_PLL_RATE(1188000000, 2, 198, 1, 0),
> +	RK3588_PLL_RATE(1100000000, 3, 550, 2, 0),
> +	RK3588_PLL_RATE(1008000000, 2, 336, 2, 0),
> +	RK3588_PLL_RATE(1000000000, 3, 500, 2, 0),
> +	RK3588_PLL_RATE(900000000, 2, 300, 2, 0),
> +	RK3588_PLL_RATE(850000000, 3, 425, 2, 0),
> +	RK3588_PLL_RATE(816000000, 2, 272, 2, 0),
> +	RK3588_PLL_RATE(786432000, 2, 262, 2, 9437),
> +	RK3588_PLL_RATE(786000000, 1, 131, 2, 0),
> +	RK3588_PLL_RATE(722534400, 8, 963, 2, 24850),
> +	RK3588_PLL_RATE(600000000, 2, 200, 2, 0),
> +	RK3588_PLL_RATE(594000000, 2, 198, 2, 0),
> +	RK3588_PLL_RATE(200000000, 3, 400, 4, 0),
> +	RK3588_PLL_RATE(100000000, 3, 400, 5, 0),
> +	{ /* sentinel */ },
> +};
> +
> +static struct rockchip_pll_clock rk3588_pll_clks[] = {
> +	[B0PLL] = PLL(pll_rk3588, PLL_B0PLL, RK3588_B0_PLL_CON(0),
> +		      RK3588_B0_PLL_MODE_CON, 0, 15, 0,
> +		      rk3588_pll_rates),
> +	[B1PLL] = PLL(pll_rk3588, PLL_B1PLL, RK3588_B1_PLL_CON(8),
> +		      RK3588_B1_PLL_MODE_CON, 0, 15, 0,
> +		      rk3588_pll_rates),
> +	[LPLL] = PLL(pll_rk3588, PLL_LPLL, RK3588_LPLL_CON(16),
> +		     RK3588_LPLL_MODE_CON, 0, 15, 0, rk3588_pll_rates),
> +	[V0PLL] = PLL(pll_rk3588, PLL_V0PLL, RK3588_PLL_CON(88),
> +		      RK3588_MODE_CON0, 4, 15, 0, rk3588_pll_rates),
> +	[AUPLL] = PLL(pll_rk3588, PLL_AUPLL, RK3588_PLL_CON(96),
> +		      RK3588_MODE_CON0, 6, 15, 0, rk3588_pll_rates),
> +	[CPLL] = PLL(pll_rk3588, PLL_CPLL, RK3588_PLL_CON(104),
> +		     RK3588_MODE_CON0, 8, 15, 0, rk3588_pll_rates),
> +	[GPLL] = PLL(pll_rk3588, PLL_GPLL, RK3588_PLL_CON(112),
> +		     RK3588_MODE_CON0, 2, 15, 0, rk3588_pll_rates),
> +	[NPLL] = PLL(pll_rk3588, PLL_NPLL, RK3588_PLL_CON(120),
> +		     RK3588_MODE_CON0, 0, 15, 0, rk3588_pll_rates),
> +	[PPLL] = PLL(pll_rk3588, PLL_PPLL, RK3588_PMU_PLL_CON(128),
> +		     RK3588_MODE_CON0, 10, 15, 0, rk3588_pll_rates),
> +};
> +
> +#ifndef CONFIG_SPL_BUILD
> +/*
> + *
> + * rational_best_approximation(31415, 10000,
> + *		(1 << 8) - 1, (1 << 5) - 1, &n, &d);
> + *
> + * you may look at given_numerator as a fixed point number,
> + * with the fractional part size described in given_denominator.
> + *
> + * for theoretical background, see:
> + * http://en.wikipedia.org/wiki/Continued_fraction
> + */
> +static void rational_best_approximation(unsigned long given_numerator,
> +					unsigned long given_denominator,
> +					unsigned long max_numerator,
> +					unsigned long max_denominator,
> +					unsigned long *best_numerator,
> +					unsigned long *best_denominator)
> +{
> +	unsigned long n, d, n0, d0, n1, d1;
> +
> +	n = given_numerator;
> +	d = given_denominator;
> +	n0 = 0;
> +	d1 = 0;
> +	n1 = 1;
> +	d0 = 1;
> +	for (;;) {
> +		unsigned long t, a;
> +
> +		if (n1 > max_numerator || d1 > max_denominator) {
> +			n1 = n0;
> +			d1 = d0;
> +			break;
> +		}
> +		if (d == 0)
> +			break;
> +		t = d;
> +		a = n / d;
> +		d = n % d;
> +		n = t;
> +		t = n0 + a * n1;
> +		n0 = n1;
> +		n1 = t;
> +		t = d0 + a * d1;
> +		d0 = d1;
> +		d1 = t;
> +	}
> +	*best_numerator = n1;
> +	*best_denominator = d1;
> +}
> +#endif
> +
> +static ulong rk3588_center_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 con, sel, rate;
> +
> +	switch (clk_id) {
> +	case ACLK_CENTER_ROOT:
> +		con = readl(&cru->clksel_con[165]);
> +		sel = (con & ACLK_CENTER_ROOT_SEL_MASK) >>
> +		      ACLK_CENTER_ROOT_SEL_SHIFT;
> +		if (sel == ACLK_CENTER_ROOT_SEL_700M)
> +			rate = 702 * MHz;
> +		else if (sel == ACLK_CENTER_ROOT_SEL_400M)
> +			rate = 396 * MHz;
> +		else if (sel == ACLK_CENTER_ROOT_SEL_200M)
> +			rate = 200 * MHz;
> +		else
> +			rate = OSC_HZ;
> +		break;
> +	case ACLK_CENTER_LOW_ROOT:
> +		con = readl(&cru->clksel_con[165]);
> +		sel = (con & ACLK_CENTER_LOW_ROOT_SEL_MASK) >>
> +		      ACLK_CENTER_LOW_ROOT_SEL_SHIFT;
> +		if (sel == ACLK_CENTER_LOW_ROOT_SEL_500M)
> +			rate = 500 * MHz;
> +		else if (sel == ACLK_CENTER_LOW_ROOT_SEL_250M)
> +			rate = 250 * MHz;
> +		else if (sel == ACLK_CENTER_LOW_ROOT_SEL_100M)
> +			rate = 100 * MHz;
> +		else
> +			rate = OSC_HZ;
> +		break;
> +	case HCLK_CENTER_ROOT:
> +		con = readl(&cru->clksel_con[165]);
> +		sel = (con & HCLK_CENTER_ROOT_SEL_MASK) >>
> +		      HCLK_CENTER_ROOT_SEL_SHIFT;
> +		if (sel == HCLK_CENTER_ROOT_SEL_400M)
> +			rate = 396 * MHz;
> +		else if (sel == HCLK_CENTER_ROOT_SEL_200M)
> +			rate = 200 * MHz;
> +		else if (sel == HCLK_CENTER_ROOT_SEL_100M)
> +			rate = 100 * MHz;
> +		else
> +			rate = OSC_HZ;
> +		break;
> +	case PCLK_CENTER_ROOT:
> +		con = readl(&cru->clksel_con[165]);
> +		sel = (con & PCLK_CENTER_ROOT_SEL_MASK) >>
> +		      PCLK_CENTER_ROOT_SEL_SHIFT;
> +		if (sel == PCLK_CENTER_ROOT_SEL_200M)
> +			rate = 200 * MHz;
> +		else if (sel == PCLK_CENTER_ROOT_SEL_100M)
> +			rate = 100 * MHz;
> +		else if (sel == PCLK_CENTER_ROOT_SEL_50M)
> +			rate = 50 * MHz;
> +		else
> +			rate = OSC_HZ;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rate;
> +}
> +
> +static ulong rk3588_center_set_clk(struct rk3588_clk_priv *priv,
> +				   ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int src_clk;
> +
> +	switch (clk_id) {
> +	case ACLK_CENTER_ROOT:
> +		if (rate >= 700 * MHz)
> +			src_clk = ACLK_CENTER_ROOT_SEL_700M;
> +		else if (rate >= 396 * MHz)
> +			src_clk = ACLK_CENTER_ROOT_SEL_400M;
> +		else if (rate >= 200 * MHz)
> +			src_clk = ACLK_CENTER_ROOT_SEL_200M;
> +		else
> +			src_clk = ACLK_CENTER_ROOT_SEL_24M;
> +		rk_clrsetreg(&cru->clksel_con[165],
> +			     ACLK_CENTER_ROOT_SEL_MASK,
> +			     src_clk << ACLK_CENTER_ROOT_SEL_SHIFT);
> +		break;
> +	case ACLK_CENTER_LOW_ROOT:
> +		if (rate >= 500 * MHz)
> +			src_clk = ACLK_CENTER_LOW_ROOT_SEL_500M;
> +		else if (rate >= 250 * MHz)
> +			src_clk = ACLK_CENTER_LOW_ROOT_SEL_250M;
> +		else if (rate >= 99 * MHz)
> +			src_clk = ACLK_CENTER_LOW_ROOT_SEL_100M;
> +		else
> +			src_clk = ACLK_CENTER_LOW_ROOT_SEL_24M;
> +		rk_clrsetreg(&cru->clksel_con[165],
> +			     ACLK_CENTER_LOW_ROOT_SEL_MASK,
> +			     src_clk << ACLK_CENTER_LOW_ROOT_SEL_SHIFT);
> +		break;
> +	case HCLK_CENTER_ROOT:
> +		if (rate >= 396 * MHz)
> +			src_clk = HCLK_CENTER_ROOT_SEL_400M;
> +		else if (rate >= 198 * MHz)
> +			src_clk = HCLK_CENTER_ROOT_SEL_200M;
> +		else if (rate >= 99 * MHz)
> +			src_clk = HCLK_CENTER_ROOT_SEL_100M;
> +		else
> +			src_clk = HCLK_CENTER_ROOT_SEL_24M;
> +		rk_clrsetreg(&cru->clksel_con[165],
> +			     HCLK_CENTER_ROOT_SEL_MASK,
> +			     src_clk << HCLK_CENTER_ROOT_SEL_SHIFT);
> +		break;
> +	case PCLK_CENTER_ROOT:
> +		if (rate >= 198 * MHz)
> +			src_clk = PCLK_CENTER_ROOT_SEL_200M;
> +		else if (rate >= 99 * MHz)
> +			src_clk = PCLK_CENTER_ROOT_SEL_100M;
> +		else if (rate >= 50 * MHz)
> +			src_clk = PCLK_CENTER_ROOT_SEL_50M;
> +		else
> +			src_clk = PCLK_CENTER_ROOT_SEL_24M;
> +		rk_clrsetreg(&cru->clksel_con[165],
> +			     PCLK_CENTER_ROOT_SEL_MASK,
> +			     src_clk << PCLK_CENTER_ROOT_SEL_SHIFT);
> +		break;
> +	default:
> +		printf("do not support this center freq\n");
> +		return -EINVAL;
> +	}
> +
> +	return rk3588_center_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_top_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 con, sel, div, rate, prate;
> +
> +	switch (clk_id) {
> +	case ACLK_TOP_ROOT:
> +		con = readl(&cru->clksel_con[8]);
> +		div = (con & ACLK_TOP_ROOT_DIV_MASK) >>
> +		      ACLK_TOP_ROOT_DIV_SHIFT;
> +		sel = (con & ACLK_TOP_ROOT_SRC_SEL_MASK) >>
> +		      ACLK_TOP_ROOT_SRC_SEL_SHIFT;
> +		if (sel == ACLK_TOP_ROOT_SRC_SEL_CPLL)
> +			prate = priv->cpll_hz;
> +		else
> +			prate = priv->cpll_hz;
> +		return DIV_TO_RATE(prate, div);
> +	case ACLK_LOW_TOP_ROOT:
> +		con = readl(&cru->clksel_con[8]);
> +		div = (con & ACLK_LOW_TOP_ROOT_DIV_MASK) >>
> +		      ACLK_LOW_TOP_ROOT_DIV_SHIFT;
> +		sel = (con & ACLK_LOW_TOP_ROOT_SRC_SEL_MASK) >>
> +		      ACLK_LOW_TOP_ROOT_SRC_SEL_SHIFT;
> +		if (sel == ACLK_LOW_TOP_ROOT_SRC_SEL_CPLL)
> +			prate = priv->cpll_hz;
> +		else
> +			prate = priv->gpll_hz;
> +		return DIV_TO_RATE(prate, div);
> +	case PCLK_TOP_ROOT:
> +		con = readl(&cru->clksel_con[8]);
> +		sel = (con & PCLK_TOP_ROOT_SEL_MASK) >> PCLK_TOP_ROOT_SEL_SHIFT;
> +		if (sel == PCLK_TOP_ROOT_SEL_100M)
> +			rate = 100 * MHz;
> +		else if (sel == PCLK_TOP_ROOT_SEL_50M)
> +			rate = 50 * MHz;
> +		else
> +			rate = OSC_HZ;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rate;
> +}
> +
> +static ulong rk3588_top_set_clk(struct rk3588_clk_priv *priv,
> +				ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int src_clk, src_clk_div;
> +
> +	switch (clk_id) {
> +	case ACLK_TOP_ROOT:
> +		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +		assert(src_clk_div - 1 <= 31);
> +		rk_clrsetreg(&cru->clksel_con[8],
> +			     ACLK_TOP_ROOT_DIV_MASK |
> +			     ACLK_TOP_ROOT_SRC_SEL_MASK,
> +			     (ACLK_TOP_ROOT_SRC_SEL_GPLL <<
> +			      ACLK_TOP_ROOT_SRC_SEL_SHIFT) |
> +			     (src_clk_div - 1) << ACLK_TOP_ROOT_DIV_SHIFT);
> +		break;
> +	case ACLK_LOW_TOP_ROOT:
> +		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +		assert(src_clk_div - 1 <= 31);
> +		rk_clrsetreg(&cru->clksel_con[8],
> +			     ACLK_LOW_TOP_ROOT_DIV_MASK |
> +			     ACLK_LOW_TOP_ROOT_SRC_SEL_MASK,
> +			     (ACLK_LOW_TOP_ROOT_SRC_SEL_GPLL <<
> +			      ACLK_LOW_TOP_ROOT_SRC_SEL_SHIFT) |
> +			     (src_clk_div - 1) << ACLK_LOW_TOP_ROOT_DIV_SHIFT);
> +		break;
> +	case PCLK_TOP_ROOT:
> +		if (rate == 100 * MHz)
> +			src_clk = PCLK_TOP_ROOT_SEL_100M;
> +		else if (rate == 50 * MHz)
> +			src_clk = PCLK_TOP_ROOT_SEL_50M;
> +		else
> +			src_clk = PCLK_TOP_ROOT_SEL_24M;
> +		rk_clrsetreg(&cru->clksel_con[8],
> +			     PCLK_TOP_ROOT_SEL_MASK,
> +			     src_clk << PCLK_TOP_ROOT_SEL_SHIFT);
> +		break;
> +	default:
> +		printf("do not support this top freq\n");
> +		return -EINVAL;
> +	}
> +
> +	return rk3588_top_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_i2c_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 sel, con;
> +	ulong rate;
> +
> +	switch (clk_id) {
> +	case CLK_I2C0:
> +		con = readl(&cru->pmuclksel_con[3]);
> +		sel = (con & CLK_I2C0_SEL_MASK) >> CLK_I2C0_SEL_SHIFT;
> +		break;
> +	case CLK_I2C1:
> +		con = readl(&cru->clksel_con[38]);
> +		sel = (con & CLK_I2C1_SEL_MASK) >> CLK_I2C1_SEL_SHIFT;
> +		break;
> +	case CLK_I2C2:
> +		con = readl(&cru->clksel_con[38]);
> +		sel = (con & CLK_I2C2_SEL_MASK) >> CLK_I2C2_SEL_SHIFT;
> +		break;
> +	case CLK_I2C3:
> +		con = readl(&cru->clksel_con[38]);
> +		sel = (con & CLK_I2C3_SEL_MASK) >> CLK_I2C3_SEL_SHIFT;
> +		break;
> +	case CLK_I2C4:
> +		con = readl(&cru->clksel_con[38]);
> +		sel = (con & CLK_I2C4_SEL_MASK) >> CLK_I2C4_SEL_SHIFT;
> +		break;
> +	case CLK_I2C5:
> +		con = readl(&cru->clksel_con[38]);
> +		sel = (con & CLK_I2C5_SEL_MASK) >> CLK_I2C5_SEL_SHIFT;
> +		break;
> +	case CLK_I2C6:
> +		con = readl(&cru->clksel_con[38]);
> +		sel = (con & CLK_I2C6_SEL_MASK) >> CLK_I2C6_SEL_SHIFT;
> +		break;
> +	case CLK_I2C7:
> +		con = readl(&cru->clksel_con[38]);
> +		sel = (con & CLK_I2C7_SEL_MASK) >> CLK_I2C7_SEL_SHIFT;
> +		break;
> +	case CLK_I2C8:
> +		con = readl(&cru->clksel_con[38]);
> +		sel = (con & CLK_I2C8_SEL_MASK) >> CLK_I2C8_SEL_SHIFT;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +	if (sel == CLK_I2C_SEL_200M)
> +		rate = 200 * MHz;
> +	else
> +		rate = 100 * MHz;
> +
> +	return rate;
> +}
> +
> +static ulong rk3588_i2c_set_clk(struct rk3588_clk_priv *priv, ulong clk_id,
> +				ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int src_clk;
> +
> +	if (rate >= 198 * MHz)
> +		src_clk = CLK_I2C_SEL_200M;
> +	else
> +		src_clk = CLK_I2C_SEL_100M;
> +
> +	switch (clk_id) {
> +	case CLK_I2C0:
> +		rk_clrsetreg(&cru->pmuclksel_con[3], CLK_I2C0_SEL_MASK,
> +			     src_clk << CLK_I2C0_SEL_SHIFT);
> +		break;
> +	case CLK_I2C1:
> +		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C1_SEL_MASK,
> +			     src_clk << CLK_I2C1_SEL_SHIFT);
> +		break;
> +	case CLK_I2C2:
> +		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C2_SEL_MASK,
> +			     src_clk << CLK_I2C2_SEL_SHIFT);
> +		break;
> +	case CLK_I2C3:
> +		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C3_SEL_MASK,
> +			     src_clk << CLK_I2C3_SEL_SHIFT);
> +		break;
> +	case CLK_I2C4:
> +		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C4_SEL_MASK,
> +			     src_clk << CLK_I2C4_SEL_SHIFT);
> +		break;
> +	case CLK_I2C5:
> +		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C5_SEL_MASK,
> +			     src_clk << CLK_I2C5_SEL_SHIFT);
> +		break;
> +	case CLK_I2C6:
> +		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C6_SEL_MASK,
> +			     src_clk << CLK_I2C6_SEL_SHIFT);
> +		break;
> +	case CLK_I2C7:
> +		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C7_SEL_MASK,
> +			     src_clk << CLK_I2C7_SEL_SHIFT);
> +		break;
> +	case CLK_I2C8:
> +		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C8_SEL_MASK,
> +			     src_clk << CLK_I2C8_SEL_SHIFT);
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rk3588_i2c_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_spi_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 sel, con;
> +
> +	con = readl(&cru->clksel_con[59]);
> +
> +	switch (clk_id) {
> +	case CLK_SPI0:
> +		sel = (con & CLK_SPI0_SEL_MASK) >> CLK_SPI0_SEL_SHIFT;
> +		break;
> +	case CLK_SPI1:
> +		sel = (con & CLK_SPI1_SEL_MASK) >> CLK_SPI1_SEL_SHIFT;
> +		break;
> +	case CLK_SPI2:
> +		sel = (con & CLK_SPI2_SEL_MASK) >> CLK_SPI2_SEL_SHIFT;
> +		break;
> +	case CLK_SPI3:
> +		sel = (con & CLK_SPI3_SEL_MASK) >> CLK_SPI3_SEL_SHIFT;
> +		break;
> +	case CLK_SPI4:
> +		sel = (con & CLK_SPI4_SEL_MASK) >> CLK_SPI4_SEL_SHIFT;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	switch (sel) {
> +	case CLK_SPI_SEL_200M:
> +		return 200 * MHz;
> +	case CLK_SPI_SEL_150M:
> +		return 150 * MHz;
> +	case CLK_SPI_SEL_24M:
> +		return OSC_HZ;
> +	default:
> +		return -ENOENT;
> +	}
> +}
> +
> +static ulong rk3588_spi_set_clk(struct rk3588_clk_priv *priv,
> +				ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int src_clk;
> +
> +	if (rate >= 198 * MHz)
> +		src_clk = CLK_SPI_SEL_200M;
> +	else if (rate >= 140 * MHz)
> +		src_clk = CLK_SPI_SEL_150M;
> +	else
> +		src_clk = CLK_SPI_SEL_24M;
> +
> +	switch (clk_id) {
> +	case CLK_SPI0:
> +		rk_clrsetreg(&cru->clksel_con[59],
> +			     CLK_SPI0_SEL_MASK,
> +			     src_clk << CLK_SPI0_SEL_SHIFT);
> +		break;
> +	case CLK_SPI1:
> +		rk_clrsetreg(&cru->clksel_con[59],
> +			     CLK_SPI1_SEL_MASK,
> +			     src_clk << CLK_SPI1_SEL_SHIFT);
> +		break;
> +	case CLK_SPI2:
> +		rk_clrsetreg(&cru->clksel_con[59],
> +			     CLK_SPI2_SEL_MASK,
> +			     src_clk << CLK_SPI2_SEL_SHIFT);
> +		break;
> +	case CLK_SPI3:
> +		rk_clrsetreg(&cru->clksel_con[59],
> +			     CLK_SPI3_SEL_MASK,
> +			     src_clk << CLK_SPI3_SEL_SHIFT);
> +		break;
> +	case CLK_SPI4:
> +		rk_clrsetreg(&cru->clksel_con[59],
> +			     CLK_SPI4_SEL_MASK,
> +			     src_clk << CLK_SPI4_SEL_SHIFT);
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rk3588_spi_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_pwm_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 sel, con;
> +
> +	switch (clk_id) {
> +	case CLK_PWM1:
> +		con = readl(&cru->clksel_con[59]);
> +		sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM1_SEL_SHIFT;
> +		break;
> +	case CLK_PWM2:
> +		con = readl(&cru->clksel_con[59]);
> +		sel = (con & CLK_PWM2_SEL_MASK) >> CLK_PWM2_SEL_SHIFT;
> +		break;
> +	case CLK_PWM3:
> +		con = readl(&cru->clksel_con[60]);
> +		sel = (con & CLK_PWM3_SEL_MASK) >> CLK_PWM3_SEL_SHIFT;
> +		break;
> +	case CLK_PMU1PWM:
> +		con = readl(&cru->pmuclksel_con[2]);
> +		sel = (con & CLK_PMU1PWM_SEL_MASK) >> CLK_PMU1PWM_SEL_SHIFT;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	switch (sel) {
> +	case CLK_PWM_SEL_100M:
> +		return 100 * MHz;
> +	case CLK_PWM_SEL_50M:
> +		return 50 * MHz;
> +	case CLK_PWM_SEL_24M:
> +		return OSC_HZ;
> +	default:
> +		return -ENOENT;
> +	}
> +}
> +
> +static ulong rk3588_pwm_set_clk(struct rk3588_clk_priv *priv,
> +				ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int src_clk;
> +
> +	if (rate >= 99 * MHz)
> +		src_clk = CLK_PWM_SEL_100M;
> +	else if (rate >= 50 * MHz)
> +		src_clk = CLK_PWM_SEL_50M;
> +	else
> +		src_clk = CLK_PWM_SEL_24M;
> +
> +	switch (clk_id) {
> +	case CLK_PWM1:
> +		rk_clrsetreg(&cru->clksel_con[59],
> +			     CLK_PWM1_SEL_MASK,
> +			     src_clk << CLK_PWM1_SEL_SHIFT);
> +		break;
> +	case CLK_PWM2:
> +		rk_clrsetreg(&cru->clksel_con[59],
> +			     CLK_PWM2_SEL_MASK,
> +			     src_clk << CLK_PWM2_SEL_SHIFT);
> +		break;
> +	case CLK_PWM3:
> +		rk_clrsetreg(&cru->clksel_con[60],
> +			     CLK_PWM3_SEL_MASK,
> +			     src_clk << CLK_PWM3_SEL_SHIFT);
> +		break;
> +	case CLK_PMU1PWM:
> +		rk_clrsetreg(&cru->pmuclksel_con[2],
> +			     CLK_PMU1PWM_SEL_MASK,
> +			     src_clk << CLK_PMU1PWM_SEL_SHIFT);
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rk3588_pwm_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_adc_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 div, sel, con, prate;
> +
> +	switch (clk_id) {
> +	case CLK_SARADC:
> +		con = readl(&cru->clksel_con[40]);
> +		div = (con & CLK_SARADC_DIV_MASK) >> CLK_SARADC_DIV_SHIFT;
> +		sel = (con & CLK_SARADC_SEL_MASK) >>
> +		      CLK_SARADC_SEL_SHIFT;
> +		if (sel == CLK_SARADC_SEL_24M)
> +			prate = OSC_HZ;
> +		else
> +			prate = priv->gpll_hz;
> +		return DIV_TO_RATE(prate, div);
> +	case CLK_TSADC:
> +		con = readl(&cru->clksel_con[41]);
> +		div = (con & CLK_TSADC_DIV_MASK) >>
> +		      CLK_TSADC_DIV_SHIFT;
> +		sel = (con & CLK_TSADC_SEL_MASK) >>
> +		      CLK_TSADC_SEL_SHIFT;
> +		if (sel == CLK_TSADC_SEL_24M)
> +			prate = OSC_HZ;
> +		else
> +			prate = 100 * MHz;
> +		return DIV_TO_RATE(prate, div);
> +	default:
> +		return -ENOENT;
> +	}
> +}
> +
> +static ulong rk3588_adc_set_clk(struct rk3588_clk_priv *priv,
> +				ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int src_clk_div;
> +
> +	switch (clk_id) {
> +	case CLK_SARADC:
> +		if (!(OSC_HZ % rate)) {
> +			src_clk_div = DIV_ROUND_UP(OSC_HZ, rate);
> +			assert(src_clk_div - 1 <= 255);
> +			rk_clrsetreg(&cru->clksel_con[40],
> +				     CLK_SARADC_SEL_MASK |
> +				     CLK_SARADC_DIV_MASK,
> +				     (CLK_SARADC_SEL_24M <<
> +				      CLK_SARADC_SEL_SHIFT) |
> +				     (src_clk_div - 1) <<
> +				     CLK_SARADC_DIV_SHIFT);
> +		} else {
> +			src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +			assert(src_clk_div - 1 <= 255);
> +			rk_clrsetreg(&cru->clksel_con[40],
> +				     CLK_SARADC_SEL_MASK |
> +				     CLK_SARADC_DIV_MASK,
> +				     (CLK_SARADC_SEL_GPLL <<
> +				      CLK_SARADC_SEL_SHIFT) |
> +				     (src_clk_div - 1) <<
> +				     CLK_SARADC_DIV_SHIFT);
> +		}
> +		break;
> +	case CLK_TSADC:
> +		if (!(OSC_HZ % rate)) {
> +			src_clk_div = DIV_ROUND_UP(OSC_HZ, rate);
> +			assert(src_clk_div - 1 <= 255);
> +			rk_clrsetreg(&cru->clksel_con[41],
> +				     CLK_TSADC_SEL_MASK |
> +				     CLK_TSADC_DIV_MASK,
> +				     (CLK_TSADC_SEL_24M <<
> +				      CLK_TSADC_SEL_SHIFT) |
> +				     (src_clk_div - 1) <<
> +				     CLK_TSADC_DIV_SHIFT);
> +		} else {
> +			src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +			assert(src_clk_div - 1 <= 7);
> +			rk_clrsetreg(&cru->clksel_con[41],
> +				     CLK_TSADC_SEL_MASK |
> +				     CLK_TSADC_DIV_MASK,
> +				     (CLK_TSADC_SEL_GPLL <<
> +				      CLK_TSADC_SEL_SHIFT) |
> +				     (src_clk_div - 1) <<
> +				     CLK_TSADC_DIV_SHIFT);
> +		}
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +	return rk3588_adc_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_mmc_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 sel, con, div, prate;
> +
> +	switch (clk_id) {
> +	case CCLK_SRC_SDIO:
> +		con = readl(&cru->clksel_con[172]);
> +		div = (con & CCLK_SDIO_SRC_DIV_MASK) >> CCLK_SDIO_SRC_DIV_SHIFT;
> +		sel = (con & CCLK_SDIO_SRC_SEL_MASK) >>
> +		      CCLK_SDIO_SRC_SEL_SHIFT;
> +		if (sel == CCLK_SDIO_SRC_SEL_GPLL)
> +			prate = priv->gpll_hz;
> +		else if (sel == CCLK_SDIO_SRC_SEL_CPLL)
> +			prate = priv->cpll_hz;
> +		else
> +			prate = OSC_HZ;
> +		return DIV_TO_RATE(prate, div);
> +	case CCLK_EMMC:
> +		con = readl(&cru->clksel_con[77]);
> +		div = (con & CCLK_EMMC_DIV_MASK) >> CCLK_EMMC_DIV_SHIFT;
> +		sel = (con & CCLK_EMMC_SEL_MASK) >>
> +		      CCLK_EMMC_SEL_SHIFT;
> +		if (sel == CCLK_EMMC_SEL_GPLL)
> +			prate = priv->gpll_hz;
> +		else if (sel == CCLK_EMMC_SEL_CPLL)
> +			prate = priv->cpll_hz;
> +		else
> +			prate = OSC_HZ;
> +		return DIV_TO_RATE(prate, div);
> +	case BCLK_EMMC:
> +		con = readl(&cru->clksel_con[78]);
> +		div = (con & BCLK_EMMC_DIV_MASK) >> BCLK_EMMC_DIV_SHIFT;
> +		sel = (con & BCLK_EMMC_SEL_MASK) >>
> +		      BCLK_EMMC_SEL_SHIFT;
> +		if (sel == CCLK_EMMC_SEL_CPLL)
> +			prate = priv->cpll_hz;
> +		else
> +			prate = priv->gpll_hz;
> +		return DIV_TO_RATE(prate, div);
> +	case SCLK_SFC:
> +		con = readl(&cru->clksel_con[78]);
> +		div = (con & SCLK_SFC_DIV_MASK) >> SCLK_SFC_DIV_SHIFT;
> +		sel = (con & SCLK_SFC_SEL_MASK) >>
> +		      SCLK_SFC_SEL_SHIFT;
> +		if (sel == SCLK_SFC_SEL_GPLL)
> +			prate = priv->gpll_hz;
> +		else if (sel == SCLK_SFC_SEL_CPLL)
> +			prate = priv->cpll_hz;
> +		else
> +			prate = OSC_HZ;
> +		return DIV_TO_RATE(prate, div);
> +	case DCLK_DECOM:
> +		con = readl(&cru->clksel_con[62]);
> +		div = (con & DCLK_DECOM_DIV_MASK) >> DCLK_DECOM_DIV_SHIFT;
> +		sel = (con & DCLK_DECOM_SEL_MASK) >>
> +		      DCLK_DECOM_SEL_SHIFT;
> +		if (sel == DCLK_DECOM_SEL_SPLL)
> +			prate = 702 * MHz;
> +		else
> +			prate = priv->gpll_hz;
> +		return DIV_TO_RATE(prate, div);
> +	default:
> +		return -ENOENT;
> +	}
> +}
> +
> +static ulong rk3588_mmc_set_clk(struct rk3588_clk_priv *priv,
> +				ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int src_clk, div;
> +
> +	switch (clk_id) {
> +	case CCLK_SRC_SDIO:
> +	case CCLK_EMMC:
> +	case SCLK_SFC:
> +		if (!(OSC_HZ % rate)) {
> +			src_clk = SCLK_SFC_SEL_24M;
> +			div = DIV_ROUND_UP(OSC_HZ, rate);
> +		} else if (!(priv->cpll_hz % rate)) {
> +			src_clk = SCLK_SFC_SEL_CPLL;
> +			div = DIV_ROUND_UP(priv->cpll_hz, rate);
> +		} else {
> +			src_clk = SCLK_SFC_SEL_GPLL;
> +			div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +		}
> +		break;
> +	case BCLK_EMMC:
> +		if (!(priv->cpll_hz % rate)) {
> +			src_clk = CCLK_EMMC_SEL_CPLL;
> +			div = DIV_ROUND_UP(priv->cpll_hz, rate);
> +		} else {
> +			src_clk = CCLK_EMMC_SEL_GPLL;
> +			div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +		}
> +		break;
> +	case DCLK_DECOM:
> +		if (!(702 * MHz % rate)) {
> +			src_clk = DCLK_DECOM_SEL_SPLL;
> +			div = DIV_ROUND_UP(702 * MHz, rate);
> +		} else {
> +			src_clk = DCLK_DECOM_SEL_GPLL;
> +			div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +		}
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	switch (clk_id) {
> +	case CCLK_SRC_SDIO:
> +		rk_clrsetreg(&cru->clksel_con[172],
> +			     CCLK_SDIO_SRC_SEL_MASK |
> +			     CCLK_SDIO_SRC_DIV_MASK,
> +			     (src_clk << CCLK_SDIO_SRC_SEL_SHIFT) |
> +			     (div - 1) << CCLK_SDIO_SRC_DIV_SHIFT);
> +		break;
> +	case CCLK_EMMC:
> +		rk_clrsetreg(&cru->clksel_con[77],
> +			     CCLK_EMMC_SEL_MASK |
> +			     CCLK_EMMC_DIV_MASK,
> +			     (src_clk << CCLK_EMMC_SEL_SHIFT) |
> +			     (div - 1) << CCLK_EMMC_DIV_SHIFT);
> +		break;
> +	case BCLK_EMMC:
> +		rk_clrsetreg(&cru->clksel_con[78],
> +			     BCLK_EMMC_DIV_MASK |
> +			     BCLK_EMMC_SEL_MASK,
> +			     (src_clk << BCLK_EMMC_SEL_SHIFT) |
> +			     (div - 1) << BCLK_EMMC_DIV_SHIFT);
> +		break;
> +	case SCLK_SFC:
> +		rk_clrsetreg(&cru->clksel_con[78],
> +			     SCLK_SFC_DIV_MASK |
> +			     SCLK_SFC_SEL_MASK,
> +			     (src_clk << SCLK_SFC_SEL_SHIFT) |
> +			     (div - 1) << SCLK_SFC_DIV_SHIFT);
> +		break;
> +	case DCLK_DECOM:
> +		rk_clrsetreg(&cru->clksel_con[62],
> +			     DCLK_DECOM_DIV_MASK |
> +			     DCLK_DECOM_SEL_MASK,
> +			     (src_clk << DCLK_DECOM_SEL_SHIFT) |
> +			     (div - 1) << DCLK_DECOM_DIV_SHIFT);
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rk3588_mmc_get_clk(priv, clk_id);
> +}
> +
> +#ifndef CONFIG_SPL_BUILD
> +static ulong rk3588_aux16m_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 div, con, parent;
> +
> +	parent = priv->gpll_hz;
> +	con = readl(&cru->clksel_con[117]);
> +
> +	switch (clk_id) {
> +	case CLK_AUX16M_0:
> +		div = (con & CLK_AUX16MHZ_0_DIV_MASK) >> CLK_AUX16MHZ_0_DIV_SHIFT;
> +		return DIV_TO_RATE(parent, div);
> +	case CLK_AUX16M_1:
> +		div = (con & CLK_AUX16MHZ_1_DIV_MASK) >> CLK_AUX16MHZ_1_DIV_SHIFT;
> +		return DIV_TO_RATE(parent, div);
> +	default:
> +		return -ENOENT;
> +	}
> +}
> +
> +static ulong rk3588_aux16m_set_clk(struct rk3588_clk_priv *priv,
> +				   ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 div;
> +
> +	if (!priv->gpll_hz) {
> +		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
> +		return -ENOENT;
> +	}
> +
> +	div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +
> +	switch (clk_id) {
> +	case CLK_AUX16M_0:
> +		rk_clrsetreg(&cru->clksel_con[117], CLK_AUX16MHZ_0_DIV_MASK,
> +			     (div - 1) << CLK_AUX16MHZ_0_DIV_SHIFT);
> +		break;
> +	case CLK_AUX16M_1:
> +		rk_clrsetreg(&cru->clksel_con[117], CLK_AUX16MHZ_1_DIV_MASK,
> +			     (div - 1) << CLK_AUX16MHZ_1_DIV_SHIFT);
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rk3588_aux16m_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_aclk_vop_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 div, sel, con, parent;
> +
> +	switch (clk_id) {
> +	case ACLK_VOP_ROOT:
> +	case ACLK_VOP:
> +		con = readl(&cru->clksel_con[110]);
> +		div = (con & ACLK_VOP_ROOT_DIV_MASK) >> ACLK_VOP_ROOT_DIV_SHIFT;
> +		sel = (con & ACLK_VOP_ROOT_SEL_MASK) >> ACLK_VOP_ROOT_SEL_SHIFT;
> +		if (sel == ACLK_VOP_ROOT_SEL_GPLL)
> +			parent = priv->gpll_hz;
> +		else if (sel == ACLK_VOP_ROOT_SEL_CPLL)
> +			parent = priv->cpll_hz;
> +		else if (sel == ACLK_VOP_ROOT_SEL_AUPLL)
> +			parent = priv->aupll_hz;
> +		else if (sel == ACLK_VOP_ROOT_SEL_NPLL)
> +			parent = priv->npll_hz;
> +		else
> +			parent = 702 * MHz;
> +		return DIV_TO_RATE(parent, div);
> +	case ACLK_VOP_LOW_ROOT:
> +		con = readl(&cru->clksel_con[110]);
> +		sel = (con & ACLK_VOP_LOW_ROOT_SEL_MASK) >>
> +		      ACLK_VOP_LOW_ROOT_SEL_SHIFT;
> +		if (sel == ACLK_VOP_LOW_ROOT_SEL_400M)
> +			return 396 * MHz;
> +		else if (sel == ACLK_VOP_LOW_ROOT_SEL_200M)
> +			return 200 * MHz;
> +		else if (sel == ACLK_VOP_LOW_ROOT_SEL_100M)
> +			return 100 * MHz;
> +		else
> +			return OSC_HZ;
> +	case HCLK_VOP_ROOT:
> +		con = readl(&cru->clksel_con[110]);
> +		sel = (con & HCLK_VOP_ROOT_SEL_MASK) >> HCLK_VOP_ROOT_SEL_SHIFT;
> +		if (sel == HCLK_VOP_ROOT_SEL_200M)
> +			return 200 * MHz;
> +		else if (sel == HCLK_VOP_ROOT_SEL_100M)
> +			return 100 * MHz;
> +		else if (sel == HCLK_VOP_ROOT_SEL_50M)
> +			return 50 * MHz;
> +		else
> +			return OSC_HZ;
> +	default:
> +		return -ENOENT;
> +	}
> +}
> +
> +static ulong rk3588_aclk_vop_set_clk(struct rk3588_clk_priv *priv,
> +				     ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int src_clk, div;
> +
> +	switch (clk_id) {
> +	case ACLK_VOP_ROOT:
> +	case ACLK_VOP:
> +		if (rate >= 850 * MHz) {
> +			src_clk = ACLK_VOP_ROOT_SEL_NPLL;
> +			div = 1;
> +		} else if (rate >= 750 * MHz) {
> +			src_clk = ACLK_VOP_ROOT_SEL_CPLL;
> +			div = 2;
> +		} else if (rate >= 700 * MHz) {
> +			src_clk = ACLK_VOP_ROOT_SEL_SPLL;
> +			div = 1;
> +		} else if (!(priv->cpll_hz % rate)) {
> +			src_clk = ACLK_VOP_ROOT_SEL_CPLL;
> +			div = DIV_ROUND_UP(priv->cpll_hz, rate);
> +		} else {
> +			src_clk = ACLK_VOP_ROOT_SEL_GPLL;
> +			div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +		}
> +		rk_clrsetreg(&cru->clksel_con[110],
> +			     ACLK_VOP_ROOT_DIV_MASK |
> +			     ACLK_VOP_ROOT_SEL_MASK,
> +			     (src_clk << ACLK_VOP_ROOT_SEL_SHIFT) |
> +			     (div - 1) << ACLK_VOP_ROOT_DIV_SHIFT);
> +		break;
> +	case ACLK_VOP_LOW_ROOT:
> +		if (rate == 400 * MHz || rate == 396 * MHz)
> +			src_clk = ACLK_VOP_LOW_ROOT_SEL_400M;
> +		else if (rate == 200 * MHz)
> +			src_clk = ACLK_VOP_LOW_ROOT_SEL_200M;
> +		else if (rate == 100 * MHz)
> +			src_clk = ACLK_VOP_LOW_ROOT_SEL_100M;
> +		else
> +			src_clk = ACLK_VOP_LOW_ROOT_SEL_24M;
> +		rk_clrsetreg(&cru->clksel_con[110],
> +			     ACLK_VOP_LOW_ROOT_SEL_MASK,
> +			     src_clk << ACLK_VOP_LOW_ROOT_SEL_SHIFT);
> +		break;
> +	case HCLK_VOP_ROOT:
> +		if (rate == 200 * MHz)
> +			src_clk = HCLK_VOP_ROOT_SEL_200M;
> +		else if (rate == 100 * MHz)
> +			src_clk = HCLK_VOP_ROOT_SEL_100M;
> +		else if (rate == 50 * MHz)
> +			src_clk = HCLK_VOP_ROOT_SEL_50M;
> +		else
> +			src_clk = HCLK_VOP_ROOT_SEL_24M;
> +		rk_clrsetreg(&cru->clksel_con[110],
> +			     HCLK_VOP_ROOT_SEL_MASK,
> +			     src_clk << HCLK_VOP_ROOT_SEL_SHIFT);
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rk3588_aclk_vop_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_dclk_vop_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 div, sel, con, parent;
> +
> +	switch (clk_id) {
> +	case DCLK_VOP0:
> +	case DCLK_VOP0_SRC:
> +		con = readl(&cru->clksel_con[111]);
> +		div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT;
> +		sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
> +		break;
> +	case DCLK_VOP1:
> +	case DCLK_VOP1_SRC:
> +		con = readl(&cru->clksel_con[111]);
> +		div = (con & DCLK1_VOP_SRC_DIV_MASK) >> DCLK1_VOP_SRC_DIV_SHIFT;
> +		sel = (con & DCLK1_VOP_SRC_SEL_MASK) >> DCLK1_VOP_SRC_SEL_SHIFT;
> +		break;
> +	case DCLK_VOP2:
> +	case DCLK_VOP2_SRC:
> +		con = readl(&cru->clksel_con[112]);
> +		div = (con & DCLK2_VOP_SRC_DIV_MASK) >> DCLK2_VOP_SRC_DIV_SHIFT;
> +		sel = (con & DCLK2_VOP_SRC_SEL_MASK) >> DCLK2_VOP_SRC_SEL_SHIFT;
> +		break;
> +	case DCLK_VOP3:
> +		con = readl(&cru->clksel_con[113]);
> +		div = (con & DCLK3_VOP_SRC_DIV_MASK) >> DCLK3_VOP_SRC_DIV_SHIFT;
> +		sel = (con & DCLK3_VOP_SRC_SEL_MASK) >> DCLK3_VOP_SRC_SEL_SHIFT;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	if (sel == DCLK_VOP_SRC_SEL_AUPLL)
> +		parent = priv->aupll_hz;
> +	else if (sel == DCLK_VOP_SRC_SEL_V0PLL)
> +		parent = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL],
> +					       priv->cru, V0PLL);
> +	else if (sel == DCLK_VOP_SRC_SEL_GPLL)
> +		parent = priv->gpll_hz;
> +	else if (sel == DCLK_VOP_SRC_SEL_CPLL)
> +		parent = priv->cpll_hz;
> +	else
> +		return -ENOENT;
> +
> +	return DIV_TO_RATE(parent, div);
> +}
> +
> +#define RK3588_VOP_PLL_LIMIT_FREQ 600000000
> +
> +static ulong rk3588_dclk_vop_set_clk(struct rk3588_clk_priv *priv,
> +				     ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	ulong pll_rate, now, best_rate = 0;
> +	u32 i, conid, con, sel, div, best_div = 0, best_sel = 0;
> +	u32 mask, div_shift, sel_shift;
> +
> +	switch (clk_id) {
> +	case DCLK_VOP0:
> +	case DCLK_VOP0_SRC:
> +		conid = 111;
> +		con = readl(&cru->clksel_con[111]);
> +		sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
> +		mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK;
> +		div_shift = DCLK0_VOP_SRC_DIV_SHIFT;
> +		sel_shift = DCLK0_VOP_SRC_SEL_SHIFT;
> +		break;
> +	case DCLK_VOP1:
> +	case DCLK_VOP1_SRC:
> +		conid = 111;
> +		con = readl(&cru->clksel_con[111]);
> +		sel = (con & DCLK1_VOP_SRC_SEL_MASK) >> DCLK1_VOP_SRC_SEL_SHIFT;
> +		mask = DCLK1_VOP_SRC_SEL_MASK | DCLK1_VOP_SRC_DIV_MASK;
> +		div_shift = DCLK1_VOP_SRC_DIV_SHIFT;
> +		sel_shift = DCLK1_VOP_SRC_SEL_SHIFT;
> +		break;
> +	case DCLK_VOP2:
> +	case DCLK_VOP2_SRC:
> +		conid = 112;
> +		con = readl(&cru->clksel_con[112]);
> +		sel = (con & DCLK2_VOP_SRC_SEL_MASK) >> DCLK2_VOP_SRC_SEL_SHIFT;
> +		mask = DCLK2_VOP_SRC_SEL_MASK | DCLK2_VOP_SRC_DIV_MASK;
> +		div_shift = DCLK2_VOP_SRC_DIV_SHIFT;
> +		sel_shift = DCLK2_VOP_SRC_SEL_SHIFT;
> +		break;
> +	case DCLK_VOP3:
> +		conid = 113;
> +		con = readl(&cru->clksel_con[113]);
> +		sel = (con & DCLK3_VOP_SRC_SEL_MASK) >> DCLK3_VOP_SRC_SEL_SHIFT;
> +		mask = DCLK3_VOP_SRC_SEL_MASK | DCLK3_VOP_SRC_DIV_MASK;
> +		div_shift = DCLK3_VOP_SRC_DIV_SHIFT;
> +		sel_shift = DCLK3_VOP_SRC_SEL_SHIFT;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	if (sel == DCLK_VOP_SRC_SEL_V0PLL) {
> +		div = DIV_ROUND_UP(RK3588_VOP_PLL_LIMIT_FREQ, rate);
> +		rk_clrsetreg(&cru->clksel_con[conid],
> +			     mask,
> +			     DCLK_VOP_SRC_SEL_V0PLL << sel_shift |
> +			     ((div - 1) << div_shift));
> +		rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL],
> +				      priv->cru, V0PLL, div * rate);
> +	} else {
> +		for (i = 0; i <= DCLK_VOP_SRC_SEL_AUPLL; i++) {
> +			switch (i) {
> +			case DCLK_VOP_SRC_SEL_GPLL:
> +				pll_rate = priv->gpll_hz;
> +				break;
> +			case DCLK_VOP_SRC_SEL_CPLL:
> +				pll_rate = priv->cpll_hz;
> +				break;
> +			case DCLK_VOP_SRC_SEL_AUPLL:
> +				pll_rate = priv->aupll_hz;
> +				break;
> +			case DCLK_VOP_SRC_SEL_V0PLL:
> +				pll_rate = 0;
> +				break;
> +			default:
> +				printf("do not support this vop pll sel\n");
> +				return -EINVAL;
> +			}
> +
> +			div = DIV_ROUND_UP(pll_rate, rate);
> +			if (div > 255)
> +				continue;
> +			now = pll_rate / div;
> +			if (abs(rate - now) < abs(rate - best_rate)) {
> +				best_rate = now;
> +				best_div = div;
> +				best_sel = i;
> +			}
> +			debug("p_rate=%lu, best_rate=%lu, div=%u, sel=%u\n",
> +			      pll_rate, best_rate, best_div, best_sel);
> +		}
> +
> +		if (best_rate) {
> +			rk_clrsetreg(&cru->clksel_con[conid],
> +				     mask,
> +				     best_sel << sel_shift |
> +				     (best_div - 1) << div_shift);
> +		} else {
> +			printf("do not support this vop freq %lu\n", rate);
> +			return -EINVAL;
> +		}
> +	}
> +	return rk3588_dclk_vop_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_gmac_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 con, div;
> +
> +	switch (clk_id) {
> +	case CLK_GMAC0_PTP_REF:
> +		con = readl(&cru->clksel_con[81]);
> +		div = (con & CLK_GMAC0_PTP_DIV_MASK) >> CLK_GMAC0_PTP_DIV_SHIFT;
> +		return DIV_TO_RATE(priv->cpll_hz, div);
> +	case CLK_GMAC1_PTP_REF:
> +		con = readl(&cru->clksel_con[81]);
> +		div = (con & CLK_GMAC1_PTP_DIV_MASK) >> CLK_GMAC1_PTP_DIV_SHIFT;
> +		return DIV_TO_RATE(priv->cpll_hz, div);
> +	case CLK_GMAC_125M:
> +		con = readl(&cru->clksel_con[83]);
> +		div = (con & CLK_GMAC_125M_DIV_MASK) >> CLK_GMAC_125M_DIV_SHIFT;
> +		return DIV_TO_RATE(priv->cpll_hz, div);
> +	case CLK_GMAC_50M:
> +		con = readl(&cru->clksel_con[84]);
> +		div = (con & CLK_GMAC_50M_DIV_MASK) >> CLK_GMAC_50M_DIV_SHIFT;
> +		return DIV_TO_RATE(priv->cpll_hz, div);
> +	default:
> +		return -ENOENT;
> +	}
> +}
> +
> +static ulong rk3588_gmac_set_clk(struct rk3588_clk_priv *priv,
> +				 ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	int div;
> +
> +	div = DIV_ROUND_UP(priv->cpll_hz, rate);
> +
> +	switch (clk_id) {
> +	case CLK_GMAC0_PTP_REF:
> +		rk_clrsetreg(&cru->clksel_con[81],
> +			     CLK_GMAC0_PTP_DIV_MASK | CLK_GMAC0_PTP_SEL_MASK,
> +			     CLK_GMAC0_PTP_SEL_CPLL << CLK_GMAC0_PTP_SEL_SHIFT |
> +			     (div - 1) << CLK_GMAC0_PTP_DIV_SHIFT);
> +		break;
> +	case CLK_GMAC1_PTP_REF:
> +		rk_clrsetreg(&cru->clksel_con[81],
> +			     CLK_GMAC1_PTP_DIV_MASK | CLK_GMAC1_PTP_SEL_MASK,
> +			     CLK_GMAC1_PTP_SEL_CPLL << CLK_GMAC1_PTP_SEL_SHIFT |
> +			     (div - 1) << CLK_GMAC1_PTP_DIV_SHIFT);
> +		break;
> +
> +	case CLK_GMAC_125M:
> +		rk_clrsetreg(&cru->clksel_con[83],
> +			     CLK_GMAC_125M_DIV_MASK | CLK_GMAC_125M_SEL_MASK,
> +			     CLK_GMAC_125M_SEL_CPLL << CLK_GMAC_125M_SEL_SHIFT |
> +			     (div - 1) << CLK_GMAC_125M_DIV_SHIFT);
> +		break;
> +	case CLK_GMAC_50M:
> +		rk_clrsetreg(&cru->clksel_con[84],
> +			     CLK_GMAC_50M_DIV_MASK | CLK_GMAC_50M_SEL_MASK,
> +			     CLK_GMAC_50M_SEL_CPLL << CLK_GMAC_50M_SEL_SHIFT |
> +			     (div - 1) << CLK_GMAC_50M_DIV_SHIFT);
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rk3588_gmac_get_clk(priv, clk_id);
> +}
> +
> +static ulong rk3588_uart_get_rate(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 reg, con, fracdiv, div, src, p_src, p_rate;
> +	unsigned long m, n;
> +
> +	switch (clk_id) {
> +	case SCLK_UART1:
> +		reg = 41;
> +		break;
> +	case SCLK_UART2:
> +		reg = 43;
> +		break;
> +	case SCLK_UART3:
> +		reg = 45;
> +		break;
> +	case SCLK_UART4:
> +		reg = 47;
> +		break;
> +	case SCLK_UART5:
> +		reg = 49;
> +		break;
> +	case SCLK_UART6:
> +		reg = 51;
> +		break;
> +	case SCLK_UART7:
> +		reg = 53;
> +		break;
> +	case SCLK_UART8:
> +		reg = 55;
> +		break;
> +	case SCLK_UART9:
> +		reg = 57;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +	con = readl(&cru->clksel_con[reg + 2]);
> +	src = (con & CLK_UART_SEL_MASK) >> CLK_UART_SEL_SHIFT;
> +	con = readl(&cru->clksel_con[reg]);
> +	div = (con & CLK_UART_SRC_DIV_MASK) >> CLK_UART_SRC_DIV_SHIFT;
> +	p_src = (con & CLK_UART_SRC_SEL_MASK) >> CLK_UART_SRC_SEL_SHIFT;
> +	if (p_src == CLK_UART_SRC_SEL_GPLL)
> +		p_rate = priv->gpll_hz;
> +	else
> +		p_rate = priv->cpll_hz;
> +
> +	if (src == CLK_UART_SEL_SRC) {
> +		return DIV_TO_RATE(p_rate, div);
> +	} else if (src == CLK_UART_SEL_FRAC) {
> +		fracdiv = readl(&cru->clksel_con[reg + 1]);
> +		n = fracdiv & CLK_UART_FRAC_NUMERATOR_MASK;
> +		n >>= CLK_UART_FRAC_NUMERATOR_SHIFT;
> +		m = fracdiv & CLK_UART_FRAC_DENOMINATOR_MASK;
> +		m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT;
> +		return DIV_TO_RATE(p_rate, div) * n / m;
> +	} else {
> +		return OSC_HZ;
> +	}
> +}
> +
> +static ulong rk3588_uart_set_rate(struct rk3588_clk_priv *priv,
> +				  ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 reg, clk_src, uart_src, div;
> +	unsigned long m = 0, n = 0, val;
> +
> +	if (priv->gpll_hz % rate == 0) {
> +		clk_src = CLK_UART_SRC_SEL_GPLL;
> +		uart_src = CLK_UART_SEL_SRC;
> +		div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +	} else if (priv->cpll_hz % rate == 0) {
> +		clk_src = CLK_UART_SRC_SEL_CPLL;
> +		uart_src = CLK_UART_SEL_SRC;
> +		div = DIV_ROUND_UP(priv->gpll_hz, rate);
> +	} else if (rate == OSC_HZ) {
> +		clk_src = CLK_UART_SRC_SEL_GPLL;
> +		uart_src = CLK_UART_SEL_XIN24M;
> +		div = 2;
> +	} else {
> +		clk_src = CLK_UART_SRC_SEL_GPLL;
> +		uart_src = CLK_UART_SEL_FRAC;
> +		div = 2;
> +		rational_best_approximation(rate, priv->gpll_hz / div,
> +					    GENMASK(16 - 1, 0),
> +					    GENMASK(16 - 1, 0),
> +					    &m, &n);
> +	}
> +
> +	switch (clk_id) {
> +	case SCLK_UART1:
> +		reg = 41;
> +		break;
> +	case SCLK_UART2:
> +		reg = 43;
> +		break;
> +	case SCLK_UART3:
> +		reg = 45;
> +		break;
> +	case SCLK_UART4:
> +		reg = 47;
> +		break;
> +	case SCLK_UART5:
> +		reg = 49;
> +		break;
> +	case SCLK_UART6:
> +		reg = 51;
> +		break;
> +	case SCLK_UART7:
> +		reg = 53;
> +		break;
> +	case SCLK_UART8:
> +		reg = 55;
> +		break;
> +	case SCLK_UART9:
> +		reg = 57;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +	rk_clrsetreg(&cru->clksel_con[reg],
> +		     CLK_UART_SRC_SEL_MASK |
> +		     CLK_UART_SRC_DIV_MASK,
> +		     (clk_src << CLK_UART_SRC_SEL_SHIFT) |
> +		     ((div - 1) << CLK_UART_SRC_DIV_SHIFT));
> +	rk_clrsetreg(&cru->clksel_con[reg + 2],
> +		     CLK_UART_SEL_MASK,
> +		     (uart_src << CLK_UART_SEL_SHIFT));
> +	if (m && n) {
> +		val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n;
> +		writel(val, &cru->clksel_con[reg + 1]);
> +	}
> +
> +	return rk3588_uart_get_rate(priv, clk_id);
> +}
> +
> +static ulong rk3588_pciephy_get_rate(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 con, div, src;
> +
> +	switch (clk_id) {
> +	case CLK_REF_PIPE_PHY0:
> +		con = readl(&cru->clksel_con[177]);
> +		src = (con & CLK_PCIE_PHY0_REF_SEL_MASK) >> CLK_PCIE_PHY0_REF_SEL_SHIFT;
> +		con = readl(&cru->clksel_con[176]);
> +		div = (con & CLK_PCIE_PHY0_PLL_DIV_MASK) >> CLK_PCIE_PHY0_PLL_DIV_SHIFT;
> +		break;
> +	case CLK_REF_PIPE_PHY1:
> +		con = readl(&cru->clksel_con[177]);
> +		src = (con & CLK_PCIE_PHY1_REF_SEL_MASK) >> CLK_PCIE_PHY1_REF_SEL_SHIFT;
> +		con = readl(&cru->clksel_con[176]);
> +		div = (con & CLK_PCIE_PHY1_PLL_DIV_MASK) >> CLK_PCIE_PHY1_PLL_DIV_SHIFT;
> +		break;
> +	case CLK_REF_PIPE_PHY2:
> +		con = readl(&cru->clksel_con[177]);
> +		src = (con & CLK_PCIE_PHY2_REF_SEL_MASK) >> CLK_PCIE_PHY2_REF_SEL_SHIFT;
> +		div = (con & CLK_PCIE_PHY2_PLL_DIV_MASK) >> CLK_PCIE_PHY2_PLL_DIV_SHIFT;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	if (src == CLK_PCIE_PHY_REF_SEL_PPLL)
> +		return DIV_TO_RATE(priv->ppll_hz, div);
> +	else
> +		return OSC_HZ;
> +}
> +
> +static ulong rk3588_pciephy_set_rate(struct rk3588_clk_priv *priv,
> +				     ulong clk_id, ulong rate)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 clk_src, div;
> +
> +	if (rate == OSC_HZ) {
> +		clk_src = CLK_PCIE_PHY_REF_SEL_24M;
> +		div = 1;
> +	} else {
> +		clk_src = CLK_PCIE_PHY_REF_SEL_PPLL;
> +		div = DIV_ROUND_UP(priv->ppll_hz, rate);
> +	}
> +
> +	switch (clk_id) {
> +	case CLK_REF_PIPE_PHY0:
> +		rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY0_REF_SEL_MASK,
> +			     (clk_src << CLK_PCIE_PHY0_REF_SEL_SHIFT));
> +		rk_clrsetreg(&cru->clksel_con[176], CLK_PCIE_PHY0_PLL_DIV_MASK,
> +			     ((div - 1) << CLK_PCIE_PHY0_PLL_DIV_SHIFT));
> +		break;
> +	case CLK_REF_PIPE_PHY1:
> +		rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY1_REF_SEL_MASK,
> +			     (clk_src << CLK_PCIE_PHY1_REF_SEL_SHIFT));
> +		rk_clrsetreg(&cru->clksel_con[176], CLK_PCIE_PHY1_PLL_DIV_MASK,
> +			     ((div - 1) << CLK_PCIE_PHY1_PLL_DIV_SHIFT));
> +		break;
> +	case CLK_REF_PIPE_PHY2:
> +		rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY2_REF_SEL_MASK |
> +			     CLK_PCIE_PHY2_PLL_DIV_MASK,
> +			     (clk_src << CLK_PCIE_PHY2_REF_SEL_SHIFT) |
> +			     ((div - 1) << CLK_PCIE_PHY2_PLL_DIV_SHIFT));
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rk3588_pciephy_get_rate(priv, clk_id);
> +}
> +#endif
> +
> +static ulong rk3588_clk_get_rate(struct clk *clk)
> +{
> +	struct rk3588_clk_priv *priv = dev_get_priv(clk->dev);
> +	ulong rate = 0;
> +
> +	if (!priv->gpll_hz) {
> +		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
> +		return -ENOENT;
> +	}
> +
> +	if (!priv->ppll_hz) {
> +		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
> +						      priv->cru, PPLL);
> +	}
> +
> +	switch (clk->id) {
> +	case PLL_LPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[LPLL], priv->cru,
> +					     LPLL);
> +		break;
> +	case PLL_B0PLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[B0PLL], priv->cru,
> +					     B0PLL);
> +		break;
> +	case PLL_B1PLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[B1PLL], priv->cru,
> +					     B1PLL);
> +		break;
> +	case PLL_GPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[GPLL], priv->cru,
> +					     GPLL);
> +		break;
> +	case PLL_CPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[CPLL], priv->cru,
> +					     CPLL);
> +		break;
> +	case PLL_NPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[NPLL], priv->cru,
> +					     NPLL);
> +		break;
> +	case PLL_V0PLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL], priv->cru,
> +					     V0PLL);
> +		break;
> +	case PLL_AUPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[AUPLL], priv->cru,
> +					     AUPLL);
> +		break;
> +	case PLL_PPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL], priv->cru,
> +					     PPLL);
> +		break;
> +	case ACLK_CENTER_ROOT:
> +	case PCLK_CENTER_ROOT:
> +	case HCLK_CENTER_ROOT:
> +	case ACLK_CENTER_LOW_ROOT:
> +		rate = rk3588_center_get_clk(priv, clk->id);
> +		break;
> +	case ACLK_TOP_ROOT:
> +	case PCLK_TOP_ROOT:
> +	case ACLK_LOW_TOP_ROOT:
> +		rate = rk3588_top_get_clk(priv, clk->id);
> +		break;
> +	case CLK_I2C0:
> +	case CLK_I2C1:
> +	case CLK_I2C2:
> +	case CLK_I2C3:
> +	case CLK_I2C4:
> +	case CLK_I2C5:
> +	case CLK_I2C6:
> +	case CLK_I2C7:
> +	case CLK_I2C8:
> +		rate = rk3588_i2c_get_clk(priv, clk->id);
> +		break;
> +	case CLK_SPI0:
> +	case CLK_SPI1:
> +	case CLK_SPI2:
> +	case CLK_SPI3:
> +	case CLK_SPI4:
> +		rate = rk3588_spi_get_clk(priv, clk->id);
> +		break;
> +	case CLK_PWM1:
> +	case CLK_PWM2:
> +	case CLK_PWM3:
> +	case CLK_PMU1PWM:
> +		rate = rk3588_pwm_get_clk(priv, clk->id);
> +		break;
> +	case CLK_SARADC:
> +	case CLK_TSADC:
> +		rate = rk3588_adc_get_clk(priv, clk->id);
> +		break;
> +	case CCLK_SRC_SDIO:
> +	case CCLK_EMMC:
> +	case BCLK_EMMC:
> +	case SCLK_SFC:
> +	case DCLK_DECOM:
> +		rate = rk3588_mmc_get_clk(priv, clk->id);
> +		break;
> +	case TCLK_WDT0:
> +		rate = OSC_HZ;
> +		break;
> +#ifndef CONFIG_SPL_BUILD
> +	case CLK_AUX16M_0:
> +	case CLK_AUX16M_1:
> +		rk3588_aux16m_get_clk(priv, clk->id);
> +		break;
> +	case ACLK_VOP_ROOT:
> +	case ACLK_VOP:
> +	case ACLK_VOP_LOW_ROOT:
> +	case HCLK_VOP_ROOT:
> +		rate = rk3588_aclk_vop_get_clk(priv, clk->id);
> +		break;
> +	case DCLK_VOP0:
> +	case DCLK_VOP0_SRC:
> +	case DCLK_VOP1:
> +	case DCLK_VOP1_SRC:
> +	case DCLK_VOP2:
> +	case DCLK_VOP2_SRC:
> +	case DCLK_VOP3:
> +		rate = rk3588_dclk_vop_get_clk(priv, clk->id);
> +		break;
> +	case CLK_GMAC0_PTP_REF:
> +	case CLK_GMAC1_PTP_REF:
> +	case CLK_GMAC_125M:
> +	case CLK_GMAC_50M:
> +		rate = rk3588_gmac_get_clk(priv, clk->id);
> +		break;
> +	case SCLK_UART1:
> +	case SCLK_UART2:
> +	case SCLK_UART3:
> +	case SCLK_UART4:
> +	case SCLK_UART5:
> +	case SCLK_UART6:
> +	case SCLK_UART7:
> +	case SCLK_UART8:
> +	case SCLK_UART9:
> +		rate = rk3588_uart_get_rate(priv, clk->id);
> +		break;
> +	case CLK_REF_PIPE_PHY0:
> +	case CLK_REF_PIPE_PHY1:
> +	case CLK_REF_PIPE_PHY2:
> +		rate = rk3588_pciephy_get_rate(priv, clk->id);
> +		break;
> +#endif
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rate;
> +};
> +
> +static ulong rk3588_clk_set_rate(struct clk *clk, ulong rate)
> +{
> +	struct rk3588_clk_priv *priv = dev_get_priv(clk->dev);
> +	ulong ret = 0;
> +
> +	if (!priv->gpll_hz) {
> +		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
> +		return -ENOENT;
> +	}
> +
> +	if (!priv->ppll_hz) {
> +		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
> +						      priv->cru, PPLL);
> +	}
> +
> +	switch (clk->id) {
> +	case PLL_CPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[CPLL], priv->cru,
> +					    CPLL, rate);
> +		priv->cpll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[CPLL],
> +						      priv->cru, CPLL);
> +		break;
> +	case PLL_GPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[GPLL], priv->cru,
> +					    GPLL, rate);
> +		priv->gpll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[GPLL],
> +						      priv->cru, GPLL);
> +		break;
> +	case PLL_NPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[NPLL], priv->cru,
> +					    NPLL, rate);
> +		break;
> +	case PLL_V0PLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL], priv->cru,
> +					    V0PLL, rate);
> +		priv->v0pll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL],
> +						       priv->cru, V0PLL);
> +		break;
> +	case PLL_AUPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[AUPLL], priv->cru,
> +					    AUPLL, rate);
> +		priv->aupll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[AUPLL],
> +						       priv->cru, AUPLL);
> +		break;
> +	case PLL_PPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[PPLL], priv->cru,
> +					    PPLL, rate);
> +		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
> +						      priv->cru, PPLL);
> +		break;
> +	case ACLK_CENTER_ROOT:
> +	case PCLK_CENTER_ROOT:
> +	case HCLK_CENTER_ROOT:
> +	case ACLK_CENTER_LOW_ROOT:
> +		ret = rk3588_center_set_clk(priv, clk->id, rate);
> +		break;
> +	case ACLK_TOP_ROOT:
> +	case PCLK_TOP_ROOT:
> +	case ACLK_LOW_TOP_ROOT:
> +		ret = rk3588_top_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_I2C0:
> +	case CLK_I2C1:
> +	case CLK_I2C2:
> +	case CLK_I2C3:
> +	case CLK_I2C4:
> +	case CLK_I2C5:
> +	case CLK_I2C6:
> +	case CLK_I2C7:
> +	case CLK_I2C8:
> +		ret = rk3588_i2c_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_SPI0:
> +	case CLK_SPI1:
> +	case CLK_SPI2:
> +	case CLK_SPI3:
> +	case CLK_SPI4:
> +		ret = rk3588_spi_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_PWM1:
> +	case CLK_PWM2:
> +	case CLK_PWM3:
> +	case CLK_PMU1PWM:
> +		ret = rk3588_pwm_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_SARADC:
> +	case CLK_TSADC:
> +		ret = rk3588_adc_set_clk(priv, clk->id, rate);
> +		break;
> +	case CCLK_SRC_SDIO:
> +	case CCLK_EMMC:
> +	case BCLK_EMMC:
> +	case SCLK_SFC:
> +	case DCLK_DECOM:
> +		ret = rk3588_mmc_set_clk(priv, clk->id, rate);
> +		break;
> +	case TCLK_WDT0:
> +		ret = OSC_HZ;
> +		break;
> +#ifndef CONFIG_SPL_BUILD
> +	case CLK_AUX16M_0:
> +	case CLK_AUX16M_1:
> +		rk3588_aux16m_set_clk(priv, clk->id, rate);
> +		break;
> +	case ACLK_VOP_ROOT:
> +	case ACLK_VOP:
> +	case ACLK_VOP_LOW_ROOT:
> +	case HCLK_VOP_ROOT:
> +		ret = rk3588_aclk_vop_set_clk(priv, clk->id, rate);
> +		break;
> +	case DCLK_VOP0:
> +	case DCLK_VOP0_SRC:
> +	case DCLK_VOP1:
> +	case DCLK_VOP1_SRC:
> +	case DCLK_VOP2:
> +	case DCLK_VOP2_SRC:
> +	case DCLK_VOP3:
> +		ret = rk3588_dclk_vop_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_GMAC0_PTP_REF:
> +	case CLK_GMAC1_PTP_REF:
> +	case CLK_GMAC_125M:
> +	case CLK_GMAC_50M:
> +		ret = rk3588_gmac_set_clk(priv, clk->id, rate);
> +		break;
> +	case SCLK_UART1:
> +	case SCLK_UART2:
> +	case SCLK_UART3:
> +	case SCLK_UART4:
> +	case SCLK_UART5:
> +	case SCLK_UART6:
> +	case SCLK_UART7:
> +	case SCLK_UART8:
> +	case SCLK_UART9:
> +		ret = rk3588_uart_set_rate(priv, clk->id, rate);
> +		break;
> +	case CLK_REF_PIPE_PHY0:
> +	case CLK_REF_PIPE_PHY1:
> +	case CLK_REF_PIPE_PHY2:
> +		ret = rk3588_pciephy_set_rate(priv, clk->id, rate);
> +		break;
> +#endif
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return ret;
> +};
> +
> +#define ROCKCHIP_MMC_DELAY_SEL		BIT(10)
> +#define ROCKCHIP_MMC_DEGREE_MASK	0x3
> +#define ROCKCHIP_MMC_DELAYNUM_OFFSET	2
> +#define ROCKCHIP_MMC_DELAYNUM_MASK	(0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
> +
> +#define PSECS_PER_SEC 1000000000000LL
> +/*
> + * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
> + * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
> + */
> +#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
> +
> +#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA))
> +static int __maybe_unused rk3588_dclk_vop_set_parent(struct clk *clk,
> +						     struct clk *parent)
> +{
> +	struct rk3588_clk_priv *priv = dev_get_priv(clk->dev);
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 sel;
> +	const char *clock_dev_name = parent->dev->name;
> +
> +	if (parent->id == PLL_V0PLL)
> +		sel = 2;
> +	else if (parent->id == PLL_GPLL)
> +		sel = 0;
> +	else if (parent->id == PLL_CPLL)
> +		sel = 1;
> +	else
> +		sel = 3;
> +
> +	switch (clk->id) {
> +	case DCLK_VOP0_SRC:
> +		rk_clrsetreg(&cru->clksel_con[111], DCLK0_VOP_SRC_SEL_MASK,
> +			     sel << DCLK0_VOP_SRC_SEL_SHIFT);
> +		break;
> +	case DCLK_VOP1_SRC:
> +		rk_clrsetreg(&cru->clksel_con[111], DCLK1_VOP_SRC_SEL_MASK,
> +			     sel << DCLK1_VOP_SRC_SEL_SHIFT);
> +		break;
> +	case DCLK_VOP2_SRC:
> +		rk_clrsetreg(&cru->clksel_con[112], DCLK2_VOP_SRC_SEL_MASK,
> +			     sel << DCLK2_VOP_SRC_SEL_SHIFT);
> +		break;
> +	case DCLK_VOP3:
> +		rk_clrsetreg(&cru->clksel_con[113], DCLK3_VOP_SRC_SEL_MASK,
> +			     sel << DCLK3_VOP_SRC_SEL_SHIFT);
> +		break;
> +	case DCLK_VOP0:
> +		if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
> +			sel = 1;
> +		else if (!strcmp(clock_dev_name, "hdmiphypll_clk1"))
> +			sel = 2;
> +		else
> +			sel = 0;
> +		rk_clrsetreg(&cru->clksel_con[112], DCLK0_VOP_SEL_MASK,
> +			     sel << DCLK0_VOP_SEL_SHIFT);
> +		break;
> +	case DCLK_VOP1:
> +		if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
> +			sel = 1;
> +		else if (!strcmp(clock_dev_name, "hdmiphypll_clk1"))
> +			sel = 2;
> +		else
> +			sel = 0;
> +		rk_clrsetreg(&cru->clksel_con[112], DCLK1_VOP_SEL_MASK,
> +			     sel << DCLK1_VOP_SEL_SHIFT);
> +		break;
> +	case DCLK_VOP2:
> +		if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
> +			sel = 1;
> +		else if (!strcmp(clock_dev_name, "hdmiphypll_clk1"))
> +			sel = 2;
> +		else
> +			sel = 0;
> +		rk_clrsetreg(&cru->clksel_con[112], DCLK2_VOP_SEL_MASK,
> +			     sel << DCLK2_VOP_SEL_SHIFT);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static int rk3588_clk_set_parent(struct clk *clk, struct clk *parent)
> +{
> +	switch (clk->id) {
> +	case DCLK_VOP0_SRC:
> +	case DCLK_VOP1_SRC:
> +	case DCLK_VOP2_SRC:
> +	case DCLK_VOP0:
> +	case DCLK_VOP1:
> +	case DCLK_VOP2:
> +	case DCLK_VOP3:
> +		return rk3588_dclk_vop_set_parent(clk, parent);
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static struct clk_ops rk3588_clk_ops = {
> +	.get_rate = rk3588_clk_get_rate,
> +	.set_rate = rk3588_clk_set_rate,
> +#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA))
> +	.set_parent = rk3588_clk_set_parent,
> +#endif
> +};
> +
> +static void rk3588_clk_init(struct rk3588_clk_priv *priv)
> +{
> +	int ret, div;
> +
> +	div = DIV_ROUND_UP(GPLL_HZ, 300 * MHz);
> +	rk_clrsetreg(&priv->cru->clksel_con[38],
> +		     ACLK_BUS_ROOT_SEL_MASK |
> +		     ACLK_BUS_ROOT_DIV_MASK,
> +		     div << ACLK_BUS_ROOT_DIV_SHIFT);
> +
> +	if (priv->cpll_hz != CPLL_HZ) {
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[CPLL], priv->cru,
> +					    CPLL, CPLL_HZ);
> +		if (!ret)
> +			priv->cpll_hz = CPLL_HZ;
> +	}
> +	if (priv->gpll_hz != GPLL_HZ) {
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[GPLL], priv->cru,
> +					    GPLL, GPLL_HZ);
> +		if (!ret)
> +			priv->gpll_hz = GPLL_HZ;
> +	}
> +
> +#ifdef CONFIG_PCI
> +	if (priv->ppll_hz != PPLL_HZ) {
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[PPLL], priv->cru,
> +					    PPLL, PPLL_HZ);
> +		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
> +						      priv->cru, PPLL);
> +	}
> +#endif
> +	rk_clrsetreg(&priv->cru->clksel_con[9],
> +		     ACLK_TOP_S400_SEL_MASK |
> +		     ACLK_TOP_S200_SEL_MASK,
> +		     (ACLK_TOP_S400_SEL_400M << ACLK_TOP_S400_SEL_SHIFT) |
> +		     (ACLK_TOP_S200_SEL_200M << ACLK_TOP_S200_SEL_SHIFT));
> +}
> +
> +static int rk3588_clk_probe(struct udevice *dev)
> +{
> +	struct rk3588_clk_priv *priv = dev_get_priv(dev);
> +	int ret;
> +
> +	priv->sync_kernel = false;
> +
> +#ifdef CONFIG_SPL_BUILD
> +	rockchip_pll_set_rate(&rk3588_pll_clks[B0PLL], priv->cru,
> +			      B0PLL, LPLL_HZ);
> +	rockchip_pll_set_rate(&rk3588_pll_clks[B1PLL], priv->cru,
> +			      B1PLL, LPLL_HZ);
> +	if (!priv->armclk_enter_hz) {
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[LPLL], priv->cru,
> +					    LPLL, LPLL_HZ);
> +		priv->armclk_enter_hz =
> +			rockchip_pll_get_rate(&rk3588_pll_clks[LPLL],
> +					      priv->cru, LPLL);
> +		priv->armclk_init_hz = priv->armclk_enter_hz;
> +	}
> +#endif
> +
> +	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
> +	if (IS_ERR(priv->grf))
> +		return PTR_ERR(priv->grf);
> +
> +	rk3588_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 rk3588_clk_ofdata_to_platdata(struct udevice *dev)
> +{
> +	struct rk3588_clk_priv *priv = dev_get_priv(dev);
> +
> +	priv->cru = dev_read_addr_ptr(dev);
> +
> +	return 0;
> +}
> +
> +static int rk3588_clk_bind(struct udevice *dev)
> +{
> +	int ret;
> +	struct udevice *sys_child;
> +	struct sysreset_reg *priv;
> +
> +	/* 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 rk3588_cru,
> +						    glb_srst_fst);
> +		priv->glb_srst_snd_value = offsetof(struct rk3588_cru,
> +						    glb_srsr_snd);
> +		dev_set_priv(sys_child, priv);
> +	}
> +
> +#if CONFIG_IS_ENABLED(RESET_ROCKCHIP)
> +	ret = offsetof(struct rk3588_cru, softrst_con[0]);
> +	ret = rockchip_reset_bind(dev, ret, 49158);
> +	if (ret)
> +		debug("Warning: software reset driver bind faile\n");
> +#endif
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id rk3588_clk_ids[] = {
> +	{ .compatible = "rockchip,rk3588-cru" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(rockchip_rk3588_cru) = {
> +	.name		= "rockchip_rk3588_cru",
> +	.id		= UCLASS_CLK,
> +	.of_match	= rk3588_clk_ids,
> +	.priv_auto	= sizeof(struct rk3588_clk_priv),
> +	.of_to_plat	= rk3588_clk_ofdata_to_platdata,
> +	.ops		= &rk3588_clk_ops,
> +	.bind		= rk3588_clk_bind,
> +	.probe		= rk3588_clk_probe,
> +};
Eugen Hristev Feb. 16, 2023, 9 a.m. UTC | #5
On 1/30/23 16:57, Jagan Teki wrote:
> Add clock driver support for Rockchip RK3588 SoC.
> 
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> Signed-off-by: Jagan Teki <jagan@edgeble.ai>
> ---
>   drivers/clk/rockchip/Makefile     |    1 +
>   drivers/clk/rockchip/clk_rk3588.c | 1996 +++++++++++++++++++++++++++++
>   2 files changed, 1997 insertions(+)
>   create mode 100644 drivers/clk/rockchip/clk_rk3588.c
> 
> diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
> index f719f4e379..9e379cc2e3 100644
> --- a/drivers/clk/rockchip/Makefile
> +++ b/drivers/clk/rockchip/Makefile
> @@ -16,5 +16,6 @@ obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
>   obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o
>   obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
>   obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o
> +obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o
>   obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o
>   obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o
> diff --git a/drivers/clk/rockchip/clk_rk3588.c b/drivers/clk/rockchip/clk_rk3588.c


Hello Jagan,

Have you noticed my questions on your previous version? I don't see any 
changes in your current version on these aspects:

https://patchwork.ozlabs.org/project/uboot/patch/20230125222741.303259-6-jagan@edgeble.ai/

and here

https://patchwork.ozlabs.org/project/uboot/patch/20230125222741.303259-12-jagan@edgeble.ai/

Thanks
Jagan Teki Feb. 16, 2023, 9:02 a.m. UTC | #6
Hi Eugen,

On Thu, 16 Feb 2023 at 14:30, Eugen Hristev <eugen.hristev@collabora.com> wrote:
>
> On 1/30/23 16:57, Jagan Teki wrote:
> > Add clock driver support for Rockchip RK3588 SoC.
> >
> > Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> > Signed-off-by: Jagan Teki <jagan@edgeble.ai>
> > ---
> >   drivers/clk/rockchip/Makefile     |    1 +
> >   drivers/clk/rockchip/clk_rk3588.c | 1996 +++++++++++++++++++++++++++++
> >   2 files changed, 1997 insertions(+)
> >   create mode 100644 drivers/clk/rockchip/clk_rk3588.c
> >
> > diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
> > index f719f4e379..9e379cc2e3 100644
> > --- a/drivers/clk/rockchip/Makefile
> > +++ b/drivers/clk/rockchip/Makefile
> > @@ -16,5 +16,6 @@ obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
> >   obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o
> >   obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
> >   obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o
> > +obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o
> >   obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o
> >   obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o
> > diff --git a/drivers/clk/rockchip/clk_rk3588.c b/drivers/clk/rockchip/clk_rk3588.c
>
>
> Hello Jagan,
>
> Have you noticed my questions on your previous version? I don't see any
> changes in your current version on these aspects:
>
> https://patchwork.ozlabs.org/project/uboot/patch/20230125222741.303259-6-jagan@edgeble.ai/
>
> and here
>
> https://patchwork.ozlabs.org/project/uboot/patch/20230125222741.303259-12-jagan@edgeble.ai/

Sorry for the late response, I will respond to it on that thread.

Thanks,
Jagan.
Eugen Hristev Feb. 16, 2023, 9:42 a.m. UTC | #7
On 2/16/23 11:02, Jagan Teki wrote:
> Hi Eugen,
> 
> On Thu, 16 Feb 2023 at 14:30, Eugen Hristev <eugen.hristev@collabora.com> wrote:
>>
>> On 1/30/23 16:57, Jagan Teki wrote:
>>> Add clock driver support for Rockchip RK3588 SoC.
>>>
>>> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
>>> Signed-off-by: Jagan Teki <jagan@edgeble.ai>
>>> ---
>>>    drivers/clk/rockchip/Makefile     |    1 +
>>>    drivers/clk/rockchip/clk_rk3588.c | 1996 +++++++++++++++++++++++++++++
>>>    2 files changed, 1997 insertions(+)
>>>    create mode 100644 drivers/clk/rockchip/clk_rk3588.c
>>>
>>> diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
>>> index f719f4e379..9e379cc2e3 100644
>>> --- a/drivers/clk/rockchip/Makefile
>>> +++ b/drivers/clk/rockchip/Makefile
>>> @@ -16,5 +16,6 @@ obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
>>>    obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o
>>>    obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
>>>    obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o
>>> +obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o
>>>    obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o
>>>    obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o
>>> diff --git a/drivers/clk/rockchip/clk_rk3588.c b/drivers/clk/rockchip/clk_rk3588.c
>>
>>
>> Hello Jagan,
>>
>> Have you noticed my questions on your previous version? I don't see any
>> changes in your current version on these aspects:
>>
>> https://patchwork.ozlabs.org/project/uboot/patch/20230125222741.303259-6-jagan@edgeble.ai/
>>
>> and here
>>
>> https://patchwork.ozlabs.org/project/uboot/patch/20230125222741.303259-12-jagan@edgeble.ai/
> 
> Sorry for the late response, I will respond to it on that thread.
> 
> Thanks,
> Jagan.


Sorry, I was wrong, you did some changes, now the print messages are 
removed from the code, so no more errors are printed.
I don't know if this is the right way though.

I am testing your patches on the rk3588 rock5b and come back with any 
findings.

Have you figured out what to do with the SDMMC init clock (400 kHz) ? 
This still appears to be broken

Thanks,
Eugen
Jonas Karlman Feb. 16, 2023, 9:44 p.m. UTC | #8
Hi Jagan,

On 2023-01-30 15:57, Jagan Teki wrote:
> Add clock driver support for Rockchip RK3588 SoC.
> 
> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
> Signed-off-by: Jagan Teki <jagan@edgeble.ai>
> ---
>  drivers/clk/rockchip/Makefile     |    1 +
>  drivers/clk/rockchip/clk_rk3588.c | 1996 +++++++++++++++++++++++++++++
>  2 files changed, 1997 insertions(+)
>  create mode 100644 drivers/clk/rockchip/clk_rk3588.c
> 
> diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
> index f719f4e379..9e379cc2e3 100644
> --- a/drivers/clk/rockchip/Makefile
> +++ b/drivers/clk/rockchip/Makefile
> @@ -16,5 +16,6 @@ obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
>  obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o
>  obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
>  obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o
> +obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o
>  obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o
>  obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o
> diff --git a/drivers/clk/rockchip/clk_rk3588.c b/drivers/clk/rockchip/clk_rk3588.c
> new file mode 100644
> index 0000000000..b87b023bd7
> --- /dev/null
> +++ b/drivers/clk/rockchip/clk_rk3588.c
> @@ -0,0 +1,1996 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 Fuzhou Rockchip Electronics Co., Ltd
> + * Author: Elaine Zhang <zhangqing@rock-chips.com>
> + */
> +
> +#include <common.h>
> +#include <bitfield.h>
> +#include <clk-uclass.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <syscon.h>
> +#include <asm/arch-rockchip/cru_rk3588.h>
> +#include <asm/arch-rockchip/clock.h>
> +#include <asm/arch-rockchip/hardware.h>
> +#include <asm/io.h>
> +#include <dm/device-internal.h>
> +#include <dm/lists.h>
> +#include <dt-bindings/clock/rockchip,rk3588-cru.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +#define DIV_TO_RATE(input_rate, div)	((input_rate) / ((div) + 1))
> +
> +static struct rockchip_pll_rate_table rk3588_pll_rates[] = {
> +	/* _mhz, _p, _m, _s, _k */
> +	RK3588_PLL_RATE(1500000000, 2, 250, 1, 0),
> +	RK3588_PLL_RATE(1200000000, 2, 200, 1, 0),
> +	RK3588_PLL_RATE(1188000000, 2, 198, 1, 0),
> +	RK3588_PLL_RATE(1100000000, 3, 550, 2, 0),
> +	RK3588_PLL_RATE(1008000000, 2, 336, 2, 0),
> +	RK3588_PLL_RATE(1000000000, 3, 500, 2, 0),
> +	RK3588_PLL_RATE(900000000, 2, 300, 2, 0),
> +	RK3588_PLL_RATE(850000000, 3, 425, 2, 0),
> +	RK3588_PLL_RATE(816000000, 2, 272, 2, 0),
> +	RK3588_PLL_RATE(786432000, 2, 262, 2, 9437),
> +	RK3588_PLL_RATE(786000000, 1, 131, 2, 0),
> +	RK3588_PLL_RATE(722534400, 8, 963, 2, 24850),
> +	RK3588_PLL_RATE(600000000, 2, 200, 2, 0),
> +	RK3588_PLL_RATE(594000000, 2, 198, 2, 0),
> +	RK3588_PLL_RATE(200000000, 3, 400, 4, 0),
> +	RK3588_PLL_RATE(100000000, 3, 400, 5, 0),
> +	{ /* sentinel */ },
> +};
> +
> +static struct rockchip_pll_clock rk3588_pll_clks[] = {
> +	[B0PLL] = PLL(pll_rk3588, PLL_B0PLL, RK3588_B0_PLL_CON(0),
> +		      RK3588_B0_PLL_MODE_CON, 0, 15, 0,
> +		      rk3588_pll_rates),
> +	[B1PLL] = PLL(pll_rk3588, PLL_B1PLL, RK3588_B1_PLL_CON(8),
> +		      RK3588_B1_PLL_MODE_CON, 0, 15, 0,
> +		      rk3588_pll_rates),
> +	[LPLL] = PLL(pll_rk3588, PLL_LPLL, RK3588_LPLL_CON(16),
> +		     RK3588_LPLL_MODE_CON, 0, 15, 0, rk3588_pll_rates),
> +	[V0PLL] = PLL(pll_rk3588, PLL_V0PLL, RK3588_PLL_CON(88),
> +		      RK3588_MODE_CON0, 4, 15, 0, rk3588_pll_rates),
> +	[AUPLL] = PLL(pll_rk3588, PLL_AUPLL, RK3588_PLL_CON(96),
> +		      RK3588_MODE_CON0, 6, 15, 0, rk3588_pll_rates),
> +	[CPLL] = PLL(pll_rk3588, PLL_CPLL, RK3588_PLL_CON(104),
> +		     RK3588_MODE_CON0, 8, 15, 0, rk3588_pll_rates),
> +	[GPLL] = PLL(pll_rk3588, PLL_GPLL, RK3588_PLL_CON(112),
> +		     RK3588_MODE_CON0, 2, 15, 0, rk3588_pll_rates),
> +	[NPLL] = PLL(pll_rk3588, PLL_NPLL, RK3588_PLL_CON(120),
> +		     RK3588_MODE_CON0, 0, 15, 0, rk3588_pll_rates),
> +	[PPLL] = PLL(pll_rk3588, PLL_PPLL, RK3588_PMU_PLL_CON(128),
> +		     RK3588_MODE_CON0, 10, 15, 0, rk3588_pll_rates),
> +};
> +
> +#ifndef CONFIG_SPL_BUILD
> +/*
> + *
> + * rational_best_approximation(31415, 10000,
> + *		(1 << 8) - 1, (1 << 5) - 1, &n, &d);
> + *
> + * you may look at given_numerator as a fixed point number,
> + * with the fractional part size described in given_denominator.
> + *
> + * for theoretical background, see:
> + * http://en.wikipedia.org/wiki/Continued_fraction
> + */
> +static void rational_best_approximation(unsigned long given_numerator,
> +					unsigned long given_denominator,
> +					unsigned long max_numerator,
> +					unsigned long max_denominator,
> +					unsigned long *best_numerator,
> +					unsigned long *best_denominator)
> +{
> +	unsigned long n, d, n0, d0, n1, d1;
> +
> +	n = given_numerator;
> +	d = given_denominator;
> +	n0 = 0;
> +	d1 = 0;
> +	n1 = 1;
> +	d0 = 1;
> +	for (;;) {
> +		unsigned long t, a;
> +
> +		if (n1 > max_numerator || d1 > max_denominator) {
> +			n1 = n0;
> +			d1 = d0;
> +			break;
> +		}
> +		if (d == 0)
> +			break;
> +		t = d;
> +		a = n / d;
> +		d = n % d;
> +		n = t;
> +		t = n0 + a * n1;
> +		n0 = n1;
> +		n1 = t;
> +		t = d0 + a * d1;
> +		d0 = d1;
> +		d1 = t;
> +	}
> +	*best_numerator = n1;
> +	*best_denominator = d1;
> +}
> +#endif
> +

[...]

> +
> +static ulong rk3588_top_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
> +{
> +	struct rk3588_cru *cru = priv->cru;
> +	u32 con, sel, div, rate, prate;
> +
> +	switch (clk_id) {
> +	case ACLK_TOP_ROOT:
> +		con = readl(&cru->clksel_con[8]);
> +		div = (con & ACLK_TOP_ROOT_DIV_MASK) >>
> +		      ACLK_TOP_ROOT_DIV_SHIFT;
> +		sel = (con & ACLK_TOP_ROOT_SRC_SEL_MASK) >>
> +		      ACLK_TOP_ROOT_SRC_SEL_SHIFT;
> +		if (sel == ACLK_TOP_ROOT_SRC_SEL_CPLL)
> +			prate = priv->cpll_hz;
> +		else
> +			prate = priv->cpll_hz;

Should be gpll_hz instead of cpll_hz.

> +		return DIV_TO_RATE(prate, div);
> +	case ACLK_LOW_TOP_ROOT:
> +		con = readl(&cru->clksel_con[8]);
> +		div = (con & ACLK_LOW_TOP_ROOT_DIV_MASK) >>
> +		      ACLK_LOW_TOP_ROOT_DIV_SHIFT;
> +		sel = (con & ACLK_LOW_TOP_ROOT_SRC_SEL_MASK) >>
> +		      ACLK_LOW_TOP_ROOT_SRC_SEL_SHIFT;
> +		if (sel == ACLK_LOW_TOP_ROOT_SRC_SEL_CPLL)
> +			prate = priv->cpll_hz;
> +		else
> +			prate = priv->gpll_hz;
> +		return DIV_TO_RATE(prate, div);
> +	case PCLK_TOP_ROOT:
> +		con = readl(&cru->clksel_con[8]);
> +		sel = (con & PCLK_TOP_ROOT_SEL_MASK) >> PCLK_TOP_ROOT_SEL_SHIFT;
> +		if (sel == PCLK_TOP_ROOT_SEL_100M)
> +			rate = 100 * MHz;
> +		else if (sel == PCLK_TOP_ROOT_SEL_50M)
> +			rate = 50 * MHz;
> +		else
> +			rate = OSC_HZ;
> +		break;
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rate;
> +}
> +

[...]

> +
> +static ulong rk3588_clk_get_rate(struct clk *clk)
> +{
> +	struct rk3588_clk_priv *priv = dev_get_priv(clk->dev);
> +	ulong rate = 0;
> +
> +	if (!priv->gpll_hz) {
> +		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
> +		return -ENOENT;
> +	}
> +
> +	if (!priv->ppll_hz) {
> +		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
> +						      priv->cru, PPLL);
> +	}
> +
> +	switch (clk->id) {
> +	case PLL_LPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[LPLL], priv->cru,
> +					     LPLL);
> +		break;
> +	case PLL_B0PLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[B0PLL], priv->cru,
> +					     B0PLL);
> +		break;
> +	case PLL_B1PLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[B1PLL], priv->cru,
> +					     B1PLL);
> +		break;
> +	case PLL_GPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[GPLL], priv->cru,
> +					     GPLL);
> +		break;
> +	case PLL_CPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[CPLL], priv->cru,
> +					     CPLL);
> +		break;
> +	case PLL_NPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[NPLL], priv->cru,
> +					     NPLL);
> +		break;
> +	case PLL_V0PLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL], priv->cru,
> +					     V0PLL);
> +		break;
> +	case PLL_AUPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[AUPLL], priv->cru,
> +					     AUPLL);
> +		break;
> +	case PLL_PPLL:
> +		rate = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL], priv->cru,
> +					     PPLL);
> +		break;
> +	case ACLK_CENTER_ROOT:
> +	case PCLK_CENTER_ROOT:
> +	case HCLK_CENTER_ROOT:
> +	case ACLK_CENTER_LOW_ROOT:
> +		rate = rk3588_center_get_clk(priv, clk->id);
> +		break;
> +	case ACLK_TOP_ROOT:
> +	case PCLK_TOP_ROOT:
> +	case ACLK_LOW_TOP_ROOT:
> +		rate = rk3588_top_get_clk(priv, clk->id);
> +		break;
> +	case CLK_I2C0:
> +	case CLK_I2C1:
> +	case CLK_I2C2:
> +	case CLK_I2C3:
> +	case CLK_I2C4:
> +	case CLK_I2C5:
> +	case CLK_I2C6:
> +	case CLK_I2C7:
> +	case CLK_I2C8:
> +		rate = rk3588_i2c_get_clk(priv, clk->id);
> +		break;
> +	case CLK_SPI0:
> +	case CLK_SPI1:
> +	case CLK_SPI2:
> +	case CLK_SPI3:
> +	case CLK_SPI4:
> +		rate = rk3588_spi_get_clk(priv, clk->id);
> +		break;
> +	case CLK_PWM1:
> +	case CLK_PWM2:
> +	case CLK_PWM3:
> +	case CLK_PMU1PWM:
> +		rate = rk3588_pwm_get_clk(priv, clk->id);
> +		break;
> +	case CLK_SARADC:
> +	case CLK_TSADC:
> +		rate = rk3588_adc_get_clk(priv, clk->id);
> +		break;
> +	case CCLK_SRC_SDIO:
> +	case CCLK_EMMC:
> +	case BCLK_EMMC:
> +	case SCLK_SFC:
> +	case DCLK_DECOM:
> +		rate = rk3588_mmc_get_clk(priv, clk->id);
> +		break;
> +	case TCLK_WDT0:
> +		rate = OSC_HZ;
> +		break;
> +#ifndef CONFIG_SPL_BUILD
> +	case CLK_AUX16M_0:
> +	case CLK_AUX16M_1:
> +		rk3588_aux16m_get_clk(priv, clk->id);

This is missing "rate =" before function call.

> +		break;
> +	case ACLK_VOP_ROOT:
> +	case ACLK_VOP:
> +	case ACLK_VOP_LOW_ROOT:
> +	case HCLK_VOP_ROOT:
> +		rate = rk3588_aclk_vop_get_clk(priv, clk->id);
> +		break;
> +	case DCLK_VOP0:
> +	case DCLK_VOP0_SRC:
> +	case DCLK_VOP1:
> +	case DCLK_VOP1_SRC:
> +	case DCLK_VOP2:
> +	case DCLK_VOP2_SRC:
> +	case DCLK_VOP3:
> +		rate = rk3588_dclk_vop_get_clk(priv, clk->id);
> +		break;
> +	case CLK_GMAC0_PTP_REF:
> +	case CLK_GMAC1_PTP_REF:
> +	case CLK_GMAC_125M:
> +	case CLK_GMAC_50M:
> +		rate = rk3588_gmac_get_clk(priv, clk->id);
> +		break;
> +	case SCLK_UART1:
> +	case SCLK_UART2:
> +	case SCLK_UART3:
> +	case SCLK_UART4:
> +	case SCLK_UART5:
> +	case SCLK_UART6:
> +	case SCLK_UART7:
> +	case SCLK_UART8:
> +	case SCLK_UART9:
> +		rate = rk3588_uart_get_rate(priv, clk->id);
> +		break;
> +	case CLK_REF_PIPE_PHY0:
> +	case CLK_REF_PIPE_PHY1:
> +	case CLK_REF_PIPE_PHY2:
> +		rate = rk3588_pciephy_get_rate(priv, clk->id);
> +		break;
> +#endif
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return rate;
> +};
> +
> +static ulong rk3588_clk_set_rate(struct clk *clk, ulong rate)
> +{
> +	struct rk3588_clk_priv *priv = dev_get_priv(clk->dev);
> +	ulong ret = 0;
> +
> +	if (!priv->gpll_hz) {
> +		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
> +		return -ENOENT;
> +	}
> +
> +	if (!priv->ppll_hz) {
> +		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
> +						      priv->cru, PPLL);
> +	}
> +
> +	switch (clk->id) {
> +	case PLL_CPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[CPLL], priv->cru,
> +					    CPLL, rate);
> +		priv->cpll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[CPLL],
> +						      priv->cru, CPLL);
> +		break;
> +	case PLL_GPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[GPLL], priv->cru,
> +					    GPLL, rate);
> +		priv->gpll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[GPLL],
> +						      priv->cru, GPLL);
> +		break;
> +	case PLL_NPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[NPLL], priv->cru,
> +					    NPLL, rate);
> +		break;
> +	case PLL_V0PLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL], priv->cru,
> +					    V0PLL, rate);
> +		priv->v0pll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL],
> +						       priv->cru, V0PLL);
> +		break;
> +	case PLL_AUPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[AUPLL], priv->cru,
> +					    AUPLL, rate);
> +		priv->aupll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[AUPLL],
> +						       priv->cru, AUPLL);
> +		break;
> +	case PLL_PPLL:
> +		ret = rockchip_pll_set_rate(&rk3588_pll_clks[PPLL], priv->cru,
> +					    PPLL, rate);
> +		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
> +						      priv->cru, PPLL);
> +		break;
> +	case ACLK_CENTER_ROOT:
> +	case PCLK_CENTER_ROOT:
> +	case HCLK_CENTER_ROOT:
> +	case ACLK_CENTER_LOW_ROOT:
> +		ret = rk3588_center_set_clk(priv, clk->id, rate);
> +		break;
> +	case ACLK_TOP_ROOT:
> +	case PCLK_TOP_ROOT:
> +	case ACLK_LOW_TOP_ROOT:
> +		ret = rk3588_top_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_I2C0:
> +	case CLK_I2C1:
> +	case CLK_I2C2:
> +	case CLK_I2C3:
> +	case CLK_I2C4:
> +	case CLK_I2C5:
> +	case CLK_I2C6:
> +	case CLK_I2C7:
> +	case CLK_I2C8:
> +		ret = rk3588_i2c_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_SPI0:
> +	case CLK_SPI1:
> +	case CLK_SPI2:
> +	case CLK_SPI3:
> +	case CLK_SPI4:
> +		ret = rk3588_spi_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_PWM1:
> +	case CLK_PWM2:
> +	case CLK_PWM3:
> +	case CLK_PMU1PWM:
> +		ret = rk3588_pwm_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_SARADC:
> +	case CLK_TSADC:
> +		ret = rk3588_adc_set_clk(priv, clk->id, rate);
> +		break;
> +	case CCLK_SRC_SDIO:
> +	case CCLK_EMMC:
> +	case BCLK_EMMC:
> +	case SCLK_SFC:
> +	case DCLK_DECOM:
> +		ret = rk3588_mmc_set_clk(priv, clk->id, rate);
> +		break;
> +	case TCLK_WDT0:
> +		ret = OSC_HZ;
> +		break;
> +#ifndef CONFIG_SPL_BUILD
> +	case CLK_AUX16M_0:
> +	case CLK_AUX16M_1:
> +		rk3588_aux16m_set_clk(priv, clk->id, rate);

This is missing "ret = " before function call.

Regards,
Jonas

> +		break;
> +	case ACLK_VOP_ROOT:
> +	case ACLK_VOP:
> +	case ACLK_VOP_LOW_ROOT:
> +	case HCLK_VOP_ROOT:
> +		ret = rk3588_aclk_vop_set_clk(priv, clk->id, rate);
> +		break;
> +	case DCLK_VOP0:
> +	case DCLK_VOP0_SRC:
> +	case DCLK_VOP1:
> +	case DCLK_VOP1_SRC:
> +	case DCLK_VOP2:
> +	case DCLK_VOP2_SRC:
> +	case DCLK_VOP3:
> +		ret = rk3588_dclk_vop_set_clk(priv, clk->id, rate);
> +		break;
> +	case CLK_GMAC0_PTP_REF:
> +	case CLK_GMAC1_PTP_REF:
> +	case CLK_GMAC_125M:
> +	case CLK_GMAC_50M:
> +		ret = rk3588_gmac_set_clk(priv, clk->id, rate);
> +		break;
> +	case SCLK_UART1:
> +	case SCLK_UART2:
> +	case SCLK_UART3:
> +	case SCLK_UART4:
> +	case SCLK_UART5:
> +	case SCLK_UART6:
> +	case SCLK_UART7:
> +	case SCLK_UART8:
> +	case SCLK_UART9:
> +		ret = rk3588_uart_set_rate(priv, clk->id, rate);
> +		break;
> +	case CLK_REF_PIPE_PHY0:
> +	case CLK_REF_PIPE_PHY1:
> +	case CLK_REF_PIPE_PHY2:
> +		ret = rk3588_pciephy_set_rate(priv, clk->id, rate);
> +		break;
> +#endif
> +	default:
> +		return -ENOENT;
> +	}
> +
> +	return ret;
> +};
> +

[...]
Kever Yang Feb. 21, 2023, 9:38 a.m. UTC | #9
Hi Jagan,

On 2023/2/17 05:44, Jonas Karlman wrote:
>> +			prate = priv->cpll_hz;
> Should be gpll_hz instead of cpll_hz.
>
Do you have new patchset for this series, I will fix this if there is no 
new version for other patches.


Thanks,
- Kever
Jagan Teki Feb. 21, 2023, 11:50 a.m. UTC | #10
Hi Kever,

On Tue, 21 Feb 2023 at 15:08, Kever Yang <kever.yang@rock-chips.com> wrote:
>
> Hi Jagan,
>
> On 2023/2/17 05:44, Jonas Karlman wrote:
>
> + prate = priv->cpll_hz;
>
> Should be gpll_hz instead of cpll_hz.
>
> Do you have new patchset for this series, I will fix this if there is no new version for other patches.

Please fix this, I don't have any new patchset.

Thanksm
Jagan.
diff mbox series

Patch

diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index f719f4e379..9e379cc2e3 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -16,5 +16,6 @@  obj-$(CONFIG_ROCKCHIP_RK3328) += clk_rk3328.o
 obj-$(CONFIG_ROCKCHIP_RK3368) += clk_rk3368.o
 obj-$(CONFIG_ROCKCHIP_RK3399) += clk_rk3399.o
 obj-$(CONFIG_ROCKCHIP_RK3568) += clk_rk3568.o
+obj-$(CONFIG_ROCKCHIP_RK3588) += clk_rk3588.o
 obj-$(CONFIG_ROCKCHIP_RV1108) += clk_rv1108.o
 obj-$(CONFIG_ROCKCHIP_RV1126) += clk_rv1126.o
diff --git a/drivers/clk/rockchip/clk_rk3588.c b/drivers/clk/rockchip/clk_rk3588.c
new file mode 100644
index 0000000000..b87b023bd7
--- /dev/null
+++ b/drivers/clk/rockchip/clk_rk3588.c
@@ -0,0 +1,1996 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Elaine Zhang <zhangqing@rock-chips.com>
+ */
+
+#include <common.h>
+#include <bitfield.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/arch-rockchip/cru_rk3588.h>
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/hardware.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dt-bindings/clock/rockchip,rk3588-cru.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DIV_TO_RATE(input_rate, div)	((input_rate) / ((div) + 1))
+
+static struct rockchip_pll_rate_table rk3588_pll_rates[] = {
+	/* _mhz, _p, _m, _s, _k */
+	RK3588_PLL_RATE(1500000000, 2, 250, 1, 0),
+	RK3588_PLL_RATE(1200000000, 2, 200, 1, 0),
+	RK3588_PLL_RATE(1188000000, 2, 198, 1, 0),
+	RK3588_PLL_RATE(1100000000, 3, 550, 2, 0),
+	RK3588_PLL_RATE(1008000000, 2, 336, 2, 0),
+	RK3588_PLL_RATE(1000000000, 3, 500, 2, 0),
+	RK3588_PLL_RATE(900000000, 2, 300, 2, 0),
+	RK3588_PLL_RATE(850000000, 3, 425, 2, 0),
+	RK3588_PLL_RATE(816000000, 2, 272, 2, 0),
+	RK3588_PLL_RATE(786432000, 2, 262, 2, 9437),
+	RK3588_PLL_RATE(786000000, 1, 131, 2, 0),
+	RK3588_PLL_RATE(722534400, 8, 963, 2, 24850),
+	RK3588_PLL_RATE(600000000, 2, 200, 2, 0),
+	RK3588_PLL_RATE(594000000, 2, 198, 2, 0),
+	RK3588_PLL_RATE(200000000, 3, 400, 4, 0),
+	RK3588_PLL_RATE(100000000, 3, 400, 5, 0),
+	{ /* sentinel */ },
+};
+
+static struct rockchip_pll_clock rk3588_pll_clks[] = {
+	[B0PLL] = PLL(pll_rk3588, PLL_B0PLL, RK3588_B0_PLL_CON(0),
+		      RK3588_B0_PLL_MODE_CON, 0, 15, 0,
+		      rk3588_pll_rates),
+	[B1PLL] = PLL(pll_rk3588, PLL_B1PLL, RK3588_B1_PLL_CON(8),
+		      RK3588_B1_PLL_MODE_CON, 0, 15, 0,
+		      rk3588_pll_rates),
+	[LPLL] = PLL(pll_rk3588, PLL_LPLL, RK3588_LPLL_CON(16),
+		     RK3588_LPLL_MODE_CON, 0, 15, 0, rk3588_pll_rates),
+	[V0PLL] = PLL(pll_rk3588, PLL_V0PLL, RK3588_PLL_CON(88),
+		      RK3588_MODE_CON0, 4, 15, 0, rk3588_pll_rates),
+	[AUPLL] = PLL(pll_rk3588, PLL_AUPLL, RK3588_PLL_CON(96),
+		      RK3588_MODE_CON0, 6, 15, 0, rk3588_pll_rates),
+	[CPLL] = PLL(pll_rk3588, PLL_CPLL, RK3588_PLL_CON(104),
+		     RK3588_MODE_CON0, 8, 15, 0, rk3588_pll_rates),
+	[GPLL] = PLL(pll_rk3588, PLL_GPLL, RK3588_PLL_CON(112),
+		     RK3588_MODE_CON0, 2, 15, 0, rk3588_pll_rates),
+	[NPLL] = PLL(pll_rk3588, PLL_NPLL, RK3588_PLL_CON(120),
+		     RK3588_MODE_CON0, 0, 15, 0, rk3588_pll_rates),
+	[PPLL] = PLL(pll_rk3588, PLL_PPLL, RK3588_PMU_PLL_CON(128),
+		     RK3588_MODE_CON0, 10, 15, 0, rk3588_pll_rates),
+};
+
+#ifndef CONFIG_SPL_BUILD
+/*
+ *
+ * rational_best_approximation(31415, 10000,
+ *		(1 << 8) - 1, (1 << 5) - 1, &n, &d);
+ *
+ * you may look at given_numerator as a fixed point number,
+ * with the fractional part size described in given_denominator.
+ *
+ * for theoretical background, see:
+ * http://en.wikipedia.org/wiki/Continued_fraction
+ */
+static void rational_best_approximation(unsigned long given_numerator,
+					unsigned long given_denominator,
+					unsigned long max_numerator,
+					unsigned long max_denominator,
+					unsigned long *best_numerator,
+					unsigned long *best_denominator)
+{
+	unsigned long n, d, n0, d0, n1, d1;
+
+	n = given_numerator;
+	d = given_denominator;
+	n0 = 0;
+	d1 = 0;
+	n1 = 1;
+	d0 = 1;
+	for (;;) {
+		unsigned long t, a;
+
+		if (n1 > max_numerator || d1 > max_denominator) {
+			n1 = n0;
+			d1 = d0;
+			break;
+		}
+		if (d == 0)
+			break;
+		t = d;
+		a = n / d;
+		d = n % d;
+		n = t;
+		t = n0 + a * n1;
+		n0 = n1;
+		n1 = t;
+		t = d0 + a * d1;
+		d0 = d1;
+		d1 = t;
+	}
+	*best_numerator = n1;
+	*best_denominator = d1;
+}
+#endif
+
+static ulong rk3588_center_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 con, sel, rate;
+
+	switch (clk_id) {
+	case ACLK_CENTER_ROOT:
+		con = readl(&cru->clksel_con[165]);
+		sel = (con & ACLK_CENTER_ROOT_SEL_MASK) >>
+		      ACLK_CENTER_ROOT_SEL_SHIFT;
+		if (sel == ACLK_CENTER_ROOT_SEL_700M)
+			rate = 702 * MHz;
+		else if (sel == ACLK_CENTER_ROOT_SEL_400M)
+			rate = 396 * MHz;
+		else if (sel == ACLK_CENTER_ROOT_SEL_200M)
+			rate = 200 * MHz;
+		else
+			rate = OSC_HZ;
+		break;
+	case ACLK_CENTER_LOW_ROOT:
+		con = readl(&cru->clksel_con[165]);
+		sel = (con & ACLK_CENTER_LOW_ROOT_SEL_MASK) >>
+		      ACLK_CENTER_LOW_ROOT_SEL_SHIFT;
+		if (sel == ACLK_CENTER_LOW_ROOT_SEL_500M)
+			rate = 500 * MHz;
+		else if (sel == ACLK_CENTER_LOW_ROOT_SEL_250M)
+			rate = 250 * MHz;
+		else if (sel == ACLK_CENTER_LOW_ROOT_SEL_100M)
+			rate = 100 * MHz;
+		else
+			rate = OSC_HZ;
+		break;
+	case HCLK_CENTER_ROOT:
+		con = readl(&cru->clksel_con[165]);
+		sel = (con & HCLK_CENTER_ROOT_SEL_MASK) >>
+		      HCLK_CENTER_ROOT_SEL_SHIFT;
+		if (sel == HCLK_CENTER_ROOT_SEL_400M)
+			rate = 396 * MHz;
+		else if (sel == HCLK_CENTER_ROOT_SEL_200M)
+			rate = 200 * MHz;
+		else if (sel == HCLK_CENTER_ROOT_SEL_100M)
+			rate = 100 * MHz;
+		else
+			rate = OSC_HZ;
+		break;
+	case PCLK_CENTER_ROOT:
+		con = readl(&cru->clksel_con[165]);
+		sel = (con & PCLK_CENTER_ROOT_SEL_MASK) >>
+		      PCLK_CENTER_ROOT_SEL_SHIFT;
+		if (sel == PCLK_CENTER_ROOT_SEL_200M)
+			rate = 200 * MHz;
+		else if (sel == PCLK_CENTER_ROOT_SEL_100M)
+			rate = 100 * MHz;
+		else if (sel == PCLK_CENTER_ROOT_SEL_50M)
+			rate = 50 * MHz;
+		else
+			rate = OSC_HZ;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rate;
+}
+
+static ulong rk3588_center_set_clk(struct rk3588_clk_priv *priv,
+				   ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int src_clk;
+
+	switch (clk_id) {
+	case ACLK_CENTER_ROOT:
+		if (rate >= 700 * MHz)
+			src_clk = ACLK_CENTER_ROOT_SEL_700M;
+		else if (rate >= 396 * MHz)
+			src_clk = ACLK_CENTER_ROOT_SEL_400M;
+		else if (rate >= 200 * MHz)
+			src_clk = ACLK_CENTER_ROOT_SEL_200M;
+		else
+			src_clk = ACLK_CENTER_ROOT_SEL_24M;
+		rk_clrsetreg(&cru->clksel_con[165],
+			     ACLK_CENTER_ROOT_SEL_MASK,
+			     src_clk << ACLK_CENTER_ROOT_SEL_SHIFT);
+		break;
+	case ACLK_CENTER_LOW_ROOT:
+		if (rate >= 500 * MHz)
+			src_clk = ACLK_CENTER_LOW_ROOT_SEL_500M;
+		else if (rate >= 250 * MHz)
+			src_clk = ACLK_CENTER_LOW_ROOT_SEL_250M;
+		else if (rate >= 99 * MHz)
+			src_clk = ACLK_CENTER_LOW_ROOT_SEL_100M;
+		else
+			src_clk = ACLK_CENTER_LOW_ROOT_SEL_24M;
+		rk_clrsetreg(&cru->clksel_con[165],
+			     ACLK_CENTER_LOW_ROOT_SEL_MASK,
+			     src_clk << ACLK_CENTER_LOW_ROOT_SEL_SHIFT);
+		break;
+	case HCLK_CENTER_ROOT:
+		if (rate >= 396 * MHz)
+			src_clk = HCLK_CENTER_ROOT_SEL_400M;
+		else if (rate >= 198 * MHz)
+			src_clk = HCLK_CENTER_ROOT_SEL_200M;
+		else if (rate >= 99 * MHz)
+			src_clk = HCLK_CENTER_ROOT_SEL_100M;
+		else
+			src_clk = HCLK_CENTER_ROOT_SEL_24M;
+		rk_clrsetreg(&cru->clksel_con[165],
+			     HCLK_CENTER_ROOT_SEL_MASK,
+			     src_clk << HCLK_CENTER_ROOT_SEL_SHIFT);
+		break;
+	case PCLK_CENTER_ROOT:
+		if (rate >= 198 * MHz)
+			src_clk = PCLK_CENTER_ROOT_SEL_200M;
+		else if (rate >= 99 * MHz)
+			src_clk = PCLK_CENTER_ROOT_SEL_100M;
+		else if (rate >= 50 * MHz)
+			src_clk = PCLK_CENTER_ROOT_SEL_50M;
+		else
+			src_clk = PCLK_CENTER_ROOT_SEL_24M;
+		rk_clrsetreg(&cru->clksel_con[165],
+			     PCLK_CENTER_ROOT_SEL_MASK,
+			     src_clk << PCLK_CENTER_ROOT_SEL_SHIFT);
+		break;
+	default:
+		printf("do not support this center freq\n");
+		return -EINVAL;
+	}
+
+	return rk3588_center_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_top_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 con, sel, div, rate, prate;
+
+	switch (clk_id) {
+	case ACLK_TOP_ROOT:
+		con = readl(&cru->clksel_con[8]);
+		div = (con & ACLK_TOP_ROOT_DIV_MASK) >>
+		      ACLK_TOP_ROOT_DIV_SHIFT;
+		sel = (con & ACLK_TOP_ROOT_SRC_SEL_MASK) >>
+		      ACLK_TOP_ROOT_SRC_SEL_SHIFT;
+		if (sel == ACLK_TOP_ROOT_SRC_SEL_CPLL)
+			prate = priv->cpll_hz;
+		else
+			prate = priv->cpll_hz;
+		return DIV_TO_RATE(prate, div);
+	case ACLK_LOW_TOP_ROOT:
+		con = readl(&cru->clksel_con[8]);
+		div = (con & ACLK_LOW_TOP_ROOT_DIV_MASK) >>
+		      ACLK_LOW_TOP_ROOT_DIV_SHIFT;
+		sel = (con & ACLK_LOW_TOP_ROOT_SRC_SEL_MASK) >>
+		      ACLK_LOW_TOP_ROOT_SRC_SEL_SHIFT;
+		if (sel == ACLK_LOW_TOP_ROOT_SRC_SEL_CPLL)
+			prate = priv->cpll_hz;
+		else
+			prate = priv->gpll_hz;
+		return DIV_TO_RATE(prate, div);
+	case PCLK_TOP_ROOT:
+		con = readl(&cru->clksel_con[8]);
+		sel = (con & PCLK_TOP_ROOT_SEL_MASK) >> PCLK_TOP_ROOT_SEL_SHIFT;
+		if (sel == PCLK_TOP_ROOT_SEL_100M)
+			rate = 100 * MHz;
+		else if (sel == PCLK_TOP_ROOT_SEL_50M)
+			rate = 50 * MHz;
+		else
+			rate = OSC_HZ;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rate;
+}
+
+static ulong rk3588_top_set_clk(struct rk3588_clk_priv *priv,
+				ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int src_clk, src_clk_div;
+
+	switch (clk_id) {
+	case ACLK_TOP_ROOT:
+		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		assert(src_clk_div - 1 <= 31);
+		rk_clrsetreg(&cru->clksel_con[8],
+			     ACLK_TOP_ROOT_DIV_MASK |
+			     ACLK_TOP_ROOT_SRC_SEL_MASK,
+			     (ACLK_TOP_ROOT_SRC_SEL_GPLL <<
+			      ACLK_TOP_ROOT_SRC_SEL_SHIFT) |
+			     (src_clk_div - 1) << ACLK_TOP_ROOT_DIV_SHIFT);
+		break;
+	case ACLK_LOW_TOP_ROOT:
+		src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		assert(src_clk_div - 1 <= 31);
+		rk_clrsetreg(&cru->clksel_con[8],
+			     ACLK_LOW_TOP_ROOT_DIV_MASK |
+			     ACLK_LOW_TOP_ROOT_SRC_SEL_MASK,
+			     (ACLK_LOW_TOP_ROOT_SRC_SEL_GPLL <<
+			      ACLK_LOW_TOP_ROOT_SRC_SEL_SHIFT) |
+			     (src_clk_div - 1) << ACLK_LOW_TOP_ROOT_DIV_SHIFT);
+		break;
+	case PCLK_TOP_ROOT:
+		if (rate == 100 * MHz)
+			src_clk = PCLK_TOP_ROOT_SEL_100M;
+		else if (rate == 50 * MHz)
+			src_clk = PCLK_TOP_ROOT_SEL_50M;
+		else
+			src_clk = PCLK_TOP_ROOT_SEL_24M;
+		rk_clrsetreg(&cru->clksel_con[8],
+			     PCLK_TOP_ROOT_SEL_MASK,
+			     src_clk << PCLK_TOP_ROOT_SEL_SHIFT);
+		break;
+	default:
+		printf("do not support this top freq\n");
+		return -EINVAL;
+	}
+
+	return rk3588_top_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_i2c_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 sel, con;
+	ulong rate;
+
+	switch (clk_id) {
+	case CLK_I2C0:
+		con = readl(&cru->pmuclksel_con[3]);
+		sel = (con & CLK_I2C0_SEL_MASK) >> CLK_I2C0_SEL_SHIFT;
+		break;
+	case CLK_I2C1:
+		con = readl(&cru->clksel_con[38]);
+		sel = (con & CLK_I2C1_SEL_MASK) >> CLK_I2C1_SEL_SHIFT;
+		break;
+	case CLK_I2C2:
+		con = readl(&cru->clksel_con[38]);
+		sel = (con & CLK_I2C2_SEL_MASK) >> CLK_I2C2_SEL_SHIFT;
+		break;
+	case CLK_I2C3:
+		con = readl(&cru->clksel_con[38]);
+		sel = (con & CLK_I2C3_SEL_MASK) >> CLK_I2C3_SEL_SHIFT;
+		break;
+	case CLK_I2C4:
+		con = readl(&cru->clksel_con[38]);
+		sel = (con & CLK_I2C4_SEL_MASK) >> CLK_I2C4_SEL_SHIFT;
+		break;
+	case CLK_I2C5:
+		con = readl(&cru->clksel_con[38]);
+		sel = (con & CLK_I2C5_SEL_MASK) >> CLK_I2C5_SEL_SHIFT;
+		break;
+	case CLK_I2C6:
+		con = readl(&cru->clksel_con[38]);
+		sel = (con & CLK_I2C6_SEL_MASK) >> CLK_I2C6_SEL_SHIFT;
+		break;
+	case CLK_I2C7:
+		con = readl(&cru->clksel_con[38]);
+		sel = (con & CLK_I2C7_SEL_MASK) >> CLK_I2C7_SEL_SHIFT;
+		break;
+	case CLK_I2C8:
+		con = readl(&cru->clksel_con[38]);
+		sel = (con & CLK_I2C8_SEL_MASK) >> CLK_I2C8_SEL_SHIFT;
+		break;
+	default:
+		return -ENOENT;
+	}
+	if (sel == CLK_I2C_SEL_200M)
+		rate = 200 * MHz;
+	else
+		rate = 100 * MHz;
+
+	return rate;
+}
+
+static ulong rk3588_i2c_set_clk(struct rk3588_clk_priv *priv, ulong clk_id,
+				ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int src_clk;
+
+	if (rate >= 198 * MHz)
+		src_clk = CLK_I2C_SEL_200M;
+	else
+		src_clk = CLK_I2C_SEL_100M;
+
+	switch (clk_id) {
+	case CLK_I2C0:
+		rk_clrsetreg(&cru->pmuclksel_con[3], CLK_I2C0_SEL_MASK,
+			     src_clk << CLK_I2C0_SEL_SHIFT);
+		break;
+	case CLK_I2C1:
+		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C1_SEL_MASK,
+			     src_clk << CLK_I2C1_SEL_SHIFT);
+		break;
+	case CLK_I2C2:
+		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C2_SEL_MASK,
+			     src_clk << CLK_I2C2_SEL_SHIFT);
+		break;
+	case CLK_I2C3:
+		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C3_SEL_MASK,
+			     src_clk << CLK_I2C3_SEL_SHIFT);
+		break;
+	case CLK_I2C4:
+		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C4_SEL_MASK,
+			     src_clk << CLK_I2C4_SEL_SHIFT);
+		break;
+	case CLK_I2C5:
+		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C5_SEL_MASK,
+			     src_clk << CLK_I2C5_SEL_SHIFT);
+		break;
+	case CLK_I2C6:
+		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C6_SEL_MASK,
+			     src_clk << CLK_I2C6_SEL_SHIFT);
+		break;
+	case CLK_I2C7:
+		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C7_SEL_MASK,
+			     src_clk << CLK_I2C7_SEL_SHIFT);
+		break;
+	case CLK_I2C8:
+		rk_clrsetreg(&cru->clksel_con[38], CLK_I2C8_SEL_MASK,
+			     src_clk << CLK_I2C8_SEL_SHIFT);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3588_i2c_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_spi_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 sel, con;
+
+	con = readl(&cru->clksel_con[59]);
+
+	switch (clk_id) {
+	case CLK_SPI0:
+		sel = (con & CLK_SPI0_SEL_MASK) >> CLK_SPI0_SEL_SHIFT;
+		break;
+	case CLK_SPI1:
+		sel = (con & CLK_SPI1_SEL_MASK) >> CLK_SPI1_SEL_SHIFT;
+		break;
+	case CLK_SPI2:
+		sel = (con & CLK_SPI2_SEL_MASK) >> CLK_SPI2_SEL_SHIFT;
+		break;
+	case CLK_SPI3:
+		sel = (con & CLK_SPI3_SEL_MASK) >> CLK_SPI3_SEL_SHIFT;
+		break;
+	case CLK_SPI4:
+		sel = (con & CLK_SPI4_SEL_MASK) >> CLK_SPI4_SEL_SHIFT;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	switch (sel) {
+	case CLK_SPI_SEL_200M:
+		return 200 * MHz;
+	case CLK_SPI_SEL_150M:
+		return 150 * MHz;
+	case CLK_SPI_SEL_24M:
+		return OSC_HZ;
+	default:
+		return -ENOENT;
+	}
+}
+
+static ulong rk3588_spi_set_clk(struct rk3588_clk_priv *priv,
+				ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int src_clk;
+
+	if (rate >= 198 * MHz)
+		src_clk = CLK_SPI_SEL_200M;
+	else if (rate >= 140 * MHz)
+		src_clk = CLK_SPI_SEL_150M;
+	else
+		src_clk = CLK_SPI_SEL_24M;
+
+	switch (clk_id) {
+	case CLK_SPI0:
+		rk_clrsetreg(&cru->clksel_con[59],
+			     CLK_SPI0_SEL_MASK,
+			     src_clk << CLK_SPI0_SEL_SHIFT);
+		break;
+	case CLK_SPI1:
+		rk_clrsetreg(&cru->clksel_con[59],
+			     CLK_SPI1_SEL_MASK,
+			     src_clk << CLK_SPI1_SEL_SHIFT);
+		break;
+	case CLK_SPI2:
+		rk_clrsetreg(&cru->clksel_con[59],
+			     CLK_SPI2_SEL_MASK,
+			     src_clk << CLK_SPI2_SEL_SHIFT);
+		break;
+	case CLK_SPI3:
+		rk_clrsetreg(&cru->clksel_con[59],
+			     CLK_SPI3_SEL_MASK,
+			     src_clk << CLK_SPI3_SEL_SHIFT);
+		break;
+	case CLK_SPI4:
+		rk_clrsetreg(&cru->clksel_con[59],
+			     CLK_SPI4_SEL_MASK,
+			     src_clk << CLK_SPI4_SEL_SHIFT);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3588_spi_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_pwm_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 sel, con;
+
+	switch (clk_id) {
+	case CLK_PWM1:
+		con = readl(&cru->clksel_con[59]);
+		sel = (con & CLK_PWM1_SEL_MASK) >> CLK_PWM1_SEL_SHIFT;
+		break;
+	case CLK_PWM2:
+		con = readl(&cru->clksel_con[59]);
+		sel = (con & CLK_PWM2_SEL_MASK) >> CLK_PWM2_SEL_SHIFT;
+		break;
+	case CLK_PWM3:
+		con = readl(&cru->clksel_con[60]);
+		sel = (con & CLK_PWM3_SEL_MASK) >> CLK_PWM3_SEL_SHIFT;
+		break;
+	case CLK_PMU1PWM:
+		con = readl(&cru->pmuclksel_con[2]);
+		sel = (con & CLK_PMU1PWM_SEL_MASK) >> CLK_PMU1PWM_SEL_SHIFT;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	switch (sel) {
+	case CLK_PWM_SEL_100M:
+		return 100 * MHz;
+	case CLK_PWM_SEL_50M:
+		return 50 * MHz;
+	case CLK_PWM_SEL_24M:
+		return OSC_HZ;
+	default:
+		return -ENOENT;
+	}
+}
+
+static ulong rk3588_pwm_set_clk(struct rk3588_clk_priv *priv,
+				ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int src_clk;
+
+	if (rate >= 99 * MHz)
+		src_clk = CLK_PWM_SEL_100M;
+	else if (rate >= 50 * MHz)
+		src_clk = CLK_PWM_SEL_50M;
+	else
+		src_clk = CLK_PWM_SEL_24M;
+
+	switch (clk_id) {
+	case CLK_PWM1:
+		rk_clrsetreg(&cru->clksel_con[59],
+			     CLK_PWM1_SEL_MASK,
+			     src_clk << CLK_PWM1_SEL_SHIFT);
+		break;
+	case CLK_PWM2:
+		rk_clrsetreg(&cru->clksel_con[59],
+			     CLK_PWM2_SEL_MASK,
+			     src_clk << CLK_PWM2_SEL_SHIFT);
+		break;
+	case CLK_PWM3:
+		rk_clrsetreg(&cru->clksel_con[60],
+			     CLK_PWM3_SEL_MASK,
+			     src_clk << CLK_PWM3_SEL_SHIFT);
+		break;
+	case CLK_PMU1PWM:
+		rk_clrsetreg(&cru->pmuclksel_con[2],
+			     CLK_PMU1PWM_SEL_MASK,
+			     src_clk << CLK_PMU1PWM_SEL_SHIFT);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3588_pwm_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_adc_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 div, sel, con, prate;
+
+	switch (clk_id) {
+	case CLK_SARADC:
+		con = readl(&cru->clksel_con[40]);
+		div = (con & CLK_SARADC_DIV_MASK) >> CLK_SARADC_DIV_SHIFT;
+		sel = (con & CLK_SARADC_SEL_MASK) >>
+		      CLK_SARADC_SEL_SHIFT;
+		if (sel == CLK_SARADC_SEL_24M)
+			prate = OSC_HZ;
+		else
+			prate = priv->gpll_hz;
+		return DIV_TO_RATE(prate, div);
+	case CLK_TSADC:
+		con = readl(&cru->clksel_con[41]);
+		div = (con & CLK_TSADC_DIV_MASK) >>
+		      CLK_TSADC_DIV_SHIFT;
+		sel = (con & CLK_TSADC_SEL_MASK) >>
+		      CLK_TSADC_SEL_SHIFT;
+		if (sel == CLK_TSADC_SEL_24M)
+			prate = OSC_HZ;
+		else
+			prate = 100 * MHz;
+		return DIV_TO_RATE(prate, div);
+	default:
+		return -ENOENT;
+	}
+}
+
+static ulong rk3588_adc_set_clk(struct rk3588_clk_priv *priv,
+				ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int src_clk_div;
+
+	switch (clk_id) {
+	case CLK_SARADC:
+		if (!(OSC_HZ % rate)) {
+			src_clk_div = DIV_ROUND_UP(OSC_HZ, rate);
+			assert(src_clk_div - 1 <= 255);
+			rk_clrsetreg(&cru->clksel_con[40],
+				     CLK_SARADC_SEL_MASK |
+				     CLK_SARADC_DIV_MASK,
+				     (CLK_SARADC_SEL_24M <<
+				      CLK_SARADC_SEL_SHIFT) |
+				     (src_clk_div - 1) <<
+				     CLK_SARADC_DIV_SHIFT);
+		} else {
+			src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
+			assert(src_clk_div - 1 <= 255);
+			rk_clrsetreg(&cru->clksel_con[40],
+				     CLK_SARADC_SEL_MASK |
+				     CLK_SARADC_DIV_MASK,
+				     (CLK_SARADC_SEL_GPLL <<
+				      CLK_SARADC_SEL_SHIFT) |
+				     (src_clk_div - 1) <<
+				     CLK_SARADC_DIV_SHIFT);
+		}
+		break;
+	case CLK_TSADC:
+		if (!(OSC_HZ % rate)) {
+			src_clk_div = DIV_ROUND_UP(OSC_HZ, rate);
+			assert(src_clk_div - 1 <= 255);
+			rk_clrsetreg(&cru->clksel_con[41],
+				     CLK_TSADC_SEL_MASK |
+				     CLK_TSADC_DIV_MASK,
+				     (CLK_TSADC_SEL_24M <<
+				      CLK_TSADC_SEL_SHIFT) |
+				     (src_clk_div - 1) <<
+				     CLK_TSADC_DIV_SHIFT);
+		} else {
+			src_clk_div = DIV_ROUND_UP(priv->gpll_hz, rate);
+			assert(src_clk_div - 1 <= 7);
+			rk_clrsetreg(&cru->clksel_con[41],
+				     CLK_TSADC_SEL_MASK |
+				     CLK_TSADC_DIV_MASK,
+				     (CLK_TSADC_SEL_GPLL <<
+				      CLK_TSADC_SEL_SHIFT) |
+				     (src_clk_div - 1) <<
+				     CLK_TSADC_DIV_SHIFT);
+		}
+		break;
+	default:
+		return -ENOENT;
+	}
+	return rk3588_adc_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_mmc_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 sel, con, div, prate;
+
+	switch (clk_id) {
+	case CCLK_SRC_SDIO:
+		con = readl(&cru->clksel_con[172]);
+		div = (con & CCLK_SDIO_SRC_DIV_MASK) >> CCLK_SDIO_SRC_DIV_SHIFT;
+		sel = (con & CCLK_SDIO_SRC_SEL_MASK) >>
+		      CCLK_SDIO_SRC_SEL_SHIFT;
+		if (sel == CCLK_SDIO_SRC_SEL_GPLL)
+			prate = priv->gpll_hz;
+		else if (sel == CCLK_SDIO_SRC_SEL_CPLL)
+			prate = priv->cpll_hz;
+		else
+			prate = OSC_HZ;
+		return DIV_TO_RATE(prate, div);
+	case CCLK_EMMC:
+		con = readl(&cru->clksel_con[77]);
+		div = (con & CCLK_EMMC_DIV_MASK) >> CCLK_EMMC_DIV_SHIFT;
+		sel = (con & CCLK_EMMC_SEL_MASK) >>
+		      CCLK_EMMC_SEL_SHIFT;
+		if (sel == CCLK_EMMC_SEL_GPLL)
+			prate = priv->gpll_hz;
+		else if (sel == CCLK_EMMC_SEL_CPLL)
+			prate = priv->cpll_hz;
+		else
+			prate = OSC_HZ;
+		return DIV_TO_RATE(prate, div);
+	case BCLK_EMMC:
+		con = readl(&cru->clksel_con[78]);
+		div = (con & BCLK_EMMC_DIV_MASK) >> BCLK_EMMC_DIV_SHIFT;
+		sel = (con & BCLK_EMMC_SEL_MASK) >>
+		      BCLK_EMMC_SEL_SHIFT;
+		if (sel == CCLK_EMMC_SEL_CPLL)
+			prate = priv->cpll_hz;
+		else
+			prate = priv->gpll_hz;
+		return DIV_TO_RATE(prate, div);
+	case SCLK_SFC:
+		con = readl(&cru->clksel_con[78]);
+		div = (con & SCLK_SFC_DIV_MASK) >> SCLK_SFC_DIV_SHIFT;
+		sel = (con & SCLK_SFC_SEL_MASK) >>
+		      SCLK_SFC_SEL_SHIFT;
+		if (sel == SCLK_SFC_SEL_GPLL)
+			prate = priv->gpll_hz;
+		else if (sel == SCLK_SFC_SEL_CPLL)
+			prate = priv->cpll_hz;
+		else
+			prate = OSC_HZ;
+		return DIV_TO_RATE(prate, div);
+	case DCLK_DECOM:
+		con = readl(&cru->clksel_con[62]);
+		div = (con & DCLK_DECOM_DIV_MASK) >> DCLK_DECOM_DIV_SHIFT;
+		sel = (con & DCLK_DECOM_SEL_MASK) >>
+		      DCLK_DECOM_SEL_SHIFT;
+		if (sel == DCLK_DECOM_SEL_SPLL)
+			prate = 702 * MHz;
+		else
+			prate = priv->gpll_hz;
+		return DIV_TO_RATE(prate, div);
+	default:
+		return -ENOENT;
+	}
+}
+
+static ulong rk3588_mmc_set_clk(struct rk3588_clk_priv *priv,
+				ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int src_clk, div;
+
+	switch (clk_id) {
+	case CCLK_SRC_SDIO:
+	case CCLK_EMMC:
+	case SCLK_SFC:
+		if (!(OSC_HZ % rate)) {
+			src_clk = SCLK_SFC_SEL_24M;
+			div = DIV_ROUND_UP(OSC_HZ, rate);
+		} else if (!(priv->cpll_hz % rate)) {
+			src_clk = SCLK_SFC_SEL_CPLL;
+			div = DIV_ROUND_UP(priv->cpll_hz, rate);
+		} else {
+			src_clk = SCLK_SFC_SEL_GPLL;
+			div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		}
+		break;
+	case BCLK_EMMC:
+		if (!(priv->cpll_hz % rate)) {
+			src_clk = CCLK_EMMC_SEL_CPLL;
+			div = DIV_ROUND_UP(priv->cpll_hz, rate);
+		} else {
+			src_clk = CCLK_EMMC_SEL_GPLL;
+			div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		}
+		break;
+	case DCLK_DECOM:
+		if (!(702 * MHz % rate)) {
+			src_clk = DCLK_DECOM_SEL_SPLL;
+			div = DIV_ROUND_UP(702 * MHz, rate);
+		} else {
+			src_clk = DCLK_DECOM_SEL_GPLL;
+			div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		}
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	switch (clk_id) {
+	case CCLK_SRC_SDIO:
+		rk_clrsetreg(&cru->clksel_con[172],
+			     CCLK_SDIO_SRC_SEL_MASK |
+			     CCLK_SDIO_SRC_DIV_MASK,
+			     (src_clk << CCLK_SDIO_SRC_SEL_SHIFT) |
+			     (div - 1) << CCLK_SDIO_SRC_DIV_SHIFT);
+		break;
+	case CCLK_EMMC:
+		rk_clrsetreg(&cru->clksel_con[77],
+			     CCLK_EMMC_SEL_MASK |
+			     CCLK_EMMC_DIV_MASK,
+			     (src_clk << CCLK_EMMC_SEL_SHIFT) |
+			     (div - 1) << CCLK_EMMC_DIV_SHIFT);
+		break;
+	case BCLK_EMMC:
+		rk_clrsetreg(&cru->clksel_con[78],
+			     BCLK_EMMC_DIV_MASK |
+			     BCLK_EMMC_SEL_MASK,
+			     (src_clk << BCLK_EMMC_SEL_SHIFT) |
+			     (div - 1) << BCLK_EMMC_DIV_SHIFT);
+		break;
+	case SCLK_SFC:
+		rk_clrsetreg(&cru->clksel_con[78],
+			     SCLK_SFC_DIV_MASK |
+			     SCLK_SFC_SEL_MASK,
+			     (src_clk << SCLK_SFC_SEL_SHIFT) |
+			     (div - 1) << SCLK_SFC_DIV_SHIFT);
+		break;
+	case DCLK_DECOM:
+		rk_clrsetreg(&cru->clksel_con[62],
+			     DCLK_DECOM_DIV_MASK |
+			     DCLK_DECOM_SEL_MASK,
+			     (src_clk << DCLK_DECOM_SEL_SHIFT) |
+			     (div - 1) << DCLK_DECOM_DIV_SHIFT);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3588_mmc_get_clk(priv, clk_id);
+}
+
+#ifndef CONFIG_SPL_BUILD
+static ulong rk3588_aux16m_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 div, con, parent;
+
+	parent = priv->gpll_hz;
+	con = readl(&cru->clksel_con[117]);
+
+	switch (clk_id) {
+	case CLK_AUX16M_0:
+		div = (con & CLK_AUX16MHZ_0_DIV_MASK) >> CLK_AUX16MHZ_0_DIV_SHIFT;
+		return DIV_TO_RATE(parent, div);
+	case CLK_AUX16M_1:
+		div = (con & CLK_AUX16MHZ_1_DIV_MASK) >> CLK_AUX16MHZ_1_DIV_SHIFT;
+		return DIV_TO_RATE(parent, div);
+	default:
+		return -ENOENT;
+	}
+}
+
+static ulong rk3588_aux16m_set_clk(struct rk3588_clk_priv *priv,
+				   ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 div;
+
+	if (!priv->gpll_hz) {
+		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
+		return -ENOENT;
+	}
+
+	div = DIV_ROUND_UP(priv->gpll_hz, rate);
+
+	switch (clk_id) {
+	case CLK_AUX16M_0:
+		rk_clrsetreg(&cru->clksel_con[117], CLK_AUX16MHZ_0_DIV_MASK,
+			     (div - 1) << CLK_AUX16MHZ_0_DIV_SHIFT);
+		break;
+	case CLK_AUX16M_1:
+		rk_clrsetreg(&cru->clksel_con[117], CLK_AUX16MHZ_1_DIV_MASK,
+			     (div - 1) << CLK_AUX16MHZ_1_DIV_SHIFT);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3588_aux16m_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_aclk_vop_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 div, sel, con, parent;
+
+	switch (clk_id) {
+	case ACLK_VOP_ROOT:
+	case ACLK_VOP:
+		con = readl(&cru->clksel_con[110]);
+		div = (con & ACLK_VOP_ROOT_DIV_MASK) >> ACLK_VOP_ROOT_DIV_SHIFT;
+		sel = (con & ACLK_VOP_ROOT_SEL_MASK) >> ACLK_VOP_ROOT_SEL_SHIFT;
+		if (sel == ACLK_VOP_ROOT_SEL_GPLL)
+			parent = priv->gpll_hz;
+		else if (sel == ACLK_VOP_ROOT_SEL_CPLL)
+			parent = priv->cpll_hz;
+		else if (sel == ACLK_VOP_ROOT_SEL_AUPLL)
+			parent = priv->aupll_hz;
+		else if (sel == ACLK_VOP_ROOT_SEL_NPLL)
+			parent = priv->npll_hz;
+		else
+			parent = 702 * MHz;
+		return DIV_TO_RATE(parent, div);
+	case ACLK_VOP_LOW_ROOT:
+		con = readl(&cru->clksel_con[110]);
+		sel = (con & ACLK_VOP_LOW_ROOT_SEL_MASK) >>
+		      ACLK_VOP_LOW_ROOT_SEL_SHIFT;
+		if (sel == ACLK_VOP_LOW_ROOT_SEL_400M)
+			return 396 * MHz;
+		else if (sel == ACLK_VOP_LOW_ROOT_SEL_200M)
+			return 200 * MHz;
+		else if (sel == ACLK_VOP_LOW_ROOT_SEL_100M)
+			return 100 * MHz;
+		else
+			return OSC_HZ;
+	case HCLK_VOP_ROOT:
+		con = readl(&cru->clksel_con[110]);
+		sel = (con & HCLK_VOP_ROOT_SEL_MASK) >> HCLK_VOP_ROOT_SEL_SHIFT;
+		if (sel == HCLK_VOP_ROOT_SEL_200M)
+			return 200 * MHz;
+		else if (sel == HCLK_VOP_ROOT_SEL_100M)
+			return 100 * MHz;
+		else if (sel == HCLK_VOP_ROOT_SEL_50M)
+			return 50 * MHz;
+		else
+			return OSC_HZ;
+	default:
+		return -ENOENT;
+	}
+}
+
+static ulong rk3588_aclk_vop_set_clk(struct rk3588_clk_priv *priv,
+				     ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int src_clk, div;
+
+	switch (clk_id) {
+	case ACLK_VOP_ROOT:
+	case ACLK_VOP:
+		if (rate >= 850 * MHz) {
+			src_clk = ACLK_VOP_ROOT_SEL_NPLL;
+			div = 1;
+		} else if (rate >= 750 * MHz) {
+			src_clk = ACLK_VOP_ROOT_SEL_CPLL;
+			div = 2;
+		} else if (rate >= 700 * MHz) {
+			src_clk = ACLK_VOP_ROOT_SEL_SPLL;
+			div = 1;
+		} else if (!(priv->cpll_hz % rate)) {
+			src_clk = ACLK_VOP_ROOT_SEL_CPLL;
+			div = DIV_ROUND_UP(priv->cpll_hz, rate);
+		} else {
+			src_clk = ACLK_VOP_ROOT_SEL_GPLL;
+			div = DIV_ROUND_UP(priv->gpll_hz, rate);
+		}
+		rk_clrsetreg(&cru->clksel_con[110],
+			     ACLK_VOP_ROOT_DIV_MASK |
+			     ACLK_VOP_ROOT_SEL_MASK,
+			     (src_clk << ACLK_VOP_ROOT_SEL_SHIFT) |
+			     (div - 1) << ACLK_VOP_ROOT_DIV_SHIFT);
+		break;
+	case ACLK_VOP_LOW_ROOT:
+		if (rate == 400 * MHz || rate == 396 * MHz)
+			src_clk = ACLK_VOP_LOW_ROOT_SEL_400M;
+		else if (rate == 200 * MHz)
+			src_clk = ACLK_VOP_LOW_ROOT_SEL_200M;
+		else if (rate == 100 * MHz)
+			src_clk = ACLK_VOP_LOW_ROOT_SEL_100M;
+		else
+			src_clk = ACLK_VOP_LOW_ROOT_SEL_24M;
+		rk_clrsetreg(&cru->clksel_con[110],
+			     ACLK_VOP_LOW_ROOT_SEL_MASK,
+			     src_clk << ACLK_VOP_LOW_ROOT_SEL_SHIFT);
+		break;
+	case HCLK_VOP_ROOT:
+		if (rate == 200 * MHz)
+			src_clk = HCLK_VOP_ROOT_SEL_200M;
+		else if (rate == 100 * MHz)
+			src_clk = HCLK_VOP_ROOT_SEL_100M;
+		else if (rate == 50 * MHz)
+			src_clk = HCLK_VOP_ROOT_SEL_50M;
+		else
+			src_clk = HCLK_VOP_ROOT_SEL_24M;
+		rk_clrsetreg(&cru->clksel_con[110],
+			     HCLK_VOP_ROOT_SEL_MASK,
+			     src_clk << HCLK_VOP_ROOT_SEL_SHIFT);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3588_aclk_vop_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_dclk_vop_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 div, sel, con, parent;
+
+	switch (clk_id) {
+	case DCLK_VOP0:
+	case DCLK_VOP0_SRC:
+		con = readl(&cru->clksel_con[111]);
+		div = (con & DCLK0_VOP_SRC_DIV_MASK) >> DCLK0_VOP_SRC_DIV_SHIFT;
+		sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
+		break;
+	case DCLK_VOP1:
+	case DCLK_VOP1_SRC:
+		con = readl(&cru->clksel_con[111]);
+		div = (con & DCLK1_VOP_SRC_DIV_MASK) >> DCLK1_VOP_SRC_DIV_SHIFT;
+		sel = (con & DCLK1_VOP_SRC_SEL_MASK) >> DCLK1_VOP_SRC_SEL_SHIFT;
+		break;
+	case DCLK_VOP2:
+	case DCLK_VOP2_SRC:
+		con = readl(&cru->clksel_con[112]);
+		div = (con & DCLK2_VOP_SRC_DIV_MASK) >> DCLK2_VOP_SRC_DIV_SHIFT;
+		sel = (con & DCLK2_VOP_SRC_SEL_MASK) >> DCLK2_VOP_SRC_SEL_SHIFT;
+		break;
+	case DCLK_VOP3:
+		con = readl(&cru->clksel_con[113]);
+		div = (con & DCLK3_VOP_SRC_DIV_MASK) >> DCLK3_VOP_SRC_DIV_SHIFT;
+		sel = (con & DCLK3_VOP_SRC_SEL_MASK) >> DCLK3_VOP_SRC_SEL_SHIFT;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (sel == DCLK_VOP_SRC_SEL_AUPLL)
+		parent = priv->aupll_hz;
+	else if (sel == DCLK_VOP_SRC_SEL_V0PLL)
+		parent = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL],
+					       priv->cru, V0PLL);
+	else if (sel == DCLK_VOP_SRC_SEL_GPLL)
+		parent = priv->gpll_hz;
+	else if (sel == DCLK_VOP_SRC_SEL_CPLL)
+		parent = priv->cpll_hz;
+	else
+		return -ENOENT;
+
+	return DIV_TO_RATE(parent, div);
+}
+
+#define RK3588_VOP_PLL_LIMIT_FREQ 600000000
+
+static ulong rk3588_dclk_vop_set_clk(struct rk3588_clk_priv *priv,
+				     ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	ulong pll_rate, now, best_rate = 0;
+	u32 i, conid, con, sel, div, best_div = 0, best_sel = 0;
+	u32 mask, div_shift, sel_shift;
+
+	switch (clk_id) {
+	case DCLK_VOP0:
+	case DCLK_VOP0_SRC:
+		conid = 111;
+		con = readl(&cru->clksel_con[111]);
+		sel = (con & DCLK0_VOP_SRC_SEL_MASK) >> DCLK0_VOP_SRC_SEL_SHIFT;
+		mask = DCLK0_VOP_SRC_SEL_MASK | DCLK0_VOP_SRC_DIV_MASK;
+		div_shift = DCLK0_VOP_SRC_DIV_SHIFT;
+		sel_shift = DCLK0_VOP_SRC_SEL_SHIFT;
+		break;
+	case DCLK_VOP1:
+	case DCLK_VOP1_SRC:
+		conid = 111;
+		con = readl(&cru->clksel_con[111]);
+		sel = (con & DCLK1_VOP_SRC_SEL_MASK) >> DCLK1_VOP_SRC_SEL_SHIFT;
+		mask = DCLK1_VOP_SRC_SEL_MASK | DCLK1_VOP_SRC_DIV_MASK;
+		div_shift = DCLK1_VOP_SRC_DIV_SHIFT;
+		sel_shift = DCLK1_VOP_SRC_SEL_SHIFT;
+		break;
+	case DCLK_VOP2:
+	case DCLK_VOP2_SRC:
+		conid = 112;
+		con = readl(&cru->clksel_con[112]);
+		sel = (con & DCLK2_VOP_SRC_SEL_MASK) >> DCLK2_VOP_SRC_SEL_SHIFT;
+		mask = DCLK2_VOP_SRC_SEL_MASK | DCLK2_VOP_SRC_DIV_MASK;
+		div_shift = DCLK2_VOP_SRC_DIV_SHIFT;
+		sel_shift = DCLK2_VOP_SRC_SEL_SHIFT;
+		break;
+	case DCLK_VOP3:
+		conid = 113;
+		con = readl(&cru->clksel_con[113]);
+		sel = (con & DCLK3_VOP_SRC_SEL_MASK) >> DCLK3_VOP_SRC_SEL_SHIFT;
+		mask = DCLK3_VOP_SRC_SEL_MASK | DCLK3_VOP_SRC_DIV_MASK;
+		div_shift = DCLK3_VOP_SRC_DIV_SHIFT;
+		sel_shift = DCLK3_VOP_SRC_SEL_SHIFT;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (sel == DCLK_VOP_SRC_SEL_V0PLL) {
+		div = DIV_ROUND_UP(RK3588_VOP_PLL_LIMIT_FREQ, rate);
+		rk_clrsetreg(&cru->clksel_con[conid],
+			     mask,
+			     DCLK_VOP_SRC_SEL_V0PLL << sel_shift |
+			     ((div - 1) << div_shift));
+		rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL],
+				      priv->cru, V0PLL, div * rate);
+	} else {
+		for (i = 0; i <= DCLK_VOP_SRC_SEL_AUPLL; i++) {
+			switch (i) {
+			case DCLK_VOP_SRC_SEL_GPLL:
+				pll_rate = priv->gpll_hz;
+				break;
+			case DCLK_VOP_SRC_SEL_CPLL:
+				pll_rate = priv->cpll_hz;
+				break;
+			case DCLK_VOP_SRC_SEL_AUPLL:
+				pll_rate = priv->aupll_hz;
+				break;
+			case DCLK_VOP_SRC_SEL_V0PLL:
+				pll_rate = 0;
+				break;
+			default:
+				printf("do not support this vop pll sel\n");
+				return -EINVAL;
+			}
+
+			div = DIV_ROUND_UP(pll_rate, rate);
+			if (div > 255)
+				continue;
+			now = pll_rate / div;
+			if (abs(rate - now) < abs(rate - best_rate)) {
+				best_rate = now;
+				best_div = div;
+				best_sel = i;
+			}
+			debug("p_rate=%lu, best_rate=%lu, div=%u, sel=%u\n",
+			      pll_rate, best_rate, best_div, best_sel);
+		}
+
+		if (best_rate) {
+			rk_clrsetreg(&cru->clksel_con[conid],
+				     mask,
+				     best_sel << sel_shift |
+				     (best_div - 1) << div_shift);
+		} else {
+			printf("do not support this vop freq %lu\n", rate);
+			return -EINVAL;
+		}
+	}
+	return rk3588_dclk_vop_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_gmac_get_clk(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 con, div;
+
+	switch (clk_id) {
+	case CLK_GMAC0_PTP_REF:
+		con = readl(&cru->clksel_con[81]);
+		div = (con & CLK_GMAC0_PTP_DIV_MASK) >> CLK_GMAC0_PTP_DIV_SHIFT;
+		return DIV_TO_RATE(priv->cpll_hz, div);
+	case CLK_GMAC1_PTP_REF:
+		con = readl(&cru->clksel_con[81]);
+		div = (con & CLK_GMAC1_PTP_DIV_MASK) >> CLK_GMAC1_PTP_DIV_SHIFT;
+		return DIV_TO_RATE(priv->cpll_hz, div);
+	case CLK_GMAC_125M:
+		con = readl(&cru->clksel_con[83]);
+		div = (con & CLK_GMAC_125M_DIV_MASK) >> CLK_GMAC_125M_DIV_SHIFT;
+		return DIV_TO_RATE(priv->cpll_hz, div);
+	case CLK_GMAC_50M:
+		con = readl(&cru->clksel_con[84]);
+		div = (con & CLK_GMAC_50M_DIV_MASK) >> CLK_GMAC_50M_DIV_SHIFT;
+		return DIV_TO_RATE(priv->cpll_hz, div);
+	default:
+		return -ENOENT;
+	}
+}
+
+static ulong rk3588_gmac_set_clk(struct rk3588_clk_priv *priv,
+				 ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	int div;
+
+	div = DIV_ROUND_UP(priv->cpll_hz, rate);
+
+	switch (clk_id) {
+	case CLK_GMAC0_PTP_REF:
+		rk_clrsetreg(&cru->clksel_con[81],
+			     CLK_GMAC0_PTP_DIV_MASK | CLK_GMAC0_PTP_SEL_MASK,
+			     CLK_GMAC0_PTP_SEL_CPLL << CLK_GMAC0_PTP_SEL_SHIFT |
+			     (div - 1) << CLK_GMAC0_PTP_DIV_SHIFT);
+		break;
+	case CLK_GMAC1_PTP_REF:
+		rk_clrsetreg(&cru->clksel_con[81],
+			     CLK_GMAC1_PTP_DIV_MASK | CLK_GMAC1_PTP_SEL_MASK,
+			     CLK_GMAC1_PTP_SEL_CPLL << CLK_GMAC1_PTP_SEL_SHIFT |
+			     (div - 1) << CLK_GMAC1_PTP_DIV_SHIFT);
+		break;
+
+	case CLK_GMAC_125M:
+		rk_clrsetreg(&cru->clksel_con[83],
+			     CLK_GMAC_125M_DIV_MASK | CLK_GMAC_125M_SEL_MASK,
+			     CLK_GMAC_125M_SEL_CPLL << CLK_GMAC_125M_SEL_SHIFT |
+			     (div - 1) << CLK_GMAC_125M_DIV_SHIFT);
+		break;
+	case CLK_GMAC_50M:
+		rk_clrsetreg(&cru->clksel_con[84],
+			     CLK_GMAC_50M_DIV_MASK | CLK_GMAC_50M_SEL_MASK,
+			     CLK_GMAC_50M_SEL_CPLL << CLK_GMAC_50M_SEL_SHIFT |
+			     (div - 1) << CLK_GMAC_50M_DIV_SHIFT);
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3588_gmac_get_clk(priv, clk_id);
+}
+
+static ulong rk3588_uart_get_rate(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 reg, con, fracdiv, div, src, p_src, p_rate;
+	unsigned long m, n;
+
+	switch (clk_id) {
+	case SCLK_UART1:
+		reg = 41;
+		break;
+	case SCLK_UART2:
+		reg = 43;
+		break;
+	case SCLK_UART3:
+		reg = 45;
+		break;
+	case SCLK_UART4:
+		reg = 47;
+		break;
+	case SCLK_UART5:
+		reg = 49;
+		break;
+	case SCLK_UART6:
+		reg = 51;
+		break;
+	case SCLK_UART7:
+		reg = 53;
+		break;
+	case SCLK_UART8:
+		reg = 55;
+		break;
+	case SCLK_UART9:
+		reg = 57;
+		break;
+	default:
+		return -ENOENT;
+	}
+	con = readl(&cru->clksel_con[reg + 2]);
+	src = (con & CLK_UART_SEL_MASK) >> CLK_UART_SEL_SHIFT;
+	con = readl(&cru->clksel_con[reg]);
+	div = (con & CLK_UART_SRC_DIV_MASK) >> CLK_UART_SRC_DIV_SHIFT;
+	p_src = (con & CLK_UART_SRC_SEL_MASK) >> CLK_UART_SRC_SEL_SHIFT;
+	if (p_src == CLK_UART_SRC_SEL_GPLL)
+		p_rate = priv->gpll_hz;
+	else
+		p_rate = priv->cpll_hz;
+
+	if (src == CLK_UART_SEL_SRC) {
+		return DIV_TO_RATE(p_rate, div);
+	} else if (src == CLK_UART_SEL_FRAC) {
+		fracdiv = readl(&cru->clksel_con[reg + 1]);
+		n = fracdiv & CLK_UART_FRAC_NUMERATOR_MASK;
+		n >>= CLK_UART_FRAC_NUMERATOR_SHIFT;
+		m = fracdiv & CLK_UART_FRAC_DENOMINATOR_MASK;
+		m >>= CLK_UART_FRAC_DENOMINATOR_SHIFT;
+		return DIV_TO_RATE(p_rate, div) * n / m;
+	} else {
+		return OSC_HZ;
+	}
+}
+
+static ulong rk3588_uart_set_rate(struct rk3588_clk_priv *priv,
+				  ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 reg, clk_src, uart_src, div;
+	unsigned long m = 0, n = 0, val;
+
+	if (priv->gpll_hz % rate == 0) {
+		clk_src = CLK_UART_SRC_SEL_GPLL;
+		uart_src = CLK_UART_SEL_SRC;
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+	} else if (priv->cpll_hz % rate == 0) {
+		clk_src = CLK_UART_SRC_SEL_CPLL;
+		uart_src = CLK_UART_SEL_SRC;
+		div = DIV_ROUND_UP(priv->gpll_hz, rate);
+	} else if (rate == OSC_HZ) {
+		clk_src = CLK_UART_SRC_SEL_GPLL;
+		uart_src = CLK_UART_SEL_XIN24M;
+		div = 2;
+	} else {
+		clk_src = CLK_UART_SRC_SEL_GPLL;
+		uart_src = CLK_UART_SEL_FRAC;
+		div = 2;
+		rational_best_approximation(rate, priv->gpll_hz / div,
+					    GENMASK(16 - 1, 0),
+					    GENMASK(16 - 1, 0),
+					    &m, &n);
+	}
+
+	switch (clk_id) {
+	case SCLK_UART1:
+		reg = 41;
+		break;
+	case SCLK_UART2:
+		reg = 43;
+		break;
+	case SCLK_UART3:
+		reg = 45;
+		break;
+	case SCLK_UART4:
+		reg = 47;
+		break;
+	case SCLK_UART5:
+		reg = 49;
+		break;
+	case SCLK_UART6:
+		reg = 51;
+		break;
+	case SCLK_UART7:
+		reg = 53;
+		break;
+	case SCLK_UART8:
+		reg = 55;
+		break;
+	case SCLK_UART9:
+		reg = 57;
+		break;
+	default:
+		return -ENOENT;
+	}
+	rk_clrsetreg(&cru->clksel_con[reg],
+		     CLK_UART_SRC_SEL_MASK |
+		     CLK_UART_SRC_DIV_MASK,
+		     (clk_src << CLK_UART_SRC_SEL_SHIFT) |
+		     ((div - 1) << CLK_UART_SRC_DIV_SHIFT));
+	rk_clrsetreg(&cru->clksel_con[reg + 2],
+		     CLK_UART_SEL_MASK,
+		     (uart_src << CLK_UART_SEL_SHIFT));
+	if (m && n) {
+		val = m << CLK_UART_FRAC_NUMERATOR_SHIFT | n;
+		writel(val, &cru->clksel_con[reg + 1]);
+	}
+
+	return rk3588_uart_get_rate(priv, clk_id);
+}
+
+static ulong rk3588_pciephy_get_rate(struct rk3588_clk_priv *priv, ulong clk_id)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 con, div, src;
+
+	switch (clk_id) {
+	case CLK_REF_PIPE_PHY0:
+		con = readl(&cru->clksel_con[177]);
+		src = (con & CLK_PCIE_PHY0_REF_SEL_MASK) >> CLK_PCIE_PHY0_REF_SEL_SHIFT;
+		con = readl(&cru->clksel_con[176]);
+		div = (con & CLK_PCIE_PHY0_PLL_DIV_MASK) >> CLK_PCIE_PHY0_PLL_DIV_SHIFT;
+		break;
+	case CLK_REF_PIPE_PHY1:
+		con = readl(&cru->clksel_con[177]);
+		src = (con & CLK_PCIE_PHY1_REF_SEL_MASK) >> CLK_PCIE_PHY1_REF_SEL_SHIFT;
+		con = readl(&cru->clksel_con[176]);
+		div = (con & CLK_PCIE_PHY1_PLL_DIV_MASK) >> CLK_PCIE_PHY1_PLL_DIV_SHIFT;
+		break;
+	case CLK_REF_PIPE_PHY2:
+		con = readl(&cru->clksel_con[177]);
+		src = (con & CLK_PCIE_PHY2_REF_SEL_MASK) >> CLK_PCIE_PHY2_REF_SEL_SHIFT;
+		div = (con & CLK_PCIE_PHY2_PLL_DIV_MASK) >> CLK_PCIE_PHY2_PLL_DIV_SHIFT;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	if (src == CLK_PCIE_PHY_REF_SEL_PPLL)
+		return DIV_TO_RATE(priv->ppll_hz, div);
+	else
+		return OSC_HZ;
+}
+
+static ulong rk3588_pciephy_set_rate(struct rk3588_clk_priv *priv,
+				     ulong clk_id, ulong rate)
+{
+	struct rk3588_cru *cru = priv->cru;
+	u32 clk_src, div;
+
+	if (rate == OSC_HZ) {
+		clk_src = CLK_PCIE_PHY_REF_SEL_24M;
+		div = 1;
+	} else {
+		clk_src = CLK_PCIE_PHY_REF_SEL_PPLL;
+		div = DIV_ROUND_UP(priv->ppll_hz, rate);
+	}
+
+	switch (clk_id) {
+	case CLK_REF_PIPE_PHY0:
+		rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY0_REF_SEL_MASK,
+			     (clk_src << CLK_PCIE_PHY0_REF_SEL_SHIFT));
+		rk_clrsetreg(&cru->clksel_con[176], CLK_PCIE_PHY0_PLL_DIV_MASK,
+			     ((div - 1) << CLK_PCIE_PHY0_PLL_DIV_SHIFT));
+		break;
+	case CLK_REF_PIPE_PHY1:
+		rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY1_REF_SEL_MASK,
+			     (clk_src << CLK_PCIE_PHY1_REF_SEL_SHIFT));
+		rk_clrsetreg(&cru->clksel_con[176], CLK_PCIE_PHY1_PLL_DIV_MASK,
+			     ((div - 1) << CLK_PCIE_PHY1_PLL_DIV_SHIFT));
+		break;
+	case CLK_REF_PIPE_PHY2:
+		rk_clrsetreg(&cru->clksel_con[177], CLK_PCIE_PHY2_REF_SEL_MASK |
+			     CLK_PCIE_PHY2_PLL_DIV_MASK,
+			     (clk_src << CLK_PCIE_PHY2_REF_SEL_SHIFT) |
+			     ((div - 1) << CLK_PCIE_PHY2_PLL_DIV_SHIFT));
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return rk3588_pciephy_get_rate(priv, clk_id);
+}
+#endif
+
+static ulong rk3588_clk_get_rate(struct clk *clk)
+{
+	struct rk3588_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong rate = 0;
+
+	if (!priv->gpll_hz) {
+		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
+		return -ENOENT;
+	}
+
+	if (!priv->ppll_hz) {
+		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
+						      priv->cru, PPLL);
+	}
+
+	switch (clk->id) {
+	case PLL_LPLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[LPLL], priv->cru,
+					     LPLL);
+		break;
+	case PLL_B0PLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[B0PLL], priv->cru,
+					     B0PLL);
+		break;
+	case PLL_B1PLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[B1PLL], priv->cru,
+					     B1PLL);
+		break;
+	case PLL_GPLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[GPLL], priv->cru,
+					     GPLL);
+		break;
+	case PLL_CPLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[CPLL], priv->cru,
+					     CPLL);
+		break;
+	case PLL_NPLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[NPLL], priv->cru,
+					     NPLL);
+		break;
+	case PLL_V0PLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL], priv->cru,
+					     V0PLL);
+		break;
+	case PLL_AUPLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[AUPLL], priv->cru,
+					     AUPLL);
+		break;
+	case PLL_PPLL:
+		rate = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL], priv->cru,
+					     PPLL);
+		break;
+	case ACLK_CENTER_ROOT:
+	case PCLK_CENTER_ROOT:
+	case HCLK_CENTER_ROOT:
+	case ACLK_CENTER_LOW_ROOT:
+		rate = rk3588_center_get_clk(priv, clk->id);
+		break;
+	case ACLK_TOP_ROOT:
+	case PCLK_TOP_ROOT:
+	case ACLK_LOW_TOP_ROOT:
+		rate = rk3588_top_get_clk(priv, clk->id);
+		break;
+	case CLK_I2C0:
+	case CLK_I2C1:
+	case CLK_I2C2:
+	case CLK_I2C3:
+	case CLK_I2C4:
+	case CLK_I2C5:
+	case CLK_I2C6:
+	case CLK_I2C7:
+	case CLK_I2C8:
+		rate = rk3588_i2c_get_clk(priv, clk->id);
+		break;
+	case CLK_SPI0:
+	case CLK_SPI1:
+	case CLK_SPI2:
+	case CLK_SPI3:
+	case CLK_SPI4:
+		rate = rk3588_spi_get_clk(priv, clk->id);
+		break;
+	case CLK_PWM1:
+	case CLK_PWM2:
+	case CLK_PWM3:
+	case CLK_PMU1PWM:
+		rate = rk3588_pwm_get_clk(priv, clk->id);
+		break;
+	case CLK_SARADC:
+	case CLK_TSADC:
+		rate = rk3588_adc_get_clk(priv, clk->id);
+		break;
+	case CCLK_SRC_SDIO:
+	case CCLK_EMMC:
+	case BCLK_EMMC:
+	case SCLK_SFC:
+	case DCLK_DECOM:
+		rate = rk3588_mmc_get_clk(priv, clk->id);
+		break;
+	case TCLK_WDT0:
+		rate = OSC_HZ;
+		break;
+#ifndef CONFIG_SPL_BUILD
+	case CLK_AUX16M_0:
+	case CLK_AUX16M_1:
+		rk3588_aux16m_get_clk(priv, clk->id);
+		break;
+	case ACLK_VOP_ROOT:
+	case ACLK_VOP:
+	case ACLK_VOP_LOW_ROOT:
+	case HCLK_VOP_ROOT:
+		rate = rk3588_aclk_vop_get_clk(priv, clk->id);
+		break;
+	case DCLK_VOP0:
+	case DCLK_VOP0_SRC:
+	case DCLK_VOP1:
+	case DCLK_VOP1_SRC:
+	case DCLK_VOP2:
+	case DCLK_VOP2_SRC:
+	case DCLK_VOP3:
+		rate = rk3588_dclk_vop_get_clk(priv, clk->id);
+		break;
+	case CLK_GMAC0_PTP_REF:
+	case CLK_GMAC1_PTP_REF:
+	case CLK_GMAC_125M:
+	case CLK_GMAC_50M:
+		rate = rk3588_gmac_get_clk(priv, clk->id);
+		break;
+	case SCLK_UART1:
+	case SCLK_UART2:
+	case SCLK_UART3:
+	case SCLK_UART4:
+	case SCLK_UART5:
+	case SCLK_UART6:
+	case SCLK_UART7:
+	case SCLK_UART8:
+	case SCLK_UART9:
+		rate = rk3588_uart_get_rate(priv, clk->id);
+		break;
+	case CLK_REF_PIPE_PHY0:
+	case CLK_REF_PIPE_PHY1:
+	case CLK_REF_PIPE_PHY2:
+		rate = rk3588_pciephy_get_rate(priv, clk->id);
+		break;
+#endif
+	default:
+		return -ENOENT;
+	}
+
+	return rate;
+};
+
+static ulong rk3588_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct rk3588_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong ret = 0;
+
+	if (!priv->gpll_hz) {
+		printf("%s gpll=%lu\n", __func__, priv->gpll_hz);
+		return -ENOENT;
+	}
+
+	if (!priv->ppll_hz) {
+		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
+						      priv->cru, PPLL);
+	}
+
+	switch (clk->id) {
+	case PLL_CPLL:
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[CPLL], priv->cru,
+					    CPLL, rate);
+		priv->cpll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[CPLL],
+						      priv->cru, CPLL);
+		break;
+	case PLL_GPLL:
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[GPLL], priv->cru,
+					    GPLL, rate);
+		priv->gpll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[GPLL],
+						      priv->cru, GPLL);
+		break;
+	case PLL_NPLL:
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[NPLL], priv->cru,
+					    NPLL, rate);
+		break;
+	case PLL_V0PLL:
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[V0PLL], priv->cru,
+					    V0PLL, rate);
+		priv->v0pll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[V0PLL],
+						       priv->cru, V0PLL);
+		break;
+	case PLL_AUPLL:
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[AUPLL], priv->cru,
+					    AUPLL, rate);
+		priv->aupll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[AUPLL],
+						       priv->cru, AUPLL);
+		break;
+	case PLL_PPLL:
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[PPLL], priv->cru,
+					    PPLL, rate);
+		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
+						      priv->cru, PPLL);
+		break;
+	case ACLK_CENTER_ROOT:
+	case PCLK_CENTER_ROOT:
+	case HCLK_CENTER_ROOT:
+	case ACLK_CENTER_LOW_ROOT:
+		ret = rk3588_center_set_clk(priv, clk->id, rate);
+		break;
+	case ACLK_TOP_ROOT:
+	case PCLK_TOP_ROOT:
+	case ACLK_LOW_TOP_ROOT:
+		ret = rk3588_top_set_clk(priv, clk->id, rate);
+		break;
+	case CLK_I2C0:
+	case CLK_I2C1:
+	case CLK_I2C2:
+	case CLK_I2C3:
+	case CLK_I2C4:
+	case CLK_I2C5:
+	case CLK_I2C6:
+	case CLK_I2C7:
+	case CLK_I2C8:
+		ret = rk3588_i2c_set_clk(priv, clk->id, rate);
+		break;
+	case CLK_SPI0:
+	case CLK_SPI1:
+	case CLK_SPI2:
+	case CLK_SPI3:
+	case CLK_SPI4:
+		ret = rk3588_spi_set_clk(priv, clk->id, rate);
+		break;
+	case CLK_PWM1:
+	case CLK_PWM2:
+	case CLK_PWM3:
+	case CLK_PMU1PWM:
+		ret = rk3588_pwm_set_clk(priv, clk->id, rate);
+		break;
+	case CLK_SARADC:
+	case CLK_TSADC:
+		ret = rk3588_adc_set_clk(priv, clk->id, rate);
+		break;
+	case CCLK_SRC_SDIO:
+	case CCLK_EMMC:
+	case BCLK_EMMC:
+	case SCLK_SFC:
+	case DCLK_DECOM:
+		ret = rk3588_mmc_set_clk(priv, clk->id, rate);
+		break;
+	case TCLK_WDT0:
+		ret = OSC_HZ;
+		break;
+#ifndef CONFIG_SPL_BUILD
+	case CLK_AUX16M_0:
+	case CLK_AUX16M_1:
+		rk3588_aux16m_set_clk(priv, clk->id, rate);
+		break;
+	case ACLK_VOP_ROOT:
+	case ACLK_VOP:
+	case ACLK_VOP_LOW_ROOT:
+	case HCLK_VOP_ROOT:
+		ret = rk3588_aclk_vop_set_clk(priv, clk->id, rate);
+		break;
+	case DCLK_VOP0:
+	case DCLK_VOP0_SRC:
+	case DCLK_VOP1:
+	case DCLK_VOP1_SRC:
+	case DCLK_VOP2:
+	case DCLK_VOP2_SRC:
+	case DCLK_VOP3:
+		ret = rk3588_dclk_vop_set_clk(priv, clk->id, rate);
+		break;
+	case CLK_GMAC0_PTP_REF:
+	case CLK_GMAC1_PTP_REF:
+	case CLK_GMAC_125M:
+	case CLK_GMAC_50M:
+		ret = rk3588_gmac_set_clk(priv, clk->id, rate);
+		break;
+	case SCLK_UART1:
+	case SCLK_UART2:
+	case SCLK_UART3:
+	case SCLK_UART4:
+	case SCLK_UART5:
+	case SCLK_UART6:
+	case SCLK_UART7:
+	case SCLK_UART8:
+	case SCLK_UART9:
+		ret = rk3588_uart_set_rate(priv, clk->id, rate);
+		break;
+	case CLK_REF_PIPE_PHY0:
+	case CLK_REF_PIPE_PHY1:
+	case CLK_REF_PIPE_PHY2:
+		ret = rk3588_pciephy_set_rate(priv, clk->id, rate);
+		break;
+#endif
+	default:
+		return -ENOENT;
+	}
+
+	return ret;
+};
+
+#define ROCKCHIP_MMC_DELAY_SEL		BIT(10)
+#define ROCKCHIP_MMC_DEGREE_MASK	0x3
+#define ROCKCHIP_MMC_DELAYNUM_OFFSET	2
+#define ROCKCHIP_MMC_DELAYNUM_MASK	(0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
+
+#define PSECS_PER_SEC 1000000000000LL
+/*
+ * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
+ * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
+ */
+#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
+
+#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA))
+static int __maybe_unused rk3588_dclk_vop_set_parent(struct clk *clk,
+						     struct clk *parent)
+{
+	struct rk3588_clk_priv *priv = dev_get_priv(clk->dev);
+	struct rk3588_cru *cru = priv->cru;
+	u32 sel;
+	const char *clock_dev_name = parent->dev->name;
+
+	if (parent->id == PLL_V0PLL)
+		sel = 2;
+	else if (parent->id == PLL_GPLL)
+		sel = 0;
+	else if (parent->id == PLL_CPLL)
+		sel = 1;
+	else
+		sel = 3;
+
+	switch (clk->id) {
+	case DCLK_VOP0_SRC:
+		rk_clrsetreg(&cru->clksel_con[111], DCLK0_VOP_SRC_SEL_MASK,
+			     sel << DCLK0_VOP_SRC_SEL_SHIFT);
+		break;
+	case DCLK_VOP1_SRC:
+		rk_clrsetreg(&cru->clksel_con[111], DCLK1_VOP_SRC_SEL_MASK,
+			     sel << DCLK1_VOP_SRC_SEL_SHIFT);
+		break;
+	case DCLK_VOP2_SRC:
+		rk_clrsetreg(&cru->clksel_con[112], DCLK2_VOP_SRC_SEL_MASK,
+			     sel << DCLK2_VOP_SRC_SEL_SHIFT);
+		break;
+	case DCLK_VOP3:
+		rk_clrsetreg(&cru->clksel_con[113], DCLK3_VOP_SRC_SEL_MASK,
+			     sel << DCLK3_VOP_SRC_SEL_SHIFT);
+		break;
+	case DCLK_VOP0:
+		if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
+			sel = 1;
+		else if (!strcmp(clock_dev_name, "hdmiphypll_clk1"))
+			sel = 2;
+		else
+			sel = 0;
+		rk_clrsetreg(&cru->clksel_con[112], DCLK0_VOP_SEL_MASK,
+			     sel << DCLK0_VOP_SEL_SHIFT);
+		break;
+	case DCLK_VOP1:
+		if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
+			sel = 1;
+		else if (!strcmp(clock_dev_name, "hdmiphypll_clk1"))
+			sel = 2;
+		else
+			sel = 0;
+		rk_clrsetreg(&cru->clksel_con[112], DCLK1_VOP_SEL_MASK,
+			     sel << DCLK1_VOP_SEL_SHIFT);
+		break;
+	case DCLK_VOP2:
+		if (!strcmp(clock_dev_name, "hdmiphypll_clk0"))
+			sel = 1;
+		else if (!strcmp(clock_dev_name, "hdmiphypll_clk1"))
+			sel = 2;
+		else
+			sel = 0;
+		rk_clrsetreg(&cru->clksel_con[112], DCLK2_VOP_SEL_MASK,
+			     sel << DCLK2_VOP_SEL_SHIFT);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int rk3588_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	switch (clk->id) {
+	case DCLK_VOP0_SRC:
+	case DCLK_VOP1_SRC:
+	case DCLK_VOP2_SRC:
+	case DCLK_VOP0:
+	case DCLK_VOP1:
+	case DCLK_VOP2:
+	case DCLK_VOP3:
+		return rk3588_dclk_vop_set_parent(clk, parent);
+	default:
+		return -ENOENT;
+	}
+
+	return 0;
+}
+#endif
+
+static struct clk_ops rk3588_clk_ops = {
+	.get_rate = rk3588_clk_get_rate,
+	.set_rate = rk3588_clk_set_rate,
+#if (IS_ENABLED(OF_CONTROL)) || (!IS_ENABLED(OF_PLATDATA))
+	.set_parent = rk3588_clk_set_parent,
+#endif
+};
+
+static void rk3588_clk_init(struct rk3588_clk_priv *priv)
+{
+	int ret, div;
+
+	div = DIV_ROUND_UP(GPLL_HZ, 300 * MHz);
+	rk_clrsetreg(&priv->cru->clksel_con[38],
+		     ACLK_BUS_ROOT_SEL_MASK |
+		     ACLK_BUS_ROOT_DIV_MASK,
+		     div << ACLK_BUS_ROOT_DIV_SHIFT);
+
+	if (priv->cpll_hz != CPLL_HZ) {
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[CPLL], priv->cru,
+					    CPLL, CPLL_HZ);
+		if (!ret)
+			priv->cpll_hz = CPLL_HZ;
+	}
+	if (priv->gpll_hz != GPLL_HZ) {
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[GPLL], priv->cru,
+					    GPLL, GPLL_HZ);
+		if (!ret)
+			priv->gpll_hz = GPLL_HZ;
+	}
+
+#ifdef CONFIG_PCI
+	if (priv->ppll_hz != PPLL_HZ) {
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[PPLL], priv->cru,
+					    PPLL, PPLL_HZ);
+		priv->ppll_hz = rockchip_pll_get_rate(&rk3588_pll_clks[PPLL],
+						      priv->cru, PPLL);
+	}
+#endif
+	rk_clrsetreg(&priv->cru->clksel_con[9],
+		     ACLK_TOP_S400_SEL_MASK |
+		     ACLK_TOP_S200_SEL_MASK,
+		     (ACLK_TOP_S400_SEL_400M << ACLK_TOP_S400_SEL_SHIFT) |
+		     (ACLK_TOP_S200_SEL_200M << ACLK_TOP_S200_SEL_SHIFT));
+}
+
+static int rk3588_clk_probe(struct udevice *dev)
+{
+	struct rk3588_clk_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	priv->sync_kernel = false;
+
+#ifdef CONFIG_SPL_BUILD
+	rockchip_pll_set_rate(&rk3588_pll_clks[B0PLL], priv->cru,
+			      B0PLL, LPLL_HZ);
+	rockchip_pll_set_rate(&rk3588_pll_clks[B1PLL], priv->cru,
+			      B1PLL, LPLL_HZ);
+	if (!priv->armclk_enter_hz) {
+		ret = rockchip_pll_set_rate(&rk3588_pll_clks[LPLL], priv->cru,
+					    LPLL, LPLL_HZ);
+		priv->armclk_enter_hz =
+			rockchip_pll_get_rate(&rk3588_pll_clks[LPLL],
+					      priv->cru, LPLL);
+		priv->armclk_init_hz = priv->armclk_enter_hz;
+	}
+#endif
+
+	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+	if (IS_ERR(priv->grf))
+		return PTR_ERR(priv->grf);
+
+	rk3588_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 rk3588_clk_ofdata_to_platdata(struct udevice *dev)
+{
+	struct rk3588_clk_priv *priv = dev_get_priv(dev);
+
+	priv->cru = dev_read_addr_ptr(dev);
+
+	return 0;
+}
+
+static int rk3588_clk_bind(struct udevice *dev)
+{
+	int ret;
+	struct udevice *sys_child;
+	struct sysreset_reg *priv;
+
+	/* 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 rk3588_cru,
+						    glb_srst_fst);
+		priv->glb_srst_snd_value = offsetof(struct rk3588_cru,
+						    glb_srsr_snd);
+		dev_set_priv(sys_child, priv);
+	}
+
+#if CONFIG_IS_ENABLED(RESET_ROCKCHIP)
+	ret = offsetof(struct rk3588_cru, softrst_con[0]);
+	ret = rockchip_reset_bind(dev, ret, 49158);
+	if (ret)
+		debug("Warning: software reset driver bind faile\n");
+#endif
+
+	return 0;
+}
+
+static const struct udevice_id rk3588_clk_ids[] = {
+	{ .compatible = "rockchip,rk3588-cru" },
+	{ }
+};
+
+U_BOOT_DRIVER(rockchip_rk3588_cru) = {
+	.name		= "rockchip_rk3588_cru",
+	.id		= UCLASS_CLK,
+	.of_match	= rk3588_clk_ids,
+	.priv_auto	= sizeof(struct rk3588_clk_priv),
+	.of_to_plat	= rk3588_clk_ofdata_to_platdata,
+	.ops		= &rk3588_clk_ops,
+	.bind		= rk3588_clk_bind,
+	.probe		= rk3588_clk_probe,
+};