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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user