xref: /netbsd-src/sys/dev/fdt/fdt_pinctrl.c (revision f3cfa6f6ce31685c6c4a758bc430e69eb99f50a4)
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 
43 struct 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 
51 static LIST_HEAD(, fdtbus_pinctrl_controller) fdtbus_pinctrl_controllers =
52     LIST_HEAD_INITIALIZER(fdtbus_pinctrl_controllers);
53 
54 int
55 fdtbus_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 
70 static struct fdtbus_pinctrl_controller *
71 fdtbus_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 
83 int
84 fdtbus_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 
118 int
119 fdtbus_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 
131 static void
132 fdtbus_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 
158 void
159 fdtbus_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 
179 const char *
180 fdtbus_pinctrl_parse_function(int phandle)
181 {
182 	return fdtbus_get_string(phandle, "function");
183 }
184 
185 const void *
186 fdtbus_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 
204 const char *
205 fdtbus_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 
218 const u_int *
219 fdtbus_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 
231 int
232 fdtbus_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 
268 int
269 fdtbus_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 
283 int
284 fdtbus_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 }
298 int 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