xref: /netbsd-src/sys/dev/clk/clk.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /* $NetBSD: clk.c,v 1.4 2018/04/28 15:20:33 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.4 2018/04/28 15:20:33 jmcneill Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/sysctl.h>
34 
35 #include <dev/clk/clk.h>
36 #include <dev/clk/clk_backend.h>
37 
38 static struct sysctllog *clk_log;
39 static const struct sysctlnode *clk_node;
40 
41 static int
42 create_clk_node(void)
43 {
44 	const struct sysctlnode *hw_node;
45 	int error;
46 
47 	if (clk_node)
48 		return 0;
49 
50 	error = sysctl_createv(&clk_log, 0, NULL, &hw_node,
51 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
52 	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL);
53 	if (error)
54 		return error;
55 
56 	error = sysctl_createv(&clk_log, 0, &hw_node, &clk_node,
57 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "clk", NULL,
58 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
59 	if (error)
60 		return error;
61 
62 	return 0;
63 }
64 
65 static int
66 create_domain_node(struct clk_domain *domain)
67 {
68 	int error;
69 
70 	if (domain->node)
71 		return 0;
72 
73 	error = create_clk_node();
74 	if (error)
75 		return error;
76 
77 	error = sysctl_createv(&clk_log, 0, &clk_node, &domain->node,
78 	    0, CTLTYPE_NODE, domain->name, NULL,
79 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
80 	if (error)
81 		return error;
82 
83 	return 0;
84 }
85 
86 static int
87 clk_sysctl_rate_helper(SYSCTLFN_ARGS)
88 {
89 	struct sysctlnode node;
90 	struct clk *clk;
91 	uint64_t rate;
92 
93 	node = *rnode;
94 	clk = node.sysctl_data;
95 	node.sysctl_data = &rate;
96 
97 	rate = clk_get_rate(clk);
98 
99 	return sysctl_lookup(SYSCTLFN_CALL(&node));
100 }
101 
102 static int
103 clk_sysctl_parent_helper(SYSCTLFN_ARGS)
104 {
105 	struct sysctlnode node;
106 	struct clk *clk, *clk_parent;
107 
108 	node = *rnode;
109 	clk = node.sysctl_data;
110 
111 	clk_parent = clk_get_parent(clk);
112 	if (clk_parent && clk_parent->name)
113 		node.sysctl_data = __UNCONST(clk_parent->name);
114 	else
115 		node.sysctl_data = __UNCONST("?");
116 
117 	return sysctl_lookup(SYSCTLFN_CALL(&node));
118 }
119 
120 static int
121 clk_sysctl_parent_domain_helper(SYSCTLFN_ARGS)
122 {
123 	struct sysctlnode node;
124 	struct clk *clk, *clk_parent;
125 
126 	node = *rnode;
127 	clk = node.sysctl_data;
128 
129 	clk_parent = clk_get_parent(clk);
130 	if (clk_parent && clk_parent->domain && clk_parent->domain->name)
131 		node.sysctl_data = __UNCONST(clk_parent->domain->name);
132 	else
133 		node.sysctl_data = __UNCONST("?");
134 
135 	return sysctl_lookup(SYSCTLFN_CALL(&node));
136 }
137 
138 int
139 clk_attach(struct clk *clk)
140 {
141 	const struct sysctlnode *node;
142 	struct clk_domain *domain = clk->domain;
143 	int error;
144 
145 	KASSERT(domain != NULL);
146 
147 	if (!domain->name || !clk->name) {
148 		/* Names are required to create sysctl nodes */
149 		return 0;
150 	}
151 
152 	error = create_domain_node(domain);
153 	if (error != 0)
154 		goto sysctl_failed;
155 
156 	error = sysctl_createv(&clk_log, 0, &domain->node, &node,
157 	    CTLFLAG_PRIVATE, CTLTYPE_NODE, clk->name, NULL,
158 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
159 	if (error)
160 		goto sysctl_failed;
161 
162 	error = sysctl_createv(&clk_log, 0, &node, NULL,
163 	    CTLFLAG_PRIVATE, CTLTYPE_QUAD, "rate", NULL,
164 	    clk_sysctl_rate_helper, 0, (void *)clk, 0,
165 	    CTL_CREATE, CTL_EOL);
166 	if (error)
167 		goto sysctl_failed;
168 
169 	error = sysctl_createv(&clk_log, 0, &node, NULL,
170 	    CTLFLAG_PRIVATE, CTLTYPE_STRING, "parent", NULL,
171 	    clk_sysctl_parent_helper, 0, (void *)clk, 0,
172 	    CTL_CREATE, CTL_EOL);
173 	if (error)
174 		goto sysctl_failed;
175 
176 	error = sysctl_createv(&clk_log, 0, &node, NULL,
177 	    CTLFLAG_PRIVATE, CTLTYPE_STRING, "parent_domain", NULL,
178 	    clk_sysctl_parent_domain_helper, 0, (void *)clk, 0,
179 	    CTL_CREATE, CTL_EOL);
180 	if (error)
181 		goto sysctl_failed;
182 
183 sysctl_failed:
184 	if (error)
185 		aprint_error("%s: failed to create sysctl node for %s: %d\n",
186 		    domain->name, clk->name, error);
187 	return error;
188 }
189 
190 struct clk *
191 clk_get(struct clk_domain *domain, const char *name)
192 {
193 	return domain->funcs->get(domain->priv, name);
194 }
195 
196 void
197 clk_put(struct clk *clk)
198 {
199 	return clk->domain->funcs->put(clk->domain->priv, clk);
200 }
201 
202 u_int
203 clk_get_rate(struct clk *clk)
204 {
205 	return clk->domain->funcs->get_rate(clk->domain->priv, clk);
206 }
207 
208 int
209 clk_set_rate(struct clk *clk, u_int rate)
210 {
211 	if (clk->flags & CLK_SET_RATE_PARENT) {
212 		return clk_set_rate(clk_get_parent(clk), rate);
213 	} else if (clk->domain->funcs->set_rate) {
214 		return clk->domain->funcs->set_rate(clk->domain->priv,
215 		    clk, rate);
216 	} else {
217 		return EINVAL;
218 	}
219 }
220 
221 u_int
222 clk_round_rate(struct clk *clk, u_int rate)
223 {
224 	if (clk->domain->funcs->round_rate) {
225 		return clk->domain->funcs->round_rate(clk->domain->priv,
226 		    clk, rate);
227 	}
228 	return 0;
229 }
230 
231 int
232 clk_enable(struct clk *clk)
233 {
234 	if (clk->domain->funcs->enable)
235 		return clk->domain->funcs->enable(clk->domain->priv, clk);
236 	else
237 		return 0;
238 }
239 
240 int
241 clk_disable(struct clk *clk)
242 {
243 	if (clk->domain->funcs->disable)
244 		return clk->domain->funcs->disable(clk->domain->priv, clk);
245 	else
246 		return EINVAL;
247 }
248 
249 int
250 clk_set_parent(struct clk *clk, struct clk *parent_clk)
251 {
252 	if (clk->domain->funcs->set_parent)
253 		return clk->domain->funcs->set_parent(clk->domain->priv,
254 		    clk, parent_clk);
255 	else
256 		return EINVAL;
257 }
258 
259 struct clk *
260 clk_get_parent(struct clk *clk)
261 {
262 	if (clk->domain->funcs->get_parent)
263 		return clk->domain->funcs->get_parent(clk->domain->priv, clk);
264 	else
265 		return NULL;
266 }
267