162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2018 NXP.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This driver supports the fractional plls found in the imx8m SOCs
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Documentation for this fractional pll can be found at:
862306a36Sopenharmony_ci *   https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/clk-provider.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/export.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/iopoll.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/bitfield.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "clk.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PLL_CFG0		0x0
2262306a36Sopenharmony_ci#define PLL_CFG1		0x4
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define PLL_LOCK_STATUS		BIT(31)
2562306a36Sopenharmony_ci#define PLL_PD_MASK		BIT(19)
2662306a36Sopenharmony_ci#define PLL_BYPASS_MASK		BIT(14)
2762306a36Sopenharmony_ci#define PLL_NEWDIV_VAL		BIT(12)
2862306a36Sopenharmony_ci#define PLL_NEWDIV_ACK		BIT(11)
2962306a36Sopenharmony_ci#define PLL_FRAC_DIV_MASK	GENMASK(30, 7)
3062306a36Sopenharmony_ci#define PLL_INT_DIV_MASK	GENMASK(6, 0)
3162306a36Sopenharmony_ci#define PLL_OUTPUT_DIV_MASK	GENMASK(4, 0)
3262306a36Sopenharmony_ci#define PLL_FRAC_DENOM		0x1000000
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define PLL_FRAC_LOCK_TIMEOUT	10000
3562306a36Sopenharmony_ci#define PLL_FRAC_ACK_TIMEOUT	500000
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct clk_frac_pll {
3862306a36Sopenharmony_ci	struct clk_hw	hw;
3962306a36Sopenharmony_ci	void __iomem	*base;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define to_clk_frac_pll(_hw) container_of(_hw, struct clk_frac_pll, hw)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int clk_wait_lock(struct clk_frac_pll *pll)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	u32 val;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return readl_poll_timeout(pll->base, val, val & PLL_LOCK_STATUS, 0,
4962306a36Sopenharmony_ci					PLL_FRAC_LOCK_TIMEOUT);
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int clk_wait_ack(struct clk_frac_pll *pll)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	u32 val;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* return directly if the pll is in powerdown or in bypass */
5762306a36Sopenharmony_ci	if (readl_relaxed(pll->base) & (PLL_PD_MASK | PLL_BYPASS_MASK))
5862306a36Sopenharmony_ci		return 0;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* Wait for the pll's divfi and divff to be reloaded */
6162306a36Sopenharmony_ci	return readl_poll_timeout(pll->base, val, val & PLL_NEWDIV_ACK, 0,
6262306a36Sopenharmony_ci					PLL_FRAC_ACK_TIMEOUT);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int clk_pll_prepare(struct clk_hw *hw)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct clk_frac_pll *pll = to_clk_frac_pll(hw);
6862306a36Sopenharmony_ci	u32 val;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG0);
7162306a36Sopenharmony_ci	val &= ~PLL_PD_MASK;
7262306a36Sopenharmony_ci	writel_relaxed(val, pll->base + PLL_CFG0);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return clk_wait_lock(pll);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void clk_pll_unprepare(struct clk_hw *hw)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct clk_frac_pll *pll = to_clk_frac_pll(hw);
8062306a36Sopenharmony_ci	u32 val;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG0);
8362306a36Sopenharmony_ci	val |= PLL_PD_MASK;
8462306a36Sopenharmony_ci	writel_relaxed(val, pll->base + PLL_CFG0);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int clk_pll_is_prepared(struct clk_hw *hw)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct clk_frac_pll *pll = to_clk_frac_pll(hw);
9062306a36Sopenharmony_ci	u32 val;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG0);
9362306a36Sopenharmony_ci	return (val & PLL_PD_MASK) ? 0 : 1;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
9762306a36Sopenharmony_ci					 unsigned long parent_rate)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct clk_frac_pll *pll = to_clk_frac_pll(hw);
10062306a36Sopenharmony_ci	u32 val, divff, divfi, divq;
10162306a36Sopenharmony_ci	u64 temp64 = parent_rate;
10262306a36Sopenharmony_ci	u64 rate;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG0);
10562306a36Sopenharmony_ci	divq = (FIELD_GET(PLL_OUTPUT_DIV_MASK, val) + 1) * 2;
10662306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG1);
10762306a36Sopenharmony_ci	divff = FIELD_GET(PLL_FRAC_DIV_MASK, val);
10862306a36Sopenharmony_ci	divfi = FIELD_GET(PLL_INT_DIV_MASK, val);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	temp64 *= 8;
11162306a36Sopenharmony_ci	temp64 *= divff;
11262306a36Sopenharmony_ci	do_div(temp64, PLL_FRAC_DENOM);
11362306a36Sopenharmony_ci	do_div(temp64, divq);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	rate = parent_rate * 8 * (divfi + 1);
11662306a36Sopenharmony_ci	do_div(rate, divq);
11762306a36Sopenharmony_ci	rate += temp64;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return rate;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
12362306a36Sopenharmony_ci			       unsigned long *prate)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	u64 parent_rate = *prate;
12662306a36Sopenharmony_ci	u32 divff, divfi;
12762306a36Sopenharmony_ci	u64 temp64;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	parent_rate *= 8;
13062306a36Sopenharmony_ci	rate *= 2;
13162306a36Sopenharmony_ci	temp64 = rate;
13262306a36Sopenharmony_ci	do_div(temp64, parent_rate);
13362306a36Sopenharmony_ci	divfi = temp64;
13462306a36Sopenharmony_ci	temp64 = rate - divfi * parent_rate;
13562306a36Sopenharmony_ci	temp64 *= PLL_FRAC_DENOM;
13662306a36Sopenharmony_ci	do_div(temp64, parent_rate);
13762306a36Sopenharmony_ci	divff = temp64;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	temp64 = parent_rate;
14062306a36Sopenharmony_ci	temp64 *= divff;
14162306a36Sopenharmony_ci	do_div(temp64, PLL_FRAC_DENOM);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	rate = parent_rate * divfi + temp64;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return rate / 2;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/*
14962306a36Sopenharmony_ci * To simplify the clock calculation, we can keep the 'PLL_OUTPUT_VAL' at zero
15062306a36Sopenharmony_ci * (means the PLL output will be divided by 2). So the PLL output can use
15162306a36Sopenharmony_ci * the below formula:
15262306a36Sopenharmony_ci * pllout = parent_rate * 8 / 2 * DIVF_VAL;
15362306a36Sopenharmony_ci * where DIVF_VAL = 1 + DIVFI + DIVFF / 2^24.
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistatic int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
15662306a36Sopenharmony_ci			    unsigned long parent_rate)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct clk_frac_pll *pll = to_clk_frac_pll(hw);
15962306a36Sopenharmony_ci	u32 val, divfi, divff;
16062306a36Sopenharmony_ci	u64 temp64;
16162306a36Sopenharmony_ci	int ret;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	parent_rate *= 8;
16462306a36Sopenharmony_ci	rate *= 2;
16562306a36Sopenharmony_ci	divfi = rate / parent_rate;
16662306a36Sopenharmony_ci	temp64 = parent_rate * divfi;
16762306a36Sopenharmony_ci	temp64 = rate - temp64;
16862306a36Sopenharmony_ci	temp64 *= PLL_FRAC_DENOM;
16962306a36Sopenharmony_ci	do_div(temp64, parent_rate);
17062306a36Sopenharmony_ci	divff = temp64;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG1);
17362306a36Sopenharmony_ci	val &= ~(PLL_FRAC_DIV_MASK | PLL_INT_DIV_MASK);
17462306a36Sopenharmony_ci	val |= (divff << 7) | (divfi - 1);
17562306a36Sopenharmony_ci	writel_relaxed(val, pll->base + PLL_CFG1);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG0);
17862306a36Sopenharmony_ci	val &= ~0x1f;
17962306a36Sopenharmony_ci	writel_relaxed(val, pll->base + PLL_CFG0);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* Set the NEV_DIV_VAL to reload the DIVFI and DIVFF */
18262306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG0);
18362306a36Sopenharmony_ci	val |= PLL_NEWDIV_VAL;
18462306a36Sopenharmony_ci	writel_relaxed(val, pll->base + PLL_CFG0);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	ret = clk_wait_ack(pll);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* clear the NEV_DIV_VAL */
18962306a36Sopenharmony_ci	val = readl_relaxed(pll->base + PLL_CFG0);
19062306a36Sopenharmony_ci	val &= ~PLL_NEWDIV_VAL;
19162306a36Sopenharmony_ci	writel_relaxed(val, pll->base + PLL_CFG0);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return ret;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic const struct clk_ops clk_frac_pll_ops = {
19762306a36Sopenharmony_ci	.prepare	= clk_pll_prepare,
19862306a36Sopenharmony_ci	.unprepare	= clk_pll_unprepare,
19962306a36Sopenharmony_ci	.is_prepared	= clk_pll_is_prepared,
20062306a36Sopenharmony_ci	.recalc_rate	= clk_pll_recalc_rate,
20162306a36Sopenharmony_ci	.round_rate	= clk_pll_round_rate,
20262306a36Sopenharmony_ci	.set_rate	= clk_pll_set_rate,
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistruct clk_hw *imx_clk_hw_frac_pll(const char *name,
20662306a36Sopenharmony_ci				   const char *parent_name,
20762306a36Sopenharmony_ci				   void __iomem *base)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct clk_init_data init;
21062306a36Sopenharmony_ci	struct clk_frac_pll *pll;
21162306a36Sopenharmony_ci	struct clk_hw *hw;
21262306a36Sopenharmony_ci	int ret;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
21562306a36Sopenharmony_ci	if (!pll)
21662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	init.name = name;
21962306a36Sopenharmony_ci	init.ops = &clk_frac_pll_ops;
22062306a36Sopenharmony_ci	init.flags = 0;
22162306a36Sopenharmony_ci	init.parent_names = &parent_name;
22262306a36Sopenharmony_ci	init.num_parents = 1;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	pll->base = base;
22562306a36Sopenharmony_ci	pll->hw.init = &init;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	hw = &pll->hw;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	ret = clk_hw_register(NULL, hw);
23062306a36Sopenharmony_ci	if (ret) {
23162306a36Sopenharmony_ci		kfree(pll);
23262306a36Sopenharmony_ci		return ERR_PTR(ret);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return hw;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_clk_hw_frac_pll);
238