Author: skra
Date: Tue Apr  5 13:45:23 2016
New Revision: 297582
URL: https://svnweb.freebsd.org/changeset/base/297582

Log:
  Rework BCM283x gpio interrupt controller for INTRNG. It's used on RPI-B
  and RPI2 where INTRNG is already enabled by default.
  
  Differential Revision:        https://reviews.freebsd.org/D5810

Modified:
  head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
  head/sys/boot/fdt/dts/arm/bcm2835.dtsi
  head/sys/boot/fdt/dts/arm/bcm2836.dtsi

Modified: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
==============================================================================
--- head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c        Tue Apr  5 13:41:51 
2016        (r297581)
+++ head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c        Tue Apr  5 13:45:23 
2016        (r297582)
@@ -28,6 +28,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
@@ -37,10 +39,12 @@ __FBSDID("$FreeBSD$");
 #include <sys/lock.h>
 #include <sys/module.h>
 #include <sys/mutex.h>
+#include <sys/proc.h>
 #include <sys/rman.h>
 #include <sys/sysctl.h>
 
 #include <machine/bus.h>
+#include <machine/intr.h>
 
 #include <dev/gpio/gpiobusvar.h>
 #include <dev/ofw/ofw_bus.h>
@@ -49,6 +53,10 @@ __FBSDID("$FreeBSD$");
 
 #include "gpio_if.h"
 
+#ifdef ARM_INTRNG
+#include "pic_if.h"
+#endif
+
 #ifdef DEBUG
 #define dprintf(fmt, args...) do { printf("%s(): ", __func__);   \
     printf(fmt,##args); } while (0)
@@ -64,10 +72,10 @@ __FBSDID("$FreeBSD$");
 
 static struct resource_spec bcm_gpio_res_spec[] = {
        { SYS_RES_MEMORY, 0, RF_ACTIVE },
-       { SYS_RES_IRQ, 0, RF_ACTIVE },
-       { SYS_RES_IRQ, 1, RF_ACTIVE },
-       { SYS_RES_IRQ, 2, RF_ACTIVE },
-       { SYS_RES_IRQ, 3, RF_ACTIVE },
+       { SYS_RES_IRQ, 0, RF_ACTIVE },  /* bank 0 interrupt */
+       { SYS_RES_IRQ, 1, RF_ACTIVE },  /* bank 1 interrupt */
+       { SYS_RES_IRQ, 2, RF_ACTIVE },  /* bank 1 interrupt (mirrored) */
+       { SYS_RES_IRQ, 3, RF_ACTIVE },  /* bank 0-1 interrupt (united) */
        { -1, 0, 0 }
 };
 
@@ -76,6 +84,15 @@ struct bcm_gpio_sysctl {
        uint32_t                pin;
 };
 
+#ifdef ARM_INTRNG
+struct bcm_gpio_irqsrc {
+       struct intr_irqsrc      bgi_isrc;
+       uint32_t                bgi_irq;
+       uint32_t                bgi_reg;
+       uint32_t                bgi_mask;
+};
+#endif
+
 struct bcm_gpio_softc {
        device_t                sc_dev;
        device_t                sc_busdev;
@@ -88,10 +105,16 @@ struct bcm_gpio_softc {
        int                     sc_ro_npins;
        int                     sc_ro_pins[BCM_GPIO_PINS];
        struct gpio_pin         sc_gpio_pins[BCM_GPIO_PINS];
+#ifndef ARM_INTRNG
        struct intr_event *     sc_events[BCM_GPIO_PINS];
+#endif
        struct bcm_gpio_sysctl  sc_sysctl[BCM_GPIO_PINS];
+#ifdef ARM_INTRNG
+       struct bcm_gpio_irqsrc  sc_isrcs[BCM_GPIO_PINS];
+#else
        enum intr_trigger       sc_irq_trigger[BCM_GPIO_PINS];
        enum intr_polarity      sc_irq_polarity[BCM_GPIO_PINS];
+#endif
 };
 
 enum bcm_gpio_pud {
@@ -130,6 +153,13 @@ enum bcm_gpio_pud {
 
 static struct bcm_gpio_softc *bcm_gpio_sc = NULL;
 
+#ifdef ARM_INTRNG
+static int bcm_gpio_intr_bank0(void *arg);
+static int bcm_gpio_intr_bank1(void *arg);
+static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc);
+static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc);
+#endif
+
 static int
 bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin)
 {
@@ -661,6 +691,7 @@ bcm_gpio_get_reserved_pins(struct bcm_gp
        return (0);
 }
 
+#ifndef ARM_INTRNG
 static int
 bcm_gpio_intr(void *arg)
 {
@@ -694,6 +725,7 @@ bcm_gpio_intr(void *arg)
 
        return (FILTER_HANDLED);
 }
+#endif
 
 static int
 bcm_gpio_probe(device_t dev)
@@ -709,6 +741,49 @@ bcm_gpio_probe(device_t dev)
        return (BUS_PROBE_DEFAULT);
 }
 
+#ifdef ARM_INTRNG
+static int
+bcm_gpio_intr_attach(device_t dev)
+{
+       struct bcm_gpio_softc *sc;
+
+       /*
+        *  Only first two interrupt lines are used. Third line is
+        *  mirrored second line and forth line is common for all banks.
+        */
+       sc = device_get_softc(dev);
+       if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL)
+               return (-1);
+
+       if (bcm_gpio_pic_attach(sc) != 0) {
+               device_printf(dev, "unable to attach PIC\n");
+               return (-1);
+       }
+       if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE,
+           bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0)
+               return (-1);
+       if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE,
+           bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0)
+               return (-1);
+
+       return (0);
+}
+
+static void
+bcm_gpio_intr_detach(device_t dev)
+{
+       struct bcm_gpio_softc *sc;
+
+       sc = device_get_softc(dev);
+       if (sc->sc_intrhand[0] != NULL)
+               bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]);
+       if (sc->sc_intrhand[1] != NULL)
+               bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]);
+
+       bcm_gpio_pic_detach(sc);
+}
+
+#else
 static int
 bcm_gpio_intr_attach(device_t dev)
 {
@@ -741,6 +816,7 @@ bcm_gpio_intr_detach(device_t dev)
                }
        }
 }
