]>
Commit | Line | Data |
---|---|---|
0eecc636 TL |
1 | /* |
2 | * ti-sysc.c - Texas Instruments sysc interconnect target driver | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
9 | * kind, whether express or implied; without even the implied warranty | |
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/io.h> | |
15 | #include <linux/clk.h> | |
2c355ff6 | 16 | #include <linux/clkdev.h> |
0eecc636 TL |
17 | #include <linux/module.h> |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/pm_runtime.h> | |
20 | #include <linux/of_address.h> | |
21 | #include <linux/of_platform.h> | |
2c355ff6 TL |
22 | #include <linux/slab.h> |
23 | ||
70a65240 TL |
24 | #include <linux/platform_data/ti-sysc.h> |
25 | ||
26 | #include <dt-bindings/bus/ti-sysc.h> | |
0eecc636 TL |
27 | |
28 | enum sysc_registers { | |
29 | SYSC_REVISION, | |
30 | SYSC_SYSCONFIG, | |
31 | SYSC_SYSSTATUS, | |
32 | SYSC_MAX_REGS, | |
33 | }; | |
34 | ||
35 | static const char * const reg_names[] = { "rev", "sysc", "syss", }; | |
36 | ||
37 | enum sysc_clocks { | |
38 | SYSC_FCK, | |
39 | SYSC_ICK, | |
40 | SYSC_MAX_CLOCKS, | |
41 | }; | |
42 | ||
43 | static const char * const clock_names[] = { "fck", "ick", }; | |
44 | ||
c5a2de97 TL |
45 | #define SYSC_IDLEMODE_MASK 3 |
46 | #define SYSC_CLOCKACTIVITY_MASK 3 | |
47 | ||
0eecc636 TL |
48 | /** |
49 | * struct sysc - TI sysc interconnect target module registers and capabilities | |
50 | * @dev: struct device pointer | |
51 | * @module_pa: physical address of the interconnect target module | |
52 | * @module_size: size of the interconnect target module | |
53 | * @module_va: virtual address of the interconnect target module | |
54 | * @offsets: register offsets from module base | |
55 | * @clocks: clocks used by the interconnect target module | |
56 | * @legacy_mode: configured for legacy mode if set | |
70a65240 TL |
57 | * @cap: interconnect target module capabilities |
58 | * @cfg: interconnect target module configuration | |
566a9b05 TL |
59 | * @name: name if available |
60 | * @revision: interconnect target module revision | |
62020f23 | 61 | * @needs_resume: runtime resume needed on resume from suspend |
0eecc636 TL |
62 | */ |
63 | struct sysc { | |
64 | struct device *dev; | |
65 | u64 module_pa; | |
66 | u32 module_size; | |
67 | void __iomem *module_va; | |
68 | int offsets[SYSC_MAX_REGS]; | |
69 | struct clk *clocks[SYSC_MAX_CLOCKS]; | |
70 | const char *legacy_mode; | |
70a65240 TL |
71 | const struct sysc_capabilities *cap; |
72 | struct sysc_config cfg; | |
566a9b05 TL |
73 | const char *name; |
74 | u32 revision; | |
62020f23 TL |
75 | bool enabled; |
76 | bool needs_resume; | |
0eecc636 TL |
77 | }; |
78 | ||
566a9b05 TL |
79 | static u32 sysc_read(struct sysc *ddata, int offset) |
80 | { | |
81 | if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) { | |
82 | u32 val; | |
83 | ||
84 | val = readw_relaxed(ddata->module_va + offset); | |
85 | val |= (readw_relaxed(ddata->module_va + offset + 4) << 16); | |
86 | ||
87 | return val; | |
88 | } | |
89 | ||
90 | return readl_relaxed(ddata->module_va + offset); | |
91 | } | |
92 | ||
0eecc636 TL |
93 | static u32 sysc_read_revision(struct sysc *ddata) |
94 | { | |
566a9b05 TL |
95 | int offset = ddata->offsets[SYSC_REVISION]; |
96 | ||
97 | if (offset < 0) | |
98 | return 0; | |
99 | ||
100 | return sysc_read(ddata, offset); | |
0eecc636 TL |
101 | } |
102 | ||
103 | static int sysc_get_one_clock(struct sysc *ddata, | |
104 | enum sysc_clocks index) | |
105 | { | |
106 | const char *name; | |
107 | int error; | |
108 | ||
109 | switch (index) { | |
110 | case SYSC_FCK: | |
111 | break; | |
112 | case SYSC_ICK: | |
113 | break; | |
114 | default: | |
115 | return -EINVAL; | |
116 | } | |
117 | name = clock_names[index]; | |
118 | ||
119 | ddata->clocks[index] = devm_clk_get(ddata->dev, name); | |
120 | if (IS_ERR(ddata->clocks[index])) { | |
121 | if (PTR_ERR(ddata->clocks[index]) == -ENOENT) | |
122 | return 0; | |
123 | ||
124 | dev_err(ddata->dev, "clock get error for %s: %li\n", | |
125 | name, PTR_ERR(ddata->clocks[index])); | |
126 | ||
127 | return PTR_ERR(ddata->clocks[index]); | |
128 | } | |
129 | ||
130 | error = clk_prepare(ddata->clocks[index]); | |
131 | if (error) { | |
132 | dev_err(ddata->dev, "clock prepare error for %s: %i\n", | |
133 | name, error); | |
134 | ||
135 | return error; | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | static int sysc_get_clocks(struct sysc *ddata) | |
142 | { | |
143 | int i, error; | |
144 | ||
0eecc636 TL |
145 | for (i = 0; i < SYSC_MAX_CLOCKS; i++) { |
146 | error = sysc_get_one_clock(ddata, i); | |
147 | if (error && error != -ENOENT) | |
148 | return error; | |
149 | } | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | /** | |
155 | * sysc_parse_and_check_child_range - parses module IO region from ranges | |
156 | * @ddata: device driver data | |
157 | * | |
158 | * In general we only need rev, syss, and sysc registers and not the whole | |
159 | * module range. But we do want the offsets for these registers from the | |
160 | * module base. This allows us to check them against the legacy hwmod | |
161 | * platform data. Let's also check the ranges are configured properly. | |
162 | */ | |
163 | static int sysc_parse_and_check_child_range(struct sysc *ddata) | |
164 | { | |
165 | struct device_node *np = ddata->dev->of_node; | |
166 | const __be32 *ranges; | |
167 | u32 nr_addr, nr_size; | |
168 | int len, error; | |
169 | ||
170 | ranges = of_get_property(np, "ranges", &len); | |
171 | if (!ranges) { | |
172 | dev_err(ddata->dev, "missing ranges for %pOF\n", np); | |
173 | ||
174 | return -ENOENT; | |
175 | } | |
176 | ||
177 | len /= sizeof(*ranges); | |
178 | ||
179 | if (len < 3) { | |
180 | dev_err(ddata->dev, "incomplete ranges for %pOF\n", np); | |
181 | ||
182 | return -EINVAL; | |
183 | } | |
184 | ||
185 | error = of_property_read_u32(np, "#address-cells", &nr_addr); | |
186 | if (error) | |
187 | return -ENOENT; | |
188 | ||
189 | error = of_property_read_u32(np, "#size-cells", &nr_size); | |
190 | if (error) | |
191 | return -ENOENT; | |
192 | ||
193 | if (nr_addr != 1 || nr_size != 1) { | |
194 | dev_err(ddata->dev, "invalid ranges for %pOF\n", np); | |
195 | ||
196 | return -EINVAL; | |
197 | } | |
198 | ||
199 | ranges++; | |
200 | ddata->module_pa = of_translate_address(np, ranges++); | |
201 | ddata->module_size = be32_to_cpup(ranges); | |
202 | ||
203 | dev_dbg(ddata->dev, "interconnect target 0x%llx size 0x%x for %pOF\n", | |
204 | ddata->module_pa, ddata->module_size, np); | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
3bb37c8e TL |
209 | static struct device_node *stdout_path; |
210 | ||
211 | static void sysc_init_stdout_path(struct sysc *ddata) | |
212 | { | |
213 | struct device_node *np = NULL; | |
214 | const char *uart; | |
215 | ||
216 | if (IS_ERR(stdout_path)) | |
217 | return; | |
218 | ||
219 | if (stdout_path) | |
220 | return; | |
221 | ||
222 | np = of_find_node_by_path("/chosen"); | |
223 | if (!np) | |
224 | goto err; | |
225 | ||
226 | uart = of_get_property(np, "stdout-path", NULL); | |
227 | if (!uart) | |
228 | goto err; | |
229 | ||
230 | np = of_find_node_by_path(uart); | |
231 | if (!np) | |
232 | goto err; | |
233 | ||
234 | stdout_path = np; | |
235 | ||
236 | return; | |
237 | ||
238 | err: | |
239 | stdout_path = ERR_PTR(-ENODEV); | |
240 | } | |
241 | ||
242 | static void sysc_check_quirk_stdout(struct sysc *ddata, | |
243 | struct device_node *np) | |
244 | { | |
245 | sysc_init_stdout_path(ddata); | |
246 | if (np != stdout_path) | |
247 | return; | |
248 | ||
249 | ddata->cfg.quirks |= SYSC_QUIRK_NO_IDLE_ON_INIT | | |
250 | SYSC_QUIRK_NO_RESET_ON_INIT; | |
251 | } | |
252 | ||
0eecc636 TL |
253 | /** |
254 | * sysc_check_one_child - check child configuration | |
255 | * @ddata: device driver data | |
256 | * @np: child device node | |
257 | * | |
258 | * Let's avoid messy situations where we have new interconnect target | |
259 | * node but children have "ti,hwmods". These belong to the interconnect | |
260 | * target node and are managed by this driver. | |
261 | */ | |
262 | static int sysc_check_one_child(struct sysc *ddata, | |
263 | struct device_node *np) | |
264 | { | |
265 | const char *name; | |
266 | ||
267 | name = of_get_property(np, "ti,hwmods", NULL); | |
268 | if (name) | |
269 | dev_warn(ddata->dev, "really a child ti,hwmods property?"); | |
270 | ||
3bb37c8e TL |
271 | sysc_check_quirk_stdout(ddata, np); |
272 | ||
0eecc636 TL |
273 | return 0; |
274 | } | |
275 | ||
276 | static int sysc_check_children(struct sysc *ddata) | |
277 | { | |
278 | struct device_node *child; | |
279 | int error; | |
280 | ||
281 | for_each_child_of_node(ddata->dev->of_node, child) { | |
282 | error = sysc_check_one_child(ddata, child); | |
283 | if (error) | |
284 | return error; | |
285 | } | |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
a7199e2b TL |
290 | /* |
291 | * So far only I2C uses 16-bit read access with clockactivity with revision | |
292 | * in two registers with stride of 4. We can detect this based on the rev | |
293 | * register size to configure things far enough to be able to properly read | |
294 | * the revision register. | |
295 | */ | |
296 | static void sysc_check_quirk_16bit(struct sysc *ddata, struct resource *res) | |
297 | { | |
298 | if (resource_size(res) == 8) { | |
299 | dev_dbg(ddata->dev, | |
300 | "enabling 16-bit and clockactivity quirks\n"); | |
301 | ddata->cfg.quirks |= SYSC_QUIRK_16BIT | SYSC_QUIRK_USE_CLOCKACT; | |
302 | } | |
303 | } | |
304 | ||
0eecc636 TL |
305 | /** |
306 | * sysc_parse_one - parses the interconnect target module registers | |
307 | * @ddata: device driver data | |
308 | * @reg: register to parse | |
309 | */ | |
310 | static int sysc_parse_one(struct sysc *ddata, enum sysc_registers reg) | |
311 | { | |
312 | struct resource *res; | |
313 | const char *name; | |
314 | ||
315 | switch (reg) { | |
316 | case SYSC_REVISION: | |
317 | case SYSC_SYSCONFIG: | |
318 | case SYSC_SYSSTATUS: | |
319 | name = reg_names[reg]; | |
320 | break; | |
321 | default: | |
322 | return -EINVAL; | |
323 | } | |
324 | ||
325 | res = platform_get_resource_byname(to_platform_device(ddata->dev), | |
326 | IORESOURCE_MEM, name); | |
327 | if (!res) { | |
328 | dev_dbg(ddata->dev, "has no %s register\n", name); | |
329 | ddata->offsets[reg] = -ENODEV; | |
330 | ||
331 | return 0; | |
332 | } | |
333 | ||
334 | ddata->offsets[reg] = res->start - ddata->module_pa; | |
a7199e2b TL |
335 | if (reg == SYSC_REVISION) |
336 | sysc_check_quirk_16bit(ddata, res); | |
0eecc636 TL |
337 | |
338 | return 0; | |
339 | } | |
340 | ||
341 | static int sysc_parse_registers(struct sysc *ddata) | |
342 | { | |
343 | int i, error; | |
344 | ||
345 | for (i = 0; i < SYSC_MAX_REGS; i++) { | |
346 | error = sysc_parse_one(ddata, i); | |
347 | if (error) | |
348 | return error; | |
349 | } | |
350 | ||
351 | return 0; | |
352 | } | |
353 | ||
354 | /** | |
355 | * sysc_check_registers - check for misconfigured register overlaps | |
356 | * @ddata: device driver data | |
357 | */ | |
358 | static int sysc_check_registers(struct sysc *ddata) | |
359 | { | |
360 | int i, j, nr_regs = 0, nr_matches = 0; | |
361 | ||
362 | for (i = 0; i < SYSC_MAX_REGS; i++) { | |
363 | if (ddata->offsets[i] < 0) | |
364 | continue; | |
365 | ||
366 | if (ddata->offsets[i] > (ddata->module_size - 4)) { | |
367 | dev_err(ddata->dev, "register outside module range"); | |
368 | ||
369 | return -EINVAL; | |
370 | } | |
371 | ||
372 | for (j = 0; j < SYSC_MAX_REGS; j++) { | |
373 | if (ddata->offsets[j] < 0) | |
374 | continue; | |
375 | ||
376 | if (ddata->offsets[i] == ddata->offsets[j]) | |
377 | nr_matches++; | |
378 | } | |
379 | nr_regs++; | |
380 | } | |
381 | ||
382 | if (nr_regs < 1) { | |
383 | dev_err(ddata->dev, "missing registers\n"); | |
384 | ||
385 | return -EINVAL; | |
386 | } | |
387 | ||
388 | if (nr_matches > nr_regs) { | |
389 | dev_err(ddata->dev, "overlapping registers: (%i/%i)", | |
390 | nr_regs, nr_matches); | |
391 | ||
392 | return -EINVAL; | |
393 | } | |
394 | ||
395 | return 0; | |
396 | } | |
397 | ||
398 | /** | |
399 | * syc_ioremap - ioremap register space for the interconnect target module | |
400 | * @ddata: deviec driver data | |
401 | * | |
402 | * Note that the interconnect target module registers can be anywhere | |
403 | * within the first child device address space. For example, SGX has | |
404 | * them at offset 0x1fc00 in the 32MB module address space. We just | |
405 | * what we need around the interconnect target module registers. | |
406 | */ | |
407 | static int sysc_ioremap(struct sysc *ddata) | |
408 | { | |
409 | u32 size = 0; | |
410 | ||
411 | if (ddata->offsets[SYSC_SYSSTATUS] >= 0) | |
412 | size = ddata->offsets[SYSC_SYSSTATUS]; | |
413 | else if (ddata->offsets[SYSC_SYSCONFIG] >= 0) | |
414 | size = ddata->offsets[SYSC_SYSCONFIG]; | |
415 | else if (ddata->offsets[SYSC_REVISION] >= 0) | |
416 | size = ddata->offsets[SYSC_REVISION]; | |
417 | else | |
418 | return -EINVAL; | |
419 | ||
420 | size &= 0xfff00; | |
421 | size += SZ_256; | |
422 | ||
423 | ddata->module_va = devm_ioremap(ddata->dev, | |
424 | ddata->module_pa, | |
425 | size); | |
426 | if (!ddata->module_va) | |
427 | return -EIO; | |
428 | ||
429 | return 0; | |
430 | } | |
431 | ||
432 | /** | |
433 | * sysc_map_and_check_registers - ioremap and check device registers | |
434 | * @ddata: device driver data | |
435 | */ | |
436 | static int sysc_map_and_check_registers(struct sysc *ddata) | |
437 | { | |
438 | int error; | |
439 | ||
440 | error = sysc_parse_and_check_child_range(ddata); | |
441 | if (error) | |
442 | return error; | |
443 | ||
444 | error = sysc_check_children(ddata); | |
445 | if (error) | |
446 | return error; | |
447 | ||
448 | error = sysc_parse_registers(ddata); | |
449 | if (error) | |
450 | return error; | |
451 | ||
452 | error = sysc_ioremap(ddata); | |
453 | if (error) | |
454 | return error; | |
455 | ||
456 | error = sysc_check_registers(ddata); | |
457 | if (error) | |
458 | return error; | |
459 | ||
460 | return 0; | |
461 | } | |
462 | ||
463 | /** | |
464 | * sysc_show_rev - read and show interconnect target module revision | |
465 | * @bufp: buffer to print the information to | |
466 | * @ddata: device driver data | |
467 | */ | |
468 | static int sysc_show_rev(char *bufp, struct sysc *ddata) | |
469 | { | |
566a9b05 | 470 | int len; |
0eecc636 TL |
471 | |
472 | if (ddata->offsets[SYSC_REVISION] < 0) | |
473 | return sprintf(bufp, ":NA"); | |
474 | ||
566a9b05 | 475 | len = sprintf(bufp, ":%08x", ddata->revision); |
0eecc636 TL |
476 | |
477 | return len; | |
478 | } | |
479 | ||
480 | static int sysc_show_reg(struct sysc *ddata, | |
481 | char *bufp, enum sysc_registers reg) | |
482 | { | |
483 | if (ddata->offsets[reg] < 0) | |
484 | return sprintf(bufp, ":NA"); | |
485 | ||
486 | return sprintf(bufp, ":%x", ddata->offsets[reg]); | |
487 | } | |
488 | ||
489 | /** | |
490 | * sysc_show_registers - show information about interconnect target module | |
491 | * @ddata: device driver data | |
492 | */ | |
493 | static void sysc_show_registers(struct sysc *ddata) | |
494 | { | |
495 | char buf[128]; | |
496 | char *bufp = buf; | |
497 | int i; | |
498 | ||
499 | for (i = 0; i < SYSC_MAX_REGS; i++) | |
500 | bufp += sysc_show_reg(ddata, bufp, i); | |
501 | ||
502 | bufp += sysc_show_rev(bufp, ddata); | |
503 | ||
504 | dev_dbg(ddata->dev, "%llx:%x%s\n", | |
505 | ddata->module_pa, ddata->module_size, | |
506 | buf); | |
507 | } | |
508 | ||
a4a5d493 | 509 | static int __maybe_unused sysc_runtime_suspend(struct device *dev) |
0eecc636 TL |
510 | { |
511 | struct sysc *ddata; | |
512 | int i; | |
513 | ||
514 | ddata = dev_get_drvdata(dev); | |
515 | ||
516 | if (ddata->legacy_mode) | |
517 | return 0; | |
518 | ||
519 | for (i = 0; i < SYSC_MAX_CLOCKS; i++) { | |
520 | if (IS_ERR_OR_NULL(ddata->clocks[i])) | |
521 | continue; | |
522 | clk_disable(ddata->clocks[i]); | |
523 | } | |
524 | ||
525 | return 0; | |
526 | } | |
527 | ||
a4a5d493 | 528 | static int __maybe_unused sysc_runtime_resume(struct device *dev) |
0eecc636 TL |
529 | { |
530 | struct sysc *ddata; | |
531 | int i, error; | |
532 | ||
533 | ddata = dev_get_drvdata(dev); | |
534 | ||
535 | if (ddata->legacy_mode) | |
536 | return 0; | |
537 | ||
538 | for (i = 0; i < SYSC_MAX_CLOCKS; i++) { | |
539 | if (IS_ERR_OR_NULL(ddata->clocks[i])) | |
540 | continue; | |
541 | error = clk_enable(ddata->clocks[i]); | |
542 | if (error) | |
543 | return error; | |
544 | } | |
545 | ||
546 | return 0; | |
547 | } | |
548 | ||
62020f23 TL |
549 | #ifdef CONFIG_PM_SLEEP |
550 | static int sysc_suspend(struct device *dev) | |
551 | { | |
552 | struct sysc *ddata; | |
553 | ||
554 | ddata = dev_get_drvdata(dev); | |
555 | ||
556 | if (!ddata->enabled) | |
557 | return 0; | |
558 | ||
559 | ddata->needs_resume = true; | |
560 | ||
561 | return sysc_runtime_suspend(dev); | |
562 | } | |
563 | ||
564 | static int sysc_resume(struct device *dev) | |
565 | { | |
566 | struct sysc *ddata; | |
567 | ||
568 | ddata = dev_get_drvdata(dev); | |
569 | if (ddata->needs_resume) { | |
570 | ddata->needs_resume = false; | |
571 | ||
572 | return sysc_runtime_resume(dev); | |
573 | } | |
574 | ||
575 | return 0; | |
576 | } | |
577 | #endif | |
578 | ||
0eecc636 | 579 | static const struct dev_pm_ops sysc_pm_ops = { |
62020f23 | 580 | SET_SYSTEM_SLEEP_PM_OPS(sysc_suspend, sysc_resume) |
0eecc636 TL |
581 | SET_RUNTIME_PM_OPS(sysc_runtime_suspend, |
582 | sysc_runtime_resume, | |
583 | NULL) | |
584 | }; | |
585 | ||
566a9b05 TL |
586 | /* At this point the module is configured enough to read the revision */ |
587 | static int sysc_init_module(struct sysc *ddata) | |
588 | { | |
589 | int error; | |
590 | ||
591 | error = pm_runtime_get_sync(ddata->dev); | |
592 | if (error < 0) { | |
593 | pm_runtime_put_noidle(ddata->dev); | |
594 | ||
595 | return 0; | |
596 | } | |
597 | ddata->revision = sysc_read_revision(ddata); | |
598 | pm_runtime_put_sync(ddata->dev); | |
599 | ||
600 | return 0; | |
601 | } | |
602 | ||
c5a2de97 TL |
603 | static int sysc_init_sysc_mask(struct sysc *ddata) |
604 | { | |
605 | struct device_node *np = ddata->dev->of_node; | |
606 | int error; | |
607 | u32 val; | |
608 | ||
609 | error = of_property_read_u32(np, "ti,sysc-mask", &val); | |
610 | if (error) | |
611 | return 0; | |
612 | ||
613 | if (val) | |
614 | ddata->cfg.sysc_val = val & ddata->cap->sysc_mask; | |
615 | else | |
616 | ddata->cfg.sysc_val = ddata->cap->sysc_mask; | |
617 | ||
618 | return 0; | |
619 | } | |
620 | ||
621 | static int sysc_init_idlemode(struct sysc *ddata, u8 *idlemodes, | |
622 | const char *name) | |
623 | { | |
624 | struct device_node *np = ddata->dev->of_node; | |
625 | struct property *prop; | |
626 | const __be32 *p; | |
627 | u32 val; | |
628 | ||
629 | of_property_for_each_u32(np, name, prop, p, val) { | |
630 | if (val >= SYSC_NR_IDLEMODES) { | |
631 | dev_err(ddata->dev, "invalid idlemode: %i\n", val); | |
632 | return -EINVAL; | |
633 | } | |
634 | *idlemodes |= (1 << val); | |
635 | } | |
636 | ||
637 | return 0; | |
638 | } | |
639 | ||
640 | static int sysc_init_idlemodes(struct sysc *ddata) | |
641 | { | |
642 | int error; | |
643 | ||
644 | error = sysc_init_idlemode(ddata, &ddata->cfg.midlemodes, | |
645 | "ti,sysc-midle"); | |
646 | if (error) | |
647 | return error; | |
648 | ||
649 | error = sysc_init_idlemode(ddata, &ddata->cfg.sidlemodes, | |
650 | "ti,sysc-sidle"); | |
651 | if (error) | |
652 | return error; | |
653 | ||
654 | return 0; | |
655 | } | |
656 | ||
657 | /* | |
658 | * Only some devices on omap4 and later have SYSCONFIG reset done | |
659 | * bit. We can detect this if there is no SYSSTATUS at all, or the | |
660 | * SYSTATUS bit 0 is not used. Note that some SYSSTATUS registers | |
661 | * have multiple bits for the child devices like OHCI and EHCI. | |
662 | * Depends on SYSC being parsed first. | |
663 | */ | |
664 | static int sysc_init_syss_mask(struct sysc *ddata) | |
665 | { | |
666 | struct device_node *np = ddata->dev->of_node; | |
667 | int error; | |
668 | u32 val; | |
669 | ||
670 | error = of_property_read_u32(np, "ti,syss-mask", &val); | |
671 | if (error) { | |
672 | if ((ddata->cap->type == TI_SYSC_OMAP4 || | |
673 | ddata->cap->type == TI_SYSC_OMAP4_TIMER) && | |
674 | (ddata->cfg.sysc_val & SYSC_OMAP4_SOFTRESET)) | |
675 | ddata->cfg.quirks |= SYSC_QUIRK_RESET_STATUS; | |
676 | ||
677 | return 0; | |
678 | } | |
679 | ||
680 | if (!(val & 1) && (ddata->cfg.sysc_val & SYSC_OMAP4_SOFTRESET)) | |
681 | ddata->cfg.quirks |= SYSC_QUIRK_RESET_STATUS; | |
682 | ||
683 | ddata->cfg.syss_mask = val; | |
684 | ||
685 | return 0; | |
686 | } | |
687 | ||
2c355ff6 TL |
688 | /* |
689 | * Many child device drivers need to have fck available to get the clock | |
690 | * rate for device internal configuration. | |
691 | */ | |
692 | static int sysc_child_add_fck(struct sysc *ddata, | |
693 | struct device *child) | |
694 | { | |
695 | struct clk *fck; | |
696 | struct clk_lookup *l; | |
697 | const char *name = clock_names[SYSC_FCK]; | |
698 | ||
699 | if (IS_ERR_OR_NULL(ddata->clocks[SYSC_FCK])) | |
700 | return 0; | |
701 | ||
702 | fck = clk_get(child, name); | |
703 | if (!IS_ERR(fck)) { | |
704 | clk_put(fck); | |
705 | ||
706 | return -EEXIST; | |
707 | } | |
708 | ||
709 | l = clkdev_create(ddata->clocks[SYSC_FCK], name, dev_name(child)); | |
710 | ||
711 | return l ? 0 : -ENODEV; | |
712 | } | |
713 | ||
714 | static struct device_type sysc_device_type = { | |
715 | }; | |
716 | ||
717 | static struct sysc *sysc_child_to_parent(struct device *dev) | |
718 | { | |
719 | struct device *parent = dev->parent; | |
720 | ||
721 | if (!parent || parent->type != &sysc_device_type) | |
722 | return NULL; | |
723 | ||
724 | return dev_get_drvdata(parent); | |
725 | } | |
726 | ||
727 | static int sysc_notifier_call(struct notifier_block *nb, | |
728 | unsigned long event, void *device) | |
729 | { | |
730 | struct device *dev = device; | |
731 | struct sysc *ddata; | |
732 | int error; | |
733 | ||
734 | ddata = sysc_child_to_parent(dev); | |
735 | if (!ddata) | |
736 | return NOTIFY_DONE; | |
737 | ||
738 | switch (event) { | |
739 | case BUS_NOTIFY_ADD_DEVICE: | |
740 | error = sysc_child_add_fck(ddata, dev); | |
741 | if (error && error != -EEXIST) | |
742 | dev_warn(ddata->dev, "could not add %s fck: %i\n", | |
743 | dev_name(dev), error); | |
744 | break; | |
745 | default: | |
746 | break; | |
747 | } | |
748 | ||
749 | return NOTIFY_DONE; | |
750 | } | |
751 | ||
752 | static struct notifier_block sysc_nb = { | |
753 | .notifier_call = sysc_notifier_call, | |
754 | }; | |
755 | ||
566a9b05 TL |
756 | /* Device tree configured quirks */ |
757 | struct sysc_dts_quirk { | |
758 | const char *name; | |
759 | u32 mask; | |
760 | }; | |
761 | ||
762 | static const struct sysc_dts_quirk sysc_dts_quirks[] = { | |
763 | { .name = "ti,no-idle-on-init", | |
764 | .mask = SYSC_QUIRK_NO_IDLE_ON_INIT, }, | |
765 | { .name = "ti,no-reset-on-init", | |
766 | .mask = SYSC_QUIRK_NO_RESET_ON_INIT, }, | |
767 | }; | |
768 | ||
769 | static int sysc_init_dts_quirks(struct sysc *ddata) | |
770 | { | |
771 | struct device_node *np = ddata->dev->of_node; | |
772 | const struct property *prop; | |
773 | int i, len, error; | |
774 | u32 val; | |
775 | ||
776 | ddata->legacy_mode = of_get_property(np, "ti,hwmods", NULL); | |
777 | ||
778 | for (i = 0; i < ARRAY_SIZE(sysc_dts_quirks); i++) { | |
779 | prop = of_get_property(np, sysc_dts_quirks[i].name, &len); | |
780 | if (!prop) | |
781 | break; | |
782 | ||
783 | ddata->cfg.quirks |= sysc_dts_quirks[i].mask; | |
784 | } | |
785 | ||
786 | error = of_property_read_u32(np, "ti,sysc-delay-us", &val); | |
787 | if (!error) { | |
788 | if (val > 255) { | |
789 | dev_warn(ddata->dev, "bad ti,sysc-delay-us: %i\n", | |
790 | val); | |
791 | } | |
792 | ||
793 | ddata->cfg.srst_udelay = (u8)val; | |
794 | } | |
795 | ||
796 | return 0; | |
797 | } | |
798 | ||
0eecc636 TL |
799 | static void sysc_unprepare(struct sysc *ddata) |
800 | { | |
801 | int i; | |
802 | ||
803 | for (i = 0; i < SYSC_MAX_CLOCKS; i++) { | |
804 | if (!IS_ERR_OR_NULL(ddata->clocks[i])) | |
805 | clk_unprepare(ddata->clocks[i]); | |
806 | } | |
807 | } | |
808 | ||
70a65240 TL |
809 | /* |
810 | * Common sysc register bits found on omap2, also known as type1 | |
811 | */ | |
812 | static const struct sysc_regbits sysc_regbits_omap2 = { | |
813 | .dmadisable_shift = -ENODEV, | |
814 | .midle_shift = 12, | |
815 | .sidle_shift = 3, | |
816 | .clkact_shift = 8, | |
817 | .emufree_shift = 5, | |
818 | .enwkup_shift = 2, | |
819 | .srst_shift = 1, | |
820 | .autoidle_shift = 0, | |
821 | }; | |
822 | ||
823 | static const struct sysc_capabilities sysc_omap2 = { | |
824 | .type = TI_SYSC_OMAP2, | |
825 | .sysc_mask = SYSC_OMAP2_CLOCKACTIVITY | SYSC_OMAP2_EMUFREE | | |
826 | SYSC_OMAP2_ENAWAKEUP | SYSC_OMAP2_SOFTRESET | | |
827 | SYSC_OMAP2_AUTOIDLE, | |
828 | .regbits = &sysc_regbits_omap2, | |
829 | }; | |
830 | ||
831 | /* All omap2 and 3 timers, and timers 1, 2 & 10 on omap 4 and 5 */ | |
832 | static const struct sysc_capabilities sysc_omap2_timer = { | |
833 | .type = TI_SYSC_OMAP2_TIMER, | |
834 | .sysc_mask = SYSC_OMAP2_CLOCKACTIVITY | SYSC_OMAP2_EMUFREE | | |
835 | SYSC_OMAP2_ENAWAKEUP | SYSC_OMAP2_SOFTRESET | | |
836 | SYSC_OMAP2_AUTOIDLE, | |
837 | .regbits = &sysc_regbits_omap2, | |
838 | .mod_quirks = SYSC_QUIRK_USE_CLOCKACT, | |
839 | }; | |
840 | ||
841 | /* | |
842 | * SHAM2 (SHA1/MD5) sysc found on omap3, a variant of sysc_regbits_omap2 | |
843 | * with different sidle position | |
844 | */ | |
845 | static const struct sysc_regbits sysc_regbits_omap3_sham = { | |
846 | .dmadisable_shift = -ENODEV, | |
847 | .midle_shift = -ENODEV, | |
848 | .sidle_shift = 4, | |
849 | .clkact_shift = -ENODEV, | |
850 | .enwkup_shift = -ENODEV, | |
851 | .srst_shift = 1, | |
852 | .autoidle_shift = 0, | |
853 | .emufree_shift = -ENODEV, | |
854 | }; | |
855 | ||
856 | static const struct sysc_capabilities sysc_omap3_sham = { | |
857 | .type = TI_SYSC_OMAP3_SHAM, | |
858 | .sysc_mask = SYSC_OMAP2_SOFTRESET | SYSC_OMAP2_AUTOIDLE, | |
859 | .regbits = &sysc_regbits_omap3_sham, | |
860 | }; | |
861 | ||
862 | /* | |
863 | * AES register bits found on omap3 and later, a variant of | |
864 | * sysc_regbits_omap2 with different sidle position | |
865 | */ | |
866 | static const struct sysc_regbits sysc_regbits_omap3_aes = { | |
867 | .dmadisable_shift = -ENODEV, | |
868 | .midle_shift = -ENODEV, | |
869 | .sidle_shift = 6, | |
870 | .clkact_shift = -ENODEV, | |
871 | .enwkup_shift = -ENODEV, | |
872 | .srst_shift = 1, | |
873 | .autoidle_shift = 0, | |
874 | .emufree_shift = -ENODEV, | |
875 | }; | |
876 | ||
877 | static const struct sysc_capabilities sysc_omap3_aes = { | |
878 | .type = TI_SYSC_OMAP3_AES, | |
879 | .sysc_mask = SYSC_OMAP2_SOFTRESET | SYSC_OMAP2_AUTOIDLE, | |
880 | .regbits = &sysc_regbits_omap3_aes, | |
881 | }; | |
882 | ||
883 | /* | |
884 | * Common sysc register bits found on omap4, also known as type2 | |
885 | */ | |
886 | static const struct sysc_regbits sysc_regbits_omap4 = { | |
887 | .dmadisable_shift = 16, | |
888 | .midle_shift = 4, | |
889 | .sidle_shift = 2, | |
890 | .clkact_shift = -ENODEV, | |
891 | .enwkup_shift = -ENODEV, | |
892 | .emufree_shift = 1, | |
893 | .srst_shift = 0, | |
894 | .autoidle_shift = -ENODEV, | |
895 | }; | |
896 | ||
897 | static const struct sysc_capabilities sysc_omap4 = { | |
898 | .type = TI_SYSC_OMAP4, | |
899 | .sysc_mask = SYSC_OMAP4_DMADISABLE | SYSC_OMAP4_FREEEMU | | |
900 | SYSC_OMAP4_SOFTRESET, | |
901 | .regbits = &sysc_regbits_omap4, | |
902 | }; | |
903 | ||
904 | static const struct sysc_capabilities sysc_omap4_timer = { | |
905 | .type = TI_SYSC_OMAP4_TIMER, | |
906 | .sysc_mask = SYSC_OMAP4_DMADISABLE | SYSC_OMAP4_FREEEMU | | |
907 | SYSC_OMAP4_SOFTRESET, | |
908 | .regbits = &sysc_regbits_omap4, | |
909 | }; | |
910 | ||
911 | /* | |
912 | * Common sysc register bits found on omap4, also known as type3 | |
913 | */ | |
914 | static const struct sysc_regbits sysc_regbits_omap4_simple = { | |
915 | .dmadisable_shift = -ENODEV, | |
916 | .midle_shift = 2, | |
917 | .sidle_shift = 0, | |
918 | .clkact_shift = -ENODEV, | |
919 | .enwkup_shift = -ENODEV, | |
920 | .srst_shift = -ENODEV, | |
921 | .emufree_shift = -ENODEV, | |
922 | .autoidle_shift = -ENODEV, | |
923 | }; | |
924 | ||
925 | static const struct sysc_capabilities sysc_omap4_simple = { | |
926 | .type = TI_SYSC_OMAP4_SIMPLE, | |
927 | .regbits = &sysc_regbits_omap4_simple, | |
928 | }; | |
929 | ||
930 | /* | |
931 | * SmartReflex sysc found on omap34xx | |
932 | */ | |
933 | static const struct sysc_regbits sysc_regbits_omap34xx_sr = { | |
934 | .dmadisable_shift = -ENODEV, | |
935 | .midle_shift = -ENODEV, | |
936 | .sidle_shift = -ENODEV, | |
937 | .clkact_shift = 20, | |
938 | .enwkup_shift = -ENODEV, | |
939 | .srst_shift = -ENODEV, | |
940 | .emufree_shift = -ENODEV, | |
941 | .autoidle_shift = -ENODEV, | |
942 | }; | |
943 | ||
944 | static const struct sysc_capabilities sysc_34xx_sr = { | |
945 | .type = TI_SYSC_OMAP34XX_SR, | |
946 | .sysc_mask = SYSC_OMAP2_CLOCKACTIVITY, | |
947 | .regbits = &sysc_regbits_omap34xx_sr, | |
948 | .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED, | |
949 | }; | |
950 | ||
951 | /* | |
952 | * SmartReflex sysc found on omap36xx and later | |
953 | */ | |
954 | static const struct sysc_regbits sysc_regbits_omap36xx_sr = { | |
955 | .dmadisable_shift = -ENODEV, | |
956 | .midle_shift = -ENODEV, | |
957 | .sidle_shift = 24, | |
958 | .clkact_shift = -ENODEV, | |
959 | .enwkup_shift = 26, | |
960 | .srst_shift = -ENODEV, | |
961 | .emufree_shift = -ENODEV, | |
962 | .autoidle_shift = -ENODEV, | |
963 | }; | |
964 | ||
965 | static const struct sysc_capabilities sysc_36xx_sr = { | |
966 | .type = TI_SYSC_OMAP36XX_SR, | |
3267c081 | 967 | .sysc_mask = SYSC_OMAP3_SR_ENAWAKEUP, |
70a65240 TL |
968 | .regbits = &sysc_regbits_omap36xx_sr, |
969 | .mod_quirks = SYSC_QUIRK_UNCACHED, | |
970 | }; | |
971 | ||
972 | static const struct sysc_capabilities sysc_omap4_sr = { | |
973 | .type = TI_SYSC_OMAP4_SR, | |
974 | .regbits = &sysc_regbits_omap36xx_sr, | |
975 | }; | |
976 | ||
977 | /* | |
978 | * McASP register bits found on omap4 and later | |
979 | */ | |
980 | static const struct sysc_regbits sysc_regbits_omap4_mcasp = { | |
981 | .dmadisable_shift = -ENODEV, | |
982 | .midle_shift = -ENODEV, | |
983 | .sidle_shift = 0, | |
984 | .clkact_shift = -ENODEV, | |
985 | .enwkup_shift = -ENODEV, | |
986 | .srst_shift = -ENODEV, | |
987 | .emufree_shift = -ENODEV, | |
988 | .autoidle_shift = -ENODEV, | |
989 | }; | |
990 | ||
991 | static const struct sysc_capabilities sysc_omap4_mcasp = { | |
992 | .type = TI_SYSC_OMAP4_MCASP, | |
993 | .regbits = &sysc_regbits_omap4_mcasp, | |
994 | }; | |
995 | ||
996 | /* | |
997 | * FS USB host found on omap4 and later | |
998 | */ | |
999 | static const struct sysc_regbits sysc_regbits_omap4_usb_host_fs = { | |
1000 | .dmadisable_shift = -ENODEV, | |
1001 | .midle_shift = -ENODEV, | |
1002 | .sidle_shift = 24, | |
1003 | .clkact_shift = -ENODEV, | |
1004 | .enwkup_shift = 26, | |
1005 | .srst_shift = -ENODEV, | |
1006 | .emufree_shift = -ENODEV, | |
1007 | .autoidle_shift = -ENODEV, | |
1008 | }; | |
1009 | ||
1010 | static const struct sysc_capabilities sysc_omap4_usb_host_fs = { | |
1011 | .type = TI_SYSC_OMAP4_USB_HOST_FS, | |
1012 | .sysc_mask = SYSC_OMAP2_ENAWAKEUP, | |
1013 | .regbits = &sysc_regbits_omap4_usb_host_fs, | |
1014 | }; | |
1015 | ||
1016 | static int sysc_init_match(struct sysc *ddata) | |
1017 | { | |
1018 | const struct sysc_capabilities *cap; | |
1019 | ||
1020 | cap = of_device_get_match_data(ddata->dev); | |
1021 | if (!cap) | |
1022 | return -EINVAL; | |
1023 | ||
1024 | ddata->cap = cap; | |
1025 | if (ddata->cap) | |
1026 | ddata->cfg.quirks |= ddata->cap->mod_quirks; | |
1027 | ||
1028 | return 0; | |
1029 | } | |
1030 | ||
0eecc636 TL |
1031 | static int sysc_probe(struct platform_device *pdev) |
1032 | { | |
0eecc636 TL |
1033 | struct sysc *ddata; |
1034 | int error; | |
1035 | ||
1036 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | |
1037 | if (!ddata) | |
1038 | return -ENOMEM; | |
1039 | ||
1040 | ddata->dev = &pdev->dev; | |
566a9b05 | 1041 | platform_set_drvdata(pdev, ddata); |
0eecc636 | 1042 | |
70a65240 TL |
1043 | error = sysc_init_match(ddata); |
1044 | if (error) | |
1045 | return error; | |
1046 | ||
566a9b05 TL |
1047 | error = sysc_init_dts_quirks(ddata); |
1048 | if (error) | |
1049 | goto unprepare; | |
1050 | ||
0eecc636 TL |
1051 | error = sysc_get_clocks(ddata); |
1052 | if (error) | |
1053 | return error; | |
1054 | ||
1055 | error = sysc_map_and_check_registers(ddata); | |
1056 | if (error) | |
1057 | goto unprepare; | |
1058 | ||
c5a2de97 TL |
1059 | error = sysc_init_sysc_mask(ddata); |
1060 | if (error) | |
1061 | goto unprepare; | |
1062 | ||
1063 | error = sysc_init_idlemodes(ddata); | |
1064 | if (error) | |
1065 | goto unprepare; | |
1066 | ||
1067 | error = sysc_init_syss_mask(ddata); | |
1068 | if (error) | |
1069 | goto unprepare; | |
1070 | ||
0eecc636 | 1071 | pm_runtime_enable(ddata->dev); |
566a9b05 TL |
1072 | |
1073 | error = sysc_init_module(ddata); | |
1074 | if (error) | |
1075 | goto unprepare; | |
1076 | ||
0eecc636 TL |
1077 | error = pm_runtime_get_sync(ddata->dev); |
1078 | if (error < 0) { | |
1079 | pm_runtime_put_noidle(ddata->dev); | |
1080 | pm_runtime_disable(ddata->dev); | |
1081 | goto unprepare; | |
1082 | } | |
1083 | ||
1084 | pm_runtime_use_autosuspend(ddata->dev); | |
1085 | ||
1086 | sysc_show_registers(ddata); | |
1087 | ||
2c355ff6 | 1088 | ddata->dev->type = &sysc_device_type; |
0eecc636 TL |
1089 | error = of_platform_populate(ddata->dev->of_node, |
1090 | NULL, NULL, ddata->dev); | |
1091 | if (error) | |
1092 | goto err; | |
1093 | ||
1094 | pm_runtime_mark_last_busy(ddata->dev); | |
1095 | pm_runtime_put_autosuspend(ddata->dev); | |
1096 | ||
1097 | return 0; | |
1098 | ||
1099 | err: | |
1100 | pm_runtime_dont_use_autosuspend(&pdev->dev); | |
1101 | pm_runtime_put_sync(&pdev->dev); | |
1102 | pm_runtime_disable(&pdev->dev); | |
1103 | unprepare: | |
1104 | sysc_unprepare(ddata); | |
1105 | ||
1106 | return error; | |
1107 | } | |
1108 | ||
684be5a4 TL |
1109 | static int sysc_remove(struct platform_device *pdev) |
1110 | { | |
1111 | struct sysc *ddata = platform_get_drvdata(pdev); | |
1112 | int error; | |
1113 | ||
1114 | error = pm_runtime_get_sync(ddata->dev); | |
1115 | if (error < 0) { | |
1116 | pm_runtime_put_noidle(ddata->dev); | |
1117 | pm_runtime_disable(ddata->dev); | |
1118 | goto unprepare; | |
1119 | } | |
1120 | ||
1121 | of_platform_depopulate(&pdev->dev); | |
1122 | ||
1123 | pm_runtime_dont_use_autosuspend(&pdev->dev); | |
1124 | pm_runtime_put_sync(&pdev->dev); | |
1125 | pm_runtime_disable(&pdev->dev); | |
1126 | ||
1127 | unprepare: | |
1128 | sysc_unprepare(ddata); | |
1129 | ||
1130 | return 0; | |
1131 | } | |
1132 | ||
0eecc636 | 1133 | static const struct of_device_id sysc_match[] = { |
70a65240 TL |
1134 | { .compatible = "ti,sysc-omap2", .data = &sysc_omap2, }, |
1135 | { .compatible = "ti,sysc-omap2-timer", .data = &sysc_omap2_timer, }, | |
1136 | { .compatible = "ti,sysc-omap4", .data = &sysc_omap4, }, | |
1137 | { .compatible = "ti,sysc-omap4-timer", .data = &sysc_omap4_timer, }, | |
1138 | { .compatible = "ti,sysc-omap4-simple", .data = &sysc_omap4_simple, }, | |
1139 | { .compatible = "ti,sysc-omap3430-sr", .data = &sysc_34xx_sr, }, | |
1140 | { .compatible = "ti,sysc-omap3630-sr", .data = &sysc_36xx_sr, }, | |
1141 | { .compatible = "ti,sysc-omap4-sr", .data = &sysc_omap4_sr, }, | |
1142 | { .compatible = "ti,sysc-omap3-sham", .data = &sysc_omap3_sham, }, | |
1143 | { .compatible = "ti,sysc-omap-aes", .data = &sysc_omap3_aes, }, | |
1144 | { .compatible = "ti,sysc-mcasp", .data = &sysc_omap4_mcasp, }, | |
1145 | { .compatible = "ti,sysc-usb-host-fs", | |
1146 | .data = &sysc_omap4_usb_host_fs, }, | |
0eecc636 TL |
1147 | { }, |
1148 | }; | |
1149 | MODULE_DEVICE_TABLE(of, sysc_match); | |
1150 | ||
1151 | static struct platform_driver sysc_driver = { | |
1152 | .probe = sysc_probe, | |
684be5a4 | 1153 | .remove = sysc_remove, |
0eecc636 TL |
1154 | .driver = { |
1155 | .name = "ti-sysc", | |
1156 | .of_match_table = sysc_match, | |
1157 | .pm = &sysc_pm_ops, | |
1158 | }, | |
1159 | }; | |
2c355ff6 TL |
1160 | |
1161 | static int __init sysc_init(void) | |
1162 | { | |
1163 | bus_register_notifier(&platform_bus_type, &sysc_nb); | |
1164 | ||
1165 | return platform_driver_register(&sysc_driver); | |
1166 | } | |
1167 | module_init(sysc_init); | |
1168 | ||
1169 | static void __exit sysc_exit(void) | |
1170 | { | |
1171 | bus_unregister_notifier(&platform_bus_type, &sysc_nb); | |
1172 | platform_driver_unregister(&sysc_driver); | |
1173 | } | |
1174 | module_exit(sysc_exit); | |
0eecc636 TL |
1175 | |
1176 | MODULE_DESCRIPTION("TI sysc interconnect target driver"); | |
1177 | MODULE_LICENSE("GPL v2"); |