svn commit: r278215 - head/sys/arm/broadcom/bcm2835

Luiz Otavio O Souza loos at FreeBSD.org
Wed Feb 4 18:15:29 UTC 2015


Author: loos
Date: Wed Feb  4 18:15:28 2015
New Revision: 278215
URL: https://svnweb.freebsd.org/changeset/base/278215

Log:
  Add GPIO interrupt support for BCM2835 (Raspberry pi).
  
  With this commit any of the GPIO pins can now be programmed to act as an
  interrupt source for GPIO devices (i.e. limited to devices directly
  attached to gpiobus - at least for now).
  
  Differential Revision:	https://reviews.freebsd.org/D1000

Modified:
  head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c

Modified: head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c
==============================================================================
--- head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c	Wed Feb  4 17:23:02 2015	(r278214)
+++ head/sys/arm/broadcom/bcm2835/bcm2835_gpio.c	Wed Feb  4 18:15:28 2015	(r278215)
@@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
 #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 @@ __FBSDID("$FreeBSD$");
 
 #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 bcm_gpio_softc {
 	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 @@ enum bcm_gpio_pud {
 	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 @@ bcm_gpio_get_reserved_pins(struct bcm_gp
 }
 
 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 @@ bcm_gpio_probe(device_t dev)
 }
 
 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 @@ bcm_gpio_attach(device_t dev)
 	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 @@ 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);
+		/* 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 @@ bcm_gpio_attach(device_t dev)
 	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 @@ bcm_gpio_detach(device_t dev)
 	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 @@ static device_method_t bcm_gpio_methods[
 	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),
 


More information about the svn-src-all mailing list