Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142089392
D26407.id76930.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
36 KB
Referenced Files
None
Subscribers
None
D26407.id76930.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D26407: [PREVIEW] support for gpio interrupts on !intrng platforms
Attached
Detach File
Event Timeline
Log In to Comment