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
clknode_method_init(struct clknode * clknode,device_t dev)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
clknode_method_recalc_freq(struct clknode * clknode,uint64_t * freq)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
clknode_method_set_freq(struct clknode * clknode,uint64_t fin,uint64_t * fout,int flags,int * stop)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
clknode_method_set_gate(struct clknode * clk,bool enable)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
clknode_method_set_mux(struct clknode * clk,int idx)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 **
strdup_list(const char ** names,int num)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
clknode_refresh_cache(struct clknode * clknode,uint64_t freq)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 *
clknode_find_by_name(const char * name)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 *
clknode_find_by_id(struct clkdom * clkdom,intptr_t id)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 *
clkdom_get_by_dev(const device_t dev)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
clknode_default_ofw_map(struct clkdom * clkdom,uint32_t ncells,phandle_t * cells,struct clknode ** clk)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 *
clkdom_create(device_t dev)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
clkdom_unlock(struct clkdom * clkdom)414be82b3a0SEmmanuel Vadot clkdom_unlock(struct clkdom *clkdom)
415be82b3a0SEmmanuel Vadot {
416be82b3a0SEmmanuel Vadot
417be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
418be82b3a0SEmmanuel Vadot }
419be82b3a0SEmmanuel Vadot
420be82b3a0SEmmanuel Vadot void
clkdom_xlock(struct clkdom * clkdom)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
clkdom_finit(struct clkdom * clkdom)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
clkdom_dump(struct clkdom * clkdom)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);
515*08635c51SMitchell Horne if (rv != 0) {
516*08635c51SMitchell Horne printf("Clock: %s, error getting frequency: %d\n",
517*08635c51SMitchell Horne clknode->name, rv);
518*08635c51SMitchell Horne continue;
519*08635c51SMitchell Horne }
520*08635c51SMitchell Horne
521*08635c51SMitchell Horne if (clknode->parent != NULL) {
522*08635c51SMitchell Horne printf("Clock: %s, parent: %s(%d), freq: %ju\n",
523*08635c51SMitchell Horne clknode->name, clknode->parent->name,
524*08635c51SMitchell Horne clknode->parent_idx, (uintmax_t)freq);
525*08635c51SMitchell Horne } else {
526*08635c51SMitchell Horne printf("Clock: %s, parent: none, freq: %ju\n",
527*08635c51SMitchell Horne clknode->name, (uintmax_t)freq);
528*08635c51SMitchell Horne }
529be82b3a0SEmmanuel Vadot }
530be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
531be82b3a0SEmmanuel Vadot }
532be82b3a0SEmmanuel Vadot
533be82b3a0SEmmanuel Vadot /*
534be82b3a0SEmmanuel Vadot * Create and initialize clock object, but do not register it.
535be82b3a0SEmmanuel Vadot */
536be82b3a0SEmmanuel Vadot struct clknode *
clknode_create(struct clkdom * clkdom,clknode_class_t clknode_class,const struct clknode_init_def * def)537be82b3a0SEmmanuel Vadot clknode_create(struct clkdom * clkdom, clknode_class_t clknode_class,
538be82b3a0SEmmanuel Vadot const struct clknode_init_def *def)
539be82b3a0SEmmanuel Vadot {
540be82b3a0SEmmanuel Vadot struct clknode *clknode;
541be82b3a0SEmmanuel Vadot struct sysctl_oid *clknode_oid;
542be82b3a0SEmmanuel Vadot bool replaced;
543be82b3a0SEmmanuel Vadot kobjop_desc_t kobj_desc;
544be82b3a0SEmmanuel Vadot kobj_method_t *kobj_method;
545be82b3a0SEmmanuel Vadot
546be82b3a0SEmmanuel Vadot KASSERT(def->name != NULL, ("clock name is NULL"));
547be82b3a0SEmmanuel Vadot KASSERT(def->name[0] != '\0', ("clock name is empty"));
548be82b3a0SEmmanuel Vadot if (def->flags & CLK_NODE_LINKED) {
549be82b3a0SEmmanuel Vadot KASSERT(def->parent_cnt == 0,
550be82b3a0SEmmanuel Vadot ("Linked clock must not have parents"));
551be82b3a0SEmmanuel Vadot KASSERT(clknode_class->size== 0,
552be82b3a0SEmmanuel Vadot ("Linked clock cannot have own softc"));
553be82b3a0SEmmanuel Vadot }
554be82b3a0SEmmanuel Vadot
555be82b3a0SEmmanuel Vadot /* Process duplicated clocks */
556be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
557be82b3a0SEmmanuel Vadot clknode = clknode_find_by_name(def->name);
558be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
559be82b3a0SEmmanuel Vadot if (clknode != NULL) {
560be82b3a0SEmmanuel Vadot if (!(clknode->flags & CLK_NODE_LINKED) &&
561be82b3a0SEmmanuel Vadot def->flags & CLK_NODE_LINKED) {
562be82b3a0SEmmanuel Vadot /*
563be82b3a0SEmmanuel Vadot * New clock is linked and real already exists.
564be82b3a0SEmmanuel Vadot * Do nothing and return real node. It is in right
565be82b3a0SEmmanuel Vadot * domain, enqueued in right lists and fully initialized.
566be82b3a0SEmmanuel Vadot */
567be82b3a0SEmmanuel Vadot return (clknode);
568be82b3a0SEmmanuel Vadot } else if (clknode->flags & CLK_NODE_LINKED &&
569be82b3a0SEmmanuel Vadot !(def->flags & CLK_NODE_LINKED)) {
570be82b3a0SEmmanuel Vadot /*
571be82b3a0SEmmanuel Vadot * New clock is real but linked already exists.
572be82b3a0SEmmanuel Vadot * Remove old linked node from originating domain
573be82b3a0SEmmanuel Vadot * (real clock must be owned by another) and from
574be82b3a0SEmmanuel Vadot * global names link (it will be added back into it
575be82b3a0SEmmanuel Vadot * again in following clknode_register()). Then reuse
576be82b3a0SEmmanuel Vadot * original clknode structure and reinitialize it
577be82b3a0SEmmanuel Vadot * with new dat. By this, all lists containing this
578be82b3a0SEmmanuel Vadot * node remains valid, but the new node virtually
579be82b3a0SEmmanuel Vadot * replace the linked one.
580be82b3a0SEmmanuel Vadot */
581be82b3a0SEmmanuel Vadot KASSERT(clkdom != clknode->clkdom,
582be82b3a0SEmmanuel Vadot ("linked clock must be from another "
583be82b3a0SEmmanuel Vadot "domain that real one"));
584be82b3a0SEmmanuel Vadot TAILQ_REMOVE(&clkdom->clknode_list, clknode,
585be82b3a0SEmmanuel Vadot clkdom_link);
586be82b3a0SEmmanuel Vadot TAILQ_REMOVE(&clknode_list, clknode, clklist_link);
587be82b3a0SEmmanuel Vadot replaced = true;
588be82b3a0SEmmanuel Vadot } else if (clknode->flags & CLK_NODE_LINKED &&
589be82b3a0SEmmanuel Vadot def->flags & CLK_NODE_LINKED) {
590be82b3a0SEmmanuel Vadot /*
591be82b3a0SEmmanuel Vadot * Both clocks are linked.
592be82b3a0SEmmanuel Vadot * Return old one, so we hold only one copy od link.
593be82b3a0SEmmanuel Vadot */
594be82b3a0SEmmanuel Vadot return (clknode);
595be82b3a0SEmmanuel Vadot } else {
596be82b3a0SEmmanuel Vadot /* Both clocks are real */
597be82b3a0SEmmanuel Vadot panic("Duplicated clock registration: %s\n", def->name);
598be82b3a0SEmmanuel Vadot }
599be82b3a0SEmmanuel Vadot } else {
600be82b3a0SEmmanuel Vadot /* Create clknode object and initialize it. */
601be82b3a0SEmmanuel Vadot clknode = malloc(sizeof(struct clknode), M_CLOCK,
602be82b3a0SEmmanuel Vadot M_WAITOK | M_ZERO);
603be82b3a0SEmmanuel Vadot sx_init(&clknode->lock, "Clocknode lock");
604be82b3a0SEmmanuel Vadot TAILQ_INIT(&clknode->children);
605be82b3a0SEmmanuel Vadot replaced = false;
606be82b3a0SEmmanuel Vadot }
607be82b3a0SEmmanuel Vadot
608be82b3a0SEmmanuel Vadot kobj_init((kobj_t)clknode, (kobj_class_t)clknode_class);
609be82b3a0SEmmanuel Vadot
610be82b3a0SEmmanuel Vadot /* Allocate softc if required. */
611be82b3a0SEmmanuel Vadot if (clknode_class->size > 0) {
612be82b3a0SEmmanuel Vadot clknode->softc = malloc(clknode_class->size,
613be82b3a0SEmmanuel Vadot M_CLOCK, M_WAITOK | M_ZERO);
614be82b3a0SEmmanuel Vadot }
615be82b3a0SEmmanuel Vadot
616be82b3a0SEmmanuel Vadot /* Prepare array for ptrs to parent clocks. */
617be82b3a0SEmmanuel Vadot clknode->parents = malloc(sizeof(struct clknode *) * def->parent_cnt,
618be82b3a0SEmmanuel Vadot M_CLOCK, M_WAITOK | M_ZERO);
619be82b3a0SEmmanuel Vadot
620be82b3a0SEmmanuel Vadot /* Copy all strings unless they're flagged as static. */
621be82b3a0SEmmanuel Vadot if (def->flags & CLK_NODE_STATIC_STRINGS) {
622be82b3a0SEmmanuel Vadot clknode->name = def->name;
623be82b3a0SEmmanuel Vadot clknode->parent_names = def->parent_names;
624be82b3a0SEmmanuel Vadot } else {
625be82b3a0SEmmanuel Vadot clknode->name = strdup(def->name, M_CLOCK);
626be82b3a0SEmmanuel Vadot clknode->parent_names =
627be82b3a0SEmmanuel Vadot strdup_list(def->parent_names, def->parent_cnt);
628be82b3a0SEmmanuel Vadot }
629be82b3a0SEmmanuel Vadot
630be82b3a0SEmmanuel Vadot /* Rest of init. */
631be82b3a0SEmmanuel Vadot clknode->id = def->id;
632be82b3a0SEmmanuel Vadot clknode->clkdom = clkdom;
633be82b3a0SEmmanuel Vadot clknode->flags = def->flags;
634be82b3a0SEmmanuel Vadot clknode->parent_cnt = def->parent_cnt;
635be82b3a0SEmmanuel Vadot clknode->parent = NULL;
636be82b3a0SEmmanuel Vadot clknode->parent_idx = CLKNODE_IDX_NONE;
637be82b3a0SEmmanuel Vadot
638be82b3a0SEmmanuel Vadot if (replaced)
639be82b3a0SEmmanuel Vadot return (clknode);
640be82b3a0SEmmanuel Vadot
641be82b3a0SEmmanuel Vadot sysctl_ctx_init(&clknode->sysctl_ctx);
642be82b3a0SEmmanuel Vadot clknode_oid = SYSCTL_ADD_NODE(&clknode->sysctl_ctx,
643be82b3a0SEmmanuel Vadot SYSCTL_STATIC_CHILDREN(_hw_clock),
644be82b3a0SEmmanuel Vadot OID_AUTO, clknode->name,
645be82b3a0SEmmanuel Vadot CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "A clock node");
646be82b3a0SEmmanuel Vadot
647be82b3a0SEmmanuel Vadot SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
648be82b3a0SEmmanuel Vadot SYSCTL_CHILDREN(clknode_oid),
649be82b3a0SEmmanuel Vadot OID_AUTO, "frequency",
650be82b3a0SEmmanuel Vadot CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
651be82b3a0SEmmanuel Vadot clknode, CLKNODE_SYSCTL_FREQUENCY, clknode_sysctl,
652be82b3a0SEmmanuel Vadot "A",
653be82b3a0SEmmanuel Vadot "The clock frequency");
654be82b3a0SEmmanuel Vadot
655be82b3a0SEmmanuel Vadot /* Install gate handler only if clknode have 'set_gate' method */
656be82b3a0SEmmanuel Vadot kobj_desc = &clknode_set_gate_desc;
657be82b3a0SEmmanuel Vadot kobj_method = kobj_lookup_method(((kobj_t)clknode)->ops->cls, NULL,
658be82b3a0SEmmanuel Vadot kobj_desc);
659be82b3a0SEmmanuel Vadot if (kobj_method != &kobj_desc->deflt &&
660be82b3a0SEmmanuel Vadot kobj_method->func != (kobjop_t)clknode_method_set_gate) {
661be82b3a0SEmmanuel Vadot SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
662be82b3a0SEmmanuel Vadot SYSCTL_CHILDREN(clknode_oid),
663be82b3a0SEmmanuel Vadot OID_AUTO, "gate",
664be82b3a0SEmmanuel Vadot CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
665be82b3a0SEmmanuel Vadot clknode, CLKNODE_SYSCTL_GATE, clknode_sysctl,
666be82b3a0SEmmanuel Vadot "A",
667be82b3a0SEmmanuel Vadot "The clock gate status");
668be82b3a0SEmmanuel Vadot }
669be82b3a0SEmmanuel Vadot
670be82b3a0SEmmanuel Vadot SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
671be82b3a0SEmmanuel Vadot SYSCTL_CHILDREN(clknode_oid),
672be82b3a0SEmmanuel Vadot OID_AUTO, "parent",
673be82b3a0SEmmanuel Vadot CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
674be82b3a0SEmmanuel Vadot clknode, CLKNODE_SYSCTL_PARENT, clknode_sysctl,
675be82b3a0SEmmanuel Vadot "A",
676be82b3a0SEmmanuel Vadot "The clock parent");
677be82b3a0SEmmanuel Vadot SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
678be82b3a0SEmmanuel Vadot SYSCTL_CHILDREN(clknode_oid),
679be82b3a0SEmmanuel Vadot OID_AUTO, "parents",
680be82b3a0SEmmanuel Vadot CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
681be82b3a0SEmmanuel Vadot clknode, CLKNODE_SYSCTL_PARENTS_LIST, clknode_sysctl,
682be82b3a0SEmmanuel Vadot "A",
683be82b3a0SEmmanuel Vadot "The clock parents list");
684be82b3a0SEmmanuel Vadot SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
685be82b3a0SEmmanuel Vadot SYSCTL_CHILDREN(clknode_oid),
686be82b3a0SEmmanuel Vadot OID_AUTO, "childrens",
687be82b3a0SEmmanuel Vadot CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
688be82b3a0SEmmanuel Vadot clknode, CLKNODE_SYSCTL_CHILDREN_LIST, clknode_sysctl,
689be82b3a0SEmmanuel Vadot "A",
690be82b3a0SEmmanuel Vadot "The clock childrens list");
691be82b3a0SEmmanuel Vadot SYSCTL_ADD_INT(&clknode->sysctl_ctx,
692be82b3a0SEmmanuel Vadot SYSCTL_CHILDREN(clknode_oid),
693be82b3a0SEmmanuel Vadot OID_AUTO, "enable_cnt",
694be82b3a0SEmmanuel Vadot CTLFLAG_RD, &clknode->enable_cnt, 0, "The clock enable counter");
695be82b3a0SEmmanuel Vadot
696be82b3a0SEmmanuel Vadot return (clknode);
697be82b3a0SEmmanuel Vadot }
698be82b3a0SEmmanuel Vadot
699be82b3a0SEmmanuel Vadot /*
700be82b3a0SEmmanuel Vadot * Register clock object into clock domain hierarchy.
701be82b3a0SEmmanuel Vadot */
702be82b3a0SEmmanuel Vadot struct clknode *
clknode_register(struct clkdom * clkdom,struct clknode * clknode)703be82b3a0SEmmanuel Vadot clknode_register(struct clkdom * clkdom, struct clknode *clknode)
704be82b3a0SEmmanuel Vadot {
705be82b3a0SEmmanuel Vadot int rv;
706be82b3a0SEmmanuel Vadot
707be82b3a0SEmmanuel Vadot /* Skip already registered linked node */
708be82b3a0SEmmanuel Vadot if (clknode->flags & CLK_NODE_REGISTERED)
709be82b3a0SEmmanuel Vadot return(clknode);
710be82b3a0SEmmanuel Vadot
711be82b3a0SEmmanuel Vadot rv = CLKNODE_INIT(clknode, clknode_get_device(clknode));
712be82b3a0SEmmanuel Vadot if (rv != 0) {
713be82b3a0SEmmanuel Vadot printf(" CLKNODE_INIT failed: %d\n", rv);
714be82b3a0SEmmanuel Vadot return (NULL);
715be82b3a0SEmmanuel Vadot }
716be82b3a0SEmmanuel Vadot
717be82b3a0SEmmanuel Vadot TAILQ_INSERT_TAIL(&clkdom->clknode_list, clknode, clkdom_link);
718be82b3a0SEmmanuel Vadot clknode->flags |= CLK_NODE_REGISTERED;
719be82b3a0SEmmanuel Vadot return (clknode);
720be82b3a0SEmmanuel Vadot }
721be82b3a0SEmmanuel Vadot
722be82b3a0SEmmanuel Vadot
723be82b3a0SEmmanuel Vadot static void
clknode_finish(void * dummy)724be82b3a0SEmmanuel Vadot clknode_finish(void *dummy)
725be82b3a0SEmmanuel Vadot {
726be82b3a0SEmmanuel Vadot struct clknode *clknode;
727be82b3a0SEmmanuel Vadot
728be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
729be82b3a0SEmmanuel Vadot TAILQ_FOREACH(clknode, &clknode_list, clklist_link) {
730be82b3a0SEmmanuel Vadot if (clknode->flags & CLK_NODE_LINKED)
731be82b3a0SEmmanuel Vadot printf("Unresolved linked clock found: %s\n",
732be82b3a0SEmmanuel Vadot clknode->name);
733be82b3a0SEmmanuel Vadot }
734be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
735be82b3a0SEmmanuel Vadot }
736be82b3a0SEmmanuel Vadot /*
737be82b3a0SEmmanuel Vadot * Clock providers interface.
738be82b3a0SEmmanuel Vadot */
739be82b3a0SEmmanuel Vadot
740be82b3a0SEmmanuel Vadot /*
741be82b3a0SEmmanuel Vadot * Reparent clock node.
742be82b3a0SEmmanuel Vadot */
743be82b3a0SEmmanuel Vadot static void
clknode_adjust_parent(struct clknode * clknode,int idx)744be82b3a0SEmmanuel Vadot clknode_adjust_parent(struct clknode *clknode, int idx)
745be82b3a0SEmmanuel Vadot {
746be82b3a0SEmmanuel Vadot
747be82b3a0SEmmanuel Vadot CLK_TOPO_XASSERT();
748be82b3a0SEmmanuel Vadot
749be82b3a0SEmmanuel Vadot if (clknode->parent_cnt == 0)
750be82b3a0SEmmanuel Vadot return;
751be82b3a0SEmmanuel Vadot if ((idx == CLKNODE_IDX_NONE) || (idx >= clknode->parent_cnt))
752be82b3a0SEmmanuel Vadot panic("%s: Invalid parent index %d for clock %s",
753be82b3a0SEmmanuel Vadot __func__, idx, clknode->name);
754be82b3a0SEmmanuel Vadot
755be82b3a0SEmmanuel Vadot if (clknode->parents[idx] == NULL)
756be82b3a0SEmmanuel Vadot panic("%s: Invalid parent index %d for clock %s",
757be82b3a0SEmmanuel Vadot __func__, idx, clknode->name);
758be82b3a0SEmmanuel Vadot
759be82b3a0SEmmanuel Vadot /* Remove me from old children list. */
760be82b3a0SEmmanuel Vadot if (clknode->parent != NULL) {
761be82b3a0SEmmanuel Vadot TAILQ_REMOVE(&clknode->parent->children, clknode, sibling_link);
762be82b3a0SEmmanuel Vadot }
763be82b3a0SEmmanuel Vadot
764be82b3a0SEmmanuel Vadot /* Insert into children list of new parent. */
765be82b3a0SEmmanuel Vadot clknode->parent_idx = idx;
766be82b3a0SEmmanuel Vadot clknode->parent = clknode->parents[idx];
767be82b3a0SEmmanuel Vadot TAILQ_INSERT_TAIL(&clknode->parent->children, clknode, sibling_link);
768be82b3a0SEmmanuel Vadot }
769be82b3a0SEmmanuel Vadot
770be82b3a0SEmmanuel Vadot /*
771be82b3a0SEmmanuel Vadot * Set parent index - init function.
772be82b3a0SEmmanuel Vadot */
773be82b3a0SEmmanuel Vadot void
clknode_init_parent_idx(struct clknode * clknode,int idx)774be82b3a0SEmmanuel Vadot clknode_init_parent_idx(struct clknode *clknode, int idx)
775be82b3a0SEmmanuel Vadot {
776be82b3a0SEmmanuel Vadot
777be82b3a0SEmmanuel Vadot if (clknode->parent_cnt == 0) {
778be82b3a0SEmmanuel Vadot clknode->parent_idx = CLKNODE_IDX_NONE;
779be82b3a0SEmmanuel Vadot clknode->parent = NULL;
780be82b3a0SEmmanuel Vadot return;
781be82b3a0SEmmanuel Vadot }
782be82b3a0SEmmanuel Vadot if ((idx == CLKNODE_IDX_NONE) ||
783be82b3a0SEmmanuel Vadot (idx >= clknode->parent_cnt) ||
784be82b3a0SEmmanuel Vadot (clknode->parent_names[idx] == NULL))
785be82b3a0SEmmanuel Vadot panic("%s: Invalid parent index %d for clock %s",
786be82b3a0SEmmanuel Vadot __func__, idx, clknode->name);
787be82b3a0SEmmanuel Vadot clknode->parent_idx = idx;
788be82b3a0SEmmanuel Vadot }
789be82b3a0SEmmanuel Vadot
790be82b3a0SEmmanuel Vadot int
clknode_set_parent_by_idx(struct clknode * clknode,int idx)791be82b3a0SEmmanuel Vadot clknode_set_parent_by_idx(struct clknode *clknode, int idx)
792be82b3a0SEmmanuel Vadot {
793be82b3a0SEmmanuel Vadot int rv;
794be82b3a0SEmmanuel Vadot uint64_t freq;
795be82b3a0SEmmanuel Vadot int oldidx;
796be82b3a0SEmmanuel Vadot
797be82b3a0SEmmanuel Vadot /* We have exclusive topology lock, node lock is not needed. */
798be82b3a0SEmmanuel Vadot CLK_TOPO_XASSERT();
799be82b3a0SEmmanuel Vadot
800be82b3a0SEmmanuel Vadot if (clknode->parent_cnt == 0)
801be82b3a0SEmmanuel Vadot return (0);
802be82b3a0SEmmanuel Vadot
803be82b3a0SEmmanuel Vadot if (clknode->parent_idx == idx)
804be82b3a0SEmmanuel Vadot return (0);
805be82b3a0SEmmanuel Vadot
806be82b3a0SEmmanuel Vadot oldidx = clknode->parent_idx;
807be82b3a0SEmmanuel Vadot clknode_adjust_parent(clknode, idx);
808be82b3a0SEmmanuel Vadot rv = CLKNODE_SET_MUX(clknode, idx);
809be82b3a0SEmmanuel Vadot if (rv != 0) {
810be82b3a0SEmmanuel Vadot clknode_adjust_parent(clknode, oldidx);
811be82b3a0SEmmanuel Vadot return (rv);
812be82b3a0SEmmanuel Vadot }
813be82b3a0SEmmanuel Vadot rv = clknode_get_freq(clknode->parent, &freq);
814be82b3a0SEmmanuel Vadot if (rv != 0)
815be82b3a0SEmmanuel Vadot return (rv);
816be82b3a0SEmmanuel Vadot rv = clknode_refresh_cache(clknode, freq);
817be82b3a0SEmmanuel Vadot return (rv);
818be82b3a0SEmmanuel Vadot }
819be82b3a0SEmmanuel Vadot
820be82b3a0SEmmanuel Vadot int
clknode_set_parent_by_name(struct clknode * clknode,const char * name)821be82b3a0SEmmanuel Vadot clknode_set_parent_by_name(struct clknode *clknode, const char *name)
822be82b3a0SEmmanuel Vadot {
823be82b3a0SEmmanuel Vadot int rv;
824be82b3a0SEmmanuel Vadot uint64_t freq;
825be82b3a0SEmmanuel Vadot int oldidx, idx;
826be82b3a0SEmmanuel Vadot
827be82b3a0SEmmanuel Vadot /* We have exclusive topology lock, node lock is not needed. */
828be82b3a0SEmmanuel Vadot CLK_TOPO_XASSERT();
829be82b3a0SEmmanuel Vadot
830be82b3a0SEmmanuel Vadot if (clknode->parent_cnt == 0)
831be82b3a0SEmmanuel Vadot return (0);
832be82b3a0SEmmanuel Vadot
833be82b3a0SEmmanuel Vadot /*
834be82b3a0SEmmanuel Vadot * If this node doesnt have mux, then passthrough request to parent.
835be82b3a0SEmmanuel Vadot * This feature is used in clock domain initialization and allows us to
836be82b3a0SEmmanuel Vadot * set clock source and target frequency on the tail node of the clock
837be82b3a0SEmmanuel Vadot * chain.
838be82b3a0SEmmanuel Vadot */
839be82b3a0SEmmanuel Vadot if (clknode->parent_cnt == 1) {
840be82b3a0SEmmanuel Vadot rv = clknode_set_parent_by_name(clknode->parent, name);
841be82b3a0SEmmanuel Vadot return (rv);
842be82b3a0SEmmanuel Vadot }
843be82b3a0SEmmanuel Vadot
844be82b3a0SEmmanuel Vadot for (idx = 0; idx < clknode->parent_cnt; idx++) {
845be82b3a0SEmmanuel Vadot if (clknode->parent_names[idx] == NULL)
846be82b3a0SEmmanuel Vadot continue;
847be82b3a0SEmmanuel Vadot if (strcmp(clknode->parent_names[idx], name) == 0)
848be82b3a0SEmmanuel Vadot break;
849be82b3a0SEmmanuel Vadot }
850be82b3a0SEmmanuel Vadot if (idx >= clknode->parent_cnt) {
851be82b3a0SEmmanuel Vadot return (ENXIO);
852be82b3a0SEmmanuel Vadot }
853be82b3a0SEmmanuel Vadot if (clknode->parent_idx == idx)
854be82b3a0SEmmanuel Vadot return (0);
855be82b3a0SEmmanuel Vadot
856be82b3a0SEmmanuel Vadot oldidx = clknode->parent_idx;
857be82b3a0SEmmanuel Vadot clknode_adjust_parent(clknode, idx);
858be82b3a0SEmmanuel Vadot rv = CLKNODE_SET_MUX(clknode, idx);
859be82b3a0SEmmanuel Vadot if (rv != 0) {
860be82b3a0SEmmanuel Vadot clknode_adjust_parent(clknode, oldidx);
861be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
862be82b3a0SEmmanuel Vadot return (rv);
863be82b3a0SEmmanuel Vadot }
864be82b3a0SEmmanuel Vadot rv = clknode_get_freq(clknode->parent, &freq);
865be82b3a0SEmmanuel Vadot if (rv != 0)
866be82b3a0SEmmanuel Vadot return (rv);
867be82b3a0SEmmanuel Vadot rv = clknode_refresh_cache(clknode, freq);
868be82b3a0SEmmanuel Vadot return (rv);
869be82b3a0SEmmanuel Vadot }
870be82b3a0SEmmanuel Vadot
871be82b3a0SEmmanuel Vadot struct clknode *
clknode_get_parent(struct clknode * clknode)872be82b3a0SEmmanuel Vadot clknode_get_parent(struct clknode *clknode)
873be82b3a0SEmmanuel Vadot {
874be82b3a0SEmmanuel Vadot
875be82b3a0SEmmanuel Vadot return (clknode->parent);
876be82b3a0SEmmanuel Vadot }
877be82b3a0SEmmanuel Vadot
878be82b3a0SEmmanuel Vadot const char *
clknode_get_name(struct clknode * clknode)879be82b3a0SEmmanuel Vadot clknode_get_name(struct clknode *clknode)
880be82b3a0SEmmanuel Vadot {
881be82b3a0SEmmanuel Vadot
882be82b3a0SEmmanuel Vadot return (clknode->name);
883be82b3a0SEmmanuel Vadot }
884be82b3a0SEmmanuel Vadot
885be82b3a0SEmmanuel Vadot const char **
clknode_get_parent_names(struct clknode * clknode)886be82b3a0SEmmanuel Vadot clknode_get_parent_names(struct clknode *clknode)
887be82b3a0SEmmanuel Vadot {
888be82b3a0SEmmanuel Vadot
889be82b3a0SEmmanuel Vadot return (clknode->parent_names);
890be82b3a0SEmmanuel Vadot }
891be82b3a0SEmmanuel Vadot
892be82b3a0SEmmanuel Vadot int
clknode_get_parents_num(struct clknode * clknode)893be82b3a0SEmmanuel Vadot clknode_get_parents_num(struct clknode *clknode)
894be82b3a0SEmmanuel Vadot {
895be82b3a0SEmmanuel Vadot
896be82b3a0SEmmanuel Vadot return (clknode->parent_cnt);
897be82b3a0SEmmanuel Vadot }
898be82b3a0SEmmanuel Vadot
899be82b3a0SEmmanuel Vadot int
clknode_get_parent_idx(struct clknode * clknode)900be82b3a0SEmmanuel Vadot clknode_get_parent_idx(struct clknode *clknode)
901be82b3a0SEmmanuel Vadot {
902be82b3a0SEmmanuel Vadot
903be82b3a0SEmmanuel Vadot return (clknode->parent_idx);
904be82b3a0SEmmanuel Vadot }
905be82b3a0SEmmanuel Vadot
906be82b3a0SEmmanuel Vadot int
clknode_get_flags(struct clknode * clknode)907be82b3a0SEmmanuel Vadot clknode_get_flags(struct clknode *clknode)
908be82b3a0SEmmanuel Vadot {
909be82b3a0SEmmanuel Vadot
910be82b3a0SEmmanuel Vadot return (clknode->flags);
911be82b3a0SEmmanuel Vadot }
912be82b3a0SEmmanuel Vadot
913be82b3a0SEmmanuel Vadot
914be82b3a0SEmmanuel Vadot void *
clknode_get_softc(struct clknode * clknode)915be82b3a0SEmmanuel Vadot clknode_get_softc(struct clknode *clknode)
916be82b3a0SEmmanuel Vadot {
917be82b3a0SEmmanuel Vadot
918be82b3a0SEmmanuel Vadot return (clknode->softc);
919be82b3a0SEmmanuel Vadot }
920be82b3a0SEmmanuel Vadot
921be82b3a0SEmmanuel Vadot device_t
clknode_get_device(struct clknode * clknode)922be82b3a0SEmmanuel Vadot clknode_get_device(struct clknode *clknode)
923be82b3a0SEmmanuel Vadot {
924be82b3a0SEmmanuel Vadot
925be82b3a0SEmmanuel Vadot return (clknode->clkdom->dev);
926be82b3a0SEmmanuel Vadot }
927be82b3a0SEmmanuel Vadot
928be82b3a0SEmmanuel Vadot #ifdef FDT
929be82b3a0SEmmanuel Vadot void
clkdom_set_ofw_mapper(struct clkdom * clkdom,clknode_ofw_mapper_func * map)930be82b3a0SEmmanuel Vadot clkdom_set_ofw_mapper(struct clkdom * clkdom, clknode_ofw_mapper_func *map)
931be82b3a0SEmmanuel Vadot {
932be82b3a0SEmmanuel Vadot
933be82b3a0SEmmanuel Vadot clkdom->ofw_mapper = map;
934be82b3a0SEmmanuel Vadot }
935be82b3a0SEmmanuel Vadot #endif
936be82b3a0SEmmanuel Vadot
937be82b3a0SEmmanuel Vadot /*
938be82b3a0SEmmanuel Vadot * Real consumers executive
939be82b3a0SEmmanuel Vadot */
940be82b3a0SEmmanuel Vadot int
clknode_get_freq(struct clknode * clknode,uint64_t * freq)941be82b3a0SEmmanuel Vadot clknode_get_freq(struct clknode *clknode, uint64_t *freq)
942be82b3a0SEmmanuel Vadot {
943be82b3a0SEmmanuel Vadot int rv;
944be82b3a0SEmmanuel Vadot
945be82b3a0SEmmanuel Vadot CLK_TOPO_ASSERT();
946be82b3a0SEmmanuel Vadot
947be82b3a0SEmmanuel Vadot /* Use cached value, if it exists. */
948be82b3a0SEmmanuel Vadot *freq = clknode->freq;
949be82b3a0SEmmanuel Vadot if (*freq != 0)
950be82b3a0SEmmanuel Vadot return (0);
951be82b3a0SEmmanuel Vadot
952be82b3a0SEmmanuel Vadot /* Get frequency from parent, if the clock has a parent. */
953be82b3a0SEmmanuel Vadot if (clknode->parent_cnt > 0) {
954be82b3a0SEmmanuel Vadot rv = clknode_get_freq(clknode->parent, freq);
955be82b3a0SEmmanuel Vadot if (rv != 0) {
956be82b3a0SEmmanuel Vadot return (rv);
957be82b3a0SEmmanuel Vadot }
958be82b3a0SEmmanuel Vadot }
959be82b3a0SEmmanuel Vadot
960be82b3a0SEmmanuel Vadot /* And recalculate my output frequency. */
961be82b3a0SEmmanuel Vadot CLKNODE_XLOCK(clknode);
962be82b3a0SEmmanuel Vadot rv = CLKNODE_RECALC_FREQ(clknode, freq);
963be82b3a0SEmmanuel Vadot if (rv != 0) {
964be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
965be82b3a0SEmmanuel Vadot printf("Cannot get frequency for clk: %s, error: %d\n",
966be82b3a0SEmmanuel Vadot clknode->name, rv);
967be82b3a0SEmmanuel Vadot return (rv);
968be82b3a0SEmmanuel Vadot }
969be82b3a0SEmmanuel Vadot
970be82b3a0SEmmanuel Vadot /* Save new frequency to cache. */
971be82b3a0SEmmanuel Vadot clknode->freq = *freq;
972be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
973be82b3a0SEmmanuel Vadot return (0);
974be82b3a0SEmmanuel Vadot }
975be82b3a0SEmmanuel Vadot
976be82b3a0SEmmanuel Vadot static int
_clknode_set_freq(struct clknode * clknode,uint64_t * freq,int flags,int enablecnt)977be82b3a0SEmmanuel Vadot _clknode_set_freq(struct clknode *clknode, uint64_t *freq, int flags,
978be82b3a0SEmmanuel Vadot int enablecnt)
979be82b3a0SEmmanuel Vadot {
980be82b3a0SEmmanuel Vadot int rv, done;
981be82b3a0SEmmanuel Vadot uint64_t parent_freq;
982be82b3a0SEmmanuel Vadot
983be82b3a0SEmmanuel Vadot /* We have exclusive topology lock, node lock is not needed. */
984be82b3a0SEmmanuel Vadot CLK_TOPO_XASSERT();
985be82b3a0SEmmanuel Vadot
986be82b3a0SEmmanuel Vadot /* Check for no change */
987be82b3a0SEmmanuel Vadot if (clknode->freq == *freq)
988be82b3a0SEmmanuel Vadot return (0);
989be82b3a0SEmmanuel Vadot
990be82b3a0SEmmanuel Vadot parent_freq = 0;
991be82b3a0SEmmanuel Vadot
992be82b3a0SEmmanuel Vadot /*
993be82b3a0SEmmanuel Vadot * We can set frequency only if
994be82b3a0SEmmanuel Vadot * clock is disabled
995be82b3a0SEmmanuel Vadot * OR
996be82b3a0SEmmanuel Vadot * clock is glitch free and is enabled by calling consumer only
997be82b3a0SEmmanuel Vadot */
998be82b3a0SEmmanuel Vadot if ((flags & CLK_SET_DRYRUN) == 0 &&
999be82b3a0SEmmanuel Vadot clknode->enable_cnt > 1 &&
1000be82b3a0SEmmanuel Vadot clknode->enable_cnt > enablecnt &&
1001be82b3a0SEmmanuel Vadot (clknode->flags & CLK_NODE_GLITCH_FREE) == 0) {
1002be82b3a0SEmmanuel Vadot return (EBUSY);
1003be82b3a0SEmmanuel Vadot }
1004be82b3a0SEmmanuel Vadot
1005be82b3a0SEmmanuel Vadot /* Get frequency from parent, if the clock has a parent. */
1006be82b3a0SEmmanuel Vadot if (clknode->parent_cnt > 0) {
1007be82b3a0SEmmanuel Vadot rv = clknode_get_freq(clknode->parent, &parent_freq);
1008be82b3a0SEmmanuel Vadot if (rv != 0) {
1009be82b3a0SEmmanuel Vadot return (rv);
1010be82b3a0SEmmanuel Vadot }
1011be82b3a0SEmmanuel Vadot }
1012be82b3a0SEmmanuel Vadot
1013be82b3a0SEmmanuel Vadot /* Set frequency for this clock. */
1014be82b3a0SEmmanuel Vadot rv = CLKNODE_SET_FREQ(clknode, parent_freq, freq, flags, &done);
1015be82b3a0SEmmanuel Vadot if (rv != 0) {
1016be82b3a0SEmmanuel Vadot printf("Cannot set frequency for clk: %s, error: %d\n",
1017be82b3a0SEmmanuel Vadot clknode->name, rv);
1018be82b3a0SEmmanuel Vadot if ((flags & CLK_SET_DRYRUN) == 0)
1019be82b3a0SEmmanuel Vadot clknode_refresh_cache(clknode, parent_freq);
1020be82b3a0SEmmanuel Vadot return (rv);
1021be82b3a0SEmmanuel Vadot }
1022be82b3a0SEmmanuel Vadot
1023be82b3a0SEmmanuel Vadot if (done) {
1024be82b3a0SEmmanuel Vadot /* Success - invalidate frequency cache for all children. */
1025be82b3a0SEmmanuel Vadot if ((flags & CLK_SET_DRYRUN) == 0) {
1026be82b3a0SEmmanuel Vadot clknode->freq = *freq;
1027be82b3a0SEmmanuel Vadot /* Clock might have reparent during set_freq */
1028be82b3a0SEmmanuel Vadot if (clknode->parent_cnt > 0) {
1029be82b3a0SEmmanuel Vadot rv = clknode_get_freq(clknode->parent,
1030be82b3a0SEmmanuel Vadot &parent_freq);
1031be82b3a0SEmmanuel Vadot if (rv != 0) {
1032be82b3a0SEmmanuel Vadot return (rv);
1033be82b3a0SEmmanuel Vadot }
1034be82b3a0SEmmanuel Vadot }
1035be82b3a0SEmmanuel Vadot clknode_refresh_cache(clknode, parent_freq);
1036be82b3a0SEmmanuel Vadot }
1037be82b3a0SEmmanuel Vadot } else if (clknode->parent != NULL) {
1038be82b3a0SEmmanuel Vadot /* Nothing changed, pass request to parent. */
1039be82b3a0SEmmanuel Vadot rv = _clknode_set_freq(clknode->parent, freq, flags,
1040be82b3a0SEmmanuel Vadot enablecnt);
1041be82b3a0SEmmanuel Vadot } else {
1042be82b3a0SEmmanuel Vadot /* End of chain without action. */
1043be82b3a0SEmmanuel Vadot printf("Cannot set frequency for clk: %s, end of chain\n",
1044be82b3a0SEmmanuel Vadot clknode->name);
1045be82b3a0SEmmanuel Vadot rv = ENXIO;
1046be82b3a0SEmmanuel Vadot }
1047be82b3a0SEmmanuel Vadot
1048be82b3a0SEmmanuel Vadot return (rv);
1049be82b3a0SEmmanuel Vadot }
1050be82b3a0SEmmanuel Vadot
1051be82b3a0SEmmanuel Vadot int
clknode_set_freq(struct clknode * clknode,uint64_t freq,int flags,int enablecnt)1052be82b3a0SEmmanuel Vadot clknode_set_freq(struct clknode *clknode, uint64_t freq, int flags,
1053be82b3a0SEmmanuel Vadot int enablecnt)
1054be82b3a0SEmmanuel Vadot {
1055be82b3a0SEmmanuel Vadot
1056be82b3a0SEmmanuel Vadot return (_clknode_set_freq(clknode, &freq, flags, enablecnt));
1057be82b3a0SEmmanuel Vadot }
1058be82b3a0SEmmanuel Vadot
1059be82b3a0SEmmanuel Vadot int
clknode_test_freq(struct clknode * clknode,uint64_t freq,int flags,int enablecnt,uint64_t * out_freq)1060be82b3a0SEmmanuel Vadot clknode_test_freq(struct clknode *clknode, uint64_t freq, int flags,
1061be82b3a0SEmmanuel Vadot int enablecnt, uint64_t *out_freq)
1062be82b3a0SEmmanuel Vadot {
1063be82b3a0SEmmanuel Vadot int rv;
1064be82b3a0SEmmanuel Vadot
1065be82b3a0SEmmanuel Vadot rv = _clknode_set_freq(clknode, &freq, flags | CLK_SET_DRYRUN,
1066be82b3a0SEmmanuel Vadot enablecnt);
1067be82b3a0SEmmanuel Vadot if (out_freq != NULL)
1068be82b3a0SEmmanuel Vadot *out_freq = freq;
1069be82b3a0SEmmanuel Vadot
1070be82b3a0SEmmanuel Vadot return (rv);
1071be82b3a0SEmmanuel Vadot }
1072be82b3a0SEmmanuel Vadot
1073be82b3a0SEmmanuel Vadot int
clknode_enable(struct clknode * clknode)1074be82b3a0SEmmanuel Vadot clknode_enable(struct clknode *clknode)
1075be82b3a0SEmmanuel Vadot {
1076be82b3a0SEmmanuel Vadot int rv;
1077be82b3a0SEmmanuel Vadot
1078be82b3a0SEmmanuel Vadot CLK_TOPO_ASSERT();
1079be82b3a0SEmmanuel Vadot
1080be82b3a0SEmmanuel Vadot /* Enable clock for each node in chain, starting from source. */
1081be82b3a0SEmmanuel Vadot if (clknode->parent_cnt > 0) {
1082be82b3a0SEmmanuel Vadot rv = clknode_enable(clknode->parent);
1083be82b3a0SEmmanuel Vadot if (rv != 0) {
1084be82b3a0SEmmanuel Vadot return (rv);
1085be82b3a0SEmmanuel Vadot }
1086be82b3a0SEmmanuel Vadot }
1087be82b3a0SEmmanuel Vadot
1088be82b3a0SEmmanuel Vadot /* Handle this node */
1089be82b3a0SEmmanuel Vadot CLKNODE_XLOCK(clknode);
1090be82b3a0SEmmanuel Vadot if (clknode->enable_cnt == 0) {
1091be82b3a0SEmmanuel Vadot rv = CLKNODE_SET_GATE(clknode, 1);
1092be82b3a0SEmmanuel Vadot if (rv != 0) {
1093be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
1094be82b3a0SEmmanuel Vadot return (rv);
1095be82b3a0SEmmanuel Vadot }
1096be82b3a0SEmmanuel Vadot }
1097be82b3a0SEmmanuel Vadot clknode->enable_cnt++;
1098be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
1099be82b3a0SEmmanuel Vadot return (0);
1100be82b3a0SEmmanuel Vadot }
1101be82b3a0SEmmanuel Vadot
1102be82b3a0SEmmanuel Vadot int
clknode_disable(struct clknode * clknode)1103be82b3a0SEmmanuel Vadot clknode_disable(struct clknode *clknode)
1104be82b3a0SEmmanuel Vadot {
1105be82b3a0SEmmanuel Vadot int rv;
1106be82b3a0SEmmanuel Vadot
1107be82b3a0SEmmanuel Vadot CLK_TOPO_ASSERT();
1108be82b3a0SEmmanuel Vadot rv = 0;
1109be82b3a0SEmmanuel Vadot
1110be82b3a0SEmmanuel Vadot CLKNODE_XLOCK(clknode);
1111be82b3a0SEmmanuel Vadot /* Disable clock for each node in chain, starting from consumer. */
1112be82b3a0SEmmanuel Vadot if ((clknode->enable_cnt == 1) &&
1113be82b3a0SEmmanuel Vadot ((clknode->flags & CLK_NODE_CANNOT_STOP) == 0)) {
1114be82b3a0SEmmanuel Vadot rv = CLKNODE_SET_GATE(clknode, 0);
1115be82b3a0SEmmanuel Vadot if (rv != 0) {
1116be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
1117be82b3a0SEmmanuel Vadot return (rv);
1118be82b3a0SEmmanuel Vadot }
1119be82b3a0SEmmanuel Vadot }
1120be82b3a0SEmmanuel Vadot clknode->enable_cnt--;
1121be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
1122be82b3a0SEmmanuel Vadot
1123be82b3a0SEmmanuel Vadot if (clknode->parent_cnt > 0) {
1124be82b3a0SEmmanuel Vadot rv = clknode_disable(clknode->parent);
1125be82b3a0SEmmanuel Vadot }
1126be82b3a0SEmmanuel Vadot return (rv);
1127be82b3a0SEmmanuel Vadot }
1128be82b3a0SEmmanuel Vadot
1129be82b3a0SEmmanuel Vadot int
clknode_stop(struct clknode * clknode,int depth)1130be82b3a0SEmmanuel Vadot clknode_stop(struct clknode *clknode, int depth)
1131be82b3a0SEmmanuel Vadot {
1132be82b3a0SEmmanuel Vadot int rv;
1133be82b3a0SEmmanuel Vadot
1134be82b3a0SEmmanuel Vadot CLK_TOPO_ASSERT();
1135be82b3a0SEmmanuel Vadot rv = 0;
1136be82b3a0SEmmanuel Vadot
1137be82b3a0SEmmanuel Vadot CLKNODE_XLOCK(clknode);
1138be82b3a0SEmmanuel Vadot /* The first node cannot be enabled. */
1139be82b3a0SEmmanuel Vadot if ((clknode->enable_cnt != 0) && (depth == 0)) {
1140be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
1141be82b3a0SEmmanuel Vadot return (EBUSY);
1142be82b3a0SEmmanuel Vadot }
1143be82b3a0SEmmanuel Vadot /* Stop clock for each node in chain, starting from consumer. */
1144be82b3a0SEmmanuel Vadot if ((clknode->enable_cnt == 0) &&
1145be82b3a0SEmmanuel Vadot ((clknode->flags & CLK_NODE_CANNOT_STOP) == 0)) {
1146be82b3a0SEmmanuel Vadot rv = CLKNODE_SET_GATE(clknode, 0);
1147be82b3a0SEmmanuel Vadot if (rv != 0) {
1148be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
1149be82b3a0SEmmanuel Vadot return (rv);
1150be82b3a0SEmmanuel Vadot }
1151be82b3a0SEmmanuel Vadot }
1152be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
1153be82b3a0SEmmanuel Vadot
1154be82b3a0SEmmanuel Vadot if (clknode->parent_cnt > 0)
1155be82b3a0SEmmanuel Vadot rv = clknode_stop(clknode->parent, depth + 1);
1156be82b3a0SEmmanuel Vadot return (rv);
1157be82b3a0SEmmanuel Vadot }
1158be82b3a0SEmmanuel Vadot
1159be82b3a0SEmmanuel Vadot /* --------------------------------------------------------------------------
1160be82b3a0SEmmanuel Vadot *
1161be82b3a0SEmmanuel Vadot * Clock consumers interface.
1162be82b3a0SEmmanuel Vadot *
1163be82b3a0SEmmanuel Vadot */
1164be82b3a0SEmmanuel Vadot /* Helper function for clk_get*() */
1165be82b3a0SEmmanuel Vadot static clk_t
clk_create(struct clknode * clknode,device_t dev)1166be82b3a0SEmmanuel Vadot clk_create(struct clknode *clknode, device_t dev)
1167be82b3a0SEmmanuel Vadot {
1168be82b3a0SEmmanuel Vadot struct clk *clk;
1169be82b3a0SEmmanuel Vadot
1170be82b3a0SEmmanuel Vadot CLK_TOPO_ASSERT();
1171be82b3a0SEmmanuel Vadot
1172be82b3a0SEmmanuel Vadot clk = malloc(sizeof(struct clk), M_CLOCK, M_WAITOK);
1173be82b3a0SEmmanuel Vadot clk->dev = dev;
1174be82b3a0SEmmanuel Vadot clk->clknode = clknode;
1175be82b3a0SEmmanuel Vadot clk->enable_cnt = 0;
1176be82b3a0SEmmanuel Vadot clknode->ref_cnt++;
1177be82b3a0SEmmanuel Vadot
1178be82b3a0SEmmanuel Vadot return (clk);
1179be82b3a0SEmmanuel Vadot }
1180be82b3a0SEmmanuel Vadot
1181be82b3a0SEmmanuel Vadot int
clk_get_freq(clk_t clk,uint64_t * freq)1182be82b3a0SEmmanuel Vadot clk_get_freq(clk_t clk, uint64_t *freq)
1183be82b3a0SEmmanuel Vadot {
1184be82b3a0SEmmanuel Vadot int rv;
1185be82b3a0SEmmanuel Vadot struct clknode *clknode;
1186be82b3a0SEmmanuel Vadot
1187be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1188be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1189be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1190be82b3a0SEmmanuel Vadot
1191be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1192be82b3a0SEmmanuel Vadot rv = clknode_get_freq(clknode, freq);
1193be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1194be82b3a0SEmmanuel Vadot return (rv);
1195be82b3a0SEmmanuel Vadot }
1196be82b3a0SEmmanuel Vadot
1197be82b3a0SEmmanuel Vadot int
clk_set_freq(clk_t clk,uint64_t freq,int flags)1198be82b3a0SEmmanuel Vadot clk_set_freq(clk_t clk, uint64_t freq, int flags)
1199be82b3a0SEmmanuel Vadot {
1200be82b3a0SEmmanuel Vadot int rv;
1201be82b3a0SEmmanuel Vadot struct clknode *clknode;
1202be82b3a0SEmmanuel Vadot
1203be82b3a0SEmmanuel Vadot flags &= CLK_SET_USER_MASK;
1204be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1205be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1206be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1207be82b3a0SEmmanuel Vadot
1208be82b3a0SEmmanuel Vadot CLK_TOPO_XLOCK();
1209be82b3a0SEmmanuel Vadot rv = clknode_set_freq(clknode, freq, flags, clk->enable_cnt);
1210be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1211be82b3a0SEmmanuel Vadot return (rv);
1212be82b3a0SEmmanuel Vadot }
1213be82b3a0SEmmanuel Vadot
1214be82b3a0SEmmanuel Vadot int
clk_test_freq(clk_t clk,uint64_t freq,int flags)1215be82b3a0SEmmanuel Vadot clk_test_freq(clk_t clk, uint64_t freq, int flags)
1216be82b3a0SEmmanuel Vadot {
1217be82b3a0SEmmanuel Vadot int rv;
1218be82b3a0SEmmanuel Vadot struct clknode *clknode;
1219be82b3a0SEmmanuel Vadot
1220be82b3a0SEmmanuel Vadot flags &= CLK_SET_USER_MASK;
1221be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1222be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1223be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1224be82b3a0SEmmanuel Vadot
1225be82b3a0SEmmanuel Vadot CLK_TOPO_XLOCK();
1226be82b3a0SEmmanuel Vadot rv = clknode_set_freq(clknode, freq, flags | CLK_SET_DRYRUN, 0);
1227be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1228be82b3a0SEmmanuel Vadot return (rv);
1229be82b3a0SEmmanuel Vadot }
1230be82b3a0SEmmanuel Vadot
1231be82b3a0SEmmanuel Vadot int
clk_get_parent(clk_t clk,clk_t * parent)1232be82b3a0SEmmanuel Vadot clk_get_parent(clk_t clk, clk_t *parent)
1233be82b3a0SEmmanuel Vadot {
1234be82b3a0SEmmanuel Vadot struct clknode *clknode;
1235be82b3a0SEmmanuel Vadot struct clknode *parentnode;
1236be82b3a0SEmmanuel Vadot
1237be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1238be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1239be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1240be82b3a0SEmmanuel Vadot
1241be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1242be82b3a0SEmmanuel Vadot parentnode = clknode_get_parent(clknode);
1243be82b3a0SEmmanuel Vadot if (parentnode == NULL) {
1244be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1245be82b3a0SEmmanuel Vadot return (ENODEV);
1246be82b3a0SEmmanuel Vadot }
1247be82b3a0SEmmanuel Vadot *parent = clk_create(parentnode, clk->dev);
1248be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1249be82b3a0SEmmanuel Vadot return (0);
1250be82b3a0SEmmanuel Vadot }
1251be82b3a0SEmmanuel Vadot
1252be82b3a0SEmmanuel Vadot int
clk_set_parent_by_clk(clk_t clk,clk_t parent)1253be82b3a0SEmmanuel Vadot clk_set_parent_by_clk(clk_t clk, clk_t parent)
1254be82b3a0SEmmanuel Vadot {
1255be82b3a0SEmmanuel Vadot int rv;
1256be82b3a0SEmmanuel Vadot struct clknode *clknode;
1257be82b3a0SEmmanuel Vadot struct clknode *parentnode;
1258be82b3a0SEmmanuel Vadot
1259be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1260be82b3a0SEmmanuel Vadot parentnode = parent->clknode;
1261be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1262be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1263be82b3a0SEmmanuel Vadot KASSERT(parentnode->ref_cnt > 0,
1264be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1265be82b3a0SEmmanuel Vadot CLK_TOPO_XLOCK();
1266be82b3a0SEmmanuel Vadot rv = clknode_set_parent_by_name(clknode, parentnode->name);
1267be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1268be82b3a0SEmmanuel Vadot return (rv);
1269be82b3a0SEmmanuel Vadot }
1270be82b3a0SEmmanuel Vadot
1271be82b3a0SEmmanuel Vadot int
clk_enable(clk_t clk)1272be82b3a0SEmmanuel Vadot clk_enable(clk_t clk)
1273be82b3a0SEmmanuel Vadot {
1274be82b3a0SEmmanuel Vadot int rv;
1275be82b3a0SEmmanuel Vadot struct clknode *clknode;
1276be82b3a0SEmmanuel Vadot
1277be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1278be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1279be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1280be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1281be82b3a0SEmmanuel Vadot rv = clknode_enable(clknode);
1282be82b3a0SEmmanuel Vadot if (rv == 0)
1283be82b3a0SEmmanuel Vadot clk->enable_cnt++;
1284be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1285be82b3a0SEmmanuel Vadot return (rv);
1286be82b3a0SEmmanuel Vadot }
1287be82b3a0SEmmanuel Vadot
1288be82b3a0SEmmanuel Vadot int
clk_disable(clk_t clk)1289be82b3a0SEmmanuel Vadot clk_disable(clk_t clk)
1290be82b3a0SEmmanuel Vadot {
1291be82b3a0SEmmanuel Vadot int rv;
1292be82b3a0SEmmanuel Vadot struct clknode *clknode;
1293be82b3a0SEmmanuel Vadot
1294be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1295be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1296be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1297be82b3a0SEmmanuel Vadot KASSERT(clk->enable_cnt > 0,
1298be82b3a0SEmmanuel Vadot ("Attempt to disable already disabled clock: %s\n", clknode->name));
1299be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1300be82b3a0SEmmanuel Vadot rv = clknode_disable(clknode);
1301be82b3a0SEmmanuel Vadot if (rv == 0)
1302be82b3a0SEmmanuel Vadot clk->enable_cnt--;
1303be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1304be82b3a0SEmmanuel Vadot return (rv);
1305be82b3a0SEmmanuel Vadot }
1306be82b3a0SEmmanuel Vadot
1307be82b3a0SEmmanuel Vadot int
clk_stop(clk_t clk)1308be82b3a0SEmmanuel Vadot clk_stop(clk_t clk)
1309be82b3a0SEmmanuel Vadot {
1310be82b3a0SEmmanuel Vadot int rv;
1311be82b3a0SEmmanuel Vadot struct clknode *clknode;
1312be82b3a0SEmmanuel Vadot
1313be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1314be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1315be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1316be82b3a0SEmmanuel Vadot KASSERT(clk->enable_cnt == 0,
1317be82b3a0SEmmanuel Vadot ("Attempt to stop already enabled clock: %s\n", clknode->name));
1318be82b3a0SEmmanuel Vadot
1319be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1320be82b3a0SEmmanuel Vadot rv = clknode_stop(clknode, 0);
1321be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1322be82b3a0SEmmanuel Vadot return (rv);
1323be82b3a0SEmmanuel Vadot }
1324be82b3a0SEmmanuel Vadot
1325be82b3a0SEmmanuel Vadot int
clk_release(clk_t clk)1326be82b3a0SEmmanuel Vadot clk_release(clk_t clk)
1327be82b3a0SEmmanuel Vadot {
1328be82b3a0SEmmanuel Vadot struct clknode *clknode;
1329be82b3a0SEmmanuel Vadot
1330be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1331be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1332be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1333be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1334be82b3a0SEmmanuel Vadot while (clk->enable_cnt > 0) {
1335be82b3a0SEmmanuel Vadot clknode_disable(clknode);
1336be82b3a0SEmmanuel Vadot clk->enable_cnt--;
1337be82b3a0SEmmanuel Vadot }
1338be82b3a0SEmmanuel Vadot CLKNODE_XLOCK(clknode);
1339be82b3a0SEmmanuel Vadot clknode->ref_cnt--;
1340be82b3a0SEmmanuel Vadot CLKNODE_UNLOCK(clknode);
1341be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1342be82b3a0SEmmanuel Vadot
1343be82b3a0SEmmanuel Vadot free(clk, M_CLOCK);
1344be82b3a0SEmmanuel Vadot return (0);
1345be82b3a0SEmmanuel Vadot }
1346be82b3a0SEmmanuel Vadot
1347be82b3a0SEmmanuel Vadot const char *
clk_get_name(clk_t clk)1348be82b3a0SEmmanuel Vadot clk_get_name(clk_t clk)
1349be82b3a0SEmmanuel Vadot {
1350be82b3a0SEmmanuel Vadot const char *name;
1351be82b3a0SEmmanuel Vadot struct clknode *clknode;
1352be82b3a0SEmmanuel Vadot
1353be82b3a0SEmmanuel Vadot clknode = clk->clknode;
1354be82b3a0SEmmanuel Vadot KASSERT(clknode->ref_cnt > 0,
1355be82b3a0SEmmanuel Vadot ("Attempt to access unreferenced clock: %s\n", clknode->name));
1356be82b3a0SEmmanuel Vadot name = clknode_get_name(clknode);
1357be82b3a0SEmmanuel Vadot return (name);
1358be82b3a0SEmmanuel Vadot }
1359be82b3a0SEmmanuel Vadot
1360be82b3a0SEmmanuel Vadot int
clk_get_by_name(device_t dev,const char * name,clk_t * clk)1361be82b3a0SEmmanuel Vadot clk_get_by_name(device_t dev, const char *name, clk_t *clk)
1362be82b3a0SEmmanuel Vadot {
1363be82b3a0SEmmanuel Vadot struct clknode *clknode;
1364be82b3a0SEmmanuel Vadot
1365be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1366be82b3a0SEmmanuel Vadot clknode = clknode_find_by_name(name);
1367be82b3a0SEmmanuel Vadot if (clknode == NULL) {
1368be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1369be82b3a0SEmmanuel Vadot return (ENODEV);
1370be82b3a0SEmmanuel Vadot }
1371be82b3a0SEmmanuel Vadot *clk = clk_create(clknode, dev);
1372be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1373be82b3a0SEmmanuel Vadot return (0);
1374be82b3a0SEmmanuel Vadot }
1375be82b3a0SEmmanuel Vadot
1376be82b3a0SEmmanuel Vadot int
clk_get_by_id(device_t dev,struct clkdom * clkdom,intptr_t id,clk_t * clk)1377be82b3a0SEmmanuel Vadot clk_get_by_id(device_t dev, struct clkdom *clkdom, intptr_t id, clk_t *clk)
1378be82b3a0SEmmanuel Vadot {
1379be82b3a0SEmmanuel Vadot struct clknode *clknode;
1380be82b3a0SEmmanuel Vadot
1381be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1382be82b3a0SEmmanuel Vadot
1383be82b3a0SEmmanuel Vadot clknode = clknode_find_by_id(clkdom, id);
1384be82b3a0SEmmanuel Vadot if (clknode == NULL) {
1385be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1386be82b3a0SEmmanuel Vadot return (ENODEV);
1387be82b3a0SEmmanuel Vadot }
1388be82b3a0SEmmanuel Vadot *clk = clk_create(clknode, dev);
1389be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1390be82b3a0SEmmanuel Vadot
1391be82b3a0SEmmanuel Vadot return (0);
1392be82b3a0SEmmanuel Vadot }
1393be82b3a0SEmmanuel Vadot
1394be82b3a0SEmmanuel Vadot #ifdef FDT
1395be82b3a0SEmmanuel Vadot
1396be82b3a0SEmmanuel Vadot static void
clk_set_assigned_parent(device_t dev,clk_t clk,int idx)1397be82b3a0SEmmanuel Vadot clk_set_assigned_parent(device_t dev, clk_t clk, int idx)
1398be82b3a0SEmmanuel Vadot {
1399be82b3a0SEmmanuel Vadot clk_t parent;
1400be82b3a0SEmmanuel Vadot const char *pname;
1401be82b3a0SEmmanuel Vadot int rv;
1402be82b3a0SEmmanuel Vadot
1403be82b3a0SEmmanuel Vadot rv = clk_get_by_ofw_index_prop(dev, 0,
1404be82b3a0SEmmanuel Vadot "assigned-clock-parents", idx, &parent);
1405be82b3a0SEmmanuel Vadot if (rv != 0) {
1406be82b3a0SEmmanuel Vadot device_printf(dev,
1407be82b3a0SEmmanuel Vadot "cannot get parent at idx %d\n", idx);
1408be82b3a0SEmmanuel Vadot return;
1409be82b3a0SEmmanuel Vadot }
1410be82b3a0SEmmanuel Vadot
1411be82b3a0SEmmanuel Vadot pname = clk_get_name(parent);
1412be82b3a0SEmmanuel Vadot rv = clk_set_parent_by_clk(clk, parent);
1413be82b3a0SEmmanuel Vadot if (rv != 0)
1414be82b3a0SEmmanuel Vadot device_printf(dev,
1415be82b3a0SEmmanuel Vadot "Cannot set parent %s for clock %s\n",
1416be82b3a0SEmmanuel Vadot pname, clk_get_name(clk));
1417be82b3a0SEmmanuel Vadot else if (bootverbose)
1418be82b3a0SEmmanuel Vadot device_printf(dev, "Set %s as the parent of %s\n",
1419be82b3a0SEmmanuel Vadot pname, clk_get_name(clk));
1420be82b3a0SEmmanuel Vadot clk_release(parent);
1421be82b3a0SEmmanuel Vadot }
1422be82b3a0SEmmanuel Vadot
1423be82b3a0SEmmanuel Vadot static void
clk_set_assigned_rates(device_t dev,clk_t clk,uint32_t freq)1424be82b3a0SEmmanuel Vadot clk_set_assigned_rates(device_t dev, clk_t clk, uint32_t freq)
1425be82b3a0SEmmanuel Vadot {
1426be82b3a0SEmmanuel Vadot int rv;
1427be82b3a0SEmmanuel Vadot
1428be82b3a0SEmmanuel Vadot rv = clk_set_freq(clk, freq, CLK_SET_ROUND_DOWN | CLK_SET_ROUND_UP);
1429be82b3a0SEmmanuel Vadot if (rv != 0) {
1430be82b3a0SEmmanuel Vadot device_printf(dev, "Failed to set %s to a frequency of %u\n",
1431be82b3a0SEmmanuel Vadot clk_get_name(clk), freq);
1432be82b3a0SEmmanuel Vadot return;
1433be82b3a0SEmmanuel Vadot }
1434be82b3a0SEmmanuel Vadot if (bootverbose)
1435be82b3a0SEmmanuel Vadot device_printf(dev, "Set %s to %u\n",
1436be82b3a0SEmmanuel Vadot clk_get_name(clk), freq);
1437be82b3a0SEmmanuel Vadot }
1438be82b3a0SEmmanuel Vadot
1439be82b3a0SEmmanuel Vadot int
clk_set_assigned(device_t dev,phandle_t node)1440be82b3a0SEmmanuel Vadot clk_set_assigned(device_t dev, phandle_t node)
1441be82b3a0SEmmanuel Vadot {
1442be82b3a0SEmmanuel Vadot clk_t clk;
1443be82b3a0SEmmanuel Vadot uint32_t *rates;
1444be82b3a0SEmmanuel Vadot int rv, nclocks, nrates, nparents, i;
1445be82b3a0SEmmanuel Vadot
1446be82b3a0SEmmanuel Vadot rv = ofw_bus_parse_xref_list_get_length(node,
1447be82b3a0SEmmanuel Vadot "assigned-clocks", "#clock-cells", &nclocks);
1448be82b3a0SEmmanuel Vadot
1449be82b3a0SEmmanuel Vadot if (rv != 0) {
1450be82b3a0SEmmanuel Vadot if (rv != ENOENT)
1451be82b3a0SEmmanuel Vadot device_printf(dev,
1452be82b3a0SEmmanuel Vadot "cannot parse assigned-clock property\n");
1453be82b3a0SEmmanuel Vadot return (rv);
1454be82b3a0SEmmanuel Vadot }
1455be82b3a0SEmmanuel Vadot
1456be82b3a0SEmmanuel Vadot nrates = OF_getencprop_alloc_multi(node, "assigned-clock-rates",
1457be82b3a0SEmmanuel Vadot sizeof(*rates), (void **)&rates);
1458be82b3a0SEmmanuel Vadot if (nrates <= 0)
1459be82b3a0SEmmanuel Vadot nrates = 0;
1460be82b3a0SEmmanuel Vadot
1461be82b3a0SEmmanuel Vadot if (ofw_bus_parse_xref_list_get_length(node,
1462be82b3a0SEmmanuel Vadot "assigned-clock-parents", "#clock-cells", &nparents) != 0)
1463be82b3a0SEmmanuel Vadot nparents = -1;
1464be82b3a0SEmmanuel Vadot for (i = 0; i < nclocks; i++) {
1465be82b3a0SEmmanuel Vadot /* First get the clock we are supposed to modify */
1466be82b3a0SEmmanuel Vadot rv = clk_get_by_ofw_index_prop(dev, 0, "assigned-clocks",
1467be82b3a0SEmmanuel Vadot i, &clk);
1468be82b3a0SEmmanuel Vadot if (rv != 0) {
1469be82b3a0SEmmanuel Vadot if (bootverbose)
1470be82b3a0SEmmanuel Vadot device_printf(dev,
1471be82b3a0SEmmanuel Vadot "cannot get assigned clock at idx %d\n",
1472be82b3a0SEmmanuel Vadot i);
1473be82b3a0SEmmanuel Vadot continue;
1474be82b3a0SEmmanuel Vadot }
1475be82b3a0SEmmanuel Vadot
1476be82b3a0SEmmanuel Vadot /* First set it's parent if needed */
1477be82b3a0SEmmanuel Vadot if (i < nparents)
1478be82b3a0SEmmanuel Vadot clk_set_assigned_parent(dev, clk, i);
1479be82b3a0SEmmanuel Vadot
1480be82b3a0SEmmanuel Vadot /* Then set a new frequency */
1481be82b3a0SEmmanuel Vadot if (i < nrates && rates[i] != 0)
1482be82b3a0SEmmanuel Vadot clk_set_assigned_rates(dev, clk, rates[i]);
1483be82b3a0SEmmanuel Vadot
1484be82b3a0SEmmanuel Vadot clk_release(clk);
1485be82b3a0SEmmanuel Vadot }
1486be82b3a0SEmmanuel Vadot if (rates != NULL)
1487be82b3a0SEmmanuel Vadot OF_prop_free(rates);
1488be82b3a0SEmmanuel Vadot
1489be82b3a0SEmmanuel Vadot return (0);
1490be82b3a0SEmmanuel Vadot }
1491be82b3a0SEmmanuel Vadot
1492be82b3a0SEmmanuel Vadot int
clk_get_by_ofw_index_prop(device_t dev,phandle_t cnode,const char * prop,int idx,clk_t * clk)1493be82b3a0SEmmanuel Vadot clk_get_by_ofw_index_prop(device_t dev, phandle_t cnode, const char *prop, int idx, clk_t *clk)
1494be82b3a0SEmmanuel Vadot {
1495be82b3a0SEmmanuel Vadot phandle_t parent, *cells;
1496be82b3a0SEmmanuel Vadot device_t clockdev;
1497be82b3a0SEmmanuel Vadot int ncells, rv;
1498be82b3a0SEmmanuel Vadot struct clkdom *clkdom;
1499be82b3a0SEmmanuel Vadot struct clknode *clknode;
1500be82b3a0SEmmanuel Vadot
1501be82b3a0SEmmanuel Vadot *clk = NULL;
1502be82b3a0SEmmanuel Vadot if (cnode <= 0)
1503be82b3a0SEmmanuel Vadot cnode = ofw_bus_get_node(dev);
1504be82b3a0SEmmanuel Vadot if (cnode <= 0) {
1505be82b3a0SEmmanuel Vadot device_printf(dev, "%s called on not ofw based device\n",
1506be82b3a0SEmmanuel Vadot __func__);
1507be82b3a0SEmmanuel Vadot return (ENXIO);
1508be82b3a0SEmmanuel Vadot }
1509be82b3a0SEmmanuel Vadot
1510be82b3a0SEmmanuel Vadot
1511be82b3a0SEmmanuel Vadot rv = ofw_bus_parse_xref_list_alloc(cnode, prop, "#clock-cells", idx,
1512be82b3a0SEmmanuel Vadot &parent, &ncells, &cells);
1513be82b3a0SEmmanuel Vadot if (rv != 0) {
1514be82b3a0SEmmanuel Vadot return (rv);
1515be82b3a0SEmmanuel Vadot }
1516be82b3a0SEmmanuel Vadot
1517be82b3a0SEmmanuel Vadot clockdev = OF_device_from_xref(parent);
1518be82b3a0SEmmanuel Vadot if (clockdev == NULL) {
1519be82b3a0SEmmanuel Vadot rv = ENODEV;
1520be82b3a0SEmmanuel Vadot goto done;
1521be82b3a0SEmmanuel Vadot }
1522be82b3a0SEmmanuel Vadot
1523be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1524be82b3a0SEmmanuel Vadot clkdom = clkdom_get_by_dev(clockdev);
1525be82b3a0SEmmanuel Vadot if (clkdom == NULL){
1526be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1527be82b3a0SEmmanuel Vadot rv = ENXIO;
1528be82b3a0SEmmanuel Vadot goto done;
1529be82b3a0SEmmanuel Vadot }
1530be82b3a0SEmmanuel Vadot
1531be82b3a0SEmmanuel Vadot rv = clkdom->ofw_mapper(clkdom, ncells, cells, &clknode);
1532be82b3a0SEmmanuel Vadot if (rv == 0) {
1533be82b3a0SEmmanuel Vadot *clk = clk_create(clknode, dev);
1534be82b3a0SEmmanuel Vadot }
1535be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1536be82b3a0SEmmanuel Vadot
1537be82b3a0SEmmanuel Vadot done:
1538be82b3a0SEmmanuel Vadot if (cells != NULL)
1539be82b3a0SEmmanuel Vadot OF_prop_free(cells);
1540be82b3a0SEmmanuel Vadot return (rv);
1541be82b3a0SEmmanuel Vadot }
1542be82b3a0SEmmanuel Vadot
1543be82b3a0SEmmanuel Vadot int
clk_get_by_ofw_index(device_t dev,phandle_t cnode,int idx,clk_t * clk)1544be82b3a0SEmmanuel Vadot clk_get_by_ofw_index(device_t dev, phandle_t cnode, int idx, clk_t *clk)
1545be82b3a0SEmmanuel Vadot {
1546be82b3a0SEmmanuel Vadot return (clk_get_by_ofw_index_prop(dev, cnode, "clocks", idx, clk));
1547be82b3a0SEmmanuel Vadot }
1548be82b3a0SEmmanuel Vadot
1549be82b3a0SEmmanuel Vadot int
clk_get_by_ofw_name(device_t dev,phandle_t cnode,const char * name,clk_t * clk)1550be82b3a0SEmmanuel Vadot clk_get_by_ofw_name(device_t dev, phandle_t cnode, const char *name, clk_t *clk)
1551be82b3a0SEmmanuel Vadot {
1552be82b3a0SEmmanuel Vadot int rv, idx;
1553be82b3a0SEmmanuel Vadot
1554be82b3a0SEmmanuel Vadot if (cnode <= 0)
1555be82b3a0SEmmanuel Vadot cnode = ofw_bus_get_node(dev);
1556be82b3a0SEmmanuel Vadot if (cnode <= 0) {
1557be82b3a0SEmmanuel Vadot device_printf(dev, "%s called on not ofw based device\n",
1558be82b3a0SEmmanuel Vadot __func__);
1559be82b3a0SEmmanuel Vadot return (ENXIO);
1560be82b3a0SEmmanuel Vadot }
1561be82b3a0SEmmanuel Vadot rv = ofw_bus_find_string_index(cnode, "clock-names", name, &idx);
1562be82b3a0SEmmanuel Vadot if (rv != 0)
1563be82b3a0SEmmanuel Vadot return (rv);
1564be82b3a0SEmmanuel Vadot return (clk_get_by_ofw_index(dev, cnode, idx, clk));
1565be82b3a0SEmmanuel Vadot }
1566be82b3a0SEmmanuel Vadot
1567be82b3a0SEmmanuel Vadot /* --------------------------------------------------------------------------
1568be82b3a0SEmmanuel Vadot *
1569be82b3a0SEmmanuel Vadot * Support functions for parsing various clock related OFW things.
1570be82b3a0SEmmanuel Vadot */
1571be82b3a0SEmmanuel Vadot
1572be82b3a0SEmmanuel Vadot /*
1573be82b3a0SEmmanuel Vadot * Get "clock-output-names" and (optional) "clock-indices" lists.
1574be82b3a0SEmmanuel Vadot * Both lists are allocated using M_OFWPROP specifier.
1575be82b3a0SEmmanuel Vadot *
1576be82b3a0SEmmanuel Vadot * Returns number of items or 0.
1577be82b3a0SEmmanuel Vadot */
1578be82b3a0SEmmanuel Vadot int
clk_parse_ofw_out_names(device_t dev,phandle_t node,const char *** out_names,uint32_t ** indices)1579be82b3a0SEmmanuel Vadot clk_parse_ofw_out_names(device_t dev, phandle_t node, const char ***out_names,
1580be82b3a0SEmmanuel Vadot uint32_t **indices)
1581be82b3a0SEmmanuel Vadot {
1582be82b3a0SEmmanuel Vadot int name_items, rv;
1583be82b3a0SEmmanuel Vadot
1584be82b3a0SEmmanuel Vadot *out_names = NULL;
1585be82b3a0SEmmanuel Vadot *indices = NULL;
1586be82b3a0SEmmanuel Vadot if (!OF_hasprop(node, "clock-output-names"))
1587be82b3a0SEmmanuel Vadot return (0);
1588be82b3a0SEmmanuel Vadot rv = ofw_bus_string_list_to_array(node, "clock-output-names",
1589be82b3a0SEmmanuel Vadot out_names);
1590be82b3a0SEmmanuel Vadot if (rv <= 0)
1591be82b3a0SEmmanuel Vadot return (0);
1592be82b3a0SEmmanuel Vadot name_items = rv;
1593be82b3a0SEmmanuel Vadot
1594be82b3a0SEmmanuel Vadot if (!OF_hasprop(node, "clock-indices"))
1595be82b3a0SEmmanuel Vadot return (name_items);
1596be82b3a0SEmmanuel Vadot rv = OF_getencprop_alloc_multi(node, "clock-indices", sizeof (uint32_t),
1597be82b3a0SEmmanuel Vadot (void **)indices);
1598be82b3a0SEmmanuel Vadot if (rv != name_items) {
1599be82b3a0SEmmanuel Vadot device_printf(dev, " Size of 'clock-output-names' and "
1600be82b3a0SEmmanuel Vadot "'clock-indices' differs\n");
1601be82b3a0SEmmanuel Vadot OF_prop_free(*out_names);
1602be82b3a0SEmmanuel Vadot OF_prop_free(*indices);
1603be82b3a0SEmmanuel Vadot return (0);
1604be82b3a0SEmmanuel Vadot }
1605be82b3a0SEmmanuel Vadot return (name_items);
1606be82b3a0SEmmanuel Vadot }
1607be82b3a0SEmmanuel Vadot
1608be82b3a0SEmmanuel Vadot /*
1609be82b3a0SEmmanuel Vadot * Get output clock name for single output clock node.
1610be82b3a0SEmmanuel Vadot */
1611be82b3a0SEmmanuel Vadot int
clk_parse_ofw_clk_name(device_t dev,phandle_t node,const char ** name)1612be82b3a0SEmmanuel Vadot clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name)
1613be82b3a0SEmmanuel Vadot {
1614be82b3a0SEmmanuel Vadot const char **out_names;
1615be82b3a0SEmmanuel Vadot const char *tmp_name;
1616be82b3a0SEmmanuel Vadot int rv;
1617be82b3a0SEmmanuel Vadot
1618be82b3a0SEmmanuel Vadot *name = NULL;
1619be82b3a0SEmmanuel Vadot if (!OF_hasprop(node, "clock-output-names")) {
1620be82b3a0SEmmanuel Vadot tmp_name = ofw_bus_get_name(dev);
1621be82b3a0SEmmanuel Vadot if (tmp_name == NULL)
1622be82b3a0SEmmanuel Vadot return (ENXIO);
1623be82b3a0SEmmanuel Vadot *name = strdup(tmp_name, M_OFWPROP);
1624be82b3a0SEmmanuel Vadot return (0);
1625be82b3a0SEmmanuel Vadot }
1626be82b3a0SEmmanuel Vadot rv = ofw_bus_string_list_to_array(node, "clock-output-names",
1627be82b3a0SEmmanuel Vadot &out_names);
1628be82b3a0SEmmanuel Vadot if (rv != 1) {
1629be82b3a0SEmmanuel Vadot OF_prop_free(out_names);
1630be82b3a0SEmmanuel Vadot device_printf(dev, "Malformed 'clock-output-names' property\n");
1631be82b3a0SEmmanuel Vadot return (ENXIO);
1632be82b3a0SEmmanuel Vadot }
1633be82b3a0SEmmanuel Vadot *name = strdup(out_names[0], M_OFWPROP);
1634be82b3a0SEmmanuel Vadot OF_prop_free(out_names);
1635be82b3a0SEmmanuel Vadot return (0);
1636be82b3a0SEmmanuel Vadot }
1637be82b3a0SEmmanuel Vadot #endif
1638be82b3a0SEmmanuel Vadot
1639be82b3a0SEmmanuel Vadot static int
clkdom_sysctl(SYSCTL_HANDLER_ARGS)1640be82b3a0SEmmanuel Vadot clkdom_sysctl(SYSCTL_HANDLER_ARGS)
1641be82b3a0SEmmanuel Vadot {
1642be82b3a0SEmmanuel Vadot struct clkdom *clkdom = arg1;
1643be82b3a0SEmmanuel Vadot struct clknode *clknode;
1644be82b3a0SEmmanuel Vadot struct sbuf *sb;
1645be82b3a0SEmmanuel Vadot int ret;
1646be82b3a0SEmmanuel Vadot
1647be82b3a0SEmmanuel Vadot sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
1648be82b3a0SEmmanuel Vadot if (sb == NULL)
1649be82b3a0SEmmanuel Vadot return (ENOMEM);
1650be82b3a0SEmmanuel Vadot
1651be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1652be82b3a0SEmmanuel Vadot TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
1653be82b3a0SEmmanuel Vadot sbuf_printf(sb, "%s ", clknode->name);
1654be82b3a0SEmmanuel Vadot }
1655be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1656be82b3a0SEmmanuel Vadot
1657be82b3a0SEmmanuel Vadot ret = sbuf_finish(sb);
1658be82b3a0SEmmanuel Vadot sbuf_delete(sb);
1659be82b3a0SEmmanuel Vadot return (ret);
1660be82b3a0SEmmanuel Vadot }
1661be82b3a0SEmmanuel Vadot
1662be82b3a0SEmmanuel Vadot static int
clknode_sysctl(SYSCTL_HANDLER_ARGS)1663be82b3a0SEmmanuel Vadot clknode_sysctl(SYSCTL_HANDLER_ARGS)
1664be82b3a0SEmmanuel Vadot {
1665be82b3a0SEmmanuel Vadot struct clknode *clknode, *children;
1666be82b3a0SEmmanuel Vadot enum clknode_sysctl_type type = arg2;
1667be82b3a0SEmmanuel Vadot struct sbuf *sb;
1668be82b3a0SEmmanuel Vadot const char **parent_names;
1669be82b3a0SEmmanuel Vadot uint64_t freq;
1670be82b3a0SEmmanuel Vadot bool enable;
1671be82b3a0SEmmanuel Vadot int ret, i;
1672be82b3a0SEmmanuel Vadot
1673be82b3a0SEmmanuel Vadot clknode = arg1;
1674be82b3a0SEmmanuel Vadot sb = sbuf_new_for_sysctl(NULL, NULL, 512, req);
1675be82b3a0SEmmanuel Vadot if (sb == NULL)
1676be82b3a0SEmmanuel Vadot return (ENOMEM);
1677be82b3a0SEmmanuel Vadot
1678be82b3a0SEmmanuel Vadot CLK_TOPO_SLOCK();
1679be82b3a0SEmmanuel Vadot switch (type) {
1680be82b3a0SEmmanuel Vadot case CLKNODE_SYSCTL_PARENT:
1681be82b3a0SEmmanuel Vadot if (clknode->parent)
1682be82b3a0SEmmanuel Vadot sbuf_printf(sb, "%s", clknode->parent->name);
1683be82b3a0SEmmanuel Vadot break;
1684be82b3a0SEmmanuel Vadot case CLKNODE_SYSCTL_PARENTS_LIST:
1685be82b3a0SEmmanuel Vadot parent_names = clknode_get_parent_names(clknode);
1686be82b3a0SEmmanuel Vadot for (i = 0; i < clknode->parent_cnt; i++)
1687be82b3a0SEmmanuel Vadot sbuf_printf(sb, "%s ", parent_names[i]);
1688be82b3a0SEmmanuel Vadot break;
1689be82b3a0SEmmanuel Vadot case CLKNODE_SYSCTL_CHILDREN_LIST:
1690be82b3a0SEmmanuel Vadot TAILQ_FOREACH(children, &(clknode->children), sibling_link) {
1691be82b3a0SEmmanuel Vadot sbuf_printf(sb, "%s ", children->name);
1692be82b3a0SEmmanuel Vadot }
1693be82b3a0SEmmanuel Vadot break;
1694be82b3a0SEmmanuel Vadot case CLKNODE_SYSCTL_FREQUENCY:
1695be82b3a0SEmmanuel Vadot ret = clknode_get_freq(clknode, &freq);
1696be82b3a0SEmmanuel Vadot if (ret == 0)
1697be82b3a0SEmmanuel Vadot sbuf_printf(sb, "%ju ", (uintmax_t)freq);
1698be82b3a0SEmmanuel Vadot else
1699be82b3a0SEmmanuel Vadot sbuf_printf(sb, "Error: %d ", ret);
1700be82b3a0SEmmanuel Vadot break;
1701be82b3a0SEmmanuel Vadot case CLKNODE_SYSCTL_GATE:
1702be82b3a0SEmmanuel Vadot ret = CLKNODE_GET_GATE(clknode, &enable);
1703be82b3a0SEmmanuel Vadot if (ret == 0)
1704be82b3a0SEmmanuel Vadot sbuf_printf(sb, enable ? "enabled": "disabled");
1705be82b3a0SEmmanuel Vadot else if (ret == ENXIO)
1706be82b3a0SEmmanuel Vadot sbuf_printf(sb, "unimplemented");
1707be82b3a0SEmmanuel Vadot else if (ret == ENOENT)
1708be82b3a0SEmmanuel Vadot sbuf_printf(sb, "unreadable");
1709be82b3a0SEmmanuel Vadot else
1710be82b3a0SEmmanuel Vadot sbuf_printf(sb, "Error: %d ", ret);
1711be82b3a0SEmmanuel Vadot break;
1712be82b3a0SEmmanuel Vadot }
1713be82b3a0SEmmanuel Vadot CLK_TOPO_UNLOCK();
1714be82b3a0SEmmanuel Vadot
1715be82b3a0SEmmanuel Vadot ret = sbuf_finish(sb);
1716be82b3a0SEmmanuel Vadot sbuf_delete(sb);
1717be82b3a0SEmmanuel Vadot return (ret);
1718be82b3a0SEmmanuel Vadot }
1719