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@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