162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/clk.h>
462306a36Sopenharmony_ci#include <linux/io.h>
562306a36Sopenharmony_ci#include <linux/errno.h>
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <asm/div64.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "clk.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define to_clk_pllv2(clk) (container_of(clk, struct clk_pllv2, clk))
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/* PLL Register Offsets */
1762306a36Sopenharmony_ci#define MXC_PLL_DP_CTL			0x00
1862306a36Sopenharmony_ci#define MXC_PLL_DP_CONFIG		0x04
1962306a36Sopenharmony_ci#define MXC_PLL_DP_OP			0x08
2062306a36Sopenharmony_ci#define MXC_PLL_DP_MFD			0x0C
2162306a36Sopenharmony_ci#define MXC_PLL_DP_MFN			0x10
2262306a36Sopenharmony_ci#define MXC_PLL_DP_MFNMINUS		0x14
2362306a36Sopenharmony_ci#define MXC_PLL_DP_MFNPLUS		0x18
2462306a36Sopenharmony_ci#define MXC_PLL_DP_HFS_OP		0x1C
2562306a36Sopenharmony_ci#define MXC_PLL_DP_HFS_MFD		0x20
2662306a36Sopenharmony_ci#define MXC_PLL_DP_HFS_MFN		0x24
2762306a36Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC		0x28
2862306a36Sopenharmony_ci#define MXC_PLL_DP_DESTAT		0x2c
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* PLL Register Bit definitions */
3162306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_MUL_CTRL		0x2000
3262306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_DPDCK0_2_EN	0x1000
3362306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET	12
3462306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_ADE		0x800
3562306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_REF_CLK_DIV	0x400
3662306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK	(3 << 8)
3762306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET	8
3862306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_HFSM		0x80
3962306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_PRE		0x40
4062306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_UPEN		0x20
4162306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_RST		0x10
4262306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_RCP		0x8
4362306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_PLM		0x4
4462306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_BRM0		0x2
4562306a36Sopenharmony_ci#define MXC_PLL_DP_CTL_LRF		0x1
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define MXC_PLL_DP_CONFIG_BIST		0x8
4862306a36Sopenharmony_ci#define MXC_PLL_DP_CONFIG_SJC_CE	0x4
4962306a36Sopenharmony_ci#define MXC_PLL_DP_CONFIG_AREN		0x2
5062306a36Sopenharmony_ci#define MXC_PLL_DP_CONFIG_LDREQ		0x1
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define MXC_PLL_DP_OP_MFI_OFFSET	4
5362306a36Sopenharmony_ci#define MXC_PLL_DP_OP_MFI_MASK		(0xF << 4)
5462306a36Sopenharmony_ci#define MXC_PLL_DP_OP_PDF_OFFSET	0
5562306a36Sopenharmony_ci#define MXC_PLL_DP_OP_PDF_MASK		0xF
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define MXC_PLL_DP_MFD_OFFSET		0
5862306a36Sopenharmony_ci#define MXC_PLL_DP_MFD_MASK		0x07FFFFFF
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define MXC_PLL_DP_MFN_OFFSET		0x0
6162306a36Sopenharmony_ci#define MXC_PLL_DP_MFN_MASK		0x07FFFFFF
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC_TOG_DIS	(1 << 17)
6462306a36Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC_TOG_EN	(1 << 16)
6562306a36Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET	0x0
6662306a36Sopenharmony_ci#define MXC_PLL_DP_MFN_TOGC_CNT_MASK	0xFFFF
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define MXC_PLL_DP_DESTAT_TOG_SEL	(1 << 31)
6962306a36Sopenharmony_ci#define MXC_PLL_DP_DESTAT_MFN		0x07FFFFFF
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define MAX_DPLL_WAIT_TRIES	1000 /* 1000 * udelay(1) = 1ms */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct clk_pllv2 {
7462306a36Sopenharmony_ci	struct clk_hw	hw;
7562306a36Sopenharmony_ci	void __iomem	*base;
7662306a36Sopenharmony_ci};
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate,
7962306a36Sopenharmony_ci		u32 dp_ctl, u32 dp_op, u32 dp_mfd, u32 dp_mfn)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	long mfi, mfn, mfd, pdf, ref_clk;
8262306a36Sopenharmony_ci	unsigned long dbl;
8362306a36Sopenharmony_ci	u64 temp;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK;
8862306a36Sopenharmony_ci	mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET;
8962306a36Sopenharmony_ci	mfi = (mfi <= 5) ? 5 : mfi;
9062306a36Sopenharmony_ci	mfd = dp_mfd & MXC_PLL_DP_MFD_MASK;
9162306a36Sopenharmony_ci	mfn = dp_mfn & MXC_PLL_DP_MFN_MASK;
9262306a36Sopenharmony_ci	mfn = sign_extend32(mfn, 26);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	ref_clk = 2 * parent_rate;
9562306a36Sopenharmony_ci	if (dbl != 0)
9662306a36Sopenharmony_ci		ref_clk *= 2;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	ref_clk /= (pdf + 1);
9962306a36Sopenharmony_ci	temp = (u64) ref_clk * abs(mfn);
10062306a36Sopenharmony_ci	do_div(temp, mfd + 1);
10162306a36Sopenharmony_ci	if (mfn < 0)
10262306a36Sopenharmony_ci		temp = (ref_clk * mfi) - temp;
10362306a36Sopenharmony_ci	else
10462306a36Sopenharmony_ci		temp = (ref_clk * mfi) + temp;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return temp;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic unsigned long clk_pllv2_recalc_rate(struct clk_hw *hw,
11062306a36Sopenharmony_ci		unsigned long parent_rate)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	u32 dp_op, dp_mfd, dp_mfn, dp_ctl;
11362306a36Sopenharmony_ci	void __iomem *pllbase;
11462306a36Sopenharmony_ci	struct clk_pllv2 *pll = to_clk_pllv2(hw);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	pllbase = pll->base;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
11962306a36Sopenharmony_ci	dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP);
12062306a36Sopenharmony_ci	dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD);
12162306a36Sopenharmony_ci	dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return __clk_pllv2_recalc_rate(parent_rate, dp_ctl, dp_op, dp_mfd, dp_mfn);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int __clk_pllv2_set_rate(unsigned long rate, unsigned long parent_rate,
12762306a36Sopenharmony_ci		u32 *dp_op, u32 *dp_mfd, u32 *dp_mfn)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	u32 reg;
13062306a36Sopenharmony_ci	long mfi, pdf, mfn, mfd = 999999;
13162306a36Sopenharmony_ci	u64 temp64;
13262306a36Sopenharmony_ci	unsigned long quad_parent_rate;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	quad_parent_rate = 4 * parent_rate;
13562306a36Sopenharmony_ci	pdf = mfi = -1;
13662306a36Sopenharmony_ci	while (++pdf < 16 && mfi < 5)
13762306a36Sopenharmony_ci		mfi = rate * (pdf+1) / quad_parent_rate;
13862306a36Sopenharmony_ci	if (mfi > 15)
13962306a36Sopenharmony_ci		return -EINVAL;
14062306a36Sopenharmony_ci	pdf--;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	temp64 = rate * (pdf + 1) - quad_parent_rate * mfi;
14362306a36Sopenharmony_ci	do_div(temp64, quad_parent_rate / 1000000);
14462306a36Sopenharmony_ci	mfn = (long)temp64;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	reg = mfi << 4 | pdf;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	*dp_op = reg;
14962306a36Sopenharmony_ci	*dp_mfd = mfd;
15062306a36Sopenharmony_ci	*dp_mfn = mfn;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate,
15662306a36Sopenharmony_ci		unsigned long parent_rate)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct clk_pllv2 *pll = to_clk_pllv2(hw);
15962306a36Sopenharmony_ci	void __iomem *pllbase;
16062306a36Sopenharmony_ci	u32 dp_ctl, dp_op, dp_mfd, dp_mfn;
16162306a36Sopenharmony_ci	int ret;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	pllbase = pll->base;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ret = __clk_pllv2_set_rate(rate, parent_rate, &dp_op, &dp_mfd, &dp_mfn);
16762306a36Sopenharmony_ci	if (ret)
16862306a36Sopenharmony_ci		return ret;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);
17162306a36Sopenharmony_ci	/* use dpdck0_2 */
17262306a36Sopenharmony_ci	__raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	__raw_writel(dp_op, pllbase + MXC_PLL_DP_OP);
17562306a36Sopenharmony_ci	__raw_writel(dp_mfd, pllbase + MXC_PLL_DP_MFD);
17662306a36Sopenharmony_ci	__raw_writel(dp_mfn, pllbase + MXC_PLL_DP_MFN);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate,
18262306a36Sopenharmony_ci		unsigned long *prate)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	u32 dp_op, dp_mfd, dp_mfn;
18562306a36Sopenharmony_ci	int ret;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	ret = __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn);
18862306a36Sopenharmony_ci	if (ret)
18962306a36Sopenharmony_ci		return ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return __clk_pllv2_recalc_rate(*prate, MXC_PLL_DP_CTL_DPDCK0_2_EN,
19262306a36Sopenharmony_ci			dp_op, dp_mfd, dp_mfn);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int clk_pllv2_prepare(struct clk_hw *hw)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct clk_pllv2 *pll = to_clk_pllv2(hw);
19862306a36Sopenharmony_ci	u32 reg;
19962306a36Sopenharmony_ci	void __iomem *pllbase;
20062306a36Sopenharmony_ci	int i = 0;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	pllbase = pll->base;
20362306a36Sopenharmony_ci	reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) | MXC_PLL_DP_CTL_UPEN;
20462306a36Sopenharmony_ci	__raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Wait for lock */
20762306a36Sopenharmony_ci	do {
20862306a36Sopenharmony_ci		reg = __raw_readl(pllbase + MXC_PLL_DP_CTL);
20962306a36Sopenharmony_ci		if (reg & MXC_PLL_DP_CTL_LRF)
21062306a36Sopenharmony_ci			break;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		udelay(1);
21362306a36Sopenharmony_ci	} while (++i < MAX_DPLL_WAIT_TRIES);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (i == MAX_DPLL_WAIT_TRIES) {
21662306a36Sopenharmony_ci		pr_err("MX5: pll locking failed\n");
21762306a36Sopenharmony_ci		return -EINVAL;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void clk_pllv2_unprepare(struct clk_hw *hw)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct clk_pllv2 *pll = to_clk_pllv2(hw);
22662306a36Sopenharmony_ci	u32 reg;
22762306a36Sopenharmony_ci	void __iomem *pllbase;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	pllbase = pll->base;
23062306a36Sopenharmony_ci	reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN;
23162306a36Sopenharmony_ci	__raw_writel(reg, pllbase + MXC_PLL_DP_CTL);
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic const struct clk_ops clk_pllv2_ops = {
23562306a36Sopenharmony_ci	.prepare = clk_pllv2_prepare,
23662306a36Sopenharmony_ci	.unprepare = clk_pllv2_unprepare,
23762306a36Sopenharmony_ci	.recalc_rate = clk_pllv2_recalc_rate,
23862306a36Sopenharmony_ci	.round_rate = clk_pllv2_round_rate,
23962306a36Sopenharmony_ci	.set_rate = clk_pllv2_set_rate,
24062306a36Sopenharmony_ci};
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistruct clk_hw *imx_clk_hw_pllv2(const char *name, const char *parent,
24362306a36Sopenharmony_ci		void __iomem *base)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct clk_pllv2 *pll;
24662306a36Sopenharmony_ci	struct clk_hw *hw;
24762306a36Sopenharmony_ci	struct clk_init_data init;
24862306a36Sopenharmony_ci	int ret;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
25162306a36Sopenharmony_ci	if (!pll)
25262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	pll->base = base;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	init.name = name;
25762306a36Sopenharmony_ci	init.ops = &clk_pllv2_ops;
25862306a36Sopenharmony_ci	init.flags = 0;
25962306a36Sopenharmony_ci	init.parent_names = &parent;
26062306a36Sopenharmony_ci	init.num_parents = 1;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	pll->hw.init = &init;
26362306a36Sopenharmony_ci	hw = &pll->hw;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	ret = clk_hw_register(NULL, hw);
26662306a36Sopenharmony_ci	if (ret) {
26762306a36Sopenharmony_ci		kfree(pll);
26862306a36Sopenharmony_ci		return ERR_PTR(ret);
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return hw;
27262306a36Sopenharmony_ci}
273