/*++ 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 (!BusHandler) { break; } 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, PciData->RevisionID, &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 = (PULONG)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; }