The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/extres/clk/clk.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: f6232c5d442e3046e735ce405113dce4


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.