ofw_regulator.c revision 1.8
1/*	$OpenBSD: ofw_regulator.c,v 1.8 2018/10/23 16:41:53 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	i = 0;
222	idx = 0;
223	gpio = gpios;
224	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
225		idx |= (1 << i);
226		gpio = gpio_controller_next_pin(gpio);
227		i++;
228	}
229
230	voltage = 0;
231	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
232		if (states[2 * i + 1] == idx) {
233			voltage = states[2 * i];
234			break;
235		}
236	}
237	if (i >= slen / (2 * sizeof(uint32_t)))
238		return 0;
239
240	free(gpios, M_TEMP, glen);
241	free(states, M_TEMP, slen);
242
243	return voltage;
244}
245
246int
247regulator_gpio_set_voltage(int node, uint32_t voltage)
248{
249	uint32_t *gpio, *gpios, *states;
250	size_t glen, slen;
251	uint32_t min, max;
252	uint32_t idx;
253	int i;
254
255	pinctrl_byname(node, "default");
256
257	/* Check limits. */
258	min = OF_getpropint(node, "regulator-min-microvolt", 0);
259	max = OF_getpropint(node, "regulator-max-microvolt", 0);
260	if (voltage < min || voltage > max)
261		return EINVAL;
262
263	if ((glen = OF_getproplen(node, "gpios")) <= 0)
264		return EINVAL;
265	if ((slen = OF_getproplen(node, "states")) <= 0)
266		return EINVAL;
267
268	if (slen % (2 * sizeof(uint32_t)) != 0)
269		return EINVAL;
270
271	gpios = malloc(glen, M_TEMP, M_WAITOK);
272	states = malloc(slen, M_TEMP, M_WAITOK);
273
274	OF_getpropintarray(node, "gpios", gpios, glen);
275	OF_getpropintarray(node, "states", states, slen);
276
277	idx = 0;
278	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
279		if (states[2 * i] < min || states[2 * i] > max)
280			continue;
281		if (states[2 * i] == voltage) {
282			idx = states[2 * i + 1];
283			break;
284		}
285	}
286	if (i >= slen / (2 * sizeof(uint32_t)))
287		return EINVAL;
288
289	i = 0;
290	gpio = gpios;
291	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
292		gpio_controller_set_pin(gpio, !!(idx & (1 << i)));
293		gpio = gpio_controller_next_pin(gpio);
294		i++;
295	}
296
297	free(gpios, M_TEMP, glen);
298	free(states, M_TEMP, slen);
299
300	return 0;
301}
302