xref: /optee_os/core/drivers/clk/sam/at91_pll.c (revision dd6be6315e6d4377d3f761c613df65d032212465)
1*dd6be631SClément Léger // SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause
2*dd6be631SClément Léger /*
3*dd6be631SClément Léger  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4*dd6be631SClément Léger  *  Copyright (C) 2021 Microchip
5*dd6be631SClément Léger  */
6*dd6be631SClément Léger 
7*dd6be631SClément Léger #include <io.h>
8*dd6be631SClément Léger #include <kernel/delay.h>
9*dd6be631SClément Léger #include <kernel/panic.h>
10*dd6be631SClément Léger #include <util.h>
11*dd6be631SClément Léger #include <mm/core_memprot.h>
12*dd6be631SClément Léger #include <types_ext.h>
13*dd6be631SClément Léger 
14*dd6be631SClément Léger #include "at91_clk.h"
15*dd6be631SClément Léger 
16*dd6be631SClément Léger #define PLL_STATUS_MASK(id)	BIT(1 + (id))
17*dd6be631SClément Léger #define PLL_REG(id)		(AT91_CKGR_PLLAR + ((id) * 4))
18*dd6be631SClément Léger #define PLL_DIV_MASK		0xff
19*dd6be631SClément Léger #define PLL_DIV_MAX		PLL_DIV_MASK
20*dd6be631SClément Léger #define PLL_DIV(reg)		((reg) & PLL_DIV_MASK)
21*dd6be631SClément Léger #define PLL_MUL(reg, layout) \
22*dd6be631SClément Léger 	({ \
23*dd6be631SClément Léger 		typeof(layout) __layout = layout; \
24*dd6be631SClément Léger 		\
25*dd6be631SClément Léger 		(((reg) >> (__layout)->mul_shift) & (__layout)->mul_mask); \
26*dd6be631SClément Léger 	})
27*dd6be631SClément Léger #define PLL_MUL_MIN		2
28*dd6be631SClément Léger #define PLL_MUL_MASK(layout)	((layout)->mul_mask)
29*dd6be631SClément Léger #define PLL_MUL_MAX(layout)	(PLL_MUL_MASK(layout) + 1)
30*dd6be631SClément Léger #define PLL_ICPR_SHIFT(id)	((id) * 16)
31*dd6be631SClément Léger #define PLL_ICPR_MASK(id)	(0xffff << PLL_ICPR_SHIFT(id))
32*dd6be631SClément Léger #define PLL_MAX_COUNT		0x3f
33*dd6be631SClément Léger #define PLL_COUNT_SHIFT		8
34*dd6be631SClément Léger #define PLL_OUT_SHIFT		14
35*dd6be631SClément Léger #define PLL_MAX_ID		1
36*dd6be631SClément Léger 
37*dd6be631SClément Léger struct clk_pll {
38*dd6be631SClément Léger 	vaddr_t base;
39*dd6be631SClément Léger 	uint8_t id;
40*dd6be631SClément Léger 	uint8_t div;
41*dd6be631SClément Léger 	uint8_t range;
42*dd6be631SClément Léger 	uint16_t mul;
43*dd6be631SClément Léger 	const struct clk_pll_layout *layout;
44*dd6be631SClément Léger 	const struct clk_pll_charac *charac;
45*dd6be631SClément Léger };
46*dd6be631SClément Léger 
clk_pll_ready(vaddr_t base,int id)47*dd6be631SClément Léger static bool clk_pll_ready(vaddr_t base, int id)
48*dd6be631SClément Léger {
49*dd6be631SClément Léger 	unsigned int status = io_read32(base + AT91_PMC_SR);
50*dd6be631SClément Léger 
51*dd6be631SClément Léger 	return status & PLL_STATUS_MASK(id);
52*dd6be631SClément Léger }
53*dd6be631SClément Léger 
clk_pll_enable(struct clk * clk)54*dd6be631SClément Léger static TEE_Result clk_pll_enable(struct clk *clk)
55*dd6be631SClément Léger {
56*dd6be631SClément Léger 	struct clk_pll *pll = clk->priv;
57*dd6be631SClément Léger 	const struct clk_pll_layout *layout = pll->layout;
58*dd6be631SClément Léger 	const struct clk_pll_charac *charac = pll->charac;
59*dd6be631SClément Léger 	uint8_t id = pll->id;
60*dd6be631SClément Léger 	uint32_t mask = PLL_STATUS_MASK(id);
61*dd6be631SClément Léger 	int offset = PLL_REG(id);
62*dd6be631SClément Léger 	uint8_t out = 0;
63*dd6be631SClément Léger 	unsigned int pllr = 0;
64*dd6be631SClément Léger 	unsigned int status = 0;
65*dd6be631SClément Léger 	uint8_t div = 0;
66*dd6be631SClément Léger 	uint16_t mul = 0;
67*dd6be631SClément Léger 
68*dd6be631SClément Léger 	pllr = io_read32(pll->base + offset);
69*dd6be631SClément Léger 	div = PLL_DIV(pllr);
70*dd6be631SClément Léger 	mul = PLL_MUL(pllr, layout);
71*dd6be631SClément Léger 
72*dd6be631SClément Léger 	status = io_read32(pll->base + AT91_PMC_SR);
73*dd6be631SClément Léger 	if ((status & mask) &&
74*dd6be631SClément Léger 	    (div == pll->div && mul == pll->mul))
75*dd6be631SClément Léger 		return TEE_SUCCESS;
76*dd6be631SClément Léger 
77*dd6be631SClément Léger 	if (charac->out)
78*dd6be631SClément Léger 		out = charac->out[pll->range];
79*dd6be631SClément Léger 
80*dd6be631SClément Léger 	if (charac->icpll)
81*dd6be631SClément Léger 		io_clrsetbits32(pll->base + AT91_PMC_PLLICPR, PLL_ICPR_MASK(id),
82*dd6be631SClément Léger 				charac->icpll[pll->range] <<
83*dd6be631SClément Léger 				PLL_ICPR_SHIFT(id));
84*dd6be631SClément Léger 
85*dd6be631SClément Léger 	io_clrsetbits32(pll->base + offset, layout->pllr_mask,
86*dd6be631SClément Léger 			pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
87*dd6be631SClément Léger 			(out << PLL_OUT_SHIFT) |
88*dd6be631SClément Léger 			((pll->mul & layout->mul_mask) << layout->mul_shift));
89*dd6be631SClément Léger 
90*dd6be631SClément Léger 	while (!clk_pll_ready(pll->base, pll->id))
91*dd6be631SClément Léger 		;
92*dd6be631SClément Léger 
93*dd6be631SClément Léger 	return TEE_SUCCESS;
94*dd6be631SClément Léger }
95*dd6be631SClément Léger 
clk_pll_disable(struct clk * clk)96*dd6be631SClément Léger static void clk_pll_disable(struct clk *clk)
97*dd6be631SClément Léger {
98*dd6be631SClément Léger 	struct clk_pll *pll = clk->priv;
99*dd6be631SClément Léger 	unsigned int mask = pll->layout->pllr_mask;
100*dd6be631SClément Léger 
101*dd6be631SClément Léger 	io_clrsetbits32(pll->base + PLL_REG(pll->id), mask, ~mask);
102*dd6be631SClément Léger }
103*dd6be631SClément Léger 
clk_pll_get_rate(struct clk * clk,unsigned long parent_rate)104*dd6be631SClément Léger static unsigned long clk_pll_get_rate(struct clk *clk,
105*dd6be631SClément Léger 				      unsigned long parent_rate)
106*dd6be631SClément Léger {
107*dd6be631SClément Léger 	struct clk_pll *pll = clk->priv;
108*dd6be631SClément Léger 
109*dd6be631SClément Léger 	if (!pll->div || !pll->mul)
110*dd6be631SClément Léger 		return 0;
111*dd6be631SClément Léger 
112*dd6be631SClément Léger 	return (parent_rate / pll->div) * (pll->mul + 1);
113*dd6be631SClément Léger }
114*dd6be631SClément Léger 
clk_pll_get_best_div_mul(struct clk_pll * pll,unsigned long rate,unsigned long parent_rate,uint32_t * div,uint32_t * mul,uint32_t * index)115*dd6be631SClément Léger static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
116*dd6be631SClément Léger 				     unsigned long parent_rate,
117*dd6be631SClément Léger 				     uint32_t *div, uint32_t *mul,
118*dd6be631SClément Léger 				     uint32_t *index)
119*dd6be631SClément Léger {
120*dd6be631SClément Léger 	const struct clk_pll_layout *layout = pll->layout;
121*dd6be631SClément Léger 	const struct clk_pll_charac *charac = pll->charac;
122*dd6be631SClément Léger 	unsigned long bestremainder = ULONG_MAX;
123*dd6be631SClément Léger 	unsigned long maxdiv = 1;
124*dd6be631SClément Léger 	unsigned long mindiv = 1;
125*dd6be631SClément Léger 	unsigned long tmpdiv = 1;
126*dd6be631SClément Léger 	long bestrate = -1;
127*dd6be631SClément Léger 	unsigned long bestdiv = 0;
128*dd6be631SClément Léger 	unsigned long bestmul = 0;
129*dd6be631SClément Léger 	int i = 0;
130*dd6be631SClément Léger 
131*dd6be631SClément Léger 	/* Check if parent_rate is a valid input rate */
132*dd6be631SClément Léger 	if (parent_rate < charac->input.min)
133*dd6be631SClément Léger 		return -1;
134*dd6be631SClément Léger 
135*dd6be631SClément Léger 	/*
136*dd6be631SClément Léger 	 * Calculate minimum divider based on the minimum multiplier, the
137*dd6be631SClément Léger 	 * parent_rate and the requested rate.
138*dd6be631SClément Léger 	 * Should always be 2 according to the input and output charac
139*dd6be631SClément Léger 	 * of the PLL blocks.
140*dd6be631SClément Léger 	 */
141*dd6be631SClément Léger 	mindiv = (parent_rate * PLL_MUL_MIN) / rate;
142*dd6be631SClément Léger 	if (!mindiv)
143*dd6be631SClément Léger 		mindiv = 1;
144*dd6be631SClément Léger 
145*dd6be631SClément Léger 	if (parent_rate > charac->input.max) {
146*dd6be631SClément Léger 		tmpdiv = DIV_ROUND_UP(parent_rate, charac->input.max);
147*dd6be631SClément Léger 		if (tmpdiv > PLL_DIV_MAX)
148*dd6be631SClément Léger 			return -1;
149*dd6be631SClément Léger 
150*dd6be631SClément Léger 		if (tmpdiv > mindiv)
151*dd6be631SClément Léger 			mindiv = tmpdiv;
152*dd6be631SClément Léger 	}
153*dd6be631SClément Léger 
154*dd6be631SClément Léger 	/*
155*dd6be631SClément Léger 	 * Calculate the maximum divider which is limited by PLL register
156*dd6be631SClément Léger 	 * layout (limited by the MUL or DIV field size).
157*dd6be631SClément Léger 	 */
158*dd6be631SClément Léger 	maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate);
159*dd6be631SClément Léger 	if (maxdiv > PLL_DIV_MAX)
160*dd6be631SClément Léger 		maxdiv = PLL_DIV_MAX;
161*dd6be631SClément Léger 
162*dd6be631SClément Léger 	/*
163*dd6be631SClément Léger 	 * Iterate over the acceptable divider values to find the best
164*dd6be631SClément Léger 	 * divider/multiplier pair (the one that generates the closest
165*dd6be631SClément Léger 	 * rate to the requested one).
166*dd6be631SClément Léger 	 */
167*dd6be631SClément Léger 	for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
168*dd6be631SClément Léger 		unsigned long remainder = 0;
169*dd6be631SClément Léger 		unsigned long tmprate = 0;
170*dd6be631SClément Léger 		unsigned long tmpmul = 0;
171*dd6be631SClément Léger 
172*dd6be631SClément Léger 		/*
173*dd6be631SClément Léger 		 * Calculate the multiplier associated with the current
174*dd6be631SClément Léger 		 * divider that provide the closest rate to the requested one.
175*dd6be631SClément Léger 		 */
176*dd6be631SClément Léger 		tmpmul = UDIV_ROUND_NEAREST(rate, parent_rate / tmpdiv);
177*dd6be631SClément Léger 		tmprate = (parent_rate / tmpdiv) * tmpmul;
178*dd6be631SClément Léger 		if (tmprate > rate)
179*dd6be631SClément Léger 			remainder = tmprate - rate;
180*dd6be631SClément Léger 		else
181*dd6be631SClément Léger 			remainder = rate - tmprate;
182*dd6be631SClément Léger 
183*dd6be631SClément Léger 		/*
184*dd6be631SClément Léger 		 * Compare the remainder with the best remainder found until
185*dd6be631SClément Léger 		 * now and elect a new best multiplier/divider pair if the
186*dd6be631SClément Léger 		 * current remainder is smaller than the best one.
187*dd6be631SClément Léger 		 */
188*dd6be631SClément Léger 		if (remainder < bestremainder) {
189*dd6be631SClément Léger 			bestremainder = remainder;
190*dd6be631SClément Léger 			bestdiv = tmpdiv;
191*dd6be631SClément Léger 			bestmul = tmpmul;
192*dd6be631SClément Léger 			bestrate = tmprate;
193*dd6be631SClément Léger 		}
194*dd6be631SClément Léger 
195*dd6be631SClément Léger 		/*
196*dd6be631SClément Léger 		 * We've found a perfect match!
197*dd6be631SClément Léger 		 * Stop searching now and use this multiplier/divider pair.
198*dd6be631SClément Léger 		 */
199*dd6be631SClément Léger 		if (!remainder)
200*dd6be631SClément Léger 			break;
201*dd6be631SClément Léger 	}
202*dd6be631SClément Léger 
203*dd6be631SClément Léger 	/* We haven't found any multiplier/divider pair => return -ERANGE */
204*dd6be631SClément Léger 	if (bestrate < 0)
205*dd6be631SClément Léger 		return bestrate;
206*dd6be631SClément Léger 
207*dd6be631SClément Léger 	/* Check if bestrate is a valid output rate  */
208*dd6be631SClément Léger 	for (i = 0; i < charac->num_output; i++) {
209*dd6be631SClément Léger 		if (bestrate >= (long)charac->output[i].min &&
210*dd6be631SClément Léger 		    bestrate <= (long)charac->output[i].max)
211*dd6be631SClément Léger 			break;
212*dd6be631SClément Léger 	}
213*dd6be631SClément Léger 
214*dd6be631SClément Léger 	if (i >= charac->num_output)
215*dd6be631SClément Léger 		return -1;
216*dd6be631SClément Léger 
217*dd6be631SClément Léger 	if (div)
218*dd6be631SClément Léger 		*div = bestdiv;
219*dd6be631SClément Léger 	if (mul)
220*dd6be631SClément Léger 		*mul = bestmul - 1;
221*dd6be631SClément Léger 	if (index)
222*dd6be631SClément Léger 		*index = i;
223*dd6be631SClément Léger 
224*dd6be631SClément Léger 	return bestrate;
225*dd6be631SClément Léger }
226*dd6be631SClément Léger 
clk_pll_set_rate(struct clk * clk,unsigned long rate,unsigned long parent_rate)227*dd6be631SClément Léger static TEE_Result clk_pll_set_rate(struct clk *clk, unsigned long rate,
228*dd6be631SClément Léger 				   unsigned long parent_rate)
229*dd6be631SClément Léger {
230*dd6be631SClément Léger 	struct clk_pll *pll = clk->priv;
231*dd6be631SClément Léger 	long ret = -1;
232*dd6be631SClément Léger 	uint32_t div = 1;
233*dd6be631SClément Léger 	uint32_t mul = 0;
234*dd6be631SClément Léger 	uint32_t index = 0;
235*dd6be631SClément Léger 
236*dd6be631SClément Léger 	ret = clk_pll_get_best_div_mul(pll, rate, parent_rate,
237*dd6be631SClément Léger 				       &div, &mul, &index);
238*dd6be631SClément Léger 	if (ret < 0)
239*dd6be631SClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
240*dd6be631SClément Léger 
241*dd6be631SClément Léger 	pll->range = index;
242*dd6be631SClément Léger 	pll->div = div;
243*dd6be631SClément Léger 	pll->mul = mul;
244*dd6be631SClément Léger 
245*dd6be631SClément Léger 	return TEE_SUCCESS;
246*dd6be631SClément Léger }
247*dd6be631SClément Léger 
248*dd6be631SClément Léger static const struct clk_ops pll_ops = {
249*dd6be631SClément Léger 	.enable = clk_pll_enable,
250*dd6be631SClément Léger 	.disable = clk_pll_disable,
251*dd6be631SClément Léger 	.get_rate = clk_pll_get_rate,
252*dd6be631SClément Léger 	.set_rate = clk_pll_set_rate,
253*dd6be631SClément Léger };
254*dd6be631SClément Léger 
255*dd6be631SClément Léger struct clk *
at91_clk_register_pll(struct pmc_data * pmc,const char * name,struct clk * parent,uint8_t id,const struct clk_pll_layout * layout,const struct clk_pll_charac * charac)256*dd6be631SClément Léger at91_clk_register_pll(struct pmc_data *pmc, const char *name,
257*dd6be631SClément Léger 		      struct clk *parent, uint8_t id,
258*dd6be631SClément Léger 		      const struct clk_pll_layout *layout,
259*dd6be631SClément Léger 		      const struct clk_pll_charac *charac)
260*dd6be631SClément Léger {
261*dd6be631SClément Léger 	struct clk *clk = NULL;
262*dd6be631SClément Léger 	struct clk_pll *pll = NULL;
263*dd6be631SClément Léger 	int offset = PLL_REG(id);
264*dd6be631SClément Léger 	uint32_t pllr = 0;
265*dd6be631SClément Léger 
266*dd6be631SClément Léger 	if (!name || !parent)
267*dd6be631SClément Léger 		return NULL;
268*dd6be631SClément Léger 
269*dd6be631SClément Léger 	clk = clk_alloc(name, &pll_ops, &parent, 1);
270*dd6be631SClément Léger 	if (!clk)
271*dd6be631SClément Léger 		return NULL;
272*dd6be631SClément Léger 
273*dd6be631SClément Léger 	if (id > PLL_MAX_ID)
274*dd6be631SClément Léger 		return NULL;
275*dd6be631SClément Léger 
276*dd6be631SClément Léger 	pll = calloc(1, sizeof(*pll));
277*dd6be631SClément Léger 	if (!pll) {
278*dd6be631SClément Léger 		clk_free(clk);
279*dd6be631SClément Léger 		return NULL;
280*dd6be631SClément Léger 	}
281*dd6be631SClément Léger 
282*dd6be631SClément Léger 	pll->id = id;
283*dd6be631SClément Léger 	pll->layout = layout;
284*dd6be631SClément Léger 	pll->charac = charac;
285*dd6be631SClément Léger 	pll->base = pmc->base;
286*dd6be631SClément Léger 	pllr = io_read32(pmc->base + offset);
287*dd6be631SClément Léger 	pll->div = PLL_DIV(pllr);
288*dd6be631SClément Léger 	pll->mul = PLL_MUL(pllr, layout);
289*dd6be631SClément Léger 
290*dd6be631SClément Léger 	clk->flags = CLK_SET_RATE_GATE;
291*dd6be631SClément Léger 	clk->priv = pll;
292*dd6be631SClément Léger 
293*dd6be631SClément Léger 	if (clk_register(clk)) {
294*dd6be631SClément Léger 		clk_free(clk);
295*dd6be631SClément Léger 		free(pll);
296*dd6be631SClément Léger 		return NULL;
297*dd6be631SClément Léger 	}
298*dd6be631SClément Léger 
299*dd6be631SClément Léger 	return clk;
300*dd6be631SClément Léger }
301*dd6be631SClément Léger 
302*dd6be631SClément Léger const struct clk_pll_layout sama5d3_pll_layout = {
303*dd6be631SClément Léger 	.pllr_mask = 0x1FFFFFF,
304*dd6be631SClément Léger 	.mul_shift = 18,
305*dd6be631SClément Léger 	.mul_mask = 0x7F,
306*dd6be631SClément Léger };
307