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