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