Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

646 lines
16 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Copyright (c) 1992,1993,1994,1995,1996 Digital Equipment Corporation
Module Name:
pciir.c
Abstract:
The module provides the interrupt support for the Lego's PCI
interrupts.
Author:
James Livingston 2-May-1994
Revision History:
Janet Schneider (Digital) 27-July-1995
Added support for the Noritake.
Gene Morgan (Digital) 28-Oct-1995
Initial version for Lego. Adapted from Mikasa/Noritake.
Gene Morgan 15-Apr-1996
Fix PICMG-mode initialization.
--*/
#include "halp.h"
//
// Define external function prototypes
//
UCHAR
HalpAcknowledgePciInterrupt(
PVOID ServiceContext
);
//
// Import save area for PCI interrupt mask register.
//
USHORT HalpLegoPciInterruptMasterMask;
// Cache current contents of interrupt mask registers
//
USHORT HalpLegoPciInterruptMask[4];
//
// PCI Interrupt control -- defined in I/O mapping module
//
extern PVOID HalpLegoPciInterruptConfigQva;
extern PVOID HalpLegoPciInterruptMasterQva;
extern PVOID HalpLegoPciInterruptRegisterBaseQva;
extern PVOID HalpLegoPciInterruptRegisterQva[];
extern PVOID HalpLegoPciIntMaskRegisterQva[];
//
// Globals for conveying Cpu and Backplane type
//
extern BOOLEAN HalpLegoCpu;
extern BOOLEAN HalpLegoBackplane;
extern ULONG HalpLegoCpuType;
extern ULONG HalpLegoBackplaneType;
extern UCHAR HalpLegoFeatureMask;
extern ULONG HalpLegoPciRoutingType;
VOID
DbgDumpIntRegs(
VOID
)
{
#if DBG
LEGO_PCI_INT_MASTER MasterRegister;
USHORT MaskReg[4];
USHORT IntReg[4];
int i;
MasterRegister.All = READ_REGISTER_USHORT((PUSHORT)HalpLegoPciInterruptMasterQva);
MaskReg[0] = READ_REGISTER_USHORT( (PUSHORT) HalpLegoPciIntMaskRegisterQva[0]);
MaskReg[1] = READ_REGISTER_USHORT( (PUSHORT) HalpLegoPciIntMaskRegisterQva[1]);
MaskReg[2] = READ_REGISTER_USHORT( (PUSHORT) HalpLegoPciIntMaskRegisterQva[2]);
MaskReg[3] = READ_REGISTER_USHORT( (PUSHORT) HalpLegoPciIntMaskRegisterQva[3]);
IntReg[0] = READ_REGISTER_USHORT( (PUSHORT) HalpLegoPciInterruptRegisterQva[0]);
IntReg[1] = READ_REGISTER_USHORT( (PUSHORT) HalpLegoPciInterruptRegisterQva[1]);
IntReg[2] = READ_REGISTER_USHORT( (PUSHORT) HalpLegoPciInterruptRegisterQva[2]);
IntReg[3] = READ_REGISTER_USHORT( (PUSHORT) HalpLegoPciInterruptRegisterQva[3]);
DbgPrint ("\nMaster: %04X\n",MasterRegister.All);
DbgPrint (" Mask: %04X %04X %04X %04X\n", MaskReg[0], MaskReg[1], MaskReg[2], MaskReg[3]);
DbgPrint ("IntReg: %04X %04X %04X %04X\n", IntReg[0], IntReg[1], IntReg[2], IntReg[3]);
#endif
}
VOID
HalpInitializePciInterrupts(
VOID
)
/*++
Routine Description:
This routine initializes the Lego PCI interrupts.
Arguments:
None.
Return Value:
None.
--*/
{
LEGO_PCI_INT_CONFIG ConfigRegister;
LEGO_PCI_INT_MASTER MasterRegister;
#if DBG
//
// Dump current contents of master and config interrupt registers
//
ConfigRegister.All = READ_REGISTER_USHORT((PUSHORT)HalpLegoPciInterruptConfigQva);
MasterRegister.All = READ_REGISTER_USHORT((PUSHORT)HalpLegoPciInterruptMasterQva);
DbgPrint("PCI-INTACC[config:%04x,master:%04x]\n",
ConfigRegister.All, MasterRegister.All);
#endif
if (HalpLegoPciRoutingType != PCI_INTERRUPT_ROUTING_FULL) {
//
// disable interrupt accelerator and return
//
MasterRegister.All = READ_REGISTER_USHORT((PUSHORT)HalpLegoPciInterruptMasterQva); //[wem] added PEFT
MasterRegister.InterruptMode = 0; // MODE - select SIO routing
MasterRegister.InterruptEnable = 0; // valid iff MODE==0
MasterRegister.IntRegMaskEnable = 0;
MasterRegister.IntMask = 0;
WRITE_REGISTER_USHORT( (PUSHORT)HalpLegoPciInterruptMasterQva,
MasterRegister.All );
MasterRegister.All = READ_REGISTER_USHORT((PUSHORT)HalpLegoPciInterruptMasterQva);
#if DBG
DbgPrint("PCI-INTACC[config:%04x,master:%04x]\n",
ConfigRegister.All, MasterRegister.All);
#endif
MasterRegister.InterruptMode = 0; // MODE - select SIO routing
MasterRegister.InterruptEnable = 0; // valid iff MODE==0
MasterRegister.IntRegMaskEnable = 0;
MasterRegister.IntMask = 0;
WRITE_REGISTER_USHORT( (PUSHORT)HalpLegoPciInterruptMasterQva,
MasterRegister.All );
#if DBG
MasterRegister.All = READ_REGISTER_USHORT((PUSHORT)HalpLegoPciInterruptMasterQva);
DbgPrint("PCI-INTACC[config:%04x,master:%04x]\n",
ConfigRegister.All, MasterRegister.All);
#endif
return;
}
//
// Initialize the Lego PCI interrupts. There's a master
// interrupt and mask register, plus individual interrupt
// and mask registers for each physical slot.
//
// 1. Write base address of interrupt registers.
// 2. Write MODE, MSKEN, and MINT[D:A].
// 3. Set all mask bits to "disabled".
//
ConfigRegister.All = 0;
ConfigRegister.IntRegisterBaseAddr = PCI_INTERRUPT_BASE_REGISTER >> 4;
WRITE_REGISTER_USHORT( (PUSHORT)HalpLegoPciInterruptConfigQva,
ConfigRegister.All );
//
// Set interrupt accelerator mode
// Mask interrupts until interrupt registers are setup.
//
// Note: The interrupt mask in the master register is always clear
//
MasterRegister.All = 0;
MasterRegister.InterruptMode = 1; // MODE
MasterRegister.InterruptEnable = 0; // valid iff MODE==0
MasterRegister.IntRegMaskEnable = 1; // valid iff MODE==1
#if 0 //[wem] remove PEFT
MasterRegister.IntMask = HalpLegoPciInterruptMasterMask;
#endif
WRITE_REGISTER_USHORT( (PUSHORT)HalpLegoPciInterruptMasterQva,
MasterRegister.All );
//
// Turn on mask bits for each interrupt register
//
HalpLegoPciInterruptMask[0] = (USHORT)~0;
HalpLegoPciInterruptMask[1] = (USHORT)~0;
HalpLegoPciInterruptMask[2] = (USHORT)~0;
HalpLegoPciInterruptMask[3] = (USHORT)~0;
WRITE_REGISTER_USHORT( (PUSHORT) HalpLegoPciIntMaskRegisterQva[0],
HalpLegoPciInterruptMask[0] );
WRITE_REGISTER_USHORT( (PUSHORT) HalpLegoPciIntMaskRegisterQva[1],
HalpLegoPciInterruptMask[1] );
WRITE_REGISTER_USHORT( (PUSHORT) HalpLegoPciIntMaskRegisterQva[2],
HalpLegoPciInterruptMask[2] );
WRITE_REGISTER_USHORT( (PUSHORT) HalpLegoPciIntMaskRegisterQva[3],
HalpLegoPciInterruptMask[3] );
// Turn off mask bits in master interrupt register
//
MasterRegister.All = 0;
MasterRegister.InterruptMode = 1; // MODE
MasterRegister.InterruptEnable = 0; // valid iff MODE==0
MasterRegister.IntRegMaskEnable = 1; // valid iff MODE==1
MasterRegister.IntMask = (USHORT)~(INTA | INTB | INTC | INTD);
WRITE_REGISTER_USHORT( (PUSHORT)HalpLegoPciInterruptMasterQva,
MasterRegister.All );
#if DBG
DbgDumpIntRegs();
#endif
}
VOID
HalpDisablePciInterrupt(
IN ULONG Vector
)
/*++
Routine Description:
This function disables the PCI interrupt specified by Vector.
Arguments:
Vector - Supplies the vector of the PCI interrupt to disable.
Return Value:
None.
Notes:
The register contents are cached in HalpLegoPciInterruptMask
array. [wem] ??? Is this safe ?
--*/
{
ULONG RegSelect;
USHORT BitMask, NewMask;
// Remove offset to yield register and bit position.
//
Vector -= PCI_DEVICE_VECTORS;
// High order nibble of Vector selects the register, low order
// selects the bit.
//
RegSelect = (Vector >> 4) - 1;
BitMask = 1 << ((Vector & 0xf) - 1);
// Assume the current state of the interrupt mask register
// is in HalpLegoPciInterruptMask.
//
// Set bit indicated by Vector to disable interrupts
//
NewMask = HalpLegoPciInterruptMask[RegSelect] | BitMask;
// If changed, write new register contents
//
if (NewMask != HalpLegoPciInterruptMask[RegSelect]) {
HalpLegoPciInterruptMask[RegSelect] = NewMask;
WRITE_REGISTER_USHORT( (PUSHORT)
HalpLegoPciIntMaskRegisterQva[RegSelect],
HalpLegoPciInterruptMask[RegSelect] );
}
#if DBG
DbgPrint("\nDisable PCI vector: %02X", Vector);
DbgDumpIntRegs();
#endif
}
VOID
HalpEnablePciInterrupt(
IN ULONG Vector,
IN KINTERRUPT_MODE InterruptMode
)
/*++
Routine Description:
This function enables the PCI interrupt specified by Vector.
Arguments:
Vector - Supplies the vector of the PCI interrupt that is enabled.
InterruptMode - Supplies the mode of the interrupt;
LevelSensitive or Latched (ignored for Lego PCI
interrupts; they're always levels).
Return Value:
None.
--*/
{
ULONG RegSelect;
USHORT BitMask, NewMask;
//
// Remove offset to yield register and bit position.
//
Vector -= PCI_DEVICE_VECTORS;
//
// High order nibble of Vector selects the register, low order
// selects the bit.
//
RegSelect = (Vector >> 4) - 1;
BitMask = 1 << ((Vector & 0xf) - 1);
//
// Assume the current state of the interrupt mask register
// is in HalpLegoPciInterruptMask.
//
// Clear bit indicated by Vector to enable interrupts.
//
NewMask = HalpLegoPciInterruptMask[RegSelect] & ~BitMask;
//
// If changed, write new register contents
//
if (NewMask != HalpLegoPciInterruptMask[RegSelect]) {
HalpLegoPciInterruptMask[RegSelect] = NewMask;
WRITE_REGISTER_USHORT( (PUSHORT)
HalpLegoPciIntMaskRegisterQva[RegSelect],
HalpLegoPciInterruptMask[RegSelect] );
}
#if DBG
DbgPrint("\nEnable PCI vector: %02X", Vector);
DbgPrint("\n Old: %04X, New: %04X", HalpLegoPciInterruptMask[RegSelect], NewMask);
DbgDumpIntRegs();
#endif
}
// Table for mapping from per-slot interrupt signal
// to which slot to service.
// Index is 4-bit value representing interrupt status for
// <slot 4> <slot 3> <slot 2> <slot 1>
// Current priority ordering is slot 1, 2, 3, 4
// Index zero has no meaning, but it should do no harm.
//
//
USHORT
HighestPrioritySlot[16] = {
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f // value
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 // slot (based from zero)
};
ULONG
HighestPrioritySlotHits[4][16] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
UCHAR
HighestPriorityLine (
LEGO_PCI_INT_MASTER MasterReg
)
/*++
Routine Description:
Policy routine for determining which PCI device to service next.
Arguments:
MasterReg -- current Lego Master Interrupt register. Interrupt
field indicates which slots have pending interrupts.
Return Value:
Value in range of 0..64, indicating which device should be serviced.
--*/
{
USHORT IntReg;
UCHAR Nib;
USHORT IrContents;
USHORT RegSelect, BitSelect;
int i;
RegSelect = HighestPrioritySlot[MasterReg.Interrupt];
// Read interrupt register for slot
//
IntReg = READ_REGISTER_USHORT( (PUSHORT)
HalpLegoPciInterruptRegisterQva[RegSelect]);
if (IntReg==0)
return 0xff;
// Find lowest numbered bit.
//
BitSelect = 0;
while (IntReg!=0) {
Nib = IntReg & 0xf;
if (Nib != 0) {
BitSelect += HighestPrioritySlot[Nib];
break;
}
IntReg = IntReg >> 4;
BitSelect += 4;
}
#if DBG
if (HighestPrioritySlotHits[RegSelect][BitSelect]++ == 0) {
DbgPrint("\nSlot: %d, %d", RegSelect, BitSelect);
DbgDumpIntRegs();
}
#endif
return ((RegSelect + 1) << 4) + (BitSelect + 1);
}
UCHAR
HalpAcknowledgePciInterrupt(
PVOID ServiceContext
)
/*++
Routine Description:
Acknowledge the PCI interrupt. Return the vector number of the
highest priority pending interrupt.
Arguments:
ServiceContext - Service context of the interrupt service - supplies
a QVA to Lego's Master PCI interrupt register.
Return Value:
Return the value of the highest priority pending interrupt,
or 0xff if none.
--*/
{
LEGO_PCI_INT_MASTER MasterReg;
UCHAR InterruptVector;
UCHAR ISAVector = 0; //[wem] DEBUG - ack PCI int
static ULONG Count = 0;
//
// Check the master register to see which primary slot is
// requesting service (slot may be a PPB requesting service
// on behalf of one of its slots). Then check that slot's
// interrupt register.
//
// A set bit in the master or slot register indicates an active
// interrupt. Note that if the interrupt is masked, the bit will
// not be set. Also note that the code does not use the master
// register's mask bits unless all PCI interrupts need to be
// disabled at once.
//
// Scan is currently depth-first from slot 1 to slot 4. For
// each slot, INTA is serviced first and INTD serviced last.
//
// [wem] There's some interesting opportunities here.
// For example, it is possible to scan and detect how many
// waiting interrupts there are. It is also possible remember
// last vector and scan forward from that point (as in round
// robin), or to implement a fairness algorithm that incorporates
// priority.
//
#if 0
//
//[wem] This requires an interrupt controller to respond to the
// interrupt acknowledge. The SIO has been programmed to not
// respond, so someone else has to do it (the interrupt accelerator?).
//
ISAVector = READ_PORT_UCHAR(HalpEisaIntAckBase);
#endif
// Read master register.
//
MasterReg.All = READ_REGISTER_USHORT(
(PUSHORT) HalpLegoPciInterruptMasterQva);
if (MasterReg.Interrupt==0) {
return 0xff; // no interrupts
}
// Scan
//
InterruptVector = HighestPriorityLine (MasterReg);
#if DBG
if (Count++ < 5 && ISAVector != 0xff) {
DbgPrint("<PCIACK:%02X:%02X>",ISAVector,InterruptVector);
}
#endif
return( InterruptVector );
}
BOOLEAN
HalpPciDispatch(
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext,
IN PKTRAP_FRAME TrapFrame
)
/*++
Routine Description:
This routine is entered as the result of an interrupt having been generated
via the vector connected to the PCI device interrupt object. Its function
is to call the second-level interrupt dispatch routine.
This service routine could have been connected as follows, where the
ISR is the assembly wrapper that does the handoff to this function:
KeInitializeInterrupt( &Interrupt,
HalpPciInterruptHandler,
(PVOID) HalpPciIrQva,
(PKSPIN_LOCK)NULL,
PCI_VECTOR,
PCI_DEVICE_LEVEL,
PCI_DEVICE_LEVEL,
LevelSensitive,
TRUE,
0,
FALSE);
KeConnectInterrupt(&Interrupt);
Arguments:
Interrupt - Supplies a pointer to the interrupt object.
ServiceContext - Supplies a pointer to the PCI interrupt register.
TrapFrame - Supplies a pointer to the trap frame for this interrupt.
Return Value:
Returns the value returned from the second level routine.
--*/
{
UCHAR PCIVector;
BOOLEAN returnValue;
USHORT PCRInOffset;
//[wem] DEBUG - count SIO dispatch entries
//
static long PciCount = 0;
#if DBG //[wem]
PciCount++;
if (PciCount<5) {
DbgPrint("II<PCI><");
}
if (PciCount % 5000 == 0) {
DbgPrint("II<PCI><%08x><",PciCount);
}
#endif
//
// Acknowledge interrupt and receive the returned interrupt vector.
// If we got 0xff back, there were no enabled interrupts, so we
// signal that with a FALSE return, immediately.
//
PCIVector = HalpAcknowledgePciInterrupt(ServiceContext);
if (PCIVector == 0xff) {
#if DBG //[wem]
if (PciCount<5 || (PciCount % 5000) == 0) {
DbgPrint("ff>.");
}
#endif
return( FALSE );
}
// Compute new vector based on PCI bus state
//
PCRInOffset = PCIVector + PCI_DEVICE_VECTORS;
// Re-dispatch via new vector
//
returnValue = ((PSECONDARY_DISPATCH) PCR->InterruptRoutine[PCRInOffset])(
PCR->InterruptRoutine[PCRInOffset],
TrapFrame
);
#if DBG //[wem]
if (PciCount<5 || (PciCount % 5000) == 0) {
DbgPrint("%02x>.",returnValue);
}
#endif
return( returnValue );
}