mirror of https://github.com/lianthony/NT4.0
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.
2044 lines
46 KiB
2044 lines
46 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Copyright (c) 1994 MOTOROLA, INC. All Rights Reserved. This file
|
|
contains copyrighted material. Use of this file is restricted
|
|
by the provisions of a Motorola Software License Agreement.
|
|
|
|
Copyright (c) 1993-1996 International Business Machines Corporation
|
|
|
|
Module Name:
|
|
|
|
pxfirsup.c
|
|
|
|
Abstract:
|
|
|
|
The module provides the support for the fire coral PCI-ISA bridge.
|
|
|
|
Author:
|
|
|
|
Jim Wooldridge ([email protected])
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
|
|
#include "halp.h"
|
|
#include "eisa.h"
|
|
#include "pxfirsup.h"
|
|
#include <pxmemctl.h>
|
|
#include "bugcodes.h"
|
|
#include "pxidesup.h"
|
|
|
|
extern UCHAR VectorToIrqlTable[];
|
|
|
|
#define SioId 0x04848086
|
|
|
|
PVOID HalpPciIsaBridgeConfigBase;
|
|
|
|
extern PADAPTER_OBJECT MasterAdapterObject;
|
|
extern ULONG HalpPciMaxSlots;
|
|
extern ULONG HalpPhase0GetPciDataByOffset();
|
|
extern ULONG HalpPhase0SetPciDataByOffset();
|
|
|
|
//
|
|
// Define the context structure for use by the interrupt routine.
|
|
//
|
|
|
|
|
|
typedef BOOLEAN (*PSECONDARY_DISPATCH)(
|
|
PVOID InterruptRoutine,
|
|
PVOID ServiceContext,
|
|
PVOID TrapFrame
|
|
);
|
|
|
|
//
|
|
// Declare the interupt structure for profile interrupt
|
|
//
|
|
|
|
KINTERRUPT HalpProfileInterrupt;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
//
|
|
// Declare the interupt structure for machine checks
|
|
//
|
|
|
|
KINTERRUPT HalpMachineCheckInterrupt;
|
|
|
|
//
|
|
// Declare the interupt structure for the clock interrupt
|
|
//
|
|
|
|
KINTERRUPT HalpDecrementerInterrupt;
|
|
|
|
|
|
//
|
|
// Add spurious and bogus interrupt counts
|
|
//
|
|
|
|
#if DBG
|
|
ULONG HalpSpuriousInterruptCount = 0;
|
|
ULONG HalpBogusInterruptCount = 0;
|
|
#endif
|
|
|
|
|
|
//
|
|
// Define Isa bus interrupt affinity.
|
|
//
|
|
|
|
KAFFINITY HalpIsaBusAffinity;
|
|
|
|
|
|
//
|
|
// The following function is called when a IDE interrupt occurs.
|
|
//
|
|
|
|
BOOLEAN
|
|
HalpHandleIdeInterrupt(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID ServiceContext
|
|
);
|
|
|
|
//
|
|
// The following function is called when a machine check occurs.
|
|
//
|
|
|
|
BOOLEAN
|
|
HalpHandleMachineCheck(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID ServiceContext
|
|
);
|
|
|
|
//
|
|
// Define save area for ISA adapter objects.
|
|
//
|
|
|
|
PADAPTER_OBJECT HalpIsaAdapter[8];
|
|
|
|
//
|
|
// Define save area for ISA interrupt mask resiters and level\edge control
|
|
// registers.
|
|
//
|
|
|
|
#define ISA_CONTROL ((PEISA_CONTROL) HalpIoControlBase)
|
|
extern UCHAR HalpSioInterrupt1Mask;
|
|
extern UCHAR HalpSioInterrupt2Mask;
|
|
extern USHORT Halp8259MaskTable[];
|
|
|
|
UCHAR HalpSioInterrupt1Mask = 0xff;
|
|
UCHAR HalpSioInterrupt2Mask = 0xff;
|
|
UCHAR HalpSioInterrupt1Level;
|
|
UCHAR HalpSioInterrupt2Level;
|
|
|
|
#if defined(SOFT_HDD_LAMP)
|
|
|
|
//
|
|
// On PowerPC machines the HDD lamp is software driven. We
|
|
// turn it on any time we take an interrupt from a Mass Storage
|
|
// Controller (assuming it isn't already on) and turn it off the 2nd
|
|
// clock tick after we turn it on if we have not received
|
|
// any more MSC interrupts since the first clock tick.
|
|
//
|
|
// N.B. Hardwired to cause any IDE interrupt to turn the light on.
|
|
//
|
|
|
|
HDD_LAMP_STATUS HalpHddLamp;
|
|
|
|
ULONG HalpMassStorageControllerVectors = 1 << IDE_DISPATCH_VECTOR;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
BOOLEAN
|
|
HalpInitializeInterrupts (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called from phase 0 initialization, it initializes the
|
|
8259 interrupt controller ( currently it masks all 8259 interrupts).
|
|
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Vector;
|
|
|
|
//
|
|
// Mask all 8259 interrupts (except the cascade interrupt)
|
|
//
|
|
|
|
for (Vector=0;Vector<16;Vector++) {
|
|
if (Vector == 2)
|
|
continue;
|
|
HalpDisableSioInterrupt(Vector + DEVICE_VECTORS);
|
|
}
|
|
|
|
//
|
|
// Reserve the external interrupt vector for exclusive use by the HAL.
|
|
//
|
|
|
|
PCR->ReservedVectors |= (1 << EXTERNAL_INTERRUPT_VECTOR);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
HalpCreateSioStructures (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the structures necessary for SIO operations
|
|
and connects the intermediate interrupt dispatcher. It also initializes the
|
|
SIO interrupt controller.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
If the second level interrupt dispatcher is connected, then a value of
|
|
TRUE is returned. Otherwise, a value of FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UCHAR DataByte;
|
|
KIRQL oldIrql;
|
|
|
|
|
|
//
|
|
// Initialize the Machine Check interrupt handler
|
|
//
|
|
|
|
if (HalpEnableInterruptHandler(&HalpMachineCheckInterrupt,
|
|
HalpHandleMachineCheck,
|
|
NULL,
|
|
NULL,
|
|
MACHINE_CHECK_VECTOR,
|
|
MACHINE_CHECK_LEVEL,
|
|
MACHINE_CHECK_LEVEL,
|
|
Latched,
|
|
FALSE,
|
|
0,
|
|
FALSE,
|
|
InternalUsage,
|
|
MACHINE_CHECK_VECTOR
|
|
) == FALSE) {
|
|
KeBugCheck(HAL_INITIALIZATION_FAILED);
|
|
}
|
|
|
|
//
|
|
// Enable NMI IOCHK# and PCI SERR#
|
|
//
|
|
|
|
DataByte = READ_REGISTER_UCHAR(&((PEISA_CONTROL)HalpIoControlBase)->NmiStatus);
|
|
WRITE_REGISTER_UCHAR(&((PEISA_CONTROL)HalpIoControlBase)->NmiStatus,
|
|
DataByte & ~DISABLE_IOCHK_NMI & ~DISABLE_PCI_SERR_NMI);
|
|
|
|
//
|
|
// Clear the SIO NMI disable bit. This bit is the high order of the
|
|
// NMI enable register.
|
|
//
|
|
|
|
DataByte = 0;
|
|
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->NmiEnable,
|
|
DataByte
|
|
);
|
|
|
|
//
|
|
// Connect the external interrupt handler
|
|
//
|
|
|
|
PCR->InterruptRoutine[EXTERNAL_INTERRUPT_VECTOR] =
|
|
(PKINTERRUPT_ROUTINE)HalpHandleExternalInterrupt;
|
|
|
|
|
|
//
|
|
// All IDE interrupts come thru the IDE_DISPATCH_VECTOR. Install
|
|
// a handler for that vector that will determine the actual IDE
|
|
// device and dispatch appropriately.
|
|
//
|
|
|
|
PCR->InterruptRoutine[DEVICE_VECTORS + IDE_DISPATCH_VECTOR] =
|
|
(PKINTERRUPT_ROUTINE)HalpHandleIdeInterrupt;
|
|
|
|
//
|
|
// register the interrupt vector
|
|
//
|
|
|
|
HalpRegisterVector(InternalUsage,
|
|
EXTERNAL_INTERRUPT_VECTOR,
|
|
EXTERNAL_INTERRUPT_VECTOR,
|
|
HIGH_LEVEL);
|
|
|
|
|
|
|
|
|
|
// Connect directly to the decrementer handler. This is done
|
|
// directly rather than thru HalpEnableInterruptHandler due to
|
|
// special handling required because the handler calls KdPollBreakIn().
|
|
//
|
|
|
|
PCR->InterruptRoutine[DECREMENT_VECTOR] =
|
|
(PKINTERRUPT_ROUTINE)HalpHandleDecrementerInterrupt;
|
|
|
|
//
|
|
// Initialize and connect the Timer 1 interrupt (IRQ0)
|
|
//
|
|
|
|
if (HalpEnableInterruptHandler( &HalpProfileInterrupt,
|
|
(PKSERVICE_ROUTINE) HalpHandleProfileInterrupt,
|
|
(PVOID) NULL,
|
|
(PKSPIN_LOCK)NULL,
|
|
PROFILE_VECTOR,
|
|
PROFILE_LEVEL,
|
|
PROFILE_LEVEL,
|
|
Latched,
|
|
TRUE,
|
|
0,
|
|
FALSE,
|
|
DeviceUsage,
|
|
PROFILE_VECTOR
|
|
) == FALSE) {
|
|
KeBugCheck(HAL_INITIALIZATION_FAILED);
|
|
}
|
|
|
|
|
|
//
|
|
// Disable Timer 1; only used by profiling
|
|
//
|
|
|
|
HalDisableSystemInterrupt(PROFILE_VECTOR, PROFILE_LEVEL);
|
|
|
|
//
|
|
// Set default profile rate
|
|
//
|
|
|
|
HalSetProfileInterval(5000);
|
|
|
|
|
|
//
|
|
// Raise the IRQL while the SIO interrupt controller is initalized.
|
|
//
|
|
|
|
KeRaiseIrql(CLOCK2_LEVEL, &oldIrql);
|
|
|
|
|
|
|
|
//
|
|
// Initialize any planar registers
|
|
//
|
|
|
|
HalpInitPlanar();
|
|
|
|
//
|
|
// Initialize the PCI/ISA bridge chip
|
|
//
|
|
|
|
HalpInitPciIsaBridge();
|
|
|
|
|
|
|
|
//
|
|
// Initialize the SIO interrupt controller. There are two cascaded
|
|
// interrupt controllers, each of which must initialized with 4 initialize
|
|
// control words.
|
|
//
|
|
|
|
DataByte = 0;
|
|
((PINITIALIZATION_COMMAND_1) &DataByte)->Icw4Needed = 1;
|
|
((PINITIALIZATION_COMMAND_1) &DataByte)->InitializationFlag = 1;
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1ControlPort0,
|
|
DataByte
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2ControlPort0,
|
|
DataByte
|
|
);
|
|
|
|
//
|
|
// The second intitialization control word sets the iterrupt vector to
|
|
// 0-15.
|
|
//
|
|
|
|
DataByte = 0;
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1ControlPort1,
|
|
DataByte
|
|
);
|
|
|
|
DataByte = 0x08;
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2ControlPort1,
|
|
DataByte
|
|
);
|
|
|
|
//
|
|
// The thrid initialization control word set the controls for slave mode.
|
|
// The master ICW3 uses bit position and the slave ICW3 uses a numberic.
|
|
//
|
|
|
|
DataByte = 1 << SLAVE_IRQL_LEVEL;
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1ControlPort1,
|
|
DataByte
|
|
);
|
|
|
|
DataByte = SLAVE_IRQL_LEVEL;
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2ControlPort1,
|
|
DataByte
|
|
);
|
|
|
|
//
|
|
// The fourth initialization control word is used to specify normal
|
|
// end-of-interrupt mode and not special-fully-nested mode.
|
|
//
|
|
|
|
DataByte = 0;
|
|
((PINITIALIZATION_COMMAND_4) &DataByte)->I80x86Mode = 1;
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1ControlPort1,
|
|
DataByte
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2ControlPort1,
|
|
DataByte
|
|
);
|
|
|
|
|
|
//
|
|
// Disable all of the interrupts except the slave.
|
|
//
|
|
|
|
HalpSioInterrupt1Mask = (UCHAR) ~(1 << SLAVE_IRQL_LEVEL);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1ControlPort1,
|
|
HalpSioInterrupt1Mask
|
|
);
|
|
|
|
HalpSioInterrupt2Mask = 0xFF;
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2ControlPort1,
|
|
HalpSioInterrupt2Mask
|
|
);
|
|
|
|
//
|
|
// Initialize the edge/level register masks to 0 which is the default
|
|
// edge sensitive value.
|
|
//
|
|
|
|
HalpSioInterrupt1Level = 0;
|
|
HalpSioInterrupt2Level = 0;
|
|
|
|
//
|
|
// Enable the clock interrupt
|
|
//
|
|
|
|
|
|
HalpUpdateDecrementer(1000); // Get those decrementer ticks going
|
|
|
|
|
|
//
|
|
// Set ISA bus interrupt affinity.
|
|
//
|
|
|
|
HalpIsaBusAffinity = PCR->SetMember;
|
|
|
|
|
|
//
|
|
// Restore IRQL level.
|
|
//
|
|
|
|
KeLowerIrql(oldIrql);
|
|
|
|
|
|
//
|
|
// DMA command - set assert level
|
|
//
|
|
|
|
DataByte = READ_REGISTER_UCHAR(&((PEISA_CONTROL)HalpIoControlBase)->Dma1BasePort.DmaStatus);
|
|
WRITE_REGISTER_UCHAR(&((PEISA_CONTROL)HalpIoControlBase)->Dma1BasePort.DmaStatus,
|
|
DataByte & ~DACK_ASSERT_HIGH & ~DREQ_ASSERT_LOW);
|
|
|
|
//
|
|
// Initialize the DMA mode registers to a default value.
|
|
// Disable all of the DMA channels except channel 4 which is that
|
|
// cascade of channels 0-3.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Dma1BasePort.AllMask,
|
|
0x0F
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Dma2BasePort.AllMask,
|
|
0x0E
|
|
);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOLEAN
|
|
HalpInitPciIsaBridge (
|
|
VOID
|
|
)
|
|
|
|
|
|
{
|
|
|
|
UCHAR DataByte;
|
|
BOOLEAN Found;
|
|
ULONG SlotNumber;
|
|
ULONG ChipId;
|
|
ULONG BufferLength;
|
|
|
|
|
|
Found = FALSE;
|
|
SlotNumber = 0;
|
|
|
|
while (!Found && SlotNumber < HalpPciMaxSlots) {
|
|
|
|
BufferLength = HalpPhase0GetPciDataByOffset(0,
|
|
SlotNumber,
|
|
&ChipId,
|
|
0,
|
|
sizeof(ChipId));
|
|
if (ChipId == SioId)
|
|
Found = TRUE;
|
|
else
|
|
SlotNumber++;
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that we found a valid chip id
|
|
//
|
|
|
|
if (!Found)
|
|
return FALSE;
|
|
|
|
//
|
|
// Define macros for reading and writing to the SIO config space
|
|
//
|
|
|
|
#define READ_SIO_CONFIG_UCHAR(offset,byte) \
|
|
( \
|
|
HalpPhase0GetPciDataByOffset( \
|
|
0, \
|
|
SlotNumber, \
|
|
&byte, \
|
|
FIELD_OFFSET(SIO_CONFIG,offset), \
|
|
1 \
|
|
) \
|
|
)
|
|
|
|
#define WRITE_SIO_CONFIG_UCHAR(offset,byte) \
|
|
( \
|
|
HalpPhase0SetPciDataByOffset( \
|
|
0, \
|
|
SlotNumber, \
|
|
&byte, \
|
|
FIELD_OFFSET(SIO_CONFIG,offset), \
|
|
1 \
|
|
) \
|
|
)
|
|
|
|
|
|
|
|
//
|
|
// Enable ISA Master line buffering
|
|
//
|
|
|
|
|
|
|
|
READ_SIO_CONFIG_UCHAR(PciControl,DataByte);
|
|
|
|
DataByte |= ENABLE_PCI_POSTED_WRITE_BUFFER
|
|
| ENABLE_ISA_MASTER_LINE_BUFFER
|
|
| EANBLE_DMA_LINE_BUFFER;
|
|
|
|
WRITE_SIO_CONFIG_UCHAR(PciControl, DataByte );
|
|
|
|
//
|
|
// Disable Gauranteed Access Time Mode
|
|
//
|
|
|
|
READ_SIO_CONFIG_UCHAR(PciArbiterControl,DataByte);
|
|
|
|
DataByte &= ~ENABLE_GAT;
|
|
|
|
WRITE_SIO_CONFIG_UCHAR(PciArbiterControl, DataByte);
|
|
|
|
|
|
|
|
|
|
//
|
|
// Initialize SuperIO chip
|
|
//
|
|
|
|
if (!HalpInitSuperIo())
|
|
|
|
return FALSE;
|
|
|
|
|
|
//
|
|
// Utility Bus A chip select
|
|
//
|
|
|
|
READ_SIO_CONFIG_UCHAR(UtilityBusEnableA,DataByte);
|
|
|
|
DataByte |= ENABLE_RTC | ENABLE_KEYBOARD & ~ENABLE_IDE_DECODE;
|
|
|
|
WRITE_SIO_CONFIG_UCHAR(UtilityBusEnableA, DataByte);
|
|
|
|
//
|
|
// Utility Bus B chip select
|
|
//
|
|
|
|
READ_SIO_CONFIG_UCHAR(UtilityBusEnableB,DataByte);
|
|
|
|
DataByte |= ENABLE_RAM_DECODE | ENABLE_PORT92 | DISABLE_PARALLEL_PORT
|
|
| DISABLE_SERIAL_PORTA | DISABLE_SERIAL_PORTB;
|
|
|
|
WRITE_SIO_CONFIG_UCHAR(UtilityBusEnableB, DataByte);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
HalpMapIoControlSpace (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine maps the HAL SIO control space for a PowerPC system.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
If the initialization is successfully completed, than a value of TRUE
|
|
is returned. Otherwise, a value of FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
PHYSICAL_ADDRESS physicalAddress;
|
|
|
|
//
|
|
// Map SIO control space.
|
|
//
|
|
|
|
physicalAddress.HighPart = 0;
|
|
physicalAddress.LowPart = IO_CONTROL_PHYSICAL_BASE;
|
|
HalpIoControlBase = MmMapIoSpace(physicalAddress,
|
|
PAGE_SIZE * 16,
|
|
FALSE);
|
|
|
|
|
|
if (HalpIoControlBase == NULL)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
|
|
}
|
|
BOOLEAN
|
|
HalpHandleExternalInterrupt(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID ServiceContext,
|
|
IN PVOID 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 that describes
|
|
the SIO device interrupts. Its function is to call the second
|
|
level interrupt dispatch routine and acknowledge the interrupt at the SIO
|
|
controller.
|
|
|
|
N.B. This routine in entered and left with external interrupts disabled.
|
|
|
|
|
|
Arguments:
|
|
|
|
Interrupt - Supplies a pointer to the interrupt object.
|
|
|
|
ServiceContext - Supplies a pointer to the SIO interrupt acknowledge
|
|
register.
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns the value returned from the second level routine.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSECONDARY_DISPATCH SioHandler;
|
|
PKINTERRUPT SioInterrupt;
|
|
USHORT interruptVector;
|
|
BOOLEAN returnValue;
|
|
UCHAR OldIrql;
|
|
USHORT Isr;
|
|
UCHAR Irql;
|
|
PUCHAR PIC_Address;
|
|
UCHAR PIC_Mask;
|
|
|
|
|
|
//
|
|
// Read the interrupt vector.
|
|
//
|
|
|
|
interruptVector = READ_REGISTER_UCHAR(HalpInterruptBase);
|
|
|
|
//
|
|
// check for spurious interrupt
|
|
//
|
|
|
|
if (interruptVector == SPURIOUS_VECTOR) {
|
|
|
|
WRITE_REGISTER_UCHAR(&((PEISA_CONTROL)HalpIoControlBase)->Interrupt1ControlPort0,
|
|
0x0B);
|
|
Isr = READ_REGISTER_UCHAR(&((PEISA_CONTROL)HalpIoControlBase)->Interrupt1ControlPort0);
|
|
if (!(Isr & 0x80)) {
|
|
|
|
//
|
|
// Spurious interrupt
|
|
//
|
|
|
|
#if DBG
|
|
//DbgPrint("A spurious interrupt occurred. \n");
|
|
HalpSpuriousInterruptCount++;
|
|
#endif
|
|
return(0);
|
|
|
|
}
|
|
}
|
|
|
|
#if defined(SOFT_HDD_LAMP)
|
|
|
|
if ( HalpMassStorageControllerVectors & (1 << interruptVector) ) {
|
|
//
|
|
// On any Mass Storage Controller interrupt, light the HDD lamp.
|
|
// The system timer routines will turn it off again in a little
|
|
// while.
|
|
//
|
|
|
|
if ( !HalpHddLamp.Count ) {
|
|
*(PUCHAR)((PUCHAR)HalpIoControlBase + HDD_LAMP_PORT) = 1;
|
|
}
|
|
HalpHddLamp.Count = SOFT_HDD_TICK_COUNT;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// raise irql
|
|
//
|
|
|
|
Irql = VectorToIrqlTable[interruptVector];
|
|
|
|
//
|
|
// Raise Irql to appropriate device level
|
|
//
|
|
|
|
OldIrql = PCR->CurrentIrql;
|
|
PCR->CurrentIrql = Irql;
|
|
|
|
HalpEnableInterrupts();
|
|
|
|
//
|
|
// Dispatch to the secondary interrupt service routine.
|
|
//
|
|
|
|
SioHandler = (PSECONDARY_DISPATCH)
|
|
PCR->InterruptRoutine[DEVICE_VECTORS + interruptVector];
|
|
SioInterrupt = CONTAINING_RECORD(SioHandler,
|
|
KINTERRUPT,
|
|
DispatchCode[0]);
|
|
|
|
returnValue = SioHandler(SioInterrupt,
|
|
SioInterrupt->ServiceContext,
|
|
TrapFrame
|
|
);
|
|
|
|
//
|
|
// Dismiss the interrupt in the SIO interrupt controllers. We disable
|
|
// interrupts first so we won't get multiple interrupts at this level
|
|
// without popping the stack.
|
|
//
|
|
|
|
HalpDisableInterrupts();
|
|
|
|
//
|
|
// If this is a cascaded interrupt then the interrupt must be dismissed in
|
|
// both controllers.
|
|
//
|
|
|
|
if (interruptVector & 0x08) {
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2ControlPort0,
|
|
NONSPECIFIC_END_OF_INTERRUPT
|
|
);
|
|
|
|
}
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1ControlPort0,
|
|
NONSPECIFIC_END_OF_INTERRUPT
|
|
);
|
|
|
|
//
|
|
// Lower IRQL but leave external interrupts disabled.
|
|
// Return to caller with interrupts disabled.
|
|
//
|
|
|
|
PCR->CurrentIrql = OldIrql;
|
|
|
|
//
|
|
// Partial lazy irql - if hardware irql mask isn't what is should be
|
|
// set it and the interrupt controllers mask.
|
|
//
|
|
|
|
if (HALPCR->HardPriority > PCR->CurrentIrql) {
|
|
|
|
HALPCR->HardPriority = OldIrql;
|
|
|
|
PIC_Mask = HalpSioInterrupt1Mask | (Halp8259MaskTable[OldIrql] & 0x00FF);
|
|
PIC_Address = &(ISA_CONTROL->Interrupt1ControlPort1);
|
|
WRITE_REGISTER_UCHAR(PIC_Address, PIC_Mask);
|
|
|
|
//
|
|
// Get contoller 2 interrupt mask
|
|
//
|
|
|
|
PIC_Mask = HalpSioInterrupt2Mask | (Halp8259MaskTable[OldIrql] >> 8 );
|
|
PIC_Address = &(ISA_CONTROL->Interrupt2ControlPort1);
|
|
WRITE_REGISTER_UCHAR(PIC_Address, PIC_Mask);
|
|
}
|
|
|
|
|
|
return(returnValue);
|
|
|
|
}
|
|
|
|
VOID
|
|
HalpDisableSioInterrupt(
|
|
IN ULONG Vector
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function Disables the SIO interrupt.
|
|
|
|
Arguments:
|
|
|
|
Vector - Supplies the vector of the ESIA interrupt that is Disabled.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Calculate the SIO interrupt vector.
|
|
//
|
|
|
|
Vector -= DEVICE_VECTORS;
|
|
|
|
//
|
|
// Determine if this vector is for interrupt controller 1 or 2.
|
|
//
|
|
|
|
if (Vector & 0x08) {
|
|
|
|
//
|
|
// The interrupt is in controller 2.
|
|
//
|
|
|
|
Vector &= 0x7;
|
|
|
|
HalpSioInterrupt2Mask |= (UCHAR) 1 << Vector;
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2ControlPort1,
|
|
HalpSioInterrupt2Mask
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The interrupt is in controller 1.
|
|
//
|
|
|
|
Vector &= 0x7;
|
|
|
|
HalpSioInterrupt1Mask |= (ULONG) 1 << Vector;
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1ControlPort1,
|
|
HalpSioInterrupt1Mask
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
HalpIsaMapTransfer(
|
|
IN PADAPTER_OBJECT AdapterObject,
|
|
IN ULONG Offset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN WriteToDevice
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function programs the SIO DMA controller for a transfer.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Supplies the DMA adapter object to be programed.
|
|
|
|
Offset - Supplies the logical address to use for the transfer.
|
|
|
|
Length - Supplies the length of the transfer in bytes.
|
|
|
|
WriteToDevice - Indicates the direction of the transfer.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PUCHAR BytePtr;
|
|
UCHAR adapterMode;
|
|
UCHAR dataByte;
|
|
KIRQL Irql;
|
|
|
|
|
|
ASSERT(Offset >= IO_CONTROL_PHYSICAL_BASE);
|
|
|
|
adapterMode = AdapterObject->AdapterMode;
|
|
|
|
//
|
|
// Check to see if this request is for a master I/O card.
|
|
//
|
|
|
|
if (((PDMA_EISA_MODE) &adapterMode)->RequestMode == CASCADE_REQUEST_MODE) {
|
|
|
|
//
|
|
// Set the mode, Disable the request and return.
|
|
//
|
|
|
|
if (AdapterObject->AdapterNumber == 1) {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA1_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
//
|
|
// Unmask the DMA channel.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | AdapterObject->ChannelNumber)
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA2_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
//
|
|
// Unmask the DMA channel.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | AdapterObject->ChannelNumber)
|
|
);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
//
|
|
// Determine the mode based on the transfer direction.
|
|
//
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->TransferType = (UCHAR) (WriteToDevice ?
|
|
WRITE_TRANSFER : READ_TRANSFER);
|
|
|
|
BytePtr = (PUCHAR) &Offset;
|
|
|
|
if (AdapterObject->Width16Bits) {
|
|
|
|
//
|
|
// If this is a 16 bit transfer then adjust the length and the address
|
|
// for the 16 bit DMA mode.
|
|
//
|
|
|
|
Length >>= 1;
|
|
|
|
//
|
|
// In 16 bit DMA mode the low 16 bits are shifted right one and the
|
|
// page register value is unchanged. So save the page register value
|
|
// and shift the logical address then restore the page value.
|
|
//
|
|
|
|
dataByte = BytePtr[2];
|
|
Offset >>= 1;
|
|
BytePtr[2] = dataByte;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// grab the spinlock for the system DMA controller
|
|
//
|
|
|
|
KeAcquireSpinLock( &AdapterObject->MasterAdapter->SpinLock, &Irql );
|
|
|
|
//
|
|
// Determine the controller number based on the Adapter number.
|
|
//
|
|
|
|
if (AdapterObject->AdapterNumber == 1) {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA1_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseAddress,
|
|
BytePtr[0]
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseAddress,
|
|
BytePtr[1]
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpIoControlBase)->DmaPageLowPort) +
|
|
(ULONG)AdapterObject->PagePort,
|
|
BytePtr[2]
|
|
);
|
|
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpIoControlBase)->DmaPageHighPort) +
|
|
(ULONG)AdapterObject->PagePort,
|
|
BytePtr[3]
|
|
);
|
|
|
|
//
|
|
// Notify DMA chip of the length to transfer.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount,
|
|
(UCHAR) ((Length - 1) & 0xff)
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount,
|
|
(UCHAR) ((Length - 1) >> 8)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the DMA chip to read or write mode; and unmask it.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | AdapterObject->ChannelNumber)
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This request is for DMA controller 2
|
|
//
|
|
|
|
PDMA2_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseAddress,
|
|
BytePtr[0]
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseAddress,
|
|
BytePtr[1]
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpIoControlBase)->DmaPageLowPort) +
|
|
(ULONG)AdapterObject->PagePort,
|
|
BytePtr[2]
|
|
);
|
|
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpIoControlBase)->DmaPageHighPort) +
|
|
(ULONG)AdapterObject->PagePort,
|
|
BytePtr[3]
|
|
);
|
|
|
|
//
|
|
// Notify DMA chip of the length to transfer.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount,
|
|
(UCHAR) ((Length - 1) & 0xff)
|
|
);
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount,
|
|
(UCHAR) ((Length - 1) >> 8)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the DMA chip to read or write mode; and unmask it.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | AdapterObject->ChannelNumber)
|
|
);
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock (&AdapterObject->MasterAdapter->SpinLock, Irql);
|
|
|
|
}
|
|
|
|
VOID
|
|
HalpEnableSioInterrupt(
|
|
IN ULONG Vector,
|
|
IN KINTERRUPT_MODE InterruptMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function enables the SIO interrupt and sets
|
|
the level/edge register to the requested value.
|
|
|
|
Arguments:
|
|
|
|
Vector - Supplies the vector of the interrupt that is enabled.
|
|
|
|
InterruptMode - Supplies the mode of the interrupt; LevelSensitive or
|
|
Latched.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// Calculate the SIO interrupt vector.
|
|
//
|
|
|
|
Vector -= DEVICE_VECTORS;
|
|
|
|
//
|
|
// Determine if this vector is for interrupt controller 1 or 2.
|
|
//
|
|
|
|
if (Vector & 0x08) {
|
|
|
|
//
|
|
// The interrupt is in controller 2.
|
|
//
|
|
|
|
Vector &= 0x7;
|
|
|
|
HalpSioInterrupt2Mask &= (UCHAR) ~(1 << Vector);
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2ControlPort1,
|
|
HalpSioInterrupt2Mask
|
|
);
|
|
|
|
//
|
|
// Set the level/edge control register.
|
|
//
|
|
|
|
if (InterruptMode == LevelSensitive) {
|
|
|
|
HalpSioInterrupt2Level |= (UCHAR) (1 << Vector);
|
|
|
|
} else {
|
|
|
|
HalpSioInterrupt2Level &= (UCHAR) ~(1 << Vector);
|
|
|
|
}
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2EdgeLevel,
|
|
HalpSioInterrupt2Level
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// The interrupt is in controller 1.
|
|
//
|
|
|
|
Vector &= 0x7;
|
|
|
|
HalpSioInterrupt1Mask &= (UCHAR) ~(1 << Vector);
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1ControlPort1,
|
|
HalpSioInterrupt1Mask
|
|
);
|
|
|
|
//
|
|
// Set the level/edge control register.
|
|
//
|
|
|
|
if (InterruptMode == LevelSensitive) {
|
|
|
|
HalpSioInterrupt1Level |= (UCHAR) (1 << Vector);
|
|
|
|
} else {
|
|
|
|
HalpSioInterrupt1Level &= (UCHAR) ~(1 << Vector);
|
|
|
|
}
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1EdgeLevel,
|
|
HalpSioInterrupt1Level
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
PADAPTER_OBJECT
|
|
HalpAllocateIsaAdapter(
|
|
IN PDEVICE_DESCRIPTION DeviceDescriptor,
|
|
OUT PULONG NumberOfMapRegisters
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates an ISA adapter object according to the
|
|
specification supplied in the device description. The necessary device
|
|
descriptor information is saved. If there is
|
|
no existing adapter object for this channel then a new one is allocated.
|
|
The saved information in the adapter object is used to set the various DMA
|
|
modes when the channel is allocated or a map transfer is done.
|
|
|
|
Arguments:
|
|
|
|
DeviceDescription - Supplies the description of the device which want to
|
|
use the DMA adapter.
|
|
|
|
NumberofMapRegisters - number of map registers required for the adapter
|
|
object created
|
|
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the newly created adapter object or NULL if one
|
|
cannot be created.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PADAPTER_OBJECT adapterObject;
|
|
PVOID adapterBaseVa;
|
|
ULONG channelNumber;
|
|
ULONG numberOfMapRegisters;
|
|
ULONG controllerNumber;
|
|
DMA_EXTENDED_MODE extendedMode;
|
|
UCHAR adapterMode;
|
|
BOOLEAN useChannel;
|
|
ULONG maximumLength;
|
|
|
|
//
|
|
// Determine if the the channel number is important. Master cards
|
|
// do not use a channel number.
|
|
//
|
|
|
|
|
|
if ((DeviceDescriptor->Master) && (DeviceDescriptor->InterfaceType != Isa)) {
|
|
|
|
useChannel = FALSE;
|
|
|
|
} else {
|
|
|
|
useChannel = TRUE;
|
|
}
|
|
|
|
//
|
|
// Channel 4 cannot be used since it is used for chaining. Return null if
|
|
// it is requested.
|
|
//
|
|
|
|
if ((DeviceDescriptor->DmaChannel == 4 ||
|
|
DeviceDescriptor->DmaChannel > 7) && useChannel) {
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Limit the maximum length to 2 GB this is done so that the BYTES_TO_PAGES
|
|
// macro works correctly.
|
|
//
|
|
|
|
maximumLength = DeviceDescriptor->MaximumLength & 0x7fffffff;
|
|
|
|
//
|
|
// Determine the number of map registers for this device.
|
|
//
|
|
|
|
if (DeviceDescriptor->ScatterGather &&
|
|
!(DeviceDescriptor->InterfaceType == Isa &&
|
|
DeviceDescriptor->Master)) {
|
|
|
|
|
|
//
|
|
// Scatter gather not supported in SIO
|
|
//
|
|
|
|
if (!DeviceDescriptor->Master)
|
|
|
|
//
|
|
// one map register will be required when the the SIO supports this
|
|
//
|
|
// numberOfMapRegisters = 1;
|
|
|
|
return NULL;
|
|
|
|
|
|
//
|
|
// Since the device support scatter/Gather then map registers are not
|
|
// required.
|
|
//
|
|
|
|
numberOfMapRegisters = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Determine the number of map registers required based on the maximum
|
|
// transfer length, up to a maximum number.
|
|
//
|
|
|
|
numberOfMapRegisters = BYTES_TO_PAGES(maximumLength)
|
|
+ 1;
|
|
numberOfMapRegisters = numberOfMapRegisters > MAXIMUM_ISA_MAP_REGISTER ?
|
|
MAXIMUM_ISA_MAP_REGISTER : numberOfMapRegisters;
|
|
|
|
//
|
|
// If the device is not a master then it only needs one map register
|
|
// and does scatter/Gather.
|
|
//
|
|
|
|
if (DeviceDescriptor->ScatterGather && !DeviceDescriptor->Master) {
|
|
|
|
numberOfMapRegisters = 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the channel number number.
|
|
//
|
|
|
|
channelNumber = DeviceDescriptor->DmaChannel & 0x03;
|
|
|
|
//
|
|
// Set the adapter base address to the Base address register and controller
|
|
// number.
|
|
//
|
|
|
|
if (!(DeviceDescriptor->DmaChannel & 0x04)) {
|
|
|
|
controllerNumber = 1;
|
|
adapterBaseVa = (PVOID) &((PEISA_CONTROL) HalpIoControlBase)->Dma1BasePort;
|
|
|
|
} else {
|
|
|
|
controllerNumber = 2;
|
|
adapterBaseVa = &((PEISA_CONTROL) HalpIoControlBase)->Dma2BasePort;
|
|
|
|
}
|
|
|
|
//
|
|
// Determine if a new adapter object is necessary. If so then allocate it.
|
|
//
|
|
|
|
if (useChannel && HalpIsaAdapter[DeviceDescriptor->DmaChannel] != NULL) {
|
|
|
|
adapterObject = HalpIsaAdapter[DeviceDescriptor->DmaChannel];
|
|
|
|
if (adapterObject->NeedsMapRegisters) {
|
|
|
|
if (numberOfMapRegisters > adapterObject->MapRegistersPerChannel) {
|
|
|
|
adapterObject->MapRegistersPerChannel = numberOfMapRegisters;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Allocate an adapter object.
|
|
//
|
|
|
|
adapterObject = (PADAPTER_OBJECT) HalpAllocateAdapter(
|
|
numberOfMapRegisters,
|
|
adapterBaseVa,
|
|
NULL
|
|
);
|
|
|
|
if (adapterObject == NULL) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
if (useChannel) {
|
|
|
|
HalpIsaAdapter[DeviceDescriptor->DmaChannel] = adapterObject;
|
|
|
|
}
|
|
|
|
//
|
|
// Set the maximum number of map registers for this channel bus on
|
|
// the number requested and the type of device.
|
|
//
|
|
|
|
if (numberOfMapRegisters) {
|
|
|
|
//
|
|
// The speicified number of registers are actually allowed to be
|
|
// allocated.
|
|
//
|
|
|
|
adapterObject->MapRegistersPerChannel = numberOfMapRegisters;
|
|
|
|
//
|
|
// Increase the commitment for the map registers.
|
|
//
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
|
|
//
|
|
// Master I/O devices use several sets of map registers double
|
|
// their commitment.
|
|
//
|
|
|
|
MasterAdapterObject->CommittedMapRegisters +=
|
|
numberOfMapRegisters * 2;
|
|
|
|
} else {
|
|
|
|
MasterAdapterObject->CommittedMapRegisters +=
|
|
numberOfMapRegisters;
|
|
|
|
}
|
|
|
|
//
|
|
// If the committed map registers is signicantly greater than the
|
|
// number allocated then grow the map buffer.
|
|
//
|
|
|
|
if (MasterAdapterObject->CommittedMapRegisters >
|
|
MasterAdapterObject->NumberOfMapRegisters &&
|
|
MasterAdapterObject->CommittedMapRegisters -
|
|
MasterAdapterObject->NumberOfMapRegisters >
|
|
MAXIMUM_ISA_MAP_REGISTER ) {
|
|
|
|
HalpGrowMapBuffers(
|
|
MasterAdapterObject,
|
|
INCREMENT_MAP_BUFFER_SIZE
|
|
);
|
|
}
|
|
|
|
adapterObject->NeedsMapRegisters = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// No real map registers were allocated. If this is a master
|
|
// device, then the device can have as may registers as it wants.
|
|
//
|
|
|
|
adapterObject->NeedsMapRegisters = FALSE;
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
|
|
adapterObject->MapRegistersPerChannel = BYTES_TO_PAGES(
|
|
maximumLength
|
|
)
|
|
+ 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The device only gets one register. It must call
|
|
// IoMapTransfer repeatedly to do a large transfer.
|
|
//
|
|
|
|
adapterObject->MapRegistersPerChannel = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
adapterObject->ScatterGather = DeviceDescriptor->ScatterGather;
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
|
|
adapterObject->MasterDevice = TRUE;
|
|
|
|
} else {
|
|
|
|
adapterObject->MasterDevice = FALSE;
|
|
|
|
}
|
|
|
|
|
|
if (DeviceDescriptor->Master && (DeviceDescriptor->InterfaceType == Isa)) {
|
|
|
|
adapterObject->IsaBusMaster = TRUE;
|
|
|
|
} else {
|
|
|
|
adapterObject->IsaBusMaster = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// If the channel number is not used then we are finished. The rest of
|
|
// the work deals with channels.
|
|
//
|
|
|
|
*NumberOfMapRegisters = adapterObject->MapRegistersPerChannel;
|
|
|
|
if (!useChannel) {
|
|
adapterObject->PagePort = (PVOID) (~0x0);
|
|
((PDMA_EISA_MODE) &adapterMode)->RequestMode = CASCADE_REQUEST_MODE;
|
|
return(adapterObject);
|
|
}
|
|
|
|
//
|
|
// Setup the pointers to all the random registers.
|
|
//
|
|
|
|
adapterObject->ChannelNumber = (UCHAR) channelNumber;
|
|
|
|
if (controllerNumber == 1) {
|
|
|
|
switch ((UCHAR)channelNumber) {
|
|
|
|
case 0:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel0;
|
|
break;
|
|
|
|
case 1:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel1;
|
|
break;
|
|
|
|
case 2:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel2;
|
|
break;
|
|
|
|
case 3:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel3;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the adapter number.
|
|
//
|
|
|
|
adapterObject->AdapterNumber = 1;
|
|
|
|
//
|
|
// Save the extended mode register address.
|
|
//
|
|
|
|
adapterBaseVa =
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Dma1ExtendedModePort;
|
|
|
|
} else {
|
|
|
|
switch (channelNumber) {
|
|
case 1:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel5;
|
|
break;
|
|
|
|
case 2:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel6;
|
|
break;
|
|
|
|
case 3:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel7;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the adapter number.
|
|
//
|
|
|
|
adapterObject->AdapterNumber = 2;
|
|
|
|
//
|
|
// Save the extended mode register address.
|
|
//
|
|
adapterBaseVa =
|
|
&((PEISA_CONTROL) HalpIoControlBase)->Dma2ExtendedModePort;
|
|
|
|
}
|
|
|
|
|
|
adapterObject->Width16Bits = FALSE;
|
|
|
|
|
|
//
|
|
// Initialzie the extended mode port.
|
|
//
|
|
|
|
*((PUCHAR) &extendedMode) = 0;
|
|
extendedMode.ChannelNumber = (UCHAR) channelNumber;
|
|
|
|
switch (DeviceDescriptor->DmaSpeed) {
|
|
case Compatible:
|
|
extendedMode.TimingMode = COMPATIBLITY_TIMING;
|
|
break;
|
|
|
|
case TypeA:
|
|
extendedMode.TimingMode = TYPE_A_TIMING;
|
|
break;
|
|
|
|
case TypeB:
|
|
extendedMode.TimingMode = TYPE_B_TIMING;
|
|
break;
|
|
|
|
case TypeC:
|
|
extendedMode.TimingMode = BURST_TIMING;
|
|
break;
|
|
|
|
default:
|
|
ObDereferenceObject( adapterObject );
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
switch (DeviceDescriptor->DmaWidth) {
|
|
case Width8Bits:
|
|
|
|
extendedMode.TransferSize = BY_BYTE_8_BITS;
|
|
break;
|
|
|
|
case Width16Bits:
|
|
extendedMode.TransferSize = BY_BYTE_16_BITS;
|
|
|
|
//
|
|
// Note Width16bits should not be set here because there is no need
|
|
// to shift the address and the transfer count.
|
|
//
|
|
|
|
break;
|
|
|
|
default:
|
|
ObDereferenceObject( adapterObject );
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
//
|
|
// bit 2 in the extended mode register must be set to 1 for ISA busmastering to work
|
|
// correctly on Firecoral
|
|
//
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
extendedMode.TransferSize |= 1;
|
|
}
|
|
|
|
WRITE_REGISTER_UCHAR( adapterBaseVa, *((PUCHAR) &extendedMode));
|
|
|
|
|
|
//
|
|
// Initialize the adapter mode register value to the correct parameters,
|
|
// and save them in the adapter object.
|
|
//
|
|
|
|
adapterMode = 0;
|
|
((PDMA_EISA_MODE) &adapterMode)->Channel = adapterObject->ChannelNumber;
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->RequestMode = CASCADE_REQUEST_MODE;
|
|
|
|
//
|
|
// Set the mode, and enable the request.
|
|
//
|
|
|
|
if (adapterObject->AdapterNumber == 1) {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA1_CONTROL dmaControl;
|
|
|
|
dmaControl = adapterObject->AdapterBaseVa;
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
//
|
|
// DMA high page must be set to 0x80 for ISA bus masters to work on
|
|
// FireCoral.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpIoControlBase)->DmaPageHighPort) +
|
|
(ULONG)adapterObject->PagePort,
|
|
0x80
|
|
);
|
|
|
|
//
|
|
// Unmask the DMA channel.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | adapterObject->ChannelNumber)
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA2_CONTROL dmaControl;
|
|
|
|
dmaControl = adapterObject->AdapterBaseVa;
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
//
|
|
// DMA high page must be set to 0x80 for ISA bus masters to work on
|
|
// FireCoral.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpIoControlBase)->DmaPageHighPort) +
|
|
(ULONG)adapterObject->PagePort,
|
|
0x80
|
|
);
|
|
|
|
//
|
|
// Unmask the DMA channel.
|
|
//
|
|
|
|
WRITE_REGISTER_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | adapterObject->ChannelNumber)
|
|
);
|
|
|
|
}
|
|
|
|
} else if (DeviceDescriptor->DemandMode) {
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->RequestMode = DEMAND_REQUEST_MODE;
|
|
|
|
} else {
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->RequestMode = SINGLE_REQUEST_MODE;
|
|
|
|
}
|
|
|
|
if (DeviceDescriptor->AutoInitialize) {
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->AutoInitialize = 1;
|
|
|
|
}
|
|
|
|
adapterObject->AdapterMode = adapterMode;
|
|
|
|
return(adapterObject);
|
|
}
|
|
|
|
ULONG
|
|
HalReadDmaCounter(
|
|
IN PADAPTER_OBJECT AdapterObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function reads the DMA counter and returns the number of bytes left
|
|
to be transfered.
|
|
|
|
Arguments:
|
|
|
|
AdapterObject - Supplies a pointer to the adapter object to be read.
|
|
|
|
Return Value:
|
|
|
|
Returns the number of bytes still be be transfered.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG count;
|
|
ULONG high;
|
|
|
|
|
|
//
|
|
// Determine the controller number based on the Adapter number.
|
|
//
|
|
|
|
if (AdapterObject->AdapterNumber == 1) {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA1_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
//
|
|
// Initialize count to a value which will not match.
|
|
//
|
|
|
|
count = 0xFFFF00;
|
|
|
|
//
|
|
// Loop until the same high byte is read twice.
|
|
//
|
|
|
|
do {
|
|
|
|
high = count;
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
//
|
|
// Read the current DMA count.
|
|
//
|
|
|
|
count = READ_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount
|
|
);
|
|
|
|
count |= READ_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount
|
|
) << 8;
|
|
|
|
} while ((count & 0xFFFF00) != (high & 0xFFFF00));
|
|
|
|
} else {
|
|
|
|
//
|
|
// This request is for DMA controller 2
|
|
//
|
|
|
|
PDMA2_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
//
|
|
// Initialize count to a value which will not match.
|
|
//
|
|
|
|
count = 0xFFFF00;
|
|
|
|
//
|
|
// Loop until the same high byte is read twice.
|
|
//
|
|
|
|
do {
|
|
|
|
high = count;
|
|
|
|
WRITE_REGISTER_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
//
|
|
// Read the current DMA count.
|
|
//
|
|
|
|
count = READ_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount
|
|
);
|
|
|
|
count |= READ_REGISTER_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount
|
|
) << 8;
|
|
|
|
} while ((count & 0xFFFF00) != (high & 0xFFFF00));
|
|
|
|
}
|
|
|
|
//
|
|
// The DMA counter has a bias of one and can only be 16 bit long.
|
|
//
|
|
|
|
count = (count + 1) & 0xFFFF;
|
|
|
|
|
|
|
|
return(count);
|
|
}
|
|
|
|
|
|
VOID
|
|
HalpHandleIoError (
|
|
VOID
|
|
)
|
|
|
|
{
|
|
|
|
UCHAR StatusByte;
|
|
|
|
|
|
//
|
|
// Read NMI status
|
|
//
|
|
|
|
StatusByte = READ_REGISTER_UCHAR(&((PEISA_CONTROL) HalpIoControlBase)->NmiStatus);
|
|
|
|
//
|
|
// Test for PCI bus error
|
|
//
|
|
|
|
if (StatusByte & 0x40) {
|
|
HalDisplayString ("NMI: IOCHK\n");
|
|
}
|
|
|
|
//
|
|
// Test for ISA IOCHK
|
|
//
|
|
|
|
if (StatusByte & 0x80) {
|
|
HalDisplayString ("NMI: PCI System Error\n");
|
|
}
|
|
|
|
}
|