Commit | Line | Data |
---|---|---|
a8aceccb TK |
1 | /* |
2 | * TI clock support | |
3 | * | |
4 | * Copyright (C) 2013 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 | ||
1b29e601 | 18 | #include <linux/clk.h> |
a8aceccb TK |
19 | #include <linux/clk-provider.h> |
20 | #include <linux/clkdev.h> | |
21 | #include <linux/clk/ti.h> | |
22 | #include <linux/of.h> | |
819b4861 TK |
23 | #include <linux/of_address.h> |
24 | #include <linux/list.h> | |
989feafb TK |
25 | #include <linux/regmap.h> |
26 | #include <linux/bootmem.h> | |
a8aceccb | 27 | |
c82f8957 TK |
28 | #include "clock.h" |
29 | ||
a8aceccb TK |
30 | #undef pr_fmt |
31 | #define pr_fmt(fmt) "%s: " fmt, __func__ | |
32 | ||
819b4861 | 33 | struct ti_clk_ll_ops *ti_clk_ll_ops; |
c08ee14c | 34 | static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS]; |
819b4861 | 35 | |
3fe6d697 | 36 | static struct ti_clk_features ti_clk_features; |
e9e63088 | 37 | |
989feafb TK |
38 | struct clk_iomap { |
39 | struct regmap *regmap; | |
40 | void __iomem *mem; | |
41 | }; | |
42 | ||
43 | static struct clk_iomap *clk_memmaps[CLK_MAX_MEMMAPS]; | |
44 | ||
45 | static void clk_memmap_writel(u32 val, void __iomem *reg) | |
46 | { | |
47 | struct clk_omap_reg *r = (struct clk_omap_reg *)® | |
48 | struct clk_iomap *io = clk_memmaps[r->index]; | |
49 | ||
50 | if (io->regmap) | |
51 | regmap_write(io->regmap, r->offset, val); | |
52 | else | |
53 | writel_relaxed(val, io->mem + r->offset); | |
54 | } | |
55 | ||
56 | static u32 clk_memmap_readl(void __iomem *reg) | |
57 | { | |
58 | u32 val; | |
59 | struct clk_omap_reg *r = (struct clk_omap_reg *)® | |
60 | struct clk_iomap *io = clk_memmaps[r->index]; | |
61 | ||
62 | if (io->regmap) | |
63 | regmap_read(io->regmap, r->offset, &val); | |
64 | else | |
65 | val = readl_relaxed(io->mem + r->offset); | |
66 | ||
67 | return val; | |
68 | } | |
69 | ||
e9e63088 TK |
70 | /** |
71 | * ti_clk_setup_ll_ops - setup low level clock operations | |
72 | * @ops: low level clock ops descriptor | |
73 | * | |
74 | * Sets up low level clock operations for TI clock driver. This is used | |
75 | * to provide various callbacks for the clock driver towards platform | |
76 | * specific code. Returns 0 on success, -EBUSY if ll_ops have been | |
77 | * registered already. | |
78 | */ | |
79 | int ti_clk_setup_ll_ops(struct ti_clk_ll_ops *ops) | |
80 | { | |
81 | if (ti_clk_ll_ops) { | |
82 | pr_err("Attempt to register ll_ops multiple times.\n"); | |
83 | return -EBUSY; | |
84 | } | |
85 | ||
86 | ti_clk_ll_ops = ops; | |
989feafb TK |
87 | ops->clk_readl = clk_memmap_readl; |
88 | ops->clk_writel = clk_memmap_writel; | |
e9e63088 TK |
89 | |
90 | return 0; | |
91 | } | |
f3b19aa5 | 92 | |
a8aceccb TK |
93 | /** |
94 | * ti_dt_clocks_register - register DT alias clocks during boot | |
95 | * @oclks: list of clocks to register | |
96 | * | |
97 | * Register alias or non-standard DT clock entries during boot. By | |
98 | * default, DT clocks are found based on their node name. If any | |
99 | * additional con-id / dev-id -> clock mapping is required, use this | |
100 | * function to list these. | |
101 | */ | |
102 | void __init ti_dt_clocks_register(struct ti_dt_clk oclks[]) | |
103 | { | |
104 | struct ti_dt_clk *c; | |
105 | struct device_node *node; | |
106 | struct clk *clk; | |
107 | struct of_phandle_args clkspec; | |
108 | ||
109 | for (c = oclks; c->node_name != NULL; c++) { | |
110 | node = of_find_node_by_name(NULL, c->node_name); | |
111 | clkspec.np = node; | |
112 | clk = of_clk_get_from_provider(&clkspec); | |
113 | ||
114 | if (!IS_ERR(clk)) { | |
115 | c->lk.clk = clk; | |
116 | clkdev_add(&c->lk); | |
117 | } else { | |
118 | pr_warn("failed to lookup clock node %s\n", | |
119 | c->node_name); | |
120 | } | |
121 | } | |
122 | } | |
819b4861 TK |
123 | |
124 | struct clk_init_item { | |
125 | struct device_node *node; | |
126 | struct clk_hw *hw; | |
127 | ti_of_clk_init_cb_t func; | |
128 | struct list_head link; | |
129 | }; | |
130 | ||
131 | static LIST_HEAD(retry_list); | |
132 | ||
133 | /** | |
134 | * ti_clk_retry_init - retries a failed clock init at later phase | |
135 | * @node: device not for the clock | |
136 | * @hw: partially initialized clk_hw struct for the clock | |
137 | * @func: init function to be called for the clock | |
138 | * | |
139 | * Adds a failed clock init to the retry list. The retry list is parsed | |
140 | * once all the other clocks have been initialized. | |
141 | */ | |
142 | int __init ti_clk_retry_init(struct device_node *node, struct clk_hw *hw, | |
143 | ti_of_clk_init_cb_t func) | |
144 | { | |
145 | struct clk_init_item *retry; | |
146 | ||
147 | pr_debug("%s: adding to retry list...\n", node->name); | |
148 | retry = kzalloc(sizeof(*retry), GFP_KERNEL); | |
149 | if (!retry) | |
150 | return -ENOMEM; | |
151 | ||
152 | retry->node = node; | |
153 | retry->func = func; | |
154 | retry->hw = hw; | |
155 | list_add(&retry->link, &retry_list); | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | /** | |
161 | * ti_clk_get_reg_addr - get register address for a clock register | |
162 | * @node: device node for the clock | |
163 | * @index: register index from the clock node | |
164 | * | |
165 | * Builds clock register address from device tree information. This | |
c807dbed TK |
166 | * is a struct of type clk_omap_reg. Returns a pointer to the register |
167 | * address, or a pointer error value in failure. | |
819b4861 TK |
168 | */ |
169 | void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) | |
170 | { | |
171 | struct clk_omap_reg *reg; | |
172 | u32 val; | |
173 | u32 tmp; | |
c08ee14c | 174 | int i; |
819b4861 TK |
175 | |
176 | reg = (struct clk_omap_reg *)&tmp; | |
c08ee14c TK |
177 | |
178 | for (i = 0; i < CLK_MAX_MEMMAPS; i++) { | |
179 | if (clocks_node_ptr[i] == node->parent) | |
180 | break; | |
181 | } | |
182 | ||
183 | if (i == CLK_MAX_MEMMAPS) { | |
184 | pr_err("clk-provider not found for %s!\n", node->name); | |
412d6b47 | 185 | return IOMEM_ERR_PTR(-ENOENT); |
c08ee14c TK |
186 | } |
187 | ||
188 | reg->index = i; | |
819b4861 TK |
189 | |
190 | if (of_property_read_u32_index(node, "reg", index, &val)) { | |
191 | pr_err("%s must have reg[%d]!\n", node->name, index); | |
412d6b47 | 192 | return IOMEM_ERR_PTR(-EINVAL); |
819b4861 TK |
193 | } |
194 | ||
195 | reg->offset = val; | |
196 | ||
14cc4e95 | 197 | return (__force void __iomem *)tmp; |
819b4861 TK |
198 | } |
199 | ||
200 | /** | |
989feafb | 201 | * omap2_clk_provider_init - init master clock provider |
819b4861 TK |
202 | * @parent: master node |
203 | * @index: internal index for clk_reg_ops | |
989feafb TK |
204 | * @syscon: syscon regmap pointer for accessing clock registers |
205 | * @mem: iomem pointer for the clock provider memory area, only used if | |
206 | * syscon is not provided | |
819b4861 | 207 | * |
c08ee14c TK |
208 | * Initializes a master clock IP block. This basically sets up the |
209 | * mapping from clocks node to the memory map index. All the clocks | |
210 | * are then initialized through the common of_clk_init call, and the | |
211 | * clocks will access their memory maps based on the node layout. | |
989feafb | 212 | * Returns 0 in success. |
819b4861 | 213 | */ |
989feafb TK |
214 | int __init omap2_clk_provider_init(struct device_node *parent, int index, |
215 | struct regmap *syscon, void __iomem *mem) | |
819b4861 | 216 | { |
819b4861 | 217 | struct device_node *clocks; |
989feafb | 218 | struct clk_iomap *io; |
819b4861 TK |
219 | |
220 | /* get clocks for this parent */ | |
221 | clocks = of_get_child_by_name(parent, "clocks"); | |
222 | if (!clocks) { | |
223 | pr_err("%s missing 'clocks' child node.\n", parent->name); | |
989feafb | 224 | return -EINVAL; |
819b4861 TK |
225 | } |
226 | ||
c08ee14c TK |
227 | /* add clocks node info */ |
228 | clocks_node_ptr[index] = clocks; | |
989feafb TK |
229 | |
230 | io = kzalloc(sizeof(*io), GFP_KERNEL); | |
f645f72d SB |
231 | if (!io) |
232 | return -ENOMEM; | |
989feafb TK |
233 | |
234 | io->regmap = syscon; | |
235 | io->mem = mem; | |
236 | ||
237 | clk_memmaps[index] = io; | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
242 | /** | |
243 | * omap2_clk_legacy_provider_init - initialize a legacy clock provider | |
244 | * @index: index for the clock provider | |
245 | * @mem: iomem pointer for the clock provider memory area | |
246 | * | |
247 | * Initializes a legacy clock provider memory mapping. | |
248 | */ | |
249 | void __init omap2_clk_legacy_provider_init(int index, void __iomem *mem) | |
250 | { | |
251 | struct clk_iomap *io; | |
252 | ||
253 | io = memblock_virt_alloc(sizeof(*io), 0); | |
254 | ||
255 | io->mem = mem; | |
256 | ||
257 | clk_memmaps[index] = io; | |
c08ee14c | 258 | } |
819b4861 | 259 | |
c08ee14c TK |
260 | /** |
261 | * ti_dt_clk_init_retry_clks - init clocks from the retry list | |
262 | * | |
263 | * Initializes any clocks that have failed to initialize before, | |
264 | * reasons being missing parent node(s) during earlier init. This | |
265 | * typically happens only for DPLLs which need to have both of their | |
266 | * parent clocks ready during init. | |
267 | */ | |
268 | void ti_dt_clk_init_retry_clks(void) | |
269 | { | |
270 | struct clk_init_item *retry; | |
271 | struct clk_init_item *tmp; | |
272 | int retries = 5; | |
273 | ||
274 | while (!list_empty(&retry_list) && retries) { | |
275 | list_for_each_entry_safe(retry, tmp, &retry_list, link) { | |
276 | pr_debug("retry-init: %s\n", retry->node->name); | |
277 | retry->func(retry->hw, retry->node); | |
278 | list_del(&retry->link); | |
279 | kfree(retry); | |
280 | } | |
281 | retries--; | |
819b4861 TK |
282 | } |
283 | } | |
c82f8957 | 284 | |
6793a30a | 285 | #if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_ATAGS) |
c82f8957 TK |
286 | void __init ti_clk_patch_legacy_clks(struct ti_clk **patch) |
287 | { | |
288 | while (*patch) { | |
289 | memcpy((*patch)->patch, *patch, sizeof(**patch)); | |
290 | patch++; | |
291 | } | |
292 | } | |
293 | ||
294 | struct clk __init *ti_clk_register_clk(struct ti_clk *setup) | |
295 | { | |
296 | struct clk *clk; | |
297 | struct ti_clk_fixed *fixed; | |
298 | struct ti_clk_fixed_factor *fixed_factor; | |
299 | struct clk_hw *clk_hw; | |
300 | ||
301 | if (setup->clk) | |
302 | return setup->clk; | |
303 | ||
304 | switch (setup->type) { | |
305 | case TI_CLK_FIXED: | |
306 | fixed = setup->data; | |
307 | ||
308 | clk = clk_register_fixed_rate(NULL, setup->name, NULL, | |
309 | CLK_IS_ROOT, fixed->frequency); | |
310 | break; | |
7c18a65c TK |
311 | case TI_CLK_MUX: |
312 | clk = ti_clk_register_mux(setup); | |
313 | break; | |
d96f774b TK |
314 | case TI_CLK_DIVIDER: |
315 | clk = ti_clk_register_divider(setup); | |
316 | break; | |
b26bcf9b TK |
317 | case TI_CLK_COMPOSITE: |
318 | clk = ti_clk_register_composite(setup); | |
319 | break; | |
c82f8957 TK |
320 | case TI_CLK_FIXED_FACTOR: |
321 | fixed_factor = setup->data; | |
322 | ||
323 | clk = clk_register_fixed_factor(NULL, setup->name, | |
324 | fixed_factor->parent, | |
325 | 0, fixed_factor->mult, | |
326 | fixed_factor->div); | |
327 | break; | |
f187616b TK |
328 | case TI_CLK_GATE: |
329 | clk = ti_clk_register_gate(setup); | |
330 | break; | |
ed405a23 TK |
331 | case TI_CLK_DPLL: |
332 | clk = ti_clk_register_dpll(setup); | |
333 | break; | |
c82f8957 TK |
334 | default: |
335 | pr_err("bad type for %s!\n", setup->name); | |
336 | clk = ERR_PTR(-EINVAL); | |
337 | } | |
338 | ||
339 | if (!IS_ERR(clk)) { | |
340 | setup->clk = clk; | |
341 | if (setup->clkdm_name) { | |
98d8a60e SB |
342 | clk_hw = __clk_get_hw(clk); |
343 | if (clk_hw_get_flags(clk_hw) & CLK_IS_BASIC) { | |
c82f8957 TK |
344 | pr_warn("can't setup clkdm for basic clk %s\n", |
345 | setup->name); | |
346 | } else { | |
c82f8957 TK |
347 | to_clk_hw_omap(clk_hw)->clkdm_name = |
348 | setup->clkdm_name; | |
349 | omap2_init_clk_clkdm(clk_hw); | |
350 | } | |
351 | } | |
352 | } | |
353 | ||
354 | return clk; | |
355 | } | |
356 | ||
357 | int __init ti_clk_register_legacy_clks(struct ti_clk_alias *clks) | |
358 | { | |
359 | struct clk *clk; | |
360 | bool retry; | |
361 | struct ti_clk_alias *retry_clk; | |
362 | struct ti_clk_alias *tmp; | |
363 | ||
364 | while (clks->clk) { | |
365 | clk = ti_clk_register_clk(clks->clk); | |
366 | if (IS_ERR(clk)) { | |
367 | if (PTR_ERR(clk) == -EAGAIN) { | |
368 | list_add(&clks->link, &retry_list); | |
369 | } else { | |
370 | pr_err("register for %s failed: %ld\n", | |
371 | clks->clk->name, PTR_ERR(clk)); | |
372 | return PTR_ERR(clk); | |
373 | } | |
374 | } else { | |
375 | clks->lk.clk = clk; | |
376 | clkdev_add(&clks->lk); | |
377 | } | |
378 | clks++; | |
379 | } | |
380 | ||
381 | retry = true; | |
382 | ||
383 | while (!list_empty(&retry_list) && retry) { | |
384 | retry = false; | |
385 | list_for_each_entry_safe(retry_clk, tmp, &retry_list, link) { | |
386 | pr_debug("retry-init: %s\n", retry_clk->clk->name); | |
387 | clk = ti_clk_register_clk(retry_clk->clk); | |
388 | if (IS_ERR(clk)) { | |
389 | if (PTR_ERR(clk) == -EAGAIN) { | |
390 | continue; | |
391 | } else { | |
392 | pr_err("register for %s failed: %ld\n", | |
393 | retry_clk->clk->name, | |
394 | PTR_ERR(clk)); | |
395 | return PTR_ERR(clk); | |
396 | } | |
397 | } else { | |
398 | retry = true; | |
399 | retry_clk->lk.clk = clk; | |
400 | clkdev_add(&retry_clk->lk); | |
401 | list_del(&retry_clk->link); | |
402 | } | |
403 | } | |
404 | } | |
405 | ||
406 | return 0; | |
407 | } | |
6793a30a | 408 | #endif |
f3b19aa5 TK |
409 | |
410 | /** | |
411 | * ti_clk_setup_features - setup clock features flags | |
412 | * @features: features definition to use | |
413 | * | |
414 | * Initializes the clock driver features flags based on platform | |
415 | * provided data. No return value. | |
416 | */ | |
417 | void __init ti_clk_setup_features(struct ti_clk_features *features) | |
418 | { | |
419 | memcpy(&ti_clk_features, features, sizeof(*features)); | |
420 | } | |
421 | ||
422 | /** | |
423 | * ti_clk_get_features - get clock driver features flags | |
424 | * | |
425 | * Get TI clock driver features description. Returns a pointer | |
426 | * to the current feature setup. | |
427 | */ | |
428 | const struct ti_clk_features *ti_clk_get_features(void) | |
429 | { | |
430 | return &ti_clk_features; | |
431 | } | |
a5aa8a60 TK |
432 | |
433 | /** | |
434 | * omap2_clk_enable_init_clocks - prepare & enable a list of clocks | |
435 | * @clk_names: ptr to an array of strings of clock names to enable | |
436 | * @num_clocks: number of clock names in @clk_names | |
437 | * | |
438 | * Prepare and enable a list of clocks, named by @clk_names. No | |
439 | * return value. XXX Deprecated; only needed until these clocks are | |
440 | * properly claimed and enabled by the drivers or core code that uses | |
441 | * them. XXX What code disables & calls clk_put on these clocks? | |
442 | */ | |
443 | void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks) | |
444 | { | |
445 | struct clk *init_clk; | |
446 | int i; | |
447 | ||
448 | for (i = 0; i < num_clocks; i++) { | |
449 | init_clk = clk_get(NULL, clk_names[i]); | |
450 | if (WARN(IS_ERR(init_clk), "could not find init clock %s\n", | |
451 | clk_names[i])) | |
452 | continue; | |
453 | clk_prepare_enable(init_clk); | |
454 | } | |
455 | } |