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