Commit 8f659449 authored by Elaine Zhang's avatar Elaine Zhang Committed by Heiko Stuebner
Browse files

clk: rockchip: add pll type for RK3588



Add RK3588 PLL support fully relying on lookup tables like
the other upstream supported rockchip platforms.

Signed-off-by: default avatarElaine Zhang <zhangqing@rock-chips.com>
[rebase and modify code to avoid PLL parameter calculation]
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
Link: https://lore.kernel.org/r/20221018151407.63395-6-sebastian.reichel@collabora.com


Signed-off-by: default avatarHeiko Stuebner <heiko@sntech.de>
parent cf87691f
Loading
Loading
Loading
Loading
+217 −1
Original line number Diff line number Diff line
@@ -842,6 +842,213 @@ static const struct clk_ops rockchip_rk3399_pll_clk_ops = {
	.init = rockchip_rk3399_pll_init,
};

/*
 * PLL used in RK3588
 */

#define RK3588_PLLCON(i)               (i * 0x4)
#define RK3588_PLLCON0_M_MASK          0x3ff
#define RK3588_PLLCON0_M_SHIFT         0
#define RK3588_PLLCON1_P_MASK          0x3f
#define RK3588_PLLCON1_P_SHIFT         0
#define RK3588_PLLCON1_S_MASK          0x7
#define RK3588_PLLCON1_S_SHIFT         6
#define RK3588_PLLCON2_K_MASK          0xffff
#define RK3588_PLLCON2_K_SHIFT         0
#define RK3588_PLLCON1_PWRDOWN         BIT(13)
#define RK3588_PLLCON6_LOCK_STATUS     BIT(15)

static int rockchip_rk3588_pll_wait_lock(struct rockchip_clk_pll *pll)
{
	u32 pllcon;
	int ret;

	/*
	 * Lock time typical 250, max 500 input clock cycles @24MHz
	 * So define a very safe maximum of 1000us, meaning 24000 cycles.
	 */
	ret = readl_relaxed_poll_timeout(pll->reg_base + RK3588_PLLCON(6),
					 pllcon,
					 pllcon & RK3588_PLLCON6_LOCK_STATUS,
					 0, 1000);
	if (ret)
		pr_err("%s: timeout waiting for pll to lock\n", __func__);

	return ret;
}

static void rockchip_rk3588_pll_get_params(struct rockchip_clk_pll *pll,
					   struct rockchip_pll_rate_table *rate)
{
	u32 pllcon;

	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(0));
	rate->m = ((pllcon >> RK3588_PLLCON0_M_SHIFT) & RK3588_PLLCON0_M_MASK);

	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1));
	rate->p = ((pllcon >> RK3588_PLLCON1_P_SHIFT) & RK3588_PLLCON1_P_MASK);
	rate->s = ((pllcon >> RK3588_PLLCON1_S_SHIFT) & RK3588_PLLCON1_S_MASK);

	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(2));
	rate->k = ((pllcon >> RK3588_PLLCON2_K_SHIFT) & RK3588_PLLCON2_K_MASK);
}

static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
	struct rockchip_pll_rate_table cur;
	u64 rate64 = prate, postdiv;

	rockchip_rk3588_pll_get_params(pll, &cur);

	rate64 *= cur.m;
	do_div(rate64, cur.p);

	if (cur.k) {
		/* fractional mode */
		u64 frac_rate64 = prate * cur.k;

		postdiv = cur.p * 65535;
		do_div(frac_rate64, postdiv);
		rate64 += frac_rate64;
	}
	rate64 = rate64 >> cur.s;

	return (unsigned long)rate64;
}

static int rockchip_rk3588_pll_set_params(struct rockchip_clk_pll *pll,
					  const struct rockchip_pll_rate_table *rate)
{
	const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
	struct clk_mux *pll_mux = &pll->pll_mux;
	struct rockchip_pll_rate_table cur;
	int rate_change_remuxed = 0;
	int cur_parent;
	int ret;

	pr_debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n",
		 __func__, rate->rate, rate->p, rate->m, rate->s, rate->k);

	rockchip_rk3588_pll_get_params(pll, &cur);
	cur.rate = 0;

	if (pll->type == pll_rk3588) {
		cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
		if (cur_parent == PLL_MODE_NORM) {
			pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
			rate_change_remuxed = 1;
		}
	}

	/* set pll power down */
	writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN,
			     RK3588_PLLCON1_PWRDOWN, 0),
	       pll->reg_base + RK3399_PLLCON(1));

	/* update pll values */
	writel_relaxed(HIWORD_UPDATE(rate->m, RK3588_PLLCON0_M_MASK, RK3588_PLLCON0_M_SHIFT),
		       pll->reg_base + RK3399_PLLCON(0));

	writel_relaxed(HIWORD_UPDATE(rate->p, RK3588_PLLCON1_P_MASK, RK3588_PLLCON1_P_SHIFT) |
		       HIWORD_UPDATE(rate->s, RK3588_PLLCON1_S_MASK, RK3588_PLLCON1_S_SHIFT),
		       pll->reg_base + RK3399_PLLCON(1));

	writel_relaxed(HIWORD_UPDATE(rate->k, RK3588_PLLCON2_K_MASK, RK3588_PLLCON2_K_SHIFT),
		       pll->reg_base + RK3399_PLLCON(2));

	/* set pll power up */
	writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0),
	       pll->reg_base + RK3588_PLLCON(1));

	/* wait for the pll to lock */
	ret = rockchip_rk3588_pll_wait_lock(pll);
	if (ret) {
		pr_warn("%s: pll update unsuccessful, trying to restore old params\n",
			__func__);
		rockchip_rk3588_pll_set_params(pll, &cur);
	}

	if ((pll->type == pll_rk3588) && rate_change_remuxed)
		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);

	return ret;
}

