1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * clk-flexgen.c |
4 | * |
5 | * Copyright (C) ST-Microelectronics SA 2013 |
6 | * Author: Maxime Coquelin <maxime.coquelin@st.com> for ST-Microelectronics. |
7 | */ |
8 | |
9 | #include <linux/clk.h> |
10 | #include <linux/clk-provider.h> |
11 | #include <linux/module.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/io.h> |
14 | #include <linux/err.h> |
15 | #include <linux/string.h> |
16 | #include <linux/of.h> |
17 | #include <linux/of_address.h> |
18 | |
19 | struct clkgen_clk_out { |
20 | const char *name; |
21 | unsigned long flags; |
22 | }; |
23 | |
24 | struct clkgen_data { |
25 | unsigned long flags; |
26 | bool mode; |
27 | const struct clkgen_clk_out *outputs; |
28 | const unsigned int outputs_nb; |
29 | }; |
30 | |
31 | struct flexgen { |
32 | struct clk_hw hw; |
33 | |
34 | /* Crossbar */ |
35 | struct clk_mux mux; |
36 | /* Pre-divisor's gate */ |
37 | struct clk_gate pgate; |
38 | /* Pre-divisor */ |
39 | struct clk_divider pdiv; |
40 | /* Final divisor's gate */ |
41 | struct clk_gate fgate; |
42 | /* Final divisor */ |
43 | struct clk_divider fdiv; |
44 | /* Asynchronous mode control */ |
45 | struct clk_gate sync; |
46 | /* hw control flags */ |
47 | bool control_mode; |
48 | }; |
49 | |
50 | #define to_flexgen(_hw) container_of(_hw, struct flexgen, hw) |
51 | #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) |
52 | |
53 | static int flexgen_enable(struct clk_hw *hw) |
54 | { |
55 | struct flexgen *flexgen = to_flexgen(hw); |
56 | struct clk_hw *pgate_hw = &flexgen->pgate.hw; |
57 | struct clk_hw *fgate_hw = &flexgen->fgate.hw; |
58 | |
59 | __clk_hw_set_clk(dst: pgate_hw, src: hw); |
60 | __clk_hw_set_clk(dst: fgate_hw, src: hw); |
61 | |
62 | clk_gate_ops.enable(pgate_hw); |
63 | |
64 | clk_gate_ops.enable(fgate_hw); |
65 | |
66 | pr_debug("%s: flexgen output enabled\n" , clk_hw_get_name(hw)); |
67 | return 0; |
68 | } |
69 | |
70 | static void flexgen_disable(struct clk_hw *hw) |
71 | { |
72 | struct flexgen *flexgen = to_flexgen(hw); |
73 | struct clk_hw *fgate_hw = &flexgen->fgate.hw; |
74 | |
75 | /* disable only the final gate */ |
76 | __clk_hw_set_clk(dst: fgate_hw, src: hw); |
77 | |
78 | clk_gate_ops.disable(fgate_hw); |
79 | |
80 | pr_debug("%s: flexgen output disabled\n" , clk_hw_get_name(hw)); |
81 | } |
82 | |
83 | static int flexgen_is_enabled(struct clk_hw *hw) |
84 | { |
85 | struct flexgen *flexgen = to_flexgen(hw); |
86 | struct clk_hw *fgate_hw = &flexgen->fgate.hw; |
87 | |
88 | __clk_hw_set_clk(dst: fgate_hw, src: hw); |
89 | |
90 | if (!clk_gate_ops.is_enabled(fgate_hw)) |
91 | return 0; |
92 | |
93 | return 1; |
94 | } |
95 | |
96 | static u8 flexgen_get_parent(struct clk_hw *hw) |
97 | { |
98 | struct flexgen *flexgen = to_flexgen(hw); |
99 | struct clk_hw *mux_hw = &flexgen->mux.hw; |
100 | |
101 | __clk_hw_set_clk(dst: mux_hw, src: hw); |
102 | |
103 | return clk_mux_ops.get_parent(mux_hw); |
104 | } |
105 | |
106 | static int flexgen_set_parent(struct clk_hw *hw, u8 index) |
107 | { |
108 | struct flexgen *flexgen = to_flexgen(hw); |
109 | struct clk_hw *mux_hw = &flexgen->mux.hw; |
110 | |
111 | __clk_hw_set_clk(dst: mux_hw, src: hw); |
112 | |
113 | return clk_mux_ops.set_parent(mux_hw, index); |
114 | } |
115 | |
116 | static inline unsigned long |
117 | clk_best_div(unsigned long parent_rate, unsigned long rate) |
118 | { |
119 | return parent_rate / rate + ((rate > (2*(parent_rate % rate))) ? 0 : 1); |
120 | } |
121 | |
122 | static int flexgen_determine_rate(struct clk_hw *hw, |
123 | struct clk_rate_request *req) |
124 | { |
125 | unsigned long div; |
126 | |
127 | /* Round div according to exact prate and wished rate */ |
128 | div = clk_best_div(parent_rate: req->best_parent_rate, rate: req->rate); |
129 | |
130 | if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { |
131 | req->best_parent_rate = req->rate * div; |
132 | return 0; |
133 | } |
134 | |
135 | req->rate = req->best_parent_rate / div; |
136 | return 0; |
137 | } |
138 | |
139 | static unsigned long flexgen_recalc_rate(struct clk_hw *hw, |
140 | unsigned long parent_rate) |
141 | { |
142 | struct flexgen *flexgen = to_flexgen(hw); |
143 | struct clk_hw *pdiv_hw = &flexgen->pdiv.hw; |
144 | struct clk_hw *fdiv_hw = &flexgen->fdiv.hw; |
145 | unsigned long mid_rate; |
146 | |
147 | __clk_hw_set_clk(dst: pdiv_hw, src: hw); |
148 | __clk_hw_set_clk(dst: fdiv_hw, src: hw); |
149 | |
150 | mid_rate = clk_divider_ops.recalc_rate(pdiv_hw, parent_rate); |
151 | |
152 | return clk_divider_ops.recalc_rate(fdiv_hw, mid_rate); |
153 | } |
154 | |
155 | static int flexgen_set_rate(struct clk_hw *hw, unsigned long rate, |
156 | unsigned long parent_rate) |
157 | { |
158 | struct flexgen *flexgen = to_flexgen(hw); |
159 | struct clk_hw *pdiv_hw = &flexgen->pdiv.hw; |
160 | struct clk_hw *fdiv_hw = &flexgen->fdiv.hw; |
161 | struct clk_hw *sync_hw = &flexgen->sync.hw; |
162 | struct clk_gate *config = to_clk_gate(sync_hw); |
163 | unsigned long div = 0; |
164 | int ret = 0; |
165 | u32 reg; |
166 | |
167 | __clk_hw_set_clk(dst: pdiv_hw, src: hw); |
168 | __clk_hw_set_clk(dst: fdiv_hw, src: hw); |
169 | |
170 | if (flexgen->control_mode) { |
171 | reg = readl(addr: config->reg); |
172 | reg &= ~BIT(config->bit_idx); |
173 | writel(val: reg, addr: config->reg); |
174 | } |
175 | |
176 | div = clk_best_div(parent_rate, rate); |
177 | |
178 | /* |
179 | * pdiv is mainly targeted for low freq results, while fdiv |
180 | * should be used for div <= 64. The other way round can |
181 | * lead to 'duty cycle' issues. |
182 | */ |
183 | |
184 | if (div <= 64) { |
185 | clk_divider_ops.set_rate(pdiv_hw, parent_rate, parent_rate); |
186 | ret = clk_divider_ops.set_rate(fdiv_hw, rate, rate * div); |
187 | } else { |
188 | clk_divider_ops.set_rate(fdiv_hw, parent_rate, parent_rate); |
189 | ret = clk_divider_ops.set_rate(pdiv_hw, rate, rate * div); |
190 | } |
191 | |
192 | return ret; |
193 | } |
194 | |
195 | static const struct clk_ops flexgen_ops = { |
196 | .enable = flexgen_enable, |
197 | .disable = flexgen_disable, |
198 | .is_enabled = flexgen_is_enabled, |
199 | .get_parent = flexgen_get_parent, |
200 | .set_parent = flexgen_set_parent, |
201 | .determine_rate = flexgen_determine_rate, |
202 | .recalc_rate = flexgen_recalc_rate, |
203 | .set_rate = flexgen_set_rate, |
204 | }; |
205 | |
206 | static struct clk *clk_register_flexgen(const char *name, |
207 | const char **parent_names, u8 num_parents, |
208 | void __iomem *reg, spinlock_t *lock, u32 idx, |
209 | unsigned long flexgen_flags, bool mode) { |
210 | struct flexgen *fgxbar; |
211 | struct clk *clk; |
212 | struct clk_init_data init; |
213 | u32 xbar_shift; |
214 | void __iomem *xbar_reg, *fdiv_reg; |
215 | |
216 | fgxbar = kzalloc(size: sizeof(struct flexgen), GFP_KERNEL); |
217 | if (!fgxbar) |
218 | return ERR_PTR(error: -ENOMEM); |
219 | |
220 | init.name = name; |
221 | init.ops = &flexgen_ops; |
222 | init.flags = CLK_GET_RATE_NOCACHE | flexgen_flags; |
223 | init.parent_names = parent_names; |
224 | init.num_parents = num_parents; |
225 | |
226 | xbar_reg = reg + 0x18 + (idx & ~0x3); |
227 | xbar_shift = (idx % 4) * 0x8; |
228 | fdiv_reg = reg + 0x164 + idx * 4; |
229 | |
230 | /* Crossbar element config */ |
231 | fgxbar->mux.lock = lock; |
232 | fgxbar->mux.mask = BIT(6) - 1; |
233 | fgxbar->mux.reg = xbar_reg; |
234 | fgxbar->mux.shift = xbar_shift; |
235 | fgxbar->mux.table = NULL; |
236 | |
237 | |
238 | /* Pre-divider's gate config (in xbar register)*/ |
239 | fgxbar->pgate.lock = lock; |
240 | fgxbar->pgate.reg = xbar_reg; |
241 | fgxbar->pgate.bit_idx = xbar_shift + 6; |
242 | |
243 | /* Pre-divider config */ |
244 | fgxbar->pdiv.lock = lock; |
245 | fgxbar->pdiv.reg = reg + 0x58 + idx * 4; |
246 | fgxbar->pdiv.width = 10; |
247 | |
248 | /* Final divider's gate config */ |
249 | fgxbar->fgate.lock = lock; |
250 | fgxbar->fgate.reg = fdiv_reg; |
251 | fgxbar->fgate.bit_idx = 6; |
252 | |
253 | /* Final divider config */ |
254 | fgxbar->fdiv.lock = lock; |
255 | fgxbar->fdiv.reg = fdiv_reg; |
256 | fgxbar->fdiv.width = 6; |
257 | |
258 | /* Final divider sync config */ |
259 | fgxbar->sync.lock = lock; |
260 | fgxbar->sync.reg = fdiv_reg; |
261 | fgxbar->sync.bit_idx = 7; |
262 | |
263 | fgxbar->control_mode = mode; |
264 | |
265 | fgxbar->hw.init = &init; |
266 | |
267 | clk = clk_register(NULL, hw: &fgxbar->hw); |
268 | if (IS_ERR(ptr: clk)) |
269 | kfree(objp: fgxbar); |
270 | else |
271 | pr_debug("%s: parent %s rate %u\n" , |
272 | __clk_get_name(clk), |
273 | __clk_get_name(clk_get_parent(clk)), |
274 | (unsigned int)clk_get_rate(clk)); |
275 | return clk; |
276 | } |
277 | |
278 | static const char ** __init flexgen_get_parents(struct device_node *np, |
279 | int *num_parents) |
280 | { |
281 | const char **parents; |
282 | unsigned int nparents; |
283 | |
284 | nparents = of_clk_get_parent_count(np); |
285 | if (WARN_ON(!nparents)) |
286 | return NULL; |
287 | |
288 | parents = kcalloc(n: nparents, size: sizeof(const char *), GFP_KERNEL); |
289 | if (!parents) |
290 | return NULL; |
291 | |
292 | *num_parents = of_clk_parent_fill(np, parents, size: nparents); |
293 | |
294 | return parents; |
295 | } |
296 | |
297 | static const struct clkgen_data clkgen_audio = { |
298 | .flags = CLK_SET_RATE_PARENT, |
299 | }; |
300 | |
301 | static const struct clkgen_data clkgen_video = { |
302 | .flags = CLK_SET_RATE_PARENT, |
303 | .mode = 1, |
304 | }; |
305 | |
306 | static const struct clkgen_clk_out clkgen_stih407_a0_clk_out[] = { |
307 | /* This clk needs to be on so that memory interface is accessible */ |
308 | { .name = "clk-ic-lmi0" , .flags = CLK_IS_CRITICAL }, |
309 | }; |
310 | |
311 | static const struct clkgen_data clkgen_stih407_a0 = { |
312 | .outputs = clkgen_stih407_a0_clk_out, |
313 | .outputs_nb = ARRAY_SIZE(clkgen_stih407_a0_clk_out), |
314 | }; |
315 | |
316 | static const struct clkgen_clk_out clkgen_stih410_a0_clk_out[] = { |
317 | /* Those clks need to be on so that memory interface is accessible */ |
318 | { .name = "clk-ic-lmi0" , .flags = CLK_IS_CRITICAL }, |
319 | { .name = "clk-ic-lmi1" , .flags = CLK_IS_CRITICAL }, |
320 | }; |
321 | |
322 | static const struct clkgen_data clkgen_stih410_a0 = { |
323 | .outputs = clkgen_stih410_a0_clk_out, |
324 | .outputs_nb = ARRAY_SIZE(clkgen_stih410_a0_clk_out), |
325 | }; |
326 | |
327 | static const struct clkgen_clk_out clkgen_stih407_c0_clk_out[] = { |
328 | { .name = "clk-icn-gpu" , }, |
329 | { .name = "clk-fdma" , }, |
330 | { .name = "clk-nand" , }, |
331 | { .name = "clk-hva" , }, |
332 | { .name = "clk-proc-stfe" , }, |
333 | { .name = "clk-proc-tp" , }, |
334 | { .name = "clk-rx-icn-dmu" , }, |
335 | { .name = "clk-rx-icn-hva" , }, |
336 | /* This clk needs to be on to keep bus interconnect alive */ |
337 | { .name = "clk-icn-cpu" , .flags = CLK_IS_CRITICAL }, |
338 | /* This clk needs to be on to keep bus interconnect alive */ |
339 | { .name = "clk-tx-icn-dmu" , .flags = CLK_IS_CRITICAL }, |
340 | { .name = "clk-mmc-0" , }, |
341 | { .name = "clk-mmc-1" , }, |
342 | { .name = "clk-jpegdec" , }, |
343 | /* This clk needs to be on to keep A9 running */ |
344 | { .name = "clk-ext2fa9" , .flags = CLK_IS_CRITICAL }, |
345 | { .name = "clk-ic-bdisp-0" , }, |
346 | { .name = "clk-ic-bdisp-1" , }, |
347 | { .name = "clk-pp-dmu" , }, |
348 | { .name = "clk-vid-dmu" , }, |
349 | { .name = "clk-dss-lpc" , }, |
350 | { .name = "clk-st231-aud-0" , }, |
351 | { .name = "clk-st231-gp-1" , }, |
352 | { .name = "clk-st231-dmu" , }, |
353 | /* This clk needs to be on to keep bus interconnect alive */ |
354 | { .name = "clk-icn-lmi" , .flags = CLK_IS_CRITICAL }, |
355 | { .name = "clk-tx-icn-disp-1" , }, |
356 | /* This clk needs to be on to keep bus interconnect alive */ |
357 | { .name = "clk-icn-sbc" , .flags = CLK_IS_CRITICAL }, |
358 | { .name = "clk-stfe-frc2" , }, |
359 | { .name = "clk-eth-phy" , }, |
360 | { .name = "clk-eth-ref-phyclk" , }, |
361 | { .name = "clk-flash-promip" , }, |
362 | { .name = "clk-main-disp" , }, |
363 | { .name = "clk-aux-disp" , }, |
364 | { .name = "clk-compo-dvp" , }, |
365 | }; |
366 | |
367 | static const struct clkgen_data clkgen_stih407_c0 = { |
368 | .outputs = clkgen_stih407_c0_clk_out, |
369 | .outputs_nb = ARRAY_SIZE(clkgen_stih407_c0_clk_out), |
370 | }; |
371 | |
372 | static const struct clkgen_clk_out clkgen_stih410_c0_clk_out[] = { |
373 | { .name = "clk-icn-gpu" , }, |
374 | { .name = "clk-fdma" , }, |
375 | { .name = "clk-nand" , }, |
376 | { .name = "clk-hva" , }, |
377 | { .name = "clk-proc-stfe" , }, |
378 | { .name = "clk-proc-tp" , }, |
379 | { .name = "clk-rx-icn-dmu" , }, |
380 | { .name = "clk-rx-icn-hva" , }, |
381 | /* This clk needs to be on to keep bus interconnect alive */ |
382 | { .name = "clk-icn-cpu" , .flags = CLK_IS_CRITICAL }, |
383 | /* This clk needs to be on to keep bus interconnect alive */ |
384 | { .name = "clk-tx-icn-dmu" , .flags = CLK_IS_CRITICAL }, |
385 | { .name = "clk-mmc-0" , }, |
386 | { .name = "clk-mmc-1" , }, |
387 | { .name = "clk-jpegdec" , }, |
388 | /* This clk needs to be on to keep A9 running */ |
389 | { .name = "clk-ext2fa9" , .flags = CLK_IS_CRITICAL }, |
390 | { .name = "clk-ic-bdisp-0" , }, |
391 | { .name = "clk-ic-bdisp-1" , }, |
392 | { .name = "clk-pp-dmu" , }, |
393 | { .name = "clk-vid-dmu" , }, |
394 | { .name = "clk-dss-lpc" , }, |
395 | { .name = "clk-st231-aud-0" , }, |
396 | { .name = "clk-st231-gp-1" , }, |
397 | { .name = "clk-st231-dmu" , }, |
398 | /* This clk needs to be on to keep bus interconnect alive */ |
399 | { .name = "clk-icn-lmi" , .flags = CLK_IS_CRITICAL }, |
400 | { .name = "clk-tx-icn-disp-1" , }, |
401 | /* This clk needs to be on to keep bus interconnect alive */ |
402 | { .name = "clk-icn-sbc" , .flags = CLK_IS_CRITICAL }, |
403 | { .name = "clk-stfe-frc2" , }, |
404 | { .name = "clk-eth-phy" , }, |
405 | { .name = "clk-eth-ref-phyclk" , }, |
406 | { .name = "clk-flash-promip" , }, |
407 | { .name = "clk-main-disp" , }, |
408 | { .name = "clk-aux-disp" , }, |
409 | { .name = "clk-compo-dvp" , }, |
410 | { .name = "clk-tx-icn-hades" , }, |
411 | { .name = "clk-rx-icn-hades" , }, |
412 | /* This clk needs to be on to keep bus interconnect alive */ |
413 | { .name = "clk-icn-reg-16" , .flags = CLK_IS_CRITICAL }, |
414 | { .name = "clk-pp-hades" , }, |
415 | { .name = "clk-clust-hades" , }, |
416 | { .name = "clk-hwpe-hades" , }, |
417 | { .name = "clk-fc-hades" , }, |
418 | }; |
419 | |
420 | static const struct clkgen_data clkgen_stih410_c0 = { |
421 | .outputs = clkgen_stih410_c0_clk_out, |
422 | .outputs_nb = ARRAY_SIZE(clkgen_stih410_c0_clk_out), |
423 | }; |
424 | |
425 | static const struct clkgen_clk_out clkgen_stih418_c0_clk_out[] = { |
426 | { .name = "clk-icn-gpu" , }, |
427 | { .name = "clk-fdma" , }, |
428 | { .name = "clk-nand" , }, |
429 | { .name = "clk-hva" , }, |
430 | { .name = "clk-proc-stfe" , }, |
431 | { .name = "clk-tp" , }, |
432 | /* This clk needs to be on to keep bus interconnect alive */ |
433 | { .name = "clk-rx-icn-dmu" , .flags = CLK_IS_CRITICAL }, |
434 | /* This clk needs to be on to keep bus interconnect alive */ |
435 | { .name = "clk-rx-icn-hva" , .flags = CLK_IS_CRITICAL }, |
436 | { .name = "clk-icn-cpu" , .flags = CLK_IS_CRITICAL }, |
437 | /* This clk needs to be on to keep bus interconnect alive */ |
438 | { .name = "clk-tx-icn-dmu" , .flags = CLK_IS_CRITICAL }, |
439 | { .name = "clk-mmc-0" , }, |
440 | { .name = "clk-mmc-1" , }, |
441 | { .name = "clk-jpegdec" , }, |
442 | /* This clk needs to be on to keep bus interconnect alive */ |
443 | { .name = "clk-icn-reg" , .flags = CLK_IS_CRITICAL }, |
444 | { .name = "clk-proc-bdisp-0" , }, |
445 | { .name = "clk-proc-bdisp-1" , }, |
446 | { .name = "clk-pp-dmu" , }, |
447 | { .name = "clk-vid-dmu" , }, |
448 | { .name = "clk-dss-lpc" , }, |
449 | { .name = "clk-st231-aud-0" , }, |
450 | { .name = "clk-st231-gp-1" , }, |
451 | { .name = "clk-st231-dmu" , }, |
452 | /* This clk needs to be on to keep bus interconnect alive */ |
453 | { .name = "clk-icn-lmi" , .flags = CLK_IS_CRITICAL }, |
454 | /* This clk needs to be on to keep bus interconnect alive */ |
455 | { .name = "clk-tx-icn-1" , .flags = CLK_IS_CRITICAL }, |
456 | /* This clk needs to be on to keep bus interconnect alive */ |
457 | { .name = "clk-icn-sbc" , .flags = CLK_IS_CRITICAL }, |
458 | { .name = "clk-stfe-frc2" , }, |
459 | { .name = "clk-eth-phyref" , }, |
460 | { .name = "clk-eth-ref-phyclk" , }, |
461 | { .name = "clk-flash-promip" , }, |
462 | { .name = "clk-main-disp" , }, |
463 | { .name = "clk-aux-disp" , }, |
464 | { .name = "clk-compo-dvp" , }, |
465 | /* This clk needs to be on to keep bus interconnect alive */ |
466 | { .name = "clk-tx-icn-hades" , .flags = CLK_IS_CRITICAL }, |
467 | /* This clk needs to be on to keep bus interconnect alive */ |
468 | { .name = "clk-rx-icn-hades" , .flags = CLK_IS_CRITICAL }, |
469 | /* This clk needs to be on to keep bus interconnect alive */ |
470 | { .name = "clk-icn-reg-16" , .flags = CLK_IS_CRITICAL }, |
471 | { .name = "clk-pp-hevc" , }, |
472 | { .name = "clk-clust-hevc" , }, |
473 | { .name = "clk-hwpe-hevc" , }, |
474 | { .name = "clk-fc-hevc" , }, |
475 | { .name = "clk-proc-mixer" , }, |
476 | { .name = "clk-proc-sc" , }, |
477 | { .name = "clk-avsp-hevc" , }, |
478 | }; |
479 | |
480 | static const struct clkgen_data clkgen_stih418_c0 = { |
481 | .outputs = clkgen_stih418_c0_clk_out, |
482 | .outputs_nb = ARRAY_SIZE(clkgen_stih418_c0_clk_out), |
483 | }; |
484 | |
485 | static const struct clkgen_clk_out clkgen_stih407_d0_clk_out[] = { |
486 | { .name = "clk-pcm-0" , }, |
487 | { .name = "clk-pcm-1" , }, |
488 | { .name = "clk-pcm-2" , }, |
489 | { .name = "clk-spdiff" , }, |
490 | }; |
491 | |
492 | static const struct clkgen_data clkgen_stih407_d0 = { |
493 | .flags = CLK_SET_RATE_PARENT, |
494 | .outputs = clkgen_stih407_d0_clk_out, |
495 | .outputs_nb = ARRAY_SIZE(clkgen_stih407_d0_clk_out), |
496 | }; |
497 | |
498 | static const struct clkgen_clk_out clkgen_stih410_d0_clk_out[] = { |
499 | { .name = "clk-pcm-0" , }, |
500 | { .name = "clk-pcm-1" , }, |
501 | { .name = "clk-pcm-2" , }, |
502 | { .name = "clk-spdiff" , }, |
503 | { .name = "clk-pcmr10-master" , }, |
504 | { .name = "clk-usb2-phy" , }, |
505 | }; |
506 | |
507 | static const struct clkgen_data clkgen_stih410_d0 = { |
508 | .flags = CLK_SET_RATE_PARENT, |
509 | .outputs = clkgen_stih410_d0_clk_out, |
510 | .outputs_nb = ARRAY_SIZE(clkgen_stih410_d0_clk_out), |
511 | }; |
512 | |
513 | static const struct clkgen_clk_out clkgen_stih407_d2_clk_out[] = { |
514 | { .name = "clk-pix-main-disp" , }, |
515 | { .name = "clk-pix-pip" , }, |
516 | { .name = "clk-pix-gdp1" , }, |
517 | { .name = "clk-pix-gdp2" , }, |
518 | { .name = "clk-pix-gdp3" , }, |
519 | { .name = "clk-pix-gdp4" , }, |
520 | { .name = "clk-pix-aux-disp" , }, |
521 | { .name = "clk-denc" , }, |
522 | { .name = "clk-pix-hddac" , }, |
523 | { .name = "clk-hddac" , }, |
524 | { .name = "clk-sddac" , }, |
525 | { .name = "clk-pix-dvo" , }, |
526 | { .name = "clk-dvo" , }, |
527 | { .name = "clk-pix-hdmi" , }, |
528 | { .name = "clk-tmds-hdmi" , }, |
529 | { .name = "clk-ref-hdmiphy" , }, |
530 | }; |
531 | |
532 | static const struct clkgen_data clkgen_stih407_d2 = { |
533 | .outputs = clkgen_stih407_d2_clk_out, |
534 | .outputs_nb = ARRAY_SIZE(clkgen_stih407_d2_clk_out), |
535 | .flags = CLK_SET_RATE_PARENT, |
536 | .mode = 1, |
537 | }; |
538 | |
539 | static const struct clkgen_clk_out clkgen_stih418_d2_clk_out[] = { |
540 | { .name = "clk-pix-main-disp" , }, |
541 | { .name = "" , }, |
542 | { .name = "" , }, |
543 | { .name = "" , }, |
544 | { .name = "" , }, |
545 | { .name = "clk-tmds-hdmi-div2" , }, |
546 | { .name = "clk-pix-aux-disp" , }, |
547 | { .name = "clk-denc" , }, |
548 | { .name = "clk-pix-hddac" , }, |
549 | { .name = "clk-hddac" , }, |
550 | { .name = "clk-sddac" , }, |
551 | { .name = "clk-pix-dvo" , }, |
552 | { .name = "clk-dvo" , }, |
553 | { .name = "clk-pix-hdmi" , }, |
554 | { .name = "clk-tmds-hdmi" , }, |
555 | { .name = "clk-ref-hdmiphy" , }, |
556 | { .name = "" , }, { .name = "" , }, { .name = "" , }, { .name = "" , }, |
557 | { .name = "" , }, { .name = "" , }, { .name = "" , }, { .name = "" , }, |
558 | { .name = "" , }, { .name = "" , }, { .name = "" , }, { .name = "" , }, |
559 | { .name = "" , }, { .name = "" , }, { .name = "" , }, { .name = "" , }, |
560 | { .name = "" , }, { .name = "" , }, { .name = "" , }, { .name = "" , }, |
561 | { .name = "" , }, { .name = "" , }, { .name = "" , }, { .name = "" , }, |
562 | { .name = "" , }, { .name = "" , }, { .name = "" , }, { .name = "" , }, |
563 | { .name = "" , }, { .name = "" , }, { .name = "" , }, |
564 | { .name = "clk-vp9" , }, |
565 | }; |
566 | |
567 | static const struct clkgen_data clkgen_stih418_d2 = { |
568 | .outputs = clkgen_stih418_d2_clk_out, |
569 | .outputs_nb = ARRAY_SIZE(clkgen_stih418_d2_clk_out), |
570 | .flags = CLK_SET_RATE_PARENT, |
571 | .mode = 1, |
572 | }; |
573 | |
574 | static const struct clkgen_clk_out clkgen_stih407_d3_clk_out[] = { |
575 | { .name = "clk-stfe-frc1" , }, |
576 | { .name = "clk-tsout-0" , }, |
577 | { .name = "clk-tsout-1" , }, |
578 | { .name = "clk-mchi" , }, |
579 | { .name = "clk-vsens-compo" , }, |
580 | { .name = "clk-frc1-remote" , }, |
581 | { .name = "clk-lpc-0" , }, |
582 | { .name = "clk-lpc-1" , }, |
583 | }; |
584 | |
585 | static const struct clkgen_data clkgen_stih407_d3 = { |
586 | .outputs = clkgen_stih407_d3_clk_out, |
587 | .outputs_nb = ARRAY_SIZE(clkgen_stih407_d3_clk_out), |
588 | }; |
589 | |
590 | static const struct of_device_id flexgen_of_match[] = { |
591 | { |
592 | .compatible = "st,flexgen-audio" , |
593 | .data = &clkgen_audio, |
594 | }, |
595 | { |
596 | .compatible = "st,flexgen-video" , |
597 | .data = &clkgen_video, |
598 | }, |
599 | { |
600 | .compatible = "st,flexgen-stih407-a0" , |
601 | .data = &clkgen_stih407_a0, |
602 | }, |
603 | { |
604 | .compatible = "st,flexgen-stih410-a0" , |
605 | .data = &clkgen_stih410_a0, |
606 | }, |
607 | { |
608 | .compatible = "st,flexgen-stih407-c0" , |
609 | .data = &clkgen_stih407_c0, |
610 | }, |
611 | { |
612 | .compatible = "st,flexgen-stih410-c0" , |
613 | .data = &clkgen_stih410_c0, |
614 | }, |
615 | { |
616 | .compatible = "st,flexgen-stih418-c0" , |
617 | .data = &clkgen_stih418_c0, |
618 | }, |
619 | { |
620 | .compatible = "st,flexgen-stih407-d0" , |
621 | .data = &clkgen_stih407_d0, |
622 | }, |
623 | { |
624 | .compatible = "st,flexgen-stih410-d0" , |
625 | .data = &clkgen_stih410_d0, |
626 | }, |
627 | { |
628 | .compatible = "st,flexgen-stih407-d2" , |
629 | .data = &clkgen_stih407_d2, |
630 | }, |
631 | { |
632 | .compatible = "st,flexgen-stih418-d2" , |
633 | .data = &clkgen_stih418_d2, |
634 | }, |
635 | { |
636 | .compatible = "st,flexgen-stih407-d3" , |
637 | .data = &clkgen_stih407_d3, |
638 | }, |
639 | {} |
640 | }; |
641 | |
642 | static void __init st_of_flexgen_setup(struct device_node *np) |
643 | { |
644 | struct device_node *pnode; |
645 | void __iomem *reg; |
646 | struct clk_onecell_data *clk_data; |
647 | const char **parents; |
648 | int num_parents, i; |
649 | spinlock_t *rlock = NULL; |
650 | const struct of_device_id *match; |
651 | struct clkgen_data *data = NULL; |
652 | unsigned long flex_flags = 0; |
653 | int ret; |
654 | bool clk_mode = 0; |
655 | const char *clk_name; |
656 | |
657 | pnode = of_get_parent(node: np); |
658 | if (!pnode) |
659 | return; |
660 | |
661 | reg = of_iomap(node: pnode, index: 0); |
662 | of_node_put(node: pnode); |
663 | if (!reg) |
664 | return; |
665 | |
666 | parents = flexgen_get_parents(np, num_parents: &num_parents); |
667 | if (!parents) { |
668 | iounmap(addr: reg); |
669 | return; |
670 | } |
671 | |
672 | match = of_match_node(matches: flexgen_of_match, node: np); |
673 | if (match) { |
674 | data = (struct clkgen_data *)match->data; |
675 | flex_flags = data->flags; |
676 | clk_mode = data->mode; |
677 | } |
678 | |
679 | clk_data = kzalloc(size: sizeof(*clk_data), GFP_KERNEL); |
680 | if (!clk_data) |
681 | goto err; |
682 | |
683 | /* First try to get output information from the compatible data */ |
684 | if (!data || !data->outputs_nb || !data->outputs) { |
685 | ret = of_property_count_strings(np, propname: "clock-output-names" ); |
686 | if (ret <= 0) { |
687 | pr_err("%s: Failed to get number of output clocks (%d)" , |
688 | __func__, clk_data->clk_num); |
689 | goto err; |
690 | } |
691 | clk_data->clk_num = ret; |
692 | } else |
693 | clk_data->clk_num = data->outputs_nb; |
694 | |
695 | clk_data->clks = kcalloc(n: clk_data->clk_num, size: sizeof(struct clk *), |
696 | GFP_KERNEL); |
697 | if (!clk_data->clks) |
698 | goto err; |
699 | |
700 | rlock = kzalloc(size: sizeof(spinlock_t), GFP_KERNEL); |
701 | if (!rlock) |
702 | goto err; |
703 | |
704 | spin_lock_init(rlock); |
705 | |
706 | for (i = 0; i < clk_data->clk_num; i++) { |
707 | struct clk *clk; |
708 | |
709 | if (!data || !data->outputs_nb || !data->outputs) { |
710 | if (of_property_read_string_index(np, |
711 | propname: "clock-output-names" , |
712 | index: i, output: &clk_name)) |
713 | break; |
714 | flex_flags &= ~CLK_IS_CRITICAL; |
715 | of_clk_detect_critical(np, index: i, flags: &flex_flags); |
716 | } else { |
717 | clk_name = data->outputs[i].name; |
718 | flex_flags = data->flags | data->outputs[i].flags; |
719 | } |
720 | |
721 | /* |
722 | * If we read an empty clock name then the output is unused |
723 | */ |
724 | if (*clk_name == '\0') |
725 | continue; |
726 | |
727 | clk = clk_register_flexgen(name: clk_name, parent_names: parents, num_parents, |
728 | reg, lock: rlock, idx: i, flexgen_flags: flex_flags, mode: clk_mode); |
729 | |
730 | if (IS_ERR(ptr: clk)) |
731 | goto err; |
732 | |
733 | clk_data->clks[i] = clk; |
734 | } |
735 | |
736 | kfree(objp: parents); |
737 | of_clk_add_provider(np, clk_src_get: of_clk_src_onecell_get, data: clk_data); |
738 | |
739 | return; |
740 | |
741 | err: |
742 | iounmap(addr: reg); |
743 | if (clk_data) |
744 | kfree(objp: clk_data->clks); |
745 | kfree(objp: clk_data); |
746 | kfree(objp: parents); |
747 | kfree(objp: rlock); |
748 | } |
749 | CLK_OF_DECLARE(flexgen, "st,flexgen" , st_of_flexgen_setup); |
750 | |