1*0060d28cSjmcneill /* $NetBSD: fdt_pinctrl.c,v 1.10 2019/10/01 23:32:52 jmcneill Exp $ */
2737457c0Smarty
3737457c0Smarty /*-
4dfaf6e79Sthorpej * Copyright (c) 2019 Jason R. Thorpe
5b5a701b9Sjmcneill * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
67e1b70caSmarty * Copyright (c) 2015 Martin Fouts
7737457c0Smarty * All rights reserved.
8737457c0Smarty *
9737457c0Smarty * Redistribution and use in source and binary forms, with or without
10737457c0Smarty * modification, are permitted provided that the following conditions
11737457c0Smarty * are met:
12737457c0Smarty * 1. Redistributions of source code must retain the above copyright
13737457c0Smarty * notice, this list of conditions and the following disclaimer.
14737457c0Smarty * 2. Redistributions in binary form must reproduce the above copyright
15737457c0Smarty * notice, this list of conditions and the following disclaimer in the
16737457c0Smarty * documentation and/or other materials provided with the distribution.
17737457c0Smarty *
18737457c0Smarty * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19737457c0Smarty * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20737457c0Smarty * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21737457c0Smarty * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22737457c0Smarty * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23737457c0Smarty * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24737457c0Smarty * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25737457c0Smarty * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26737457c0Smarty * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27737457c0Smarty * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28737457c0Smarty * SUCH DAMAGE.
29737457c0Smarty */
30737457c0Smarty
31737457c0Smarty #include <sys/cdefs.h>
32*0060d28cSjmcneill __KERNEL_RCSID(0, "$NetBSD: fdt_pinctrl.c,v 1.10 2019/10/01 23:32:52 jmcneill Exp $");
33737457c0Smarty
34737457c0Smarty #include <sys/param.h>
35737457c0Smarty #include <sys/bus.h>
36dfaf6e79Sthorpej #include <sys/gpio.h>
37737457c0Smarty #include <sys/kmem.h>
38233c5d85Sjmcneill #include <sys/queue.h>
39737457c0Smarty
40737457c0Smarty #include <libfdt.h>
41737457c0Smarty #include <dev/fdt/fdtvar.h>
42737457c0Smarty
43737457c0Smarty struct fdtbus_pinctrl_controller {
44b5a701b9Sjmcneill device_t pc_dev;
45737457c0Smarty int pc_phandle;
46737457c0Smarty const struct fdtbus_pinctrl_controller_func *pc_funcs;
47737457c0Smarty
48233c5d85Sjmcneill LIST_ENTRY(fdtbus_pinctrl_controller) pc_next;
49737457c0Smarty };
50737457c0Smarty
51233c5d85Sjmcneill static LIST_HEAD(, fdtbus_pinctrl_controller) fdtbus_pinctrl_controllers =
52233c5d85Sjmcneill LIST_HEAD_INITIALIZER(fdtbus_pinctrl_controllers);
53737457c0Smarty
54737457c0Smarty int
fdtbus_register_pinctrl_config(device_t dev,int phandle,const struct fdtbus_pinctrl_controller_func * funcs)55b5a701b9Sjmcneill fdtbus_register_pinctrl_config(device_t dev, int phandle,
56737457c0Smarty const struct fdtbus_pinctrl_controller_func *funcs)
57737457c0Smarty {
58737457c0Smarty struct fdtbus_pinctrl_controller *pc;
59737457c0Smarty
60737457c0Smarty pc = kmem_alloc(sizeof(*pc), KM_SLEEP);
61b5a701b9Sjmcneill pc->pc_dev = dev;
62737457c0Smarty pc->pc_phandle = phandle;
63737457c0Smarty pc->pc_funcs = funcs;
64737457c0Smarty
65233c5d85Sjmcneill LIST_INSERT_HEAD(&fdtbus_pinctrl_controllers, pc, pc_next);
66737457c0Smarty
67737457c0Smarty return 0;
68737457c0Smarty }
69737457c0Smarty
707e1b70caSmarty static struct fdtbus_pinctrl_controller *
fdtbus_pinctrl_lookup(int phandle)717e1b70caSmarty fdtbus_pinctrl_lookup(int phandle)
72737457c0Smarty {
73737457c0Smarty struct fdtbus_pinctrl_controller *pc;
74737457c0Smarty
75233c5d85Sjmcneill LIST_FOREACH(pc, &fdtbus_pinctrl_controllers, pc_next) {
767e1b70caSmarty if (pc->pc_phandle == phandle)
777e1b70caSmarty return pc;
78233c5d85Sjmcneill }
79737457c0Smarty
80737457c0Smarty return NULL;
81737457c0Smarty }
82737457c0Smarty
837e1b70caSmarty int
fdtbus_pinctrl_set_config_index(int phandle,u_int index)847e1b70caSmarty fdtbus_pinctrl_set_config_index(int phandle, u_int index)
857e1b70caSmarty {
867e1b70caSmarty struct fdtbus_pinctrl_controller *pc;
87b5a701b9Sjmcneill const u_int *pinctrl_data;
88b5a701b9Sjmcneill char buf[16];
89b5a701b9Sjmcneill u_int xref, pinctrl_cells;
90b5a701b9Sjmcneill int len, error;
917e1b70caSmarty
92b5a701b9Sjmcneill snprintf(buf, sizeof(buf), "pinctrl-%u", index);
937e1b70caSmarty
94b5a701b9Sjmcneill pinctrl_data = fdtbus_get_prop(phandle, buf, &len);
95b5a701b9Sjmcneill if (pinctrl_data == NULL)
96b5a701b9Sjmcneill return ENOENT;
97b5a701b9Sjmcneill
98b5a701b9Sjmcneill while (len > 0) {
99b5a701b9Sjmcneill xref = fdtbus_get_phandle_from_native(be32toh(pinctrl_data[0]));
100b5a701b9Sjmcneill pc = fdtbus_pinctrl_lookup(xref);
101b5a701b9Sjmcneill if (pc == NULL)
102b5a701b9Sjmcneill return ENXIO;
103b5a701b9Sjmcneill
104b5a701b9Sjmcneill if (of_getprop_uint32(OF_parent(xref), "#pinctrl-cells", &pinctrl_cells) != 0)
105b5a701b9Sjmcneill pinctrl_cells = 1;
106b5a701b9Sjmcneill
107b5a701b9Sjmcneill error = pc->pc_funcs->set_config(pc->pc_dev, pinctrl_data, pinctrl_cells * 4);
108b5a701b9Sjmcneill if (error != 0)
109b5a701b9Sjmcneill return error;
110b5a701b9Sjmcneill
111b5a701b9Sjmcneill pinctrl_data += pinctrl_cells;
112b5a701b9Sjmcneill len -= (pinctrl_cells * 4);
113737457c0Smarty }
114737457c0Smarty
115b5a701b9Sjmcneill return 0;
116737457c0Smarty }
117737457c0Smarty
1187e1b70caSmarty int
fdtbus_pinctrl_set_config(int phandle,const char * cfgname)1197e1b70caSmarty fdtbus_pinctrl_set_config(int phandle, const char *cfgname)
120737457c0Smarty {
12121aceb10Sjakllsch u_int index;
12221aceb10Sjakllsch int err;
123737457c0Smarty
12421aceb10Sjakllsch err = fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index);
12521aceb10Sjakllsch if (err != 0)
126*0060d28cSjmcneill return ENOENT;
1277e1b70caSmarty
1287e1b70caSmarty return fdtbus_pinctrl_set_config_index(phandle, index);
1297e1b70caSmarty }
130b5a701b9Sjmcneill
131*0060d28cSjmcneill bool
fdtbus_pinctrl_has_config(int phandle,const char * cfgname)132*0060d28cSjmcneill fdtbus_pinctrl_has_config(int phandle, const char *cfgname)
133b5a701b9Sjmcneill {
134*0060d28cSjmcneill u_int index;
135b5a701b9Sjmcneill
136*0060d28cSjmcneill return fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index) == 0;
137737457c0Smarty }
138dfaf6e79Sthorpej
139dfaf6e79Sthorpej /*
140dfaf6e79Sthorpej * Helper routines for parsing put properties related to pinctrl bindings.
141dfaf6e79Sthorpej */
142dfaf6e79Sthorpej
143dfaf6e79Sthorpej /*
144dfaf6e79Sthorpej * Pin mux settings apply to sets of pins specified by one of 3
145dfaf6e79Sthorpej * sets of properties:
146dfaf6e79Sthorpej *
147dfaf6e79Sthorpej * - "pins" + "function"
148dfaf6e79Sthorpej * - "groups" + "function"
149dfaf6e79Sthorpej * - "pinmux"
150dfaf6e79Sthorpej *
151dfaf6e79Sthorpej * Eactly one of those 3 combinations must be specified.
152dfaf6e79Sthorpej */
153dfaf6e79Sthorpej
154dfaf6e79Sthorpej const char *
fdtbus_pinctrl_parse_function(int phandle)155dfaf6e79Sthorpej fdtbus_pinctrl_parse_function(int phandle)
156dfaf6e79Sthorpej {
157dfaf6e79Sthorpej return fdtbus_get_string(phandle, "function");
158dfaf6e79Sthorpej }
159dfaf6e79Sthorpej
160dfaf6e79Sthorpej const void *
fdtbus_pinctrl_parse_pins(int phandle,int * pins_len)161dfaf6e79Sthorpej fdtbus_pinctrl_parse_pins(int phandle, int *pins_len)
162dfaf6e79Sthorpej {
163dfaf6e79Sthorpej int len;
164dfaf6e79Sthorpej
165dfaf6e79Sthorpej /*
166dfaf6e79Sthorpej * The pinctrl bindings specify that entries in "pins"
167dfaf6e79Sthorpej * may be integers or strings; this is determined by
168dfaf6e79Sthorpej * the hardware-specific binding.
169dfaf6e79Sthorpej */
170dfaf6e79Sthorpej
171dfaf6e79Sthorpej len = OF_getproplen(phandle, "pins");
172dfaf6e79Sthorpej if (len > 0) {
173dfaf6e79Sthorpej return fdtbus_get_prop(phandle, "pins", pins_len);
174dfaf6e79Sthorpej }
175dfaf6e79Sthorpej
176dfaf6e79Sthorpej return NULL;
177dfaf6e79Sthorpej }
178dfaf6e79Sthorpej
179dfaf6e79Sthorpej const char *
fdtbus_pinctrl_parse_groups(int phandle,int * groups_len)180dfaf6e79Sthorpej fdtbus_pinctrl_parse_groups(int phandle, int *groups_len)
181dfaf6e79Sthorpej {
182dfaf6e79Sthorpej int len;
183dfaf6e79Sthorpej
184dfaf6e79Sthorpej len = OF_getproplen(phandle, "groups");
185dfaf6e79Sthorpej if (len > 0) {
186dfaf6e79Sthorpej *groups_len = len;
187dfaf6e79Sthorpej return fdtbus_get_string(phandle, "groups");
188dfaf6e79Sthorpej }
189dfaf6e79Sthorpej
190dfaf6e79Sthorpej return NULL;
191dfaf6e79Sthorpej }
192dfaf6e79Sthorpej
193dfaf6e79Sthorpej const u_int *
fdtbus_pinctrl_parse_pinmux(int phandle,int * pinmux_len)194dfaf6e79Sthorpej fdtbus_pinctrl_parse_pinmux(int phandle, int *pinmux_len)
195dfaf6e79Sthorpej {
196dfaf6e79Sthorpej int len;
197dfaf6e79Sthorpej
198dfaf6e79Sthorpej len = OF_getproplen(phandle, "pinmux");
199dfaf6e79Sthorpej if (len > 0) {
200dfaf6e79Sthorpej return fdtbus_get_prop(phandle, "pinmux", pinmux_len);
201dfaf6e79Sthorpej }
202dfaf6e79Sthorpej
203dfaf6e79Sthorpej return NULL;
204dfaf6e79Sthorpej }
205dfaf6e79Sthorpej
206dfaf6e79Sthorpej int
fdtbus_pinctrl_parse_bias(int phandle,int * pull_strength)207dfaf6e79Sthorpej fdtbus_pinctrl_parse_bias(int phandle, int *pull_strength)
208dfaf6e79Sthorpej {
209dfaf6e79Sthorpej const char *bias_prop = NULL;
210dfaf6e79Sthorpej int bias = -1;
211dfaf6e79Sthorpej
212dfaf6e79Sthorpej /*
213dfaf6e79Sthorpej * bias-pull-{up,down,pin-default} properties have an optional
214dfaf6e79Sthorpej * argument: the pull strength in Ohms. (In practice, this is
215dfaf6e79Sthorpej * sometimes a hardware-specific constant.)
216dfaf6e79Sthorpej *
217dfaf6e79Sthorpej * XXXJRT How to represent bias-pull-pin-default?
218dfaf6e79Sthorpej */
219dfaf6e79Sthorpej
220dfaf6e79Sthorpej if (of_hasprop(phandle, "bias-disable")) {
221dfaf6e79Sthorpej bias = 0;
222dfaf6e79Sthorpej } else if (of_hasprop(phandle, "bias-pull-up")) {
223dfaf6e79Sthorpej bias_prop = "bias-pull-up";
224dfaf6e79Sthorpej bias = GPIO_PIN_PULLUP;
225dfaf6e79Sthorpej } else if (of_hasprop(phandle, "bias-pull-down")) {
226dfaf6e79Sthorpej bias_prop = "bias-pull-down";
227dfaf6e79Sthorpej bias = GPIO_PIN_PULLDOWN;
228dfaf6e79Sthorpej }
229dfaf6e79Sthorpej
230dfaf6e79Sthorpej if (pull_strength) {
231dfaf6e79Sthorpej *pull_strength = -1;
232dfaf6e79Sthorpej if (bias_prop) {
233dfaf6e79Sthorpej uint32_t val;
234dfaf6e79Sthorpej if (of_getprop_uint32(phandle, bias_prop, &val) == 0) {
235dfaf6e79Sthorpej *pull_strength = (int)val;
236dfaf6e79Sthorpej }
237dfaf6e79Sthorpej }
238dfaf6e79Sthorpej }
239dfaf6e79Sthorpej
240dfaf6e79Sthorpej return bias;
241dfaf6e79Sthorpej }
242dfaf6e79Sthorpej
243dfaf6e79Sthorpej int
fdtbus_pinctrl_parse_drive(int phandle)244dfaf6e79Sthorpej fdtbus_pinctrl_parse_drive(int phandle)
245dfaf6e79Sthorpej {
246dfaf6e79Sthorpej int drive = -1;
247dfaf6e79Sthorpej
248dfaf6e79Sthorpej if (of_hasprop(phandle, "drive-push-pull"))
249dfaf6e79Sthorpej drive = GPIO_PIN_PUSHPULL;
250dfaf6e79Sthorpej else if (of_hasprop(phandle, "drive-open-drain"))
251dfaf6e79Sthorpej drive = GPIO_PIN_OPENDRAIN;
252dfaf6e79Sthorpej else if (of_hasprop(phandle, "drive-open-source"))
253dfaf6e79Sthorpej drive = 0;
254dfaf6e79Sthorpej
255dfaf6e79Sthorpej return drive;
256dfaf6e79Sthorpej }
257dfaf6e79Sthorpej
258dfaf6e79Sthorpej int
fdtbus_pinctrl_parse_drive_strength(int phandle)259dfaf6e79Sthorpej fdtbus_pinctrl_parse_drive_strength(int phandle)
260dfaf6e79Sthorpej {
261dfaf6e79Sthorpej int val;
262dfaf6e79Sthorpej
263dfaf6e79Sthorpej /*
264dfaf6e79Sthorpej * drive-strength has as an argument the target strength
265dfaf6e79Sthorpej * in mA.
266dfaf6e79Sthorpej */
267dfaf6e79Sthorpej
268dfaf6e79Sthorpej if (of_getprop_uint32(phandle, "drive-strength", &val) == 0)
269dfaf6e79Sthorpej return val;
270dfaf6e79Sthorpej
271dfaf6e79Sthorpej return -1;
272dfaf6e79Sthorpej }
fdtbus_pinctrl_parse_input_output(int phandle,int * output_value)273dfaf6e79Sthorpej int fdtbus_pinctrl_parse_input_output(int phandle, int *output_value)
274dfaf6e79Sthorpej {
275dfaf6e79Sthorpej int direction = -1;
276dfaf6e79Sthorpej int pinval = -1;
277dfaf6e79Sthorpej
278dfaf6e79Sthorpej if (of_hasprop(phandle, "input-enable")) {
279dfaf6e79Sthorpej direction = GPIO_PIN_INPUT;
280dfaf6e79Sthorpej } else if (of_hasprop(phandle, "input-disable")) {
281dfaf6e79Sthorpej /*
282dfaf6e79Sthorpej * XXXJRT How to represent this? This is more than
283dfaf6e79Sthorpej * just "don't set the direction" - it's an active
284dfaf6e79Sthorpej * command that might involve disabling an input
285dfaf6e79Sthorpej * buffer on the pin.
286dfaf6e79Sthorpej */
287dfaf6e79Sthorpej }
288dfaf6e79Sthorpej
289dfaf6e79Sthorpej if (of_hasprop(phandle, "output-enable")) {
290dfaf6e79Sthorpej if (direction == -1)
291dfaf6e79Sthorpej direction = 0;
292dfaf6e79Sthorpej direction |= GPIO_PIN_OUTPUT;
293dfaf6e79Sthorpej } else if (of_hasprop(phandle, "output-disable")) {
294dfaf6e79Sthorpej if (direction == -1)
295dfaf6e79Sthorpej direction = 0;
296dfaf6e79Sthorpej direction |= GPIO_PIN_TRISTATE;
297dfaf6e79Sthorpej }
298dfaf6e79Sthorpej
299dfaf6e79Sthorpej if (of_hasprop(phandle, "output-low")) {
300dfaf6e79Sthorpej if (direction == -1)
301dfaf6e79Sthorpej direction = 0;
302dfaf6e79Sthorpej direction |= GPIO_PIN_OUTPUT;
303dfaf6e79Sthorpej pinval = GPIO_PIN_LOW;
304dfaf6e79Sthorpej } else if (of_hasprop(phandle, "output-high")) {
305dfaf6e79Sthorpej if (direction == -1)
306dfaf6e79Sthorpej direction = 0;
307dfaf6e79Sthorpej direction |= GPIO_PIN_OUTPUT;
308dfaf6e79Sthorpej pinval = GPIO_PIN_HIGH;
309dfaf6e79Sthorpej }
310dfaf6e79Sthorpej
311dfaf6e79Sthorpej if (output_value)
312dfaf6e79Sthorpej *output_value = pinval;
313dfaf6e79Sthorpej
314dfaf6e79Sthorpej /*
315dfaf6e79Sthorpej * XXX input-schmitt-enable
316dfaf6e79Sthorpej * XXX input-schmitt-disable
317dfaf6e79Sthorpej */
318dfaf6e79Sthorpej
319dfaf6e79Sthorpej if (direction != -1
320dfaf6e79Sthorpej && (direction & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
321dfaf6e79Sthorpej == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
322dfaf6e79Sthorpej direction |= GPIO_PIN_INOUT;
323dfaf6e79Sthorpej }
324dfaf6e79Sthorpej
325dfaf6e79Sthorpej return direction;
326dfaf6e79Sthorpej }
327