meson_pinctrl.c revision 1.5 1 /* $NetBSD: meson_pinctrl.c,v 1.5 2019/04/19 19:07:56 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2019 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "opt_soc.h"
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: meson_pinctrl.c,v 1.5 2019/04/19 19:07:56 jmcneill Exp $");
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/device.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/mutex.h>
40 #include <sys/kmem.h>
41 #include <sys/gpio.h>
42
43 #include <dev/gpio/gpiovar.h>
44
45 #include <dev/fdt/fdtvar.h>
46
47 #include <arm/amlogic/meson_pinctrl.h>
48
49 struct meson_pinctrl_softc {
50 device_t sc_dev;
51 bus_space_tag_t sc_bst;
52 bus_space_handle_t sc_bsh_mux;
53 bus_space_handle_t sc_bsh_pull;
54 bus_space_handle_t sc_bsh_pull_enable;
55 bus_space_handle_t sc_bsh_gpio;
56 int sc_phandle;
57 int sc_phandle_gpio;
58
59 kmutex_t sc_lock;
60
61 const struct meson_pinctrl_config *sc_conf;
62
63 struct gpio_chipset_tag sc_gp;
64 gpio_pin_t *sc_pins;
65 };
66
67 struct meson_pinctrl_gpio_pin {
68 struct meson_pinctrl_softc *pin_sc;
69 const struct meson_pinctrl_gpio *pin_def;
70 int pin_flags;
71 bool pin_actlo;
72 };
73
74 static const struct of_compat_data compat_data[] = {
75 #ifdef SOC_MESON8B
76 { "amlogic,meson8b-aobus-pinctrl", (uintptr_t)&meson8b_aobus_pinctrl_config },
77 { "amlogic,meson8b-cbus-pinctrl", (uintptr_t)&meson8b_cbus_pinctrl_config },
78 #endif
79 #ifdef SOC_MESONGXBB
80 { "amlogic,meson-gxbb-aobus-pinctrl", (uintptr_t)&mesongxbb_aobus_pinctrl_config },
81 { "amlogic,meson-gxbb-periphs-pinctrl", (uintptr_t)&mesongxbb_periphs_pinctrl_config },
82 #endif
83 #ifdef SOC_MESONGXL
84 { "amlogic,meson-gxl-aobus-pinctrl", (uintptr_t)&mesongxl_aobus_pinctrl_config },
85 { "amlogic,meson-gxl-periphs-pinctrl", (uintptr_t)&mesongxl_periphs_pinctrl_config },
86 #endif
87 { NULL, 0 }
88 };
89
90 #define MUX_READ(sc, reg) \
91 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh_mux, (reg))
92 #define MUX_WRITE(sc, reg, val) \
93 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh_mux, (reg), (val))
94
95 static const struct meson_pinctrl_group *
96 meson_pinctrl_find_group(struct meson_pinctrl_softc *sc,
97 const char *name)
98 {
99 const struct meson_pinctrl_group *group;
100 u_int n;
101
102 for (n = 0; n < sc->sc_conf->ngroups; n++) {
103 group = &sc->sc_conf->groups[n];
104 if (strcmp(group->name, name) == 0)
105 return group;
106 }
107
108 return NULL;
109 }
110
111 static bool
112 meson_pinctrl_group_in_bank(struct meson_pinctrl_softc *sc,
113 const struct meson_pinctrl_group *group, u_int bankno)
114 {
115 u_int n;
116
117 for (n = 0; n < group->nbank; n++) {
118 if (group->bank[n] == bankno)
119 return true;
120 }
121
122 return false;
123 }
124
125 static void
126 meson_pinctrl_set_group(struct meson_pinctrl_softc *sc,
127 const struct meson_pinctrl_group *group, bool enable)
128 {
129 uint32_t val;
130
131 val = MUX_READ(sc, group->reg);
132 if (enable)
133 val |= __BIT(group->bit);
134 else
135 val &= ~__BIT(group->bit);
136 MUX_WRITE(sc, group->reg, val);
137 }
138
139 static void
140 meson_pinctrl_setfunc(struct meson_pinctrl_softc *sc, const char *name)
141 {
142 const struct meson_pinctrl_group *group, *target_group;
143 u_int n, bank;
144
145 target_group = meson_pinctrl_find_group(sc, name);
146 if (target_group == NULL) {
147 aprint_error_dev(sc->sc_dev, "function '%s' not supported\n", name);
148 return;
149 }
150
151 /* Disable conflicting groups */
152 for (n = 0; n < sc->sc_conf->ngroups; n++) {
153 group = &sc->sc_conf->groups[n];
154 if (target_group == group)
155 continue;
156 for (bank = 0; bank < target_group->nbank; bank++) {
157 if (meson_pinctrl_group_in_bank(sc, group, target_group->bank[bank]))
158 meson_pinctrl_set_group(sc, group, false);
159 }
160 }
161
162 /* Enable target group */
163 meson_pinctrl_set_group(sc, target_group, true);
164 }
165
166 static int
167 meson_pinctrl_set_config(device_t dev, const void *data, size_t len)
168 {
169 struct meson_pinctrl_softc * const sc = device_private(dev);
170 const char *groups;
171 int groups_len;
172
173 if (len != 4)
174 return -1;
175
176 const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
177 const int mux = of_find_firstchild_byname(phandle, "mux");
178 if (mux == -1)
179 return -1;
180
181 groups = fdtbus_pinctrl_parse_groups(mux, &groups_len);
182 if (groups == NULL)
183 return -1;
184
185 for (; groups_len > 0;
186 groups_len -= strlen(groups) + 1, groups += strlen(groups) + 1) {
187 meson_pinctrl_setfunc(sc, groups);
188 }
189
190 return 0;
191 }
192
193 static struct fdtbus_pinctrl_controller_func meson_pinctrl_funcs = {
194 .set_config = meson_pinctrl_set_config,
195 };
196
197 static bus_space_handle_t
198 meson_pinctrl_gpio_handle(struct meson_pinctrl_softc *sc,
199 const struct meson_pinctrl_gpioreg *gpioreg)
200 {
201 switch (gpioreg->type) {
202 case MESON_PINCTRL_REGTYPE_PULL:
203 return sc->sc_bsh_pull;
204 case MESON_PINCTRL_REGTYPE_PULL_ENABLE:
205 return sc->sc_bsh_pull_enable;
206 case MESON_PINCTRL_REGTYPE_GPIO:
207 return sc->sc_bsh_gpio;
208 default:
209 panic("unsupported GPIO regtype %d", gpioreg->type);
210 }
211 }
212
213 static int
214 meson_pinctrl_pin_read(void *priv, int pin)
215 {
216 struct meson_pinctrl_softc * const sc = priv;
217 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin];
218 const struct meson_pinctrl_gpioreg *gpio_reg = &pin_def->in;
219 bus_space_handle_t bsh;
220 uint32_t data;
221 int val;
222
223 KASSERT(pin < sc->sc_conf->ngpios);
224
225 bsh = meson_pinctrl_gpio_handle(sc, gpio_reg);
226 data = bus_space_read_4(sc->sc_bst, bsh, gpio_reg->reg);
227 val = __SHIFTOUT(data, gpio_reg->mask);
228
229 return val;
230 }
231
232 static void
233 meson_pinctrl_pin_write(void *priv, int pin, int val)
234 {
235 struct meson_pinctrl_softc * const sc = priv;
236 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin];
237 const struct meson_pinctrl_gpioreg *gpio_reg = &pin_def->out;
238 bus_space_handle_t bsh;
239 uint32_t data;
240
241 KASSERT(pin < sc->sc_conf->ngpios);
242
243 bsh = meson_pinctrl_gpio_handle(sc, gpio_reg);
244
245 mutex_enter(&sc->sc_lock);
246 data = bus_space_read_4(sc->sc_bst, bsh, gpio_reg->reg);
247 if (val)
248 data |= gpio_reg->mask;
249 else
250 data &= ~gpio_reg->mask;
251 bus_space_write_4(sc->sc_bst, bsh, gpio_reg->reg, data);
252 mutex_exit(&sc->sc_lock);
253 }
254
255 static void
256 meson_pinctrl_pin_dir(struct meson_pinctrl_softc *sc,
257 const struct meson_pinctrl_gpio *pin_def, int flags)
258 {
259 bus_space_handle_t bsh;
260 uint32_t data;
261
262 KASSERT(mutex_owned(&sc->sc_lock));
263
264 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->oen);
265 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->oen.reg);
266 if ((flags & GPIO_PIN_INPUT) != 0)
267 data |= pin_def->oen.mask;
268 else
269 data &= ~pin_def->oen.mask;
270 bus_space_write_4(sc->sc_bst, bsh, pin_def->oen.reg, data);
271 }
272
273 static void
274 meson_pinctrl_pin_ctl(void *priv, int pin, int flags)
275 {
276 struct meson_pinctrl_softc * const sc = priv;
277 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin];
278 bus_space_handle_t bsh;
279 uint32_t data;
280
281 KASSERT(pin < sc->sc_conf->ngpios);
282
283 mutex_enter(&sc->sc_lock);
284
285 if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0)
286 meson_pinctrl_pin_dir(sc, pin_def, flags);
287
288 if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) != 0) {
289 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupd);
290 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupd.reg);
291 if ((flags & GPIO_PIN_PULLUP) != 0)
292 data |= pin_def->pupd.mask;
293 else
294 data &= ~pin_def->pupd.mask;
295 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupd.reg, data);
296
297 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupden);
298 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupden.reg);
299 data |= pin_def->pupden.mask;
300 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupden.reg, data);
301 } else {
302 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupden);
303 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupden.reg);
304 data &= ~pin_def->pupden.mask;
305 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupden.reg, data);
306 }
307
308 mutex_exit(&sc->sc_lock);
309 }
310
311 static const struct meson_pinctrl_gpio *
312 meson_pinctrl_gpio_lookup(struct meson_pinctrl_softc *sc, u_int id)
313 {
314 if (id >= sc->sc_conf->ngpios)
315 return NULL;
316
317 if (sc->sc_conf->gpios[id].name == NULL)
318 return NULL;
319
320 return &sc->sc_conf->gpios[id];
321 }
322
323 static void *
324 meson_pinctrl_gpio_acquire(device_t dev, const void *data, size_t len, int flags)
325 {
326 struct meson_pinctrl_softc * const sc = device_private(dev);
327 const struct meson_pinctrl_gpio *pin_def;
328 const struct meson_pinctrl_group *group;
329 struct meson_pinctrl_gpio_pin *gpin;
330 const u_int *gpio = data;
331 u_int n, bank;
332
333 if (len != 12)
334 return NULL;
335
336 const u_int id = be32toh(gpio[1]);
337 const bool actlo = be32toh(gpio[2]) & 1;
338
339 pin_def = meson_pinctrl_gpio_lookup(sc, id);
340 if (pin_def == NULL)
341 return NULL;
342
343 /* Disable conflicting groups */
344 for (n = 0; n < sc->sc_conf->ngroups; n++) {
345 group = &sc->sc_conf->groups[n];
346 for (bank = 0; bank < group->nbank; bank++) {
347 if (group->bank[bank] == pin_def->id)
348 meson_pinctrl_set_group(sc, group, false);
349 }
350 }
351
352 mutex_enter(&sc->sc_lock);
353 meson_pinctrl_pin_dir(sc, pin_def, flags);
354 mutex_exit(&sc->sc_lock);
355
356 gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP);
357 gpin->pin_sc = sc;
358 gpin->pin_def = pin_def;
359 gpin->pin_flags = flags;
360 gpin->pin_actlo = actlo;
361
362 return gpin;
363 }
364
365 static void
366 meson_pinctrl_gpio_release(device_t dev, void *priv)
367 {
368 struct meson_pinctrl_softc * const sc = device_private(dev);
369 struct meson_pinctrl_gpio_pin *gpin = priv;
370 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def;
371
372 KASSERT(sc == gpin->pin_sc);
373
374 mutex_enter(&sc->sc_lock);
375 meson_pinctrl_pin_dir(sc, pin_def, GPIO_PIN_INPUT);
376 mutex_exit(&sc->sc_lock);
377
378 kmem_free(gpin, sizeof(*gpin));
379 }
380
381 static int
382 meson_pinctrl_gpio_read(device_t dev, void *priv, bool raw)
383 {
384 struct meson_pinctrl_softc * const sc = device_private(dev);
385 struct meson_pinctrl_gpio_pin *gpin = priv;
386 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def;
387 int val;
388
389 val = meson_pinctrl_pin_read(sc, pin_def->id);
390 if (!raw && gpin->pin_actlo)
391 val = !val;
392
393 return val;
394 }
395
396 static void
397 meson_pinctrl_gpio_write(device_t dev, void *priv, int val, bool raw)
398 {
399 struct meson_pinctrl_softc * const sc = device_private(dev);
400 struct meson_pinctrl_gpio_pin *gpin = priv;
401 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def;
402
403 if (!raw && gpin->pin_actlo)
404 val = !val;
405
406 meson_pinctrl_pin_write(sc, pin_def->id, val);
407 }
408
409 static struct fdtbus_gpio_controller_func meson_pinctrl_gpio_funcs = {
410 .acquire = meson_pinctrl_gpio_acquire,
411 .release = meson_pinctrl_gpio_release,
412 .read = meson_pinctrl_gpio_read,
413 .write = meson_pinctrl_gpio_write,
414 };
415
416 static int
417 meson_pinctrl_initres(struct meson_pinctrl_softc *sc)
418 {
419 bool gpio_found = false;
420 bus_addr_t addr;
421 bus_size_t size;
422 int child;
423
424 for (child = OF_child(sc->sc_phandle); child; child = OF_peer(child)) {
425 if (of_hasprop(child, "gpio-controller")) {
426 if (gpio_found)
427 continue;
428 gpio_found = true;
429
430 if (fdtbus_get_reg_byname(child, "mux", &addr, &size) != 0 ||
431 bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_mux) != 0) {
432 aprint_error(": couldn't map mux registers\n");
433 return ENXIO;
434 }
435 if (fdtbus_get_reg_byname(child, "pull", &addr, &size) != 0 ||
436 bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_pull) != 0) {
437 aprint_error(": couldn't map pull registers\n");
438 return ENXIO;
439 }
440 if (fdtbus_get_reg_byname(child, "gpio", &addr, &size) != 0 ||
441 bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_gpio) != 0) {
442 aprint_error(": couldn't map gpio registers\n");
443 return ENXIO;
444 }
445
446 /* pull-enable register is optional */
447 if (fdtbus_get_reg_byname(child, "pull-enable", &addr, &size) == 0) {
448 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_pull_enable) != 0) {
449 aprint_error(": couldn't map pull-enable registers\n");
450 return ENXIO;
451 }
452 }
453
454 sc->sc_phandle_gpio = child;
455 } else if (of_find_firstchild_byname(child, "mux") != -1) {
456 fdtbus_register_pinctrl_config(sc->sc_dev, child, &meson_pinctrl_funcs);
457 }
458 }
459
460 if (!gpio_found) {
461 aprint_error(": couldn't find gpio controller\n");
462 return ENOENT;
463 }
464
465 return 0;
466 }
467
468 static void
469 meson_pinctrl_initgpio(struct meson_pinctrl_softc *sc)
470 {
471 const struct meson_pinctrl_gpio *pin_def;
472 struct gpio_chipset_tag *gp;
473 struct gpiobus_attach_args gba;
474 int child, len, val;
475 u_int pin;
476
477 fdtbus_register_gpio_controller(sc->sc_dev, sc->sc_phandle_gpio, &meson_pinctrl_gpio_funcs);
478
479 for (child = OF_child(sc->sc_phandle_gpio); child; child = OF_peer(child)) {
480 if (!of_hasprop(child, "gpio-hog"))
481 continue;
482
483 const char *line_name = fdtbus_get_string(child, "line-name");
484 if (line_name == NULL)
485 line_name = fdtbus_get_string(child, "name");
486
487 const bool input = of_hasprop(child, "input");
488 const bool output_low = of_hasprop(child, "output-low");
489 const bool output_high = of_hasprop(child, "output-high");
490
491 if (!input && !output_low && !output_high) {
492 aprint_error_dev(sc->sc_dev, "no configuration for line %s\n", line_name);
493 continue;
494 }
495
496 const u_int *gpio = fdtbus_get_prop(child, "gpios", &len);
497 while (len >= 8) {
498 const u_int id = be32toh(gpio[0]);
499 const bool actlo = be32toh(gpio[1]) & 1;
500
501 pin_def = meson_pinctrl_gpio_lookup(sc, id);
502 if (pin_def != NULL) {
503 if (input) {
504 device_printf(sc->sc_dev, "%s %s set to input\n",
505 line_name, pin_def->name);
506 meson_pinctrl_pin_ctl(sc, pin_def->id, GPIO_PIN_INPUT);
507 } else {
508 val = output_high;
509 if (actlo)
510 val = !val;
511 device_printf(sc->sc_dev, "%s %s set to output (%s)\n",
512 line_name, pin_def->name, val ? "high" : "low");
513 meson_pinctrl_pin_write(sc, pin_def->id, val);
514 meson_pinctrl_pin_ctl(sc, pin_def->id, GPIO_PIN_OUTPUT);
515 }
516 } else {
517 aprint_error_dev(sc->sc_dev, "%s: unsupported pin %d\n", line_name, id);
518 }
519
520 len -= 8;
521 gpio += 8;
522 }
523 }
524
525 const u_int npins = sc->sc_conf->ngpios;
526 sc->sc_pins = kmem_zalloc(sizeof(*sc->sc_pins) * npins, KM_SLEEP);
527 for (pin = 0; pin < npins; pin++) {
528 pin_def = &sc->sc_conf->gpios[pin];
529 sc->sc_pins[pin].pin_num = pin;
530 if (pin_def->name == NULL)
531 continue;
532 sc->sc_pins[pin].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
533 GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
534 sc->sc_pins[pin].pin_state = meson_pinctrl_pin_read(sc, pin);
535 strlcpy(sc->sc_pins[pin].pin_defname, pin_def->name,
536 sizeof(sc->sc_pins[pin].pin_defname));
537 }
538
539 gp = &sc->sc_gp;
540 gp->gp_cookie = sc;
541 gp->gp_pin_read = meson_pinctrl_pin_read;
542 gp->gp_pin_write = meson_pinctrl_pin_write;
543 gp->gp_pin_ctl = meson_pinctrl_pin_ctl;
544
545 memset(&gba, 0, sizeof(gba));
546 gba.gba_gc = gp;
547 gba.gba_pins = sc->sc_pins;
548 gba.gba_npins = npins;
549 config_found_ia(sc->sc_dev, "gpiobus", &gba, NULL);
550 }
551
552 static int
553 meson_pinctrl_match(device_t parent, cfdata_t cf, void *aux)
554 {
555 struct fdt_attach_args * const faa = aux;
556
557 return of_match_compat_data(faa->faa_phandle, compat_data);
558 }
559
560 static void
561 meson_pinctrl_attach(device_t parent, device_t self, void *aux)
562 {
563 struct meson_pinctrl_softc * const sc = device_private(self);
564 struct fdt_attach_args * const faa = aux;
565
566 sc->sc_dev = self;
567 sc->sc_phandle = faa->faa_phandle;
568 sc->sc_bst = faa->faa_bst;
569 sc->sc_conf = (void *)of_search_compatible(sc->sc_phandle, compat_data)->data;
570 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
571
572 if (meson_pinctrl_initres(sc) != 0)
573 return;
574
575 aprint_naive("\n");
576 aprint_normal(": %s\n", sc->sc_conf->name);
577
578 fdtbus_pinctrl_configure();
579
580 meson_pinctrl_initgpio(sc);
581 }
582
583 CFATTACH_DECL_NEW(meson_pinctrl, sizeof(struct meson_pinctrl_softc),
584 meson_pinctrl_match, meson_pinctrl_attach, NULL, NULL);
585