Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/ti Add support for GPIO interrupts and fix read...



details:   https://anonhg.NetBSD.org/src/rev/0512c3b71b3f
branches:  trunk
changeset: 460769:0512c3b71b3f
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Sun Nov 03 11:34:40 2019 +0000

description:
Add support for GPIO interrupts and fix reading the state of output pins.

diffstat:

 sys/arch/arm/ti/ti_gpio.c |  267 ++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 247 insertions(+), 20 deletions(-)

diffs (truncated from 363 to 300 lines):

diff -r d4cd0e282e54 -r 0512c3b71b3f sys/arch/arm/ti/ti_gpio.c
--- a/sys/arch/arm/ti/ti_gpio.c Sun Nov 03 11:13:45 2019 +0000
+++ b/sys/arch/arm/ti/ti_gpio.c Sun Nov 03 11:34:40 2019 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: ti_gpio.c,v 1.2 2019/10/29 22:19:13 jmcneill Exp $ */
+/* $NetBSD: ti_gpio.c,v 1.3 2019/11/03 11:34:40 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ti_gpio.c,v 1.2 2019/10/29 22:19:13 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ti_gpio.c,v 1.3 2019/11/03 11:34:40 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -44,27 +44,87 @@
 
 #include <arm/ti/ti_prcm.h>
 
-#define        GPIO_OE                         0x34
-#define        GPIO_DATAIN                     0x38
-#define        GPIO_CLEARDATAOUT               0x90
-#define        GPIO_SETDATAOUT                 0x94
+#define        TI_GPIO_NPINS                   32
+
+enum ti_gpio_type {
+       TI_GPIO_OMAP3,
+       TI_GPIO_OMAP4,
+       TI_NGPIO
+};
+
+enum {
+       GPIO_IRQSTATUS1,
+       GPIO_IRQENABLE1,        /* OMAP3 */
+       GPIO_IRQENABLE1_SET,    /* OMAP4 */
+       GPIO_IRQENABLE1_CLR,    /* OMAP4 */
+       GPIO_OE,
+       GPIO_DATAIN,
+       GPIO_DATAOUT,
+       GPIO_LEVELDETECT0,
+       GPIO_LEVELDETECT1,
+       GPIO_RISINGDETECT,
+       GPIO_FALLINGDETECT,
+       GPIO_CLEARDATAOUT,
+       GPIO_SETDATAOUT,
+       GPIO_NREG
+};
+
+static const u_int ti_gpio_regmap[TI_NGPIO][GPIO_NREG] = {
+       [TI_GPIO_OMAP3] = {
+               [GPIO_IRQSTATUS1]       = 0x18,
+               [GPIO_IRQENABLE1]       = 0x1c,
+               [GPIO_OE]               = 0x34,
+               [GPIO_DATAIN]           = 0x38,
+               [GPIO_DATAOUT]          = 0x3c,
+               [GPIO_LEVELDETECT0]     = 0x40,
+               [GPIO_LEVELDETECT1]     = 0x44,
+               [GPIO_RISINGDETECT]     = 0x48,
+               [GPIO_FALLINGDETECT]    = 0x4c,
+               [GPIO_CLEARDATAOUT]     = 0x90,
+               [GPIO_SETDATAOUT]       = 0x94,
+       },
+       [TI_GPIO_OMAP4] = {
+               [GPIO_IRQSTATUS1]       = 0x2c,
+               [GPIO_IRQENABLE1_SET]   = 0x34,
+               [GPIO_IRQENABLE1_CLR]   = 0x38,
+               [GPIO_OE]               = 0x134,
+               [GPIO_DATAIN]           = 0x138,
+               [GPIO_DATAOUT]          = 0x13c,
+               [GPIO_LEVELDETECT0]     = 0x140,
+               [GPIO_LEVELDETECT1]     = 0x144,
+               [GPIO_RISINGDETECT]     = 0x148,
+               [GPIO_FALLINGDETECT]    = 0x14c,
+               [GPIO_CLEARDATAOUT]     = 0x190,
+               [GPIO_SETDATAOUT]       = 0x194,
+       },
+};
 
 static const struct of_compat_data compat_data[] = {
-       /* compatible                   reg offset */
-       { "ti,omap3-gpio",              0x0 },
-       { "ti,omap4-gpio",              0x100 },
+       { "ti,omap3-gpio",              TI_GPIO_OMAP3 },
+       { "ti,omap4-gpio",              TI_GPIO_OMAP4 },
        { NULL }
 };
 
