summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Lotz <mmlr@mlotz.ch>2010-04-13 15:24:18 (GMT)
committerMichael Lotz <mmlr@mlotz.ch>2010-04-13 15:24:18 (GMT)
commit2b5b2e045a5e5c6a0bac2bcab050bdf0decc4caf (patch)
treeb219ae099a92e79d80bb8d36152db04c2aeed260
parentbe982b9cd5a8704cc80c4b28fba3a7a76aae3616 (diff)
Implement MSI support using the x86 specific PCI module in the FreeBSDhrev36225
compatibility layer. This should make some network and wireless hardware use MSIs and therefore solve issues related to interrupt sharing and wrongly advertised interrupt lines. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36225 a95241bf-73f2-0310-859d-f6bbb57e9c96
-rw-r--r--src/libs/compat/freebsd_network/bus.c73
-rw-r--r--src/libs/compat/freebsd_network/compat.c1
-rw-r--r--src/libs/compat/freebsd_network/device.h1
-rw-r--r--src/libs/compat/freebsd_network/driver.c10
4 files changed, 79 insertions, 6 deletions
diff --git a/src/libs/compat/freebsd_network/bus.c b/src/libs/compat/freebsd_network/bus.c
index 09e40a4..48200d8 100644
--- a/src/libs/compat/freebsd_network/bus.c
+++ b/src/libs/compat/freebsd_network/bus.c
@@ -22,6 +22,8 @@
// private kernel header to get B_NO_HANDLED_INFO
#include <int.h>
+#include <PCI_x86.h>
+
//#define DEBUG_BUS_SPACE_RW
#ifdef DEBUG_BUS_SPACE_RW
@@ -48,6 +50,7 @@ struct internal_intr {
void *arg;
int irq;
uint32 flags;
+ bool is_msi;
thread_id thread;
sem_id sem;
@@ -138,9 +141,21 @@ bus_alloc_resource(device_t dev, int type, int *rid, unsigned long start,
if (res == NULL)
return NULL;
- if (type == SYS_RES_IRQ)
- result = bus_alloc_irq_resource(dev, res);
- else if (type == SYS_RES_MEMORY)
+ if (type == SYS_RES_IRQ) {
+ if (*rid == 0) {
+ // pinned interrupt
+ result = bus_alloc_irq_resource(dev, res);
+ } else {
+ // msi or msi-x interrupt at index *rid - 1
+ pci_info *info;
+ info = &((struct root_device_softc *)dev->root->softc)->pci_info;
+ res->r_bustag = 1;
+ res->r_bushandle = info->u.h0.interrupt_line + *rid - 1;
+ result = 0;
+
+ // TODO: msi-x interrupts
+ }
+ } else if (type == SYS_RES_MEMORY)
result = bus_alloc_mem_resource(dev, res, *rid);
else if (type == SYS_RES_IOPORT)
result = bus_alloc_ioport_resource(dev, res, *rid);
@@ -309,6 +324,7 @@ bus_setup_intr(device_t dev, struct resource *res, int flags,
intr->arg = arg;
intr->irq = res->r_bushandle;
intr->flags = flags;
+ intr->is_msi = false;
intr->sem = -1;
intr->thread = -1;
@@ -341,6 +357,20 @@ bus_setup_intr(device_t dev, struct resource *res, int flags,
intr_wrapper, intr, B_NO_HANDLED_INFO);
}
+ if (status == B_OK && res->r_bustag == 1 && gPCIx86 != NULL) {
+ // this is an msi, enable it
+ pci_info *info
+ = &((struct root_device_softc *)dev->root->softc)->pci_info;
+ if (gPCIx86->enable_msi(info->bus, info->device,
+ info->function) != B_OK) {
+ device_printf(dev, "enabling msi failed\n");
+ bus_teardown_intr(dev, res, intr);
+ return ENODEV;
+ }
+
+ intr->is_msi = true;
+ }
+
if (status < B_OK) {
free_internal_intr(intr);
return status;
@@ -358,6 +388,13 @@ bus_teardown_intr(device_t dev, struct resource *res, void *arg)
{
struct internal_intr *intr = arg;
+ if (intr->is_msi && gPCIx86 != NULL) {
+ // disable msi generation
+ pci_info *info
+ = &((struct root_device_softc *)dev->root->softc)->pci_info;
+ gPCIx86->disable_msi(info->bus, info->device, info->function);
+ }
+
if (intr->filter != NULL) {
remove_io_interrupt_handler(intr->irq, (interrupt_handler)intr->filter,
intr->arg);
@@ -659,21 +696,45 @@ pci_find_extcap(device_t child, int capability, int *_capabilityRegister)
int
pci_msi_count(device_t dev)
{
- return 0;
+ pci_info *info;
+ if (gPCIx86 == NULL)
+ return 0;
+
+ info = &((struct root_device_softc *)dev->root->softc)->pci_info;
+ return gPCIx86->get_msi_count(info->bus, info->device, info->function);
}
int
pci_alloc_msi(device_t dev, int *count)
{
- return ENODEV;
+ pci_info *info;
+ uint8 startVector = 0;
+ if (gPCIx86 == NULL)
+ return ENODEV;
+
+ info = &((struct root_device_softc *)dev->root->softc)->pci_info;
+
+ if (gPCIx86->configure_msi(info->bus, info->device, info->function, *count,
+ &startVector) != B_OK) {
+ return ENODEV;
+ }
+
+ info->u.h0.interrupt_line = startVector;
+ return EOK;
}
int
pci_release_msi(device_t dev)
{
- return ENODEV;
+ pci_info *info;
+ if (gPCIx86 == NULL)
+ return ENODEV;
+
+ info = &((struct root_device_softc *)dev->root->softc)->pci_info;
+ gPCIx86->unconfigure_msi(info->bus, info->device, info->function);
+ return EOK;
}
diff --git a/src/libs/compat/freebsd_network/compat.c b/src/libs/compat/freebsd_network/compat.c
index 3e66aeb..d2e780d 100644
--- a/src/libs/compat/freebsd_network/compat.c
+++ b/src/libs/compat/freebsd_network/compat.c
@@ -27,6 +27,7 @@ spinlock __haiku_intr_spinlock;
struct net_stack_module_info *gStack;
pci_module_info *gPci;
+struct pci_x86_module_info *gPCIx86;
static struct list sRootDevices;
static int sNextUnit;
diff --git a/src/libs/compat/freebsd_network/device.h b/src/libs/compat/freebsd_network/device.h
index 78e1d59..0a6349f 100644
--- a/src/libs/compat/freebsd_network/device.h
+++ b/src/libs/compat/freebsd_network/device.h
@@ -43,6 +43,7 @@ enum {
extern struct net_stack_module_info *gStack;
extern pci_module_info *gPci;
+extern struct pci_x86_module_info *gPCIx86;
static inline void
diff --git a/src/libs/compat/freebsd_network/driver.c b/src/libs/compat/freebsd_network/driver.c
index e37ce49..1679a44 100644
--- a/src/libs/compat/freebsd_network/driver.c
+++ b/src/libs/compat/freebsd_network/driver.c
@@ -19,6 +19,7 @@
#include <Drivers.h>
#include <ether_driver.h>
+#include <PCI_x86.h>
#include <compat/sys/haiku-module.h>
@@ -149,6 +150,10 @@ _fbsd_init_driver(driver_t *driver)
if (status < B_OK)
return status;
+ // if it fails we just don't support x86 specific features (like MSIs)
+ if (get_module(B_PCI_X86_MODULE_NAME, (module_info **)&gPCIx86) != B_OK)
+ gPCIx86 = NULL;
+
status = init_hard_clock();
if (status < B_OK)
goto err1;
@@ -220,6 +225,9 @@ err2:
uninit_hard_clock();
err1:
put_module(B_PCI_MODULE_NAME);
+ if (gPCIx86 != NULL)
+ put_module(B_PCI_X86_MODULE_NAME);
+
return status;
}
@@ -243,4 +251,6 @@ _fbsd_uninit_driver(driver_t *driver)
uninit_mutexes();
put_module(B_PCI_MODULE_NAME);
+ if (gPCIx86 != NULL)
+ put_module(B_PCI_X86_MODULE_NAME);
}