git: ec556724d7ad - main - Add interrupt handling to rk_gpio driver.
Date: Sat, 20 Aug 2022 11:32:26 UTC
The branch main has been updated by ganbold: URL: https://cgit.FreeBSD.org/src/commit/?id=ec556724d7ad1ee117fe595728e73dc9ddf78048 commit ec556724d7ad1ee117fe595728e73dc9ddf78048 Author: Søren Schmidt <sos@FreeBSD.org> AuthorDate: 2022-08-20 06:09:49 +0000 Commit: Ganbold Tsagaankhuu <ganbold@FreeBSD.org> CommitDate: 2022-08-20 11:30:54 +0000 Add interrupt handling to rk_gpio driver. --- sys/arm64/rockchip/rk_gpio.c | 227 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 226 insertions(+), 1 deletion(-) diff --git a/sys/arm64/rockchip/rk_gpio.c b/sys/arm64/rockchip/rk_gpio.c index c9ad1c9ea1df..c3b1044df2f7 100644 --- a/sys/arm64/rockchip/rk_gpio.c +++ b/sys/arm64/rockchip/rk_gpio.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/module.h> +#include <sys/proc.h> #include <sys/rman.h> #include <sys/lock.h> #include <sys/mutex.h> @@ -51,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <dev/extres/clk/clk.h> #include "gpio_if.h" +#include "pic_if.h" #include "fdt_pinctrl_if.h" @@ -73,7 +75,9 @@ enum gpio_regs { #define RK_GPIO_LS_SYNC 0x60 /* Level sensitive syncronization enable register */ #define RK_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ - GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) + GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_EDGE_BOTH | \ + GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | \ + GPIO_INTR_LEVEL_HIGH | GPIO_INTR_LEVEL_LOW) #define GPIO_FLAGS_PINCTRL GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN #define RK_GPIO_MAX_PINS 32 @@ -83,6 +87,12 @@ struct pin_cached { uint32_t flags; }; +struct rk_pin_irqsrc { + struct intr_irqsrc isrc; + uint32_t irq; + uint32_t mode; +}; + struct rk_gpio_softc { device_t sc_dev; device_t sc_busdev; @@ -97,6 +107,8 @@ struct rk_gpio_softc { uint32_t version; struct pin_cached pin_cached[RK_GPIO_MAX_PINS]; uint8_t regs[RK_GPIO_REGNUM]; + void *ihandle; + struct rk_pin_irqsrc isrcs[RK_GPIO_MAX_PINS]; }; static struct ofw_compat_data compat_data[] = { @@ -113,6 +125,7 @@ static struct resource_spec rk_gpio_spec[] = { #define RK_GPIO_VERSION 0x78 #define RK_GPIO_TYPE_V1 0x00000000 #define RK_GPIO_TYPE_V2 0x01000c2b +#define RK_GPIO_ISRC(sc, irq) (&(sc->isrcs[irq].isrc)) static int rk_gpio_detach(device_t dev); @@ -141,6 +154,29 @@ rk_gpio_read_bit(struct rk_gpio_softc *sc, int reg, int bit) return (value & 1); } +static void +rk_gpio_write_bit(struct rk_gpio_softc *sc, int reg, int bit, int data) +{ + int offset = sc->regs[reg]; + uint32_t value; + + if (sc->version == RK_GPIO_TYPE_V1) { + value = RK_GPIO_READ(sc, offset); + if (data) + value |= (1 << bit); + else + value &= ~(1 << bit); + RK_GPIO_WRITE(sc, offset, value); + } else { + if (data) + value = (1 << (bit % 16)); + else + value = 0; + value |= (1 << ((bit % 16) + 16)); + RK_GPIO_WRITE(sc, bit > 15 ? offset + 4 : offset, value); + } +} + static uint32_t rk_gpio_read_4(struct rk_gpio_softc *sc, int reg) { @@ -168,6 +204,43 @@ rk_gpio_write_4(struct rk_gpio_softc *sc, int reg, uint32_t value) } } +static int +rk_gpio_intr(void *arg) +{ + struct rk_gpio_softc *sc = (struct rk_gpio_softc *)arg;; + struct trapframe *tf = curthread->td_intr_frame; + uint32_t status; + + RK_GPIO_LOCK(sc); + status = rk_gpio_read_4(sc, RK_GPIO_INT_STATUS); + rk_gpio_write_4(sc, RK_GPIO_PORTA_EOI, status); + RK_GPIO_UNLOCK(sc); + + while (status) { + int pin = ffs(status) - 1; + + status &= ~(1 << pin); + if (intr_isrc_dispatch(RK_GPIO_ISRC(sc, pin), tf)) { + device_printf(sc->sc_dev, "Interrupt pin=%d unhandled\n", + pin); + continue; + } + + if ((sc->version == RK_GPIO_TYPE_V1) && + (sc->isrcs[pin].mode & GPIO_INTR_EDGE_BOTH)) { + RK_GPIO_LOCK(sc); + if (rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin)) + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, + (1 << pin), 0); + else + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, + (1 << pin), 1); + RK_GPIO_UNLOCK(sc); + } + } + return (FILTER_HANDLED); +} + static int rk_gpio_probe(device_t dev) { @@ -221,6 +294,15 @@ rk_gpio_attach(device_t dev) rk_gpio_detach(dev); return (ENXIO); } + + if ((err = bus_setup_intr(dev, sc->sc_res[1], + INTR_TYPE_MISC | INTR_MPSAFE, rk_gpio_intr, NULL, + sc, &sc->ihandle))) { + device_printf(dev, "Can not setup IRQ\n"); + rk_gpio_detach(dev); + return (ENXIO); + } + RK_GPIO_LOCK(sc); sc->version = rk_gpio_read_4(sc, RK_GPIO_VERSION); RK_GPIO_UNLOCK(sc); @@ -259,6 +341,23 @@ rk_gpio_attach(device_t dev) return (ENXIO); } + for (i = 0; i < RK_GPIO_MAX_PINS; i++) { + sc->isrcs[i].irq = i; + sc->isrcs[i].mode = GPIO_INTR_CONFORM; + if ((err = intr_isrc_register(RK_GPIO_ISRC(sc, i), + dev, 0, "%s", device_get_nameunit(dev)))) { + device_printf(dev, "Can not register isrc %d\n", err); + rk_gpio_detach(dev); + return (ENXIO); + } + } + + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { + device_printf(dev, "Can not register pic\n"); + rk_gpio_detach(dev); + return (ENXIO); + } + sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) { rk_gpio_detach(dev); @@ -549,6 +648,127 @@ rk_gpio_get_node(device_t bus, device_t dev) return (ofw_bus_get_node(bus)); } +static int +rk_pic_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct rk_gpio_softc *sc = device_get_softc(dev); + struct intr_map_data_gpio *gdata; + uint32_t irq; + + if (data->type != INTR_MAP_DATA_GPIO) { + device_printf(dev, "Wrong type\n"); + return (ENOTSUP); + } + gdata = (struct intr_map_data_gpio *)data; + irq = gdata->gpio_pin_num; + if (irq >= RK_GPIO_MAX_PINS) { + device_printf(dev, "Invalid interrupt %u\n", irq); + return (EINVAL); + } + *isrcp = RK_GPIO_ISRC(sc, irq); + return (0); +} + +static int +rk_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct rk_gpio_softc *sc = device_get_softc(dev); + struct rk_pin_irqsrc *rkisrc = (struct rk_pin_irqsrc *)isrc; + struct intr_map_data_gpio *gdata; + uint32_t mode; + uint8_t pin; + + if (!data) { + device_printf(dev, "No map data\n"); + return (ENOTSUP); + } + gdata = (struct intr_map_data_gpio *)data; + mode = gdata->gpio_intr_mode; + pin = gdata->gpio_pin_num; + + if (rkisrc->irq != gdata->gpio_pin_num) { + device_printf(dev, "Interrupts don't match\n"); + return (EINVAL); + } + + if (isrc->isrc_handlers != 0) { + device_printf(dev, "Handler already attached\n"); + return (rkisrc->mode == mode ? 0 : EINVAL); + } + rkisrc->mode = mode; + + RK_GPIO_LOCK(sc); + + switch (mode & GPIO_INTR_MASK) { + case GPIO_INTR_EDGE_RISING: + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1); + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 1); + break; + case GPIO_INTR_EDGE_FALLING: + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1); + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 0); + break; + case GPIO_INTR_EDGE_BOTH: + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 1); + if (sc->version == RK_GPIO_TYPE_V1) { + if (rk_gpio_read_bit(sc, RK_GPIO_EXT_PORTA, pin)) + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, + pin, 0); + else + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, + pin, 1); + } else + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_BOTH, pin, 1); + break; + case GPIO_INTR_LEVEL_HIGH: + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 0); + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 1); + break; + case GPIO_INTR_LEVEL_LOW: + rk_gpio_write_bit(sc, RK_GPIO_SWPORTA_DDR, pin, 0); + rk_gpio_write_bit(sc, RK_GPIO_INTTYPE_LEVEL, pin, 0); + rk_gpio_write_bit(sc, RK_GPIO_INT_POLARITY, pin, 0); + break; + default: + rk_gpio_write_bit(sc, RK_GPIO_INTMASK, pin, 1); + rk_gpio_write_bit(sc, RK_GPIO_INTEN, pin, 0); + RK_GPIO_UNLOCK(sc); + return (EINVAL); + } + rk_gpio_write_bit(sc, RK_GPIO_DEBOUNCE, pin, 1); + rk_gpio_write_bit(sc, RK_GPIO_INTMASK, pin, 0); + rk_gpio_write_bit(sc, RK_GPIO_INTEN, pin, 1); + RK_GPIO_UNLOCK(sc); + + return (0); +} + +static int +rk_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct rk_gpio_softc *sc = device_get_softc(dev); + struct rk_pin_irqsrc *irqsrc; + + irqsrc = (struct rk_pin_irqsrc *)isrc; + + if (isrc->isrc_handlers == 0) { + irqsrc->mode = GPIO_INTR_CONFORM; + RK_GPIO_LOCK(sc); + rk_gpio_write_bit(sc, RK_GPIO_INTEN, irqsrc->irq, 0); + rk_gpio_write_bit(sc, RK_GPIO_INTMASK, irqsrc->irq, 0); + rk_gpio_write_bit(sc, RK_GPIO_DEBOUNCE, irqsrc->irq, 0); + RK_GPIO_UNLOCK(sc); + } + return (0); +} + static device_method_t rk_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_gpio_probe), @@ -569,6 +789,11 @@ static device_method_t rk_gpio_methods[] = { DEVMETHOD(gpio_pin_config_32, rk_gpio_pin_config_32), DEVMETHOD(gpio_map_gpios, rk_gpio_map_gpios), + /* Interrupt controller interface */ + DEVMETHOD(pic_map_intr, rk_pic_map_intr), + DEVMETHOD(pic_setup_intr, rk_pic_setup_intr), + DEVMETHOD(pic_teardown_intr, rk_pic_teardown_intr), + /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, rk_gpio_get_node),