+struct ti_gpio_intr {
+       u_int intr_pin;
+       int (*intr_func)(void *);
+       void *intr_arg;
+       bool intr_mpsafe;
+};
+
 struct ti_gpio_softc {
        device_t sc_dev;
        bus_space_tag_t sc_bst;
        bus_space_handle_t sc_bsh;
        kmutex_t sc_lock;
-       bus_size_t sc_regoff;
+       enum ti_gpio_type sc_type;
+       const char *sc_modname;
+       void *sc_ih;
 
        struct gpio_chipset_tag sc_gp;
-       gpio_pin_t sc_pins[32];
+       gpio_pin_t sc_pins[TI_GPIO_NPINS];
+       bool sc_pinout[TI_GPIO_NPINS];
+       struct ti_gpio_intr sc_intr[TI_GPIO_NPINS];
        device_t sc_gpiodev;
 };
 
@@ -76,9 +136,9 @@
 };
 
 #define RD4(sc, reg)           \
-    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg) + (sc)->sc_regoff)
+    bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, ti_gpio_regmap[(sc)->sc_type][(reg)])
 #define WR4(sc, reg, val)      \
-    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg) + (sc)->sc_regoff, (val))
+    bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, ti_gpio_regmap[(sc)->sc_type][(reg)], (val))
 
 static int     ti_gpio_match(device_t, cfdata_t, void *);
 static void    ti_gpio_attach(device_t, device_t, void *);
@@ -100,6 +160,8 @@
                oe &= ~__BIT(pin);
        WR4(sc, GPIO_OE, oe);
 
+       sc->sc_pinout[pin] = (flags & GPIO_PIN_OUTPUT) != 0;
+
        return 0;
 }
 
@@ -162,7 +224,10 @@
        const uint32_t data_mask = __BIT(pin->pin_nr);
 
        /* No lock required for reads */
-       data = RD4(sc, GPIO_DATAIN);
+       if (sc->sc_pinout[pin->pin_nr])
+               data = RD4(sc, GPIO_DATAOUT);
+       else
+               data = RD4(sc, GPIO_DATAIN);
        val = __SHIFTOUT(data, data_mask);
        if (!raw && pin->pin_actlo)
                val = !val;
@@ -195,6 +260,126 @@
        .write = ti_gpio_write,
 };
 
