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