brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · ff17f06 Raw
273 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/kernel.h>3#include <linux/clk.h>4#include <linux/io.h>5#include <linux/errno.h>6#include <linux/delay.h>7#include <linux/slab.h>8#include <linux/err.h>9 10#include <asm/div64.h>11 12#include "clk.h"13 14#define to_clk_pllv2(clk) (container_of(clk, struct clk_pllv2, clk))15 16/* PLL Register Offsets */17#define MXC_PLL_DP_CTL			0x0018#define MXC_PLL_DP_CONFIG		0x0419#define MXC_PLL_DP_OP			0x0820#define MXC_PLL_DP_MFD			0x0C21#define MXC_PLL_DP_MFN			0x1022#define MXC_PLL_DP_MFNMINUS		0x1423#define MXC_PLL_DP_MFNPLUS		0x1824#define MXC_PLL_DP_HFS_OP		0x1C25#define MXC_PLL_DP_HFS_MFD		0x2026#define MXC_PLL_DP_HFS_MFN		0x2427#define MXC_PLL_DP_MFN_TOGC		0x2828#define MXC_PLL_DP_DESTAT		0x2c29 30/* PLL Register Bit definitions */31#define MXC_PLL_DP_CTL_MUL_CTRL		0x200032#define MXC_PLL_DP_CTL_DPDCK0_2_EN	0x100033#define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET	1234#define MXC_PLL_DP_CTL_ADE		0x80035#define MXC_PLL_DP_CTL_REF_CLK_DIV	0x40036#define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK	(3 << 8)37#define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET	838#define MXC_PLL_DP_CTL_HFSM		0x8039#define MXC_PLL_DP_CTL_PRE		0x4040#define MXC_PLL_DP_CTL_UPEN		0x2041#define MXC_PLL_DP_CTL_RST		0x1042#define MXC_PLL_DP_CTL_RCP		0x843#define MXC_PLL_DP_CTL_PLM		0x444#define MXC_PLL_DP_CTL_BRM0		0x245#define MXC_PLL_DP_CTL_LRF		0x146 47#define MXC_PLL_DP_CONFIG_BIST		0x848#define MXC_PLL_DP_CONFIG_SJC_CE	0x449#define MXC_PLL_DP_CONFIG_AREN		0x250#define MXC_PLL_DP_CONFIG_LDREQ		0x151 52#define MXC_PLL_DP_OP_MFI_OFFSET	453#define MXC_PLL_DP_OP_MFI_MASK		(0xF << 4)54#define MXC_PLL_DP_OP_PDF_OFFSET	055#define MXC_PLL_DP_OP_PDF_MASK		0xF56 57#define MXC_PLL_DP_MFD_OFFSET		058#define MXC_PLL_DP_MFD_MASK		0x07FFFFFF59 60#define MXC_PLL_DP_MFN_OFFSET		0x061#define MXC_PLL_DP_MFN_MASK		0x07FFFFFF62 63#define MXC_PLL_DP_MFN_TOGC_TOG_DIS	(1 << 17)64#define MXC_PLL_DP_MFN_TOGC_TOG_EN	(1 << 16)65#define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET	0x066#define MXC_PLL_DP_MFN_TOGC_CNT_MASK	0xFFFF67 68#define MXC_PLL_DP_DESTAT_TOG_SEL	(1 << 31)69#define MXC_PLL_DP_DESTAT_MFN		0x07FFFFFF70 71#define MAX_DPLL_WAIT_TRIES	1000 /* 1000 * udelay(1) = 1ms */72 73struct clk_pllv2 {74	struct clk_hw	hw;75	void __iomem	*base;76};77 78static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate,79		u32 dp_ctl, u32 dp_op, u32 dp_mfd, u32 dp_mfn)80{81	long mfi, mfn, mfd, pdf, ref_clk;82	unsigned long dbl;83	u64 temp;84 85	dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN;86 87	pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK;88	mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET;89	mfi = (mfi <= 5) ? 5 : mfi;90	mfd = dp_mfd & MXC_PLL_DP_MFD_MASK;91	mfn = dp_mfn & MXC_PLL_DP_MFN_MASK;92	mfn = sign_extend32(mfn, 26);93 94	ref_clk = 2 * parent_rate;95	if (dbl != 0)96		ref_clk *= 2;97 98	ref_clk /= (pdf + 1);99	temp = (u64) ref_clk * abs(mfn);100	do_div(temp, mfd + 1);101	if (mfn < 0)102		temp = (ref_clk * mfi) - temp;103	else104		temp = (ref_clk * mfi) + temp;105 106	return temp;107}108 109static unsigned long clk_pllv2_recalc_rate(struct clk_hw *hw,110		unsigned long parent_rate)111{112	u32 dp_op, dp_mfd, dp_mfn, dp_ctl;113	void __iomem *pllbase;114	struct clk_pllv2 *pll = to_clk_pllv2(hw);115 116	pllbase = pll->base;117 118	dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);119	dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP);120	dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD);121	dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN);122 123	return __clk_pllv2_recalc_rate(parent_rate, dp_ctl, dp_op, dp_mfd, dp_mfn);124}125 126static int __clk_pllv2_set_rate(unsigned long rate, unsigned long parent_rate,127		u32 *dp_op, u32 *dp_mfd, u32 *dp_mfn)128{129	u32 reg;130	long mfi, pdf, mfn, mfd = 999999;131	u64 temp64;132	unsigned long quad_parent_rate;133 134	quad_parent_rate = 4 * parent_rate;135	pdf = mfi = -1;136	while (++pdf < 16 && mfi < 5)137		mfi = rate * (pdf+1) / quad_parent_rate;138	if (mfi > 15)139		return -EINVAL;140	pdf--;141 142	temp64 = rate * (pdf + 1) - quad_parent_rate * mfi;143	do_div(temp64, quad_parent_rate / 1000000);144	mfn = (long)temp64;145 146	reg = mfi << 4 | pdf;147 148	*dp_op = reg;149	*dp_mfd = mfd;150	*dp_mfn = mfn;151 152	return 0;153}154 155static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate,156		unsigned long parent_rate)157{158	struct clk_pllv2 *pll = to_clk_pllv2(hw);159	void __iomem *pllbase;160	u32 dp_ctl, dp_op, dp_mfd, dp_mfn;161	int ret;162 163	pllbase = pll->base;164 165 166	ret = __clk_pllv2_set_rate(rate, parent_rate, &dp_op, &dp_mfd, &dp_mfn);167	if (ret)168		return ret;169 170	dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL);171	/* use dpdck0_2 */172	__raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL);173 174	__raw_writel(dp_op, pllbase + MXC_PLL_DP_OP);175	__raw_writel(dp_mfd, pllbase + MXC_PLL_DP_MFD);176	__raw_writel(dp_mfn, pllbase + MXC_PLL_DP_MFN);177 178	return 0;179}180 181static long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate,182		unsigned long *prate)183{184	u32 dp_op, dp_mfd, dp_mfn;185	int ret;186 187	ret = __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn);188	if (ret)189		return ret;190 191	return __clk_pllv2_recalc_rate(*prate, MXC_PLL_DP_CTL_DPDCK0_2_EN,192			dp_op, dp_mfd, dp_mfn);193}194 195static int clk_pllv2_prepare(struct clk_hw *hw)196{197	struct clk_pllv2 *pll = to_clk_pllv2(hw);198	u32 reg;199	void __iomem *pllbase;200	int i = 0;201 202	pllbase = pll->base;203	reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) | MXC_PLL_DP_CTL_UPEN;204	__raw_writel(reg, pllbase + MXC_PLL_DP_CTL);205 206	/* Wait for lock */207	do {208		reg = __raw_readl(pllbase + MXC_PLL_DP_CTL);209		if (reg & MXC_PLL_DP_CTL_LRF)210			break;211 212		udelay(1);213	} while (++i < MAX_DPLL_WAIT_TRIES);214 215	if (i == MAX_DPLL_WAIT_TRIES) {216		pr_err("MX5: pll locking failed\n");217		return -EINVAL;218	}219 220	return 0;221}222 223static void clk_pllv2_unprepare(struct clk_hw *hw)224{225	struct clk_pllv2 *pll = to_clk_pllv2(hw);226	u32 reg;227	void __iomem *pllbase;228 229	pllbase = pll->base;230	reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN;231	__raw_writel(reg, pllbase + MXC_PLL_DP_CTL);232}233 234static const struct clk_ops clk_pllv2_ops = {235	.prepare = clk_pllv2_prepare,236	.unprepare = clk_pllv2_unprepare,237	.recalc_rate = clk_pllv2_recalc_rate,238	.round_rate = clk_pllv2_round_rate,239	.set_rate = clk_pllv2_set_rate,240};241 242struct clk_hw *imx_clk_hw_pllv2(const char *name, const char *parent,243		void __iomem *base)244{245	struct clk_pllv2 *pll;246	struct clk_hw *hw;247	struct clk_init_data init;248	int ret;249 250	pll = kzalloc(sizeof(*pll), GFP_KERNEL);251	if (!pll)252		return ERR_PTR(-ENOMEM);253 254	pll->base = base;255 256	init.name = name;257	init.ops = &clk_pllv2_ops;258	init.flags = 0;259	init.parent_names = &parent;260	init.num_parents = 1;261 262	pll->hw.init = &init;263	hw = &pll->hw;264 265	ret = clk_hw_register(NULL, hw);266	if (ret) {267		kfree(pll);268		return ERR_PTR(ret);269	}270 271	return hw;272}273