ofw_regulator.c revision 1.6
1/*	$OpenBSD: ofw_regulator.c,v 1.6 2018/08/13 15:14:27 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-voltage", 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-gpio"))
192		return regulator_gpio_set_voltage(node, voltage);
193
194	return ENODEV;
195}
196
197uint32_t
198regulator_gpio_get_voltage(int node)
199{
200	uint32_t *gpio, *gpios, *states;
201	uint32_t idx, voltage;
202	size_t glen, slen;
203	int i;
204
205	pinctrl_byname(node, "default");
206
207	if ((glen = OF_getproplen(node, "gpios")) <= 0)
208		return EINVAL;
209	if ((slen = OF_getproplen(node, "states")) <= 0)
210		return EINVAL;
211
212	if (slen % (2 * sizeof(uint32_t)) != 0)
213		return EINVAL;
214
215	gpios = malloc(glen, M_TEMP, M_WAITOK);
216	states = malloc(slen, M_TEMP, M_WAITOK);
217
218	OF_getpropintarray(node, "gpios", gpios, glen);
219	OF_getpropintarray(node, "states", states, slen);
220
221	idx = 0;
222	gpio = gpios;
223	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
224		idx |= (1 << i);
225		gpio = gpio_controller_next_pin(gpio);
226		i++;
227	}
228
229	voltage = 0;
230	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
231		if (states[2 * i + 1] == idx) {
232			voltage = states[2 * i];
233			break;
234		}
235	}
236	if (i >= slen / (2 * sizeof(uint32_t)))
237		return 0;
238
239	free(gpios, M_TEMP, glen);
240	free(states, M_TEMP, slen);
241
242	return voltage;
243}
244
245int
246regulator_gpio_set_voltage(int node, uint32_t voltage)
247{
248	uint32_t *gpio, *gpios, *states;
249	size_t glen, slen;
250	uint32_t min, max;
251	uint32_t idx;
252	int i;
253
254	pinctrl_byname(node, "default");
255
256	/* Check limits. */
257	min = OF_getpropint(node, "regulator-min-microvolt", 0);
258	max = OF_getpropint(node, "regulator-max-microvolt", 0);
259	if (voltage < min || voltage > max)
260		return EINVAL;
261
262	if ((glen = OF_getproplen(node, "gpios")) <= 0)
263		return EINVAL;
264	if ((slen = OF_getproplen(node, "states")) <= 0)
265		return EINVAL;
266
267	if (slen % (2 * sizeof(uint32_t)) != 0)
268		return EINVAL;
269
270	gpios = malloc(glen, M_TEMP, M_WAITOK);
271	states = malloc(slen, M_TEMP, M_WAITOK);
272
273	OF_getpropintarray(node, "gpios", gpios, glen);
274	OF_getpropintarray(node, "states", states, slen);
275
276	idx = 0;
277	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
278		if (states[2 * i] < min || states[2 * i] > max)
279			continue;
280		if (states[2 * i] == voltage)
281			idx = states[2 * i + 1];
282	}
283	if (i >= slen / (2 * sizeof(uint32_t)))
284		return EINVAL;
285
286	i = 0;
287	gpio = gpios;
288	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
289		gpio_controller_set_pin(gpio, !!(idx & (1 << i)));
290		gpio = gpio_controller_next_pin(gpio);
291		i++;
292	}
293
294	free(gpios, M_TEMP, glen);
295	free(states, M_TEMP, slen);
296
297	return 0;
298}
299