/*++
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 (jimw@vnet.ibm.com)
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");
}
}