OvmfPkg: PlatformBdsLib: Dynamic PCI Interrupt Line register setup

Remove hard-coded list of PCI devices for which the Interrupt Line
register is initialized. Instead, provide a "visitor" function to
initialize the register only for present and applicable PCI devices.

At this time, we match the behavior of SeaBIOS (file src/fw/pciinit.c,
functions *_pci_slot_get_irq() and "map the interrupt" block from
pci_bios_init_device()).

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Gabriel Somlo <somlo@cmu.edu>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16398 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Gabriel Somlo 2014-11-17 19:09:12 +00:00 committed by lersek
parent 9840b1299d
commit 5218c27950
1 changed files with 140 additions and 44 deletions

View File

@ -25,7 +25,20 @@ EFI_EVENT mEfiDevPathEvent;
VOID *mEmuVariableEventReg; VOID *mEmuVariableEventReg;
EFI_EVENT mEmuVariableEvent; EFI_EVENT mEmuVariableEvent;
BOOLEAN mDetectVgaOnly; BOOLEAN mDetectVgaOnly;
UINT16 mHostBridgeDevId;
//
// Table of host IRQs matching PCI IRQs A-D
// (for configuring PCI Interrupt Line register)
//
CONST UINT8 PciHostIrqs[] = {
0x0a, 0x0a, 0x0b, 0x0b
};
//
// Array Size macro
//
#define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0]))
// //
// Type definitions // Type definitions
@ -716,18 +729,136 @@ Returns:
} }
/**
Configure PCI Interrupt Line register for applicable devices
Ported from SeaBIOS, src/fw/pciinit.c, *_pci_slot_get_irq()
@param[in] Handle - Handle of PCI device instance
@param[in] PciIo - PCI IO protocol instance
@param[in] PciHdr - PCI Header register block
@retval EFI_SUCCESS - PCI Interrupt Line register configured successfully.
**/
EFI_STATUS
EFIAPI
SetPciIntLine (
IN EFI_HANDLE Handle,
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN PCI_TYPE00 *PciHdr
)
{
EFI_DEVICE_PATH_PROTOCOL *DevPathNode;
UINTN RootSlot;
UINTN Idx;
UINT8 IrqLine;
EFI_STATUS Status;
Status = EFI_SUCCESS;
if (PciHdr->Device.InterruptPin != 0) {
DevPathNode = DevicePathFromHandle (Handle);
ASSERT (DevPathNode != NULL);
//
// Compute index into PciHostIrqs[] table by walking
// the device path and adding up all device numbers
//
Status = EFI_NOT_FOUND;
RootSlot = 0;
Idx = PciHdr->Device.InterruptPin - 1;
while (!IsDevicePathEnd (DevPathNode)) {
if (DevicePathType (DevPathNode) == HARDWARE_DEVICE_PATH &&
DevicePathSubType (DevPathNode) == HW_PCI_DP) {
Idx += ((PCI_DEVICE_PATH *)DevPathNode)->Device;
//
// Unlike SeaBIOS, which starts climbing from the leaf device
// up toward the root, we traverse the device path starting at
// the root moving toward the leaf node.
// The slot number of the top-level parent bridge is needed for
// Q35 cases with more than 24 slots on the root bus.
//
if (Status != EFI_SUCCESS) {
Status = EFI_SUCCESS;
RootSlot = ((PCI_DEVICE_PATH *)DevPathNode)->Device;
}
}
DevPathNode = NextDevicePathNode (DevPathNode);
}
if (EFI_ERROR (Status)) {
return Status;
}
if (RootSlot == 0) {
DEBUG((
EFI_D_ERROR,
"%a: PCI host bridge (00:00.0) should have no interrupts!\n",
__FUNCTION__
));
ASSERT (FALSE);
}
//
// Final PciHostIrqs[] index calculation depends on the platform
// and should match SeaBIOS src/fw/pciinit.c *_pci_slot_get_irq()
//
switch (mHostBridgeDevId) {
case INTEL_82441_DEVICE_ID:
Idx -= 1;
break;
case INTEL_Q35_MCH_DEVICE_ID:
//
// SeaBIOS contains the following comment:
// "Slots 0-24 rotate slot:pin mapping similar to piix above, but
// with a different starting index - see q35-acpi-dsdt.dsl.
//
// Slots 25-31 all use LNKA mapping (or LNKE, but A:D = E:H)"
//
if (RootSlot > 24) {
//
// in this case, subtract back out RootSlot from Idx
// (SeaBIOS never adds it to begin with, but that would make our
// device path traversal loop above too awkward)
//
Idx -= RootSlot;
}
break;
default:
ASSERT (FALSE); // should never get here
}
Idx %= ARRAY_SIZE (PciHostIrqs);
IrqLine = PciHostIrqs[Idx];
//
// Set PCI Interrupt Line register for this device to PciHostIrqs[Idx]
//
Status = PciIo->Pci.Write (
PciIo,
EfiPciIoWidthUint8,
PCI_INT_LINE_OFFSET,
1,
&IrqLine
);
}
return Status;
}
VOID VOID
PciAcpiInitialization ( PciAcpiInitialization (
) )
{ {
UINT16 HostBridgeDevId;
UINTN Pmba; UINTN Pmba;
// //
// Query Host Bridge DID to determine platform type // Query Host Bridge DID to determine platform type
// //
HostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId); mHostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);
switch (HostBridgeDevId) { switch (mHostBridgeDevId) {
case INTEL_82441_DEVICE_ID: case INTEL_82441_DEVICE_ID:
Pmba = POWER_MGMT_REGISTER_PIIX4 (0x40); Pmba = POWER_MGMT_REGISTER_PIIX4 (0x40);
// //
@ -754,55 +885,20 @@ PciAcpiInitialization (
break; break;
default: default:
DEBUG ((EFI_D_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n", DEBUG ((EFI_D_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
__FUNCTION__, HostBridgeDevId)); __FUNCTION__, mHostBridgeDevId));
ASSERT (FALSE); ASSERT (FALSE);
return; return;
} }
//
// Initialize PCI_INTERRUPT_LINE for applicable present PCI devices
//
VisitAllPciInstances (SetPciIntLine);
// //
// Set ACPI SCI_EN bit in PMCNTRL // Set ACPI SCI_EN bit in PMCNTRL
// //
IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0); IoOr16 ((PciRead32 (Pmba) & ~BIT0) + 4, BIT0);
//
// Initialize PCI_INTERRUPT_LINE for commonly encountered devices and slots
//
// FIXME: This should instead be accomplished programmatically by
// ennumerating all PCI devices present in the system and
// computing PCI_INTERRUPT_LINE from PCI_INTERRUPT_PIN, the
// slot/position of the device, and the available host IRQs
// (for an example, see SeaBIOS pci_bios_init_devices() in
// src/fw/pciinit.c)
//
switch (HostBridgeDevId) {
case INTEL_82441_DEVICE_ID:
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 2, 0x3c), 0x0b); // usb (northbr.)
PciWrite8 (PCI_LIB_ADDRESS (0, 1, 3, 0x3c), 0x0a); // acpi (northbr.)
PciWrite8 (PCI_LIB_ADDRESS (0, 3, 0, 0x3c), 0x0b);
PciWrite8 (PCI_LIB_ADDRESS (0, 4, 0, 0x3c), 0x0b);
PciWrite8 (PCI_LIB_ADDRESS (0, 5, 0, 0x3c), 0x0a);
PciWrite8 (PCI_LIB_ADDRESS (0, 6, 0, 0x3c), 0x0a);
PciWrite8 (PCI_LIB_ADDRESS (0, 7, 0, 0x3c), 0x0b);
PciWrite8 (PCI_LIB_ADDRESS (0, 8, 0, 0x3c), 0x0b);
break;
case INTEL_Q35_MCH_DEVICE_ID:
PciWrite8 (PCI_LIB_ADDRESS (0, 2, 0, 0x3c), 0x0b);
PciWrite8 (PCI_LIB_ADDRESS (0, 3, 0, 0x3c), 0x0b);
PciWrite8 (PCI_LIB_ADDRESS (0, 4, 0, 0x3c), 0x0a);
PciWrite8 (PCI_LIB_ADDRESS (0, 5, 0, 0x3c), 0x0a);
PciWrite8 (PCI_LIB_ADDRESS (0, 6, 0, 0x3c), 0x0b);
PciWrite8 (PCI_LIB_ADDRESS (0, 7, 0, 0x3c), 0x0b);
PciWrite8 (PCI_LIB_ADDRESS (0, 8, 0, 0x3c), 0x0a);
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1d, 0, 0x3c), 0x0a); // uhci1
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1d, 1, 0x3c), 0x0a); // uhci2
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1d, 2, 0x3c), 0x0b); // uhci3
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1d, 7, 0x3c), 0x0b); // ehci1
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 2, 0x3c), 0x0a); // ahci (northbr.)
PciWrite8 (PCI_LIB_ADDRESS (0, 0x1f, 3, 0x3c), 0x0a); // smbus (northbr.)
break;
default:
ASSERT (FALSE); // should never be reached
}
} }