meson_pinctrl.c revision 1.6.8.1 1 /* $NetBSD: meson_pinctrl.c,v 1.6.8.1 2021/01/03 16:34:50 thorpej 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.6.8.1 2021/01/03 16:34:50 thorpej 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 #ifdef SOC_MESONG12
88 { "amlogic,meson-g12a-aobus-pinctrl", (uintptr_t)&mesong12a_aobus_pinctrl_config },
89 { "amlogic,meson-g12a-periphs-pinctrl", (uintptr_t)&mesong12a_periphs_pinctrl_config },
90 #endif
91 { NULL, 0 }
92 };
93
94 #define MUX_READ(sc, reg) \
95 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh_mux, (reg))
96 #define MUX_WRITE(sc, reg, val) \
97 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh_mux, (reg), (val))
98
99 static const struct meson_pinctrl_group *
100 meson_pinctrl_find_group(struct meson_pinctrl_softc *sc,
101 const char *name)
102 {
103 const struct meson_pinctrl_group *group;
104 u_int n;
105
106 for (n = 0; n < sc->sc_conf->ngroups; n++) {
107 group = &sc->sc_conf->groups[n];
108 if (strcmp(group->name, name) == 0)
109 return group;
110 }
111
112 return NULL;
113 }
114
115 static bool
116 meson_pinctrl_group_in_bank(struct meson_pinctrl_softc *sc,
117 const struct meson_pinctrl_group *group, u_int bankno)
118 {
119 u_int n;
120
121 for (n = 0; n < group->nbank; n++) {
122 if (group->bank[n] == bankno)
123 return true;
124 }
125
126 return false;
127 }
128
129 static void
130 meson_pinctrl_set_group(struct meson_pinctrl_softc *sc,
131 const struct meson_pinctrl_group *group, bool enable)
132 {
133 uint32_t val;
134
135 val = MUX_READ(sc, group->reg);
136 if (group->mask == 0) {
137 if (enable)
138 val |= __BIT(group->bit);
139 else
140 val &= ~__BIT(group->bit);
141 } else {
142 val &= ~group->mask;
143 if (enable)
144 val |= __SHIFTIN(group->func, group->mask);
145 }
146 MUX_WRITE(sc, group->reg, val);
147 }
148
149 static void
150 meson_pinctrl_setfunc(struct meson_pinctrl_softc *sc, const char *name)
151 {
152 const struct meson_pinctrl_group *group, *target_group;
153 u_int n, bank;
154
155 target_group = meson_pinctrl_find_group(sc, name);
156 if (target_group == NULL) {
157 aprint_error_dev(sc->sc_dev, "function '%s' not supported\n", name);
158 return;
159 }
160
161 /* Disable conflicting groups */
162 for (n = 0; n < sc->sc_conf->ngroups; n++) {
163 group = &sc->sc_conf->groups[n];
164 if (target_group == group)
165 continue;
166 for (bank = 0; bank < target_group->nbank; bank++) {
167 if (meson_pinctrl_group_in_bank(sc, group, target_group->bank[bank]))
168 meson_pinctrl_set_group(sc, group, false);
169 }
170 }
171
172 /* Enable target group */
173 meson_pinctrl_set_group(sc, target_group, true);
174 }
175
176 static int
177 meson_pinctrl_set_config(device_t dev, const void *data, size_t len)
178 {
179 struct meson_pinctrl_softc * const sc = device_private(dev);
180 const char *groups;
181 int groups_len;
182
183 if (len != 4)
184 return -1;
185
186 const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
187 const int mux = of_find_firstchild_byname(phandle, "mux");
188 if (mux == -1)
189 return -1;
190
191 groups = fdtbus_pinctrl_parse_groups(mux, &groups_len);
192 if (groups == NULL)
193 return -1;
194
195 for (; groups_len > 0;
196 groups_len -= strlen(groups) + 1, groups += strlen(groups) + 1) {
197 meson_pinctrl_setfunc(sc, groups);
198 }
199
200 return 0;
201 }
202
203 static struct fdtbus_pinctrl_controller_func meson_pinctrl_funcs = {
204 .set_config = meson_pinctrl_set_config,
205 };
206
207 static bus_space_handle_t
208 meson_pinctrl_gpio_handle(struct meson_pinctrl_softc *sc,
209 const struct meson_pinctrl_gpioreg *gpioreg)
210 {
211 switch (gpioreg->type) {
212 case MESON_PINCTRL_REGTYPE_PULL:
213 return sc->sc_bsh_pull;
214 case MESON_PINCTRL_REGTYPE_PULL_ENABLE:
215 return sc->sc_bsh_pull_enable;
216 case MESON_PINCTRL_REGTYPE_GPIO:
217 return sc->sc_bsh_gpio;
218 default:
219 panic("unsupported GPIO regtype %d", gpioreg->type);
220 }
221 }
222
223 static int
224 meson_pinctrl_pin_read(void *priv, int pin)
225 {
226 struct meson_pinctrl_softc * const sc = priv;
227 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin];
228 const struct meson_pinctrl_gpioreg *gpio_reg = &pin_def->in;
229 bus_space_handle_t bsh;
230 uint32_t data;
231 int val;
232
233 KASSERT(pin < sc->sc_conf->ngpios);
234
235 bsh = meson_pinctrl_gpio_handle(sc, gpio_reg);
236 data = bus_space_read_4(sc->sc_bst, bsh, gpio_reg->reg);
237 val = __SHIFTOUT(data, gpio_reg->mask);
238
239 return val;
240 }
241
242 static void
243 meson_pinctrl_pin_write(void *priv, int pin, int val)
244 {
245 struct meson_pinctrl_softc * const sc = priv;
246 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin];
247 const struct meson_pinctrl_gpioreg *gpio_reg = &pin_def->out;
248 bus_space_handle_t bsh;
249 uint32_t data;
250
251 KASSERT(pin < sc->sc_conf->ngpios);
252
253 bsh = meson_pinctrl_gpio_handle(sc, gpio_reg);
254
255 mutex_enter(&sc->sc_lock);
256 data = bus_space_read_4(sc->sc_bst, bsh, gpio_reg->reg);
257 if (val)
258 data |= gpio_reg->mask;
259 else
260 data &= ~gpio_reg->mask;
261 bus_space_write_4(sc->sc_bst, bsh, gpio_reg->reg, data);
262 mutex_exit(&sc->sc_lock);
263 }
264
265 static void
266 meson_pinctrl_pin_dir(struct meson_pinctrl_softc *sc,
267 const struct meson_pinctrl_gpio *pin_def, int flags)
268 {
269 bus_space_handle_t bsh;
270 uint32_t data;
271
272 KASSERT(mutex_owned(&sc->sc_lock));
273
274 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->oen);
275 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->oen.reg);
276 if ((flags & GPIO_PIN_INPUT) != 0)
277 data |= pin_def->oen.mask;
278 else
279 data &= ~pin_def->oen.mask;
280 bus_space_write_4(sc->sc_bst, bsh, pin_def->oen.reg, data);
281 }
282
283 static void
284 meson_pinctrl_pin_ctl(void *priv, int pin, int flags)
285 {
286 struct meson_pinctrl_softc * const sc = priv;
287 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin];
288 bus_space_handle_t bsh;
289 uint32_t data;
290
291 KASSERT(pin < sc->sc_conf->ngpios);
292
293 mutex_enter(&sc->sc_lock);
294
295 if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0)
296 meson_pinctrl_pin_dir(sc, pin_def, flags);
297
298 if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) != 0) {
299 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupd);
300 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupd.reg);
301 if ((flags & GPIO_PIN_PULLUP) != 0)
302 data |= pin_def->pupd.mask;
303 else
304 data &= ~pin_def->pupd.mask;
305 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupd.reg, data);
306
307 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupden);
308 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupden.reg);
309 data |= pin_def->pupden.mask;
310 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupden.reg, data);
311 } else {
312 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupden);
313 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupden.reg);
314 data &= ~pin_def->pupden.mask;
315 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupden.reg, data);
316 }
317
318 mutex_exit(&sc->sc_lock);
319 }
320
321 static const struct meson_pinctrl_gpio *
322 meson_pinctrl_gpio_lookup(struct meson_pinctrl_softc *sc, u_int id)
323 {
324 if (id >= sc->sc_conf->ngpios)
325 return NULL;
326
327 if (sc->sc_conf->gpios[id].name == NULL)
328 return NULL;
329
330 return &sc->sc_conf->gpios[id];
331 }
332
333 static void *
334 meson_pinctrl_gpio_acquire(device_t dev, const void *data, size_t len, int flags)
335 {
336 struct meson_pinctrl_softc * const sc = device_private(dev);
337 const struct meson_pinctrl_gpio *pin_def;
338 const struct meson_pinctrl_group *group;
339 struct meson_pinctrl_gpio_pin *gpin;
340 const u_int *gpio = data;
341 u_int n, bank;
342
343 if (len != 12)
344 return NULL;
345
346 const u_int id = be32toh(gpio[1]);
347 const bool actlo = be32toh(gpio[2]) & 1;
348
349 pin_def = meson_pinctrl_gpio_lookup(sc, id);
350 if (pin_def == NULL)
351 return NULL;
352
353 /* Disable conflicting groups */
354 for (n = 0; n < sc->sc_conf->ngroups; n++) {
355 group = &sc->sc_conf->groups[n];
356 for (bank = 0; bank < group->nbank; bank++) {
357 if (group->bank[bank] == pin_def->id)
358 meson_pinctrl_set_group(sc, group, false);
359 }
360 }
361
362 mutex_enter(&sc->sc_lock);
363 meson_pinctrl_pin_dir(sc, pin_def, flags);
364 mutex_exit(&sc->sc_lock);
365
366 gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP);
367 gpin->pin_sc = sc;
368 gpin->pin_def = pin_def;
369 gpin->pin_flags = flags;
370 gpin->pin_actlo = actlo;
371
372 return gpin;
373 }
374
375 static void
376 meson_pinctrl_gpio_release(device_t dev, void *priv)
377 {
378 struct meson_pinctrl_softc * const sc = device_private(dev);
379 struct meson_pinctrl_gpio_pin *gpin = priv;
380 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def;
381
382 KASSERT(sc == gpin->pin_sc);
383
384 mutex_enter(&sc->sc_lock);
385 meson_pinctrl_pin_dir(sc, pin_def, GPIO_PIN_INPUT);
386 mutex_exit(&sc->sc_lock);
387
388 kmem_free(gpin, sizeof(*gpin));
389 }
390
391 static int
392 meson_pinctrl_gpio_read(device_t dev, void *priv, bool raw)
393 {
394 struct meson_pinctrl_softc * const sc = device_private(dev);
395 struct meson_pinctrl_gpio_pin *gpin = priv;
396 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def;
397 int val;
398
399 val = meson_pinctrl_pin_read(sc, pin_def->id);
400 if (!raw && gpin->pin_actlo)
401 val = !val;
402
403 return val;
404 }
405
406 static void
407 meson_pinctrl_gpio_write(device_t dev, void *priv, int val, bool raw)
408 {
409 struct meson_pinctrl_softc * const sc = device_private(dev);
410 struct meson_pinctrl_gpio_pin *gpin = priv;
411 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def;
412
413 if (!raw && gpin->pin_actlo)
414 val = !val;
415
416 meson_pinctrl_pin_write(sc, pin_def->id, val);
417 }
418
419 static struct fdtbus_gpio_controller_func meson_pinctrl_gpio_funcs = {
420 .acquire = meson_pinctrl_gpio_acquire,
421 .release = meson_pinctrl_gpio_release,
422 .read = meson_pinctrl_gpio_read,
423 .write = meson_pinctrl_gpio_write,
424 };
425
426 static int
427 meson_pinctrl_initres(struct meson_pinctrl_softc *sc)
428 {
429 bool gpio_found = false;
430 bus_addr_t addr;
431 bus_size_t size;
432 int child;
433
434 for (child = OF_child(sc->sc_phandle); child; child = OF_peer(child)) {
435 if (of_hasprop(child, "gpio-controller")) {
436 if (gpio_found)
437 continue;
438 gpio_found = true;
439
440 if (fdtbus_get_reg_byname(child, "mux", &addr, &size) != 0 ||
441 bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_mux) != 0) {
442 aprint_error(": couldn't map mux registers\n");
443 return ENXIO;
444 }
445 if (fdtbus_get_reg_byname(child, "gpio", &addr, &size) != 0 ||
446 bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_gpio) != 0) {
447 aprint_error(": couldn't map gpio registers\n");
448 return ENXIO;
449 }
450
451 /* pull register is optional */
452 if (fdtbus_get_reg_byname(child, "pull", &addr, &size) == 0) {
453 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_pull) != 0) {
454 aprint_error(": couldn't map pull registers\n");
455 return ENXIO;
456 }
457 }
458 /* pull-enable register is optional */
459 if (fdtbus_get_reg_byname(child, "pull-enable", &addr, &size) == 0) {
460 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_pull_enable) != 0) {
461 aprint_error(": couldn't map pull-enable registers\n");
462 return ENXIO;
463 }
464 }
465
466 sc->sc_phandle_gpio = child;
467 } else if (of_find_firstchild_byname(child, "mux") != -1) {
468 fdtbus_register_pinctrl_config(sc->sc_dev, child, &meson_pinctrl_funcs);
469 }
470 }
471
472 if (!gpio_found) {
473 aprint_error(": couldn't find gpio controller\n");
474 return ENOENT;
475 }
476
477 return 0;
478 }
479
480 static void
481 meson_pinctrl_initgpio(struct meson_pinctrl_softc *sc)
482 {
483 const struct meson_pinctrl_gpio *pin_def;
484 struct gpio_chipset_tag *gp;
485 struct gpiobus_attach_args gba;
486 int child, len, val;
487 u_int pin;
488
489 fdtbus_register_gpio_controller(sc->sc_dev, sc->sc_phandle_gpio, &meson_pinctrl_gpio_funcs);
490
491 for (child = OF_child(sc->sc_phandle_gpio); child; child = OF_peer(child)) {
492 if (!of_hasprop(child, "gpio-hog"))
493 continue;
494
495 const char *line_name = fdtbus_get_string(child, "line-name");
496 if (line_name == NULL)
497 line_name = fdtbus_get_string(child, "name");
498
499 const bool input = of_hasprop(child, "input");
500 const bool output_low = of_hasprop(child, "output-low");
501 const bool output_high = of_hasprop(child, "output-high");
502
503 if (!input && !output_low && !output_high) {
504 aprint_error_dev(sc->sc_dev, "no configuration for line %s\n", line_name);
505 continue;
506 }
507
508 const u_int *gpio = fdtbus_get_prop(child, "gpios", &len);
509 while (len >= 8) {
510 const u_int id = be32toh(gpio[0]);
511 const bool actlo = be32toh(gpio[1]) & 1;
512
513 pin_def = meson_pinctrl_gpio_lookup(sc, id);
514 if (pin_def != NULL) {
515 if (input) {
516 device_printf(sc->sc_dev, "%s %s set to input\n",
517 line_name, pin_def->name);
518 meson_pinctrl_pin_ctl(sc, pin_def->id, GPIO_PIN_INPUT);
519 } else {
520 val = output_high;
521 if (actlo)
522 val = !val;
523 device_printf(sc->sc_dev, "%s %s set to output (%s)\n",
524 line_name, pin_def->name, val ? "high" : "low");
525 meson_pinctrl_pin_write(sc, pin_def->id, val);
526 meson_pinctrl_pin_ctl(sc, pin_def->id, GPIO_PIN_OUTPUT);
527 }
528 } else {
529 aprint_error_dev(sc->sc_dev, "%s: unsupported pin %d\n", line_name, id);
530 }
531
532 len -= 8;
533 gpio += 8;
534 }
535 }
536
537 const u_int npins = sc->sc_conf->ngpios;
538 sc->sc_pins = kmem_zalloc(sizeof(*sc->sc_pins) * npins, KM_SLEEP);
539 for (pin = 0; pin < npins; pin++) {
540 pin_def = &sc->sc_conf->gpios[pin];
541 sc->sc_pins[pin].pin_num = pin;
542 if (pin_def->name == NULL)
543 continue;
544 sc->sc_pins[pin].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
545 GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
546 sc->sc_pins[pin].pin_state = meson_pinctrl_pin_read(sc, pin);
547 strlcpy(sc->sc_pins[pin].pin_defname, pin_def->name,
548 sizeof(sc->sc_pins[pin].pin_defname));
549 }
550
551 gp = &sc->sc_gp;
552 gp->gp_cookie = sc;
553 gp->gp_pin_read = meson_pinctrl_pin_read;
554 gp->gp_pin_write = meson_pinctrl_pin_write;
555 gp->gp_pin_ctl = meson_pinctrl_pin_ctl;
556
557 memset(&gba, 0, sizeof(gba));
558 gba.gba_gc = gp;
559 gba.gba_pins = sc->sc_pins;
560 gba.gba_npins = npins;
561 config_found_ia(sc->sc_dev, "gpiobus", &gba, NULL);
562 }
563
564 static int
565 meson_pinctrl_match(device_t parent, cfdata_t cf, void *aux)
566 {
567 struct fdt_attach_args * const faa = aux;
568
569 return of_match_compat_data(faa->faa_phandle, compat_data);
570 }
571
572 static void
573 meson_pinctrl_attach(device_t parent, device_t self, void *aux)
574 {
575 struct meson_pinctrl_softc * const sc = device_private(self);
576 struct fdt_attach_args * const faa = aux;
577
578 sc->sc_dev = self;
579 sc->sc_phandle = faa->faa_phandle;
580 sc->sc_bst = faa->faa_bst;
581 sc->sc_conf = (void *)of_search_compatible(sc->sc_phandle, compat_data)->data;
582 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
583
584 if (meson_pinctrl_initres(sc) != 0)
585 return;
586
587 aprint_naive("\n");
588 aprint_normal(": %s\n", sc->sc_conf->name);
589
590 meson_pinctrl_initgpio(sc);
591 }
592
593 CFATTACH_DECL_NEW(meson_pinctrl, sizeof(struct meson_pinctrl_softc),
594 meson_pinctrl_match, meson_pinctrl_attach, NULL, NULL);
595