Re: git: ec556724d7ad - main - Add interrupt handling to rk_gpio driver.
- In reply to: Ganbold Tsagaankhuu : "git: ec556724d7ad - main - Add interrupt handling to rk_gpio driver."
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 20 Aug 2022 11:38:11 UTC
On Sat, Aug 20, 2022 at 7:32 PM Ganbold Tsagaankhuu <ganbold@freebsd.org> wrote: > 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. > Sorry, it was reviewed by manu and differential revision is https://reviews.freebsd.org/D36273 Probably I missed git arc ... command before pushing. Ganbold > --- > 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), > > >