ofw_regulator.c revision 1.15
1/*	$OpenBSD: ofw_regulator.c,v 1.15 2020/12/23 11:58:36 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
30LIST_HEAD(, regulator_device) regulator_devices =
31	LIST_HEAD_INITIALIZER(regulator_devices);
32
33int regulator_type(int);
34uint32_t regulator_gpio_get(int);
35int regulator_gpio_set(int, uint32_t);
36
37void
38regulator_register(struct regulator_device *rd)
39{
40	rd->rd_volt_min = OF_getpropint(rd->rd_node,
41	    "regulator-min-microvolt", 0);
42	rd->rd_volt_max = OF_getpropint(rd->rd_node,
43	    "regulator-max-microvolt", ~0);
44	KASSERT(rd->rd_volt_min <= rd->rd_volt_max);
45
46	rd->rd_amp_min = OF_getpropint(rd->rd_node,
47	    "regulator-min-microamp", 0);
48	rd->rd_amp_max = OF_getpropint(rd->rd_node,
49	    "regulator-max-microamp", ~0);
50	KASSERT(rd->rd_amp_min <= rd->rd_amp_max);
51
52	rd->rd_ramp_delay =
53	    OF_getpropint(rd->rd_node, "regulator-ramp-delay", 0);
54
55	if (rd->rd_get_voltage && rd->rd_set_voltage) {
56		uint32_t voltage = rd->rd_get_voltage(rd->rd_cookie);
57		if (voltage < rd->rd_volt_min)
58			rd->rd_set_voltage(rd->rd_cookie, rd->rd_volt_min);
59		if (voltage > rd->rd_volt_max)
60			rd->rd_set_voltage(rd->rd_cookie, rd->rd_volt_max);
61	}
62
63	if (rd->rd_get_current && rd->rd_set_current) {
64		uint32_t current = rd->rd_get_current(rd->rd_cookie);
65		if (current < rd->rd_amp_min)
66			rd->rd_set_current(rd->rd_cookie, rd->rd_amp_min);
67		if (current > rd->rd_amp_max)
68			rd->rd_set_current(rd->rd_cookie, rd->rd_amp_max);
69	}
70
71	rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0);
72	if (rd->rd_phandle == 0)
73		return;
74
75	LIST_INSERT_HEAD(&regulator_devices, rd, rd_list);
76}
77
78int
79regulator_type(int node)
80{
81	char type[16] = { 0 };
82
83	OF_getprop(node, "regulator-type", type, sizeof(type));
84	if (strcmp(type, "current") == 0)
85		return REGULATOR_CURRENT;
86
87	return REGULATOR_VOLTAGE;
88}
89
90int
91regulator_fixed_set(int node, int enable)
92{
93	uint32_t *gpio;
94	uint32_t startup_delay;
95	int len;
96
97	pinctrl_byname(node, "default");
98
99	/* The "gpio" property is optional. */
100	len = OF_getproplen(node, "gpio");
101	if (len < 0)
102		return 0;
103
104	/*
105	 * We deliberately ignore the "enable-active-high" property
106	 * here.  Its presence (or absence) is used to override the
107	 * polarity encoded by the GPIO flags in the device tree.  But
108	 * supporting this behaviour is awkward since it would require
109	 * interpreting the GPIO flags here which would be a layer
110	 * violation since those flags may be driver-specific.  In
111	 * practice the presence of "enable-active-high" is always
112	 * aligned with the polarity encoded by the GPIO flags and any
113	 * discrepancy is considered to be a bug by the Linux device
114	 * tree maintainers.
115	 */
116
117	gpio = malloc(len, M_TEMP, M_WAITOK);
118	OF_getpropintarray(node, "gpio", gpio, len);
119	gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
120	if (enable)
121		gpio_controller_set_pin(gpio, 1);
122	else
123		gpio_controller_set_pin(gpio, 0);
124	free(gpio, M_TEMP, len);
125
126	startup_delay = OF_getpropint(node, "startup-delay-us", 0);
127	if (enable && startup_delay > 0)
128		delay(startup_delay);
129
130	return 0;
131}
132
133int
134regulator_set(uint32_t phandle, int enable)
135{
136	struct regulator_device *rd;
137	int node;
138
139	if (phandle == 0)
140		return ENODEV;
141
142	node = OF_getnodebyphandle(phandle);
143	if (node == 0)
144		return ENODEV;
145
146	/* Never turn off regulators that should always be on. */
147	if (OF_getproplen(node, "regulator-always-on") == 0 && !enable)
148		return 0;
149
150	LIST_FOREACH(rd, &regulator_devices, rd_list) {
151		if (rd->rd_phandle == phandle)
152			break;
153	}
154
155	if (rd && rd->rd_enable)
156		return rd->rd_enable(rd->rd_cookie, enable);
157
158	if (OF_is_compatible(node, "regulator-fixed"))
159		return regulator_fixed_set(node, enable);
160
161	return ENODEV;
162}
163
164int
165regulator_enable(uint32_t phandle)
166{
167	return regulator_set(phandle, 1);
168}
169
170int
171regulator_disable(uint32_t phandle)
172{
173	return regulator_set(phandle, 0);
174}
175
176uint32_t
177regulator_get_voltage(uint32_t phandle)
178{
179	struct regulator_device *rd;
180	int node;
181
182	if (phandle == 0)
183		return 0;
184
185	LIST_FOREACH(rd, &regulator_devices, rd_list) {
186		if (rd->rd_phandle == phandle)
187			break;
188	}
189
190	if (rd && rd->rd_get_voltage)
191		return rd->rd_get_voltage(rd->rd_cookie);
192
193	node = OF_getnodebyphandle(phandle);
194	if (node == 0)
195		return 0;
196
197	if (OF_is_compatible(node, "regulator-fixed"))
198		return OF_getpropint(node, "regulator-min-microvolt", 0);
199
200	if (OF_is_compatible(node, "regulator-gpio") &&
201	    regulator_type(node) == REGULATOR_VOLTAGE)
202		return regulator_gpio_get(node);
203
204	return 0;
205}
206
207int
208regulator_set_voltage(uint32_t phandle, uint32_t voltage)
209{
210	struct regulator_device *rd;
211	uint32_t old, delta;
212	int error, node;
213
214	if (phandle == 0)
215		return ENODEV;
216
217	LIST_FOREACH(rd, &regulator_devices, rd_list) {
218		if (rd->rd_phandle == phandle)
219			break;
220	}
221
222	/* Check limits. */
223	if (rd && (voltage < rd->rd_volt_min || voltage > rd->rd_volt_max))
224		return EINVAL;
225
226	if (rd && rd->rd_set_voltage) {
227		old = rd->rd_get_voltage(rd->rd_cookie);
228		error = rd->rd_set_voltage(rd->rd_cookie, voltage);
229		if (voltage > old && rd->rd_ramp_delay > 0) {
230			delta = voltage - old;
231			delay(howmany(delta, rd->rd_ramp_delay));
232		}
233		return error;
234	}
235
236	node = OF_getnodebyphandle(phandle);
237	if (node == 0)
238		return ENODEV;
239
240	if (OF_is_compatible(node, "regulator-fixed") &&
241	    OF_getpropint(node, "regulator-min-microvolt", 0) == voltage)
242		return 0;
243
244	if (OF_is_compatible(node, "regulator-gpio") &&
245	    regulator_type(node) == REGULATOR_VOLTAGE)
246		return regulator_gpio_set(node, voltage);
247
248	return ENODEV;
249}
250
251uint32_t
252regulator_get_current(uint32_t phandle)
253{
254	struct regulator_device *rd;
255	int node;
256
257	if (phandle == 0)
258		return 0;
259
260	LIST_FOREACH(rd, &regulator_devices, rd_list) {
261		if (rd->rd_phandle == phandle)
262			break;
263	}
264
265	if (rd && rd->rd_get_current)
266		return rd->rd_get_current(rd->rd_cookie);
267
268	node = OF_getnodebyphandle(phandle);
269	if (node == 0)
270		return 0;
271
272	if (OF_is_compatible(node, "regulator-fixed"))
273		return OF_getpropint(node, "regulator-min-microamp", 0);
274
275	if (OF_is_compatible(node, "regulator-gpio") &&
276	    regulator_type(node) == REGULATOR_CURRENT)
277		return regulator_gpio_get(node);
278
279	return 0;
280}
281
282int
283regulator_set_current(uint32_t phandle, uint32_t current)
284{
285	struct regulator_device *rd;
286	uint32_t old, delta;
287	int error, node;
288
289	if (phandle == 0)
290		return ENODEV;
291
292	LIST_FOREACH(rd, &regulator_devices, rd_list) {
293		if (rd->rd_phandle == phandle)
294			break;
295	}
296
297	/* Check limits. */
298	if (rd && (current < rd->rd_amp_min || current > rd->rd_amp_max))
299		return EINVAL;
300
301	if (rd && rd->rd_set_current) {
302		old = rd->rd_get_current(rd->rd_cookie);
303		error = rd->rd_set_current(rd->rd_cookie, current);
304		if (current > old && rd->rd_ramp_delay > 0) {
305			delta = current - old;
306			delay(howmany(delta, rd->rd_ramp_delay));
307		}
308		return error;
309	}
310
311	node = OF_getnodebyphandle(phandle);
312	if (node == 0)
313		return ENODEV;
314
315	if (OF_is_compatible(node, "regulator-fixed") &&
316	    OF_getpropint(node, "regulator-min-microamp", 0) == current)
317		return 0;
318
319	if (OF_is_compatible(node, "regulator-gpio") &&
320	    regulator_type(node) == REGULATOR_CURRENT)
321		return regulator_gpio_set(node, current);
322
323	return ENODEV;
324}
325
326uint32_t
327regulator_gpio_get(int node)
328{
329	uint32_t *gpio, *gpios, *states;
330	uint32_t idx, value;
331	size_t glen, slen;
332	int i;
333
334	pinctrl_byname(node, "default");
335
336	if ((glen = OF_getproplen(node, "gpios")) <= 0)
337		return EINVAL;
338	if ((slen = OF_getproplen(node, "states")) <= 0)
339		return EINVAL;
340
341	if (slen % (2 * sizeof(uint32_t)) != 0)
342		return EINVAL;
343
344	gpios = malloc(glen, M_TEMP, M_WAITOK);
345	states = malloc(slen, M_TEMP, M_WAITOK);
346
347	OF_getpropintarray(node, "gpios", gpios, glen);
348	OF_getpropintarray(node, "states", states, slen);
349
350	i = 0;
351	idx = 0;
352	gpio = gpios;
353	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
354		if (gpio_controller_get_pin(gpio))
355			idx |= (1 << i);
356		gpio = gpio_controller_next_pin(gpio);
357		i++;
358	}
359
360	value = 0;
361	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
362		if (states[2 * i + 1] == idx) {
363			value = states[2 * i];
364			break;
365		}
366	}
367	if (i >= slen / (2 * sizeof(uint32_t)))
368		return 0;
369
370	free(gpios, M_TEMP, glen);
371	free(states, M_TEMP, slen);
372
373	return value;
374}
375
376int
377regulator_gpio_set(int node, uint32_t value)
378{
379	uint32_t *gpio, *gpios, *states;
380	size_t glen, slen;
381	uint32_t min, max;
382	uint32_t idx;
383	int i;
384
385	pinctrl_byname(node, "default");
386
387	if (regulator_type(node) == REGULATOR_VOLTAGE) {
388		min = OF_getpropint(node, "regulator-min-microvolt", 0);
389		max = OF_getpropint(node, "regulator-max-microvolt", 0);
390	}
391
392	if (regulator_type(node) == REGULATOR_CURRENT) {
393		min = OF_getpropint(node, "regulator-min-microamp", 0);
394		max = OF_getpropint(node, "regulator-max-microamp", 0);
395	}
396
397	/* Check limits. */
398	if (value < min || value > max)
399		return EINVAL;
400
401	if ((glen = OF_getproplen(node, "gpios")) <= 0)
402		return EINVAL;
403	if ((slen = OF_getproplen(node, "states")) <= 0)
404		return EINVAL;
405
406	if (slen % (2 * sizeof(uint32_t)) != 0)
407		return EINVAL;
408
409	gpios = malloc(glen, M_TEMP, M_WAITOK);
410	states = malloc(slen, M_TEMP, M_WAITOK);
411
412	OF_getpropintarray(node, "gpios", gpios, glen);
413	OF_getpropintarray(node, "states", states, slen);
414
415	idx = 0;
416	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
417		if (states[2 * i] < min || states[2 * i] > max)
418			continue;
419		if (states[2 * i] == value) {
420			idx = states[2 * i + 1];
421			break;
422		}
423	}
424	if (i >= slen / (2 * sizeof(uint32_t)))
425		return EINVAL;
426
427	i = 0;
428	gpio = gpios;
429	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
430		gpio_controller_set_pin(gpio, !!(idx & (1 << i)));
431		gpio = gpio_controller_next_pin(gpio);
432		i++;
433	}
434
435	free(gpios, M_TEMP, glen);
436	free(states, M_TEMP, slen);
437
438	return 0;
439}
440