fdt_pinctrl.c revision 1.4.6.3 1 /* $NetBSD: fdt_pinctrl.c,v 1.4.6.3 2019/01/26 22:00:06 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c) 2019 Jason R. Thorpe
5 * Copyright (c) 2017 Jared McNeill <jmcneill (at) 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.4.6.3 2019/01/26 22:00:06 pgoyette 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 const char *pinctrl_names, *name;
122 int len, index;
123
124 if ((len = OF_getproplen(phandle, "pinctrl-names")) < 0)
125 return -1;
126
127 pinctrl_names = fdtbus_get_string(phandle, "pinctrl-names");
128
129 for (name = pinctrl_names, index = 0; len > 0;
130 len -= strlen(name) + 1, name += strlen(name) + 1, index++) {
131 if (strcmp(name, cfgname) == 0)
132 return fdtbus_pinctrl_set_config_index(phandle, index);
133 }
134
135 /* Not found */
136 return -1;
137 }
138
139 static void
140 fdtbus_pinctrl_configure_node(int phandle)
141 {
142 char buf[256];
143 int child, error;
144
145 for (child = OF_child(phandle); child; child = OF_peer(child)) {
146 if (!fdtbus_status_okay(child))
147 continue;
148
149 /* Configure child nodes */
150 fdtbus_pinctrl_configure_node(child);
151
152 /*
153 * Set configuration 0 for this node. This may fail if the
154 * pinctrl provider is missing; that's OK, we will re-configure
155 * when that provider attaches.
156 */
157 fdtbus_get_path(child, buf, sizeof(buf));
158 error = fdtbus_pinctrl_set_config_index(child, 0);
159 if (error == 0)
160 aprint_debug("pinctrl: set config pinctrl-0 for %s\n", buf);
161 else if (error != ENOENT)
162 aprint_debug("pinctrl: failed to set config pinctrl-0 for %s: %d\n", buf, error);
163 }
164 }
165
166 void
167 fdtbus_pinctrl_configure(void)
168 {
169 fdtbus_pinctrl_configure_node(OF_finddevice("/"));
170 }
171
172 /*
173 * Helper routines for parsing put properties related to pinctrl bindings.
174 */
175
176 /*
177 * Pin mux settings apply to sets of pins specified by one of 3
178 * sets of properties:
179 *
180 * - "pins" + "function"
181 * - "groups" + "function"
182 * - "pinmux"
183 *
184 * Eactly one of those 3 combinations must be specified.
185 */
186
187 const char *
188 fdtbus_pinctrl_parse_function(int phandle)
189 {
190 return fdtbus_get_string(phandle, "function");
191 }
192
193 const void *
194 fdtbus_pinctrl_parse_pins(int phandle, int *pins_len)
195 {
196 int len;
197
198 /*
199 * The pinctrl bindings specify that entries in "pins"
200 * may be integers or strings; this is determined by
201 * the hardware-specific binding.
202 */
203
204 len = OF_getproplen(phandle, "pins");
205 if (len > 0) {
206 return fdtbus_get_prop(phandle, "pins", pins_len);
207 }
208
209 return NULL;
210 }
211
212 const char *
213 fdtbus_pinctrl_parse_groups(int phandle, int *groups_len)
214 {
215 int len;
216
217 len = OF_getproplen(phandle, "groups");
218 if (len > 0) {
219 *groups_len = len;
220 return fdtbus_get_string(phandle, "groups");
221 }
222
223 return NULL;
224 }
225
226 const u_int *
227 fdtbus_pinctrl_parse_pinmux(int phandle, int *pinmux_len)
228 {
229 int len;
230
231 len = OF_getproplen(phandle, "pinmux");
232 if (len > 0) {
233 return fdtbus_get_prop(phandle, "pinmux", pinmux_len);
234 }
235
236 return NULL;
237 }
238
239 int
240 fdtbus_pinctrl_parse_bias(int phandle, int *pull_strength)
241 {
242 const char *bias_prop = NULL;
243 int bias = -1;
244
245 /*
246 * bias-pull-{up,down,pin-default} properties have an optional
247 * argument: the pull strength in Ohms. (In practice, this is
248 * sometimes a hardware-specific constant.)
249 *
250 * XXXJRT How to represent bias-pull-pin-default?
251 */
252
253 if (of_hasprop(phandle, "bias-disable")) {
254 bias = 0;
255 } else if (of_hasprop(phandle, "bias-pull-up")) {
256 bias_prop = "bias-pull-up";
257 bias = GPIO_PIN_PULLUP;
258 } else if (of_hasprop(phandle, "bias-pull-down")) {
259 bias_prop = "bias-pull-down";
260 bias = GPIO_PIN_PULLDOWN;
261 }
262
263 if (pull_strength) {
264 *pull_strength = -1;
265 if (bias_prop) {
266 uint32_t val;
267 if (of_getprop_uint32(phandle, bias_prop, &val) == 0) {
268 *pull_strength = (int)val;
269 }
270 }
271 }
272
273 return bias;
274 }
275
276 int
277 fdtbus_pinctrl_parse_drive(int phandle)
278 {
279 int drive = -1;
280
281 if (of_hasprop(phandle, "drive-push-pull"))
282 drive = GPIO_PIN_PUSHPULL;
283 else if (of_hasprop(phandle, "drive-open-drain"))
284 drive = GPIO_PIN_OPENDRAIN;
285 else if (of_hasprop(phandle, "drive-open-source"))
286 drive = 0;
287
288 return drive;
289 }
290
291 int
292 fdtbus_pinctrl_parse_drive_strength(int phandle)
293 {
294 int val;
295
296 /*
297 * drive-strength has as an argument the target strength
298 * in mA.
299 */
300
301 if (of_getprop_uint32(phandle, "drive-strength", &val) == 0)
302 return val;
303
304 return -1;
305 }
306 int fdtbus_pinctrl_parse_input_output(int phandle, int *output_value)
307 {
308 int direction = -1;
309 int pinval = -1;
310
311 if (of_hasprop(phandle, "input-enable")) {
312 direction = GPIO_PIN_INPUT;
313 } else if (of_hasprop(phandle, "input-disable")) {
314 /*
315 * XXXJRT How to represent this? This is more than
316 * just "don't set the direction" - it's an active
317 * command that might involve disabling an input
318 * buffer on the pin.
319 */
320 }
321
322 if (of_hasprop(phandle, "output-enable")) {
323 if (direction == -1)
324 direction = 0;
325 direction |= GPIO_PIN_OUTPUT;
326 } else if (of_hasprop(phandle, "output-disable")) {
327 if (direction == -1)
328 direction = 0;
329 direction |= GPIO_PIN_TRISTATE;
330 }
331
332 if (of_hasprop(phandle, "output-low")) {
333 if (direction == -1)
334 direction = 0;
335 direction |= GPIO_PIN_OUTPUT;
336 pinval = GPIO_PIN_LOW;
337 } else if (of_hasprop(phandle, "output-high")) {
338 if (direction == -1)
339 direction = 0;
340 direction |= GPIO_PIN_OUTPUT;
341 pinval = GPIO_PIN_HIGH;
342 }
343
344 if (output_value)
345 *output_value = pinval;
346
347 /*
348 * XXX input-schmitt-enable
349 * XXX input-schmitt-disable
350 */
351
352 if (direction != -1
353 && (direction & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
354 == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
355 direction |= GPIO_PIN_INOUT;
356 }
357
358 return direction;
359 }
360