PCI IRQ Routing on a Multiprocessor ACPI SystemUpdated: December 4, 2001
This article describes the requirements for PCI IRQ routing on a multiprocessor Advanced Configuration and Power Interface (ACPI) system. On This Page
IntroductionThe purpose of this article is to describe what is required for PCI interrupt request (IRQ) routing on a multiprocessor (MP) ACPI machine. This article contains the following information:
Caution: This article shows fragments of ASL code used as examples to illustrate various concepts. The examples used are taken primarily from working machines, but is has been necessary in cases to modify the code or to use hypothetical examples. Every effort has been made to present correct ASL code, but please note that the intent of these examples is to illustrate the concepts being discussed and that it will be necessary for you to tailor the ASL code to your specific machine. Please do not take these examples as representing ready-to-compile ASL, but as guides to help make your ASL coding easier.
Note: The Advanced Configuration and Power Interface Specification, Revision 1.0b replaces the Multiprocessor Specification, Version 1.4. The table information contained in the MP specification was carried over into the ACPI Specification where appropriate. However, please note that interrupt routing is now described in the ACPI namespace using ASL. The ACPI Specification can be obtained from http://www.acpi.info/spec.htm#spec ACPI Specification Information for MP SystemsThis section describes the tables and ASL constructs needed to implement ACPI on MP machines. INT_MODEL FieldThe FADT defines the fixed ACPI information that is needed by the operating system as defined in section 5.2.5 of the ACPI specification. Currently, this table provides a field for INT_MODEL, which was intended to be used to report the interrupt mode of the machine. This field is ignored by the Microsoft Windows 2000 and later operating system because the Microsoft Windows 98 operating system only supports programmable interrupt controller (PIC) mode. This forces system manufacturers (OEMs) who want to support Windows 98 to enter a "0" in the INT_MODEL field. This would then preclude using multiple processors on Windows 2000 and later versions. Because the INT_MODEL field in the FADT cannot be dynamically updated while the machine is running, it cannot be used to report the interrupt mode, and therefore it is ignored by Windows 98, and Windows 2000 and later versions. _PIC(x) MethodInclude the _PIC(x) method if you want your MP machine to run with I/O Advanced Programmable Interrupt Controllers (APICs) turned on. This control method is used by Windows 2000 and later versions to report the current interrupt model to the BIOS. This control method returns nothing. The argument passed into the method signifies which interrupt model the operating system has chosen: PIC mode or APIC mode. Note that calling this method is optional for the operating system. If the method is never called, the BIOS should assume PIC mode. It is important that the BIOS save the value passed in by the operating system for later use. _PIC(x) method and interrupt model mode chosen by the operating system:
Example:
SCI_INT FieldThe FADT also contains the SCI_INT field. It reports which physical pin the system control interrupt (SCI) is wired to when the machine is in PIC mode. On most machines, the SCI interrupt is wired to IRQ 9 when the machine is in PIC mode. In APIC mode, SCI_INT is also used, but additional ISA vector override entries in the MAPIC table may be necessary. This will be explained in detail later in this article. The following rules apply to the information provided in this article:
MAPIC Table and ISA Vector OverridesThe MAPIC table is the structure used by ACPI to describe the interrupt routing on a particular machine to the operating system. The MAPIC table is required for any machine using the APIC to route interrupts. The MAPIC table must have the following components:
A local APIC entry for each processor in the system, as described in section 5.2.8.1 of the ACPI specification.
An entry for each I/O APIC in the system, as described in section 5.2.8.2 of the ACPI specification.
One entry for each ISA vector override as necessary, as described in section 5.2.8.3.1 of the ACPI specification.
An example would be a machine that has the ISA Programmable Interrupt Timer (PIT) connected to ISA IRQ 0, but in APIC mode, it triggers I/O APIC source 2. In this case, you need an Interrupt Source Override where the source entry is "0" and the Global System Interrupt Vector is "2." The ISA vector overrides are also necessary in cases in which the ISA interrupts vectors are identity mapped, but the polarity differs. The flags field in the entry allows you to specify this. The following is the format for the interrupt source override field.
Note: You must have an ISA vector override entry for the IRQ mapped to the SCI interrupt if this IRQ is not identity-mapped. This entry will override the value in SCI_INT in FADT. For example, if SCI is connected to IRQ 9 in PIC mode, and IRQ 9 is connected to INTIN11 in APIC mode, there should be a value of 9 entered in SCI_INT in the FADT and an ISA vector override entry mapping IRQ 9 to INTIN11.
An entry for nonmaskable interrupt (NMI).
Structure describing the connectivity of NMI to Local APIC INTI (LINTIN) pins.
For example, if the platform has four processors with the ID 0-3, and NMI is connected LINTIN1 for processors 2 and 3, two entries would be needed in the MAPIC table. _PRT MethodFor reasons that will become clear from the discussion below, the _PRT for an MP machine may be different in PIC mode than in APIC mode, based on how the PCI interrupts are routed on the machine. It is important to return the correct _PRT based on the interrupt model of the machine. You should use the variable set in the _PIC method to return the appropriate _PRT. PIC Mode _PRT MethodThe following is true for the _PRT in PIC mode.
A more detailed example of this is provided later in this article. APIC Mode _PRT MethodThe following is true for the _PRT in APIC mode:
The following snippet of ASL shows an example of how bimodal _PRT can be implemented in a machine:
Name(\PICM,0)
Method(\_PIC,1) { Store(Arg0, \PICM) }
Device(PCI0) {
Name(_HID, EISAID("PNP0A03"))
Name(_ADR, 0x00000000)
Name(_CRS, ResourceTemplate () {(.)})
Method(_PRT) {
If (LEqual(\PICM, Zero) ) {
Return(
Package(
Package(){0x0010ffff, 0, \_SB.LNKB, 0}, // Device 10,Int A
Package(){0x0010ffff, 1, \_SB.LNKC, 0}, // Device 10,Int B
Package(){0x0011ffff, 1, \_SB.LNKD, 0}, // Device 11,Int B
Package(){0x0012ffff, 2, \_SB.LNKB, 0}, // Device 12,Int C
Package(){0x0012ffff, 3, \_SB.LNKC, 0}, // Device 12,Int D
} // end Package
) // end Return
} // end If
Return(
Package(){
Package(){0x0010ffff, 0, 0, 51}, // Slot 10 IntA
Package(){0x0010ffff, 1, IR1, 0}, // Slot 10 IntB
Package(){0x0011ffff, 1, 0, 51}, // Slot 11 IntB
Package(){0x0011ffff, 2, IR1, 0}, // Slot 11 IntC
Package(){0x0012ffff, 2, 0, 51}, // Slot 12 IntC
Package(){0x0012ffff, 3, IR1, 0} // Slot 12 IntD
}
)
} // end Method
Device(IR1) {
Name(_HID, EISAID("PNP0C0F"))
Name(_UID, 1)
Name(_PRS, ResourceTemplate() {
Interrupt (ResourceConsumer, ... ) (23,39,55) } )
Name(_DIS) { }
Name(_CRS) { }
Name(_SRS) { }
}
Device(LNKB) {
Name(_HID, EISAID("PNP0C0F"))
Name(_UID, 2)
Name(_PRS, ResourceTemplate() {
IRQ (Level, ActiveLow, Shared, , ... ) {5,7} } )
Name(_DIS) { }
Name(_CRS) { }
Name(_SRS) { }
}
Device(LNKC) {
Name(_HID, EISAID("PNP0C0F"))
Name(_UID, 2)
Name(_PRS, ResourceTemplate() {
IRQ (Level, ActiveLow, Shared, , ... ) {3,11} } )
Name(_DIS) { }
Name(_CRS) { }
Name(_SRS) { }
}
Device(LNKD) {
Name(_HID, EISAID("PNP0C0F"))
Name(_UID, 3)
Name(_PRS, ResourceTemplate() {
IRQ (Level, ActiveLow, Shared, , ... ) {8,15} } )
Name(_DIS) { }
Name(_CRS) { }
Name(_SRS) { }
}
Configuring an MP System to Run ACPIEarlier sections of this article describe the sections of the ACPI Specification relevant to MP machines. This material should provide the background needed to implement the appropriate ASL and tables for an MP machine to run ACPI. The following section discusses some of the issues crucial to running ACPI successfully on an MP machine. The first, and most important, step is to understand the hardware details of the machine in question. As stated earlier, the details of the interrupt routing on the machine must be clearly understood. Once you have this information, ask the following questions to ensure an MP machine is correctly running ACPI: How is IRQ 0 connected in APIC mode? In most systems, IRQ 0 is not connected to INTIN0, but to INTIN2 on the I/O APIC. If this is the case, an ISA vector override must be added to the MAPIC table to describe the mapping of IRQ 0. If, however, IRQ 0 is connected to INTIN0, no action is necessary. Is the real-time clock (RTC) IRQ (IRQ 8) active high? The active level of this signal must be examined at the input to the I/O APIC. That is, be sure to account for any inversions along the way to the I/O APIC. If the signal is active high, no action is necessary. If the signal is active low, an ISA vector override must be added to the MAPIC table to specify the polarity. What interrupt does the SCI map to in APIC mode? If the polarity and the mapping of the IRQ for the SCI are the same in APIC mode as in PIC mode, no action is necessary. Otherwise, an ISA vector override must be added to the MAPIC table to provide this information to the operating system. Once again, be careful to note the polarity of the interrupt at the input to the I/O APIC. Are the PCI interrupts active low at the I/O APIC?
Notice that in the following discussion, the _PRT refers to the APIC part of the _PRT described earlier in this article.
Notice that the preceding four issues represent only a few of the details that designers need to pay attention to. It is still necessary to implement all the MP-related ACPI structures described earlier in this article. Working ASL from an MP System Running ACPIThis section contains ASL excerpts that have been taken from a working MP machine running Windows 2000 with ACPI enabled. The ASL code is presented courtesy of IBM Corporation. Only the parts of the ASL that are relevant to the topics discussed in this article are reproduced. The code shown here has been compiled and IBM engineers have successfully run ACPI on Windows 2000 on this machine with more than one processor. Special thanks to IBM for making this code available for this article. Note: In the following example, bimodal link nodes are used instead of bimodal _PRTs, that is, the link nodes use either standard interrupt descriptors (IRQ macro) or extended interrupt descriptors (Interrupt macro) based on the mode in which the machine is executing.
//
// This code snippet is provided for illustrative purposes only, in order to demonstrate
// how ACPI Methods may be used to support dynamically routable interrupts
//
Scope (\) {
. . .
Name (PICF , 0) // Global PIC Flag indicating PIC\APIC mode
. . .
Method ( _PIC,1) { // Set Global PIC/APIC flag
Store( Arg0 , PICF)
}
. . .
} // end of scope
Scope (\_SB.PCI0) {
Name(PRSP, // _PRS for PIC mode
ResourceTemplate() {
// IRQ descriptor, level, low
IRQ(Level, ActiveLow, Shared) {
5, 7, 10, 11, 14, 15 // Possible IRQs
}
}
)
Name(PRSA,
ResourceTemplate() { // _PRS for APIC Mode
// Extended IRQ descriptor. Necessary to indicate Level &
// Active High properties
Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {
5, 7, 10, 11, 14, 15
}
}
)
// Typical PCI Interrupt Link Node:
Device(LK6A) { // PCI Slot 6 / INTA
Name(_HID, EISAID("PNP0C0F")) // PCI Interrupt Link Device
Name(_UID, 61)
Method(_STA, 0) {
If (LEqual (MX6A, 2)){ // 0010b indicates IMUX is disabled
Return(0x09) // Present, disabled, no UI
}
Else {
Return(0x0B) // Present, enabled, functioning
}
}
Method(_PRS,0) {
If (LNot (PICF)) { // Depending on whether we are
return(PRSP) // running in PIC or APIC mode
} Else { // select appropriate _PRS
return(PRSA)
}
}
Method(_DIS) { // We disable INT by writing 0010b
Store(2, MX6A) // to corresponding IMUX
}
Method(_CRS, 0) {
If (LNot (PICF)) {
Name( B6AP,
ResourceTemplate() {
IRQ(Level, ActiveLow, Shared, I6AP) {}
}
)
CreateWordField( B6AP, I6AP._INT, M6AP) //Interrupt Mask
If (LNotEqual(MX6A, 0x2)) {
Store( ShiftLeft(One, MX6A), M6AP)
}
Return(B6AP)
} Else {
Name( B6AA,
ResourceTemplate() {
Interrupt(ResourceConsumer,Level,ActiveHigh,Shared,,,I6AA) {0}
}
)
CreateByteField( B6AA, I6AA._INT, M6AA) //Interrupt Mask
If (LNotEqual(MX6A , 0x2)) {
Store(MX6A, M6AA)
}
Return(B6AA)
}
}
Method(_SRS, 1) {
If (LNot (PICF)) {
CreateWordField( Arg0, 0x01, P6A2 )
FindSetRightBit( P6A2, Local0)
Store( Decrement( Local0 ), MX6A )
TRIG( Local0) // Set Triggering
} Else {
CreateByteField( Arg0, 0x05, A6A2 )
Store( A6A2, MX6A)
TRIG(A6A2)
}
}
}
. . .
. . .
Device(LK5D) {
Name(_HID, EISAID("PNP0C0F"))
Name(_UID, 54)
Method(_STA, 0) {
If (LEqual (MX5D, 2)) {
Return(0x09) // Present, disabled, no UI
}
Else {
Return(0x0B) // Present, enabled, functioning
}
}
Method(_PRS,0) {
If (LNot (PICF)) {
return(PRSP)
} Else {
return(PRSA)
}
}
Method(_DIS) {
// 0010: no interrupt
Store(2, MX5D)
}
Method(_CRS, 0) {
If (LNot (PICF)) {
Name( B5DP,
ResourceTemplate() {
IRQ(Level, ActiveLow, Shared, I5DP) {}
}
)
CreateWordField( B5DP, I5DP._INT, M5DP) //Interrupt Mask
If (LNotEqual(MX5D , 0x2)) {
Store( ShiftLeft( One, MX5D), M5DP)
}
Return(B5DP)
} Else {
Name( B5DA,
ResourceTemplate() {
Interrupt(ResourceConsumer,Level,ActiveHigh,Shared,,,I5DA){0}
}
)
CreateByteField( B5DA, I5DA._INT, M5DA) //Interrupt Mask
If (LNotEqual(MX5D , 0x2)) {
Store(MX5D, M5DA)
}
Return(B5DA)
}
}
Method(_SRS, 1) {
If (LNot (PICF)) {
CreateWordField( Arg0, 0x01, P5D2 )
FindSetRightBit( P5D2, Local0)
Store( Decrement( Local0 ), MX5D )
TRIG( Local0) // Set Triggering
} Else {
CreateByteField( Arg0, 0x05, A5D2 )
Store( A5D2, MX5D)
TRIG(A5D2)
}
}
}
. . .
//
// The Triggering Imux is set up the following way:
//
// Offset written to
// Address Port 0xEA Writing to Data Port 0xEB
// Offset Controls triggering of:
// ----------------------+-----------------------------------------
// 10h bits 0-3 = IRQ 3 triggering
// bits 4-7 = IRQ 4 triggering
// 11h bits 0-3 = IRQ 5 triggering
// bits 4-7 = IRQ 7 triggering
// 12h bits 0-3 = IRQ 9 triggering
// bits 4-7 = IRQ 10 triggering
// 13h bits 0-3 = IRQ 11 triggering
// bits 4-7 = IRQ 12 triggering
// 14h bits 0-3 = IRQ 14 triggering
// bits 4-7 = IRQ 15 triggering
//
// A value of 0010b written to the Trig. Imux Data Port sets it's
// corresponding IRQ to Level, while the table below indicates the
// value necessary to set the IRQ to Edge
// Offset : Value for Edge
// 10h : 43h => IRQ 4 and 3
// 11h : 75h => IRQ 7 and 5
// 12h : A9h => IRQ 10 and 9
// 13h : CBh => IRQ 12 and 11
// 14h : FEh => IRQ 15 and 14
//
// At chipset initialization of the Imux, POST sets the values listed above
// which sets each of the corresponding IRQs to an ISA edge signal. During
// ACPI, Service Processor, and PCI IRQ assignment (which are all level
// signals) we change the corresponding nibble in Triggering Imux to a
// value of 2h which changes the corresponding Imux output to level.
//
// So, to change IRQ 5 and 7 to level, simply set its corresponding
// location in the Triggering Imux to a 2:
// 0xEA <- 0x11
// 0xEB <- 0x22
// To change a level IRQ 5 and 7 back to edge simply set the IRQ's
// corresponding location in Triggering
// Imux to the value of the interrupt:
// 0xEA <- 0x11
// 0xEB <- 0x75
//
Method(TRIG,1) {
If (LEqual (5, Arg0))
{Store(2, MT05)}
Else {If (LEqual (7, Arg0))
{Store(2, MT07)}
Else {If (LEqual (10, Arg0))
{Store(2,MT10)}
Else {If (LEqual (11, Arg0))
{Store(2,MT11)}
Else {If (LEqual (14, Arg0))
{Store(2,MT14)}
Else {If (LEqual (15, Arg0))
{Store(2,MT15)}
}
}
-
}
}
-
}
-
}
. . .
// These Interrupt Multiplexers (IMUXs) take the PCI interrupts from each
// slot and individually route each of them to a particular interrupt
// controller input
// We write an address in the IMUX address port (0xEA) to access a
// particular MUX. We then read/write the corresponding IMUX data port
// 0xEB) to access it's routing info.
//
// IMUX Address Port IMUX Data Port
// I/O Address 0xEA I/O Address 0xEB
// ----------------------+--------------------------------------------
// 00 Bits 0-3 = muxing of PCI Card Slot 6 IntA
// Bits 0-3 = muxing of PCI Card Slot 6 IntB
// 01 Bits 0-3 = muxing of PCI Card Slot 6 IntC
// Bits 0-3 = muxing of PCI Card Slot 6 IntD
//
// 02 Bits 0-3 = muxing of PCI Card Slot 5 IntA
// Bits 0-3 = muxing of PCI Card Slot 5 IntB
// 03 Bits 0-3 = muxing of PCI Card Slot 5 IntC
// Bits 0-3 = muxing of PCI Card Slot 5 IntD
. . .
// IMUX Data Bits Connected to Interrupt
// read/written to 0xEB
// -----------------------+---------------------------
// . . .
// 0010 NO INTERRUPT - DISCONNECT
// 0011 IRQ-3 (PIC) / INTIN3 (IOAPIC)
// 0100 IRQ-4 (PIC) / INTIN4 (IOAPIC)
// . . .
//
// PCI +------+ +-----+
// Device ->| IMUX +-+->| PIC +----+
// INT +------+ | +-----+ | +----+
// | +-->|CPUs|
// | +--------+ | +----+
// +->| IOAPIC |-+
// +--------+
//
//
// Define Interrupt IMUXs (in I/O Space)
// IMUX Address Port: 0xEA
// Data Port : 0xEB
//
OperationRegion(IMUX, SystemIO, 0xEA, 2)
Field(IMUX, ByteAcc, NoLock, Preserve) {
MXAD, 8,
MXDT, 8
}
IndexField(MXAD, MXDT, ByteAcc, NoLock, Preserve) { //
//
MX6A, 4, MX6B, 4, // EA: 00
MX6C, 4, MX6D, 4, // 01
MX5A, 4, MX5B, 4, // 02
MX5C, 4, MX5D, 4, // 03
MX4A, 4, MX4B, 4, // 04
MX4C, 4, MX4D, 4, // 05
. . .
, 4, , 4, // 0F
MT03, 4, MT04, 4, // MUX3: 10 - 14 controls int trigger
MT05, 4, MT07, 4, //
MT09, 4, MT10, 4, //
MT11, 4, MT12, 4, //
MT14, 4, MT15, 4 //
}
}
Scope (\_SB.PCI0){ // Primary PCI bus 0
Name(_PRT ,Package(){ // Single _PRT used for both PIC\APIC Mode
. . .
// Device#5 / AD16 / Slot#5:
Package() {0x0005ffff, 0, LK5A , 0} , // INT A
Package() {0x0005ffff, 1, LK5B , 0} , // INT B
Package() {0x0005ffff, 2, LK5C , 0} , // INT C
Package() {0x0005ffff, 3, LK5D , 0} , // INT D
// Device#6 / AD17 / Slot#6:
Package() {0x0006ffff, 0, LK6A , 0} , // INT A
Package() {0x0006ffff, 1, LK6B , 0} , // INT B
Package() {0x0006ffff, 2, LK6C , 0} , // INT C
Package() {0x0006ffff, 3, LK6D , 0} , // INT D
. . .
) //End _PRT
} // end of scope
|
|

