Module Name: src Committed By: jmcneill Date: Sat Aug 26 17:59:24 UTC 2017
Modified Files: src/sys/arch/arm/sunxi: sunxi_gpio.c Log Message: Add support for interrupt pins. To generate a diff of this commit: cvs rdiff -u -r1.11 -r1.12 src/sys/arch/arm/sunxi/sunxi_gpio.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/sunxi/sunxi_gpio.c diff -u src/sys/arch/arm/sunxi/sunxi_gpio.c:1.11 src/sys/arch/arm/sunxi/sunxi_gpio.c:1.12 --- src/sys/arch/arm/sunxi/sunxi_gpio.c:1.11 Fri Aug 25 00:07:03 2017 +++ src/sys/arch/arm/sunxi/sunxi_gpio.c Sat Aug 26 17:59:24 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_gpio.c,v 1.11 2017/08/25 00:07:03 jmcneill Exp $ */ +/* $NetBSD: sunxi_gpio.c,v 1.12 2017/08/26 17:59:24 jmcneill Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -29,7 +29,7 @@ #include "opt_soc.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c,v 1.11 2017/08/25 00:07:03 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c,v 1.12 2017/08/26 17:59:24 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -39,12 +39,15 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c #include <sys/mutex.h> #include <sys/kmem.h> #include <sys/gpio.h> +#include <sys/bitops.h> #include <dev/fdt/fdtvar.h> #include <dev/gpio/gpiovar.h> #include <arm/sunxi/sunxi_gpio.h> +#define SUNXI_GPIO_MAX_EINT 32 + #define SUNXI_GPIO_PORT(port) (0x24 * (port)) #define SUNXI_GPIO_CFG(port, pin) (SUNXI_GPIO_PORT(port) + 0x00 + (0x4 * ((pin) / 8))) #define SUNXI_GPIO_CFG_PINMASK(pin) (0x7 << (((pin) % 8) * 4)) @@ -56,6 +59,15 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c #define SUNXI_GPIO_PULL_UP 1 #define SUNXI_GPIO_PULL_DOWN 2 #define SUNXI_GPIO_PULL_PINMASK(pin) (0x3 << (((pin) % 16) * 2)) +#define SUNXI_GPIO_INT_CFG(eint) (0x200 + (0x4 * ((eint) / 8))) +#define SUNXI_GPIO_INT_MODEMASK(eint) (0xf << (((eint) % 8) * 4)) +#define SUNXI_GPIO_INT_MODE_POS_EDGE 0x0 +#define SUNXI_GPIO_INT_MODE_NEG_EDGE 0x1 +#define SUNXI_GPIO_INT_MODE_HIGH_LEVEL 0x2 +#define SUNXI_GPIO_INT_MODE_LOW_LEVEL 0x3 +#define SUNXI_GPIO_INT_MODE_DOUBLE_EDGE 0x4 +#define SUNXI_GPIO_INT_CTL 0x210 +#define SUNXI_GPIO_INT_STATUS 0x214 static const struct of_compat_data compat_data[] = { #ifdef SOC_SUN5I_A13 @@ -80,6 +92,13 @@ static const struct of_compat_data compa { NULL } }; +struct sunxi_gpio_eint { + int (*eint_func)(void *); + void *eint_arg; + int eint_flags; + int eint_num; +}; + struct sunxi_gpio_softc { device_t sc_dev; bus_space_tag_t sc_bst; @@ -90,6 +109,9 @@ struct sunxi_gpio_softc { struct gpio_chipset_tag sc_gp; gpio_pin_t *sc_pins; device_t sc_gpiodev; + + void *sc_ih; + struct sunxi_gpio_eint sc_eint[SUNXI_GPIO_MAX_EINT]; }; struct sunxi_gpio_pin { @@ -353,6 +375,175 @@ static struct fdtbus_gpio_controller_fun .write = sunxi_gpio_write, }; +static int +sunxi_gpio_intr(void *priv) +{ + struct sunxi_gpio_softc * const sc = priv; + struct sunxi_gpio_eint *eint; + uint32_t status, bit; + int ret = 0; + + status = GPIO_READ(sc, SUNXI_GPIO_INT_STATUS); + GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS, status); + + while ((bit = ffs32(status)) != 0) { + status &= ~__BIT(bit - 1); + eint = &sc->sc_eint[bit - 1]; + if (eint->eint_func == NULL) + continue; + const bool mpsafe = (eint->eint_flags & FDT_INTR_MPSAFE) != 0; + if (!mpsafe) + KERNEL_LOCK(1, curlwp); + ret |= eint->eint_func(eint->eint_arg); + if (!mpsafe) + KERNEL_UNLOCK_ONE(curlwp); + } + + return ret; +} + +static void * +sunxi_gpio_establish(device_t dev, u_int *specifier, int ipl, int flags, + int (*func)(void *), void *arg) +{ + struct sunxi_gpio_softc * const sc = device_private(dev); + const struct sunxi_gpio_pins *pin_def; + struct sunxi_gpio_eint *eint; + uint32_t val; + u_int mode; + + if (ipl != IPL_VM) { + aprint_error_dev(dev, "%s: wrong IPL %d (expected %d)\n", + __func__, ipl, IPL_VM); + return NULL; + } + + /* 1st cell is the bank */ + /* 2nd cell is the pin */ + /* 3rd cell is flags */ + const u_int port = be32toh(specifier[0]); + const u_int pin = be32toh(specifier[1]); + const u_int type = be32toh(specifier[2]) & 0xf; + + switch (type) { + case 0x1: + mode = SUNXI_GPIO_INT_MODE_POS_EDGE; + break; + case 0x2: + mode = SUNXI_GPIO_INT_MODE_NEG_EDGE; + break; + case 0x3: + mode = SUNXI_GPIO_INT_MODE_DOUBLE_EDGE; + break; + case 0x4: + mode = SUNXI_GPIO_INT_MODE_HIGH_LEVEL; + break; + case 0x8: + mode = SUNXI_GPIO_INT_MODE_LOW_LEVEL; + break; + default: + aprint_error_dev(dev, "%s: unsupported irq type 0x%x\n", + __func__, type); + return NULL; + } + + pin_def = sunxi_gpio_lookup(sc, port, pin); + if (pin_def == NULL) + return NULL; + if (pin_def->functions[pin_def->eint_func] == NULL || + strcmp(pin_def->functions[pin_def->eint_func], "eint") != 0) + return NULL; + + KASSERT(pin_def->eint_num < SUNXI_GPIO_MAX_EINT); + + mutex_enter(&sc->sc_lock); + + eint = &sc->sc_eint[pin_def->eint_num]; + if (eint->eint_func != NULL) { + mutex_exit(&sc->sc_lock); + return NULL; /* in use */ + } + + /* Set function */ + if (sunxi_gpio_setfunc(sc, pin_def, "eint") != 0) { + mutex_exit(&sc->sc_lock); + return NULL; + } + + eint->eint_func = func; + eint->eint_arg = arg; + eint->eint_flags = flags; + eint->eint_num = pin_def->eint_num; + + /* Configure eint mode */ + val = GPIO_READ(sc, SUNXI_GPIO_INT_CFG(eint->eint_num)); + val &= ~SUNXI_GPIO_INT_MODEMASK(eint->eint_num); + val |= __SHIFTIN(mode, SUNXI_GPIO_INT_MODEMASK(eint->eint_num)); + GPIO_WRITE(sc, SUNXI_GPIO_INT_CFG(eint->eint_num), val); + + /* Enable eint */ + val = GPIO_READ(sc, SUNXI_GPIO_INT_CTL); + val |= __BIT(eint->eint_num); + GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL, val); + + mutex_exit(&sc->sc_lock); + + return eint; +} + +static void +sunxi_gpio_disestablish(device_t dev, void *ih) +{ + struct sunxi_gpio_softc * const sc = device_private(dev); + struct sunxi_gpio_eint * const eint = ih; + uint32_t val; + + KASSERT(eint->eint_func != NULL); + + mutex_enter(&sc->sc_lock); + + /* Disable eint */ + val = GPIO_READ(sc, SUNXI_GPIO_INT_CTL); + val &= ~__BIT(eint->eint_num); + GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL, val); + GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS, __BIT(eint->eint_num)); + + eint->eint_func = NULL; + eint->eint_arg = NULL; + eint->eint_flags = 0; + + mutex_exit(&sc->sc_lock); +} + +static bool +sunxi_gpio_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen) +{ + struct sunxi_gpio_softc * const sc = device_private(dev); + const struct sunxi_gpio_pins *pin_def; + + /* 1st cell is the bank */ + /* 2nd cell is the pin */ + /* 3rd cell is flags */ + if (!specifier) + return false; + const u_int port = be32toh(specifier[0]); + const u_int pin = be32toh(specifier[1]); + + pin_def = sunxi_gpio_lookup(sc, port, pin); + if (pin_def == NULL) + return false; + + snprintf(buf, buflen, "GPIO %s", pin_def->name); + + return true; +} + +static struct fdtbus_interrupt_controller_func sunxi_gpio_intrfuncs = { + .establish = sunxi_gpio_establish, + .disestablish = sunxi_gpio_disestablish, + .intrstr = sunxi_gpio_intrstr, +}; + static const char * sunxi_pinctrl_parse_function(int phandle) { @@ -586,6 +777,7 @@ sunxi_gpio_attach(device_t parent, devic struct sunxi_gpio_softc * const sc = device_private(self); struct fdt_attach_args * const faa = aux; const int phandle = faa->faa_phandle; + char intrstr[128]; struct fdtbus_reset *rst; struct clk *clk; bus_addr_t addr; @@ -632,4 +824,23 @@ sunxi_gpio_attach(device_t parent, devic fdtbus_pinctrl_configure(); sunxi_gpio_attach_ports(sc); + + /* Disable all external interrupts */ + GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL, 0); + GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS, GPIO_READ(sc, SUNXI_GPIO_INT_STATUS)); + + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error_dev(self, "failed to decode interrupt\n"); + return; + } + sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_VM, FDT_INTR_MPSAFE, + sunxi_gpio_intr, sc); + if (sc->sc_ih == NULL) { + aprint_error_dev(self, "failed to establish interrupt on %s\n", + intrstr); + return; + } + aprint_normal_dev(self, "interrupting on %s\n", intrstr); + fdtbus_register_interrupt_controller(self, phandle, + &sunxi_gpio_intrfuncs); }