18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/clk.h>
48c2ecf20Sopenharmony_ci#include <linux/io.h>
58c2ecf20Sopenharmony_ci#include <linux/errno.h>
68c2ecf20Sopenharmony_ci#include <linux/delay.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <asm/div64.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "clk.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define to_clk_pllv2(clk) (container_of(clk, struct clk_pllv2, clk))
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/* PLL Register Offsets */
178c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL			0x00
188c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CONFIG		0x04
198c2ecf20Sopenharmony_ci#define MXC_PLL_DP_OP			0x08
208c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFD			0x0C
218c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFN			0x10
228c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFNMINUS		0x14
238c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFNPLUS		0x18
248c2ecf20Sopenharmony_ci#define MXC_PLL_DP_HFS_OP		0x1C
258c2ecf20Sopenharmony_ci#define MXC_PLL_DP_HFS_MFD		0x20
268c2ecf20Sopenharmony_ci#define MXC_PLL_DP_HFS_MFN		0x24
278c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC		0x28
288c2ecf20Sopenharmony_ci#define MXC_PLL_DP_DESTAT		0x2c
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* PLL Register Bit definitions */
318c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_MUL_CTRL		0x2000
328c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_DPDCK0_2_EN	0x1000
338c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET	12
348c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_ADE		0x800
358c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_REF_CLK_DIV	0x400
368c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK	(3 << 8)
378c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET	8
388c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_HFSM		0x80
398c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_PRE		0x40
408c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_UPEN		0x20
418c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_RST		0x10
428c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_RCP		0x8
438c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_PLM		0x4
448c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_BRM0		0x2
458c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CTL_LRF		0x1
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CONFIG_BIST		0x8
488c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CONFIG_SJC_CE	0x4
498c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CONFIG_AREN		0x2
508c2ecf20Sopenharmony_ci#define MXC_PLL_DP_CONFIG_LDREQ		0x1
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define MXC_PLL_DP_OP_MFI_OFFSET	4
538c2ecf20Sopenharmony_ci#define MXC_PLL_DP_OP_MFI_MASK		(0xF << 4)
548c2ecf20Sopenharmony_ci#define MXC_PLL_DP_OP_PDF_OFFSET	0
558c2ecf20Sopenharmony_ci#define MXC_PLL_DP_OP_PDF_MASK		0xF
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFD_OFFSET		0
588c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFD_MASK		0x07FFFFFF
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFN_OFFSET		0x0
618c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFN_MASK		0x07FFFFFF
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC_TOG_DIS	(1 << 17)
648c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC_TOG_EN	(1 << 16)
658c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET	0x0
668c2ecf20Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC_CNT_MASK	0xFFFF
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define MXC_PLL_DP_DESTAT_TOG_SEL	(1 << 31)
698c2ecf20Sopenharmony_ci#define MXC_PLL_DP_DESTAT_MFN		0x07FFFFFF
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define MAX_DPLL_WAIT_TRIES	1000 /* 1000 * udelay(1) = 1ms */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct clk_pllv2 {
748c2ecf20Sopenharmony_ci	struct clk_hw	hw;
758c2ecf20Sopenharmony_ci	void __iomem	*base;
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate,
798c2ecf20Sopenharmony_ci		u32 dp_ctl, u32 dp_op, u32 dp_mfd, u32 dp_mfn)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	long mfi, mfn, mfd, pdf, ref_clk;
828c2ecf20Sopenharmony_ci	unsigned long dbl;
838c2ecf20Sopenharmony_ci	u64 temp;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK;
888c2ecf20Sopenharmony_ci	mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET;
898c2ecf20Sopenharmony_ci	mfi = (mfi <= 5) ? 5 : mfi;
908c2ecf20Sopenharmony_ci	mfd = dp_mfd & MXC_PLL_DP_MFD_MASK;
918c2ecf20Sopenharmony_ci	mfn = dp_mfn & MXC_PLL_DP_MFN_MASK;
928c2ecf20Sopenharmony_ci	mfn = sign_extend32(mfn, 26);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ref_clk = 2 * parent_rate;
958c2ecf20Sopenharmony_ci	if (dbl != 0)
968c2ecf20Sopenharmony_ci		ref_clk *= 2;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	ref_clk /= (pdf + 1);
998c2ecf20Sopenharmony_ci	temp = (u64) ref_clk * abs(mfn);
1008c2ecf20Sopenharmony_ci	do_div(temp, mfd + 1);
1018c2ecf20Sopenharmony_ci	if (mfn < 0)
1028c2ecf20Sopenharmony_ci		temp = (ref_clk * mfi) - temp;
1038c2ecf20Sopenharmony_ci	else
1048c2ecf20Sopenharmony_ci		temp = (ref_clk * mfi) + temp;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return temp;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic unsigned long clk_pllv2_recalc_rate(struct clk_hw *hw,
1108c2ecf20Sopenharmony_ci		unsigned long parent_rate)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	u32 dp_op, dp_mfd, dp_mfn, dp_ctl;
1138c2ecf20Sopenharmony_ci	void __iomem *pllbase;
1148c2ecf20Sopenharmony_ci	struct clk_pllv2 *pll = to_clk_pllv2(hw);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	pllbase = pll->base;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
1198c2ecf20Sopenharmony_ci	dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP);
1208c2ecf20Sopenharmony_ci	dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD);
1218c2ecf20Sopenharmony_ci	dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return __clk_pllv2_recalc_rate(parent_rate, dp_ctl, dp_op, dp_mfd, dp_mfn);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int __clk_pllv2_set_rate(unsigned long rate, unsigned long parent_rate,
1278c2ecf20Sopenharmony_ci		u32 *dp_op, u32 *dp_mfd, u32 *dp_mfn)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	u32 reg;
1308c2ecf20Sopenharmony_ci	long mfi, pdf, mfn, mfd = 999999;
1318c2ecf20Sopenharmony_ci	u64 temp64;
1328c2ecf20Sopenharmony_ci	unsigned long quad_parent_rate;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	quad_parent_rate = 4 * parent_rate;
1358c2ecf20Sopenharmony_ci	pdf = mfi = -1;
1368c2ecf20Sopenharmony_ci	while (++pdf < 16 && mfi < 5)
1378c2ecf20Sopenharmony_ci		mfi = rate * (pdf+1) / quad_parent_rate;
1388c2ecf20Sopenharmony_ci	if (mfi > 15)
1398c2ecf20Sopenharmony_ci		return -EINVAL;
1408c2ecf20Sopenharmony_ci	pdf--;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	temp64 = rate * (pdf + 1) - quad_parent_rate * mfi;
1438c2ecf20Sopenharmony_ci	do_div(temp64, quad_parent_rate / 1000000);
1448c2ecf20Sopenharmony_ci	mfn = (long)temp64;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	reg = mfi << 4 | pdf;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	*dp_op = reg;
1498c2ecf20Sopenharmony_ci	*dp_mfd = mfd;
1508c2ecf20Sopenharmony_ci	*dp_mfn = mfn;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate,
1568c2ecf20Sopenharmony_ci		unsigned long parent_rate)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct clk_pllv2 *pll = to_clk_pllv2(hw);
1598c2ecf20Sopenharmony_ci	void __iomem *pllbase;
1608c2ecf20Sopenharmony_ci	u32 dp_ctl, dp_op, dp_mfd, dp_mfn;
1618c2ecf20Sopenharmony_ci	int ret;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	pllbase = pll->base;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	ret = __clk_pllv2_set_rate(rate, parent_rate, &dp_op, &dp_mfd, &dp_mfn);
1678c2ecf20Sopenharmony_ci	if (ret)
1688c2ecf20Sopenharmony_ci		return ret;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
1718c2ecf20Sopenharmony_ci	/* use dpdck0_2 */
1728c2ecf20Sopenharmony_ci	__raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	__raw_writel(dp_op, pllbase + MXC_PLL_DP_OP);
1758c2ecf20Sopenharmony_ci	__raw_writel(dp_mfd, pllbase + MXC_PLL_DP_MFD);
1768c2ecf20Sopenharmony_ci	__raw_writel(dp_mfn, pllbase + MXC_PLL_DP_MFN);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate,
1828c2ecf20Sopenharmony_ci		unsigned long *prate)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	u32 dp_op, dp_mfd, dp_mfn;
1858c2ecf20Sopenharmony_ci	int ret;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	ret = __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn);
1888c2ecf20Sopenharmony_ci	if (ret)
1898c2ecf20Sopenharmony_ci		return ret;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return __clk_pllv2_recalc_rate(*prate, MXC_PLL_DP_CTL_DPDCK0_2_EN,
1928c2ecf20Sopenharmony_ci			dp_op, dp_mfd, dp_mfn);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int clk_pllv2_prepare(struct clk_hw *hw)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct clk_pllv2 *pll = to_clk_pllv2(hw);
1988c2ecf20Sopenharmony_ci	u32 reg;
1998c2ecf20Sopenharmony_ci	void __iomem *pllbase;
2008c2ecf20Sopenharmony_ci	int i = 0;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	pllbase = pll->base;
2038c2ecf20Sopenharmony_ci	reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) | MXC_PLL_DP_CTL_UPEN;
2048c2ecf20Sopenharmony_ci	__raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Wait for lock */
2078c2ecf20Sopenharmony_ci	do {
2088c2ecf20Sopenharmony_ci		reg = __raw_readl(pllbase + MXC_PLL_DP_CTL);
2098c2ecf20Sopenharmony_ci		if (reg & MXC_PLL_DP_CTL_LRF)
2108c2ecf20Sopenharmony_ci			break;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		udelay(1);
2138c2ecf20Sopenharmony_ci	} while (++i < MAX_DPLL_WAIT_TRIES);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (i == MAX_DPLL_WAIT_TRIES) {
2168c2ecf20Sopenharmony_ci		pr_err("MX5: pll locking failed\n");
2178c2ecf20Sopenharmony_ci		return -EINVAL;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void clk_pllv2_unprepare(struct clk_hw *hw)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct clk_pllv2 *pll = to_clk_pllv2(hw);
2268c2ecf20Sopenharmony_ci	u32 reg;
2278c2ecf20Sopenharmony_ci	void __iomem *pllbase;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	pllbase = pll->base;
2308c2ecf20Sopenharmony_ci	reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN;
2318c2ecf20Sopenharmony_ci	__raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic const struct clk_ops clk_pllv2_ops = {
2358c2ecf20Sopenharmony_ci	.prepare = clk_pllv2_prepare,
2368c2ecf20Sopenharmony_ci	.unprepare = clk_pllv2_unprepare,
2378c2ecf20Sopenharmony_ci	.recalc_rate = clk_pllv2_recalc_rate,
2388c2ecf20Sopenharmony_ci	.round_rate = clk_pllv2_round_rate,
2398c2ecf20Sopenharmony_ci	.set_rate = clk_pllv2_set_rate,
2408c2ecf20Sopenharmony_ci};
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistruct clk_hw *imx_clk_hw_pllv2(const char *name, const char *parent,
2438c2ecf20Sopenharmony_ci		void __iomem *base)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct clk_pllv2 *pll;
2468c2ecf20Sopenharmony_ci	struct clk_hw *hw;
2478c2ecf20Sopenharmony_ci	struct clk_init_data init;
2488c2ecf20Sopenharmony_ci	int ret;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
2518c2ecf20Sopenharmony_ci	if (!pll)
2528c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	pll->base = base;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	init.name = name;
2578c2ecf20Sopenharmony_ci	init.ops = &clk_pllv2_ops;
2588c2ecf20Sopenharmony_ci	init.flags = 0;
2598c2ecf20Sopenharmony_ci	init.parent_names = &parent;
2608c2ecf20Sopenharmony_ci	init.num_parents = 1;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	pll->hw.init = &init;
2638c2ecf20Sopenharmony_ci	hw = &pll->hw;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	ret = clk_hw_register(NULL, hw);
2668c2ecf20Sopenharmony_ci	if (ret) {
2678c2ecf20Sopenharmony_ci		kfree(pll);
2688c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return hw;
2728c2ecf20Sopenharmony_ci}
273