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