Author: skra
Date: Mon Apr  4 09:29:30 2016
New Revision: 297541
URL: https://svnweb.freebsd.org/changeset/base/297541

Log:
  Rework TI gpio interrupt controller for INTRNG. It's used on PANDABOARD
  and BEAGLEBONE where INTRNG is already enabled by default.
  
  Reviewed by:  gonzo
  Differential Revision:        https://reviews.freebsd.org/D5806

Modified:
  head/sys/arm/ti/omap4/omap4_gpio.c
  head/sys/arm/ti/ti_gpio.c
  head/sys/arm/ti/ti_gpio.h

Modified: head/sys/arm/ti/omap4/omap4_gpio.c
==============================================================================
--- head/sys/arm/ti/omap4/omap4_gpio.c  Mon Apr  4 09:23:21 2016        
(r297540)
+++ head/sys/arm/ti/omap4/omap4_gpio.c  Mon Apr  4 09:29:30 2016        
(r297541)
@@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/gpio.h>
 
 #include <machine/bus.h>
+#include <machine/intr.h>
 
 #include <dev/fdt/fdt_common.h>
 #include <dev/ofw/ofw_bus.h>

Modified: head/sys/arm/ti/ti_gpio.c
==============================================================================
--- head/sys/arm/ti/ti_gpio.c   Mon Apr  4 09:23:21 2016        (r297540)
+++ head/sys/arm/ti/ti_gpio.c   Mon Apr  4 09:29:30 2016        (r297541)
@@ -33,12 +33,15 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
 
 #include <sys/kernel.h>
 #include <sys/module.h>
+#include <sys/proc.h>
 #include <sys/rman.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
@@ -46,6 +49,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/interrupt.h>
 
 #include <machine/bus.h>
+#include <machine/intr.h>
 #include <machine/resource.h>
 
 #include <arm/ti/ti_cpuid.h>
@@ -62,6 +66,9 @@ __FBSDID("$FreeBSD$");
 
 #include "gpio_if.h"
 #include "ti_gpio_if.h"
+#ifdef ARM_INTRNG
+#include "pic_if.h"
+#endif
 
 #if !defined(SOC_OMAP4) && !defined(SOC_TI_AM335X)
 #error "Unknown SoC"
@@ -72,12 +79,12 @@ __FBSDID("$FreeBSD$");
 #define        TI_GPIO_SYSCONFIG               0x0010
 #define        TI_GPIO_IRQSTATUS_RAW_0         0x0024
 #define        TI_GPIO_IRQSTATUS_RAW_1         0x0028
-#define        TI_GPIO_IRQSTATUS_0             0x002C
-#define        TI_GPIO_IRQSTATUS_1             0x0030
-#define        TI_GPIO_IRQSTATUS_SET_0         0x0034
-#define        TI_GPIO_IRQSTATUS_SET_1         0x0038
-#define        TI_GPIO_IRQSTATUS_CLR_0         0x003C
-#define        TI_GPIO_IRQSTATUS_CLR_1         0x0040
+#define        TI_GPIO_IRQSTATUS_0             0x002C  /* writing a 0 has no 
effect */
+#define        TI_GPIO_IRQSTATUS_1             0x0030  /* writing a 0 has no 
effect */
+#define        TI_GPIO_IRQSTATUS_SET_0         0x0034  /* writing a 0 has no 
effect */
+#define        TI_GPIO_IRQSTATUS_SET_1         0x0038  /* writing a 0 has no 
effect */
+#define        TI_GPIO_IRQSTATUS_CLR_0         0x003C  /* writing a 0 has no 
effect */
+#define        TI_GPIO_IRQSTATUS_CLR_1         0x0040  /* writing a 0 has no 
effect */
 #define        TI_GPIO_IRQWAKEN_0              0x0044
 #define        TI_GPIO_IRQWAKEN_1              0x0048
 #define        TI_GPIO_SYSSTATUS               0x0114
@@ -90,10 +97,10 @@ __FBSDID("$FreeBSD$");
 #define        TI_GPIO_OE                      0x0134
 #define        TI_GPIO_DATAIN                  0x0138
 #define        TI_GPIO_DATAOUT                 0x013C
