fdt_pinctrl.c revision 1.8
1/* $NetBSD: fdt_pinctrl.c,v 1.8 2019/02/27 16:56:00 jakllsch Exp $ */
2
3/*-
4 * Copyright (c) 2019 Jason R. Thorpe
5 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
6 * Copyright (c) 2015 Martin Fouts
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32__KERNEL_RCSID(0, "$NetBSD: fdt_pinctrl.c,v 1.8 2019/02/27 16:56:00 jakllsch Exp $");
33
34#include <sys/param.h>
35#include <sys/bus.h>
36#include <sys/gpio.h>
37#include <sys/kmem.h>
38#include <sys/queue.h>
39
40#include <libfdt.h>
41#include <dev/fdt/fdtvar.h>
42
43struct fdtbus_pinctrl_controller {
44	device_t pc_dev;
45	int pc_phandle;
46	const struct fdtbus_pinctrl_controller_func *pc_funcs;
47
48	LIST_ENTRY(fdtbus_pinctrl_controller) pc_next;
49};
50
51static LIST_HEAD(, fdtbus_pinctrl_controller) fdtbus_pinctrl_controllers =
52    LIST_HEAD_INITIALIZER(fdtbus_pinctrl_controllers);
53
54int
55fdtbus_register_pinctrl_config(device_t dev, int phandle,
56    const struct fdtbus_pinctrl_controller_func *funcs)
57{
58	struct fdtbus_pinctrl_controller *pc;
59
60	pc = kmem_alloc(sizeof(*pc), KM_SLEEP);
61	pc->pc_dev = dev;
62	pc->pc_phandle = phandle;
63	pc->pc_funcs = funcs;
64
65	LIST_INSERT_HEAD(&fdtbus_pinctrl_controllers, pc, pc_next);
66
67	return 0;
68}
69
70static struct fdtbus_pinctrl_controller *
71fdtbus_pinctrl_lookup(int phandle)
72{
73	struct fdtbus_pinctrl_controller *pc;
74
75	LIST_FOREACH(pc, &fdtbus_pinctrl_controllers, pc_next) {
76		if (pc->pc_phandle == phandle)
77			return pc;
78	}
79
80	return NULL;
81}
82
83int
84fdtbus_pinctrl_set_config_index(int phandle, u_int index)
85{
86	struct fdtbus_pinctrl_controller *pc;
87	const u_int *pinctrl_data;
88	char buf[16];
89	u_int xref, pinctrl_cells;
90	int len, error;
91
92	snprintf(buf, sizeof(buf), "pinctrl-%u", index);
93
94	pinctrl_data = fdtbus_get_prop(phandle, buf, &len);
95	if (pinctrl_data == NULL)
96		return ENOENT;
97
98	while (len > 0) {
99		xref = fdtbus_get_phandle_from_native(be32toh(pinctrl_data[0]));
100		pc = fdtbus_pinctrl_lookup(xref);
101		if (pc == NULL)
102			return ENXIO;
103
104		if (of_getprop_uint32(OF_parent(xref), "#pinctrl-cells", &pinctrl_cells) != 0)
105			pinctrl_cells = 1;
106
107		error = pc->pc_funcs->set_config(pc->pc_dev, pinctrl_data, pinctrl_cells * 4);
108		if (error != 0)
109			return error;
110
111		pinctrl_data += pinctrl_cells;
112		len -= (pinctrl_cells * 4);
113	}
114
115	return 0;
116}
117
118int
119fdtbus_pinctrl_set_config(int phandle, const char *cfgname)
120{
121	u_int index;
122	int err;
123
124	err = fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index);
125	if (err != 0)
126		return -1;
127
128	return fdtbus_pinctrl_set_config_index(phandle, index);
129}
130
131static void
132fdtbus_pinctrl_configure_node(int phandle)
133{
134	char buf[256];
135	int child, error;
136
137	for (child = OF_child(phandle); child; child = OF_peer(child)) {
138		if (!fdtbus_status_okay(child))
139			continue;
140
141		/* Configure child nodes */
142		fdtbus_pinctrl_configure_node(child);
143
144		/*
145		 * Set configuration 0 for this node. This may fail if the
146		 * pinctrl provider is missing; that's OK, we will re-configure
147		 * when that provider attaches.
148		 */
149		fdtbus_get_path(child, buf, sizeof(buf));
150		error = fdtbus_pinctrl_set_config_index(child, 0);
151		if (error == 0)
152			aprint_debug("pinctrl: set config pinctrl-0 for %s\n", buf);
153		else if (error != ENOENT)
154			aprint_debug("pinctrl: failed to set config pinctrl-0 for %s: %d\n", buf, error);
155	}
156}
157
158void
159fdtbus_pinctrl_configure(void)
160{
161	fdtbus_pinctrl_configure_node(OF_finddevice("/"));
162}
163
164/*
165 * Helper routines for parsing put properties related to pinctrl bindings.
166 */
167
168/*
169 * Pin mux settings apply to sets of pins specified by one of 3
170 * sets of properties:
171 *
172 *	- "pins" + "function"
173 *	- "groups" + "function"
174 *	- "pinmux"
175 *
176 * Eactly one of those 3 combinations must be specified.
177 */
178
179const char *
180fdtbus_pinctrl_parse_function(int phandle)
181{
182	return fdtbus_get_string(phandle, "function");
183}
184
185const void *
186fdtbus_pinctrl_parse_pins(int phandle, int *pins_len)
187{
188	int len;
189
190	/*
191	 * The pinctrl bindings specify that entries in "pins"
192	 * may be integers or strings; this is determined by
193	 * the hardware-specific binding.
194	 */
195
196	len = OF_getproplen(phandle, "pins");
197	if (len > 0) {
198		return fdtbus_get_prop(phandle, "pins", pins_len);
199	}
200
201	return NULL;
202}
203
204const char *
205fdtbus_pinctrl_parse_groups(int phandle, int *groups_len)
206{
207	int len;
208
209	len = OF_getproplen(phandle, "groups");
210	if (len > 0) {
211		*groups_len = len;
212		return fdtbus_get_string(phandle, "groups");
213	}
214
215	return NULL;
216}
217
218const u_int *
219fdtbus_pinctrl_parse_pinmux(int phandle, int *pinmux_len)
220{
221	int len;
222
223	len = OF_getproplen(phandle, "pinmux");
224	if (len > 0) {
225		return fdtbus_get_prop(phandle, "pinmux", pinmux_len);
226	}
227
228	return NULL;
229}
230
231int
232fdtbus_pinctrl_parse_bias(int phandle, int *pull_strength)
233{
234	const char *bias_prop = NULL;
235	int bias = -1;
236
237	/*
238	 * bias-pull-{up,down,pin-default} properties have an optional
239	 * argument: the pull strength in Ohms.  (In practice, this is
240	 * sometimes a hardware-specific constant.)
241	 *
242	 * XXXJRT How to represent bias-pull-pin-default?
243	 */
244
245	if (of_hasprop(phandle, "bias-disable")) {
246		bias = 0;
247	} else if (of_hasprop(phandle, "bias-pull-up")) {
248		bias_prop = "bias-pull-up";
249		bias = GPIO_PIN_PULLUP;
250	} else if (of_hasprop(phandle, "bias-pull-down")) {
251		bias_prop = "bias-pull-down";
252		bias = GPIO_PIN_PULLDOWN;
253	}
254
255	if (pull_strength) {
256		*pull_strength = -1;
257		if (bias_prop) {
258			uint32_t val;
259			if (of_getprop_uint32(phandle, bias_prop, &val) == 0) {
260				*pull_strength = (int)val;
261			}
262		}
263	}
264
265	return bias;
266}
267
268int
269fdtbus_pinctrl_parse_drive(int phandle)
270{
271	int drive = -1;
272
273	if (of_hasprop(phandle, "drive-push-pull"))
274		drive = GPIO_PIN_PUSHPULL;
275	else if (of_hasprop(phandle, "drive-open-drain"))
276		drive = GPIO_PIN_OPENDRAIN;
277	else if (of_hasprop(phandle, "drive-open-source"))
278		drive = 0;
279
280	return drive;
281}
282
283int
284fdtbus_pinctrl_parse_drive_strength(int phandle)
285{
286	int val;
287
288	/*
289	 * drive-strength has as an argument the target strength
290	 * in mA.
291	 */
292
293	if (of_getprop_uint32(phandle, "drive-strength", &val) == 0)
294		return val;
295
296	return -1;
297}
298int fdtbus_pinctrl_parse_input_output(int phandle, int *output_value)
299{
300	int direction = -1;
301	int pinval = -1;
302
303	if (of_hasprop(phandle, "input-enable")) {
304		direction = GPIO_PIN_INPUT;
305	} else if (of_hasprop(phandle, "input-disable")) {
306		/*
307		 * XXXJRT How to represent this?  This is more than
308		 * just "don't set the direction" - it's an active
309		 * command that might involve disabling an input
310		 * buffer on the pin.
311		 */
312	}
313
314	if (of_hasprop(phandle, "output-enable")) {
315		if (direction == -1)
316			direction = 0;
317		direction |= GPIO_PIN_OUTPUT;
318	} else if (of_hasprop(phandle, "output-disable")) {
319		if (direction == -1)
320			direction = 0;
321		direction |= GPIO_PIN_TRISTATE;
322	}
323
324	if (of_hasprop(phandle, "output-low")) {
325		if (direction == -1)
326			direction = 0;
327		direction |= GPIO_PIN_OUTPUT;
328		pinval = GPIO_PIN_LOW;
329	} else if (of_hasprop(phandle, "output-high")) {
330		if (direction == -1)
331			direction = 0;
332		direction |= GPIO_PIN_OUTPUT;
333		pinval = GPIO_PIN_HIGH;
334	}
335
336	if (output_value)
337		*output_value = pinval;
338
339	/*
340	 * XXX input-schmitt-enable
341	 * XXX input-schmitt-disable
342	 */
343
344	if (direction != -1
345	    && (direction & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
346			 == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
347		direction |= GPIO_PIN_INOUT;
348	}
349
350	return direction;
351}
352