Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177600112
D1000.1794820460.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
11 KB
Referenced Files
None
Subscribers
None
D1000.1794820460.diff
View Options
Index: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
===================================================================
--- head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
+++ head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
@@ -32,6 +32,7 @@
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/gpio.h>
+#include <sys/interrupt.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/module.h>
@@ -57,6 +58,7 @@
#define BCM_GPIO_IRQS 4
#define BCM_GPIO_PINS 54
+#define BCM_GPIO_PINS_PER_BANK 32
#define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
@@ -81,12 +83,15 @@
struct resource * sc_res[BCM_GPIO_IRQS + 1];
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
- void * sc_intrhand;
+ void * sc_intrhand[BCM_GPIO_IRQS];
int sc_gpio_npins;
int sc_ro_npins;
int sc_ro_pins[BCM_GPIO_PINS];
struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS];
+ struct intr_event * sc_events[BCM_GPIO_PINS];
struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS];
+ enum intr_trigger sc_irq_trigger[BCM_GPIO_PINS];
+ enum intr_polarity sc_irq_polarity[BCM_GPIO_PINS];
};
enum bcm_gpio_pud {
@@ -95,21 +100,35 @@
BCM_GPIO_PULLUP,
};
-#define BCM_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx)
-#define BCM_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx)
-#define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED)
-
-#define BCM_GPIO_GPFSEL(_bank) 0x00 + _bank * 4
-#define BCM_GPIO_GPSET(_bank) 0x1c + _bank * 4
-#define BCM_GPIO_GPCLR(_bank) 0x28 + _bank * 4
-#define BCM_GPIO_GPLEV(_bank) 0x34 + _bank * 4
-#define BCM_GPIO_GPPUD(_bank) 0x94
-#define BCM_GPIO_GPPUDCLK(_bank) 0x98 + _bank * 4
-
+#define BCM_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
+#define BCM_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
+#define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
#define BCM_GPIO_WRITE(_sc, _off, _val) \
- bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val)
+ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val)
#define BCM_GPIO_READ(_sc, _off) \
- bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off)
+ bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off)
+#define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \
+ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits))
+#define BCM_GPIO_SET_BITS(_sc, _off, _bits) \
+ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits)
+#define BCM_GPIO_BANK(a) (a / BCM_GPIO_PINS_PER_BANK)
+#define BCM_GPIO_MASK(a) (1U << (a % BCM_GPIO_PINS_PER_BANK))
+
+#define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */
+#define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */
+#define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */
+#define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */
+#define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */
+#define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge irq */
+#define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge irq */
+#define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level irq */
+#define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level irq */
+#define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */
+#define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */
+#define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */
+#define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */
+
+static struct bcm_gpio_softc *bcm_gpio_sc = NULL;
static int
bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin)
@@ -657,6 +676,40 @@
}
static int
+bcm_gpio_intr(void *arg)
+{
+ int bank_last, irq;
+ struct bcm_gpio_softc *sc;
+ struct intr_event *event;
+ uint32_t bank, mask, reg;
+
+ sc = (struct bcm_gpio_softc *)arg;
+ reg = 0;
+ bank_last = -1;
+ for (irq = 0; irq < BCM_GPIO_PINS; irq++) {
+ bank = BCM_GPIO_BANK(irq);
+ mask = BCM_GPIO_MASK(irq);
+ if (bank != bank_last) {
+ reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank));
+ bank_last = bank;
+ }
+ if (reg & mask) {
+ event = sc->sc_events[irq];
+ if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers))
+ intr_event_handle(event, NULL);
+ else {
+ device_printf(sc->sc_dev, "Stray IRQ %d\n",
+ irq);
+ }
+ /* Clear the Status bit by writing '1' to it. */
+ BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), mask);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
+static int
bcm_gpio_probe(device_t dev)
{
@@ -671,6 +724,39 @@
}
static int
+bcm_gpio_intr_attach(device_t dev)
+{
+ struct bcm_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < BCM_GPIO_IRQS; i++) {
+ if (bus_setup_intr(dev, sc->sc_res[i + 1],
+ INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr,
+ NULL, sc, &sc->sc_intrhand[i]) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+bcm_gpio_intr_detach(device_t dev)
+{
+ struct bcm_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < BCM_GPIO_IRQS; i++) {
+ if (sc->sc_intrhand[i]) {
+ bus_teardown_intr(dev, sc->sc_res[i + 1],
+ sc->sc_intrhand[i]);
+ }
+ }
+}
+
+static int
bcm_gpio_attach(device_t dev)
{
int i, j;
@@ -678,30 +764,34 @@
struct bcm_gpio_softc *sc;
uint32_t func;
- sc = device_get_softc(dev);
- sc->sc_dev = dev;
- mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_DEF);
+ if (bcm_gpio_sc != NULL)
+ return (ENXIO);
+
+ bcm_gpio_sc = sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN);
if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) {
device_printf(dev, "cannot allocate resources\n");
goto fail;
}
sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
-
+ /* Setup the GPIO interrupt handler. */
+ if (bcm_gpio_intr_attach(dev)) {
+ device_printf(dev, "unable to setup the gpio irq handler\n");
+ goto fail;
+ }
/* Find our node. */
gpio = ofw_bus_get_node(sc->sc_dev);
-
if (!OF_hasprop(gpio, "gpio-controller"))
/* Node is not a GPIO controller. */
goto fail;
-
/*
* Find the read-only pins. These are pins we never touch or bad
* things could happen.
*/
if (bcm_gpio_get_reserved_pins(sc) == -1)
goto fail;
-
/* Initialize the software controlled pins. */
for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) {
snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
@@ -710,6 +800,9 @@
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);
+ /* The default is active-low interrupts. */
+ sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL;
+ sc->sc_irq_polarity[i] = INTR_POLARITY_LOW;
i++;
}
sc->sc_gpio_npins = i;
@@ -721,6 +814,7 @@
return (0);
fail:
+ bcm_gpio_intr_detach(dev);
bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res);
mtx_destroy(&sc->sc_mtx);
@@ -734,6 +828,177 @@
return (EBUSY);
}
+static uint32_t
+bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank)
+{
+
+ if (irq > BCM_GPIO_PINS)
+ return (0);
+ if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) {
+ if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
+ return (BCM_GPIO_GPLEN(bank));
+ else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
+ return (BCM_GPIO_GPHEN(bank));
+ } else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) {
+ if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
+ return (BCM_GPIO_GPFEN(bank));
+ else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
+ return (BCM_GPIO_GPREN(bank));
+ }
+
+ return (0);
+}
+
+static void
+bcm_gpio_mask_irq(void *source)
+{
+ uint32_t bank, mask, reg;
+ unsigned int irq;
+
+ irq = (unsigned int)source;
+ if (irq > BCM_GPIO_PINS)
+ return;
+ if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq))
+ return;
+ bank = BCM_GPIO_BANK(irq);
+ mask = BCM_GPIO_MASK(irq);
+ BCM_GPIO_LOCK(bcm_gpio_sc);
+ reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank);
+ if (reg != 0)
+ BCM_GPIO_CLEAR_BITS(bcm_gpio_sc, reg, mask);
+ BCM_GPIO_UNLOCK(bcm_gpio_sc);
+}
+
+static void
+bcm_gpio_unmask_irq(void *source)
+{
+ uint32_t bank, mask, reg;
+ unsigned int irq;
+
+ irq = (unsigned int)source;
+ if (irq > BCM_GPIO_PINS)
+ return;
+ if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq))
+ return;
+ bank = BCM_GPIO_BANK(irq);
+ mask = BCM_GPIO_MASK(irq);
+ BCM_GPIO_LOCK(bcm_gpio_sc);
+ reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank);
+ if (reg != 0)
+ BCM_GPIO_SET_BITS(bcm_gpio_sc, reg, mask);
+ BCM_GPIO_UNLOCK(bcm_gpio_sc);
+}
+
+static int
+bcm_gpio_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int pin;
+
+ if (type != SYS_RES_IRQ)
+ return (ENXIO);
+ /* Unmask the interrupt. */
+ pin = rman_get_start(res);
+ bcm_gpio_unmask_irq((void *)pin);
+
+ return (0);
+}
+
+static int
+bcm_gpio_deactivate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int pin;
+
+ if (type != SYS_RES_IRQ)
+ return (ENXIO);
+ /* Mask the interrupt. */
+ pin = rman_get_start(res);
+ bcm_gpio_mask_irq((void *)pin);
+
+ return (0);
+}
+
+static int
+bcm_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ int bank;
+ struct bcm_gpio_softc *sc;
+ uint32_t mask, oldreg, reg;
+
+ if (irq > BCM_GPIO_PINS)
+ return (EINVAL);
+ /* There is no standard trigger or polarity. */
+ if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
+ return (EINVAL);
+ sc = device_get_softc(dev);
+ if (bcm_gpio_pin_is_ro(sc, irq))
+ return (EINVAL);
+ bank = BCM_GPIO_BANK(irq);
+ mask = BCM_GPIO_MASK(irq);
+ BCM_GPIO_LOCK(sc);
+ oldreg = bcm_gpio_intr_reg(sc, irq, bank);
+ sc->sc_irq_trigger[irq] = trig;
+ sc->sc_irq_polarity[irq] = pol;
+ reg = bcm_gpio_intr_reg(sc, irq, bank);
+ if (reg != 0)
+ BCM_GPIO_SET_BITS(sc, reg, mask);
+ if (reg != oldreg && oldreg != 0)
+ BCM_GPIO_CLEAR_BITS(sc, oldreg, mask);
+ BCM_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_gpio_setup_intr(device_t bus, device_t child, struct resource *ires,
+ int flags, driver_filter_t *filt, driver_intr_t *handler,
+ void *arg, void **cookiep)
+{
+ struct bcm_gpio_softc *sc;
+ struct intr_event *event;
+ int pin, error;
+
+ sc = device_get_softc(bus);
+ pin = rman_get_start(ires);
+ if (pin > BCM_GPIO_PINS)
+ panic("%s: bad pin %d", __func__, pin);
+ event = sc->sc_events[pin];
+ if (event == NULL) {
+ error = intr_event_create(&event, (void *)pin, 0, pin,
+ bcm_gpio_mask_irq, bcm_gpio_unmask_irq, NULL, NULL,
+ "gpio%d pin%d:", device_get_unit(bus), pin);
+ if (error != 0)
+ return (error);
+ sc->sc_events[pin] = event;
+ }
+ intr_event_add_handler(event, device_get_nameunit(child), filt,
+ handler, arg, intr_priority(flags), flags, cookiep);
+
+ return (0);
+}
+
+static int
+bcm_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires,
+ void *cookie)
+{
+ struct bcm_gpio_softc *sc;
+ int pin, err;
+
+ sc = device_get_softc(dev);
+ pin = rman_get_start(ires);
+ if (pin > BCM_GPIO_PINS)
+ panic("%s: bad pin %d", __func__, pin);
+ if (sc->sc_events[pin] == NULL)
+ panic("Trying to teardown unoccupied IRQ");
+ err = intr_event_remove_handler(cookie);
+ if (!err)
+ sc->sc_events[pin] = NULL;
+
+ return (err);
+}
+
static phandle_t
bcm_gpio_get_node(device_t bus, device_t dev)
{
@@ -759,6 +1024,13 @@
DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set),
DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle),
+ /* 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),
+
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node),
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 16, 9:14 AM (10 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41770607
Default Alt Text
D1000.1794820460.diff (11 KB)
Attached To
Mode
D1000: Backend changes to add interrupt support to GPIO pins on bcm2835_gpio (RPi)
Attached
Detach File
Event Timeline