1 /* $NetBSD: cpufreq_dt.c,v 1.3 2017/12/16 16:41:18 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2015-2017 Jared 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: cpufreq_dt.c,v 1.3 2017/12/16 16:41:18 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/device.h> 35 #include <sys/kmem.h> 36 #include <sys/bus.h> 37 #include <sys/atomic.h> 38 #include <sys/xcall.h> 39 #include <sys/sysctl.h> 40 41 #include <dev/fdt/fdtvar.h> 42 43 struct cpufreq_dt_opp { 44 u_int freq_khz; 45 u_int voltage_uv; 46 }; 47 48 struct cpufreq_dt_softc { 49 device_t sc_dev; 50 int sc_phandle; 51 struct clk *sc_clk; 52 struct fdtbus_regulator *sc_supply; 53 54 struct cpufreq_dt_opp *sc_opp; 55 ssize_t sc_nopp; 56 int sc_latency; 57 58 u_int sc_freq_target; 59 bool sc_freq_throttle; 60 61 u_int sc_busy; 62 63 char *sc_freq_available; 64 int sc_node_target; 65 int sc_node_current; 66 int sc_node_available; 67 }; 68 69 static void 70 cpufreq_dt_change_cb(void *arg1, void *arg2) 71 { 72 #if notyet 73 struct cpu_info *ci = curcpu(); 74 ci->ci_data.cpu_cc_freq = cpufreq_get_rate() * 1000000; 75 #endif 76 } 77 78 static int 79 cpufreq_dt_set_rate(struct cpufreq_dt_softc *sc, u_int freq_khz) 80 { 81 struct cpufreq_dt_opp *opp = NULL; 82 u_int old_rate, new_rate, old_uv, new_uv; 83 uint64_t xc; 84 int error; 85 ssize_t n; 86 87 for (n = 0; n < sc->sc_nopp; n++) 88 if (sc->sc_opp[n].freq_khz == freq_khz) { 89 opp = &sc->sc_opp[n]; 90 break; 91 } 92 if (opp == NULL) 93 return EINVAL; 94 95 old_rate = clk_get_rate(sc->sc_clk); 96 new_rate = freq_khz * 1000; 97 new_uv = opp->voltage_uv; 98 99 if (old_rate == new_rate) 100 return 0; 101 102 if (sc->sc_supply != NULL) { 103 error = fdtbus_regulator_get_voltage(sc->sc_supply, &old_uv); 104 if (error != 0) 105 return error; 106 107 if (new_uv > old_uv) { 108 error = fdtbus_regulator_set_voltage(sc->sc_supply, 109 new_uv, new_uv); 110 if (error != 0) 111 return error; 112 } 113 } 114 115 error = clk_set_rate(sc->sc_clk, new_rate); 116 if (error != 0) 117 return error; 118 119 if (sc->sc_supply != NULL) { 120 if (new_uv < old_uv) { 121 error = fdtbus_regulator_set_voltage(sc->sc_supply, 122 new_uv, new_uv); 123 if (error != 0) 124 return error; 125 } 126 } 127 128 if (error == 0) { 129 xc = xc_broadcast(0, cpufreq_dt_change_cb, sc, NULL); 130 xc_wait(xc); 131 132 pmf_event_inject(NULL, PMFE_SPEED_CHANGED); 133 } 134 135 return 0; 136 } 137 138 static void 139 cpufreq_dt_throttle_enable(device_t dev) 140 { 141 struct cpufreq_dt_softc * const sc = device_private(dev); 142 143 if (sc->sc_freq_throttle) 144 return; 145 146 const u_int freq_khz = sc->sc_opp[sc->sc_nopp - 1].freq_khz; 147 148 while (atomic_cas_uint(&sc->sc_busy, 0, 1) != 0) 149 kpause("throttle", false, 1, NULL); 150 151 if (cpufreq_dt_set_rate(sc, freq_khz) == 0) { 152 aprint_debug_dev(sc->sc_dev, "throttle enabled (%u.%03u MHz)\n", 153 freq_khz / 1000, freq_khz % 1000); 154 sc->sc_freq_throttle = true; 155 if (sc->sc_freq_target == 0) 156 sc->sc_freq_target = clk_get_rate(sc->sc_clk) / 1000000; 157 } 158 159 atomic_dec_uint(&sc->sc_busy); 160 } 161 162 static void 163 cpufreq_dt_throttle_disable(device_t dev) 164 { 165 struct cpufreq_dt_softc * const sc = device_private(dev); 166 167 if (!sc->sc_freq_throttle) 168 return; 169 170 while (atomic_cas_uint(&sc->sc_busy, 0, 1) != 0) 171 kpause("throttle", false, 1, NULL); 172 173 const u_int freq_khz = sc->sc_freq_target * 1000; 174 175 if (cpufreq_dt_set_rate(sc, freq_khz) == 0) { 176 aprint_debug_dev(sc->sc_dev, "throttle disabled (%u.%03u MHz)\n", 177 freq_khz / 1000, freq_khz % 1000); 178 sc->sc_freq_throttle = false; 179 } 180 181 atomic_dec_uint(&sc->sc_busy); 182 } 183 184 static int 185 cpufreq_dt_sysctl_helper(SYSCTLFN_ARGS) 186 { 187 struct cpufreq_dt_softc * const sc = rnode->sysctl_data; 188 struct sysctlnode node; 189 u_int fq, oldfq = 0; 190 int error, n; 191 192 node = *rnode; 193 node.sysctl_data = &fq; 194 195 if (rnode->sysctl_num == sc->sc_node_target) { 196 if (sc->sc_freq_target == 0) 197 sc->sc_freq_target = clk_get_rate(sc->sc_clk) / 1000000; 198 fq = sc->sc_freq_target; 199 } else 200 fq = clk_get_rate(sc->sc_clk) / 1000000; 201 202 if (rnode->sysctl_num == sc->sc_node_target) 203 oldfq = fq; 204 205 if (sc->sc_freq_target == 0) 206 sc->sc_freq_target = fq; 207 208 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 209 if (error || newp == NULL) 210 return error; 211 212 if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target) 213 return 0; 214 215 for (n = 0; n < sc->sc_nopp; n++) 216 if (sc->sc_opp[n].freq_khz / 1000 == fq) 217 break; 218 if (n == sc->sc_nopp) 219 return EINVAL; 220 221 if (atomic_cas_uint(&sc->sc_busy, 0, 1) != 0) 222 return EBUSY; 223 224 sc->sc_freq_target = fq; 225 226 if (sc->sc_freq_throttle) 227 error = 0; 228 else 229 error = cpufreq_dt_set_rate(sc, fq * 1000); 230 231 atomic_dec_uint(&sc->sc_busy); 232 233 return error; 234 } 235 236 static void 237 cpufreq_dt_init_sysctl(struct cpufreq_dt_softc *sc) 238 { 239 const struct sysctlnode *node, *cpunode, *freqnode; 240 struct sysctllog *cpufreq_log = NULL; 241 int error, i; 242 243 sc->sc_freq_available = kmem_zalloc(strlen("XXXX ") * sc->sc_nopp, KM_SLEEP); 244 for (i = 0; i < sc->sc_nopp; i++) { 245 char buf[6]; 246 snprintf(buf, sizeof(buf), i ? " %u" : "%u", sc->sc_opp[i].freq_khz / 1000); 247 strcat(sc->sc_freq_available, buf); 248 } 249 250 error = sysctl_createv(&cpufreq_log, 0, NULL, &node, 251 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, 252 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); 253 if (error) 254 goto sysctl_failed; 255 error = sysctl_createv(&cpufreq_log, 0, &node, &cpunode, 256 0, CTLTYPE_NODE, "cpu", NULL, 257 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 258 if (error) 259 goto sysctl_failed; 260 error = sysctl_createv(&cpufreq_log, 0, &cpunode, &freqnode, 261 0, CTLTYPE_NODE, "frequency", NULL, 262 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 263 if (error) 264 goto sysctl_failed; 265 266 error = sysctl_createv(&cpufreq_log, 0, &freqnode, &node, 267 CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL, 268 cpufreq_dt_sysctl_helper, 0, (void *)sc, 0, 269 CTL_CREATE, CTL_EOL); 270 if (error) 271 goto sysctl_failed; 272 sc->sc_node_target = node->sysctl_num; 273 274 error = sysctl_createv(&cpufreq_log, 0, &freqnode, &node, 275 CTLFLAG_READWRITE, CTLTYPE_INT, "current", NULL, 276 cpufreq_dt_sysctl_helper, 0, (void *)sc, 0, 277 CTL_CREATE, CTL_EOL); 278 if (error) 279 goto sysctl_failed; 280 sc->sc_node_current = node->sysctl_num; 281 282 error = sysctl_createv(&cpufreq_log, 0, &freqnode, &node, 283 0, CTLTYPE_STRING, "available", NULL, 284 NULL, 0, sc->sc_freq_available, 0, 285 CTL_CREATE, CTL_EOL); 286 if (error) 287 goto sysctl_failed; 288 sc->sc_node_available = node->sysctl_num; 289 290 return; 291 292 sysctl_failed: 293 aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes: %d\n", error); 294 sysctl_teardown(&cpufreq_log); 295 } 296 297 static int 298 cpufreq_dt_parse(struct cpufreq_dt_softc *sc) 299 { 300 const int phandle = sc->sc_phandle; 301 const u_int *opp; 302 int len, i; 303 u_int lat; 304 305 if (of_hasprop(phandle, "cpu-supply")) { 306 sc->sc_supply = fdtbus_regulator_acquire(phandle, "cpu-supply"); 307 if (sc->sc_supply == NULL) { 308 aprint_error_dev(sc->sc_dev, 309 "couldn't acquire cpu-supply\n"); 310 return ENXIO; 311 } 312 } 313 sc->sc_clk = fdtbus_clock_get_index(phandle, 0); 314 if (sc->sc_clk == NULL) { 315 aprint_error_dev(sc->sc_dev, "couldn't acquire clock\n"); 316 return ENXIO; 317 } 318 319 opp = fdtbus_get_prop(phandle, "operating-points", &len); 320 if (len < 8) 321 return ENXIO; 322 323 sc->sc_nopp = len / 8; 324 sc->sc_opp = kmem_zalloc(sizeof(*sc->sc_opp) * sc->sc_nopp, KM_SLEEP); 325 for (i = 0; i < sc->sc_nopp; i++, opp += 2) { 326 sc->sc_opp[i].freq_khz = be32toh(opp[0]); 327 sc->sc_opp[i].voltage_uv = be32toh(opp[1]); 328 329 aprint_verbose_dev(sc->sc_dev, "%u.%03u MHz, %u uV\n", 330 sc->sc_opp[i].freq_khz / 1000, 331 sc->sc_opp[i].freq_khz % 1000, 332 sc->sc_opp[i].voltage_uv); 333 } 334 335 if (of_getprop_uint32(phandle, "clock-latency", &lat) == 0) 336 sc->sc_latency = lat; 337 else 338 sc->sc_latency = -1; 339 340 return 0; 341 } 342 343 static int 344 cpufreq_dt_match(device_t parent, cfdata_t cf, void *aux) 345 { 346 struct fdt_attach_args * const faa = aux; 347 const int phandle = faa->faa_phandle; 348 bus_addr_t addr; 349 350 if (fdtbus_get_reg(phandle, 0, &addr, NULL) != 0) 351 return 0; 352 /* Generic DT cpufreq driver properties must be defined under /cpus/cpu@0 */ 353 if (addr != 0) 354 return 0; 355 356 if (!of_hasprop(phandle, "operating-points") || 357 !of_hasprop(phandle, "clocks")) 358 return 0; 359 360 return 1; 361 } 362 363 static void 364 cpufreq_dt_init(device_t self) 365 { 366 struct cpufreq_dt_softc * const sc = device_private(self); 367 int error; 368 369 if ((error = cpufreq_dt_parse(sc)) != 0) 370 return; 371 372 cpufreq_dt_init_sysctl(sc); 373 } 374 375 static void 376 cpufreq_dt_attach(device_t parent, device_t self, void *aux) 377 { 378 struct cpufreq_dt_softc * const sc = device_private(self); 379 struct fdt_attach_args * const faa = aux; 380 381 sc->sc_dev = self; 382 sc->sc_phandle = faa->faa_phandle; 383 384 aprint_naive("\n"); 385 aprint_normal("\n"); 386 387 pmf_event_register(self, PMFE_THROTTLE_ENABLE, cpufreq_dt_throttle_enable, true); 388 pmf_event_register(self, PMFE_THROTTLE_DISABLE, cpufreq_dt_throttle_disable, true); 389 390 config_interrupts(self, cpufreq_dt_init); 391 } 392 393 CFATTACH_DECL_NEW(cpufreq_dt, sizeof(struct cpufreq_dt_softc), 394 cpufreq_dt_match, cpufreq_dt_attach, NULL, NULL); 395