4429 lines
92 KiB
C
4429 lines
92 KiB
C
/*++
|
||
|
||
Copyright (c) 1997-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
tcicsup.c
|
||
|
||
Abstract:
|
||
|
||
This module supplies functions that control the Databook TCIC family
|
||
of chips. In turn, these functions are abstracted out to the main PCMCIA
|
||
support module.
|
||
|
||
Author(s):
|
||
(pcicsup.c - Source that this file was derived from)
|
||
Bob Rinne (BobRi) 3-Aug-1994
|
||
Jeff McLeman (mcleman@zso.dec.com)
|
||
(tcicsup.c - this file)
|
||
John Keys - Databook Inc. 7-Apr-1995
|
||
|
||
Revisions:
|
||
Overhaul for plug'n'play support
|
||
Ravisankar Pudipeddi (ravisp) 8-Jan-1997
|
||
new setpower and init routine interface
|
||
Neil Sandlin (neilsa) 3-Mar-99
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
VOID
|
||
TcicRegistryLookupScanLimits(
|
||
PULONG Start,
|
||
PULONG End
|
||
);
|
||
|
||
NTSTATUS
|
||
TcicDetectSockets(
|
||
IN PFDO_EXTENSION DeviceExtension,
|
||
IN BOOLEAN LegacyDetection
|
||
);
|
||
|
||
BOOLEAN
|
||
TcicInitializePcmciaSocket(
|
||
IN PSOCKET SocketPtr
|
||
);
|
||
|
||
NTSTATUS
|
||
TcicResetCard(
|
||
IN PSOCKET SocketPtr,
|
||
OUT PULONG pDelayTime
|
||
);
|
||
|
||
ULONG
|
||
TcicReadCardMemory(
|
||
IN PPDO_EXTENSION PdoExtension,
|
||
IN MEMORY_SPACE MemorySpace,
|
||
IN ULONG Offset,
|
||
IN PUCHAR Buffer,
|
||
IN ULONG Length
|
||
);
|
||
|
||
ULONG
|
||
TcicWriteCardMemory(
|
||
IN PPDO_EXTENSION PdoExtension,
|
||
IN MEMORY_SPACE MemorySpace,
|
||
IN ULONG Offset,
|
||
IN PUCHAR Buffer,
|
||
IN ULONG Length
|
||
);
|
||
|
||
BOOLEAN
|
||
TcicDetectCardInSocket(
|
||
IN PSOCKET SocketPtr
|
||
);
|
||
|
||
BOOLEAN
|
||
TcicDetectCardChanged(
|
||
IN PSOCKET SocketPtr
|
||
);
|
||
|
||
BOOLEAN
|
||
TcicDetectReadyChanged(
|
||
IN PSOCKET SocketPtr
|
||
);
|
||
|
||
NTSTATUS
|
||
TcicGetPowerRequirements(
|
||
IN PSOCKET Socket
|
||
);
|
||
|
||
BOOLEAN
|
||
TcicProcessConfigureRequest(
|
||
IN PSOCKET SocketPtr,
|
||
IN PVOID ConfigRequest,
|
||
IN PUCHAR Base
|
||
);
|
||
|
||
BOOLEAN
|
||
TcicEnableDisableCardDetectEvent(
|
||
IN PSOCKET SocketPtr,
|
||
IN BOOLEAN Enable
|
||
);
|
||
|
||
VOID
|
||
TcicDisableControllerInterrupt(
|
||
IN PSOCKET socketPtr
|
||
);
|
||
|
||
BOOLEAN
|
||
TcicPCCardReady(
|
||
IN PSOCKET SocketPtr
|
||
);
|
||
|
||
VOID
|
||
TcicGetRegisters(
|
||
IN PFDO_EXTENSION DeviceExtension,
|
||
IN PSOCKET SocketPtr,
|
||
IN PUCHAR Buffer
|
||
);
|
||
|
||
ULONG
|
||
TcicGetIrqMask(
|
||
IN PFDO_EXTENSION deviceExtension
|
||
);
|
||
|
||
BOOLEAN
|
||
TcicCardBusCardInSocket(
|
||
IN PSOCKET SocketPtr
|
||
);
|
||
|
||
#if DBG
|
||
VOID
|
||
TcicDump(
|
||
IN PSOCKET socketPtr
|
||
);
|
||
#endif
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,TcicDetect)
|
||
#pragma alloc_text(PAGE,TcicFillInAdapter)
|
||
#pragma alloc_text(PAGE,TcicGetAdapterInfo)
|
||
#pragma alloc_text(PAGE,TcicAllocateMemRange)
|
||
#pragma alloc_text(PAGE,TcicReservedBitsOK)
|
||
#pragma alloc_text(PAGE,TcicChipID)
|
||
#pragma alloc_text(PAGE,TcicCheckSkt)
|
||
#pragma alloc_text(PAGE,TcicCheckAliasing)
|
||
#pragma alloc_text(PAGE,TcicCheckAliasType)
|
||
#pragma alloc_text(PAGE,TcicCheckXBufNeeded)
|
||
#pragma alloc_text(PAGE,TcicSetMemWindow)
|
||
#pragma alloc_text(PAGE,TcicGetPossibleIRQs)
|
||
#pragma alloc_text(PAGE,TcicClockRate)
|
||
#pragma alloc_text(PAGE,TcicGetIRQMap)
|
||
#pragma alloc_text(PAGE,TcicGet5vVccVal)
|
||
#pragma alloc_text(PAGE,TcicHasSktIRQPin)
|
||
#pragma alloc_text(PAGE,TcicGetFlags)
|
||
#pragma alloc_text(PAGE,TcicGetnMemWins)
|
||
#pragma alloc_text(PAGE,TcicGetnIOWins)
|
||
#pragma alloc_text(PAGE,TcicRegistryLookupScanLimits)
|
||
#endif
|
||
|
||
#define TCIC_LOW_ADDR_LIMIT 0x240
|
||
#define TCIC_HIGH_ADDR_LIMIT 0x2ff
|
||
|
||
|
||
/*
|
||
|| IRQ Tables -
|
||
|| Each table consists of 16 bytes. Each byte maps an IRQ level (implied by
|
||
|| table index) to a register value that will select that IRQ. For instance,
|
||
|| with table irqcaps_082, table[11] gives a value of 1, so using '1' as the
|
||
|| card status change IRQ value will cause IRQ11 to be fired.
|
||
||
|
||
*/
|
||
/********************* 0 1 2 3 4 5 6 7 8 9 A B C D E F *****************/
|
||
UCHAR irqcaps_082[] ={0,0,0,3,4,5,6,7,0, 0,10,1, 0, 0, 14,0};
|
||
UCHAR irqcaps_082sw[] ={0,0,0,3,4,5,0,7,0, 6,10,1, 0, 0, 14,0};
|
||
UCHAR irqcaps_072[] ={0,0,0,3,4,5,0,7,0, 0,10,1, 0, 0, 14,0};
|
||
UCHAR irqcaps_072sw[] ={0,0,0,3,4,5,0,7,0,14,10,1, 0, 0, 0, 0};
|
||
/* in the case of x84 parts, we determine 6,9,12,&15 at run time */
|
||
UCHAR irqcaps_084[] ={0,0,0,3,4,5,0,7,0, 0,10,11,0, 0, 14,0};
|
||
|
||
|
||
/* The Socket Services Public Power Table */
|
||
unsigned short PubPwrTbl[] = {
|
||
3, /* number of Public Entries */
|
||
SPWR_ALL_SUPPLY | SPWR_0p0V, /* Public entry */
|
||
SPWR_ALL_SUPPLY | SPWR_5p0V, /* Public entry */
|
||
SPWR_VPP_SUPPLY | SPWR_12p0V /* Public entry */
|
||
};
|
||
|
||
|
||
/* The corresponding Private Table for a TMI-140 type implementation */
|
||
USHORT PwrTbl140[] = {
|
||
3, 0x0000, 0x0001, 0x0800, /* Private table */
|
||
0x0001 /* CtlBits for Vcc=5V */
|
||
};
|
||
|
||
/* The other Private Table for a DB86082/071/072 type implementation */
|
||
USHORT PwrTbl082[] = {
|
||
3, 0x0000, 0x0809, 0x0100, /* Private table */
|
||
0x0001 /* CtlBits for Vcc=5V */
|
||
};
|
||
|
||
/* The corresponding Private Table for a DB86084/184 implementation */
|
||
USHORT PwrTbl084[] ={
|
||
3, 0x0000, 0x0207, 0x0100, /* Private table */
|
||
0x0007 /* CtlBits for Vcc=5V */
|
||
};
|
||
|
||
|
||
/* Properties table - use this to bind possible capabilites to a Chip ID */
|
||
|
||
CHIPPROPS ChipProperties[] = {
|
||
{SILID_DB86082_1,
|
||
PwrTbl082, 0, irqcaps_082, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_082, (fEXTBUF_CHK | fSKTIRQPIN)},
|
||
{SILID_DB86082A,
|
||
PwrTbl082, 0, irqcaps_082, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_082A, (fEXTBUF_CHK | fSKTIRQPIN)},
|
||
{SILID_DB86082B,
|
||
PwrTbl082, 0, irqcaps_082, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_082B, (fEXTBUF_CHK | fSKTIRQPIN)},
|
||
{SILID_DB86082B_ES,
|
||
PwrTbl082, 0, irqcaps_082, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_082B, (fEXTBUF_CHK | fSKTIRQPIN)},
|
||
{SILID_DB86084_1,
|
||
PwrTbl084, 0, irqcaps_084, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_084, fIS_PNP},
|
||
{SILID_DB86084A,
|
||
PwrTbl084, 0, irqcaps_084, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_084, fIS_PNP},
|
||
{SILID_DB86184_1,
|
||
PwrTbl084, 0, irqcaps_084, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_184, fIS_PNP},
|
||
{SILID_DB86072_1,
|
||
PwrTbl082, 0, irqcaps_072, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_072, fSKTIRQPIN},
|
||
{SILID_DB86072_1_ES,
|
||
PwrTbl082, 0, irqcaps_072, NUMSOCKETS, IR_IOWIN_NUM,
|
||
IR_MWIN_NUM_072, fSKTIRQPIN},
|
||
{0, NULL, 0, NULL, 0, 0, 0, 0}
|
||
};
|
||
|
||
|
||
|
||
#ifdef POOL_TAGGING
|
||
#undef ExAllocatePool
|
||
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'bdcP')
|
||
#endif
|
||
|
||
PUCHAR TcicCisBufferBase;
|
||
ULONG TcicPhysicalBase;
|
||
ULONG TcicStallCounter = 5000;
|
||
ULONG TcicStallPower = 20000;
|
||
|
||
PCMCIA_CTRL_BLOCK TcicSupportFns = {
|
||
TcicInitializePcmciaSocket,
|
||
TcicResetCard,
|
||
TcicDetectCardInSocket,
|
||
TcicDetectCardChanged,
|
||
NULL, // DetectCardStatus
|
||
TcicDetectReadyChanged,
|
||
NULL, // GetPowerRequirements
|
||
TcicProcessConfigureRequest,
|
||
TcicEnableDisableCardDetectEvent,
|
||
NULL, // EnableDisableWakeupEvent
|
||
TcicGetIrqMask,
|
||
TcicReadCardMemory,
|
||
TcicWriteCardMemory,
|
||
NULL, // ModifyMemoryWindow
|
||
NULL, // SetVpp
|
||
NULL // IsWriteProtected
|
||
};
|
||
|
||
|
||
|
||
VOID
|
||
TcicGetControllerProperties(
|
||
IN PSOCKET socketPtr,
|
||
IN PUSHORT pIoPortBase,
|
||
IN PUSHORT pIoPortSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets the Port base and range from the DBSOCKET pointer. The original code
|
||
stored these values in the device extension, but this did not allow for
|
||
multiple controller products such as the TMB-270.
|
||
|
||
Arguments:
|
||
|
||
socketPtr - pointer to our socket structure
|
||
pIoPortBase - where to write the base address.
|
||
pIoPortSize - where to write the range.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBSOCKET pdb;
|
||
|
||
if (Databook(socketPtr)) {
|
||
pdb = (PDBSOCKET)socketPtr;
|
||
*pIoPortBase = (USHORT)pdb->physPortAddr;
|
||
*pIoPortSize = 16;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
ULONG
|
||
TcicGetIrqMask(
|
||
IN PFDO_EXTENSION deviceExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets the IRQ mask from the DBSOCKET pointer. The original code
|
||
had this mask hardcoded in PCMCIA.C but this did not provide the
|
||
flexibility needed to correctly state the mask for Databook products.
|
||
|
||
Arguments:
|
||
|
||
deviceExtension - the root of the socket list
|
||
|
||
Return Value:
|
||
|
||
The compiled IRQ mask for the 1st socket in the list since this socket
|
||
should be representative of all sockets on this controller.
|
||
|
||
--*/
|
||
{
|
||
PDBSOCKET pdb = (PDBSOCKET)(deviceExtension->SocketList);
|
||
ULONG mask = 0;
|
||
int j;
|
||
|
||
for (j = 0; j < 16; j++) {
|
||
//
|
||
// Changed the way the mask is constructed
|
||
// The older (non-PnP) code put 0s for valid IRQs
|
||
// and 1s for non-valid IRQs. Now it's flipped
|
||
// (since the ControllerInterruptMask operates the same
|
||
// way)
|
||
//
|
||
if (pdb->IRQMapTbl[j] != (UCHAR)0) {
|
||
mask |= ((ULONG)1 << j);
|
||
}
|
||
}
|
||
return (mask);
|
||
}
|
||
|
||
|
||
|
||
#if DBG
|
||
#include "tcicregs.h"
|
||
|
||
VOID
|
||
TcicDump(
|
||
IN PSOCKET socketPtr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Debug routine to print the registers to the debugger.
|
||
|
||
Arguments:
|
||
|
||
socketPtr - provides base address information for the contoller to dump.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
TCIC tcic;
|
||
ULONG origAddr;
|
||
USHORT j;
|
||
|
||
origAddr = TcicReadAddrReg(socketPtr);
|
||
for (j = 0; j < 2; j++) {
|
||
|
||
//
|
||
// Select the socket
|
||
//
|
||
|
||
TcicSocketSelect(socketPtr, j);
|
||
|
||
//
|
||
// Read TCIC base registers for this socket
|
||
//
|
||
|
||
tcic.baseregs[j].sctrl = (UCHAR)TcicReadBaseReg(socketPtr, R_SCTRL);
|
||
tcic.baseregs[j].sstat = (UCHAR)TcicReadBaseReg(socketPtr, R_SSTAT);
|
||
tcic.baseregs[j].mode = (UCHAR)TcicReadBaseReg(socketPtr, R_MODE);
|
||
tcic.baseregs[j].pwr = (UCHAR)TcicReadBaseReg(socketPtr, R_PWR);
|
||
tcic.baseregs[j].edc = TcicReadBaseReg(socketPtr, R_EDC);
|
||
tcic.baseregs[j].icsr = (UCHAR)TcicReadBaseReg(socketPtr, R_ICSR);
|
||
tcic.baseregs[j].iena = (UCHAR)TcicReadBaseReg(socketPtr, R_IENA);
|
||
|
||
//
|
||
// Read TCIC aux regsiters for this socket
|
||
//
|
||
|
||
tcic.baseregs[j].wctl = TcicReadAuxReg(socketPtr, MODE_AR_WCTL);
|
||
tcic.baseregs[j].syscfg = TcicReadAuxReg(socketPtr, MODE_AR_SYSCFG);
|
||
tcic.baseregs[j].ilock = TcicReadAuxReg(socketPtr, MODE_AR_ILOCK);
|
||
tcic.baseregs[j].test = TcicReadAuxReg(socketPtr, MODE_AR_TEST);
|
||
|
||
//
|
||
// Restore R_MODE - trashed by reading aux regs
|
||
//
|
||
|
||
TcicWriteBaseReg(socketPtr, R_MODE, tcic.baseregs[j].mode);
|
||
}
|
||
|
||
for (j = 0; j < 2; j++) {
|
||
TcicReadIndirectRegs(socketPtr, IR_SCFG_S(j), 2, (PUSHORT)&tcic.sktregs[j]);
|
||
}
|
||
|
||
for (j = 0; j < 4; j++) {
|
||
TcicReadIndirectRegs(socketPtr, IR_IOBASE_W(j), 2, (PUSHORT)&tcic.iowins[j]);
|
||
}
|
||
|
||
for (j = 0; j < 10; j++) {
|
||
TcicReadIndirectRegs(socketPtr, IR_MBASE_W(j), 3, (PUSHORT)&tcic.memwins[j]);
|
||
}
|
||
|
||
TcicWriteAddrReg(socketPtr, origAddr);
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "SCTRL\t%02X\t%02X\n",
|
||
tcic.baseregs[0].sctrl, tcic.baseregs[1].sctrl));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "SSTAT\t%02X\t%02X\n",
|
||
tcic.baseregs[0].sstat, tcic.baseregs[1].sstat));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "MODE \t%02X\t%02X\n",
|
||
tcic.baseregs[0].mode, tcic.baseregs[1].mode));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "PWR \t%02X\t%02X\n",
|
||
tcic.baseregs[0].pwr , tcic.baseregs[1].pwr ));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "EDC \t%04X\t%04X\n",
|
||
tcic.baseregs[0].edc , tcic.baseregs[1].edc ));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "ICSR \t%02X\t%02X\n",
|
||
tcic.baseregs[0].icsr , tcic.baseregs[1].icsr ));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "IENA \t%02X\t%02X\n",
|
||
tcic.baseregs[0].iena , tcic.baseregs[1].iena ));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "WCTL \t%02X\t%02X\n",
|
||
tcic.baseregs[0].wctl , tcic.baseregs[1].wctl ));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "SYSCFG\t%02X\t%02X\n",
|
||
tcic.baseregs[0].syscfg, tcic.baseregs[1].syscfg));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "ILOCK\t%02X\t%02X\n",
|
||
tcic.baseregs[0].ilock, tcic.baseregs[1].ilock));
|
||
|
||
DebugPrint((PCMCIA_DUMP_SOCKET, "TEST \t%02X\t%02X\n",
|
||
tcic.baseregs[0].test , tcic.baseregs[1].test ));
|
||
|
||
for (j = 0; j < 2; j++ ) {
|
||
DebugPrint((PCMCIA_DUMP_SOCKET,
|
||
"SKT%d\tSCF1 %04X\tSCF2 %04X\n",
|
||
j, tcic.sktregs[j].scfg1, tcic.sktregs[j].scfg2));
|
||
}
|
||
|
||
for (j = 0; j < 4; j++ ) {
|
||
DebugPrint((PCMCIA_DUMP_SOCKET,
|
||
"IOWIN%d\tIOBASE %04X\tIOCTL %04X\n",
|
||
j, tcic.iowins[j].iobase, tcic.iowins[j].ioctl));
|
||
}
|
||
|
||
for (j = 0; j < 10; j++ ) {
|
||
DebugPrint((PCMCIA_DUMP_SOCKET,
|
||
"MEMWIN%d\tMBASE %04X\tMMAP %04X\tMCTL %04X\n",
|
||
j, tcic.memwins[j].mbase,
|
||
tcic.memwins[j].mmap,
|
||
tcic.memwins[j].mctl));
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicEnableDisableCardDetectEvent(
|
||
IN PSOCKET SocketPtr,
|
||
IN BOOLEAN Enable
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Enable card detect interrupt.
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - socket information
|
||
Irq - the interrupt value to set if enable is true.
|
||
Enable - if TRUE, CSC interrupt is enabled,
|
||
if FALSE, it is disabled
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
UCHAR mappedIrq;
|
||
PDBSOCKET pdb = (PDBSOCKET)SocketPtr;
|
||
BOOLEAN retVal;
|
||
|
||
switch (Enable) {
|
||
case TRUE:
|
||
//
|
||
// Validate the interrupt request. Only setup if the IRQ is valid
|
||
// for this controller
|
||
//
|
||
|
||
if ((mappedIrq = pdb->IRQMapTbl[SocketPtr->FdoIrq]) != (UCHAR)0) {
|
||
|
||
USHORT word;
|
||
|
||
//
|
||
// Mask status change conditions other than CD. The pcic code comments
|
||
// claimed to setup CD and RDY/BSY notification, but the code itself
|
||
// only allows for CD.
|
||
//
|
||
|
||
word = (USHORT)(IRSCF2_MLBAT1 | IRSCF2_MLBAT2 | IRSCF2_MRDY | IRSCF2_MWP);
|
||
TcicWriteIndirectRegs(SocketPtr,
|
||
IR_SCF2_S(SocketPtr->RegisterOffset),
|
||
1,
|
||
&word);
|
||
|
||
//
|
||
// Set the correct IRQ value in the SYSCFG register
|
||
//
|
||
|
||
word = TcicReadAuxReg(SocketPtr, MODE_AR_SYSCFG);
|
||
word &= ~SYSCFG_IRQ_MASK;
|
||
word |= (USHORT)mappedIrq;
|
||
TcicWriteAuxReg(SocketPtr, MODE_AR_SYSCFG, word);
|
||
|
||
//
|
||
// Set IRQ polarity and enable via R_IENA
|
||
//
|
||
|
||
TcicSocketSelect(SocketPtr, SocketPtr->RegisterOffset);
|
||
TcicWriteBaseReg(SocketPtr, R_IENA, IENA_CDCHG | IENA_CFG_HIGH);
|
||
|
||
PcmciaWait(TcicStallCounter);
|
||
//
|
||
// Clear ICSR - so future insertions/removals will generate interrupts
|
||
//
|
||
(VOID) TcicDetectCardChanged(SocketPtr);
|
||
retVal = TRUE;
|
||
} else {
|
||
retVal = FALSE;
|
||
}
|
||
break;
|
||
case FALSE:{
|
||
retVal = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
return retVal;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
TcicResetCard(
|
||
IN PSOCKET SocketPtr,
|
||
OUT PULONG pDelayTime
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - socket information
|
||
pDelayTime - specifies delay (msec) to occur after the current phase
|
||
|
||
Return value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED - increment phase, perform delay, recall
|
||
other status values terminate sequence
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PDBSOCKET pdb = (PDBSOCKET)SocketPtr;
|
||
|
||
USHORT ilock;
|
||
PDBSOCKET dbskt = (PDBSOCKET)(SocketPtr->DeviceExtension->SocketList);
|
||
|
||
switch(SocketPtr->CardResetPhase) {
|
||
case 1:
|
||
//
|
||
// reset PCCARD
|
||
//
|
||
|
||
ilock = TcicReadAuxReg(SocketPtr, MODE_AR_ILOCK);
|
||
ilock &= ~(ILOCK_CRESET | ILOCK_CRESENA | ILOCK_CWAIT);
|
||
TcicWriteAuxReg(SocketPtr, MODE_AR_ILOCK, (USHORT)(ilock | ILOCK_CRESENA | ILOCK_CRESET));
|
||
*pDelayTime = TcicStallCounter;
|
||
SocketPtr->PowerData = (ULONG) ilock;
|
||
status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
break;
|
||
|
||
case 2:
|
||
|
||
ilock = (USHORT) SocketPtr->PowerData;
|
||
TcicWriteAuxReg(SocketPtr, MODE_AR_ILOCK, (USHORT)(ilock | ILOCK_CRESENA));
|
||
*pDelayTime = TcicStallCounter;
|
||
status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
break;
|
||
|
||
case 3:
|
||
|
||
ilock = TcicReadAuxReg(SocketPtr, MODE_AR_ILOCK);
|
||
if (!(ilock & ILOCK_CWAITSNS)) {
|
||
TcicWriteAuxReg(SocketPtr, MODE_AR_ILOCK, (USHORT)(ilock | ILOCK_CWAIT));
|
||
}
|
||
//
|
||
// If not already started, start a timer to drive the BusyLED
|
||
// Monitor routine.
|
||
if (dbskt->timerStarted == FALSE) {
|
||
IoInitializeTimer(pdb->skt.DeviceExtension->DeviceObject,
|
||
TcicBusyLedRoutine, NULL);
|
||
IoStartTimer(pdb->skt.DeviceExtension->DeviceObject);
|
||
dbskt->timerStarted = TRUE;
|
||
}
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
default:
|
||
ASSERT(FALSE);
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
TcicSetPower(
|
||
IN PSOCKET socketPtr,
|
||
IN BOOLEAN Enable,
|
||
OUT PULONG pDelayTime
|
||
)
|
||
|
||
/*++
|
||
|
||
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.
|
||
pDelayTime - specifies delay (msec) to occur after the current phase
|
||
|
||
Return Value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED - increment phase, perform delay, recall
|
||
other status values terminate sequence
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Get the specified socket mapped into the TCIC registers
|
||
//
|
||
|
||
TcicSocketSelect(socketPtr, socketPtr->RegisterOffset);
|
||
|
||
if (Enable) {
|
||
PDBSOCKET pdb = (PDBSOCKET)socketPtr;
|
||
|
||
switch(socketPtr->PowerPhase) {
|
||
case 1:
|
||
|
||
//
|
||
// Turn on power
|
||
//
|
||
|
||
DebugPrint((PCMCIA_DEBUG_INFO, "TcicSetPower: Powering UP pccard socket\n"));
|
||
|
||
TcicWriteBaseReg(socketPtr, R_PWR, pdb->dflt_vcc5v);
|
||
|
||
//
|
||
// Enable other strobes to socket
|
||
//
|
||
|
||
TcicWriteBaseReg(socketPtr, R_SCTRL, SCTRL_ENA);
|
||
|
||
//
|
||
// When power is enabled always stall to give the PCCARD
|
||
// a chance to react.
|
||
//
|
||
*pDelayTime = TcicStallCounter;
|
||
status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
break;
|
||
|
||
case 2:
|
||
|
||
if (!TcicPCCardReady(socketPtr)) {
|
||
DebugPrint((PCMCIA_PCCARD_READY,
|
||
"Tcic: PCCARD %x not ready after power\n",
|
||
socketPtr->RegisterOffset));
|
||
}
|
||
status = STATUS_SUCCESS;
|
||
break;
|
||
default:
|
||
ASSERT(FALSE);
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Disable socket strobes
|
||
//
|
||
DebugPrint((PCMCIA_DEBUG_INFO, "TcicSetPower: Powering DOWN pccard socket\n"));
|
||
|
||
TcicWriteBaseReg(socketPtr, R_SCTRL, 0);
|
||
|
||
//
|
||
// Diable power
|
||
//
|
||
|
||
TcicWriteBaseReg(socketPtr, R_PWR, 0);
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicInitializePcmciaSocket(
|
||
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 info
|
||
|
||
Return Value:
|
||
|
||
TRUE if successful
|
||
FALSE if not successful
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBSOCKET pdb = (PDBSOCKET)SocketPtr;
|
||
USHORT speedbits = WCTL_300NS;
|
||
|
||
speedbits >>= pdb->clkdiv;
|
||
|
||
//
|
||
// If this is the first socket on this controller,
|
||
// Reset the controller and do controller-wide initialization.
|
||
//
|
||
|
||
if (SocketPtr->RegisterOffset == 0) {
|
||
USHORT words[4];
|
||
int j;
|
||
|
||
//
|
||
// Reset Controller
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_SCTRL, SCTRL_RESET);
|
||
TcicWriteBaseReg(SocketPtr, R_SCTRL, 0);
|
||
|
||
//
|
||
// Initialize indirect socket regs
|
||
//
|
||
|
||
words[0] = pdb->dflt_scfg1;
|
||
words[1] = (USHORT)(IRSCF2_MLBAT1 | IRSCF2_MLBAT2 | IRSCF2_MRDY | IRSCF2_MWP);
|
||
TcicWriteIndirectRegs(SocketPtr, IR_SCFG_S(0), 2, words);
|
||
TcicWriteIndirectRegs(SocketPtr, IR_SCFG_S(1), 2, words);
|
||
|
||
//
|
||
// Initialize indirect memwin regs
|
||
//
|
||
|
||
words[0] = words[1] = 0;
|
||
words[2] = pdb->dflt_wrmctl;
|
||
for (j = 0; j < pdb->nmemwins; j++) {
|
||
TcicWriteIndirectRegs(SocketPtr, IR_MBASE_W(j), 3, words);
|
||
}
|
||
|
||
//
|
||
// Initialize indirect iowin regs
|
||
//
|
||
|
||
for (j = 0; j < pdb->niowins; j++ ) {
|
||
TcicWriteIndirectRegs(SocketPtr, IR_IOBASE_W(j), 2, words);
|
||
}
|
||
|
||
|
||
//
|
||
// Initialize SYSCFG
|
||
//
|
||
|
||
TcicWriteAuxReg(SocketPtr, MODE_AR_SYSCFG, pdb->dflt_syscfg);
|
||
}
|
||
|
||
//
|
||
// Get the specified Socket mapped into the TCIC registers
|
||
//
|
||
|
||
TcicSocketSelect(SocketPtr, SocketPtr->RegisterOffset);
|
||
|
||
//
|
||
// Per/socket we initialize the following base and aux regs:
|
||
// WCTL & ILOCK
|
||
//
|
||
|
||
TcicWriteAuxReg(SocketPtr, MODE_AR_WCTL, (USHORT)(pdb->dflt_wctl | speedbits));
|
||
TcicWriteAuxReg(SocketPtr, MODE_AR_ILOCK, pdb->dflt_ilock);
|
||
|
||
//
|
||
// Say card is there
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
USHORT
|
||
TcicReadBaseReg(
|
||
IN PSOCKET SocketPtr,
|
||
IN ULONG Register
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reads the specified TCIC base register,
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
Register - index of register to read
|
||
|
||
Return Value:
|
||
|
||
register value read
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT readData = 0;
|
||
|
||
switch (Register) {
|
||
case R_DATA:
|
||
case R_ADDR:
|
||
case R_ADDR2:
|
||
case R_EDC:
|
||
case R_AUX:
|
||
readData = READ_PORT_USHORT((PUSHORT)(SocketPtr->AddressPort + Register));
|
||
break;
|
||
|
||
case R_SCTRL:
|
||
case R_SSTAT:
|
||
case R_MODE:
|
||
case R_PWR:
|
||
case R_ICSR:
|
||
case R_IENA:
|
||
readData = (USHORT)READ_PORT_UCHAR(SocketPtr->AddressPort + Register);
|
||
break;
|
||
}
|
||
return readData;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicWriteBaseReg(
|
||
IN PSOCKET SocketPtr,
|
||
IN ULONG Register,
|
||
IN USHORT value
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write a value to the specified TCIC base register
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
Register - index of register to write
|
||
value - value to write to register
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT readData = 0;
|
||
|
||
switch (Register) {
|
||
case R_DATA:
|
||
case R_ADDR:
|
||
case R_ADDR2:
|
||
case R_EDC:
|
||
case R_AUX:
|
||
WRITE_PORT_USHORT((PUSHORT)(SocketPtr->AddressPort + Register), value);
|
||
break;
|
||
|
||
case R_SCTRL:
|
||
case R_SSTAT:
|
||
case R_MODE:
|
||
case R_PWR:
|
||
case R_ICSR:
|
||
case R_IENA:
|
||
WRITE_PORT_UCHAR(SocketPtr->AddressPort + Register, (UCHAR)value);
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
ULONG
|
||
TcicReadAddrReg(
|
||
IN PSOCKET SocketPtr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Read the current value of the TCIC address register
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
|
||
Return Value:
|
||
|
||
Address read from register
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG retaddr;
|
||
retaddr = (ULONG)TcicReadBaseReg(SocketPtr, R_ADDR);
|
||
retaddr |= ((ULONG)TcicReadBaseReg(SocketPtr, R_ADDR2) << 16);
|
||
return (retaddr);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicWriteAddrReg(
|
||
IN PSOCKET SocketPtr,
|
||
IN ULONG addr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write an address to the TCIC address register
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
addr - address to write to register
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR, (USHORT)(addr & 0x0000ffff));
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR2, (USHORT)(addr >> 16));
|
||
}
|
||
|
||
|
||
|
||
USHORT
|
||
TcicReadAuxReg(
|
||
IN PSOCKET SocketPtr,
|
||
IN ULONG Register
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Read the specified TCIC AUX register
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
Register - MODE_AR_xxx justified index of AUX register to read
|
||
|
||
Return Value:
|
||
|
||
contents of specified AUX register
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT readData = 0;
|
||
USHORT OldMode;
|
||
|
||
//
|
||
// Get the current mode register value
|
||
//
|
||
|
||
OldMode = TcicReadBaseReg(SocketPtr, R_MODE);
|
||
|
||
//
|
||
// Mask out previous AUX register selection and add in new selection
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_MODE,
|
||
(USHORT)((OldMode & ~MODE_AUXSEL_MASK) | Register));
|
||
|
||
|
||
//
|
||
// Read the selected AUX register
|
||
//
|
||
|
||
readData = TcicReadBaseReg(SocketPtr, R_AUX);
|
||
|
||
//
|
||
// Restore the mode reg to its original state
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_MODE, OldMode);
|
||
return readData;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicWriteAuxReg(
|
||
IN PSOCKET SocketPtr,
|
||
IN ULONG Register,
|
||
IN USHORT value
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write a value into the specified AUX register
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
Register - MODE_AR_xxx justified index of AUX register to write
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT readData = 0;
|
||
USHORT OldMode;
|
||
|
||
//
|
||
// Get the current mode register value
|
||
//
|
||
|
||
OldMode = TcicReadBaseReg(SocketPtr, R_MODE);
|
||
|
||
//
|
||
// Mask out previous AUX register selection and add in new selection
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_MODE,
|
||
(USHORT)((OldMode & ~MODE_AUXSEL_MASK) | Register));
|
||
|
||
//
|
||
// Write the data to the selected AUX register
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_AUX, value);
|
||
|
||
//
|
||
// Restore the mode reg to its original state
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_MODE, OldMode);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicReadIndirectRegs(
|
||
IN PSOCKET SocketPtr,
|
||
IN ULONG StartRegister,
|
||
IN USHORT numWords,
|
||
IN PUSHORT ReadBuffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Read one or multiple TCIC indirect registers.
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
StartRegister - starting indirect register
|
||
numWords - number of consecutive registers to read
|
||
ReadBuffer - data buffer
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT OldHaddr;
|
||
USHORT OldLaddr;
|
||
USHORT OldSctrl;
|
||
USHORT j;
|
||
|
||
//
|
||
// Get the current TCIC state
|
||
//
|
||
|
||
if (numWords > 1) {
|
||
|
||
//
|
||
// We won't set AUTO-Inc if only 1 word
|
||
//
|
||
|
||
OldSctrl = TcicReadBaseReg(SocketPtr, R_SCTRL);
|
||
}
|
||
|
||
OldLaddr = TcicReadBaseReg(SocketPtr, R_ADDR);
|
||
OldHaddr = TcicReadBaseReg(SocketPtr, R_ADDR2);
|
||
|
||
|
||
//
|
||
// Set the TCIC state required for reading the indirect registers
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR2,
|
||
(USHORT)(OldHaddr | ADR2_INDREG));
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR, (USHORT)StartRegister);
|
||
if (numWords > 1) {
|
||
TcicWriteBaseReg(SocketPtr, R_SCTRL, (USHORT)(OldSctrl | SCTRL_INCMODE_AUTO));
|
||
}
|
||
|
||
//
|
||
// Read the Indirect registert requested
|
||
//
|
||
|
||
for (j = 0; j < numWords; j++) {
|
||
*ReadBuffer++ = TcicReadBaseReg(SocketPtr, R_DATA);
|
||
}
|
||
|
||
//
|
||
// Restore the original TCIC state
|
||
//
|
||
|
||
if (numWords > 1) {
|
||
|
||
//
|
||
// We didn't set AUTO-Inc if only 1 word
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_SCTRL, OldSctrl);
|
||
}
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR2, OldHaddr);
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR, OldLaddr);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicWriteIndirectRegs(
|
||
IN PSOCKET SocketPtr,
|
||
IN ULONG StartRegister,
|
||
IN USHORT numWords,
|
||
IN PUSHORT WriteBuffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Write one or multiple TCIC indirect registers.
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
StartRegister - starting indirect register
|
||
numWords - number of consecutive registers to write
|
||
WriteBuffer - data buffer
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT OldHaddr;
|
||
USHORT OldLaddr;
|
||
USHORT OldSctrl;
|
||
USHORT j;
|
||
|
||
//
|
||
// Get the current TCIC state
|
||
//
|
||
|
||
if (numWords > 1) {
|
||
|
||
//
|
||
// We won't set AUTO-Inc if only 1 word
|
||
//
|
||
|
||
OldSctrl = TcicReadBaseReg(SocketPtr, R_SCTRL);
|
||
}
|
||
|
||
OldLaddr = TcicReadBaseReg(SocketPtr, R_ADDR);
|
||
OldHaddr = TcicReadBaseReg(SocketPtr, R_ADDR2);
|
||
|
||
//
|
||
// Set the TCIC state required for reading the indirect registers
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR2, (USHORT)(OldHaddr | (USHORT)ADR2_INDREG));
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR, (USHORT)StartRegister);
|
||
if (numWords > 1) {
|
||
TcicWriteBaseReg(SocketPtr, R_SCTRL, (USHORT)(OldSctrl | SCTRL_INCMODE_AUTO));
|
||
}
|
||
|
||
//
|
||
// Read the Indirect registert requested
|
||
//
|
||
|
||
for (j = 0; j < numWords; j++) {
|
||
TcicWriteBaseReg(SocketPtr, R_DATA, *WriteBuffer++);
|
||
}
|
||
|
||
//
|
||
// Restore the original TCIC state
|
||
//
|
||
|
||
if (numWords > 1) {
|
||
|
||
//
|
||
// We didn't set AUTO-Inc if only 1 word
|
||
//
|
||
|
||
TcicWriteBaseReg(SocketPtr, R_SCTRL, OldSctrl);
|
||
}
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR2, OldHaddr);
|
||
TcicWriteBaseReg(SocketPtr, R_ADDR, OldLaddr);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
USHORT
|
||
TcicSocketSelect(
|
||
IN PSOCKET SocketPtr,
|
||
IN USHORT sktnum
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Map the specified socket registers into TCIC register space.
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for this socket
|
||
sktnum - socket number to map.
|
||
|
||
Return Value:
|
||
|
||
previous socket mapped.
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT OldAddrHi;
|
||
|
||
OldAddrHi = READ_PORT_USHORT((PUSHORT)(SocketPtr->AddressPort + R_ADDR2));
|
||
|
||
WRITE_PORT_USHORT((PUSHORT)(SocketPtr->AddressPort + R_ADDR2),
|
||
(USHORT)((OldAddrHi & ~TCIC_SS_MASK) | (USHORT)(sktnum << TCIC_SS_SHFT)));
|
||
|
||
return (USHORT)((OldAddrHi & TCIC_SS_MASK) >> TCIC_SS_SHFT);
|
||
}
|
||
|
||
|
||
|
||
ULONG
|
||
TcicReadCardMemory(
|
||
IN PPDO_EXTENSION PdoExtension,
|
||
IN MEMORY_SPACE MemorySpace,
|
||
IN ULONG Offset,
|
||
IN PUCHAR Buffer,
|
||
IN ULONG Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will set up the card to read attribute memory.
|
||
|
||
Arguments:
|
||
|
||
SocketPtr -- The socket info in for the card being read
|
||
Offset -- Offset from which to read
|
||
MemorySpace -- Attribute memory or Common Memory
|
||
Buffer -- Pointer to buffer in which memory contents are returned
|
||
Length -- No. of bytes to be returned
|
||
|
||
Return Value:
|
||
|
||
TRUE - if read was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSOCKET SocketPtr = PdoExtension->Socket;
|
||
ULONG size;
|
||
ULONG tcicaddr;
|
||
ULONG i;
|
||
USHORT word;
|
||
|
||
//
|
||
// Make sure the card is ready
|
||
//
|
||
|
||
if (!TcicPCCardReady(SocketPtr)) {
|
||
DebugPrint((PCMCIA_PCCARD_READY,
|
||
"Tcic: PCCARD %x not ready for read attribute memory\n",
|
||
SocketPtr->RegisterOffset));
|
||
}
|
||
|
||
if (MemorySpace != PCCARD_ATTRIBUTE_MEMORY) {
|
||
|
||
return 0;
|
||
}
|
||
|
||
tcicaddr = ADDR_REG | (SocketPtr->RegisterOffset << ADDR_SS_SHFT);
|
||
TcicWriteAddrReg(SocketPtr, tcicaddr);
|
||
|
||
word = TcicReadBaseReg(SocketPtr, R_SCTRL);
|
||
word |= SCTRL_INCMODE_AUTO;
|
||
TcicWriteBaseReg(SocketPtr, R_SCTRL, word);
|
||
|
||
//
|
||
// Hardware needs to settle
|
||
//
|
||
PcmciaWait(50000);
|
||
|
||
//
|
||
// Skip up to the offset
|
||
//
|
||
for (i = 0; i < Offset; i++) {
|
||
(VOID)TcicReadBaseReg(SocketPtr, R_DATA);
|
||
}
|
||
|
||
//
|
||
// Read the attribute memory
|
||
//
|
||
for (i = 0; i < Length; i++) {
|
||
*Buffer++ = (UCHAR)TcicReadBaseReg(SocketPtr, R_DATA);
|
||
}
|
||
|
||
return Length;
|
||
}
|
||
|
||
|
||
|
||
ULONG
|
||
TcicWriteCardMemory(
|
||
IN PPDO_EXTENSION PdoExtension,
|
||
IN MEMORY_SPACE MemorySpace,
|
||
IN ULONG Offset,
|
||
IN PUCHAR Buffer,
|
||
IN ULONG Length
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
This routine will write into the configuration memory on the card
|
||
with the supplied buffer. This is provided as a service to certain
|
||
client drivers (netcard) which need to write to the attribute memory
|
||
(say) to set parameters etc.
|
||
|
||
Arguments:
|
||
|
||
SocketPtr -- The socket info in for the card being written to
|
||
MemorySpace -- indicates which space - attribute or common memory
|
||
Offset -- Offset in the memory to write to
|
||
Buffer -- Buffer contents being dumped to the card
|
||
Length -- Length of the buffer being written out
|
||
|
||
--*/
|
||
{
|
||
|
||
PSOCKET SocketPtr = PdoExtension->Socket;
|
||
#define TCIC_ATTRIBUTE_MEM_WINDOW_INDEX 5
|
||
PUCHAR memoryPtr;
|
||
ULONG index;
|
||
UCHAR memGran;
|
||
|
||
memGran = (MemorySpace == PCCARD_ATTRIBUTE_MEMORY)? 2 : 1;
|
||
|
||
memoryPtr=((PFDO_EXTENSION) (SocketPtr->DeviceExtension))->AttributeMemoryBase +
|
||
memGran * Offset;
|
||
|
||
TcicSetMemWin(SocketPtr,
|
||
(USHORT) (TCIC_ATTRIBUTE_MEM_WINDOW_INDEX+SocketPtr->SocketNumber),
|
||
0,
|
||
SocketPtr->DeviceExtension->PhysicalBase.LowPart,
|
||
SocketPtr->DeviceExtension->AttributeMemorySize,
|
||
(UCHAR) (MemorySpace == PCCARD_ATTRIBUTE_MEMORY),
|
||
0,
|
||
0);
|
||
|
||
if (!TcicPCCardReady(SocketPtr)) {
|
||
DebugPrint((PCMCIA_PCCARD_READY,
|
||
"TCIC: PCCARD in socket %x not ready for write memory\n",
|
||
SocketPtr->RegisterOffset));
|
||
}
|
||
for (index=0; index < Length; index++) {
|
||
WRITE_REGISTER_UCHAR(memoryPtr, Buffer[index]);
|
||
memoryPtr += memGran;
|
||
}
|
||
|
||
TcicSetMemWin(SocketPtr,
|
||
(USHORT) (TCIC_ATTRIBUTE_MEM_WINDOW_INDEX+SocketPtr->SocketNumber),
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0);
|
||
return Length;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicProcessConfigureRequest(
|
||
IN PSOCKET socketPtr,
|
||
IN PCARD_REQUEST request,
|
||
IN PUCHAR Base
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes a configure or IRQ setup request.
|
||
|
||
Arguments:
|
||
|
||
socketPtr - instance data for this socket
|
||
ConfigRequest -- Socket config structure
|
||
Base - the I/O port base - not used
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT index, index2;
|
||
USHORT tmp;
|
||
ULONG ltmp;
|
||
USHORT words[3];
|
||
PDBSOCKET pdbs;
|
||
|
||
//
|
||
// 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:
|
||
|
||
//
|
||
// Set up I/O ranges on the controller
|
||
//
|
||
|
||
for (index = 0; index < request->u.Io.NumberOfRanges; index++) {
|
||
if (request->u.Io.IoEntry[index].BasePort != 0) {
|
||
TcicSetIoWin(socketPtr, index,
|
||
request->u.Io.IoEntry[index].BasePort,
|
||
request->u.Io.IoEntry[index].NumPorts,
|
||
request->u.Io.IoEntry[index].Attributes);
|
||
} else {
|
||
DebugPrint((PCMCIA_DEBUG_FAIL, "PCMCIA: Got an IO Configure Request with an invalid Port\n"));
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case IRQ_REQUEST:
|
||
|
||
pdbs = (PDBSOCKET)socketPtr;
|
||
ltmp = ADDR_INDREG | (socketPtr->RegisterOffset << ADDR_SS_SHFT);
|
||
ltmp |= (ULONG)IR_SCFG_S(socketPtr->RegisterOffset);
|
||
TcicWriteAddrReg(socketPtr, ltmp);
|
||
TcicWriteBaseReg(socketPtr, R_SCTRL, SCTRL_ENA);
|
||
tmp = TcicReadBaseReg(socketPtr, R_DATA);
|
||
tmp &= ~IRSCFG_IRQ_MASK;
|
||
tmp |= pdbs->IRQMapTbl[request->u.Irq.AssignedIRQ];
|
||
TcicWriteBaseReg(socketPtr, R_DATA, tmp);
|
||
break;
|
||
|
||
case CONFIGURE_REQUEST:
|
||
|
||
//
|
||
// This is where we setup the card and get it ready for operation
|
||
//
|
||
|
||
if (!TcicPCCardReady(socketPtr)) {
|
||
DebugPrint((PCMCIA_PCCARD_READY,
|
||
"Tcic: PCCARD %x not ready for configuration index\n",
|
||
socketPtr));
|
||
return FALSE;
|
||
}
|
||
|
||
if (request->u.Config.RegisterWriteMask & REGISTER_WRITE_CONFIGURATION_INDEX) {
|
||
ltmp = request->u.Config.ConfigBase;
|
||
ltmp |= ADDR_REG | (socketPtr->RegisterOffset << ADDR_SS_SHFT);
|
||
TcicWriteAddrReg(socketPtr, ltmp);
|
||
TcicWriteBaseReg(socketPtr, R_SCTRL, SCTRL_ENA);
|
||
|
||
TcicWriteBaseReg(socketPtr, R_DATA, request->u.Config.ConfigIndex);
|
||
PcmciaWait(TcicStallCounter);
|
||
TcicWriteBaseReg(socketPtr, R_DATA,
|
||
(USHORT)(request->u.Config.ConfigIndex | 0x40));
|
||
PcmciaWait(TcicStallCounter);
|
||
}
|
||
if (request->u.Config.RegisterWriteMask & REGISTER_WRITE_CARD_CONFIGURATION) {
|
||
ltmp = request->u.Config.ConfigBase + 2;
|
||
ltmp |= ADDR_REG | (socketPtr->RegisterOffset << ADDR_SS_SHFT);
|
||
TcicWriteAddrReg(socketPtr, ltmp);
|
||
TcicWriteBaseReg(socketPtr, R_SCTRL, SCTRL_ENA);
|
||
|
||
tmp = TcicReadBaseReg(socketPtr, R_DATA);
|
||
tmp |= request->u.Config.CardConfiguration;
|
||
|
||
//
|
||
// turn off power control bit
|
||
//
|
||
|
||
tmp &= ~0x04;
|
||
|
||
TcicWriteBaseReg(socketPtr, R_DATA, tmp);
|
||
}
|
||
break;
|
||
|
||
case DECONFIGURE_REQUEST:
|
||
//
|
||
// Deregister the interrupt
|
||
//
|
||
pdbs = (PDBSOCKET)socketPtr;
|
||
ltmp = ADDR_INDREG | (socketPtr->RegisterOffset << ADDR_SS_SHFT);
|
||
ltmp |= (ULONG)IR_SCFG_S(socketPtr->RegisterOffset);
|
||
TcicWriteAddrReg(socketPtr, ltmp);
|
||
TcicWriteBaseReg(socketPtr, R_SCTRL, SCTRL_ENA);
|
||
tmp = TcicReadBaseReg(socketPtr, R_DATA);
|
||
tmp &= ~IRSCFG_IRQ_MASK;
|
||
TcicWriteBaseReg(socketPtr, R_DATA, tmp);
|
||
|
||
//
|
||
// Disable I/O, memory windows
|
||
//
|
||
break;
|
||
|
||
case MEM_REQUEST:
|
||
|
||
//
|
||
// Set up memory ranges on the controller.
|
||
//
|
||
|
||
for (index = 0; index < request->u.Memory.NumberOfRanges; index++) {
|
||
|
||
TcicSetMemWin(socketPtr,
|
||
index,
|
||
request->u.Memory.MemoryEntry[index].BaseAddress,
|
||
request->u.Memory.MemoryEntry[index].HostAddress,
|
||
request->u.Memory.MemoryEntry[index].WindowSize,
|
||
request->u.Memory.MemoryEntry[index].AttributeMemory,
|
||
request->u.Memory.AccessSpeed,
|
||
request->u.Memory.Attributes);
|
||
}
|
||
break;
|
||
|
||
|
||
default:
|
||
DebugPrint((PCMCIA_DEBUG_FAIL, "PCMCIA: ConfigRequest is INVALID!\n"));
|
||
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicDetectCardInSocket(
|
||
IN PSOCKET socketPtr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will determine if a card is in the socket
|
||
|
||
Arguments:
|
||
|
||
SocketPtr -- Socket info.
|
||
|
||
Return Value:
|
||
|
||
TRUE if card is present.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Get the specified socket mapped into the TCIC registers
|
||
//
|
||
|
||
TcicSocketSelect(socketPtr, socketPtr->RegisterOffset);
|
||
|
||
//
|
||
// Read the Tcic status register to see if the card is in there.
|
||
//
|
||
return (TcicReadBaseReg(socketPtr, R_SSTAT) & SSTAT_CD) ?TRUE :FALSE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicDetectCardChanged(
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN changed;
|
||
|
||
//
|
||
// Get the specified socket mapped into the TCIC registers
|
||
//
|
||
|
||
TcicSocketSelect(socketPtr, socketPtr->RegisterOffset);
|
||
|
||
//
|
||
// Read the Tcic ICSR register to see if CD's have changed.
|
||
//
|
||
|
||
changed = (TcicReadBaseReg(socketPtr, R_ICSR) & ICSR_CDCHG) ?TRUE :FALSE;
|
||
|
||
//
|
||
// Clear bits in ICSR
|
||
//
|
||
|
||
while (TcicReadBaseReg(socketPtr, R_ICSR)) {
|
||
TcicWriteBaseReg(socketPtr, R_ICSR, ICSR_JAM);
|
||
}
|
||
|
||
return (changed);
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicDetectReadyChanged(
|
||
IN PSOCKET socketPtr
|
||
)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicPCCardReady(
|
||
IN PSOCKET SocketPtr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Loop for a reasonable amount of time waiting for the card status to
|
||
return ready.
|
||
|
||
Arguments:
|
||
|
||
SocketPtr - instance data for the socket to check.
|
||
|
||
Return Value:
|
||
|
||
TRUE - the card is ready.
|
||
FALSE - after a reasonable delay the card is still not ready.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG index;
|
||
|
||
//
|
||
// Get the specified socket mapped into the TCIC registers
|
||
//
|
||
|
||
TcicSocketSelect(SocketPtr, SocketPtr->RegisterOffset);
|
||
|
||
for (index = 0;
|
||
index < 500000
|
||
&& !(TcicReadBaseReg(SocketPtr, R_SSTAT) & SSTAT_RDY);
|
||
index++) {
|
||
|
||
PcmciaWait(20);
|
||
//
|
||
// Check if the card is still there: if not, we can return
|
||
//
|
||
if (!TcicDetectCardInSocket(SocketPtr)) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (index < 500000) {
|
||
DebugPrint((PCMCIA_COUNTERS, "TcicPCCardReady: %d\n", index));
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
TcicDetect(
|
||
IN PFDO_EXTENSION DeviceExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Locate any PCMCIA sockets supported by this driver. This routine
|
||
will find the TCIC2 and compatible parts.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - the root for the SocketList.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if a socket is found - failure status otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG ioPortBase = 0x100;
|
||
ULONG ioBaseIncrement = 0x10;
|
||
ULONG tcicLowAddr;
|
||
ULONG tcicHighAddr;
|
||
ULONG addressSpace;
|
||
BOOLEAN mapped;
|
||
PHYSICAL_ADDRESS cardAddress;
|
||
PHYSICAL_ADDRESS portAddress;
|
||
SOCKET locskt;
|
||
UCHAR socketNumber = 0;
|
||
static BOOLEAN foundOne = FALSE;
|
||
BOOLEAN resourcesAllocated = FALSE;
|
||
BOOLEAN conflict;
|
||
PCM_RESOURCE_LIST cmResourceList = NULL;
|
||
PCM_PARTIAL_RESOURCE_LIST cmPartialResourceList;
|
||
NTSTATUS status;
|
||
|
||
if (foundOne) {
|
||
//
|
||
// No support for multiple controllers currently..
|
||
// So we just give up if one controller was already reported
|
||
//
|
||
return STATUS_NO_MORE_ENTRIES;
|
||
}
|
||
|
||
|
||
DeviceExtension->Configuration.InterfaceType = Isa;
|
||
DeviceExtension->Configuration.BusNumber = 0x0;
|
||
|
||
TcicRegistryLookupScanLimits(&tcicLowAddr, &tcicHighAddr);
|
||
|
||
//
|
||
// Get the resources used for detection
|
||
//
|
||
cmResourceList = ExAllocatePool(PagedPool, sizeof(CM_RESOURCE_LIST));
|
||
|
||
if (!cmResourceList) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(cmResourceList, sizeof(CM_RESOURCE_LIST));
|
||
cmResourceList->Count = 1;
|
||
cmResourceList->List[0].InterfaceType = Isa;
|
||
cmPartialResourceList = &(cmResourceList->List[0].PartialResourceList);
|
||
cmPartialResourceList->Version = 1;
|
||
cmPartialResourceList->Revision = 1;
|
||
cmPartialResourceList->Count = 1;
|
||
cmPartialResourceList->PartialDescriptors[0].Type = CmResourceTypePort;
|
||
cmPartialResourceList->PartialDescriptors[0].ShareDisposition = CmResourceShareDeviceExclusive;
|
||
cmPartialResourceList->PartialDescriptors[0].Flags = CM_RESOURCE_PORT_IO | CM_RESOURCE_PORT_10_BIT_DECODE;
|
||
cmPartialResourceList->PartialDescriptors[0].u.Port.Length = 2;
|
||
|
||
|
||
for (ioPortBase = tcicLowAddr;
|
||
ioPortBase < tcicHighAddr;
|
||
ioPortBase += ioBaseIncrement) {
|
||
|
||
//
|
||
// Reset ioBaseIncrement to default value
|
||
//
|
||
|
||
ioBaseIncrement = 0x10;
|
||
|
||
addressSpace = 1; // port space
|
||
portAddress.LowPart = ioPortBase;
|
||
portAddress.HighPart = 0;
|
||
|
||
//
|
||
// Free up the allocated resources if any
|
||
//
|
||
if (resourcesAllocated) {
|
||
IoReportResourceForDetection(DeviceExtension->DriverObject,
|
||
NULL, 0, NULL, NULL, 0, &conflict);
|
||
}
|
||
|
||
resourcesAllocated = FALSE;
|
||
cmPartialResourceList->PartialDescriptors[0].u.Port.Start = portAddress;
|
||
|
||
status=IoReportResourceForDetection(
|
||
DeviceExtension->DriverObject,
|
||
cmResourceList,
|
||
sizeof(CM_RESOURCE_LIST),
|
||
NULL,
|
||
NULL,
|
||
0,
|
||
&conflict);
|
||
if (!NT_SUCCESS(status) || conflict) {
|
||
continue;
|
||
}
|
||
resourcesAllocated = TRUE;
|
||
|
||
|
||
if (!HalTranslateBusAddress(Isa, 0, portAddress, &addressSpace,&cardAddress)) {
|
||
continue;
|
||
}
|
||
|
||
if (addressSpace) {
|
||
mapped = FALSE;
|
||
locskt.AddressPort = (PUCHAR)(cardAddress.QuadPart);
|
||
} else {
|
||
mapped = TRUE;
|
||
locskt.AddressPort = MmMapIoSpace(cardAddress, 0x10, FALSE);
|
||
}
|
||
|
||
locskt.RegisterOffset = 0;
|
||
|
||
//
|
||
// Sniff the address to see if it even resembles a TCIC chip
|
||
//
|
||
|
||
foundOne = TcicReservedBitsOK(&locskt);
|
||
if (mapped) {
|
||
MmUnmapIoSpace(locskt.AddressPort, 0x10);
|
||
}
|
||
|
||
//
|
||
// Found an adapter
|
||
//
|
||
|
||
if (foundOne) {
|
||
PcmciaSetControllerType(DeviceExtension, PcmciaDatabook);
|
||
break;
|
||
}
|
||
#if 0
|
||
//
|
||
// Now check for the aliases
|
||
//
|
||
|
||
switch (TcicCheckAliasType((PDBSOCKET)socketPtr)) {
|
||
case TCIC_IS140:
|
||
|
||
//
|
||
// TMI-140s decode 32 consecutive bytes, make
|
||
// sure we skip past the alias
|
||
//
|
||
|
||
ioBaseIncrement += 0x10;
|
||
break;
|
||
|
||
}
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// Free up the allocated resources if any
|
||
//
|
||
if (resourcesAllocated) {
|
||
IoReportResourceForDetection(DeviceExtension->DriverObject,
|
||
NULL, 0, NULL, NULL, 0, &conflict);
|
||
}
|
||
//
|
||
// Free up allocated memory if any
|
||
//
|
||
if (cmResourceList) {
|
||
ExFreePool(cmResourceList);
|
||
}
|
||
return foundOne ? STATUS_SUCCESS : STATUS_NO_MORE_ENTRIES;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
TcicBuildSocketList(
|
||
IN PFDO_EXTENSION DeviceExtension
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Locate any PCMCIA sockets supported by this driver. This routine
|
||
will find the TCIC2 and compatible parts and construct DBSOCKET
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSOCKET socketPtr = NULL;
|
||
PSOCKET previousSocketPtr;
|
||
SOCKET locskt;
|
||
static UCHAR socketNumber = 0;
|
||
|
||
previousSocketPtr = NULL;
|
||
|
||
|
||
locskt.RegisterOffset = 0;
|
||
locskt.AddressPort = (PUCHAR)DeviceExtension->Configuration.UntranslatedPortAddress;
|
||
|
||
//
|
||
// Sniff the address to see if it even resembles a TCIC chip
|
||
//
|
||
|
||
if (TcicReservedBitsOK(&locskt) == FALSE ) {
|
||
return STATUS_NO_MORE_ENTRIES;
|
||
}
|
||
|
||
//
|
||
// Found an adapter
|
||
//
|
||
|
||
TcicFillInAdapter(&locskt,
|
||
&socketPtr,
|
||
&previousSocketPtr,
|
||
DeviceExtension,
|
||
(ULONG)DeviceExtension->Configuration.UntranslatedPortAddress);
|
||
|
||
if (socketPtr == NULL) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
socketPtr->SocketNumber = socketNumber++;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
TcicFillInAdapter(
|
||
IN PSOCKET plocskt,
|
||
IN PSOCKET *psocketPtr,
|
||
IN PSOCKET *previousSocketPtr,
|
||
IN PFDO_EXTENSION DeviceExtension,
|
||
IN ULONG ioPortBase
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Fill in the DBSOCKET pointer info for the adapter just located by
|
||
TcicDetect(). This routine is not part of TcicDetect() so as to allow
|
||
for logic flow when dealing with multiple sockets or adapters.
|
||
|
||
Arguments:
|
||
|
||
plocskt - info regarding the socket just found
|
||
psocketPtr - current socket ptr from caller
|
||
previousSocketPtr - prev socket ptr form caller
|
||
DeviceExtension - head of socket list
|
||
ioPortBase - physical i/o addr for this controller
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBSOCKET dbsocketPtr = ExAllocatePool(NonPagedPool, sizeof(DBSOCKET));
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!dbsocketPtr) {
|
||
return;
|
||
}
|
||
RtlZeroMemory(dbsocketPtr, sizeof(DBSOCKET));
|
||
dbsocketPtr->physPortAddr = ioPortBase;
|
||
*psocketPtr = (PSOCKET)dbsocketPtr;
|
||
|
||
(*psocketPtr)->DeviceExtension = DeviceExtension;
|
||
(*psocketPtr)->RegisterOffset = 0;
|
||
(*psocketPtr)->AddressPort = plocskt->AddressPort;
|
||
(*psocketPtr)->SocketFnPtr = &TcicSupportFns;
|
||
|
||
if (*previousSocketPtr) {
|
||
(*previousSocketPtr)->NextSocket = *psocketPtr;
|
||
} else {
|
||
DeviceExtension->SocketList = *psocketPtr;
|
||
}
|
||
*previousSocketPtr = *psocketPtr;
|
||
|
||
DebugPrint((PCMCIA_DEBUG_DETECT,
|
||
"PCMCIA: TCIC Port %x\n",
|
||
plocskt->AddressPort));
|
||
|
||
//
|
||
// Fill in the rest of the adapter info here...
|
||
//
|
||
|
||
TcicGetAdapterInfo(dbsocketPtr);
|
||
|
||
//
|
||
// See if there is a second socket on this TCIC
|
||
//
|
||
|
||
if (TcicCheckSkt(plocskt, 1)) {
|
||
dbsocketPtr = ExAllocatePool(NonPagedPool, sizeof(DBSOCKET));
|
||
if (dbsocketPtr) {
|
||
RtlMoveMemory(dbsocketPtr, *psocketPtr, sizeof(DBSOCKET));
|
||
*psocketPtr = (PSOCKET)dbsocketPtr;
|
||
(*psocketPtr)->RegisterOffset = 1;
|
||
(*previousSocketPtr)->NextSocket = *psocketPtr;
|
||
*previousSocketPtr = *psocketPtr;
|
||
dbsocketPtr->dflt_vcc5v = TcicGet5vVccVal(dbsocketPtr);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
TcicGetAdapterInfo(
|
||
IN PDBSOCKET dbsocketPtr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Deterimine adapter specific information from detection heuristics.
|
||
|
||
Arguments:
|
||
|
||
dbsocketPtr - structure to fill in.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
TcicChipID(dbsocketPtr);
|
||
|
||
dbsocketPtr->niowins = (UCHAR)TcicGetnIOWins(dbsocketPtr);
|
||
dbsocketPtr->nmemwins = (UCHAR)TcicGetnMemWins(dbsocketPtr);
|
||
dbsocketPtr->clkdiv = TcicClockRate(&dbsocketPtr->skt) - (USHORT)1;
|
||
dbsocketPtr->dflt_vcc5v = TcicGet5vVccVal(dbsocketPtr);
|
||
|
||
dbsocketPtr->dflt_wctl = (USHORT)((dbsocketPtr->clkdiv != 0)
|
||
? (WAIT_BCLK | WAIT_RISING | WAIT_ASYNC)
|
||
: (WAIT_ASYNC | WAIT_RISING));
|
||
|
||
dbsocketPtr->dflt_syscfg = (USHORT)(SYSCFGMPSEL_EXTSEL | SYSCFG_MCSFULL);
|
||
|
||
if (TcicCheckXBufNeeded(&dbsocketPtr->skt)) {
|
||
dbsocketPtr->dflt_syscfg |= (USHORT)(SYSCFG_ICSXB | SYSCFG_MCSXB);
|
||
}
|
||
|
||
dbsocketPtr->dflt_ilock = (USHORT)ILOCK_HOLD_CCLK;
|
||
dbsocketPtr->dflt_wrmctl = (USHORT)0;
|
||
dbsocketPtr->dflt_scfg1 = (USHORT)IRSCFG_IOSTS;
|
||
|
||
TcicGetIRQMap(dbsocketPtr);
|
||
|
||
//
|
||
// Fiddle the map for all but 084/184 so that SKTIRQ (0bh) has
|
||
// the correct map code (1) (PNPFIX)
|
||
//
|
||
|
||
if (TcicHasSktIRQPin(dbsocketPtr) == TRUE && dbsocketPtr->IRQMapTbl[11] == 11) {
|
||
dbsocketPtr->IRQMapTbl[11] = 1;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
PUCHAR
|
||
TcicAllocateMemRange(
|
||
IN PFDO_EXTENSION DeviceExtension,
|
||
IN PULONG Mapped,
|
||
IN PULONG Physical
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Search the 640K to 1MB region for an 8K open area to be used
|
||
for XBuffer checking.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - head of socket list
|
||
Mapped - state info from caller to allow later release
|
||
Physical - state info from caller to allow later release
|
||
|
||
Return Value:
|
||
|
||
A physical address for the window to the card or zero meaning
|
||
there is no opening.
|
||
|
||
--*/
|
||
|
||
{
|
||
#define NUMBER_OF_TEST_BYTES 5
|
||
PHYSICAL_ADDRESS physicalMemoryAddress;
|
||
PHYSICAL_ADDRESS halMemoryAddress;
|
||
BOOLEAN translated;
|
||
ULONG untranslatedAddress;
|
||
PUCHAR memoryAddress;
|
||
PUCHAR bogus;
|
||
ULONG addressSpace;
|
||
ULONG index;
|
||
UCHAR memory[NUMBER_OF_TEST_BYTES];
|
||
|
||
PAGED_CODE();
|
||
|
||
*Mapped = FALSE;
|
||
|
||
if (DeviceExtension->PhysicalBase.QuadPart) {
|
||
untranslatedAddress = DeviceExtension->PhysicalBase.LowPart;
|
||
} else {
|
||
untranslatedAddress = 0xd0000;
|
||
}
|
||
|
||
for (/* nothing */; untranslatedAddress < 0xFF000; untranslatedAddress += TCIC_WINDOW_ALIGNMENT) {
|
||
|
||
if (untranslatedAddress == 0xc0000) {
|
||
|
||
//
|
||
// This is VGA. Keep this test if the for loop should
|
||
// ever change.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
addressSpace = 0;
|
||
physicalMemoryAddress.LowPart = untranslatedAddress;
|
||
physicalMemoryAddress.HighPart = 0;
|
||
|
||
translated = HalTranslateBusAddress(Isa,
|
||
0,
|
||
physicalMemoryAddress,
|
||
&addressSpace,
|
||
&halMemoryAddress);
|
||
|
||
if (!translated) {
|
||
|
||
//
|
||
// HAL doesn't like this translation
|
||
//
|
||
|
||
continue;
|
||
}
|
||
if (addressSpace) {
|
||
memoryAddress = (PUCHAR)(halMemoryAddress.QuadPart);
|
||
} else {
|
||
memoryAddress = MmMapIoSpace(halMemoryAddress, TCIC_WINDOW_SIZE, FALSE);
|
||
}
|
||
|
||
//
|
||
// Test the memory window to determine if it is a BIOS, video
|
||
// memory, or open memory. Only want to keep the window if it
|
||
// is not being used by something else.
|
||
//
|
||
|
||
for (index = 0; index < NUMBER_OF_TEST_BYTES; index++) {
|
||
memory[index] = READ_REGISTER_UCHAR(memoryAddress + index);
|
||
if (index) {
|
||
if (memory[index] != memory[index - 1]) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (index == NUMBER_OF_TEST_BYTES) {
|
||
|
||
//
|
||
// There isn't a BIOS here
|
||
//
|
||
|
||
UCHAR memoryPattern[NUMBER_OF_TEST_BYTES];
|
||
BOOLEAN changed = FALSE;
|
||
|
||
//
|
||
// Check for video memory - open memory should always remain
|
||
// the same regardless what the changes are. Change the
|
||
// pattern previously found.
|
||
//
|
||
|
||
for (index = 0; index < NUMBER_OF_TEST_BYTES; index++) {
|
||
memoryPattern[index] = ~memory[index];
|
||
WRITE_REGISTER_UCHAR(memoryAddress + index,
|
||
memoryPattern[index]);
|
||
}
|
||
|
||
//
|
||
// See if the pattern in memory changed.
|
||
// Some system exhibit a problem where the memory pattern
|
||
// seems to be cached. If this code is debugged it will
|
||
// work as expected, but if it is run normally it will
|
||
// always return that the memory changed. This random
|
||
// wandering seems to remove this problem.
|
||
//
|
||
|
||
for (index = 0; index < NUMBER_OF_TEST_BYTES; index++) {
|
||
memoryPattern[index] = 0;
|
||
}
|
||
bogus = ExAllocatePool(NonPagedPool, 64 * 1024);
|
||
|
||
if (bogus) {
|
||
for (index = 0; index < 64 * 1024; index++) {
|
||
bogus[index] = 0;
|
||
}
|
||
ExFreePool(bogus);
|
||
}
|
||
|
||
//
|
||
// Now go off and do the actual check to see if the memory
|
||
// changed.
|
||
//
|
||
|
||
for (index = 0; index < NUMBER_OF_TEST_BYTES; index++) {
|
||
|
||
if ((memoryPattern[index] = READ_REGISTER_UCHAR(memoryAddress + index)) != memory[index]) {
|
||
|
||
//
|
||
// It changed - this is not an area of open memory
|
||
//
|
||
|
||
changed = TRUE;
|
||
}
|
||
WRITE_REGISTER_UCHAR(memoryAddress + index,
|
||
memory[index]);
|
||
}
|
||
|
||
if (!changed) {
|
||
|
||
//
|
||
// Area isn't a BIOS and didn't change when written.
|
||
// Use this region for the memory window to PCMCIA
|
||
// attribute memory.
|
||
//
|
||
|
||
*Mapped = addressSpace ? FALSE : TRUE;
|
||
*Physical = untranslatedAddress;
|
||
return memoryAddress;
|
||
}
|
||
}
|
||
|
||
if (!addressSpace) {
|
||
MmUnmapIoSpace(memoryAddress, TCIC_WINDOW_SIZE);
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicReservedBitsOK(
|
||
IN PSOCKET pskt
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Various offsets from a base IO address are read and checked for
|
||
reasonable values (e.g., see that reserved bits are zero)
|
||
First the primary registers are checked, then if the mode
|
||
register is pointing at an aux register that has reserved bits,
|
||
then that value is checked as well.
|
||
|
||
If the TCIC is not in reset, then the programming timers
|
||
will have expired by the time this runs
|
||
|
||
Further, a read from the data register should change the
|
||
EDC register.
|
||
|
||
Note that these tests are as nondestructive as possible, e.g.
|
||
initially only read accesses are made to the IO range in question.
|
||
|
||
Arguments:
|
||
|
||
pskt - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
TRUE if all reserved bits are zero
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT i, j, bits;
|
||
|
||
PAGED_CODE();
|
||
//
|
||
// R_ADDR bits 30:28 have restricted range
|
||
//
|
||
|
||
i = (USHORT)((TcicReadBaseReg(pskt, R_ADDR2) & TCIC_SS_MASK) >> TCIC_SS_SHFT);
|
||
if ( i > 1) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// R_SCTRL bits 6,2,1 are reserved
|
||
//
|
||
|
||
if (TcicReadBaseReg(pskt, R_SCTRL) & ((~(SCTRL_ENA|SCTRL_INCMODE|SCTRL_EDCSUM|SCTRL_RESET)) & 0x00ff)) {
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// R_ICSR bit 2 must be same as bit 3
|
||
//
|
||
|
||
i = TcicReadBaseReg(pskt, R_ICSR);
|
||
i &= (ICSR_ILOCK | ICSR_STOPCPU);
|
||
if ((i != 0) && (i != (ICSR_ILOCK | ICSR_STOPCPU))) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// R_IENA bits 7,2 are reserved
|
||
//
|
||
|
||
if (TcicReadBaseReg(pskt, R_IENA) & ((~(IENA_CDCHG|IENA_PROGTIME|IENA_ILOCK|IENA_CFG_MASK)) & 0xff)) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Some aux registers have reserved bits
|
||
// Which are we looking at?
|
||
//
|
||
|
||
i = (USHORT)(TcicReadBaseReg(pskt, R_MODE) & MODE_AUXSEL_MASK);
|
||
j = TcicReadBaseReg(pskt, R_AUX);
|
||
switch (i) {
|
||
case MODE_AR_SYSCFG:
|
||
if (INVALID_AR_SYSCFG(j)) {
|
||
return FALSE;
|
||
}
|
||
break;
|
||
|
||
|
||
case MODE_AR_ILOCK:
|
||
if (INVALID_AR_ILOCK(j)) {
|
||
return FALSE;
|
||
}
|
||
break;
|
||
|
||
case MODE_AR_TEST:
|
||
if (INVALID_AR_TEST(j)) {
|
||
return FALSE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Various bits set or not depending if in RESET mode
|
||
//
|
||
|
||
i = TcicReadBaseReg(pskt, R_SCTRL);
|
||
if (i & SCTRL_RESET) {
|
||
|
||
//
|
||
// address bits must be 0 */
|
||
//
|
||
|
||
if ((TcicReadBaseReg(pskt, R_ADDR) != 0) || (TcicReadBaseReg(pskt, R_ADDR2) != 0)) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// EDC bits must be 0 */
|
||
//
|
||
|
||
if (TcicReadBaseReg(pskt, R_EDC) != 0) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// We're OK, so take it out of reset
|
||
// Note: we can write a 0 because RESET guarantees us that the
|
||
// other bits in SCTRL are 0.
|
||
//
|
||
|
||
TcicWriteBaseReg(pskt, R_SCTRL, 0);
|
||
|
||
} else {
|
||
|
||
//
|
||
// not in reset
|
||
// programming timers must be expired
|
||
//
|
||
|
||
i = TcicReadBaseReg(pskt, R_SSTAT);
|
||
if ((i & (SSTAT_6US | SSTAT_10US | SSTAT_PROGTIME)) != (SSTAT_6US | SSTAT_10US | SSTAT_PROGTIME)) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// EDC bits should change on read from data space
|
||
// as long as either EDC or the data are nonzero
|
||
//
|
||
|
||
if ((TcicReadBaseReg(pskt, R_ADDR2) & ADR2_INDREG) == 0) {
|
||
|
||
j = TcicReadBaseReg(pskt, R_EDC);
|
||
i = TcicReadBaseReg(pskt, R_DATA);
|
||
|
||
if ( i | j ) {
|
||
i = TcicReadBaseReg(pskt, R_EDC);
|
||
if (i==j) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
j = TcicReadBaseReg(pskt, R_MODE);
|
||
i = (USHORT)(j ^ MODE_AUXSEL_MASK);
|
||
TcicWriteBaseReg(pskt, R_MODE, i);
|
||
if (TcicReadBaseReg(pskt, R_MODE) != i) {
|
||
return(FALSE);
|
||
}
|
||
|
||
TcicWriteBaseReg(pskt, R_MODE, j);
|
||
}
|
||
|
||
//
|
||
// All tests passed
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
USHORT
|
||
TcicChipID(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Read the silicon ID from a TCIC
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
The TCIC chip id.
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT id, oldtest;
|
||
|
||
PAGED_CODE();
|
||
oldtest = TcicReadAuxReg (&pInst->skt, MODE_AR_TEST);
|
||
TcicWriteAuxReg (&pInst->skt, MODE_AR_TEST, (USHORT)TEST_DIAG);
|
||
id = TcicReadAuxReg (&pInst->skt, MODE_AR_ILOCK);
|
||
TcicWriteAuxReg (&pInst->skt, MODE_AR_TEST, oldtest);
|
||
id &= ILOCKTEST_ID_MASK;
|
||
id >>= ILOCKTEST_ID_SHFT;
|
||
|
||
//
|
||
// clearn up IRQs inside TCIC
|
||
//
|
||
|
||
while (TcicReadBaseReg (&pInst->skt, R_ICSR)) {
|
||
TcicWriteBaseReg (&pInst->skt, R_ICSR, ICSR_JAM);
|
||
}
|
||
|
||
return (pInst->chipType = id);
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicCheckSkt(
|
||
IN PSOCKET pInst,
|
||
IN int iSocket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If R_SSTAT shows a card inserted, we're done already.
|
||
otherwise, we set up /CRDYBSY and /CWAIT such that if
|
||
there is a socket present, they will float high
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
iSocket - zero-based socket number
|
||
|
||
Return Value:
|
||
|
||
TRUE if given socket exists
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT old_addr2;
|
||
USHORT mode, pwr, sctrl;
|
||
BOOLEAN retval = FALSE;
|
||
BOOLEAN card_in = FALSE;
|
||
int j, rdy, wait;
|
||
USHORT save_pic;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Socket number OK?
|
||
//
|
||
|
||
if (iSocket > 1) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// save current socket, look at requested
|
||
//
|
||
|
||
old_addr2 = TcicReadBaseReg(pInst, R_ADDR2);
|
||
TcicWriteBaseReg(pInst, R_ADDR2,
|
||
(USHORT)((old_addr2 & ~TCIC_SS_MASK) |
|
||
(iSocket << ADR2_SS_SHFT)));
|
||
|
||
//
|
||
// is there a card?
|
||
//
|
||
|
||
if (TcicReadBaseReg(pInst, R_SSTAT) & SSTAT_CD) {
|
||
|
||
//
|
||
// should set back address register before return.
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_ADDR2, old_addr2);
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// save mode, sctrl, and power for selected socket
|
||
//
|
||
|
||
mode = TcicReadBaseReg(pInst, (USHORT)R_MODE);
|
||
pwr = TcicReadBaseReg(pInst, (USHORT)R_PWR);
|
||
sctrl = TcicReadBaseReg(pInst, (USHORT)R_SCTRL);
|
||
|
||
//
|
||
// check if power is already on- in case someone has
|
||
// inadvertently turned on our power
|
||
//
|
||
|
||
if (pwr & 0x27) {
|
||
TcicWriteBaseReg(pInst, R_PWR, (UCHAR)(pwr & ~0x27));
|
||
}
|
||
|
||
|
||
//
|
||
// put chip into diagnostic mode, turn on VPP enables
|
||
//
|
||
|
||
TcicWriteAuxReg(pInst, MODE_AR_TEST,
|
||
(USHORT)(TEST_DIAG | TEST_VCTL));
|
||
|
||
|
||
//
|
||
// should see /CRDYBSY and /CWAIT low
|
||
//
|
||
|
||
if (!(TcicReadBaseReg(pInst, R_SSTAT) & SSTAT_RDY) &&
|
||
(TcicReadAuxReg(pInst, MODE_AR_ILOCK) & ILOCK_CWAITSNS)) {
|
||
|
||
//
|
||
// 5V power on */
|
||
//
|
||
|
||
if (TcicIsPnP ((PDBSOCKET)pInst)) {
|
||
TcicWriteBaseReg(pInst, R_PWR, (USHORT)(pwr | 0x27));
|
||
} else {
|
||
TcicWriteBaseReg(pInst, R_PWR,
|
||
(UCHAR)(pwr | (iSocket==0? 1 : 2)));
|
||
}
|
||
|
||
//
|
||
// should see /CRDYBSY and /CWAIT high within about 1.5 sec
|
||
//
|
||
|
||
for (j = 0; j < 75; j++) {
|
||
rdy = TcicReadBaseReg(pInst, R_SSTAT) & SSTAT_RDY;
|
||
wait = TcicReadAuxReg(pInst, MODE_AR_ILOCK) & ILOCK_CWAITSNS;
|
||
|
||
if (rdy && !wait) {
|
||
retval = TRUE;
|
||
break;
|
||
}
|
||
PcmciaWait(20000);
|
||
}
|
||
|
||
//
|
||
// Now be sure /CRDYBSY and /CWAIT drain
|
||
//
|
||
// turn power off
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_PWR, 0);
|
||
|
||
//
|
||
// force card enable */
|
||
//
|
||
|
||
TcicWriteAuxReg(pInst, MODE_AR_TEST,
|
||
(USHORT)(TEST_DIAG | TEST_VCTL | TEST_ENA) );
|
||
|
||
//
|
||
// turn on a bunch of bits for drain path
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_MODE,
|
||
MODE_PGMWR | MODE_PGMRD |
|
||
MODE_PGMCE | MODE_PGMWORD );
|
||
|
||
//
|
||
// enable the socket
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_SCTRL, 1);
|
||
|
||
//
|
||
// expect CRDYBSY to drain
|
||
//
|
||
|
||
for (j = 0; j < 75; j++) {
|
||
rdy = TcicReadBaseReg(pInst, R_SSTAT) & SSTAT_RDY;
|
||
if (!rdy) {
|
||
break;
|
||
}
|
||
PcmciaWait(20000);
|
||
}
|
||
|
||
//
|
||
// Wait for noise to settle
|
||
//
|
||
|
||
for (j = 0; j < 50; j++) {
|
||
PcmciaWait(20000);
|
||
}
|
||
}
|
||
|
||
//
|
||
// out of diag mode
|
||
//
|
||
|
||
TcicWriteAuxReg(pInst, MODE_AR_TEST, 0);
|
||
|
||
//
|
||
// clearn up IRQs inside TCIC
|
||
//
|
||
|
||
while (TcicReadBaseReg (pInst, R_ICSR)) {
|
||
TcicWriteBaseReg (pInst, R_ICSR, ICSR_JAM);
|
||
}
|
||
|
||
//
|
||
// restore original mode
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_MODE, mode);
|
||
|
||
//
|
||
// restore SCTRL
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_SCTRL, sctrl);
|
||
|
||
//
|
||
// set socket's power correctly
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_PWR, pwr);
|
||
|
||
//
|
||
// restore originally selected socket
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_ADDR2, old_addr2);
|
||
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
|
||
|
||
USHORT
|
||
TcicCheckAliasing(
|
||
IN PDBSOCKET pdbskt,
|
||
IN USHORT offst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
For each of the 16 I/O locations in the TCIC, if any of
|
||
the corresponding locations |offst| bytes higher are different,
|
||
then aliasing is not occurring. Exceptions, if the chip is
|
||
active, may be found in R_DATA and R_SSTAT; accordingly, we
|
||
avoid these registers in this check.
|
||
If they all compare, then the R_MODE register is changed;
|
||
if the corresponding change occurs in the image,
|
||
then we have aliasing.
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
offst - offset to check for image of this TCIC at.
|
||
|
||
Return Value:
|
||
|
||
TCIC_NONE: no TCIC found
|
||
TCIC_NOALIAS: different TCIC found
|
||
TCIC_ALIAS: aliasing found
|
||
|
||
--*/
|
||
|
||
{
|
||
int j;
|
||
USHORT mode, flipmode;
|
||
SOCKET locskt;
|
||
USHORT retval;
|
||
PHYSICAL_ADDRESS cardAddress;
|
||
PHYSICAL_ADDRESS portAddress;
|
||
BOOLEAN mapped;
|
||
ULONG addressSpace;
|
||
|
||
PAGED_CODE();
|
||
//
|
||
// Check for TCIC at image location, returning NONE if none found:
|
||
//
|
||
|
||
addressSpace = 1; // port space
|
||
portAddress.LowPart = pdbskt->physPortAddr + offst;
|
||
portAddress.HighPart = 0;
|
||
|
||
if (!HalTranslateBusAddress(Isa, 0, portAddress, &addressSpace,&cardAddress)) {
|
||
return retval = TCIC_NONE;
|
||
}
|
||
|
||
if (addressSpace) {
|
||
mapped = FALSE;
|
||
locskt.AddressPort = (PUCHAR)(cardAddress.QuadPart);
|
||
|
||
} else {
|
||
mapped = TRUE;
|
||
locskt.AddressPort = MmMapIoSpace(cardAddress, 0x10, FALSE);
|
||
}
|
||
|
||
if (!TcicReservedBitsOK(&locskt)) {
|
||
if (mapped) {
|
||
MmUnmapIoSpace(locskt.AddressPort, 0x10);
|
||
}
|
||
|
||
return (retval = TCIC_NONE);
|
||
}
|
||
|
||
//
|
||
// Check the R_xxx range for differences
|
||
//
|
||
|
||
for (j = R_ADDR; j < 16; ++j) {
|
||
if (j != R_SSTAT) {
|
||
if (READ_PORT_UCHAR(pdbskt->skt.AddressPort + j) != READ_PORT_UCHAR((locskt.AddressPort + j))) {
|
||
if (mapped) {
|
||
MmUnmapIoSpace(locskt.AddressPort, 0x10);
|
||
}
|
||
return (retval = TCIC_NOALIAS);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// OK, flip the mode register and see if it changes in the
|
||
// aliased range
|
||
//
|
||
|
||
mode = TcicReadBaseReg(&pdbskt->skt, R_MODE) ^ 0xe0;
|
||
TcicWriteBaseReg(&pdbskt->skt, R_MODE, mode);
|
||
flipmode = TcicReadBaseReg(&pdbskt->skt, (USHORT)R_MODE + offst);
|
||
TcicWriteBaseReg(&pdbskt->skt, R_MODE, (USHORT)(mode ^ 0xe0));
|
||
|
||
if (flipmode == mode) {
|
||
retval = TCIC_ALIAS;
|
||
} else {
|
||
retval = TCIC_NOALIAS;
|
||
}
|
||
|
||
if (mapped) {
|
||
MmUnmapIoSpace(locskt.AddressPort, 0x10);
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
|
||
|
||
USHORT
|
||
TcicCheckAliasType (
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is useful for distinguishing among Databook
|
||
controller cards. For instance, the TMI-140 will be found
|
||
at its base address and again at the base address + 10h, while
|
||
the TMB-270 has two controllers separated by 400h, with aliases
|
||
at an offset of 800h.
|
||
|
||
Use TcicCheckAliasing to determine:
|
||
1) Do we have a 270 (two non-identical TCICs appear, 400h
|
||
apart)?
|
||
2) Do we have an "new-style" controller, with an image of
|
||
itself 800h away from the base address?
|
||
For more detail, see TcicCheckAliasing above.
|
||
|
||
Arguments:
|
||
|
||
pInst - socket instance info.
|
||
|
||
Return Value:
|
||
|
||
A value encoding the results found:
|
||
TCIC_IS270 : indicates 270 found
|
||
TCIC_ALIAS800 : indicates base+800h alias found
|
||
TCIC_IS140 : indicates base+10h alias found
|
||
TCIC_ALIAS400 : indicates base+400h alias found
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT retval = 0;
|
||
|
||
PAGED_CODE();
|
||
switch (TcicCheckAliasing (pInst, TCIC_OFFSET_400)) {
|
||
case TCIC_NOALIAS :
|
||
/* (indicating TCIC found, but not aliased) */
|
||
retval |= TCIC_IS270;
|
||
break;
|
||
|
||
case TCIC_ALIAS :
|
||
/* (indicating this TCIC appears again there) */
|
||
retval |= TCIC_ALIAS400;
|
||
break;
|
||
}
|
||
|
||
if (TcicCheckAliasing (pInst, TCIC_OFFSET_800) == TCIC_ALIAS) {
|
||
retval |= TCIC_ALIAS800;
|
||
}
|
||
|
||
if (TcicCheckAliasing (pInst, TCIC_ALIAS_OFFSET) == TCIC_ALIAS) {
|
||
retval |= TCIC_IS140;
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicCheckXBufNeeded(
|
||
IN PSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Two overlapping memory windows are set up, a 16 bit and
|
||
an 8 bit.
|
||
We make two accesses to the memory area: 1st one accesses
|
||
the 16-bit window, 2nd accesses the 8-bit window. They
|
||
MUST be done back-to-back so that MCS16# doesn't have time
|
||
to settle between the two accesses.
|
||
We then check the value from accessing win2. (We don't
|
||
care about the value from Win1, we just use it to make
|
||
sure that MSC16# was asserted.) It should either match
|
||
the value in PDATA or match the low byte in PDATA (082
|
||
mem window bug.) If it matches for all iterations of the
|
||
test, then we assume that external buffers are not
|
||
present.
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
TRUE if external buffering needs to be turned on.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUCHAR winPhysAddr;
|
||
PUCHAR WinMappedAddr;
|
||
BOOLEAN ena_buffers = FALSE;
|
||
PUSHORT pfoo1, pfoo2;
|
||
USHORT foo1, foo2;
|
||
int j;
|
||
ULONG mapped;
|
||
|
||
PAGED_CODE();
|
||
//
|
||
// Alloc addr space for an 8K mem window
|
||
//
|
||
|
||
WinMappedAddr = TcicAllocateMemRange(pInst->DeviceExtension,
|
||
&mapped,
|
||
(PULONG)&winPhysAddr);
|
||
|
||
//
|
||
// If the alloc failed (WinLinear == NULL), there
|
||
// really is no point in doing the test
|
||
//
|
||
|
||
if (WinMappedAddr != NULL) {
|
||
|
||
//
|
||
// Set R_ADDR to 0 to make sure that socket 0 is selected
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_ADDR, 0);
|
||
TcicWriteBaseReg(pInst, R_ADDR2, 0);
|
||
|
||
//
|
||
// Turn on HA24-12 decoding
|
||
//
|
||
|
||
TcicWriteAuxReg(pInst, MODE_AR_SYSCFG, SYSCFG_MCSFULL);
|
||
|
||
//
|
||
// Setup a test value to drive into the mem windows
|
||
//
|
||
|
||
TcicWriteAuxReg(pInst, MODE_AR_PDATA, 0x5678);
|
||
|
||
//
|
||
// Set the window to USHORT regardless of CD states
|
||
//
|
||
|
||
TcicWriteAuxReg(pInst, MODE_AR_TEST, TEST_ENA | TEST_DRIVECDB);
|
||
|
||
//
|
||
// Make sure that PDATA is being driven to the windows
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_MODE, MODE_PGMDBW | MODE_PGMWORD);
|
||
|
||
//
|
||
// Enable the socket, set INCMODE for convenience
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_SCTRL, SCTRL_ENA | SCTRL_INCMODE_AUTO);
|
||
|
||
//
|
||
// cook the TCIC's idea of the base addr
|
||
//
|
||
|
||
((ULONG_PTR)winPhysAddr) >>= MBASE_HA_SHFT;
|
||
|
||
//
|
||
// setup the two windows
|
||
//
|
||
|
||
TcicSetMemWindow(pInst, 0, (ULONG_PTR)winPhysAddr, 1, (USHORT)MCTL_ENA);
|
||
TcicSetMemWindow(pInst, 1, (ULONG_PTR)(winPhysAddr + 1), 1,
|
||
(USHORT)(MCTL_ENA | MCTL_B8));
|
||
|
||
//
|
||
// Now setup two pointers, one into each window.
|
||
// We'll set pfoo2 to point to the 1st USHORT of Win2 and
|
||
// pfoo1 to point to the last USHORT of Win1.
|
||
//
|
||
|
||
pfoo1 = pfoo2 = (PUSHORT)(WinMappedAddr + 0x1000);
|
||
pfoo1--;
|
||
|
||
//
|
||
// Now the test
|
||
//
|
||
|
||
for (j = 0; j < 100; j++) {
|
||
foo1 = READ_REGISTER_USHORT(pfoo1);
|
||
foo2 = READ_REGISTER_USHORT(pfoo2);
|
||
|
||
if (foo2 != 0x5678 && foo2 != 0x7878) {
|
||
ena_buffers = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// last, restore the TCIC to a sane condition
|
||
//
|
||
|
||
TcicSetMemWindow(pInst, 0, 0, 0, 0);
|
||
TcicSetMemWindow(pInst, 1, 0, 0, 0);
|
||
TcicWriteAuxReg(pInst, MODE_AR_SYSCFG, 0);
|
||
TcicWriteAuxReg(pInst, MODE_AR_PDATA, 0);
|
||
TcicWriteAuxReg(pInst, MODE_AR_TEST, 0);
|
||
TcicWriteBaseReg(pInst, R_MODE, 0);
|
||
TcicWriteBaseReg(pInst, R_SCTRL, 0);
|
||
}
|
||
|
||
if (WinMappedAddr != NULL && mapped) {
|
||
MmUnmapIoSpace(WinMappedAddr, TCIC_WINDOW_SIZE);
|
||
}
|
||
|
||
return ena_buffers;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicSetMemWindow(
|
||
IN PSOCKET pInst,
|
||
IN USHORT wnum,
|
||
IN ULONG_PTR base,
|
||
IN USHORT npages,
|
||
IN USHORT mctl
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Helper function for TcicCheckXBufNeeded()
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
wnum - window number (0 - n memwindows)
|
||
base - base Host addr to map to
|
||
npages- window size in 4k pages
|
||
mctl - window ctrl reg value
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT map;
|
||
USHORT winvals[3];
|
||
|
||
PAGED_CODE();
|
||
winvals[1] = (USHORT)(((short)base * -1) & 0x3fff);
|
||
winvals[0] = npages == 1 ? (USHORT)base | MBASE_4K :(USHORT)base;
|
||
winvals[2] = mctl;
|
||
|
||
TcicWriteIndirectRegs(pInst, (USHORT)IR_MBASE_W(wnum), 3, winvals);
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
TcicGetPossibleIRQs(
|
||
IN PDBSOCKET pInst,
|
||
IN UCHAR *ptbl
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The given array is filled in with the irqcaps data determined
|
||
from the chip properties.
|
||
If this is a Plug n Play chip, the IR_ADPTCFG register is
|
||
used to provide additional data
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
ptbl - pointer to list buffer to fill in.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
int j;
|
||
CHIPPROPS *pcp;
|
||
UCHAR *pbtbl;
|
||
|
||
PAGED_CODE();
|
||
if ((pcp = TcicGetChipProperties(pInst)) == NULL) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// If we're using the 082 table, and we've got a divided clock,
|
||
// assume that IRQ6 and IRQ9 are crossed. Likewise, if we've got
|
||
// an 072 table and divided clock, assume that 9 and 14 are
|
||
// crossed.
|
||
//
|
||
|
||
pbtbl = pcp->irqcaps;
|
||
|
||
if (pInst->clkdiv != 0) {
|
||
if (pbtbl == irqcaps_082) {
|
||
pbtbl = irqcaps_082sw;
|
||
} else {
|
||
if (pbtbl == irqcaps_072) {
|
||
pbtbl = irqcaps_072sw;
|
||
}
|
||
}
|
||
}
|
||
|
||
for (j = 0; j < 16 ; j++) {
|
||
ptbl[j] = pbtbl[j];
|
||
}
|
||
|
||
|
||
/*
|
||
* If this chip is a PNP chip, then we need to consult the
|
||
* IR_ADPTCFG reg to see if additional IRQs are available
|
||
*/
|
||
if (TcicIsPnP(pInst)) {
|
||
USHORT adptcfg;
|
||
long old_addr;
|
||
|
||
old_addr = TcicReadAddrReg(&pInst->skt);
|
||
TcicWriteAddrReg(&pInst->skt, ADDR_INDREG | IR_ADPTCFG0);
|
||
|
||
adptcfg = TcicReadBaseReg(&pInst->skt, R_DATA);
|
||
TcicWriteAddrReg(&pInst->skt, old_addr);
|
||
|
||
if (adptcfg & IRADPCF0_IRQ6) {
|
||
ptbl[6] = 6;
|
||
}
|
||
if (adptcfg & IRADPCF0_IRQ9) {
|
||
ptbl[9] = 9;
|
||
}
|
||
if (adptcfg & IRADPCF0_IRQ12) {
|
||
ptbl[12] = 12;
|
||
}
|
||
if (adptcfg & IRADPCF0_IRQ15) {
|
||
ptbl[15] = 15;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
CHIPPROPS *
|
||
TcicGetChipProperties(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Search the ChipProperties table for the matching entry
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
ptr to chip properties table entry.
|
||
|
||
--*/
|
||
|
||
{
|
||
int j;
|
||
|
||
for (j = 0; ChipProperties[j].chip_id != 0 ;j++) {
|
||
if (ChipProperties[j].chip_id == pInst->chipType) {
|
||
return &ChipProperties[j];
|
||
}
|
||
}
|
||
return (CHIPPROPS *)NULL;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicChipIDKnown(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine if the chip id makes sense
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
TRUE if chip ID is sane.
|
||
|
||
--*/
|
||
|
||
{
|
||
return (TcicGetChipProperties(pInst) != NULL);
|
||
}
|
||
|
||
|
||
|
||
|
||
USHORT
|
||
TcicGetnIOWins(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the I/O window count based on chip properties, or zero
|
||
if the chip is unidentified
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
number of io windows present.
|
||
|
||
--*/
|
||
|
||
{
|
||
CHIPPROPS *pcp = TcicGetChipProperties(pInst);
|
||
|
||
PAGED_CODE();
|
||
return (pcp ?pcp->niowins :0);
|
||
}
|
||
|
||
|
||
|
||
USHORT
|
||
TcicGetnMemWins(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the memory window count based on chip properties, or zero
|
||
if the chip is unidentified
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
number of memory windows present.
|
||
|
||
--*/
|
||
|
||
{
|
||
CHIPPROPS *pcp = TcicGetChipProperties(pInst);
|
||
|
||
PAGED_CODE();
|
||
return (pcp ?pcp->nmemwins :0);
|
||
}
|
||
|
||
|
||
|
||
USHORT
|
||
TcicGetFlags(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the properties flag bits for this model of TCIC
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
flag bits from chip properties table.
|
||
|
||
--*/
|
||
|
||
{
|
||
CHIPPROPS *pcp = TcicGetChipProperties (pInst);
|
||
PAGED_CODE();
|
||
return (pcp ? pcp->fprops : fINVALID);
|
||
}
|
||
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicIsPnP(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine if this chip is a Plug-n-Play chip
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
True if chip is PnP (084/184)
|
||
|
||
--*/
|
||
|
||
{
|
||
CHIPPROPS *pcp = TcicGetChipProperties(pInst);
|
||
|
||
return (pcp ?pcp->fprops & fIS_PNP :FALSE);
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
TcicHasSktIRQPin(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine if this chip has a SKT IRQ pin.
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
TRUE if chip has the SktIRQ pin.
|
||
|
||
--*/
|
||
|
||
{
|
||
CHIPPROPS *pcp = TcicGetChipProperties(pInst);
|
||
|
||
PAGED_CODE();
|
||
return (pcp ?pcp->fprops & fSKTIRQPIN :FALSE);
|
||
}
|
||
|
||
|
||
|
||
|
||
USHORT
|
||
TcicGet5vVccVal(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get the correct R_PWR bits to establish 5V.
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
5v Vcc R_PWR bits.
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT j;
|
||
USHORT pwr;
|
||
CHIPPROPS *pcp = TcicGetChipProperties(pInst);
|
||
|
||
PAGED_CODE();
|
||
//
|
||
// Get Table size
|
||
//
|
||
if (pcp == NULL) {
|
||
return 0;
|
||
}
|
||
|
||
j = pcp->privpwrtbl[0];
|
||
|
||
pwr = pcp->privpwrtbl[j + 1];
|
||
|
||
//
|
||
// If not from the 084 family, adjust power value for socket number.
|
||
//
|
||
|
||
if (!TcicIsPnP(pInst)) {
|
||
pwr <<= pInst->skt.RegisterOffset;
|
||
}
|
||
return pwr;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicGetIRQMap(
|
||
IN PDBSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Constructs an IRQ cross-mapping table for the controller in question.
|
||
This code just does a copy from a static table. It should be replaced
|
||
with the Win95 heuristic code.
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
int i, j;
|
||
UCHAR loc_tbl[16] = {0};
|
||
|
||
PAGED_CODE();
|
||
TcicGetPossibleIRQs(pInst, loc_tbl);
|
||
|
||
for (j = 0; j < 16; j++) {
|
||
pInst->IRQMapTbl[j] = loc_tbl[j];
|
||
}
|
||
|
||
//
|
||
// Don't let IRQ 14 through.. This is also done for the PCIC
|
||
//
|
||
|
||
pInst->IRQMapTbl[14] = 0;
|
||
}
|
||
|
||
|
||
|
||
USHORT
|
||
TcicClockRate(
|
||
PSOCKET pInst
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if CCLK is running at 1:1 (14.318 Mhz) or divided
|
||
by 2.
|
||
|
||
Arguments:
|
||
|
||
pInst - pointer to an Instance data to work from.
|
||
|
||
Return Value:
|
||
|
||
CCLK divisor as a shift count (0 or 1)
|
||
|
||
--*/
|
||
|
||
{
|
||
int i;
|
||
LARGE_INTEGER accum = RtlConvertLongToLargeInteger(0L);
|
||
LARGE_INTEGER start, stop, pc, tmp, tmp2;
|
||
USHORT mode;
|
||
USHORT wctl;
|
||
|
||
PAGED_CODE();
|
||
//
|
||
// The Ratio break point is the midpoint between 16K ticks at 14.31813 Mhz
|
||
// (14,318130 / 16K = 873 | 1:1 CCLK) and 16K ticks at 7.159065 Mhz
|
||
// (7,159,065 / 16K = 436 | 1:2 CCLK). We calculate the midpoint between
|
||
// these two values: ((873 - 436) / 2) + 436 = 654 to give a comparison
|
||
// value. If x < 654 we assume 1/2 CCLK, otherwise we assume 1/1 CCLK.
|
||
//
|
||
|
||
#define CLKRATIO_BRKPOINT 654L
|
||
|
||
mode = TcicReadBaseReg(pInst, R_MODE);
|
||
|
||
//
|
||
// set AR_PCTL to 0x4000
|
||
//
|
||
|
||
TcicWriteAuxReg(pInst, MODE_AR_PCTL, 0x4000);
|
||
TcicWriteBaseReg(pInst, R_MODE, MODE_AR_WCTL);
|
||
wctl = TcicReadBaseReg(pInst, R_AUX);
|
||
|
||
//
|
||
// Get the performance counter time base
|
||
//
|
||
|
||
KeQueryPerformanceCounter(&pc);
|
||
|
||
for (i = 0; i < 10; i++) {
|
||
|
||
//
|
||
// start the TCIC timer */
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_AUX, (USHORT)(wctl & 0xff));
|
||
start = KeQueryPerformanceCounter(NULL);
|
||
|
||
//
|
||
// wait for SSTAT_PROGTIME to go high */
|
||
//
|
||
|
||
while (!(TcicReadBaseReg(pInst, R_SSTAT) & SSTAT_PROGTIME))
|
||
;
|
||
|
||
//
|
||
// nab the timer count
|
||
//
|
||
|
||
stop = KeQueryPerformanceCounter(NULL);
|
||
tmp = RtlLargeIntegerSubtract(stop, start);
|
||
accum = RtlLargeIntegerAdd(accum, tmp);
|
||
}
|
||
|
||
//
|
||
// Zero out the timer for power conservation
|
||
//
|
||
|
||
TcicWriteAuxReg(pInst, MODE_AR_PCTL, 0);
|
||
|
||
//
|
||
// replace Mode
|
||
//
|
||
|
||
TcicWriteBaseReg(pInst, R_MODE, mode);
|
||
|
||
//
|
||
// Get average elapsed time for 1 iter.
|
||
//
|
||
|
||
accum = RtlLargeIntegerDivide(accum, RtlConvertLongToLargeInteger(10L), &tmp2);
|
||
|
||
//
|
||
// Divide PC Freq by accum to base accum on some portion of 1 second
|
||
//
|
||
|
||
tmp = RtlLargeIntegerDivide(pc, accum, &tmp2);
|
||
|
||
return (RtlLargeIntegerLessThan(tmp, RtlConvertLongToLargeInteger(CLKRATIO_BRKPOINT))
|
||
?(USHORT)2 : (USHORT)1);
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicSetIoWin(
|
||
IN PSOCKET socketPtr,
|
||
IN USHORT winIdx,
|
||
IN ULONG BasePort,
|
||
IN ULONG NumPorts,
|
||
IN UCHAR Attributes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Setup a TCIC I/O window.
|
||
|
||
Arguments:
|
||
|
||
socketPtr - ptr to socket instance data
|
||
winIdx - index of window to setup
|
||
BasePort - start base port address
|
||
NumPorts - size of range - 1
|
||
Attributes- window attributes
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBSOCKET pdb = (PDBSOCKET)socketPtr;
|
||
USHORT tmp;
|
||
USHORT words[2];
|
||
|
||
//
|
||
// Simulate 365 by arbitrary attachment of IOW1:2 to SKT1 and IOW3:4 to SKT2
|
||
//
|
||
|
||
winIdx += (socketPtr->RegisterOffset * 2);
|
||
|
||
//
|
||
// NumPorts from CIS metaformat is really (NumPorts -1), normalize it now.
|
||
//
|
||
|
||
++NumPorts;
|
||
|
||
words[0] = (USHORT)(BasePort + (NumPorts >> 1));
|
||
|
||
TcicReadIndirectRegs(socketPtr, IR_SCFG_S(socketPtr->RegisterOffset), 1, &tmp);
|
||
tmp |= (USHORT)(IRSCFG_SPKR | IRSCFG_FINPACK);
|
||
TcicWriteIndirectRegs(socketPtr, IR_SCFG_S(socketPtr->RegisterOffset), 1, &tmp);
|
||
|
||
TcicReadIndirectRegs(socketPtr, IR_SCF2_S(socketPtr->RegisterOffset), 1, &tmp);
|
||
tmp &= ~(IRSCF2_IDBR | IRSCF2_MDBR);
|
||
|
||
if (Attributes & IO_DATA_PATH_WIDTH) {
|
||
words[1] = ICTL_ENA;
|
||
tmp |= IRSCF2_IDBR;
|
||
} else {
|
||
words[1] = ICTL_B8 | ICTL_QUIET | ICTL_ENA;
|
||
}
|
||
TcicWriteIndirectRegs(socketPtr, IR_SCF2_S(socketPtr->RegisterOffset), 1, &tmp);
|
||
|
||
if (NumPorts < 1024) {
|
||
words[1] |= ICTL_1K;
|
||
|
||
if (NumPorts == 1) {
|
||
words[1] |= ICTL_TINY;
|
||
}
|
||
}
|
||
words[1] |= socketPtr->RegisterOffset << ICTL_SS_SHFT;
|
||
words[1] |= 3 + pdb->clkdiv;
|
||
|
||
TcicWriteIndirectRegs(socketPtr, IR_IOBASE_W(winIdx), 2, words);
|
||
}
|
||
|
||
|
||
|
||
|
||
USHORT
|
||
TcicMapSpeedCode(
|
||
IN PDBSOCKET pdb,
|
||
IN UCHAR AccessSpeed
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine the correct wait state bits for this controller
|
||
|
||
Arguments:
|
||
|
||
pdb - socket instance data
|
||
AccessSpeed - callers desired speed (unused)
|
||
|
||
Return Value:
|
||
|
||
TCIC wait state bits.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
UNREFERENCED_PARAMETER(AccessSpeed);
|
||
|
||
if (pdb->clkdiv) {
|
||
return (3);
|
||
} else {
|
||
return (7);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicSetMemWin(
|
||
IN PSOCKET socketPtr,
|
||
IN USHORT winIdx,
|
||
IN ULONG cardbase,
|
||
IN ULONG hostbase,
|
||
IN ULONG size,
|
||
IN UCHAR AttrMem,
|
||
IN UCHAR AccessSpeed,
|
||
IN USHORT Attributes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Setup the specified TCIC memory window
|
||
|
||
Arguments:
|
||
|
||
socketPtr - socket instance data
|
||
winIdx - index of window to setup
|
||
cardbase - PCCard base address
|
||
hostbase - host base address
|
||
size - window size
|
||
AttrMem - attribute or common space
|
||
AccessSpeed - wait states
|
||
Attributes - window attributes
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PDBSOCKET pdb = (PDBSOCKET)socketPtr;
|
||
USHORT tmp;
|
||
USHORT words[4];
|
||
|
||
//
|
||
// Simulate 365 by arbitrary attachment of MEM1:(x/2-1) to SKT1
|
||
// and MEMx/2:x to SKT2
|
||
//
|
||
|
||
winIdx += (socketPtr->RegisterOffset * (pdb->nmemwins / 2));
|
||
|
||
//
|
||
// convert base, size, & map to 4K pages
|
||
//
|
||
|
||
cardbase >>= 12;
|
||
size >>= 12;
|
||
hostbase >>= 12;
|
||
|
||
//
|
||
// combine hostbase & size
|
||
//
|
||
|
||
words[0] = (USHORT)hostbase | (USHORT)(size / 2);
|
||
|
||
//
|
||
// Check if 4K bit is needed
|
||
//
|
||
|
||
if (size == 1) {
|
||
words[0] |= MBASE_4K;
|
||
}
|
||
|
||
//
|
||
// setup mapping of cardbase to host addr space
|
||
//
|
||
|
||
words[1] = (USHORT)(cardbase - (hostbase & 0xfff)) & 0x3fff;
|
||
if (AttrMem) {
|
||
words[1] |= MMAP_REG;
|
||
}
|
||
|
||
//
|
||
// now cook the control bits
|
||
//
|
||
|
||
words[2] = MCTL_ENA | MCTL_QUIET;
|
||
if (!(Attributes & MEM_DATA_PATH_WIDTH_16)) {
|
||
words[2] |= MCTL_B8;
|
||
}
|
||
|
||
//
|
||
// Now add in the socket selector
|
||
//
|
||
|
||
words[2] |= (socketPtr->RegisterOffset << MCTL_SS_SHFT);
|
||
|
||
//
|
||
// Last, add in the speed bits
|
||
//
|
||
|
||
words[2] |= TcicMapSpeedCode(pdb, AccessSpeed);
|
||
|
||
//
|
||
// HW BugFix1: First Rev of 082 needs to have SYSCFG_MCSFULL turned on
|
||
// if we have any open windows. We're opening one so we better assert.
|
||
//
|
||
|
||
tmp = TcicReadAuxReg(socketPtr, MODE_AR_SYSCFG);
|
||
tmp |= SYSCFG_MCSFULL;
|
||
TcicWriteAuxReg(socketPtr, MODE_AR_SYSCFG, tmp);
|
||
|
||
//
|
||
// HW BugFix2: '2' Step of 082 needs the wait state count written into
|
||
// window[~index] instead of index.
|
||
//
|
||
|
||
if (pdb->chipType != SILID_DB86082_1) {
|
||
|
||
//
|
||
// No bug case
|
||
//
|
||
|
||
TcicWriteIndirectRegs(socketPtr, IR_MBASE_W(winIdx), 3, words);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Bug case
|
||
//
|
||
|
||
words[3] = words[2] & MCTL_WSCNT_MASK;
|
||
words[2] &= ~MCTL_WSCNT_MASK;
|
||
TcicWriteIndirectRegs(socketPtr, IR_MBASE_W(winIdx), 3, words);
|
||
TcicWriteIndirectRegs(socketPtr, IR_MBASE_W((~winIdx) & 7), 1, &words[3]);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicAutoBusyOff(
|
||
IN PDBSOCKET pdbs
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Turn off the busy LED, re-arm so that it comes on automatically with
|
||
any card access.
|
||
|
||
Arguments:
|
||
|
||
pdbs - socket instance data
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT syscfg;
|
||
USHORT oldmode;
|
||
|
||
//
|
||
// Save R_MODE for later restore
|
||
//
|
||
|
||
oldmode = TcicReadBaseReg(&pdbs->skt, R_MODE);
|
||
|
||
//
|
||
// R/M/W SYSCFG to add in the autobusy bit.
|
||
// This will turn LED off for now but allow it to come on automatically
|
||
// with the next access to this socket.
|
||
//
|
||
|
||
syscfg = TcicReadAuxReg(&pdbs->skt, MODE_AR_SYSCFG);
|
||
syscfg |= SYSCFG_AUTOBUSY;
|
||
TcicWriteAuxReg(&pdbs->skt, MODE_AR_SYSCFG, syscfg);
|
||
|
||
//
|
||
// Restore Mode
|
||
//
|
||
|
||
TcicWriteBaseReg(&pdbs->skt, R_MODE, oldmode);
|
||
}
|
||
|
||
|
||
|
||
UCHAR
|
||
TcicAutoBusyCheck(
|
||
IN PDBSOCKET pdbs
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Check SYSCFG access bit to see if PCCard has been accessed since last
|
||
call. If so, force LED to stay on and clear access bit.
|
||
|
||
Arguments:
|
||
|
||
pdbs - socket instance data
|
||
|
||
Return Value:
|
||
|
||
access bit as a right-justified UCHAR
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT syscfg;
|
||
USHORT oldmode;
|
||
UCHAR activity = 0;
|
||
|
||
//
|
||
// Save R_MODE for later restore
|
||
//
|
||
|
||
oldmode = TcicReadBaseReg(&pdbs->skt, R_MODE);
|
||
|
||
//
|
||
// Read AR_SYSCFG to check for recent activity
|
||
//
|
||
|
||
syscfg = TcicReadAuxReg(&pdbs->skt, MODE_AR_SYSCFG);
|
||
if (syscfg & SYSCFG_ACC) {
|
||
|
||
//
|
||
// the socket has been accessed since last check
|
||
// clear the access bit and disable AUTOBUSY to force LED to
|
||
// follow socket SCTRL_ENA.
|
||
//
|
||
|
||
syscfg &= ~(SYSCFG_ACC | SYSCFG_AUTOBUSY);
|
||
TcicWriteAuxReg(&pdbs->skt, MODE_AR_SYSCFG, syscfg);
|
||
++activity;
|
||
}
|
||
|
||
//
|
||
// Restore Mode
|
||
//
|
||
|
||
TcicWriteBaseReg(&pdbs->skt, R_MODE, oldmode);
|
||
|
||
return activity;
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
TcicCheckSktLED(
|
||
IN PDBSOCKET pdbs
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Drive the low-level functions to check for PCcard access and control
|
||
the busy LED on this socket/controller.
|
||
|
||
Arguments:
|
||
|
||
pdbs - socket instance data
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
UCHAR lastbusy = pdbs->busyLed;
|
||
|
||
pdbs->busyLed = TcicAutoBusyCheck(pdbs);
|
||
|
||
if (lastbusy & !(pdbs->busyLed)) {
|
||
TcicAutoBusyOff(pdbs);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
TcicBusyLedRoutine(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Main timer routine to drive Busy LED monitor
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - instance data for driver
|
||
Context - unused parameter
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PFDO_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
||
PDBSOCKET pdbs;
|
||
|
||
UNREFERENCED_PARAMETER(Context);
|
||
|
||
pdbs = (PDBSOCKET)(deviceExtension->SocketList);
|
||
|
||
while (pdbs) {
|
||
|
||
//
|
||
// If this device is from the 084 family, LED control is per/socket
|
||
//
|
||
|
||
if (TcicIsPnP(pdbs)) {
|
||
ULONG oldaddr = TcicReadAddrReg(&pdbs->skt);
|
||
|
||
// Do the first socket
|
||
//
|
||
|
||
TcicSocketSelect(&pdbs->skt, pdbs->skt.RegisterOffset);
|
||
TcicCheckSktLED(pdbs);
|
||
TcicWriteAddrReg(&pdbs->skt, oldaddr);
|
||
pdbs = (PDBSOCKET)(pdbs->skt.NextSocket);
|
||
|
||
// If a second socket is present, do it too.
|
||
//
|
||
|
||
if (pdbs && pdbs->skt.RegisterOffset == 1) {
|
||
oldaddr = TcicReadAddrReg(&pdbs->skt);
|
||
TcicSocketSelect(&pdbs->skt, pdbs->skt.RegisterOffset);
|
||
TcicCheckSktLED(pdbs);
|
||
TcicWriteAddrReg(&pdbs->skt, oldaddr);
|
||
pdbs = (PDBSOCKET)(pdbs->skt.NextSocket);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Otherwise, LED control is per adapter so do the check and skip
|
||
// over the second socket if present.
|
||
//
|
||
|
||
TcicCheckSktLED(pdbs);
|
||
pdbs = (PDBSOCKET)(pdbs->skt.NextSocket);
|
||
if (pdbs && pdbs->skt.RegisterOffset == 1) {
|
||
pdbs = (PDBSOCKET)(pdbs->skt.NextSocket);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicDecodeMemWin(
|
||
USHORT mbase,
|
||
USHORT mmap,
|
||
USHORT mctl,
|
||
ULONG *Host,
|
||
ULONG *Card,
|
||
ULONG *Size,
|
||
UCHAR *Attr
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Convert TCIC mem window register values to something understandable
|
||
|
||
Arguments:
|
||
|
||
mbase - TCIC MBASE register value
|
||
mmap - TCIC MMAP register value
|
||
mctl - TCIC MCTL register value
|
||
Host - where to put Host address
|
||
Card - where to put PCCard address
|
||
Size - where to put window size
|
||
Attr - where to put attribute space flag
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
USHORT shft;
|
||
USHORT tmp;
|
||
|
||
//
|
||
// take care of mapping to common or attr space first.
|
||
// Strip ATTR bit if set
|
||
//
|
||
|
||
*Attr = 0;
|
||
if (mmap & MMAP_REG) {
|
||
*Attr = 1;
|
||
mmap &= ~MMAP_REG;
|
||
}
|
||
|
||
//
|
||
// Now concentrate on getting the host addr and window size
|
||
//
|
||
|
||
if (mbase & MBASE_4K) {
|
||
*Size = 1;
|
||
*Host = (ULONG)(mbase & ~MBASE_4K);
|
||
} else {
|
||
for (*Size = 2, shft = 0, tmp = mbase; !(tmp & 1) ; shft++ ) {
|
||
tmp >>= 1;
|
||
*Size <<= 1;
|
||
}
|
||
*Host = (ULONG)(mbase - (1 << shft));
|
||
}
|
||
|
||
//
|
||
// Now for the fun part. We're left with mmap being a 14-bit signed
|
||
// number. We need to normalize it so we can work with it.
|
||
//
|
||
// Check for negative (bit 13 set)
|
||
//
|
||
|
||
if (mmap & (1 << 13)) {
|
||
mmap |= 0xc000;
|
||
*Card = (ULONG)((short)mmap + (short)*Host);
|
||
} else {
|
||
*Card = (ULONG)(mmap) + *Host;
|
||
}
|
||
(*Size)--;
|
||
*Host <<= MBASE_HA_SHFT;
|
||
*Size <<= MBASE_HA_SHFT;
|
||
*Card <<= MMAP_CA_SHFT;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicDecodeIoWin(
|
||
USHORT iobase,
|
||
USHORT ioctl,
|
||
USHORT *NumPorts,
|
||
USHORT *BasePort
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Convert TCIC I/O window register values to something understandable
|
||
|
||
Arguments:
|
||
|
||
iobase - TCIC IOBASE register contents
|
||
ioctl - TCIC IOCTL register contents
|
||
NumPorts - where to put window size (size - 1)
|
||
BasePort - where to put base address
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
if (ioctl & ICTL_TINY) {
|
||
*BasePort = iobase;
|
||
*NumPorts = 1;
|
||
} else {
|
||
USHORT shft;
|
||
USHORT tmp;
|
||
|
||
for (*NumPorts = 2, shft = 0, tmp = iobase; !(tmp & 1) ; shft++ ) {
|
||
tmp >>= 1;
|
||
*NumPorts <<= 1;
|
||
}
|
||
|
||
*BasePort = (iobase - (1 << shft));
|
||
}
|
||
*NumPorts -= 1;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
TcicRegistryLookupScanLimits(
|
||
PULONG Start,
|
||
PULONG End
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Open the registry key in the services entry for pcmcia and see if there
|
||
are some values set for TCIC searching. If not, use the defaults.
|
||
|
||
Arguments:
|
||
|
||
Start - the I/O location for start of search.
|
||
End - the I/O location to end the search (i.e. nothing greater than).
|
||
|
||
Return Values:
|
||
|
||
None - parameters are modified.
|
||
|
||
--*/
|
||
|
||
{
|
||
#define ITEMS_TO_QUERY 4
|
||
ULONG defaultStart = TCIC_LOW_ADDR_LIMIT;
|
||
ULONG defaultEnd = TCIC_HIGH_ADDR_LIMIT;
|
||
PRTL_QUERY_REGISTRY_TABLE params;
|
||
NTSTATUS status;
|
||
PWSTR keyName;
|
||
|
||
PAGED_CODE();
|
||
//
|
||
// Set up return codes in case there are errors in setting up processing.
|
||
//
|
||
|
||
*Start = defaultStart;
|
||
*End = defaultEnd;
|
||
|
||
//
|
||
// Allocate memory for operation.
|
||
//
|
||
|
||
params = ExAllocatePool(NonPagedPool,
|
||
sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY);
|
||
|
||
if (!params) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Set up registry path. This should not be hard coded, but is for now.
|
||
//
|
||
|
||
keyName = L"\\registry\\machine\\system\\currentcontrolset\\services\\pcmcia";
|
||
|
||
//
|
||
// Set up query structure.
|
||
//
|
||
|
||
RtlZeroMemory(params, sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY);
|
||
params[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
params[0].Name = L"TCICStartSearch";
|
||
params[0].EntryContext = Start;
|
||
params[0].DefaultType = REG_DWORD;
|
||
params[0].DefaultData = &defaultStart;
|
||
params[0].DefaultLength = sizeof(ULONG);
|
||
|
||
params[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
||
params[1].Name = L"TCICStopSearch";
|
||
params[1].EntryContext = End;
|
||
params[1].DefaultType = REG_DWORD;
|
||
params[1].DefaultData = &defaultEnd;
|
||
params[1].DefaultLength = sizeof(ULONG);
|
||
|
||
//
|
||
// Perform the registry search
|
||
//
|
||
|
||
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
||
keyName,
|
||
params,
|
||
NULL,
|
||
NULL);
|
||
|
||
//
|
||
// Insure that start is less than end - if not, go back to default
|
||
// values
|
||
//
|
||
|
||
if (*Start > *End) {
|
||
*Start = defaultStart;
|
||
*End = defaultEnd;
|
||
}
|
||
|
||
//
|
||
// Free resources.
|
||
//
|
||
|
||
ExFreePool(params);
|
||
}
|