ofw_regulator.c revision 1.10
1/* $OpenBSD: ofw_regulator.c,v 1.10 2019/01/02 18:50:15 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 27LIST_HEAD(, regulator_device) regulator_devices = 28 LIST_HEAD_INITIALIZER(regulator_devices); 29 30uint32_t regulator_gpio_get_voltage(int); 31int regulator_gpio_set_voltage(int, uint32_t); 32 33void 34regulator_register(struct regulator_device *rd) 35{ 36 rd->rd_min = OF_getpropint(rd->rd_node, "regulator-min-microvolt", 0); 37 rd->rd_max = OF_getpropint(rd->rd_node, "regulator-max-microvolt", ~0); 38 KASSERT(rd->rd_min <= rd->rd_max); 39 40 rd->rd_ramp_delay = 41 OF_getpropint(rd->rd_node, "regulator-ramp-delay", 0); 42 43 if (rd->rd_get_voltage && rd->rd_set_voltage) { 44 uint32_t voltage = rd->rd_get_voltage(rd->rd_cookie); 45 if (voltage < rd->rd_min) 46 rd->rd_set_voltage(rd->rd_cookie, rd->rd_min); 47 if (voltage > rd->rd_max) 48 rd->rd_set_voltage(rd->rd_cookie, rd->rd_max); 49 } 50 51 rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0); 52 if (rd->rd_phandle == 0) 53 return; 54 55 LIST_INSERT_HEAD(®ulator_devices, rd, rd_list); 56} 57 58int 59regulator_fixed_set(int node, int enable) 60{ 61 uint32_t *gpio; 62 uint32_t startup_delay; 63 int active; 64 int len; 65 66 pinctrl_byname(node, "default"); 67 68 if (OF_getproplen(node, "enable-active-high") == 0) 69 active = 1; 70 else 71 active = 0; 72 73 /* The "gpio" property is optional. */ 74 len = OF_getproplen(node, "gpio"); 75 if (len < 0) 76 return 0; 77 78 gpio = malloc(len, M_TEMP, M_WAITOK); 79 OF_getpropintarray(node, "gpio", gpio, len); 80 gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT); 81 if (enable) 82 gpio_controller_set_pin(gpio, active); 83 else 84 gpio_controller_set_pin(gpio, !active); 85 free(gpio, M_TEMP, len); 86 87 startup_delay = OF_getpropint(node, "startup-delay-us", 0); 88 if (enable && startup_delay > 0) 89 delay(startup_delay); 90 91 return 0; 92} 93 94int 95regulator_set(uint32_t phandle, int enable) 96{ 97 struct regulator_device *rd; 98 int node; 99 100 node = OF_getnodebyphandle(phandle); 101 if (node == 0) 102 return ENODEV; 103 104 /* Don't mess around with regulators that are always on. */ 105 if (OF_getproplen(node, "regulator-always-on") == 0) 106 return 0; 107 108 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 109 if (rd->rd_phandle == phandle) 110 break; 111 } 112 113 if (rd && rd->rd_enable) 114 return rd->rd_enable(rd->rd_cookie, enable); 115 116 if (OF_is_compatible(node, "regulator-fixed")) 117 return regulator_fixed_set(node, enable); 118 119 return ENODEV; 120} 121 122int 123regulator_enable(uint32_t phandle) 124{ 125 return regulator_set(phandle, 1); 126} 127 128int 129regulator_disable(uint32_t phandle) 130{ 131 return regulator_set(phandle, 0); 132} 133 134uint32_t 135regulator_get_voltage(uint32_t phandle) 136{ 137 struct regulator_device *rd; 138 int node; 139 140 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 141 if (rd->rd_phandle == phandle) 142 break; 143 } 144 145 if (rd && rd->rd_get_voltage) 146 return rd->rd_get_voltage(rd->rd_cookie); 147 148 node = OF_getnodebyphandle(phandle); 149 if (node == 0) 150 return 0; 151 152 if (OF_is_compatible(node, "regulator-fixed")) 153 return OF_getpropint(node, "regulator-min-microvolt", 0); 154 155 if (OF_is_compatible(node, "regulator-gpio")) 156 return regulator_gpio_get_voltage(node); 157 158 return 0; 159} 160 161int 162regulator_set_voltage(uint32_t phandle, uint32_t voltage) 163{ 164 struct regulator_device *rd; 165 uint32_t old, delta; 166 int error, node; 167 168 LIST_FOREACH(rd, ®ulator_devices, rd_list) { 169 if (rd->rd_phandle == phandle) 170 break; 171 } 172 173 /* Check limits. */ 174 if (rd && (voltage < rd->rd_min || voltage > rd->rd_max)) 175 return EINVAL; 176 177 if (rd && rd->rd_set_voltage) { 178 old = rd->rd_get_voltage(rd->rd_cookie); 179 error = rd->rd_set_voltage(rd->rd_cookie, voltage); 180 if (voltage > old && rd->rd_ramp_delay > 0) { 181 delta = voltage - old; 182 delay(howmany(delta, rd->rd_ramp_delay)); 183 } 184 return error; 185 } 186 187 node = OF_getnodebyphandle(phandle); 188 if (node == 0) 189 return ENODEV; 190 191 if (OF_is_compatible(node, "regulator-fixed") && 192 OF_getpropint(node, "regulator-min-microvolt", 0) == voltage) 193 return 0; 194 195 if (OF_is_compatible(node, "regulator-gpio")) 196 return regulator_gpio_set_voltage(node, voltage); 197 198 return ENODEV; 199} 200 201uint32_t 202regulator_gpio_get_voltage(int node) 203{ 204 uint32_t *gpio, *gpios, *states; 205 uint32_t idx, voltage; 206 size_t glen, slen; 207 int i; 208 209 pinctrl_byname(node, "default"); 210 211 if ((glen = OF_getproplen(node, "gpios")) <= 0) 212 return EINVAL; 213 if ((slen = OF_getproplen(node, "states")) <= 0) 214 return EINVAL; 215 216 if (slen % (2 * sizeof(uint32_t)) != 0) 217 return EINVAL; 218 219 gpios = malloc(glen, M_TEMP, M_WAITOK); 220 states = malloc(slen, M_TEMP, M_WAITOK); 221 222 OF_getpropintarray(node, "gpios", gpios, glen); 223 OF_getpropintarray(node, "states", states, slen); 224 225 i = 0; 226 idx = 0; 227 gpio = gpios; 228 while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) { 229 idx |= (1 << i); 230 gpio = gpio_controller_next_pin(gpio); 231 i++; 232 } 233 234 voltage = 0; 235 for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) { 236 if (states[2 * i + 1] == idx) { 237 voltage = states[2 * i]; 238 break; 239 } 240 } 241 if (i >= slen / (2 * sizeof(uint32_t))) 242 return 0; 243 244 free(gpios, M_TEMP, glen); 245 free(states, M_TEMP, slen); 246 247 return voltage; 248} 249 250int 251regulator_gpio_set_voltage(int node, uint32_t voltage) 252{ 253 uint32_t *gpio, *gpios, *states; 254 size_t glen, slen; 255 uint32_t min, max; 256 uint32_t idx; 257 int i; 258 259 pinctrl_byname(node, "default"); 260 261 /* Check limits. */ 262 min = OF_getpropint(node, "regulator-min-microvolt", 0); 263 max = OF_getpropint(node, "regulator-max-microvolt", 0); 264 if (voltage < min || voltage > max) 265 return EINVAL; 266 267 if ((glen = OF_getproplen(node, "gpios")) <= 0) 268 return EINVAL; 269 if ((slen = OF_getproplen(node, "states")) <= 0) 270 return EINVAL; 271 272 if (slen % (2 * sizeof(uint32_t)) != 0) 273 return EINVAL; 274 275 gpios = malloc(glen, M_TEMP, M_WAITOK); 276 states = malloc(slen, M_TEMP, M_WAITOK); 277 278 OF_getpropintarray(node, "gpios", gpios, glen); 279 OF_getpropintarray(node, "states", states, slen); 280 281 idx = 0; 282 for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) { 283 if (states[2 * i] < min || states[2 * i] > max) 284 continue; 285 if (states[2 * i] == voltage) { 286 idx = states[2 * i + 1]; 287 break; 288 } 289 } 290 if (i >= slen / (2 * sizeof(uint32_t))) 291 return EINVAL; 292 293 i = 0; 294 gpio = gpios; 295 while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) { 296 gpio_controller_set_pin(gpio, !!(idx & (1 << i))); 297 gpio = gpio_controller_next_pin(gpio); 298 i++; 299 } 300 301 free(gpios, M_TEMP, glen); 302 free(states, M_TEMP, slen); 303 304 return 0; 305} 306