ofw_regulator.c revision 1.12
1/* $OpenBSD: ofw_regulator.c,v 1.12 2019/02/20 07:36:37 patrick 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 33int regulator_type(int); 34uint32_t regulator_gpio_get(int); 35int regulator_gpio_set(int, uint32_t); 36 37void 38regulator_register(struct regulator_device *rd) 39{ 40 rd->rd_volt_min = OF_getpropint(rd->rd_node, 41 "regulator-min-microvolt", 0); 42 rd->rd_volt_max = OF_getpropint(rd->rd_node, 43 "regulator-max-microvolt", ~0); 44 KASSERT(rd->rd_volt_min <= rd->rd_volt_max); 45 46 rd->rd_amp_min = OF_getpropint(rd->rd_node, 47 "regulator-min-microamp", 0); 48 rd->rd_amp_max = OF_getpropint(rd->rd_node, 49 "regulator-max-microamp", ~0); 50 KASSERT(rd->rd_amp_min <= rd->rd_amp_max); 51 52 rd->rd_ramp_delay = 53 OF_getpropint(rd->rd_node, "regulator-ramp-delay", 0); 54 55 if (rd->rd_get_voltage && rd->rd_set_voltage) { 56 uint32_t voltage = rd->rd_get_voltage(rd->rd_cookie); 57 if (voltage < rd->rd_volt_min) 58 rd->rd_set_voltage(rd->rd_cookie, rd->rd_volt_min); 59 if (voltage > rd->rd_volt_max) 60 rd->rd_set_voltage(rd->rd_cookie, rd->rd_volt_max); 61 } 62 63 if (rd->rd_get_current && rd->rd_set_current) { 64 uint32_t current = rd->rd_get_current(rd->rd_cookie); 65 if (current < rd->rd_amp_min) 66 rd->rd_set_current(rd->rd_cookie, rd->rd_amp_min); 67 if (current > rd->rd_amp_max) 68 rd->rd_set_current(rd->rd_cookie, rd->rd_amp_max); 69 } 70 71 rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0); 72 if (rd->rd_phandle == 0) 73 return; 74 75 LIST_INSERT_HEAD(®ulator_devices, rd, rd_list); 76} 77 78int 79regulator_type(int node) 80{ 81 char type[16] = { 0 }; 82 83 OF_getprop(node, "regulator-type", type, sizeof(type)); 84 if (strcmp(type, "current") == 0) 85 return REGULATOR_CURRENT; 86 87 return REGULATOR_VOLTAGE; 88} 89 90int 91regulator_fixed_set(int node, int enable) 92{ 93 uint32_t *gpio; 94 uint32_t startup_delay; 95 int active; 96 int len; 97 98 pinctrl_byname(node, "default"); 99 100 if (OF_getproplen(node, "enable-active-high") == 0) 101 active = 1; 102 else 103 active = 0; 104 105 /* The "gpio" property is optional. */ 106 len = OF_getproplen(node, "gpio"); 107 if (len < 0) 108 return 0; 109 110 gpio = malloc(len, M_TEMP, M_WAITOK); 111 OF_getpropintarray(node, "gpio", gpio, len); 112 gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT); 113 if (enable) 114 gpio_controller_set_pin(gpio, active); 115 else 116 gpio_controller_set_pin(gpio, !active); 117 free(gpio, M_TEMP, len); 118 119 startup_delay = OF_getpropint(node, "startup-delay-us", 0); 120 if (enable && startup_delay > 0) 121 delay(startup_delay); 122 123 return 0; 124} 125 126int 127regulator_set(uint32_t phandle, int enable) 128{ 129 struct regulator_device *rd; 130 int node; 131 132 node = OF_getnodebyphandle(phandle); 133 if (node == 0) 134 return ENODEV; 135 136 /* Don't mess around with regulators that are always on. */ 137 if (OF_getproplen(node, "regulator-always-on") == 0) 138 return 0; 139 140 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 141 if (rd->rd_phandle == phandle) 142 break; 143 } 144 145 if (rd && rd->rd_enable) 146 return rd->rd_enable(rd->rd_cookie, enable); 147 148 if (OF_is_compatible(node, "regulator-fixed")) 149 return regulator_fixed_set(node, enable); 150 151 return ENODEV; 152} 153 154int 155regulator_enable(uint32_t phandle) 156{ 157 return regulator_set(phandle, 1); 158} 159 160int 161regulator_disable(uint32_t phandle) 162{ 163 return regulator_set(phandle, 0); 164} 165 166uint32_t 167regulator_get_voltage(uint32_t phandle) 168{ 169 struct regulator_device *rd; 170 int node; 171 172 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 173 if (rd->rd_phandle == phandle) 174 break; 175 } 176 177 if (rd && rd->rd_get_voltage) 178 return rd->rd_get_voltage(rd->rd_cookie); 179 180 node = OF_getnodebyphandle(phandle); 181 if (node == 0) 182 return 0; 183 184 if (OF_is_compatible(node, "regulator-fixed")) 185 return OF_getpropint(node, "regulator-min-microvolt", 0); 186 187 if (OF_is_compatible(node, "regulator-gpio") && 188 regulator_type(node) == REGULATOR_VOLTAGE) 189 return regulator_gpio_get(node); 190 191 return 0; 192} 193 194int 195regulator_set_voltage(uint32_t phandle, uint32_t voltage) 196{ 197 struct regulator_device *rd; 198 uint32_t old, delta; 199 int error, node; 200 201 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 202 if (rd->rd_phandle == phandle) 203 break; 204 } 205 206 /* Check limits. */ 207 if (rd && (voltage < rd->rd_volt_min || voltage > rd->rd_volt_max)) 208 return EINVAL; 209 210 if (rd && rd->rd_set_voltage) { 211 old = rd->rd_get_voltage(rd->rd_cookie); 212 error = rd->rd_set_voltage(rd->rd_cookie, voltage); 213 if (voltage > old && rd->rd_ramp_delay > 0) { 214 delta = voltage - old; 215 delay(howmany(delta, rd->rd_ramp_delay)); 216 } 217 return error; 218 } 219 220 node = OF_getnodebyphandle(phandle); 221 if (node == 0) 222 return ENODEV; 223 224 if (OF_is_compatible(node, "regulator-fixed") && 225 OF_getpropint(node, "regulator-min-microvolt", 0) == voltage) 226 return 0; 227 228 if (OF_is_compatible(node, "regulator-gpio") && 229 regulator_type(node) == REGULATOR_VOLTAGE) 230 return regulator_gpio_set(node, voltage); 231 232 return ENODEV; 233} 234 235uint32_t 236regulator_get_current(uint32_t phandle) 237{ 238 struct regulator_device *rd; 239 int node; 240 241 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 242 if (rd->rd_phandle == phandle) 243 break; 244 } 245 246 if (rd && rd->rd_get_current) 247 return rd->rd_get_current(rd->rd_cookie); 248 249 node = OF_getnodebyphandle(phandle); 250 if (node == 0) 251 return 0; 252 253 if (OF_is_compatible(node, "regulator-fixed")) 254 return OF_getpropint(node, "regulator-min-microamp", 0); 255 256 if (OF_is_compatible(node, "regulator-gpio") && 257 regulator_type(node) == REGULATOR_CURRENT) 258 return regulator_gpio_get(node); 259 260 return 0; 261} 262 263int 264regulator_set_current(uint32_t phandle, uint32_t current) 265{ 266 struct regulator_device *rd; 267 uint32_t old, delta; 268 int error, node; 269 270 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 271 if (rd->rd_phandle == phandle) 272 break; 273 } 274 275 /* Check limits. */ 276 if (rd && (current < rd->rd_amp_min || current > rd->rd_amp_max)) 277 return EINVAL; 278 279 if (rd && rd->rd_set_current) { 280 old = rd->rd_get_current(rd->rd_cookie); 281 error = rd->rd_set_current(rd->rd_cookie, current); 282 if (current > old && rd->rd_ramp_delay > 0) { 283 delta = current - old; 284 delay(howmany(delta, rd->rd_ramp_delay)); 285 } 286 return error; 287 } 288 289 node = OF_getnodebyphandle(phandle); 290 if (node == 0) 291 return ENODEV; 292 293 if (OF_is_compatible(node, "regulator-fixed") && 294 OF_getpropint(node, "regulator-min-microamp", 0) == current) 295 return 0; 296 297 if (OF_is_compatible(node, "regulator-gpio") && 298 regulator_type(node) == REGULATOR_CURRENT) 299 return regulator_gpio_set(node, current); 300 301 return ENODEV; 302} 303 304uint32_t 305regulator_gpio_get(int node) 306{ 307 uint32_t *gpio, *gpios, *states; 308 uint32_t idx, value; 309 size_t glen, slen; 310 int i; 311 312 pinctrl_byname(node, "default"); 313 314 if ((glen = OF_getproplen(node, "gpios")) <= 0) 315 return EINVAL; 316 if ((slen = OF_getproplen(node, "states")) <= 0) 317 return EINVAL; 318 319 if (slen % (2 * sizeof(uint32_t)) != 0) 320 return EINVAL; 321 322 gpios = malloc(glen, M_TEMP, M_WAITOK); 323 states = malloc(slen, M_TEMP, M_WAITOK); 324 325 OF_getpropintarray(node, "gpios", gpios, glen); 326 OF_getpropintarray(node, "states", states, slen); 327 328 i = 0; 329 idx = 0; 330 gpio = gpios; 331 while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) { 332 if (gpio_controller_get_pin(gpio)) 333 idx |= (1 << i); 334 gpio = gpio_controller_next_pin(gpio); 335 i++; 336 } 337 338 value = 0; 339 for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) { 340 if (states[2 * i + 1] == idx) { 341 value = states[2 * i]; 342 break; 343 } 344 } 345 if (i >= slen / (2 * sizeof(uint32_t))) 346 return 0; 347 348 free(gpios, M_TEMP, glen); 349 free(states, M_TEMP, slen); 350 351 return value; 352} 353 354int 355regulator_gpio_set(int node, uint32_t value) 356{ 357 uint32_t *gpio, *gpios, *states; 358 size_t glen, slen; 359 uint32_t min, max; 360 uint32_t idx; 361 int i; 362 363 pinctrl_byname(node, "default"); 364 365 if (regulator_type(node) == REGULATOR_VOLTAGE) { 366 min = OF_getpropint(node, "regulator-min-microvolt", 0); 367 max = OF_getpropint(node, "regulator-max-microvolt", 0); 368 } 369 370 if (regulator_type(node) == REGULATOR_CURRENT) { 371 min = OF_getpropint(node, "regulator-min-microamp", 0); 372 max = OF_getpropint(node, "regulator-max-microamp", 0); 373 } 374 375 /* Check limits. */ 376 if (value < min || value > max) 377 return EINVAL; 378 379 if ((glen = OF_getproplen(node, "gpios")) <= 0) 380 return EINVAL; 381 if ((slen = OF_getproplen(node, "states")) <= 0) 382 return EINVAL; 383 384 if (slen % (2 * sizeof(uint32_t)) != 0) 385 return EINVAL; 386 387 gpios = malloc(glen, M_TEMP, M_WAITOK); 388 states = malloc(slen, M_TEMP, M_WAITOK); 389 390 OF_getpropintarray(node, "gpios", gpios, glen); 391 OF_getpropintarray(node, "states", states, slen); 392 393 idx = 0; 394 for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) { 395 if (states[2 * i] < min || states[2 * i] > max) 396 continue; 397 if (states[2 * i] == value) { 398 idx = states[2 * i + 1]; 399 break; 400 } 401 } 402 if (i >= slen / (2 * sizeof(uint32_t))) 403 return EINVAL; 404 405 i = 0; 406 gpio = gpios; 407 while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) { 408 gpio_controller_set_pin(gpio, !!(idx & (1 << i))); 409 gpio = gpio_controller_next_pin(gpio); 410 i++; 411 } 412 413 free(gpios, M_TEMP, glen); 414 free(states, M_TEMP, slen); 415 416 return 0; 417} 418