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