ofw_regulator.c revision 1.10
1/*	$OpenBSD: ofw_regulator.c,v 1.10 2019/01/02 18:50:15 patrick 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
27LIST_HEAD(, regulator_device) regulator_devices =
28	LIST_HEAD_INITIALIZER(regulator_devices);
29
30uint32_t regulator_gpio_get_voltage(int);
31int regulator_gpio_set_voltage(int, uint32_t);
32
33void
34regulator_register(struct regulator_device *rd)
35{
36	rd->rd_min = OF_getpropint(rd->rd_node, "regulator-min-microvolt", 0);
37	rd->rd_max = OF_getpropint(rd->rd_node, "regulator-max-microvolt", ~0);
38	KASSERT(rd->rd_min <= rd->rd_max);
39
40	rd->rd_ramp_delay =
41	    OF_getpropint(rd->rd_node, "regulator-ramp-delay", 0);
42
43	if (rd->rd_get_voltage && rd->rd_set_voltage) {
44		uint32_t voltage = rd->rd_get_voltage(rd->rd_cookie);
45		if (voltage < rd->rd_min)
46			rd->rd_set_voltage(rd->rd_cookie, rd->rd_min);
47		if (voltage > rd->rd_max)
48			rd->rd_set_voltage(rd->rd_cookie, rd->rd_max);
49	}
50
51	rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0);
52	if (rd->rd_phandle == 0)
53		return;
54
55	LIST_INSERT_HEAD(&regulator_devices, rd, rd_list);
56}
57
58int
59regulator_fixed_set(int node, int enable)
60{
61	uint32_t *gpio;
62	uint32_t startup_delay;
63	int active;
64	int len;
65
66	pinctrl_byname(node, "default");
67
68	if (OF_getproplen(node, "enable-active-high") == 0)
69		active = 1;
70	else
71		active = 0;
72
73	/* The "gpio" property is optional. */
74	len = OF_getproplen(node, "gpio");
75	if (len < 0)
76		return 0;
77
78	gpio = malloc(len, M_TEMP, M_WAITOK);
79	OF_getpropintarray(node, "gpio", gpio, len);
80	gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
81	if (enable)
82		gpio_controller_set_pin(gpio, active);
83	else
84		gpio_controller_set_pin(gpio, !active);
85	free(gpio, M_TEMP, len);
86
87	startup_delay = OF_getpropint(node, "startup-delay-us", 0);
88	if (enable && startup_delay > 0)
89		delay(startup_delay);
90
91	return 0;
92}
93
94int
95regulator_set(uint32_t phandle, int enable)
96{
97	struct regulator_device *rd;
98	int node;
99
100	node = OF_getnodebyphandle(phandle);
101	if (node == 0)
102		return ENODEV;
103
104	/* Don't mess around with regulators that are always on. */
105	if (OF_getproplen(node, "regulator-always-on") == 0)
106		return 0;
107
108	LIST_FOREACH(rd, &regulator_devices, rd_list) {
109		if (rd->rd_phandle == phandle)
110			break;
111	}
112
113	if (rd && rd->rd_enable)
114		return rd->rd_enable(rd->rd_cookie, enable);
115
116	if (OF_is_compatible(node, "regulator-fixed"))
117		return regulator_fixed_set(node, enable);
118
119	return ENODEV;
120}
121
122int
123regulator_enable(uint32_t phandle)
124{
125	return regulator_set(phandle, 1);
126}
127
128int
129regulator_disable(uint32_t phandle)
130{
131	return regulator_set(phandle, 0);
132}
133
134uint32_t
135regulator_get_voltage(uint32_t phandle)
136{
137	struct regulator_device *rd;
138	int node;
139
140	LIST_FOREACH(rd, &regulator_devices, rd_list) {
141		if (rd->rd_phandle == phandle)
142			break;
143	}
144
145	if (rd && rd->rd_get_voltage)
146		return rd->rd_get_voltage(rd->rd_cookie);
147
148	node = OF_getnodebyphandle(phandle);
149	if (node == 0)
150		return 0;
151
152	if (OF_is_compatible(node, "regulator-fixed"))
153		return OF_getpropint(node, "regulator-min-microvolt", 0);
154
155	if (OF_is_compatible(node, "regulator-gpio"))
156		return regulator_gpio_get_voltage(node);
157
158	return 0;
159}
160
161int
162regulator_set_voltage(uint32_t phandle, uint32_t voltage)
163{
164	struct regulator_device *rd;
165	uint32_t old, delta;
166	int error, node;
167
168	LIST_FOREACH(rd, &regulator_devices, rd_list) {
169		if (rd->rd_phandle == phandle)
170			break;
171	}
172
173	/* Check limits. */
174	if (rd && (voltage < rd->rd_min || voltage > rd->rd_max))
175		return EINVAL;
176
177	if (rd && rd->rd_set_voltage) {
178		old = rd->rd_get_voltage(rd->rd_cookie);
179		error = rd->rd_set_voltage(rd->rd_cookie, voltage);
180		if (voltage > old && rd->rd_ramp_delay > 0) {
181			delta = voltage - old;
182			delay(howmany(delta, rd->rd_ramp_delay));
183		}
184		return error;
185	}
186
187	node = OF_getnodebyphandle(phandle);
188	if (node == 0)
189		return ENODEV;
190
191	if (OF_is_compatible(node, "regulator-fixed") &&
192	    OF_getpropint(node, "regulator-min-microvolt", 0) == voltage)
193		return 0;
194
195	if (OF_is_compatible(node, "regulator-gpio"))
196		return regulator_gpio_set_voltage(node, voltage);
197
198	return ENODEV;
199}
200
201uint32_t
202regulator_gpio_get_voltage(int node)
203{
204	uint32_t *gpio, *gpios, *states;
205	uint32_t idx, voltage;
206	size_t glen, slen;
207	int i;
208
209	pinctrl_byname(node, "default");
210
211	if ((glen = OF_getproplen(node, "gpios")) <= 0)
212		return EINVAL;
213	if ((slen = OF_getproplen(node, "states")) <= 0)
214		return EINVAL;
215
216	if (slen % (2 * sizeof(uint32_t)) != 0)
217		return EINVAL;
218
219	gpios = malloc(glen, M_TEMP, M_WAITOK);
220	states = malloc(slen, M_TEMP, M_WAITOK);
221
222	OF_getpropintarray(node, "gpios", gpios, glen);
223	OF_getpropintarray(node, "states", states, slen);
224
225	i = 0;
226	idx = 0;
227	gpio = gpios;
228	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
229		idx |= (1 << i);
230		gpio = gpio_controller_next_pin(gpio);
231		i++;
232	}
233
234	voltage = 0;
235	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
236		if (states[2 * i + 1] == idx) {
237			voltage = states[2 * i];
238			break;
239		}
240	}
241	if (i >= slen / (2 * sizeof(uint32_t)))
242		return 0;
243
244	free(gpios, M_TEMP, glen);
245	free(states, M_TEMP, slen);
246
247	return voltage;
248}
249
250int
251regulator_gpio_set_voltage(int node, uint32_t voltage)
252{
253	uint32_t *gpio, *gpios, *states;
254	size_t glen, slen;
255	uint32_t min, max;
256	uint32_t idx;
257	int i;
258
259	pinctrl_byname(node, "default");
260
261	/* Check limits. */
262	min = OF_getpropint(node, "regulator-min-microvolt", 0);
263	max = OF_getpropint(node, "regulator-max-microvolt", 0);
264	if (voltage < min || voltage > max)
265		return EINVAL;
266
267	if ((glen = OF_getproplen(node, "gpios")) <= 0)
268		return EINVAL;
269	if ((slen = OF_getproplen(node, "states")) <= 0)
270		return EINVAL;
271
272	if (slen % (2 * sizeof(uint32_t)) != 0)
273		return EINVAL;
274
275	gpios = malloc(glen, M_TEMP, M_WAITOK);
276	states = malloc(slen, M_TEMP, M_WAITOK);
277
278	OF_getpropintarray(node, "gpios", gpios, glen);
279	OF_getpropintarray(node, "states", states, slen);
280
281	idx = 0;
282	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
283		if (states[2 * i] < min || states[2 * i] > max)
284			continue;
285		if (states[2 * i] == voltage) {
286			idx = states[2 * i + 1];
287			break;
288		}
289	}
290	if (i >= slen / (2 * sizeof(uint32_t)))
291		return EINVAL;
292
293	i = 0;
294	gpio = gpios;
295	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
296		gpio_controller_set_pin(gpio, !!(idx & (1 << i)));
297		gpio = gpio_controller_next_pin(gpio);
298		i++;
299	}
300
301	free(gpios, M_TEMP, glen);
302	free(states, M_TEMP, slen);
303
304	return 0;
305}
306