]>
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 | ||
22a6564f | 27 | #define NO_IDLEST 0 |
88a17252 TK |
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 | ||
2209b72d TK |
37 | #define OMAP4_STBYST_MASK BIT(18) |
38 | #define OMAP4_STBYST_SHIFT 18 | |
39 | ||
88a17252 TK |
40 | #define CLKCTRL_IDLEST_FUNCTIONAL 0x0 |
41 | #define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 | |
42 | #define CLKCTRL_IDLEST_DISABLED 0x3 | |
43 | ||
44 | /* These timeouts are in us */ | |
45 | #define OMAP4_MAX_MODULE_READY_TIME 2000 | |
46 | #define OMAP4_MAX_MODULE_DISABLE_TIME 5000 | |
47 | ||
48 | static bool _early_timeout = true; | |
49 | ||
50 | struct omap_clkctrl_provider { | |
51 | void __iomem *base; | |
52 | struct list_head clocks; | |
ddfb183e | 53 | char *clkdm_name; |
88a17252 TK |
54 | }; |
55 | ||
56 | struct omap_clkctrl_clk { | |
57 | struct clk_hw *clk; | |
58 | u16 reg_offset; | |
59 | int bit_offset; | |
60 | struct list_head node; | |
61 | }; | |
62 | ||
63 | union omap4_timeout { | |
64 | u32 cycles; | |
65 | ktime_t start; | |
66 | }; | |
67 | ||
68 | static const struct omap_clkctrl_data default_clkctrl_data[] __initconst = { | |
69 | { 0 }, | |
70 | }; | |
71 | ||
72 | static u32 _omap4_idlest(u32 val) | |
73 | { | |
74 | val &= OMAP4_IDLEST_MASK; | |
75 | val >>= OMAP4_IDLEST_SHIFT; | |
76 | ||
77 | return val; | |
78 | } | |
79 | ||
80 | static bool _omap4_is_idle(u32 val) | |
81 | { | |
82 | val = _omap4_idlest(val); | |
83 | ||
84 | return val == CLKCTRL_IDLEST_DISABLED; | |
85 | } | |
86 | ||
87 | static bool _omap4_is_ready(u32 val) | |
88 | { | |
89 | val = _omap4_idlest(val); | |
90 | ||
91 | return val == CLKCTRL_IDLEST_FUNCTIONAL || | |
92 | val == CLKCTRL_IDLEST_INTERFACE_IDLE; | |
93 | } | |
94 | ||
95 | static bool _omap4_is_timeout(union omap4_timeout *time, u32 timeout) | |
96 | { | |
3d8598fb TK |
97 | /* |
98 | * There are two special cases where ktime_to_ns() can't be | |
99 | * used to track the timeouts. First one is during early boot | |
100 | * when the timers haven't been initialized yet. The second | |
101 | * one is during suspend-resume cycle while timekeeping is | |
102 | * being suspended / resumed. Clocksource for the system | |
103 | * can be from a timer that requires pm_runtime access, which | |
104 | * will eventually bring us here with timekeeping_suspended, | |
105 | * during both suspend entry and resume paths. This happens | |
81a41901 TL |
106 | * at least on am43xx platform. Account for flakeyness |
107 | * with udelay() by multiplying the timeout value by 2. | |
3d8598fb TK |
108 | */ |
109 | if (unlikely(_early_timeout || timekeeping_suspended)) { | |
88a17252 | 110 | if (time->cycles++ < timeout) { |
81a41901 | 111 | udelay(1 * 2); |
88a17252 TK |
112 | return false; |
113 | } | |
114 | } else { | |
115 | if (!ktime_to_ns(time->start)) { | |
116 | time->start = ktime_get(); | |
117 | return false; | |
118 | } | |
119 | ||
120 | if (ktime_us_delta(ktime_get(), time->start) < timeout) { | |
121 | cpu_relax(); | |
122 | return false; | |
123 | } | |
124 | } | |
125 | ||
126 | return true; | |
127 | } | |
128 | ||
129 | static int __init _omap4_disable_early_timeout(void) | |
130 | { | |
131 | _early_timeout = false; | |
132 | ||
133 | return 0; | |
134 | } | |
135 | arch_initcall(_omap4_disable_early_timeout); | |
136 | ||
137 | static int _omap4_clkctrl_clk_enable(struct clk_hw *hw) | |
138 | { | |
139 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | |
140 | u32 val; | |
141 | int ret; | |
142 | union omap4_timeout timeout = { 0 }; | |
143 | ||
88a17252 TK |
144 | if (clk->clkdm) { |
145 | ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); | |
146 | if (ret) { | |
147 | WARN(1, | |
148 | "%s: could not enable %s's clockdomain %s: %d\n", | |
149 | __func__, clk_hw_get_name(hw), | |
150 | clk->clkdm_name, ret); | |
151 | return ret; | |
152 | } | |
153 | } | |
154 | ||
1cc54078 TL |
155 | if (!clk->enable_bit) |
156 | return 0; | |
157 | ||
88a17252 TK |
158 | val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); |
159 | ||
160 | val &= ~OMAP4_MODULEMODE_MASK; | |
161 | val |= clk->enable_bit; | |
162 | ||
163 | ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); | |
164 | ||
22a6564f | 165 | if (test_bit(NO_IDLEST, &clk->flags)) |
88a17252 TK |
166 | return 0; |
167 | ||
168 | /* Wait until module is enabled */ | |
169 | while (!_omap4_is_ready(ti_clk_ll_ops->clk_readl(&clk->enable_reg))) { | |
170 | if (_omap4_is_timeout(&timeout, OMAP4_MAX_MODULE_READY_TIME)) { | |
171 | pr_err("%s: failed to enable\n", clk_hw_get_name(hw)); | |
172 | return -EBUSY; | |
173 | } | |
174 | } | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | static void _omap4_clkctrl_clk_disable(struct clk_hw *hw) | |
180 | { | |
181 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | |
182 | u32 val; | |
183 | union omap4_timeout timeout = { 0 }; | |
184 | ||
185 | if (!clk->enable_bit) | |
1cc54078 | 186 | goto exit; |
88a17252 TK |
187 | |
188 | val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); | |
189 | ||
190 | val &= ~OMAP4_MODULEMODE_MASK; | |
191 | ||
192 | ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); | |
193 | ||
22a6564f | 194 | if (test_bit(NO_IDLEST, &clk->flags)) |
88a17252 TK |
195 | goto exit; |
196 | ||
197 | /* Wait until module is disabled */ | |
198 | while (!_omap4_is_idle(ti_clk_ll_ops->clk_readl(&clk->enable_reg))) { | |
199 | if (_omap4_is_timeout(&timeout, | |
200 | OMAP4_MAX_MODULE_DISABLE_TIME)) { | |
201 | pr_err("%s: failed to disable\n", clk_hw_get_name(hw)); | |
202 | break; | |
203 | } | |
204 | } | |
205 | ||
206 | exit: | |
207 | if (clk->clkdm) | |
208 | ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); | |
209 | } | |
210 | ||
211 | static int _omap4_clkctrl_clk_is_enabled(struct clk_hw *hw) | |
212 | { | |
213 | struct clk_hw_omap *clk = to_clk_hw_omap(hw); | |
214 | u32 val; | |
215 | ||
216 | val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); | |
217 | ||
218 | if (val & clk->enable_bit) | |
219 | return 1; | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | static const struct clk_ops omap4_clkctrl_clk_ops = { | |
225 | .enable = _omap4_clkctrl_clk_enable, | |
226 | .disable = _omap4_clkctrl_clk_disable, | |
227 | .is_enabled = _omap4_clkctrl_clk_is_enabled, | |
ddfb183e | 228 | .init = omap2_init_clk_clkdm, |
88a17252 TK |
229 | }; |
230 | ||
231 | static struct clk_hw *_ti_omap4_clkctrl_xlate(struct of_phandle_args *clkspec, | |
232 | void *data) | |
233 | { | |
234 | struct omap_clkctrl_provider *provider = data; | |
235 | struct omap_clkctrl_clk *entry; | |
41b3588d | 236 | bool found = false; |
88a17252 TK |
237 | |
238 | if (clkspec->args_count != 2) | |
239 | return ERR_PTR(-EINVAL); | |
240 | ||
241 | pr_debug("%s: looking for %x:%x\n", __func__, | |
242 | clkspec->args[0], clkspec->args[1]); | |
243 | ||
244 | list_for_each_entry(entry, &provider->clocks, node) { | |
245 | if (entry->reg_offset == clkspec->args[0] && | |
41b3588d TL |
246 | entry->bit_offset == clkspec->args[1]) { |
247 | found = true; | |
88a17252 | 248 | break; |
41b3588d | 249 | } |
88a17252 TK |
250 | } |
251 | ||
41b3588d | 252 | if (!found) |
88a17252 TK |
253 | return ERR_PTR(-EINVAL); |
254 | ||
255 | return entry->clk; | |
256 | } | |
257 | ||
85204959 TK |
258 | /* Get clkctrl clock base name based on clkctrl_name or dts node */ |
259 | static const char * __init clkctrl_get_clock_name(struct device_node *np, | |
260 | const char *clkctrl_name, | |
261 | int offset, int index, | |
262 | bool legacy_naming) | |
263 | { | |
264 | char *clock_name; | |
265 | ||
266 | /* l4per-clkctrl:1234:0 style naming based on clkctrl_name */ | |
267 | if (clkctrl_name && !legacy_naming) { | |
268 | clock_name = kasprintf(GFP_KERNEL, "%s-clkctrl:%04x:%d", | |
269 | clkctrl_name, offset, index); | |
270 | strreplace(clock_name, '_', '-'); | |
271 | ||
272 | return clock_name; | |
273 | } | |
274 | ||
275 | /* l4per:1234:0 old style naming based on clkctrl_name */ | |
276 | if (clkctrl_name) | |
277 | return kasprintf(GFP_KERNEL, "%s_cm:clk:%04x:%d", | |
278 | clkctrl_name, offset, index); | |
279 | ||
280 | /* l4per_cm:1234:0 old style naming based on parent node name */ | |
281 | if (legacy_naming) | |
282 | return kasprintf(GFP_KERNEL, "%pOFn:clk:%04x:%d", | |
283 | np->parent, offset, index); | |
284 | ||
285 | /* l4per-clkctrl:1234:0 style naming based on node name */ | |
286 | return kasprintf(GFP_KERNEL, "%pOFn:%04x:%d", np, offset, index); | |
287 | } | |
288 | ||
88a17252 TK |
289 | static int __init |
290 | _ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider, | |
291 | struct device_node *node, struct clk_hw *clk_hw, | |
292 | u16 offset, u8 bit, const char * const *parents, | |
85204959 TK |
293 | int num_parents, const struct clk_ops *ops, |
294 | const char *clkctrl_name) | |
88a17252 TK |
295 | { |
296 | struct clk_init_data init = { NULL }; | |
297 | struct clk *clk; | |
298 | struct omap_clkctrl_clk *clkctrl_clk; | |
299 | int ret = 0; | |
300 | ||
85204959 TK |
301 | init.name = clkctrl_get_clock_name(node, clkctrl_name, offset, bit, |
302 | ti_clk_get_features()->flags & | |
303 | TI_CLK_CLKCTRL_COMPAT); | |
304 | ||
88a17252 TK |
305 | clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); |
306 | if (!init.name || !clkctrl_clk) { | |
307 | ret = -ENOMEM; | |
308 | goto cleanup; | |
309 | } | |
310 | ||
311 | clk_hw->init = &init; | |
312 | init.parent_names = parents; | |
313 | init.num_parents = num_parents; | |
314 | init.ops = ops; | |
8aa09cf3 | 315 | init.flags = 0; |
88a17252 TK |
316 | |
317 | clk = ti_clk_register(NULL, clk_hw, init.name); | |
318 | if (IS_ERR_OR_NULL(clk)) { | |
319 | ret = -EINVAL; | |
320 | goto cleanup; | |
321 | } | |
322 | ||
323 | clkctrl_clk->reg_offset = offset; | |
324 | clkctrl_clk->bit_offset = bit; | |
325 | clkctrl_clk->clk = clk_hw; | |
326 | ||
327 | list_add(&clkctrl_clk->node, &provider->clocks); | |
328 | ||
329 | return 0; | |
330 | ||
331 | cleanup: | |
332 | kfree(init.name); | |
333 | kfree(clkctrl_clk); | |
334 | return ret; | |
335 | } | |
336 | ||
337 | static void __init | |
338 | _ti_clkctrl_setup_gate(struct omap_clkctrl_provider *provider, | |
339 | struct device_node *node, u16 offset, | |
340 | const struct omap_clkctrl_bit_data *data, | |
85204959 | 341 | void __iomem *reg, const char *clkctrl_name) |
88a17252 TK |
342 | { |
343 | struct clk_hw_omap *clk_hw; | |
344 | ||
345 | clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); | |
346 | if (!clk_hw) | |
347 | return; | |
348 | ||
349 | clk_hw->enable_bit = data->bit; | |
350 | clk_hw->enable_reg.ptr = reg; | |
351 | ||
352 | if (_ti_clkctrl_clk_register(provider, node, &clk_hw->hw, offset, | |
353 | data->bit, data->parents, 1, | |
85204959 | 354 | &omap_gate_clk_ops, clkctrl_name)) |
88a17252 TK |
355 | kfree(clk_hw); |
356 | } | |
357 | ||
358 | static void __init | |
359 | _ti_clkctrl_setup_mux(struct omap_clkctrl_provider *provider, | |
360 | struct device_node *node, u16 offset, | |
361 | const struct omap_clkctrl_bit_data *data, | |
85204959 | 362 | void __iomem *reg, const char *clkctrl_name) |
88a17252 TK |
363 | { |
364 | struct clk_omap_mux *mux; | |
365 | int num_parents = 0; | |
366 | const char * const *pname; | |
367 | ||
368 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); | |
369 | if (!mux) | |
370 | return; | |
371 | ||
372 | pname = data->parents; | |
373 | while (*pname) { | |
374 | num_parents++; | |
375 | pname++; | |
376 | } | |
377 | ||
378 | mux->mask = num_parents; | |
49eec6fb TK |
379 | if (!(mux->flags & CLK_MUX_INDEX_ONE)) |
380 | mux->mask--; | |
381 | ||
88a17252 TK |
382 | mux->mask = (1 << fls(mux->mask)) - 1; |
383 | ||
384 | mux->shift = data->bit; | |
385 | mux->reg.ptr = reg; | |
386 | ||
387 | if (_ti_clkctrl_clk_register(provider, node, &mux->hw, offset, | |
388 | data->bit, data->parents, num_parents, | |
85204959 | 389 | &ti_clk_mux_ops, clkctrl_name)) |
88a17252 TK |
390 | kfree(mux); |
391 | } | |
392 | ||
393 | static void __init | |
394 | _ti_clkctrl_setup_div(struct omap_clkctrl_provider *provider, | |
395 | struct device_node *node, u16 offset, | |
396 | const struct omap_clkctrl_bit_data *data, | |
85204959 | 397 | void __iomem *reg, const char *clkctrl_name) |
88a17252 TK |
398 | { |
399 | struct clk_omap_divider *div; | |
400 | const struct omap_clkctrl_div_data *div_data = data->data; | |
49eec6fb | 401 | u8 div_flags = 0; |
88a17252 TK |
402 | |
403 | div = kzalloc(sizeof(*div), GFP_KERNEL); | |
404 | if (!div) | |
405 | return; | |
406 | ||
407 | div->reg.ptr = reg; | |
408 | div->shift = data->bit; | |
49eec6fb TK |
409 | div->flags = div_data->flags; |
410 | ||
411 | if (div->flags & CLK_DIVIDER_POWER_OF_TWO) | |
412 | div_flags |= CLKF_INDEX_POWER_OF_TWO; | |
88a17252 | 413 | |
49eec6fb TK |
414 | if (ti_clk_parse_divider_data((int *)div_data->dividers, 0, |
415 | div_data->max_div, div_flags, | |
a229965c | 416 | div)) { |
c2c296c3 TK |
417 | pr_err("%s: Data parsing for %pOF:%04x:%d failed\n", __func__, |
418 | node, offset, data->bit); | |
88a17252 TK |
419 | kfree(div); |
420 | return; | |
421 | } | |
422 | ||
423 | if (_ti_clkctrl_clk_register(provider, node, &div->hw, offset, | |
424 | data->bit, data->parents, 1, | |
85204959 | 425 | &ti_clk_divider_ops, clkctrl_name)) |
88a17252 TK |
426 | kfree(div); |
427 | } | |
428 | ||
429 | static void __init | |
430 | _ti_clkctrl_setup_subclks(struct omap_clkctrl_provider *provider, | |
431 | struct device_node *node, | |
432 | const struct omap_clkctrl_reg_data *data, | |
85204959 | 433 | void __iomem *reg, const char *clkctrl_name) |
88a17252 TK |
434 | { |
435 | const struct omap_clkctrl_bit_data *bits = data->bit_data; | |
436 | ||
437 | if (!bits) | |
438 | return; | |
439 | ||
440 | while (bits->bit) { | |
441 | switch (bits->type) { | |
442 | case TI_CLK_GATE: | |
443 | _ti_clkctrl_setup_gate(provider, node, data->offset, | |
85204959 | 444 | bits, reg, clkctrl_name); |
88a17252 TK |
445 | break; |
446 | ||
447 | case TI_CLK_DIVIDER: | |
448 | _ti_clkctrl_setup_div(provider, node, data->offset, | |
85204959 | 449 | bits, reg, clkctrl_name); |
88a17252 TK |
450 | break; |
451 | ||
452 | case TI_CLK_MUX: | |
453 | _ti_clkctrl_setup_mux(provider, node, data->offset, | |
85204959 | 454 | bits, reg, clkctrl_name); |
88a17252 TK |
455 | break; |
456 | ||
457 | default: | |
458 | pr_err("%s: bad subclk type: %d\n", __func__, | |
459 | bits->type); | |
460 | return; | |
461 | } | |
462 | bits++; | |
463 | } | |
464 | } | |
465 | ||
729e13bf TK |
466 | static void __init _clkctrl_add_provider(void *data, |
467 | struct device_node *np) | |
468 | { | |
469 | of_clk_add_hw_provider(np, _ti_omap4_clkctrl_xlate, data); | |
470 | } | |
471 | ||
6c309052 TL |
472 | /* Get clock name based on compatible string for clkctrl */ |
473 | static char * __init clkctrl_get_name(struct device_node *np) | |
474 | { | |
475 | struct property *prop; | |
476 | const int prefix_len = 11; | |
477 | const char *compat; | |
478 | char *name; | |
479 | ||
480 | of_property_for_each_string(np, "compatible", prop, compat) { | |
481 | if (!strncmp("ti,clkctrl-", compat, prefix_len)) { | |
482 | /* Two letter minimum name length for l3, l4 etc */ | |
483 | if (strnlen(compat + prefix_len, 16) < 2) | |
484 | continue; | |
485 | name = kasprintf(GFP_KERNEL, "%s", compat + prefix_len); | |
486 | if (!name) | |
487 | continue; | |
488 | strreplace(name, '-', '_'); | |
489 | ||
490 | return name; | |
491 | } | |
492 | } | |
6c309052 TL |
493 | |
494 | return NULL; | |
495 | } | |
496 | ||
88a17252 TK |
497 | static void __init _ti_omap4_clkctrl_setup(struct device_node *node) |
498 | { | |
499 | struct omap_clkctrl_provider *provider; | |
500 | const struct omap_clkctrl_data *data = default_clkctrl_data; | |
501 | const struct omap_clkctrl_reg_data *reg_data; | |
502 | struct clk_init_data init = { NULL }; | |
503 | struct clk_hw_omap *hw; | |
504 | struct clk *clk; | |
6c309052 | 505 | struct omap_clkctrl_clk *clkctrl_clk = NULL; |
88a17252 | 506 | const __be32 *addrp; |
6c309052 TL |
507 | bool legacy_naming; |
508 | char *clkctrl_name; | |
88a17252 | 509 | u32 addr; |
729e13bf | 510 | int ret; |
1dc88f78 | 511 | char *c; |
2b1202d7 | 512 | u16 soc_mask = 0; |
88a17252 | 513 | |
47b00dcf | 514 | if (!(ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) && |
87ab1151 | 515 | of_node_name_eq(node, "clk")) |
47b00dcf | 516 | ti_clk_features.flags |= TI_CLK_CLKCTRL_COMPAT; |
88a17252 TK |
517 | |
518 | addrp = of_get_address(node, 0, NULL, NULL); | |
519 | addr = (u32)of_translate_address(node, addrp); | |
520 | ||
1c881b5a TK |
521 | #ifdef CONFIG_ARCH_OMAP4 |
522 | if (of_machine_is_compatible("ti,omap4")) | |
523 | data = omap4_clkctrl_data; | |
524 | #endif | |
0ad902f6 TK |
525 | #ifdef CONFIG_SOC_OMAP5 |
526 | if (of_machine_is_compatible("ti,omap5")) | |
527 | data = omap5_clkctrl_data; | |
528 | #endif | |
24d504a3 | 529 | #ifdef CONFIG_SOC_DRA7XX |
dffa9051 TK |
530 | if (of_machine_is_compatible("ti,dra7")) { |
531 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
532 | data = dra7_clkctrl_compat_data; | |
533 | else | |
534 | data = dra7_clkctrl_data; | |
535 | } | |
2b1202d7 TK |
536 | |
537 | if (of_machine_is_compatible("ti,dra72")) | |
538 | soc_mask = CLKF_SOC_DRA72; | |
539 | if (of_machine_is_compatible("ti,dra74")) | |
540 | soc_mask = CLKF_SOC_DRA74; | |
541 | if (of_machine_is_compatible("ti,dra76")) | |
542 | soc_mask = CLKF_SOC_DRA76; | |
24d504a3 | 543 | #endif |
df54bfc5 | 544 | #ifdef CONFIG_SOC_AM33XX |
296e583e TK |
545 | if (of_machine_is_compatible("ti,am33xx")) { |
546 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
547 | data = am3_clkctrl_compat_data; | |
548 | else | |
549 | data = am3_clkctrl_data; | |
550 | } | |
df54bfc5 | 551 | #endif |
a3da10b7 | 552 | #ifdef CONFIG_SOC_AM43XX |
76a1049b TK |
553 | if (of_machine_is_compatible("ti,am4372")) { |
554 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
555 | data = am4_clkctrl_compat_data; | |
556 | else | |
557 | data = am4_clkctrl_data; | |
558 | } | |
559 | ||
560 | if (of_machine_is_compatible("ti,am438x")) { | |
561 | if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) | |
562 | data = am438x_clkctrl_compat_data; | |
563 | else | |
564 | data = am438x_clkctrl_data; | |
565 | } | |
a3da10b7 | 566 | #endif |
26ca2e97 TK |
567 | #ifdef CONFIG_SOC_TI81XX |
568 | if (of_machine_is_compatible("ti,dm814")) | |
569 | data = dm814_clkctrl_data; | |
50ef5089 TK |
570 | |
571 | if (of_machine_is_compatible("ti,dm816")) | |
572 | data = dm816_clkctrl_data; | |
26ca2e97 | 573 | #endif |
1c881b5a | 574 | |
869decd1 TK |
575 | if (ti_clk_get_features()->flags & TI_CLK_DEVICE_TYPE_GP) |
576 | soc_mask |= CLKF_SOC_NONSEC; | |
577 | ||
88a17252 TK |
578 | while (data->addr) { |
579 | if (addr == data->addr) | |
580 | break; | |
581 | ||
582 | data++; | |
583 | } | |
584 | ||
585 | if (!data->addr) { | |
c2c296c3 | 586 | pr_err("%pOF not found from clkctrl data.\n", node); |
88a17252 TK |
587 | return; |
588 | } | |
589 | ||
590 | provider = kzalloc(sizeof(*provider), GFP_KERNEL); | |
591 | if (!provider) | |
592 | return; | |
593 | ||
594 | provider->base = of_iomap(node, 0); | |
595 | ||
6c309052 TL |
596 | legacy_naming = ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT; |
597 | clkctrl_name = clkctrl_get_name(node); | |
598 | if (clkctrl_name) { | |
599 | provider->clkdm_name = kasprintf(GFP_KERNEL, | |
600 | "%s_clkdm", clkctrl_name); | |
601 | goto clkdm_found; | |
602 | } | |
603 | ||
604 | /* | |
605 | * The code below can be removed when all clkctrl nodes use domain | |
606 | * specific compatible proprerty and standard clock node naming | |
607 | */ | |
608 | if (legacy_naming) { | |
a72d7850 | 609 | provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFnxxx", node->parent); |
47b00dcf TK |
610 | if (!provider->clkdm_name) { |
611 | kfree(provider); | |
612 | return; | |
613 | } | |
614 | ||
615 | /* | |
616 | * Create default clkdm name, replace _cm from end of parent | |
617 | * node name with _clkdm | |
618 | */ | |
d17a718d | 619 | provider->clkdm_name[strlen(provider->clkdm_name) - 2] = 0; |
47b00dcf | 620 | } else { |
a72d7850 | 621 | provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFn", node); |
47b00dcf TK |
622 | if (!provider->clkdm_name) { |
623 | kfree(provider); | |
624 | return; | |
625 | } | |
626 | ||
627 | /* | |
628 | * Create default clkdm name, replace _clkctrl from end of | |
629 | * node name with _clkdm | |
630 | */ | |
47b00dcf | 631 | provider->clkdm_name[strlen(provider->clkdm_name) - 7] = 0; |
ddfb183e TK |
632 | } |
633 | ||
ddfb183e TK |
634 | strcat(provider->clkdm_name, "clkdm"); |
635 | ||
1dc88f78 TK |
636 | /* Replace any dash from the clkdm name with underscore */ |
637 | c = provider->clkdm_name; | |
638 | ||
639 | while (*c) { | |
640 | if (*c == '-') | |
641 | *c = '_'; | |
642 | c++; | |
643 | } | |
6c309052 | 644 | clkdm_found: |
88a17252 TK |
645 | INIT_LIST_HEAD(&provider->clocks); |
646 | ||
647 | /* Generate clocks */ | |
648 | reg_data = data->regs; | |
649 | ||
650 | while (reg_data->parent) { | |
2b1202d7 TK |
651 | if ((reg_data->flags & CLKF_SOC_MASK) && |
652 | (reg_data->flags & soc_mask) == 0) { | |
653 | reg_data++; | |
654 | continue; | |
655 | } | |
656 | ||
88a17252 TK |
657 | hw = kzalloc(sizeof(*hw), GFP_KERNEL); |
658 | if (!hw) | |
659 | return; | |
660 | ||
661 | hw->enable_reg.ptr = provider->base + reg_data->offset; | |
662 | ||
663 | _ti_clkctrl_setup_subclks(provider, node, reg_data, | |
85204959 | 664 | hw->enable_reg.ptr, clkctrl_name); |
88a17252 TK |
665 | |
666 | if (reg_data->flags & CLKF_SW_SUP) | |
667 | hw->enable_bit = MODULEMODE_SWCTRL; | |
668 | if (reg_data->flags & CLKF_HW_SUP) | |
669 | hw->enable_bit = MODULEMODE_HWCTRL; | |
670 | if (reg_data->flags & CLKF_NO_IDLEST) | |
22a6564f | 671 | set_bit(NO_IDLEST, &hw->flags); |
88a17252 | 672 | |
ddfb183e TK |
673 | if (reg_data->clkdm_name) |
674 | hw->clkdm_name = reg_data->clkdm_name; | |
675 | else | |
676 | hw->clkdm_name = provider->clkdm_name; | |
677 | ||
88a17252 TK |
678 | init.parent_names = ®_data->parent; |
679 | init.num_parents = 1; | |
680 | init.flags = 0; | |
49159a9d TK |
681 | if (reg_data->flags & CLKF_SET_RATE_PARENT) |
682 | init.flags |= CLK_SET_RATE_PARENT; | |
6c309052 TL |
683 | |
684 | init.name = clkctrl_get_clock_name(node, clkctrl_name, | |
685 | reg_data->offset, 0, | |
686 | legacy_naming); | |
687 | if (!init.name) | |
688 | goto cleanup; | |
689 | ||
88a17252 | 690 | clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); |
6c309052 | 691 | if (!clkctrl_clk) |
88a17252 TK |
692 | goto cleanup; |
693 | ||
694 | init.ops = &omap4_clkctrl_clk_ops; | |
695 | hw->hw.init = &init; | |
696 | ||
581eb61a | 697 | clk = ti_clk_register_omap_hw(NULL, &hw->hw, init.name); |
88a17252 TK |
698 | if (IS_ERR_OR_NULL(clk)) |
699 | goto cleanup; | |
700 | ||
701 | clkctrl_clk->reg_offset = reg_data->offset; | |
702 | clkctrl_clk->clk = &hw->hw; | |
703 | ||
704 | list_add(&clkctrl_clk->node, &provider->clocks); | |
705 | ||
706 | reg_data++; | |
707 | } | |
708 | ||
729e13bf TK |
709 | ret = of_clk_add_hw_provider(node, _ti_omap4_clkctrl_xlate, provider); |
710 | if (ret == -EPROBE_DEFER) | |
711 | ti_clk_retry_init(node, provider, _clkctrl_add_provider); | |
712 | ||
6c309052 TL |
713 | kfree(clkctrl_name); |
714 | ||
88a17252 TK |
715 | return; |
716 | ||
717 | cleanup: | |
718 | kfree(hw); | |
719 | kfree(init.name); | |
6c309052 | 720 | kfree(clkctrl_name); |
88a17252 TK |
721 | kfree(clkctrl_clk); |
722 | } | |
723 | CLK_OF_DECLARE(ti_omap4_clkctrl_clock, "ti,clkctrl", | |
724 | _ti_omap4_clkctrl_setup); | |
2209b72d TK |
725 | |
726 | /** | |
727 | * ti_clk_is_in_standby - Check if clkctrl clock is in standby or not | |
728 | * @clk: clock to check standby status for | |
729 | * | |
730 | * Finds whether the provided clock is in standby mode or not. Returns | |
731 | * true if the provided clock is a clkctrl type clock and it is in standby, | |
732 | * false otherwise. | |
733 | */ | |
734 | bool ti_clk_is_in_standby(struct clk *clk) | |
735 | { | |
736 | struct clk_hw *hw; | |
737 | struct clk_hw_omap *hwclk; | |
738 | u32 val; | |
739 | ||
740 | hw = __clk_get_hw(clk); | |
741 | ||
742 | if (!omap2_clk_is_hw_omap(hw)) | |
743 | return false; | |
744 | ||
745 | hwclk = to_clk_hw_omap(hw); | |
746 | ||
747 | val = ti_clk_ll_ops->clk_readl(&hwclk->enable_reg); | |
748 | ||
749 | if (val & OMAP4_STBYST_MASK) | |
750 | return true; | |
751 | ||
752 | return false; | |
753 | } | |
754 | EXPORT_SYMBOL_GPL(ti_clk_is_in_standby); |