Page MenuHomeFreeBSD

D26407.id76930.diff
No OneTemporary

D26407.id76930.diff

Index: sys/dev/amdgpio/amdgpio.h
===================================================================
--- sys/dev/amdgpio/amdgpio.h
+++ sys/dev/amdgpio/amdgpio.h
@@ -43,6 +43,13 @@
#define AMD_GPIO_PINS_PER_BANK 64
#define AMD_GPIO_PINS_MAX 256 /* 4 banks * 64 pins */
+#define AMD_GPIO_PINS_PER_INTR_BIT 4
+#define AMD_GPIO_NUM_INTR_BITS 46
+#define AMD_GPIO_RESERVED_INTR_BIT 15 /* pins 60 - 63 */
+#define AMD_GPIO_INTR_MASK \
+ ((((uint64_t)1 << AMD_GPIO_NUM_INTR_BITS) - 1) & \
+ ~(1u << AMD_GPIO_RESERVED_INTR_BIT))
+
/* Number of pins in each bank */
#define AMD_GPIO_PINS_BANK0 63
#define AMD_GPIO_PINS_BANK1 64
@@ -52,15 +59,18 @@
AMD_GPIO_PINS_BANK1 + \
AMD_GPIO_PINS_BANK2 + \
AMD_GPIO_PINS_BANK3)
-#define AMDGPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
+#define AMDGPIO_DEFAULT_CAPS \
+ (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
+#define AMDGPIO_INTR_CAPS GPIO_INTR_MASK
/* Register related macros */
#define AMDGPIO_PIN_REGISTER(pin) (pin * 4)
#define WAKE_INT_MASTER_REG 0xfc
#define EOI_MASK (1 << 29)
-#define WAKE_INT_STATUS_REG0 0x2f8
-#define WAKE_INT_STATUS_REG1 0x2fc
+#define INTR_EN_MASK (1 << 30)
+#define INT_STATUS_REG0 0x2f8
+#define INT_STATUS_REG1 0x2fc
/* Bit definition of 32 bits of each pin register */
#define DB_TMR_OUT_OFF 0
@@ -302,7 +312,7 @@
/* Macros for driver mutex locking */
#define AMDGPIO_LOCK_INIT(_sc) \
mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \
- "amdgpio", MTX_SPIN)
+ "amdgpio", MTX_SPIN | MTX_RECURSE)
#define AMDGPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)
#define AMDGPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
#define AMDGPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
@@ -321,6 +331,10 @@
struct resource *sc_res[AMD_GPIO_NUM_PIN_BANK + 1];
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
+ int sc_intr_en_count;
+ int sc_intr_rid;
+ struct resource *sc_intr_res;
+ void *sc_intr_handle;
struct gpio_pin sc_gpio_pins[AMD_GPIO_PINS_MAX];
const struct pin_info *sc_pin_info;
const struct amd_pingroup *sc_groups;
Index: sys/dev/amdgpio/amdgpio.c
===================================================================
--- sys/dev/amdgpio/amdgpio.c
+++ sys/dev/amdgpio/amdgpio.c
@@ -145,9 +145,7 @@
if (!amdgpio_valid_pin(sc, pin))
return (EINVAL);
- /* Set a very simple name */
- snprintf(name, GPIOMAXNAME, "%s", sc->sc_gpio_pins[pin].gp_name);
- name[GPIOMAXNAME - 1] = '\0';
+ strlcpy(name, sc->sc_gpio_pins[pin].gp_name, GPIOMAXNAME);
dprintf("pin %d name %s\n", pin, name);
@@ -198,7 +196,7 @@
amdgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
{
struct amdgpio_softc *sc;
- uint32_t reg, val, allowed;
+ uint32_t reg, val;
sc = device_get_softc(dev);
@@ -206,36 +204,47 @@
if (!amdgpio_valid_pin(sc, pin))
return (EINVAL);
- allowed = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+ /* Only supported flags are allowed. */
+ if (flags & ~AMDGPIO_DEFAULT_CAPS)
+ return (EINVAL);
- /*
- * Only directtion flag allowed
- */
- if (flags & ~allowed)
+ /* Either input our output must be selected. */
+ if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0)
return (EINVAL);
- /*
- * Not both directions simultaneously
- */
- if ((flags & allowed) == allowed)
+ /* Not both directions simultaneously. */
+ if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
+ (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
return (EINVAL);
- /* Set the GPIO mode and state */
+ /* No pull-up and pull-down at the same time. */
+ if ((flags & (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)) ==
+ (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN))
+ return (EINVAL);
+
AMDGPIO_LOCK(sc);
reg = AMDGPIO_PIN_REGISTER(pin);
val = amdgpio_read_4(sc, reg);
- if (flags & GPIO_PIN_INPUT) {
+ if (flags & GPIO_PIN_INPUT)
val &= ~BIT(OUTPUT_ENABLE_OFF);
- sc->sc_gpio_pins[pin].gp_flags = GPIO_PIN_INPUT;
- } else {
+ else
val |= BIT(OUTPUT_ENABLE_OFF);
- sc->sc_gpio_pins[pin].gp_flags = GPIO_PIN_OUTPUT;
- }
+ if (flags & GPIO_PIN_PULLUP)
+ val |= BIT(PULL_UP_ENABLE_OFF);
+ else
+ val &= ~BIT(PULL_UP_ENABLE_OFF);
+ if (flags & GPIO_PIN_PULLDOWN)
+ val |= BIT(PULL_DOWN_ENABLE_OFF);
+ else
+ val &= ~BIT(PULL_DOWN_ENABLE_OFF);
+
amdgpio_write_4(sc, reg, val);
+ sc->sc_gpio_pins[pin].gp_flags = flags;
+
dprintf("pin %d flags 0x%x val 0x%x gp_flags 0x%x\n",
pin, flags, val, sc->sc_gpio_pins[pin].gp_flags);
@@ -346,7 +355,232 @@
return (0);
}
+static bool
+amdgpio_valid_intr_pin(struct amdgpio_softc *sc, int pin)
+{
+ if (!amdgpio_valid_pin(sc, pin))
+ return (false);
+ if ((sc->sc_gpio_pins[pin].gp_caps & GPIO_INTR_MASK) == GPIO_INTR_NONE)
+ return (false);
+ return (true);
+}
+
+static void
+amdgpio_pin_config_intr(device_t dev, uint32_t pin, uint32_t intr_mode)
+{
+ struct amdgpio_softc *sc;
+ uint32_t reg, val;
+ int trig, act;
+
+ sc = device_get_softc(dev);
+
+ dprintf("pin %d mode 0x%x\n", pin, mode);
+ KASSERT(amdgpio_valid_intr_pin(sc, pin), ("invalid pin"));
+
+ /* XXX Linux also sets various debounce modes. */
+ switch (intr_mode) {
+ case GPIO_INTR_EDGE_FALLING:
+ trig = EDGE_TRIGGER;
+ act = ACTIVE_LOW;
+ break;
+ case GPIO_INTR_EDGE_RISING:
+ trig = EDGE_TRIGGER;
+ act = ACTIVE_HIGH;
+ break;
+ case GPIO_INTR_EDGE_BOTH:
+ trig = EDGE_TRIGGER;
+ act = BOTH_EDGE;
+ break;
+ case GPIO_INTR_LEVEL_LOW:
+ trig = LEVEL_TRIGGER;
+ act = ACTIVE_LOW;
+ break;
+ case GPIO_INTR_LEVEL_HIGH:
+ trig = LEVEL_TRIGGER;
+ act = ACTIVE_HIGH;
+ break;
+ default:
+ KASSERT(0, ("invalid intr mode 0x%x", intr_mode));
+ return;
+ }
+
+ reg = AMDGPIO_PIN_REGISTER(pin);
+
+ /* Set the GPIO mode and state */
+ AMDGPIO_LOCK(sc);
+ val = amdgpio_read_4(sc, reg);
+ val &= ~(1 << LEVEL_TRIG_OFF);
+ val |= trig << LEVEL_TRIG_OFF;
+ val &= ~(3 << ACTIVE_LEVEL_OFF);
+ val |= act << ACTIVE_LEVEL_OFF;
+ amdgpio_write_4(sc, reg, val);
+ AMDGPIO_UNLOCK(sc);
+}
+
+static void
+amdgpio_pin_enable_intr(device_t dev, uint32_t pin)
+{
+ struct amdgpio_softc *sc;
+ uint32_t reg, val;
+
+ sc = device_get_softc(dev);
+
+ dprintf("pin %d enable intr\n", pin);
+ KASSERT(amdgpio_valid_intr_pin(sc, pin), ("invalid pin"));
+
+ reg = AMDGPIO_PIN_REGISTER(pin);
+
+ AMDGPIO_LOCK(sc);
+ val = amdgpio_read_4(sc, reg);
+ val |= BIT(INTERRUPT_STS_OFF); /* clear previous status as well */
+ val |= BIT(INTERRUPT_ENABLE_OFF);
+ amdgpio_write_4(sc, reg, val);
+ sc->sc_intr_en_count++;
+ AMDGPIO_UNLOCK(sc);
+}
+
+static void
+amdgpio_pin_disable_intr(device_t dev, uint32_t pin)
+{
+ struct amdgpio_softc *sc;
+ uint32_t reg, val;
+
+ sc = device_get_softc(dev);
+
+ dprintf("pin %d disable intr\n", pin);
+ KASSERT(amdgpio_valid_intr_pin(sc, pin), ("invalid pin"));
+
+ reg = AMDGPIO_PIN_REGISTER(pin);
+
+ AMDGPIO_LOCK(sc);
+ val = amdgpio_read_4(sc, reg);
+ val &= ~BIT(INTERRUPT_ENABLE_OFF);
+ amdgpio_write_4(sc, reg, val);
+ sc->sc_intr_en_count--;
+ AMDGPIO_UNLOCK(sc);
+}
+
+static void
+amdgpio_pin_unmask_intr(device_t dev, uint32_t pin)
+{
+ struct amdgpio_softc *sc;
+ uint32_t reg, val;
+
+ sc = device_get_softc(dev);
+
+ dprintf("pin %d unmask intr\n", pin);
+ KASSERT(amdgpio_valid_intr_pin(sc, pin), ("invalid pin"));
+
+ reg = AMDGPIO_PIN_REGISTER(pin);
+
+ AMDGPIO_LOCK(sc);
+ val = amdgpio_read_4(sc, reg);
+ val |= BIT(INTERRUPT_MASK_OFF);
+ amdgpio_write_4(sc, reg, val);
+ AMDGPIO_UNLOCK(sc);
+}
+
+static void
+amdgpio_pin_mask_intr(device_t dev, uint32_t pin)
+{
+ struct amdgpio_softc *sc;
+ uint32_t reg, val;
+
+ sc = device_get_softc(dev);
+
+ dprintf("pin %d mask intr\n", pin);
+ KASSERT(amdgpio_valid_intr_pin(sc, pin), ("invalid pin"));
+
+ reg = AMDGPIO_PIN_REGISTER(pin);
+
+ AMDGPIO_LOCK(sc);
+ val = amdgpio_read_4(sc, reg);
+ val &= ~BIT(INTERRUPT_MASK_OFF);
+ val |= BIT(INTERRUPT_STS_OFF); /* clear status as well */
+ amdgpio_write_4(sc, reg, val);
+ AMDGPIO_UNLOCK(sc);
+}
+
+static void
+amdgpio_pin_eoi(device_t dev, uint32_t pin)
+{
+ struct amdgpio_softc *sc;
+ uint32_t reg, val;
+
+ sc = device_get_softc(dev);
+
+ dprintf("pin %d eoi intr\n", pin);
+ KASSERT(amdgpio_valid_intr_pin(sc, pin), ("invalid pin"));
+
+ reg = AMDGPIO_PIN_REGISTER(pin);
+
+ AMDGPIO_LOCK(sc);
+ /* XXX Linux does EOI via WAKE_INT_MASTER_REG and EIO_MASK. */
+ val = amdgpio_read_4(sc, reg);
+ val |= BIT(INTERRUPT_STS_OFF);
+ amdgpio_write_4(sc, reg, val);
+ AMDGPIO_UNLOCK(sc);
+}
+
static int
+amdgpio_intr_filter(void *arg)
+{
+ struct amdgpio_softc *sc = arg;
+ uint64_t status;
+ uint32_t status0, status1;
+ uint32_t reg, val;
+ int ret = FILTER_STRAY;
+ int i, j;
+
+ AMDGPIO_LOCK(sc);
+ if (sc->sc_busdev == NULL) {
+ /* Too early interrupt ? */
+ AMDGPIO_UNLOCK(sc);
+ return (ret);
+ }
+
+ /* Merge into a single 46-bit status. */
+ status0 = amdgpio_read_4(sc, INT_STATUS_REG0);
+ status1 = amdgpio_read_4(sc, INT_STATUS_REG1);
+ status = status1;
+ status <<= 32;
+ status |= status0;
+ status &= AMD_GPIO_INTR_MASK;
+ for (i = 0; i < AMD_GPIO_NUM_INTR_BITS; i++) {
+ if ((status & (1 << i)) == 0)
+ continue;
+ for (j = 0; j < AMD_GPIO_PINS_PER_INTR_BIT; j++) {
+ uint32_t pin;
+
+ pin = i * AMD_GPIO_PINS_PER_INTR_BIT + j;
+ reg = AMDGPIO_PIN_REGISTER(pin);
+ val = amdgpio_read_4(sc, reg);
+
+ /*
+ * XXX consider that WAKE_STS_OFF can be set but
+ * INTERRUPT_STS_OFF unset after a wakeup.
+ */
+ if ((val & BIT(INTERRUPT_STS_OFF)) == 0)
+ continue;
+
+ /* Ignore masked pins as well. */
+ if ((val & BIT(INTERRUPT_MASK_OFF)) == 0)
+ continue;
+ gpiobus_handle_intr(sc->sc_busdev, pin);
+ ret = FILTER_HANDLED;
+ }
+ }
+
+ /* Ready for more interrupts. */
+ val = amdgpio_read_4(sc, WAKE_INT_MASTER_REG);
+ val |= EOI_MASK;
+ amdgpio_write_4(sc, WAKE_INT_MASTER_REG, val);
+
+ AMDGPIO_UNLOCK(sc);
+ return (ret);
+}
+
+static int
amdgpio_probe(device_t dev)
{
static char *gpio_ids[] = { "AMD0030", "AMDI0030", NULL };
@@ -365,7 +599,9 @@
amdgpio_attach(device_t dev)
{
struct amdgpio_softc *sc;
- int i, pin, bank;
+ device_t busdev;
+ int i, pin, bank, intrbit;
+ int err;
sc = device_get_softc(dev);
sc->sc_dev = dev;
@@ -388,6 +624,23 @@
sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
+ sc->sc_intr_rid = 0;
+ sc->sc_intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->sc_intr_rid, RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_intr_res != NULL) {
+ err = bus_setup_intr(dev, sc->sc_intr_res, INTR_TYPE_MISC,
+ amdgpio_intr_filter, NULL, sc, &sc->sc_intr_handle);
+ if (err != 0) {
+ device_printf(dev, "Unable to setup irq, error %d\n",
+ err);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_intr_rid,
+ sc->sc_intr_res);
+ sc->sc_intr_res = NULL;
+ } else {
+ device_printf(dev, "pin interrupts supported\n");
+ }
+ }
+
/* Initialize all possible pins to be Invalid */
for (i = 0; i < AMD_GPIO_PINS_MAX ; i++) {
snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
@@ -400,25 +653,40 @@
/* Initialize only driver exposed pins with appropriate capabilities */
for (i = 0; i < AMD_GPIO_PINS_EXPOSED ; i++) {
pin = kernzp_pins[i].pin_num;
- bank = pin/AMD_GPIO_PINS_PER_BANK;
- snprintf(sc->sc_gpio_pins[pin].gp_name, GPIOMAXNAME, "%s%d_%s",
- AMD_GPIO_PREFIX, bank, kernzp_pins[i].pin_name);
+ bank = pin / AMD_GPIO_PINS_PER_BANK;
+ intrbit = pin / AMD_GPIO_PINS_PER_INTR_BIT;
+ snprintf(sc->sc_gpio_pins[pin].gp_name, GPIOMAXNAME, "%s",
+ kernzp_pins[i].pin_name);
sc->sc_gpio_pins[pin].gp_pin = pin;
sc->sc_gpio_pins[pin].gp_caps = AMDGPIO_DEFAULT_CAPS;
+ if (sc->sc_intr_handle != NULL &&
+ intrbit < AMD_GPIO_NUM_INTR_BITS &&
+ intrbit != AMD_GPIO_RESERVED_INTR_BIT)
+ sc->sc_gpio_pins[pin].gp_caps |= AMDGPIO_INTR_CAPS;
sc->sc_gpio_pins[pin].gp_flags =
amdgpio_is_pin_output(sc, pin) ?
GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
}
- sc->sc_busdev = gpiobus_attach_bus(dev);
- if (sc->sc_busdev == NULL) {
+ busdev = gpiobus_attach_bus(dev);
+ if (busdev == NULL) {
device_printf(dev, "could not attach gpiobus\n");
goto err_bus;
}
+ AMDGPIO_LOCK(sc);
+ sc->sc_busdev = busdev;
+ AMDGPIO_UNLOCK(sc);
+
return (0);
err_bus:
+ if (sc->sc_intr_handle)
+ bus_teardown_intr(dev, sc->sc_intr_res, sc->sc_intr_handle);
+ if (sc->sc_intr_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_intr_rid,
+ sc->sc_intr_res);
+ }
bus_release_resources(dev, amdgpio_spec, sc->sc_res);
err_rsrc:
@@ -436,6 +704,12 @@
if (sc->sc_busdev)
gpiobus_detach_bus(dev);
+ if (sc->sc_intr_handle)
+ bus_teardown_intr(dev, sc->sc_intr_res, sc->sc_intr_handle);
+ if (sc->sc_intr_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_intr_rid,
+ sc->sc_intr_res);
+ }
bus_release_resources(dev, amdgpio_spec, sc->sc_res);
AMDGPIO_LOCK_DESTROY(sc);
@@ -460,6 +734,13 @@
DEVMETHOD(gpio_pin_set, amdgpio_pin_set),
DEVMETHOD(gpio_pin_toggle, amdgpio_pin_toggle),
+ /* GPIO interrupt controller interface. */
+ DEVMETHOD(gpio_pin_config_intr, amdgpio_pin_config_intr),
+ DEVMETHOD(gpio_pin_enable_intr, amdgpio_pin_enable_intr),
+ DEVMETHOD(gpio_pin_disable_intr, amdgpio_pin_disable_intr),
+ DEVMETHOD(gpio_pin_mask_intr, amdgpio_pin_mask_intr),
+ DEVMETHOD(gpio_pin_unmask_intr, amdgpio_pin_unmask_intr),
+ DEVMETHOD(gpio_pin_eoi, amdgpio_pin_eoi),
DEVMETHOD_END
};
Index: sys/dev/gpio/gpio_if.m
===================================================================
--- sys/dev/gpio/gpio_if.m
+++ sys/dev/gpio/gpio_if.m
@@ -186,3 +186,38 @@
uint32_t num_pins;
uint32_t *pin_flags;
} DEFAULT gpio_default_nosupport;
+
+
+#
+# Methods for interrupt handling on !INTRNG platforms.
+#
+METHOD void pin_config_intr {
+ device_t dev;
+ uint32_t pin_num;
+ uint32_t intr_mode;
+};
+
+METHOD void pin_enable_intr {
+ device_t dev;
+ uint32_t pin_num;
+};
+
+METHOD void pin_disable_intr {
+ device_t dev;
+ uint32_t pin_num;
+};
+
+METHOD void pin_eoi {
+ device_t dev;
+ uint32_t pin_num;
+};
+
+METHOD void pin_mask_intr {
+ device_t dev;
+ uint32_t pin_num;
+};
+
+METHOD void pin_unmask_intr {
+ device_t dev;
+ uint32_t pin_num;
+};
Index: sys/dev/gpio/gpiobus.c
===================================================================
--- sys/dev/gpio/gpiobus.c
+++ sys/dev/gpio/gpiobus.c
@@ -35,6 +35,13 @@
#include <sys/gpio.h>
#ifdef INTRNG
#include <sys/intr.h>
+#else
+#include <sys/interrupt.h>
+#include <sys/proc.h>
+#include <sys/sbuf.h>
+#include <sys/sx.h>
+#include <sys/sysctl.h>
+#include <sys/syslog.h>
#endif
#include <sys/kernel.h>
#include <sys/malloc.h>
@@ -64,6 +71,11 @@
static int gpiobus_child_pnpinfo_str(device_t, device_t, char *, size_t);
static device_t gpiobus_add_child(device_t, u_int, const char *, int);
static void gpiobus_hinted_child(device_t, const char *, int);
+#ifndef INTRNG
+static void gpiobus_pic_init(struct gpiobus_softc *);
+static void gpiobus_pic_destroy(struct gpiopic *);
+static void gpiobus_pic_resume(struct gpiopic *);
+#endif
/*
* GPIOBUS interface
@@ -120,14 +132,6 @@
rman_set_virtual(res, gpio_data);
return (res);
}
-#else
-struct resource *
-gpio_alloc_intr_resource(device_t consumer_dev, int *rid, u_int alloc_flags,
- gpio_pin_t pin, uint32_t intr_mode)
-{
-
- return (NULL);
-}
#endif
int
@@ -341,20 +345,29 @@
sc = GPIOBUS_SOFTC(dev);
sc->sc_busdev = dev;
sc->sc_dev = device_get_parent(dev);
- sc->sc_intr_rman.rm_type = RMAN_ARRAY;
- sc->sc_intr_rman.rm_descr = "GPIO Interrupts";
- if (rman_init(&sc->sc_intr_rman) != 0 ||
- rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0)
- panic("%s: failed to set up rman.", __func__);
if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0)
return (ENXIO);
-
KASSERT(sc->sc_npins >= 0, ("GPIO device with no pins"));
/* Pins = GPIO_PIN_MAX() + 1 */
sc->sc_npins++;
+ sc->sc_intr_rman.rm_type = RMAN_ARRAY;
+ sc->sc_intr_rman.rm_descr = "GPIO Interrupts";
+#ifndef INTRNG
+ sc->sc_intr_rman.rm_start = 0;
+ sc->sc_intr_rman.rm_end = sc->sc_npins - 1;
+#endif
+ if (rman_init(&sc->sc_intr_rman) != 0)
+ panic("%s: failed to set up rman.", __func__);
+#ifdef INTRNG
+ if (rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0)
+#else
+ if (rman_manage_region(&sc->sc_intr_rman, 0, sc->sc_npins - 1) != 0)
+#endif
+ panic("%s: failed to set up rman.", __func__);
+
sc->sc_pins = malloc(sizeof(*sc->sc_pins) * sc->sc_npins, M_DEVBUF,
M_NOWAIT | M_ZERO);
if (sc->sc_pins == NULL)
@@ -363,6 +376,9 @@
/* Initialize the bus lock. */
GPIOBUS_LOCK_INIT(sc);
+#ifndef INTRNG
+ gpiobus_pic_init(sc);
+#endif
return (0);
}
@@ -459,6 +475,10 @@
GPIOBUS_PIN_SETNAME(dev, devi->pins[i],
device_get_nameunit(child));
+ /* Set pin as a potential interrupt resource for the child. */
+ resource_list_add(&devi->rl, SYS_RES_IRQ, i, devi->pins[i],
+ devi->pins[i], 1);
+
}
return (0);
}
@@ -581,13 +601,14 @@
{
struct gpiobus_softc *sc;
struct gpiobus_ivar *devi;
+ struct gpiopic *pic;
device_t *devlist;
int i, err, ndevs;
sc = GPIOBUS_SOFTC(dev);
+ pic = sc->sc_pic;
KASSERT(mtx_initialized(&sc->sc_mtx),
("gpiobus mutex not initialized"));
- GPIOBUS_LOCK_DESTROY(sc);
if ((err = bus_generic_detach(dev)) != 0)
return (err);
@@ -613,6 +634,10 @@
sc->sc_pins = NULL;
}
+#ifndef INTRNG
+ gpiobus_pic_destroy(pic);
+#endif
+ GPIOBUS_LOCK_DESTROY(sc);
return (0);
}
@@ -626,7 +651,10 @@
static int
gpiobus_resume(device_t dev)
{
+ struct gpiobus_softc *sc;
+ sc = GPIOBUS_SOFTC(dev);
+ gpiobus_pic_resume(sc->sc_pic);
return (bus_generic_resume(dev));
}
@@ -778,6 +806,16 @@
dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n",
__func__, dev, child, type, rid, (void *)(intptr_t)start, count);
+
+ /*
+ * FIXME support indirect discendants and non-IRQ resources.
+ * The request should be forwarded upwards for those.
+ */
+ if (device_get_parent(child) != dev)
+ return (EINVAL);
+ if (type != SYS_RES_IRQ)
+ return (EINVAL);
+
devi = GPIOBUS_IVAR(child);
rle = resource_list_add(&devi->rl, type, rid, start,
start + count - 1, count);
@@ -842,8 +880,18 @@
return (0);
}
+/*
+ * For now, this method supports only requests for GPIO IRQ resources.
+ * The requesting device, a consumer, does not have to be a child or
+ * a descendant of the bus device.
+ *
+ * FIXME
+ * The method does not support non-IRQ resources or non-GPIO IRQ resources
+ * even for its children. This should be fixed as soon as there is
+ * a child driver needs access to system resources.
+ */
static struct resource *
-gpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
+gpiobus_alloc_resource(device_t bus, device_t consumer, int type, int *rid,
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
{
struct gpiobus_softc *sc;
@@ -854,10 +902,10 @@
if (type != SYS_RES_IRQ)
return (NULL);
- isdefault = (RMAN_IS_DEFAULT_RANGE(start, end) && count == 1);
- rle = NULL;
+ isdefault = RMAN_IS_DEFAULT_RANGE(start, end) && count == 1 &&
+ device_get_parent(consumer) == bus;;
if (isdefault) {
- rl = BUS_GET_RESOURCE_LIST(bus, child);
+ rl = BUS_GET_RESOURCE_LIST(bus, consumer);
if (rl == NULL)
return (NULL);
rle = resource_list_find(rl, type, *rid);
@@ -871,12 +919,11 @@
}
sc = device_get_softc(bus);
rv = rman_reserve_resource(&sc->sc_intr_rman, start, end, count, flags,
- child);
+ consumer);
if (rv == NULL)
return (NULL);
rman_set_rid(rv, *rid);
- if ((flags & RF_ACTIVE) != 0 &&
- bus_activate_resource(child, type, *rid, rv) != 0) {
+ if ((flags & RF_ACTIVE) != 0 && rman_activate_resource(rv) != 0) {
rman_release_resource(rv);
return (NULL);
}
@@ -885,13 +932,17 @@
}
static int
-gpiobus_release_resource(device_t bus __unused, device_t child, int type,
+gpiobus_release_resource(device_t bus, device_t consumer, int type,
int rid, struct resource *r)
{
+ struct gpiobus_softc *sc;
int error;
+ sc = device_get_softc(bus);
+ if (!rman_is_region_manager(r, &sc->sc_intr_rman))
+ return (EINVAL);
if (rman_get_flags(r) & RF_ACTIVE) {
- error = bus_deactivate_resource(child, type, rid, r);
+ error = rman_deactivate_resource(r);
if (error)
return (error);
}
@@ -910,7 +961,7 @@
}
static int
-gpiobus_acquire_bus(device_t busdev, device_t child, int how)
+gpiobus_acquire_bus(device_t busdev, device_t consumer, int how)
{
struct gpiobus_softc *sc;
@@ -918,10 +969,10 @@
GPIOBUS_ASSERT_UNLOCKED(sc);
GPIOBUS_LOCK(sc);
if (sc->sc_owner != NULL) {
- if (sc->sc_owner == child)
+ if (sc->sc_owner == consumer)
panic("%s: %s still owns the bus.",
device_get_nameunit(busdev),
- device_get_nameunit(child));
+ device_get_nameunit(consumer));
if (how == GPIOBUS_DONTWAIT) {
GPIOBUS_UNLOCK(sc);
return (EWOULDBLOCK);
@@ -929,14 +980,14 @@
while (sc->sc_owner != NULL)
mtx_sleep(sc, &sc->sc_mtx, 0, "gpiobuswait", 0);
}
- sc->sc_owner = child;
+ sc->sc_owner = consumer;
GPIOBUS_UNLOCK(sc);
return (0);
}
static void
-gpiobus_release_bus(device_t busdev, device_t child)
+gpiobus_release_bus(device_t busdev, device_t consumer)
{
struct gpiobus_softc *sc;
@@ -946,11 +997,11 @@
if (sc->sc_owner == NULL)
panic("%s: %s releasing unowned bus.",
device_get_nameunit(busdev),
- device_get_nameunit(child));
- if (sc->sc_owner != child)
+ device_get_nameunit(consumer));
+ if (sc->sc_owner != consumer)
panic("%s: %s trying to release bus owned by %s",
device_get_nameunit(busdev),
- device_get_nameunit(child),
+ device_get_nameunit(consumer),
device_get_nameunit(sc->sc_owner));
sc->sc_owner = NULL;
wakeup(sc);
@@ -1076,6 +1127,519 @@
return (0);
}
+#ifndef INTRNG
+
+struct gpiopic;
+
+struct gpiopic_intsrc {
+ struct gpiopic *is_pic;
+ struct intr_event *is_event;
+ u_long *is_count; /* TODO expose to userland. */
+ u_long *is_straycount; /* TODO expose to userland. */
+ int is_handlers;
+ u_int is_pin;
+ u_int is_mode;
+ u_int is_enabled:1;
+ u_int is_masked:1;
+};
+
+struct gpiopic {
+ struct sx intrsrc_lock;
+ struct gpiobus_softc *sc;
+ struct gpiopic_intsrc *intr_srcs;
+ u_long *intr_counts;
+
+};
+
+static int
+sysctl_gpio_intrs(SYSCTL_HANDLER_ARGS)
+{
+ struct sbuf sbuf;
+ struct gpiopic *pic = arg1;
+ struct gpiopic_intsrc *isrc;
+ u_int i;
+ int error;
+
+ error = sysctl_wire_old_buffer(req, 0);
+ if (error != 0)
+ return (error);
+
+ sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
+ sx_slock(&pic->intrsrc_lock);
+ for (i = 0; i < pic->sc->sc_npins; i++) {
+ isrc = &pic->intr_srcs[i];
+ if (isrc->is_event == NULL)
+ continue;
+ sbuf_printf(&sbuf, "%s:%u (mode 0x%08x): %lu\n",
+ isrc->is_event->ie_fullname,
+ isrc->is_pin,
+ isrc->is_mode,
+ *isrc->is_count);
+ }
+
+ sx_sunlock(&pic->intrsrc_lock);
+ error = sbuf_finish(&sbuf);
+ sbuf_delete(&sbuf);
+ return (error);
+}
+
+static int
+gpiopic_assign_cpu(void *arg, int cpu)
+{
+ /*
+ * This could be made to work if only a single pin is configured
+ * to be an interrupt source but not in general case.
+ * At this time there does not appear to be a case for binding
+ * GPIO interrupts, so not bothering with the only case that could
+ * be made to work.
+ */
+ return (EOPNOTSUPP);
+}
+
+static void
+gpiopic_unmask_intr(void *arg)
+{
+ struct gpiopic_intsrc *gisrc = arg;
+
+ gisrc->is_masked = 0;
+ GPIO_PIN_UNMASK_INTR(gisrc->is_pic->sc->sc_dev, gisrc->is_pin);
+}
+
+static void
+gpiopic_mask_intr(void *arg)
+{
+ struct gpiopic_intsrc *gisrc = arg;
+
+ gisrc->is_masked = 1;
+ GPIO_PIN_MASK_INTR(gisrc->is_pic->sc->sc_dev, gisrc->is_pin);
+}
+
+static void
+gpiopic_eoi(void *arg)
+{
+ struct gpiopic_intsrc *gisrc = arg;
+
+ GPIO_PIN_EOI(gisrc->is_pic->sc->sc_dev, gisrc->is_pin);
+}
+
+static void
+gpiopic_enable_intr(struct gpiopic_intsrc *gisrc)
+{
+ gisrc->is_enabled = 1;
+ GPIO_PIN_ENABLE_INTR(gisrc->is_pic->sc->sc_dev, gisrc->is_pin);
+}
+
+static void
+gpiopic_disable_intr(struct gpiopic_intsrc *gisrc)
+{
+ gisrc->is_enabled = 0;
+ GPIO_PIN_DISABLE_INTR(gisrc->is_pic->sc->sc_dev, gisrc->is_pin);
+}
+
+static void
+gpiopic_config_intr(struct gpiopic_intsrc *gisrc, uint32_t intr_mode) {
+ gisrc->is_mode = intr_mode;
+ GPIO_PIN_CONFIG_INTR(gisrc->is_pic->sc->sc_dev, gisrc->is_pin,
+ intr_mode);
+}
+
+static int
+gpiopic_check_intr_pin(struct gpiopic *gpiopic, uint32_t pin)
+{
+ struct gpiopic_intsrc *gisrc;
+
+ if (gpiopic == NULL)
+ return (ENOENT);
+ if (pin >= gpiopic->sc->sc_npins)
+ return (ENOENT);
+ gisrc = &gpiopic->intr_srcs[pin];
+ if (gisrc->is_event == NULL)
+ return (ENOENT);
+ return (0);
+}
+
+static int
+gpiopic_register_sources(struct gpiopic *gpiopic)
+{
+ char name[GPIOMAXNAME];
+ struct gpiobus_softc *sc = gpiopic->sc;
+ struct gpiopic_intsrc *gisrc;
+ int i, count;
+ int err;
+
+ count = 0;
+ for (i = 0; i < sc->sc_npins; i++) {
+ uint32_t pincaps;
+
+ err = GPIO_PIN_GETCAPS(sc->sc_dev, i, &pincaps);
+ if (err != 0)
+ continue;
+ if ((pincaps & GPIO_INTR_MASK) == GPIO_INTR_NONE)
+ continue;
+
+ gisrc = &gpiopic->intr_srcs[i];
+ gisrc->is_pic = gpiopic;
+ gisrc->is_pin = i;
+ gisrc->is_count = &gpiopic->intr_counts[i * 2];
+ gisrc->is_straycount = &gpiopic->intr_counts[i * 2 + 1];
+ gisrc->is_enabled = 0;
+ gisrc->is_mode = GPIO_INTR_CONFORM;
+
+ /*
+ * We use IE_BUS_PRIV to indicate that this interrupt event is
+ * completely private to this bus. It's managed by the bus and
+ * it is not visible to the global interrupt management.
+ * As a consequence, its number / vector is in the private
+ * space and is meaningless in the global space.
+ */
+ (void)gpiobus_pin_getname(gpiopic->sc->sc_busdev, i, name);
+ err = intr_event_create(&gisrc->is_event, gisrc, IE_BUS_PRIV,
+ i,
+ gpiopic_mask_intr, /* pre_ithread */
+ gpiopic_unmask_intr, /* post_ithread */
+ gpiopic_eoi, /* post_filter */
+ gpiopic_assign_cpu,
+ "%s", name);
+ if (err != 0) {
+ device_printf(sc->sc_busdev, "gpiopic failed to "
+ "create interrupt event for pin %u: %d\n", i, err);
+ continue;
+ }
+ count++;
+ }
+ return (count);
+}
+
+static void
+gpiobus_pic_init(struct gpiobus_softc *sc)
+{
+
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree_node;
+ struct sysctl_oid_list *tree;
+ struct gpiopic *gpiopic;
+ int i, count;
+ int err;
+
+ for (i = 0; i < sc->sc_npins; i++) {
+ uint32_t pincaps;
+
+ err = GPIO_PIN_GETCAPS(sc->sc_dev, i, &pincaps);
+ if (err != 0)
+ continue;
+ if ((pincaps & GPIO_INTR_MASK) != GPIO_INTR_NONE)
+ break;
+ }
+ if (i == sc->sc_npins)
+ return;
+
+ gpiopic = malloc(sizeof(struct gpiopic), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (gpiopic == NULL) {
+ device_printf(sc->sc_busdev, "gpiopic allocation failed\n");
+ return;
+ }
+
+ gpiopic->intr_srcs = malloc(sc->sc_npins *
+ sizeof(struct gpiopic_intsrc), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (gpiopic->intr_srcs == NULL) {
+ free(gpiopic, M_DEVBUF);
+ device_printf(sc->sc_busdev, "gpiopic allocation failed\n");
+ return;
+ }
+
+ gpiopic->intr_counts = malloc(2 * sc->sc_npins *
+ sizeof(*gpiopic->intr_counts), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (gpiopic->intr_counts == NULL) {
+ free(gpiopic->intr_srcs, M_DEVBUF);
+ free(gpiopic, M_DEVBUF);
+ device_printf(sc->sc_busdev, "gpiopic allocation failed\n");
+ return;
+ }
+
+ sx_init(&gpiopic->intrsrc_lock, "gpiopic lock");
+ gpiopic->sc = sc;
+ count = gpiopic_register_sources(gpiopic);
+ sc->sc_pic = gpiopic;
+ device_printf(sc->sc_busdev, "initialized %d interrupt sources\n",
+ count);
+
+ ctx = device_get_sysctl_ctx(sc->sc_busdev);
+ tree_node = device_get_sysctl_tree(sc->sc_busdev);
+ tree = SYSCTL_CHILDREN(tree_node);
+ SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "interrupts",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ gpiopic, 0, sysctl_gpio_intrs, "A",
+ "interrupt:pin (current mode): count");
+}
+
+static void
+gpiobus_pic_destroy(struct gpiopic *gpiopic)
+{
+ struct gpiopic_intsrc *gisrc;
+ int i;
+
+ if (gpiopic == NULL)
+ return;
+
+ sx_xlock(&gpiopic->intrsrc_lock);
+ for (i = 0; i < gpiopic->sc->sc_npins; i++) {
+ gisrc = &gpiopic->intr_srcs[i];
+ if (gisrc->is_event == NULL)
+ continue;
+ /* XXX what to do if there is a busy event? */
+ (void)intr_event_destroy(gisrc->is_event);
+ }
+ sx_xunlock(&gpiopic->intrsrc_lock);
+
+ sx_destroy(&gpiopic->intrsrc_lock);
+ free(gpiopic->intr_counts, M_DEVBUF);
+ free(gpiopic->intr_srcs, M_DEVBUF);
+ free(gpiopic, M_DEVBUF);
+}
+
+static void
+gpiobus_pic_resume(struct gpiopic *gpiopic)
+{
+ struct gpiopic_intsrc *gisrc;
+ int i;
+
+ if (gpiopic == NULL)
+ return;
+
+ sx_xlock(&gpiopic->intrsrc_lock);
+ for (i = 0; i < gpiopic->sc->sc_npins; i++) {
+ gisrc = &gpiopic->intr_srcs[i];
+ if (gisrc->is_event == NULL)
+ continue;
+
+ /* Just in case. */
+ gpiopic_disable_intr(gisrc);
+
+ if (!gisrc->is_enabled)
+ continue;
+
+ if (gisrc->is_mode != GPIO_INTR_CONFORM)
+ gpiopic_config_intr(gisrc, gisrc->is_mode);
+ if (gisrc->is_masked)
+ gpiopic_mask_intr(gisrc);
+ else
+ gpiopic_unmask_intr(gisrc);
+ gpiopic_enable_intr(gisrc);
+ }
+ sx_xunlock(&gpiopic->intrsrc_lock);
+}
+
+#define GPIO_MAX_STRAY 10
+
+void
+gpiobus_handle_intr(device_t busdev, uint32_t pin)
+{
+ struct gpiobus_softc *sc;
+ struct gpiopic *gpiopic;
+ struct gpiopic_intsrc *gisrc;
+ struct intr_event *ie;
+
+ sc = device_get_softc(busdev);
+ gpiopic = sc->sc_pic;
+
+ KASSERT(pin < sc->sc_npins,
+ ("%s for unsupported pin %u", __func__, pin));
+ gisrc = &gpiopic->intr_srcs[pin];
+ ie = gisrc->is_event;
+
+ (*gisrc->is_count)++;
+
+ /*
+ * For stray interrupts, mask the source, bump the
+ * stray count, and log the condition.
+ */
+ if (intr_event_handle(ie, curthread->td_intr_frame) != 0) {
+ gpiopic_mask_intr(gisrc);
+ gpiopic_eoi(gisrc);
+ (*gisrc->is_straycount)++;
+ if (*gisrc->is_straycount < GPIO_MAX_STRAY) {
+ log(LOG_ERR, "stray irq on pin %u\n", pin);
+ } else if (*gisrc->is_straycount == GPIO_MAX_STRAY) {
+ log(LOG_CRIT, "too many stray irq's on pin %u: "
+ "not logging anymore\n", pin);
+ }
+ }
+}
+
+static int
+gpiopic_add_handler(const char *name, struct gpiopic *gpiopic, uint32_t pin,
+ driver_filter_t filter, driver_intr_t handler,
+ void *arg, enum intr_type flags, void **cookiep)
+{
+ struct gpiopic_intsrc *gisrc;
+ int err;
+
+ KASSERT(gpiopic_check_intr_pin(gpiopic, pin) == 0,
+ ("setup_intr for unsupported pin %u", pin));
+ gisrc = &gpiopic->intr_srcs[pin];
+ err = intr_event_add_handler(gisrc->is_event,
+ name, filter, handler, arg,
+ intr_priority(flags), flags, cookiep);
+ if (err == 0) {
+ sx_xlock(&gpiopic->intrsrc_lock);
+ gisrc->is_handlers++;
+ if (gisrc->is_handlers == 1) {
+ gpiopic_enable_intr(gisrc);
+ gpiopic_unmask_intr(gisrc); /* unmask source */
+ }
+ sx_xunlock(&gpiopic->intrsrc_lock);
+ }
+ return (err);
+}
+
+static int
+gpiobus_setup_intr(device_t bus, device_t dev, struct resource *irq,
+ int flags, driver_filter_t filter, void (*ihand)(void *),
+ void *arg, void **cookiep)
+{
+ struct gpiobus_softc *sc;
+ int err;
+
+ sc = device_get_softc(bus);
+ if (!rman_is_region_manager(irq, &sc->sc_intr_rman))
+ return (EINVAL);
+
+ *cookiep = NULL;
+ if ((rman_get_flags(irq) & RF_SHAREABLE) == 0)
+ flags |= INTR_EXCL;
+
+ err = rman_activate_resource(irq);
+ if (err != 0)
+ return (err);
+
+ err = gpiopic_add_handler(device_get_nameunit(dev), sc->sc_pic,
+ rman_get_start(irq), filter, ihand, arg, flags, cookiep);
+ return (err);
+}
+
+static int
+gpiopic_remove_handler(void *cookie)
+{
+ struct gpiopic *gpiopic;
+ struct gpiopic_intsrc *gisrc;
+ int err;
+
+ gisrc = intr_handler_source(cookie);
+ if (gisrc == NULL)
+ return (EINVAL);
+
+ gpiopic = gisrc->is_pic;
+ err = intr_event_remove_handler(cookie);
+ if (err == 0) {
+ sx_xlock(&gpiopic->intrsrc_lock);
+ gisrc->is_handlers--;
+ if (gisrc->is_handlers == 0) {
+ gpiopic_mask_intr(gisrc); /* mask source */
+ gpiopic_disable_intr(gisrc);
+ }
+ sx_xunlock(&gpiopic->intrsrc_lock);
+ }
+ return (err);
+}
+
+static int
+gpiobus_teardown_intr(device_t bus, device_t dev, struct resource *r,
+ void *ih)
+{
+ struct gpiobus_softc *sc;
+ int err;
+
+ sc = device_get_softc(bus);
+ if (!rman_is_region_manager(r, &sc->sc_intr_rman))
+ return (EINVAL);
+ err = gpiopic_remove_handler(ih);
+ return (err);
+}
+
+static int
+gpiopic_describe(void *ih, const char *descr)
+{
+ struct gpiopic_intsrc *gisrc;
+ int err;
+
+ gisrc = intr_handler_source(ih);
+ if (gisrc == NULL)
+ return (EINVAL);
+ err = intr_event_describe_handler(gisrc->is_event, ih, descr);
+ return (err);
+}
+
+static int
+gpiobus_describe_intr(device_t bus, device_t child, struct resource *irq,
+ void *cookie, const char *descr)
+{
+ struct gpiobus_softc *sc;
+ int err;
+
+ sc = device_get_softc(bus);
+ if (!rman_is_region_manager(irq, &sc->sc_intr_rman))
+ return (EINVAL);
+ err = gpiopic_describe(cookie, descr);
+ return (err);
+}
+
+static int
+gpiobus_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ return (EOPNOTSUPP);
+}
+
+struct resource *
+gpio_alloc_intr_resource(device_t consumer_dev, int *rid, u_int alloc_flags,
+ gpio_pin_t pin, uint32_t intr_mode)
+{
+ struct resource *res;
+ struct gpiobus_softc *sc;
+ struct gpiopic_intsrc *gisrc;
+ device_t busdev;
+ uint32_t caps;
+ int err;
+
+ switch (intr_mode) {
+ case GPIO_INTR_EDGE_FALLING:
+ case GPIO_INTR_EDGE_RISING:
+ case GPIO_INTR_EDGE_BOTH:
+ case GPIO_INTR_LEVEL_LOW:
+ case GPIO_INTR_LEVEL_HIGH:
+ case GPIO_INTR_CONFORM:
+ break;
+ default:
+ return (NULL);
+ }
+
+ err = GPIO_PIN_GETCAPS(pin->dev, pin->pin, &caps);
+ if (err != 0)
+ return (NULL);
+ if ((intr_mode & caps) == 0)
+ return (NULL);
+
+ busdev = GPIO_GET_BUS(pin->dev);
+ sc = device_get_softc(busdev);
+ err = gpiopic_check_intr_pin(sc->sc_pic, pin->pin);
+ if (err != 0) {
+ device_printf(busdev, "PIC not set up or bad pin %u\n",
+ pin->pin);
+ return (NULL);
+ }
+
+ KASSERT(sc->sc_pins[pin->pin].mapped,
+ ("%s: unmapped pin %u", __func__, pin->pin));
+ res = BUS_ALLOC_RESOURCE(busdev, consumer_dev, SYS_RES_IRQ, rid,
+ pin->pin, pin->pin, 1, alloc_flags);
+ if (res != NULL && intr_mode != GPIO_INTR_CONFORM) {
+ gisrc = &sc->sc_pic->intr_srcs[pin->pin];
+ gpiopic_config_intr(gisrc, intr_mode);
+ }
+ return (res);
+}
+#endif /* !INTRNG */
+
static device_method_t gpiobus_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, gpiobus_probe),
@@ -1086,9 +1650,16 @@
DEVMETHOD(device_resume, gpiobus_resume),
/* Bus interface */
+#ifdef INTRNG
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
DEVMETHOD(bus_config_intr, bus_generic_config_intr),
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+#else
+ DEVMETHOD(bus_setup_intr, gpiobus_setup_intr),
+ DEVMETHOD(bus_config_intr, gpiobus_config_intr),
+ DEVMETHOD(bus_describe_intr, gpiobus_describe_intr),
+ DEVMETHOD(bus_teardown_intr, gpiobus_teardown_intr),
+#endif
DEVMETHOD(bus_set_resource, gpiobus_set_resource),
DEVMETHOD(bus_alloc_resource, gpiobus_alloc_resource),
DEVMETHOD(bus_release_resource, gpiobus_release_resource),
Index: sys/dev/gpio/gpiobusvar.h
===================================================================
--- sys/dev/gpio/gpiobusvar.h
+++ sys/dev/gpio/gpiobusvar.h
@@ -84,10 +84,13 @@
};
#endif
+struct gpiopic;
+
struct gpiobus_softc
{
struct mtx sc_mtx; /* bus mutex */
struct rman sc_intr_rman; /* isr resources */
+ struct gpiopic *sc_pic; /* interrupt controller logic */
device_t sc_busdev; /* bus device */
device_t sc_owner; /* bus owner */
device_t sc_dev; /* driver device */
@@ -176,6 +179,7 @@
int gpio_check_flags(uint32_t, uint32_t);
device_t gpiobus_attach_bus(device_t);
int gpiobus_detach_bus(device_t);
+void gpiobus_handle_intr(device_t, uint32_t);
int gpiobus_init_softc(device_t);
int gpiobus_alloc_ivars(struct gpiobus_ivar *);
void gpiobus_free_ivars(struct gpiobus_ivar *);
Index: sys/kern/kern_intr.c
===================================================================
--- sys/kern/kern_intr.c
+++ sys/kern/kern_intr.c
@@ -261,8 +261,8 @@
struct intr_event *ie;
va_list ap;
- /* The only valid flag during creation is IE_SOFT. */
- if ((flags & ~IE_SOFT) != 0)
+ /* Check for internal flags. */
+ if ((flags & ~(IE_SOFT | IE_BUS_PRIV)) != 0)
return (EINVAL);
ie = malloc(sizeof(struct intr_event), M_ITHREAD, M_WAITOK | M_ZERO);
ie->ie_source = source;
@@ -413,6 +413,7 @@
TAILQ_FOREACH(ie, &event_list, ie_list)
if (ie->ie_irq == irq &&
(ie->ie_flags & IE_SOFT) == 0 &&
+ (ie->ie_flags & IE_BUS_PRIV) == 0 &&
CK_SLIST_FIRST(&ie->ie_handlers) != NULL)
break;
mtx_unlock(&event_lock);
Index: sys/sys/interrupt.h
===================================================================
--- sys/sys/interrupt.h
+++ sys/sys/interrupt.h
@@ -132,6 +132,9 @@
/* Interrupt event flags kept in ie_flags. */
#define IE_SOFT 0x000001 /* Software interrupt. */
#define IE_ADDING_THREAD 0x000004 /* Currently building an ithread. */
+#define IE_BUS_PRIV 0x000008 /* Interrupt is handled at bus level,
+ invisible to nexus and MD interrupt
+ code. */
/* Flags to pass to swi_sched. */
#define SWI_FROMNMI 0x1

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 16, 11:02 PM (1 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27672412
Default Alt Text
D26407.id76930.diff (36 KB)

Event Timeline