Commit | Line | Data |
---|---|---|
88a17252 TK |
1 | /* |
2 | * OMAP clkctrl clock support | |
3 | * | |
4 | * Copyright (C) 2017 Texas Instruments, Inc. | |
5 | * | |
6 | * Tero Kristo <t-kristo@ti.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
13 | * kind, whether express or implied; without even the implied warranty | |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | */ | |
17 | ||
18 | #include <linux/clk-provider.h> | |
19 | #include <linux/slab.h> | |
20 | #include <linux/of.h> | |
21 | #include <linux/of_address.h> | |
22 | #include <linux/clk/ti.h> | |
23 | #include <linux/delay.h> | |
3d8598fb | 24 | #include <linux/timekeeping.h> |
88a17252 TK |
25 | #include "clock.h" |
26 | ||
27 | #define NO_IDLEST 0x1 | |
28 | ||
29 | #define OMAP4_MODULEMODE_MASK 0x3 | |
30 | ||
31 | #define MODULEMODE_HWCTRL 0x1 | |
32 | #define MODULEMODE_SWCTRL 0x2 | |
33 | ||
34 | #define OMAP4_IDLEST_MASK (0x3 << 16) | |
35 | #define OMAP4_IDLEST_SHIFT 16 | |
36 | ||
37 | #define CLKCTRL_IDLEST_FUNCTIONAL 0x0 | |
38 | #define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 | |
39 | #define CLKCTRL_IDLEST_DISABLED 0x3 | |
40 | ||
41 | /* These timeouts are in us */ | |
42 | #define OMAP4_MAX_MODULE_READY_TIME 2000 | |
43 | #define OMAP4_MAX_MODULE_DISABLE_TIME 5000 | |
44 | ||
45 | static bool _early_timeout = true; | |
46 | ||
47 | struct omap_clkctrl_provider { | |
48 | void __iomem *base; | |
49 | struct list_head clocks; | |
ddfb183e | 50 | char *clkdm_name; |
88a17252 TK |
51 | }; |
52 | ||
53 | struct omap_clkctrl_clk { | |
54 | struct clk_hw *clk; | |
55 | u16 reg_offset; | |
56 | int bit_offset; | |
57 | struct list_head node; | |
58 | }; | |
59 | ||
60 | union omap4_timeout { | |
61 | u32 cycles; | |
62 | ktime_t start; | |
63 | }; | |
64 | ||
65 | static const struct omap_clkctrl_data default_clkctrl_data[] __initconst = { | |
66 | { 0 }, | |
67 | }; | |
68 | ||
69 | static u32 _omap4_idlest(u32 val) | |
70 | { | |
71 | val &= OMAP4_IDLEST_MASK; | |
72 | val >>= OMAP4_IDLEST_SHIFT; | |
73 | ||
74 | return val; | |
75 | } | |
76 | ||
77 | static bool _omap4_is_idle(u32 val) | |
78 | { | |
79 | val = _omap4_idlest(val); | |
80 | ||
81 | return val == CLKCTRL_IDLEST_DISABLED; | |
82 | } | |
83 | ||
84 | static bool _omap4_is_ready(u32 val) | |
85 | { | |
86 | val = _omap4_idlest(val); | |
87 | ||
88 | return val == CLKCTRL_IDLEST_FUNCTIONAL || | |
89 | val == CLKCTRL_IDLEST_INTERFACE_IDLE; | |
90 | } | |
91 | ||
92 | static bool _omap4_is_timeout(union omap4_timeout *time, u32 timeout) | |
93 | { | |
3d8598fb TK |
94 | /* |
95 | * There are two special cases where ktime_to_ns() can't be | |
96 | * used to track the timeouts. First one is during early boot | |
97 | * when the timers haven't been initialized yet. The second | |
98 | * one is during suspend-resume cycle while timekeeping is | |
99 | * being suspended / resumed. Clocksource for the system | |
100 | * can be from a timer that requires pm_runtime access, which | |
101 | * will eventually bring us here with timekeeping_suspended, | |
102 | * during both suspend entry and resume paths. This happens | |
103 | * at least on am43xx platform. | |
104 | */ | |
105 | if (unlikely(_early_timeout || timekeeping_suspended)) { | |
88a17252 TK |
106 | if (time->cycles++ < timeout) { |
107 | udelay(1); | |
108 | return false; | |
109 | } | |
110 | } else { | |
111 | if (!ktime_to_ns(time->start)) { | |
112 | time->start = ktime_get(); | |
113 | return false; | |
114 | } | |
115 | ||
116 | if (ktime_us_delta(ktime_get(), time->start) < timeout) { | |
117 | cpu_relax(); | |
118 | return false; | |
119 | } | |
120 | } | |
121 | ||
122 | return true; | |
123 | } | |
124 | ||
125 | static int __init _omap4_disable_early_timeout(void) | |
126 | { | |
127 | _early_timeout = false; | |
128 | ||
129 | return 0; | |
130 | } | |
131 | arch_initcall(_omap4_disable_early_timeout); | |
132 | ||
133 | static int _omap4_clkctrl_clk_enable(struct clk_hw *hw) | |
134 | { | |
135 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | |
136 | u32 val; | |
137 | int ret; | |
138 | union omap4_timeout timeout = { 0 }; | |
139 | ||
88a17252 TK |
140 | if (clk->clkdm) { |
141 | ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); | |
142 | if (ret) { | |
143 | WARN(1, | |
144 | "%s: could not enable %s's clockdomain %s: %d\n", | |
145 | __func__, clk_hw_get_name(hw), | |
146 | clk->clkdm_name, ret); | |
147 | return ret; | |
148 | } | |
149 | } | |
150 | ||
1cc54078 TL |
151 | if (!clk->enable_bit) |
152 | return 0; | |
153 | ||
88a17252 TK |
154 | val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); |
155 | ||
156 | val &= ~OMAP4_MODULEMODE_MASK; | |
157 | val |= clk->enable_bit; | |
158 | ||
159 | ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); | |
160 | ||
161 | if (clk->flags & NO_IDLEST) | |
162 | return 0; | |
163 | ||
164 | /* Wait until module is enabled */ | |
165 | while (!_omap4_is_ready(ti_clk_ll_ops->clk_readl(&clk->enable_reg))) { | |
166 | if (_omap4_is_timeout(&timeout, OMAP4_MAX_MODULE_READY_TIME)) { | |
167 | pr_err("%s: failed to enable\n", clk_hw_get_name(hw)); | |
168 | return -EBUSY; | |
169 | } | |
170 | } | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | static void _omap4_clkctrl_clk_disable(struct clk_hw *hw) | |
176 | { | |
177 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | |
178 | u32 val; | |
179 | union omap4_timeout timeout = { 0 }; | |
180 | ||
181 | if (!clk->enable_bit) | |
1cc54078 | 182 | goto exit; |
88a17252 TK |
183 | |
184 | val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); | |
185 | ||
186 | val &= ~OMAP4_MODULEMODE_MASK; | |
187 | ||
188 | ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); | |
189 | ||
190 | if (clk->flags & NO_IDLEST) | |
191 | goto exit; | |
192 | ||
193 | /* Wait until module is disabled */ | |
194 | while (!_omap4_is_idle(ti_clk_ll_ops->clk_readl(&clk->enable_reg))) { | |
195 | if (_omap4_is_timeout(&timeout, | |
196 | OMAP4_MAX_MODULE_DISABLE_TIME)) { | |
197 | pr_err("%s: failed to disable\n", clk_hw_get_name(hw)); | |
198 | break; | |
199 | } | |
200 | } | |
201 | ||
202 | exit: | |
203 | if (clk->clkdm) | |
204 | ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); | |
205 | } | |
206 | ||
207 | static int _omap4_clkctrl_clk_is_enabled(struct clk_hw *hw) | |
208 | { | |
209 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | |
210 | u32 val; | |
211 | ||
212 | val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); | |
213 | ||
214 | if (val & clk->enable_bit) | |
215 | return 1; | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | static const struct clk_ops omap4_clkctrl_clk_ops = { | |
221 | .enable = _omap4_clkctrl_clk_enable, | |
222 | .disable = _omap4_clkctrl_clk_disable, | |
223 | .is_enabled = _omap4_clkctrl_clk_is_enabled, | |
ddfb183e | 224 | .init = omap2_init_clk_clkdm, |
88a17252 TK |
225 | }; |
226 | ||
227 | static struct clk_hw *_ti_omap4_clkctrl_xlate(struct of_phandle_args *clkspec, | |
228 | void *data) | |
229 | { | |
230 | struct omap_clkctrl_provider *provider = data; | |
231 | struct omap_clkctrl_clk *entry; | |
41b3588d | 232 | bool found = false; |
88a17252 TK |
233 | |
234 | if (clkspec->args_count != 2) | |
235 | return ERR_PTR(-EINVAL); | |
236 | ||
237 | pr_debug("%s: looking for %x:%x\n", __func__, | |
238 | clkspec->args[0], clkspec->args[1]); | |
239 | ||
240 | list_for_each_entry(entry, &provider->clocks, node) { | |
241 | if (entry->reg_offset == clkspec->args[0] && | |
41b3588d TL |
242 | entry->bit_offset == clkspec->args[1]) { |
243 | found = true; | |
88a17252 | 244 | break; |
41b3588d | 245 | } |
88a17252 TK |
246 | } |
247 | ||
41b3588d | 248 | if (!found) |
88a17252 TK |
249 | return ERR_PTR(-EINVAL); |
250 | ||
251 | return entry->clk; | |
252 | } | |
253 | ||
254 | static int __init | |
255 | _ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider, | |
256 | struct device_node *node, struct clk_hw *clk_hw, | |
257 | u16 offset, u8 bit, const char * const *parents, | |
258 | int num_parents, const struct clk_ops *ops) | |
259 | { | |
260 | struct clk_init_data init = { NULL }; | |
261 | struct clk *clk; | |
262 | struct omap_clkctrl_clk *clkctrl_clk; | |
263 | int ret = 0; | |
264 | ||
47b00dcf | 265 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) |
8a69f1d4 SB |
266 | init.name = kasprintf(GFP_KERNEL, "%pOFn:%pOFn:%04x:%d", |
267 | node->parent, node, offset, | |
47b00dcf TK |
268 | bit); |
269 | else | |
8a69f1d4 | 270 | init.name = kasprintf(GFP_KERNEL, "%pOFn:%04x:%d", node, |
47b00dcf | 271 | offset, bit); |
88a17252 TK |
272 | clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); |
273 | if (!init.name || !clkctrl_clk) { | |
274 | ret = -ENOMEM; | |
275 | goto cleanup; | |
276 | } | |
277 | ||
278 | clk_hw->init = &init; | |
279 | init.parent_names = parents; | |
280 | init.num_parents = num_parents; | |
281 | init.ops = ops; | |
8aa09cf3 | 282 | init.flags = 0; |
88a17252 TK |
283 | |
284 | clk = ti_clk_register(NULL, clk_hw, init.name); | |
285 | if (IS_ERR_OR_NULL(clk)) { | |
286 | ret = -EINVAL; | |
287 | goto cleanup; | |
288 | } | |
289 | ||
290 | clkctrl_clk->reg_offset = offset; | |
291 | clkctrl_clk->bit_offset = bit; | |
292 | clkctrl_clk->clk = clk_hw; | |
293 | ||
294 | list_add(&clkctrl_clk->node, &provider->clocks); | |
295 | ||
296 | return 0; | |
297 | ||
298 | cleanup: | |
299 | kfree(init.name); | |
300 | kfree(clkctrl_clk); | |
301 | return ret; | |
302 | } | |
303 | ||
304 | static void __init | |
305 | _ti_clkctrl_setup_gate(struct omap_clkctrl_provider *provider, | |
306 | struct device_node *node, u16 offset, | |
307 | const struct omap_clkctrl_bit_data *data, | |
308 | void __iomem *reg) | |
309 | { | |
310 | struct clk_hw_omap *clk_hw; | |
311 | ||
312 | clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); | |
313 | if (!clk_hw) | |
314 | return; | |
315 | ||
316 | clk_hw->enable_bit = data->bit; | |
317 | clk_hw->enable_reg.ptr = reg; | |
318 | ||
319 | if (_ti_clkctrl_clk_register(provider, node, &clk_hw->hw, offset, | |
320 | data->bit, data->parents, 1, | |
321 | &omap_gate_clk_ops)) | |
322 | kfree(clk_hw); | |
323 | } | |
324 | ||
325 | static void __init | |
326 | _ti_clkctrl_setup_mux(struct omap_clkctrl_provider *provider, | |
327 | struct device_node *node, u16 offset, | |
328 | const struct omap_clkctrl_bit_data *data, | |
329 | void __iomem *reg) | |
330 | { | |
331 | struct clk_omap_mux *mux; | |
332 | int num_parents = 0; | |
333 | const char * const *pname; | |
334 | ||
335 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); | |
336 | if (!mux) | |
337 | return; | |
338 | ||
339 | pname = data->parents; | |
340 | while (*pname) { | |
341 | num_parents++; | |
342 | pname++; | |
343 | } | |
344 | ||
345 | mux->mask = num_parents; | |
49eec6fb TK |
346 | if (!(mux->flags & CLK_MUX_INDEX_ONE)) |
347 | mux->mask--; | |
348 | ||
88a17252 TK |
349 | mux->mask = (1 << fls(mux->mask)) - 1; |
350 | ||
351 | mux->shift = data->bit; | |
352 | mux->reg.ptr = reg; | |
353 | ||
354 | if (_ti_clkctrl_clk_register(provider, node, &mux->hw, offset, | |
355 | data->bit, data->parents, num_parents, | |
356 | &ti_clk_mux_ops)) | |
357 | kfree(mux); | |
358 | } | |
359 | ||
360 | static void __init | |
361 | _ti_clkctrl_setup_div(struct omap_clkctrl_provider *provider, | |
362 | struct device_node *node, u16 offset, | |
363 | const struct omap_clkctrl_bit_data *data, | |
364 | void __iomem *reg) | |
365 | { | |
366 | struct clk_omap_divider *div; | |
367 | const struct omap_clkctrl_div_data *div_data = data->data; | |
49eec6fb | 368 | u8 div_flags = 0; |
88a17252 TK |
369 | |
370 | div = kzalloc(sizeof(*div), GFP_KERNEL); | |
371 | if (!div) | |
372 | return; | |
373 | ||
374 | div->reg.ptr = reg; | |
375 | div->shift = data->bit; | |
49eec6fb TK |
376 | div->flags = div_data->flags; |
377 | ||
378 | if (div->flags & CLK_DIVIDER_POWER_OF_TWO) | |
379 | div_flags |= CLKF_INDEX_POWER_OF_TWO; | |
88a17252 | 380 | |
49eec6fb TK |
381 | if (ti_clk_parse_divider_data((int *)div_data->dividers, 0, |
382 | div_data->max_div, div_flags, | |
88a17252 | 383 | &div->width, &div->table)) { |
c2c296c3 TK |
384 | pr_err("%s: Data parsing for %pOF:%04x:%d failed\n", __func__, |
385 | node, offset, data->bit); | |
88a17252 TK |
386 | kfree(div); |
387 | return; | |
388 | } | |
389 | ||
390 | if (_ti_clkctrl_clk_register(provider, node, &div->hw, offset, | |
391 | data->bit, data->parents, 1, | |
392 | &ti_clk_divider_ops)) | |
393 | kfree(div); | |
394 | } | |
395 | ||
396 | static void __init | |
397 | _ti_clkctrl_setup_subclks(struct omap_clkctrl_provider *provider, | |
398 | struct device_node *node, | |
399 | const struct omap_clkctrl_reg_data *data, | |
400 | void __iomem *reg) | |
401 | { | |
402 | const struct omap_clkctrl_bit_data *bits = data->bit_data; | |
403 | ||
404 | if (!bits) | |
405 | return; | |
406 | ||
407 | while (bits->bit) { | |
408 | switch (bits->type) { | |
409 | case TI_CLK_GATE: | |
410 | _ti_clkctrl_setup_gate(provider, node, data->offset, | |
411 | bits, reg); | |
412 | break; | |
413 | ||
414 | case TI_CLK_DIVIDER: | |
415 | _ti_clkctrl_setup_div(provider, node, data->offset, | |
416 | bits, reg); | |
417 | break; | |
418 | ||
419 | case TI_CLK_MUX: | |
420 | _ti_clkctrl_setup_mux(provider, node, data->offset, | |
421 | bits, reg); | |
422 | break; | |
423 | ||
424 | default: | |
425 | pr_err("%s: bad subclk type: %d\n", __func__, | |
426 | bits->type); | |
427 | return; | |
428 | } | |
429 | bits++; | |
430 | } | |
431 | } | |
432 | ||
729e13bf TK |
433 | static void __init _clkctrl_add_provider(void *data, |
434 | struct device_node *np) | |
435 | { | |
436 | of_clk_add_hw_provider(np, _ti_omap4_clkctrl_xlate, data); | |
437 | } | |
438 | ||
88a17252 TK |
439 | static void __init _ti_omap4_clkctrl_setup(struct device_node *node) |
440 | { | |
441 | struct omap_clkctrl_provider *provider; | |
442 | const struct omap_clkctrl_data *data = default_clkctrl_data; | |
443 | const struct omap_clkctrl_reg_data *reg_data; | |
444 | struct clk_init_data init = { NULL }; | |
445 | struct clk_hw_omap *hw; | |
446 | struct clk *clk; | |
447 | struct omap_clkctrl_clk *clkctrl_clk; | |
448 | const __be32 *addrp; | |
449 | u32 addr; | |
729e13bf | 450 | int ret; |
1dc88f78 | 451 | char *c; |
2b1202d7 | 452 | u16 soc_mask = 0; |
88a17252 | 453 | |
47b00dcf | 454 | if (!(ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) && |
87ab1151 | 455 | of_node_name_eq(node, "clk")) |
47b00dcf | 456 | ti_clk_features.flags |= TI_CLK_CLKCTRL_COMPAT; |
88a17252 TK |
457 | |
458 | addrp = of_get_address(node, 0, NULL, NULL); | |
459 | addr = (u32)of_translate_address(node, addrp); | |
460 | ||
1c881b5a TK |
461 | #ifdef CONFIG_ARCH_OMAP4 |
462 | if (of_machine_is_compatible("ti,omap4")) | |
463 | data = omap4_clkctrl_data; | |
464 | #endif | |
0ad902f6 TK |
465 | #ifdef CONFIG_SOC_OMAP5 |
466 | if (of_machine_is_compatible("ti,omap5")) | |
467 | data = omap5_clkctrl_data; | |
468 | #endif | |
24d504a3 | 469 | #ifdef CONFIG_SOC_DRA7XX |
dffa9051 TK |
470 | if (of_machine_is_compatible("ti,dra7")) { |
471 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
472 | data = dra7_clkctrl_compat_data; | |
473 | else | |
474 | data = dra7_clkctrl_data; | |
475 | } | |
2b1202d7 TK |
476 | |
477 | if (of_machine_is_compatible("ti,dra72")) | |
478 | soc_mask = CLKF_SOC_DRA72; | |
479 | if (of_machine_is_compatible("ti,dra74")) | |
480 | soc_mask = CLKF_SOC_DRA74; | |
481 | if (of_machine_is_compatible("ti,dra76")) | |
482 | soc_mask = CLKF_SOC_DRA76; | |
24d504a3 | 483 | #endif |
df54bfc5 | 484 | #ifdef CONFIG_SOC_AM33XX |
296e583e TK |
485 | if (of_machine_is_compatible("ti,am33xx")) { |
486 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
487 | data = am3_clkctrl_compat_data; | |
488 | else | |
489 | data = am3_clkctrl_data; | |
490 | } | |
df54bfc5 | 491 | #endif |
a3da10b7 | 492 | #ifdef CONFIG_SOC_AM43XX |
76a1049b TK |
493 | if (of_machine_is_compatible("ti,am4372")) { |
494 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
495 | data = am4_clkctrl_compat_data; | |
496 | else | |
497 | data = am4_clkctrl_data; | |
498 | } | |
499 | ||
500 | if (of_machine_is_compatible("ti,am438x")) { | |
501 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
502 | data = am438x_clkctrl_compat_data; | |
503 | else | |
504 | data = am438x_clkctrl_data; | |
505 | } | |
a3da10b7 | 506 | #endif |
26ca2e97 TK |
507 | #ifdef CONFIG_SOC_TI81XX |
508 | if (of_machine_is_compatible("ti,dm814")) | |
509 | data = dm814_clkctrl_data; | |
50ef5089 TK |
510 | |
511 | if (of_machine_is_compatible("ti,dm816")) | |
512 | data = dm816_clkctrl_data; | |
26ca2e97 | 513 | #endif |
1c881b5a | 514 | |
869decd1 TK |
515 | if (ti_clk_get_features()->flags & TI_CLK_DEVICE_TYPE_GP) |
516 | soc_mask |= CLKF_SOC_NONSEC; | |
517 | ||
88a17252 TK |
518 | while (data->addr) { |
519 | if (addr == data->addr) | |
520 | break; | |
521 | ||
522 | data++; | |
523 | } | |
524 | ||
525 | if (!data->addr) { | |
c2c296c3 | 526 | pr_err("%pOF not found from clkctrl data.\n", node); |
88a17252 TK |
527 | return; |
528 | } | |
529 | ||
530 | provider = kzalloc(sizeof(*provider), GFP_KERNEL); | |
531 | if (!provider) | |
532 | return; | |
533 | ||
534 | provider->base = of_iomap(node, 0); | |
535 | ||
47b00dcf | 536 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) { |
a72d7850 | 537 | provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFnxxx", node->parent); |
47b00dcf TK |
538 | if (!provider->clkdm_name) { |
539 | kfree(provider); | |
540 | return; | |
541 | } | |
542 | ||
543 | /* | |
544 | * Create default clkdm name, replace _cm from end of parent | |
545 | * node name with _clkdm | |
546 | */ | |
d17a718d | 547 | provider->clkdm_name[strlen(provider->clkdm_name) - 2] = 0; |
47b00dcf | 548 | } else { |
a72d7850 | 549 | provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFn", node); |
47b00dcf TK |
550 | if (!provider->clkdm_name) { |
551 | kfree(provider); | |
552 | return; | |
553 | } | |
554 | ||
555 | /* | |
556 | * Create default clkdm name, replace _clkctrl from end of | |
557 | * node name with _clkdm | |
558 | */ | |
47b00dcf | 559 | provider->clkdm_name[strlen(provider->clkdm_name) - 7] = 0; |
ddfb183e TK |
560 | } |
561 | ||
ddfb183e TK |
562 | strcat(provider->clkdm_name, "clkdm"); |
563 | ||
1dc88f78 TK |
564 | /* Replace any dash from the clkdm name with underscore */ |
565 | c = provider->clkdm_name; | |
566 | ||
567 | while (*c) { | |
568 | if (*c == '-') | |
569 | *c = '_'; | |
570 | c++; | |
571 | } | |
572 | ||
88a17252 TK |
573 | INIT_LIST_HEAD(&provider->clocks); |
574 | ||
575 | /* Generate clocks */ | |
576 | reg_data = data->regs; | |
577 | ||
578 | while (reg_data->parent) { | |
2b1202d7 TK |
579 | if ((reg_data->flags & CLKF_SOC_MASK) && |
580 | (reg_data->flags & soc_mask) == 0) { | |
581 | reg_data++; | |
582 | continue; | |
583 | } | |
584 | ||
88a17252 TK |
585 | hw = kzalloc(sizeof(*hw), GFP_KERNEL); |
586 | if (!hw) | |
587 | return; | |
588 | ||
589 | hw->enable_reg.ptr = provider->base + reg_data->offset; | |
590 | ||
591 | _ti_clkctrl_setup_subclks(provider, node, reg_data, | |
592 | hw->enable_reg.ptr); | |
593 | ||
594 | if (reg_data->flags & CLKF_SW_SUP) | |
595 | hw->enable_bit = MODULEMODE_SWCTRL; | |
596 | if (reg_data->flags & CLKF_HW_SUP) | |
597 | hw->enable_bit = MODULEMODE_HWCTRL; | |
598 | if (reg_data->flags & CLKF_NO_IDLEST) | |
599 | hw->flags |= NO_IDLEST; | |
600 | ||
ddfb183e TK |
601 | if (reg_data->clkdm_name) |
602 | hw->clkdm_name = reg_data->clkdm_name; | |
603 | else | |
604 | hw->clkdm_name = provider->clkdm_name; | |
605 | ||
88a17252 TK |
606 | init.parent_names = ®_data->parent; |
607 | init.num_parents = 1; | |
608 | init.flags = 0; | |
49159a9d TK |
609 | if (reg_data->flags & CLKF_SET_RATE_PARENT) |
610 | init.flags |= CLK_SET_RATE_PARENT; | |
47b00dcf | 611 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) |
8a69f1d4 SB |
612 | init.name = kasprintf(GFP_KERNEL, "%pOFn:%pOFn:%04x:%d", |
613 | node->parent, node, | |
47b00dcf TK |
614 | reg_data->offset, 0); |
615 | else | |
8a69f1d4 SB |
616 | init.name = kasprintf(GFP_KERNEL, "%pOFn:%04x:%d", |
617 | node, reg_data->offset, 0); | |
88a17252 TK |
618 | clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); |
619 | if (!init.name || !clkctrl_clk) | |
620 | goto cleanup; | |
621 | ||
622 | init.ops = &omap4_clkctrl_clk_ops; | |
623 | hw->hw.init = &init; | |
624 | ||
625 | clk = ti_clk_register(NULL, &hw->hw, init.name); | |
626 | if (IS_ERR_OR_NULL(clk)) | |
627 | goto cleanup; | |
628 | ||
629 | clkctrl_clk->reg_offset = reg_data->offset; | |
630 | clkctrl_clk->clk = &hw->hw; | |
631 | ||
632 | list_add(&clkctrl_clk->node, &provider->clocks); | |
633 | ||
634 | reg_data++; | |
635 | } | |
636 | ||
729e13bf TK |
637 | ret = of_clk_add_hw_provider(node, _ti_omap4_clkctrl_xlate, provider); |
638 | if (ret == -EPROBE_DEFER) | |
639 | ti_clk_retry_init(node, provider, _clkctrl_add_provider); | |
640 | ||
88a17252 TK |
641 | return; |
642 | ||
643 | cleanup: | |
644 | kfree(hw); | |
645 | kfree(init.name); | |
646 | kfree(clkctrl_clk); | |
647 | } | |
648 | CLK_OF_DECLARE(ti_omap4_clkctrl_clock, "ti,clkctrl", | |
649 | _ti_omap4_clkctrl_setup); |