xref: /netbsd-src/sys/dev/clk/clk.c (revision 5244dd86794ff096eb8dfc93d85ab9bb4e1c957b)
1 /* $NetBSD: clk.c,v 1.8 2024/06/12 06:23:56 rin Exp $ */
2 
3 /*-
4  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: clk.c,v 1.8 2024/06/12 06:23:56 rin Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/sysctl.h>
34 #include <sys/kmem.h>
35 
36 #include <dev/clk/clk.h>
37 #include <dev/clk/clk_backend.h>
38 
39 static struct sysctllog *clk_log;
40 static const struct sysctlnode *clk_node;
41 
42 static int
create_clk_node(void)43 create_clk_node(void)
44 {
45 	const struct sysctlnode *hw_node;
46 	int error;
47 
48 	if (clk_node)
49 		return 0;
50 
51 	error = sysctl_createv(&clk_log, 0, NULL, &hw_node,
52 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
53 	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL);
54 	if (error)
55 		return error;
56 
57 	error = sysctl_createv(&clk_log, 0, &hw_node, &clk_node,
58 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "clk", NULL,
59 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
60 	if (error)
61 		return error;
62 
63 	return 0;
64 }
65 
66 static int
create_domain_node(struct clk_domain * domain)67 create_domain_node(struct clk_domain *domain)
68 {
69 	int error;
70 
71 	if (domain->node)
72 		return 0;
73 
74 	error = create_clk_node();
75 	if (error)
76 		return error;
77 
78 	error = sysctl_createv(&clk_log, 0, &clk_node, &domain->node,
79 	    0, CTLTYPE_NODE, domain->name, NULL,
80 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
81 	if (error)
82 		return error;
83 
84 	return 0;
85 }
86 
87 static int
clk_sysctl_rate_helper(SYSCTLFN_ARGS)88 clk_sysctl_rate_helper(SYSCTLFN_ARGS)
89 {
90 	struct sysctlnode node;
91 	struct clk *clk;
92 	uint64_t rate;
93 
94 	node = *rnode;
95 	clk = node.sysctl_data;
96 	node.sysctl_data = &rate;
97 
98 	rate = clk_get_rate(clk);
99 
100 	return sysctl_lookup(SYSCTLFN_CALL(&node));
101 }
102 
103 static int
clk_sysctl_parent_helper(SYSCTLFN_ARGS)104 clk_sysctl_parent_helper(SYSCTLFN_ARGS)
105 {
106 	struct sysctlnode node;
107 	struct clk *clk, *clk_parent;
108 
109 	node = *rnode;
110 	clk = node.sysctl_data;
111 
112 	clk_parent = clk_get_parent(clk);
113 	if (clk_parent && clk_parent->name)
114 		node.sysctl_data = __UNCONST(clk_parent->name);
115 	else
116 		node.sysctl_data = __UNCONST("?");
117 
118 	return sysctl_lookup(SYSCTLFN_CALL(&node));
119 }
120 
121 static int
clk_sysctl_parent_domain_helper(SYSCTLFN_ARGS)122 clk_sysctl_parent_domain_helper(SYSCTLFN_ARGS)
123 {
124 	struct sysctlnode node;
125 	struct clk *clk, *clk_parent;
126 
127 	node = *rnode;
128 	clk = node.sysctl_data;
129 
130 	clk_parent = clk_get_parent(clk);
131 	if (clk_parent && clk_parent->domain && clk_parent->domain->name)
132 		node.sysctl_data = __UNCONST(clk_parent->domain->name);
133 	else
134 		node.sysctl_data = __UNCONST("?");
135 
136 	return sysctl_lookup(SYSCTLFN_CALL(&node));
137 }
138 
139 static void
clk_normalize_name(char * name)140 clk_normalize_name(char *name)
141 {
142 	unsigned char *p;
143 
144 	for (p = (unsigned char *)name; *p; p++)
145 		if (!isalpha(*p) && !isdigit(*p) && *p != '-' && *p != '_')
146 			*p = '_';
147 }
148 
149 int
clk_attach(struct clk * clk)150 clk_attach(struct clk *clk)
151 {
152 	const struct sysctlnode *node;
153 	struct clk_domain *domain = clk->domain;
154 	char *name;
155 	size_t namelen;
156 	int error;
157 
158 	KASSERT(domain != NULL);
159 
160 	if (!domain->name || !clk->name) {
161 		/* Names are required to create sysctl nodes */
162 		return 0;
163 	}
164 
165 	namelen = strlen(clk->name) + 1;
166 	name = kmem_zalloc(namelen, KM_SLEEP);
167 	memcpy(name, clk->name, namelen);
168 	clk_normalize_name(name);
169 
170 	error = create_domain_node(domain);
171 	if (error != 0)
172 		goto sysctl_failed;
173 
174 	error = sysctl_createv(&clk_log, 0, &domain->node, &node,
175 	    CTLFLAG_PRIVATE, CTLTYPE_NODE, name, NULL,
176 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
177 	if (error)
178 		goto sysctl_failed;
179 
180 	error = sysctl_createv(&clk_log, 0, &node, NULL,
181 	    CTLFLAG_PRIVATE, CTLTYPE_QUAD, "rate", NULL,
182 	    clk_sysctl_rate_helper, 0, (void *)clk, 0,
183 	    CTL_CREATE, CTL_EOL);
184 	if (error)
185 		goto sysctl_failed;
186 
187 	error = sysctl_createv(&clk_log, 0, &node, NULL,
188 	    CTLFLAG_PRIVATE, CTLTYPE_STRING, "parent", NULL,
189 	    clk_sysctl_parent_helper, 0, (void *)clk, 0,
190 	    CTL_CREATE, CTL_EOL);
191 	if (error)
192 		goto sysctl_failed;
193 
194 	error = sysctl_createv(&clk_log, 0, &node, NULL,
195 	    CTLFLAG_PRIVATE, CTLTYPE_STRING, "parent_domain", NULL,
196 	    clk_sysctl_parent_domain_helper, 0, (void *)clk, 0,
197 	    CTL_CREATE, CTL_EOL);
198 	if (error)
199 		goto sysctl_failed;
200 
201 sysctl_failed:
202 	if (error)
203 		aprint_error("%s: failed to create sysctl node for %s (%s): %d\n",
204 		    domain->name, clk->name, name, error);
205 
206 	kmem_free(name, namelen);
207 	return error;
208 }
209 
210 struct clk *
clk_get(struct clk_domain * domain,const char * name)211 clk_get(struct clk_domain *domain, const char *name)
212 {
213 	return domain->funcs->get(domain->priv, name);
214 }
215 
216 void
clk_put(struct clk * clk)217 clk_put(struct clk *clk)
218 {
219 	if (clk->domain->funcs->put)
220 		clk->domain->funcs->put(clk->domain->priv, clk);
221 }
222 
223 u_int
clk_get_rate(struct clk * clk)224 clk_get_rate(struct clk *clk)
225 {
226 	return clk->domain->funcs->get_rate(clk->domain->priv, clk);
227 }
228 
229 int
clk_set_rate(struct clk * clk,u_int rate)230 clk_set_rate(struct clk *clk, u_int rate)
231 {
232 	KASSERT(clk != NULL);
233 
234 	if (clk->flags & CLK_SET_RATE_PARENT)
235 		return clk_set_rate(clk_get_parent(clk), rate);
236 
237 	if (clk->domain->funcs->set_rate)
238 		return clk->domain->funcs->set_rate(clk->domain->priv,
239 		    clk, rate);
240 
241 	if (clk_get_rate(clk) == rate)
242 		return 0;
243 
244 	return EINVAL;
245 }
246 
247 u_int
clk_round_rate(struct clk * clk,u_int rate)248 clk_round_rate(struct clk *clk, u_int rate)
249 {
250 	if (clk->domain->funcs->round_rate) {
251 		return clk->domain->funcs->round_rate(clk->domain->priv,
252 		    clk, rate);
253 	}
254 	return 0;
255 }
256 
257 int
clk_enable(struct clk * clk)258 clk_enable(struct clk *clk)
259 {
260 	if (clk->domain->funcs->enable)
261 		return clk->domain->funcs->enable(clk->domain->priv, clk);
262 	else
263 		return 0;
264 }
265 
266 int
clk_disable(struct clk * clk)267 clk_disable(struct clk *clk)
268 {
269 	if (clk->domain->funcs->disable)
270 		return clk->domain->funcs->disable(clk->domain->priv, clk);
271 	else
272 		return EINVAL;
273 }
274 
275 int
clk_set_parent(struct clk * clk,struct clk * parent_clk)276 clk_set_parent(struct clk *clk, struct clk *parent_clk)
277 {
278 	if (clk->domain->funcs->set_parent)
279 		return clk->domain->funcs->set_parent(clk->domain->priv,
280 		    clk, parent_clk);
281 	else
282 		return EINVAL;
283 }
284 
285 struct clk *
clk_get_parent(struct clk * clk)286 clk_get_parent(struct clk *clk)
287 {
288 	if (clk->domain->funcs->get_parent)
289 		return clk->domain->funcs->get_parent(clk->domain->priv, clk);
290 	else
291 		return NULL;
292 }
293