xref: /netbsd-src/sys/dev/clk/clk.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /* $NetBSD: clk.c,v 1.7 2019/07/23 17:44:03 jmcneill 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.7 2019/07/23 17:44:03 jmcneill 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
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
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
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
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
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
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
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 *
211 clk_get(struct clk_domain *domain, const char *name)
212 {
213 	return domain->funcs->get(domain->priv, name);
214 }
215 
216 void
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
224 clk_get_rate(struct clk *clk)
225 {
226 	return clk->domain->funcs->get_rate(clk->domain->priv, clk);
227 }
228 
229 int
230 clk_set_rate(struct clk *clk, u_int rate)
231 {
232 	if (clk->flags & CLK_SET_RATE_PARENT)
233 		return clk_set_rate(clk_get_parent(clk), rate);
234 
235 	if (clk->domain->funcs->set_rate)
236 		return clk->domain->funcs->set_rate(clk->domain->priv,
237 		    clk, rate);
238 
239 	if (clk_get_rate(clk) == rate)
240 		return 0;
241 
242 	return EINVAL;
243 }
244 
245 u_int
246 clk_round_rate(struct clk *clk, u_int rate)
247 {
248 	if (clk->domain->funcs->round_rate) {
249 		return clk->domain->funcs->round_rate(clk->domain->priv,
250 		    clk, rate);
251 	}
252 	return 0;
253 }
254 
255 int
256 clk_enable(struct clk *clk)
257 {
258 	if (clk->domain->funcs->enable)
259 		return clk->domain->funcs->enable(clk->domain->priv, clk);
260 	else
261 		return 0;
262 }
263 
264 int
265 clk_disable(struct clk *clk)
266 {
267 	if (clk->domain->funcs->disable)
268 		return clk->domain->funcs->disable(clk->domain->priv, clk);
269 	else
270 		return EINVAL;
271 }
272 
273 int
274 clk_set_parent(struct clk *clk, struct clk *parent_clk)
275 {
276 	if (clk->domain->funcs->set_parent)
277 		return clk->domain->funcs->set_parent(clk->domain->priv,
278 		    clk, parent_clk);
279 	else
280 		return EINVAL;
281 }
282 
283 struct clk *
284 clk_get_parent(struct clk *clk)
285 {
286 	if (clk->domain->funcs->get_parent)
287 		return clk->domain->funcs->get_parent(clk->domain->priv, clk);
288 	else
289 		return NULL;
290 }
291