]>
Commit | Line | Data |
---|---|---|
912af842 JB |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * (C) Copyright 2018 - Beniamino Galvani <[email protected]> | |
4 | * (C) Copyright 2018 - BayLibre, SAS | |
5 | * Author: Neil Armstrong <[email protected]> | |
6 | */ | |
7 | ||
8 | #include <common.h> | |
9 | #include <asm/arch/clock-g12a.h> | |
10 | #include <asm/io.h> | |
11 | #include <clk-uclass.h> | |
12 | #include <dm.h> | |
13 | #include <regmap.h> | |
14 | #include <syscon.h> | |
15 | #include <div64.h> | |
16 | #include <dt-bindings/clock/g12a-clkc.h> | |
17 | #include "clk_meson.h" | |
18 | ||
19 | #define XTAL_RATE 24000000 | |
20 | ||
21 | struct meson_clk { | |
22 | struct regmap *map; | |
23 | }; | |
24 | ||
08e09c26 NA |
25 | static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id, |
26 | ulong rate, ulong current_rate); | |
912af842 JB |
27 | static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id); |
28 | ||
29 | #define NUM_CLKS 178 | |
30 | ||
31 | static struct meson_gate gates[NUM_CLKS] = { | |
32 | /* Everything Else (EE) domain gates */ | |
33 | MESON_GATE(CLKID_SPICC0, HHI_GCLK_MPEG0, 8), | |
34 | MESON_GATE(CLKID_I2C, HHI_GCLK_MPEG0, 9), | |
35 | MESON_GATE(CLKID_UART0, HHI_GCLK_MPEG0, 13), | |
36 | MESON_GATE(CLKID_SPICC1, HHI_GCLK_MPEG0, 14), | |
37 | MESON_GATE(CLKID_SD_EMMC_B, HHI_GCLK_MPEG0, 25), | |
38 | MESON_GATE(CLKID_SD_EMMC_C, HHI_GCLK_MPEG0, 26), | |
39 | MESON_GATE(CLKID_ETH, HHI_GCLK_MPEG1, 3), | |
40 | MESON_GATE(CLKID_UART1, HHI_GCLK_MPEG1, 16), | |
08e09c26 NA |
41 | MESON_GATE(CLKID_USB, HHI_GCLK_MPEG1, 25), |
42 | MESON_GATE(CLKID_USB1_DDR_BRIDGE, HHI_GCLK_MPEG2, 8), | |
912af842 JB |
43 | |
44 | /* Peripheral Gates */ | |
45 | MESON_GATE(CLKID_SD_EMMC_B_CLK0, HHI_SD_EMMC_CLK_CNTL, 23), | |
46 | MESON_GATE(CLKID_SD_EMMC_C_CLK0, HHI_NAND_CLK_CNTL, 7), | |
47 | }; | |
48 | ||
49 | static int meson_set_gate(struct clk *clk, bool on) | |
50 | { | |
51 | struct meson_clk *priv = dev_get_priv(clk->dev); | |
52 | struct meson_gate *gate; | |
53 | ||
54 | if (clk->id >= ARRAY_SIZE(gates)) | |
55 | return -ENOENT; | |
56 | ||
57 | gate = &gates[clk->id]; | |
58 | ||
59 | if (gate->reg == 0) | |
60 | return 0; | |
61 | ||
62 | regmap_update_bits(priv->map, gate->reg, | |
63 | BIT(gate->bit), on ? BIT(gate->bit) : 0); | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
68 | static int meson_clk_enable(struct clk *clk) | |
69 | { | |
70 | return meson_set_gate(clk, true); | |
71 | } | |
72 | ||
73 | static int meson_clk_disable(struct clk *clk) | |
74 | { | |
75 | return meson_set_gate(clk, false); | |
76 | } | |
77 | ||
78 | static unsigned long meson_clk81_get_rate(struct clk *clk) | |
79 | { | |
80 | struct meson_clk *priv = dev_get_priv(clk->dev); | |
81 | unsigned long parent_rate; | |
82 | uint reg; | |
83 | int parents[] = { | |
84 | -1, | |
85 | -1, | |
86 | CLKID_FCLK_DIV7, | |
87 | CLKID_MPLL1, | |
88 | CLKID_MPLL2, | |
89 | CLKID_FCLK_DIV4, | |
90 | CLKID_FCLK_DIV3, | |
91 | CLKID_FCLK_DIV5 | |
92 | }; | |
93 | ||
94 | /* mux */ | |
95 | regmap_read(priv->map, HHI_MPEG_CLK_CNTL, ®); | |
96 | reg = (reg >> 12) & 7; | |
97 | ||
98 | switch (reg) { | |
99 | case 0: | |
100 | parent_rate = XTAL_RATE; | |
101 | break; | |
102 | case 1: | |
103 | return -ENOENT; | |
104 | default: | |
105 | parent_rate = meson_clk_get_rate_by_id(clk, parents[reg]); | |
106 | } | |
107 | ||
108 | /* divider */ | |
109 | regmap_read(priv->map, HHI_MPEG_CLK_CNTL, ®); | |
110 | reg = reg & ((1 << 7) - 1); | |
111 | ||
112 | return parent_rate / reg; | |
113 | } | |
114 | ||
115 | static long mpll_rate_from_params(unsigned long parent_rate, | |
116 | unsigned long sdm, | |
117 | unsigned long n2) | |
118 | { | |
119 | unsigned long divisor = (SDM_DEN * n2) + sdm; | |
120 | ||
121 | if (n2 < N2_MIN) | |
122 | return -EINVAL; | |
123 | ||
124 | return DIV_ROUND_UP_ULL((u64)parent_rate * SDM_DEN, divisor); | |
125 | } | |
126 | ||
127 | static struct parm meson_mpll0_parm[2] = { | |
128 | {HHI_MPLL_CNTL1, 0, 14}, /* psdm */ | |
129 | {HHI_MPLL_CNTL1, 20, 9}, /* pn2 */ | |
130 | }; | |
131 | ||
132 | static struct parm meson_mpll1_parm[2] = { | |
133 | {HHI_MPLL_CNTL3, 0, 14}, /* psdm */ | |
134 | {HHI_MPLL_CNTL3, 20, 9}, /* pn2 */ | |
135 | }; | |
136 | ||
137 | static struct parm meson_mpll2_parm[2] = { | |
138 | {HHI_MPLL_CNTL5, 0, 14}, /* psdm */ | |
139 | {HHI_MPLL_CNTL5, 20, 9}, /* pn2 */ | |
140 | }; | |
141 | ||
142 | /* | |
143 | * MultiPhase Locked Loops are outputs from a PLL with additional frequency | |
144 | * scaling capabilities. MPLL rates are calculated as: | |
145 | * | |
146 | * f(N2_integer, SDM_IN ) = 2.0G/(N2_integer + SDM_IN/16384) | |
147 | */ | |
148 | static ulong meson_mpll_get_rate(struct clk *clk, unsigned long id) | |
149 | { | |
150 | struct meson_clk *priv = dev_get_priv(clk->dev); | |
151 | struct parm *psdm, *pn2; | |
152 | unsigned long sdm, n2; | |
153 | unsigned long parent_rate; | |
154 | uint reg; | |
155 | ||
156 | switch (id) { | |
157 | case CLKID_MPLL0: | |
158 | psdm = &meson_mpll0_parm[0]; | |
159 | pn2 = &meson_mpll0_parm[1]; | |
160 | break; | |
161 | case CLKID_MPLL1: | |
162 | psdm = &meson_mpll1_parm[0]; | |
163 | pn2 = &meson_mpll1_parm[1]; | |
164 | break; | |
165 | case CLKID_MPLL2: | |
166 | psdm = &meson_mpll2_parm[0]; | |
167 | pn2 = &meson_mpll2_parm[1]; | |
168 | break; | |
169 | default: | |
170 | return -ENOENT; | |
171 | } | |
172 | ||
173 | parent_rate = meson_clk_get_rate_by_id(clk, CLKID_FIXED_PLL); | |
174 | if (IS_ERR_VALUE(parent_rate)) | |
175 | return parent_rate; | |
176 | ||
177 | regmap_read(priv->map, psdm->reg_off, ®); | |
178 | sdm = PARM_GET(psdm->width, psdm->shift, reg); | |
179 | ||
180 | regmap_read(priv->map, pn2->reg_off, ®); | |
181 | n2 = PARM_GET(pn2->width, pn2->shift, reg); | |
182 | ||
183 | return mpll_rate_from_params(parent_rate, sdm, n2); | |
184 | } | |
185 | ||
186 | static struct parm meson_fixed_pll_parm[3] = { | |
187 | {HHI_FIX_PLL_CNTL0, 0, 8}, /* pm */ | |
188 | {HHI_FIX_PLL_CNTL0, 10, 5}, /* pn */ | |
189 | {HHI_FIX_PLL_CNTL0, 16, 2}, /* pod */ | |
190 | }; | |
191 | ||
192 | static struct parm meson_sys_pll_parm[3] = { | |
193 | {HHI_SYS_PLL_CNTL0, 0, 8}, /* pm */ | |
194 | {HHI_SYS_PLL_CNTL0, 10, 5}, /* pn */ | |
195 | {HHI_SYS_PLL_CNTL0, 16, 2}, /* pod */ | |
196 | }; | |
197 | ||
198 | static ulong meson_pll_get_rate(struct clk *clk, unsigned long id) | |
199 | { | |
200 | struct meson_clk *priv = dev_get_priv(clk->dev); | |
201 | struct parm *pm, *pn, *pod; | |
202 | unsigned long parent_rate_mhz = XTAL_RATE / 1000000; | |
203 | u16 n, m, od; | |
204 | uint reg; | |
205 | ||
206 | /* | |
207 | * FIXME: Between the unit conversion and the missing frac, we know | |
208 | * rate will be slightly off ... | |
209 | */ | |
210 | ||
211 | switch (id) { | |
212 | case CLKID_FIXED_PLL: | |
213 | pm = &meson_fixed_pll_parm[0]; | |
214 | pn = &meson_fixed_pll_parm[1]; | |
215 | pod = &meson_fixed_pll_parm[2]; | |
216 | break; | |
217 | case CLKID_SYS_PLL: | |
218 | pm = &meson_sys_pll_parm[0]; | |
219 | pn = &meson_sys_pll_parm[1]; | |
220 | pod = &meson_sys_pll_parm[2]; | |
221 | break; | |
222 | default: | |
223 | return -ENOENT; | |
224 | } | |
225 | ||
226 | regmap_read(priv->map, pn->reg_off, ®); | |
227 | n = PARM_GET(pn->width, pn->shift, reg); | |
228 | ||
229 | regmap_read(priv->map, pm->reg_off, ®); | |
230 | m = PARM_GET(pm->width, pm->shift, reg); | |
231 | ||
232 | regmap_read(priv->map, pod->reg_off, ®); | |
233 | od = PARM_GET(pod->width, pod->shift, reg); | |
234 | ||
235 | return ((parent_rate_mhz * m / n) >> od) * 1000000; | |
236 | } | |
237 | ||
08e09c26 NA |
238 | static struct parm meson_pcie_pll_parm[3] = { |
239 | {HHI_PCIE_PLL_CNTL0, 0, 8}, /* pm */ | |
240 | {HHI_PCIE_PLL_CNTL0, 10, 5}, /* pn */ | |
241 | {HHI_PCIE_PLL_CNTL0, 16, 5}, /* pod */ | |
242 | }; | |
243 | ||
244 | static ulong meson_pcie_pll_get_rate(struct clk *clk) | |
245 | { | |
246 | struct meson_clk *priv = dev_get_priv(clk->dev); | |
247 | struct parm *pm, *pn, *pod; | |
248 | unsigned long parent_rate_mhz = XTAL_RATE / 1000000; | |
249 | u16 n, m, od; | |
250 | uint reg; | |
251 | ||
252 | pm = &meson_pcie_pll_parm[0]; | |
253 | pn = &meson_pcie_pll_parm[1]; | |
254 | pod = &meson_pcie_pll_parm[2]; | |
255 | ||
256 | regmap_read(priv->map, pn->reg_off, ®); | |
257 | n = PARM_GET(pn->width, pn->shift, reg); | |
258 | ||
259 | regmap_read(priv->map, pm->reg_off, ®); | |
260 | m = PARM_GET(pm->width, pm->shift, reg); | |
261 | ||
262 | regmap_read(priv->map, pod->reg_off, ®); | |
263 | od = PARM_GET(pod->width, pod->shift, reg); | |
264 | ||
265 | return ((parent_rate_mhz * m / n) / 2 / od / 2) * 1000000; | |
266 | } | |
267 | ||
912af842 JB |
268 | static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id) |
269 | { | |
270 | ulong rate; | |
271 | ||
272 | switch (id) { | |
273 | case CLKID_FIXED_PLL: | |
274 | case CLKID_SYS_PLL: | |
275 | rate = meson_pll_get_rate(clk, id); | |
276 | break; | |
277 | case CLKID_FCLK_DIV2: | |
278 | rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 2; | |
279 | break; | |
280 | case CLKID_FCLK_DIV3: | |
281 | rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 3; | |
282 | break; | |
283 | case CLKID_FCLK_DIV4: | |
284 | rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 4; | |
285 | break; | |
286 | case CLKID_FCLK_DIV5: | |
287 | rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 5; | |
288 | break; | |
289 | case CLKID_FCLK_DIV7: | |
290 | rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 7; | |
291 | break; | |
292 | case CLKID_MPLL0: | |
293 | case CLKID_MPLL1: | |
294 | case CLKID_MPLL2: | |
295 | rate = meson_mpll_get_rate(clk, id); | |
296 | break; | |
297 | case CLKID_CLK81: | |
298 | rate = meson_clk81_get_rate(clk); | |
299 | break; | |
08e09c26 NA |
300 | case CLKID_PCIE_PLL: |
301 | rate = meson_pcie_pll_get_rate(clk); | |
302 | break; | |
912af842 JB |
303 | default: |
304 | if (gates[id].reg != 0) { | |
305 | /* a clock gate */ | |
306 | rate = meson_clk81_get_rate(clk); | |
307 | break; | |
308 | } | |
309 | return -ENOENT; | |
310 | } | |
311 | ||
312 | debug("clock %lu has rate %lu\n", id, rate); | |
313 | return rate; | |
314 | } | |
315 | ||
316 | static ulong meson_clk_get_rate(struct clk *clk) | |
317 | { | |
318 | return meson_clk_get_rate_by_id(clk, clk->id); | |
319 | } | |
320 | ||
08e09c26 NA |
321 | static ulong meson_pcie_pll_set_rate(struct clk *clk, ulong rate) |
322 | { | |
323 | struct meson_clk *priv = dev_get_priv(clk->dev); | |
324 | ||
325 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x20090496); | |
326 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x30090496); | |
327 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL1, 0x00000000); | |
328 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL2, 0x00001100); | |
329 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL3, 0x10058e00); | |
330 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL4, 0x000100c0); | |
331 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL5, 0x68000048); | |
332 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL5, 0x68000068); | |
333 | udelay(20); | |
334 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL4, 0x008100c0); | |
335 | udelay(10); | |
336 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x34090496); | |
337 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL0, 0x14090496); | |
338 | udelay(10); | |
339 | regmap_write(priv->map, HHI_PCIE_PLL_CNTL2, 0x00001000); | |
340 | regmap_update_bits(priv->map, HHI_PCIE_PLL_CNTL0, | |
341 | 0x1f << 16, 9 << 16); | |
342 | ||
343 | return 100000000; | |
344 | } | |
345 | ||
346 | static ulong meson_clk_set_rate_by_id(struct clk *clk, unsigned long id, | |
347 | ulong rate, ulong current_rate) | |
348 | { | |
349 | if (current_rate == rate) | |
350 | return 0; | |
351 | ||
352 | switch (id) { | |
353 | /* Fixed clocks */ | |
354 | case CLKID_PCIE_PLL: | |
355 | return meson_pcie_pll_set_rate(clk, rate); | |
356 | ||
357 | default: | |
358 | return -ENOENT; | |
359 | } | |
360 | ||
361 | return -EINVAL; | |
362 | } | |
363 | ||
364 | ||
365 | static ulong meson_clk_set_rate(struct clk *clk, ulong rate) | |
366 | { | |
367 | ulong current_rate = meson_clk_get_rate_by_id(clk, clk->id); | |
368 | int ret; | |
369 | ||
370 | if (IS_ERR_VALUE(current_rate)) | |
371 | return current_rate; | |
372 | ||
373 | debug("%s: setting rate of %ld from %ld to %ld\n", | |
374 | __func__, clk->id, current_rate, rate); | |
375 | ||
376 | ret = meson_clk_set_rate_by_id(clk, clk->id, rate, current_rate); | |
377 | if (IS_ERR_VALUE(ret)) | |
378 | return ret; | |
379 | ||
380 | debug("clock %lu has new rate %lu\n", clk->id, | |
381 | meson_clk_get_rate_by_id(clk, clk->id)); | |
382 | ||
383 | return 0; | |
384 | } | |
385 | ||
912af842 JB |
386 | static int meson_clk_probe(struct udevice *dev) |
387 | { | |
388 | struct meson_clk *priv = dev_get_priv(dev); | |
389 | ||
390 | priv->map = syscon_node_to_regmap(dev_get_parent(dev)->node); | |
391 | if (IS_ERR(priv->map)) | |
392 | return PTR_ERR(priv->map); | |
393 | ||
394 | debug("meson-clk-g12a: probed\n"); | |
395 | ||
396 | return 0; | |
397 | } | |
398 | ||
399 | static struct clk_ops meson_clk_ops = { | |
400 | .disable = meson_clk_disable, | |
401 | .enable = meson_clk_enable, | |
402 | .get_rate = meson_clk_get_rate, | |
08e09c26 | 403 | .set_rate = meson_clk_set_rate, |
912af842 JB |
404 | }; |
405 | ||
406 | static const struct udevice_id meson_clk_ids[] = { | |
407 | { .compatible = "amlogic,g12a-clkc" }, | |
d0e8c4ad | 408 | { .compatible = "amlogic,g12b-clkc" }, |
912af842 JB |
409 | { } |
410 | }; | |
411 | ||
412 | U_BOOT_DRIVER(meson_clk_g12a) = { | |
413 | .name = "meson_clk_g12a", | |
414 | .id = UCLASS_CLK, | |
415 | .of_match = meson_clk_ids, | |
416 | .priv_auto_alloc_size = sizeof(struct meson_clk), | |
417 | .ops = &meson_clk_ops, | |
418 | .probe = meson_clk_probe, | |
419 | }; |