Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F151510725
D5810.id14819.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D5810.id14819.diff
View Options
Index: sys/arm/broadcom/bcm2835/bcm2835_gpio.c
===================================================================
--- sys/arm/broadcom/bcm2835/bcm2835_gpio.c
+++ sys/arm/broadcom/bcm2835/bcm2835_gpio.c
@@ -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 @@
#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 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
return (0);
}
+#ifndef ARM_INTRNG
static int
bcm_gpio_intr(void *arg)
{
@@ -694,6 +725,7 @@
return (FILTER_HANDLED);
}
+#endif
static int
bcm_gpio_probe(device_t dev)
@@ -709,6 +741,49 @@
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 @@
}
}
}
+#endif
static int
bcm_gpio_attach(device_t dev)
@@ -786,9 +862,11 @@
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 @@
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,
+ ®) != 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 @@
return (err);
}
+#endif
static phandle_t
bcm_gpio_get_node(device_t bus, device_t dev)
@@ -1010,13 +1372,24 @@
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),
Index: sys/boot/fdt/dts/arm/bcm2835.dtsi
===================================================================
--- sys/boot/fdt/dts/arm/bcm2835.dtsi
+++ sys/boot/fdt/dts/arm/bcm2835.dtsi
@@ -144,7 +144,7 @@
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&pins_reserved>;
Index: sys/boot/fdt/dts/arm/bcm2836.dtsi
===================================================================
--- sys/boot/fdt/dts/arm/bcm2836.dtsi
+++ sys/boot/fdt/dts/arm/bcm2836.dtsi
@@ -126,7 +126,7 @@
#gpio-cells = <2>;
interrupt-controller;
- #interrupt-cells = <1>;
+ #interrupt-cells = <2>;
pinctrl-names = "default";
pinctrl-0 = <&pins_reserved>;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Apr 9, 10:32 PM (8 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31096333
Default Alt Text
D5810.id14819.diff (13 KB)
Attached To
Mode
D5810: ARM - BCM283x gpio interrupt controller rework for ARM_INTRNG
Attached
Detach File
Event Timeline
Log In to Comment