-#define        TI_GPIO_LEVELDETECT0            0x0140
-#define        TI_GPIO_LEVELDETECT1            0x0144
-#define        TI_GPIO_RISINGDETECT            0x0148
-#define        TI_GPIO_FALLINGDETECT           0x014C
+#define        TI_GPIO_LEVELDETECT0            0x0140  /* RW register */
+#define        TI_GPIO_LEVELDETECT1            0x0144  /* RW register */
+#define        TI_GPIO_RISINGDETECT            0x0148  /* RW register */
+#define        TI_GPIO_FALLINGDETECT           0x014C  /* RW register */
 #define        TI_GPIO_DEBOUNCENABLE           0x0150
 #define        TI_GPIO_DEBOUNCINGTIME          0x0154
 #define        TI_GPIO_CLEARWKUPENA            0x0180
@@ -111,8 +118,14 @@ __FBSDID("$FreeBSD$");
 #define        PINS_PER_BANK                   32
 #define        TI_GPIO_MASK(p)                 (1U << ((p) % PINS_PER_BANK))
 
+static int ti_gpio_intr(void *arg);
 static int ti_gpio_detach(device_t);
 
+#ifdef ARM_INTRNG
+static int ti_gpio_pic_attach(struct ti_gpio_softc *sc);
+static int ti_gpio_pic_detach(struct ti_gpio_softc *sc);
+#endif
+
 static u_int
 ti_first_gpio_bank(void)
 {
@@ -533,6 +546,7 @@ ti_gpio_pin_toggle(device_t dev, uint32_
        return (0);
 }
 
+#ifndef ARM_INTRNG
 /**
  *     ti_gpio_intr - ISR for all GPIO modules
  *     @arg: the soft context pointer
@@ -567,6 +581,7 @@ ti_gpio_intr(void *arg)
 
        return (FILTER_HANDLED);
 }
+#endif
 
 static int
 ti_gpio_bank_init(device_t dev)
@@ -640,7 +655,9 @@ static int
 ti_gpio_attach(device_t dev)
 {
        struct ti_gpio_softc *sc;
+#ifndef ARM_INTRNG
        unsigned int i;
+#endif
        int err;
 
        sc = device_get_softc(dev);
@@ -679,6 +696,13 @@ ti_gpio_attach(device_t dev)
                return (ENXIO);
        }
 
+#ifdef ARM_INTRNG
+       if (ti_gpio_pic_attach(sc) != 0) {
+               device_printf(dev, "WARNING: unable to attach PIC\n");
+               ti_gpio_detach(dev);
+               return (ENXIO);
+       }
+#else
        /*
         * Initialize the interrupt settings.  The default is active-low
         * interrupts.
@@ -699,7 +723,7 @@ ti_gpio_attach(device_t dev)
 
        sc->sc_mask_args = malloc(sizeof(struct ti_gpio_mask_arg) * 
sc->sc_maxpin,
            M_DEVBUF, M_WAITOK | M_ZERO);
-
+#endif
        /* We need to go through each block and ensure the clocks are running 
and
         * the module is enabled.  It might be better to do this only when the
         * pins are configured which would result in less power used if the GPIO
@@ -747,6 +771,10 @@ ti_gpio_detach(device_t dev)
        if (sc->sc_mem_res != NULL)
                ti_gpio_intr_clr(sc, 0xffffffff);
        gpiobus_detach_bus(dev);
+#ifdef ARM_INTRNG
+       if (sc->sc_isrcs != NULL)
+               ti_gpio_pic_detach(sc);
+#else
        if (sc->sc_events)
                free(sc->sc_events, M_DEVBUF);
        if (sc->sc_mask_args)
@@ -755,6 +783,7 @@ ti_gpio_detach(device_t dev)
                free(sc->sc_irq_polarity, M_DEVBUF);
        if (sc->sc_irq_trigger)
                free(sc->sc_irq_trigger, M_DEVBUF);
+#endif
        /* Release the memory and IRQ resources. */
        if (sc->sc_irq_hdl) {
                bus_teardown_intr(dev, sc->sc_irq_res,
@@ -769,6 +798,282 @@ ti_gpio_detach(device_t dev)
        return (0);
 }
 
+#ifdef ARM_INTRNG
+static inline void
+ti_gpio_rwreg_set(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask)
+{
+
+       ti_gpio_write_4(sc, reg, ti_gpio_read_4(sc, reg) | mask);
+}
+
+static inline void
+ti_gpio_rwreg_clr(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask)
+{
+
+       ti_gpio_write_4(sc, reg, ti_gpio_read_4(sc, reg) & ~mask);
+}
+
+static inline void
+ti_gpio_isrc_mask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+       /* Writing a 0 has no effect. */
+       ti_gpio_intr_clr(sc, tgi->tgi_mask);
+}
+
+static inline void
+ti_gpio_isrc_unmask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+       /* Writing a 0 has no effect. */
+       ti_gpio_intr_set(sc, tgi->tgi_mask);
+}
+
+static inline void
+ti_gpio_isrc_eoi(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+       /* Writing a 0 has no effect. */
+       ti_gpio_intr_ack(sc, tgi->tgi_mask);
+}
+
+static inline bool
+ti_gpio_isrc_is_level(struct ti_gpio_irqsrc *tgi)
+{
+
+       return (tgi->tgi_cfgreg == TI_GPIO_LEVELDETECT0 ||
+           tgi->tgi_cfgreg == TI_GPIO_LEVELDETECT1);
+}
+
+static int
+ti_gpio_intr(void *arg)
+{
+       u_int irq;
+       uint32_t reg;
+       struct ti_gpio_softc *sc;
+       struct trapframe *tf;
+       struct ti_gpio_irqsrc *tgi;
+
+       sc = (struct ti_gpio_softc *)arg;
+       tf = curthread->td_intr_frame;
+
+       reg = ti_gpio_intr_status(sc);
+       for (irq = 0; irq < sc->sc_maxpin; irq++) {
+               tgi = &sc->sc_isrcs[irq];
+               if ((reg & tgi->tgi_mask) == 0)
+                       continue;
+               if (!ti_gpio_isrc_is_level(tgi))
+                       ti_gpio_isrc_eoi(sc, tgi);
+               if (intr_isrc_dispatch(&tgi->tgi_isrc, tf) != 0) {
+                       ti_gpio_isrc_mask(sc, tgi);
+                       if (ti_gpio_isrc_is_level(tgi))
+                               ti_gpio_isrc_eoi(sc, tgi);
+                       device_printf(sc->sc_dev, "Stray irq %u disabled\n",
+                           irq);
+               }
+       }
+       return (FILTER_HANDLED);
+}
+
+static int
+ti_gpio_pic_attach(struct ti_gpio_softc *sc)
+{
+       int error;
+       uint32_t irq;
+       const char *name;
+
+       sc->sc_isrcs = malloc(sizeof(*sc->sc_isrcs) * sc->sc_maxpin, M_DEVBUF,
+           M_WAITOK | M_ZERO);
+
+       name = device_get_nameunit(sc->sc_dev);
+       for (irq = 0; irq < sc->sc_maxpin; irq++) {
+               sc->sc_isrcs[irq].tgi_irq = irq;
+               sc->sc_isrcs[irq].tgi_mask = TI_GPIO_MASK(irq);
+               sc->sc_isrcs[irq].tgi_cfgreg = 0;
+
+               error = intr_isrc_register(&sc->sc_isrcs[irq].tgi_isrc,
+                   sc->sc_dev, 0, "%s,%u", name, irq);
+               if (error != 0)
+                       return (error); /* XXX deregister ISRCs */
+       }
+       return (intr_pic_register(sc->sc_dev,
+           OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))));
+}
+
+static int
+ti_gpio_pic_detach(struct ti_gpio_softc *sc)
+{
+
+       /*
+        *  There has not been established any procedure yet
+        *  how to detach PIC from living system correctly.
+        */
+       device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__);
+       return (EBUSY);
+}
+
+static void
+ti_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct ti_gpio_softc *sc = device_get_softc(dev);
+       struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+       ti_gpio_isrc_mask(sc, tgi);
+}
+
+static void
+ti_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct ti_gpio_softc *sc = device_get_softc(dev);
+       struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+       arm_irq_memory_barrier(tgi->tgi_irq);
+       ti_gpio_isrc_unmask(sc, tgi);
+}
+
+static int
+ti_gpio_pic_map_fdt(struct ti_gpio_softc *sc, u_int ncells, pcell_t *cells,
+    u_int *irqp, uint32_t *regp)
+{
+       uint32_t reg;
+
+       /*
+        * The first cell is the interrupt number.
+        * The second cell is used to specify flags:
+        *      bits[3:0] trigger type and level flags:
+        *              1 = low-to-high edge triggered.
+        *              2 = high-to-low edge triggered.
+        *              4 = active high level-sensitive.
+        *              8 = active low level-sensitive.
+        */
+       if (ncells != 2 || cells[0] >= sc->sc_maxpin)
+               return (EINVAL);
+
+       /*
+        * All interrupt types could be set for an interrupt at one moment.
+        * At least, the combination of 'low-to-high' and 'high-to-low' edge
+        * triggered interrupt types can make a sense. However, no combo is
+        * supported now.
+        */
+       if (cells[1] == 1)
+               reg = TI_GPIO_RISINGDETECT;
+       else if (cells[1] == 2)
+               reg = TI_GPIO_FALLINGDETECT;
+       else if (cells[1] == 4)
+               reg = TI_GPIO_LEVELDETECT1;
+       else if (cells[1] == 8)
+               reg = TI_GPIO_LEVELDETECT0;
+       else
+               return (EINVAL);
+
+       *irqp = cells[0];
+       if (regp != NULL)
+               *regp = reg;
+       return (0);
+}
+
+static int
+ti_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+    struct intr_irqsrc **isrcp)
+{
+       int error;
+       u_int irq;
+       struct ti_gpio_softc *sc;
+
+       if (data->type != INTR_MAP_DATA_FDT)
+               return (ENOTSUP);
+
+       sc = device_get_softc(dev);
+       error = ti_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq,
+           NULL);
+       if (error == 0)
+               *isrcp = &sc->sc_isrcs[irq].tgi_isrc;
+       return (error);
+}
+
+static void
+ti_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct ti_gpio_softc *sc = device_get_softc(dev);
+       struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+       if (ti_gpio_isrc_is_level(tgi))
+               ti_gpio_isrc_eoi(sc, tgi);
+}
+
+static void
+ti_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+       ti_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+ti_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct ti_gpio_softc *sc = device_get_softc(dev);
+       struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+       ti_gpio_isrc_mask(sc, tgi);
+       if (ti_gpio_isrc_is_level(tgi))
+               ti_gpio_isrc_eoi(sc, tgi);
+}
+
+static int
+ti_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+       u_int irq;
+       uint32_t cfgreg;
+       struct ti_gpio_softc *sc;
+       struct ti_gpio_irqsrc *tgi;
+
+       if (data == NULL || data->type != INTR_MAP_DATA_FDT)
+               return (ENOTSUP);
+
+       sc = device_get_softc(dev);
+       tgi = (struct ti_gpio_irqsrc *)isrc;
+
+       /* Get and check config for an interrupt. */
+       if (ti_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq,
+           &cfgreg) != 0 || tgi->tgi_irq != irq)
+               return (EINVAL);
+
+       /*
+        * If this is a setup for another handler,
+        * only check that its configuration match.
+        */
+       if (isrc->isrc_handlers != 0)
+               return (tgi->tgi_cfgreg == cfgreg ? 0 : EINVAL);
+
+       TI_GPIO_LOCK(sc);
+       ti_gpio_rwreg_clr(sc, TI_GPIO_RISINGDETECT, tgi->tgi_mask);
+       ti_gpio_rwreg_clr(sc, TI_GPIO_FALLINGDETECT, tgi->tgi_mask);
+       ti_gpio_rwreg_clr(sc, TI_GPIO_LEVELDETECT1, tgi->tgi_mask);
+       ti_gpio_rwreg_clr(sc, TI_GPIO_LEVELDETECT0, tgi->tgi_mask);
+       tgi->tgi_cfgreg = cfgreg;
+       ti_gpio_rwreg_set(sc, cfgreg, tgi->tgi_mask);
+       TI_GPIO_UNLOCK(sc);
+       return (0);
+}
+
+static int
+ti_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+       struct ti_gpio_softc *sc = device_get_softc(dev);
+       struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+       if (isrc->isrc_handlers == 0) {
+               TI_GPIO_LOCK(sc);
+               ti_gpio_rwreg_clr(sc, tgi->tgi_cfgreg, tgi->tgi_mask);
+               tgi->tgi_cfgreg = 0;
+               TI_GPIO_UNLOCK(sc);
+       }
+       return (0);
+}
+
+#else
 static uint32_t
 ti_gpio_intr_reg(struct ti_gpio_softc *sc, int irq)
 {
@@ -970,6 +1275,7 @@ ti_gpio_teardown_intr(device_t dev, devi
 
        return (err);
 }
+#endif
 
 static phandle_t
 ti_gpio_get_node(device_t bus, device_t dev)
@@ -994,12 +1300,24 @@ static device_method_t ti_gpio_methods[]
        DEVMETHOD(gpio_pin_set, ti_gpio_pin_set),
        DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle),
 
