clk.c revision 1.6 1 /* $NetBSD: clk.c,v 1.6 2019/01/30 01:20:47 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) 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.6 2019/01/30 01:20:47 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 if (clk->domain->funcs->put)
200 clk->domain->funcs->put(clk->domain->priv, clk);
201 }
202
203 u_int
204 clk_get_rate(struct clk *clk)
205 {
206 return clk->domain->funcs->get_rate(clk->domain->priv, clk);
207 }
208
209 int
210 clk_set_rate(struct clk *clk, u_int rate)
211 {
212 if (clk->flags & CLK_SET_RATE_PARENT)
213 return clk_set_rate(clk_get_parent(clk), rate);
214
215 if (clk->domain->funcs->set_rate)
216 return clk->domain->funcs->set_rate(clk->domain->priv,
217 clk, rate);
218
219 if (clk_get_rate(clk) == rate)
220 return 0;
221
222 return EINVAL;
223 }
224
225 u_int
226 clk_round_rate(struct clk *clk, u_int rate)
227 {
228 if (clk->domain->funcs->round_rate) {
229 return clk->domain->funcs->round_rate(clk->domain->priv,
230 clk, rate);
231 }
232 return 0;
233 }
234
235 int
236 clk_enable(struct clk *clk)
237 {
238 if (clk->domain->funcs->enable)
239 return clk->domain->funcs->enable(clk->domain->priv, clk);
240 else
241 return 0;
242 }
243
244 int
245 clk_disable(struct clk *clk)
246 {
247 if (clk->domain->funcs->disable)
248 return clk->domain->funcs->disable(clk->domain->priv, clk);
249 else
250 return EINVAL;
251 }
252
253 int
254 clk_set_parent(struct clk *clk, struct clk *parent_clk)
255 {
256 if (clk->domain->funcs->set_parent)
257 return clk->domain->funcs->set_parent(clk->domain->priv,
258 clk, parent_clk);
259 else
260 return EINVAL;
261 }
262
263 struct clk *
264 clk_get_parent(struct clk *clk)
265 {
266 if (clk->domain->funcs->get_parent)
267 return clk->domain->funcs->get_parent(clk->domain->priv, clk);
268 else
269 return NULL;
270 }
271