NT4/private/ntos/dd/pcmcia/pcicsup.c

1827 lines
51 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
pcicsup.c
Abstract:
This module supplies functions that control the 82365SL chip. In turn,
these functions are abstracted out to the main PCMCIA support module.
Author(s):
Bob Rinne (BobRi) 3-Aug-1994
Jeff McLeman (mcleman@zso.dec.com)
Revisions:
6-Apr-95
Modified for databook support changes - John Keys Databook
--*/
#include "ntddk.h"
#include "stdio.h"
#include "pcmcia.h"
#include "card.h"
#include "extern.h"
#include "tuple.h"
#ifdef POOL_TAGGING
#undef ExAllocatePool
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'cicP')
#endif
PUCHAR PcicCisBufferBase;
ULONG PcicPhysicalBase;
ULONG PcicStallCounter = 5000;
ULONG PcicStallPower = 20000;
PCMCIA_CTRL_BLOCK PcicSupportFns = {
PcicInitializePcmciaSocket,
PcicReadAttributeMemory,
PcicDetectCardInSocket,
PcicDetectCardChanged,
PcicProcessConfigureRequest,
PcicEnableControllerInterrupt,
PcicPCCardReady,
PcicSetPower,
PcicGetRegisters
};
//
// Routine definitions to satisfy INIT pragma statements.
//
BOOLEAN
PcicSearchPci(
PDEVICE_EXTENSION DeviceExtension,
ULONG BusNumber,
PULONG IoPortLocation
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,PcicDetect)
#pragma alloc_text(INIT,PcicInitializePcmciaSocket)
#pragma alloc_text(INIT,PcicSearchPci)
#endif
#if DBG
VOID
PcicDumpSocketState(
IN PUCHAR Base,
IN USHORT Socket
)
/*++
Routine Description:
Debug routine to print the registers to the debugger.
Arguments:
Base - the I/O port base.
Socket - the socket offset
Return Value:
None
--*/
{
UCHAR registers[0x40];
ULONG longValue;
UCHAR i;
for (i = 0; i < 0x40; i++) {
registers[i] = PcicReadController(Base, Socket, i);
}
DebugPrint((PCMCIA_DUMP_SOCKET,
"%2x %2x %2x %2x %2x %2x %2x\n",
registers[0],
registers[1],
registers[2],
registers[3],
registers[4],
registers[5],
registers[6]));
DebugPrint((PCMCIA_DUMP_SOCKET, "Revision %2x, Interface %2x, Power %2x, Enable Window %2x\n",
registers[0],
registers[1],
registers[2],
registers[6]));
DebugPrint((PCMCIA_DUMP_SOCKET,
"I/O 0 Start - Stop %4x - %4x\n",
(registers[9] << 8) | registers[8],
(registers[0xb] << 8) | registers[0xa]));
DebugPrint((PCMCIA_DUMP_SOCKET,
"I/O 1 Start - Stop %4x - %4x\n",
(registers[0xd] << 8) | registers[0xc],
(registers[0xf] << 8) | registers[0xe]));
for (i = 0; i <= 4; i++) {
DebugPrint((PCMCIA_DUMP_SOCKET,
"Memory Address %d Stop/Start/Card %4x/%4x/%4x\n",
i,
(registers[(i*8)+0x11] << 8) | registers[(i*8)]+0x10,
(registers[(i*8)+0x13] << 8) | registers[(i*8)]+0x12,
(registers[(i*8)+0x15] << 8) | registers[(i*8)]+0x14));
}
}
#endif
VOID
PcicEnableControllerInterrupt(
IN PSOCKET SocketPtr,
IN ULONG Irq
)
/*++
Routine Description:
Enable card detect/card ready interrupt.
Arguments:
SocketPtr - socket information
Irq - the interrupt value to set.
Return Value:
None
--*/
{
UCHAR byte;
byte = (UCHAR) ((Irq << 4) & 0x00ff);
byte |= 0x08; // Card detect & ready enable
PcicWriteController(SocketPtr->AddressPort,
SocketPtr->RegisterOffset,
PCIC_CARD_INT_CONFIG,
byte);
}
VOID
PcicSetPower(
IN PSOCKET SocketPtr,
IN BOOLEAN Enable
)
/*++
Routine Description:
Set power to the specified socket.
Arguments:
SocketPtr - the socket to set
Enable - TRUE means to set power - FALSE is to turn it off.
Return Value:
None
--*/
{
PUCHAR base;
UCHAR tmp;
USHORT socket;
//
// Turn on the power - then turn on output - this is two operations
// per the Intel 82365SL documentation.
//
base = SocketPtr->AddressPort;
socket = SocketPtr->RegisterOffset;
if (Enable) {
tmp = PcicReadController(base, socket, PCIC_PWR_RST);
if (SocketPtr->ElcController) {
tmp = 0x11; // vpp1 = vcc
} else {
tmp = 0x15; // vpp1 = vpp2 = vcc
}
PcicWriteController(base, socket, PCIC_PWR_RST, tmp);
if (SocketPtr->ElcController) {
tmp = 0xf1; // vpp1 = vcc
} else {
tmp = 0xf5; // vpp1 = vpp2 = vcc
}
PcicWriteController(base, socket, PCIC_PWR_RST, tmp);
//
// When power is enabled always stall to give the PCCARD
// a chance to react.
//
KeStallExecutionProcessor(PcicStallPower);
if (!PcicPCCardReady(SocketPtr)) {
DebugPrint((PCMCIA_PCCARD_READY,
"PCIC: PCCARD %x not ready after reset\n",
socket));
}
} else {
//
// Disable IRQ
//
tmp = PcicReadController(base, socket, PCIC_INTERRUPT);
tmp = tmp & 0xf0;
PcicWriteController(base, socket, PCIC_INTERRUPT, tmp);
//
// I/O windows were set up now disable them.
//
tmp = PcicReadController(base, socket, PCIC_ADD_WIN_ENA);
tmp &= 0x3f;
PcicWriteController(base, socket, PCIC_ADD_WIN_ENA, tmp);
//
// Memory windows were set up now disable them.
//
tmp = PcicReadController(base,
socket,
PCIC_ADD_WIN_ENA);
tmp &= 0xe0;
PcicWriteController(base, socket, PCIC_ADD_WIN_ENA, tmp);
PcicWriteController(base, socket, PCIC_PWR_RST, 0x00);
}
}
UCHAR SocketInitString[] = { 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
UCHAR SocketWindowInit[] = { 0xFF, 0x07, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x0f };
BOOLEAN
PcicInitializePcmciaSocket(
PSOCKET SocketPtr
)
/*++
Routine Description:
This routine will setup the 82365 into a state where the pcmcia support
module will be able to issue commands to read device tuples from the
cards in the sockets.
Arguments:
SocketPtr - socket specific information
Return Value:
TRUE if successful
FALSE if not successful
--*/
{
PDEVICE_EXTENSION deviceExtension;
PUCHAR base;
UCHAR tmp;
USHORT socket;
PSOCKET socketPtr;
UCHAR index;
UCHAR length;
UCHAR reg;
deviceExtension = SocketPtr->DeviceExtension;
PcicCisBufferBase = (PUCHAR)deviceExtension->AttributeMemoryBase;
PcicPhysicalBase = deviceExtension->PhysicalBase;
base = SocketPtr->AddressPort;
socket = SocketPtr->RegisterOffset;
//
// Starting from the POWER register, initialize this socket.
//
for (index = 2; index < 16; index++) {
tmp = SocketInitString[index];
PcicWriteController(base, socket, index, tmp);
}
//
// Do all of the memory windows.
//
reg = PCIC_MEM_ADD0_STRT_L;
for (length = 0; length < 5; length++) {
for (index = 0; index < 6; index++) {
tmp = SocketWindowInit[index];
PcicWriteController(base, socket, reg, tmp);
reg++;
}
reg += 2; // skip reserved registers.
}
//
// Say card is there
//
if (PcicDetectCardInSocket(SocketPtr)) {
//
// Turn on the power
//
PcicSetPower(SocketPtr, TRUE);
//
// reset PCCARD
//
tmp = PcicReadController(base, socket, PCIC_INTERRUPT);
tmp &= 0xbf; // turn off bit 6 (start reset)
PcicWriteController(base, socket, PCIC_INTERRUPT, tmp);
KeStallExecutionProcessor(PcicStallCounter);
tmp |= 0x40; // turn on bit 6 (stop reset)
PcicWriteController(base, socket, PCIC_INTERRUPT, tmp);
KeStallExecutionProcessor(PcicStallCounter);
if (!PcicPCCardReady(SocketPtr)) {
DebugPrint((PCMCIA_PCCARD_READY,
"PCIC: PCCARD %x not ready after reset\n",
socket));
}
SocketPtr->CardInSocket = TRUE;
}
return TRUE;
}
UCHAR
PcicReadController(
IN PUCHAR Base,
IN USHORT Socket,
IN UCHAR Register
)
/*++
Routine Description:
This routine will read a byte from the controller data port
Arguments:
Base -- The I/O port for the controller
Socket -- The socket in for the card being read
Register -- The register to be read
Return Value:
The data returned from the port.
--*/
{
UCHAR dataByte = 0;
//
// The stalls are needed by the IBM PPC systems. Do not
// remove them.
//
WRITE_PORT_UCHAR((PUCHAR)Base, (UCHAR)(Socket+Register));
KeStallExecutionProcessor(PcicStallCounter);
dataByte = READ_PORT_UCHAR((PUCHAR)Base + 1);
KeStallExecutionProcessor(PcicStallCounter);
return dataByte;
}
VOID
PcicWriteController(
IN PUCHAR Base,
IN USHORT Socket,
IN UCHAR Register,
IN UCHAR DataByte
)
/*++
Routine Description:
This routine will write a byte to the controller data port
Arguments:
Base -- The I/O port for the controller
Socket -- The socket in for the card being read
Register -- The register to be read
DataByte -- Data to be written
Return Value:
None
--*/
{
//
// The stalls are needed by the IBM PPC systems. Do not
// remove them.
//
WRITE_PORT_UCHAR((PUCHAR)Base, (UCHAR)(Socket+Register));
KeStallExecutionProcessor(PcicStallCounter);
WRITE_PORT_UCHAR((PUCHAR)Base + 1, DataByte);
KeStallExecutionProcessor(PcicStallCounter);
}
BOOLEAN
PcicReadAttributeMemory(
IN PSOCKET SocketPtr,
IN PUCHAR *TupleBuffer,
IN PULONG TupleBufferSize
)
/*++
Routine Description:
This routine will set up the card to read attribute memory
Arguments:
SocketPtr -- The socket info in for the card being read
TupleBuffer -- pointer to pointer for tuple information.
TupleBufferSize -- maximum size of the buffer area for tuple information.
Return Value:
TRUE - if read was successful.
--*/
{
BOOLEAN ret;
PcicEnableDisableAttributeMemory(SocketPtr, 0, TRUE);
if (!PcicPCCardReady(SocketPtr)) {
DebugPrint((PCMCIA_PCCARD_READY,
"PCIC: PCCARD %x not ready for read attribute memory\n",
SocketPtr->RegisterOffset));
}
//
// Now read the CIS into the user buffer
//
ret = PcicReadCIS(SocketPtr->AddressPort,
SocketPtr->RegisterOffset,
TupleBuffer,
TupleBufferSize);
PcicEnableDisableAttributeMemory(SocketPtr, 0, FALSE);
return ret;
}
BOOLEAN
PcicReadCIS(
IN PUCHAR Base,
IN USHORT Socket,
IN PUCHAR *TupleBuffer,
IN PULONG TupleBufferSize
)
/*++
Routine Description:
This routine will read the CIS - the INTEL FAX modem (and others)
has a condition where it allows the attribute memory to be read only
once. After that read it returns 0xff for everything - therefore
the tuple data has to be transferred to a page buffer then the
size can be calculated rather than calculating the size in place.
Arguments:
Socket -- Socket to read.
TupleBuffer -- pointer to pointer for buffer to hold tuple Data.
if pointer is NULL - allocate space.
Return Value:
TRUE if read was successful
--*/
{
PUCHAR holdBuffer;
PUCHAR currentBufferPointer;
PUCHAR cisBufferPointer;
UCHAR tupleCode;
UCHAR link;
ULONG i;
ULONG size;
cisBufferPointer = PcicCisBufferBase;
currentBufferPointer = holdBuffer = ExAllocatePool(NonPagedPool, PAGE_SIZE);
for (i = 0; i < (PAGE_SIZE / 4); i++) {
*currentBufferPointer++ = READ_REGISTER_UCHAR(cisBufferPointer);
cisBufferPointer += 2;
}
//
// Calculate the size of the tuple information and allocate
// a user buffer.
//
cisBufferPointer = holdBuffer;
tupleCode = *cisBufferPointer++;
link = *cisBufferPointer++;
size = 2;
while (tupleCode != CISTPL_END) {
DebugPrint((PCMCIA_READ_TUPLE,
"PcmciaReadCIS: Code = %2x link = %2x\n",
tupleCode,
link));
size += link + 2; // add in code and link size
cisBufferPointer += link;
tupleCode = *cisBufferPointer++;
link = *cisBufferPointer++;
}
//
// Add in the end tuple.
//
size++;
size += link;
//
// Allocate memory to hold the tuple information.
//
*TupleBuffer = currentBufferPointer = ExAllocatePool(NonPagedPool, size);
*TupleBufferSize = size;
if (!currentBufferPointer) {
//
// Use the holding buffer. This is a waste of memory.
//
DebugPrint((PCMCIA_READ_TUPLE,
"PcmciaReadCIS: using hold buffer size %d\n",
size));
*TupleBuffer = holdBuffer;
} else {
//
// Copy the CIS information to a smaller buffer for the caller.
//
DebugPrint((PCMCIA_READ_TUPLE, "PcmciaReadCIS: size %d\n", size));
RtlMoveMemory(currentBufferPointer, holdBuffer, size);
ExFreePool(holdBuffer);
}
return TRUE;
}
VOID
PcicProcessConfigureRequest(
IN PSOCKET SocketPtr,
IN PVOID ConfigRequest,
IN PUCHAR Base
)
/*++
Routine Description:
Processes a configure or IRQ setup request.
Arguments:
ConfigRequest -- Socket config structure
Base - the I/O port base
Return Value:
None
--*/
{
PCARD_REQUEST request = ConfigRequest;
USHORT socket = request->Socket;
USHORT index;
UCHAR tmp;
ULONG configRegisterBase;
PCONFIG_QUERY_REQUEST query;
//
// Since all first entries in the config structure is a RequestType,
// cast the pointer comming in as a PREQUEST_CONFIG to get the proper
// RequestType
//
switch (request->RequestType) {
case IO_REQUEST:
if (!request->u.Io.BasePort1) {
DebugPrint((PCMCIA_DEBUG_FAIL,
"PCMCIA: Got an IO Configure Request with an invalid Port\n"));
break;
} else {
PcicWriteController(Base,
socket,
PCIC_IO_ADD0_STRT_L,
(UCHAR) (request->u.Io.BasePort1 & 0xff));
PcicWriteController(Base,
socket,
PCIC_IO_ADD0_STRT_H,
(UCHAR) (request->u.Io.BasePort1 >> 8));
PcicWriteController(Base,
socket,
PCIC_IO_ADD0_STOP_L,
(UCHAR) ((request->u.Io.BasePort1 +
request->u.Io.NumPorts1) & 0xff));
PcicWriteController(Base,
socket,
PCIC_IO_ADD0_STOP_H,
(UCHAR) ((request->u.Io.BasePort1 +
request->u.Io.NumPorts1) >> 8));
}
if (request->u.Io.BasePort2 != 0) {
PcicWriteController(Base,
socket,
PCIC_IO_ADD1_STRT_L,
(UCHAR) (request->u.Io.BasePort2 & 0xff));
PcicWriteController(Base,
socket,
PCIC_IO_ADD1_STRT_H,
(UCHAR) (request->u.Io.BasePort2 >> 8));
PcicWriteController(Base,
socket,
PCIC_IO_ADD1_STOP_L,
(UCHAR) ((request->u.Io.BasePort2 +
request->u.Io.NumPorts2) & 0xff));
PcicWriteController(Base,
socket,
PCIC_IO_ADD1_STOP_H,
(UCHAR) ((request->u.Io.BasePort2 +
request->u.Io.NumPorts2) >> 8));
}
tmp = PcicReadController(Base,
socket,
PCIC_ADD_WIN_ENA);
tmp |= request->u.Io.BasePort2 ? 0xc0 : 0x40;
PcicWriteController(Base,
socket,
PCIC_ADD_WIN_ENA, tmp);
//
// Now set up the datapath according to the attributes
//
if (request->u.Io.Attributes1 & IO_DATA_PATH_WIDTH) {
UCHAR waitState;
if (SocketPtr->ElcController || SocketPtr->CirrusLogic) {
waitState = 0x03;
} else {
waitState = 0x0b;
}
tmp = 0;
if (request->u.Io.BasePort2 != 0) {
tmp |= waitState << 4;
}
tmp |= waitState;
PcicWriteController(Base, socket, PCIC_IO_CONTROL, tmp);
}
break;
case IRQ_REQUEST:
//
// Do not nuke the reset and cardtype bits.
//
tmp = PcicReadController(Base, socket, PCIC_INTERRUPT);
tmp |= request->u.Irq.AssignedIRQ;
PcicWriteController(Base, socket, PCIC_INTERRUPT, tmp);
if (tmp = request->u.Irq.ReadyIRQ) {
tmp = (tmp << 4) | 0x04;
PcicWriteController(Base, socket, PCIC_CARD_INT_CONFIG, tmp);
}
break;
case CONFIGURE_REQUEST:
//
// Tell the socket controller we are an I/O card if InterfaceType says so
//
if (request->u.Config.InterfaceType == CONFIG_INTERFACE_IO_MEM) {
tmp = PcicReadController(Base, socket, PCIC_INTERRUPT);
tmp |= 0x20;
PcicWriteController(Base, socket, PCIC_INTERRUPT, tmp);
}
//
// This is where we setup the card and get it ready for operation
//
configRegisterBase = request->u.Config.ConfigBase;
if (configRegisterBase > (1 << 12)) {
ULONG moduloBase;
moduloBase = configRegisterBase >> 12;
moduloBase = moduloBase << 12;
configRegisterBase &= 0x00000FFF;
PcicEnableDisableAttributeMemory(SocketPtr, moduloBase, TRUE);
} else {
PcicEnableDisableAttributeMemory(SocketPtr, 0, TRUE);
}
if (!PcicPCCardReady(SocketPtr)) {
DebugPrint((PCMCIA_PCCARD_READY,
"PCIC: PCCARD %x not ready for configuration index\n",
socket));
}
if (request->u.Config.RegisterWriteMask & REGISTER_WRITE_CONFIGURATION_INDEX) {
WRITE_REGISTER_UCHAR((PUCHAR)PcicCisBufferBase + configRegisterBase,
request->u.Config.ConfigIndex);
KeStallExecutionProcessor(PcicStallCounter);
WRITE_REGISTER_UCHAR((PUCHAR)PcicCisBufferBase + configRegisterBase,
(UCHAR)(request->u.Config.ConfigIndex | 0x40));
KeStallExecutionProcessor(PcicStallCounter);
}
if (request->u.Config.RegisterWriteMask & REGISTER_WRITE_CARD_CONFIGURATION) {
tmp = READ_REGISTER_UCHAR((PUCHAR)PcicCisBufferBase + configRegisterBase + 2);
KeStallExecutionProcessor(PcicStallCounter);
tmp |= request->u.Config.CardConfiguration;
//
// turn off power control bit
//
tmp &= ~0x04;
WRITE_REGISTER_UCHAR((PUCHAR)PcicCisBufferBase + configRegisterBase + 2,
tmp);
KeStallExecutionProcessor(PcicStallCounter);
}
PcicEnableDisableAttributeMemory(SocketPtr, 0, FALSE);
break;
case MEM_REQUEST:
//
// Set up memory ranges on the controller.
//
for (index = 0; index < request->u.Memory.NumberOfRanges; index++) {
UCHAR registerOffset;
UCHAR regl;
UCHAR regh;
ULONG cardBase = request->u.Memory.MemoryEntry[index].BaseAddress;
ULONG base = request->u.Memory.MemoryEntry[index].HostAddress;
ULONG size = request->u.Memory.MemoryEntry[index].WindowSize;
//
// Determine offset in registers.
//
registerOffset = (index * 8);
//
// Calculate and set card base addresses.
// This is the 2's complement of the host address and
// the card offset.
//
cardBase = (cardBase - base);
regl = (UCHAR) (cardBase >> 12);
regh = (UCHAR) ((cardBase >> 20) & 0x003f);
if (request->u.Memory.MemoryEntry[index].AttributeMemory) {
regh |= 0x40;
}
PcicWriteController(Base,
socket,
(UCHAR)(PCIC_CRDMEM_OFF_ADD0_L + registerOffset),
regl);
PcicWriteController(Base,
socket,
(UCHAR)(PCIC_CRDMEM_OFF_ADD0_H + registerOffset),
regh);
//
// Calculate and set host window.
//
regl = (UCHAR) (base >> 12);
regh = (UCHAR) (base >> 20);
if (request->u.Memory.MemoryEntry[index].WindowDataSize16) {
regh |= 0x80; // 16-bit access
//
// If this is not a revision 1 part (0x82), then set
// the work around register for 16-bit windows.
//
if (SocketPtr->Revision != PCIC_REVISION) {
tmp = PcicReadController(Base,
socket,
PCIC_CARD_DETECT);
tmp |= 0x01;
PcicWriteController(Base,
socket,
PCIC_CARD_DETECT,
tmp);
}
}
PcicWriteController(Base,
socket,
(UCHAR)(PCIC_MEM_ADD0_STRT_L + registerOffset),
regl);
PcicWriteController(Base,
socket,
(UCHAR)(PCIC_MEM_ADD0_STRT_H + registerOffset),
regh);
//
// Set stop address.
//
base += size;
regl = (UCHAR) (base >> 12);
regh = (UCHAR) (base >> 20);
PcicWriteController(Base,
socket,
(UCHAR)(PCIC_MEM_ADD0_STOP_L + registerOffset),
regl);
PcicWriteController(Base,
socket,
(UCHAR)(PCIC_MEM_ADD0_STOP_H + registerOffset),
regh);
}
//
// Memory windows are set up now enable them.
//
tmp = 0;
for (index = 0; index < request->u.Memory.NumberOfRanges; index++) {
tmp |= (1 << index);
}
tmp |= PcicReadController(Base, socket, PCIC_ADD_WIN_ENA);
PcicWriteController(Base, socket, PCIC_ADD_WIN_ENA, tmp);
break;
case QUERY_REQUEST:
//
// If the card has power collect its configuration. If it does not
// have power, just return.
//
tmp = PcicReadController(Base, socket, PCIC_PWR_RST);
if (tmp) {
//
// Fill in the query information structure.
//
query = (PCONFIG_QUERY_REQUEST) request;
RtlZeroMemory(query, sizeof(CONFIG_QUERY_REQUEST));
//
// Process I/O port windows
//
tmp = PcicReadController(Base, socket, PCIC_ADD_WIN_ENA);
query->NumberOfIoPortRanges += (tmp & 0x80) ? 1 : 0;
query->NumberOfIoPortRanges += (tmp & 0x40) ? 1 : 0;
query->NumberOfMemoryRanges += (tmp & 0x01) ? 1 : 0;
query->NumberOfMemoryRanges += (tmp & 0x02) ? 1 : 0;
query->NumberOfMemoryRanges += (tmp & 0x04) ? 1 : 0;
query->NumberOfMemoryRanges += (tmp & 0x08) ? 1 : 0;
query->IoPorts[0] =
PcicReadController(Base, socket, PCIC_IO_ADD0_STRT_L) |
(PcicReadController(Base, socket, PCIC_IO_ADD0_STRT_H) << 8);
index = (PcicReadController(Base, socket, PCIC_IO_ADD0_STOP_L) |
(PcicReadController(Base, socket, PCIC_IO_ADD0_STOP_H) << 8));
query->IoPortLength[0] = index - query->IoPorts[0];
query->IoPorts[1] =
PcicReadController(Base, socket, PCIC_IO_ADD1_STRT_L) |
(PcicReadController(Base, socket, PCIC_IO_ADD1_STRT_H) << 8);
index = (PcicReadController(Base, socket, PCIC_IO_ADD1_STOP_L) |
(PcicReadController(Base, socket, PCIC_IO_ADD1_STOP_H) << 8));
query->IoPortLength[1] = index - query->IoPorts[1];
tmp = PcicReadController(Base, socket, PCIC_IO_CONTROL);
query->IoPort16[0] = tmp & 0x01;
query->IoPort16[1] = tmp & 0x10;
//
// Process Memory windows.
//
for (index = 0; index < query->NumberOfMemoryRanges; index++) {
ULONG host;
ULONG card;
ULONG length;
//
// The the raw values
//
tmp = (PcicReadController(Base,
socket,
(UCHAR) (PCIC_MEM_ADD0_STRT_H + (index * 8))) & 0x0f);
host =
(PcicReadController(Base, socket, (UCHAR) (PCIC_MEM_ADD0_STRT_L + (index * 8))) |
((ULONG) tmp << 8));
tmp = (PcicReadController(Base, socket, (UCHAR) (PCIC_MEM_ADD0_STOP_H + (index * 8))) & 0x0f);
length =
(PcicReadController(Base, socket, (UCHAR) (PCIC_MEM_ADD0_STOP_L + (index * 8))) |
((ULONG) tmp << 8));
tmp = PcicReadController(Base, socket, (UCHAR) (PCIC_CRDMEM_OFF_ADD0_H + (index * 8)));
query->AttributeMemory[index] = (tmp & 0x40) ? 1 : 0;
tmp &= 0x3f;
card =
(PcicReadController(Base, socket, (UCHAR) (PCIC_CRDMEM_OFF_ADD0_L + (index * 8))) |
((ULONG) tmp << 8));
//
// Convert the values into expected values
//
length = length - host;
card = (card + host) & 0x00003fff;
host = host << 12;
length = length << 12;
card = card << 12;
query->HostMemoryWindow[index] = host;
query->PCCARDMemoryWindow[index] = card;
query->MemoryWindowLength[index] = length;
}
//
// Get IRQ
//
query->DeviceIrq = PcicReadController(Base, socket, PCIC_INTERRUPT) & 0x0f;
query->CardReadyIrq = (PcicReadController(Base, socket, PCIC_CARD_INT_CONFIG) & 0xf0) >> 8;
}
break;
default:
DebugPrint((PCMCIA_DEBUG_FAIL, "PCMCIA: ConfigRequest is INVALID!\n"));
}
return;
}
BOOLEAN
PcicDetectCardInSocket(
IN PSOCKET SocketPtr
)
/*++
Routine Description:
This routine will determine if a card is in the socket
Arguments:
SocketPtr -- Socket information
Return Value:
TRUE if card is present.
--*/
{
UCHAR tmp;
BOOLEAN cardPresent;
PUCHAR base = SocketPtr->AddressPort;
USHORT socket = SocketPtr->RegisterOffset;
//
// Read the PCIC status register to see if the card is in there.
//
tmp = PcicReadController(base, socket, PCIC_STATUS);
tmp &= (CARD_DETECT_1 | CARD_DETECT_2);
if (tmp == (CARD_DETECT_1 | CARD_DETECT_2) ) {
cardPresent = TRUE;
} else {
cardPresent = FALSE;
}
return cardPresent;
}
BOOLEAN
PcicDetectCardChanged(
IN PSOCKET SocketPtr
)
/*++
Routine Description:
This routine will determine if socket's card insertion status has changed.
Arguments:
SocketPtr -- Socket info.
Return Value:
TRUE if card insertion status has changed.
--*/
{
UCHAR tmp;
PUCHAR Base = SocketPtr->AddressPort;
USHORT Socket = SocketPtr->RegisterOffset;
# define CARD_STATUS_CHANGE 8
//
// Read the PCIC CardStatusChange register to see if CD's have changed.
//
return (PcicReadController(Base, Socket, PCIC_CARD_CHANGE) & CARD_STATUS_CHANGE
?TRUE :FALSE);
}
VOID
PcicEnableDisableAttributeMemory(
IN PSOCKET SocketPtr,
IN ULONG CardBase,
IN BOOLEAN Enable
)
/*++
Routine Description:
This routine will enable or disable attribute memory. Use memory window
four for the this function to avoid using any other memory windows that
may already be programmed.
Arguments:
SocketPtr -- Socket information
CardBase -- card offset (base) for the attribute memory window
Enable -- If TRUE, enable, if FALSE, disable
Return Value:
None
--*/
{
#define WINDOW_TO_USE 0x10
UCHAR tmp;
UCHAR low;
UCHAR high;
ULONG location;
PUCHAR cisBufferPointer;
PUCHAR base = SocketPtr->AddressPort;
USHORT socket = SocketPtr->RegisterOffset;
if (Enable) {
//
// Insure the window is off first.
//
tmp = PcicReadController(base, socket, PCIC_ADD_WIN_ENA);
tmp &= ~WINDOW_TO_USE;
PcicWriteController(base, socket, PCIC_ADD_WIN_ENA, tmp);
//
// Calculate and set the memory windows start and stop locations.
//
location = PcicPhysicalBase;
low = (UCHAR)(location >> 12);
high = (UCHAR)(location >> 20);
PcicWriteController(base, socket, PCIC_MEM_ADD4_STRT_L, low);
PcicWriteController(base, socket, PCIC_MEM_ADD4_STRT_H, high);
location += 0x11000;
low = (UCHAR)(location >> 12);
high = (UCHAR)(location >> 20);
PcicWriteController(base, socket, PCIC_MEM_ADD4_STOP_L, low);
PcicWriteController(base, socket, PCIC_MEM_ADD4_STOP_H, high);
//
// Set up the 2's complement card offset to zero
//
location = CardBase - PcicPhysicalBase;
low = (UCHAR)(location >> 12);
high = (UCHAR)((location >> 20) & 0x3f);
PcicWriteController(base, socket, PCIC_CRDMEM_OFF_ADD4_L, low);
PcicWriteController(base, socket, PCIC_CRDMEM_OFF_ADD4_H, (UCHAR)(high | 0x40));
//
// Insure that the pccard has power
//
tmp = PcicReadController(base, socket, PCIC_PWR_RST);
if ((tmp & 0x90) != 0x90) {
tmp |= 0x10;
PcicWriteController(base, socket, PCIC_PWR_RST, tmp);
KeStallExecutionProcessor(PcicStallPower);
if (!PcicPCCardReady(SocketPtr)) {
DebugPrint((PCMCIA_PCCARD_READY,
"PCIC: PCCARD %x not ready for late power ON\n",
socket));
}
tmp |= 0x80;
PcicWriteController(base, socket, PCIC_PWR_RST, tmp);
KeStallExecutionProcessor(PcicStallPower);
if (!PcicPCCardReady(SocketPtr)) {
DebugPrint((PCMCIA_PCCARD_READY,
"PCIC: PCCARD %x not ready for late power on\n",
socket));
}
DebugPrint((PCMCIA_DEBUG_FAIL, "PCMCIA: Added power late\n"));
}
//
// Enable the address window
//
tmp = PcicReadController(base, socket, PCIC_ADD_WIN_ENA);
tmp |= WINDOW_TO_USE | 0x20;
PcicWriteController(base, socket, PCIC_ADD_WIN_ENA, tmp);
KeStallExecutionProcessor(2000);
cisBufferPointer = PcicCisBufferBase;
if (READ_REGISTER_UCHAR(cisBufferPointer) == 0xff) {
//
// Only wait for card ready if the memory window does not appear
//
PcicPCCardReady(SocketPtr);
}
} else {
//
// Disable the Address window
//
tmp = PcicReadController(base, socket, PCIC_ADD_WIN_ENA);
tmp &= ~WINDOW_TO_USE;
PcicWriteController(base, socket, PCIC_ADD_WIN_ENA, tmp);
PcicWriteController(base, socket, PCIC_MEM_ADD4_STRT_L, 0x00);
PcicWriteController(base, socket, PCIC_MEM_ADD4_STRT_H, 0x00);
PcicWriteController(base, socket, PCIC_MEM_ADD4_STOP_L, 0x00);
PcicWriteController(base, socket, PCIC_MEM_ADD4_STOP_H, 0x00);
PcicWriteController(base, socket, PCIC_CRDMEM_OFF_ADD4_L, 0x00);
PcicWriteController(base, socket, PCIC_CRDMEM_OFF_ADD4_H, 0x00);
}
return;
}
BOOLEAN
PcicPCCardReady(
IN PSOCKET SocketPtr
)
/*++
Routine Description:
Loop for a reasonable amount of time waiting for the card status to
return ready.
Arguments:
SocketPtr - the socket to check.
Return Value:
TRUE - the card is ready.
FALSE - after a reasonable delay the card is still not ready.
--*/
{
ULONG index;
UCHAR byte;
PUCHAR base = SocketPtr->AddressPort;
USHORT socket = SocketPtr->RegisterOffset;
for (index = 0; index < 500; index++) {
byte = PcicReadController(base, socket, PCIC_STATUS);
if (byte & 0x20) {
break;
}
KeStallExecutionProcessor(10);
}
if (index < 500) {
DebugPrint((PCMCIA_COUNTERS, "PcicPCCardReady: %d\n", index));
return TRUE;
}
return FALSE;
}
BOOLEAN
PcicSearchPci(
PDEVICE_EXTENSION DeviceExtension,
ULONG BusNumber,
PULONG IoPortLocation
)
/*++
Routine Description:
Search all PCI bus slots for the given set of PCI controllers. This
code will only find a SINGLE controller - it is not prepared to find
multiple controllers at this time.
Arguments:
Return Value:
TRUE - a Cirrus Logic PCI part was found.
--*/
{
ULONG pciBuffer;
ULONG slotNumber;
ULONG functionNumber;
ULONG index;
NTSTATUS status;
PCI_SLOT_NUMBER pciSlotNumber;
PPCI_COMMON_CONFIG pciData;
UNICODE_STRING unicodeString;
PCM_RESOURCE_LIST resourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialData;
PCM_FULL_RESOURCE_DESCRIPTOR fullData;
USHORT vendorID[] = { 0x1013, PCI_INVALID_VENDORID };
USHORT deviceID[] = { 0x1100, PCI_INVALID_VENDORID };
pciData = (PPCI_COMMON_CONFIG) &pciBuffer;
pciSlotNumber.u.AsULONG = 0;
for (slotNumber = 0; slotNumber < 32; slotNumber++) {
DebugPrint((PCMCIA_SEARCH_PCI, "PcicSearchPci: slot %d\n", slotNumber));
pciSlotNumber.u.bits.DeviceNumber = slotNumber;
for (functionNumber = 0; functionNumber < 8; functionNumber++) {
pciSlotNumber.u.bits.FunctionNumber = functionNumber;
DebugPrint((PCMCIA_SEARCH_PCI,
"PcicSearchPci: function %d\n",
functionNumber));
if (!HalGetBusData(PCIConfiguration,
BusNumber,
pciSlotNumber.u.AsULONG,
pciData,
sizeof(ULONG))) {
//
// No PCI data.
//
DebugPrint((PCMCIA_SEARCH_PCI, "PcicSearchPci: No more HAL data\n"));
return FALSE;
}
if (pciData->VendorID == PCI_INVALID_VENDORID) {
//
// No more functions - move to next slot.
//
break;
}
DebugPrint((PCMCIA_SEARCH_PCI,
"PcicSearchPci: checking %x:%x\n",
pciData->VendorID,
pciData->DeviceID));
for (index = 0; vendorID[index] != PCI_INVALID_VENDORID; index++) {
if (vendorID[index] == pciData->VendorID) {
if (deviceID[index] == pciData->DeviceID) {
break;
}
}
}
if (vendorID[index] != PCI_INVALID_VENDORID) {
//
// Found a controller - assign it slot resources and return.
//
DebugPrint((PCMCIA_SEARCH_PCI,
"PcicSearchPci: FOUND %d:%d\n",
slotNumber,
functionNumber));
RtlInitUnicodeString(&unicodeString, L"Pcmcia");
status = HalAssignSlotResources(DeviceExtension->RegistryPath,
&unicodeString,
DeviceExtension->DriverObject,
DeviceExtension->DeviceObject,
PCIBus,
BusNumber,
pciSlotNumber.u.AsULONG,
&resourceList);
if (!NT_SUCCESS(status)) {
DebugPrint((PCMCIA_SEARCH_PCI, "PcicSearchPci: No resources\n"));
return FALSE;
}
fullData = resourceList->List;
for (index = 0; index < fullData->PartialResourceList.Count; index++) {
partialData = &fullData->PartialResourceList.PartialDescriptors[index];
switch (partialData->Type) {
case CmResourceTypePort:
DebugPrint((PCMCIA_SEARCH_PCI,
"PcicResource: Port %x:%x\n",
partialData->u.Port.Start,
partialData->u.Port.Length));
*IoPortLocation = (ULONG)partialData->u.Port.Start.LowPart;
break;
case CmResourceTypeInterrupt:
DebugPrint((PCMCIA_SEARCH_PCI,
"PcicResource: Interrupt v=%d l=%d\n",
partialData->u.Interrupt.Vector,
partialData->u.Interrupt.Level));
break;
case CmResourceTypeMemory:
DebugPrint((PCMCIA_SEARCH_PCI,
"PcicResource: Memory %x:%x\n",
partialData->u.Memory.Start,
partialData->u.Memory.Length));
break;
case CmResourceTypeDma:
DebugPrint((PCMCIA_SEARCH_PCI,
"PcicResource: Dma c=%x p=%x\n",
partialData->u.Dma.Channel,
partialData->u.Dma.Port));
break;
default:
DebugPrint((PCMCIA_SEARCH_PCI,
"PcicResource: Unknown resource\n"));
break;
}
}
ExFreePool(resourceList);
return TRUE;
}
}
}
DebugPrint((PCMCIA_SEARCH_PCI, "PcicSearchPci: All slots searched\n"));
return FALSE;
}
NTSTATUS
PcicDetect(
IN PDEVICE_EXTENSION DeviceExtension
)
/*++
Routine Description:
Locate any PCMCIA sockets supported by this driver. This routine
will find the 82365SL and compatible parts and construct SOCKET
structures to represent all sockets found.
Arguments:
DeviceExtension - the root for the SocketList.
Return Value:
STATUS_SUCCESS if a socket is found - failure status otherwise.
--*/
{
ULONG ioPortBases[3] = { 0x3e0, 0x3e2, 0x3e4 };
ULONG addressSpace;
ULONG index;
PUCHAR port;
PUCHAR elcPort;
UCHAR saveBytes[2];
UCHAR dataByte;
UCHAR revisionByte;
USHORT socket;
BOOLEAN translated;
BOOLEAN isPci;
BOOLEAN foundOne;
BOOLEAN foundSomething;
BOOLEAN mapped;
BOOLEAN elcChip;
BOOLEAN cirrusLogic;
PSOCKET socketPtr;
PSOCKET previousSocketPtr;
PHYSICAL_ADDRESS cardAddress;
PHYSICAL_ADDRESS portAddress;
previousSocketPtr = NULL;
foundOne = FALSE;
isPci = PcicSearchPci(DeviceExtension, 0, &ioPortBases[0]);
for (index = 0; index < 3; index++) {
addressSpace = 1; // port space
portAddress.LowPart = ioPortBases[index];
portAddress.HighPart = 0;
translated = HalTranslateBusAddress(isPci ? PCIBus : Isa,
0,
portAddress,
&addressSpace,
&cardAddress);
if (!translated) {
//
// HAL would not translate the address.
//
continue;
}
if (addressSpace) {
mapped = FALSE;
port = (PUCHAR)cardAddress.LowPart;
} else {
mapped = TRUE;
port = MmMapIoSpace(cardAddress,
2,
FALSE);
}
foundSomething = elcChip = cirrusLogic = FALSE;
for (socket = 0; socket < 0xFF; socket += 0x40) {
dataByte = PcicReadController(port, socket, PCIC_IDENT);
revisionByte = dataByte;
switch (dataByte) {
case PCIC_REVISION:
cirrusLogic = TRUE;
case PCIC_REVISION2:
case PCIC_REVISION3:
dataByte = PcicReadController(port, socket, PCIC_CARD_CHANGE);
if (dataByte & 0xf0) {
//
// Not a socket.
//
continue;
}
//
// Check for IBM 750
//
if (socket & 0x80) {
ULONG i;
UCHAR tmp;
//
// See if this socket shadows the socket without
// the sign bit.
//
tmp = PcicReadController(port, socket, PCIC_MEM_ADD4_STRT_L);
for (i = 0; i < 8; i++) {
//
// See if memory window 4 is the same on both sockets
//
if (PcicReadController(port, socket, (UCHAR) (PCIC_MEM_ADD4_STRT_L + i)) !=
PcicReadController(port, (USHORT) (socket & 0x7f), (UCHAR) (PCIC_MEM_ADD4_STRT_L + i))) {
break;
}
}
if (i == 8) {
//
// Currently window is the same - change the
// window at one of the socket offsets.
//
PcicWriteController(port, (USHORT) (socket & 0x7f), PCIC_MEM_ADD4_STRT_L, (UCHAR) ~tmp);
if (PcicReadController(port, socket, PCIC_MEM_ADD4_STRT_L) == (UCHAR) ~tmp) {
//
// The sockets are the same.
//
continue;
} else {
PcicWriteController(port, (USHORT) (socket & 0x7f), PCIC_MEM_ADD4_STRT_L, tmp);
}
}
}
//
// Map and try to locate the Compaq Elite controller
// This code is a rough approximation of the code in
// the Windows 95 detection module for the PCIC part.
//
addressSpace = 1; // port space
portAddress.LowPart = ioPortBases[index] + 0x8000;
portAddress.HighPart = 0;
translated = HalTranslateBusAddress(Isa,
0,
portAddress,
&addressSpace,
&cardAddress);
if (translated) {
if (!addressSpace) {
elcPort = MmMapIoSpace(cardAddress,
2,
FALSE);
} else {
elcPort = (PUCHAR)cardAddress.LowPart;
}
//
// Save current index value.
//
saveBytes[0] = READ_PORT_UCHAR(elcPort);
WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT));
//
// Save data byte for the location that will be used
// for the test.
//
saveBytes[1] = READ_PORT_UCHAR(elcPort + 1);
//
// Check for an ELC
//
WRITE_PORT_UCHAR(elcPort+1, 0x55);
WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT));
dataByte = READ_PORT_UCHAR(elcPort+1);
if (dataByte == 0x55) {
WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT));
WRITE_PORT_UCHAR(elcPort+1, 0xaa);
WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT));
dataByte = READ_PORT_UCHAR(elcPort+1);
if (dataByte == 0xaa) {
//
// ELC found - initialize eaddr registers
//
WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + 0));
WRITE_PORT_UCHAR(elcPort+1, 0);
WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + 1));
WRITE_PORT_UCHAR(elcPort+1, 0);
WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + 2));
WRITE_PORT_UCHAR(elcPort+1, 0x10);
elcChip = TRUE;
}
}
}
if (cirrusLogic) {
ULONG i;
UCHAR data[4];
//
// The Cirrus Logic will toggle the top two lines
// from the 0x1f register on the chip. Read this
// location 3 times and verify that the top two
// lines are changing.
//
WRITE_PORT_UCHAR(port, (UCHAR)(socket + 0x1f));
for (i = 0; i < 3; i++) {
data[i] = READ_PORT_UCHAR(port+1);
if (i) {
dataByte = data[i - 1] ^ data[i];
if (dataByte != 0xc0) {
break;
}
}
}
if (i == 3) {
cirrusLogic = TRUE;
//
// Need to program the chip per code in
// Windows 95. This will turn on the
// audio support bit.
//
WRITE_PORT_UCHAR(port, (UCHAR)(socket + 0x16));
dataByte = READ_PORT_UCHAR(port+1);
dataByte |= 0x10;
WRITE_PORT_UCHAR(port+1, dataByte);
} else {
cirrusLogic = FALSE;
}
}
//
// Restore the original values.
//
WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT));
WRITE_PORT_UCHAR(elcPort+1, saveBytes[1]);
WRITE_PORT_UCHAR(elcPort, saveBytes[0]);
if (!addressSpace) {
MmUnmapIoSpace(elcPort, 2);
}
socketPtr = ExAllocatePool(NonPagedPool, sizeof(SOCKET));
if (!socketPtr) {
continue;
}
RtlZeroMemory(socketPtr, sizeof(SOCKET));
socketPtr->DeviceExtension = DeviceExtension;
socketPtr->SocketFnPtr = &PcicSupportFns;
socketPtr->RegisterOffset = socket;
socketPtr->AddressPort = port;
socketPtr->ElcController = elcChip;
socketPtr->CirrusLogic = cirrusLogic;
socketPtr->Revision = revisionByte;
socketPtr->SocketConfigured = FALSE;
if (previousSocketPtr) {
previousSocketPtr->NextSocket = socketPtr;
} else {
DeviceExtension->SocketList = socketPtr;
}
previousSocketPtr = socketPtr;
foundOne = foundSomething = TRUE;
DeviceExtension->Configuration.UntranslatedPortAddress = (USHORT)ioPortBases[index];
DeviceExtension->Configuration.PortSize = 2;
DebugPrint((PCMCIA_DEBUG_DETECT,
"PCMCIA: Port %x Offset %x Elc %d\n",
port,
socket,
elcChip));
break;
default:
DebugPrint((PCMCIA_DEBUG_FAIL,
"PCMCIA: The controller (0x%x:0x%x) is either not present or it is an\n",
portAddress.LowPart,
socket));
DebugPrint((PCMCIA_DEBUG_FAIL,
"\tearlier revision of the controller. Return was %x\n",
dataByte));
break;
}
}
if ((!foundSomething) && mapped) {
MmUnmapIoSpace(port, 2);
}
if (isPci) {
//
// The problem controller has been moved to a new location
// search the remaining addresses.
//
isPci = FALSE;
}
}
return foundOne ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
}
VOID
PcicGetRegisters(
IN PDEVICE_EXTENSION DeviceExtension,
IN PSOCKET SocketPtr,
IN PUCHAR Buffer
)
/*++
Routine Description:
Return the flat 82365SL register values. This routine is used
for debugging.
Arguments:
DeviceExtension - not used
SocketPtr - socket information
Buffer - the memory for receiving the values
Return Value:
None
--*/
{
UCHAR index;
PUCHAR port = SocketPtr->AddressPort;
USHORT socket = SocketPtr->RegisterOffset;
for (index = 0; index < 0x40; index++) {
Buffer[index] = PcicReadController(port, socket, index);
}
}