+#ifdef ARM_INTRNG
+       /* Interrupt controller interface */
+       DEVMETHOD(pic_disable_intr,     ti_gpio_pic_disable_intr),
+       DEVMETHOD(pic_enable_intr,      ti_gpio_pic_enable_intr),
+       DEVMETHOD(pic_map_intr,         ti_gpio_pic_map_intr),
+       DEVMETHOD(pic_setup_intr,       ti_gpio_pic_setup_intr),
+       DEVMETHOD(pic_teardown_intr,    ti_gpio_pic_teardown_intr),
+       DEVMETHOD(pic_post_filter,      ti_gpio_pic_post_filter),
+       DEVMETHOD(pic_post_ithread,     ti_gpio_pic_post_ithread),
+       DEVMETHOD(pic_pre_ithread,      ti_gpio_pic_pre_ithread),
+#else
        /* Bus interface */
        DEVMETHOD(bus_activate_resource, ti_gpio_activate_resource),
        DEVMETHOD(bus_deactivate_resource, ti_gpio_deactivate_resource),
        DEVMETHOD(bus_config_intr, ti_gpio_config_intr),
        DEVMETHOD(bus_setup_intr, ti_gpio_setup_intr),
        DEVMETHOD(bus_teardown_intr, ti_gpio_teardown_intr),
