]>
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; | |
232 | ||
233 | if (clkspec->args_count != 2) | |
234 | return ERR_PTR(-EINVAL); | |
235 | ||
236 | pr_debug("%s: looking for %x:%x\n", __func__, | |
237 | clkspec->args[0], clkspec->args[1]); | |
238 | ||
239 | list_for_each_entry(entry, &provider->clocks, node) { | |
240 | if (entry->reg_offset == clkspec->args[0] && | |
241 | entry->bit_offset == clkspec->args[1]) | |
242 | break; | |
243 | } | |
244 | ||
245 | if (!entry) | |
246 | return ERR_PTR(-EINVAL); | |
247 | ||
248 | return entry->clk; | |
249 | } | |
250 | ||
251 | static int __init | |
252 | _ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider, | |
253 | struct device_node *node, struct clk_hw *clk_hw, | |
254 | u16 offset, u8 bit, const char * const *parents, | |
255 | int num_parents, const struct clk_ops *ops) | |
256 | { | |
257 | struct clk_init_data init = { NULL }; | |
258 | struct clk *clk; | |
259 | struct omap_clkctrl_clk *clkctrl_clk; | |
260 | int ret = 0; | |
261 | ||
47b00dcf | 262 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) |
8a69f1d4 SB |
263 | init.name = kasprintf(GFP_KERNEL, "%pOFn:%pOFn:%04x:%d", |
264 | node->parent, node, offset, | |
47b00dcf TK |
265 | bit); |
266 | else | |
8a69f1d4 | 267 | init.name = kasprintf(GFP_KERNEL, "%pOFn:%04x:%d", node, |
47b00dcf | 268 | offset, bit); |
88a17252 TK |
269 | clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); |
270 | if (!init.name || !clkctrl_clk) { | |
271 | ret = -ENOMEM; | |
272 | goto cleanup; | |
273 | } | |
274 | ||
275 | clk_hw->init = &init; | |
276 | init.parent_names = parents; | |
277 | init.num_parents = num_parents; | |
278 | init.ops = ops; | |
8aa09cf3 | 279 | init.flags = 0; |
88a17252 TK |
280 | |
281 | clk = ti_clk_register(NULL, clk_hw, init.name); | |
282 | if (IS_ERR_OR_NULL(clk)) { | |
283 | ret = -EINVAL; | |
284 | goto cleanup; | |
285 | } | |
286 | ||
287 | clkctrl_clk->reg_offset = offset; | |
288 | clkctrl_clk->bit_offset = bit; | |
289 | clkctrl_clk->clk = clk_hw; | |
290 | ||
291 | list_add(&clkctrl_clk->node, &provider->clocks); | |
292 | ||
293 | return 0; | |
294 | ||
295 | cleanup: | |
296 | kfree(init.name); | |
297 | kfree(clkctrl_clk); | |
298 | return ret; | |
299 | } | |
300 | ||
301 | static void __init | |
302 | _ti_clkctrl_setup_gate(struct omap_clkctrl_provider *provider, | |
303 | struct device_node *node, u16 offset, | |
304 | const struct omap_clkctrl_bit_data *data, | |
305 | void __iomem *reg) | |
306 | { | |
307 | struct clk_hw_omap *clk_hw; | |
308 | ||
309 | clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); | |
310 | if (!clk_hw) | |
311 | return; | |
312 | ||
313 | clk_hw->enable_bit = data->bit; | |
314 | clk_hw->enable_reg.ptr = reg; | |
315 | ||
316 | if (_ti_clkctrl_clk_register(provider, node, &clk_hw->hw, offset, | |
317 | data->bit, data->parents, 1, | |
318 | &omap_gate_clk_ops)) | |
319 | kfree(clk_hw); | |
320 | } | |
321 | ||
322 | static void __init | |
323 | _ti_clkctrl_setup_mux(struct omap_clkctrl_provider *provider, | |
324 | struct device_node *node, u16 offset, | |
325 | const struct omap_clkctrl_bit_data *data, | |
326 | void __iomem *reg) | |
327 | { | |
328 | struct clk_omap_mux *mux; | |
329 | int num_parents = 0; | |
330 | const char * const *pname; | |
331 | ||
332 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); | |
333 | if (!mux) | |
334 | return; | |
335 | ||
336 | pname = data->parents; | |
337 | while (*pname) { | |
338 | num_parents++; | |
339 | pname++; | |
340 | } | |
341 | ||
342 | mux->mask = num_parents; | |
49eec6fb TK |
343 | if (!(mux->flags & CLK_MUX_INDEX_ONE)) |
344 | mux->mask--; | |
345 | ||
88a17252 TK |
346 | mux->mask = (1 << fls(mux->mask)) - 1; |
347 | ||
348 | mux->shift = data->bit; | |
349 | mux->reg.ptr = reg; | |
350 | ||
351 | if (_ti_clkctrl_clk_register(provider, node, &mux->hw, offset, | |
352 | data->bit, data->parents, num_parents, | |
353 | &ti_clk_mux_ops)) | |
354 | kfree(mux); | |
355 | } | |
356 | ||
357 | static void __init | |
358 | _ti_clkctrl_setup_div(struct omap_clkctrl_provider *provider, | |
359 | struct device_node *node, u16 offset, | |
360 | const struct omap_clkctrl_bit_data *data, | |
361 | void __iomem *reg) | |
362 | { | |
363 | struct clk_omap_divider *div; | |
364 | const struct omap_clkctrl_div_data *div_data = data->data; | |
49eec6fb | 365 | u8 div_flags = 0; |
88a17252 TK |
366 | |
367 | div = kzalloc(sizeof(*div), GFP_KERNEL); | |
368 | if (!div) | |
369 | return; | |
370 | ||
371 | div->reg.ptr = reg; | |
372 | div->shift = data->bit; | |
49eec6fb TK |
373 | div->flags = div_data->flags; |
374 | ||
375 | if (div->flags & CLK_DIVIDER_POWER_OF_TWO) | |
376 | div_flags |= CLKF_INDEX_POWER_OF_TWO; | |
88a17252 | 377 | |
49eec6fb TK |
378 | if (ti_clk_parse_divider_data((int *)div_data->dividers, 0, |
379 | div_data->max_div, div_flags, | |
88a17252 | 380 | &div->width, &div->table)) { |
c2c296c3 TK |
381 | pr_err("%s: Data parsing for %pOF:%04x:%d failed\n", __func__, |
382 | node, offset, data->bit); | |
88a17252 TK |
383 | kfree(div); |
384 | return; | |
385 | } | |
386 | ||
387 | if (_ti_clkctrl_clk_register(provider, node, &div->hw, offset, | |
388 | data->bit, data->parents, 1, | |
389 | &ti_clk_divider_ops)) | |
390 | kfree(div); | |
391 | } | |
392 | ||
393 | static void __init | |
394 | _ti_clkctrl_setup_subclks(struct omap_clkctrl_provider *provider, | |
395 | struct device_node *node, | |
396 | const struct omap_clkctrl_reg_data *data, | |
397 | void __iomem *reg) | |
398 | { | |
399 | const struct omap_clkctrl_bit_data *bits = data->bit_data; | |
400 | ||
401 | if (!bits) | |
402 | return; | |
403 | ||
404 | while (bits->bit) { | |
405 | switch (bits->type) { | |
406 | case TI_CLK_GATE: | |
407 | _ti_clkctrl_setup_gate(provider, node, data->offset, | |
408 | bits, reg); | |
409 | break; | |
410 | ||
411 | case TI_CLK_DIVIDER: | |
412 | _ti_clkctrl_setup_div(provider, node, data->offset, | |
413 | bits, reg); | |
414 | break; | |
415 | ||
416 | case TI_CLK_MUX: | |
417 | _ti_clkctrl_setup_mux(provider, node, data->offset, | |
418 | bits, reg); | |
419 | break; | |
420 | ||
421 | default: | |
422 | pr_err("%s: bad subclk type: %d\n", __func__, | |
423 | bits->type); | |
424 | return; | |
425 | } | |
426 | bits++; | |
427 | } | |
428 | } | |
429 | ||
729e13bf TK |
430 | static void __init _clkctrl_add_provider(void *data, |
431 | struct device_node *np) | |
432 | { | |
433 | of_clk_add_hw_provider(np, _ti_omap4_clkctrl_xlate, data); | |
434 | } | |
435 | ||
88a17252 TK |
436 | static void __init _ti_omap4_clkctrl_setup(struct device_node *node) |
437 | { | |
438 | struct omap_clkctrl_provider *provider; | |
439 | const struct omap_clkctrl_data *data = default_clkctrl_data; | |
440 | const struct omap_clkctrl_reg_data *reg_data; | |
441 | struct clk_init_data init = { NULL }; | |
442 | struct clk_hw_omap *hw; | |
443 | struct clk *clk; | |
444 | struct omap_clkctrl_clk *clkctrl_clk; | |
445 | const __be32 *addrp; | |
446 | u32 addr; | |
729e13bf | 447 | int ret; |
1dc88f78 | 448 | char *c; |
2b1202d7 | 449 | u16 soc_mask = 0; |
88a17252 | 450 | |
47b00dcf | 451 | if (!(ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) && |
87ab1151 | 452 | of_node_name_eq(node, "clk")) |
47b00dcf | 453 | ti_clk_features.flags |= TI_CLK_CLKCTRL_COMPAT; |
88a17252 TK |
454 | |
455 | addrp = of_get_address(node, 0, NULL, NULL); | |
456 | addr = (u32)of_translate_address(node, addrp); | |
457 | ||
1c881b5a TK |
458 | #ifdef CONFIG_ARCH_OMAP4 |
459 | if (of_machine_is_compatible("ti,omap4")) | |
460 | data = omap4_clkctrl_data; | |
461 | #endif | |
0ad902f6 TK |
462 | #ifdef CONFIG_SOC_OMAP5 |
463 | if (of_machine_is_compatible("ti,omap5")) | |
464 | data = omap5_clkctrl_data; | |
465 | #endif | |
24d504a3 | 466 | #ifdef CONFIG_SOC_DRA7XX |
dffa9051 TK |
467 | if (of_machine_is_compatible("ti,dra7")) { |
468 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
469 | data = dra7_clkctrl_compat_data; | |
470 | else | |
471 | data = dra7_clkctrl_data; | |
472 | } | |
2b1202d7 TK |
473 | |
474 | if (of_machine_is_compatible("ti,dra72")) | |
475 | soc_mask = CLKF_SOC_DRA72; | |
476 | if (of_machine_is_compatible("ti,dra74")) | |
477 | soc_mask = CLKF_SOC_DRA74; | |
478 | if (of_machine_is_compatible("ti,dra76")) | |
479 | soc_mask = CLKF_SOC_DRA76; | |
24d504a3 | 480 | #endif |
df54bfc5 | 481 | #ifdef CONFIG_SOC_AM33XX |
296e583e TK |
482 | if (of_machine_is_compatible("ti,am33xx")) { |
483 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
484 | data = am3_clkctrl_compat_data; | |
485 | else | |
486 | data = am3_clkctrl_data; | |
487 | } | |
df54bfc5 | 488 | #endif |
a3da10b7 | 489 | #ifdef CONFIG_SOC_AM43XX |
76a1049b TK |
490 | if (of_machine_is_compatible("ti,am4372")) { |
491 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
492 | data = am4_clkctrl_compat_data; | |
493 | else | |
494 | data = am4_clkctrl_data; | |
495 | } | |
496 | ||
497 | if (of_machine_is_compatible("ti,am438x")) { | |
498 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
499 | data = am438x_clkctrl_compat_data; | |
500 | else | |
501 | data = am438x_clkctrl_data; | |
502 | } | |
a3da10b7 | 503 | #endif |
26ca2e97 TK |
504 | #ifdef CONFIG_SOC_TI81XX |
505 | if (of_machine_is_compatible("ti,dm814")) | |
506 | data = dm814_clkctrl_data; | |
50ef5089 TK |
507 | |
508 | if (of_machine_is_compatible("ti,dm816")) | |
509 | data = dm816_clkctrl_data; | |
26ca2e97 | 510 | #endif |
1c881b5a | 511 | |
869decd1 TK |
512 | if (ti_clk_get_features()->flags & TI_CLK_DEVICE_TYPE_GP) |
513 | soc_mask |= CLKF_SOC_NONSEC; | |
514 | ||
88a17252 TK |
515 | while (data->addr) { |
516 | if (addr == data->addr) | |
517 | break; | |
518 | ||
519 | data++; | |
520 | } | |
521 | ||
522 | if (!data->addr) { | |
c2c296c3 | 523 | pr_err("%pOF not found from clkctrl data.\n", node); |
88a17252 TK |
524 | return; |
525 | } | |
526 | ||
527 | provider = kzalloc(sizeof(*provider), GFP_KERNEL); | |
528 | if (!provider) | |
529 | return; | |
530 | ||
531 | provider->base = of_iomap(node, 0); | |
532 | ||
47b00dcf | 533 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) { |
a72d7850 | 534 | provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFnxxx", node->parent); |
47b00dcf TK |
535 | if (!provider->clkdm_name) { |
536 | kfree(provider); | |
537 | return; | |
538 | } | |
539 | ||
540 | /* | |
541 | * Create default clkdm name, replace _cm from end of parent | |
542 | * node name with _clkdm | |
543 | */ | |
d17a718d | 544 | provider->clkdm_name[strlen(provider->clkdm_name) - 2] = 0; |
47b00dcf | 545 | } else { |
a72d7850 | 546 | provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFn", node); |
47b00dcf TK |
547 | if (!provider->clkdm_name) { |
548 | kfree(provider); | |
549 | return; | |
550 | } | |
551 | ||
552 | /* | |
553 | * Create default clkdm name, replace _clkctrl from end of | |
554 | * node name with _clkdm | |
555 | */ | |
47b00dcf | 556 | provider->clkdm_name[strlen(provider->clkdm_name) - 7] = 0; |
ddfb183e TK |
557 | } |
558 | ||
ddfb183e TK |
559 | strcat(provider->clkdm_name, "clkdm"); |
560 | ||
1dc88f78 TK |
561 | /* Replace any dash from the clkdm name with underscore */ |
562 | c = provider->clkdm_name; | |
563 | ||
564 | while (*c) { | |
565 | if (*c == '-') | |
566 | *c = '_'; | |
567 | c++; | |
568 | } | |
569 | ||
88a17252 TK |
570 | INIT_LIST_HEAD(&provider->clocks); |
571 | ||
572 | /* Generate clocks */ | |
573 | reg_data = data->regs; | |
574 | ||
575 | while (reg_data->parent) { | |
2b1202d7 TK |
576 | if ((reg_data->flags & CLKF_SOC_MASK) && |
577 | (reg_data->flags & soc_mask) == 0) { | |
578 | reg_data++; | |
579 | continue; | |
580 | } | |
581 | ||
88a17252 TK |
582 | hw = kzalloc(sizeof(*hw), GFP_KERNEL); |
583 | if (!hw) | |
584 | return; | |
585 | ||
586 | hw->enable_reg.ptr = provider->base + reg_data->offset; | |
587 | ||
588 | _ti_clkctrl_setup_subclks(provider, node, reg_data, | |
589 | hw->enable_reg.ptr); | |
590 | ||
591 | if (reg_data->flags & CLKF_SW_SUP) | |
592 | hw->enable_bit = MODULEMODE_SWCTRL; | |
593 | if (reg_data->flags & CLKF_HW_SUP) | |
594 | hw->enable_bit = MODULEMODE_HWCTRL; | |
595 | if (reg_data->flags & CLKF_NO_IDLEST) | |
596 | hw->flags |= NO_IDLEST; | |
597 | ||
ddfb183e TK |
598 | if (reg_data->clkdm_name) |
599 | hw->clkdm_name = reg_data->clkdm_name; | |
600 | else | |
601 | hw->clkdm_name = provider->clkdm_name; | |
602 | ||
88a17252 TK |
603 | init.parent_names = ®_data->parent; |
604 | init.num_parents = 1; | |
605 | init.flags = 0; | |
49159a9d TK |
606 | if (reg_data->flags & CLKF_SET_RATE_PARENT) |
607 | init.flags |= CLK_SET_RATE_PARENT; | |
47b00dcf | 608 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) |
8a69f1d4 SB |
609 | init.name = kasprintf(GFP_KERNEL, "%pOFn:%pOFn:%04x:%d", |
610 | node->parent, node, | |
47b00dcf TK |
611 | reg_data->offset, 0); |
612 | else | |
8a69f1d4 SB |
613 | init.name = kasprintf(GFP_KERNEL, "%pOFn:%04x:%d", |
614 | node, reg_data->offset, 0); | |
88a17252 TK |
615 | clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); |
616 | if (!init.name || !clkctrl_clk) | |
617 | goto cleanup; | |
618 | ||
619 | init.ops = &omap4_clkctrl_clk_ops; | |
620 | hw->hw.init = &init; | |
621 | ||
622 | clk = ti_clk_register(NULL, &hw->hw, init.name); | |
623 | if (IS_ERR_OR_NULL(clk)) | |
624 | goto cleanup; | |
625 | ||
626 | clkctrl_clk->reg_offset = reg_data->offset; | |
627 | clkctrl_clk->clk = &hw->hw; | |
628 | ||
629 | list_add(&clkctrl_clk->node, &provider->clocks); | |
630 | ||
631 | reg_data++; | |
632 | } | |
633 | ||
729e13bf TK |
634 | ret = of_clk_add_hw_provider(node, _ti_omap4_clkctrl_xlate, provider); |
635 | if (ret == -EPROBE_DEFER) | |
636 | ti_clk_retry_init(node, provider, _clkctrl_add_provider); | |
637 | ||
88a17252 TK |
638 | return; |
639 | ||
640 | cleanup: | |
641 | kfree(hw); | |
642 | kfree(init.name); | |
643 | kfree(clkctrl_clk); | |
644 | } | |
645 | CLK_OF_DECLARE(ti_omap4_clkctrl_clock, "ti,clkctrl", | |
646 | _ti_omap4_clkctrl_setup); |