static int rockchip_rk3588_pll_set_rate(struct clk_hw *hw, unsigned long drate,
					unsigned long prate)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
	const struct rockchip_pll_rate_table *rate;

	pr_debug("%s: changing %s to %lu with a parent rate of %lu\n",
		 __func__, __clk_get_name(hw->clk), drate, prate);

	/* Get required rate settings from table */
	rate = rockchip_get_pll_settings(pll, drate);
	if (!rate) {
		pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
			drate, __clk_get_name(hw->clk));
		return -EINVAL;
	}

	return rockchip_rk3588_pll_set_params(pll, rate);
}

static int rockchip_rk3588_pll_enable(struct clk_hw *hw)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);

	writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0),
	       pll->reg_base + RK3588_PLLCON(1));
	rockchip_rk3588_pll_wait_lock(pll);

	return 0;
}

static void rockchip_rk3588_pll_disable(struct clk_hw *hw)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);

	writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN, RK3588_PLLCON1_PWRDOWN, 0),
	       pll->reg_base + RK3588_PLLCON(1));
}

static int rockchip_rk3588_pll_is_enabled(struct clk_hw *hw)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
	u32 pllcon = readl(pll->reg_base + RK3588_PLLCON(1));

	return !(pllcon & RK3588_PLLCON1_PWRDOWN);
}

static int rockchip_rk3588_pll_init(struct clk_hw *hw)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);

	if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
		return 0;

	return 0;
}

static const struct clk_ops rockchip_rk3588_pll_clk_norate_ops = {
	.recalc_rate = rockchip_rk3588_pll_recalc_rate,
	.enable = rockchip_rk3588_pll_enable,
	.disable = rockchip_rk3588_pll_disable,
	.is_enabled = rockchip_rk3588_pll_is_enabled,
};

static const struct clk_ops rockchip_rk3588_pll_clk_ops = {
	.recalc_rate = rockchip_rk3588_pll_recalc_rate,
	.round_rate = rockchip_pll_round_rate,
	.set_rate = rockchip_rk3588_pll_set_rate,
	.enable = rockchip_rk3588_pll_enable,
	.disable = rockchip_rk3588_pll_disable,
	.is_enabled = rockchip_rk3588_pll_is_enabled,
	.init = rockchip_rk3588_pll_init,
};

/*
 * Common registering of pll clocks
 */
@@ -890,7 +1097,8 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
	if (pll_type == pll_rk3036 ||
	    pll_type == pll_rk3066 ||
	    pll_type == pll_rk3328 ||
	    pll_type == pll_rk3399)
	    pll_type == pll_rk3399 ||
	    pll_type == pll_rk3588)
		pll_mux->flags |= CLK_MUX_HIWORD_MASK;

	/* the actual muxing is xin24m, pll-output, xin32k */
@@ -957,6 +1165,14 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
		else
			init.ops = &rockchip_rk3399_pll_clk_ops;
		break;
	case pll_rk3588:
	case pll_rk3588_core:
		if (!pll->rate_table)
			init.ops = &rockchip_rk3588_pll_clk_norate_ops;
		else
			init.ops = &rockchip_rk3588_pll_clk_ops;
		init.flags = flags;
		break;
	default:
		pr_warn("%s: Unknown pll type for pll clk %s\n",
			__func__, name);
+18 −0
Original line number Diff line number Diff line
@@ -240,6 +240,8 @@ enum rockchip_pll_type {
	pll_rk3066,
	pll_rk3328,
	pll_rk3399,
	pll_rk3588,
	pll_rk3588_core,
};

#define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1,	\
@@ -272,6 +274,15 @@ enum rockchip_pll_type {
	.nb = _nb,						\
}

#define RK3588_PLL_RATE(_rate, _p, _m, _s, _k)			\
{								\
	.rate   = _rate##U,					\
	.p = _p,						\
	.m = _m,						\
	.s = _s,						\
	.k = _k,						\
}

/**
 * struct rockchip_clk_provider - information about clock provider
 * @reg_base: virtual address for the register base.
@@ -307,6 +318,13 @@ struct rockchip_pll_rate_table {
			unsigned int dsmpd;
			unsigned int frac;
		};
		struct {
			/* for RK3588 */
			unsigned int m;
			unsigned int p;
			unsigned int s;
			unsigned int k;
		};
	};
};