* Added functionality to resolve the addresses provided in the IRQ routing table
to PCI devices partly using the new ACPI interface and by using bridge config information from PCI. * Added lookup of matching device/pin combinations and update of the relevant interrupt line variable via the new PCI module function. This means that the Global System Interrupt (GSI) that is used after switching to the IO-APIC is now stored in the PCI config space and drivers enumerating these devices will now attach their interrupt handlers to the right IRQs. * Resolve all relevant interrupt information directly into the irq_routing_entry so that can be used as the single source for config information. This includes resolving the current setting of any PCI link devices into the irq field that represents a GSI now. * Use that info to configure interrupts in arch_int.cpp and remove the logic there. * Some cleanup and added debug output. This implements the final missing part for using IO-APICs and full APIC mode for interrupt routing. Note that there is no quirk handling of any form, so this may very well not work on some configurations. Note also that I have tested this only on one machine so far. Once proper testing is done the default of disabling the IO-APIC can be removed. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41367 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
0fc2a29e52
commit
8c804ef2d6
@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008-2011, Michael Lotz, mmlr@mlotz.ch.
|
||||
* Copyright 2010, Clemens Zeidler, haiku@clemens-zeidler.de.
|
||||
* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
|
||||
@ -588,6 +589,12 @@ acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
|
||||
parameter.count = 1;
|
||||
parameter.pointer = &model;
|
||||
|
||||
dprintf("setting ACPI interrupt model to %s\n",
|
||||
interruptModel == 0 ? "PIC"
|
||||
: (interruptModel == 1 ? "APIC"
|
||||
: (interruptModel == 2 ? "SAPIC"
|
||||
: "unknown")));
|
||||
|
||||
return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL);
|
||||
}
|
||||
|
||||
@ -696,30 +703,11 @@ ioapic_init(kernel_args* args)
|
||||
print_irq_routing_table(&table);
|
||||
#endif
|
||||
|
||||
// configure io apic interrupts from pci routing table
|
||||
// configure io apic interrupts from PCI routing table
|
||||
for (int i = 0; i < table.Count(); i++) {
|
||||
uint8 irq = 0;
|
||||
uint32 config = 0;
|
||||
|
||||
irq_routing_entry& entry = table.ElementAt(i);
|
||||
if (entry.source == 0) {
|
||||
// fixed irq configuration
|
||||
irq = entry.source_index;
|
||||
config = B_LEVEL_TRIGGERED | B_LOW_ACTIVE_POLARITY;
|
||||
} else {
|
||||
// irq comes from the link device and is configurable
|
||||
irq_descriptor irqDescriptor;
|
||||
status = read_current_irq(acpiModule, entry.source, &irqDescriptor);
|
||||
if (status != B_OK) {
|
||||
dprintf("failed to read current irq config of link device\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
irq = irqDescriptor.irq;
|
||||
config = irqDescriptor.polarity | irqDescriptor.interrupt_mode;
|
||||
}
|
||||
|
||||
ioapic_configure_io_interrupt(irq, config);
|
||||
ioapic_configure_io_interrupt(entry.irq,
|
||||
entry.polarity | entry.trigger_mode);
|
||||
}
|
||||
|
||||
// disable the legacy PIC
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011, Michael Lotz mmlr@mlotz.ch.
|
||||
* Copyright 2009, Clemens Zeidler haiku@clemens-zeidler.de.
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -11,8 +12,10 @@
|
||||
|
||||
#include <int.h>
|
||||
|
||||
#include <PCI.h>
|
||||
|
||||
#define TRACE_PRT
|
||||
|
||||
//#define TRACE_PRT
|
||||
#ifdef TRACE_PRT
|
||||
# define TRACE(x...) dprintf("IRQRoutingTable: "x)
|
||||
#else
|
||||
@ -31,7 +34,7 @@ irq_descriptor::irq_descriptor()
|
||||
irq(0),
|
||||
shareable(false),
|
||||
polarity(B_HIGH_ACTIVE_POLARITY),
|
||||
interrupt_mode(B_EDGE_TRIGGERED)
|
||||
trigger_mode(B_EDGE_TRIGGERED)
|
||||
{
|
||||
}
|
||||
|
||||
@ -44,54 +47,180 @@ print_irq_descriptor(irq_descriptor* descriptor)
|
||||
const char* levelTriggeredString = "level triggered";
|
||||
const char* edgeTriggeredString = "edge triggered";
|
||||
|
||||
dprintf("irq: %u, shareable: %u, polarity: %s, interrupt_mode: %s\n",
|
||||
dprintf("irq: %u, shareable: %u, polarity: %s, trigger_mode: %s\n",
|
||||
descriptor->irq, descriptor->shareable,
|
||||
descriptor->polarity == B_HIGH_ACTIVE_POLARITY ? activeHighString
|
||||
: activeLowString,
|
||||
descriptor->interrupt_mode == B_LEVEL_TRIGGERED ? levelTriggeredString
|
||||
descriptor->trigger_mode == B_LEVEL_TRIGGERED ? levelTriggeredString
|
||||
: edgeTriggeredString);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
print_irq_routing_entry(const irq_routing_entry& entry)
|
||||
{
|
||||
dprintf("address 0x%04llx; pin %u;", entry.device_address, entry.pin);
|
||||
|
||||
if (entry.source_index != 0)
|
||||
dprintf(" GSI %lu;", entry.source_index);
|
||||
else
|
||||
dprintf(" source %p %lu;", entry.source, entry.source_index);
|
||||
|
||||
dprintf(" pci %u:%u\n", entry.pci_bus, entry.pci_device);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_irq_routing_table(IRQRoutingTable* table)
|
||||
{
|
||||
dprintf("IRQ routing table with %i entries\n", (int)table->Count());
|
||||
for (int i = 0; i < table->Count(); i++) {
|
||||
irq_routing_entry& entry = table->ElementAt(i);
|
||||
dprintf("\tentry %i\n", i);
|
||||
dprintf("\t\taddress: %x\n", entry.device_address);
|
||||
dprintf("\t\tpin: %i\n", entry.pin);
|
||||
dprintf("\t\tsource: 0x%x\n", int(entry.source));
|
||||
dprintf("\t\tsource index: %i\n", entry.source_index);
|
||||
}
|
||||
for (int i = 0; i < table->Count(); i++)
|
||||
print_irq_routing_entry(table->ElementAt(i));
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
read_device_irq_routing_table(acpi_module_info* acpi, acpi_handle device,
|
||||
update_pci_info_for_entry(pci_module_info* pci, irq_routing_entry& entry)
|
||||
{
|
||||
pci_info info;
|
||||
long index = 0;
|
||||
uint32 updateCount = 0;
|
||||
while (pci->get_nth_pci_info(index++, &info) >= 0) {
|
||||
if (info.bus != entry.pci_bus || info.device != entry.pci_device)
|
||||
continue;
|
||||
|
||||
uint8 pin = 0;
|
||||
switch (info.header_type & PCI_header_type_mask) {
|
||||
case PCI_header_type_generic:
|
||||
pin = info.u.h0.interrupt_pin;
|
||||
break;
|
||||
|
||||
case PCI_header_type_PCI_to_PCI_bridge:
|
||||
// We don't really care about bridges as we won't install
|
||||
// interrupt handlers for them, but we can still map them and
|
||||
// update their info for completeness.
|
||||
pin = info.u.h1.interrupt_pin;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Skip anything unknown as we wouldn't know how to update the
|
||||
// info anyway.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pin == 0)
|
||||
continue; // no interrupts are used
|
||||
|
||||
// Now match the pin to find the corresponding function, note that PCI
|
||||
// pins are 1 based while ACPI ones are 0 based.
|
||||
if (pin == entry.pin + 1) {
|
||||
if (pci->update_interrupt_line(info.bus, info.device, info.function,
|
||||
entry.irq) == B_OK) {
|
||||
updateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Sadly multiple functions can share the same interrupt pin so we
|
||||
// have to run through the whole list each time...
|
||||
}
|
||||
|
||||
return updateCount > 0 ? B_OK : B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
read_bridge_irq_routing_table(acpi_module_info* acpi, pci_module_info* pci,
|
||||
acpi_handle possibleBridge, acpi_handle rootPciHandle,
|
||||
IRQRoutingTable* table)
|
||||
{
|
||||
acpi_data buffer;
|
||||
buffer.pointer = NULL;
|
||||
buffer.length = ACPI_ALLOCATE_BUFFER;
|
||||
status_t status = acpi->get_irq_routing_table(device, &buffer);
|
||||
status_t status = acpi->get_irq_routing_table(possibleBridge, &buffer);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// It appears to be a bridge device, resolve the PCI infos. As all routing
|
||||
// table entries will be relative to this bus we can then just address the
|
||||
// devices using the device number extracted from the address field.
|
||||
acpi_pci_info pciInfo;
|
||||
status = acpi->get_pci_info(rootPciHandle, possibleBridge, &pciInfo);
|
||||
if (status != B_OK) {
|
||||
dprintf("failed to resolve pci info of bridge device, can't configure"
|
||||
" irq routing of devices below\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// Find the secondary bus number (the "downstream" bus number for the
|
||||
// attached devices) in the bridge configuration.
|
||||
uint8 secondaryBus = pci->read_pci_config(pciInfo.bus, pciInfo.device,
|
||||
pciInfo.function, PCI_secondary_bus, 1);
|
||||
if (secondaryBus == 255)
|
||||
return B_ERROR;
|
||||
|
||||
if (secondaryBus == pciInfo.bus && possibleBridge != rootPciHandle) {
|
||||
dprintf("invalid secondary bus %u on primary bus %u, can't configure"
|
||||
" irq routing of devices below\n", secondaryBus, pciInfo.bus);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
irq_routing_entry irqEntry;
|
||||
acpi_pci_routing_table* acpiTable = (acpi_pci_routing_table*)buffer.pointer;
|
||||
while (acpiTable->length) {
|
||||
acpi_handle source;
|
||||
bool noSource = acpiTable->source == NULL || acpiTable->source[0] == 0;
|
||||
|
||||
bool noSource = acpiTable->source[0] == '\0';
|
||||
// The above line would be correct according to specs...
|
||||
noSource = acpiTable->source_index != 0;
|
||||
// ... but we use this one as there seem to be quirks where
|
||||
// a source is indicated but not actually present. With a source
|
||||
// index != 0 a GSI is generally indicated.
|
||||
|
||||
if (noSource
|
||||
|| acpi->get_handle(NULL, acpiTable->source, &source) == B_OK) {
|
||||
|
||||
irqEntry.device_address = acpiTable->address;
|
||||
irqEntry.pin = acpiTable->pin;
|
||||
irqEntry.source = noSource ? 0 : source;
|
||||
irqEntry.source = noSource ? NULL : source;
|
||||
irqEntry.source_index = acpiTable->source_index;
|
||||
table->PushBack(irqEntry);
|
||||
irqEntry.pci_bus = secondaryBus;
|
||||
irqEntry.pci_device = (uint8)(acpiTable->address >> 16);
|
||||
|
||||
// resolve any link device so we get a straight GSI in all cases
|
||||
if (noSource) {
|
||||
// fixed GSI already
|
||||
irqEntry.irq = irqEntry.source_index;
|
||||
irqEntry.polarity = B_LOW_ACTIVE_POLARITY;
|
||||
irqEntry.trigger_mode = B_LEVEL_TRIGGERED;
|
||||
} else {
|
||||
irq_descriptor irqDescriptor;
|
||||
status = read_current_irq(acpi, source, &irqDescriptor);
|
||||
if (status != B_OK) {
|
||||
dprintf("failed to read current IRQ of link device\n");
|
||||
irqEntry.irq = 0;
|
||||
} else {
|
||||
irqEntry.irq = irqDescriptor.irq;
|
||||
irqEntry.polarity = irqDescriptor.polarity;
|
||||
irqEntry.trigger_mode = irqDescriptor.trigger_mode;
|
||||
}
|
||||
}
|
||||
|
||||
if (irqEntry.irq != 0) {
|
||||
if (update_pci_info_for_entry(pci, irqEntry) != B_OK) {
|
||||
// Note: This isn't necesarily fatal, as there can be many
|
||||
// entries in the table pointing to disabled/optional
|
||||
// devices. Also they can be used to describe the full actual
|
||||
// wireing regardless of the presence of devices, in which
|
||||
// case many entries won't have matches.
|
||||
#ifdef TRACE_PRT
|
||||
dprintf("didn't find a matching PCI device for irq entry,"
|
||||
" can't update interrupt_line info:\n");
|
||||
print_irq_routing_entry(irqEntry);
|
||||
#endif
|
||||
}
|
||||
|
||||
table->PushBack(irqEntry);
|
||||
}
|
||||
}
|
||||
|
||||
acpiTable = (acpi_pci_routing_table*)((uint8*)acpiTable
|
||||
@ -116,28 +245,44 @@ read_irq_routing_table(acpi_module_info* acpi, IRQRoutingTable* table)
|
||||
status = acpi->get_handle(NULL, rootPciName, &rootPciHandle);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
TRACE("Read root pci bus irq rooting table\n");
|
||||
status = read_device_irq_routing_table(acpi, rootPciHandle, table);
|
||||
if (status != B_OK)
|
||||
|
||||
pci_module_info* pci;
|
||||
status = get_module(B_PCI_MODULE_NAME, (module_info**)&pci);
|
||||
if (status != B_OK) {
|
||||
// shouldn't happen, since the PCI module is a dependency of the
|
||||
// ACPI module and we shouldn't be here at all if it wasn't loaded
|
||||
dprintf("failed to get PCI module!\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
TRACE("read root pci bus irq routing table\n");
|
||||
status = read_bridge_irq_routing_table(acpi, pci, rootPciHandle,
|
||||
rootPciHandle, table);
|
||||
if (status != B_OK) {
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
return status;
|
||||
}
|
||||
|
||||
TRACE("find routing tables \n");
|
||||
|
||||
char name[255];
|
||||
name[0] = 0;
|
||||
void *counter = NULL;
|
||||
void* counter = NULL;
|
||||
while (acpi->get_next_entry(ACPI_TYPE_DEVICE, rootPciName, name,
|
||||
sizeof(name), &counter) == B_OK) {
|
||||
acpi_handle brigde;
|
||||
status = acpi->get_handle(NULL, name, &brigde);
|
||||
acpi_handle possibleBridge;
|
||||
status = acpi->get_handle(NULL, name, &possibleBridge);
|
||||
if (status != B_OK)
|
||||
continue;
|
||||
|
||||
status = read_device_irq_routing_table(acpi, brigde, table);
|
||||
status = read_bridge_irq_routing_table(acpi, pci, possibleBridge,
|
||||
rootPciHandle, table);
|
||||
if (status == B_OK)
|
||||
TRACE("routing table found %s\n", name);
|
||||
// if it failed it simply was no bridge at all
|
||||
}
|
||||
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
return table->Count() > 0 ? B_OK : B_ERROR;
|
||||
}
|
||||
|
||||
@ -179,7 +324,7 @@ read_irq_descriptor(acpi_module_info* acpi, acpi_handle device,
|
||||
|
||||
descriptor->irq = irq->interrupts[0];
|
||||
descriptor->shareable = irq->sharable != 0;
|
||||
descriptor->interrupt_mode = irq->triggering == 0
|
||||
descriptor->trigger_mode = irq->triggering == 0
|
||||
? B_LEVEL_TRIGGERED : B_EDGE_TRIGGERED;
|
||||
descriptor->polarity = irq->polarity == 0
|
||||
? B_HIGH_ACTIVE_POLARITY : B_LOW_ACTIVE_POLARITY;
|
||||
@ -214,7 +359,7 @@ read_irq_descriptor(acpi_module_info* acpi, acpi_handle device,
|
||||
|
||||
descriptor->irq = irq->interrupts[0];
|
||||
descriptor->shareable = irq->sharable != 0;
|
||||
descriptor->interrupt_mode = irq->triggering == 0
|
||||
descriptor->trigger_mode = irq->triggering == 0
|
||||
? B_LEVEL_TRIGGERED : B_EDGE_TRIGGERED;
|
||||
descriptor->polarity = irq->polarity == 0
|
||||
? B_HIGH_ACTIVE_POLARITY : B_LOW_ACTIVE_POLARITY;
|
||||
@ -282,7 +427,7 @@ set_acpi_irq(acpi_module_info* acpi, acpi_handle device,
|
||||
|
||||
data[3] = 0;
|
||||
int8 bit;
|
||||
bit = (descriptor->interrupt_mode == B_HIGH_ACTIVE_POLARITY) ? 0 : 1;
|
||||
bit = (descriptor->trigger_mode == B_HIGH_ACTIVE_POLARITY) ? 0 : 1;
|
||||
data[3] |= bit;
|
||||
bit = (descriptor->polarity == B_LEVEL_TRIGGERED) ? 0 : 1;
|
||||
data[3] |= bit << 3;
|
||||
|
@ -13,15 +13,21 @@
|
||||
|
||||
|
||||
struct irq_routing_entry {
|
||||
int device_address;
|
||||
int8 pin;
|
||||
// ACPI specifics
|
||||
uint64 device_address;
|
||||
uint8 pin;
|
||||
|
||||
acpi_handle source;
|
||||
int source_index;
|
||||
uint32 source_index;
|
||||
|
||||
// pci busmanager connection
|
||||
uchar pci_bus;
|
||||
uchar pci_device;
|
||||
// PCI bus_manager connection
|
||||
uint8 pci_bus;
|
||||
uint8 pci_device;
|
||||
|
||||
// Distilled configuration info
|
||||
uint8 irq; // Global System Interrupt (GSI)
|
||||
uint8 polarity; // B_{HIGH|LOW}_ACTIVE_POLARITY
|
||||
uint8 trigger_mode; // B_{LEVEL|EDGE}_TRIGGERED
|
||||
};
|
||||
|
||||
|
||||
@ -36,7 +42,7 @@ struct irq_descriptor {
|
||||
// B_LOW_ACTIVE_POLARITY or B_HIGH_ACTIVE_POLARITY
|
||||
uint8 polarity;
|
||||
// B_LEVEL_TRIGGERED or B_EDGE_TRIGGERED
|
||||
uint8 interrupt_mode;
|
||||
uint8 trigger_mode;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user