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
regulator_register(struct regulator_device * rd)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
regulator_type(int node)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
regulator_fixed_set(int node,int enable)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
regulator_set(uint32_t phandle,int enable)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
regulator_enable(uint32_t phandle)196 regulator_enable(uint32_t phandle)
197 {
198 return regulator_set(phandle, 1);
199 }
200
201 int
regulator_disable(uint32_t phandle)202 regulator_disable(uint32_t phandle)
203 {
204 return regulator_set(phandle, 0);
205 }
206
207 uint32_t
regulator_get_voltage(uint32_t phandle)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
regulator_set_voltage(uint32_t phandle,uint32_t voltage)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
regulator_get_current(uint32_t phandle)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
regulator_set_current(uint32_t phandle,uint32_t current)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
regulator_gpio_get(int node)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
regulator_gpio_set(int node,uint32_t value)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
regulator_notify(struct regulator_notifier * rn)488 regulator_notify(struct regulator_notifier *rn)
489 {
490 LIST_INSERT_HEAD(®ulator_notifiers, rn, rn_list);
491 }
492
493 void
regulator_do_notify(uint32_t phandle,uint32_t value)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