xref: /freebsd/sys/dev/clk/clk.c (revision 08635c51d1e34f8a3e42c7cf35dc7264a5b68118)
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