]>
Commit | Line | Data |
---|---|---|
0bd7dc74 RL |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * MediaTek common clock driver | |
4 | * | |
5 | * Copyright (C) 2018 MediaTek Inc. | |
6 | * Author: Ryder Lee <[email protected]> | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <clk-uclass.h> | |
11 | #include <div64.h> | |
12 | #include <dm.h> | |
13 | #include <asm/io.h> | |
cd93d625 | 14 | #include <linux/bitops.h> |
c05ed00a | 15 | #include <linux/delay.h> |
0bd7dc74 RL |
16 | |
17 | #include "clk-mtk.h" | |
18 | ||
19 | #define REG_CON0 0 | |
20 | #define REG_CON1 4 | |
21 | ||
22 | #define CON0_BASE_EN BIT(0) | |
23 | #define CON0_PWR_ON BIT(0) | |
24 | #define CON0_ISO_EN BIT(1) | |
25 | #define CON1_PCW_CHG BIT(31) | |
26 | ||
27 | #define POSTDIV_MASK 0x7 | |
28 | #define INTEGER_BITS 7 | |
29 | ||
30 | /* scpsys clock off control */ | |
31 | #define CLK_SCP_CFG0 0x200 | |
32 | #define CLK_SCP_CFG1 0x204 | |
33 | #define SCP_ARMCK_OFF_EN GENMASK(9, 0) | |
34 | #define SCP_AXICK_DCM_DIS_EN BIT(0) | |
35 | #define SCP_AXICK_26M_SEL_EN BIT(4) | |
36 | ||
37 | /* shared functions */ | |
38 | ||
39 | /* | |
40 | * In case the rate change propagation to parent clocks is undesirable, | |
41 | * this function is recursively called to find the parent to calculate | |
42 | * the accurate frequency. | |
43 | */ | |
d8588ba5 | 44 | static ulong mtk_clk_find_parent_rate(struct clk *clk, int id, |
832685f0 | 45 | const struct driver *drv) |
0bd7dc74 RL |
46 | { |
47 | struct clk parent = { .id = id, }; | |
48 | ||
49 | if (drv) { | |
50 | struct udevice *dev; | |
51 | ||
52 | if (uclass_get_device_by_driver(UCLASS_CLK, drv, &dev)) | |
53 | return -ENODEV; | |
54 | ||
55 | parent.dev = dev; | |
56 | } else { | |
57 | parent.dev = clk->dev; | |
58 | } | |
59 | ||
60 | return clk_get_rate(&parent); | |
61 | } | |
62 | ||
63 | static int mtk_clk_mux_set_parent(void __iomem *base, u32 parent, | |
64 | const struct mtk_composite *mux) | |
65 | { | |
66 | u32 val, index = 0; | |
67 | ||
68 | while (mux->parent[index] != parent) | |
69 | if (++index == mux->num_parents) | |
70 | return -EINVAL; | |
71 | ||
f62168d3 | 72 | if (mux->flags & CLK_MUX_SETCLR_UPD) { |
73 | val = (mux->mux_mask << mux->mux_shift); | |
74 | writel(val, base + mux->mux_clr_reg); | |
0bd7dc74 | 75 | |
f62168d3 | 76 | val = (index << mux->mux_shift); |
77 | writel(val, base + mux->mux_set_reg); | |
78 | ||
79 | if (mux->upd_shift >= 0) | |
80 | writel(BIT(mux->upd_shift), base + mux->upd_reg); | |
81 | } else { | |
82 | /* switch mux to a select parent */ | |
83 | val = readl(base + mux->mux_reg); | |
84 | val &= ~(mux->mux_mask << mux->mux_shift); | |
85 | ||
86 | val |= index << mux->mux_shift; | |
87 | writel(val, base + mux->mux_reg); | |
88 | } | |
0bd7dc74 RL |
89 | |
90 | return 0; | |
91 | } | |
92 | ||
93 | /* apmixedsys functions */ | |
94 | ||
95 | static unsigned long __mtk_pll_recalc_rate(const struct mtk_pll_data *pll, | |
96 | u32 fin, u32 pcw, int postdiv) | |
97 | { | |
98 | int pcwbits = pll->pcwbits; | |
99 | int pcwfbits; | |
0670adb2 | 100 | int ibits; |
0bd7dc74 RL |
101 | u64 vco; |
102 | u8 c = 0; | |
103 | ||
104 | /* The fractional part of the PLL divider. */ | |
0670adb2 | 105 | ibits = pll->pcwibits ? pll->pcwibits : INTEGER_BITS; |
106 | pcwfbits = pcwbits > ibits ? pcwbits - ibits : 0; | |
0bd7dc74 RL |
107 | |
108 | vco = (u64)fin * pcw; | |
109 | ||
110 | if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) | |
111 | c = 1; | |
112 | ||
113 | vco >>= pcwfbits; | |
114 | ||
115 | if (c) | |
116 | vco++; | |
117 | ||
118 | return ((unsigned long)vco + postdiv - 1) / postdiv; | |
119 | } | |
120 | ||
121 | /** | |
122 | * MediaTek PLLs are configured through their pcw value. The pcw value | |
123 | * describes a divider in the PLL feedback loop which consists of 7 bits | |
124 | * for the integer part and the remaining bits (if present) for the | |
125 | * fractional part. Also they have a 3 bit power-of-two post divider. | |
126 | */ | |
127 | static void mtk_pll_set_rate_regs(struct clk *clk, u32 pcw, int postdiv) | |
128 | { | |
129 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
130 | const struct mtk_pll_data *pll = &priv->tree->plls[clk->id]; | |
0670adb2 | 131 | u32 val, chg; |
0bd7dc74 RL |
132 | |
133 | /* set postdiv */ | |
134 | val = readl(priv->base + pll->pd_reg); | |
135 | val &= ~(POSTDIV_MASK << pll->pd_shift); | |
136 | val |= (ffs(postdiv) - 1) << pll->pd_shift; | |
137 | ||
138 | /* postdiv and pcw need to set at the same time if on same register */ | |
139 | if (pll->pd_reg != pll->pcw_reg) { | |
140 | writel(val, priv->base + pll->pd_reg); | |
141 | val = readl(priv->base + pll->pcw_reg); | |
142 | } | |
143 | ||
144 | /* set pcw */ | |
145 | val &= ~GENMASK(pll->pcw_shift + pll->pcwbits - 1, pll->pcw_shift); | |
146 | val |= pcw << pll->pcw_shift; | |
0bd7dc74 | 147 | |
0670adb2 | 148 | if (pll->pcw_chg_reg) { |
149 | chg = readl(priv->base + pll->pcw_chg_reg); | |
150 | chg |= CON1_PCW_CHG; | |
151 | writel(val, priv->base + pll->pcw_reg); | |
152 | writel(chg, priv->base + pll->pcw_chg_reg); | |
153 | } else { | |
154 | val |= CON1_PCW_CHG; | |
155 | writel(val, priv->base + pll->pcw_reg); | |
156 | } | |
0bd7dc74 RL |
157 | |
158 | udelay(20); | |
159 | } | |
160 | ||
161 | /** | |
162 | * mtk_pll_calc_values - calculate good values for a given input frequency. | |
163 | * @clk: The clk | |
164 | * @pcw: The pcw value (output) | |
165 | * @postdiv: The post divider (output) | |
166 | * @freq: The desired target frequency | |
167 | */ | |
168 | static void mtk_pll_calc_values(struct clk *clk, u32 *pcw, u32 *postdiv, | |
169 | u32 freq) | |
170 | { | |
171 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
172 | const struct mtk_pll_data *pll = &priv->tree->plls[clk->id]; | |
0670adb2 | 173 | unsigned long fmin = pll->fmin ? pll->fmin : 1000 * MHZ; |
0bd7dc74 | 174 | u64 _pcw; |
0670adb2 | 175 | int ibits; |
0bd7dc74 RL |
176 | u32 val; |
177 | ||
178 | if (freq > pll->fmax) | |
179 | freq = pll->fmax; | |
180 | ||
181 | for (val = 0; val < 5; val++) { | |
182 | *postdiv = 1 << val; | |
183 | if ((u64)freq * *postdiv >= fmin) | |
184 | break; | |
185 | } | |
186 | ||
187 | /* _pcw = freq * postdiv / xtal_rate * 2^pcwfbits */ | |
0670adb2 | 188 | ibits = pll->pcwibits ? pll->pcwibits : INTEGER_BITS; |
189 | _pcw = ((u64)freq << val) << (pll->pcwbits - ibits); | |
0bd7dc74 RL |
190 | do_div(_pcw, priv->tree->xtal2_rate); |
191 | ||
192 | *pcw = (u32)_pcw; | |
193 | } | |
194 | ||
195 | static ulong mtk_apmixedsys_set_rate(struct clk *clk, ulong rate) | |
196 | { | |
197 | u32 pcw = 0; | |
198 | u32 postdiv; | |
199 | ||
200 | mtk_pll_calc_values(clk, &pcw, &postdiv, rate); | |
201 | mtk_pll_set_rate_regs(clk, pcw, postdiv); | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
206 | static ulong mtk_apmixedsys_get_rate(struct clk *clk) | |
207 | { | |
208 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
209 | const struct mtk_pll_data *pll = &priv->tree->plls[clk->id]; | |
210 | u32 postdiv; | |
211 | u32 pcw; | |
212 | ||
213 | postdiv = (readl(priv->base + pll->pd_reg) >> pll->pd_shift) & | |
214 | POSTDIV_MASK; | |
215 | postdiv = 1 << postdiv; | |
216 | ||
217 | pcw = readl(priv->base + pll->pcw_reg) >> pll->pcw_shift; | |
218 | pcw &= GENMASK(pll->pcwbits - 1, 0); | |
219 | ||
220 | return __mtk_pll_recalc_rate(pll, priv->tree->xtal2_rate, | |
221 | pcw, postdiv); | |
222 | } | |
223 | ||
224 | static int mtk_apmixedsys_enable(struct clk *clk) | |
225 | { | |
226 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
227 | const struct mtk_pll_data *pll = &priv->tree->plls[clk->id]; | |
228 | u32 r; | |
229 | ||
230 | r = readl(priv->base + pll->pwr_reg) | CON0_PWR_ON; | |
231 | writel(r, priv->base + pll->pwr_reg); | |
232 | udelay(1); | |
233 | ||
234 | r = readl(priv->base + pll->pwr_reg) & ~CON0_ISO_EN; | |
235 | writel(r, priv->base + pll->pwr_reg); | |
236 | udelay(1); | |
237 | ||
238 | r = readl(priv->base + pll->reg + REG_CON0); | |
239 | r |= pll->en_mask; | |
240 | writel(r, priv->base + pll->reg + REG_CON0); | |
241 | ||
242 | udelay(20); | |
243 | ||
244 | if (pll->flags & HAVE_RST_BAR) { | |
245 | r = readl(priv->base + pll->reg + REG_CON0); | |
246 | r |= pll->rst_bar_mask; | |
247 | writel(r, priv->base + pll->reg + REG_CON0); | |
248 | } | |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
253 | static int mtk_apmixedsys_disable(struct clk *clk) | |
254 | { | |
255 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
256 | const struct mtk_pll_data *pll = &priv->tree->plls[clk->id]; | |
257 | u32 r; | |
258 | ||
259 | if (pll->flags & HAVE_RST_BAR) { | |
260 | r = readl(priv->base + pll->reg + REG_CON0); | |
261 | r &= ~pll->rst_bar_mask; | |
262 | writel(r, priv->base + pll->reg + REG_CON0); | |
263 | } | |
264 | ||
265 | r = readl(priv->base + pll->reg + REG_CON0); | |
266 | r &= ~CON0_BASE_EN; | |
267 | writel(r, priv->base + pll->reg + REG_CON0); | |
268 | ||
269 | r = readl(priv->base + pll->pwr_reg) | CON0_ISO_EN; | |
270 | writel(r, priv->base + pll->pwr_reg); | |
271 | ||
272 | r = readl(priv->base + pll->pwr_reg) & ~CON0_PWR_ON; | |
273 | writel(r, priv->base + pll->pwr_reg); | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
278 | /* topckgen functions */ | |
279 | ||
280 | static ulong mtk_factor_recalc_rate(const struct mtk_fixed_factor *fdiv, | |
281 | ulong parent_rate) | |
282 | { | |
283 | u64 rate = parent_rate * fdiv->mult; | |
284 | ||
285 | do_div(rate, fdiv->div); | |
286 | ||
287 | return rate; | |
288 | } | |
289 | ||
d8588ba5 | 290 | static ulong mtk_topckgen_get_factor_rate(struct clk *clk, u32 off) |
0bd7dc74 RL |
291 | { |
292 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
293 | const struct mtk_fixed_factor *fdiv = &priv->tree->fdivs[off]; | |
294 | ulong rate; | |
295 | ||
296 | switch (fdiv->flags & CLK_PARENT_MASK) { | |
297 | case CLK_PARENT_APMIXED: | |
298 | rate = mtk_clk_find_parent_rate(clk, fdiv->parent, | |
65e25bea | 299 | DM_DRIVER_GET(mtk_clk_apmixedsys)); |
0bd7dc74 RL |
300 | break; |
301 | case CLK_PARENT_TOPCKGEN: | |
302 | rate = mtk_clk_find_parent_rate(clk, fdiv->parent, NULL); | |
303 | break; | |
304 | ||
305 | default: | |
306 | rate = priv->tree->xtal_rate; | |
307 | } | |
308 | ||
309 | return mtk_factor_recalc_rate(fdiv, rate); | |
310 | } | |
311 | ||
d8588ba5 | 312 | static ulong mtk_topckgen_get_mux_rate(struct clk *clk, u32 off) |
0bd7dc74 RL |
313 | { |
314 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
315 | const struct mtk_composite *mux = &priv->tree->muxes[off]; | |
316 | u32 index; | |
317 | ||
318 | index = readl(priv->base + mux->mux_reg); | |
319 | index &= mux->mux_mask << mux->mux_shift; | |
320 | index = index >> mux->mux_shift; | |
321 | ||
322 | if (mux->parent[index]) | |
323 | return mtk_clk_find_parent_rate(clk, mux->parent[index], | |
324 | NULL); | |
325 | ||
326 | return priv->tree->xtal_rate; | |
327 | } | |
328 | ||
329 | static ulong mtk_topckgen_get_rate(struct clk *clk) | |
330 | { | |
331 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
332 | ||
333 | if (clk->id < priv->tree->fdivs_offs) | |
334 | return priv->tree->fclks[clk->id].rate; | |
335 | else if (clk->id < priv->tree->muxes_offs) | |
336 | return mtk_topckgen_get_factor_rate(clk, clk->id - | |
337 | priv->tree->fdivs_offs); | |
338 | else | |
339 | return mtk_topckgen_get_mux_rate(clk, clk->id - | |
340 | priv->tree->muxes_offs); | |
341 | } | |
342 | ||
343 | static int mtk_topckgen_enable(struct clk *clk) | |
344 | { | |
345 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
346 | const struct mtk_composite *mux; | |
347 | u32 val; | |
348 | ||
349 | if (clk->id < priv->tree->muxes_offs) | |
350 | return 0; | |
351 | ||
352 | mux = &priv->tree->muxes[clk->id - priv->tree->muxes_offs]; | |
353 | if (mux->gate_shift < 0) | |
354 | return 0; | |
355 | ||
356 | /* enable clock gate */ | |
f62168d3 | 357 | if (mux->flags & CLK_MUX_SETCLR_UPD) { |
358 | val = BIT(mux->gate_shift); | |
359 | writel(val, priv->base + mux->mux_clr_reg); | |
360 | } else { | |
361 | val = readl(priv->base + mux->gate_reg); | |
362 | val &= ~BIT(mux->gate_shift); | |
363 | writel(val, priv->base + mux->gate_reg); | |
364 | } | |
0bd7dc74 RL |
365 | |
366 | if (mux->flags & CLK_DOMAIN_SCPSYS) { | |
367 | /* enable scpsys clock off control */ | |
368 | writel(SCP_ARMCK_OFF_EN, priv->base + CLK_SCP_CFG0); | |
369 | writel(SCP_AXICK_DCM_DIS_EN | SCP_AXICK_26M_SEL_EN, | |
370 | priv->base + CLK_SCP_CFG1); | |
371 | } | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
376 | static int mtk_topckgen_disable(struct clk *clk) | |
377 | { | |
378 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
379 | const struct mtk_composite *mux; | |
380 | u32 val; | |
381 | ||
382 | if (clk->id < priv->tree->muxes_offs) | |
383 | return 0; | |
384 | ||
385 | mux = &priv->tree->muxes[clk->id - priv->tree->muxes_offs]; | |
386 | if (mux->gate_shift < 0) | |
387 | return 0; | |
388 | ||
389 | /* disable clock gate */ | |
f62168d3 | 390 | if (mux->flags & CLK_MUX_SETCLR_UPD) { |
391 | val = BIT(mux->gate_shift); | |
392 | writel(val, priv->base + mux->mux_set_reg); | |
393 | } else { | |
394 | val = readl(priv->base + mux->gate_reg); | |
395 | val |= BIT(mux->gate_shift); | |
396 | writel(val, priv->base + mux->gate_reg); | |
397 | } | |
0bd7dc74 RL |
398 | |
399 | return 0; | |
400 | } | |
401 | ||
402 | static int mtk_topckgen_set_parent(struct clk *clk, struct clk *parent) | |
403 | { | |
404 | struct mtk_clk_priv *priv = dev_get_priv(clk->dev); | |
405 | ||
406 | if (clk->id < priv->tree->muxes_offs) | |
407 | return 0; | |
408 | ||
409 | return mtk_clk_mux_set_parent(priv->base, parent->id, | |
410 | &priv->tree->muxes[clk->id - priv->tree->muxes_offs]); | |
411 | } | |
412 | ||
413 | /* CG functions */ | |
414 | ||
415 | static int mtk_clk_gate_enable(struct clk *clk) | |
416 | { | |
417 | struct mtk_cg_priv *priv = dev_get_priv(clk->dev); | |
418 | const struct mtk_gate *gate = &priv->gates[clk->id]; | |
419 | u32 bit = BIT(gate->shift); | |
420 | ||
421 | switch (gate->flags & CLK_GATE_MASK) { | |
422 | case CLK_GATE_SETCLR: | |
423 | writel(bit, priv->base + gate->regs->clr_ofs); | |
424 | break; | |
fe913a8b FP |
425 | case CLK_GATE_SETCLR_INV: |
426 | writel(bit, priv->base + gate->regs->set_ofs); | |
427 | break; | |
428 | case CLK_GATE_NO_SETCLR: | |
429 | clrsetbits_le32(priv->base + gate->regs->sta_ofs, bit, 0); | |
430 | break; | |
0bd7dc74 RL |
431 | case CLK_GATE_NO_SETCLR_INV: |
432 | clrsetbits_le32(priv->base + gate->regs->sta_ofs, bit, bit); | |
433 | break; | |
434 | ||
435 | default: | |
436 | return -EINVAL; | |
437 | } | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | static int mtk_clk_gate_disable(struct clk *clk) | |
443 | { | |
444 | struct mtk_cg_priv *priv = dev_get_priv(clk->dev); | |
445 | const struct mtk_gate *gate = &priv->gates[clk->id]; | |
446 | u32 bit = BIT(gate->shift); | |
447 | ||
448 | switch (gate->flags & CLK_GATE_MASK) { | |
449 | case CLK_GATE_SETCLR: | |
450 | writel(bit, priv->base + gate->regs->set_ofs); | |
451 | break; | |
fe913a8b FP |
452 | case CLK_GATE_SETCLR_INV: |
453 | writel(bit, priv->base + gate->regs->clr_ofs); | |
454 | break; | |
455 | case CLK_GATE_NO_SETCLR: | |
456 | clrsetbits_le32(priv->base + gate->regs->sta_ofs, bit, bit); | |
457 | break; | |
0bd7dc74 RL |
458 | case CLK_GATE_NO_SETCLR_INV: |
459 | clrsetbits_le32(priv->base + gate->regs->sta_ofs, bit, 0); | |
460 | break; | |
461 | ||
462 | default: | |
463 | return -EINVAL; | |
464 | } | |
465 | ||
466 | return 0; | |
467 | } | |
468 | ||
469 | static ulong mtk_clk_gate_get_rate(struct clk *clk) | |
470 | { | |
471 | struct mtk_cg_priv *priv = dev_get_priv(clk->dev); | |
472 | const struct mtk_gate *gate = &priv->gates[clk->id]; | |
473 | ||
474 | switch (gate->flags & CLK_PARENT_MASK) { | |
475 | case CLK_PARENT_APMIXED: | |
476 | return mtk_clk_find_parent_rate(clk, gate->parent, | |
65e25bea | 477 | DM_DRIVER_GET(mtk_clk_apmixedsys)); |
0bd7dc74 RL |
478 | break; |
479 | case CLK_PARENT_TOPCKGEN: | |
480 | return mtk_clk_find_parent_rate(clk, gate->parent, | |
65e25bea | 481 | DM_DRIVER_GET(mtk_clk_topckgen)); |
0bd7dc74 RL |
482 | break; |
483 | ||
484 | default: | |
485 | return priv->tree->xtal_rate; | |
486 | } | |
487 | } | |
488 | ||
489 | const struct clk_ops mtk_clk_apmixedsys_ops = { | |
490 | .enable = mtk_apmixedsys_enable, | |
491 | .disable = mtk_apmixedsys_disable, | |
492 | .set_rate = mtk_apmixedsys_set_rate, | |
493 | .get_rate = mtk_apmixedsys_get_rate, | |
494 | }; | |
495 | ||
496 | const struct clk_ops mtk_clk_topckgen_ops = { | |
497 | .enable = mtk_topckgen_enable, | |
498 | .disable = mtk_topckgen_disable, | |
499 | .get_rate = mtk_topckgen_get_rate, | |
500 | .set_parent = mtk_topckgen_set_parent, | |
501 | }; | |
502 | ||
503 | const struct clk_ops mtk_clk_gate_ops = { | |
504 | .enable = mtk_clk_gate_enable, | |
505 | .disable = mtk_clk_gate_disable, | |
506 | .get_rate = mtk_clk_gate_get_rate, | |
507 | }; | |
508 | ||
509 | int mtk_common_clk_init(struct udevice *dev, | |
510 | const struct mtk_clk_tree *tree) | |
511 | { | |
512 | struct mtk_clk_priv *priv = dev_get_priv(dev); | |
513 | ||
514 | priv->base = dev_read_addr_ptr(dev); | |
515 | if (!priv->base) | |
516 | return -ENOENT; | |
517 | ||
518 | priv->tree = tree; | |
519 | ||
520 | return 0; | |
521 | } | |
522 | ||
523 | int mtk_common_clk_gate_init(struct udevice *dev, | |
524 | const struct mtk_clk_tree *tree, | |
525 | const struct mtk_gate *gates) | |
526 | { | |
527 | struct mtk_cg_priv *priv = dev_get_priv(dev); | |
528 | ||
529 | priv->base = dev_read_addr_ptr(dev); | |
530 | if (!priv->base) | |
531 | return -ENOENT; | |
532 | ||
533 | priv->tree = tree; | |
534 | priv->gates = gates; | |
535 | ||
536 | return 0; | |
537 | } |