+#endif
 
        /* ofw_bus interface */
        DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node),

Modified: head/sys/arm/ti/ti_gpio.h
==============================================================================
--- head/sys/arm/ti/ti_gpio.h   Mon Apr  4 09:23:21 2016        (r297540)
+++ head/sys/arm/ti/ti_gpio.h   Mon Apr  4 09:29:30 2016        (r297541)
@@ -39,10 +39,19 @@
  */
 #define        MAX_GPIO_INTRS                  8
 
+#ifndef ARM_INTRNG
 struct ti_gpio_mask_arg {
        void    *softc;
        int     pin;
 };
+#else
+struct ti_gpio_irqsrc {
+       struct intr_irqsrc      tgi_isrc;
+       u_int                   tgi_irq;
+       uint32_t                tgi_mask;
+       uint32_t                tgi_cfgreg;
+};
+#endif
 
 /**
  *     Structure that stores the driver context.
@@ -52,11 +61,11 @@ struct ti_gpio_mask_arg {
 struct ti_gpio_softc {
        device_t                sc_dev;
        device_t                sc_busdev;
-
+#ifndef ARM_INTRNG
        /* Interrupt trigger type and level. */
        enum intr_trigger       *sc_irq_trigger;
        enum intr_polarity      *sc_irq_polarity;
-
+#endif
        int                     sc_bank;
        int                     sc_maxpin;
        struct mtx              sc_mtx;
@@ -65,11 +74,13 @@ struct ti_gpio_softc {
        struct resource         *sc_mem_res;
        int                     sc_irq_rid;
        struct resource         *sc_irq_res;
-
+#ifndef ARM_INTRNG
        /* Interrupt events. */
        struct intr_event       **sc_events;
        struct ti_gpio_mask_arg *sc_mask_args;
-
+#else
+       struct ti_gpio_irqsrc   *sc_isrcs;
+#endif
        /* The handle for the register IRQ handlers. */
        void                    *sc_irq_hdl;
 };
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to