]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/extres/clk/clk.c
MFV r331400: 8484 Implement aggregate sum and use for arc counters
[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("%s: Invalid parent index %d for clock %s",
646                     __func__, idx, clknode->name);
647
648         if (clknode->parents[idx] == NULL)
649                 panic("%s: Invalid parent index %d for clock %s",
650                     __func__, idx, clknode->name);
651
652         /* Remove me from old children list. */
653         if (clknode->parent != NULL) {
654                 TAILQ_REMOVE(&clknode->parent->children, clknode, sibling_link);
655         }
656
657         /* Insert into children list of new parent. */
658         clknode->parent_idx = idx;
659         clknode->parent = clknode->parents[idx];
660         TAILQ_INSERT_TAIL(&clknode->parent->children, clknode, sibling_link);
661 }
662
663 /*
664  * Set parent index - init function.
665  */
666 void
667 clknode_init_parent_idx(struct clknode *clknode, int idx)
668 {
669
670         if (clknode->parent_cnt == 0) {
671                 clknode->parent_idx = CLKNODE_IDX_NONE;
672                 clknode->parent = NULL;
673                 return;
674         }
675         if ((idx == CLKNODE_IDX_NONE) ||
676             (idx >= clknode->parent_cnt) ||
677             (clknode->parent_names[idx] == NULL))
678                 panic("%s: Invalid parent index %d for clock %s",
679                     __func__, idx, clknode->name);
680         clknode->parent_idx = idx;
681 }
682
683 int
684 clknode_set_parent_by_idx(struct clknode *clknode, int idx)
685 {
686         int rv;
687         uint64_t freq;
688         int  oldidx;
689
690         /* We have exclusive topology lock, node lock is not needed. */
691         CLK_TOPO_XASSERT();
692
693         if (clknode->parent_cnt == 0)
694                 return (0);
695
696         if (clknode->parent_idx == idx)
697                 return (0);
698
699         oldidx = clknode->parent_idx;
700         clknode_adjust_parent(clknode, idx);
701         rv = CLKNODE_SET_MUX(clknode, idx);
702         if (rv != 0) {
703                 clknode_adjust_parent(clknode, oldidx);
704                 return (rv);
705         }
706         rv = clknode_get_freq(clknode->parent, &freq);
707         if (rv != 0)
708                 return (rv);
709         rv = clknode_refresh_cache(clknode, freq);
710         return (rv);
711 }
712
713 int
714 clknode_set_parent_by_name(struct clknode *clknode, const char *name)
715 {
716         int rv;
717         uint64_t freq;
718         int  oldidx, idx;
719
720         /* We have exclusive topology lock, node lock is not needed. */
721         CLK_TOPO_XASSERT();
722
723         if (clknode->parent_cnt == 0)
724                 return (0);
725
726         /*
727          * If this node doesnt have mux, then passthrough request to parent.
728          * This feature is used in clock domain initialization and allows us to
729          * set clock source and target frequency on the tail node of the clock
730          * chain.
731          */
732         if (clknode->parent_cnt == 1) {
733                 rv = clknode_set_parent_by_name(clknode->parent, name);
734                 return (rv);
735         }
736
737         for (idx = 0; idx < clknode->parent_cnt; idx++) {
738                 if (clknode->parent_names[idx] == NULL)
739                         continue;
740                 if (strcmp(clknode->parent_names[idx], name) == 0)
741                         break;
742         }
743         if (idx >= clknode->parent_cnt) {
744                 return (ENXIO);
745         }
746         if (clknode->parent_idx == idx)
747                 return (0);
748
749         oldidx = clknode->parent_idx;
750         clknode_adjust_parent(clknode, idx);
751         rv = CLKNODE_SET_MUX(clknode, idx);
752         if (rv != 0) {
753                 clknode_adjust_parent(clknode, oldidx);
754                 CLKNODE_UNLOCK(clknode);
755                 return (rv);
756         }
757         rv = clknode_get_freq(clknode->parent, &freq);
758         if (rv != 0)
759                 return (rv);
760         rv = clknode_refresh_cache(clknode, freq);
761         return (rv);
762 }
763
764 struct clknode *
765 clknode_get_parent(struct clknode *clknode)
766 {
767
768         return (clknode->parent);
769 }
770
771 const char *
772 clknode_get_name(struct clknode *clknode)
773 {
774
775         return (clknode->name);
776 }
777
778 const char **
779 clknode_get_parent_names(struct clknode *clknode)
780 {
781
782         return (clknode->parent_names);
783 }
784
785 int
786 clknode_get_parents_num(struct clknode *clknode)
787 {
788
789         return (clknode->parent_cnt);
790 }
791
792 int
793 clknode_get_parent_idx(struct clknode *clknode)
794 {
795
796         return (clknode->parent_idx);
797 }
798
799 int
800 clknode_get_flags(struct clknode *clknode)
801 {
802
803         return (clknode->flags);
804 }
805
806
807 void *
808 clknode_get_softc(struct clknode *clknode)
809 {
810
811         return (clknode->softc);
812 }
813
814 device_t
815 clknode_get_device(struct clknode *clknode)
816 {
817
818         return (clknode->clkdom->dev);
819 }
820
821 #ifdef FDT
822 void
823 clkdom_set_ofw_mapper(struct clkdom * clkdom, clknode_ofw_mapper_func *map)
824 {
825
826         clkdom->ofw_mapper = map;
827 }
828 #endif
829
830 /*
831  * Real consumers executive
832  */
833 int
834 clknode_get_freq(struct clknode *clknode, uint64_t *freq)
835 {
836         int rv;
837
838         CLK_TOPO_ASSERT();
839
840         /* Use cached value, if it exists. */
841         *freq  = clknode->freq;
842         if (*freq != 0)
843                 return (0);
844
845         /* Get frequency from parent, if the clock has a parent. */
846         if (clknode->parent_cnt > 0) {
847                 rv = clknode_get_freq(clknode->parent, freq);
848                 if (rv != 0) {
849                         return (rv);
850                 }
851         }
852
853         /* And recalculate my output frequency. */
854         CLKNODE_XLOCK(clknode);
855         rv = CLKNODE_RECALC_FREQ(clknode, freq);
856         if (rv != 0) {
857                 CLKNODE_UNLOCK(clknode);
858                 printf("Cannot get frequency for clk: %s, error: %d\n",
859                     clknode->name, rv);
860                 return (rv);
861         }
862
863         /* Save new frequency to cache. */
864         clknode->freq = *freq;
865         CLKNODE_UNLOCK(clknode);
866         return (0);
867 }
868
869 int
870 clknode_set_freq(struct clknode *clknode, uint64_t freq, int flags,
871     int enablecnt)
872 {
873         int rv, done;
874         uint64_t parent_freq;
875
876         /* We have exclusive topology lock, node lock is not needed. */
877         CLK_TOPO_XASSERT();
878
879         /* Check for no change */
880         if (clknode->freq == freq)
881                 return (0);
882
883         parent_freq = 0;
884
885         /*
886          * We can set frequency only if
887          *   clock is disabled
888          * OR
889          *   clock is glitch free and is enabled by calling consumer only
890          */
891         if ((flags & CLK_SET_DRYRUN) == 0 &&
892             clknode->enable_cnt > 1 &&
893             clknode->enable_cnt > enablecnt &&
894             (clknode->flags & CLK_NODE_GLITCH_FREE) == 0) {
895                 return (EBUSY);
896         }
897
898         /* Get frequency from parent, if the clock has a parent. */
899         if (clknode->parent_cnt > 0) {
900                 rv = clknode_get_freq(clknode->parent, &parent_freq);
901                 if (rv != 0) {
902                         return (rv);
903                 }
904         }
905
906         /* Set frequency for this clock. */
907         rv = CLKNODE_SET_FREQ(clknode, parent_freq, &freq, flags, &done);
908         if (rv != 0) {
909                 printf("Cannot set frequency for clk: %s, error: %d\n",
910                     clknode->name, rv);
911                 if ((flags & CLK_SET_DRYRUN) == 0)
912                         clknode_refresh_cache(clknode, parent_freq);
913                 return (rv);
914         }
915
916         if (done) {
917                 /* Success - invalidate frequency cache for all children. */
918                 if ((flags & CLK_SET_DRYRUN) == 0) {
919                         clknode->freq = freq;
920                         /* Clock might have reparent during set_freq */
921                         if (clknode->parent_cnt > 0) {
922                                 rv = clknode_get_freq(clknode->parent,
923                                     &parent_freq);
924                                 if (rv != 0) {
925                                         return (rv);
926                                 }
927                         }
928                         clknode_refresh_cache(clknode, parent_freq);
929                 }
930         } else if (clknode->parent != NULL) {
931                 /* Nothing changed, pass request to parent. */
932                 rv = clknode_set_freq(clknode->parent, freq, flags, enablecnt);
933         } else {
934                 /* End of chain without action. */
935                 printf("Cannot set frequency for clk: %s, end of chain\n",
936                     clknode->name);
937                 rv = ENXIO;
938         }
939
940         return (rv);
941 }
942
943 int
944 clknode_enable(struct clknode *clknode)
945 {
946         int rv;
947
948         CLK_TOPO_ASSERT();
949
950         /* Enable clock for each node in chain, starting from source. */
951         if (clknode->parent_cnt > 0) {
952                 rv = clknode_enable(clknode->parent);
953                 if (rv != 0) {
954                         return (rv);
955                 }
956         }
957
958         /* Handle this node */
959         CLKNODE_XLOCK(clknode);
960         if (clknode->enable_cnt == 0) {
961                 rv = CLKNODE_SET_GATE(clknode, 1);
962                 if (rv != 0) {
963                         CLKNODE_UNLOCK(clknode);
964                         return (rv);
965                 }
966         }
967         clknode->enable_cnt++;
968         CLKNODE_UNLOCK(clknode);
969         return (0);
970 }
971
972 int
973 clknode_disable(struct clknode *clknode)
974 {
975         int rv;
976
977         CLK_TOPO_ASSERT();
978         rv = 0;
979
980         CLKNODE_XLOCK(clknode);
981         /* Disable clock for each node in chain, starting from consumer. */
982         if ((clknode->enable_cnt == 1) &&
983             ((clknode->flags & CLK_NODE_CANNOT_STOP) == 0)) {
984                 rv = CLKNODE_SET_GATE(clknode, 0);
985                 if (rv != 0) {
986                         CLKNODE_UNLOCK(clknode);
987                         return (rv);
988                 }
989         }
990         clknode->enable_cnt--;
991         CLKNODE_UNLOCK(clknode);
992
993         if (clknode->parent_cnt > 0) {
994                 rv = clknode_disable(clknode->parent);
995         }
996         return (rv);
997 }
998
999 int
1000 clknode_stop(struct clknode *clknode, int depth)
1001 {
1002         int rv;
1003
1004         CLK_TOPO_ASSERT();
1005         rv = 0;
1006
1007         CLKNODE_XLOCK(clknode);
1008         /* The first node cannot be enabled. */
1009         if ((clknode->enable_cnt != 0) && (depth == 0)) {
1010                 CLKNODE_UNLOCK(clknode);
1011                 return (EBUSY);
1012         }
1013         /* Stop clock for each node in chain, starting from consumer. */
1014         if ((clknode->enable_cnt == 0) &&
1015             ((clknode->flags & CLK_NODE_CANNOT_STOP) == 0)) {
1016                 rv = CLKNODE_SET_GATE(clknode, 0);
1017                 if (rv != 0) {
1018                         CLKNODE_UNLOCK(clknode);
1019                         return (rv);
1020                 }
1021         }
1022         CLKNODE_UNLOCK(clknode);
1023
1024         if (clknode->parent_cnt > 0)
1025                 rv = clknode_stop(clknode->parent, depth + 1);
1026         return (rv);
1027 }
1028
1029 /* --------------------------------------------------------------------------
1030  *
1031  * Clock consumers interface.
1032  *
1033  */
1034 /* Helper function for clk_get*() */
1035 static clk_t
1036 clk_create(struct clknode *clknode, device_t dev)
1037 {
1038         struct clk *clk;
1039
1040         CLK_TOPO_ASSERT();
1041
1042         clk =  malloc(sizeof(struct clk), M_CLOCK, M_WAITOK);
1043         clk->dev = dev;
1044         clk->clknode = clknode;
1045         clk->enable_cnt = 0;
1046         clknode->ref_cnt++;
1047
1048         return (clk);
1049 }
1050
1051 int
1052 clk_get_freq(clk_t clk, uint64_t *freq)
1053 {
1054         int rv;
1055         struct clknode *clknode;
1056
1057         clknode = clk->clknode;
1058         KASSERT(clknode->ref_cnt > 0,
1059            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1060
1061         CLK_TOPO_SLOCK();
1062         rv = clknode_get_freq(clknode, freq);
1063         CLK_TOPO_UNLOCK();
1064         return (rv);
1065 }
1066
1067 int
1068 clk_set_freq(clk_t clk, uint64_t freq, int flags)
1069 {
1070         int rv;
1071         struct clknode *clknode;
1072
1073         flags &= CLK_SET_USER_MASK;
1074         clknode = clk->clknode;
1075         KASSERT(clknode->ref_cnt > 0,
1076            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1077
1078         CLK_TOPO_XLOCK();
1079         rv = clknode_set_freq(clknode, freq, flags, clk->enable_cnt);
1080         CLK_TOPO_UNLOCK();
1081         return (rv);
1082 }
1083
1084 int
1085 clk_test_freq(clk_t clk, uint64_t freq, int flags)
1086 {
1087         int rv;
1088         struct clknode *clknode;
1089
1090         flags &= CLK_SET_USER_MASK;
1091         clknode = clk->clknode;
1092         KASSERT(clknode->ref_cnt > 0,
1093            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1094
1095         CLK_TOPO_XLOCK();
1096         rv = clknode_set_freq(clknode, freq, flags | CLK_SET_DRYRUN, 0);
1097         CLK_TOPO_UNLOCK();
1098         return (rv);
1099 }
1100
1101 int
1102 clk_get_parent(clk_t clk, clk_t *parent)
1103 {
1104         struct clknode *clknode;
1105         struct clknode *parentnode;
1106
1107         clknode = clk->clknode;
1108         KASSERT(clknode->ref_cnt > 0,
1109            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1110
1111         CLK_TOPO_SLOCK();
1112         parentnode = clknode_get_parent(clknode);
1113         if (parentnode == NULL) {
1114                 CLK_TOPO_UNLOCK();
1115                 return (ENODEV);
1116         }
1117         *parent = clk_create(parentnode, clk->dev);
1118         CLK_TOPO_UNLOCK();
1119         return (0);
1120 }
1121
1122 int
1123 clk_set_parent_by_clk(clk_t clk, clk_t parent)
1124 {
1125         int rv;
1126         struct clknode *clknode;
1127         struct clknode *parentnode;
1128
1129         clknode = clk->clknode;
1130         parentnode = parent->clknode;
1131         KASSERT(clknode->ref_cnt > 0,
1132            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1133         KASSERT(parentnode->ref_cnt > 0,
1134            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1135         CLK_TOPO_XLOCK();
1136         rv = clknode_set_parent_by_name(clknode, parentnode->name);
1137         CLK_TOPO_UNLOCK();
1138         return (rv);
1139 }
1140
1141 int
1142 clk_enable(clk_t clk)
1143 {
1144         int rv;
1145         struct clknode *clknode;
1146
1147         clknode = clk->clknode;
1148         KASSERT(clknode->ref_cnt > 0,
1149            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1150         CLK_TOPO_SLOCK();
1151         rv = clknode_enable(clknode);
1152         if (rv == 0)
1153                 clk->enable_cnt++;
1154         CLK_TOPO_UNLOCK();
1155         return (rv);
1156 }
1157
1158 int
1159 clk_disable(clk_t clk)
1160 {
1161         int rv;
1162         struct clknode *clknode;
1163
1164         clknode = clk->clknode;
1165         KASSERT(clknode->ref_cnt > 0,
1166            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1167         KASSERT(clk->enable_cnt > 0,
1168            ("Attempt to disable already disabled clock: %s\n", clknode->name));
1169         CLK_TOPO_SLOCK();
1170         rv = clknode_disable(clknode);
1171         if (rv == 0)
1172                 clk->enable_cnt--;
1173         CLK_TOPO_UNLOCK();
1174         return (rv);
1175 }
1176
1177 int
1178 clk_stop(clk_t clk)
1179 {
1180         int rv;
1181         struct clknode *clknode;
1182
1183         clknode = clk->clknode;
1184         KASSERT(clknode->ref_cnt > 0,
1185            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1186         KASSERT(clk->enable_cnt == 0,
1187            ("Attempt to stop already enabled clock: %s\n", clknode->name));
1188
1189         CLK_TOPO_SLOCK();
1190         rv = clknode_stop(clknode, 0);
1191         CLK_TOPO_UNLOCK();
1192         return (rv);
1193 }
1194
1195 int
1196 clk_release(clk_t clk)
1197 {
1198         struct clknode *clknode;
1199
1200         clknode = clk->clknode;
1201         KASSERT(clknode->ref_cnt > 0,
1202            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1203         CLK_TOPO_SLOCK();
1204         while (clk->enable_cnt > 0) {
1205                 clknode_disable(clknode);
1206                 clk->enable_cnt--;
1207         }
1208         CLKNODE_XLOCK(clknode);
1209         clknode->ref_cnt--;
1210         CLKNODE_UNLOCK(clknode);
1211         CLK_TOPO_UNLOCK();
1212
1213         free(clk, M_CLOCK);
1214         return (0);
1215 }
1216
1217 const char *
1218 clk_get_name(clk_t clk)
1219 {
1220         const char *name;
1221         struct clknode *clknode;
1222
1223         clknode = clk->clknode;
1224         KASSERT(clknode->ref_cnt > 0,
1225            ("Attempt to access unreferenced clock: %s\n", clknode->name));
1226         name = clknode_get_name(clknode);
1227         return (name);
1228 }
1229
1230 int
1231 clk_get_by_name(device_t dev, const char *name, clk_t *clk)
1232 {
1233         struct clknode *clknode;
1234
1235         CLK_TOPO_SLOCK();
1236         clknode = clknode_find_by_name(name);
1237         if (clknode == NULL) {
1238                 CLK_TOPO_UNLOCK();
1239                 return (ENODEV);
1240         }
1241         *clk = clk_create(clknode, dev);
1242         CLK_TOPO_UNLOCK();
1243         return (0);
1244 }
1245
1246 int
1247 clk_get_by_id(device_t dev, struct clkdom *clkdom, intptr_t id, clk_t *clk)
1248 {
1249         struct clknode *clknode;
1250
1251         CLK_TOPO_SLOCK();
1252
1253         clknode = clknode_find_by_id(clkdom, id);
1254         if (clknode == NULL) {
1255                 CLK_TOPO_UNLOCK();
1256                 return (ENODEV);
1257         }
1258         *clk = clk_create(clknode, dev);
1259         CLK_TOPO_UNLOCK();
1260
1261         return (0);
1262 }
1263
1264 #ifdef FDT
1265
1266 int
1267 clk_set_assigned(device_t dev, phandle_t node)
1268 {
1269         clk_t clk, clk_parent;
1270         int error, nclocks, i;
1271
1272         error = ofw_bus_parse_xref_list_get_length(node,
1273             "assigned-clock-parents", "#clock-cells", &nclocks);
1274
1275         if (error != 0) {
1276                 if (error != ENOENT)
1277                         device_printf(dev,
1278                             "cannot parse assigned-clock-parents property\n");
1279                 return (error);
1280         }
1281
1282         for (i = 0; i < nclocks; i++) {
1283                 error = clk_get_by_ofw_index_prop(dev, 0,
1284                     "assigned-clock-parents", i, &clk_parent);
1285                 if (error != 0) {
1286                         device_printf(dev, "cannot get parent %d\n", i);
1287                         return (error);
1288                 }
1289
1290                 error = clk_get_by_ofw_index_prop(dev, 0, "assigned-clocks",
1291                     i, &clk);
1292                 if (error != 0) {
1293                         device_printf(dev, "cannot get assigned clock %d\n", i);
1294                         clk_release(clk_parent);
1295                         return (error);
1296                 }
1297
1298                 error = clk_set_parent_by_clk(clk, clk_parent);
1299                 clk_release(clk_parent);
1300                 clk_release(clk);
1301                 if (error != 0)
1302                         return (error);
1303         }
1304
1305         return (0);
1306 }
1307
1308 int
1309 clk_get_by_ofw_index_prop(device_t dev, phandle_t cnode, const char *prop, int idx, clk_t *clk)
1310 {
1311         phandle_t parent, *cells;
1312         device_t clockdev;
1313         int ncells, rv;
1314         struct clkdom *clkdom;
1315         struct clknode *clknode;
1316
1317         *clk = NULL;
1318         if (cnode <= 0)
1319                 cnode = ofw_bus_get_node(dev);
1320         if (cnode <= 0) {
1321                 device_printf(dev, "%s called on not ofw based device\n",
1322                  __func__);
1323                 return (ENXIO);
1324         }
1325
1326
1327         rv = ofw_bus_parse_xref_list_alloc(cnode, prop, "#clock-cells", idx,
1328             &parent, &ncells, &cells);
1329         if (rv != 0) {
1330                 return (rv);
1331         }
1332
1333         clockdev = OF_device_from_xref(parent);
1334         if (clockdev == NULL) {
1335                 rv = ENODEV;
1336                 goto done;
1337         }
1338
1339         CLK_TOPO_SLOCK();
1340         clkdom = clkdom_get_by_dev(clockdev);
1341         if (clkdom == NULL){
1342                 CLK_TOPO_UNLOCK();
1343                 rv = ENXIO;
1344                 goto done;
1345         }
1346
1347         rv = clkdom->ofw_mapper(clkdom, ncells, cells, &clknode);
1348         if (rv == 0) {
1349                 *clk = clk_create(clknode, dev);
1350         }
1351         CLK_TOPO_UNLOCK();
1352
1353 done:
1354         if (cells != NULL)
1355                 OF_prop_free(cells);
1356         return (rv);
1357 }
1358
1359 int
1360 clk_get_by_ofw_index(device_t dev, phandle_t cnode, int idx, clk_t *clk)
1361 {
1362         return (clk_get_by_ofw_index_prop(dev, cnode, "clocks", idx, clk));
1363 }
1364
1365 int
1366 clk_get_by_ofw_name(device_t dev, phandle_t cnode, const char *name, clk_t *clk)
1367 {
1368         int rv, idx;
1369
1370         if (cnode <= 0)
1371                 cnode = ofw_bus_get_node(dev);
1372         if (cnode <= 0) {
1373                 device_printf(dev, "%s called on not ofw based device\n",
1374                  __func__);
1375                 return (ENXIO);
1376         }
1377         rv = ofw_bus_find_string_index(cnode, "clock-names", name, &idx);
1378         if (rv != 0)
1379                 return (rv);
1380         return (clk_get_by_ofw_index(dev, cnode, idx, clk));
1381 }
1382
1383 /* --------------------------------------------------------------------------
1384  *
1385  * Support functions for parsing various clock related OFW things.
1386  */
1387
1388 /*
1389  * Get "clock-output-names" and  (optional) "clock-indices" lists.
1390  * Both lists are alocated using M_OFWPROP specifier.
1391  *
1392  * Returns number of items or 0.
1393  */
1394 int
1395 clk_parse_ofw_out_names(device_t dev, phandle_t node, const char ***out_names,
1396         uint32_t **indices)
1397 {
1398         int name_items, rv;
1399
1400         *out_names = NULL;
1401         *indices = NULL;
1402         if (!OF_hasprop(node, "clock-output-names"))
1403                 return (0);
1404         rv = ofw_bus_string_list_to_array(node, "clock-output-names",
1405             out_names);
1406         if (rv <= 0)
1407                 return (0);
1408         name_items = rv;
1409
1410         if (!OF_hasprop(node, "clock-indices"))
1411                 return (name_items);
1412         rv = OF_getencprop_alloc(node, "clock-indices", sizeof (uint32_t),
1413             (void **)indices);
1414         if (rv != name_items) {
1415                 device_printf(dev, " Size of 'clock-output-names' and "
1416                     "'clock-indices' differs\n");
1417                 OF_prop_free(*out_names);
1418                 OF_prop_free(*indices);
1419                 return (0);
1420         }
1421         return (name_items);
1422 }
1423
1424 /*
1425  * Get output clock name for single output clock node.
1426  */
1427 int
1428 clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name)
1429 {
1430         const char **out_names;
1431         const char  *tmp_name;
1432         int rv;
1433
1434         *name = NULL;
1435         if (!OF_hasprop(node, "clock-output-names")) {
1436                 tmp_name  = ofw_bus_get_name(dev);
1437                 if (tmp_name == NULL)
1438                         return (ENXIO);
1439                 *name = strdup(tmp_name, M_OFWPROP);
1440                 return (0);
1441         }
1442         rv = ofw_bus_string_list_to_array(node, "clock-output-names",
1443             &out_names);
1444         if (rv != 1) {
1445                 OF_prop_free(out_names);
1446                 device_printf(dev, "Malformed 'clock-output-names' property\n");
1447                 return (ENXIO);
1448         }
1449         *name = strdup(out_names[0], M_OFWPROP);
1450         OF_prop_free(out_names);
1451         return (0);
1452 }
1453 #endif
1454
1455 static int
1456 clkdom_sysctl(SYSCTL_HANDLER_ARGS)
1457 {
1458         struct clkdom *clkdom = arg1;
1459         struct clknode *clknode;
1460         struct sbuf *sb;
1461         int ret;
1462
1463         sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
1464         if (sb == NULL)
1465                 return (ENOMEM);
1466
1467         CLK_TOPO_SLOCK();
1468         TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
1469                 sbuf_printf(sb, "%s ", clknode->name);
1470         }
1471         CLK_TOPO_UNLOCK();
1472
1473         ret = sbuf_finish(sb);
1474         sbuf_delete(sb);
1475         return (ret);
1476 }
1477
1478 static int
1479 clknode_sysctl(SYSCTL_HANDLER_ARGS)
1480 {
1481         struct clknode *clknode, *children;
1482         enum clknode_sysctl_type type = arg2;
1483         struct sbuf *sb;
1484         const char **parent_names;
1485         int ret, i;
1486
1487         clknode = arg1;
1488         sb = sbuf_new_for_sysctl(NULL, NULL, 512, req);
1489         if (sb == NULL)
1490                 return (ENOMEM);
1491
1492         CLK_TOPO_SLOCK();
1493         switch (type) {
1494         case CLKNODE_SYSCTL_PARENT:
1495                 if (clknode->parent)
1496                         sbuf_printf(sb, "%s", clknode->parent->name);
1497                 break;
1498         case CLKNODE_SYSCTL_PARENTS_LIST:
1499                 parent_names = clknode_get_parent_names(clknode);
1500                 for (i = 0; i < clknode->parent_cnt; i++)
1501                         sbuf_printf(sb, "%s ", parent_names[i]);
1502                 break;
1503         case CLKNODE_SYSCTL_CHILDREN_LIST:
1504                 TAILQ_FOREACH(children, &(clknode->children), sibling_link) {
1505                         sbuf_printf(sb, "%s ", children->name);
1506                 }
1507                 break;
1508         }
1509         CLK_TOPO_UNLOCK();
1510
1511         ret = sbuf_finish(sb);
1512         sbuf_delete(sb);
1513         return (ret);
1514 }