18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014 MundoReader S.L.
48c2ecf20Sopenharmony_ci * Author: Heiko Stuebner <heiko@sntech.de>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2015 Rockchip Electronics Co. Ltd.
78c2ecf20Sopenharmony_ci * Author: Xing Zheng <zhengxing@rock-chips.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/div64.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
158c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
168c2ecf20Sopenharmony_ci#include <linux/regmap.h>
178c2ecf20Sopenharmony_ci#include <linux/clk.h>
188c2ecf20Sopenharmony_ci#include "clk.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define PLL_MODE_MASK		0x3
218c2ecf20Sopenharmony_ci#define PLL_MODE_SLOW		0x0
228c2ecf20Sopenharmony_ci#define PLL_MODE_NORM		0x1
238c2ecf20Sopenharmony_ci#define PLL_MODE_DEEP		0x2
248c2ecf20Sopenharmony_ci#define PLL_RK3328_MODE_MASK	0x1
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct rockchip_clk_pll {
278c2ecf20Sopenharmony_ci	struct clk_hw		hw;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	struct clk_mux		pll_mux;
308c2ecf20Sopenharmony_ci	const struct clk_ops	*pll_mux_ops;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	struct notifier_block	clk_nb;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	void __iomem		*reg_base;
358c2ecf20Sopenharmony_ci	int			lock_offset;
368c2ecf20Sopenharmony_ci	unsigned int		lock_shift;
378c2ecf20Sopenharmony_ci	enum rockchip_pll_type	type;
388c2ecf20Sopenharmony_ci	u8			flags;
398c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table *rate_table;
408c2ecf20Sopenharmony_ci	unsigned int		rate_count;
418c2ecf20Sopenharmony_ci	spinlock_t		*lock;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	struct rockchip_clk_provider *ctx;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw)
478c2ecf20Sopenharmony_ci#define to_rockchip_clk_pll_nb(nb) \
488c2ecf20Sopenharmony_ci			container_of(nb, struct rockchip_clk_pll, clk_nb)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic const struct rockchip_pll_rate_table *rockchip_get_pll_settings(
518c2ecf20Sopenharmony_ci			    struct rockchip_clk_pll *pll, unsigned long rate)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table  *rate_table = pll->rate_table;
548c2ecf20Sopenharmony_ci	int i;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	for (i = 0; i < pll->rate_count; i++) {
578c2ecf20Sopenharmony_ci		if (rate == rate_table[i].rate)
588c2ecf20Sopenharmony_ci			return &rate_table[i];
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return NULL;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic long rockchip_pll_round_rate(struct clk_hw *hw,
658c2ecf20Sopenharmony_ci			    unsigned long drate, unsigned long *prate)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
688c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table *rate_table = pll->rate_table;
698c2ecf20Sopenharmony_ci	int i;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/* Assumming rate_table is in descending order */
728c2ecf20Sopenharmony_ci	for (i = 0; i < pll->rate_count; i++) {
738c2ecf20Sopenharmony_ci		if (drate >= rate_table[i].rate)
748c2ecf20Sopenharmony_ci			return rate_table[i].rate;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* return minimum supported value */
788c2ecf20Sopenharmony_ci	return rate_table[i - 1].rate;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/*
828c2ecf20Sopenharmony_ci * Wait for the pll to reach the locked state.
838c2ecf20Sopenharmony_ci * The calling set_rate function is responsible for making sure the
848c2ecf20Sopenharmony_ci * grf regmap is available.
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct regmap *grf = pll->ctx->grf;
898c2ecf20Sopenharmony_ci	unsigned int val;
908c2ecf20Sopenharmony_ci	int ret;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	ret = regmap_read_poll_timeout(grf, pll->lock_offset, val,
938c2ecf20Sopenharmony_ci				       val & BIT(pll->lock_shift), 0, 1000);
948c2ecf20Sopenharmony_ci	if (ret)
958c2ecf20Sopenharmony_ci		pr_err("%s: timeout waiting for pll to lock\n", __func__);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return ret;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/**
1018c2ecf20Sopenharmony_ci * PLL used in RK3036
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define RK3036_PLLCON(i)			(i * 0x4)
1058c2ecf20Sopenharmony_ci#define RK3036_PLLCON0_FBDIV_MASK		0xfff
1068c2ecf20Sopenharmony_ci#define RK3036_PLLCON0_FBDIV_SHIFT		0
1078c2ecf20Sopenharmony_ci#define RK3036_PLLCON0_POSTDIV1_MASK		0x7
1088c2ecf20Sopenharmony_ci#define RK3036_PLLCON0_POSTDIV1_SHIFT		12
1098c2ecf20Sopenharmony_ci#define RK3036_PLLCON1_REFDIV_MASK		0x3f
1108c2ecf20Sopenharmony_ci#define RK3036_PLLCON1_REFDIV_SHIFT		0
1118c2ecf20Sopenharmony_ci#define RK3036_PLLCON1_POSTDIV2_MASK		0x7
1128c2ecf20Sopenharmony_ci#define RK3036_PLLCON1_POSTDIV2_SHIFT		6
1138c2ecf20Sopenharmony_ci#define RK3036_PLLCON1_LOCK_STATUS		BIT(10)
1148c2ecf20Sopenharmony_ci#define RK3036_PLLCON1_DSMPD_MASK		0x1
1158c2ecf20Sopenharmony_ci#define RK3036_PLLCON1_DSMPD_SHIFT		12
1168c2ecf20Sopenharmony_ci#define RK3036_PLLCON1_PWRDOWN			BIT(13)
1178c2ecf20Sopenharmony_ci#define RK3036_PLLCON2_FRAC_MASK		0xffffff
1188c2ecf20Sopenharmony_ci#define RK3036_PLLCON2_FRAC_SHIFT		0
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int rockchip_rk3036_pll_wait_lock(struct rockchip_clk_pll *pll)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	u32 pllcon;
1238c2ecf20Sopenharmony_ci	int ret;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/*
1268c2ecf20Sopenharmony_ci	 * Lock time typical 250, max 500 input clock cycles @24MHz
1278c2ecf20Sopenharmony_ci	 * So define a very safe maximum of 1000us, meaning 24000 cycles.
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	ret = readl_relaxed_poll_timeout(pll->reg_base + RK3036_PLLCON(1),
1308c2ecf20Sopenharmony_ci					 pllcon,
1318c2ecf20Sopenharmony_ci					 pllcon & RK3036_PLLCON1_LOCK_STATUS,
1328c2ecf20Sopenharmony_ci					 0, 1000);
1338c2ecf20Sopenharmony_ci	if (ret)
1348c2ecf20Sopenharmony_ci		pr_err("%s: timeout waiting for pll to lock\n", __func__);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return ret;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic void rockchip_rk3036_pll_get_params(struct rockchip_clk_pll *pll,
1408c2ecf20Sopenharmony_ci					struct rockchip_pll_rate_table *rate)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	u32 pllcon;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3036_PLLCON(0));
1458c2ecf20Sopenharmony_ci	rate->fbdiv = ((pllcon >> RK3036_PLLCON0_FBDIV_SHIFT)
1468c2ecf20Sopenharmony_ci				& RK3036_PLLCON0_FBDIV_MASK);
1478c2ecf20Sopenharmony_ci	rate->postdiv1 = ((pllcon >> RK3036_PLLCON0_POSTDIV1_SHIFT)
1488c2ecf20Sopenharmony_ci				& RK3036_PLLCON0_POSTDIV1_MASK);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3036_PLLCON(1));
1518c2ecf20Sopenharmony_ci	rate->refdiv = ((pllcon >> RK3036_PLLCON1_REFDIV_SHIFT)
1528c2ecf20Sopenharmony_ci				& RK3036_PLLCON1_REFDIV_MASK);
1538c2ecf20Sopenharmony_ci	rate->postdiv2 = ((pllcon >> RK3036_PLLCON1_POSTDIV2_SHIFT)
1548c2ecf20Sopenharmony_ci				& RK3036_PLLCON1_POSTDIV2_MASK);
1558c2ecf20Sopenharmony_ci	rate->dsmpd = ((pllcon >> RK3036_PLLCON1_DSMPD_SHIFT)
1568c2ecf20Sopenharmony_ci				& RK3036_PLLCON1_DSMPD_MASK);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3036_PLLCON(2));
1598c2ecf20Sopenharmony_ci	rate->frac = ((pllcon >> RK3036_PLLCON2_FRAC_SHIFT)
1608c2ecf20Sopenharmony_ci				& RK3036_PLLCON2_FRAC_MASK);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic unsigned long rockchip_rk3036_pll_recalc_rate(struct clk_hw *hw,
1648c2ecf20Sopenharmony_ci						     unsigned long prate)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
1678c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
1688c2ecf20Sopenharmony_ci	u64 rate64 = prate;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	rockchip_rk3036_pll_get_params(pll, &cur);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	rate64 *= cur.fbdiv;
1738c2ecf20Sopenharmony_ci	do_div(rate64, cur.refdiv);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (cur.dsmpd == 0) {
1768c2ecf20Sopenharmony_ci		/* fractional mode */
1778c2ecf20Sopenharmony_ci		u64 frac_rate64 = prate * cur.frac;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		do_div(frac_rate64, cur.refdiv);
1808c2ecf20Sopenharmony_ci		rate64 += frac_rate64 >> 24;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	do_div(rate64, cur.postdiv1);
1848c2ecf20Sopenharmony_ci	do_div(rate64, cur.postdiv2);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return (unsigned long)rate64;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic int rockchip_rk3036_pll_set_params(struct rockchip_clk_pll *pll,
1908c2ecf20Sopenharmony_ci				const struct rockchip_pll_rate_table *rate)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
1938c2ecf20Sopenharmony_ci	struct clk_mux *pll_mux = &pll->pll_mux;
1948c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
1958c2ecf20Sopenharmony_ci	u32 pllcon;
1968c2ecf20Sopenharmony_ci	int rate_change_remuxed = 0;
1978c2ecf20Sopenharmony_ci	int cur_parent;
1988c2ecf20Sopenharmony_ci	int ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	pr_debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
2018c2ecf20Sopenharmony_ci		__func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv,
2028c2ecf20Sopenharmony_ci		rate->postdiv2, rate->dsmpd, rate->frac);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	rockchip_rk3036_pll_get_params(pll, &cur);
2058c2ecf20Sopenharmony_ci	cur.rate = 0;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
2088c2ecf20Sopenharmony_ci	if (cur_parent == PLL_MODE_NORM) {
2098c2ecf20Sopenharmony_ci		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
2108c2ecf20Sopenharmony_ci		rate_change_remuxed = 1;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* update pll values */
2148c2ecf20Sopenharmony_ci	writel_relaxed(HIWORD_UPDATE(rate->fbdiv, RK3036_PLLCON0_FBDIV_MASK,
2158c2ecf20Sopenharmony_ci					  RK3036_PLLCON0_FBDIV_SHIFT) |
2168c2ecf20Sopenharmony_ci		       HIWORD_UPDATE(rate->postdiv1, RK3036_PLLCON0_POSTDIV1_MASK,
2178c2ecf20Sopenharmony_ci					     RK3036_PLLCON0_POSTDIV1_SHIFT),
2188c2ecf20Sopenharmony_ci		       pll->reg_base + RK3036_PLLCON(0));
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	writel_relaxed(HIWORD_UPDATE(rate->refdiv, RK3036_PLLCON1_REFDIV_MASK,
2218c2ecf20Sopenharmony_ci						   RK3036_PLLCON1_REFDIV_SHIFT) |
2228c2ecf20Sopenharmony_ci		       HIWORD_UPDATE(rate->postdiv2, RK3036_PLLCON1_POSTDIV2_MASK,
2238c2ecf20Sopenharmony_ci						     RK3036_PLLCON1_POSTDIV2_SHIFT) |
2248c2ecf20Sopenharmony_ci		       HIWORD_UPDATE(rate->dsmpd, RK3036_PLLCON1_DSMPD_MASK,
2258c2ecf20Sopenharmony_ci						  RK3036_PLLCON1_DSMPD_SHIFT),
2268c2ecf20Sopenharmony_ci		       pll->reg_base + RK3036_PLLCON(1));
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* GPLL CON2 is not HIWORD_MASK */
2298c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3036_PLLCON(2));
2308c2ecf20Sopenharmony_ci	pllcon &= ~(RK3036_PLLCON2_FRAC_MASK << RK3036_PLLCON2_FRAC_SHIFT);
2318c2ecf20Sopenharmony_ci	pllcon |= rate->frac << RK3036_PLLCON2_FRAC_SHIFT;
2328c2ecf20Sopenharmony_ci	writel_relaxed(pllcon, pll->reg_base + RK3036_PLLCON(2));
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* wait for the pll to lock */
2358c2ecf20Sopenharmony_ci	ret = rockchip_rk3036_pll_wait_lock(pll);
2368c2ecf20Sopenharmony_ci	if (ret) {
2378c2ecf20Sopenharmony_ci		pr_warn("%s: pll update unsuccessful, trying to restore old params\n",
2388c2ecf20Sopenharmony_ci			__func__);
2398c2ecf20Sopenharmony_ci		rockchip_rk3036_pll_set_params(pll, &cur);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (rate_change_remuxed)
2438c2ecf20Sopenharmony_ci		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return ret;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int rockchip_rk3036_pll_set_rate(struct clk_hw *hw, unsigned long drate,
2498c2ecf20Sopenharmony_ci					unsigned long prate)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
2528c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table *rate;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	pr_debug("%s: changing %s to %lu with a parent rate of %lu\n",
2558c2ecf20Sopenharmony_ci		 __func__, __clk_get_name(hw->clk), drate, prate);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* Get required rate settings from table */
2588c2ecf20Sopenharmony_ci	rate = rockchip_get_pll_settings(pll, drate);
2598c2ecf20Sopenharmony_ci	if (!rate) {
2608c2ecf20Sopenharmony_ci		pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
2618c2ecf20Sopenharmony_ci			drate, __clk_get_name(hw->clk));
2628c2ecf20Sopenharmony_ci		return -EINVAL;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return rockchip_rk3036_pll_set_params(pll, rate);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int rockchip_rk3036_pll_enable(struct clk_hw *hw)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(0, RK3036_PLLCON1_PWRDOWN, 0),
2738c2ecf20Sopenharmony_ci	       pll->reg_base + RK3036_PLLCON(1));
2748c2ecf20Sopenharmony_ci	rockchip_rk3036_pll_wait_lock(pll);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return 0;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void rockchip_rk3036_pll_disable(struct clk_hw *hw)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(RK3036_PLLCON1_PWRDOWN,
2848c2ecf20Sopenharmony_ci			     RK3036_PLLCON1_PWRDOWN, 0),
2858c2ecf20Sopenharmony_ci	       pll->reg_base + RK3036_PLLCON(1));
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic int rockchip_rk3036_pll_is_enabled(struct clk_hw *hw)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
2918c2ecf20Sopenharmony_ci	u32 pllcon = readl(pll->reg_base + RK3036_PLLCON(1));
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return !(pllcon & RK3036_PLLCON1_PWRDOWN);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int rockchip_rk3036_pll_init(struct clk_hw *hw)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
2998c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table *rate;
3008c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
3018c2ecf20Sopenharmony_ci	unsigned long drate;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
3048c2ecf20Sopenharmony_ci		return 0;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	drate = clk_hw_get_rate(hw);
3078c2ecf20Sopenharmony_ci	rate = rockchip_get_pll_settings(pll, drate);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* when no rate setting for the current rate, rely on clk_set_rate */
3108c2ecf20Sopenharmony_ci	if (!rate)
3118c2ecf20Sopenharmony_ci		return 0;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	rockchip_rk3036_pll_get_params(pll, &cur);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	pr_debug("%s: pll %s@%lu: Hz\n", __func__, __clk_get_name(hw->clk),
3168c2ecf20Sopenharmony_ci		 drate);
3178c2ecf20Sopenharmony_ci	pr_debug("old - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
3188c2ecf20Sopenharmony_ci		 cur.fbdiv, cur.postdiv1, cur.refdiv, cur.postdiv2,
3198c2ecf20Sopenharmony_ci		 cur.dsmpd, cur.frac);
3208c2ecf20Sopenharmony_ci	pr_debug("new - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
3218c2ecf20Sopenharmony_ci		 rate->fbdiv, rate->postdiv1, rate->refdiv, rate->postdiv2,
3228c2ecf20Sopenharmony_ci		 rate->dsmpd, rate->frac);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (rate->fbdiv != cur.fbdiv || rate->postdiv1 != cur.postdiv1 ||
3258c2ecf20Sopenharmony_ci		rate->refdiv != cur.refdiv || rate->postdiv2 != cur.postdiv2 ||
3268c2ecf20Sopenharmony_ci		rate->dsmpd != cur.dsmpd ||
3278c2ecf20Sopenharmony_ci		(!cur.dsmpd && (rate->frac != cur.frac))) {
3288c2ecf20Sopenharmony_ci		struct clk *parent = clk_get_parent(hw->clk);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		if (!parent) {
3318c2ecf20Sopenharmony_ci			pr_warn("%s: parent of %s not available\n",
3328c2ecf20Sopenharmony_ci				__func__, __clk_get_name(hw->clk));
3338c2ecf20Sopenharmony_ci			return 0;
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n",
3378c2ecf20Sopenharmony_ci			 __func__, __clk_get_name(hw->clk));
3388c2ecf20Sopenharmony_ci		rockchip_rk3036_pll_set_params(pll, rate);
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_rk3036_pll_clk_norate_ops = {
3458c2ecf20Sopenharmony_ci	.recalc_rate = rockchip_rk3036_pll_recalc_rate,
3468c2ecf20Sopenharmony_ci	.enable = rockchip_rk3036_pll_enable,
3478c2ecf20Sopenharmony_ci	.disable = rockchip_rk3036_pll_disable,
3488c2ecf20Sopenharmony_ci	.is_enabled = rockchip_rk3036_pll_is_enabled,
3498c2ecf20Sopenharmony_ci};
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_rk3036_pll_clk_ops = {
3528c2ecf20Sopenharmony_ci	.recalc_rate = rockchip_rk3036_pll_recalc_rate,
3538c2ecf20Sopenharmony_ci	.round_rate = rockchip_pll_round_rate,
3548c2ecf20Sopenharmony_ci	.set_rate = rockchip_rk3036_pll_set_rate,
3558c2ecf20Sopenharmony_ci	.enable = rockchip_rk3036_pll_enable,
3568c2ecf20Sopenharmony_ci	.disable = rockchip_rk3036_pll_disable,
3578c2ecf20Sopenharmony_ci	.is_enabled = rockchip_rk3036_pll_is_enabled,
3588c2ecf20Sopenharmony_ci	.init = rockchip_rk3036_pll_init,
3598c2ecf20Sopenharmony_ci};
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/**
3628c2ecf20Sopenharmony_ci * PLL used in RK3066, RK3188 and RK3288
3638c2ecf20Sopenharmony_ci */
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci#define RK3066_PLL_RESET_DELAY(nr)	((nr * 500) / 24 + 1)
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci#define RK3066_PLLCON(i)		(i * 0x4)
3688c2ecf20Sopenharmony_ci#define RK3066_PLLCON0_OD_MASK		0xf
3698c2ecf20Sopenharmony_ci#define RK3066_PLLCON0_OD_SHIFT		0
3708c2ecf20Sopenharmony_ci#define RK3066_PLLCON0_NR_MASK		0x3f
3718c2ecf20Sopenharmony_ci#define RK3066_PLLCON0_NR_SHIFT		8
3728c2ecf20Sopenharmony_ci#define RK3066_PLLCON1_NF_MASK		0x1fff
3738c2ecf20Sopenharmony_ci#define RK3066_PLLCON1_NF_SHIFT		0
3748c2ecf20Sopenharmony_ci#define RK3066_PLLCON2_NB_MASK		0xfff
3758c2ecf20Sopenharmony_ci#define RK3066_PLLCON2_NB_SHIFT		0
3768c2ecf20Sopenharmony_ci#define RK3066_PLLCON3_RESET		(1 << 5)
3778c2ecf20Sopenharmony_ci#define RK3066_PLLCON3_PWRDOWN		(1 << 1)
3788c2ecf20Sopenharmony_ci#define RK3066_PLLCON3_BYPASS		(1 << 0)
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic void rockchip_rk3066_pll_get_params(struct rockchip_clk_pll *pll,
3818c2ecf20Sopenharmony_ci					struct rockchip_pll_rate_table *rate)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	u32 pllcon;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0));
3868c2ecf20Sopenharmony_ci	rate->nr = ((pllcon >> RK3066_PLLCON0_NR_SHIFT)
3878c2ecf20Sopenharmony_ci				& RK3066_PLLCON0_NR_MASK) + 1;
3888c2ecf20Sopenharmony_ci	rate->no = ((pllcon >> RK3066_PLLCON0_OD_SHIFT)
3898c2ecf20Sopenharmony_ci				& RK3066_PLLCON0_OD_MASK) + 1;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1));
3928c2ecf20Sopenharmony_ci	rate->nf = ((pllcon >> RK3066_PLLCON1_NF_SHIFT)
3938c2ecf20Sopenharmony_ci				& RK3066_PLLCON1_NF_MASK) + 1;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(2));
3968c2ecf20Sopenharmony_ci	rate->nb = ((pllcon >> RK3066_PLLCON2_NB_SHIFT)
3978c2ecf20Sopenharmony_ci				& RK3066_PLLCON2_NB_MASK) + 1;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic unsigned long rockchip_rk3066_pll_recalc_rate(struct clk_hw *hw,
4018c2ecf20Sopenharmony_ci						     unsigned long prate)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
4048c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
4058c2ecf20Sopenharmony_ci	u64 rate64 = prate;
4068c2ecf20Sopenharmony_ci	u32 pllcon;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(3));
4098c2ecf20Sopenharmony_ci	if (pllcon & RK3066_PLLCON3_BYPASS) {
4108c2ecf20Sopenharmony_ci		pr_debug("%s: pll %s is bypassed\n", __func__,
4118c2ecf20Sopenharmony_ci			clk_hw_get_name(hw));
4128c2ecf20Sopenharmony_ci		return prate;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	rockchip_rk3066_pll_get_params(pll, &cur);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	rate64 *= cur.nf;
4188c2ecf20Sopenharmony_ci	do_div(rate64, cur.nr);
4198c2ecf20Sopenharmony_ci	do_div(rate64, cur.no);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	return (unsigned long)rate64;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic int rockchip_rk3066_pll_set_params(struct rockchip_clk_pll *pll,
4258c2ecf20Sopenharmony_ci				const struct rockchip_pll_rate_table *rate)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
4288c2ecf20Sopenharmony_ci	struct clk_mux *pll_mux = &pll->pll_mux;
4298c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
4308c2ecf20Sopenharmony_ci	int rate_change_remuxed = 0;
4318c2ecf20Sopenharmony_ci	int cur_parent;
4328c2ecf20Sopenharmony_ci	int ret;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n",
4358c2ecf20Sopenharmony_ci		 __func__, rate->rate, rate->nr, rate->no, rate->nf);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	rockchip_rk3066_pll_get_params(pll, &cur);
4388c2ecf20Sopenharmony_ci	cur.rate = 0;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
4418c2ecf20Sopenharmony_ci	if (cur_parent == PLL_MODE_NORM) {
4428c2ecf20Sopenharmony_ci		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
4438c2ecf20Sopenharmony_ci		rate_change_remuxed = 1;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* enter reset mode */
4478c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0),
4488c2ecf20Sopenharmony_ci	       pll->reg_base + RK3066_PLLCON(3));
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	/* update pll values */
4518c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(rate->nr - 1, RK3066_PLLCON0_NR_MASK,
4528c2ecf20Sopenharmony_ci					   RK3066_PLLCON0_NR_SHIFT) |
4538c2ecf20Sopenharmony_ci	       HIWORD_UPDATE(rate->no - 1, RK3066_PLLCON0_OD_MASK,
4548c2ecf20Sopenharmony_ci					   RK3066_PLLCON0_OD_SHIFT),
4558c2ecf20Sopenharmony_ci	       pll->reg_base + RK3066_PLLCON(0));
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	writel_relaxed(HIWORD_UPDATE(rate->nf - 1, RK3066_PLLCON1_NF_MASK,
4588c2ecf20Sopenharmony_ci						   RK3066_PLLCON1_NF_SHIFT),
4598c2ecf20Sopenharmony_ci		       pll->reg_base + RK3066_PLLCON(1));
4608c2ecf20Sopenharmony_ci	writel_relaxed(HIWORD_UPDATE(rate->nb - 1, RK3066_PLLCON2_NB_MASK,
4618c2ecf20Sopenharmony_ci						   RK3066_PLLCON2_NB_SHIFT),
4628c2ecf20Sopenharmony_ci		       pll->reg_base + RK3066_PLLCON(2));
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* leave reset and wait the reset_delay */
4658c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(0, RK3066_PLLCON3_RESET, 0),
4668c2ecf20Sopenharmony_ci	       pll->reg_base + RK3066_PLLCON(3));
4678c2ecf20Sopenharmony_ci	udelay(RK3066_PLL_RESET_DELAY(rate->nr));
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* wait for the pll to lock */
4708c2ecf20Sopenharmony_ci	ret = rockchip_pll_wait_lock(pll);
4718c2ecf20Sopenharmony_ci	if (ret) {
4728c2ecf20Sopenharmony_ci		pr_warn("%s: pll update unsuccessful, trying to restore old params\n",
4738c2ecf20Sopenharmony_ci			__func__);
4748c2ecf20Sopenharmony_ci		rockchip_rk3066_pll_set_params(pll, &cur);
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (rate_change_remuxed)
4788c2ecf20Sopenharmony_ci		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return ret;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
4848c2ecf20Sopenharmony_ci					unsigned long prate)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
4878c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table *rate;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	pr_debug("%s: changing %s to %lu with a parent rate of %lu\n",
4908c2ecf20Sopenharmony_ci		 __func__, clk_hw_get_name(hw), drate, prate);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	/* Get required rate settings from table */
4938c2ecf20Sopenharmony_ci	rate = rockchip_get_pll_settings(pll, drate);
4948c2ecf20Sopenharmony_ci	if (!rate) {
4958c2ecf20Sopenharmony_ci		pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
4968c2ecf20Sopenharmony_ci			drate, clk_hw_get_name(hw));
4978c2ecf20Sopenharmony_ci		return -EINVAL;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	return rockchip_rk3066_pll_set_params(pll, rate);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic int rockchip_rk3066_pll_enable(struct clk_hw *hw)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(0, RK3066_PLLCON3_PWRDOWN, 0),
5088c2ecf20Sopenharmony_ci	       pll->reg_base + RK3066_PLLCON(3));
5098c2ecf20Sopenharmony_ci	rockchip_pll_wait_lock(pll);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	return 0;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic void rockchip_rk3066_pll_disable(struct clk_hw *hw)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(RK3066_PLLCON3_PWRDOWN,
5198c2ecf20Sopenharmony_ci			     RK3066_PLLCON3_PWRDOWN, 0),
5208c2ecf20Sopenharmony_ci	       pll->reg_base + RK3066_PLLCON(3));
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic int rockchip_rk3066_pll_is_enabled(struct clk_hw *hw)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
5268c2ecf20Sopenharmony_ci	u32 pllcon = readl(pll->reg_base + RK3066_PLLCON(3));
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	return !(pllcon & RK3066_PLLCON3_PWRDOWN);
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic int rockchip_rk3066_pll_init(struct clk_hw *hw)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
5348c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table *rate;
5358c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
5368c2ecf20Sopenharmony_ci	unsigned long drate;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
5398c2ecf20Sopenharmony_ci		return 0;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	drate = clk_hw_get_rate(hw);
5428c2ecf20Sopenharmony_ci	rate = rockchip_get_pll_settings(pll, drate);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	/* when no rate setting for the current rate, rely on clk_set_rate */
5458c2ecf20Sopenharmony_ci	if (!rate)
5468c2ecf20Sopenharmony_ci		return 0;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	rockchip_rk3066_pll_get_params(pll, &cur);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	pr_debug("%s: pll %s@%lu: nr (%d:%d); no (%d:%d); nf(%d:%d), nb(%d:%d)\n",
5518c2ecf20Sopenharmony_ci		 __func__, clk_hw_get_name(hw), drate, rate->nr, cur.nr,
5528c2ecf20Sopenharmony_ci		 rate->no, cur.no, rate->nf, cur.nf, rate->nb, cur.nb);
5538c2ecf20Sopenharmony_ci	if (rate->nr != cur.nr || rate->no != cur.no || rate->nf != cur.nf
5548c2ecf20Sopenharmony_ci						     || rate->nb != cur.nb) {
5558c2ecf20Sopenharmony_ci		pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n",
5568c2ecf20Sopenharmony_ci			 __func__, clk_hw_get_name(hw));
5578c2ecf20Sopenharmony_ci		rockchip_rk3066_pll_set_params(pll, rate);
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	return 0;
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_rk3066_pll_clk_norate_ops = {
5648c2ecf20Sopenharmony_ci	.recalc_rate = rockchip_rk3066_pll_recalc_rate,
5658c2ecf20Sopenharmony_ci	.enable = rockchip_rk3066_pll_enable,
5668c2ecf20Sopenharmony_ci	.disable = rockchip_rk3066_pll_disable,
5678c2ecf20Sopenharmony_ci	.is_enabled = rockchip_rk3066_pll_is_enabled,
5688c2ecf20Sopenharmony_ci};
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_rk3066_pll_clk_ops = {
5718c2ecf20Sopenharmony_ci	.recalc_rate = rockchip_rk3066_pll_recalc_rate,
5728c2ecf20Sopenharmony_ci	.round_rate = rockchip_pll_round_rate,
5738c2ecf20Sopenharmony_ci	.set_rate = rockchip_rk3066_pll_set_rate,
5748c2ecf20Sopenharmony_ci	.enable = rockchip_rk3066_pll_enable,
5758c2ecf20Sopenharmony_ci	.disable = rockchip_rk3066_pll_disable,
5768c2ecf20Sopenharmony_ci	.is_enabled = rockchip_rk3066_pll_is_enabled,
5778c2ecf20Sopenharmony_ci	.init = rockchip_rk3066_pll_init,
5788c2ecf20Sopenharmony_ci};
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/**
5818c2ecf20Sopenharmony_ci * PLL used in RK3399
5828c2ecf20Sopenharmony_ci */
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci#define RK3399_PLLCON(i)			(i * 0x4)
5858c2ecf20Sopenharmony_ci#define RK3399_PLLCON0_FBDIV_MASK		0xfff
5868c2ecf20Sopenharmony_ci#define RK3399_PLLCON0_FBDIV_SHIFT		0
5878c2ecf20Sopenharmony_ci#define RK3399_PLLCON1_REFDIV_MASK		0x3f
5888c2ecf20Sopenharmony_ci#define RK3399_PLLCON1_REFDIV_SHIFT		0
5898c2ecf20Sopenharmony_ci#define RK3399_PLLCON1_POSTDIV1_MASK		0x7
5908c2ecf20Sopenharmony_ci#define RK3399_PLLCON1_POSTDIV1_SHIFT		8
5918c2ecf20Sopenharmony_ci#define RK3399_PLLCON1_POSTDIV2_MASK		0x7
5928c2ecf20Sopenharmony_ci#define RK3399_PLLCON1_POSTDIV2_SHIFT		12
5938c2ecf20Sopenharmony_ci#define RK3399_PLLCON2_FRAC_MASK		0xffffff
5948c2ecf20Sopenharmony_ci#define RK3399_PLLCON2_FRAC_SHIFT		0
5958c2ecf20Sopenharmony_ci#define RK3399_PLLCON2_LOCK_STATUS		BIT(31)
5968c2ecf20Sopenharmony_ci#define RK3399_PLLCON3_PWRDOWN			BIT(0)
5978c2ecf20Sopenharmony_ci#define RK3399_PLLCON3_DSMPD_MASK		0x1
5988c2ecf20Sopenharmony_ci#define RK3399_PLLCON3_DSMPD_SHIFT		3
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic int rockchip_rk3399_pll_wait_lock(struct rockchip_clk_pll *pll)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	u32 pllcon;
6038c2ecf20Sopenharmony_ci	int ret;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	/*
6068c2ecf20Sopenharmony_ci	 * Lock time typical 250, max 500 input clock cycles @24MHz
6078c2ecf20Sopenharmony_ci	 * So define a very safe maximum of 1000us, meaning 24000 cycles.
6088c2ecf20Sopenharmony_ci	 */
6098c2ecf20Sopenharmony_ci	ret = readl_relaxed_poll_timeout(pll->reg_base + RK3399_PLLCON(2),
6108c2ecf20Sopenharmony_ci					 pllcon,
6118c2ecf20Sopenharmony_ci					 pllcon & RK3399_PLLCON2_LOCK_STATUS,
6128c2ecf20Sopenharmony_ci					 0, 1000);
6138c2ecf20Sopenharmony_ci	if (ret)
6148c2ecf20Sopenharmony_ci		pr_err("%s: timeout waiting for pll to lock\n", __func__);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	return ret;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic void rockchip_rk3399_pll_get_params(struct rockchip_clk_pll *pll,
6208c2ecf20Sopenharmony_ci					struct rockchip_pll_rate_table *rate)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	u32 pllcon;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(0));
6258c2ecf20Sopenharmony_ci	rate->fbdiv = ((pllcon >> RK3399_PLLCON0_FBDIV_SHIFT)
6268c2ecf20Sopenharmony_ci				& RK3399_PLLCON0_FBDIV_MASK);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(1));
6298c2ecf20Sopenharmony_ci	rate->refdiv = ((pllcon >> RK3399_PLLCON1_REFDIV_SHIFT)
6308c2ecf20Sopenharmony_ci				& RK3399_PLLCON1_REFDIV_MASK);
6318c2ecf20Sopenharmony_ci	rate->postdiv1 = ((pllcon >> RK3399_PLLCON1_POSTDIV1_SHIFT)
6328c2ecf20Sopenharmony_ci				& RK3399_PLLCON1_POSTDIV1_MASK);
6338c2ecf20Sopenharmony_ci	rate->postdiv2 = ((pllcon >> RK3399_PLLCON1_POSTDIV2_SHIFT)
6348c2ecf20Sopenharmony_ci				& RK3399_PLLCON1_POSTDIV2_MASK);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2));
6378c2ecf20Sopenharmony_ci	rate->frac = ((pllcon >> RK3399_PLLCON2_FRAC_SHIFT)
6388c2ecf20Sopenharmony_ci				& RK3399_PLLCON2_FRAC_MASK);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(3));
6418c2ecf20Sopenharmony_ci	rate->dsmpd = ((pllcon >> RK3399_PLLCON3_DSMPD_SHIFT)
6428c2ecf20Sopenharmony_ci				& RK3399_PLLCON3_DSMPD_MASK);
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic unsigned long rockchip_rk3399_pll_recalc_rate(struct clk_hw *hw,
6468c2ecf20Sopenharmony_ci						     unsigned long prate)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
6498c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
6508c2ecf20Sopenharmony_ci	u64 rate64 = prate;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	rockchip_rk3399_pll_get_params(pll, &cur);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	rate64 *= cur.fbdiv;
6558c2ecf20Sopenharmony_ci	do_div(rate64, cur.refdiv);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	if (cur.dsmpd == 0) {
6588c2ecf20Sopenharmony_ci		/* fractional mode */
6598c2ecf20Sopenharmony_ci		u64 frac_rate64 = prate * cur.frac;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci		do_div(frac_rate64, cur.refdiv);
6628c2ecf20Sopenharmony_ci		rate64 += frac_rate64 >> 24;
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	do_div(rate64, cur.postdiv1);
6668c2ecf20Sopenharmony_ci	do_div(rate64, cur.postdiv2);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	return (unsigned long)rate64;
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_cistatic int rockchip_rk3399_pll_set_params(struct rockchip_clk_pll *pll,
6728c2ecf20Sopenharmony_ci				const struct rockchip_pll_rate_table *rate)
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
6758c2ecf20Sopenharmony_ci	struct clk_mux *pll_mux = &pll->pll_mux;
6768c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
6778c2ecf20Sopenharmony_ci	u32 pllcon;
6788c2ecf20Sopenharmony_ci	int rate_change_remuxed = 0;
6798c2ecf20Sopenharmony_ci	int cur_parent;
6808c2ecf20Sopenharmony_ci	int ret;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	pr_debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
6838c2ecf20Sopenharmony_ci		__func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv,
6848c2ecf20Sopenharmony_ci		rate->postdiv2, rate->dsmpd, rate->frac);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	rockchip_rk3399_pll_get_params(pll, &cur);
6878c2ecf20Sopenharmony_ci	cur.rate = 0;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
6908c2ecf20Sopenharmony_ci	if (cur_parent == PLL_MODE_NORM) {
6918c2ecf20Sopenharmony_ci		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
6928c2ecf20Sopenharmony_ci		rate_change_remuxed = 1;
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* update pll values */
6968c2ecf20Sopenharmony_ci	writel_relaxed(HIWORD_UPDATE(rate->fbdiv, RK3399_PLLCON0_FBDIV_MASK,
6978c2ecf20Sopenharmony_ci						  RK3399_PLLCON0_FBDIV_SHIFT),
6988c2ecf20Sopenharmony_ci		       pll->reg_base + RK3399_PLLCON(0));
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	writel_relaxed(HIWORD_UPDATE(rate->refdiv, RK3399_PLLCON1_REFDIV_MASK,
7018c2ecf20Sopenharmony_ci						   RK3399_PLLCON1_REFDIV_SHIFT) |
7028c2ecf20Sopenharmony_ci		       HIWORD_UPDATE(rate->postdiv1, RK3399_PLLCON1_POSTDIV1_MASK,
7038c2ecf20Sopenharmony_ci						     RK3399_PLLCON1_POSTDIV1_SHIFT) |
7048c2ecf20Sopenharmony_ci		       HIWORD_UPDATE(rate->postdiv2, RK3399_PLLCON1_POSTDIV2_MASK,
7058c2ecf20Sopenharmony_ci						     RK3399_PLLCON1_POSTDIV2_SHIFT),
7068c2ecf20Sopenharmony_ci		       pll->reg_base + RK3399_PLLCON(1));
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/* xPLL CON2 is not HIWORD_MASK */
7098c2ecf20Sopenharmony_ci	pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2));
7108c2ecf20Sopenharmony_ci	pllcon &= ~(RK3399_PLLCON2_FRAC_MASK << RK3399_PLLCON2_FRAC_SHIFT);
7118c2ecf20Sopenharmony_ci	pllcon |= rate->frac << RK3399_PLLCON2_FRAC_SHIFT;
7128c2ecf20Sopenharmony_ci	writel_relaxed(pllcon, pll->reg_base + RK3399_PLLCON(2));
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	writel_relaxed(HIWORD_UPDATE(rate->dsmpd, RK3399_PLLCON3_DSMPD_MASK,
7158c2ecf20Sopenharmony_ci					    RK3399_PLLCON3_DSMPD_SHIFT),
7168c2ecf20Sopenharmony_ci		       pll->reg_base + RK3399_PLLCON(3));
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	/* wait for the pll to lock */
7198c2ecf20Sopenharmony_ci	ret = rockchip_rk3399_pll_wait_lock(pll);
7208c2ecf20Sopenharmony_ci	if (ret) {
7218c2ecf20Sopenharmony_ci		pr_warn("%s: pll update unsuccessful, trying to restore old params\n",
7228c2ecf20Sopenharmony_ci			__func__);
7238c2ecf20Sopenharmony_ci		rockchip_rk3399_pll_set_params(pll, &cur);
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (rate_change_remuxed)
7278c2ecf20Sopenharmony_ci		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	return ret;
7308c2ecf20Sopenharmony_ci}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_cistatic int rockchip_rk3399_pll_set_rate(struct clk_hw *hw, unsigned long drate,
7338c2ecf20Sopenharmony_ci					unsigned long prate)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
7368c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table *rate;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	pr_debug("%s: changing %s to %lu with a parent rate of %lu\n",
7398c2ecf20Sopenharmony_ci		 __func__, __clk_get_name(hw->clk), drate, prate);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	/* Get required rate settings from table */
7428c2ecf20Sopenharmony_ci	rate = rockchip_get_pll_settings(pll, drate);
7438c2ecf20Sopenharmony_ci	if (!rate) {
7448c2ecf20Sopenharmony_ci		pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
7458c2ecf20Sopenharmony_ci			drate, __clk_get_name(hw->clk));
7468c2ecf20Sopenharmony_ci		return -EINVAL;
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	return rockchip_rk3399_pll_set_params(pll, rate);
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic int rockchip_rk3399_pll_enable(struct clk_hw *hw)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(0, RK3399_PLLCON3_PWRDOWN, 0),
7578c2ecf20Sopenharmony_ci	       pll->reg_base + RK3399_PLLCON(3));
7588c2ecf20Sopenharmony_ci	rockchip_rk3399_pll_wait_lock(pll);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return 0;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic void rockchip_rk3399_pll_disable(struct clk_hw *hw)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	writel(HIWORD_UPDATE(RK3399_PLLCON3_PWRDOWN,
7688c2ecf20Sopenharmony_ci			     RK3399_PLLCON3_PWRDOWN, 0),
7698c2ecf20Sopenharmony_ci	       pll->reg_base + RK3399_PLLCON(3));
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic int rockchip_rk3399_pll_is_enabled(struct clk_hw *hw)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
7758c2ecf20Sopenharmony_ci	u32 pllcon = readl(pll->reg_base + RK3399_PLLCON(3));
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	return !(pllcon & RK3399_PLLCON3_PWRDOWN);
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic int rockchip_rk3399_pll_init(struct clk_hw *hw)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
7838c2ecf20Sopenharmony_ci	const struct rockchip_pll_rate_table *rate;
7848c2ecf20Sopenharmony_ci	struct rockchip_pll_rate_table cur;
7858c2ecf20Sopenharmony_ci	unsigned long drate;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
7888c2ecf20Sopenharmony_ci		return 0;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	drate = clk_hw_get_rate(hw);
7918c2ecf20Sopenharmony_ci	rate = rockchip_get_pll_settings(pll, drate);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* when no rate setting for the current rate, rely on clk_set_rate */
7948c2ecf20Sopenharmony_ci	if (!rate)
7958c2ecf20Sopenharmony_ci		return 0;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	rockchip_rk3399_pll_get_params(pll, &cur);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	pr_debug("%s: pll %s@%lu: Hz\n", __func__, __clk_get_name(hw->clk),
8008c2ecf20Sopenharmony_ci		 drate);
8018c2ecf20Sopenharmony_ci	pr_debug("old - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
8028c2ecf20Sopenharmony_ci		 cur.fbdiv, cur.postdiv1, cur.refdiv, cur.postdiv2,
8038c2ecf20Sopenharmony_ci		 cur.dsmpd, cur.frac);
8048c2ecf20Sopenharmony_ci	pr_debug("new - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
8058c2ecf20Sopenharmony_ci		 rate->fbdiv, rate->postdiv1, rate->refdiv, rate->postdiv2,
8068c2ecf20Sopenharmony_ci		 rate->dsmpd, rate->frac);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (rate->fbdiv != cur.fbdiv || rate->postdiv1 != cur.postdiv1 ||
8098c2ecf20Sopenharmony_ci		rate->refdiv != cur.refdiv || rate->postdiv2 != cur.postdiv2 ||
8108c2ecf20Sopenharmony_ci		rate->dsmpd != cur.dsmpd ||
8118c2ecf20Sopenharmony_ci		(!cur.dsmpd && (rate->frac != cur.frac))) {
8128c2ecf20Sopenharmony_ci		struct clk *parent = clk_get_parent(hw->clk);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		if (!parent) {
8158c2ecf20Sopenharmony_ci			pr_warn("%s: parent of %s not available\n",
8168c2ecf20Sopenharmony_ci				__func__, __clk_get_name(hw->clk));
8178c2ecf20Sopenharmony_ci			return 0;
8188c2ecf20Sopenharmony_ci		}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci		pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n",
8218c2ecf20Sopenharmony_ci			 __func__, __clk_get_name(hw->clk));
8228c2ecf20Sopenharmony_ci		rockchip_rk3399_pll_set_params(pll, rate);
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	return 0;
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_rk3399_pll_clk_norate_ops = {
8298c2ecf20Sopenharmony_ci	.recalc_rate = rockchip_rk3399_pll_recalc_rate,
8308c2ecf20Sopenharmony_ci	.enable = rockchip_rk3399_pll_enable,
8318c2ecf20Sopenharmony_ci	.disable = rockchip_rk3399_pll_disable,
8328c2ecf20Sopenharmony_ci	.is_enabled = rockchip_rk3399_pll_is_enabled,
8338c2ecf20Sopenharmony_ci};
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic const struct clk_ops rockchip_rk3399_pll_clk_ops = {
8368c2ecf20Sopenharmony_ci	.recalc_rate = rockchip_rk3399_pll_recalc_rate,
8378c2ecf20Sopenharmony_ci	.round_rate = rockchip_pll_round_rate,
8388c2ecf20Sopenharmony_ci	.set_rate = rockchip_rk3399_pll_set_rate,
8398c2ecf20Sopenharmony_ci	.enable = rockchip_rk3399_pll_enable,
8408c2ecf20Sopenharmony_ci	.disable = rockchip_rk3399_pll_disable,
8418c2ecf20Sopenharmony_ci	.is_enabled = rockchip_rk3399_pll_is_enabled,
8428c2ecf20Sopenharmony_ci	.init = rockchip_rk3399_pll_init,
8438c2ecf20Sopenharmony_ci};
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci/*
8468c2ecf20Sopenharmony_ci * Common registering of pll clocks
8478c2ecf20Sopenharmony_ci */
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistruct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
8508c2ecf20Sopenharmony_ci		enum rockchip_pll_type pll_type,
8518c2ecf20Sopenharmony_ci		const char *name, const char *const *parent_names,
8528c2ecf20Sopenharmony_ci		u8 num_parents, int con_offset, int grf_lock_offset,
8538c2ecf20Sopenharmony_ci		int lock_shift, int mode_offset, int mode_shift,
8548c2ecf20Sopenharmony_ci		struct rockchip_pll_rate_table *rate_table,
8558c2ecf20Sopenharmony_ci		unsigned long flags, u8 clk_pll_flags)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	const char *pll_parents[3];
8588c2ecf20Sopenharmony_ci	struct clk_init_data init;
8598c2ecf20Sopenharmony_ci	struct rockchip_clk_pll *pll;
8608c2ecf20Sopenharmony_ci	struct clk_mux *pll_mux;
8618c2ecf20Sopenharmony_ci	struct clk *pll_clk, *mux_clk;
8628c2ecf20Sopenharmony_ci	char pll_name[20];
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if ((pll_type != pll_rk3328 && num_parents != 2) ||
8658c2ecf20Sopenharmony_ci	    (pll_type == pll_rk3328 && num_parents != 1)) {
8668c2ecf20Sopenharmony_ci		pr_err("%s: needs two parent clocks\n", __func__);
8678c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* name the actual pll */
8718c2ecf20Sopenharmony_ci	snprintf(pll_name, sizeof(pll_name), "pll_%s", name);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
8748c2ecf20Sopenharmony_ci	if (!pll)
8758c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* create the mux on top of the real pll */
8788c2ecf20Sopenharmony_ci	pll->pll_mux_ops = &clk_mux_ops;
8798c2ecf20Sopenharmony_ci	pll_mux = &pll->pll_mux;
8808c2ecf20Sopenharmony_ci	pll_mux->reg = ctx->reg_base + mode_offset;
8818c2ecf20Sopenharmony_ci	pll_mux->shift = mode_shift;
8828c2ecf20Sopenharmony_ci	if (pll_type == pll_rk3328)
8838c2ecf20Sopenharmony_ci		pll_mux->mask = PLL_RK3328_MODE_MASK;
8848c2ecf20Sopenharmony_ci	else
8858c2ecf20Sopenharmony_ci		pll_mux->mask = PLL_MODE_MASK;
8868c2ecf20Sopenharmony_ci	pll_mux->flags = 0;
8878c2ecf20Sopenharmony_ci	pll_mux->lock = &ctx->lock;
8888c2ecf20Sopenharmony_ci	pll_mux->hw.init = &init;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (pll_type == pll_rk3036 ||
8918c2ecf20Sopenharmony_ci	    pll_type == pll_rk3066 ||
8928c2ecf20Sopenharmony_ci	    pll_type == pll_rk3328 ||
8938c2ecf20Sopenharmony_ci	    pll_type == pll_rk3399)
8948c2ecf20Sopenharmony_ci		pll_mux->flags |= CLK_MUX_HIWORD_MASK;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	/* the actual muxing is xin24m, pll-output, xin32k */
8978c2ecf20Sopenharmony_ci	pll_parents[0] = parent_names[0];
8988c2ecf20Sopenharmony_ci	pll_parents[1] = pll_name;
8998c2ecf20Sopenharmony_ci	pll_parents[2] = parent_names[1];
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	init.name = name;
9028c2ecf20Sopenharmony_ci	init.flags = CLK_SET_RATE_PARENT;
9038c2ecf20Sopenharmony_ci	init.ops = pll->pll_mux_ops;
9048c2ecf20Sopenharmony_ci	init.parent_names = pll_parents;
9058c2ecf20Sopenharmony_ci	if (pll_type == pll_rk3328)
9068c2ecf20Sopenharmony_ci		init.num_parents = 2;
9078c2ecf20Sopenharmony_ci	else
9088c2ecf20Sopenharmony_ci		init.num_parents = ARRAY_SIZE(pll_parents);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	mux_clk = clk_register(NULL, &pll_mux->hw);
9118c2ecf20Sopenharmony_ci	if (IS_ERR(mux_clk))
9128c2ecf20Sopenharmony_ci		goto err_mux;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	/* now create the actual pll */
9158c2ecf20Sopenharmony_ci	init.name = pll_name;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* keep all plls untouched for now */
9188c2ecf20Sopenharmony_ci	init.flags = flags | CLK_IGNORE_UNUSED;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	init.parent_names = &parent_names[0];
9218c2ecf20Sopenharmony_ci	init.num_parents = 1;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	if (rate_table) {
9248c2ecf20Sopenharmony_ci		int len;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		/* find count of rates in rate_table */
9278c2ecf20Sopenharmony_ci		for (len = 0; rate_table[len].rate != 0; )
9288c2ecf20Sopenharmony_ci			len++;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci		pll->rate_count = len;
9318c2ecf20Sopenharmony_ci		pll->rate_table = kmemdup(rate_table,
9328c2ecf20Sopenharmony_ci					pll->rate_count *
9338c2ecf20Sopenharmony_ci					sizeof(struct rockchip_pll_rate_table),
9348c2ecf20Sopenharmony_ci					GFP_KERNEL);
9358c2ecf20Sopenharmony_ci		WARN(!pll->rate_table,
9368c2ecf20Sopenharmony_ci			"%s: could not allocate rate table for %s\n",
9378c2ecf20Sopenharmony_ci			__func__, name);
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	switch (pll_type) {
9418c2ecf20Sopenharmony_ci	case pll_rk3036:
9428c2ecf20Sopenharmony_ci	case pll_rk3328:
9438c2ecf20Sopenharmony_ci		if (!pll->rate_table)
9448c2ecf20Sopenharmony_ci			init.ops = &rockchip_rk3036_pll_clk_norate_ops;
9458c2ecf20Sopenharmony_ci		else
9468c2ecf20Sopenharmony_ci			init.ops = &rockchip_rk3036_pll_clk_ops;
9478c2ecf20Sopenharmony_ci		break;
9488c2ecf20Sopenharmony_ci	case pll_rk3066:
9498c2ecf20Sopenharmony_ci		if (!pll->rate_table || IS_ERR(ctx->grf))
9508c2ecf20Sopenharmony_ci			init.ops = &rockchip_rk3066_pll_clk_norate_ops;
9518c2ecf20Sopenharmony_ci		else
9528c2ecf20Sopenharmony_ci			init.ops = &rockchip_rk3066_pll_clk_ops;
9538c2ecf20Sopenharmony_ci		break;
9548c2ecf20Sopenharmony_ci	case pll_rk3399:
9558c2ecf20Sopenharmony_ci		if (!pll->rate_table)
9568c2ecf20Sopenharmony_ci			init.ops = &rockchip_rk3399_pll_clk_norate_ops;
9578c2ecf20Sopenharmony_ci		else
9588c2ecf20Sopenharmony_ci			init.ops = &rockchip_rk3399_pll_clk_ops;
9598c2ecf20Sopenharmony_ci		break;
9608c2ecf20Sopenharmony_ci	default:
9618c2ecf20Sopenharmony_ci		pr_warn("%s: Unknown pll type for pll clk %s\n",
9628c2ecf20Sopenharmony_ci			__func__, name);
9638c2ecf20Sopenharmony_ci	}
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	pll->hw.init = &init;
9668c2ecf20Sopenharmony_ci	pll->type = pll_type;
9678c2ecf20Sopenharmony_ci	pll->reg_base = ctx->reg_base + con_offset;
9688c2ecf20Sopenharmony_ci	pll->lock_offset = grf_lock_offset;
9698c2ecf20Sopenharmony_ci	pll->lock_shift = lock_shift;
9708c2ecf20Sopenharmony_ci	pll->flags = clk_pll_flags;
9718c2ecf20Sopenharmony_ci	pll->lock = &ctx->lock;
9728c2ecf20Sopenharmony_ci	pll->ctx = ctx;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	pll_clk = clk_register(NULL, &pll->hw);
9758c2ecf20Sopenharmony_ci	if (IS_ERR(pll_clk)) {
9768c2ecf20Sopenharmony_ci		pr_err("%s: failed to register pll clock %s : %ld\n",
9778c2ecf20Sopenharmony_ci			__func__, name, PTR_ERR(pll_clk));
9788c2ecf20Sopenharmony_ci		goto err_pll;
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	return mux_clk;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cierr_pll:
9848c2ecf20Sopenharmony_ci	kfree(pll->rate_table);
9858c2ecf20Sopenharmony_ci	clk_unregister(mux_clk);
9868c2ecf20Sopenharmony_ci	mux_clk = pll_clk;
9878c2ecf20Sopenharmony_cierr_mux:
9888c2ecf20Sopenharmony_ci	kfree(pll);
9898c2ecf20Sopenharmony_ci	return mux_clk;
9908c2ecf20Sopenharmony_ci}
991