* 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:
Michael Lotz 2011-05-07 17:32:12 +00:00
parent 0fc2a29e52
commit 8c804ef2d6
3 changed files with 195 additions and 56 deletions

View File

@ -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", &parameter, 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

View File

@ -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;

View File

@ -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;
};