Home | History | Annotate | Line # | Download | only in clk
      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 (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.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
     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 	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
    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
    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
    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
    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 *
    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