ofw_regulator.c revision 1.19
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
30LIST_HEAD(, regulator_device) regulator_devices =
31	LIST_HEAD_INITIALIZER(regulator_devices);
32
33LIST_HEAD(, regulator_notifier) regulator_notifiers =
34	LIST_HEAD_INITIALIZER(regulator_notifiers);
35
36int regulator_type(int);
37uint32_t regulator_gpio_get(int);
38int regulator_gpio_set(int, uint32_t);
39void regulator_do_notify(uint32_t, uint32_t);
40
41void
42regulator_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
91int
92regulator_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
103int
104regulator_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
159int
160regulator_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
190int
191regulator_enable(uint32_t phandle)
192{
193	return regulator_set(phandle, 1);
194}
195
196int
197regulator_disable(uint32_t phandle)
198{
199	return regulator_set(phandle, 0);
200}
201
202uint32_t
203regulator_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
233int
234regulator_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
281uint32_t
282regulator_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
312int
313regulator_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
360uint32_t
361regulator_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
409int
410regulator_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
478void
479regulator_notify(struct regulator_notifier *rn)
480{
481	LIST_INSERT_HEAD(&regulator_notifiers, rn, rn_list);
482}
483
484void
485regulator_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