Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/arch/arm/broadcom Add support for interrupt...
details: https://anonhg.NetBSD.org/src/rev/e71cf4256a34
branches: trunk
changeset: 319166:e71cf4256a34
user: thorpej <thorpej%NetBSD.org@localhost>
date: Sat May 19 14:02:10 2018 +0000
description:
Add support for interrupts on GPIO pins. We support both FDT-driven
interrupt registration as well as the new GPIO interrupt interface.
Based on initial work by Brad Spencer.
PR kern/51676
diffstat:
sys/arch/arm/broadcom/bcm2835_gpio.c | 434 +++++++++++++++++++++++++++++++++-
1 files changed, 420 insertions(+), 14 deletions(-)
diffs (truncated from 516 to 300 lines):
diff -r 43b2ead06c3f -r e71cf4256a34 sys/arch/arm/broadcom/bcm2835_gpio.c
--- a/sys/arch/arm/broadcom/bcm2835_gpio.c Sat May 19 13:59:06 2018 +0000
+++ b/sys/arch/arm/broadcom/bcm2835_gpio.c Sat May 19 14:02:10 2018 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: bcm2835_gpio.c,v 1.6 2017/12/10 21:38:26 skrll Exp $ */
+/* $NetBSD: bcm2835_gpio.c,v 1.7 2018/05/19 14:02:10 thorpej Exp $ */
/*-
* Copyright (c) 2013, 2014, 2017 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bcm2835_gpio.c,v 1.6 2017/12/10 21:38:26 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bcm2835_gpio.c,v 1.7 2018/05/19 14:02:10 thorpej Exp $");
/*
* Driver for BCM2835 GPIO
@@ -46,6 +46,7 @@
#include <sys/intr.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
+#include <sys/proc.h>
#include <sys/gpio.h>
#include <sys/bitops.h>
@@ -66,6 +67,29 @@
#define BCMGPIO_MAXPINS 54
+struct bcmgpio_eint {
+ int (*eint_func)(void *);
+ void *eint_arg;
+ int eint_flags;
+ int eint_bank;
+ int eint_num;
+};
+
+#define BCMGPIO_INTR_POS_EDGE 0x01
+#define BCMGPIO_INTR_NEG_EDGE 0x02
+#define BCMGPIO_INTR_HIGH_LEVEL 0x04
+#define BCMGPIO_INTR_LOW_LEVEL 0x08
+#define BCMGPIO_INTR_MPSAFE 0x10
+
+struct bcmgpio_softc;
+struct bcmgpio_bank {
+ struct bcmgpio_softc *sc_bcm;
+ void *sc_ih;
+ struct bcmgpio_eint sc_eint[32];
+ int sc_bankno;
+};
+#define BCMGPIO_NBANKS 2
+
struct bcmgpio_softc {
device_t sc_dev;
bus_space_tag_t sc_iot;
@@ -74,6 +98,9 @@
kmutex_t sc_lock;
gpio_pin_t sc_gpio_pins[BCMGPIO_MAXPINS];
+
+ /* For interrupt support. */
+ struct bcmgpio_bank sc_banks[BCMGPIO_NBANKS];
};
struct bcmgpio_pin {
@@ -90,6 +117,13 @@
static void bcm2835gpio_gpio_pin_write(void *, int, int);
static void bcm2835gpio_gpio_pin_ctl(void *, int, int);
+static void * bcmgpio_gpio_intr_establish(void *, int, int, int,
+ int (*)(void *), void *);
+static void bcmgpio_gpio_intr_disestablish(void *, void *);
+static bool bcmgpio_gpio_intrstr(void *, int, int, char *, size_t);
+
+static int bcmgpio_intr(void *);
+
u_int bcm283x_pin_getfunc(const struct bcmgpio_softc * const, u_int);
void bcm283x_pin_setfunc(const struct bcmgpio_softc * const, u_int,
u_int);
@@ -110,6 +144,17 @@
.write = bcmgpio_fdt_write
};
+static void * bcmgpio_fdt_intr_establish(device_t, u_int *, int, int,
+ int (*func)(void *), void *);
+static void bcmgpio_fdt_intr_disestablish(device_t, void *);
+static bool bcmgpio_fdt_intrstr(device_t, u_int *, char *, size_t);
+
+static struct fdtbus_interrupt_controller_func bcmgpio_fdt_intrfuncs = {
+ .establish = bcmgpio_fdt_intr_establish,
+ .disestablish = bcmgpio_fdt_intr_disestablish,
+ .intrstr = bcmgpio_fdt_intrstr,
+};
+
CFATTACH_DECL_NEW(bcmgpio, sizeof(struct bcmgpio_softc),
bcmgpio_match, bcmgpio_attach, NULL, NULL);
@@ -208,6 +253,7 @@
u_int func;
int error;
int pin;
+ int bank;
const int phandle = faa->faa_phandle;
if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
@@ -238,10 +284,18 @@
if (func == BCM2835_GPIO_IN ||
func == BCM2835_GPIO_OUT) {
+ /* XXX TRISTATE? Really? */
sc->sc_gpio_pins[pin].pin_caps = GPIO_PIN_INPUT |
GPIO_PIN_OUTPUT |
GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
+ sc->sc_gpio_pins[pin].pin_intrcaps =
+ GPIO_INTR_POS_EDGE |
+ GPIO_INTR_NEG_EDGE |
+ GPIO_INTR_DOUBLE_EDGE |
+ GPIO_INTR_HIGH_LEVEL |
+ GPIO_INTR_LOW_LEVEL |
+ GPIO_INTR_MPSAFE;
/* read initial state */
sc->sc_gpio_pins[pin].pin_state =
bcm2835gpio_gpio_pin_read(sc, pin);
@@ -253,19 +307,33 @@
}
}
- /* create controller tag */
- sc->sc_gpio_gc.gp_cookie = sc;
- sc->sc_gpio_gc.gp_pin_read = bcm2835gpio_gpio_pin_read;
- sc->sc_gpio_gc.gp_pin_write = bcm2835gpio_gpio_pin_write;
- sc->sc_gpio_gc.gp_pin_ctl = bcm2835gpio_gpio_pin_ctl;
+ /* Initialize interrupts. */
+ for (bank = 0; bank < BCMGPIO_NBANKS; bank++) {
+ char intrstr[128];
+
+ if (!fdtbus_intr_str(phandle, bank, intrstr, sizeof(intrstr))) {
+ aprint_error_dev(self, "failed to decode interrupt\n");
+ continue;
+ }
- gba.gba_gc = &sc->sc_gpio_gc;
- for (pin = 0; pin < BCMGPIO_MAXPINS;) {
- const int npins = MIN(BCMGPIO_MAXPINS - pin, 32);
- gba.gba_pins = &sc->sc_gpio_pins[pin];
- gba.gba_npins = npins;
- config_found_ia(self, "gpiobus", &gba, gpiobus_print);
- pin += npins;
+ sc->sc_banks[bank].sc_bankno = bank;
+ sc->sc_banks[bank].sc_bcm = sc;
+ sc->sc_banks[bank].sc_ih =
+ fdtbus_intr_establish(phandle, bank, IPL_VM,
+ FDT_INTR_MPSAFE,
+ bcmgpio_intr, &sc->sc_banks[bank]);
+ if (sc->sc_banks[bank].sc_ih) {
+ aprint_normal_dev(self,
+ "pins %d..%d interrupting on %s\n",
+ bank * 32,
+ MIN((bank * 32) + 31, BCMGPIO_MAXPINS),
+ intrstr);
+ } else {
+ aprint_normal_dev(self,
+ "failed to establish interrupt for pins %d..%d\n",
+ bank * 32,
+ MIN((bank * 32) + 31, BCMGPIO_MAXPINS));
+ }
}
fdtbus_register_gpio_controller(self, faa->faa_phandle, &bcmgpio_funcs);
@@ -278,6 +346,344 @@
}
fdtbus_pinctrl_configure();
+
+ fdtbus_register_interrupt_controller(self, phandle,
+ &bcmgpio_fdt_intrfuncs);
+
+ /* create controller tag */
+ sc->sc_gpio_gc.gp_cookie = sc;
+ sc->sc_gpio_gc.gp_pin_read = bcm2835gpio_gpio_pin_read;
+ sc->sc_gpio_gc.gp_pin_write = bcm2835gpio_gpio_pin_write;
+ sc->sc_gpio_gc.gp_pin_ctl = bcm2835gpio_gpio_pin_ctl;
+ sc->sc_gpio_gc.gp_intr_establish = bcmgpio_gpio_intr_establish;
+ sc->sc_gpio_gc.gp_intr_disestablish = bcmgpio_gpio_intr_disestablish;
+ sc->sc_gpio_gc.gp_intr_str = bcmgpio_gpio_intrstr;
+
+ gba.gba_gc = &sc->sc_gpio_gc;
+ gba.gba_pins = &sc->sc_gpio_pins[0];
+ gba.gba_npins = BCMGPIO_MAXPINS;
+ (void) config_found_ia(self, "gpiobus", &gba, gpiobus_print);
+}
+
+/* GPIO interrupt support functions */
+
+static int
+bcmgpio_intr(void *arg)
+{
+ struct bcmgpio_bank * const b = arg;
+ struct bcmgpio_softc * const sc = b->sc_bcm;
+ struct bcmgpio_eint *eint;
+ uint32_t status, pending, bit;
+ uint32_t clear_level;
+ int (*func)(void *);
+ int rv = 0;
+
+ for (;;) {
+ status = pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+ BCM2835_GPIO_GPEDS(b->sc_bankno));
+ if (status == 0)
+ break;
+
+ /*
+ * This will clear the indicator for any pending
+ * edge-triggered pins, but level-triggered pins
+ * will still be indicated until the pin is
+ * de-asserted. We'll have to clear level-triggered
+ * indicators below.
+ */
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+ BCM2835_GPIO_GPEDS(b->sc_bankno), status);
+ clear_level = 0;
+
+ while ((bit = ffs32(pending)) != 0) {
+ pending &= ~__BIT(bit - 1);
+ eint = &b->sc_eint[bit - 1];
+ if ((func = eint->eint_func) == NULL)
+ continue;
+ if (eint->eint_flags & (BCMGPIO_INTR_HIGH_LEVEL |
+ BCMGPIO_INTR_LOW_LEVEL))
+ clear_level |= __BIT(bit - 1);
+ const bool mpsafe =
+ (eint->eint_flags & BCMGPIO_INTR_MPSAFE) != 0;
+ if (!mpsafe)
+ KERNEL_LOCK(1, curlwp);
+ rv |= (*func)(eint->eint_arg);
+ if (!mpsafe)
+ KERNEL_UNLOCK_ONE(curlwp);
+ }
+
+ /*
+ * Now that all of the handlers have been called,
+ * we can clear the indicators for any level-triggered
+ * pins.
+ */
+ if (clear_level)
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh,
+ BCM2835_GPIO_GPEDS(b->sc_bankno), clear_level);
+ }
+
+ return (rv);
+}
+
+static void *
+bmcgpio_intr_enable(struct bcmgpio_softc *sc, int (*func)(void *), void *arg,
+ int bank, int pin, int flags)
+{
+ struct bcmgpio_eint *eint;
+ uint32_t mask, enabled_ren, enabled_fen, enabled_hen, enabled_len;
+ int has_edge = flags & (BCMGPIO_INTR_POS_EDGE|BCMGPIO_INTR_NEG_EDGE);
+ int has_level = flags &
+ (BCMGPIO_INTR_HIGH_LEVEL|BCMGPIO_INTR_LOW_LEVEL);
+
+ if (bank < 0 || bank >= BCMGPIO_NBANKS)
+ return NULL;
+ if (pin < 0 || pin >= 32)
+ return (NULL);
+
+ /* Must specify a mode. */
+ if (!has_edge && !has_level)
+ return (NULL);
+
+ /* Can't have HIGH and LOW together. */
+ if (has_level == (BCMGPIO_INTR_HIGH_LEVEL|BCMGPIO_INTR_LOW_LEVEL))
+ return (NULL);
+
+ /* Can't have EDGE and LEVEL together. */
+ if (has_edge && has_level)
+ return (NULL);
+
+ eint = &sc->sc_banks[bank].sc_eint[pin];
+
+ mask = __BIT(pin);
+
+ mutex_enter(&sc->sc_lock);
+
+ if (eint->eint_func != NULL) {
+ mutex_exit(&sc->sc_lock);
+ return (NULL); /* in use */
+ }
+
+ eint->eint_func = func;
+ eint->eint_arg = arg;
+ eint->eint_flags = flags;
+ eint->eint_bank = bank;
+ eint->eint_num = pin;
+
+ enabled_ren = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+ BCM2835_GPIO_GPREN(bank));
Home |
Main Index |
Thread Index |
Old Index