+static void
+ti_gpio_intr_disestablish(device_t dev, void *ih)
+{
+       struct ti_gpio_softc * const sc = device_private(dev);
+       struct ti_gpio_intr *intr = ih;
+       const u_int pin = intr->intr_pin;
+       const uint32_t pin_mask = __BIT(pin);
+       uint32_t val;
+
+       /* Disable interrupts */
+       if (sc->sc_type == TI_GPIO_OMAP3) {
+               val = RD4(sc, GPIO_IRQENABLE1);
+               WR4(sc, GPIO_IRQENABLE1, val & ~pin_mask);
+       } else {
+               WR4(sc, GPIO_IRQENABLE1_CLR, pin_mask);
+       }
+
+       intr->intr_func = NULL;
+       intr->intr_arg = NULL;
+}
+
+static void *
+ti_gpio_intr_establish(device_t dev, u_int *specifier, int ipl, int flags,
+    int (*func)(void *), void *arg)
+{
+       struct ti_gpio_softc * const sc = device_private(dev);
+       uint32_t val;
+
+       /* 1st cell is the pin */
+       /* 2nd cell is flags */
+       const u_int pin = be32toh(specifier[0]);
+       const u_int type = be32toh(specifier[2]) & 0xf;
+
+       if (ipl != IPL_VM || pin >= __arraycount(sc->sc_pins))
+               return NULL;
+
+       /*
+        * Enabling both high and low level triggers will cause the GPIO
+        * controller to always assert the interrupt.
+        */
+       if ((type & (FDT_INTR_TYPE_LOW_LEVEL|FDT_INTR_TYPE_HIGH_LEVEL)) ==
+           (FDT_INTR_TYPE_LOW_LEVEL|FDT_INTR_TYPE_HIGH_LEVEL))
+               return NULL;
+
+       if (sc->sc_intr[pin].intr_func != NULL)
+               return NULL;
+
+       /* Set pin as input */
+       if (ti_gpio_ctl(sc, pin, GPIO_PIN_INPUT) != 0)
+               return NULL;
+
+       sc->sc_intr[pin].intr_pin = pin;
+       sc->sc_intr[pin].intr_func = func;
+       sc->sc_intr[pin].intr_arg = arg;
+       sc->sc_intr[pin].intr_mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
+
+       const uint32_t pin_mask = __BIT(pin);
+
+       /* Configure triggers */
+       val = RD4(sc, GPIO_LEVELDETECT0);
+       if ((type & FDT_INTR_TYPE_LOW_LEVEL) != 0)
+               val |= pin_mask;
+       else
+               val &= ~pin_mask;
+       WR4(sc, GPIO_LEVELDETECT0, val);
+
+       val = RD4(sc, GPIO_LEVELDETECT1);
+       if ((type & FDT_INTR_TYPE_HIGH_LEVEL) != 0)
+               val |= pin_mask;
+       else
+               val &= ~pin_mask;
+       WR4(sc, GPIO_LEVELDETECT1, val);
+
+       val = RD4(sc, GPIO_RISINGDETECT);
+       if ((type & FDT_INTR_TYPE_POS_EDGE) != 0)
+               val |= pin_mask;
+       else
+               val &= ~pin_mask;
+       WR4(sc, GPIO_RISINGDETECT, val);
+
+       val = RD4(sc, GPIO_FALLINGDETECT);
+       if ((type & FDT_INTR_TYPE_NEG_EDGE) != 0)
+               val |= pin_mask;
+       else
+               val &= ~pin_mask;
+       WR4(sc, GPIO_FALLINGDETECT, val);
+
+       /* Enable interrupts */
+       if (sc->sc_type == TI_GPIO_OMAP3) {
+               val = RD4(sc, GPIO_IRQENABLE1);
+               WR4(sc, GPIO_IRQENABLE1, val | pin_mask);
+       } else {
+               WR4(sc, GPIO_IRQENABLE1_SET, pin_mask);
+       }
+
+       return &sc->sc_intr[pin];
+}
+
+static bool
+ti_gpio_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
+{
+       struct ti_gpio_softc * const sc = device_private(dev);
+
+       /* 1st cell is the pin */
+       /* 2nd cell is flags */
+       const u_int pin = be32toh(specifier[0]);
+
+       if (pin >= __arraycount(sc->sc_pins))
+               return false;
+
+       snprintf(buf, buflen, "%s pin %d", sc->sc_modname, pin);
+       return true;
+}
+
+static struct fdtbus_interrupt_controller_func ti_gpio_intrfuncs = {
+       .establish = ti_gpio_intr_establish,
+       .disestablish = ti_gpio_intr_disestablish,
+       .intrstr = ti_gpio_intrstr,
+};
+
 static int
 ti_gpio_pin_read(void *priv, int pin)
 {
@@ -263,6 +448,34 @@
 }
 
 static int
+ti_gpio_intr(void *priv)
+{
+       struct ti_gpio_softc * const sc = priv;
+       uint32_t status;
+       u_int bit;
+       int rv = 0;
+
+       status = RD4(sc, GPIO_IRQSTATUS1);
+       WR4(sc, GPIO_IRQSTATUS1, status);
+
+       while ((bit = ffs32(status)) != 0) {
+               const u_int pin = bit - 1;
+               const uint32_t pin_mask = __BIT(pin);
+               struct ti_gpio_intr *intr = &sc->sc_intr[pin];
+               status &= ~pin_mask;
+               if (intr->intr_func == NULL)
+                       continue;
+               if (!intr->intr_mpsafe)
+                       KERNEL_LOCK(1, curlwp);
+               rv |= intr->intr_func(intr->intr_arg);
+               if (!intr->intr_mpsafe)



Home | Main Index | Thread Index | Old Index