windows-nt/Source/XPSP1/NT/base/busdrv/pccard/pcmcibus/cb.c
2020-09-26 16:20:57 +08:00

1245 lines
28 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
cb.c
Abstract:
This module contains the code that contains
generic (Yenta compliant) cardbus controller
specific initialization and other dispatches
Author:
Ravisankar Pudipeddi (ravisp) 1-Nov-97
Neil Sandlin (neilsa) June 1 1999
Environment:
Kernel mode
Revision History :
Neil Sandlin (neilsa) 3-Mar-99
new setpower routine interface
--*/
#include "pch.h"
//
// Function Prototypes
//
BOOLEAN
CBInitializePcmciaSocket(
PSOCKET Socket
);
NTSTATUS
CBResetCard(
PSOCKET Socket,
PULONG pDelayTime
);
BOOLEAN
CBDetectCardInSocket(
IN PSOCKET Socket
);
BOOLEAN
CBDetectCardChanged(
IN PSOCKET Socket
);
BOOLEAN
CBDetectCardStatus(
IN PSOCKET Socket
);
BOOLEAN
CBDetectReadyChanged(
IN PSOCKET Socket
);
NTSTATUS
CBGetPowerRequirements(
IN PSOCKET Socket
);
BOOLEAN
CBProcessConfigureRequest(
IN PSOCKET Socket,
IN PVOID ConfigRequest,
IN PUCHAR Base
);
BOOLEAN
CBEnableDisableCardDetectEvent(
IN PSOCKET Socket,
IN BOOLEAN Enable
);
ULONG
CBGetIrqMask(
IN PFDO_EXTENSION DeviceExtension
);
ULONG
CBReadCardMemory(
IN PPDO_EXTENSION PdoExtension,
IN MEMORY_SPACE MemorySpace,
IN ULONG Offset,
IN PUCHAR Buffer,
IN ULONG Length
);
ULONG
CBWriteCardMemory(
IN PPDO_EXTENSION PdoExtension,
IN MEMORY_SPACE MemorySpace,
IN ULONG Offset,
IN PUCHAR Buffer,
IN ULONG Length
);
VOID
CBEnableDisableWakeupEvent(
IN PSOCKET Socket,
IN PPDO_EXTENSION PdoExtension,
IN BOOLEAN Enable
);
BOOLEAN
CBModifyMemoryWindow(
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
);
BOOLEAN
CBSetVpp(
IN PDEVICE_OBJECT Pdo,
IN UCHAR Vpp
);
BOOLEAN
CBIsWriteProtected(
IN PDEVICE_OBJECT Pdo
);
//
// Function dispatch data block
//
PCMCIA_CTRL_BLOCK CBSupportFns = {
CBInitializePcmciaSocket,
CBResetCard,
CBDetectCardInSocket,
CBDetectCardChanged,
CBDetectCardStatus,
CBDetectReadyChanged,
CBGetPowerRequirements,
CBProcessConfigureRequest,
CBEnableDisableCardDetectEvent,
CBEnableDisableWakeupEvent,
CBGetIrqMask,
CBReadCardMemory,
CBWriteCardMemory,
CBModifyMemoryWindow,
CBSetVpp,
CBIsWriteProtected
};
extern PCMCIA_CTRL_BLOCK PcicSupportFns;
//
// Support functions
//
NTSTATUS
CBBuildSocketList(
IN PFDO_EXTENSION FdoExtension
)
/*++
Routine Description:
This routine builds the socket list for the given FDO. This is very
simple for cardbus since there is always only 1 socket per controller.
Arguments:
FdoExtension - device extension for the controller
Return Value:
ntstatus
--*/
{
PSOCKET socket = NULL;
socket = ExAllocatePool(NonPagedPool, sizeof(SOCKET));
if (!socket) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(socket, sizeof(SOCKET));
FdoExtension->SocketList = socket;
socket->DeviceExtension = FdoExtension;
socket->SocketFnPtr = &CBSupportFns;
return STATUS_SUCCESS;
}
BOOLEAN
CBInitializePcmciaSocket(
PSOCKET Socket
)
/*++
Routine Description:
This routine will setup the controller 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 reg;
//
// Initialize exca registers
//
if (!PcicSupportFns.PCBInitializePcmciaSocket(Socket)) {
return FALSE;
}
//
// Clear pending events
//
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_EVENT_REG, 0x0000000F);
//
// Since we may have just powered up, do a cvstest to make sure the socket registers
// are valid
//
if (CBDetectCardInSocket(Socket) &&
!IsDeviceFlagSet(Socket->DeviceExtension, PCMCIA_FDO_ON_DEBUG_PATH)) {
CBIssueCvsTest(Socket);
}
return TRUE;
}
VOID
CBIssueCvsTest(
IN PSOCKET Socket
)
/*++
Routine Description:
This routine forces the controller to reinterrogate the card type and voltage
requirements. This is to insure correct values read from the socket registers.
Arguments:
Socket - socket specific information
Return Value:
none
--*/
{
ULONG dwSktMask;
//
// Issue CVSTEST to interrogate card
// Disable interrupt temporarily because TI 12xx could cause spurious
// interrupt when playing with SktForce register.
//
dwSktMask = CBReadSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG);
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG, 0);
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_FORCE_EVENT_REG, SKTFORCE_CVSTEST);
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG, dwSktMask);
// it would be nice to figure out a cleaner way to determine when interrogation is complete
PcmciaWait(300000);
}
BOOLEAN
CBEnableDeviceInterruptRouting(
IN PSOCKET Socket
)
/*++
Routine Description:
Arguments:
Socket - socket specific information
Return Value:
FALSE - irq to PCI
TRUE - route to ISA
--*/
{
USHORT word, orig_word;
//
// set up IRQ routing
//
GetPciConfigSpace(Socket->DeviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
orig_word = word;
if (IsCardBusCardInSocket(Socket) ||
(Is16BitCardInSocket(Socket) && IsSocketFlagSet(Socket, SOCKET_CB_ROUTE_R2_TO_PCI))) {
//
// route to PCI
//
word &= ~BCTRL_IRQROUTING_ENABLE;
} else {
//
// route to ISA
//
word |= BCTRL_IRQROUTING_ENABLE;
}
if (orig_word != word) {
SetPciConfigSpace(Socket->DeviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
}
// return TRUE for routing to ISA
return ((word & BCTRL_IRQROUTING_ENABLE) == BCTRL_IRQROUTING_ENABLE);
}
NTSTATUS
CBResetCard(
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 = STATUS_MORE_PROCESSING_REQUIRED;
UCHAR byte;
USHORT word;
PFDO_EXTENSION deviceExtension=Socket->DeviceExtension;
if (Is16BitCardInSocket(Socket)) {
if (Socket->CardResetPhase == 2) {
GetPciConfigSpace(deviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
//
// R2 card. Turn off write posting
//
word &= ~BCTRL_WRITE_POSTING_ENABLE;
SetPciConfigSpace(deviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
}
status = PcicSupportFns.PCBResetCard(Socket, pDelayTime);
return status;
}
switch(Socket->CardResetPhase) {
case 1:
//
// Reset via bridge control
//
GetPciConfigSpace(deviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
word |= BCTRL_CRST;
SetPciConfigSpace(deviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
Socket->PowerData = (ULONG) word;
*pDelayTime = CBResetWidthDelay;
break;
case 2:
word = (USHORT)Socket->PowerData;
word &= ~BCTRL_CRST;
//
// CardBus card. Turn on write posting
//
word |= BCTRL_WRITE_POSTING_ENABLE;
word &= ~BCTRL_IRQROUTING_ENABLE;
//
// Hack: turn of write posting for topic95 to avoid hardware
// bug with intel NICs
//
if (deviceExtension->ControllerType == PcmciaTopic95) {
word &= ~BCTRL_WRITE_POSTING_ENABLE;
}
//
// Stop bridge control reset
//
SetPciConfigSpace(deviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
*pDelayTime = CBResetSetupDelay;
break;
case 3:
status = STATUS_SUCCESS;
break;
default:
ASSERT(FALSE);
status = STATUS_UNSUCCESSFUL;
}
return status;
}
BOOLEAN
CBDetectCardInSocket(
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.
--*/
{
ULONG state;
BOOLEAN cardPresent=FALSE;
//
// Read the CARDBUS status register to see if the card is in there.
//
state = CBReadSocketRegister(Socket, CARDBUS_SOCKET_PRESENT_STATE_REG);
if ((state & SKTSTATE_CCD_MASK) == 0) {
cardPresent = TRUE;
}
return(cardPresent);
}
BOOLEAN
CBDetectCardChanged(
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.
--*/
{
BOOLEAN retVal = FALSE;
ULONG tmp;
//
// Read SOCKET Event register to see if CD's changed
//
tmp = CBReadSocketRegister(Socket, CARDBUS_SOCKET_EVENT_REG);
if ((tmp & SKTEVENT_CCD_MASK) != 0) {
//
// Yes they did..
// first clear the interrupt
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_EVENT_REG, SKTEVENT_CCD_MASK);
retVal = TRUE;
}
return retVal;
}
BOOLEAN
CBDetectCardStatus(
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.
--*/
{
BOOLEAN retVal = FALSE;
ULONG tmp;
if (Is16BitCardInSocket(Socket)) {
// NOTE: UNIMPLEMENTED: may need to do something for 16-bit cards
return FALSE;
}
//
// Read SOCKET Event register to see if CD's changed
//
tmp = CBReadSocketRegister(Socket, CARDBUS_SOCKET_EVENT_REG);
if ((tmp & SKTEVENT_CSTSCHG) != 0) {
//
// Yes they did..
// first clear the interrupt
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_EVENT_REG, SKTEVENT_CSTSCHG);
retVal = TRUE;
}
return retVal;
}
BOOLEAN
CBDetectReadyChanged(
IN PSOCKET Socket
)
{
return(PcicSupportFns.PCBDetectReadyChanged(Socket));
}
BOOLEAN
CBProcessConfigureRequest(
IN PSOCKET Socket,
IN PCARD_REQUEST Request,
IN PUCHAR Base
)
{
BOOLEAN bStatus = TRUE;
USHORT word;
//
// Shouldn't this check for 16-bit cards?
//
switch (Request->RequestType) {
case IRQ_REQUEST:
if (CBEnableDeviceInterruptRouting(Socket)) {
bStatus = PcicSupportFns.PCBProcessConfigureRequest(Socket, Request, Base);
}
break;
case DECONFIGURE_REQUEST:
bStatus = PcicSupportFns.PCBProcessConfigureRequest(Socket, Request, Base);
GetPciConfigSpace(Socket->DeviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
word |= BCTRL_IRQROUTING_ENABLE;
SetPciConfigSpace(Socket->DeviceExtension, CFGSPACE_BRIDGE_CTRL, &word, 2);
break;
default:
bStatus = PcicSupportFns.PCBProcessConfigureRequest(Socket, Request, Base);
}
return bStatus;
}
BOOLEAN
CBEnableDisableCardDetectEvent(
IN PSOCKET Socket,
IN BOOLEAN Enable
)
/*++
Routine Description:
Enable card detect/card ready interrupt.
Arguments:
Socket - socket information
Enable - if TRUE, CSC interrupt is enabled,
if FALSE, it is disabled
Return Value:
TRUE if successful
FALSE if not successful
--*/
{
switch (Enable) {
case TRUE: {
UCHAR byte;
//
// Only if TI 1130/1250?
// Route through PCI interrupts
byte = PcicReadSocket(Socket, PCIC_INTERRUPT);
byte |= IGC_INTR_ENABLE;
PcicWriteSocket(Socket, PCIC_INTERRUPT, byte);
//
// Clear the bits in Socket Event Register
//
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_EVENT_REG, 0xF);
//
// Enable card-detect interrupt in Socket Mask Register
//
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG, SKTMSK_CCD);
break;
}
case FALSE: {
ULONG oldValue;
//
// Clear the bits in Socket Event Register
//
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_EVENT_REG, 0xF);
//
// Disable card-detect interrupt in Socket Mask Register
//
oldValue = CBReadSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG);
oldValue &= ~SKTMSK_CCD;
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG, oldValue);
break;
}
}
return TRUE;
}
VOID
CBEnableDisableWakeupEvent(
IN PSOCKET Socket,
IN PPDO_EXTENSION PdoExtension,
IN BOOLEAN Enable
)
/*++
Routine Description:
Arguments:
Socket - socket information
Enable - if TRUE, interrupt is enabled,
if FALSE, it is disabled
Return Value:
none
--*/
{
ULONG dwValue;
switch (Enable) {
case TRUE: {
//
// Enable card-status interrupt in Socket Mask Register
//
dwValue = CBReadSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG);
dwValue |= SKTMSK_CSTSCHG;
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG, dwValue);
if (PdoExtension && IsCardBusCard(PdoExtension)) {
UCHAR capptr;
ULONG powercaps;
//
// HACK ALERT - should be handled by PCI.SYS
// Have a look to see if PME_ENABLE has been turned on by PCI. If not then we do it.
//
GetPciConfigSpace(PdoExtension, CBCFG_CAPPTR, &capptr, sizeof(capptr));
if (capptr) {
GetPciConfigSpace(PdoExtension, capptr, &powercaps, sizeof(powercaps));
if ((powercaps & 0xff) == 1) {
GetPciConfigSpace(PdoExtension, capptr+4, &powercaps, sizeof(powercaps));
if (!(powercaps & PME_EN)) {
powercaps |= PME_EN;
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x setting PME_EN!\n", PdoExtension->DeviceObject));
SetPciConfigSpace(PdoExtension, capptr+4, &powercaps, sizeof(powercaps));
}
}
}
}
break;
}
case FALSE: {
PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
UCHAR capptr;
ULONG powercaps, newPowercaps;
//
// Check to see if PMESTAT is on... It shouldn't be. If it is, it probably means
// that the BIOS did not notify us that the device did the wake, and PCI didn't
// get a chance to clear the condition. This is really a BIOS bug.
//
if (PdoExtension && IsCardBusCard(PdoExtension)) {
GetPciConfigSpace(PdoExtension, CBCFG_CAPPTR, &capptr, sizeof(capptr));
if (capptr) {
GetPciConfigSpace(PdoExtension, capptr, &powercaps, sizeof(powercaps));
if ((powercaps & 0xff) == 1) {
GetPciConfigSpace(PdoExtension, capptr+4, &powercaps, sizeof(powercaps));
if (powercaps & (PME_STAT | PME_EN)) {
DebugPrint((PCMCIA_DEBUG_POWER, "pdo %08x PME bits still set! stat=%x en=%x\n",
PdoExtension->DeviceObject, ((powercaps&PME_STAT)!=0), ((powercaps&PME_EN)!=0)));
powercaps |= PME_STAT;
powercaps &= ~PME_EN;
SetPciConfigSpace(PdoExtension, capptr+4, &powercaps, sizeof(powercaps));
}
}
}
}
GetPciConfigSpace(fdoExtension, CFGSPACE_CAPPTR, &capptr, sizeof(capptr));
if (capptr) {
GetPciConfigSpace(fdoExtension, capptr, &powercaps, sizeof(powercaps));
if ((powercaps & 0xff) == 1) {
//
// Clear PMESTAT, if on
//
GetPciConfigSpace(fdoExtension, capptr+4, &powercaps, sizeof(powercaps));
if (powercaps & PME_STAT) {
DebugPrint((PCMCIA_DEBUG_POWER, "fdo %08x PME_STAT still set!\n", fdoExtension->DeviceObject));
SetPciConfigSpace(fdoExtension, capptr+4, &powercaps, sizeof(powercaps));
}
}
}
//
// Disable card-status interrupt in Socket Mask Register
//
dwValue = CBReadSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG);
dwValue &= ~SKTMSK_CSTSCHG;
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG, dwValue);
//
// Clear the event in Socket Event Register
//
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_EVENT_REG, SKTEVENT_CSTSCHG);
break;
}
}
}
ULONG
CBGetIrqMask(
IN PFDO_EXTENSION DeviceExtension
)
{
return(PcicSupportFns.PCBGetIrqMask(DeviceExtension));
}
ULONG
CBReadCardMemory(
IN PPDO_EXTENSION PdoExtension,
IN MEMORY_SPACE MemorySpace,
IN ULONG Offset,
IN PUCHAR Buffer,
IN ULONG Length
)
{
ULONG bytesCopied = 0;
if (!IsCardBusCard(PdoExtension)) {
return(PcicSupportFns.PCBReadCardMemory(PdoExtension,
MemorySpace,
Offset,
Buffer,
Length));
}
switch(MemorySpace){
case PCCARD_PCI_CONFIGURATION_SPACE:
bytesCopied = GetPciConfigSpace(PdoExtension, Offset, Buffer, Length);
break;
case PCCARD_CARDBUS_BAR0:
case PCCARD_CARDBUS_BAR1:
case PCCARD_CARDBUS_BAR2:
case PCCARD_CARDBUS_BAR3:
case PCCARD_CARDBUS_BAR4:
case PCCARD_CARDBUS_BAR5:
break;
case PCCARD_CARDBUS_ROM:
bytesCopied = PdoExtension->PciBusInterface.GetBusData(
PdoExtension->PciBusInterface.Context,
PCI_WHICHSPACE_ROM, Buffer, Offset, Length);
break;
}
return bytesCopied;
}
ULONG
CBWriteCardMemory(
IN PPDO_EXTENSION PdoExtension,
IN MEMORY_SPACE MemorySpace,
IN ULONG Offset,
IN PUCHAR Buffer,
IN ULONG Length
)
{
if (IsCardBusCard(PdoExtension)) {
return 0;
}
return(PcicSupportFns.PCBWriteCardMemory(PdoExtension,
MemorySpace,
Offset,
Buffer,
Length));
}
BOOLEAN
CBModifyMemoryWindow(
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
)
{
return(PcicSupportFns.PCBModifyMemoryWindow(Pdo,
HostBase,
CardBase,
Enable,
WindowSize,
AccessSpeed,
BusWidth,
IsAttributeMemory));
}
BOOLEAN
CBSetVpp(
IN PDEVICE_OBJECT Pdo,
IN UCHAR Vpp
)
{
return(PcicSupportFns.PCBSetVpp(Pdo, Vpp));
}
BOOLEAN
CBIsWriteProtected(
IN PDEVICE_OBJECT Pdo
)
{
return(PcicSupportFns.PCBIsWriteProtected(Pdo));
}
NTSTATUS
CBGetPowerRequirements(
IN PSOCKET Socket
)
/*++
Routine Description:
Look at the hardware to see what it says the card needs, and update the
socket structure accordingly.
Arguments:
Socket - the socket to examine
Return Value:
n/a
--*/
{
ULONG state;
UCHAR voltage;
//
// Check what voltages are supported by this card
//
state = CBReadSocketRegister(Socket, CARDBUS_SOCKET_PRESENT_STATE_REG);
if (!(state & (SKTSTATE_5VCARD | SKTSTATE_3VCARD))) {
ULONG dwSktMask;
//
// neither 5v or 3v is set... try cvstest
// Disable interrupt temporarily because TI 12xx could cause spurious
// interrupt when playing with SktForce register.
//
dwSktMask = CBReadSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG);
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG, 0);
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_FORCE_EVENT_REG, SKTFORCE_CVSTEST);
state = CBReadSocketRegister(Socket, CARDBUS_SOCKET_PRESENT_STATE_REG);
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_MASK_REG, dwSktMask);
}
state &= (SKTSTATE_5VCARD | SKTSTATE_3VCARD);
if (state == 0) {
return STATUS_UNSUCCESSFUL;
}
if (state == (SKTSTATE_5VCARD | SKTSTATE_3VCARD)) {
//
// both are specified. Check for preference
//
voltage = IsDeviceFlagSet(Socket->DeviceExtension, PCMCIA_FDO_PREFER_3V) ? 33 : 50;
} else {
voltage = (state & SKTSTATE_5VCARD) ? 50 : 33;
}
Socket->Vcc = Socket->Vpp1 = Socket->Vpp2 = voltage;
return STATUS_SUCCESS;
}
NTSTATUS
CBSetPower(
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;
ULONG oldPower, state, newPower;
ULONG vcc, vpp;
UCHAR tmp;
USHORT word;
switch(Socket->PowerPhase) {
case 1:
if (Enable) {
//
// Turn on the power
//
switch(Socket->Vcc) {
case 50: vcc = SKTPOWER_VCC_050V; break;
case 33: vcc = SKTPOWER_VCC_033V; break;
default: vcc = SKTPOWER_VCC_OFF;
}
switch(Socket->Vpp1) {
case 120: vpp = SKTPOWER_VPP_120V; break;
case 50: vpp = SKTPOWER_VPP_050V; break;
case 33: vpp = SKTPOWER_VPP_033V; break;
default: vpp = SKTPOWER_VPP_OFF;
}
} else {
//
// Power off
//
vcc = SKTPOWER_VCC_OFF;
vpp = SKTPOWER_VPP_OFF;
//
// Disable output before powering down to avoid spurious signals
// from reaching the card
//
if (Is16BitCardInSocket(Socket)) {
tmp = PcicReadSocket(Socket, PCIC_PWR_RST);
if (tmp & PC_OUTPUT_ENABLE) {
tmp &= ~PC_OUTPUT_ENABLE;
PcicWriteSocket(Socket, PCIC_PWR_RST, tmp);
}
}
}
oldPower = CBReadSocketRegister(Socket, CARDBUS_SOCKET_CONTROL_REG);
newPower = vcc | vpp;
newPower|= oldPower & ~(SKTPOWER_VPP_CONTROL |SKTPOWER_VCC_CONTROL);
if (newPower != oldPower) {
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_CONTROL_REG, newPower);
//
// When power is enabled always stall to give the PCCARD
// a chance to react.
//
*pDelayTime = PcicStallPower;
Socket->PowerData = newPower;
status = STATUS_MORE_PROCESSING_REQUIRED;
} else {
//
// Indicate that nothing was done
//
status = STATUS_INVALID_DEVICE_STATE;
}
break;
case 2:
case 3:
case 4:
newPower = Socket->PowerData;
//
// Try to apply the required power setting a few times.
// We bail if it doesn't succeed after the given number of tries
//
state = CBReadSocketRegister(Socket, CARDBUS_SOCKET_PRESENT_STATE_REG);
if (state & SKTSTATE_BADVCCREQ) {
DebugPrint((PCMCIA_DEBUG_INFO, "skt %08 CBSetPower: Bad vcc request\n", Socket));
//
// Clear the status bits & try again
//
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_FORCE_EVENT_REG, 0);
CBWriteSocketRegister(Socket, CARDBUS_SOCKET_CONTROL_REG, newPower);
*pDelayTime = PcicStallPower;
status = STATUS_MORE_PROCESSING_REQUIRED;
} else {
status = STATUS_SUCCESS;
if (Is16BitCardInSocket(Socket)) {
tmp = PcicReadSocket(Socket, PCIC_PWR_RST);
if (newPower & SKTPOWER_VCC_CONTROL) {
//
// Vcc is on..
//
tmp |= PC_OUTPUT_ENABLE | PC_AUTOPWR_ENABLE;
PcicWriteSocket(Socket, PCIC_PWR_RST, tmp);
*pDelayTime = PcicStallPower;
} else {
//
// power off..
//
tmp &= ~(PC_OUTPUT_ENABLE | PC_AUTOPWR_ENABLE);
PcicWriteSocket(Socket, PCIC_PWR_RST, tmp);
}
}
}
break;
default:
DebugPrint((PCMCIA_DEBUG_FAIL, "skt %08 CBSetPower: Final retry failed - bad vcc\n", Socket));
ASSERT(FALSE);
status = STATUS_UNSUCCESSFUL;
}
return status;
}
BOOLEAN
CBSetWindowPage(
IN PSOCKET Socket,
USHORT Index,
UCHAR Page
)
{
ASSERT(Index <= 4);
PcicWriteSocket(Socket, (UCHAR) (PCIC_PAGE_REG + Index), Page);
return TRUE;
}
ULONG
CBReadSocketRegister(
IN PSOCKET Socket,
IN UCHAR Register
)
/*++
Routine Description
Returns the contents of the specified Cardbus socket register for the
given socket
Arguments
Socket - Pointer to the socket
Register - Cardbus socket register
Return Value
Contents of the register
--*/
{
ULONG data;
ASSERT (CardBus(Socket));
ASSERT (Socket->DeviceExtension->CardBusSocketRegisterBase != NULL);
ASSERT ((Register&3) == 0);
//
// Sanity check in case controller wasn't started
// or if the register is not dword aligned
//
if ((Socket->DeviceExtension->CardBusSocketRegisterBase) && ((Register&3) == 0)) {
data = READ_REGISTER_ULONG((PULONG) (Socket->DeviceExtension->CardBusSocketRegisterBase+Register));
} else {
data = 0xFFFFFFFF;
}
return data;
}
VOID
CBWriteSocketRegister(
IN PSOCKET Socket,
IN UCHAR Register,
IN ULONG Data
)
/*++
Routine Description
Writes the supplied value to the Cardbus socket register for the
given socket
Arguments
Socket - Pointer to the socket
Register - Cardbus socket register
Data - Value to be written to the register
Return Value
--*/
{
ASSERT (CardBus(Socket));
ASSERT (Socket->DeviceExtension->CardBusSocketRegisterBase != NULL);
ASSERT ((Register&3) == 0);
//
// Sanity check in case controller wasn't started
// or if the register is not dword aligned
//
if ((Socket->DeviceExtension->CardBusSocketRegisterBase) && ((Register&3) == 0)) {
WRITE_REGISTER_ULONG((PULONG) (Socket->DeviceExtension->CardBusSocketRegisterBase+Register), Data);
}
}