/*++ 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); }