+#endif
 
 static int
 bcm_gpio_attach(device_t dev)
@@ -786,9 +862,11 @@ bcm_gpio_attach(device_t dev)
                sc->sc_gpio_pins[i].gp_pin = j;
                sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
                sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
+#ifndef ARM_INTRNG
                /* The default is active-low interrupts. */
                sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL;
                sc->sc_irq_polarity[i] = INTR_POLARITY_LOW;
+#endif
                i++;
        }
        sc->sc_gpio_npins = i;
@@ -814,6 +892,289 @@ bcm_gpio_detach(device_t dev)
        return (EBUSY);
 }
 
+#ifdef ARM_INTRNG
+static inline void
+bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi)
+{
+       uint32_t bank;
+
+       /* Write 1 to clear. */
+       bank = BCM_GPIO_BANK(bgi->bgi_irq);
+       BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask);
+}
+
+static inline bool
+bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi)
+{
+       uint32_t bank;
+
+       bank = BCM_GPIO_BANK(bgi->bgi_irq);
+       return (bgi->bgi_reg == BCM_GPIO_GPHEN(bank) ||
+           bgi->bgi_reg == BCM_GPIO_GPLEN(bank));
+}
+
+static inline void
+bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi)
+{
+
+       BCM_GPIO_LOCK(sc);
+       BCM_GPIO_CLEAR_BITS(sc, bgi->bgi_reg, bgi->bgi_mask);
+       BCM_GPIO_UNLOCK(bcm_gpio_sc);
+}
+
+static inline void
+bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi)
+{
+
+       BCM_GPIO_LOCK(sc);
+       BCM_GPIO_SET_BITS(sc, bgi->bgi_reg, bgi->bgi_mask);
+       BCM_GPIO_UNLOCK(sc);
+}
+
+static int
+bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank)
+{
+       u_int irq;
+       struct bcm_gpio_irqsrc *bgi;
+       uint32_t reg;
+
+       /* Do not care of spurious interrupt on GPIO. */
+       reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank));
+       while (reg != 0) {
+               irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1;
+               bgi = sc->sc_isrcs + irq;
+               if (!bcm_gpio_isrc_is_level(bgi))
+                       bcm_gpio_isrc_eoi(sc, bgi);
+               if (intr_isrc_dispatch(&bgi->bgi_isrc,
+                   curthread->td_intr_frame) != 0) {
+                       bcm_gpio_isrc_mask(sc, bgi);
+                       if (bcm_gpio_isrc_is_level(bgi))
+                               bcm_gpio_isrc_eoi(sc, bgi);
+                       device_printf(sc->sc_dev, "Stray irq %u disabled\n",
+                           irq);
+               }
+               reg &= ~bgi->bgi_mask;
+       }
+       return (FILTER_HANDLED);
+}
+
+static int
+bcm_gpio_intr_bank0(void *arg)
+{
+
+       return (bcm_gpio_intr_internal(arg, 0));
+}
+
+static int
+bcm_gpio_intr_bank1(void *arg)
+{
+
+       return (bcm_gpio_intr_internal(arg, 1));
+}
+
+static int
+bcm_gpio_pic_attach(struct bcm_gpio_softc *sc)
+{
+       int error;
+       uint32_t irq;
+       const char *name;
+
+       name = device_get_nameunit(sc->sc_dev);
+       for (irq = 0; irq < BCM_GPIO_PINS; irq++) {
+               sc->sc_isrcs[irq].bgi_irq = irq;
+               sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq);
+               sc->sc_isrcs[irq].bgi_reg = 0;
+
+               error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_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
+bcm_gpio_pic_detach(struct bcm_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
+bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct bcm_gpio_softc *sc = device_get_softc(dev);
+       struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+       bcm_gpio_isrc_mask(sc, bgi);
+}
+
+static void
+bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct bcm_gpio_softc *sc = device_get_softc(dev);
+       struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+       arm_irq_memory_barrier(bgi->bgi_irq);
+       bcm_gpio_isrc_unmask(sc, bgi);
+}
+
+static int
+bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, u_int ncells, pcell_t *cells,
+    u_int *irqp, uint32_t *regp)
+{
+       u_int irq;
+       uint32_t reg, bank;
+
+       /*
+        * 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)
+               return (EINVAL);
+
+       irq = cells[0];
+       if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq))
+               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.
+        */
+       bank = BCM_GPIO_BANK(irq);
+       if (cells[1] == 1)
+               reg = BCM_GPIO_GPREN(bank);
+       else if (cells[1] == 2)
+               reg = BCM_GPIO_GPFEN(bank);
+       else if (cells[1] == 4)
+               reg = BCM_GPIO_GPHEN(bank);
+       else if (cells[1] == 8)
+               reg = BCM_GPIO_GPLEN(bank);
+       else
+               return (EINVAL);
+
+       *irqp = irq;
+       if (regp != NULL)
+               *regp = reg;
+       return (0);
+}
+
+static int
+bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+    struct intr_irqsrc **isrcp)
+{
+       int error;
+       u_int irq;
+       struct bcm_gpio_softc *sc;
+
+       if (data->type != INTR_MAP_DATA_FDT)
+               return (ENOTSUP);
+
+       sc = device_get_softc(dev);
+       error = bcm_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells,
+           &irq, NULL);
+       if (error == 0)
+               *isrcp = &sc->sc_isrcs[irq].bgi_isrc;
+       return (error);
+}
+
+static void
+bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct bcm_gpio_softc *sc = device_get_softc(dev);
+       struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+       if (bcm_gpio_isrc_is_level(bgi))
+               bcm_gpio_isrc_eoi(sc, bgi);
+}
+
+static void
+bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+       bcm_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+       struct bcm_gpio_softc *sc = device_get_softc(dev);
+       struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+       bcm_gpio_isrc_mask(sc, bgi);
+       if (bcm_gpio_isrc_is_level(bgi))
+               bcm_gpio_isrc_eoi(sc, bgi);
+}
+
+static int
+bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+       u_int irq;
+       uint32_t bank, reg;
+       struct bcm_gpio_softc *sc;
+       struct bcm_gpio_irqsrc *bgi;
+
+       if (data == NULL || data->type != INTR_MAP_DATA_FDT)
+               return (ENOTSUP);
+
+       sc = device_get_softc(dev);
+       bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+       /* Get and check config for an interrupt. */
+       if (bcm_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq,
+           &reg) != 0 || bgi->bgi_irq != irq)
+               return (EINVAL);
+
+       /*
+        * If this is a setup for another handler,
+        * only check that its configuration match.
+        */
+       if (isrc->isrc_handlers != 0)
+               return (bgi->bgi_reg == reg ? 0 : EINVAL);
+
+       bank = BCM_GPIO_BANK(irq);
+       BCM_GPIO_LOCK(sc);
+       BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask);
+       BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask);
+       BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask);
+       BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask);
+       bgi->bgi_reg = reg;
+       BCM_GPIO_SET_BITS(sc, reg, bgi->bgi_mask);
+       BCM_GPIO_UNLOCK(sc);
+       return (0);
+}
+
+static int
+bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+       struct bcm_gpio_softc *sc = device_get_softc(dev);
+       struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc;
+
+       if (isrc->isrc_handlers == 0) {
+               BCM_GPIO_LOCK(sc);
+               BCM_GPIO_CLEAR_BITS(sc, bgi->bgi_reg, bgi->bgi_mask);
+               bgi->bgi_reg = 0;
+               BCM_GPIO_UNLOCK(sc);
+       }
+       return (0);
+}
+
+#else
 static uint32_t
 bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank)
 {
@@ -984,6 +1345,7 @@ bcm_gpio_teardown_intr(device_t dev, dev
 
        return (err);
 }
+#endif
 
 static phandle_t
 bcm_gpio_get_node(device_t bus, device_t dev)
@@ -1010,13 +1372,24 @@ static device_method_t bcm_gpio_methods[
        DEVMETHOD(gpio_pin_set,         bcm_gpio_pin_set),
        DEVMETHOD(gpio_pin_toggle,      bcm_gpio_pin_toggle),
 
+#ifdef ARM_INTRNG
+       /* Interrupt controller interface */
+       DEVMETHOD(pic_disable_intr,     bcm_gpio_pic_disable_intr),
+       DEVMETHOD(pic_enable_intr,      bcm_gpio_pic_enable_intr),
+       DEVMETHOD(pic_map_intr,         bcm_gpio_pic_map_intr),
+       DEVMETHOD(pic_post_filter,      bcm_gpio_pic_post_filter),
+       DEVMETHOD(pic_post_ithread,     bcm_gpio_pic_post_ithread),
+       DEVMETHOD(pic_pre_ithread,      bcm_gpio_pic_pre_ithread),
+       DEVMETHOD(pic_setup_intr,       bcm_gpio_pic_setup_intr),
+       DEVMETHOD(pic_teardown_intr,    bcm_gpio_pic_teardown_intr),
+#else
        /* Bus interface */
        DEVMETHOD(bus_activate_resource,        bcm_gpio_activate_resource),
        DEVMETHOD(bus_deactivate_resource,      bcm_gpio_deactivate_resource),
        DEVMETHOD(bus_config_intr,      bcm_gpio_config_intr),
        DEVMETHOD(bus_setup_intr,       bcm_gpio_setup_intr),
        DEVMETHOD(bus_teardown_intr,    bcm_gpio_teardown_intr),
-
+#endif
        /* ofw_bus interface */
        DEVMETHOD(ofw_bus_get_node,     bcm_gpio_get_node),
 

Modified: head/sys/boot/fdt/dts/arm/bcm2835.dtsi
==============================================================================
--- head/sys/boot/fdt/dts/arm/bcm2835.dtsi      Tue Apr  5 13:41:51 2016        
(r297581)
+++ head/sys/boot/fdt/dts/arm/bcm2835.dtsi      Tue Apr  5 13:45:23 2016        
(r297582)
@@ -144,7 +144,7 @@
                        #gpio-cells = <2>;
 
                        interrupt-controller;
-                       #interrupt-cells = <1>;
+                       #interrupt-cells = <2>;
 
                        pinctrl-names = "default";
                        pinctrl-0 = <&pins_reserved>;

Modified: head/sys/boot/fdt/dts/arm/bcm2836.dtsi
==============================================================================
--- head/sys/boot/fdt/dts/arm/bcm2836.dtsi      Tue Apr  5 13:41:51 2016        
(r297581)
+++ head/sys/boot/fdt/dts/arm/bcm2836.dtsi      Tue Apr  5 13:45:23 2016        
(r297582)
@@ -137,7 +137,7 @@
                        #gpio-cells = <2>;
 
                        interrupt-controller;
-                       #interrupt-cells = <1>;
+                       #interrupt-cells = <2>;
 
                        pinctrl-names = "default";
                        pinctrl-0 = <&pins_reserved>;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to