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