1 /* $OpenBSD: ofw_regulator.c,v 1.20 2024/06/14 20:00:32 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2016 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/systm.h> 20 #include <sys/malloc.h> 21 22 #include <dev/ofw/openfirm.h> 23 #include <dev/ofw/ofw_gpio.h> 24 #include <dev/ofw/ofw_pinctrl.h> 25 #include <dev/ofw/ofw_regulator.h> 26 27 #define REGULATOR_VOLTAGE 0 28 #define REGULATOR_CURRENT 1 29 30 LIST_HEAD(, regulator_device) regulator_devices = 31 LIST_HEAD_INITIALIZER(regulator_devices); 32 33 LIST_HEAD(, regulator_notifier) regulator_notifiers = 34 LIST_HEAD_INITIALIZER(regulator_notifiers); 35 36 int regulator_type(int); 37 uint32_t regulator_gpio_get(int); 38 int regulator_gpio_set(int, uint32_t); 39 void regulator_do_notify(uint32_t, uint32_t); 40 41 void 42 regulator_register(struct regulator_device *rd) 43 { 44 rd->rd_volt_min = OF_getpropint(rd->rd_node, 45 "regulator-min-microvolt", 0); 46 rd->rd_volt_max = OF_getpropint(rd->rd_node, 47 "regulator-max-microvolt", ~0); 48 KASSERT(rd->rd_volt_min <= rd->rd_volt_max); 49 50 rd->rd_amp_min = OF_getpropint(rd->rd_node, 51 "regulator-min-microamp", 0); 52 rd->rd_amp_max = OF_getpropint(rd->rd_node, 53 "regulator-max-microamp", ~0); 54 KASSERT(rd->rd_amp_min <= rd->rd_amp_max); 55 56 rd->rd_ramp_delay = 57 OF_getpropint(rd->rd_node, "regulator-ramp-delay", 0); 58 59 rd->rd_coupled = 60 OF_getpropint(rd->rd_node, "regulator-coupled-with", 0); 61 rd->rd_max_spread = 62 OF_getpropint(rd->rd_node, "regulator-coupled-max-spread", 0); 63 64 if (rd->rd_get_voltage && rd->rd_set_voltage) { 65 uint32_t voltage = rd->rd_get_voltage(rd->rd_cookie); 66 if (voltage < rd->rd_volt_min) 67 rd->rd_set_voltage(rd->rd_cookie, rd->rd_volt_min); 68 if (voltage > rd->rd_volt_max) 69 rd->rd_set_voltage(rd->rd_cookie, rd->rd_volt_max); 70 } 71 72 if (rd->rd_get_current && rd->rd_set_current) { 73 uint32_t current = rd->rd_get_current(rd->rd_cookie); 74 if (current < rd->rd_amp_min) 75 rd->rd_set_current(rd->rd_cookie, rd->rd_amp_min); 76 if (current > rd->rd_amp_max) 77 rd->rd_set_current(rd->rd_cookie, rd->rd_amp_max); 78 } 79 80 rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0); 81 if (rd->rd_phandle == 0) 82 return; 83 84 LIST_INSERT_HEAD(®ulator_devices, rd, rd_list); 85 86 if (rd->rd_get_voltage) { 87 regulator_do_notify(rd->rd_phandle, 88 regulator_get_voltage(rd->rd_phandle)); 89 } 90 if (rd->rd_get_current) { 91 regulator_do_notify(rd->rd_phandle, 92 regulator_get_current(rd->rd_phandle)); 93 } 94 } 95 96 int 97 regulator_type(int node) 98 { 99 char type[16] = { 0 }; 100 101 OF_getprop(node, "regulator-type", type, sizeof(type)); 102 if (strcmp(type, "current") == 0) 103 return REGULATOR_CURRENT; 104 105 return REGULATOR_VOLTAGE; 106 } 107 108 int 109 regulator_fixed_set(int node, int enable) 110 { 111 uint32_t *gpio; 112 uint32_t startup_delay; 113 int len; 114 char *prop = "gpio"; 115 116 /* 117 * This regulator may rely on another. That "parent" regulator 118 * may be used by multiple other devices/regulators, so unless 119 * we refcnt use of a regulator we can only turn it on. 120 */ 121 if (enable) 122 regulator_enable(OF_getpropint(node, "vin-supply", 0)); 123 124 pinctrl_byname(node, "default"); 125 126 /* The "gpio"/"gpios" property is optional. */ 127 len = OF_getproplen(node, prop); 128 if (len < 0) { 129 prop = "gpios"; 130 len = OF_getproplen(node, prop); 131 if (len < 0) 132 return 0; 133 } 134 135 /* 136 * We deliberately ignore the "enable-active-high" property 137 * here. Its presence (or absence) is used to override the 138 * polarity encoded by the GPIO flags in the device tree. But 139 * supporting this behaviour is awkward since it would require 140 * interpreting the GPIO flags here which would be a layer 141 * violation since those flags may be driver-specific. In 142 * practice the presence of "enable-active-high" is always 143 * aligned with the polarity encoded by the GPIO flags and any 144 * discrepancy is considered to be a bug by the Linux device 145 * tree maintainers. 146 */ 147 148 gpio = malloc(len, M_TEMP, M_WAITOK); 149 OF_getpropintarray(node, prop, gpio, len); 150 gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT); 151 if (enable) 152 gpio_controller_set_pin(gpio, 1); 153 else 154 gpio_controller_set_pin(gpio, 0); 155 free(gpio, M_TEMP, len); 156 157 startup_delay = OF_getpropint(node, "startup-delay-us", 0); 158 if (enable && startup_delay > 0) 159 delay(startup_delay); 160 161 return 0; 162 } 163 164 int 165 regulator_set(uint32_t phandle, int enable) 166 { 167 struct regulator_device *rd; 168 int node; 169 170 if (phandle == 0) 171 return ENODEV; 172 173 node = OF_getnodebyphandle(phandle); 174 if (node == 0) 175 return ENODEV; 176 177 /* Never turn off regulators that should always be on. */ 178 if (OF_getproplen(node, "regulator-always-on") == 0 && !enable) 179 return 0; 180 181 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 182 if (rd->rd_phandle == phandle) 183 break; 184 } 185 186 if (rd && rd->rd_enable) 187 return rd->rd_enable(rd->rd_cookie, enable); 188 189 if (OF_is_compatible(node, "regulator-fixed")) 190 return regulator_fixed_set(node, enable); 191 192 return ENODEV; 193 } 194 195 int 196 regulator_enable(uint32_t phandle) 197 { 198 return regulator_set(phandle, 1); 199 } 200 201 int 202 regulator_disable(uint32_t phandle) 203 { 204 return regulator_set(phandle, 0); 205 } 206 207 uint32_t 208 regulator_get_voltage(uint32_t phandle) 209 { 210 struct regulator_device *rd; 211 int node; 212 213 if (phandle == 0) 214 return 0; 215 216 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 217 if (rd->rd_phandle == phandle) 218 break; 219 } 220 221 if (rd && rd->rd_get_voltage) 222 return rd->rd_get_voltage(rd->rd_cookie); 223 224 node = OF_getnodebyphandle(phandle); 225 if (node == 0) 226 return 0; 227 228 if (OF_is_compatible(node, "regulator-fixed")) 229 return OF_getpropint(node, "regulator-min-microvolt", 0); 230 231 if (OF_is_compatible(node, "regulator-gpio") && 232 regulator_type(node) == REGULATOR_VOLTAGE) 233 return regulator_gpio_get(node); 234 235 return 0; 236 } 237 238 int 239 regulator_set_voltage(uint32_t phandle, uint32_t voltage) 240 { 241 struct regulator_device *rd; 242 uint32_t old, delta; 243 int error, node; 244 245 if (phandle == 0) 246 return ENODEV; 247 248 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 249 if (rd->rd_phandle == phandle) 250 break; 251 } 252 253 /* Check limits. */ 254 if (rd && (voltage < rd->rd_volt_min || voltage > rd->rd_volt_max)) 255 return EINVAL; 256 257 /* XXX Coupled regulators are unsupported for now. */ 258 if (rd && rd->rd_coupled) 259 return ENOTSUP; 260 261 if (rd && rd->rd_set_voltage) { 262 regulator_do_notify(rd->rd_phandle, voltage); 263 264 old = rd->rd_get_voltage(rd->rd_cookie); 265 error = rd->rd_set_voltage(rd->rd_cookie, voltage); 266 if (voltage > old && rd->rd_ramp_delay > 0) { 267 delta = voltage - old; 268 delay(howmany(delta, rd->rd_ramp_delay)); 269 } 270 271 regulator_do_notify(rd->rd_phandle, voltage); 272 return error; 273 } 274 275 node = OF_getnodebyphandle(phandle); 276 if (node == 0) 277 return ENODEV; 278 279 if (OF_is_compatible(node, "regulator-fixed") && 280 OF_getpropint(node, "regulator-min-microvolt", 0) == voltage) 281 return 0; 282 283 if (OF_is_compatible(node, "regulator-gpio") && 284 regulator_type(node) == REGULATOR_VOLTAGE) 285 return regulator_gpio_set(node, voltage); 286 287 return ENODEV; 288 } 289 290 uint32_t 291 regulator_get_current(uint32_t phandle) 292 { 293 struct regulator_device *rd; 294 int node; 295 296 if (phandle == 0) 297 return 0; 298 299 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 300 if (rd->rd_phandle == phandle) 301 break; 302 } 303 304 if (rd && rd->rd_get_current) 305 return rd->rd_get_current(rd->rd_cookie); 306 307 node = OF_getnodebyphandle(phandle); 308 if (node == 0) 309 return 0; 310 311 if (OF_is_compatible(node, "regulator-fixed")) 312 return OF_getpropint(node, "regulator-min-microamp", 0); 313 314 if (OF_is_compatible(node, "regulator-gpio") && 315 regulator_type(node) == REGULATOR_CURRENT) 316 return regulator_gpio_get(node); 317 318 return 0; 319 } 320 321 int 322 regulator_set_current(uint32_t phandle, uint32_t current) 323 { 324 struct regulator_device *rd; 325 uint32_t old, delta; 326 int error, node; 327 328 if (phandle == 0) 329 return ENODEV; 330 331 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 332 if (rd->rd_phandle == phandle) 333 break; 334 } 335 336 /* Check limits. */ 337 if (rd && (current < rd->rd_amp_min || current > rd->rd_amp_max)) 338 return EINVAL; 339 340 if (rd && rd->rd_set_current) { 341 regulator_do_notify(rd->rd_phandle, current); 342 343 old = rd->rd_get_current(rd->rd_cookie); 344 error = rd->rd_set_current(rd->rd_cookie, current); 345 if (current > old && rd->rd_ramp_delay > 0) { 346 delta = current - old; 347 delay(howmany(delta, rd->rd_ramp_delay)); 348 } 349 350 regulator_do_notify(rd->rd_phandle, current); 351 return error; 352 } 353 354 node = OF_getnodebyphandle(phandle); 355 if (node == 0) 356 return ENODEV; 357 358 if (OF_is_compatible(node, "regulator-fixed") && 359 OF_getpropint(node, "regulator-min-microamp", 0) == current) 360 return 0; 361 362 if (OF_is_compatible(node, "regulator-gpio") && 363 regulator_type(node) == REGULATOR_CURRENT) 364 return regulator_gpio_set(node, current); 365 366 return ENODEV; 367 } 368 369 uint32_t 370 regulator_gpio_get(int node) 371 { 372 uint32_t *gpio, *gpios, *states; 373 uint32_t idx, value; 374 int glen, slen, i; 375 376 pinctrl_byname(node, "default"); 377 378 if ((glen = OF_getproplen(node, "gpios")) <= 0) 379 return EINVAL; 380 if ((slen = OF_getproplen(node, "states")) <= 0) 381 return EINVAL; 382 383 if (slen % (2 * sizeof(uint32_t)) != 0) 384 return EINVAL; 385 386 gpios = malloc(glen, M_TEMP, M_WAITOK); 387 states = malloc(slen, M_TEMP, M_WAITOK); 388 389 OF_getpropintarray(node, "gpios", gpios, glen); 390 OF_getpropintarray(node, "states", states, slen); 391 392 i = 0; 393 idx = 0; 394 gpio = gpios; 395 while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) { 396 if (gpio_controller_get_pin(gpio)) 397 idx |= (1 << i); 398 gpio = gpio_controller_next_pin(gpio); 399 i++; 400 } 401 402 value = 0; 403 for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) { 404 if (states[2 * i + 1] == idx) { 405 value = states[2 * i]; 406 break; 407 } 408 } 409 if (i >= slen / (2 * sizeof(uint32_t))) 410 return 0; 411 412 free(gpios, M_TEMP, glen); 413 free(states, M_TEMP, slen); 414 415 return value; 416 } 417 418 int 419 regulator_gpio_set(int node, uint32_t value) 420 { 421 uint32_t phandle = OF_getpropint(node, "phandle", 0); 422 uint32_t *gpio, *gpios, *states; 423 uint32_t min, max; 424 uint32_t idx; 425 int glen, slen, i; 426 427 pinctrl_byname(node, "default"); 428 429 if (regulator_type(node) == REGULATOR_VOLTAGE) { 430 min = OF_getpropint(node, "regulator-min-microvolt", 0); 431 max = OF_getpropint(node, "regulator-max-microvolt", 0); 432 } 433 434 if (regulator_type(node) == REGULATOR_CURRENT) { 435 min = OF_getpropint(node, "regulator-min-microamp", 0); 436 max = OF_getpropint(node, "regulator-max-microamp", 0); 437 } 438 439 /* Check limits. */ 440 if (value < min || value > max) 441 return EINVAL; 442 443 if ((glen = OF_getproplen(node, "gpios")) <= 0) 444 return EINVAL; 445 if ((slen = OF_getproplen(node, "states")) <= 0) 446 return EINVAL; 447 448 if (slen % (2 * sizeof(uint32_t)) != 0) 449 return EINVAL; 450 451 gpios = malloc(glen, M_TEMP, M_WAITOK); 452 states = malloc(slen, M_TEMP, M_WAITOK); 453 454 OF_getpropintarray(node, "gpios", gpios, glen); 455 OF_getpropintarray(node, "states", states, slen); 456 457 idx = 0; 458 for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) { 459 if (states[2 * i] < min || states[2 * i] > max) 460 continue; 461 if (states[2 * i] == value) { 462 idx = states[2 * i + 1]; 463 break; 464 } 465 } 466 if (i >= slen / (2 * sizeof(uint32_t))) 467 return EINVAL; 468 469 regulator_do_notify(phandle, value); 470 471 i = 0; 472 gpio = gpios; 473 while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) { 474 gpio_controller_set_pin(gpio, !!(idx & (1 << i))); 475 gpio = gpio_controller_next_pin(gpio); 476 i++; 477 } 478 479 regulator_do_notify(phandle, value); 480 481 free(gpios, M_TEMP, glen); 482 free(states, M_TEMP, slen); 483 484 return 0; 485 } 486 487 void 488 regulator_notify(struct regulator_notifier *rn) 489 { 490 LIST_INSERT_HEAD(®ulator_notifiers, rn, rn_list); 491 } 492 493 void 494 regulator_do_notify(uint32_t phandle, uint32_t value) 495 { 496 struct regulator_notifier *rn; 497 498 LIST_FOREACH(rn, ®ulator_notifiers, rn_list) { 499 if (rn->rn_phandle == phandle) 500 rn->rn_notify(rn->rn_cookie, value); 501 } 502 } 503