ofw_regulator.c revision 1.13
1/*	$OpenBSD: ofw_regulator.c,v 1.13 2019/04/30 19:53: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
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 active;
96	int len;
97
98	pinctrl_byname(node, "default");
99
100	if (OF_getproplen(node, "enable-active-high") == 0)
101		active = 1;
102	else
103		active = 0;
104
105	/* The "gpio" property is optional. */
106	len = OF_getproplen(node, "gpio");
107	if (len < 0)
108		return 0;
109
110	gpio = malloc(len, M_TEMP, M_WAITOK);
111	OF_getpropintarray(node, "gpio", gpio, len);
112	gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
113	if (enable)
114		gpio_controller_set_pin(gpio, active);
115	else
116		gpio_controller_set_pin(gpio, !active);
117	free(gpio, M_TEMP, len);
118
119	startup_delay = OF_getpropint(node, "startup-delay-us", 0);
120	if (enable && startup_delay > 0)
121		delay(startup_delay);
122
123	return 0;
124}
125
126int
127regulator_set(uint32_t phandle, int enable)
128{
129	struct regulator_device *rd;
130	int node;
131
132	node = OF_getnodebyphandle(phandle);
133	if (node == 0)
134		return ENODEV;
135
136	/* Never turn off regulators that should always be on. */
137	if (OF_getproplen(node, "regulator-always-on") == 0 && !enable)
138		return 0;
139
140	LIST_FOREACH(rd, &regulator_devices, rd_list) {
141		if (rd->rd_phandle == phandle)
142			break;
143	}
144
145	if (rd && rd->rd_enable)
146		return rd->rd_enable(rd->rd_cookie, enable);
147
148	if (OF_is_compatible(node, "regulator-fixed"))
149		return regulator_fixed_set(node, enable);
150
151	return ENODEV;
152}
153
154int
155regulator_enable(uint32_t phandle)
156{
157	return regulator_set(phandle, 1);
158}
159
160int
161regulator_disable(uint32_t phandle)
162{
163	return regulator_set(phandle, 0);
164}
165
166uint32_t
167regulator_get_voltage(uint32_t phandle)
168{
169	struct regulator_device *rd;
170	int node;
171
172	LIST_FOREACH(rd, &regulator_devices, rd_list) {
173		if (rd->rd_phandle == phandle)
174			break;
175	}
176
177	if (rd && rd->rd_get_voltage)
178		return rd->rd_get_voltage(rd->rd_cookie);
179
180	node = OF_getnodebyphandle(phandle);
181	if (node == 0)
182		return 0;
183
184	if (OF_is_compatible(node, "regulator-fixed"))
185		return OF_getpropint(node, "regulator-min-microvolt", 0);
186
187	if (OF_is_compatible(node, "regulator-gpio") &&
188	    regulator_type(node) == REGULATOR_VOLTAGE)
189		return regulator_gpio_get(node);
190
191	return 0;
192}
193
194int
195regulator_set_voltage(uint32_t phandle, uint32_t voltage)
196{
197	struct regulator_device *rd;
198	uint32_t old, delta;
199	int error, node;
200
201	LIST_FOREACH(rd, &regulator_devices, rd_list) {
202		if (rd->rd_phandle == phandle)
203			break;
204	}
205
206	/* Check limits. */
207	if (rd && (voltage < rd->rd_volt_min || voltage > rd->rd_volt_max))
208		return EINVAL;
209
210	if (rd && rd->rd_set_voltage) {
211		old = rd->rd_get_voltage(rd->rd_cookie);
212		error = rd->rd_set_voltage(rd->rd_cookie, voltage);
213		if (voltage > old && rd->rd_ramp_delay > 0) {
214			delta = voltage - old;
215			delay(howmany(delta, rd->rd_ramp_delay));
216		}
217		return error;
218	}
219
220	node = OF_getnodebyphandle(phandle);
221	if (node == 0)
222		return ENODEV;
223
224	if (OF_is_compatible(node, "regulator-fixed") &&
225	    OF_getpropint(node, "regulator-min-microvolt", 0) == voltage)
226		return 0;
227
228	if (OF_is_compatible(node, "regulator-gpio") &&
229	    regulator_type(node) == REGULATOR_VOLTAGE)
230		return regulator_gpio_set(node, voltage);
231
232	return ENODEV;
233}
234
235uint32_t
236regulator_get_current(uint32_t phandle)
237{
238	struct regulator_device *rd;
239	int node;
240
241	LIST_FOREACH(rd, &regulator_devices, rd_list) {
242		if (rd->rd_phandle == phandle)
243			break;
244	}
245
246	if (rd && rd->rd_get_current)
247		return rd->rd_get_current(rd->rd_cookie);
248
249	node = OF_getnodebyphandle(phandle);
250	if (node == 0)
251		return 0;
252
253	if (OF_is_compatible(node, "regulator-fixed"))
254		return OF_getpropint(node, "regulator-min-microamp", 0);
255
256	if (OF_is_compatible(node, "regulator-gpio") &&
257	    regulator_type(node) == REGULATOR_CURRENT)
258		return regulator_gpio_get(node);
259
260	return 0;
261}
262
263int
264regulator_set_current(uint32_t phandle, uint32_t current)
265{
266	struct regulator_device *rd;
267	uint32_t old, delta;
268	int error, node;
269
270	LIST_FOREACH(rd, &regulator_devices, rd_list) {
271		if (rd->rd_phandle == phandle)
272			break;
273	}
274
275	/* Check limits. */
276	if (rd && (current < rd->rd_amp_min || current > rd->rd_amp_max))
277		return EINVAL;
278
279	if (rd && rd->rd_set_current) {
280		old = rd->rd_get_current(rd->rd_cookie);
281		error = rd->rd_set_current(rd->rd_cookie, current);
282		if (current > old && rd->rd_ramp_delay > 0) {
283			delta = current - old;
284			delay(howmany(delta, rd->rd_ramp_delay));
285		}
286		return error;
287	}
288
289	node = OF_getnodebyphandle(phandle);
290	if (node == 0)
291		return ENODEV;
292
293	if (OF_is_compatible(node, "regulator-fixed") &&
294	    OF_getpropint(node, "regulator-min-microamp", 0) == current)
295		return 0;
296
297	if (OF_is_compatible(node, "regulator-gpio") &&
298	    regulator_type(node) == REGULATOR_CURRENT)
299		return regulator_gpio_set(node, current);
300
301	return ENODEV;
302}
303
304uint32_t
305regulator_gpio_get(int node)
306{
307	uint32_t *gpio, *gpios, *states;
308	uint32_t idx, value;
309	size_t glen, slen;
310	int i;
311
312	pinctrl_byname(node, "default");
313
314	if ((glen = OF_getproplen(node, "gpios")) <= 0)
315		return EINVAL;
316	if ((slen = OF_getproplen(node, "states")) <= 0)
317		return EINVAL;
318
319	if (slen % (2 * sizeof(uint32_t)) != 0)
320		return EINVAL;
321
322	gpios = malloc(glen, M_TEMP, M_WAITOK);
323	states = malloc(slen, M_TEMP, M_WAITOK);
324
325	OF_getpropintarray(node, "gpios", gpios, glen);
326	OF_getpropintarray(node, "states", states, slen);
327
328	i = 0;
329	idx = 0;
330	gpio = gpios;
331	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
332		if (gpio_controller_get_pin(gpio))
333			idx |= (1 << i);
334		gpio = gpio_controller_next_pin(gpio);
335		i++;
336	}
337
338	value = 0;
339	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
340		if (states[2 * i + 1] == idx) {
341			value = states[2 * i];
342			break;
343		}
344	}
345	if (i >= slen / (2 * sizeof(uint32_t)))
346		return 0;
347
348	free(gpios, M_TEMP, glen);
349	free(states, M_TEMP, slen);
350
351	return value;
352}
353
354int
355regulator_gpio_set(int node, uint32_t value)
356{
357	uint32_t *gpio, *gpios, *states;
358	size_t glen, slen;
359	uint32_t min, max;
360	uint32_t idx;
361	int i;
362
363	pinctrl_byname(node, "default");
364
365	if (regulator_type(node) == REGULATOR_VOLTAGE) {
366		min = OF_getpropint(node, "regulator-min-microvolt", 0);
367		max = OF_getpropint(node, "regulator-max-microvolt", 0);
368	}
369
370	if (regulator_type(node) == REGULATOR_CURRENT) {
371		min = OF_getpropint(node, "regulator-min-microamp", 0);
372		max = OF_getpropint(node, "regulator-max-microamp", 0);
373	}
374
375	/* Check limits. */
376	if (value < min || value > max)
377		return EINVAL;
378
379	if ((glen = OF_getproplen(node, "gpios")) <= 0)
380		return EINVAL;
381	if ((slen = OF_getproplen(node, "states")) <= 0)
382		return EINVAL;
383
384	if (slen % (2 * sizeof(uint32_t)) != 0)
385		return EINVAL;
386
387	gpios = malloc(glen, M_TEMP, M_WAITOK);
388	states = malloc(slen, M_TEMP, M_WAITOK);
389
390	OF_getpropintarray(node, "gpios", gpios, glen);
391	OF_getpropintarray(node, "states", states, slen);
392
393	idx = 0;
394	for (i = 0; i < slen / (2 * sizeof(uint32_t)); i++) {
395		if (states[2 * i] < min || states[2 * i] > max)
396			continue;
397		if (states[2 * i] == value) {
398			idx = states[2 * i + 1];
399			break;
400		}
401	}
402	if (i >= slen / (2 * sizeof(uint32_t)))
403		return EINVAL;
404
405	i = 0;
406	gpio = gpios;
407	while (gpio && gpio < gpios + (glen / sizeof(uint32_t))) {
408		gpio_controller_set_pin(gpio, !!(idx & (1 << i)));
409		gpio = gpio_controller_next_pin(gpio);
410		i++;
411	}
412
413	free(gpios, M_TEMP, glen);
414	free(states, M_TEMP, slen);
415
416	return 0;
417}
418