587 lines
17 KiB
C
587 lines
17 KiB
C
/*++
|
||
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ixpcisup.c
|
||
|
||
Abstract:
|
||
|
||
Support functions for doing PCI the bus-handler
|
||
way.
|
||
|
||
Author:
|
||
|
||
Ken Reneris (kenr) 14-June-1994
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
Moved code into this file so that it would be
|
||
easier to build a non-bus-handler HAL. This
|
||
file will only be compiled into HALs that
|
||
use bus handlers. -- Jake Oshins 2-Dec-1997
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "pci.h"
|
||
#include "pcip.h"
|
||
#include "chiphacks.h"
|
||
|
||
BOOLEAN
|
||
HalpIsIdeDevice(
|
||
IN PPCI_COMMON_CONFIG PciData
|
||
);
|
||
|
||
VOID
|
||
HalpGetNMICrashFlag (
|
||
VOID
|
||
);
|
||
|
||
extern BOOLEAN HalpDisableHibernate;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,HalpInitializePciBus)
|
||
#pragma alloc_text(INIT,HalpIsIdeDevice)
|
||
#pragma alloc_text(INIT,HalpAllocateAndInitPciBusHandler)
|
||
#endif
|
||
|
||
VOID
|
||
HalpInitializePciBus (
|
||
VOID
|
||
)
|
||
{
|
||
PPCI_REGISTRY_INFO_INTERNAL PCIRegInfo;
|
||
ULONG i, d, HwType, BusNo, f;
|
||
PBUS_HANDLER BusHandler;
|
||
PCI_SLOT_NUMBER SlotNumber;
|
||
PPCI_COMMON_CONFIG PciData;
|
||
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH + sizeof(TYPE2EXTRAS)];
|
||
ULONG OPBNumber;
|
||
BOOLEAN OPBA2B0Found, COPBInbPostingEnabled;
|
||
UCHAR buffer [4];
|
||
BOOLEAN fullDecodeChipset = FALSE;
|
||
NTSTATUS Status;
|
||
ULONG flags;
|
||
|
||
PCIRegInfo = HalpQueryPciRegistryInfo();
|
||
|
||
if (!PCIRegInfo) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Initialize spinlock for synchronizing access to PCI space
|
||
//
|
||
|
||
KeInitializeSpinLock (&HalpPCIConfigLock);
|
||
PciData = (PPCI_COMMON_CONFIG) iBuffer;
|
||
|
||
//
|
||
// PCIRegInfo describes the system's PCI support as indicated by the BIOS.
|
||
//
|
||
|
||
HwType = PCIRegInfo->HardwareMechanism & 0xf;
|
||
|
||
//
|
||
// Some AMI bioses claim machines are Type2 configuration when they
|
||
// are really type1. If this is a Type2 with at least one bus,
|
||
// try to verify it's not really a type1 bus
|
||
//
|
||
|
||
if (PCIRegInfo->NoBuses && HwType == 2) {
|
||
|
||
//
|
||
// Check each slot for a valid device. Which every style configuration
|
||
// space shows a valid device first will be used
|
||
//
|
||
|
||
SlotNumber.u.bits.Reserved = 0;
|
||
SlotNumber.u.bits.FunctionNumber = 0;
|
||
|
||
for (d = 0; d < PCI_MAX_DEVICES; d++) {
|
||
SlotNumber.u.bits.DeviceNumber = d;
|
||
|
||
//
|
||
// First try what the BIOS claims - type 2. Allocate type2
|
||
// test handle for PCI bus 0.
|
||
//
|
||
|
||
HwType = 2;
|
||
BusHandler = HalpAllocateAndInitPciBusHandler (HwType, 0, TRUE);
|
||
|
||
if (HalpIsValidPCIDevice (BusHandler, SlotNumber)) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Valid device not found on Type2 access for this slot.
|
||
// Reallocate the bus handler are Type1 and take a look.
|
||
//
|
||
|
||
HwType = 1;
|
||
BusHandler = HalpAllocateAndInitPciBusHandler (HwType, 0, TRUE);
|
||
|
||
if (HalpIsValidPCIDevice (BusHandler, SlotNumber)) {
|
||
break;
|
||
}
|
||
|
||
HwType = 2;
|
||
}
|
||
|
||
//
|
||
// Reset handler for PCI bus 0 to whatever style config space
|
||
// was finally decided.
|
||
//
|
||
|
||
HalpAllocateAndInitPciBusHandler (HwType, 0, FALSE);
|
||
}
|
||
|
||
|
||
//
|
||
// For each PCI bus present, allocate a handler structure and
|
||
// fill in the dispatch functions
|
||
//
|
||
|
||
do {
|
||
for (i=0; i < PCIRegInfo->NoBuses; i++) {
|
||
|
||
//
|
||
// If handler not already built, do it now
|
||
//
|
||
|
||
if (!HalpHandlerForBus (PCIBus, i)) {
|
||
HalpAllocateAndInitPciBusHandler (HwType, i, FALSE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Bus handlers for all PCI buses have been allocated, go collect
|
||
// pci bridge information.
|
||
//
|
||
|
||
} while (HalpGetPciBridgeConfig (HwType, &PCIRegInfo->NoBuses)) ;
|
||
|
||
//
|
||
// Fixup SUPPORTED_RANGES
|
||
//
|
||
|
||
HalpFixupPciSupportedRanges (PCIRegInfo->NoBuses);
|
||
|
||
|
||
//
|
||
// Look for PCI controllers which have known work-arounds, and make
|
||
// sure they are applied.
|
||
//
|
||
// In addition, fill in the bitmask HalpPciIrqMask with all the
|
||
// interrupts that PCI devices might use.
|
||
//
|
||
|
||
OPBNumber = 0;
|
||
OPBA2B0Found = FALSE;
|
||
COPBInbPostingEnabled = FALSE;
|
||
|
||
SlotNumber.u.bits.Reserved = 0;
|
||
for (BusNo=0; BusNo < PCIRegInfo->NoBuses; BusNo++) {
|
||
BusHandler = HalpHandlerForBus (PCIBus, BusNo);
|
||
|
||
for (d = 0; d < PCI_MAX_DEVICES; d++) {
|
||
SlotNumber.u.bits.DeviceNumber = d;
|
||
|
||
for (f = 0; f < PCI_MAX_FUNCTION; f++) {
|
||
SlotNumber.u.bits.FunctionNumber = f;
|
||
|
||
//
|
||
// Read PCI configuration information
|
||
//
|
||
|
||
HalpReadPCIConfig (BusHandler, SlotNumber, PciData, 0, PCI_COMMON_HDR_LENGTH);
|
||
|
||
if (*((PULONG)(PciData)) == 0xffffffff) {
|
||
continue;
|
||
}
|
||
|
||
if (PCI_CONFIGURATION_TYPE(PciData) == PCI_CARDBUS_BRIDGE_TYPE) {
|
||
|
||
HalpReadPCIConfig(
|
||
BusHandler,
|
||
SlotNumber,
|
||
PciData+1,
|
||
FIELD_OFFSET(PCI_COMMON_CONFIG, DeviceSpecific),
|
||
sizeof(TYPE2EXTRAS)
|
||
);
|
||
}
|
||
|
||
#ifndef SUBCLASSPCI
|
||
//
|
||
// Look at interrupt line register and fill in HalpPciIrqMask,
|
||
// but not for an IDE controller, as IDE controllers really
|
||
// trigger interrupts like ISA devices.
|
||
//
|
||
if (PCI_CONFIGURATION_TYPE(PciData) != 1) {
|
||
if ((PciData->u.type0.InterruptPin != 0) &&
|
||
(PciData->u.type0.InterruptLine != 0) &&
|
||
(PciData->u.type0.InterruptLine < PIC_VECTORS) &&
|
||
!HalpIsIdeDevice(PciData)) {
|
||
|
||
HalpPciIrqMask |= 1 << PciData->u.type0.InterruptLine;
|
||
}
|
||
}
|
||
#endif
|
||
//
|
||
// Check for chips with known work-arounds to apply
|
||
//
|
||
|
||
if (PciData->VendorID == 0x8086 &&
|
||
PciData->DeviceID == 0x04A3 &&
|
||
PciData->RevisionID < 0x11) {
|
||
|
||
//
|
||
// 82430 PCMC controller
|
||
//
|
||
|
||
HalpReadPCIConfig (BusHandler, SlotNumber, buffer, 0x53, 2);
|
||
|
||
buffer[0] &= ~0x08; // turn off bit 3 register 0x53
|
||
|
||
if (PciData->RevisionID == 0x10) { // on rev 0x10, also turn
|
||
buffer[1] &= ~0x01; // bit 0 register 0x54
|
||
}
|
||
|
||
HalpWritePCIConfig (BusHandler, SlotNumber, buffer, 0x53, 2);
|
||
}
|
||
|
||
if (PciData->VendorID == 0x8086 &&
|
||
PciData->DeviceID == 0x0484 &&
|
||
PciData->RevisionID <= 3) {
|
||
|
||
//
|
||
// 82378 ISA bridge & SIO
|
||
//
|
||
|
||
HalpReadPCIConfig (BusHandler, SlotNumber, buffer, 0x41, 1);
|
||
|
||
buffer[0] &= ~0x1; // turn off bit 0 register 0x41
|
||
|
||
HalpWritePCIConfig (BusHandler, SlotNumber, buffer, 0x41, 1);
|
||
}
|
||
|
||
//
|
||
// Look for Orion PCI Bridge
|
||
//
|
||
|
||
if (PciData->VendorID == 0x8086 &&
|
||
PciData->DeviceID == 0x84c4 ) {
|
||
|
||
//
|
||
// 82450 Orion PCI Bridge Workaround
|
||
// Need a workaround if following conditions are true:
|
||
// i) 2 OPBs present
|
||
// ii)There is an A2/B0 step OPB present.
|
||
// iii) Inbound posting on the compatibility OPB is
|
||
// enabled.
|
||
// NOTE: Inbound Posting on the non-compatibility OPB
|
||
// MUST BE disabled by BIOS
|
||
//
|
||
|
||
OPBNumber += 1;
|
||
|
||
if (PciData->RevisionID <= 4) {
|
||
OPBA2B0Found = TRUE;
|
||
}
|
||
|
||
if (SlotNumber.u.bits.DeviceNumber == (0xc8>>3)) {
|
||
|
||
// Found compatibility OPB. Determine if the compatibility
|
||
// OPB has inbound posting enabled by testing bit 0 of reg 54
|
||
|
||
HalpReadPCIConfig (BusHandler, SlotNumber, buffer, 0x54, 2);
|
||
COPBInbPostingEnabled = (buffer[0] & 0x1) ? TRUE : FALSE;
|
||
|
||
} else {
|
||
|
||
// The compatibility OPB ALWAYS has a device
|
||
// number 0xc8. Save the ncOPB slot number
|
||
// and BusHandler
|
||
|
||
HalpOrionOPB.Slot = SlotNumber;
|
||
HalpOrionOPB.Handler = BusHandler;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check the list for host bridges who's existance will mark a
|
||
// chipset as 16bit decode. We use this to cover for BIOS
|
||
// writers who list "fixed" PnPBIOS resources without noticing
|
||
// that such a descriptor implies their device is 10bit decode.
|
||
//
|
||
|
||
if ((!fullDecodeChipset) &&
|
||
HalpIsRecognizedCard(PCIRegInfo, PciData,
|
||
PCIFT_FULLDECODE_HOSTBRIDGE)) {
|
||
|
||
fullDecodeChipset = TRUE;
|
||
}
|
||
|
||
//
|
||
// Look for ICH, or any other Intel or VIA UHCI USB controller.
|
||
//
|
||
|
||
if ((PciData->BaseClass == PCI_CLASS_SERIAL_BUS_CTLR) &&
|
||
(PciData->SubClass == PCI_SUBCLASS_SB_USB) &&
|
||
(PciData->ProgIf == 0x00)) {
|
||
if (PciData->VendorID == 0x8086) {
|
||
|
||
HalpStopUhciInterrupt(BusNo,
|
||
SlotNumber,
|
||
TRUE);
|
||
|
||
} else if (PciData->VendorID == 0x1106) {
|
||
|
||
HalpStopUhciInterrupt(BusNo,
|
||
SlotNumber,
|
||
FALSE);
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Look for an OHCI-compliant USB controller.
|
||
//
|
||
|
||
if ((PciData->BaseClass == PCI_CLASS_SERIAL_BUS_CTLR) &&
|
||
(PciData->SubClass == PCI_SUBCLASS_SB_USB) &&
|
||
(PciData->ProgIf == 0x10)) {
|
||
|
||
HalpStopOhciInterrupt(BusNo,
|
||
SlotNumber);
|
||
}
|
||
|
||
Status = HalpGetChipHacks(PciData->VendorID,
|
||
PciData->DeviceID,
|
||
0,
|
||
&flags);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
if (flags & DISABLE_HIBERNATE_HACK_FLAG) {
|
||
HalpDisableHibernate = TRUE;
|
||
}
|
||
|
||
if (flags & WHACK_ICH_USB_SMI_HACK_FLAG) {
|
||
HalpWhackICHUsbSmi(BusNo, SlotNumber);
|
||
}
|
||
}
|
||
|
||
} // next function
|
||
} // next device
|
||
} // next bus
|
||
|
||
//
|
||
// Is Orion B0 workaround needed?
|
||
//
|
||
|
||
if (OPBNumber >= 2 && OPBA2B0Found && COPBInbPostingEnabled) {
|
||
|
||
//
|
||
// Replace synchronization functions with Orion specific functions
|
||
//
|
||
|
||
ASSERT (PCIConfigHandler.Synchronize == HalpPCISynchronizeType1);
|
||
MmLockPagableCodeSection (&HalpPCISynchronizeOrionB0);
|
||
PCIConfigHandler.Synchronize = HalpPCISynchronizeOrionB0;
|
||
PCIConfigHandler.ReleaseSynchronzation = HalpPCIReleaseSynchronzationOrionB0;
|
||
}
|
||
|
||
//
|
||
// Check if we should crashdump on NMI.
|
||
//
|
||
|
||
HalpGetNMICrashFlag();
|
||
|
||
#if DBG
|
||
HalpTestPci (0);
|
||
#endif
|
||
|
||
//
|
||
// Mark the chipset appropriately.
|
||
//
|
||
HalpMarkChipsetDecode(fullDecodeChipset);
|
||
|
||
ExFreePool(PCIRegInfo);
|
||
}
|
||
|
||
PBUS_HANDLER
|
||
HalpAllocateAndInitPciBusHandler (
|
||
IN ULONG HwType,
|
||
IN ULONG BusNo,
|
||
IN BOOLEAN TestAllocation
|
||
)
|
||
{
|
||
PBUS_HANDLER Bus;
|
||
PPCIPBUSDATA BusData;
|
||
|
||
Bus = HalpAllocateBusHandler (
|
||
PCIBus, // Interface type
|
||
PCIConfiguration, // Has this configuration space
|
||
BusNo, // bus #
|
||
Internal, // child of this bus
|
||
0, // and number
|
||
sizeof (PCIPBUSDATA) // sizeof bus specific buffer
|
||
);
|
||
|
||
if (!Bus) {
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Fill in PCI handlers
|
||
//
|
||
|
||
Bus->GetBusData = (PGETSETBUSDATA) HalpGetPCIData;
|
||
Bus->SetBusData = (PGETSETBUSDATA) HalpSetPCIData;
|
||
Bus->GetInterruptVector = (PGETINTERRUPTVECTOR) HalpGetPCIIntOnISABus;
|
||
Bus->AdjustResourceList = (PADJUSTRESOURCELIST) HalpAdjustPCIResourceList;
|
||
Bus->AssignSlotResources = (PASSIGNSLOTRESOURCES) HalpAssignPCISlotResources;
|
||
Bus->BusAddresses->Dma.Limit = 0;
|
||
|
||
BusData = (PPCIPBUSDATA) Bus->BusData;
|
||
|
||
//
|
||
// Fill in common PCI data
|
||
//
|
||
|
||
BusData->CommonData.Tag = PCI_DATA_TAG;
|
||
BusData->CommonData.Version = PCI_DATA_VERSION;
|
||
BusData->CommonData.ReadConfig = (PciReadWriteConfig) HalpReadPCIConfig;
|
||
BusData->CommonData.WriteConfig = (PciReadWriteConfig) HalpWritePCIConfig;
|
||
BusData->CommonData.Pin2Line = (PciPin2Line) HalpPCIPin2ISALine;
|
||
BusData->CommonData.Line2Pin = (PciLine2Pin) HalpPCIISALine2Pin;
|
||
|
||
//
|
||
// Set defaults
|
||
//
|
||
|
||
BusData->MaxDevice = PCI_MAX_DEVICES;
|
||
BusData->GetIrqRange = (PciIrqRange) HalpGetISAFixedPCIIrq;
|
||
|
||
RtlInitializeBitMap (&BusData->DeviceConfigured,
|
||
BusData->ConfiguredBits, 256);
|
||
|
||
switch (HwType) {
|
||
case 1:
|
||
//
|
||
// Initialize access port information for Type1 handlers
|
||
//
|
||
|
||
RtlCopyMemory (&PCIConfigHandler,
|
||
&PCIConfigHandlerType1,
|
||
sizeof (PCIConfigHandler));
|
||
|
||
BusData->Config.Type1.Address = PCI_TYPE1_ADDR_PORT;
|
||
BusData->Config.Type1.Data = PCI_TYPE1_DATA_PORT;
|
||
break;
|
||
|
||
case 2:
|
||
//
|
||
// Initialize access port information for Type2 handlers
|
||
//
|
||
|
||
RtlCopyMemory (&PCIConfigHandler,
|
||
&PCIConfigHandlerType2,
|
||
sizeof (PCIConfigHandler));
|
||
|
||
BusData->Config.Type2.CSE = PCI_TYPE2_CSE_PORT;
|
||
BusData->Config.Type2.Forward = PCI_TYPE2_FORWARD_PORT;
|
||
BusData->Config.Type2.Base = PCI_TYPE2_ADDRESS_BASE;
|
||
|
||
//
|
||
// Early PCI machines didn't decode the last bit of
|
||
// the device id. Shrink type 2 support max device.
|
||
//
|
||
BusData->MaxDevice = 0x10;
|
||
|
||
break;
|
||
|
||
default:
|
||
// unsupport type
|
||
DBGMSG ("HAL: Unkown PCI type\n");
|
||
}
|
||
|
||
if (!TestAllocation) {
|
||
#ifdef SUBCLASSPCI
|
||
HalpSubclassPCISupport (Bus, HwType);
|
||
#endif
|
||
}
|
||
|
||
return Bus;
|
||
}
|
||
|
||
BOOLEAN
|
||
HalpIsIdeDevice(
|
||
IN PPCI_COMMON_CONFIG PciData
|
||
)
|
||
{
|
||
if ((PciData->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
|
||
(PciData->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Now look for old, hard to recognize controllers.
|
||
//
|
||
|
||
if (PciData->VendorID == 0x1c1c) { // Old Symphony controller
|
||
return TRUE;
|
||
}
|
||
|
||
if ((PciData->VendorID == 0x10B9) &&
|
||
((PciData->DeviceID == 0x5215) ||
|
||
(PciData->DeviceID == 0x5219))) { // ALI controllers
|
||
return TRUE;
|
||
}
|
||
|
||
if ((PciData->VendorID == 0x1097) &&
|
||
(PciData->DeviceID == 0x0038)) { // Appian controller
|
||
return TRUE;
|
||
}
|
||
|
||
if ((PciData->VendorID == 0x0E11) &&
|
||
(PciData->DeviceID == 0xAE33)) { // Compaq controller
|
||
return TRUE;
|
||
}
|
||
|
||
if ((PciData->VendorID == 0x1042) &&
|
||
(PciData->DeviceID == 0x1000)) { // PCTECH controller
|
||
return TRUE;
|
||
}
|
||
|
||
if ((PciData->VendorID == 0x1039) &&
|
||
((PciData->DeviceID == 0x0601) ||
|
||
(PciData->DeviceID == 0x5513))) { // SIS controllers
|
||
return TRUE;
|
||
}
|
||
|
||
if ((PciData->VendorID == 0x10AD) &&
|
||
((PciData->DeviceID == 0x0001) ||
|
||
(PciData->DeviceID == 0x0150))) { // Newer Symphony controllers
|
||
return TRUE;
|
||
}
|
||
|
||
if ((PciData->VendorID == 0x1060) &&
|
||
(PciData->DeviceID == 0x0101)) { // United Microelectronics controller
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|