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