600 lines
12 KiB
C
600 lines
12 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
Copyright (c) 1992, 1993 Digital Equipment Corporation
|
||
|
||
Module Name:
|
||
|
||
alintsup.c
|
||
|
||
Abstract:
|
||
|
||
The module provides the interrupt support for Alcor systems.
|
||
|
||
Author:
|
||
|
||
Eric Rehm (DEC) 29-December-1993
|
||
|
||
Revision History:
|
||
|
||
Chao Chen (DEC) 26-July-1994
|
||
Adapted from Avanti module for Alcor.
|
||
|
||
--*/
|
||
|
||
|
||
#include "halp.h"
|
||
#include "eisa.h"
|
||
#include "ebsgdma.h"
|
||
#include "alcor.h"
|
||
#include "pcrtc.h"
|
||
#include "pintolin.h"
|
||
|
||
|
||
//
|
||
// Declare the interrupt structures and spinlocks for the intermediate
|
||
// interrupt dispatchers.
|
||
//
|
||
|
||
KINTERRUPT HalpPciInterrupt;
|
||
KINTERRUPT HalpEisaInterrupt;
|
||
|
||
//
|
||
// Define the context structure for use by interrupt service routines.
|
||
//
|
||
|
||
typedef BOOLEAN (*PSECOND_LEVEL_DISPATCH)(
|
||
PKINTERRUPT InterruptObject,
|
||
PVOID ServiceContext,
|
||
PKTRAP_FRAME TrapFrame
|
||
);
|
||
|
||
//
|
||
// Declare the interrupt handler for the EISA bus. The interrupt dispatch
|
||
// routine, HalpEisaDispatch, is called from this handler.
|
||
//
|
||
|
||
BOOLEAN
|
||
HalpEisaInterruptHandler(
|
||
IN PKINTERRUPT Interrupt,
|
||
IN PVOID ServiceContext
|
||
);
|
||
|
||
//
|
||
// The following is the interrupt object used for DMA controller interrupts.
|
||
// DMA controller interrupts occur when a memory parity error occurs or a
|
||
// programming error occurs to the DMA controller.
|
||
//
|
||
|
||
KINTERRUPT HalpEisaNmiInterrupt;
|
||
|
||
//
|
||
// The following function initializes NMI handling.
|
||
//
|
||
|
||
VOID
|
||
HalpInitializeNMI(
|
||
VOID
|
||
);
|
||
|
||
//
|
||
// The following function is called when an EISA NMI occurs.
|
||
//
|
||
|
||
BOOLEAN
|
||
HalHandleNMI(
|
||
IN PKINTERRUPT Interrupt,
|
||
IN PVOID ServiceContext
|
||
);
|
||
|
||
//
|
||
// The following functions handle the PCI interrupts.
|
||
//
|
||
|
||
VOID
|
||
HalpInitializePciInterrupts (
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HalpDisablePciInterrupt(
|
||
IN ULONG Vector
|
||
);
|
||
|
||
VOID
|
||
HalpEnablePciInterrupt(
|
||
IN ULONG Vector,
|
||
IN KINTERRUPT_MODE InterruptMode
|
||
);
|
||
|
||
BOOLEAN
|
||
HalpDeviceDispatch(
|
||
IN PKINTERRUPT Interrupt,
|
||
IN PVOID ServiceContext,
|
||
IN PKTRAP_FRAME TrapFrame
|
||
);
|
||
|
||
|
||
BOOLEAN
|
||
HalpInitializeAlcorInterrupts (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the structures necessary for EISA & PCI operations
|
||
and connects the intermediate interrupt dispatchers. It also initializes
|
||
the EISA interrupt controller; the Alcor ESC's interrupt controller is
|
||
compatible with the EISA interrupt contoller used on Jensen.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
If the second level interrupt dispatchers are connected, then a value of
|
||
TRUE is returned. Otherwise, a value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
|
||
//
|
||
// Initialize the EISA NMI interrupt.
|
||
//
|
||
|
||
HalpInitializeNMI();
|
||
|
||
(PVOID) HalpPCIPinToLineTable = (PVOID) AlcorPCIPinToLineTable;
|
||
|
||
//
|
||
// Intitialize interrupt controller
|
||
//
|
||
|
||
KeRaiseIrql(DEVICE_HIGH_LEVEL, &oldIrql);
|
||
|
||
//
|
||
// Initialize the PCI interrupts.
|
||
//
|
||
|
||
HalpInitializePciInterrupts();
|
||
|
||
//
|
||
// Initialize the ESC's PICs for EISA interrupts.
|
||
//
|
||
|
||
HalpInitializeEisaInterrupts();
|
||
|
||
//
|
||
// Restore the IRQL.
|
||
//
|
||
|
||
KeLowerIrql(oldIrql);
|
||
|
||
//
|
||
// Initialize the EISA DMA mode registers to a default value.
|
||
// Disable all of the DMA channels except channel 4 which is the
|
||
// cascade of channels 0-3.
|
||
//
|
||
|
||
WRITE_PORT_UCHAR(
|
||
&((PEISA_CONTROL) HalpEisaControlBase)->Dma1BasePort.AllMask,
|
||
0x0F
|
||
);
|
||
|
||
WRITE_PORT_UCHAR(
|
||
&((PEISA_CONTROL) HalpEisaControlBase)->Dma2BasePort.AllMask,
|
||
0x0E
|
||
);
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpInitializeNMI(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called to intialize ESC NMI interrupts.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
--*/
|
||
{
|
||
UCHAR DataByte;
|
||
|
||
//
|
||
// Initialize the ESC NMI interrupt.
|
||
//
|
||
|
||
KeInitializeInterrupt( &HalpEisaNmiInterrupt,
|
||
HalHandleNMI,
|
||
NULL,
|
||
NULL,
|
||
EISA_NMI_VECTOR,
|
||
EISA_NMI_LEVEL,
|
||
EISA_NMI_LEVEL,
|
||
LevelSensitive,
|
||
FALSE,
|
||
0,
|
||
FALSE
|
||
);
|
||
|
||
//
|
||
// Don't fail if the interrupt cannot be connected.
|
||
//
|
||
|
||
KeConnectInterrupt( &HalpEisaNmiInterrupt );
|
||
|
||
//
|
||
// Clear the Eisa NMI disable bit. This bit is the high order of the
|
||
// NMI enable register.
|
||
//
|
||
|
||
DataByte = 0;
|
||
|
||
WRITE_PORT_UCHAR(
|
||
&((PEISA_CONTROL) HalpEisaControlBase)->NmiEnable,
|
||
DataByte
|
||
);
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
HalHandleNMI(
|
||
IN PKINTERRUPT Interrupt,
|
||
IN PVOID ServiceContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called when an EISA NMI occurs. It prints the
|
||
appropriate status information and bugchecks.
|
||
|
||
Arguments:
|
||
|
||
Interrupt - Supplies a pointer to the interrupt object
|
||
|
||
ServiceContext - Bug number to call bugcheck with.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE.
|
||
|
||
--*/
|
||
{
|
||
UCHAR StatusByte;
|
||
|
||
StatusByte =
|
||
READ_PORT_UCHAR(&((PEISA_CONTROL) HalpEisaControlBase)->NmiStatus);
|
||
|
||
if (StatusByte & 0x80) {
|
||
HalDisplayString ("NMI: Parity Check / Parity Error\n");
|
||
}
|
||
|
||
if (StatusByte & 0x40) {
|
||
HalDisplayString ("NMI: Channel Check / IOCHK\n");
|
||
}
|
||
|
||
KeBugCheck(NMI_HARDWARE_FAILURE);
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpAcknowledgeClockInterrupt(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Acknowledge the clock interrupt from the interval timer. The interval
|
||
timer for Alcor comes from a Dallas real-time clock.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// Acknowledge the clock interrupt by reading the control register C of
|
||
// the Real Time Clock.
|
||
//
|
||
|
||
HalpReadClockRegister( RTC_CONTROL_REGISTERC );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// The enable mask for all interrupts sourced from the GRU (all device
|
||
// interrupts, and all from PCI). A "1" indicates the interrupt is enabled.
|
||
//
|
||
|
||
ULONG HalpPciInterruptMask;
|
||
|
||
|
||
VOID
|
||
HalpInitializePciInterrupts (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the Alcor PCI interrupts.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// Disable interrupts except EISA.
|
||
//
|
||
|
||
HalpPciInterruptMask = GRU_ENABLE_EISA_INT;
|
||
|
||
WRITE_GRU_REGISTER(&((PGRU_INTERRUPT_CSRS)GRU_CSRS_QVA)->IntMask,
|
||
HalpPciInterruptMask);
|
||
|
||
//
|
||
// Set all interrupts to level.
|
||
//
|
||
|
||
WRITE_GRU_REGISTER(&((PGRU_INTERRUPT_CSRS)GRU_CSRS_QVA)->IntEdge,
|
||
GRU_SET_LEVEL_INT);
|
||
|
||
//
|
||
// Set all interrupts to active low except EISA.
|
||
//
|
||
|
||
WRITE_GRU_REGISTER(&((PGRU_INTERRUPT_CSRS)GRU_CSRS_QVA)->IntHiLo,
|
||
GRU_SET_LOW_INT);
|
||
|
||
//
|
||
// Clear the interrupt clear register.
|
||
//
|
||
|
||
WRITE_GRU_REGISTER(&((PGRU_INTERRUPT_CSRS)GRU_CSRS_QVA)->IntClear, 0);
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
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 that is disabled.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Calculate the PCI interrupt vector, relative to 0.
|
||
//
|
||
|
||
Vector -= PCI_VECTORS;
|
||
|
||
//
|
||
// Get the current state of the interrupt mask register, then set
|
||
// the bit corresponding to the adjusted value of Vector to zero,
|
||
// to disable that PCI interrupt.
|
||
//
|
||
|
||
HalpPciInterruptMask &= (ULONG) ~(1 << Vector);
|
||
WRITE_GRU_REGISTER(&((PGRU_INTERRUPT_CSRS)GRU_CSRS_QVA)->IntMask,
|
||
HalpPciInterruptMask);
|
||
}
|
||
|
||
|
||
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 Alcor PCI interrupts; they're always levels).
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Calculate the PCI interrupt vector, relative to 0.
|
||
//
|
||
|
||
Vector -= PCI_VECTORS;
|
||
|
||
//
|
||
// Get the current state of the interrupt mask register, then set
|
||
// the bit corresponding to the adjusted value of Vector to one,
|
||
// to ensable that PCI interrupt.
|
||
//
|
||
|
||
HalpPciInterruptMask |= (ULONG) 1 << Vector;
|
||
WRITE_GRU_REGISTER(&((PGRU_INTERRUPT_CSRS)GRU_CSRS_QVA)->IntMask,
|
||
HalpPciInterruptMask);
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
HalpDeviceDispatch(
|
||
IN PKINTERRUPT Interrupt,
|
||
IN PVOID ServiceContext,
|
||
IN PKTRAP_FRAME TrapFrame
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is entered as the result of an interrupt being generated
|
||
via the vector that is connected to an interrupt object associated with
|
||
the PCI device interrupts. Its function is to call the second-level
|
||
interrupt dispatch routine.
|
||
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
PULONG DispatchCode;
|
||
ULONG IdtIndex;
|
||
ULONG IntNumber;
|
||
PKINTERRUPT InterruptObject;
|
||
ULONG PCIVector;
|
||
ULONG Slot;
|
||
|
||
//
|
||
// Read in the interrupt register.
|
||
//
|
||
|
||
PCIVector=READ_GRU_REGISTER(&((PGRU_INTERRUPT_CSRS)GRU_CSRS_QVA)->IntReq);
|
||
|
||
//
|
||
// Consider only those interrupts which are currently enabled.
|
||
//
|
||
|
||
PCIVector &= HalpPciInterruptMask;
|
||
|
||
//
|
||
// Continue processing interrupts while any are requested.
|
||
//
|
||
|
||
while( (PCIVector & (GRU_EISA_MASK_INT | GRU_PCI_MASK_INT)) != 0 ){
|
||
|
||
//
|
||
// Did PCI or EISA interrupt occur?
|
||
//
|
||
|
||
if (PCIVector & GRU_EISA_MASK_INT) {
|
||
|
||
//
|
||
// EISA interrupt. Call HalpEisaDispatch.
|
||
//
|
||
|
||
HalpEisaDispatch( Interrupt, (PVOID)CIA_PCI_INTACK_QVA, TrapFrame);
|
||
|
||
} else {
|
||
|
||
//
|
||
// PCI interrupt. Find out which slot.
|
||
//
|
||
|
||
for (Slot = 0; Slot < NUMBER_PCI_SLOTS; Slot++) {
|
||
|
||
if (PCIVector & 0xf)
|
||
break;
|
||
else
|
||
PCIVector = PCIVector >> 4;
|
||
}
|
||
|
||
//
|
||
// Find out which of the IntA, IntB, IntC, or IntD occurred.
|
||
//
|
||
|
||
for (IntNumber = 0; IntNumber < 4; IntNumber++) {
|
||
|
||
if (PCIVector & 0x1)
|
||
break;
|
||
else
|
||
PCIVector = PCIVector >> 1;
|
||
}
|
||
|
||
//
|
||
// Dispatch to the secondary interrupt service routine.
|
||
//
|
||
|
||
IdtIndex = (Slot * (NUMBER_PCI_SLOTS - 1) + IntNumber) +
|
||
PCI_VECTORS;
|
||
DispatchCode = (PULONG)PCR->InterruptRoutine[IdtIndex];
|
||
InterruptObject = CONTAINING_RECORD( DispatchCode,
|
||
KINTERRUPT,
|
||
DispatchCode );
|
||
|
||
((PSECOND_LEVEL_DISPATCH)InterruptObject->DispatchAddress)(
|
||
InterruptObject,
|
||
InterruptObject->ServiceContext,
|
||
TrapFrame );
|
||
}
|
||
|
||
//
|
||
// Check for more interrupts.
|
||
//
|
||
|
||
PCIVector = READ_GRU_REGISTER(
|
||
&((PGRU_INTERRUPT_CSRS)GRU_CSRS_QVA)->IntReq);
|
||
PCIVector &= HalpPciInterruptMask;
|
||
|
||
} //end while( (PCIVector & (GRU_EISA_MASK_INT | GRU_PCI_MASK_INT)) != 0 )
|
||
|
||
return TRUE;
|
||
|
||
}
|