summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2008-03-20 21:24:32 +0000
committerjhb <jhb@FreeBSD.org>2008-03-20 21:24:32 +0000
commit6cf6d7b22b5d548a6ec6807400ca3ae0731e1ef6 (patch)
tree885a4249c364d8b7ce6b6edbc94f378c0e100668
parentf997b9d36a153a47597611c46f5f3fd6569faa58 (diff)
downloadFreeBSD-src-6cf6d7b22b5d548a6ec6807400ca3ae0731e1ef6.zip
FreeBSD-src-6cf6d7b22b5d548a6ec6807400ca3ae0731e1ef6.tar.gz
Implement a BUS_BIND_INTR() method in the bus interface to bind an IRQ
resource to a CPU. The default method is to pass the request up to the parent similar to BUS_CONFIG_INTR() so that all busses don't have to explicitly implement bus_bind_intr. A bus_bind_intr(9) wrapper routine similar to bus_setup/teardown_intr() is added for device drivers to use. Unbinding an interrupt is done by binding it to NOCPU. The IRQ resource must be allocated, but it can happen in any order with respect to bus_setup_intr(). Currently it is only supported on amd64 and i386 via nexus(4) methods that simply call the intr_bind() routine. Tested by: gallatin
-rw-r--r--sys/amd64/amd64/nexus.c14
-rw-r--r--sys/i386/i386/nexus.c14
-rw-r--r--sys/kern/bus_if.m17
-rw-r--r--sys/kern/subr_bus.c31
-rw-r--r--sys/sys/bus.h3
5 files changed, 79 insertions, 0 deletions
diff --git a/sys/amd64/amd64/nexus.c b/sys/amd64/amd64/nexus.c
index de47d7d..5eafd3b 100644
--- a/sys/amd64/amd64/nexus.c
+++ b/sys/amd64/amd64/nexus.c
@@ -87,6 +87,9 @@ static device_t nexus_add_child(device_t bus, int order, const char *name,
int unit);
static struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
u_long, u_long, u_long, u_int);
+#ifdef SMP
+static int nexus_bind_intr(device_t, device_t, struct resource *, int);
+#endif
static int nexus_config_intr(device_t, int, enum intr_trigger,
enum intr_polarity);
static int nexus_activate_resource(device_t, device_t, int, int,
@@ -128,6 +131,9 @@ static device_method_t nexus_methods[] = {
DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
DEVMETHOD(bus_setup_intr, nexus_setup_intr),
DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
+#ifdef SMP
+ DEVMETHOD(bus_bind_intr, nexus_bind_intr),
+#endif
DEVMETHOD(bus_config_intr, nexus_config_intr),
DEVMETHOD(bus_get_resource_list, nexus_get_reslist),
DEVMETHOD(bus_set_resource, nexus_set_resource),
@@ -458,6 +464,14 @@ nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
return (intr_remove_handler(ih));
}
+#ifdef SMP
+static int
+nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
+{
+ return (intr_bind(rman_get_start(irq), cpu));
+}
+#endif
+
static int
nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
enum intr_polarity pol)
diff --git a/sys/i386/i386/nexus.c b/sys/i386/i386/nexus.c
index 0c09c34..98adc93 100644
--- a/sys/i386/i386/nexus.c
+++ b/sys/i386/i386/nexus.c
@@ -91,6 +91,9 @@ static device_t nexus_add_child(device_t bus, int order, const char *name,
int unit);
static struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
u_long, u_long, u_long, u_int);
+#ifdef SMP
+static int nexus_bind_intr(device_t, device_t, struct resource *, int);
+#endif
static int nexus_config_intr(device_t, int, enum intr_trigger,
enum intr_polarity);
static int nexus_activate_resource(device_t, device_t, int, int,
@@ -134,6 +137,9 @@ static device_method_t nexus_methods[] = {
DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
DEVMETHOD(bus_setup_intr, nexus_setup_intr),
DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
+#ifdef SMP
+ DEVMETHOD(bus_bind_intr, nexus_bind_intr),
+#endif
DEVMETHOD(bus_config_intr, nexus_config_intr),
DEVMETHOD(bus_get_resource_list, nexus_get_reslist),
DEVMETHOD(bus_set_resource, nexus_set_resource),
@@ -505,6 +511,14 @@ nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
return (intr_remove_handler(ih));
}
+#ifdef SMP
+static int
+nexus_bind_intr(device_t dev, device_t child, struct resource *irq, int cpu)
+{
+ return (intr_bind(rman_get_start(irq), cpu));
+}
+#endif
+
static int
nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
enum intr_polarity pol)
diff --git a/sys/kern/bus_if.m b/sys/kern/bus_if.m
index 59838db..72817e7 100644
--- a/sys/kern/bus_if.m
+++ b/sys/kern/bus_if.m
@@ -494,6 +494,23 @@ METHOD int child_location_str {
};
/**
+ * @brief Allow drivers to request that an interrupt be bound to a specific
+ * CPU.
+ *
+ * @param _dev the parent device of @p _child
+ * @param _child the device which allocated the resource
+ * @param _irq the resource representing the interrupt
+ * @param _cpu the CPU to bind the interrupt to
+ */
+METHOD int bind_intr {
+ device_t _dev;
+ device_t _child;
+ struct resource *_irq;
+ int _cpu;
+} DEFAULT bus_generic_bind_intr;
+
+
+/**
* @brief Allow (bus) drivers to specify the trigger mode and polarity
* of the specified interrupt.
*
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c
index 27a9f96..84eeb6a 100644
--- a/sys/kern/subr_bus.c
+++ b/sys/kern/subr_bus.c
@@ -3222,6 +3222,23 @@ bus_generic_deactivate_resource(device_t dev, device_t child, int type,
}
/**
+ * @brief Helper function for implementing BUS_BIND_INTR().
+ *
+ * This simple implementation of BUS_BIND_INTR() simply calls the
+ * BUS_BIND_INTR() method of the parent of @p dev.
+ */
+int
+bus_generic_bind_intr(device_t dev, device_t child, struct resource *irq,
+ int cpu)
+{
+
+ /* Propagate up the bus hierarchy until someone handles it. */
+ if (dev->parent)
+ return (BUS_BIND_INTR(dev->parent, child, irq, cpu));
+ return (EINVAL);
+}
+
+/**
* @brief Helper function for implementing BUS_CONFIG_INTR().
*
* This simple implementation of BUS_CONFIG_INTR() simply calls the
@@ -3529,6 +3546,20 @@ bus_teardown_intr(device_t dev, struct resource *r, void *cookie)
}
/**
+ * @brief Wrapper function for BUS_BIND_INTR().
+ *
+ * This function simply calls the BUS_BIND_INTR() method of the
+ * parent of @p dev.
+ */
+int
+bus_bind_intr(device_t dev, struct resource *r, int cpu)
+{
+ if (dev->parent == NULL)
+ return (EINVAL);
+ return (BUS_BIND_INTR(dev->parent, dev, r, cpu));
+}
+
+/**
* @brief Wrapper function for BUS_SET_RESOURCE().
*
* This function simply calls the BUS_SET_RESOURCE() method of the
diff --git a/sys/sys/bus.h b/sys/sys/bus.h
index 6e57c3d..585b8e2 100644
--- a/sys/sys/bus.h
+++ b/sys/sys/bus.h
@@ -286,6 +286,8 @@ struct resource *
int *rid, u_long start, u_long end,
u_long count, u_int flags);
int bus_generic_attach(device_t dev);
+int bus_generic_bind_intr(device_t dev, device_t child,
+ struct resource *irq, int cpu);
int bus_generic_child_present(device_t dev, device_t child);
int bus_generic_config_intr(device_t, int, enum intr_trigger,
enum intr_polarity);
@@ -358,6 +360,7 @@ int bus_setup_intr(device_t dev, struct resource *r, int flags,
driver_filter_t filter, driver_intr_t handler,
void *arg, void **cookiep);
int bus_teardown_intr(device_t dev, struct resource *r, void *cookie);
+int bus_bind_intr(device_t dev, struct resource *r, int cpu);
int bus_set_resource(device_t dev, int type, int rid,
u_long start, u_long count);
int bus_get_resource(device_t dev, int type, int rid,
OpenPOWER on IntegriCloud