ofw_regulator.c revision 1.4
1/*	$OpenBSD: ofw_regulator.c,v 1.4 2017/12/18 09:13:47 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
27LIST_HEAD(, regulator_device) regulator_devices =
28	LIST_HEAD_INITIALIZER(regulator_devices);
29
30void
31regulator_register(struct regulator_device *rd)
32{
33	rd->rd_min = OF_getpropint(rd->rd_node, "regulator-min-microvolt", 0);
34	rd->rd_max = OF_getpropint(rd->rd_node, "regulator-max-microvolt", ~0);
35	KASSERT(rd->rd_min <= rd->rd_max);
36
37	if (rd->rd_get_voltage && rd->rd_set_voltage) {
38		uint32_t voltage = rd->rd_get_voltage(rd->rd_cookie);
39		if (voltage < rd->rd_min)
40			rd->rd_set_voltage(rd->rd_cookie, rd->rd_min);
41		if (voltage > rd->rd_max)
42			rd->rd_set_voltage(rd->rd_cookie, rd->rd_max);
43	}
44
45	rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0);
46	if (rd->rd_phandle == 0)
47		return;
48
49	LIST_INSERT_HEAD(&regulator_devices, rd, rd_list);
50}
51
52int
53regulator_fixed_set(int node, int enable)
54{
55	uint32_t *gpio;
56	uint32_t startup_delay;
57	int active;
58	int len;
59
60	pinctrl_byname(node, "default");
61
62	if (OF_getproplen(node, "enable-active-high") == 0)
63		active = 1;
64	else
65		active = 0;
66
67	/* The "gpio" property is optional. */
68	len = OF_getproplen(node, "gpio");
69	if (len < 0)
70		return 0;
71
72	gpio = malloc(len, M_TEMP, M_WAITOK);
73	OF_getpropintarray(node, "gpio", gpio, len);
74	gpio_controller_config_pin(gpio, GPIO_CONFIG_OUTPUT);
75	if (enable)
76		gpio_controller_set_pin(gpio, active);
77	else
78		gpio_controller_set_pin(gpio, !active);
79	free(gpio, M_TEMP, len);
80
81	startup_delay = OF_getpropint(node, "startup-delay-us", 0);
82	if (enable && startup_delay > 0)
83		delay(startup_delay);
84
85	return 0;
86}
87
88int
89regulator_set(uint32_t phandle, int enable)
90{
91	struct regulator_device *rd;
92	int node;
93
94	node = OF_getnodebyphandle(phandle);
95	if (node == 0)
96		return ENODEV;
97
98	/* Don't mess around with regulators that are always on. */
99	if (OF_getproplen(node, "regulator-always-on") == 0)
100		return 0;
101
102	LIST_FOREACH(rd, &regulator_devices, rd_list) {
103		if (rd->rd_phandle == phandle)
104			break;
105	}
106
107	if (rd && rd->rd_enable)
108		return rd->rd_enable(rd->rd_cookie, enable);
109
110	if (OF_is_compatible(node, "regulator-fixed"))
111		return regulator_fixed_set(node, enable);
112
113	return ENODEV;
114}
115
116int
117regulator_enable(uint32_t phandle)
118{
119	return regulator_set(phandle, 1);
120}
121
122int
123regulator_disable(uint32_t phandle)
124{
125	return regulator_set(phandle, 0);
126}
127
128uint32_t
129regulator_get_voltage(uint32_t phandle)
130{
131	struct regulator_device *rd;
132	int node;
133
134	LIST_FOREACH(rd, &regulator_devices, rd_list) {
135		if (rd->rd_phandle == phandle)
136			break;
137	}
138
139	if (rd && rd->rd_get_voltage)
140		return rd->rd_get_voltage(rd->rd_cookie);
141
142	node = OF_getnodebyphandle(phandle);
143	if (node == 0)
144		return 0;
145
146	if (OF_is_compatible(node, "regulator-fixed"))
147		return OF_getpropint(node, "regulator-min-voltage", 0);
148
149	return 0;
150}
151
152int
153regulator_set_voltage(uint32_t phandle, uint32_t voltage)
154{
155	struct regulator_device *rd;
156
157	LIST_FOREACH(rd, &regulator_devices, rd_list) {
158		if (rd->rd_phandle == phandle)
159			break;
160	}
161
162	/* Check limits. */
163	if (rd && (voltage < rd->rd_min || voltage > rd->rd_max))
164		return EINVAL;
165
166	if (rd && rd->rd_set_voltage)
167		return rd->rd_set_voltage(rd->rd_cookie, voltage);
168
169	return ENODEV;
170}
171