]>
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 |
f6475e29 | 49 | static struct clk_hw_onecell_data *clk_data; |
18882ac5 AB |
50 | static DEFINE_SPINLOCK(lock); |
51 | static void __iomem *gbase; | |
52 | static void __iomem *cpupll_base; | |
53 | ||
54 | enum { | |
55 | REFCLK, | |
56 | SYSPLL, CPUPLL, | |
57 | AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4, | |
58 | AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8, | |
59 | }; | |
60 | ||
61 | static const char *clk_names[] = { | |
62 | [REFCLK] = "refclk", | |
63 | [SYSPLL] = "syspll", | |
64 | [CPUPLL] = "cpupll", | |
65 | [AVPLL_B1] = "avpll_b1", | |
66 | [AVPLL_B2] = "avpll_b2", | |
67 | [AVPLL_B3] = "avpll_b3", | |
68 | [AVPLL_B4] = "avpll_b4", | |
69 | [AVPLL_B5] = "avpll_b5", | |
70 | [AVPLL_B6] = "avpll_b6", | |
71 | [AVPLL_B7] = "avpll_b7", | |
72 | [AVPLL_B8] = "avpll_b8", | |
73 | }; | |
74 | ||
75 | static const struct berlin2_pll_map bg2q_pll_map __initconst = { | |
76 | .vcodiv = {1, 0, 2, 0, 3, 4, 0, 6, 8}, | |
77 | .mult = 1, | |
78 | .fbdiv_shift = 7, | |
79 | .rfdiv_shift = 2, | |
80 | .divsel_shift = 9, | |
81 | }; | |
82 | ||
83 | static const u8 default_parent_ids[] = { | |
84 | SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL | |
85 | }; | |
86 | ||
87 | static const struct berlin2_div_data bg2q_divs[] __initconst = { | |
88 | { | |
89 | .name = "sys", | |
90 | .parent_ids = default_parent_ids, | |
91 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
92 | .map = { | |
93 | BERLIN2_DIV_GATE(REG_CLKENABLE, 0), | |
94 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0), | |
95 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3), | |
96 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3), | |
97 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4), | |
98 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5), | |
99 | }, | |
100 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
101 | .flags = CLK_IGNORE_UNUSED, | |
102 | }, | |
103 | { | |
104 | .name = "drmfigo", | |
105 | .parent_ids = default_parent_ids, | |
106 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
107 | .map = { | |
108 | BERLIN2_DIV_GATE(REG_CLKENABLE, 17), | |
109 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6), | |
110 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9), | |
111 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6), | |
112 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7), | |
113 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8), | |
114 | }, | |
115 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
116 | .flags = 0, | |
117 | }, | |
118 | { | |
119 | .name = "cfg", | |
120 | .parent_ids = default_parent_ids, | |
121 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
122 | .map = { | |
123 | BERLIN2_DIV_GATE(REG_CLKENABLE, 1), | |
124 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 12), | |
125 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 15), | |
126 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 9), | |
127 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 10), | |
128 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 11), | |
129 | }, | |
130 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
131 | .flags = 0, | |
132 | }, | |
133 | { | |
134 | .name = "gfx2d", | |
135 | .parent_ids = default_parent_ids, | |
136 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
137 | .map = { | |
138 | BERLIN2_DIV_GATE(REG_CLKENABLE, 4), | |
139 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 18), | |
140 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 21), | |
141 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12), | |
142 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13), | |
143 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14), | |
144 | }, | |
145 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
146 | .flags = 0, | |
147 | }, | |
148 | { | |
149 | .name = "zsp", | |
150 | .parent_ids = default_parent_ids, | |
151 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
152 | .map = { | |
153 | BERLIN2_DIV_GATE(REG_CLKENABLE, 6), | |
154 | BERLIN2_PLL_SELECT(REG_CLKSELECT0, 24), | |
155 | BERLIN2_DIV_SELECT(REG_CLKSELECT0, 27), | |
156 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15), | |
157 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16), | |
158 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17), | |
159 | }, | |
160 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
161 | .flags = 0, | |
162 | }, | |
163 | { | |
164 | .name = "perif", | |
165 | .parent_ids = default_parent_ids, | |
166 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
167 | .map = { | |
168 | BERLIN2_DIV_GATE(REG_CLKENABLE, 7), | |
169 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 0), | |
170 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 3), | |
171 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18), | |
172 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19), | |
173 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20), | |
174 | }, | |
175 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
176 | .flags = CLK_IGNORE_UNUSED, | |
177 | }, | |
178 | { | |
179 | .name = "pcube", | |
180 | .parent_ids = default_parent_ids, | |
181 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
182 | .map = { | |
183 | BERLIN2_DIV_GATE(REG_CLKENABLE, 2), | |
184 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 6), | |
185 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 9), | |
186 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21), | |
187 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22), | |
188 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23), | |
189 | }, | |
190 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
191 | .flags = 0, | |
192 | }, | |
193 | { | |
194 | .name = "vscope", | |
195 | .parent_ids = default_parent_ids, | |
196 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
197 | .map = { | |
198 | BERLIN2_DIV_GATE(REG_CLKENABLE, 3), | |
199 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 12), | |
200 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 15), | |
201 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24), | |
202 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25), | |
203 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26), | |
204 | }, | |
205 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
206 | .flags = 0, | |
207 | }, | |
208 | { | |
209 | .name = "nfc_ecc", | |
210 | .parent_ids = default_parent_ids, | |
211 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
212 | .map = { | |
213 | BERLIN2_DIV_GATE(REG_CLKENABLE, 19), | |
214 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 18), | |
215 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 21), | |
216 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27), | |
217 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28), | |
218 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29), | |
219 | }, | |
220 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
221 | .flags = 0, | |
222 | }, | |
223 | { | |
224 | .name = "vpp", | |
225 | .parent_ids = default_parent_ids, | |
226 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
227 | .map = { | |
228 | BERLIN2_DIV_GATE(REG_CLKENABLE, 21), | |
229 | BERLIN2_PLL_SELECT(REG_CLKSELECT1, 24), | |
230 | BERLIN2_DIV_SELECT(REG_CLKSELECT1, 27), | |
231 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30), | |
232 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31), | |
233 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0), | |
234 | }, | |
235 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
236 | .flags = 0, | |
237 | }, | |
238 | { | |
239 | .name = "app", | |
240 | .parent_ids = default_parent_ids, | |
241 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
242 | .map = { | |
243 | BERLIN2_DIV_GATE(REG_CLKENABLE, 20), | |
244 | BERLIN2_PLL_SELECT(REG_CLKSELECT2, 0), | |
245 | BERLIN2_DIV_SELECT(REG_CLKSELECT2, 3), | |
246 | BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1), | |
247 | BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2), | |
248 | BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3), | |
249 | }, | |
250 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
251 | .flags = 0, | |
252 | }, | |
253 | { | |
254 | .name = "sdio0xin", | |
255 | .parent_ids = default_parent_ids, | |
256 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
257 | .map = { | |
258 | BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL), | |
259 | }, | |
260 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
261 | .flags = 0, | |
262 | }, | |
263 | { | |
264 | .name = "sdio1xin", | |
265 | .parent_ids = default_parent_ids, | |
266 | .num_parents = ARRAY_SIZE(default_parent_ids), | |
267 | .map = { | |
268 | BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL), | |
269 | }, | |
270 | .div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX, | |
271 | .flags = 0, | |
272 | }, | |
273 | }; | |
274 | ||
275 | static const struct berlin2_gate_data bg2q_gates[] __initconst = { | |
276 | { "gfx2daxi", "perif", 5 }, | |
277 | { "geth0", "perif", 8 }, | |
278 | { "sata", "perif", 9 }, | |
279 | { "ahbapb", "perif", 10, CLK_IGNORE_UNUSED }, | |
280 | { "usb0", "perif", 11 }, | |
281 | { "usb1", "perif", 12 }, | |
282 | { "usb2", "perif", 13 }, | |
283 | { "usb3", "perif", 14 }, | |
284 | { "pbridge", "perif", 15, CLK_IGNORE_UNUSED }, | |
123796bb | 285 | { "sdio", "perif", 16 }, |
18882ac5 | 286 | { "nfc", "perif", 18 }, |
18882ac5 AB |
287 | { "pcie", "perif", 22 }, |
288 | }; | |
289 | ||
290 | static void __init berlin2q_clock_setup(struct device_node *np) | |
291 | { | |
26b3b6b9 | 292 | struct device_node *parent_np = of_get_parent(np); |
18882ac5 AB |
293 | const char *parent_names[9]; |
294 | struct clk *clk; | |
f6475e29 SB |
295 | struct clk_hw **hws; |
296 | int n, ret; | |
297 | ||
298 | clk_data = kzalloc(sizeof(*clk_data) + | |
299 | sizeof(*clk_data->hws) * MAX_CLKS, GFP_KERNEL); | |
300 | if (!clk_data) | |
301 | return; | |
302 | clk_data->num = MAX_CLKS; | |
303 | hws = clk_data->hws; | |
18882ac5 | 304 | |
fd26031b | 305 | gbase = of_iomap(parent_np, 0); |
18882ac5 AB |
306 | if (!gbase) { |
307 | pr_err("%s: Unable to map global base\n", np->full_name); | |
308 | return; | |
309 | } | |
310 | ||
311 | /* BG2Q CPU PLL is not part of global registers */ | |
fd26031b | 312 | cpupll_base = of_iomap(parent_np, 1); |
18882ac5 AB |
313 | if (!cpupll_base) { |
314 | pr_err("%s: Unable to map cpupll base\n", np->full_name); | |
315 | iounmap(gbase); | |
316 | return; | |
317 | } | |
318 | ||
319 | /* overwrite default clock names with DT provided ones */ | |
320 | clk = of_clk_get_by_name(np, clk_names[REFCLK]); | |
321 | if (!IS_ERR(clk)) { | |
322 | clk_names[REFCLK] = __clk_get_name(clk); | |
323 | clk_put(clk); | |
324 | } | |
325 | ||
326 | /* simple register PLLs */ | |
f6475e29 | 327 | ret = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0, |
18882ac5 | 328 | clk_names[SYSPLL], clk_names[REFCLK], 0); |
f6475e29 | 329 | if (ret) |
18882ac5 AB |
330 | goto bg2q_fail; |
331 | ||
f6475e29 | 332 | ret = berlin2_pll_register(&bg2q_pll_map, cpupll_base, |
18882ac5 | 333 | clk_names[CPUPLL], clk_names[REFCLK], 0); |
f6475e29 | 334 | if (ret) |
18882ac5 AB |
335 | goto bg2q_fail; |
336 | ||
337 | /* TODO: add BG2Q AVPLL */ | |
338 | ||
339 | /* | |
340 | * TODO: add reference clock bypass switches: | |
341 | * memPLLSWBypass, cpuPLLSWBypass, and sysPLLSWBypass | |
342 | */ | |
343 | ||
344 | /* clock divider cells */ | |
345 | for (n = 0; n < ARRAY_SIZE(bg2q_divs); n++) { | |
346 | const struct berlin2_div_data *dd = &bg2q_divs[n]; | |
347 | int k; | |
348 | ||
349 | for (k = 0; k < dd->num_parents; k++) | |
350 | parent_names[k] = clk_names[dd->parent_ids[k]]; | |
351 | ||
f6475e29 | 352 | hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase, |
18882ac5 AB |
353 | dd->name, dd->div_flags, parent_names, |
354 | dd->num_parents, dd->flags, &lock); | |
355 | } | |
356 | ||
357 | /* clock gate cells */ | |
358 | for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) { | |
359 | const struct berlin2_gate_data *gd = &bg2q_gates[n]; | |
360 | ||
f6475e29 | 361 | hws[CLKID_GFX2DAXI + n] = clk_hw_register_gate(NULL, gd->name, |
18882ac5 AB |
362 | gd->parent_name, gd->flags, gbase + REG_CLKENABLE, |
363 | gd->bit_idx, 0, &lock); | |
364 | } | |
365 | ||
515f1a20 | 366 | /* cpuclk divider is fixed to 1 */ |
f6475e29 SB |
367 | hws[CLKID_CPU] = |
368 | clk_hw_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL], | |
515f1a20 AT |
369 | 0, 1, 1); |
370 | /* twdclk is derived from cpu/3 */ | |
f6475e29 SB |
371 | hws[CLKID_TWD] = |
372 | clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3); | |
18882ac5 AB |
373 | |
374 | /* check for errors on leaf clocks */ | |
375 | for (n = 0; n < MAX_CLKS; n++) { | |
f6475e29 | 376 | if (!IS_ERR(hws[n])) |
18882ac5 AB |
377 | continue; |
378 | ||
379 | pr_err("%s: Unable to register leaf clock %d\n", | |
380 | np->full_name, n); | |
381 | goto bg2q_fail; | |
382 | } | |
383 | ||
384 | /* register clk-provider */ | |
3ca0b51d | 385 | of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); |
18882ac5 AB |
386 | |
387 | return; | |
388 | ||
389 | bg2q_fail: | |
390 | iounmap(cpupll_base); | |
391 | iounmap(gbase); | |
392 | } | |
26b3b6b9 AT |
393 | CLK_OF_DECLARE(berlin2q_clk, "marvell,berlin2q-clk", |
394 | berlin2q_clock_setup); |