xref: /netbsd-src/sys/dev/fdt/fdt_pinctrl.c (revision 0060d28c56adf0059b0cfd00e55cdcb1012243cc)
1*0060d28cSjmcneill /* $NetBSD: fdt_pinctrl.c,v 1.10 2019/10/01 23:32:52 jmcneill Exp $ */
2737457c0Smarty 
3737457c0Smarty /*-
4dfaf6e79Sthorpej  * Copyright (c) 2019 Jason R. Thorpe
5b5a701b9Sjmcneill  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
67e1b70caSmarty  * Copyright (c) 2015 Martin Fouts
7737457c0Smarty  * All rights reserved.
8737457c0Smarty  *
9737457c0Smarty  * Redistribution and use in source and binary forms, with or without
10737457c0Smarty  * modification, are permitted provided that the following conditions
11737457c0Smarty  * are met:
12737457c0Smarty  * 1. Redistributions of source code must retain the above copyright
13737457c0Smarty  *    notice, this list of conditions and the following disclaimer.
14737457c0Smarty  * 2. Redistributions in binary form must reproduce the above copyright
15737457c0Smarty  *    notice, this list of conditions and the following disclaimer in the
16737457c0Smarty  *    documentation and/or other materials provided with the distribution.
17737457c0Smarty  *
18737457c0Smarty  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19737457c0Smarty  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20737457c0Smarty  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21737457c0Smarty  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22737457c0Smarty  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23737457c0Smarty  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24737457c0Smarty  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25737457c0Smarty  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26737457c0Smarty  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27737457c0Smarty  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28737457c0Smarty  * SUCH DAMAGE.
29737457c0Smarty  */
30737457c0Smarty 
31737457c0Smarty #include <sys/cdefs.h>
32*0060d28cSjmcneill __KERNEL_RCSID(0, "$NetBSD: fdt_pinctrl.c,v 1.10 2019/10/01 23:32:52 jmcneill Exp $");
33737457c0Smarty 
34737457c0Smarty #include <sys/param.h>
35737457c0Smarty #include <sys/bus.h>
36dfaf6e79Sthorpej #include <sys/gpio.h>
37737457c0Smarty #include <sys/kmem.h>
38233c5d85Sjmcneill #include <sys/queue.h>
39737457c0Smarty 
40737457c0Smarty #include <libfdt.h>
41737457c0Smarty #include <dev/fdt/fdtvar.h>
42737457c0Smarty 
43737457c0Smarty struct fdtbus_pinctrl_controller {
44b5a701b9Sjmcneill 	device_t pc_dev;
45737457c0Smarty 	int pc_phandle;
46737457c0Smarty 	const struct fdtbus_pinctrl_controller_func *pc_funcs;
47737457c0Smarty 
48233c5d85Sjmcneill 	LIST_ENTRY(fdtbus_pinctrl_controller) pc_next;
49737457c0Smarty };
50737457c0Smarty 
51233c5d85Sjmcneill static LIST_HEAD(, fdtbus_pinctrl_controller) fdtbus_pinctrl_controllers =
52233c5d85Sjmcneill     LIST_HEAD_INITIALIZER(fdtbus_pinctrl_controllers);
53737457c0Smarty 
54737457c0Smarty int
fdtbus_register_pinctrl_config(device_t dev,int phandle,const struct fdtbus_pinctrl_controller_func * funcs)55b5a701b9Sjmcneill fdtbus_register_pinctrl_config(device_t dev, int phandle,
56737457c0Smarty     const struct fdtbus_pinctrl_controller_func *funcs)
57737457c0Smarty {
58737457c0Smarty 	struct fdtbus_pinctrl_controller *pc;
59737457c0Smarty 
60737457c0Smarty 	pc = kmem_alloc(sizeof(*pc), KM_SLEEP);
61b5a701b9Sjmcneill 	pc->pc_dev = dev;
62737457c0Smarty 	pc->pc_phandle = phandle;
63737457c0Smarty 	pc->pc_funcs = funcs;
64737457c0Smarty 
65233c5d85Sjmcneill 	LIST_INSERT_HEAD(&fdtbus_pinctrl_controllers, pc, pc_next);
66737457c0Smarty 
67737457c0Smarty 	return 0;
68737457c0Smarty }
69737457c0Smarty 
707e1b70caSmarty static struct fdtbus_pinctrl_controller *
fdtbus_pinctrl_lookup(int phandle)717e1b70caSmarty fdtbus_pinctrl_lookup(int phandle)
72737457c0Smarty {
73737457c0Smarty 	struct fdtbus_pinctrl_controller *pc;
74737457c0Smarty 
75233c5d85Sjmcneill 	LIST_FOREACH(pc, &fdtbus_pinctrl_controllers, pc_next) {
767e1b70caSmarty 		if (pc->pc_phandle == phandle)
777e1b70caSmarty 			return pc;
78233c5d85Sjmcneill 	}
79737457c0Smarty 
80737457c0Smarty 	return NULL;
81737457c0Smarty }
82737457c0Smarty 
837e1b70caSmarty int
fdtbus_pinctrl_set_config_index(int phandle,u_int index)847e1b70caSmarty fdtbus_pinctrl_set_config_index(int phandle, u_int index)
857e1b70caSmarty {
867e1b70caSmarty 	struct fdtbus_pinctrl_controller *pc;
87b5a701b9Sjmcneill 	const u_int *pinctrl_data;
88b5a701b9Sjmcneill 	char buf[16];
89b5a701b9Sjmcneill 	u_int xref, pinctrl_cells;
90b5a701b9Sjmcneill 	int len, error;
917e1b70caSmarty 
92b5a701b9Sjmcneill 	snprintf(buf, sizeof(buf), "pinctrl-%u", index);
937e1b70caSmarty 
94b5a701b9Sjmcneill 	pinctrl_data = fdtbus_get_prop(phandle, buf, &len);
95b5a701b9Sjmcneill 	if (pinctrl_data == NULL)
96b5a701b9Sjmcneill 		return ENOENT;
97b5a701b9Sjmcneill 
98b5a701b9Sjmcneill 	while (len > 0) {
99b5a701b9Sjmcneill 		xref = fdtbus_get_phandle_from_native(be32toh(pinctrl_data[0]));
100b5a701b9Sjmcneill 		pc = fdtbus_pinctrl_lookup(xref);
101b5a701b9Sjmcneill 		if (pc == NULL)
102b5a701b9Sjmcneill 			return ENXIO;
103b5a701b9Sjmcneill 
104b5a701b9Sjmcneill 		if (of_getprop_uint32(OF_parent(xref), "#pinctrl-cells", &pinctrl_cells) != 0)
105b5a701b9Sjmcneill 			pinctrl_cells = 1;
106b5a701b9Sjmcneill 
107b5a701b9Sjmcneill 		error = pc->pc_funcs->set_config(pc->pc_dev, pinctrl_data, pinctrl_cells * 4);
108b5a701b9Sjmcneill 		if (error != 0)
109b5a701b9Sjmcneill 			return error;
110b5a701b9Sjmcneill 
111b5a701b9Sjmcneill 		pinctrl_data += pinctrl_cells;
112b5a701b9Sjmcneill 		len -= (pinctrl_cells * 4);
113737457c0Smarty 	}
114737457c0Smarty 
115b5a701b9Sjmcneill 	return 0;
116737457c0Smarty }
117737457c0Smarty 
1187e1b70caSmarty int
fdtbus_pinctrl_set_config(int phandle,const char * cfgname)1197e1b70caSmarty fdtbus_pinctrl_set_config(int phandle, const char *cfgname)
120737457c0Smarty {
12121aceb10Sjakllsch 	u_int index;
12221aceb10Sjakllsch 	int err;
123737457c0Smarty 
12421aceb10Sjakllsch 	err = fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index);
12521aceb10Sjakllsch 	if (err != 0)
126*0060d28cSjmcneill 		return ENOENT;
1277e1b70caSmarty 
1287e1b70caSmarty 	return fdtbus_pinctrl_set_config_index(phandle, index);
1297e1b70caSmarty }
130b5a701b9Sjmcneill 
131*0060d28cSjmcneill bool
fdtbus_pinctrl_has_config(int phandle,const char * cfgname)132*0060d28cSjmcneill fdtbus_pinctrl_has_config(int phandle, const char *cfgname)
133b5a701b9Sjmcneill {
134*0060d28cSjmcneill 	u_int index;
135b5a701b9Sjmcneill 
136*0060d28cSjmcneill 	return fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index) == 0;
137737457c0Smarty }
138dfaf6e79Sthorpej 
139dfaf6e79Sthorpej /*
140dfaf6e79Sthorpej  * Helper routines for parsing put properties related to pinctrl bindings.
141dfaf6e79Sthorpej  */
142dfaf6e79Sthorpej 
143dfaf6e79Sthorpej /*
144dfaf6e79Sthorpej  * Pin mux settings apply to sets of pins specified by one of 3
145dfaf6e79Sthorpej  * sets of properties:
146dfaf6e79Sthorpej  *
147dfaf6e79Sthorpej  *	- "pins" + "function"
148dfaf6e79Sthorpej  *	- "groups" + "function"
149dfaf6e79Sthorpej  *	- "pinmux"
150dfaf6e79Sthorpej  *
151dfaf6e79Sthorpej  * Eactly one of those 3 combinations must be specified.
152dfaf6e79Sthorpej  */
153dfaf6e79Sthorpej 
154dfaf6e79Sthorpej const char *
fdtbus_pinctrl_parse_function(int phandle)155dfaf6e79Sthorpej fdtbus_pinctrl_parse_function(int phandle)
156dfaf6e79Sthorpej {
157dfaf6e79Sthorpej 	return fdtbus_get_string(phandle, "function");
158dfaf6e79Sthorpej }
159dfaf6e79Sthorpej 
160dfaf6e79Sthorpej const void *
fdtbus_pinctrl_parse_pins(int phandle,int * pins_len)161dfaf6e79Sthorpej fdtbus_pinctrl_parse_pins(int phandle, int *pins_len)
162dfaf6e79Sthorpej {
163dfaf6e79Sthorpej 	int len;
164dfaf6e79Sthorpej 
165dfaf6e79Sthorpej 	/*
166dfaf6e79Sthorpej 	 * The pinctrl bindings specify that entries in "pins"
167dfaf6e79Sthorpej 	 * may be integers or strings; this is determined by
168dfaf6e79Sthorpej 	 * the hardware-specific binding.
169dfaf6e79Sthorpej 	 */
170dfaf6e79Sthorpej 
171dfaf6e79Sthorpej 	len = OF_getproplen(phandle, "pins");
172dfaf6e79Sthorpej 	if (len > 0) {
173dfaf6e79Sthorpej 		return fdtbus_get_prop(phandle, "pins", pins_len);
174dfaf6e79Sthorpej 	}
175dfaf6e79Sthorpej 
176dfaf6e79Sthorpej 	return NULL;
177dfaf6e79Sthorpej }
178dfaf6e79Sthorpej 
179dfaf6e79Sthorpej const char *
fdtbus_pinctrl_parse_groups(int phandle,int * groups_len)180dfaf6e79Sthorpej fdtbus_pinctrl_parse_groups(int phandle, int *groups_len)
181dfaf6e79Sthorpej {
182dfaf6e79Sthorpej 	int len;
183dfaf6e79Sthorpej 
184dfaf6e79Sthorpej 	len = OF_getproplen(phandle, "groups");
185dfaf6e79Sthorpej 	if (len > 0) {
186dfaf6e79Sthorpej 		*groups_len = len;
187dfaf6e79Sthorpej 		return fdtbus_get_string(phandle, "groups");
188dfaf6e79Sthorpej 	}
189dfaf6e79Sthorpej 
190dfaf6e79Sthorpej 	return NULL;
191dfaf6e79Sthorpej }
192dfaf6e79Sthorpej 
193dfaf6e79Sthorpej const u_int *
fdtbus_pinctrl_parse_pinmux(int phandle,int * pinmux_len)194dfaf6e79Sthorpej fdtbus_pinctrl_parse_pinmux(int phandle, int *pinmux_len)
195dfaf6e79Sthorpej {
196dfaf6e79Sthorpej 	int len;
197dfaf6e79Sthorpej 
198dfaf6e79Sthorpej 	len = OF_getproplen(phandle, "pinmux");
199dfaf6e79Sthorpej 	if (len > 0) {
200dfaf6e79Sthorpej 		return fdtbus_get_prop(phandle, "pinmux", pinmux_len);
201dfaf6e79Sthorpej 	}
202dfaf6e79Sthorpej 
203dfaf6e79Sthorpej 	return NULL;
204dfaf6e79Sthorpej }
205dfaf6e79Sthorpej 
206dfaf6e79Sthorpej int
fdtbus_pinctrl_parse_bias(int phandle,int * pull_strength)207dfaf6e79Sthorpej fdtbus_pinctrl_parse_bias(int phandle, int *pull_strength)
208dfaf6e79Sthorpej {
209dfaf6e79Sthorpej 	const char *bias_prop = NULL;
210dfaf6e79Sthorpej 	int bias = -1;
211dfaf6e79Sthorpej 
212dfaf6e79Sthorpej 	/*
213dfaf6e79Sthorpej 	 * bias-pull-{up,down,pin-default} properties have an optional
214dfaf6e79Sthorpej 	 * argument: the pull strength in Ohms.  (In practice, this is
215dfaf6e79Sthorpej 	 * sometimes a hardware-specific constant.)
216dfaf6e79Sthorpej 	 *
217dfaf6e79Sthorpej 	 * XXXJRT How to represent bias-pull-pin-default?
218dfaf6e79Sthorpej 	 */
219dfaf6e79Sthorpej 
220dfaf6e79Sthorpej 	if (of_hasprop(phandle, "bias-disable")) {
221dfaf6e79Sthorpej 		bias = 0;
222dfaf6e79Sthorpej 	} else if (of_hasprop(phandle, "bias-pull-up")) {
223dfaf6e79Sthorpej 		bias_prop = "bias-pull-up";
224dfaf6e79Sthorpej 		bias = GPIO_PIN_PULLUP;
225dfaf6e79Sthorpej 	} else if (of_hasprop(phandle, "bias-pull-down")) {
226dfaf6e79Sthorpej 		bias_prop = "bias-pull-down";
227dfaf6e79Sthorpej 		bias = GPIO_PIN_PULLDOWN;
228dfaf6e79Sthorpej 	}
229dfaf6e79Sthorpej 
230dfaf6e79Sthorpej 	if (pull_strength) {
231dfaf6e79Sthorpej 		*pull_strength = -1;
232dfaf6e79Sthorpej 		if (bias_prop) {
233dfaf6e79Sthorpej 			uint32_t val;
234dfaf6e79Sthorpej 			if (of_getprop_uint32(phandle, bias_prop, &val) == 0) {
235dfaf6e79Sthorpej 				*pull_strength = (int)val;
236dfaf6e79Sthorpej 			}
237dfaf6e79Sthorpej 		}
238dfaf6e79Sthorpej 	}
239dfaf6e79Sthorpej 
240dfaf6e79Sthorpej 	return bias;
241dfaf6e79Sthorpej }
242dfaf6e79Sthorpej 
243dfaf6e79Sthorpej int
fdtbus_pinctrl_parse_drive(int phandle)244dfaf6e79Sthorpej fdtbus_pinctrl_parse_drive(int phandle)
245dfaf6e79Sthorpej {
246dfaf6e79Sthorpej 	int drive = -1;
247dfaf6e79Sthorpej 
248dfaf6e79Sthorpej 	if (of_hasprop(phandle, "drive-push-pull"))
249dfaf6e79Sthorpej 		drive = GPIO_PIN_PUSHPULL;
250dfaf6e79Sthorpej 	else if (of_hasprop(phandle, "drive-open-drain"))
251dfaf6e79Sthorpej 		drive = GPIO_PIN_OPENDRAIN;
252dfaf6e79Sthorpej 	else if (of_hasprop(phandle, "drive-open-source"))
253dfaf6e79Sthorpej 		drive = 0;
254dfaf6e79Sthorpej 
255dfaf6e79Sthorpej 	return drive;
256dfaf6e79Sthorpej }
257dfaf6e79Sthorpej 
258dfaf6e79Sthorpej int
fdtbus_pinctrl_parse_drive_strength(int phandle)259dfaf6e79Sthorpej fdtbus_pinctrl_parse_drive_strength(int phandle)
260dfaf6e79Sthorpej {
261dfaf6e79Sthorpej 	int val;
262dfaf6e79Sthorpej 
263dfaf6e79Sthorpej 	/*
264dfaf6e79Sthorpej 	 * drive-strength has as an argument the target strength
265dfaf6e79Sthorpej 	 * in mA.
266dfaf6e79Sthorpej 	 */
267dfaf6e79Sthorpej 
268dfaf6e79Sthorpej 	if (of_getprop_uint32(phandle, "drive-strength", &val) == 0)
269dfaf6e79Sthorpej 		return val;
270dfaf6e79Sthorpej 
271dfaf6e79Sthorpej 	return -1;
272dfaf6e79Sthorpej }
fdtbus_pinctrl_parse_input_output(int phandle,int * output_value)273dfaf6e79Sthorpej int fdtbus_pinctrl_parse_input_output(int phandle, int *output_value)
274dfaf6e79Sthorpej {
275dfaf6e79Sthorpej 	int direction = -1;
276dfaf6e79Sthorpej 	int pinval = -1;
277dfaf6e79Sthorpej 
278dfaf6e79Sthorpej 	if (of_hasprop(phandle, "input-enable")) {
279dfaf6e79Sthorpej 		direction = GPIO_PIN_INPUT;
280dfaf6e79Sthorpej 	} else if (of_hasprop(phandle, "input-disable")) {
281dfaf6e79Sthorpej 		/*
282dfaf6e79Sthorpej 		 * XXXJRT How to represent this?  This is more than
283dfaf6e79Sthorpej 		 * just "don't set the direction" - it's an active
284dfaf6e79Sthorpej 		 * command that might involve disabling an input
285dfaf6e79Sthorpej 		 * buffer on the pin.
286dfaf6e79Sthorpej 		 */
287dfaf6e79Sthorpej 	}
288dfaf6e79Sthorpej 
289dfaf6e79Sthorpej 	if (of_hasprop(phandle, "output-enable")) {
290dfaf6e79Sthorpej 		if (direction == -1)
291dfaf6e79Sthorpej 			direction = 0;
292dfaf6e79Sthorpej 		direction |= GPIO_PIN_OUTPUT;
293dfaf6e79Sthorpej 	} else if (of_hasprop(phandle, "output-disable")) {
294dfaf6e79Sthorpej 		if (direction == -1)
295dfaf6e79Sthorpej 			direction = 0;
296dfaf6e79Sthorpej 		direction |= GPIO_PIN_TRISTATE;
297dfaf6e79Sthorpej 	}
298dfaf6e79Sthorpej 
299dfaf6e79Sthorpej 	if (of_hasprop(phandle, "output-low")) {
300dfaf6e79Sthorpej 		if (direction == -1)
301dfaf6e79Sthorpej 			direction = 0;
302dfaf6e79Sthorpej 		direction |= GPIO_PIN_OUTPUT;
303dfaf6e79Sthorpej 		pinval = GPIO_PIN_LOW;
304dfaf6e79Sthorpej 	} else if (of_hasprop(phandle, "output-high")) {
305dfaf6e79Sthorpej 		if (direction == -1)
306dfaf6e79Sthorpej 			direction = 0;
307dfaf6e79Sthorpej 		direction |= GPIO_PIN_OUTPUT;
308dfaf6e79Sthorpej 		pinval = GPIO_PIN_HIGH;
309dfaf6e79Sthorpej 	}
310dfaf6e79Sthorpej 
311dfaf6e79Sthorpej 	if (output_value)
312dfaf6e79Sthorpej 		*output_value = pinval;
313dfaf6e79Sthorpej 
314dfaf6e79Sthorpej 	/*
315dfaf6e79Sthorpej 	 * XXX input-schmitt-enable
316dfaf6e79Sthorpej 	 * XXX input-schmitt-disable
317dfaf6e79Sthorpej 	 */
318dfaf6e79Sthorpej 
319dfaf6e79Sthorpej 	if (direction != -1
320dfaf6e79Sthorpej 	    && (direction & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
321dfaf6e79Sthorpej 			 == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
322dfaf6e79Sthorpej 		direction |= GPIO_PIN_INOUT;
323dfaf6e79Sthorpej 	}
324dfaf6e79Sthorpej 
325dfaf6e79Sthorpej 	return direction;
326dfaf6e79Sthorpej }
327