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