windows-nt/Source/XPSP1/NT/base/boot/detect/i386/pccardc.c

895 lines
22 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
pccardc.c
Abstract:
This module contains the C code to set up PcCard (pcmcia, cardbus)
configuration data.
Author:
Neil Sandlin (neilsa) 16-Dec-1998
(DetectIRQMap, ToggleIRQLine were copied from win9x)
Revision History:
--*/
#include "hwdetect.h"
#include "pccard.h"
#include <string.h>
extern UCHAR DisablePccardIrqScan;
extern BOOLEAN SystemHas8259;
extern BOOLEAN SystemHas8253;
CARDBUS_BRIDGE_DEVTYPE CBTable[] = {
{0x11101013, DEVTYPE_CL_PD6832},
{0x11121013, DEVTYPE_CL_PD6834},
{0x11111013, DEVTYPE_CL_PD6833},
{0xAC12104C, DEVTYPE_TI_PCI1130},
{0xAC15104C, DEVTYPE_TI_PCI1131},
{0xAC13104C, DEVTYPE_TI_PCI1031},
{0,0}};
FPFWCONFIGURATION_COMPONENT_DATA ControllerList = NULL;
#define LEGACY_BASE_LIST_SIZE 10
USHORT LegacyBaseList[LEGACY_BASE_LIST_SIZE] = {0};
USHORT LegacyBaseListCount = 0;
VOID
SetPcCardConfigurationData(
PPCCARD_INFORMATION PcCardInfo
)
/*++
Routine Description:
This routine creates a structure containing the result of the
irq detection, and links it onto our running list. This list
eventually will show up in the registry under hardware
descriptions.
Arguments:
PcCardInfo - Structure containing the results of detection
Returns:
None.
--*/
{
FPFWCONFIGURATION_COMPONENT_DATA CurrentEntry;
static FPFWCONFIGURATION_COMPONENT_DATA PreviousEntry = NULL;
FPFWCONFIGURATION_COMPONENT Component;
FPHWRESOURCE_DESCRIPTOR_LIST DescriptorList;
CHAR Identifier[32];
FPCHAR IdentifierString;
USHORT Length;
CM_PCCARD_DEVICE_DATA far *PcCardData;
CurrentEntry = (FPFWCONFIGURATION_COMPONENT_DATA)HwAllocateHeap (
sizeof(FWCONFIGURATION_COMPONENT_DATA), TRUE);
if (!ControllerList) {
ControllerList = CurrentEntry;
}
Component = &CurrentEntry->ComponentEntry;
Component->Class = ControllerClass;
Component->Type = OtherController;
strcpy (Identifier, "PcCardController");
Length = strlen(Identifier) + 1;
IdentifierString = (FPCHAR)HwAllocateHeap(Length, FALSE);
_fstrcpy(IdentifierString, Identifier);
Component->IdentifierLength = Length;
Component->Identifier = IdentifierString;
Length = sizeof(HWRESOURCE_DESCRIPTOR_LIST) + sizeof(CM_PCCARD_DEVICE_DATA);
DescriptorList = (FPHWRESOURCE_DESCRIPTOR_LIST)HwAllocateHeap(
Length,
TRUE);
CurrentEntry->ConfigurationData = DescriptorList;
Component->ConfigurationDataLength = Length;
DescriptorList->Count = 1;
DescriptorList->PartialDescriptors[0].Type = RESOURCE_DEVICE_DATA;
DescriptorList->PartialDescriptors[0].u.DeviceSpecificData.DataSize =
sizeof(CM_PCCARD_DEVICE_DATA);
PcCardData = (CM_PCCARD_DEVICE_DATA far *)(DescriptorList + 1);
PcCardData->Flags = PcCardInfo->Flags;
PcCardData->ErrorCode = PcCardInfo->ErrorCode;
PcCardData->DeviceId = PcCardInfo->DeviceId;
PcCardData->LegacyBaseAddress = (ULONG) PcCardInfo->IoBase;
if (PcCardInfo->Flags & PCCARD_DEVICE_PCI) {
PcCardData->BusData = PcCardInfo->PciCfg1.u.bits.BusNumber |
PcCardInfo->PciCfg1.u.bits.DeviceNumber << 8 |
PcCardInfo->PciCfg1.u.bits.FunctionNumber << 16;
}
_fmemcpy(PcCardData->IRQMap, PcCardInfo->abIRQMap, 16);
if (PreviousEntry) {
PreviousEntry->Sibling = CurrentEntry;
}
PreviousEntry = CurrentEntry;
}
BOOLEAN
IsOnLegacyBaseList(
USHORT IoBase
)
/*++
Routine Description:
This routine runs our list of legacy base addresses to see if we
have looked at the address before.
Arguments:
IoBase = base address to map
Returns:
TRUE if the base address is already on the list
--*/
{
USHORT i;
for (i = 0; i<LegacyBaseListCount; i++) {
if (IoBase == LegacyBaseList[i]) {
return TRUE;
}
}
return FALSE;
}
BOOLEAN
SetLegacyBaseList(
USHORT IoBase
)
/*++
Routine Description:
This routine remembers the legacy base addresses that we have looked
at so far so we don't keep mapping the same address.
NOTE: We are using a DUMB mechanism that only builds the list in a
fixed array. We could write some generic code which creates
a linked list, but since the heap routines in ntdetect are also
dumb, it makes it not possible to free the list. It's just not worth
it.
Arguments:
IoBase = base address to map
Returns:
TRUE if the base address is unique to this point
FALSE if the base address already exists on the list
--*/
{
if (IsOnLegacyBaseList(IoBase)) {
return FALSE;
}
if (LegacyBaseListCount < LEGACY_BASE_LIST_SIZE) {
LegacyBaseList[LegacyBaseListCount++] = IoBase;
}
// note, we return true even if we overflow the list
return TRUE;
}
VOID
MapPcCardController(
PPCCARD_INFORMATION PcCardInfo
)
/*++
Routine Description:
This routine is the entry for doing ISA IRQ detection for PcCard
controllers.
Arguments:
PcCardInfo - Structure defining the device to run detection on
Returns:
None.
--*/
{
USHORT wDetected;
USHORT i;
PcCardInfo->ErrorCode = 0;
for (i=0; i<16; i++) {
PcCardInfo->abIRQMap[i]=0;
}
if (!PcCardInfo->IoBase) {
PcCardInfo->Flags |= PCCARD_MAP_ERROR;
PcCardInfo->ErrorCode = PCCARD_NO_LEGACY_BASE;
} else if (!SetLegacyBaseList(PcCardInfo->IoBase)) {
PcCardInfo->Flags |= PCCARD_MAP_ERROR;
PcCardInfo->ErrorCode = PCCARD_DUP_LEGACY_BASE;
}
if (!(PcCardInfo->Flags & PCCARD_MAP_ERROR)) {
PcCardInfo->wValidIRQs = PCCARD_POSSIBLE_IRQS;
#if DBG
BlPrint("Going to detect...\n");
#endif
//
// Do the IRQ detection
//
wDetected = DetectIRQMap(PcCardInfo);
#if DBG
BlPrint("Detect IRQ Map returns %x on iobase %x\n", wDetected, PcCardInfo->IoBase);
#endif
if (!wDetected) {
PcCardInfo->ErrorCode = PCCARD_MAP_ZERO;
}
}
#if DBG
if (PcCardInfo->Flags & PCCARD_MAP_ERROR) {
BlPrint("Error mapping device, code=%x\n", PcCardInfo->ErrorCode);
}
#endif
//
// Report the results
//
SetPcCardConfigurationData(PcCardInfo);
}
VOID
LookForPciCardBusBridges(
USHORT BusStart,
USHORT BusEnd,
)
/*++
Routine Description:
This routine is the entry for doing ISA IRQ detection for PCI-based
cardbus controllers.
Arguments:
Bus = PCI Bus number to scan
Returns:
None.
--*/
{
PCCARD_INFORMATION PcCardInfo = {0};
USHORT Device, Function;
UCHAR HeaderType;
UCHAR SecBus, SubBus;
USHORT VendorId;
USHORT DeviceId;
ULONG LegacyBaseAddress;
USHORT i;
USHORT Bus;
#if DBG
BlPrint("LookForPciCardBusBridges %x-%x\n", BusStart, BusEnd);
#endif
for (Bus = BusStart; Bus <= BusEnd; Bus++) {
PcCardInfo.PciCfg1.u.AsULONG = 0;
PcCardInfo.PciCfg1.u.bits.BusNumber = Bus;
PcCardInfo.PciCfg1.u.bits.Enable = TRUE;
for (Device = 0; Device < PCI_MAX_DEVICES; Device++) {
PcCardInfo.PciCfg1.u.bits.DeviceNumber = Device;
for (Function = 0; Function < PCI_MAX_FUNCTION; Function++) {
PcCardInfo.PciCfg1.u.bits.FunctionNumber = Function;
VendorId = 0xffff;
GetPciConfigSpace(&PcCardInfo, CFGSPACE_VENDOR_ID, &VendorId, sizeof(VendorId));
if ((VendorId == 0xffff) || (VendorId == 0)) {
if (Function == 0) {
break;
} else {
continue;
}
}
GetPciConfigSpace(&PcCardInfo, CFGSPACE_DEVICE_ID, &DeviceId, sizeof(DeviceId));
GetPciConfigSpace(&PcCardInfo, CFGSPACE_HEADER_TYPE, &HeaderType, sizeof(HeaderType));
switch(HeaderType & 0x7f) {
case PCI_CARDBUS_BRIDGE_TYPE:
#if DBG
BlPrint("%x.%x.%x : DeviceID = %lx (CardBus Bridge)\n", Bus, Device, Function, DeviceId);
#endif
PcCardInfo.DeviceId = (ULONG) (VendorId << 16) | DeviceId;
PcCardInfo.Flags = PCCARD_DEVICE_PCI;
//
// See if this is a special cased controller
//
PcCardInfo.bDevType = DEVTYPE_GENERIC_CARDBUS;
i = 0;
while (CBTable[i].DeviceId != 0) {
if (DeviceId == CBTable[i].DeviceId) {
PcCardInfo.bDevType = CBTable[i].bDevType;
break;
}
i++;
}
GetPciConfigSpace(&PcCardInfo, CFGSPACE_LEGACY_MODE_BASE_ADDR, &LegacyBaseAddress, 4);
PcCardInfo.IoBase = (USHORT) (LegacyBaseAddress & ~1);
MapPcCardController(&PcCardInfo);
break;
case PCI_BRIDGE_TYPE:
#if DBG
BlPrint("%x.%x.%x : DeviceID = %lx (Pci-Pci Bridge)\n", Bus, Device, Function, DeviceId);
#endif
GetPciConfigSpace(&PcCardInfo, CFGSPACE_SECONDARY_BUS, &SecBus, sizeof(SecBus));
GetPciConfigSpace(&PcCardInfo, CFGSPACE_SUBORDINATE_BUS, &SubBus, sizeof(SubBus));
if ((SecBus <= Bus) || (SubBus <= Bus) || (SubBus < SecBus)) {
break;
}
//
// Be conservative on stack space, only look one level deep
//
if (Bus > 0) {
break;
}
LookForPciCardBusBridges(SecBus, SubBus);
break;
}
}
}
}
}
VOID
LookForPcicControllers(
VOID
)
/*++
Routine Description:
This routine is the entry for doing ISA IRQ detection for PCIC
controllers.
Arguments:
None.
Returns:
None.
--*/
{
PCCARD_INFORMATION PcCardInfo = {0};
USHORT IoBase;
UCHAR id;
for (IoBase = 0x3e0; IoBase < 0x3e6; IoBase+=2) {
if (IsOnLegacyBaseList(IoBase)) {
continue;
}
PcCardInfo.Flags = 0;
PcCardInfo.IoBase = IoBase;
PcCardInfo.bDevType = DEVTYPE_GENERIC_PCIC;
id = PcicReadSocket(&PcCardInfo, EXCAREG_IDREV);
switch (id) {
case PCIC_REVISION:
case PCIC_REVISION2:
case PCIC_REVISION3:
#if DBG
BlPrint("Pcic Controller at base %x, rev(%x)\n", IoBase, id);
#endif
MapPcCardController(&PcCardInfo);
break;
#if DBG
default:
BlPrint("Not mapping base %x, return is (%x)\n", IoBase, id);
#endif
}
}
}
FPFWCONFIGURATION_COMPONENT_DATA
GetPcCardInformation(
VOID
)
/*++
Routine Description:
This routine is the entry for doing ISA IRQ detection for PcCard
controllers.
Arguments:
None.
Returns:
A pointer to a pccard component structure, if IRQ's were properly detected.
Otherwise a NULL pointer is returned.
--*/
{
PCCARD_INFORMATION PcCardInfo = {0};
UCHAR ErrorCode = 0;
//
// Check for things which would prevent us from attempting
// the irq detection
//
if (DisablePccardIrqScan == 1) {
ErrorCode = PCCARD_SCAN_DISABLED;
} else if (!SystemHas8259) {
ErrorCode = PCCARD_NO_PIC;
} else if (!SystemHas8253) {
ErrorCode = PCCARD_NO_TIMER;
}
//
// If things look ok so far, do the detection
//
if (!ErrorCode) {
#if DBG
BlPrint("press any key to continue...\n");
while ( !HwGetKey() ) ; // wait until key pressed to continue
clrscrn();
BlPrint("Looking for PcCard Controllers...\n");
#endif
//
// Look first for cardbus
//
LookForPciCardBusBridges(0,0);
//
// Now check for regular pcic devices
//
LookForPcicControllers();
#if DBG
BlPrint("press any key to continue...\n");
while ( !HwGetKey() ) ; // wait until key pressed to continue
#endif
if (!ControllerList) {
ErrorCode = PCCARD_NO_CONTROLLERS;
}
}
if (ErrorCode) {
//
// Something when wrong, so write a single entry to
// allow someone to see what the error was
//
PcCardInfo.Flags |= PCCARD_MAP_ERROR;
PcCardInfo.ErrorCode = ErrorCode;
SetPcCardConfigurationData(&PcCardInfo);
}
return ControllerList;
}
USHORT
DetectIRQMap(
PPCCARD_INFORMATION pa
)
/*++
Routine Description:
This routine detects the IRQ mapping of the specified cardbus controller.
Note that the controller is in PCIC mode.
Arguments:
pa -> ADAPTER structure
Returns:
returns detected IRQ bit mask
--*/
{
USHORT wRealIRQMask = 0;
USHORT wData;
UCHAR bData;
BOOLEAN fTINMIBug = FALSE;
UCHAR i;
USHORT wIRQMask, wRealIRQ, w;
if (pa->bDevType == DEVTYPE_CL_PD6832)
{
//enable CSC IRQ routing just for IRQ detection
GetPciConfigSpace(pa, CFGSPACE_BRIDGE_CTRL, &wData, sizeof(wData));
wData |= BCTRL_CL_CSCIRQROUTING_ENABLE;
SetPciConfigSpace(pa, CFGSPACE_BRIDGE_CTRL, &wData, sizeof(wData));
}
else if ((pa->bDevType == DEVTYPE_CL_PD6834) ||
(pa->bDevType == DEVTYPE_CL_PD6833))
{
//enable CSC IRQ routing just for IRQ detection
GetPciConfigSpace(pa, CFGSPACE_CL_CFGMISC1, &bData, sizeof(bData));
bData |= CL_CFGMISC1_ISACSC;
SetPciConfigSpace(pa, CFGSPACE_CL_CFGMISC1, &bData, sizeof(bData));
}
else if ((pa->bDevType == DEVTYPE_TI_PCI1130) ||
(pa->bDevType == DEVTYPE_TI_PCI1131) ||
(pa->bDevType == DEVTYPE_TI_PCI1031))
{
GetPciConfigSpace(pa, CFGSPACE_TI_DEV_CTRL, &wData, sizeof(wData));
if ((wData & DEVCTRL_INTMODE_MASK) == DEVCTRL_INTMODE_COMPAQ)
{
//
// There is an errata on TI 1130, 1131 and 1031 in which if
// the chip is programmed to use serial IRQ mode (i.e. COMPAQ
// mode) and the SERIRQ pin is not pull up with a 1K resistor,
// the SERIRQ line will rise too slowly after IRQ 15 is
// deasserted so that it looks like NMI should be asserted.
// This caused spurious NMI. This is a hardware problem.
// Unfortunately, there are a large number of machines with
// this problem on the street already, so CBSS has to work
// around the problem by temporarily disabling NMI before
// doing ISA IRQ detection.
//
fTINMIBug = TRUE;
_asm in al,SYSCTRL_B
_asm and al,0x0f
_asm push ax
//
// Mask NMI
//
_asm or al,0x08
_asm out SYSCTRL_B,al
}
}
_asm pushf
_asm cli //disable interrupt
_asm in al,PIC2_IMR //save old IMRs
_asm mov ah,al
_asm in al,PIC1_IMR
_asm push ax
_asm mov al,0xff //mask all interrupt
_asm out PIC2_IMR,al
_asm out PIC1_IMR,al
for (i = 0; i < 16; ++i)
{
w = (USHORT)(1 << i);
if ((pa->wValidIRQs & w) &&
((wIRQMask = ToggleIRQLine(pa, i)) != 0))
{
_asm mov dx, wIRQMask
_asm _emit 0x66
_asm _emit 0x0f
_asm _emit 0xbc
_asm _emit 0xc2
_asm mov wRealIRQ,ax
pa->abIRQMap[wRealIRQ] = i;
wRealIRQMask |= (USHORT)(1 << wRealIRQ);
}
}
Clear_IR_Bits(wRealIRQMask);
_asm pop ax
_asm out PIC1_IMR,al
_asm mov al,ah
_asm out PIC2_IMR,al
_asm popf
if (fTINMIBug)
{
//
// Restore NMI mask
//
_asm pop ax
_asm out SYSCTRL_B,al
}
if (pa->bDevType == DEVTYPE_CL_PD6832)
{
//disable CSC IRQ routing (use PCI interrupt for CSC)
GetPciConfigSpace(pa, CFGSPACE_BRIDGE_CTRL, &wData, sizeof(wData));
wData &= ~BCTRL_CL_CSCIRQROUTING_ENABLE;
SetPciConfigSpace(pa, CFGSPACE_BRIDGE_CTRL, &wData, sizeof(wData));
}
else if ((pa->bDevType == DEVTYPE_CL_PD6834) ||
(pa->bDevType == DEVTYPE_CL_PD6833))
{
//disable CSC IRQ routing (use PCI interrupt for CSC)
GetPciConfigSpace(pa, CFGSPACE_CL_CFGMISC1, &bData, sizeof(bData));
bData &= ~CL_CFGMISC1_ISACSC;
SetPciConfigSpace(pa, CFGSPACE_CL_CFGMISC1, &bData, sizeof(bData));
}
return wRealIRQMask;
} //DetectIRQMap
USHORT
ToggleIRQLine(
PPCCARD_INFORMATION pa,
UCHAR bIRQ
)
/*++
Routine Description:
This routine toggles the specified IRQ line from the adapter.
Arguments:
pa -> ADAPTER structure
bIRQ - IRQ line to toggle
Returns:
returns the IRR mask from PIC
--*/
{
UCHAR bOldIntCtrl, bOldIntCfg, bData;
USHORT rc = 0, irr1, irr2, irr3;
bOldIntCfg = PcicReadSocket(pa, EXCAREG_CSC_CFG);
bOldIntCtrl = PcicReadSocket(pa, EXCAREG_INT_GENCTRL);
//Set to a known state
PcicWriteSocket(pa, EXCAREG_INT_GENCTRL, IGC_PCCARD_RESETLO);
//Set irq number in interrupt control register and enable irq
PcicWriteSocket(pa, EXCAREG_CSC_CFG, (UCHAR)((bIRQ << 4) | CSCFG_CD_ENABLE));
//clear all pending interrupts
bData = PcicReadSocket(pa, EXCAREG_CARD_STATUS);
irr1 = GetPICIRR();
if (PcicReadSocket(pa, EXCAREG_IDREV) != 0x82)
{
//This is not an A stepping part, try the undocumented interrupt
//register. If this fails the other routine will be tried.
PcicWriteSocket(pa, EXCAREG_CARDDET_GENCTRL, CDGC_SW_DET_INT);
irr2 = GetPICIRR();
//reset pending interrupt
bData = PcicReadSocket(pa, EXCAREG_CARD_STATUS);
irr3 = GetPICIRR();
rc = (USHORT)((irr1 ^ irr2) & (irr2 ^ irr3));
}
if (rc == 0)
{
//Generate interrupt by de-asserting IRQ line so the PIC can pull it
//high
PcicWriteSocket(pa, EXCAREG_CSC_CFG, 0);
//if (pa->dwfAdapter & AF_TI_SERIALIRQ)
// TIReleaseSerialIRQ(pa, bIRQ);
irr2 = GetPICIRR();
//re-assert IRQ line
PcicWriteSocket(pa, EXCAREG_CSC_CFG, (UCHAR)((bIRQ << 4) | CSCFG_CD_ENABLE));
//reset pending interrupt
bData = PcicReadSocket(pa, EXCAREG_CARD_STATUS);
irr3 = GetPICIRR();
rc = (USHORT)((irr1 ^ irr2) & (irr2 ^ irr3));
}
PcicWriteSocket(pa, EXCAREG_CSC_CFG, bOldIntCfg);
PcicWriteSocket(pa, EXCAREG_INT_GENCTRL, bOldIntCtrl);
return rc;
} //ToggleIRQLine
/***LP GetPICIRR - Read PIC IRR
*
* ENTRY
* None
*
* EXIT
* returns the IRR mask from PIC
*/
USHORT GetPICIRR(VOID)
{
USHORT wData;
//
// Delay 2 usec before reading PIC because serial IRQ may be a bit slow.
//
TimeOut(4);
_asm mov al,PIC_RD_IR
_asm out PIC2_OCW3,al
_asm in al,PIC2_OCW3
_asm mov ah,al
_asm mov al,PIC_RD_IR
_asm out PIC1_OCW3,al
_asm in al,PIC1_OCW3
_asm mov wData,ax
return wData;
} //GetPICIRR
UCHAR
PcicReadSocket(
PPCCARD_INFORMATION pa,
USHORT Reg
)
{
USHORT IoBase = pa->IoBase;
UCHAR value;
_asm {
mov dx, IoBase
mov ax, Reg
out dx, al
inc dx
in al, dx
mov value, al
}
return value;
}
VOID
PcicWriteSocket(
PPCCARD_INFORMATION pa,
USHORT Reg,
UCHAR value
)
{
USHORT IoBase = pa->IoBase;
_asm {
mov dx, IoBase
mov ax, Reg
out dx, al
inc dx
mov al, value
out dx, al
}
}
UCHAR PCIDeref[4][4] = { {4,1,2,2},{1,1,1,1},{2,1,2,2},{1,1,1,1} };
VOID
SetPciConfigSpace(
PPCCARD_INFORMATION pa,
USHORT Offset,
PVOID pvBuffer,
USHORT Length
)
{
USHORT IoSize;
PUCHAR Buffer = (PUCHAR) pvBuffer;
//
// Read it
//
while (Length) {
pa->PciCfg1.u.bits.RegisterNumber = Offset / sizeof(ULONG);
IoSize = PCIDeref[Offset % sizeof(ULONG)][Length % sizeof(ULONG)];
SetPCIType1Data (pa->PciCfg1.u.AsULONG,
(Offset % sizeof(ULONG)),
Buffer,
IoSize);
Offset += IoSize;
Buffer += IoSize;
Length -= IoSize;
}
}
VOID
GetPciConfigSpace(
PPCCARD_INFORMATION pa,
USHORT Offset,
PVOID pvBuffer,
USHORT Length
)
{
USHORT IoSize;
USHORT i;
PUCHAR Buffer = (PUCHAR) pvBuffer;
//
// Zap input buffer
//
for (i=0; i < Length; i++) {
Buffer[i] = 0xff;
}
//
// Read it
//
while (Length) {
pa->PciCfg1.u.bits.RegisterNumber = Offset / sizeof(ULONG);
IoSize = PCIDeref[Offset % sizeof(ULONG)][Length % sizeof(ULONG)];
GetPCIType1Data (pa->PciCfg1.u.AsULONG,
(Offset % sizeof(ULONG)),
Buffer,
IoSize);
Offset += IoSize;
Buffer += IoSize;
Length -= IoSize;
}
}