diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2019-08-28 07:12:38 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2019-08-28 07:12:38 +0000 |
commit | 9e8391c6e299b23987dcc997a87880bb90761568 (patch) | |
tree | 949ff63ceb45b145b3ff811cf4d21425db687b97 /sys/dev/fdt | |
parent | ea371fa500e1c05b7ed1f4d9422db6751b360c55 (diff) |
Add amlpinctrl(4), a driver to configure pins and control GPIOs on Amlogic
SoCs. For now only a limited set of GPIO pins on the G12A/G12B variants
are supported.
ok patrick@
Diffstat (limited to 'sys/dev/fdt')
-rw-r--r-- | sys/dev/fdt/amlpinctrl.c | 262 | ||||
-rw-r--r-- | sys/dev/fdt/files.fdt | 6 |
2 files changed, 267 insertions, 1 deletions
diff --git a/sys/dev/fdt/amlpinctrl.c b/sys/dev/fdt/amlpinctrl.c new file mode 100644 index 00000000000..4e652b72d68 --- /dev/null +++ b/sys/dev/fdt/amlpinctrl.c @@ -0,0 +1,262 @@ +/* $OpenBSD: amlpinctrl.c,v 1.1 2019/08/28 07:12:37 kettenis Exp $ */ +/* + * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_gpio.h> +#include <dev/ofw/ofw_pinctrl.h> +#include <dev/ofw/fdt.h> + +#define PERIPHS_PIN_MUX_0 0xb0 +#define PERIPHS_PIN_MUX_B 0xbb +#define PREG_PAD_GPIO0_EN_N 0x10 +#define PREG_PAD_GPIO3_EN_N 0x19 + +struct aml_gpio_bank { + uint8_t first_pin, num_pins; + uint8_t mux_reg; + uint8_t gpio_reg; +}; + +struct aml_gpio_bank aml_g12a_gpio_banks[] = { + { 16, 9, PERIPHS_PIN_MUX_B - PERIPHS_PIN_MUX_0, /* GPIOH */ + PREG_PAD_GPIO3_EN_N - PREG_PAD_GPIO0_EN_N }, + { } +}; + +struct amlpinctrl_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_gpio_ioh; + bus_space_handle_t sc_mux_ioh; + + struct aml_gpio_bank *sc_gpio_banks; + struct gpio_controller sc_gc; +}; + +int amlpinctrl_match(struct device *, void *, void *); +void amlpinctrl_attach(struct device *, struct device *, void *); + +struct cfattach amlpinctrl_ca = { + sizeof(struct amlpinctrl_softc), amlpinctrl_match, amlpinctrl_attach +}; + +struct cfdriver amlpinctrl_cd = { + NULL, "amlpinctrl", DV_DULL +}; + +int amlpinctrl_pinctrl(uint32_t, void *); +void amlpinctrl_config_pin(void *, uint32_t *, int); +int amlpinctrl_get_pin(void *, uint32_t *); +void amlpinctrl_set_pin(void *, uint32_t *, int); + +int +amlpinctrl_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + int node = faa->fa_node; + + return OF_is_compatible(node, "amlogic,meson-g12a-periphs-pinctrl"); +} + +void +amlpinctrl_attach(struct device *parent, struct device *self, void *aux) +{ + struct amlpinctrl_softc *sc = (struct amlpinctrl_softc *)self; + struct fdt_attach_args *faa = aux; + uint64_t addr[5], size[5]; + uint32_t *cell; + uint32_t acells, scells; + uint32_t reg[20]; + int node = faa->fa_node; + int child; + int i, len, line; + + for (child = OF_child(node); child; child = OF_peer(child)) { + if (OF_getproplen(child, "gpio-controller") == 0) + break; + } + if (child == 0) { + printf(": no register banks\n"); + return; + } + + acells = OF_getpropint(node, "#address-cells", faa->fa_acells); + scells = OF_getpropint(node, "#size-cells", faa->fa_scells); + len = OF_getproplen(child, "reg"); + line = (acells + scells) * sizeof(uint32_t); + if (acells < 1 || acells > 2 || scells < 1 || scells > 2 || + len > sizeof(reg) || (len / line) > nitems(addr)) { + printf(": unexpected register layout\n"); + return; + } + + OF_getpropintarray(child, "reg", reg, len); + for (i = 0, cell = reg; i < len / line; i++) { + addr[i] = cell[0]; + if (acells > 1) + addr[i] = (addr[i] << 32) | cell[1]; + cell += acells; + size[i] = cell[0]; + if (scells > 1) + size[i] = (size[i] << 32) | cell[1]; + cell += scells; + } + + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, addr[0], size[0], 0, &sc->sc_gpio_ioh)) { + printf(": can't map gpio registers\n"); + return; + } + if (bus_space_map(sc->sc_iot, addr[3], size[3], 0, &sc->sc_mux_ioh)) { + printf(": can't map mux registers\n"); + return; + } + + printf("\n"); + + sc->sc_gpio_banks = aml_g12a_gpio_banks; + + pinctrl_register(faa->fa_node, amlpinctrl_pinctrl, sc); + + sc->sc_gc.gc_node = child; + sc->sc_gc.gc_cookie = sc; + sc->sc_gc.gc_config_pin = amlpinctrl_config_pin; + sc->sc_gc.gc_get_pin = amlpinctrl_get_pin; + sc->sc_gc.gc_set_pin = amlpinctrl_set_pin; + gpio_controller_register(&sc->sc_gc); +} + +struct aml_gpio_bank * +amlpinctrl_lookup_bank(struct amlpinctrl_softc *sc, uint32_t pin) +{ + struct aml_gpio_bank *bank; + + for (bank = sc->sc_gpio_banks; bank->num_pins > 0; bank++) { + if (pin >= bank->first_pin && + pin < bank->first_pin + bank->num_pins) + return bank; + } + + return NULL; +} + +int +amlpinctrl_pinctrl(uint32_t phandle, void *cookie) +{ + printf("%s: 0x%08x\n", __func__, phandle); + return 0; +} + +void +amlpinctrl_config_pin(void *cookie, uint32_t *cells, int config) +{ + struct amlpinctrl_softc *sc = cookie; + struct aml_gpio_bank *bank; + bus_addr_t off; + uint32_t pin = cells[0]; + uint32_t reg; + + bank = amlpinctrl_lookup_bank(sc, pin); + if (bank == NULL) { + printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]); + return; + } + + pin = pin - bank->first_pin; + + /* mux */ + off = (bank->mux_reg + pin / 8) << 2; + reg = bus_space_read_4(sc->sc_iot, sc->sc_mux_ioh, off); + reg &= ~(0xf << ((pin % 8) * 4)); + bus_space_write_4(sc->sc_iot, sc->sc_mux_ioh, off, reg); + + /* gpio */ + off = bank->gpio_reg << 2; + reg = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh, off); + if (config & GPIO_CONFIG_OUTPUT) + reg &= ~(1 << pin); + else + reg |= (1 << pin); + bus_space_write_4(sc->sc_iot, sc->sc_gpio_ioh, off, reg); +} + +int +amlpinctrl_get_pin(void *cookie, uint32_t *cells) +{ + struct amlpinctrl_softc *sc = cookie; + struct aml_gpio_bank *bank; + bus_addr_t off; + uint32_t pin = cells[0]; + uint32_t flags = cells[1]; + uint32_t reg; + int val; + + bank = amlpinctrl_lookup_bank(sc, pin); + if (bank == NULL) { + printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]); + return 0; + } + + pin = pin - bank->first_pin; + + /* gpio */ + off = (bank->gpio_reg + 2) << 2; + reg = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh, off); + val = (reg >> pin) & 1; + if (flags & GPIO_ACTIVE_LOW) + val = !val; + + return val; +} + +void +amlpinctrl_set_pin(void *cookie, uint32_t *cells, int val) +{ + struct amlpinctrl_softc *sc = cookie; + struct aml_gpio_bank *bank; + bus_addr_t off; + uint32_t pin = cells[0]; + uint32_t flags = cells[1]; + int reg; + + bank = amlpinctrl_lookup_bank(sc, pin); + if (bank == NULL) { + printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]); + return; + } + + if (flags & GPIO_ACTIVE_LOW) + val = !val; + + pin = pin - bank->first_pin; + + /* gpio */ + off = (bank->gpio_reg + 2) << 2; + reg = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh, off); + if (val) + reg |= (1 << pin); + else + reg &= ~(1 << pin); + bus_space_write_4(sc->sc_iot, sc->sc_gpio_ioh, off, reg); +} diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index 812ac4eebe6..56ed1dfcd86 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -1,4 +1,4 @@ -# $OpenBSD: files.fdt,v 1.86 2019/08/27 22:21:52 kettenis Exp $ +# $OpenBSD: files.fdt,v 1.87 2019/08/28 07:12:37 kettenis Exp $ # # Config file and device description for machine-independent FDT code. # Included by ports that need it. @@ -130,6 +130,10 @@ device amlclock attach amlclock at fdt file dev/fdt/amlclock.c amlclock +device amlpinctrl +attach amlpinctrl at fdt +file dev/fdt/amlpinctrl.c amlpinctrl + device amlreset attach amlreset at fdt file dev/fdt/amlreset.c amlreset |