/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: pcicsup.c Abstract: This module supplies functions that control the 82365SL chip. In turn, these functions are abstracted out to the main PCMCIA support module. Author(s): Bob Rinne (BobRi) 3-Aug-1994 Jeff McLeman (mcleman@zso.dec.com) Neil Sandlin (neilsa) June 1 1999 Revisions: 6-Apr-95 Modified for databook support changes - John Keys Databook 1-Nov-96 Complete overhaul for plug'n'play support, flash interfaces, power support etc. - Ravisankar Pudipeddi (ravisp) --*/ #include "pch.h" #ifdef POOL_TAGGING #undef ExAllocatePool #define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'cicP') #endif // // Internal References // NTSTATUS PcicResetCard( IN PSOCKET Socket, OUT PULONG pDelayTime ); BOOLEAN PcicInitializePcmciaSocket( IN PSOCKET Socket ); UCHAR PcicReadController( IN PUCHAR Base, IN USHORT Socket, IN UCHAR PcicRegister ); VOID PcicWriteController( IN PUCHAR Base, IN USHORT Socket, IN UCHAR PcicRegister, IN UCHAR DataByte ); NTSTATUS PcicDetect( IN PFDO_EXTENSION DeviceExtension, IN INTERFACE_TYPE InterfaceType, IN ULONG IoPortBase ); BOOLEAN PcicDetectCardInSocket( IN PSOCKET Socket ); BOOLEAN PcicDetectCardChanged( IN PSOCKET Socket ); BOOLEAN PcicPCCardReady( IN PSOCKET Socket ); BOOLEAN PcicDetectReadyChanged( IN PSOCKET Socket ); BOOLEAN PcicProcessConfigureRequest( IN PSOCKET Socket, IN PCARD_REQUEST ConfigRequest, IN PUCHAR Base ); VOID PcicEnableDisableWakeupEvent( IN PSOCKET Socket, IN PPDO_EXTENSION PdoExtension, IN BOOLEAN Enable ); VOID PcicEnableDisableMemory( IN PSOCKET Socket, IN MEMORY_SPACE MemorySpace, IN ULONG CardBase, IN UCHAR Mem16BitWindow, IN BOOLEAN Enable ); BOOLEAN PcicEnableDisableCardDetectEvent( IN PSOCKET Socket, IN BOOLEAN Enable ); UCHAR PcicReadExtendedCirrusController( IN PUCHAR Base, IN USHORT Socket, IN UCHAR Register ); VOID PcicWriteExtendedCirrusController( IN PUCHAR Base, IN USHORT Socket, IN UCHAR PcicRegister, IN UCHAR DataByte ); ULONG PcicWriteCardMemory( IN PPDO_EXTENSION PdoExtension, IN MEMORY_SPACE MemorySpace, IN ULONG Offset, IN PUCHAR Buffer, IN ULONG Length ); ULONG PcicReadCardMemory( IN PPDO_EXTENSION PdoExtension, IN MEMORY_SPACE MemorySpace, IN ULONG Offset, IN PUCHAR Buffer, IN ULONG Length ); BOOLEAN PcicModifyMemoryWindow( IN PDEVICE_OBJECT Pdo, IN ULONGLONG HostBase, IN ULONGLONG CardBase, IN BOOLEAN Enable, IN ULONG WindowSize OPTIONAL, IN UCHAR AccessSpeed OPTIONAL, IN UCHAR BusWidth OPTIONAL, IN BOOLEAN IsAttributeMemory OPTIONAL ); BOOLEAN PcicSetVpp( IN PDEVICE_OBJECT Pdo, IN UCHAR VppLevel ); BOOLEAN PcicIsWriteProtected( IN PDEVICE_OBJECT Pdo ); ULONG PcicGetIrqMask( IN PFDO_EXTENSION deviceExtension ); NTSTATUS PcicConvertSpeedToWait( IN UCHAR Speed, OUT PUCHAR WaitIndex ); // // Internal Data // ULONG PcicStallCounter = 4000; //4ms UCHAR WaitToSpeedTable[4] = { 0x42, //350ns 0x52, //450ns 0x62, //600ns 0x72 //700ns }; UCHAR DevSpeedTable[8] = { 0xff, // speed 0: invalid 0x32, // speed 1: 250ns 0x2a, // speed 2: 200ns 0x22, // speed 3: 150ns 0x0a, // speed 4: 100ns 0xff, // speed 5: reserved 0xff, // speed 6: reserved 0xff // speed 7: invalid }; PCMCIA_CTRL_BLOCK PcicSupportFns = { PcicInitializePcmciaSocket, PcicResetCard, PcicDetectCardInSocket, PcicDetectCardChanged, NULL, // PcicDetectCardStatus PcicDetectReadyChanged, NULL, // GetPowerRequirements PcicProcessConfigureRequest, PcicEnableDisableCardDetectEvent, PcicEnableDisableWakeupEvent, PcicGetIrqMask, PcicReadCardMemory, PcicWriteCardMemory, PcicModifyMemoryWindow, PcicSetVpp, PcicIsWriteProtected }; #define MEM_16BIT 1 #define MEM_8BIT 0 #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,PcicIsaDetect) #pragma alloc_text(INIT,PcicDetect) #pragma alloc_text(PAGE,PcicBuildSocketList) #endif ULONG PcicGetIrqMask( IN PFDO_EXTENSION DeviceExtension ) /*++ Routine Description: Arguments: Return value: --*/ { // // Return the set of supported IRQs for the controller // and PcCards // if (CLPD6729(DeviceExtension->SocketList)) { return CL_SUPPORTED_INTERRUPTS; } else { return PCIC_SUPPORTED_INTERRUPTS; } } BOOLEAN PcicEnableDisableCardDetectEvent( IN PSOCKET Socket, IN BOOLEAN Enable ) /*++ Routine Description: Enable card detect interrupt. Arguments: Socket - socket information Irq - the interrupt value to set. Enable - if TRUE, CSC interrupt is enabled, if FALSE, it is disabled Return Value: None --*/ { PFDO_EXTENSION deviceExtension = Socket->DeviceExtension; INTERFACE_TYPE interface; UCHAR byte; ULONG Irq = Socket->FdoIrq; switch (Enable) { case TRUE: { if (CLPD6729(Socket)) { // // For Cirrus Logic PCI controller we need to know the interrupt pin // (INTA, INTB etc.) corresponding to the level passed in. Hence the // passed in Irq is discarded. Actually the Irq parameter is redundant // since it can be fetched from the device extension itself. // If we remove the Irq param from this routine, the following is // not so inelegant.. // interface = PCIBus; switch (deviceExtension->Configuration.InterruptPin) { case 0: { // // This is what tells us that ISA interrupts are being used... // interface = Isa; break; } case 1: { Irq = PCIC_CIRRUS_INTA; break; } case 2: { Irq = PCIC_CIRRUS_INTB; break; } case 3: { Irq = PCIC_CIRRUS_INTC; break; } case 4: { Irq = PCIC_CIRRUS_INTD; break; } } // // Set the Cirrus Logic controller for PCI style interrupts // byte = PcicReadExtendedCirrusController(Socket->AddressPort, Socket->RegisterOffset, PCIC_CIRRUS_EXTENSION_CTRL_1); if (interface == PCIBus) { byte |= 0x10; // PCI style interrupt } else { byte &= ~0x10; // Isa style interrupt } PcicWriteExtendedCirrusController(Socket->AddressPort, Socket->RegisterOffset, PCIC_CIRRUS_EXTENSION_CTRL_1, byte); PcmciaWait(100); } byte=PcicReadSocket(Socket, PCIC_CARD_INT_CONFIG); byte = byte & CSCFG_BATT_MASK; // Don't nuke any other enables byte = byte | (UCHAR) ((Irq << 4) & 0x00ff); // Put IRQ in upper nibble byte |= CSCFG_CD_ENABLE; PcicWriteSocket(Socket, PCIC_CARD_INT_CONFIG, byte); break; } case FALSE: { // // Clear pending interrupt (for now) // byte = PcicReadSocket(Socket, PCIC_CARD_CHANGE); DebugPrint((PCMCIA_DEBUG_INFO, "PcicDisableInterrupt:Status Change %x\n", byte)); PcicWriteSocket(Socket, PCIC_CARD_INT_CONFIG, 0x0); break; } } return TRUE; } NTSTATUS PcicSetPower( IN PSOCKET Socket, IN BOOLEAN Enable, OUT PULONG pDelayTime ) /*++ Routine Description: Set power to the specified socket. Arguments: Socket - 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; UCHAR tmp, vcc; // // Turn on the power - then turn on output - this is two operations // per the Intel 82365SL documentation. // if (Enable) { switch(Socket->PowerPhase) { case 1: tmp = PcicReadSocket(Socket, PCIC_PWR_RST); // // 5V for R2 cards.. // vcc = PC_CARDPWR_ENABLE; if (Elc(Socket)) { tmp = PC_VPP_SETTO_VCC | vcc; // vpp1 = vcc } else { // // Apparently we need to set bit 2 also for some obscure reason // tmp = 0x4 | PC_VPP_SETTO_VCC | vcc; // vpp1 = vpp2 = vcc } PcicWriteSocket(Socket, PCIC_PWR_RST, tmp); // // OUTPUT_ENABLE & AUTOPWR_ENABLE.. // Disable RESETDRV also.. // tmp |= PC_OUTPUT_ENABLE | PC_AUTOPWR_ENABLE | PC_RESETDRV_DISABLE; PcicWriteSocket(Socket, PCIC_PWR_RST, tmp); // // When power is enabled always stall to give the PCCARD // a chance to react. // *pDelayTime = PcicStallPower; status = STATUS_MORE_PROCESSING_REQUIRED; break; case 2: // // Check for an as yet unexplained condition on Dell Latitude XPi's // tmp = PcicReadSocket(Socket, PCIC_STATUS); if (!(tmp & 0x40)) { // // power hasn't come on, flip the mystery bit // tmp = PcicReadSocket(Socket, 0x2f); if (tmp == 0x42) { PcicWriteSocket(Socket, 0x2f, 0x40); *pDelayTime = PcicStallPower; } } status = STATUS_SUCCESS; break; default: ASSERT(FALSE); status = STATUS_UNSUCCESSFUL; } } else { PcicWriteSocket(Socket, PCIC_PWR_RST, 0x00); status = STATUS_SUCCESS; } return status; } NTSTATUS PcicConvertSpeedToWait( IN UCHAR Speed, OUT PUCHAR WaitIndex ) { NTSTATUS status = STATUS_INVALID_PARAMETER; UCHAR exponent, exponent2, mantissa, index; if (Speed & SPEED_EXT_MASK) { return status; } exponent = Speed & SPEED_EXPONENT_MASK; mantissa = Speed & SPEED_MANTISSA_MASK; if (mantissa == 0) { mantissa = DevSpeedTable[exponent] & SPEED_MANTISSA_MASK; exponent = DevSpeedTable[exponent] & SPEED_EXPONENT_MASK; } for (index = 0; index < sizeof(WaitToSpeedTable); index++) { exponent2= WaitToSpeedTable[index] & SPEED_EXPONENT_MASK; if ((exponent < exponent2) || ((exponent == exponent2) && (mantissa < (WaitToSpeedTable[index] & SPEED_MANTISSA_MASK)))) { *WaitIndex = index; status = STATUS_SUCCESS; break; } } return status; } BOOLEAN PcicSetVpp( IN PDEVICE_OBJECT Pdo, IN UCHAR Vpp ) /*++ Routine Description Part of the interfaces originally developed to support flash memory cards. Sets VPP1 to the required setting Arguments Pdo - Pointer to device object for the PC-Card Vpp - Desired Vpp setting. This is currently one of PCMCIA_VPP_12V (12 volts) PCMCIA_VPP_0V (disable VPP) PCMCIA_VPP_IS_VCC (route VCC to VPP) Return TRUE - if successful FALSE - if not. This will be returned if the PC-Card is not already powered up --*/ { PSOCKET socketPtr = ((PPDO_EXTENSION) Pdo->DeviceExtension)->Socket; UCHAR tmp; ASSERT ( socketPtr != NULL ); tmp = PcicReadSocket(socketPtr, PCIC_PWR_RST); if ((tmp & 0x10) == 0) { // // Vcc not set. // return FALSE; } // // Turn off Vpp bits // tmp &= ~0x3; switch (Vpp) { case PCMCIA_VPP_IS_VCC: { tmp |= 0x1; break; } case PCMCIA_VPP_12V: { tmp |= 0x2; break; } case PCMCIA_VPP_0V: { tmp |= 0x0; break; } } PcicWriteSocket(socketPtr, PCIC_PWR_RST, tmp); if (Vpp != PCMCIA_VPP_0V) { // // When power is enabled always stall to give the PCCARD // a chance to react. // PcmciaWait(PcicStallPower); } return TRUE; } BOOLEAN PcicModifyMemoryWindow( IN PDEVICE_OBJECT Pdo, IN ULONGLONG HostBase, IN ULONGLONG CardBase OPTIONAL, IN BOOLEAN Enable, IN ULONG WindowSize OPTIONAL, IN UCHAR AccessSpeed OPTIONAL, IN UCHAR BusWidth OPTIONAL, IN BOOLEAN IsAttributeMemory OPTIONAL ) /*++ Routine Description: Part of the interfaces originally developed to support flash memory cards. This routine enables the caller to 'slide' the supplied host memory window across the given (16-bit)pc-card's card memory. i.e. the host memory window will be modified to map the pc-card at a new card memory offset Arguments: Pdo - Pointer to the device object for the PC-Card HostBase - Host memory window base to be mapped CardBase - Mandatory if Enable is TRUE New card memory offset to map the host memory window to Enable - If this is FALSE - all the remaining arguments are ignored and the host window will simply be disabled WindowSize - Specifies the size of the host memory window to be mapped. Note this must be at the proper alignment and must be less than or equal to the originally allocated window size for the host base. If this is zero, the originally allocated window size will be used. AccessSpeed - Mandatory if Enable is TRUE Specifies the new access speed for the pc-card. (AccessSpeed should be encoded as per the pc-card standard, card/socket services spec) BusWidth - Mandatory if Enable is TRUE One of PCMCIA_MEMORY_8BIT_ACCESS or PCMCIA_MEMORY_16BIT_ACCESS IsAttributeMemory - Mandatory if Enable is TRUE Specifies if the window should be mapped to the pc-card's attribute or common memory Return Value: TRUE - Memory window was enabled/disabled as requested FALSE - If not --*/ { PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PFDO_EXTENSION fdoExtension; PSOCKET socketPtr; PSOCKET_CONFIGURATION socketConfig; USHORT index; UCHAR registerOffset; UCHAR regl; UCHAR regh; UCHAR tmp, waitIndex; socketConfig = pdoExtension->SocketConfiguration; if (!socketConfig) { // doesn't look like we are started. return FALSE; } socketPtr = pdoExtension->Socket; ASSERT ( socketPtr != NULL ); fdoExtension = socketPtr->DeviceExtension; for (index = 0 ; index < socketConfig->NumberOfMemoryRanges; index++) { if (socketConfig->Memory[index].HostBase == HostBase) { break; } } if (index >= socketConfig->NumberOfMemoryRanges) { // // Unknown hostbase // return FALSE; } // // Make sure caller isn't asking a bigger window // than he is permitted to // if (WindowSize > socketConfig->Memory[index].Length) { return FALSE; } if (WindowSize == 0) { // // WindowSize not provided. Default to // the largest size permitted for this pc-card // WindowSize = socketConfig->Memory[index].Length; } // // Determine offset in registers. // registerOffset = (index * 8); // // Disable the window first (this has to be done regardless // of whether we want to enable/disable the window ultimately) // PCMCIA_ACQUIRE_DEVICE_LOCK(socketPtr->DeviceExtension); tmp = PcicReadSocket(socketPtr, PCIC_ADD_WIN_ENA); tmp &= ~(1 << index); PcicWriteSocket(socketPtr, PCIC_ADD_WIN_ENA, tmp); if (!Enable) { // // We're done.. Just write zeroes to the window registers anyway // before returning // PcicWriteSocket(socketPtr, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_L+registerOffset), 0); PcicWriteSocket(socketPtr, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_H+registerOffset), 0); PcicWriteSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STRT_L+registerOffset), 0); PcicWriteSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STRT_H+registerOffset), 0); PcicWriteSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STOP_L+registerOffset), 0); PcicWriteSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STOP_H+registerOffset), 0); PcmciaSetWindowPage(fdoExtension, socketPtr, index, 0); PCMCIA_RELEASE_DEVICE_LOCK(socketPtr->DeviceExtension); return TRUE; } if (AccessSpeed) { if (!NT_SUCCESS(PcicConvertSpeedToWait(AccessSpeed, &waitIndex))) { PCMCIA_RELEASE_DEVICE_LOCK(socketPtr->DeviceExtension); return FALSE; } } // // Calculate and set card base addresses. // This is the 2's complement of the host address and // the card offset. // CardBase = (CardBase - (HostBase & OFFSETCALC_BASE_MASK)) & OFFSETCALC_OFFSET_MASK; regl = (UCHAR) (CardBase >> 12); regh = (UCHAR) ((CardBase >> 20) & 0x003f); if (IsAttributeMemory) { regh |= 0x40; } PcicWriteSocket(socketPtr, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_L + registerOffset), regl); PcicWriteSocket(socketPtr, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_H + registerOffset), regh); // // Calculate and set host window. // if (!PcmciaSetWindowPage(fdoExtension, socketPtr, index, (UCHAR) ((ULONG) HostBase >> 24))) { if ((HostBase + WindowSize) > 0xFFFFFF) { DebugPrint((PCMCIA_DEBUG_FAIL, "PcicModifyMemorywindow: HostBase %x specified: doesn't fit in 24 bits!\n", (ULONG) HostBase)); PCMCIA_RELEASE_DEVICE_LOCK(socketPtr->DeviceExtension); return FALSE; } } regl = (UCHAR) (HostBase >> 12); regh = (UCHAR) (HostBase >> 20) & 0xF; if (BusWidth == PCMCIA_MEMORY_16BIT_ACCESS) { regh |= 0x80; // 16-bit access #if 0 // // If this is not a revision 1 part (0x82), then set // the work around register for 16-bit windows. // // This bit is not used on any chip that I have // documentation for. I have no idea why it is here, it is // not in win9x. // In any case it looks like a NOOP for the vast majority of // chips, but since it uses a NOT, then it is invoked on all // new controllers. REMOVE after next major release // if (socketPtr->Revision != PCIC_REVISION) { tmp = PcicReadSocket(socketPtr, PCIC_CARD_DETECT); tmp |= 0x01; PcicWriteSocket(socketPtr, PCIC_CARD_DETECT, tmp); } #endif } PcicWriteSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STRT_L + registerOffset), regl); PcicWriteSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STRT_H + registerOffset), regh); // // Set stop address. // HostBase += WindowSize - 1; regl = (UCHAR) (HostBase >> 12); regh = (UCHAR) (HostBase >> 20) & 0xF; // // Set the wait states // if (AccessSpeed) { // // New access speed specified, use it // regh |= (waitIndex << 6); } else { // // Use existing access speed // regh |= (PcicReadSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STOP_H + registerOffset)) & 0xC0); } PcicWriteSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STOP_L + registerOffset), regl); PcicWriteSocket(socketPtr, (UCHAR)(PCIC_MEM_ADD0_STOP_H + registerOffset), regh); // // Memory window set up now enable it // tmp = (1 << index); tmp |= PcicReadSocket(socketPtr, PCIC_ADD_WIN_ENA); PcicWriteSocket(socketPtr, PCIC_ADD_WIN_ENA, tmp); // // Allow the window to settle // (VOID) PcicPCCardReady(socketPtr); PCMCIA_RELEASE_DEVICE_LOCK(socketPtr->DeviceExtension); return TRUE; } BOOLEAN PcicIsWriteProtected( IN PDEVICE_OBJECT Pdo ) /*++ Routine Description: Part of the interfaces originally developed to support flash memory cards. Returns the status of the write protected pin for the given PC-Card Arguments: Pdo - Pointer to the device object for the PC-Card Return Value: TRUE - if the PC-Card is write-protected FALSE - if not --*/ { PSOCKET socketPtr = ((PPDO_EXTENSION) Pdo->DeviceExtension)->Socket; ASSERT ( socketPtr != NULL ); return ((PcicReadSocket(socketPtr, PCIC_STATUS) & 0x10) != 0); } VOID PcicEnableDisableWakeupEvent( IN PSOCKET Socket, IN PPDO_EXTENSION PdoExtension, IN BOOLEAN Enable ) /*++ Routine Description This routine sets/resets the Ring Indicate enable bit for the given socket, enabling a PC-Card in the socket to assert/not assert wake through the RingIndicate pin. Arguments Socket - Pointer to the socket Enable - TRUE : set ring indicate enable FALSE: turn off ring indicate, i.e. system cannot be woken up through a pc-card in this socket Return Value None --*/ { UCHAR byte; byte = PcicReadSocket(Socket, PCIC_INTERRUPT); if (Enable) { byte |= IGC_RINGIND_ENABLE; } else { byte &= ~IGC_RINGIND_ENABLE; } PcicWriteSocket(Socket, PCIC_INTERRUPT, byte); } BOOLEAN PcicInitializePcmciaSocket( PSOCKET Socket ) /*++ 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: Socket - socket specific information Return Value: TRUE if successful FALSE if not successful --*/ { UCHAR index; UCHAR byte; UCHAR reg; // // Initialize the EXCA registers // // for (index = 0; index < 0xFF; index++) { reg = (UCHAR) PcicRegisterInitTable[index].Register; if (reg == 0xFF) { // // End of table // break; } byte = (UCHAR) PcicRegisterInitTable[index].Value; if (reg == PCIC_INTERRUPT) { // // Don't clobber the Ring Enable bit // NOTE: this entire if statement should be removed // when WAIT_WAKE support is done for modems // also don't clobber the interrupt enable bit // byte |= (PcicReadSocket(Socket, reg) & (IGC_RINGIND_ENABLE | IGC_INTR_ENABLE)); } PcicWriteSocket(Socket, reg, byte); } if (CLPD6729(Socket)) { // // Need to program the chip per code in // Windows 95. This will turn on the // audio support bit. // NOTE: This used to be done in PcicDetect // byte = PcicReadSocket(Socket, PCIC_CL_MISC_CTRL1); byte |= CL_MC1_SPKR_ENABLE; PcicWriteSocket(Socket, PCIC_CL_MISC_CTRL1, byte); // // Set the Cirrus Logic controller for ISA style interrupts // byte = PcicReadExtendedCirrusController(Socket->AddressPort, Socket->RegisterOffset, PCIC_CIRRUS_EXTENSION_CTRL_1); byte &= ~0x08; // Isa style interrupt PcicWriteExtendedCirrusController(Socket->AddressPort, Socket->RegisterOffset, PCIC_CIRRUS_EXTENSION_CTRL_1, byte); } return TRUE; } NTSTATUS PcicResetCard ( IN PSOCKET Socket, OUT PULONG pDelayTime ) /*++ Routine Description: Resets the pc-card in the given socket. Arguments: Socket - Pointer to the socket in which the pc-card resides 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; UCHAR byte; switch(Socket->CardResetPhase) { case 1: // // Set interface mode to memory to begin with // byte = PcicReadSocket(Socket, PCIC_INTERRUPT); byte &= ~IGC_PCCARD_IO; PcicWriteSocket(Socket, PCIC_INTERRUPT, byte); // // Start reset // byte = PcicReadSocket(Socket, PCIC_INTERRUPT); byte = byte & ~IGC_PCCARD_RESETLO; PcicWriteSocket(Socket, PCIC_INTERRUPT, byte); *pDelayTime = PcicResetWidthDelay; status = STATUS_MORE_PROCESSING_REQUIRED; break; case 2: // // Stop reset // byte = PcicReadSocket(Socket, PCIC_INTERRUPT); byte |= IGC_PCCARD_RESETLO; PcicWriteSocket(Socket, PCIC_INTERRUPT, byte); *pDelayTime = PcicResetSetupDelay; status = STATUS_MORE_PROCESSING_REQUIRED; break; case 3: // // Wait for the card to settle // PcicPCCardReady(Socket); status = STATUS_SUCCESS; break; default: ASSERT(FALSE); status = STATUS_UNSUCCESSFUL; } return status; } UCHAR PcicReadSocket( IN PSOCKET Socket, IN ULONG Register ) /*++ Routine Description: This routine will read a byte from the specified socket EXCA register Arguments: Socket -- Pointer to the socket from which we should read Register -- The register to be read Return Value: The data returned from the port. --*/ { UCHAR byte; if (CardBus(Socket)) { // // Sanity check in case controller wasn't started // if (Socket->DeviceExtension->CardBusSocketRegisterBase) { byte = READ_REGISTER_UCHAR((PUCHAR) (Socket->DeviceExtension->CardBusSocketRegisterBase + Register + CARDBUS_EXCA_REGISTER_BASE)); } else { byte = 0xff; } } else { byte = PcicReadController(Socket->AddressPort, Socket->RegisterOffset, (UCHAR) Register); } return byte; } VOID PcicWriteSocket( IN PSOCKET Socket, IN ULONG Register, IN UCHAR DataByte ) /*++ Routine Description: This routine will write a byte to the specified socket EXCA register Arguments: Socket -- Pointer to the socket to which we write Register -- The register to be read DataByte -- Data to be written Return Value: None --*/ { if (CardBus(Socket)) { // // Sanity check in case controller wasn't started // if (Socket->DeviceExtension->CardBusSocketRegisterBase) { WRITE_REGISTER_UCHAR((PUCHAR) (Socket->DeviceExtension->CardBusSocketRegisterBase+Register+CARDBUS_EXCA_REGISTER_BASE), DataByte); } } else { PcicWriteController(Socket->AddressPort, Socket->RegisterOffset, (UCHAR)Register, DataByte); } } UCHAR PcicReadController( IN PUCHAR Base, IN USHORT Socket, IN UCHAR Register ) /*++ Routine Description: This routine will read a byte from the controller data port Arguments: Base -- The I/O port for the controller Socket -- The socket in for the card being read Register -- The register to be read Return Value: The data returned from the port. --*/ { UCHAR dataByte = 0; WRITE_PORT_UCHAR(Base, (UCHAR)(Socket+Register)); dataByte = READ_PORT_UCHAR((PUCHAR)Base + 1); return dataByte; } VOID PcicWriteController( IN PUCHAR Base, IN USHORT Socket, IN UCHAR Register, IN UCHAR DataByte ) /*++ Routine Description: This routine will write a byte to the controller data port Arguments: Base -- The I/O port for the controller Socket -- The socket in for the card being read Register -- The register to be read DataByte -- Data to be written Return Value: None --*/ { WRITE_PORT_UCHAR(Base, (UCHAR)(Socket+Register)); WRITE_PORT_UCHAR((PUCHAR)Base + 1, DataByte); } UCHAR PcicReadExtendedCirrusController( IN PUCHAR Base, IN USHORT Socket, IN UCHAR Register ) /*++ Routine Description: This routine will read a byte from the Cirrus logic extended registers Arguments: Base -- The I/O port for the controller Socket -- The socket in for the card being read Register -- The register to be read Return Value: The data returned from the port. --*/ { UCHAR dataByte = 0; PcicWriteController(Base, Socket, PCIC_CIRRUS_EXTENDED_INDEX, Register); dataByte = PcicReadController(Base, Socket, PCIC_CIRRUS_INDEX_REG); return dataByte; } VOID PcicWriteExtendedCirrusController( IN PUCHAR Base, IN USHORT Socket, IN UCHAR Register, IN UCHAR DataByte ) /*++ Routine Description: This routine will write a byte to one of the Cirrus Logic extended registers Arguments: Base -- The I/O port for the controller Socket -- The socket in for the card being read Register -- The register to be read DataByte -- Data to be written Return Value: None --*/ { // // Register needs to be written out to the extended index register // PcicWriteController(Base, Socket, PCIC_CIRRUS_EXTENDED_INDEX, Register); PcicWriteController(Base, Socket, PCIC_CIRRUS_INDEX_REG, DataByte); } ULONG PcicReadWriteCardMemory( IN PSOCKET Socket, IN MEMORY_SPACE MemorySpace, IN ULONG Offset, IN PUCHAR Buffer, IN ULONG Length, IN CONST BOOLEAN Read ) /*++ Routine Description: This routine will read or 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: Socket -- 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 Read -- boolean indicating read or write --*/ { PFDO_EXTENSION fdoExtension = Socket->DeviceExtension; PUCHAR memoryPtr, memoryPtrMax; PUCHAR bufferPtr; ULONG index, adjustedOffset, adjustedBase; UCHAR memGran; UCHAR memWidth; // // NOTE: memGran HAS to be a integral divisor of AttributeMemorySize for // the rest of the code to work! // memGran = (MemorySpace == PCCARD_ATTRIBUTE_MEMORY) ? 2 : 1; memWidth = (MemorySpace == PCCARD_ATTRIBUTE_MEMORY) ? MEM_8BIT : MEM_16BIT; // // Adjust for offsets > size of attribute memory window. // adjustedOffset = (Offset*memGran) % fdoExtension->AttributeMemorySize; // // Adjusted base is: |_ Offset _| mod AttrributeMemorySize // adjustedBase = ((Offset*memGran) / fdoExtension->AttributeMemorySize) * fdoExtension->AttributeMemorySize; bufferPtr = Buffer; PcicEnableDisableMemory(Socket, MemorySpace, adjustedBase, memWidth, TRUE); // // Now read the memory contents into the user buffer // memoryPtr = fdoExtension->AttributeMemoryBase + adjustedOffset; memoryPtrMax = fdoExtension->AttributeMemoryBase + fdoExtension->AttributeMemorySize; for (index = 0; index < Length; index++) { if (memoryPtr >= memoryPtrMax) { // // Skip to next page of attribute memory // (size of page = fdoExtension->AttributeMemorySize) // adjustedBase += fdoExtension->AttributeMemorySize; // // Remap window at new base // PcicEnableDisableMemory(Socket, MemorySpace, adjustedBase, memWidth, TRUE); memoryPtr = fdoExtension->AttributeMemoryBase; } if (Read) { *bufferPtr++ = READ_REGISTER_UCHAR(memoryPtr); } else { WRITE_REGISTER_UCHAR(memoryPtr, *bufferPtr++); } memoryPtr += memGran; } PcicEnableDisableMemory(Socket, 0,0,0, FALSE); return Length; } ULONG PcicReadWriteCardMemoryIndirect( IN PSOCKET Socket, IN MEMORY_SPACE MemorySpace, IN ULONG Offset, IN PUCHAR Buffer, IN ULONG Length, IN CONST BOOLEAN Read ) /*++ Routine Description: This routine will read or write the memory space of a pcmcia card using the indirect access method. Arguments: Socket -- 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 Read -- boolean indicating read or write --*/ { PFDO_EXTENSION fdoExtension = Socket->DeviceExtension; ULONG index, adjustedOffset; PUCHAR pMem; UCHAR Control = 0; UCHAR memGran; PcicEnableDisableMemory(Socket, PCCARD_COMMON_MEMORY, 0, MEM_8BIT, TRUE); pMem = (PUCHAR) ((ULONG_PTR)(fdoExtension->AttributeMemoryBase) + IAR_CONTROL_LOW); Control = (MemorySpace == PCCARD_ATTRIBUTE_MEMORY_INDIRECT) ? IARF_AUTO_INC : IARF_AUTO_INC | IARF_COMMON | IARF_BYTE_GRAN; WRITE_REGISTER_UCHAR(pMem, Control); memGran = (MemorySpace == PCCARD_ATTRIBUTE_MEMORY_INDIRECT) ? 2 : 1; adjustedOffset = Offset*memGran; pMem = (PUCHAR) ((ULONG_PTR)(fdoExtension->AttributeMemoryBase) + IAR_ADDRESS); for (index = 0; index < sizeof(ULONG); index++) { WRITE_REGISTER_UCHAR(pMem++, (UCHAR)(adjustedOffset>>(index*8))); } PcicEnableDisableMemory(Socket, PCCARD_COMMON_MEMORY, 0, MEM_16BIT, TRUE); for (index = 0; index < Length; index++) { // Note that pMem should be pointing to IAR_DATA, and is NOT // supposed to be incremented if (Read) { Buffer[index] = READ_REGISTER_UCHAR(pMem); } else { WRITE_REGISTER_UCHAR(pMem, Buffer[index]); } } PcicEnableDisableMemory(Socket, 0,0,0, FALSE); return Length; } ULONG PcicWriteCardMemory( 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: PdoExtension-- The extension of the device 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 Socket = PdoExtension->Socket; ULONG retLength; ASSERT (IsSocketFlagSet(Socket, SOCKET_CARD_POWERED_UP)); switch(MemorySpace) { case PCCARD_ATTRIBUTE_MEMORY_INDIRECT: case PCCARD_COMMON_MEMORY_INDIRECT: retLength = PcicReadWriteCardMemoryIndirect(Socket, MemorySpace, Offset, Buffer, Length, FALSE); break; case PCCARD_ATTRIBUTE_MEMORY: case PCCARD_COMMON_MEMORY: retLength = PcicReadWriteCardMemory(Socket, MemorySpace, Offset, Buffer, Length, FALSE); break; default: retLength = 0; ASSERT(FALSE); } return retLength; } ULONG PcicReadCardMemory( IN PPDO_EXTENSION PdoExtension, IN MEMORY_SPACE MemorySpace, IN ULONG Offset, IN PUCHAR Buffer, IN ULONG Length ) /*++ Routine Description: This routine will read the configuration memory on the card Arguments: PdoExtension-- The extension of the device being read MemorySpace -- indicates which space - attribute or common memory Offset -- Offset in the memory to read Buffer -- pointer to pointer for tuple information. Length -- maximum size of the buffer area for tuple information. Return Value: --*/ { PSOCKET Socket = PdoExtension->Socket; ULONG retLength; ASSERT (IsSocketFlagSet(Socket, SOCKET_CARD_POWERED_UP)); switch(MemorySpace) { case PCCARD_ATTRIBUTE_MEMORY_INDIRECT: case PCCARD_COMMON_MEMORY_INDIRECT: retLength = PcicReadWriteCardMemoryIndirect(Socket, MemorySpace, Offset, Buffer, Length, TRUE); break; case PCCARD_ATTRIBUTE_MEMORY: case PCCARD_COMMON_MEMORY: retLength = PcicReadWriteCardMemory(Socket, MemorySpace, Offset, Buffer, Length, TRUE); DebugPrint((PCMCIA_DEBUG_INFO,"PcicReadCardMemory: " "%.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X-%.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X\n", Buffer[0], Buffer[1], Buffer[2], Buffer[3], Buffer[4], Buffer[5], Buffer[6], Buffer[7], Buffer[8], Buffer[9], Buffer[10], Buffer[11], Buffer[12], Buffer[13], Buffer[14], Buffer[15])); break; default: retLength = 0; ASSERT(FALSE); } return retLength; } BOOLEAN PcicProcessConfigureRequest( IN PSOCKET Socket, IN PCARD_REQUEST request, IN PUCHAR Base ) /*++ Routine Description: Processes a configure or IRQ setup request. Arguments: ConfigRequest -- Socket config structure Base - the I/O port base Return Value: None --*/ { USHORT index; UCHAR tmp; // // 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: { UCHAR ioControl = 0; if (!request->u.Io.IoEntry[0].BasePort) { break; } for (index = 0; index < request->u.Io.NumberOfRanges; index++) { UCHAR registerOffset; registerOffset = (index * 4); if (request->u.Io.IoEntry[index].BasePort) { PcicWriteSocket( Socket, PCIC_IO_ADD0_STRT_L + registerOffset, (UCHAR) (request->u.Io.IoEntry[index].BasePort & 0xff)); PcicWriteSocket( Socket, PCIC_IO_ADD0_STRT_H + registerOffset, (UCHAR) (request->u.Io.IoEntry[index].BasePort >> 8)); PcicWriteSocket(Socket, PCIC_IO_ADD0_STOP_L + registerOffset, (UCHAR) ((request->u.Io.IoEntry[index].BasePort + request->u.Io.IoEntry[index].NumPorts) & 0xff)); PcicWriteSocket(Socket, PCIC_IO_ADD0_STOP_H + registerOffset, (UCHAR) ((request->u.Io.IoEntry[index].BasePort + request->u.Io.IoEntry[index].NumPorts) >> 8)); } // // set up the io control register // tmp = 0; if (request->u.Io.IoEntry[index].Attributes & IO_DATA_PATH_WIDTH) { tmp |= IOC_IO0_DATASIZE; } if ((request->u.Io.IoEntry[index].Attributes & IO_WAIT_STATE_16) && !((Elc(Socket) || CLPD6729(Socket)))) { tmp |= IOC_IO0_WAITSTATE; } if (request->u.Io.IoEntry[index].Attributes & IO_SOURCE_16) { tmp |= IOC_IO0_IOCS16; } if (request->u.Io.IoEntry[index].Attributes & IO_ZERO_WAIT_8) { tmp |= IOC_IO0_ZEROWS; } ioControl |= tmp << registerOffset; } PcicWriteSocket(Socket, PCIC_IO_CONTROL, ioControl); tmp = PcicReadSocket( Socket, PCIC_ADD_WIN_ENA); tmp &= ~(WE_IO0_ENABLE | WE_IO1_ENABLE); switch(request->u.Io.NumberOfRanges) { case 1: tmp |= WE_IO0_ENABLE; break; case 2: tmp |= (WE_IO0_ENABLE | WE_IO1_ENABLE); break; } PcicWriteSocket(Socket, PCIC_ADD_WIN_ENA, tmp); break; } case IRQ_REQUEST: { // // Do not nuke the reset and cardtype bits. // tmp = PcicReadSocket(Socket, PCIC_INTERRUPT); tmp &= ~IGC_IRQ_MASK; tmp |= request->u.Irq.AssignedIRQ; DebugPrint((PCMCIA_DEBUG_INFO, "PcicProcessConfigureRequest: Assigned IRQ %x programming IRQ %x\n", request->u.Irq.AssignedIRQ,tmp)); PcicWriteSocket(Socket, PCIC_INTERRUPT, tmp); if (tmp = request->u.Irq.ReadyIRQ) { tmp = (tmp << 4) | 0x04; PcicWriteSocket(Socket, PCIC_CARD_INT_CONFIG, tmp); } break; } case DECONFIGURE_REQUEST: { // // Deregister the interrupt, re-init to memory interface // tmp = PcicReadSocket(Socket, PCIC_INTERRUPT); tmp &= ~(IGC_PCCARD_IO | IGC_IRQ_MASK); PcicWriteSocket(Socket, PCIC_INTERRUPT, tmp); // // Disable memory/io windows // Don't touch the memory window which is // is used by the controller for reading attribute memory // if (IsSocketFlagSet(Socket, SOCKET_MEMORY_WINDOW_ENABLED)) { UCHAR enableMask; enableMask = WE_MEM0_ENABLE << Socket->CurrentMemWindow; tmp = PcicReadSocket(Socket, PCIC_ADD_WIN_ENA); tmp &= enableMask; } else { // // no attribute window enabled, just turn off everything // tmp = 0; } PcicWriteSocket(Socket, PCIC_ADD_WIN_ENA, tmp); // // Zero out the I/O windows // for (index = PCIC_IO_ADD0_STRT_L; index <= PCIC_IO_ADD1_STOP_H; index++) { PcicWriteSocket( Socket, (ULONG) index, 0); } break; } case CONFIGURE_REQUEST:{ // // Tell the socket controller we are an I/O card if InterfaceType says so // if (request->u.Config.InterfaceType == CONFIG_INTERFACE_IO_MEM) { tmp = PcicReadSocket(Socket, PCIC_INTERRUPT); tmp |= IGC_PCCARD_IO; PcicWriteSocket(Socket, PCIC_INTERRUPT, tmp); } else { tmp = PcicReadSocket(Socket, PCIC_INTERRUPT); tmp &= ~IGC_PCCARD_IO; PcicWriteSocket(Socket, PCIC_INTERRUPT, tmp); } if (request->u.Config.RegisterWriteMask & (REGISTER_WRITE_CONFIGURATION_INDEX | REGISTER_WRITE_CARD_CONFIGURATION | REGISTER_WRITE_IO_BASE)) { // // This is where we setup the card and get it ready for operation // ULONG configRegisterBase = request->u.Config.ConfigBase / 2; PDEVICE_OBJECT Pdo = Socket->PdoList; PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; MEMORY_SPACE memorySpace = IsPdoFlagSet(pdoExtension, PCMCIA_PDO_INDIRECT_CIS) ? PCCARD_ATTRIBUTE_MEMORY_INDIRECT : PCCARD_ATTRIBUTE_MEMORY; if (request->u.Config.RegisterWriteMask & REGISTER_WRITE_IO_BASE) { UCHAR ioHigh = (UCHAR)(request->u.Config.IoBaseRegister>>8); UCHAR ioLow = (UCHAR) request->u.Config.IoBaseRegister; PcicWriteCardMemory(pdoExtension, memorySpace, configRegisterBase + 5, &ioLow, 1); PcmciaWait(PcicStallCounter); PcicWriteCardMemory(pdoExtension, memorySpace, configRegisterBase + 6, &ioHigh, 1); PcmciaWait(PcicStallCounter); } if (request->u.Config.RegisterWriteMask & REGISTER_WRITE_IO_LIMIT) { PcicWriteCardMemory(pdoExtension, memorySpace, configRegisterBase + 9, (PUCHAR)&request->u.Config.IoLimitRegister, 1); PcmciaWait(PcicStallCounter); } if (request->u.Config.RegisterWriteMask & REGISTER_WRITE_CONFIGURATION_INDEX) { UCHAR configIndex = request->u.Config.ConfigIndex; PcicWriteCardMemory(pdoExtension, memorySpace, configRegisterBase, &configIndex, 1); PcmciaWait(PcicStallCounter); configIndex |= 0x40; PcicWriteCardMemory(pdoExtension, memorySpace, configRegisterBase, &configIndex, 1); PcmciaWait(PcicStallCounter); } if (request->u.Config.RegisterWriteMask & REGISTER_WRITE_CARD_CONFIGURATION) { PcicReadCardMemory(pdoExtension, memorySpace, configRegisterBase + 1, &tmp, 1); PcmciaWait(PcicStallCounter); tmp |= request->u.Config.CardConfiguration; // // turn off power control bit // tmp &= ~0x04; PcicWriteCardMemory(pdoExtension, memorySpace, configRegisterBase + 1, &tmp, 1); PcmciaWait(PcicStallCounter); } } break; } case MEM_REQUEST: { // // Set up memory ranges on the controller. // PFDO_EXTENSION deviceExtension = Socket->DeviceExtension; for (index = 0; index < request->u.Memory.NumberOfRanges; index++) { UCHAR registerOffset; UCHAR regl; UCHAR regh; ULONG cardBase = request->u.Memory.MemoryEntry[index].BaseAddress; ULONG base = request->u.Memory.MemoryEntry[index].HostAddress; ULONG size = request->u.Memory.MemoryEntry[index].WindowSize; // // Determine offset in registers. // registerOffset = (index * 8); // // Calculate and set card base addresses. // This is the 2's complement of the host address and // the card offset. // cardBase = (cardBase - (base & OFFSETCALC_BASE_MASK)) & OFFSETCALC_OFFSET_MASK; regl = (UCHAR) (cardBase >> 12); regh = (UCHAR) (cardBase >> 20); if (request->u.Memory.MemoryEntry[index].AttributeMemory) { regh |= 0x40; } PcicWriteSocket(Socket, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_L + registerOffset), regl); PcicWriteSocket(Socket, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_H + registerOffset), regh); // // Calculate and set host window. // if (!PcmciaSetWindowPage(deviceExtension, Socket, index, (UCHAR) (base >> 24))) { ASSERT (base <= 0xFFFFFF); } base &= 0xFFFFFF; // only 24bit host base allowed regl = (UCHAR) (base >> 12); regh = (UCHAR) (base >> 20); if (request->u.Memory.MemoryEntry[index].WindowDataSize16) { // // This memory window is for a 16-bit data path // to the card. Enable appropriately. // regh |= (MEMBASE_16BIT >> 8); #if 0 // // If this is not a revision 1 part (0x82), then set // the work around register for 16-bit windows. // // This bit is not used on any chip that I have // documentation for. I have no idea why it is here, it is // not in win9x. // In any case it looks like a NOOP for the vast majority of // chips, but since it uses a NOT, then it is invoked on all // new controllers. REMOVE after next major release // if (Socket->Revision != PCIC_REVISION) { tmp = PcicReadSocket(Socket, PCIC_CARD_DETECT); tmp |= 0x01; PcicWriteSocket(Socket, PCIC_CARD_DETECT, tmp); } #endif } PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STRT_L + registerOffset), regl); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STRT_H + registerOffset), regh); // // Set stop address. // base += size - 1; regl = (UCHAR) (base >> 12); regh = (UCHAR) (base >> 20); // // Add specified wait states // regh |= (request->u.Memory.MemoryEntry[index].WaitStates << 6); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STOP_L + registerOffset), regl); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STOP_H + registerOffset), regh); } // // Memory windows are set up now enable them. // tmp = 0; for (index = 0; index < request->u.Memory.NumberOfRanges; index++) { tmp |= (1 << index); } tmp |= PcicReadSocket(Socket, PCIC_ADD_WIN_ENA); PcicWriteSocket(Socket, PCIC_ADD_WIN_ENA, tmp); break; } default: { DebugPrint((PCMCIA_DEBUG_FAIL, "ConfigRequest is INVALID!\n")); } } return TRUE; } BOOLEAN PcicDetectCardInSocket( IN PSOCKET Socket ) /*++ Routine Description: This routine will determine if a card is in the socket Arguments: Socket -- Socket information Return Value: TRUE if card is present. --*/ { UCHAR tmp; BOOLEAN cardPresent=FALSE; // // Read the PCIC status register to see if the card is in there. // tmp = PcicReadSocket(Socket, PCIC_STATUS); tmp &= (CARD_DETECT_1 | CARD_DETECT_2); if (tmp == (CARD_DETECT_1 | CARD_DETECT_2)) { cardPresent = TRUE; } return cardPresent; } BOOLEAN PcicDetectCardChanged( IN PSOCKET Socket ) /*++ Routine Description: This routine will determine if socket's card insertion status has changed. Arguments: Socket -- Socket info. Return Value: TRUE if card insertion status has changed. --*/ { // // Read the PCIC CardStatusChange register to see if CD's have changed. // return (BOOLEAN) (PcicReadSocket(Socket, PCIC_CARD_CHANGE) & CSC_CD_CHANGE); } BOOLEAN PcicDetectReadyChanged( IN PSOCKET Socket ) /*++ Routine Description: This routine will determine if socket's card ready status has changed Arguments: Socket -- Socket info. Return Value: TRUE if card ready enable has changed. --*/ { // // Read the PCIC Card status change register to see if ready has changed // return (PcicReadSocket(Socket, PCIC_CARD_CHANGE) & CSC_READY_CHANGE ?TRUE :FALSE); } VOID PcicEnableDisableMemory( IN PSOCKET Socket, IN MEMORY_SPACE MemorySpace, IN ULONG CardBase, IN UCHAR memWidth, IN BOOLEAN Enable ) /*++ Routine Description: This routine will enable or disable attribute/common memory. It searches for a free window first to avoid using a window already in use. Repeated 'enable' calls to this routine without a disable (in order to remap the base) is allowed. Arguments: Socket -- Socket information MemorySpace -- Indicates which space - ATTRIBUTE_MEMORY/COMMON_MEMORY CardBase -- card offset (base) for the attribute memory window Enable -- If TRUE, enable, if FALSE, disable Return Value: None --*/ { ULONG location; PUCHAR cisBufferPointer; PFDO_EXTENSION deviceExtension= Socket->DeviceExtension; PUCHAR PcicCisBufferBase = (PUCHAR) deviceExtension->AttributeMemoryBase; ULONG PcicPhysicalBase = deviceExtension->PhysicalBase.LowPart; BOOLEAN memoryInterface; UCHAR tmp; UCHAR index; UCHAR registerOffset; UCHAR enableMask; USHORT word; ASSERT (IsSocketFlagSet(Socket, SOCKET_CARD_POWERED_UP)); if (Enable) { tmp = PcicReadSocket(Socket, PCIC_INTERRUPT); if (tmp & IGC_PCCARD_IO) { // // Card configured for i/o interface // memoryInterface = FALSE; } else { // // Card configured for Memory interface // memoryInterface = TRUE; } // // Find a window to use. // tmp = PcicReadSocket(Socket, PCIC_ADD_WIN_ENA); if (IsSocketFlagSet(Socket, SOCKET_MEMORY_WINDOW_ENABLED)) { index = Socket->CurrentMemWindow; enableMask = WE_MEM0_ENABLE << index; } else { for (index = 0, enableMask = WE_MEM0_ENABLE; index < 5; index++, enableMask <<= 1) { if (!(tmp & enableMask)) { break; } if (index==4) { // // If we are here, we didn't find an available window. Just use the last // one anyway, it is likely a pcmcia.sys bug. // // ASSERT(FALSE); // hits docked thinkpads break; } } Socket->CurrentMemWindow = index; SetSocketFlag(Socket, SOCKET_MEMORY_WINDOW_ENABLED); } registerOffset = (index * 8); // // First turn the window off // tmp &= ~enableMask; tmp &= ~WE_MEMCS16_DECODE; PcicWriteSocket(Socket, PCIC_ADD_WIN_ENA, tmp); // // Calculate and set the memory windows start and stop locations. // // // Only 24 bit addresses programmed // For cardbus controllers, 32 bit addresses are supported, // but the higher 8 bits are written to the page register (see below) location = PcicPhysicalBase & 0xFFFFFF; word = (USHORT) ((location >> 12) & MEMBASE_ADDR_MASK); // // typically run attribute memory with 8-bit window, common with 16-bit // (except for writing the registers for attribute_indirect) // if (memWidth == MEM_16BIT) { word |= MEMBASE_16BIT; } PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STRT_L+registerOffset), (UCHAR)(word)); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STRT_H+registerOffset), (UCHAR)(word >> 8)); location += (deviceExtension->AttributeMemorySize - 1); word = (USHORT) ((location >> 12) & MEMEND_ADDR_MASK); // // Impose 3 wait states..lessons learnt from win9x implementations // word |= MEMEND_WS_MASK; PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STOP_L+registerOffset), (UCHAR)(word)); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STOP_H+registerOffset), (UCHAR)(word >> 8)); // // Set up the 2's complement card offset to zero // location = (CardBase - (PcicPhysicalBase & OFFSETCALC_BASE_MASK)) & OFFSETCALC_OFFSET_MASK; word = (USHORT) ((location >> 12) & MEMOFF_ADDR_MASK); if (MemorySpace == PCCARD_ATTRIBUTE_MEMORY) { word |= MEMOFF_REG_ACTIVE; } PcicWriteSocket(Socket, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_L+registerOffset), (UCHAR)(word)); PcicWriteSocket(Socket, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_H+registerOffset), (UCHAR)(word >> 8)); // // Set the page register // (this routine is called only for R2 cards) // Use mem4 window explicitly // if (!PcmciaSetWindowPage(deviceExtension, Socket, (USHORT)index, (UCHAR) (PcicPhysicalBase >> 24))) { ASSERT (PcicPhysicalBase <= 0xFFFFFF); } // // Enable the address window // tmp = PcicReadSocket(Socket, PCIC_ADD_WIN_ENA); tmp |= enableMask | WE_MEMCS16_DECODE; PcicWriteSocket(Socket, PCIC_ADD_WIN_ENA, tmp); cisBufferPointer = PcicCisBufferBase; if (memoryInterface) { // // Only wait for card ready if the memory window does not appear // (VOID) PcicPCCardReady(Socket); } else { // // Wait a little bit for the window to appear // PcmciaWait(PcicMemoryWindowDelay); } DebugPrint((PCMCIA_DEBUG_INFO, "skt %08x memory window %d enabled %x\n", Socket, index, PcicPhysicalBase)); } else { if (IsSocketFlagSet(Socket, SOCKET_MEMORY_WINDOW_ENABLED)) { enableMask = WE_MEM0_ENABLE << Socket->CurrentMemWindow; registerOffset = (Socket->CurrentMemWindow * 8); // // Disable the Address window // tmp = PcicReadSocket(Socket, PCIC_ADD_WIN_ENA); tmp &= ~enableMask; PcicWriteSocket(Socket, PCIC_ADD_WIN_ENA, tmp); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STRT_L+registerOffset), 0xFF); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STRT_H+registerOffset), 0x0F); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STOP_L+registerOffset), 0xFF); PcicWriteSocket(Socket, (UCHAR)(PCIC_MEM_ADD0_STOP_H+registerOffset), 0x0F); PcicWriteSocket(Socket, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_L+registerOffset), 0x00); PcicWriteSocket(Socket, (UCHAR)(PCIC_CRDMEM_OFF_ADD0_H+registerOffset), 0x00); PcmciaSetWindowPage(deviceExtension, Socket, Socket->CurrentMemWindow, 0); DebugPrint((PCMCIA_DEBUG_INFO, "skt %08x memory window %d disabled\n", Socket, Socket->CurrentMemWindow)); ResetSocketFlag(Socket, SOCKET_MEMORY_WINDOW_ENABLED); } } return; } BOOLEAN PcicPCCardReady( IN PSOCKET Socket ) /*++ Routine Description: Loop for a reasonable amount of time waiting for the card status to return ready. Arguments: Socket - the socket to check. Return Value: TRUE - the card is ready. FALSE - after a reasonable delay the card is still not ready. --*/ { ULONG index; UCHAR byte; PFDO_EXTENSION fdoExtension = Socket->DeviceExtension; NTSTATUS status; LARGE_INTEGER timeout; #ifdef READY_ENABLE if (fdoExtension->PcmciaInterruptObject) { byte = PcicReadSocket(Socket, PCIC_STATUS); if (byte & 0x20) { return TRUE; } // // Enable ready enable controller interrupt // PcicEnableDisableControllerInterrupt( Socket, fdoExtension->Configuration.Interrupt.u.Interrupt.Level, TRUE, TRUE); RtlConvertLongToLargeInteger(-PCMCIA_READY_WAIT_INTERVAL); status = KeWaitForSingleObject(&Socket->PCCardReadyEvent, Executive, KernelMode, FALSE, &timeout ); if (status != STATUS_TIMEOUT) { return TRUE; } return FALSE; } #endif for (index = 0; index < fdoExtension->ReadyDelayIter; index++) { byte = PcicReadSocket(Socket, PCIC_STATUS); if (byte & 0x20) { break; } PcmciaWait(fdoExtension->ReadyStall); } if (index < fdoExtension->ReadyDelayIter) { DebugPrint((PCMCIA_COUNTERS, "PcicPCCardReady: %d\n", index)); return TRUE; } return FALSE; } NTSTATUS PcicIsaDetect( IN PFDO_EXTENSION DeviceExtension ) /*++ Routine Description: Locate any PCMCIA sockets supported by this driver. This routine will find the 82365SL and compatible parts and construct SOCKET structures to represent all sockets found Arguments: DeviceExtension - the root for the SocketList. Return Value: STATUS_SUCCESS if a controller is found: also indicates this might be called again to locate another controller STATUS_UNSUCCESSFUL if something failed/no controllers found. STATUS_NO_MORE_ENTRIES if no more pcic controllers can be found. Stop calling this routine. --*/ { #define PCMCIA_NUMBER_ISA_PORT_ADDRESSES 3 static ULONG isaIndex=0; ULONG index; ULONG ioPortBases[PCMCIA_NUMBER_ISA_PORT_ADDRESSES] = { 0x3e0, 0x3e2, 0x3e4}; NTSTATUS status=STATUS_NO_MORE_ENTRIES; PAGED_CODE(); DeviceExtension->Configuration.InterfaceType = Isa; DeviceExtension->Configuration.BusNumber = 0x0; for (index = isaIndex; !NT_SUCCESS(status) && (index < PCMCIA_NUMBER_ISA_PORT_ADDRESSES); index++) { status = PcicDetect(DeviceExtension,Isa, ioPortBases[index]); } // // Set index for next search // isaIndex = index; return status; } NTSTATUS PcicDetect( IN PFDO_EXTENSION DeviceExtension, IN INTERFACE_TYPE InterfaceType, IN ULONG IoPortBase ) /*++ Routine Description: This routine is used for legacy detecting PCIC-compatible PCMCIA controllers. This attempts to sniff the standard PCMCIA controller ports to check if anything resembling the PCMCIA revisions exists, and if so obtain and initialize socket information for the controller. Arguments: DeviceExtension - pointer to the already allocated device extension for the yet-to-be-detected pcmcia controller. InterfaceType - Bus interface type on which the pcmcia controller is expected to reside. Currently we legacy detect only ISA based controllers IoPortBase - IoPort address we need to sniff at for finding the controller Return value: STATUS_SUCCESS PCMCIA controller was found STATUS_UNSUCCESSFUL otherwise --*/ { ULONG addressSpace; NTSTATUS status; PUCHAR port; PUCHAR elcPort; PHYSICAL_ADDRESS cardAddress; PHYSICAL_ADDRESS portAddress; PCM_RESOURCE_LIST cmResourceList = NULL; PCM_PARTIAL_RESOURCE_LIST cmPartialResourceList; UCHAR saveBytes[2]; UCHAR dataByte; UCHAR revisionByte; USHORT socket; UCHAR socketNumber = 0; BOOLEAN translated; BOOLEAN mapped = FALSE; BOOLEAN conflict = TRUE; BOOLEAN resourcesAllocated = FALSE; PAGED_CODE(); portAddress.LowPart = IoPortBase; portAddress.u.HighPart = 0; // // Get the resources used for detection // cmResourceList = ExAllocatePool(PagedPool, sizeof(CM_RESOURCE_LIST)); if (!cmResourceList) { status = STATUS_INSUFFICIENT_RESOURCES; goto Exit; } 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.Start = portAddress; cmPartialResourceList->PartialDescriptors[0].u.Port.Length = 2; status=IoReportResourceForDetection( DeviceExtension->DriverObject, cmResourceList, sizeof(CM_RESOURCE_LIST), NULL, NULL, 0, &conflict); if (!NT_SUCCESS(status) || conflict) { goto Exit; } resourcesAllocated = TRUE; addressSpace = 1; // port space translated = HalTranslateBusAddress(InterfaceType, 0, portAddress, &addressSpace, &cardAddress); if (!translated) { // // HAL would not translate the address. // status = STATUS_UNSUCCESSFUL; goto Exit; } if (addressSpace) { // // I/O port space // port = (PUCHAR)(cardAddress.QuadPart); } else { // // Memory space.. we need to map this into memory // port = MmMapIoSpace(cardAddress, 2, FALSE); mapped = TRUE; } status = STATUS_UNSUCCESSFUL; socket = 0; dataByte = PcicReadController(port, socket, PCIC_IDENT); revisionByte = dataByte; switch (dataByte) { case PCIC_REVISION: case PCIC_REVISION2: case PCIC_REVISION3: { // // The cirrus logic controller will toggle top 2 lines from the chip info // register on the chip. Read from thelocation 3 times and verify that the top two // lines are changing. We do this for NEC 98 also.. // ULONG i; UCHAR data[4]; WRITE_PORT_UCHAR(port, (UCHAR)(socket + PCIC_CL_CHIP_INFO)); for (i = 0; i < 3; i++) { data[i] = READ_PORT_UCHAR(port+1); if (i) { dataByte = data[i - 1] ^ data[i]; if (dataByte != 0xc0) { break; } } } if (i == 3) { // // Ah. this is a cirrus logic controller // PcmciaSetControllerType(DeviceExtension, PcmciaCLPD6729); } dataByte = PcicReadController(port, socket, PCIC_CARD_CHANGE); if (dataByte & 0xf0) { // // Not a socket. // break; } // // Map and try to locate the Compaq Elite controller // This code is a rough approximation of the code in // the Windows 95 detection module for the PCIC part. // addressSpace = 1; // port space portAddress.LowPart = IoPortBase + 0x8000; portAddress.HighPart = 0; translated = HalTranslateBusAddress(Isa, 0, portAddress, &addressSpace, &cardAddress); if (translated) { if (!addressSpace) { elcPort = MmMapIoSpace(cardAddress, 2, FALSE); } else { elcPort = (PUCHAR)(cardAddress.QuadPart); } // // Save current index value. // saveBytes[0] = READ_PORT_UCHAR(elcPort); WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT)); // // Save data byte for the location that will be used // for the test. // saveBytes[1] = READ_PORT_UCHAR(elcPort + 1); // // Check for an ELC // WRITE_PORT_UCHAR(elcPort+1, 0x55); WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT)); dataByte = READ_PORT_UCHAR(elcPort+1); if (dataByte == 0x55) { WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT)); WRITE_PORT_UCHAR(elcPort+1, 0xaa); WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT)); dataByte = READ_PORT_UCHAR(elcPort+1); if (dataByte == 0xaa) { // // ELC found - initialize eaddr registers // WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + 0)); WRITE_PORT_UCHAR(elcPort+1, 0); WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + 1)); WRITE_PORT_UCHAR(elcPort+1, 0); WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + 2)); WRITE_PORT_UCHAR(elcPort+1, 0x10); PcmciaSetControllerType(DeviceExtension, PcmciaElcController); } } // // Restore the original values. // WRITE_PORT_UCHAR(elcPort, (UCHAR)(socket + PCIC_IDENT)); WRITE_PORT_UCHAR(elcPort+1, saveBytes[1]); WRITE_PORT_UCHAR(elcPort, saveBytes[0]); if (!addressSpace) { MmUnmapIoSpace(elcPort, 2); } } DeviceExtension->Configuration.UntranslatedPortAddress = (USHORT)IoPortBase; DeviceExtension->Configuration.PortSize = 2; DebugPrint((PCMCIA_DEBUG_DETECT, "Port %x Offset %x\n", port, socket)); status = STATUS_SUCCESS; break; } default: { DebugPrint((PCMCIA_DEBUG_DETECT, "controller at (0x%x:0x%x) not found, returns %x\n", portAddress.LowPart, socket, dataByte)); break; } } Exit: if (!NT_SUCCESS(status) && mapped) { MmUnmapIoSpace(port, 2); } // // 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 status; } NTSTATUS PcicBuildSocketList( IN PFDO_EXTENSION DeviceExtension ) /*++ Routine Description: This routine looks out at the registers of the controller to see how many sockets there are. For each socket, a SOCKET structure is allocated and chained onto the SocketList pointer of the device extension. Arguments: DeviceExtension - pointer to the device extension enumerated PDO Return value: STATUS_SUCCESS PCMCIA controller was found and socket structures were succesfully initialized STATUS_UNSUCCESSFUL otherwise --*/ { ULONG addressSpace; NTSTATUS status; PUCHAR port; PSOCKET socketPtr; PSOCKET previousSocket; UCHAR dataByte; UCHAR revisionByte; USHORT socket; UCHAR socketNumber = 0; PAGED_CODE(); previousSocket = DeviceExtension->SocketList; port = (PUCHAR)DeviceExtension->Configuration.UntranslatedPortAddress; status = STATUS_UNSUCCESSFUL; for (socket = 0; socket < 0xFF; socket += 0x40) { dataByte = PcicReadController(port, socket, PCIC_IDENT); revisionByte = dataByte; switch (dataByte) { case PCIC_REVISION: case PCIC_REVISION2: case PCIC_REVISION3: { dataByte = PcicReadController(port, socket, PCIC_CARD_CHANGE); if (dataByte & 0xf0) { // // Not a socket. // continue; } // // Check for IBM 750 // if (socket & 0x80) { ULONG i; UCHAR tmp; // // See if this socket shadows the socket without // the sign bit. // tmp = PcicReadController(port, socket, PCIC_MEM_ADD4_STRT_L); for (i = 0; i < 8; i++) { // // See if memory window 4 is the same on both sockets // if (PcicReadController(port, socket, (UCHAR) (PCIC_MEM_ADD4_STRT_L + i)) != PcicReadController(port, (USHORT) (socket & 0x7f), (UCHAR) (PCIC_MEM_ADD4_STRT_L + i))) { break; } } if (i == 8) { // // Currently window is the same - change the // window at one of the socket offsets. // PcicWriteController(port, (USHORT) (socket & 0x7f), PCIC_MEM_ADD4_STRT_L, (UCHAR) ~tmp); if (PcicReadController(port, socket, PCIC_MEM_ADD4_STRT_L) == (UCHAR) ~tmp) { // // The sockets are the same. // continue; } else { PcicWriteController(port, (USHORT) (socket & 0x7f), PCIC_MEM_ADD4_STRT_L, tmp); } } } socketPtr = ExAllocatePool(NonPagedPool, sizeof(SOCKET)); if (!socketPtr) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(socketPtr, sizeof(SOCKET)); socketPtr->DeviceExtension = DeviceExtension; socketPtr->SocketFnPtr = &PcicSupportFns; socketPtr->RegisterOffset = socket; socketPtr->AddressPort = port; socketPtr->Revision = revisionByte; socketPtr->SocketNumber = socketNumber++; if (previousSocket) { previousSocket->NextSocket = socketPtr; } else { DeviceExtension->SocketList = socketPtr; } previousSocket = socketPtr; status = STATUS_SUCCESS; break; } default: { break; } } } return status; }