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