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