xref: /openbsd-src/sys/dev/ofw/ofw_regulator.c (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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 
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 	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(&regulator_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 
91 int
92 regulator_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 
103 int
104 regulator_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 
159 int
160 regulator_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, &regulator_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 
190 int
191 regulator_enable(uint32_t phandle)
192 {
193 	return regulator_set(phandle, 1);
194 }
195 
196 int
197 regulator_disable(uint32_t phandle)
198 {
199 	return regulator_set(phandle, 0);
200 }
201 
202 uint32_t
203 regulator_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, &regulator_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 
233 int
234 regulator_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, &regulator_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 
281 uint32_t
282 regulator_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, &regulator_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 
312 int
313 regulator_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, &regulator_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 
360 uint32_t
361 regulator_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 
409 int
410 regulator_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 
478 void
479 regulator_notify(struct regulator_notifier *rn)
480 {
481 	LIST_INSERT_HEAD(&regulator_notifiers, rn, rn_list);
482 }
483 
484 void
485 regulator_do_notify(uint32_t phandle, uint32_t value)
486 {
487 	struct regulator_notifier *rn;
488 
489 	LIST_FOREACH(rn, &regulator_notifiers, rn_list) {
490 		if (rn->rn_phandle == phandle)
491 			rn->rn_notify(rn->rn_cookie, value);
492 	}
493 }
494