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