xref: /openbsd/sys/dev/ofw/ofw_regulator.c (revision 4fb9ab68)
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(&regulator_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, &regulator_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, &regulator_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, &regulator_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, &regulator_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, &regulator_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(&regulator_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, &regulator_notifiers, rn_list) {
499 		if (rn->rn_phandle == phandle)
500 			rn->rn_notify(rn->rn_cookie, value);
501 	}
502 }
503