1 /*-
2 * Copyright 2016 Michal Meloun <[email protected]>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "opt_platform.h"
31 #include <sys/param.h>
32 #include <sys/conf.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/queue.h>
36 #include <sys/kobj.h>
37 #include <sys/malloc.h>
38 #include <sys/mutex.h>
39 #include <sys/limits.h>
40 #include <sys/lock.h>
41 #include <sys/sbuf.h>
42 #include <sys/sysctl.h>
43 #include <sys/systm.h>
44 #include <sys/sx.h>
45
46 #ifdef FDT
47 #include <dev/fdt/fdt_common.h>
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50 #endif
51 #include <dev/extres/clk/clk.h>
52
53 SYSCTL_NODE(_hw, OID_AUTO, clock, CTLFLAG_RD, NULL, "Clocks");
54
55 MALLOC_DEFINE(M_CLOCK, "clocks", "Clock framework");
56
57 /* Forward declarations. */
58 struct clk;
59 struct clknodenode;
60 struct clkdom;
61
62 typedef TAILQ_HEAD(clknode_list, clknode) clknode_list_t;
63 typedef TAILQ_HEAD(clkdom_list, clkdom) clkdom_list_t;
64
65 /* Default clock methods. */
66 static int clknode_method_init(struct clknode *clk, device_t dev);
67 static int clknode_method_recalc_freq(struct clknode *clk, uint64_t *freq);
68 static int clknode_method_set_freq(struct clknode *clk, uint64_t fin,
69 uint64_t *fout, int flags, int *stop);
70 static int clknode_method_set_gate(struct clknode *clk, bool enable);
71 static int clknode_method_set_mux(struct clknode *clk, int idx);
72
73 /*
74 * Clock controller methods.
75 */
76 static clknode_method_t clknode_methods[] = {
77 CLKNODEMETHOD(clknode_init, clknode_method_init),
78 CLKNODEMETHOD(clknode_recalc_freq, clknode_method_recalc_freq),
79 CLKNODEMETHOD(clknode_set_freq, clknode_method_set_freq),
80 CLKNODEMETHOD(clknode_set_gate, clknode_method_set_gate),
81 CLKNODEMETHOD(clknode_set_mux, clknode_method_set_mux),
82
83 CLKNODEMETHOD_END
84 };
85 DEFINE_CLASS_0(clknode, clknode_class, clknode_methods, 0);
86
87 /*
88 * Clock node - basic element for modeling SOC clock graph. It holds the clock
89 * provider's data about the clock, and the links for the clock's membership in
90 * various lists.
91 */
92 struct clknode {
93 KOBJ_FIELDS;
94
95 /* Clock nodes topology. */
96 struct clkdom *clkdom; /* Owning clock domain */
97 TAILQ_ENTRY(clknode) clkdom_link; /* Domain list entry */
98 TAILQ_ENTRY(clknode) clklist_link; /* Global list entry */
99
100 /* String based parent list. */
101 const char **parent_names; /* Array of parent names */
102 int parent_cnt; /* Number of parents */
103 int parent_idx; /* Parent index or -1 */
104
105 /* Cache for already resolved names. */
106 struct clknode **parents; /* Array of potential parents */
107 struct clknode *parent; /* Current parent */
108
109 /* Parent/child relationship links. */
110 clknode_list_t children; /* List of our children */
111 TAILQ_ENTRY(clknode) sibling_link; /* Our entry in parent's list */
112
113 /* Details of this device. */
114 void *softc; /* Instance softc */
115 const char *name; /* Globally unique name */
116 intptr_t id; /* Per domain unique id */
117 int flags; /* CLK_FLAG_* */
118 struct sx lock; /* Lock for this clock */
119 int ref_cnt; /* Reference counter */
120 int enable_cnt; /* Enabled counter */
121
122 /* Cached values. */
123 uint64_t freq; /* Actual frequency */
124
125 struct sysctl_ctx_list sysctl_ctx;
126 };
127
128 /*
129 * Per consumer data, information about how a consumer is using a clock node.
130 * A pointer to this structure is used as a handle in the consumer interface.
131 */
132 struct clk {
133 device_t dev;
134 struct clknode *clknode;
135 int enable_cnt;
136 };
137
138 /*
139 * Clock domain - a group of clocks provided by one clock device.
140 */
141 struct clkdom {
142 device_t dev; /* Link to provider device */
143 TAILQ_ENTRY(clkdom) link; /* Global domain list entry */
144 clknode_list_t clknode_list; /* All clocks in the domain */
145
146 #ifdef FDT
147 clknode_ofw_mapper_func *ofw_mapper; /* Find clock using FDT xref */
148 #endif
149 };
150
151 /*
152 * The system-wide list of clock domains.
153 */
154 static clkdom_list_t clkdom_list = TAILQ_HEAD_INITIALIZER(clkdom_list);
155
156 /*
157 * Each clock node is linked on a system-wide list and can be searched by name.
158 */
159 static clknode_list_t clknode_list = TAILQ_HEAD_INITIALIZER(clknode_list);
160
161 /*
162 * Locking - we use three levels of locking:
163 * - First, topology lock is taken. This one protect all lists.
164 * - Second level is per clknode lock. It protects clknode data.
165 * - Third level is outside of this file, it protect clock device registers.
166 * First two levels use sleepable locks; clock device can use mutex or sx lock.
167 */
168 static struct sx clk_topo_lock;
169 SX_SYSINIT(clock_topology, &clk_topo_lock, "Clock topology lock");
170
171 #define CLK_TOPO_SLOCK() sx_slock(&clk_topo_lock)
172 #define CLK_TOPO_XLOCK() sx_xlock(&clk_topo_lock)
173 #define CLK_TOPO_UNLOCK() sx_unlock(&clk_topo_lock)
174 #define CLK_TOPO_ASSERT() sx_assert(&clk_topo_lock, SA_LOCKED)
175 #define CLK_TOPO_XASSERT() sx_assert(&clk_topo_lock, SA_XLOCKED)
176
177 #define CLKNODE_SLOCK(_sc) sx_slock(&((_sc)->lock))
178 #define CLKNODE_XLOCK(_sc) sx_xlock(&((_sc)->lock))
179 #define CLKNODE_UNLOCK(_sc) sx_unlock(&((_sc)->lock))
180
181 static void clknode_adjust_parent(struct clknode *clknode, int idx);
182
183 enum clknode_sysctl_type {
184 CLKNODE_SYSCTL_PARENT,
185 CLKNODE_SYSCTL_PARENTS_LIST,
186 CLKNODE_SYSCTL_CHILDREN_LIST,
187 };
188
189 static int clknode_sysctl(SYSCTL_HANDLER_ARGS);
190 static int clkdom_sysctl(SYSCTL_HANDLER_ARGS);
191
192 /*
193 * Default clock methods for base class.
194 */
195 static int
clknode_method_init(struct clknode * clknode,device_t dev)196 clknode_method_init(struct clknode *clknode, device_t dev)
197 {
198
199 return (0);
200 }
201
202 static int
clknode_method_recalc_freq(struct clknode * clknode,uint64_t * freq)203 clknode_method_recalc_freq(struct clknode *clknode, uint64_t *freq)
204 {
205
206 return (0);
207 }
208
209 static int
clknode_method_set_freq(struct clknode * clknode,uint64_t fin,uint64_t * fout,int flags,int * stop)210 clknode_method_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout,
211 int flags, int *stop)
212 {
213
214 *stop = 0;
215 return (0);
216 }
217
218 static int
clknode_method_set_gate(struct clknode * clk,bool enable)219 clknode_method_set_gate(struct clknode *clk, bool enable)
220 {
221
222 return (0);
223 }
224
225 static int
clknode_method_set_mux(struct clknode * clk,int idx)226 clknode_method_set_mux(struct clknode *clk, int idx)
227 {
228
229 return (0);
230 }
231
232 /*
233 * Internal functions.
234 */
235
236 /*
237 * Duplicate an array of parent names.
238 *
239 * Compute total size and allocate a single block which holds both the array of
240 * pointers to strings and the copied strings themselves. Returns a pointer to
241 * the start of the block where the array of copied string pointers lives.
242 *
243 * XXX Revisit this, no need for the DECONST stuff.
244 */
245 static const char **
strdup_list(const char ** names,int num)246 strdup_list(const char **names, int num)
247 {
248 size_t len, slen;
249 const char **outptr, *ptr;
250 int i;
251
252 len = sizeof(char *) * num;
253 for (i = 0; i < num; i++) {
254 if (names[i] == NULL)
255 continue;
256 slen = strlen(names[i]);
257 if (slen == 0)
258 panic("Clock parent names array have empty string");
259 len += slen + 1;
260 }
261 outptr = malloc(len, M_CLOCK, M_WAITOK | M_ZERO);
262 ptr = (char *)(outptr + num);
263 for (i = 0; i < num; i++) {
264 if (names[i] == NULL)
265 continue;
266 outptr[i] = ptr;
267 slen = strlen(names[i]) + 1;
268 bcopy(names[i], __DECONST(void *, outptr[i]), slen);
269 ptr += slen;
270 }
271 return (outptr);
272 }
273
274 /*
275 * Recompute the cached frequency for this node and all its children.
276 */
277 static int
clknode_refresh_cache(struct clknode * clknode,uint64_t freq)278 clknode_refresh_cache(struct clknode *clknode, uint64_t freq)
279 {
280 int rv;
281 struct clknode *entry;
282
283 CLK_TOPO_XASSERT();
284
285 /* Compute generated frequency. */
286 rv = CLKNODE_RECALC_FREQ(clknode, &freq);
287 if (rv != 0) {
288 /* XXX If an error happens while refreshing children
289 * this leaves the world in a partially-updated state.
290 * Panic for now.
291 */
292 panic("clknode_refresh_cache failed for '%s'\n",
293 clknode->name);
294 return (rv);
295 }
296 /* Refresh cache for this node. */
297 clknode->freq = freq;
298
299 /* Refresh cache for all children. */
300 TAILQ_FOREACH(entry, &(clknode->children), sibling_link) {
301 rv = clknode_refresh_cache(entry, freq);
302 if (rv != 0)
303 return (rv);
304 }
305 return (0);
306 }
307
308 /*
309 * Public interface.
310 */
311
312 struct clknode *
clknode_find_by_name(const char * name)313 clknode_find_by_name(const char *name)
314 {
315 struct clknode *entry;
316
317 CLK_TOPO_ASSERT();
318
319 TAILQ_FOREACH(entry, &clknode_list, clklist_link) {
320 if (strcmp(entry->name, name) == 0)
321 return (entry);
322 }
323 return (NULL);
324 }
325
326 struct clknode *
clknode_find_by_id(struct clkdom * clkdom,intptr_t id)327 clknode_find_by_id(struct clkdom *clkdom, intptr_t id)
328 {
329 struct clknode *entry;
330
331 CLK_TOPO_ASSERT();
332
333 TAILQ_FOREACH(entry, &clkdom->clknode_list, clkdom_link) {
334 if (entry->id == id)
335 return (entry);
336 }
337
338 return (NULL);
339 }
340
341 /* -------------------------------------------------------------------------- */
342 /*
343 * Clock domain functions
344 */
345
346 /* Find clock domain associated to device in global list. */
347 struct clkdom *
clkdom_get_by_dev(const device_t dev)348 clkdom_get_by_dev(const device_t dev)
349 {
350 struct clkdom *entry;
351
352 CLK_TOPO_ASSERT();
353
354 TAILQ_FOREACH(entry, &clkdom_list, link) {
355 if (entry->dev == dev)
356 return (entry);
357 }
358 return (NULL);
359 }
360
361
362 #ifdef FDT
363 /* Default DT mapper. */
364 static int
clknode_default_ofw_map(struct clkdom * clkdom,uint32_t ncells,phandle_t * cells,struct clknode ** clk)365 clknode_default_ofw_map(struct clkdom *clkdom, uint32_t ncells,
366 phandle_t *cells, struct clknode **clk)
367 {
368
369 CLK_TOPO_ASSERT();
370
371 if (ncells == 0)
372 *clk = clknode_find_by_id(clkdom, 1);
373 else if (ncells == 1)
374 *clk = clknode_find_by_id(clkdom, cells[0]);
375 else
376 return (ERANGE);
377
378 if (*clk == NULL)
379 return (ENXIO);
380 return (0);
381 }
382 #endif
383
384 /*
385 * Create a clock domain. Returns with the topo lock held.
386 */
387 struct clkdom *
clkdom_create(device_t dev)388 clkdom_create(device_t dev)
389 {
390 struct clkdom *clkdom;
391
392 clkdom = malloc(sizeof(struct clkdom), M_CLOCK, M_WAITOK | M_ZERO);
393 clkdom->dev = dev;
394 TAILQ_INIT(&clkdom->clknode_list);
395 #ifdef FDT
396 clkdom->ofw_mapper = clknode_default_ofw_map;
397 #endif
398
399 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
400 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
401 OID_AUTO, "clocks",
402 CTLTYPE_STRING | CTLFLAG_RD,
403 clkdom, 0, clkdom_sysctl,
404 "A",
405 "Clock list for the domain");
406
407 return (clkdom);
408 }
409
410 void
clkdom_unlock(struct clkdom * clkdom)411 clkdom_unlock(struct clkdom *clkdom)
412 {
413
414 CLK_TOPO_UNLOCK();
415 }
416
417 void
clkdom_xlock(struct clkdom * clkdom)418 clkdom_xlock(struct clkdom *clkdom)
419 {
420
421 CLK_TOPO_XLOCK();
422 }
423
424 /*
425 * Finalize initialization of clock domain. Releases topo lock.
426 *
427 * XXX Revisit failure handling.
428 */
429 int
clkdom_finit(struct clkdom * clkdom)430 clkdom_finit(struct clkdom *clkdom)
431 {
432 struct clknode *clknode;
433 int i, rv;
434 #ifdef FDT
435 phandle_t node;
436
437
438 if ((node = ofw_bus_get_node(clkdom->dev)) == -1) {
439 device_printf(clkdom->dev,
440 "%s called on not ofw based device\n", __func__);
441 return (ENXIO);
442 }
443 #endif
444 rv = 0;
445
446 /* Make clock domain globally visible. */
447 CLK_TOPO_XLOCK();
448 TAILQ_INSERT_TAIL(&clkdom_list, clkdom, link);
449 #ifdef FDT
450 OF_device_register_xref(OF_xref_from_node(node), clkdom->dev);
451 #endif
452
453 /* Register all clock names into global list. */
454 TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
455 TAILQ_INSERT_TAIL(&clknode_list, clknode, clklist_link);
456 }
457 /*
458 * At this point all domain nodes must be registered and all
459 * parents must be valid.
460 */
461 TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
462 if (clknode->parent_cnt == 0)
463 continue;
464 for (i = 0; i < clknode->parent_cnt; i++) {
465 if (clknode->parents[i] != NULL)
466 continue;
467 if (clknode->parent_names[i] == NULL)
468 continue;
469 clknode->parents[i] = clknode_find_by_name(
470 clknode->parent_names[i]);
471 if (clknode->parents[i] == NULL) {
472 device_printf(clkdom->dev,
473 "Clock %s have unknown parent: %s\n",
474 clknode->name, clknode->parent_names[i]);
475 rv = ENODEV;
476 }
477 }
478
479 /* If parent index is not set yet... */
480 if (clknode->parent_idx == CLKNODE_IDX_NONE) {
481 device_printf(clkdom->dev,
482 "Clock %s have not set parent idx\n",
483 clknode->name);
484 rv = ENXIO;
485 continue;
486 }
487 if (clknode->parents[clknode->parent_idx] == NULL) {
488 device_printf(clkdom->dev,
489 "Clock %s have unknown parent(idx %d): %s\n",
490 clknode->name, clknode->parent_idx,
491 clknode->parent_names[clknode->parent_idx]);
492 rv = ENXIO;
493 continue;
494 }
495 clknode_adjust_parent(clknode, clknode->parent_idx);
496 }
497 CLK_TOPO_UNLOCK();
498 return (rv);
499 }
500
501 /* Dump clock domain. */
502 void
clkdom_dump(struct clkdom * clkdom)503 clkdom_dump(struct clkdom * clkdom)
504 {
505 struct clknode *clknode;
506 int rv;
507 uint64_t freq;
508
509 CLK_TOPO_SLOCK();
510 TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
511 rv = clknode_get_freq(clknode, &freq);
512 printf("Clock: %s, parent: %s(%d), freq: %ju\n", clknode->name,
513 clknode->parent == NULL ? "(NULL)" : clknode->parent->name,
514 clknode->parent_idx,
515 (uintmax_t)((rv == 0) ? freq: rv));
516 }
517 CLK_TOPO_UNLOCK();
518 }
519
520 /*
521 * Create and initialize clock object, but do not register it.
522 */
523 struct clknode *
clknode_create(struct clkdom * clkdom,clknode_class_t clknode_class,const struct clknode_init_def * def)524 clknode_create(struct clkdom * clkdom, clknode_class_t clknode_class,
525 const struct clknode_init_def *def)
526 {
527 struct clknode *clknode;
528 struct sysctl_oid *clknode_oid;
529
530 KASSERT(def->name != NULL, ("clock name is NULL"));
531 KASSERT(def->name[0] != '\0', ("clock name is empty"));
532 #ifdef INVARIANTS
533 CLK_TOPO_SLOCK();
534 if (clknode_find_by_name(def->name) != NULL)
535 panic("Duplicated clock registration: %s\n", def->name);
536 CLK_TOPO_UNLOCK();
537 #endif
538
539 /* Create object and initialize it. */
540 clknode = malloc(sizeof(struct clknode), M_CLOCK, M_WAITOK | M_ZERO);
541 kobj_init((kobj_t)clknode, (kobj_class_t)clknode_class);
542 sx_init(&clknode->lock, "Clocknode lock");
543
544 /* Allocate softc if required. */
545 if (clknode_class->size > 0) {
546 clknode->softc = malloc(clknode_class->size,
547 M_CLOCK, M_WAITOK | M_ZERO);
548 }
549
550 /* Prepare array for ptrs to parent clocks. */
551 clknode->parents = malloc(sizeof(struct clknode *) * def->parent_cnt,
552 M_CLOCK, M_WAITOK | M_ZERO);
553
554 /* Copy all strings unless they're flagged as static. */
555 if (def->flags & CLK_NODE_STATIC_STRINGS) {
556 clknode->name = def->name;
557 clknode->parent_names = def->parent_names;
558 } else {
559 clknode->name = strdup(def->name, M_CLOCK);
560 clknode->parent_names =
561 strdup_list(def->parent_names, def->parent_cnt);
562 }
563
564 /* Rest of init. */
565 clknode->id = def->id;
566 clknode->clkdom = clkdom;
567 clknode->flags = def->flags;
568 clknode->parent_cnt = def->parent_cnt;
569 clknode->parent = NULL;
570 clknode->parent_idx = CLKNODE_IDX_NONE;
571 TAILQ_INIT(&clknode->children);
572
573 sysctl_ctx_init(&clknode->sysctl_ctx);
574 clknode_oid = SYSCTL_ADD_NODE(&clknode->sysctl_ctx,
575 SYSCTL_STATIC_CHILDREN(_hw_clock),
576 OID_AUTO, clknode->name,
577 CTLFLAG_RD, 0, "A clock node");
578
579 SYSCTL_ADD_U64(&clknode->sysctl_ctx,
580 SYSCTL_CHILDREN(clknode_oid),
581 OID_AUTO, "frequency",
582 CTLFLAG_RD, &clknode->freq, 0, "The clock frequency");
583 SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
584 SYSCTL_CHILDREN(clknode_oid),
585 OID_AUTO, "parent",
586 CTLTYPE_STRING | CTLFLAG_RD,
587 clknode, CLKNODE_SYSCTL_PARENT, clknode_sysctl,
588 "A",
589 "The clock parent");
590 SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
591 SYSCTL_CHILDREN(clknode_oid),
592 OID_AUTO, "parents",
593 CTLTYPE_STRING | CTLFLAG_RD,
594 clknode, CLKNODE_SYSCTL_PARENTS_LIST, clknode_sysctl,
595 "A",
596 "The clock parents list");
597 SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
598 SYSCTL_CHILDREN(clknode_oid),
599 OID_AUTO, "childrens",
600 CTLTYPE_STRING | CTLFLAG_RD,
601 clknode, CLKNODE_SYSCTL_CHILDREN_LIST, clknode_sysctl,
602 "A",
603 "The clock childrens list");
604 SYSCTL_ADD_INT(&clknode->sysctl_ctx,
605 SYSCTL_CHILDREN(clknode_oid),
606 OID_AUTO, "enable_cnt",
607 CTLFLAG_RD, &clknode->enable_cnt, 0, "The clock enable counter");
608
609 return (clknode);
610 }
611
612 /*
613 * Register clock object into clock domain hierarchy.
614 */
615 struct clknode *
clknode_register(struct clkdom * clkdom,struct clknode * clknode)616 clknode_register(struct clkdom * clkdom, struct clknode *clknode)
617 {
618 int rv;
619
620 rv = CLKNODE_INIT(clknode, clknode_get_device(clknode));
621 if (rv != 0) {
622 printf(" CLKNODE_INIT failed: %d\n", rv);
623 return (NULL);
624 }
625
626 TAILQ_INSERT_TAIL(&clkdom->clknode_list, clknode, clkdom_link);
627
628 return (clknode);
629 }
630
631 /*
632 * Clock providers interface.
633 */
634
635 /*
636 * Reparent clock node.
637 */
638 static void
clknode_adjust_parent(struct clknode * clknode,int idx)639 clknode_adjust_parent(struct clknode *clknode, int idx)
640 {
641
642 CLK_TOPO_XASSERT();
643
644 if (clknode->parent_cnt == 0)
645 return;
646 if ((idx == CLKNODE_IDX_NONE) || (idx >= clknode->parent_cnt))
647 panic("%s: Invalid parent index %d for clock %s",
648 __func__, idx, clknode->name);
649
650 if (clknode->parents[idx] == NULL)
651 panic("%s: Invalid parent index %d for clock %s",
652 __func__, idx, clknode->name);
653
654 /* Remove me from old children list. */
655 if (clknode->parent != NULL) {
656 TAILQ_REMOVE(&clknode->parent->children, clknode, sibling_link);
657 }
658
659 /* Insert into children list of new parent. */
660 clknode->parent_idx = idx;
661 clknode->parent = clknode->parents[idx];
662 TAILQ_INSERT_TAIL(&clknode->parent->children, clknode, sibling_link);
663 }
664
665 /*
666 * Set parent index - init function.
667 */
668 void
clknode_init_parent_idx(struct clknode * clknode,int idx)669 clknode_init_parent_idx(struct clknode *clknode, int idx)
670 {
671
672 if (clknode->parent_cnt == 0) {
673 clknode->parent_idx = CLKNODE_IDX_NONE;
674 clknode->parent = NULL;
675 return;
676 }
677 if ((idx == CLKNODE_IDX_NONE) ||
678 (idx >= clknode->parent_cnt) ||
679 (clknode->parent_names[idx] == NULL))
680 panic("%s: Invalid parent index %d for clock %s",
681 __func__, idx, clknode->name);
682 clknode->parent_idx = idx;
683 }
684
685 int
clknode_set_parent_by_idx(struct clknode * clknode,int idx)686 clknode_set_parent_by_idx(struct clknode *clknode, int idx)
687 {
688 int rv;
689 uint64_t freq;
690 int oldidx;
691
692 /* We have exclusive topology lock, node lock is not needed. */
693 CLK_TOPO_XASSERT();
694
695 if (clknode->parent_cnt == 0)
696 return (0);
697
698 if (clknode->parent_idx == idx)
699 return (0);
700
701 oldidx = clknode->parent_idx;
702 clknode_adjust_parent(clknode, idx);
703 rv = CLKNODE_SET_MUX(clknode, idx);
704 if (rv != 0) {
705 clknode_adjust_parent(clknode, oldidx);
706 return (rv);
707 }
708 rv = clknode_get_freq(clknode->parent, &freq);
709 if (rv != 0)
710 return (rv);
711 rv = clknode_refresh_cache(clknode, freq);
712 return (rv);
713 }
714
715 int
clknode_set_parent_by_name(struct clknode * clknode,const char * name)716 clknode_set_parent_by_name(struct clknode *clknode, const char *name)
717 {
718 int rv;
719 uint64_t freq;
720 int oldidx, idx;
721
722 /* We have exclusive topology lock, node lock is not needed. */
723 CLK_TOPO_XASSERT();
724
725 if (clknode->parent_cnt == 0)
726 return (0);
727
728 /*
729 * If this node doesnt have mux, then passthrough request to parent.
730 * This feature is used in clock domain initialization and allows us to
731 * set clock source and target frequency on the tail node of the clock
732 * chain.
733 */
734 if (clknode->parent_cnt == 1) {
735 rv = clknode_set_parent_by_name(clknode->parent, name);
736 return (rv);
737 }
738
739 for (idx = 0; idx < clknode->parent_cnt; idx++) {
740 if (clknode->parent_names[idx] == NULL)
741 continue;
742 if (strcmp(clknode->parent_names[idx], name) == 0)
743 break;
744 }
745 if (idx >= clknode->parent_cnt) {
746 return (ENXIO);
747 }
748 if (clknode->parent_idx == idx)
749 return (0);
750
751 oldidx = clknode->parent_idx;
752 clknode_adjust_parent(clknode, idx);
753 rv = CLKNODE_SET_MUX(clknode, idx);
754 if (rv != 0) {
755 clknode_adjust_parent(clknode, oldidx);
756 CLKNODE_UNLOCK(clknode);
757 return (rv);
758 }
759 rv = clknode_get_freq(clknode->parent, &freq);
760 if (rv != 0)
761 return (rv);
762 rv = clknode_refresh_cache(clknode, freq);
763 return (rv);
764 }
765
766 struct clknode *
clknode_get_parent(struct clknode * clknode)767 clknode_get_parent(struct clknode *clknode)
768 {
769
770 return (clknode->parent);
771 }
772
773 const char *
clknode_get_name(struct clknode * clknode)774 clknode_get_name(struct clknode *clknode)
775 {
776
777 return (clknode->name);
778 }
779
780 const char **
clknode_get_parent_names(struct clknode * clknode)781 clknode_get_parent_names(struct clknode *clknode)
782 {
783
784 return (clknode->parent_names);
785 }
786
787 int
clknode_get_parents_num(struct clknode * clknode)788 clknode_get_parents_num(struct clknode *clknode)
789 {
790
791 return (clknode->parent_cnt);
792 }
793
794 int
clknode_get_parent_idx(struct clknode * clknode)795 clknode_get_parent_idx(struct clknode *clknode)
796 {
797
798 return (clknode->parent_idx);
799 }
800
801 int
clknode_get_flags(struct clknode * clknode)802 clknode_get_flags(struct clknode *clknode)
803 {
804
805 return (clknode->flags);
806 }
807
808
809 void *
clknode_get_softc(struct clknode * clknode)810 clknode_get_softc(struct clknode *clknode)
811 {
812
813 return (clknode->softc);
814 }
815
816 device_t
clknode_get_device(struct clknode * clknode)817 clknode_get_device(struct clknode *clknode)
818 {
819
820 return (clknode->clkdom->dev);
821 }
822
823 #ifdef FDT
824 void
clkdom_set_ofw_mapper(struct clkdom * clkdom,clknode_ofw_mapper_func * map)825 clkdom_set_ofw_mapper(struct clkdom * clkdom, clknode_ofw_mapper_func *map)
826 {
827
828 clkdom->ofw_mapper = map;
829 }
830 #endif
831
832 /*
833 * Real consumers executive
834 */
835 int
clknode_get_freq(struct clknode * clknode,uint64_t * freq)836 clknode_get_freq(struct clknode *clknode, uint64_t *freq)
837 {
838 int rv;
839
840 CLK_TOPO_ASSERT();
841
842 /* Use cached value, if it exists. */
843 *freq = clknode->freq;
844 if (*freq != 0)
845 return (0);
846
847 /* Get frequency from parent, if the clock has a parent. */
848 if (clknode->parent_cnt > 0) {
849 rv = clknode_get_freq(clknode->parent, freq);
850 if (rv != 0) {
851 return (rv);
852 }
853 }
854
855 /* And recalculate my output frequency. */
856 CLKNODE_XLOCK(clknode);
857 rv = CLKNODE_RECALC_FREQ(clknode, freq);
858 if (rv != 0) {
859 CLKNODE_UNLOCK(clknode);
860 printf("Cannot get frequency for clk: %s, error: %d\n",
861 clknode->name, rv);
862 return (rv);
863 }
864
865 /* Save new frequency to cache. */
866 clknode->freq = *freq;
867 CLKNODE_UNLOCK(clknode);
868 return (0);
869 }
870
871 int
clknode_set_freq(struct clknode * clknode,uint64_t freq,int flags,int enablecnt)872 clknode_set_freq(struct clknode *clknode, uint64_t freq, int flags,
873 int enablecnt)
874 {
875 int rv, done;
876 uint64_t parent_freq;
877
878 /* We have exclusive topology lock, node lock is not needed. */
879 CLK_TOPO_XASSERT();
880
881 /* Check for no change */
882 if (clknode->freq == freq)
883 return (0);
884
885 parent_freq = 0;
886
887 /*
888 * We can set frequency only if
889 * clock is disabled
890 * OR
891 * clock is glitch free and is enabled by calling consumer only
892 */
893 if ((flags & CLK_SET_DRYRUN) == 0 &&
894 clknode->enable_cnt > 1 &&
895 clknode->enable_cnt > enablecnt &&
896 (clknode->flags & CLK_NODE_GLITCH_FREE) == 0) {
897 return (EBUSY);
898 }
899
900 /* Get frequency from parent, if the clock has a parent. */
901 if (clknode->parent_cnt > 0) {
902 rv = clknode_get_freq(clknode->parent, &parent_freq);
903 if (rv != 0) {
904 return (rv);
905 }
906 }
907
908 /* Set frequency for this clock. */
909 rv = CLKNODE_SET_FREQ(clknode, parent_freq, &freq, flags, &done);
910 if (rv != 0) {
911 printf("Cannot set frequency for clk: %s, error: %d\n",
912 clknode->name, rv);
913 if ((flags & CLK_SET_DRYRUN) == 0)
914 clknode_refresh_cache(clknode, parent_freq);
915 return (rv);
916 }
917
918 if (done) {
919 /* Success - invalidate frequency cache for all children. */
920 if ((flags & CLK_SET_DRYRUN) == 0) {
921 clknode->freq = freq;
922 /* Clock might have reparent during set_freq */
923 if (clknode->parent_cnt > 0) {
924 rv = clknode_get_freq(clknode->parent,
925 &parent_freq);
926 if (rv != 0) {
927 return (rv);
928 }
929 }
930 clknode_refresh_cache(clknode, parent_freq);
931 }
932 } else if (clknode->parent != NULL) {
933 /* Nothing changed, pass request to parent. */
934 rv = clknode_set_freq(clknode->parent, freq, flags, enablecnt);
935 } else {
936 /* End of chain without action. */
937 printf("Cannot set frequency for clk: %s, end of chain\n",
938 clknode->name);
939 rv = ENXIO;
940 }
941
942 return (rv);
943 }
944
945 int
clknode_enable(struct clknode * clknode)946 clknode_enable(struct clknode *clknode)
947 {
948 int rv;
949
950 CLK_TOPO_ASSERT();
951
952 /* Enable clock for each node in chain, starting from source. */
953 if (clknode->parent_cnt > 0) {
954 rv = clknode_enable(clknode->parent);
955 if (rv != 0) {
956 return (rv);
957 }
958 }
959
960 /* Handle this node */
961 CLKNODE_XLOCK(clknode);
962 if (clknode->enable_cnt == 0) {
963 rv = CLKNODE_SET_GATE(clknode, 1);
964 if (rv != 0) {
965 CLKNODE_UNLOCK(clknode);
966 return (rv);
967 }
968 }
969 clknode->enable_cnt++;
970 CLKNODE_UNLOCK(clknode);
971 return (0);
972 }
973
974 int
clknode_disable(struct clknode * clknode)975 clknode_disable(struct clknode *clknode)
976 {
977 int rv;
978
979 CLK_TOPO_ASSERT();
980 rv = 0;
981
982 CLKNODE_XLOCK(clknode);
983 /* Disable clock for each node in chain, starting from consumer. */
984 if ((clknode->enable_cnt == 1) &&
985 ((clknode->flags & CLK_NODE_CANNOT_STOP) == 0)) {
986 rv = CLKNODE_SET_GATE(clknode, 0);
987 if (rv != 0) {
988 CLKNODE_UNLOCK(clknode);
989 return (rv);
990 }
991 }
992 clknode->enable_cnt--;
993 CLKNODE_UNLOCK(clknode);
994
995 if (clknode->parent_cnt > 0) {
996 rv = clknode_disable(clknode->parent);
997 }
998 return (rv);
999 }
1000
1001 int
clknode_stop(struct clknode * clknode,int depth)1002 clknode_stop(struct clknode *clknode, int depth)
1003 {
1004 int rv;
1005
1006 CLK_TOPO_ASSERT();
1007 rv = 0;
1008
1009 CLKNODE_XLOCK(clknode);
1010 /* The first node cannot be enabled. */
1011 if ((clknode->enable_cnt != 0) && (depth == 0)) {
1012 CLKNODE_UNLOCK(clknode);
1013 return (EBUSY);
1014 }
1015 /* Stop clock for each node in chain, starting from consumer. */
1016 if ((clknode->enable_cnt == 0) &&
1017 ((clknode->flags & CLK_NODE_CANNOT_STOP) == 0)) {
1018 rv = CLKNODE_SET_GATE(clknode, 0);
1019 if (rv != 0) {
1020 CLKNODE_UNLOCK(clknode);
1021 return (rv);
1022 }
1023 }
1024 CLKNODE_UNLOCK(clknode);
1025
1026 if (clknode->parent_cnt > 0)
1027 rv = clknode_stop(clknode->parent, depth + 1);
1028 return (rv);
1029 }
1030
1031 /* --------------------------------------------------------------------------
1032 *
1033 * Clock consumers interface.
1034 *
1035 */
1036 /* Helper function for clk_get*() */
1037 static clk_t
clk_create(struct clknode * clknode,device_t dev)1038 clk_create(struct clknode *clknode, device_t dev)
1039 {
1040 struct clk *clk;
1041
1042 CLK_TOPO_ASSERT();
1043
1044 clk = malloc(sizeof(struct clk), M_CLOCK, M_WAITOK);
1045 clk->dev = dev;
1046 clk->clknode = clknode;
1047 clk->enable_cnt = 0;
1048 clknode->ref_cnt++;
1049
1050 return (clk);
1051 }
1052
1053 int
clk_get_freq(clk_t clk,uint64_t * freq)1054 clk_get_freq(clk_t clk, uint64_t *freq)
1055 {
1056 int rv;
1057 struct clknode *clknode;
1058
1059 clknode = clk->clknode;
1060 KASSERT(clknode->ref_cnt > 0,
1061 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1062
1063 CLK_TOPO_SLOCK();
1064 rv = clknode_get_freq(clknode, freq);
1065 CLK_TOPO_UNLOCK();
1066 return (rv);
1067 }
1068
1069 int
clk_set_freq(clk_t clk,uint64_t freq,int flags)1070 clk_set_freq(clk_t clk, uint64_t freq, int flags)
1071 {
1072 int rv;
1073 struct clknode *clknode;
1074
1075 flags &= CLK_SET_USER_MASK;
1076 clknode = clk->clknode;
1077 KASSERT(clknode->ref_cnt > 0,
1078 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1079
1080 CLK_TOPO_XLOCK();
1081 rv = clknode_set_freq(clknode, freq, flags, clk->enable_cnt);
1082 CLK_TOPO_UNLOCK();
1083 return (rv);
1084 }
1085
1086 int
clk_test_freq(clk_t clk,uint64_t freq,int flags)1087 clk_test_freq(clk_t clk, uint64_t freq, int flags)
1088 {
1089 int rv;
1090 struct clknode *clknode;
1091
1092 flags &= CLK_SET_USER_MASK;
1093 clknode = clk->clknode;
1094 KASSERT(clknode->ref_cnt > 0,
1095 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1096
1097 CLK_TOPO_XLOCK();
1098 rv = clknode_set_freq(clknode, freq, flags | CLK_SET_DRYRUN, 0);
1099 CLK_TOPO_UNLOCK();
1100 return (rv);
1101 }
1102
1103 int
clk_get_parent(clk_t clk,clk_t * parent)1104 clk_get_parent(clk_t clk, clk_t *parent)
1105 {
1106 struct clknode *clknode;
1107 struct clknode *parentnode;
1108
1109 clknode = clk->clknode;
1110 KASSERT(clknode->ref_cnt > 0,
1111 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1112
1113 CLK_TOPO_SLOCK();
1114 parentnode = clknode_get_parent(clknode);
1115 if (parentnode == NULL) {
1116 CLK_TOPO_UNLOCK();
1117 return (ENODEV);
1118 }
1119 *parent = clk_create(parentnode, clk->dev);
1120 CLK_TOPO_UNLOCK();
1121 return (0);
1122 }
1123
1124 int
clk_set_parent_by_clk(clk_t clk,clk_t parent)1125 clk_set_parent_by_clk(clk_t clk, clk_t parent)
1126 {
1127 int rv;
1128 struct clknode *clknode;
1129 struct clknode *parentnode;
1130
1131 clknode = clk->clknode;
1132 parentnode = parent->clknode;
1133 KASSERT(clknode->ref_cnt > 0,
1134 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1135 KASSERT(parentnode->ref_cnt > 0,
1136 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1137 CLK_TOPO_XLOCK();
1138 rv = clknode_set_parent_by_name(clknode, parentnode->name);
1139 CLK_TOPO_UNLOCK();
1140 return (rv);
1141 }
1142
1143 int
clk_enable(clk_t clk)1144 clk_enable(clk_t clk)
1145 {
1146 int rv;
1147 struct clknode *clknode;
1148
1149 clknode = clk->clknode;
1150 KASSERT(clknode->ref_cnt > 0,
1151 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1152 CLK_TOPO_SLOCK();
1153 rv = clknode_enable(clknode);
1154 if (rv == 0)
1155 clk->enable_cnt++;
1156 CLK_TOPO_UNLOCK();
1157 return (rv);
1158 }
1159
1160 int
clk_disable(clk_t clk)1161 clk_disable(clk_t clk)
1162 {
1163 int rv;
1164 struct clknode *clknode;
1165
1166 clknode = clk->clknode;
1167 KASSERT(clknode->ref_cnt > 0,
1168 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1169 KASSERT(clk->enable_cnt > 0,
1170 ("Attempt to disable already disabled clock: %s\n", clknode->name));
1171 CLK_TOPO_SLOCK();
1172 rv = clknode_disable(clknode);
1173 if (rv == 0)
1174 clk->enable_cnt--;
1175 CLK_TOPO_UNLOCK();
1176 return (rv);
1177 }
1178
1179 int
clk_stop(clk_t clk)1180 clk_stop(clk_t clk)
1181 {
1182 int rv;
1183 struct clknode *clknode;
1184
1185 clknode = clk->clknode;
1186 KASSERT(clknode->ref_cnt > 0,
1187 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1188 KASSERT(clk->enable_cnt == 0,
1189 ("Attempt to stop already enabled clock: %s\n", clknode->name));
1190
1191 CLK_TOPO_SLOCK();
1192 rv = clknode_stop(clknode, 0);
1193 CLK_TOPO_UNLOCK();
1194 return (rv);
1195 }
1196
1197 int
clk_release(clk_t clk)1198 clk_release(clk_t clk)
1199 {
1200 struct clknode *clknode;
1201
1202 clknode = clk->clknode;
1203 KASSERT(clknode->ref_cnt > 0,
1204 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1205 CLK_TOPO_SLOCK();
1206 while (clk->enable_cnt > 0) {
1207 clknode_disable(clknode);
1208 clk->enable_cnt--;
1209 }
1210 CLKNODE_XLOCK(clknode);
1211 clknode->ref_cnt--;
1212 CLKNODE_UNLOCK(clknode);
1213 CLK_TOPO_UNLOCK();
1214
1215 free(clk, M_CLOCK);
1216 return (0);
1217 }
1218
1219 const char *
clk_get_name(clk_t clk)1220 clk_get_name(clk_t clk)
1221 {
1222 const char *name;
1223 struct clknode *clknode;
1224
1225 clknode = clk->clknode;
1226 KASSERT(clknode->ref_cnt > 0,
1227 ("Attempt to access unreferenced clock: %s\n", clknode->name));
1228 name = clknode_get_name(clknode);
1229 return (name);
1230 }
1231
1232 int
clk_get_by_name(device_t dev,const char * name,clk_t * clk)1233 clk_get_by_name(device_t dev, const char *name, clk_t *clk)
1234 {
1235 struct clknode *clknode;
1236
1237 CLK_TOPO_SLOCK();
1238 clknode = clknode_find_by_name(name);
1239 if (clknode == NULL) {
1240 CLK_TOPO_UNLOCK();
1241 return (ENODEV);
1242 }
1243 *clk = clk_create(clknode, dev);
1244 CLK_TOPO_UNLOCK();
1245 return (0);
1246 }
1247
1248 int
clk_get_by_id(device_t dev,struct clkdom * clkdom,intptr_t id,clk_t * clk)1249 clk_get_by_id(device_t dev, struct clkdom *clkdom, intptr_t id, clk_t *clk)
1250 {
1251 struct clknode *clknode;
1252
1253 CLK_TOPO_SLOCK();
1254
1255 clknode = clknode_find_by_id(clkdom, id);
1256 if (clknode == NULL) {
1257 CLK_TOPO_UNLOCK();
1258 return (ENODEV);
1259 }
1260 *clk = clk_create(clknode, dev);
1261 CLK_TOPO_UNLOCK();
1262
1263 return (0);
1264 }
1265
1266 #ifdef FDT
1267
1268 static void
clk_set_assigned_parent(device_t dev,clk_t clk,int idx)1269 clk_set_assigned_parent(device_t dev, clk_t clk, int idx)
1270 {
1271 clk_t parent;
1272 const char *pname;
1273 int rv;
1274
1275 rv = clk_get_by_ofw_index_prop(dev, 0,
1276 "assigned-clock-parents", idx, &parent);
1277 if (rv != 0) {
1278 device_printf(dev,
1279 "cannot get parent at idx %d\n", idx);
1280 return;
1281 }
1282
1283 pname = clk_get_name(parent);
1284 rv = clk_set_parent_by_clk(clk, parent);
1285 if (rv != 0)
1286 device_printf(dev,
1287 "Cannot set parent %s for clock %s\n",
1288 pname, clk_get_name(clk));
1289 else if (bootverbose)
1290 device_printf(dev, "Set %s as the parent of %s\n",
1291 pname, clk_get_name(clk));
1292 clk_release(parent);
1293 }
1294
1295 static void
clk_set_assigned_rates(device_t dev,clk_t clk,uint32_t freq)1296 clk_set_assigned_rates(device_t dev, clk_t clk, uint32_t freq)
1297 {
1298 int rv;
1299
1300 rv = clk_set_freq(clk, freq, CLK_SET_ROUND_DOWN | CLK_SET_ROUND_UP);
1301 if (rv != 0) {
1302 device_printf(dev, "Failed to set %s to a frequency of %u\n",
1303 clk_get_name(clk), freq);
1304 return;
1305 }
1306 if (bootverbose)
1307 device_printf(dev, "Set %s to %u\n",
1308 clk_get_name(clk), freq);
1309 }
1310
1311 int
clk_set_assigned(device_t dev,phandle_t node)1312 clk_set_assigned(device_t dev, phandle_t node)
1313 {
1314 clk_t clk;
1315 uint32_t *rates;
1316 int rv, nclocks, nrates, nparents, i;
1317
1318 rv = ofw_bus_parse_xref_list_get_length(node,
1319 "assigned-clocks", "#clock-cells", &nclocks);
1320
1321 if (rv != 0) {
1322 if (rv != ENOENT)
1323 device_printf(dev,
1324 "cannot parse assigned-clock property\n");
1325 return (rv);
1326 }
1327
1328 nrates = OF_getencprop_alloc_multi(node, "assigned-clock-rates",
1329 sizeof(*rates), (void **)&rates);
1330 if (nrates <= 0)
1331 nrates = 0;
1332
1333 if (ofw_bus_parse_xref_list_get_length(node,
1334 "assigned-clock-parents", "#clock-cells", &nparents) != 0)
1335 nparents = -1;
1336 for (i = 0; i < nclocks; i++) {
1337 /* First get the clock we are supposed to modify */
1338 rv = clk_get_by_ofw_index_prop(dev, 0, "assigned-clocks",
1339 i, &clk);
1340 if (rv != 0) {
1341 if (bootverbose)
1342 device_printf(dev,
1343 "cannot get assigned clock at idx %d\n",
1344 i);
1345 continue;
1346 }
1347
1348 /* First set it's parent if needed */
1349 if (i <= nparents)
1350 clk_set_assigned_parent(dev, clk, i);
1351
1352 /* Then set a new frequency */
1353 if (i <= nrates && rates[i] != 0)
1354 clk_set_assigned_rates(dev, clk, rates[i]);
1355
1356 clk_release(clk);
1357 }
1358
1359 return (0);
1360 }
1361
1362 int
clk_get_by_ofw_index_prop(device_t dev,phandle_t cnode,const char * prop,int idx,clk_t * clk)1363 clk_get_by_ofw_index_prop(device_t dev, phandle_t cnode, const char *prop, int idx, clk_t *clk)
1364 {
1365 phandle_t parent, *cells;
1366 device_t clockdev;
1367 int ncells, rv;
1368 struct clkdom *clkdom;
1369 struct clknode *clknode;
1370
1371 *clk = NULL;
1372 if (cnode <= 0)
1373 cnode = ofw_bus_get_node(dev);
1374 if (cnode <= 0) {
1375 device_printf(dev, "%s called on not ofw based device\n",
1376 __func__);
1377 return (ENXIO);
1378 }
1379
1380
1381 rv = ofw_bus_parse_xref_list_alloc(cnode, prop, "#clock-cells", idx,
1382 &parent, &ncells, &cells);
1383 if (rv != 0) {
1384 return (rv);
1385 }
1386
1387 clockdev = OF_device_from_xref(parent);
1388 if (clockdev == NULL) {
1389 rv = ENODEV;
1390 goto done;
1391 }
1392
1393 CLK_TOPO_SLOCK();
1394 clkdom = clkdom_get_by_dev(clockdev);
1395 if (clkdom == NULL){
1396 CLK_TOPO_UNLOCK();
1397 rv = ENXIO;
1398 goto done;
1399 }
1400
1401 rv = clkdom->ofw_mapper(clkdom, ncells, cells, &clknode);
1402 if (rv == 0) {
1403 *clk = clk_create(clknode, dev);
1404 }
1405 CLK_TOPO_UNLOCK();
1406
1407 done:
1408 if (cells != NULL)
1409 OF_prop_free(cells);
1410 return (rv);
1411 }
1412
1413 int
clk_get_by_ofw_index(device_t dev,phandle_t cnode,int idx,clk_t * clk)1414 clk_get_by_ofw_index(device_t dev, phandle_t cnode, int idx, clk_t *clk)
1415 {
1416 return (clk_get_by_ofw_index_prop(dev, cnode, "clocks", idx, clk));
1417 }
1418
1419 int
clk_get_by_ofw_name(device_t dev,phandle_t cnode,const char * name,clk_t * clk)1420 clk_get_by_ofw_name(device_t dev, phandle_t cnode, const char *name, clk_t *clk)
1421 {
1422 int rv, idx;
1423
1424 if (cnode <= 0)
1425 cnode = ofw_bus_get_node(dev);
1426 if (cnode <= 0) {
1427 device_printf(dev, "%s called on not ofw based device\n",
1428 __func__);
1429 return (ENXIO);
1430 }
1431 rv = ofw_bus_find_string_index(cnode, "clock-names", name, &idx);
1432 if (rv != 0)
1433 return (rv);
1434 return (clk_get_by_ofw_index(dev, cnode, idx, clk));
1435 }
1436
1437 /* --------------------------------------------------------------------------
1438 *
1439 * Support functions for parsing various clock related OFW things.
1440 */
1441
1442 /*
1443 * Get "clock-output-names" and (optional) "clock-indices" lists.
1444 * Both lists are alocated using M_OFWPROP specifier.
1445 *
1446 * Returns number of items or 0.
1447 */
1448 int
clk_parse_ofw_out_names(device_t dev,phandle_t node,const char *** out_names,uint32_t ** indices)1449 clk_parse_ofw_out_names(device_t dev, phandle_t node, const char ***out_names,
1450 uint32_t **indices)
1451 {
1452 int name_items, rv;
1453
1454 *out_names = NULL;
1455 *indices = NULL;
1456 if (!OF_hasprop(node, "clock-output-names"))
1457 return (0);
1458 rv = ofw_bus_string_list_to_array(node, "clock-output-names",
1459 out_names);
1460 if (rv <= 0)
1461 return (0);
1462 name_items = rv;
1463
1464 if (!OF_hasprop(node, "clock-indices"))
1465 return (name_items);
1466 rv = OF_getencprop_alloc_multi(node, "clock-indices", sizeof (uint32_t),
1467 (void **)indices);
1468 if (rv != name_items) {
1469 device_printf(dev, " Size of 'clock-output-names' and "
1470 "'clock-indices' differs\n");
1471 OF_prop_free(*out_names);
1472 OF_prop_free(*indices);
1473 return (0);
1474 }
1475 return (name_items);
1476 }
1477
1478 /*
1479 * Get output clock name for single output clock node.
1480 */
1481 int
clk_parse_ofw_clk_name(device_t dev,phandle_t node,const char ** name)1482 clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name)
1483 {
1484 const char **out_names;
1485 const char *tmp_name;
1486 int rv;
1487
1488 *name = NULL;
1489 if (!OF_hasprop(node, "clock-output-names")) {
1490 tmp_name = ofw_bus_get_name(dev);
1491 if (tmp_name == NULL)
1492 return (ENXIO);
1493 *name = strdup(tmp_name, M_OFWPROP);
1494 return (0);
1495 }
1496 rv = ofw_bus_string_list_to_array(node, "clock-output-names",
1497 &out_names);
1498 if (rv != 1) {
1499 OF_prop_free(out_names);
1500 device_printf(dev, "Malformed 'clock-output-names' property\n");
1501 return (ENXIO);
1502 }
1503 *name = strdup(out_names[0], M_OFWPROP);
1504 OF_prop_free(out_names);
1505 return (0);
1506 }
1507 #endif
1508
1509 static int
clkdom_sysctl(SYSCTL_HANDLER_ARGS)1510 clkdom_sysctl(SYSCTL_HANDLER_ARGS)
1511 {
1512 struct clkdom *clkdom = arg1;
1513 struct clknode *clknode;
1514 struct sbuf *sb;
1515 int ret;
1516
1517 sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
1518 if (sb == NULL)
1519 return (ENOMEM);
1520
1521 CLK_TOPO_SLOCK();
1522 TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
1523 sbuf_printf(sb, "%s ", clknode->name);
1524 }
1525 CLK_TOPO_UNLOCK();
1526
1527 ret = sbuf_finish(sb);
1528 sbuf_delete(sb);
1529 return (ret);
1530 }
1531
1532 static int
clknode_sysctl(SYSCTL_HANDLER_ARGS)1533 clknode_sysctl(SYSCTL_HANDLER_ARGS)
1534 {
1535 struct clknode *clknode, *children;
1536 enum clknode_sysctl_type type = arg2;
1537 struct sbuf *sb;
1538 const char **parent_names;
1539 int ret, i;
1540
1541 clknode = arg1;
1542 sb = sbuf_new_for_sysctl(NULL, NULL, 512, req);
1543 if (sb == NULL)
1544 return (ENOMEM);
1545
1546 CLK_TOPO_SLOCK();
1547 switch (type) {
1548 case CLKNODE_SYSCTL_PARENT:
1549 if (clknode->parent)
1550 sbuf_printf(sb, "%s", clknode->parent->name);
1551 break;
1552 case CLKNODE_SYSCTL_PARENTS_LIST:
1553 parent_names = clknode_get_parent_names(clknode);
1554 for (i = 0; i < clknode->parent_cnt; i++)
1555 sbuf_printf(sb, "%s ", parent_names[i]);
1556 break;
1557 case CLKNODE_SYSCTL_CHILDREN_LIST:
1558 TAILQ_FOREACH(children, &(clknode->children), sibling_link) {
1559 sbuf_printf(sb, "%s ", children->name);
1560 }
1561 break;
1562 }
1563 CLK_TOPO_UNLOCK();
1564
1565 ret = sbuf_finish(sb);
1566 sbuf_delete(sb);
1567 return (ret);
1568 }
1569