]>
Commit | Line | Data |
---|---|---|
94885faf GF |
1 | /* |
2 | * clkgen-mux.c: ST GEN-MUX Clock driver | |
3 | * | |
4 | * Copyright (C) 2014 STMicroelectronics (R&D) Limited | |
5 | * | |
6 | * Authors: Stephen Gallimore <stephen.gallimore@st.com> | |
7 | * Pankaj Dev <pankaj.dev@st.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/slab.h> | |
17 | #include <linux/of_address.h> | |
d5f728ac | 18 | #include <linux/clk.h> |
94885faf GF |
19 | #include <linux/clk-provider.h> |
20 | ||
21 | static DEFINE_SPINLOCK(clkgena_divmux_lock); | |
44993d38 | 22 | static DEFINE_SPINLOCK(clkgenf_lock); |
94885faf GF |
23 | |
24 | static const char ** __init clkgen_mux_get_parents(struct device_node *np, | |
25 | int *num_parents) | |
26 | { | |
27 | const char **parents; | |
0b4e7f08 | 28 | int nparents; |
94885faf | 29 | |
0a65239c | 30 | nparents = of_clk_get_parent_count(np); |
94885faf GF |
31 | if (WARN_ON(nparents <= 0)) |
32 | return ERR_PTR(-EINVAL); | |
33 | ||
86665d28 | 34 | parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL); |
94885faf GF |
35 | if (!parents) |
36 | return ERR_PTR(-ENOMEM); | |
37 | ||
0b4e7f08 | 38 | *num_parents = of_clk_parent_fill(np, parents, nparents); |
94885faf GF |
39 | return parents; |
40 | } | |
41 | ||
42 | /** | |
43 | * DOC: Clock mux with a programmable divider on each of its three inputs. | |
44 | * The mux has an input setting which effectively gates its output. | |
45 | * | |
46 | * Traits of this clock: | |
47 | * prepare - clk_(un)prepare only ensures parent is (un)prepared | |
48 | * enable - clk_enable and clk_disable are functional & control gating | |
49 | * rate - set rate is supported | |
50 | * parent - set/get parent | |
51 | */ | |
52 | ||
53 | #define NUM_INPUTS 3 | |
54 | ||
55 | struct clkgena_divmux { | |
56 | struct clk_hw hw; | |
57 | /* Subclassed mux and divider structures */ | |
58 | struct clk_mux mux; | |
59 | struct clk_divider div[NUM_INPUTS]; | |
60 | /* Enable/running feedback register bits for each input */ | |
61 | void __iomem *feedback_reg[NUM_INPUTS]; | |
62 | int feedback_bit_idx; | |
63 | ||
64 | u8 muxsel; | |
65 | }; | |
66 | ||
67 | #define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw) | |
68 | ||
69 | struct clkgena_divmux_data { | |
70 | int num_outputs; | |
71 | int mux_offset; | |
72 | int mux_offset2; | |
73 | int mux_start_bit; | |
74 | int div_offsets[NUM_INPUTS]; | |
75 | int fb_offsets[NUM_INPUTS]; | |
76 | int fb_start_bit_idx; | |
77 | }; | |
78 | ||
79 | #define CKGAX_CLKOPSRC_SWITCH_OFF 0x3 | |
80 | ||
81 | static int clkgena_divmux_is_running(struct clkgena_divmux *mux) | |
82 | { | |
83 | u32 regval = readl(mux->feedback_reg[mux->muxsel]); | |
84 | u32 running = regval & BIT(mux->feedback_bit_idx); | |
85 | return !!running; | |
86 | } | |
87 | ||
88 | static int clkgena_divmux_enable(struct clk_hw *hw) | |
89 | { | |
90 | struct clkgena_divmux *genamux = to_clkgena_divmux(hw); | |
91 | struct clk_hw *mux_hw = &genamux->mux.hw; | |
92 | unsigned long timeout; | |
93 | int ret = 0; | |
94 | ||
4e907ef6 | 95 | __clk_hw_set_clk(mux_hw, hw); |
94885faf GF |
96 | |
97 | ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel); | |
98 | if (ret) | |
99 | return ret; | |
100 | ||
101 | timeout = jiffies + msecs_to_jiffies(10); | |
102 | ||
103 | while (!clkgena_divmux_is_running(genamux)) { | |
104 | if (time_after(jiffies, timeout)) | |
105 | return -ETIMEDOUT; | |
106 | cpu_relax(); | |
107 | } | |
108 | ||
109 | return 0; | |
110 | } | |
111 | ||
112 | static void clkgena_divmux_disable(struct clk_hw *hw) | |
113 | { | |
114 | struct clkgena_divmux *genamux = to_clkgena_divmux(hw); | |
115 | struct clk_hw *mux_hw = &genamux->mux.hw; | |
116 | ||
4e907ef6 | 117 | __clk_hw_set_clk(mux_hw, hw); |
94885faf GF |
118 | |
119 | clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF); | |
120 | } | |
121 | ||
122 | static int clkgena_divmux_is_enabled(struct clk_hw *hw) | |
123 | { | |
124 | struct clkgena_divmux *genamux = to_clkgena_divmux(hw); | |
125 | struct clk_hw *mux_hw = &genamux->mux.hw; | |
126 | ||
4e907ef6 | 127 | __clk_hw_set_clk(mux_hw, hw); |
94885faf GF |
128 | |
129 | return (s8)clk_mux_ops.get_parent(mux_hw) > 0; | |
130 | } | |
131 | ||
8e6dd77c | 132 | static u8 clkgena_divmux_get_parent(struct clk_hw *hw) |
94885faf GF |
133 | { |
134 | struct clkgena_divmux *genamux = to_clkgena_divmux(hw); | |
135 | struct clk_hw *mux_hw = &genamux->mux.hw; | |
136 | ||
4e907ef6 | 137 | __clk_hw_set_clk(mux_hw, hw); |
94885faf GF |
138 | |
139 | genamux->muxsel = clk_mux_ops.get_parent(mux_hw); | |
140 | if ((s8)genamux->muxsel < 0) { | |
141 | pr_debug("%s: %s: Invalid parent, setting to default.\n", | |
836ee0f7 | 142 | __func__, clk_hw_get_name(hw)); |
94885faf GF |
143 | genamux->muxsel = 0; |
144 | } | |
145 | ||
146 | return genamux->muxsel; | |
147 | } | |
148 | ||
149 | static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) | |
150 | { | |
151 | struct clkgena_divmux *genamux = to_clkgena_divmux(hw); | |
152 | ||
153 | if (index >= CKGAX_CLKOPSRC_SWITCH_OFF) | |
154 | return -EINVAL; | |
155 | ||
156 | genamux->muxsel = index; | |
157 | ||
158 | /* | |
159 | * If the mux is already enabled, call enable directly to set the | |
160 | * new mux position and wait for it to start running again. Otherwise | |
161 | * do nothing. | |
162 | */ | |
163 | if (clkgena_divmux_is_enabled(hw)) | |
164 | clkgena_divmux_enable(hw); | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
8e6dd77c | 169 | static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, |
94885faf GF |
170 | unsigned long parent_rate) |
171 | { | |
172 | struct clkgena_divmux *genamux = to_clkgena_divmux(hw); | |
173 | struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; | |
174 | ||
4e907ef6 | 175 | __clk_hw_set_clk(div_hw, hw); |
94885faf GF |
176 | |
177 | return clk_divider_ops.recalc_rate(div_hw, parent_rate); | |
178 | } | |
179 | ||
180 | static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate, | |
181 | unsigned long parent_rate) | |
182 | { | |
183 | struct clkgena_divmux *genamux = to_clkgena_divmux(hw); | |
184 | struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; | |
185 | ||
4e907ef6 | 186 | __clk_hw_set_clk(div_hw, hw); |
94885faf GF |
187 | |
188 | return clk_divider_ops.set_rate(div_hw, rate, parent_rate); | |
189 | } | |
190 | ||
191 | static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate, | |
192 | unsigned long *prate) | |
193 | { | |
194 | struct clkgena_divmux *genamux = to_clkgena_divmux(hw); | |
195 | struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; | |
196 | ||
4e907ef6 | 197 | __clk_hw_set_clk(div_hw, hw); |
94885faf GF |
198 | |
199 | return clk_divider_ops.round_rate(div_hw, rate, prate); | |
200 | } | |
201 | ||
202 | static const struct clk_ops clkgena_divmux_ops = { | |
203 | .enable = clkgena_divmux_enable, | |
204 | .disable = clkgena_divmux_disable, | |
205 | .is_enabled = clkgena_divmux_is_enabled, | |
206 | .get_parent = clkgena_divmux_get_parent, | |
207 | .set_parent = clkgena_divmux_set_parent, | |
208 | .round_rate = clkgena_divmux_round_rate, | |
209 | .recalc_rate = clkgena_divmux_recalc_rate, | |
210 | .set_rate = clkgena_divmux_set_rate, | |
211 | }; | |
212 | ||
213 | /** | |
214 | * clk_register_genamux - register a genamux clock with the clock framework | |
215 | */ | |
86665d28 | 216 | static struct clk * __init clk_register_genamux(const char *name, |
94885faf GF |
217 | const char **parent_names, u8 num_parents, |
218 | void __iomem *reg, | |
219 | const struct clkgena_divmux_data *muxdata, | |
220 | u32 idx) | |
221 | { | |
222 | /* | |
223 | * Fixed constants across all ClockgenA variants | |
224 | */ | |
225 | const int mux_width = 2; | |
226 | const int divider_width = 5; | |
227 | struct clkgena_divmux *genamux; | |
228 | struct clk *clk; | |
229 | struct clk_init_data init; | |
230 | int i; | |
231 | ||
232 | genamux = kzalloc(sizeof(*genamux), GFP_KERNEL); | |
233 | if (!genamux) | |
234 | return ERR_PTR(-ENOMEM); | |
235 | ||
236 | init.name = name; | |
237 | init.ops = &clkgena_divmux_ops; | |
18fee453 | 238 | init.flags = CLK_IS_BASIC | CLK_GET_RATE_NOCACHE; |
94885faf GF |
239 | init.parent_names = parent_names; |
240 | init.num_parents = num_parents; | |
241 | ||
242 | genamux->mux.lock = &clkgena_divmux_lock; | |
243 | genamux->mux.mask = BIT(mux_width) - 1; | |
244 | genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width); | |
245 | if (genamux->mux.shift > 31) { | |
246 | /* | |
247 | * We have spilled into the second mux register so | |
248 | * adjust the register address and the bit shift accordingly | |
249 | */ | |
250 | genamux->mux.reg = reg + muxdata->mux_offset2; | |
251 | genamux->mux.shift -= 32; | |
252 | } else { | |
253 | genamux->mux.reg = reg + muxdata->mux_offset; | |
254 | } | |
255 | ||
256 | for (i = 0; i < NUM_INPUTS; i++) { | |
257 | /* | |
258 | * Divider config for each input | |
259 | */ | |
260 | void __iomem *divbase = reg + muxdata->div_offsets[i]; | |
261 | genamux->div[i].width = divider_width; | |
262 | genamux->div[i].reg = divbase + (idx * sizeof(u32)); | |
263 | ||
264 | /* | |
265 | * Mux enabled/running feedback register for each input. | |
266 | */ | |
267 | genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i]; | |
268 | } | |
269 | ||
270 | genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx; | |
271 | genamux->hw.init = &init; | |
272 | ||
273 | clk = clk_register(NULL, &genamux->hw); | |
274 | if (IS_ERR(clk)) { | |
275 | kfree(genamux); | |
276 | goto err; | |
277 | } | |
278 | ||
279 | pr_debug("%s: parent %s rate %lu\n", | |
280 | __clk_get_name(clk), | |
281 | __clk_get_name(clk_get_parent(clk)), | |
282 | clk_get_rate(clk)); | |
283 | err: | |
284 | return clk; | |
285 | } | |
286 | ||
287 | static struct clkgena_divmux_data st_divmux_c65hs = { | |
288 | .num_outputs = 4, | |
289 | .mux_offset = 0x14, | |
290 | .mux_start_bit = 0, | |
291 | .div_offsets = { 0x800, 0x900, 0xb00 }, | |
292 | .fb_offsets = { 0x18, 0x1c, 0x20 }, | |
293 | .fb_start_bit_idx = 0, | |
294 | }; | |
295 | ||
296 | static struct clkgena_divmux_data st_divmux_c65ls = { | |
297 | .num_outputs = 14, | |
298 | .mux_offset = 0x14, | |
299 | .mux_offset2 = 0x24, | |
300 | .mux_start_bit = 8, | |
301 | .div_offsets = { 0x810, 0xa10, 0xb10 }, | |
302 | .fb_offsets = { 0x18, 0x1c, 0x20 }, | |
303 | .fb_start_bit_idx = 4, | |
304 | }; | |
305 | ||
306 | static struct clkgena_divmux_data st_divmux_c32odf0 = { | |
307 | .num_outputs = 8, | |
308 | .mux_offset = 0x1c, | |
309 | .mux_start_bit = 0, | |
310 | .div_offsets = { 0x800, 0x900, 0xa60 }, | |
311 | .fb_offsets = { 0x2c, 0x24, 0x28 }, | |
312 | .fb_start_bit_idx = 0, | |
313 | }; | |
314 | ||
315 | static struct clkgena_divmux_data st_divmux_c32odf1 = { | |
316 | .num_outputs = 8, | |
317 | .mux_offset = 0x1c, | |
318 | .mux_start_bit = 16, | |
319 | .div_offsets = { 0x820, 0x980, 0xa80 }, | |
320 | .fb_offsets = { 0x2c, 0x24, 0x28 }, | |
321 | .fb_start_bit_idx = 8, | |
322 | }; | |
323 | ||
324 | static struct clkgena_divmux_data st_divmux_c32odf2 = { | |
325 | .num_outputs = 8, | |
326 | .mux_offset = 0x20, | |
327 | .mux_start_bit = 0, | |
328 | .div_offsets = { 0x840, 0xa20, 0xb10 }, | |
329 | .fb_offsets = { 0x2c, 0x24, 0x28 }, | |
330 | .fb_start_bit_idx = 16, | |
331 | }; | |
332 | ||
333 | static struct clkgena_divmux_data st_divmux_c32odf3 = { | |
334 | .num_outputs = 8, | |
335 | .mux_offset = 0x20, | |
336 | .mux_start_bit = 16, | |
337 | .div_offsets = { 0x860, 0xa40, 0xb30 }, | |
338 | .fb_offsets = { 0x2c, 0x24, 0x28 }, | |
339 | .fb_start_bit_idx = 24, | |
340 | }; | |
341 | ||
f375573c | 342 | static const struct of_device_id clkgena_divmux_of_match[] = { |
94885faf GF |
343 | { |
344 | .compatible = "st,clkgena-divmux-c65-hs", | |
345 | .data = &st_divmux_c65hs, | |
346 | }, | |
347 | { | |
348 | .compatible = "st,clkgena-divmux-c65-ls", | |
349 | .data = &st_divmux_c65ls, | |
350 | }, | |
351 | { | |
352 | .compatible = "st,clkgena-divmux-c32-odf0", | |
353 | .data = &st_divmux_c32odf0, | |
354 | }, | |
355 | { | |
356 | .compatible = "st,clkgena-divmux-c32-odf1", | |
357 | .data = &st_divmux_c32odf1, | |
358 | }, | |
359 | { | |
360 | .compatible = "st,clkgena-divmux-c32-odf2", | |
361 | .data = &st_divmux_c32odf2, | |
362 | }, | |
363 | { | |
364 | .compatible = "st,clkgena-divmux-c32-odf3", | |
365 | .data = &st_divmux_c32odf3, | |
366 | }, | |
367 | {} | |
368 | }; | |
369 | ||
86665d28 | 370 | static void __iomem * __init clkgen_get_register_base(struct device_node *np) |
94885faf GF |
371 | { |
372 | struct device_node *pnode; | |
86665d28 | 373 | void __iomem *reg; |
94885faf GF |
374 | |
375 | pnode = of_get_parent(np); | |
376 | if (!pnode) | |
377 | return NULL; | |
378 | ||
379 | reg = of_iomap(pnode, 0); | |
380 | ||
381 | of_node_put(pnode); | |
382 | return reg; | |
383 | } | |
384 | ||
8e6dd77c | 385 | static void __init st_of_clkgena_divmux_setup(struct device_node *np) |
94885faf GF |
386 | { |
387 | const struct of_device_id *match; | |
388 | const struct clkgena_divmux_data *data; | |
389 | struct clk_onecell_data *clk_data; | |
390 | void __iomem *reg; | |
391 | const char **parents; | |
392 | int num_parents = 0, i; | |
393 | ||
394 | match = of_match_node(clkgena_divmux_of_match, np); | |
395 | if (WARN_ON(!match)) | |
396 | return; | |
397 | ||
86665d28 | 398 | data = match->data; |
94885faf GF |
399 | |
400 | reg = clkgen_get_register_base(np); | |
401 | if (!reg) | |
402 | return; | |
403 | ||
404 | parents = clkgen_mux_get_parents(np, &num_parents); | |
405 | if (IS_ERR(parents)) | |
86665d28 | 406 | goto err_parents; |
94885faf GF |
407 | |
408 | clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); | |
409 | if (!clk_data) | |
86665d28 | 410 | goto err_alloc; |
94885faf GF |
411 | |
412 | clk_data->clk_num = data->num_outputs; | |
86665d28 | 413 | clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), |
94885faf GF |
414 | GFP_KERNEL); |
415 | ||
416 | if (!clk_data->clks) | |
86665d28 | 417 | goto err_alloc_clks; |
94885faf GF |
418 | |
419 | for (i = 0; i < clk_data->clk_num; i++) { | |
420 | struct clk *clk; | |
421 | const char *clk_name; | |
422 | ||
423 | if (of_property_read_string_index(np, "clock-output-names", | |
424 | i, &clk_name)) | |
425 | break; | |
426 | ||
427 | /* | |
428 | * If we read an empty clock name then the output is unused | |
429 | */ | |
430 | if (*clk_name == '\0') | |
431 | continue; | |
432 | ||
433 | clk = clk_register_genamux(clk_name, parents, num_parents, | |
434 | reg, data, i); | |
435 | ||
436 | if (IS_ERR(clk)) | |
437 | goto err; | |
438 | ||
439 | clk_data->clks[i] = clk; | |
440 | } | |
441 | ||
442 | kfree(parents); | |
443 | ||
444 | of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); | |
445 | return; | |
446 | err: | |
86665d28 SB |
447 | kfree(clk_data->clks); |
448 | err_alloc_clks: | |
94885faf | 449 | kfree(clk_data); |
86665d28 | 450 | err_alloc: |
94885faf | 451 | kfree(parents); |
86665d28 SB |
452 | err_parents: |
453 | iounmap(reg); | |
94885faf GF |
454 | } |
455 | CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup); | |
456 | ||
457 | struct clkgena_prediv_data { | |
458 | u32 offset; | |
459 | u8 shift; | |
460 | struct clk_div_table *table; | |
461 | }; | |
462 | ||
463 | static struct clk_div_table prediv_table16[] = { | |
464 | { .val = 0, .div = 1 }, | |
465 | { .val = 1, .div = 16 }, | |
466 | { .div = 0 }, | |
467 | }; | |
468 | ||
469 | static struct clkgena_prediv_data prediv_c65_data = { | |
470 | .offset = 0x4c, | |
471 | .shift = 31, | |
472 | .table = prediv_table16, | |
473 | }; | |
474 | ||
475 | static struct clkgena_prediv_data prediv_c32_data = { | |
476 | .offset = 0x50, | |
477 | .shift = 1, | |
478 | .table = prediv_table16, | |
479 | }; | |
480 | ||
f375573c | 481 | static const struct of_device_id clkgena_prediv_of_match[] = { |
94885faf GF |
482 | { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data }, |
483 | { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data }, | |
484 | {} | |
485 | }; | |
486 | ||
8e6dd77c | 487 | static void __init st_of_clkgena_prediv_setup(struct device_node *np) |
94885faf GF |
488 | { |
489 | const struct of_device_id *match; | |
490 | void __iomem *reg; | |
491 | const char *parent_name, *clk_name; | |
492 | struct clk *clk; | |
86665d28 | 493 | const struct clkgena_prediv_data *data; |
94885faf GF |
494 | |
495 | match = of_match_node(clkgena_prediv_of_match, np); | |
496 | if (!match) { | |
497 | pr_err("%s: No matching data\n", __func__); | |
498 | return; | |
499 | } | |
500 | ||
86665d28 | 501 | data = match->data; |
94885faf GF |
502 | |
503 | reg = clkgen_get_register_base(np); | |
504 | if (!reg) | |
505 | return; | |
506 | ||
507 | parent_name = of_clk_get_parent_name(np, 0); | |
508 | if (!parent_name) | |
86665d28 | 509 | goto err; |
94885faf GF |
510 | |
511 | if (of_property_read_string_index(np, "clock-output-names", | |
512 | 0, &clk_name)) | |
86665d28 | 513 | goto err; |
94885faf | 514 | |
18fee453 PD |
515 | clk = clk_register_divider_table(NULL, clk_name, parent_name, |
516 | CLK_GET_RATE_NOCACHE, | |
94885faf GF |
517 | reg + data->offset, data->shift, 1, |
518 | 0, data->table, NULL); | |
519 | if (IS_ERR(clk)) | |
86665d28 | 520 | goto err; |
94885faf GF |
521 | |
522 | of_clk_add_provider(np, of_clk_src_simple_get, clk); | |
523 | pr_debug("%s: parent %s rate %u\n", | |
524 | __clk_get_name(clk), | |
525 | __clk_get_name(clk_get_parent(clk)), | |
526 | (unsigned int)clk_get_rate(clk)); | |
527 | ||
528 | return; | |
86665d28 SB |
529 | err: |
530 | iounmap(reg); | |
94885faf GF |
531 | } |
532 | CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); | |
44993d38 GF |
533 | |
534 | struct clkgen_mux_data { | |
535 | u32 offset; | |
536 | u8 shift; | |
537 | u8 width; | |
538 | spinlock_t *lock; | |
539 | unsigned long clk_flags; | |
540 | u8 mux_flags; | |
541 | }; | |
542 | ||
543 | static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = { | |
544 | .offset = 0, | |
545 | .shift = 0, | |
546 | .width = 1, | |
547 | }; | |
548 | ||
549 | static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = { | |
550 | .offset = 0, | |
551 | .shift = 0, | |
552 | .width = 1, | |
553 | }; | |
554 | ||
555 | static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = { | |
556 | .offset = 0, | |
557 | .shift = 0, | |
558 | .width = 1, | |
559 | }; | |
560 | ||
561 | static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = { | |
562 | .offset = 0, | |
563 | .shift = 16, | |
564 | .width = 1, | |
565 | .lock = &clkgenf_lock, | |
566 | }; | |
567 | ||
568 | static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = { | |
569 | .offset = 0, | |
570 | .shift = 17, | |
571 | .width = 1, | |
572 | .lock = &clkgenf_lock, | |
573 | }; | |
574 | ||
ab35dc13 GF |
575 | static struct clkgen_mux_data stih415_a9_mux_data = { |
576 | .offset = 0, | |
577 | .shift = 1, | |
578 | .width = 2, | |
579 | }; | |
580 | static struct clkgen_mux_data stih416_a9_mux_data = { | |
581 | .offset = 0, | |
582 | .shift = 0, | |
583 | .width = 2, | |
584 | }; | |
13e6f2da GF |
585 | static struct clkgen_mux_data stih407_a9_mux_data = { |
586 | .offset = 0x1a4, | |
3be6d8ce | 587 | .shift = 0, |
13e6f2da GF |
588 | .width = 2, |
589 | }; | |
ab35dc13 | 590 | |
f375573c | 591 | static const struct of_device_id mux_of_match[] = { |
44993d38 GF |
592 | { |
593 | .compatible = "st,stih416-clkgenc-vcc-hd", | |
594 | .data = &clkgen_mux_c_vcc_hd_416, | |
595 | }, | |
596 | { | |
597 | .compatible = "st,stih416-clkgenf-vcc-fvdp", | |
598 | .data = &clkgen_mux_f_vcc_fvdp_416, | |
599 | }, | |
600 | { | |
601 | .compatible = "st,stih416-clkgenf-vcc-hva", | |
602 | .data = &clkgen_mux_f_vcc_hva_416, | |
603 | }, | |
604 | { | |
605 | .compatible = "st,stih416-clkgenf-vcc-hd", | |
606 | .data = &clkgen_mux_f_vcc_hd_416, | |
607 | }, | |
608 | { | |
609 | .compatible = "st,stih416-clkgenf-vcc-sd", | |
610 | .data = &clkgen_mux_c_vcc_sd_416, | |
611 | }, | |
ab35dc13 GF |
612 | { |
613 | .compatible = "st,stih415-clkgen-a9-mux", | |
614 | .data = &stih415_a9_mux_data, | |
615 | }, | |
616 | { | |
617 | .compatible = "st,stih416-clkgen-a9-mux", | |
618 | .data = &stih416_a9_mux_data, | |
619 | }, | |
13e6f2da GF |
620 | { |
621 | .compatible = "st,stih407-clkgen-a9-mux", | |
622 | .data = &stih407_a9_mux_data, | |
623 | }, | |
44993d38 GF |
624 | {} |
625 | }; | |
626 | ||
8e6dd77c | 627 | static void __init st_of_clkgen_mux_setup(struct device_node *np) |
44993d38 GF |
628 | { |
629 | const struct of_device_id *match; | |
630 | struct clk *clk; | |
631 | void __iomem *reg; | |
632 | const char **parents; | |
633 | int num_parents; | |
86665d28 | 634 | const struct clkgen_mux_data *data; |
44993d38 GF |
635 | |
636 | match = of_match_node(mux_of_match, np); | |
637 | if (!match) { | |
638 | pr_err("%s: No matching data\n", __func__); | |
639 | return; | |
640 | } | |
641 | ||
86665d28 | 642 | data = match->data; |
44993d38 GF |
643 | |
644 | reg = of_iomap(np, 0); | |
645 | if (!reg) { | |
646 | pr_err("%s: Failed to get base address\n", __func__); | |
647 | return; | |
648 | } | |
649 | ||
650 | parents = clkgen_mux_get_parents(np, &num_parents); | |
651 | if (IS_ERR(parents)) { | |
652 | pr_err("%s: Failed to get parents (%ld)\n", | |
653 | __func__, PTR_ERR(parents)); | |
86665d28 | 654 | goto err_parents; |
44993d38 GF |
655 | } |
656 | ||
657 | clk = clk_register_mux(NULL, np->name, parents, num_parents, | |
658 | data->clk_flags | CLK_SET_RATE_PARENT, | |
659 | reg + data->offset, | |
660 | data->shift, data->width, data->mux_flags, | |
661 | data->lock); | |
662 | if (IS_ERR(clk)) | |
663 | goto err; | |
664 | ||
665 | pr_debug("%s: parent %s rate %u\n", | |
666 | __clk_get_name(clk), | |
667 | __clk_get_name(clk_get_parent(clk)), | |
668 | (unsigned int)clk_get_rate(clk)); | |
669 | ||
86665d28 | 670 | kfree(parents); |
44993d38 | 671 | of_clk_add_provider(np, of_clk_src_simple_get, clk); |
86665d28 | 672 | return; |
44993d38 GF |
673 | |
674 | err: | |
675 | kfree(parents); | |
86665d28 SB |
676 | err_parents: |
677 | iounmap(reg); | |
44993d38 GF |
678 | } |
679 | CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup); | |
680 | ||
681 | #define VCC_MAX_CHANNELS 16 | |
682 | ||
683 | #define VCC_GATE_OFFSET 0x0 | |
684 | #define VCC_MUX_OFFSET 0x4 | |
685 | #define VCC_DIV_OFFSET 0x8 | |
686 | ||
687 | struct clkgen_vcc_data { | |
688 | spinlock_t *lock; | |
689 | unsigned long clk_flags; | |
690 | }; | |
691 | ||
692 | static struct clkgen_vcc_data st_clkgenc_vcc_416 = { | |
693 | .clk_flags = CLK_SET_RATE_PARENT, | |
694 | }; | |
695 | ||
696 | static struct clkgen_vcc_data st_clkgenf_vcc_416 = { | |
697 | .lock = &clkgenf_lock, | |
698 | }; | |
699 | ||
f375573c | 700 | static const struct of_device_id vcc_of_match[] = { |
44993d38 GF |
701 | { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 }, |
702 | { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 }, | |
703 | {} | |
704 | }; | |
705 | ||
8e6dd77c | 706 | static void __init st_of_clkgen_vcc_setup(struct device_node *np) |
44993d38 GF |
707 | { |
708 | const struct of_device_id *match; | |
709 | void __iomem *reg; | |
710 | const char **parents; | |
711 | int num_parents, i; | |
712 | struct clk_onecell_data *clk_data; | |
86665d28 | 713 | const struct clkgen_vcc_data *data; |
44993d38 GF |
714 | |
715 | match = of_match_node(vcc_of_match, np); | |
716 | if (WARN_ON(!match)) | |
717 | return; | |
86665d28 | 718 | data = match->data; |
44993d38 GF |
719 | |
720 | reg = of_iomap(np, 0); | |
721 | if (!reg) | |
722 | return; | |
723 | ||
724 | parents = clkgen_mux_get_parents(np, &num_parents); | |
725 | if (IS_ERR(parents)) | |
86665d28 | 726 | goto err_parents; |
44993d38 GF |
727 | |
728 | clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); | |
729 | if (!clk_data) | |
86665d28 | 730 | goto err_alloc; |
44993d38 GF |
731 | |
732 | clk_data->clk_num = VCC_MAX_CHANNELS; | |
86665d28 | 733 | clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *), |
44993d38 GF |
734 | GFP_KERNEL); |
735 | ||
736 | if (!clk_data->clks) | |
86665d28 | 737 | goto err_alloc_clks; |
44993d38 GF |
738 | |
739 | for (i = 0; i < clk_data->clk_num; i++) { | |
740 | struct clk *clk; | |
741 | const char *clk_name; | |
742 | struct clk_gate *gate; | |
743 | struct clk_divider *div; | |
744 | struct clk_mux *mux; | |
745 | ||
746 | if (of_property_read_string_index(np, "clock-output-names", | |
747 | i, &clk_name)) | |
748 | break; | |
749 | ||
750 | /* | |
751 | * If we read an empty clock name then the output is unused | |
752 | */ | |
753 | if (*clk_name == '\0') | |
754 | continue; | |
755 | ||
86665d28 | 756 | gate = kzalloc(sizeof(*gate), GFP_KERNEL); |
44993d38 | 757 | if (!gate) |
86665d28 | 758 | goto err; |
44993d38 | 759 | |
86665d28 | 760 | div = kzalloc(sizeof(*div), GFP_KERNEL); |
44993d38 GF |
761 | if (!div) { |
762 | kfree(gate); | |
86665d28 | 763 | goto err; |
44993d38 GF |
764 | } |
765 | ||
86665d28 | 766 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); |
44993d38 GF |
767 | if (!mux) { |
768 | kfree(gate); | |
769 | kfree(div); | |
86665d28 | 770 | goto err; |
44993d38 GF |
771 | } |
772 | ||
773 | gate->reg = reg + VCC_GATE_OFFSET; | |
774 | gate->bit_idx = i; | |
775 | gate->flags = CLK_GATE_SET_TO_DISABLE; | |
776 | gate->lock = data->lock; | |
777 | ||
778 | div->reg = reg + VCC_DIV_OFFSET; | |
779 | div->shift = 2 * i; | |
780 | div->width = 2; | |
eee40bb4 GF |
781 | div->flags = CLK_DIVIDER_POWER_OF_TWO | |
782 | CLK_DIVIDER_ROUND_CLOSEST; | |
44993d38 GF |
783 | |
784 | mux->reg = reg + VCC_MUX_OFFSET; | |
785 | mux->shift = 2 * i; | |
786 | mux->mask = 0x3; | |
787 | ||
788 | clk = clk_register_composite(NULL, clk_name, parents, | |
789 | num_parents, | |
790 | &mux->hw, &clk_mux_ops, | |
791 | &div->hw, &clk_divider_ops, | |
792 | &gate->hw, &clk_gate_ops, | |
18fee453 PD |
793 | data->clk_flags | |
794 | CLK_GET_RATE_NOCACHE); | |
44993d38 GF |
795 | if (IS_ERR(clk)) { |
796 | kfree(gate); | |
797 | kfree(div); | |
798 | kfree(mux); | |
799 | goto err; | |
800 | } | |
801 | ||
802 | pr_debug("%s: parent %s rate %u\n", | |
803 | __clk_get_name(clk), | |
804 | __clk_get_name(clk_get_parent(clk)), | |
805 | (unsigned int)clk_get_rate(clk)); | |
806 | ||
807 | clk_data->clks[i] = clk; | |
808 | } | |
809 | ||
810 | kfree(parents); | |
811 | ||
812 | of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); | |
813 | return; | |
814 | ||
815 | err: | |
816 | for (i = 0; i < clk_data->clk_num; i++) { | |
817 | struct clk_composite *composite; | |
818 | ||
819 | if (!clk_data->clks[i]) | |
820 | continue; | |
821 | ||
822 | composite = container_of(__clk_get_hw(clk_data->clks[i]), | |
823 | struct clk_composite, hw); | |
824 | kfree(container_of(composite->gate_hw, struct clk_gate, hw)); | |
825 | kfree(container_of(composite->rate_hw, struct clk_divider, hw)); | |
826 | kfree(container_of(composite->mux_hw, struct clk_mux, hw)); | |
827 | } | |
828 | ||
86665d28 SB |
829 | kfree(clk_data->clks); |
830 | err_alloc_clks: | |
44993d38 | 831 | kfree(clk_data); |
86665d28 | 832 | err_alloc: |
44993d38 | 833 | kfree(parents); |
86665d28 SB |
834 | err_parents: |
835 | iounmap(reg); | |
44993d38 GF |
836 | } |
837 | CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup); |