2020-09-30 17:12:29 +02:00

1762 lines
38 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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.
Module Name:
pxsiosup.c
Abstract:
The module provides the PCI ISA bridge support.
Author:
Jim Wooldridge (jimw@vnet.ibm.com)
Revision History:
--*/
#include "halp.h"
#include "pxsystyp.h"
#include "eisa.h"
#include "pxsiosup.h"
#include "pxpcisup.h"
#include "pxmemctl.h"
#include "bugcodes.h"
#define SioId 0x04848086 // UMC 8886
PVOID HalpPciIsaBridgeConfigBase;
extern USHORT Halp8259MaskTable[];
extern PADAPTER_OBJECT MasterAdapterObject;
VOID HalpUpdate8259(KIRQL Irql);
//
// Define the context structure for use by the interrupt routine.
//
typedef BOOLEAN (*PSECONDARY_DISPATCH)(
PVOID InterruptRoutine,
PVOID ServiceContext,
PVOID TrapFrame
);
//
// Declare the interrupt 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 interrupt structure for machine checks
//
KINTERRUPT HalpMachineCheckInterrupt;
//
// Declare the interrupt structure for the clock interrupt
//
KINTERRUPT HalpDecrementerInterrupt;
//
// Map the interrupt controllers priority scheme to NT IRQL values.
// The SIO prioritizes IRQs as follows:
// IRQ0, IRQ1, IRQ8 ... IRQ15, IRQ3, IRQ4 ... IRQ7.
//
// NOTE: The following table must be coordinated with the entries
// in Halp8259MaskTable in PXIRQL.C
//
KIRQL VectorToIrql[16] = {
// IRQL Vector
// ---- ------
26, // 0
25, // 1
24, // 2
15, // 3
14, // 4
13, // 5
12, // 6
11, // 7
23, // 8
22, // 9
21, // 10
20, // 11
19, // 12
18, // 13
17, // 14
16 }; // 15
KIRQL HalpTranslateVectorToIrql(
IN ULONG Vector
)
{ // It is assumed that the caller has checked that Vector is valid (0..15)
return VectorToIrql[Vector];
}
//
// 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 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 registers
// and level\edge control registers.
//
USHORT HalpSioInterruptLevel = 0x0000; // Default to edge-sensitive
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:
--*/
{ UCHAR DataByte;
ULONG Vector;
//
// 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 initialization control word sets the interrupt 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 third initialization control word set the controls for slave mode.
// The master ICW3 uses bit position and the slave ICW3 uses a numeric.
//
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
);
//
// Mask all 8259 interrupts (except the Slave input)
//
for (Vector=0; Vector<= HIGHEST_8259_VECTOR; Vector++) {
HalpDisableSioInterrupt(Vector + DEVICE_VECTORS);
}
HalpEnableSioInterrupt(SLAVE_IRQL_LEVEL + DEVICE_VECTORS, Latched);
//
// 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.
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;
//
// 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 initialized.
//
KeRaiseIrql(CLOCK2_LEVEL, &oldIrql);
//
// Initialize any planar registers
//
HalpInitPlanar();
//
// 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
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, then 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 is 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;
//
// Read the interrupt vector.
//
interruptVector = READ_REGISTER_UCHAR(HalpInterruptBase);
//
// check for nmi interrupt before we raise irql since we would raise to a
// bogus level
//
if (interruptVector == 0xFF) {
HalpHandleMachineCheck(NULL, NULL);
}
//
// 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 (interruptVector > HIGHEST_8259_VECTOR) {
#if DBG
DbgPrint("A bogus interrupt (0x%02x) occurred. \n", interruptVector);
HalpBogusInterruptCount++;
#endif
return (0);
}
//
// Translate vector to IRQL and raise IRQL
//
Irql = HalpTranslateVectorToIrql(interruptVector);
KeRaiseIrql( Irql, &OldIrql);
//
// 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.
//
//
// 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 disable external interrupts.
// Return to caller with interrupts disabled.
//
HalpResetIrqlAfterInterrupt(OldIrql);
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.
--*/
{ USHORT MaskBit, i;
//
// Calculate the SIO interrupt vector.
//
Vector -= DEVICE_VECTORS;
if (Vector <= HIGHEST_8259_VECTOR) {
//
// Generate 8259 mask
//
MaskBit = (USHORT) (1 << Vector);
//
// Set the mask bit in Halp8259MaskTable
//
for (i = 0; i <= 31; i++) {
Halp8259MaskTable[i] |= MaskBit;
}
// Write new mask values to 8259s
HalpUpdate8259(PCR->CurrentIrql);
}
}
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.
--*/
{ USHORT MaskBit, Irql, i;
//
// Calculate the SIO interrupt vector.
//
Vector -= DEVICE_VECTORS;
if (Vector <= HIGHEST_8259_VECTOR) {
//
// Generate 8259 mask
//
MaskBit = (USHORT) ~(1 << Vector);
//
// Force interrupts to be latched (edge-triggered) if Big Bend
//
if(HalpSystemType == MOTOROLA_BIG_BEND)
InterruptMode = Latched;
//
// Set the level/edge control register.
//
if (InterruptMode == LevelSensitive) {
HalpSioInterruptLevel |= ~MaskBit;
} else {
HalpSioInterruptLevel &= MaskBit;
}
if (Vector & 0x08) {
//
// The interrupt is in controller 2.
//
WRITE_REGISTER_UCHAR(
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt2EdgeLevel,
(UCHAR) (HalpSioInterruptLevel >> 8)
);
} else {
//
// The interrupt is in controller 1.
//
WRITE_REGISTER_UCHAR(
&((PEISA_CONTROL) HalpIoControlBase)->Interrupt1EdgeLevel,
(UCHAR) HalpSioInterruptLevel
);
}
//
// Clear mask bit in Halp8259MaskTable
//
Irql = VectorToIrql[Vector];
for (i = 0; i < Irql; i++) {
Halp8259MaskTable[i] &= MaskBit;
}
// Write new mask values to 8259s
HalpUpdate8259(PCR->CurrentIrql);
}
}
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 specified 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 significantly 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;
//
// Initialize 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);
}
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 );
//
// Unmask the DMA channel.
//
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->Mode, adapterMode );
//
// 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 transferred.
Arguments:
AdapterObject - Supplies a pointer to the adapter object to be read.
Return Value:
Returns the number of bytes still to be transferred.
--*/
{
ULONG count=0;
ULONG high;
if (AdapterObject->PagePort) {
//
// 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");
}
}