From 2b5b2e045a5e5c6a0bac2bcab050bdf0decc4caf Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Tue, 13 Apr 2010 15:24:18 +0000 Subject: [PATCH] Implement MSI support using the x86 specific PCI module in the FreeBSD 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 --- src/libs/compat/freebsd_network/bus.c | 73 ++++++++++++++++++++++-- src/libs/compat/freebsd_network/compat.c | 1 + src/libs/compat/freebsd_network/device.h | 1 + src/libs/compat/freebsd_network/driver.c | 10 ++++ 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 09e40a45bf..48200d809d 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 +#include + //#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 3e66aebe8e..d2e780daa2 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 78e1d59520..0a6349f809 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 e37ce4960e..1679a44fc7 100644 --- a/src/libs/compat/freebsd_network/driver.c +++ b/src/libs/compat/freebsd_network/driver.c @@ -19,6 +19,7 @@ #include #include +#include #include @@ -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); }