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 | 25 | #include <linux/regmap.h> |
57c8a661 | 26 | #include <linux/memblock.h> |
21f0bf2d | 27 | #include <linux/device.h> |
a8aceccb | 28 | |
c82f8957 TK |
29 | #include "clock.h" |
30 | ||
a8aceccb TK |
31 | #undef pr_fmt |
32 | #define pr_fmt(fmt) "%s: " fmt, __func__ | |
33 | ||
819b4861 | 34 | struct ti_clk_ll_ops *ti_clk_ll_ops; |
c08ee14c | 35 | static struct device_node *clocks_node_ptr[CLK_MAX_MEMMAPS]; |
819b4861 | 36 | |
3fe6d697 | 37 | static struct ti_clk_features ti_clk_features; |
e9e63088 | 38 | |
989feafb TK |
39 | struct clk_iomap { |
40 | struct regmap *regmap; | |
41 | void __iomem *mem; | |
42 | }; | |
43 | ||
44 | static struct clk_iomap *clk_memmaps[CLK_MAX_MEMMAPS]; | |
45 | ||
6c0afb50 | 46 | static void clk_memmap_writel(u32 val, const struct clk_omap_reg *reg) |
989feafb | 47 | { |
6c0afb50 | 48 | struct clk_iomap *io = clk_memmaps[reg->index]; |
989feafb | 49 | |
6c0afb50 TK |
50 | if (reg->ptr) |
51 | writel_relaxed(val, reg->ptr); | |
52 | else if (io->regmap) | |
53 | regmap_write(io->regmap, reg->offset, val); | |
989feafb | 54 | else |
6c0afb50 | 55 | writel_relaxed(val, io->mem + reg->offset); |
989feafb TK |
56 | } |
57 | ||
4902c202 TK |
58 | static void _clk_rmw(u32 val, u32 mask, void __iomem *ptr) |
59 | { | |
60 | u32 v; | |
61 | ||
62 | v = readl_relaxed(ptr); | |
63 | v &= ~mask; | |
64 | v |= val; | |
65 | writel_relaxed(v, ptr); | |
66 | } | |
67 | ||
68 | static void clk_memmap_rmw(u32 val, u32 mask, const struct clk_omap_reg *reg) | |
69 | { | |
70 | struct clk_iomap *io = clk_memmaps[reg->index]; | |
71 | ||
72 | if (reg->ptr) { | |
73 | _clk_rmw(val, mask, reg->ptr); | |
74 | } else if (io->regmap) { | |
75 | regmap_update_bits(io->regmap, reg->offset, mask, val); | |
76 | } else { | |
77 | _clk_rmw(val, mask, io->mem + reg->offset); | |
78 | } | |
79 | } | |
80 | ||
6c0afb50 | 81 | static u32 clk_memmap_readl(const struct clk_omap_reg *reg) |
989feafb TK |
82 | { |
83 | u32 val; | |
6c0afb50 | 84 | struct clk_iomap *io = clk_memmaps[reg->index]; |
989feafb | 85 | |
6c0afb50 TK |
86 | if (reg->ptr) |
87 | val = readl_relaxed(reg->ptr); | |
88 | else if (io->regmap) | |
89 | regmap_read(io->regmap, reg->offset, &val); | |
989feafb | 90 | else |
6c0afb50 | 91 | val = readl_relaxed(io->mem + reg->offset); |
989feafb TK |
92 | |
93 | return val; | |
94 | } | |
95 | ||
e9e63088 TK |
96 | /** |
97 | * ti_clk_setup_ll_ops - setup low level clock operations | |
98 | * @ops: low level clock ops descriptor | |
99 | * | |
100 | * Sets up low level clock operations for TI clock driver. This is used | |
101 | * to provide various callbacks for the clock driver towards platform | |
102 | * specific code. Returns 0 on success, -EBUSY if ll_ops have been | |
103 | * registered already. | |
104 | */ | |
105 | int ti_clk_setup_ll_ops(struct ti_clk_ll_ops *ops) | |
106 | { | |
107 | if (ti_clk_ll_ops) { | |
108 | pr_err("Attempt to register ll_ops multiple times.\n"); | |
109 | return -EBUSY; | |
110 | } | |
111 | ||
112 | ti_clk_ll_ops = ops; | |
989feafb TK |
113 | ops->clk_readl = clk_memmap_readl; |
114 | ops->clk_writel = clk_memmap_writel; | |
4902c202 | 115 | ops->clk_rmw = clk_memmap_rmw; |
e9e63088 TK |
116 | |
117 | return 0; | |
118 | } | |
f3b19aa5 | 119 | |
a8aceccb TK |
120 | /** |
121 | * ti_dt_clocks_register - register DT alias clocks during boot | |
122 | * @oclks: list of clocks to register | |
123 | * | |
124 | * Register alias or non-standard DT clock entries during boot. By | |
125 | * default, DT clocks are found based on their node name. If any | |
126 | * additional con-id / dev-id -> clock mapping is required, use this | |
127 | * function to list these. | |
128 | */ | |
129 | void __init ti_dt_clocks_register(struct ti_dt_clk oclks[]) | |
130 | { | |
131 | struct ti_dt_clk *c; | |
132 | struct device_node *node; | |
133 | struct clk *clk; | |
134 | struct of_phandle_args clkspec; | |
5b385a45 TK |
135 | char buf[64]; |
136 | char *ptr; | |
137 | char *tags[2]; | |
138 | int i; | |
139 | int num_args; | |
140 | int ret; | |
141 | static bool clkctrl_nodes_missing; | |
142 | static bool has_clkctrl_data; | |
a8aceccb TK |
143 | |
144 | for (c = oclks; c->node_name != NULL; c++) { | |
5b385a45 TK |
145 | strcpy(buf, c->node_name); |
146 | ptr = buf; | |
147 | for (i = 0; i < 2; i++) | |
148 | tags[i] = NULL; | |
149 | num_args = 0; | |
150 | while (*ptr) { | |
151 | if (*ptr == ':') { | |
152 | if (num_args >= 2) { | |
153 | pr_warn("Bad number of tags on %s\n", | |
154 | c->node_name); | |
155 | return; | |
156 | } | |
157 | tags[num_args++] = ptr + 1; | |
158 | *ptr = 0; | |
159 | } | |
160 | ptr++; | |
161 | } | |
162 | ||
163 | if (num_args && clkctrl_nodes_missing) | |
164 | continue; | |
165 | ||
166 | node = of_find_node_by_name(NULL, buf); | |
167 | if (num_args) | |
168 | node = of_find_node_by_name(node, "clk"); | |
a8aceccb | 169 | clkspec.np = node; |
5b385a45 TK |
170 | clkspec.args_count = num_args; |
171 | for (i = 0; i < num_args; i++) { | |
172 | ret = kstrtoint(tags[i], i ? 10 : 16, clkspec.args + i); | |
173 | if (ret) { | |
174 | pr_warn("Bad tag in %s at %d: %s\n", | |
175 | c->node_name, i, tags[i]); | |
176 | return; | |
177 | } | |
178 | } | |
a8aceccb TK |
179 | clk = of_clk_get_from_provider(&clkspec); |
180 | ||
181 | if (!IS_ERR(clk)) { | |
182 | c->lk.clk = clk; | |
183 | clkdev_add(&c->lk); | |
184 | } else { | |
5b385a45 TK |
185 | if (num_args && !has_clkctrl_data) { |
186 | if (of_find_compatible_node(NULL, NULL, | |
187 | "ti,clkctrl")) { | |
188 | has_clkctrl_data = true; | |
189 | } else { | |
190 | clkctrl_nodes_missing = true; | |
191 | ||
192 | pr_warn("missing clkctrl nodes, please update your dts.\n"); | |
193 | continue; | |
194 | } | |
195 | } | |
196 | ||
197 | pr_warn("failed to lookup clock node %s, ret=%ld\n", | |
198 | c->node_name, PTR_ERR(clk)); | |
a8aceccb TK |
199 | } |
200 | } | |
201 | } | |
819b4861 TK |
202 | |
203 | struct clk_init_item { | |
204 | struct device_node *node; | |
ffb009b2 | 205 | void *user; |
819b4861 TK |
206 | ti_of_clk_init_cb_t func; |
207 | struct list_head link; | |
208 | }; | |
209 | ||
210 | static LIST_HEAD(retry_list); | |
211 | ||
212 | /** | |
213 | * ti_clk_retry_init - retries a failed clock init at later phase | |
214 | * @node: device not for the clock | |
ffb009b2 | 215 | * @user: user data pointer |
819b4861 TK |
216 | * @func: init function to be called for the clock |
217 | * | |
218 | * Adds a failed clock init to the retry list. The retry list is parsed | |
219 | * once all the other clocks have been initialized. | |
220 | */ | |
ffb009b2 TK |
221 | int __init ti_clk_retry_init(struct device_node *node, void *user, |
222 | ti_of_clk_init_cb_t func) | |
819b4861 TK |
223 | { |
224 | struct clk_init_item *retry; | |
225 | ||
226 | pr_debug("%s: adding to retry list...\n", node->name); | |
227 | retry = kzalloc(sizeof(*retry), GFP_KERNEL); | |
228 | if (!retry) | |
229 | return -ENOMEM; | |
230 | ||
231 | retry->node = node; | |
232 | retry->func = func; | |
ffb009b2 | 233 | retry->user = user; |
819b4861 TK |
234 | list_add(&retry->link, &retry_list); |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
239 | /** | |
240 | * ti_clk_get_reg_addr - get register address for a clock register | |
241 | * @node: device node for the clock | |
242 | * @index: register index from the clock node | |
6c0afb50 | 243 | * @reg: pointer to target register struct |
819b4861 | 244 | * |
6c0afb50 TK |
245 | * Builds clock register address from device tree information, and returns |
246 | * the data via the provided output pointer @reg. Returns 0 on success, | |
247 | * negative error value on failure. | |
819b4861 | 248 | */ |
6c0afb50 TK |
249 | int ti_clk_get_reg_addr(struct device_node *node, int index, |
250 | struct clk_omap_reg *reg) | |
819b4861 | 251 | { |
819b4861 | 252 | u32 val; |
c08ee14c | 253 | int i; |
819b4861 | 254 | |
c08ee14c TK |
255 | for (i = 0; i < CLK_MAX_MEMMAPS; i++) { |
256 | if (clocks_node_ptr[i] == node->parent) | |
257 | break; | |
258 | } | |
259 | ||
260 | if (i == CLK_MAX_MEMMAPS) { | |
261 | pr_err("clk-provider not found for %s!\n", node->name); | |
6c0afb50 | 262 | return -ENOENT; |
c08ee14c TK |
263 | } |
264 | ||
265 | reg->index = i; | |
819b4861 TK |
266 | |
267 | if (of_property_read_u32_index(node, "reg", index, &val)) { | |
268 | pr_err("%s must have reg[%d]!\n", node->name, index); | |
6c0afb50 | 269 | return -EINVAL; |
819b4861 TK |
270 | } |
271 | ||
272 | reg->offset = val; | |
6c0afb50 | 273 | reg->ptr = NULL; |
819b4861 | 274 | |
6c0afb50 | 275 | return 0; |
819b4861 TK |
276 | } |
277 | ||
e31922ed TK |
278 | void ti_clk_latch(struct clk_omap_reg *reg, s8 shift) |
279 | { | |
280 | u32 latch; | |
281 | ||
282 | if (shift < 0) | |
283 | return; | |
284 | ||
285 | latch = 1 << shift; | |
286 | ||
287 | ti_clk_ll_ops->clk_rmw(latch, latch, reg); | |
288 | ti_clk_ll_ops->clk_rmw(0, latch, reg); | |
289 | ti_clk_ll_ops->clk_readl(reg); /* OCP barrier */ | |
290 | } | |
291 | ||
819b4861 | 292 | /** |
989feafb | 293 | * omap2_clk_provider_init - init master clock provider |
819b4861 TK |
294 | * @parent: master node |
295 | * @index: internal index for clk_reg_ops | |
989feafb TK |
296 | * @syscon: syscon regmap pointer for accessing clock registers |
297 | * @mem: iomem pointer for the clock provider memory area, only used if | |
298 | * syscon is not provided | |
819b4861 | 299 | * |
c08ee14c TK |
300 | * Initializes a master clock IP block. This basically sets up the |
301 | * mapping from clocks node to the memory map index. All the clocks | |
302 | * are then initialized through the common of_clk_init call, and the | |
303 | * clocks will access their memory maps based on the node layout. | |
989feafb | 304 | * Returns 0 in success. |
819b4861 | 305 | */ |
989feafb TK |
306 | int __init omap2_clk_provider_init(struct device_node *parent, int index, |
307 | struct regmap *syscon, void __iomem *mem) | |
819b4861 | 308 | { |
819b4861 | 309 | struct device_node *clocks; |
989feafb | 310 | struct clk_iomap *io; |
819b4861 TK |
311 | |
312 | /* get clocks for this parent */ | |
313 | clocks = of_get_child_by_name(parent, "clocks"); | |
314 | if (!clocks) { | |
315 | pr_err("%s missing 'clocks' child node.\n", parent->name); | |
989feafb | 316 | return -EINVAL; |
819b4861 TK |
317 | } |
318 | ||
c08ee14c TK |
319 | /* add clocks node info */ |
320 | clocks_node_ptr[index] = clocks; | |
989feafb TK |
321 | |
322 | io = kzalloc(sizeof(*io), GFP_KERNEL); | |
f645f72d SB |
323 | if (!io) |
324 | return -ENOMEM; | |
989feafb TK |
325 | |
326 | io->regmap = syscon; | |
327 | io->mem = mem; | |
328 | ||
329 | clk_memmaps[index] = io; | |
330 | ||
331 | return 0; | |
332 | } | |
333 | ||
334 | /** | |
335 | * omap2_clk_legacy_provider_init - initialize a legacy clock provider | |
336 | * @index: index for the clock provider | |
337 | * @mem: iomem pointer for the clock provider memory area | |
338 | * | |
339 | * Initializes a legacy clock provider memory mapping. | |
340 | */ | |
341 | void __init omap2_clk_legacy_provider_init(int index, void __iomem *mem) | |
342 | { | |
343 | struct clk_iomap *io; | |
344 | ||
7e1c4e27 | 345 | io = memblock_alloc(sizeof(*io), SMP_CACHE_BYTES); |
989feafb TK |
346 | |
347 | io->mem = mem; | |
348 | ||
349 | clk_memmaps[index] = io; | |
c08ee14c | 350 | } |
819b4861 | 351 | |
c08ee14c TK |
352 | /** |
353 | * ti_dt_clk_init_retry_clks - init clocks from the retry list | |
354 | * | |
355 | * Initializes any clocks that have failed to initialize before, | |
356 | * reasons being missing parent node(s) during earlier init. This | |
357 | * typically happens only for DPLLs which need to have both of their | |
358 | * parent clocks ready during init. | |
359 | */ | |
360 | void ti_dt_clk_init_retry_clks(void) | |
361 | { | |
362 | struct clk_init_item *retry; | |
363 | struct clk_init_item *tmp; | |
364 | int retries = 5; | |
365 | ||
366 | while (!list_empty(&retry_list) && retries) { | |
367 | list_for_each_entry_safe(retry, tmp, &retry_list, link) { | |
368 | pr_debug("retry-init: %s\n", retry->node->name); | |
ffb009b2 | 369 | retry->func(retry->user, retry->node); |
c08ee14c TK |
370 | list_del(&retry->link); |
371 | kfree(retry); | |
372 | } | |
373 | retries--; | |
819b4861 TK |
374 | } |
375 | } | |
c82f8957 | 376 | |
fc04f27d AB |
377 | static const struct of_device_id simple_clk_match_table[] __initconst = { |
378 | { .compatible = "fixed-clock" }, | |
379 | { .compatible = "fixed-factor-clock" }, | |
380 | { } | |
381 | }; | |
382 | ||
c17435c5 TK |
383 | /** |
384 | * ti_clk_add_aliases - setup clock aliases | |
385 | * | |
386 | * Sets up any missing clock aliases. No return value. | |
387 | */ | |
388 | void __init ti_clk_add_aliases(void) | |
389 | { | |
390 | struct device_node *np; | |
391 | struct clk *clk; | |
392 | ||
393 | for_each_matching_node(np, simple_clk_match_table) { | |
394 | struct of_phandle_args clkspec; | |
395 | ||
396 | clkspec.np = np; | |
397 | clk = of_clk_get_from_provider(&clkspec); | |
398 | ||
399 | ti_clk_add_alias(NULL, clk, np->name); | |
400 | } | |
401 | } | |
402 | ||
f3b19aa5 TK |
403 | /** |
404 | * ti_clk_setup_features - setup clock features flags | |
405 | * @features: features definition to use | |
406 | * | |
407 | * Initializes the clock driver features flags based on platform | |
408 | * provided data. No return value. | |
409 | */ | |
410 | void __init ti_clk_setup_features(struct ti_clk_features *features) | |
411 | { | |
412 | memcpy(&ti_clk_features, features, sizeof(*features)); | |
413 | } | |
414 | ||
415 | /** | |
416 | * ti_clk_get_features - get clock driver features flags | |
417 | * | |
418 | * Get TI clock driver features description. Returns a pointer | |
419 | * to the current feature setup. | |
420 | */ | |
421 | const struct ti_clk_features *ti_clk_get_features(void) | |
422 | { | |
423 | return &ti_clk_features; | |
424 | } | |
a5aa8a60 TK |
425 | |
426 | /** | |
427 | * omap2_clk_enable_init_clocks - prepare & enable a list of clocks | |
428 | * @clk_names: ptr to an array of strings of clock names to enable | |
429 | * @num_clocks: number of clock names in @clk_names | |
430 | * | |
431 | * Prepare and enable a list of clocks, named by @clk_names. No | |
432 | * return value. XXX Deprecated; only needed until these clocks are | |
433 | * properly claimed and enabled by the drivers or core code that uses | |
434 | * them. XXX What code disables & calls clk_put on these clocks? | |
435 | */ | |
436 | void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks) | |
437 | { | |
438 | struct clk *init_clk; | |
439 | int i; | |
440 | ||
441 | for (i = 0; i < num_clocks; i++) { | |
442 | init_clk = clk_get(NULL, clk_names[i]); | |
443 | if (WARN(IS_ERR(init_clk), "could not find init clock %s\n", | |
444 | clk_names[i])) | |
445 | continue; | |
446 | clk_prepare_enable(init_clk); | |
447 | } | |
448 | } | |
21f0bf2d TK |
449 | |
450 | /** | |
451 | * ti_clk_add_alias - add a clock alias for a TI clock | |
452 | * @dev: device alias for this clock | |
453 | * @clk: clock handle to create alias for | |
454 | * @con: connection ID for this clock | |
455 | * | |
456 | * Creates a clock alias for a TI clock. Allocates the clock lookup entry | |
457 | * and assigns the data to it. Returns 0 if successful, negative error | |
458 | * value otherwise. | |
459 | */ | |
460 | int ti_clk_add_alias(struct device *dev, struct clk *clk, const char *con) | |
461 | { | |
462 | struct clk_lookup *cl; | |
463 | ||
464 | if (!clk) | |
465 | return 0; | |
466 | ||
467 | if (IS_ERR(clk)) | |
468 | return PTR_ERR(clk); | |
469 | ||
470 | cl = kzalloc(sizeof(*cl), GFP_KERNEL); | |
471 | if (!cl) | |
472 | return -ENOMEM; | |
473 | ||
474 | if (dev) | |
475 | cl->dev_id = dev_name(dev); | |
476 | cl->con_id = con; | |
477 | cl->clk = clk; | |
478 | ||
479 | clkdev_add(cl); | |
480 | ||
481 | return 0; | |
482 | } | |
483 | ||
484 | /** | |
485 | * ti_clk_register - register a TI clock to the common clock framework | |
486 | * @dev: device for this clock | |
487 | * @hw: hardware clock handle | |
488 | * @con: connection ID for this clock | |
489 | * | |
490 | * Registers a TI clock to the common clock framework, and adds a clock | |
491 | * alias for it. Returns a handle to the registered clock if successful, | |
492 | * ERR_PTR value in failure. | |
493 | */ | |
494 | struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw, | |
495 | const char *con) | |
496 | { | |
497 | struct clk *clk; | |
498 | int ret; | |
499 | ||
500 | clk = clk_register(dev, hw); | |
501 | if (IS_ERR(clk)) | |
502 | return clk; | |
503 | ||
504 | ret = ti_clk_add_alias(dev, clk, con); | |
505 | if (ret) { | |
506 | clk_unregister(clk); | |
507 | return ERR_PTR(ret); | |
508 | } | |
509 | ||
510 | return clk; | |
511 | } |