The branch main has been updated by ganbold:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=ec556724d7ad1ee117fe595728e73dc9ddf78048

commit ec556724d7ad1ee117fe595728e73dc9ddf78048
Author:     Søren Schmidt <s...@freebsd.org>
AuthorDate: 2022-08-20 06:09:49 +0000
Commit:     Ganbold Tsagaankhuu <ganb...@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),
 

Reply via email to