* Separate out matching PCI devices and updating their info. They will be called

independently. Matching will happen first to enumerate and match all devices.
  Updating will happen after configuring all link devices, so once we know the
  final GSI of all entries. The info about matching functions is kept in a
  bitmask in the table entry.
* Move routing table entry handling out of the loop into it's own function to
  faciliate early returns.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41399 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2011-05-09 13:26:41 +00:00
parent 2800b06462
commit cbfddcd6e8
2 changed files with 110 additions and 71 deletions

View File

@ -30,6 +30,8 @@ const char* kACPIPciExpressRootName = "PNP0A08";
// when querying for the standard PCI root. This is due to the compatible ID // when querying for the standard PCI root. This is due to the compatible ID
// fields in ACPI. TODO: Query both/the correct root device. // fields in ACPI. TODO: Query both/the correct root device.
// TODO: as per PCI 3.0, the PCI module hardcodes it in various places as well.
static const uint8 kMaxPCIFunctionCount = 8;
irq_descriptor::irq_descriptor() irq_descriptor::irq_descriptor()
: :
@ -68,9 +70,10 @@ print_irq_routing_entry(const irq_routing_entry& entry)
else else
dprintf(" source %p %lu;", entry.source, entry.source_index); dprintf(" source %p %lu;", entry.source, entry.source_index);
dprintf(" pci %u:%u pin %u; bios irq: %u; gsi %u; config 0x%02x\n", dprintf(" pci %u:%u pin %u func mask %lx; bios irq: %u; gsi %u;"
entry.pci_bus, entry.pci_device, entry.pin + 1, entry.bios_irq, " config 0x%02x\n", entry.pci_bus, entry.pci_device, entry.pin + 1,
entry.irq, entry.polarity | entry.trigger_mode); entry.pci_function_mask, entry.bios_irq, entry.irq,
entry.polarity | entry.trigger_mode);
} }
@ -84,7 +87,25 @@ print_irq_routing_table(IRQRoutingTable* table)
static status_t static status_t
update_pci_info_for_entry(pci_module_info* pci, irq_routing_entry& entry) update_pci_info_for_entry(pci_module_info* pci, const irq_routing_entry& entry)
{
uint32 updateCount = 0;
for (uint8 function = 0; function < kMaxPCIFunctionCount; function++) {
if ((entry.pci_function_mask & (1 << function)) == 0)
continue;
if (pci->update_interrupt_line(entry.pci_bus, entry.pci_device,
function, entry.irq) == B_OK) {
updateCount++;
}
}
return updateCount > 0 ? B_OK : B_ENTRY_NOT_FOUND;
}
static status_t
fill_pci_info_for_entry(pci_module_info* pci, irq_routing_entry& entry)
{ {
// check the base device at function 0 // check the base device at function 0
uint8 headerType = pci->read_pci_config(entry.pci_bus, entry.pci_device, 0, uint8 headerType = pci->read_pci_config(entry.pci_bus, entry.pci_device, 0,
@ -104,13 +125,9 @@ update_pci_info_for_entry(pci_module_info* pci, irq_routing_entry& entry)
// we have a device, check how many functions we need to iterate // we have a device, check how many functions we need to iterate
uint8 functionCount = 1; uint8 functionCount = 1;
if ((headerType & PCI_multifunction) != 0) { if ((headerType & PCI_multifunction) != 0)
functionCount = 8; functionCount = kMaxPCIFunctionCount;
// TODO: as per PCI 3.0, the PCI module hardcodes it in various
// places as well...
}
uint32 updateCount = 0;
for (uint8 function = 0; function < functionCount; function++) { for (uint8 function = 0; function < functionCount; function++) {
// check for device presence by looking for a valid vendor // check for device presence by looking for a valid vendor
uint16 vendorId = pci->read_pci_config(entry.pci_bus, entry.pci_device, uint16 vendorId = pci->read_pci_config(entry.pci_bus, entry.pci_device,
@ -137,13 +154,10 @@ update_pci_info_for_entry(pci_module_info* pci, irq_routing_entry& entry)
entry.pci_device, function, PCI_interrupt_line, 1); entry.pci_device, function, PCI_interrupt_line, 1);
} }
if (pci->update_interrupt_line(entry.pci_bus, entry.pci_device, entry.pci_function_mask |= 1 << function;
function, entry.irq) == B_OK) {
updateCount++;
}
} }
return updateCount > 0 ? B_OK : B_ENTRY_NOT_FOUND; return entry.pci_function_mask != 0 ? B_OK : B_ENTRY_NOT_FOUND;
} }
@ -190,6 +204,81 @@ evaluate_integer(acpi_module_info* acpi, acpi_handle handle,
} }
static status_t
handle_routing_table_entry(acpi_module_info* acpi, pci_module_info* pci,
const acpi_pci_routing_table* acpiTable, const pci_address& pciAddress,
irq_routing_entry& irqEntry)
{
bool noSource = acpiTable->Source[0] == '\0';
// The above line would be correct according to specs...
noSource = acpiTable->SourceIndex != 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.
status_t status;
acpi_handle source;
if (!noSource) {
status = acpi->get_handle(NULL, acpiTable->Source, &source);
if (status != B_OK) {
dprintf("failed to get handle to link device\n");
return status;
}
}
memset(&irqEntry, 0, sizeof(irq_routing_entry));
irqEntry.device_address = acpiTable->Address;
irqEntry.pin = acpiTable->Pin;
irqEntry.source = noSource ? NULL : source;
irqEntry.source_index = acpiTable->SourceIndex;
irqEntry.pci_bus = pciAddress.bus;
irqEntry.pci_device = (uint8)(acpiTable->Address >> 16);
status = fill_pci_info_for_entry(pci, irqEntry);
if (status != 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 a match.
#ifdef TRACE_PRT
dprintf("didn't find a matching PCI device for irq entry:\n");
print_irq_routing_entry(irqEntry);
#endif
return status;
}
// 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 = configure_link_device(acpi, source, irqDescriptor);
if (status != B_OK) {
dprintf("failed to configure link device\n");
return status;
}
irqEntry.irq = irqDescriptor.irq;
irqEntry.polarity = irqDescriptor.polarity;
irqEntry.trigger_mode = irqDescriptor.trigger_mode;
}
status = update_pci_info_for_entry(pci, irqEntry);
if (status != B_OK) {
#ifdef TRACE_PRT
dprintf("failed to update interrupt_line info for entry:\n");
print_irq_routing_entry(irqEntry);
#endif
return status;
}
return B_OK;
}
static void static void
read_irq_routing_table_recursive(acpi_module_info* acpi, pci_module_info* pci, read_irq_routing_table_recursive(acpi_module_info* acpi, pci_module_info* pci,
acpi_handle device, const pci_address& parentAddress, acpi_handle device, const pci_address& parentAddress,
@ -238,64 +327,13 @@ read_irq_routing_table_recursive(acpi_module_info* acpi, pci_module_info* pci,
pciAddress.bus = secondaryBus; pciAddress.bus = secondaryBus;
} }
irq_routing_entry irqEntry;
acpi_pci_routing_table* acpiTable = (acpi_pci_routing_table*)buffer.pointer; acpi_pci_routing_table* acpiTable = (acpi_pci_routing_table*)buffer.pointer;
while (acpiTable->Length) { while (acpiTable->Length) {
acpi_handle source; irq_routing_entry irqEntry;
status = handle_routing_table_entry(acpi, pci, acpiTable, pciAddress,
bool noSource = acpiTable->Source[0] == '\0'; irqEntry);
// The above line would be correct according to specs... if (status == B_OK)
noSource = acpiTable->SourceIndex != 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 ? NULL : source;
irqEntry.source_index = acpiTable->SourceIndex;
irqEntry.pci_bus = pciAddress.bus;
irqEntry.pci_device = (uint8)(acpiTable->Address >> 16);
irqEntry.bios_irq = 0;
// 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 = configure_link_device(acpi, source, irqDescriptor);
if (status != B_OK) {
dprintf("failed to configure 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 a match.
#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
} else
table->PushBack(irqEntry); table->PushBack(irqEntry);
}
}
acpiTable = (acpi_pci_routing_table*)((uint8*)acpiTable acpiTable = (acpi_pci_routing_table*)((uint8*)acpiTable
+ acpiTable->Length); + acpiTable->Length);

View File

@ -23,6 +23,7 @@ struct irq_routing_entry {
// PCI bus_manager connection // PCI bus_manager connection
uint8 pci_bus; uint8 pci_bus;
uint8 pci_device; uint8 pci_device;
uint32 pci_function_mask;
// Distilled configuration info // Distilled configuration info
uint8 irq; // Global System Interrupt (GSI) uint8 irq; // Global System Interrupt (GSI)