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