Date: Sun, 19 Jan 2020 19:51:20 +0000 (UTC) From: Emmanuel Vadot <manu@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r356891 - head/sys/arm/allwinner Message-ID: <202001191951.00JJpKpW005021@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: manu Date: Sun Jan 19 19:51:20 2020 New Revision: 356891 URL: https://svnweb.freebsd.org/changeset/base/356891 Log: arm: allwinner: Add GPIO Interrupt support Not all pins in Allwinner have interrupts support so we rely on the padconf data to add the proper caps when pin_getcaps is called. The pin is switch to the specific "eint" function during setup_intr and switched back to its old function in teardown_intr. Only INTR_MAP_DATA_GPIO is supported for now. MFC after: 1 month Modified: head/sys/arm/allwinner/aw_gpio.c Modified: head/sys/arm/allwinner/aw_gpio.c ============================================================================== --- head/sys/arm/allwinner/aw_gpio.c Sun Jan 19 19:47:04 2020 (r356890) +++ head/sys/arm/allwinner/aw_gpio.c Sun Jan 19 19:51:20 2020 (r356891) @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include <sys/lock.h> #include <sys/mutex.h> #include <sys/gpio.h> +#include <sys/proc.h> #include <machine/bus.h> #include <machine/resource.h> @@ -61,11 +62,18 @@ __FBSDID("$FreeBSD$"); #include "opt_soc.h" #endif +#ifdef INTRNG +#include "pic_if.h" +#endif + #include "gpio_if.h" #define AW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ - GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) + GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); +#define AW_GPIO_INTR_CAPS (GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | \ + GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) + #define AW_GPIO_NONE 0 #define AW_GPIO_PULLUP 1 #define AW_GPIO_PULLDOWN 2 @@ -249,19 +257,47 @@ struct clk_list { clk_t clk; }; +#ifdef INTRNG +struct gpio_irqsrc { + struct intr_irqsrc isrc; + u_int irq; + uint32_t mode; + uint32_t pin; + uint32_t bank; + uint32_t intnum; + uint32_t intfunc; + uint32_t oldfunc; + bool enabled; +}; +#endif + +#define AW_GPIO_MEMRES 0 +#define AW_GPIO_IRQRES 1 +#define AW_GPIO_RESSZ 2 + struct aw_gpio_softc { device_t sc_dev; device_t sc_busdev; + struct resource * sc_res[AW_GPIO_RESSZ]; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; - bus_space_tag_t sc_bst; - bus_space_handle_t sc_bsh; void * sc_intrhand; struct aw_gpio_conf *conf; TAILQ_HEAD(, clk_list) clk_list; + +#ifdef INTRNG + struct gpio_irqsrc *gpio_pic_irqsrc; + int nirqs; +#endif }; +static struct resource_spec aw_gpio_res_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, + { -1, 0, 0 } +}; + #define AW_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define AW_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define AW_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) @@ -271,15 +307,19 @@ struct aw_gpio_softc { #define AW_GPIO_GP_DRV(_bank, _idx) 0x14 + ((_bank) * 0x24) + ((_idx) << 2) #define AW_GPIO_GP_PUL(_bank, _idx) 0x1c + ((_bank) * 0x24) + ((_idx) << 2) -#define AW_GPIO_GP_INT_CFG0 0x200 -#define AW_GPIO_GP_INT_CFG1 0x204 -#define AW_GPIO_GP_INT_CFG2 0x208 -#define AW_GPIO_GP_INT_CFG3 0x20c +#define AW_GPIO_GP_INT_BASE(_bank) (0x200 + 0x20 * _bank) -#define AW_GPIO_GP_INT_CTL 0x210 -#define AW_GPIO_GP_INT_STA 0x214 -#define AW_GPIO_GP_INT_DEB 0x218 +#define AW_GPIO_GP_INT_CFG(_bank, _pin) (AW_GPIO_GP_INT_BASE(_bank) + (0x4 * ((_pin) / 8))) +#define AW_GPIO_GP_INT_CTL(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x10) +#define AW_GPIO_GP_INT_STA(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x14) +#define AW_GPIO_GP_INT_DEB(_bank) (AW_GPIO_GP_INT_BASE(_bank) + 0x18) +#define AW_GPIO_INT_EDGE_POSITIVE 0x0 +#define AW_GPIO_INT_EDGE_NEGATIVE 0x1 +#define AW_GPIO_INT_LEVEL_HIGH 0x2 +#define AW_GPIO_INT_LEVEL_LOW 0x3 +#define AW_GPIO_INT_EDGE_BOTH 0x4 + static char *aw_gpio_parse_function(phandle_t node); static const char **aw_gpio_parse_pins(phandle_t node, int *pins_nb); static uint32_t aw_gpio_parse_bias(phandle_t node); @@ -290,10 +330,16 @@ static int aw_gpio_pin_set(device_t dev, uint32_t pin, static int aw_gpio_pin_get_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int *value); static int aw_gpio_pin_set_locked(struct aw_gpio_softc *sc, uint32_t pin, unsigned int value); +static void aw_gpio_intr(void *arg); +static void aw_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc); +static void aw_gpio_pic_disable_intr_locked(struct aw_gpio_softc *sc, struct intr_irqsrc *isrc); +static void aw_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc); +static int aw_gpio_register_isrcs(struct aw_gpio_softc *sc); + #define AW_GPIO_WRITE(_sc, _off, _val) \ - bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) + bus_write_4((_sc)->sc_res[AW_GPIO_MEMRES], _off, _val) #define AW_GPIO_READ(_sc, _off) \ - bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) + bus_read_4((_sc)->sc_res[AW_GPIO_MEMRES], _off) static uint32_t aw_gpio_get_function(struct aw_gpio_softc *sc, uint32_t pin) @@ -492,6 +538,8 @@ aw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32 return (EINVAL); *caps = AW_GPIO_DEFAULT_CAPS; + if (sc->conf->padconf->pins[pin].eint_func != 0) + *caps |= AW_GPIO_INTR_CAPS; return (0); } @@ -807,6 +855,27 @@ aw_gpio_pin_config_32(device_t dev, uint32_t first_pin } static int +aw_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, + pcell_t *gpios, uint32_t *pin, uint32_t *flags) +{ + struct aw_gpio_softc *sc; + int i; + + sc = device_get_softc(bus); + + /* The GPIO pins are mapped as: <gpio-phandle bank pin flags>. */ + for (i = 0; i < sc->conf->padconf->npins; i++) + if (sc->conf->padconf->pins[i].port == gpios[0] && + sc->conf->padconf->pins[i].pin == gpios[1]) { + *pin = i; + break; + } + *flags = gpios[gcells - 1]; + + return (0); +} + +static int aw_find_pinnum_by_name(struct aw_gpio_softc *sc, const char *pinname) { int i; @@ -938,7 +1007,7 @@ aw_gpio_probe(device_t dev) static int aw_gpio_attach(device_t dev) { - int rid, error; + int error; phandle_t gpio; struct aw_gpio_softc *sc; struct clk_list *clkp, *clkp_tmp; @@ -951,22 +1020,15 @@ aw_gpio_attach(device_t dev) mtx_init(&sc->sc_mtx, "aw gpio", "gpio", MTX_SPIN); - rid = 0; - sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); - if (!sc->sc_mem_res) { - device_printf(dev, "cannot allocate memory window\n"); - goto fail; + if (bus_alloc_resources(dev, aw_gpio_res_spec, sc->sc_res) != 0) { + device_printf(dev, "cannot allocate device resources\n"); + return (ENXIO); } - sc->sc_bst = rman_get_bustag(sc->sc_mem_res); - sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); - - rid = 0; - sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE); - if (!sc->sc_irq_res) { - device_printf(dev, "cannot allocate interrupt\n"); + if (bus_setup_intr(dev, sc->sc_res[AW_GPIO_IRQRES], + INTR_TYPE_CLK | INTR_MPSAFE, NULL, aw_gpio_intr, sc, + &sc->sc_intrhand)) { + device_printf(dev, "cannot setup interrupt handler\n"); goto fail; } @@ -1009,6 +1071,11 @@ aw_gpio_attach(device_t dev) goto fail; } +#ifdef INTRNG + aw_gpio_register_isrcs(sc); + intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); +#endif + sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) goto fail; @@ -1062,40 +1129,339 @@ aw_gpio_detach(device_t dev) return (EBUSY); } -static phandle_t -aw_gpio_get_node(device_t dev, device_t bus) +static void +aw_gpio_intr(void *arg) { + struct aw_gpio_softc *sc; + struct intr_irqsrc *isrc; + uint32_t reg; + int irq; - /* We only have one child, the GPIO bus, which needs our own node. */ - return (ofw_bus_get_node(dev)); + sc = (struct aw_gpio_softc *)arg; + + AW_GPIO_LOCK(sc); + for (irq = 0; irq < sc->nirqs; irq++) { + if (!sc->gpio_pic_irqsrc[irq].enabled) + continue; + + reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_STA(sc->gpio_pic_irqsrc[irq].bank)); + if (!(reg & (1 << sc->gpio_pic_irqsrc[irq].intnum))) + continue; + + isrc = &sc->gpio_pic_irqsrc[irq].isrc; + if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { + aw_gpio_pic_disable_intr_locked(sc, isrc); + aw_gpio_pic_post_filter(sc->sc_dev, isrc); + device_printf(sc->sc_dev, "Stray irq %u disabled\n", irq); + } + } + AW_GPIO_UNLOCK(sc); } +/* + * Interrupts support + */ + static int -aw_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, - pcell_t *gpios, uint32_t *pin, uint32_t *flags) +aw_gpio_register_isrcs(struct aw_gpio_softc *sc) { + const char *name; + int nirqs; + int pin; + int err; + + name = device_get_nameunit(sc->sc_dev); + + for (nirqs = 0, pin = 0; pin < sc->conf->padconf->npins; pin++) { + if (sc->conf->padconf->pins[pin].eint_func == 0) + continue; + + nirqs++; + } + + sc->gpio_pic_irqsrc = malloc(sizeof(*sc->gpio_pic_irqsrc) * nirqs, + M_DEVBUF, M_WAITOK | M_ZERO); + for (nirqs = 0, pin = 0; pin < sc->conf->padconf->npins; pin++) { + if (sc->conf->padconf->pins[pin].eint_func == 0) + continue; + + sc->gpio_pic_irqsrc[nirqs].pin = pin; + sc->gpio_pic_irqsrc[nirqs].bank = sc->conf->padconf->pins[pin].eint_bank; + sc->gpio_pic_irqsrc[nirqs].intnum = sc->conf->padconf->pins[pin].eint_num; + sc->gpio_pic_irqsrc[nirqs].intfunc = sc->conf->padconf->pins[pin].eint_func; + sc->gpio_pic_irqsrc[nirqs].irq = nirqs; + sc->gpio_pic_irqsrc[nirqs].mode = GPIO_INTR_CONFORM; + + err = intr_isrc_register(&sc->gpio_pic_irqsrc[nirqs].isrc, + sc->sc_dev, 0, "%s,%s", name, + sc->conf->padconf->pins[pin].functions[sc->conf->padconf->pins[pin].eint_func]); + if (err) { + device_printf(sc->sc_dev, "intr_isrs_register failed for irq %d\n", nirqs); + } + + nirqs++; + } + + sc->nirqs = nirqs; + + return (0); +} + +static void +aw_gpio_pic_disable_intr_locked(struct aw_gpio_softc *sc, struct intr_irqsrc *isrc) +{ + u_int irq; + uint32_t reg; + + AW_GPIO_LOCK_ASSERT(sc); + irq = ((struct gpio_irqsrc *)isrc)->irq; + reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank)); + reg &= ~(1 << sc->gpio_pic_irqsrc[irq].intnum); + AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank), reg); + + sc->gpio_pic_irqsrc[irq].enabled = false; +} + +static void +aw_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ struct aw_gpio_softc *sc; - int i; - sc = device_get_softc(bus); + sc = device_get_softc(dev); - /* The GPIO pins are mapped as: <gpio-phandle bank pin flags>. */ - for (i = 0; i < sc->conf->padconf->npins; i++) - if (sc->conf->padconf->pins[i].port == gpios[0] && - sc->conf->padconf->pins[i].pin == gpios[1]) { - *pin = i; + AW_GPIO_LOCK(sc); + aw_gpio_pic_disable_intr_locked(sc, isrc); + AW_GPIO_UNLOCK(sc); +} + +static void +aw_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct aw_gpio_softc *sc; + u_int irq; + uint32_t reg; + + sc = device_get_softc(dev); + irq = ((struct gpio_irqsrc *)isrc)->irq; + AW_GPIO_LOCK(sc); + reg = AW_GPIO_READ(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank)); + reg |= 1 << sc->gpio_pic_irqsrc[irq].intnum; + AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_CTL(sc->gpio_pic_irqsrc[irq].bank), reg); + AW_GPIO_UNLOCK(sc); + + sc->gpio_pic_irqsrc[irq].enabled = true; +} + +static int +aw_gpio_pic_map_gpio(struct aw_gpio_softc *sc, struct intr_map_data_gpio *dag, + u_int *irqp, u_int *mode) +{ + u_int irq; + int pin; + + irq = dag->gpio_pin_num; + + for (pin = 0; pin < sc->nirqs; pin++) + if (sc->gpio_pic_irqsrc[pin].pin == irq) break; - } - *flags = gpios[gcells - 1]; + if (pin == sc->nirqs) { + device_printf(sc->sc_dev, "Invalid interrupt number %u\n", irq); + return (EINVAL); + } + switch (dag->gpio_intr_mode) { + case GPIO_INTR_LEVEL_LOW: + case GPIO_INTR_LEVEL_HIGH: + case GPIO_INTR_EDGE_RISING: + case GPIO_INTR_EDGE_FALLING: + case GPIO_INTR_EDGE_BOTH: + break; + default: + device_printf(sc->sc_dev, "Unsupported interrupt mode 0x%8x\n", + dag->gpio_intr_mode); + return (EINVAL); + } + + *irqp = pin; + if (mode != NULL) + *mode = dag->gpio_intr_mode; + return (0); } +static int +aw_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct aw_gpio_softc *sc; + u_int irq; + int err; + + sc = device_get_softc(dev); + switch (data->type) { + case INTR_MAP_DATA_GPIO: + err = aw_gpio_pic_map_gpio(sc, + (struct intr_map_data_gpio *)data, + &irq, NULL); + break; + default: + return (ENOTSUP); + }; + + if (err == 0) + *isrcp = &sc->gpio_pic_irqsrc[irq].isrc; + return (0); +} + +static int +aw_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct aw_gpio_softc *sc; + struct gpio_irqsrc *gi; + uint32_t irqcfg; + uint32_t pinidx, reg; + u_int irq, mode; + int err; + + sc = device_get_softc(dev); + gi = (struct gpio_irqsrc *)isrc; + + switch (data->type) { + case INTR_MAP_DATA_GPIO: + err = aw_gpio_pic_map_gpio(sc, + (struct intr_map_data_gpio *)data, + &irq, &mode); + break; + default: + return (ENOTSUP); + }; + + pinidx = (sc->gpio_pic_irqsrc[irq].intnum % 8) * 4; + + AW_GPIO_LOCK(sc); + switch (mode) { + case GPIO_INTR_LEVEL_LOW: + irqcfg = AW_GPIO_INT_LEVEL_LOW << pinidx; + break; + case GPIO_INTR_LEVEL_HIGH: + irqcfg = AW_GPIO_INT_LEVEL_HIGH << pinidx; + break; + case GPIO_INTR_EDGE_RISING: + irqcfg = AW_GPIO_INT_EDGE_POSITIVE << pinidx; + break; + case GPIO_INTR_EDGE_FALLING: + irqcfg = AW_GPIO_INT_EDGE_NEGATIVE << pinidx; + break; + case GPIO_INTR_EDGE_BOTH: + irqcfg = AW_GPIO_INT_EDGE_BOTH << pinidx; + break; + } + + /* Switch the pin to interrupt mode */ + sc->gpio_pic_irqsrc[irq].oldfunc = aw_gpio_get_function(sc, + sc->gpio_pic_irqsrc[irq].pin); + aw_gpio_set_function(sc, sc->gpio_pic_irqsrc[irq].pin, + sc->gpio_pic_irqsrc[irq].intfunc); + + /* Write interrupt mode */ + reg = AW_GPIO_READ(sc, + AW_GPIO_GP_INT_CFG(sc->gpio_pic_irqsrc[irq].bank, + sc->gpio_pic_irqsrc[irq].intnum)); + reg &= ~(0xF << pinidx); + reg |= irqcfg; + AW_GPIO_WRITE(sc, + AW_GPIO_GP_INT_CFG(sc->gpio_pic_irqsrc[irq].bank, + sc->gpio_pic_irqsrc[irq].intnum), + reg); + + AW_GPIO_UNLOCK(sc); + + return (0); +} + +static int +aw_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct aw_gpio_softc *sc; + struct gpio_irqsrc *gi; + + sc = device_get_softc(dev); + gi = (struct gpio_irqsrc *)isrc; + + /* Switch back the pin to it's original function */ + AW_GPIO_LOCK(sc); + aw_gpio_set_function(sc, gi->pin, gi->oldfunc); + AW_GPIO_UNLOCK(sc); + + return (0); +} + +static void +aw_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct aw_gpio_softc *sc; + struct gpio_irqsrc *gi; + + sc = device_get_softc(dev); + gi = (struct gpio_irqsrc *)isrc; + + arm_irq_memory_barrier(0); + AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_STA(gi->bank), 1 << gi->intnum); +} + +static void +aw_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct aw_gpio_softc *sc; + struct gpio_irqsrc *gi; + + sc = device_get_softc(dev); + gi = (struct gpio_irqsrc *)isrc; + + arm_irq_memory_barrier(0); + AW_GPIO_WRITE(sc, AW_GPIO_GP_INT_STA(gi->bank), 1 << gi->intnum); + aw_gpio_pic_enable_intr(dev, isrc); +} + +static void +aw_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct aw_gpio_softc *sc; + + sc = device_get_softc(dev); + aw_gpio_pic_disable_intr_locked(sc, isrc); +} + +/* + * OFWBUS Interface + */ +static phandle_t +aw_gpio_get_node(device_t dev, device_t bus) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(dev)); +} + static device_method_t aw_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, aw_gpio_probe), DEVMETHOD(device_attach, aw_gpio_attach), DEVMETHOD(device_detach, aw_gpio_detach), + +#ifdef INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, aw_gpio_pic_disable_intr), + DEVMETHOD(pic_enable_intr, aw_gpio_pic_enable_intr), + DEVMETHOD(pic_map_intr, aw_gpio_pic_map_intr), + DEVMETHOD(pic_setup_intr, aw_gpio_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, aw_gpio_pic_teardown_intr), + DEVMETHOD(pic_post_filter, aw_gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, aw_gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, aw_gpio_pic_pre_ithread), +#endif /* GPIO protocol */ DEVMETHOD(gpio_get_bus, aw_gpio_get_bus),
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202001191951.00JJpKpW005021>