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