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