Commit | Line | Data |
---|---|---|
18882ac5 AB |
1 | /* |
2 | * Copyright (c) 2014 Marvell Technology Group Ltd. | |
3 | * | |
4 | * Alexandre Belloni <alexandre.belloni@free-electrons.com> | |
5 | * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms and conditions of the GNU General Public License, | |
9 | * version 2, as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along with | |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include <linux/clk.h> | |
21 | #include <linux/clk-provider.h> | |
22 | #include <linux/kernel.h> | |
23 | #include <linux/of.h> | |
24 | #include <linux/of_address.h> | |
25 | #include <linux/slab.h> | |
26 | ||
27 | #include <dt-bindings/clock/berlin2q.h> | |
28 | ||
29 | #include "berlin2-div.h" | |
30 | #include "berlin2-pll.h" | |
31 | #include "common.h" | |
32 | ||
33 | #define REG_PINMUX0 0x0018 | |
34 | #define REG_PINMUX5 0x002c | |
35 | #define REG_SYSPLLCTL0 0x0030 | |
36 | #define REG_SYSPLLCTL4 0x0040 | |
37 | #define REG_CLKENABLE 0x00e8 | |
38 | #define REG_CLKSELECT0 0x00ec | |
39 | #define REG_CLKSELECT1 0x00f0 | |
40 | #define REG_CLKSELECT2 0x00f4 | |
41 | #define REG_CLKSWITCH0 0x00f8 | |
42 | #define REG_CLKSWITCH1 0x00fc | |
43 | #define REG_SW_GENERIC0 0x0110 | |
44 | #define REG_SW_GENERIC3 0x011c | |
45 | #define REG_SDIO0XIN_CLKCTL 0x0158 | |
46 | #define REG_SDIO1XIN_CLKCTL 0x015c | |
47 | ||
515f1a20 | 48 | #define MAX_CLKS 28 |
18882ac5 AB |
49 | static struct clk *clks[MAX_CLKS]; |
50 | static struct clk_onecell_data clk_data; | |
51 | static DEFINE_SPINLOCK(lock); | |
52 | static void __iomem *gbase; | |
53 | static void __iomem *cpupll_base; | |
54 | ||
55 | enum { | |
56 | REFCLK, | |
57 | SYSPLL, CPUPLL, | |
58 | AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4, | |
59 | AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8, | |
60 | }; | |
61 | ||
62 | static const char *clk_names[] = { | |
63 | [REFCLK] = "refclk", | |
64 | [SYSPLL] = "syspll", | |
65 | [CPUPLL] = "cpupll", | |
66 | [AVPLL_B1] = "avpll_b1", | |
67 | [AVPLL_B2] = "avpll_b2", | |
68 | [AVPLL_B3] = "avpll_b3", | |
69 | [AVPLL_B4] = "avpll_b4", | |
70 | [AVPLL_B5] = "avpll_b5", | |
71 | [AVPLL_B6] = "avpll_b6", | |
72 | [AVPLL_B7] = "avpll_b7", | |
73 | [AVPLL_B8] = "avpll_b8", | |
74 | }; | |
75 | ||
76 | static const struct berlin2_pll_map bg2q_pll_map __initconst = { | |
77 | .vcodiv = {1, 0, 2, 0, 3, 4, 0, 6, 8}, | |
78 | .mult = 1, | |
79 | .fbdiv_shift = 7, | |
80 | .rfdiv_shift = 2, | |
81 | .divsel_shift = 9, | |
82 | }; | |
83 | ||
84 | static const u8 default_parent_ids[] = { | |
85 | SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL | |
86 | }; | |
87 | ||
88 | static const struct berlin2_div_data bg2q_divs[] __initconst = { | |
89 | { | |
90 | .name = "sys", | |
91 | .parent_ids = default_parent_ids, | |
92 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
93 | .map = { | |
94 | BERLIN2_DIV_GATE(REG_CLKENABLE, 0), | |
95 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0), | |
96 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3), | |
97 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3), | |
98 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4), | |
99 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5), | |
100 | }, | |
101 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
102 | .flags = CLK_IGNORE_UNUSED, | |
103 | }, | |
104 | { | |
105 | .name = "drmfigo", | |
106 | .parent_ids = default_parent_ids, | |
107 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
108 | .map = { | |
109 | BERLIN2_DIV_GATE(REG_CLKENABLE, 17), | |
110 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6), | |
111 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9), | |
112 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6), | |
113 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7), | |
114 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8), | |
115 | }, | |
116 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
117 | .flags = 0, | |
118 | }, | |
119 | { | |
120 | .name = "cfg", | |
121 | .parent_ids = default_parent_ids, | |
122 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
123 | .map = { | |
124 | BERLIN2_DIV_GATE(REG_CLKENABLE, 1), | |
125 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 12), | |
126 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 15), | |
127 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 9), | |
128 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 10), | |
129 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 11), | |
130 | }, | |
131 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
132 | .flags = 0, | |
133 | }, | |
134 | { | |
135 | .name = "gfx2d", | |
136 | .parent_ids = default_parent_ids, | |
137 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
138 | .map = { | |
139 | BERLIN2_DIV_GATE(REG_CLKENABLE, 4), | |
140 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 18), | |
141 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 21), | |
142 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12), | |
143 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13), | |
144 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14), | |
145 | }, | |
146 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
147 | .flags = 0, | |
148 | }, | |
149 | { | |
150 | .name = "zsp", | |
151 | .parent_ids = default_parent_ids, | |
152 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
153 | .map = { | |
154 | BERLIN2_DIV_GATE(REG_CLKENABLE, 6), | |
155 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 24), | |
156 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 27), | |
157 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15), | |
158 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16), | |
159 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17), | |
160 | }, | |
161 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
162 | .flags = 0, | |
163 | }, | |
164 | { | |
165 | .name = "perif", | |
166 | .parent_ids = default_parent_ids, | |
167 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
168 | .map = { | |
169 | BERLIN2_DIV_GATE(REG_CLKENABLE, 7), | |
170 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 0), | |
171 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 3), | |
172 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18), | |
173 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19), | |
174 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20), | |
175 | }, | |
176 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
177 | .flags = CLK_IGNORE_UNUSED, | |
178 | }, | |
179 | { | |
180 | .name = "pcube", | |
181 | .parent_ids = default_parent_ids, | |
182 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
183 | .map = { | |
184 | BERLIN2_DIV_GATE(REG_CLKENABLE, 2), | |
185 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 6), | |
186 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 9), | |
187 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21), | |
188 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22), | |
189 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23), | |
190 | }, | |
191 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
192 | .flags = 0, | |
193 | }, | |
194 | { | |
195 | .name = "vscope", | |
196 | .parent_ids = default_parent_ids, | |
197 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
198 | .map = { | |
199 | BERLIN2_DIV_GATE(REG_CLKENABLE, 3), | |
200 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 12), | |
201 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 15), | |
202 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24), | |
203 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25), | |
204 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26), | |
205 | }, | |
206 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
207 | .flags = 0, | |
208 | }, | |
209 | { | |
210 | .name = "nfc_ecc", | |
211 | .parent_ids = default_parent_ids, | |
212 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
213 | .map = { | |
214 | BERLIN2_DIV_GATE(REG_CLKENABLE, 19), | |
215 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 18), | |
216 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 21), | |
217 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27), | |
218 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28), | |
219 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29), | |
220 | }, | |
221 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
222 | .flags = 0, | |
223 | }, | |
224 | { | |
225 | .name = "vpp", | |
226 | .parent_ids = default_parent_ids, | |
227 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
228 | .map = { | |
229 | BERLIN2_DIV_GATE(REG_CLKENABLE, 21), | |
230 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 24), | |
231 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 27), | |
232 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30), | |
233 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31), | |
234 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0), | |
235 | }, | |
236 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
237 | .flags = 0, | |
238 | }, | |
239 | { | |
240 | .name = "app", | |
241 | .parent_ids = default_parent_ids, | |
242 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
243 | .map = { | |
244 | BERLIN2_DIV_GATE(REG_CLKENABLE, 20), | |
245 | BERLIN2_PLL_SELECT(REG_CLKSELECT2, 0), | |
246 | BERLIN2_DIV_SELECT(REG_CLKSELECT2, 3), | |
247 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1), | |
248 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2), | |
249 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3), | |
250 | }, | |
251 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
252 | .flags = 0, | |
253 | }, | |
254 | { | |
255 | .name = "sdio0xin", | |
256 | .parent_ids = default_parent_ids, | |
257 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
258 | .map = { | |
259 | BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL), | |
260 | }, | |
261 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
262 | .flags = 0, | |
263 | }, | |
264 | { | |
265 | .name = "sdio1xin", | |
266 | .parent_ids = default_parent_ids, | |
267 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
268 | .map = { | |
269 | BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL), | |
270 | }, | |
271 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
272 | .flags = 0, | |
273 | }, | |
274 | }; | |
275 | ||
276 | static const struct berlin2_gate_data bg2q_gates[] __initconst = { | |
277 | { "gfx2daxi", "perif", 5 }, | |
278 | { "geth0", "perif", 8 }, | |
279 | { "sata", "perif", 9 }, | |
280 | { "ahbapb", "perif", 10, CLK_IGNORE_UNUSED }, | |
281 | { "usb0", "perif", 11 }, | |
282 | { "usb1", "perif", 12 }, | |
283 | { "usb2", "perif", 13 }, | |
284 | { "usb3", "perif", 14 }, | |
285 | { "pbridge", "perif", 15, CLK_IGNORE_UNUSED }, | |
123796bb | 286 | { "sdio", "perif", 16 }, |
18882ac5 | 287 | { "nfc", "perif", 18 }, |
18882ac5 AB |
288 | { "pcie", "perif", 22 }, |
289 | }; | |
290 | ||
291 | static void __init berlin2q_clock_setup(struct device_node *np) | |
292 | { | |
26b3b6b9 | 293 | struct device_node *parent_np = of_get_parent(np); |
18882ac5 AB |
294 | const char *parent_names[9]; |
295 | struct clk *clk; | |
296 | int n; | |
297 | ||
fd26031b | 298 | gbase = of_iomap(parent_np, 0); |
18882ac5 AB |
299 | if (!gbase) { |
300 | pr_err("%s: Unable to map global base\n", np->full_name); | |
301 | return; | |
302 | } | |
303 | ||
304 | /* BG2Q CPU PLL is not part of global registers */ | |
fd26031b | 305 | cpupll_base = of_iomap(parent_np, 1); |
18882ac5 AB |
306 | if (!cpupll_base) { |
307 | pr_err("%s: Unable to map cpupll base\n", np->full_name); | |
308 | iounmap(gbase); | |
309 | return; | |
310 | } | |
311 | ||
312 | /* overwrite default clock names with DT provided ones */ | |
313 | clk = of_clk_get_by_name(np, clk_names[REFCLK]); | |
314 | if (!IS_ERR(clk)) { | |
315 | clk_names[REFCLK] = __clk_get_name(clk); | |
316 | clk_put(clk); | |
317 | } | |
318 | ||
319 | /* simple register PLLs */ | |
320 | clk = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0, | |
321 | clk_names[SYSPLL], clk_names[REFCLK], 0); | |
322 | if (IS_ERR(clk)) | |
323 | goto bg2q_fail; | |
324 | ||
325 | clk = berlin2_pll_register(&bg2q_pll_map, cpupll_base, | |
326 | clk_names[CPUPLL], clk_names[REFCLK], 0); | |
327 | if (IS_ERR(clk)) | |
328 | goto bg2q_fail; | |
329 | ||
330 | /* TODO: add BG2Q AVPLL */ | |
331 | ||
332 | /* | |
333 | * TODO: add reference clock bypass switches: | |
334 | * memPLLSWBypass, cpuPLLSWBypass, and sysPLLSWBypass | |
335 | */ | |
336 | ||
337 | /* clock divider cells */ | |
338 | for (n = 0; n < ARRAY_SIZE(bg2q_divs); n++) { | |
339 | const struct berlin2_div_data *dd = &bg2q_divs[n]; | |
340 | int k; | |
341 | ||
342 | for (k = 0; k < dd->num_parents; k++) | |
343 | parent_names[k] = clk_names[dd->parent_ids[k]]; | |
344 | ||
345 | clks[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase, | |
346 | dd->name, dd->div_flags, parent_names, | |
347 | dd->num_parents, dd->flags, &lock); | |
348 | } | |
349 | ||
350 | /* clock gate cells */ | |
351 | for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) { | |
352 | const struct berlin2_gate_data *gd = &bg2q_gates[n]; | |
353 | ||
354 | clks[CLKID_GFX2DAXI + n] = clk_register_gate(NULL, gd->name, | |
355 | gd->parent_name, gd->flags, gbase + REG_CLKENABLE, | |
356 | gd->bit_idx, 0, &lock); | |
357 | } | |
358 | ||
515f1a20 AT |
359 | /* cpuclk divider is fixed to 1 */ |
360 | clks[CLKID_CPU] = | |
361 | clk_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL], | |
362 | 0, 1, 1); | |
363 | /* twdclk is derived from cpu/3 */ | |
18882ac5 | 364 | clks[CLKID_TWD] = |
515f1a20 | 365 | clk_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3); |
18882ac5 AB |
366 | |
367 | /* check for errors on leaf clocks */ | |
368 | for (n = 0; n < MAX_CLKS; n++) { | |
369 | if (!IS_ERR(clks[n])) | |
370 | continue; | |
371 | ||
372 | pr_err("%s: Unable to register leaf clock %d\n", | |
373 | np->full_name, n); | |
374 | goto bg2q_fail; | |
375 | } | |
376 | ||
377 | /* register clk-provider */ | |
378 | clk_data.clks = clks; | |
379 | clk_data.clk_num = MAX_CLKS; | |
380 | of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); | |
381 | ||
382 | return; | |
383 | ||
384 | bg2q_fail: | |
385 | iounmap(cpupll_base); | |
386 | iounmap(gbase); | |
387 | } | |
26b3b6b9 AT |
388 | CLK_OF_DECLARE(berlin2q_clk, "marvell,berlin2q-clk", |
389 | berlin2q_clock_setup); |