/*++ Copyright (c) 1995 DeskStation Technology Module Name: pcisup.c Abstract: This module contains the routines that support PCI configuration cycles and PCI interrupts. Author: Michael D. Kinney 30-Apr-1995 Environment: Kernel mode Revision History: --*/ #include "halp.h" #include "pci.h" #include "pcip.h" #define INVALID_PCI_CONFIGURATION_ADDRESS (0xffffff00) #define NO_PCI_DEVSEL_DATA_VALUE (0xffffffff) // // The following tables are used to map between PCI interrupt pins, PCI interrupt lines, // and virtual ISA interrupt indexes. The Uniflex architecture uses a 16 bit interrupt // controller for ISA interrupts and all PCI interrupts. An InterruptLine value of 0x20 // is reserved for the ISA PIC. InterruptLine values between 0x10 and 0x20 are reserved // for PCI devices. InterruptLine values between 0x00 and 0x0f are reserved for ISA IRQs. // UCHAR Treb13InterruptLineToBit[0x11] = {7,2,3,1,4,5,6,9,10,11,16,16,16,16,15,14,0}; UCHAR Treb13BitToInterruptLine[0x10] = {0x10,0x03,0x01,0x02,0x04,0x05,0x06,0x00,0x00,0x07,0x08,0x09,0x00,0x00,0x0f,0x0e}; UCHAR Treb13InterruptLineToVirtualIsa[0x10] = {0,1,2,3,8,9,10,11,4,5,0,0,0,0,0,0}; UCHAR Treb13VirtualIsaToInterruptLine[0x10] = {0x10,0x11,0x12,0x13,0x18,0x19,0,0,0x14,0x15,0x16,0x17,0,0,0,0}; UCHAR Treb20InterruptLineToBit[0x11] = {1,2,3,4,5,6,7,8,9,16,16,16,16,16,16,16,0}; UCHAR Treb20BitToInterruptLine[0x10] = {0x10,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00}; UCHAR Treb20InterruptLineToVirtualIsa[0x11] = {0,1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,9}; UCHAR Treb20VirtualIsaToInterruptLine[0x10] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x20,0,0,0,0,0,0}; // // Interrupt mask for all active PCI interrupts including ISA Bus PICs // static volatile ULONG HalpPciInterruptMask; // // Interrupt mask for PCI interrupts that have been connected through device drivers. // static volatile ULONG HalpPciDeviceInterruptMask; // // Interrupt mask showing which bit cooresponds to ISA Bus #0 PIC // static volatile ULONG HalpEisaInterruptMask; // // Interrupt mask showing which bit cooresponds to ISA Bus #1 PIC // static volatile ULONG HalpEisa1InterruptMask; VOID HalpWritePciInterruptRegister( VOID ) /*++ Routine Description: This function writes the interrupt mask register for PCI interrupts. Arguments: None. Return Value: None. --*/ { if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { HalpWriteAbsoluteUlong(0xfffffca8,0x00000010, (HalpPciInterruptMask & 0xff) << 8); HalpWriteAbsoluteUlong(0xfffffc88,0x00000010, HalpPciInterruptMask & 0xff00); } else { HalpWriteAbsoluteUlong(0xfffffc03,0x2000000c, HalpPciInterruptMask & 0xff); HalpWriteAbsoluteUlong(0xfffffc03,0x6000000c, HalpPciInterruptMask >> 8); } } ULONG HalpReadPciInterruptRegister( VOID ) /*++ Routine Description: This function reads the interrupt status register for PCI interrupts. Arguments: None. Return Value: The lower 16 bits contains the status of each interrupt line going to the PCI interrupt controller. --*/ { if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { return ( ((HalpReadAbsoluteUlong(0xfffffca8,0x00000000)>>8)&0xff) | (HalpReadAbsoluteUlong(0xfffffc88,0x00000000) & 0xff00) ); } else { return ( (HalpReadAbsoluteUlong(0xfffffc03,0x2000000c) &0xff) | ((HalpReadAbsoluteUlong(0xfffffc03,0x6000000c)<<8) & 0xff00) ); } } ULONG HalpGetModuleChipSetRevision( VOID ) /*++ Routine Description: This function identifies the chip set revision of the processor module installed in the system. Arguments: None. Return Value: The chip set revision. --*/ { ULONG Temp; ULONG ReturnValue; if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { Temp = HalpPciInterruptMask; HalpPciInterruptMask = 0; HalpWritePciInterruptRegister(); ReturnValue = (HalpReadPciInterruptRegister() >> 4) & 0x0f; HalpPciInterruptMask = Temp; HalpWritePciInterruptRegister(); } if (HalpIoArchitectureType == EV4_PROCESSOR_MODULE) { ReturnValue = HalpReadAbsoluteUlong(0xfffffc03,0xe000000c); ReturnValue = (ReturnValue & 0x0f) ^ ((ReturnValue >> 4) & 0x0f); } return(ReturnValue); } VOID HalpSetPciInterruptBit ( ULONG Bit ) /*++ Routine Description: This function sets a bit in the PCI interrupt mask and writes the new mask to the interrupt controller. Arguments: Bit - The bit number to set in the PCI interrupt mask. Return Value: None. --*/ { HalpPciDeviceInterruptMask = HalpPciDeviceInterruptMask | (1<= UNIFLEX_PCI_VECTORS && Vector <= UNIFLEX_MAXIMUM_PCI_VECTOR) { HalpSetPciInterruptBit(HalpInterruptLineToBit[Vector-UNIFLEX_PCI_VECTORS]); } } VOID HalpDisablePciInterrupt ( IN ULONG Vector ) /*++ Routine Description: This function disables a PCI interrupt. Arguments: Vector - Specifies the interrupt to disable. Return Value: None. --*/ { if (Vector >= UNIFLEX_PCI_VECTORS && Vector <= UNIFLEX_MAXIMUM_PCI_VECTOR) { HalpClearPciInterruptBit(HalpInterruptLineToBit[Vector-UNIFLEX_PCI_VECTORS]); } } ULONG HalpVirtualIsaInterruptToInterruptLine ( IN ULONG Index ) /*++ Routine Description: This function maps a virtual ISA interrupt to a PCI interrupt line value. This provides the ability to use an ISA device driver on a PCI device. Arguments: Index - Index into a platform specific table that maps PCI interrupts to virtual ISA interrupts. Return Value: None. --*/ { return(HalpVirtualIsaToInterruptLine[Index]); } ULONG HalpClearLockCacheLineAddress[32]; BOOLEAN HalpPciDispatch( IN PKINTERRUPT Interrupt, IN PVOID ServiceContext ) /*++ Routine Description: This is the interrupt dispatcher for all PCI interrupts. Arguments: Interrupt - Supplies a pointer to the interrupt object. ServiceContext - not used. Return Value: Returns the value returned from the second level routine. --*/ { ULONG PciInterruptStatus; PULONG dispatchCode; PKINTERRUPT interruptObject; USHORT PCRInOffset; BOOLEAN returnValue = FALSE; ULONG i; if (HalpIoArchitectureType != EV5_PROCESSOR_MODULE) { Halp21064ClearLockRegister(&(HalpClearLockCacheLineAddress[16])); } // // Get the active interrupt bits // PciInterruptStatus = HalpReadPciInterruptRegister(); // // See if this is the interrupt for ISA Bus #0 PIC // if (PciInterruptStatus & HalpEisaInterruptMask) { returnValue = HalpEisaDispatch(Interrupt,ServiceContext,0); // // If there really was an interrupt on ISA Bus #0, then return now. // if (returnValue) { return(returnValue); } } // // See if this is the interrupt for ISA Bus #1 PIC // if (PciInterruptStatus & HalpEisa1InterruptMask) { returnValue = HalpEisaDispatch(Interrupt,ServiceContext,1); // // If there really was an interrupt on ISA Bus #1, then return now. // if (returnValue) { return(returnValue); } } // // Only keep interrupt bits that have been connected by device drivers. // PciInterruptStatus &= HalpPciDeviceInterruptMask; // // Dispatch to the ISRs of interrupts that have been connected by device drivers. // for(i=0;i<16;i++) { if (PciInterruptStatus & (1<InterruptRoutine[PCRInOffset]); interruptObject = CONTAINING_RECORD(dispatchCode, KINTERRUPT, DispatchCode); returnValue = ((PSECONDARY_DISPATCH)interruptObject->DispatchAddress) (interruptObject); } } return(returnValue); } UCHAR HalpGetInterruptLine(ULONG BusNumber,ULONG DeviceNumber,ULONG InterruptPin) /*++ Routine Description: This routine maps a PCI interrupt described by the device's bus number, device number, and interrupt pin into the interrupt line value that is stored in the PCI config header. Arguments: BusNumber - PCI bus number of the device. DeviceNumber - PCI device number of the device. InterruptPin - PCI interrupt pin of the device (A=1,B=2,C=3,D=4). Return Value: Returns the PCI Interrupt Line value for the PCI device. --*/ { UCHAR InterruptLine; if (HalpMotherboardType == TREBBIA13) { if (BusNumber > 1) { BusNumber = 1; } switch (BusNumber<<16 | DeviceNumber<<8 | InterruptPin) { case 0x010401 : InterruptLine = 0x10; break; // Bus 1, Device 4, Int A case 0x010601 : InterruptLine = 0x11; break; // Bus 1, Device 6, Int A case 0x010501 : InterruptLine = 0x12; break; // Bus 1, Device 5, Int A case 0x010701 : InterruptLine = 0x13; break; // Bus 1, Device 7, Int A case 0x010402 : InterruptLine = 0x17; break; // Bus 1, Device 4, Int B case 0x010602 : InterruptLine = 0x14; break; // Bus 1, Device 6, Int B case 0x010502 : InterruptLine = 0x14; break; // Bus 1, Device 5, Int B case 0x010702 : InterruptLine = 0x17; break; // Bus 1, Device 7, Int B case 0x010403 : InterruptLine = 0x18; break; // Bus 1, Device 4, Int C case 0x010603 : InterruptLine = 0x15; break; // Bus 1, Device 6, Int C case 0x010503 : InterruptLine = 0x15; break; // Bus 1, Device 5, Int C case 0x010703 : InterruptLine = 0x18; break; // Bus 1, Device 7, Int C case 0x010404 : InterruptLine = 0x19; break; // Bus 1, Device 4, Int D case 0x010604 : InterruptLine = 0x16; break; // Bus 1, Device 6, Int D case 0x010504 : InterruptLine = 0x16; break; // Bus 1, Device 5, Int D case 0x010704 : InterruptLine = 0x19; break; // Bus 1, Device 7, Int D case 0x000d01 : InterruptLine = 0x1e; break; // Bus 0, Device 13, Int A case 0x000f01 : InterruptLine = 0x1f; break; // Bus 0, Device 15, Int A case 0x001001 : InterruptLine = 0x20; break; // Bus 0, Device 16, Int A default : InterruptLine = 0xff; break; } } if (HalpMotherboardType == TREBBIA20) { if (BusNumber == 0) { return(0xff); } if (BusNumber >= HalpSecondPciBridgeBusNumber) { BusNumber = 1; } else { BusNumber = 0; } switch (BusNumber<<16 | DeviceNumber<<8 | InterruptPin) { case 0x000401 : InterruptLine = 0x20; break; case 0x000501 : case 0x000603 : case 0x000704 : InterruptLine = 0x10; break; case 0x000502 : case 0x000604 : case 0x000701 : InterruptLine = 0x11; break; case 0x000503 : case 0x000601 : case 0x000702 : InterruptLine = 0x12; break; case 0x000504 : case 0x000602 : case 0x000703 : InterruptLine = 0x13; break; case 0x010401 : case 0x010504 : case 0x010603 : InterruptLine = 0x14; break; case 0x010402 : case 0x010501 : case 0x010604 : InterruptLine = 0x15; break; case 0x010403 : case 0x010502 : case 0x010601 : InterruptLine = 0x16; break; case 0x010404 : case 0x010503 : case 0x010602 : InterruptLine = 0x17; break; case 0x010701 : InterruptLine = 0x18; break; default : InterruptLine = 0xff; break; } } return(InterruptLine); } VOID HalpConnectInterruptDispatchers( VOID ) /*++ Routine Description: This function connects the PCI interrupt dispatch routine and enables ISA interrupts so they will generate processor interrupts. Arguments: None. Return Value: None. --*/ { UCHAR InterruptLine; if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { PCR->InterruptRoutine[22] = (PKINTERRUPT_ROUTINE)HalpPciDispatch; } else { PCR->InterruptRoutine[14] = (PKINTERRUPT_ROUTINE)HalpPciDispatch; } //DbgPrint("Intel82378 : Bus=%d Device=%d\n\r",HalpIntel82378BusNumber,HalpIntel82378DeviceNumber); //DbgPrint("SecondIntel82378 : Bus=%d Device=%d\n\r",HalpSecondPciBridgeBusNumber,HalpSecondIntel82378DeviceNumber); InterruptLine = HalpGetInterruptLine(HalpIntel82378BusNumber,HalpIntel82378DeviceNumber,1); HalpEisaInterruptMask = 0x0000; if (InterruptLine != 0xff) { HalpEisaInterruptMask = (1 << HalpInterruptLineToBit[InterruptLine-0x10]) & 0xffff; } InterruptLine = HalpGetInterruptLine(HalpSecondPciBridgeBusNumber,HalpSecondIntel82378DeviceNumber,1); HalpEisa1InterruptMask = 0x0000; if (InterruptLine != 0xff) { HalpEisa1InterruptMask = (1 << HalpInterruptLineToBit[InterruptLine-0x10]) & 0xffff; } //DbgPrint("HalpEisaInterruptMask = %08x\n\r",HalpEisaInterruptMask); //DbgPrint("HalpEisa1InterruptMask = %08x\n\r",HalpEisa1InterruptMask); // // Enable ISA Interrupts on Apocolypse's PIC // HalpPciDeviceInterruptMask = 0x0000; HalpPciInterruptMask = HalpEisaInterruptMask | HalpEisa1InterruptMask; HalpWritePciInterruptRegister(); } VOID HalpDisableAllInterrupts( VOID ) /*++ Routine Description: This function disables all external interrupt sources. Arguments: None. Return Value: None. --*/ { ULONG i; // // Mask off all ISA Interrupts // for(i=0;iu.AsULONG = INVALID_PCI_CONFIGURATION_ADDRESS; return; } if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { pPciAddr->u.AsULONG = 1 << (3 + Slot.u.bits.DeviceNumber); } else { pPciAddr->u.AsULONG = 1 << (11 + Slot.u.bits.DeviceNumber); } pPciAddr->u.bits0.FunctionNumber = Slot.u.bits.FunctionNumber; pPciAddr->u.bits0.Reserved1 = PciConfigType0; if (HalpIoArchitectureType == EV4_PROCESSOR_MODULE) { pPciAddr->u.AsULONG &= 0x01ffffff; pPciAddr->u.AsULONG += (ULONG)HAL_MAKE_QVA(HalpPciConfig0BasePhysical); if (Slot.u.bits.DeviceNumber >= 14 && Slot.u.bits.DeviceNumber <= 19) { pPciAddr->u.AsULONG += (Slot.u.bits.DeviceNumber-13) << 25; } } } else { // // See if this is a nonexistant PCI device on the otherside of the First PCI-PCI Bridge // if (BusNumber == 1 && (1 << Slot.u.bits.DeviceNumber) & HalpNonExistentPci1DeviceMask) { pPciAddr->u.AsULONG = INVALID_PCI_CONFIGURATION_ADDRESS; return; } // // See if this is a nonexistant PCI device on the otherside of the Second PCI-PCI Bridge // if (BusNumber == HalpSecondPciBridgeBusNumber && (1 << Slot.u.bits.DeviceNumber) & HalpNonExistentPci2DeviceMask) { pPciAddr->u.AsULONG = INVALID_PCI_CONFIGURATION_ADDRESS; return; } // // Initialize PciAddr for a type 1 configuration cycle // pPciAddr->u.AsULONG = 0; if (HalpIoArchitectureType == EV4_PROCESSOR_MODULE) { pPciAddr->u.AsULONG = (ULONG)HAL_MAKE_QVA(HalpPciConfig1BasePhysical); } pPciAddr->u.bits1.BusNumber = BusNumber; pPciAddr->u.bits1.FunctionNumber = Slot.u.bits.FunctionNumber; pPciAddr->u.bits1.DeviceNumber = Slot.u.bits.DeviceNumber; pPciAddr->u.bits1.Reserved1 = PciConfigType1; } return; } ULONG READ_CONFIG_Ux( IN PVOID ConfigurationAddress, IN ULONG ConfigurationType, IN ULONG Offset ) { switch(ConfigurationType) { case PciConfigType0 : return(HalpReadAbsoluteUlong((ULONG)(HalpPciConfig0BasePhysical >> 32), (ULONG)HalpPciConfig0BasePhysical + ((ULONG)ConfigurationAddress << IO_BIT_SHIFT) + Offset)); case PciConfigType1 : return(HalpReadAbsoluteUlong((ULONG)(HalpPciConfig1BasePhysical >> 32), (ULONG)HalpPciConfig1BasePhysical + ((ULONG)ConfigurationAddress << IO_BIT_SHIFT) + Offset)); } return(NO_PCI_DEVSEL_DATA_VALUE); } VOID WRITE_CONFIG_Ux( IN PVOID ConfigurationAddress, IN ULONG ConfigurationType, IN ULONG Offset, IN ULONG Value ) { switch(ConfigurationType) { case PciConfigType0 : HalpWriteAbsoluteUlong((ULONG)(HalpPciConfig0BasePhysical >> 32), (ULONG)HalpPciConfig0BasePhysical + ((ULONG)ConfigurationAddress << IO_BIT_SHIFT) + Offset, Value); break; case PciConfigType1 : HalpWriteAbsoluteUlong((ULONG)(HalpPciConfig1BasePhysical >> 32), (ULONG)HalpPciConfig1BasePhysical + ((ULONG)ConfigurationAddress << IO_BIT_SHIFT) + Offset, Value); break; } } UCHAR READ_CONFIG_UCHAR( IN PVOID ConfigurationAddress, IN ULONG ConfigurationType ) { if (((ULONG)ConfigurationAddress & 0xffffff00) == INVALID_PCI_CONFIGURATION_ADDRESS) { return((UCHAR)NO_PCI_DEVSEL_DATA_VALUE); } if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { return((UCHAR)(READ_CONFIG_Ux(ConfigurationAddress,ConfigurationType,IO_BYTE_LEN) >> (8*((ULONG)ConfigurationAddress & 0x03)))); } else { return(READ_REGISTER_UCHAR(ConfigurationAddress)); } } USHORT READ_CONFIG_USHORT( IN PVOID ConfigurationAddress, IN ULONG ConfigurationType ) { if (((ULONG)ConfigurationAddress & 0xffffff00) == INVALID_PCI_CONFIGURATION_ADDRESS) { return((USHORT)NO_PCI_DEVSEL_DATA_VALUE); } if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { return((USHORT)(READ_CONFIG_Ux(ConfigurationAddress,ConfigurationType,IO_WORD_LEN) >> (8*((ULONG)ConfigurationAddress & 0x03)))); } else { return(READ_REGISTER_USHORT(ConfigurationAddress)); } } ULONG READ_CONFIG_ULONG( IN PVOID ConfigurationAddress, IN ULONG ConfigurationType ) { if (((ULONG)ConfigurationAddress & 0xffffff00) == INVALID_PCI_CONFIGURATION_ADDRESS) { return((ULONG)NO_PCI_DEVSEL_DATA_VALUE); } if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { return(READ_CONFIG_Ux(ConfigurationAddress,ConfigurationType,IO_LONG_LEN) >> (8*((ULONG)ConfigurationAddress & 0x03))); } else { return(READ_REGISTER_ULONG(ConfigurationAddress)); } } VOID WRITE_CONFIG_UCHAR( IN PVOID ConfigurationAddress, IN UCHAR ConfigurationData, IN ULONG ConfigurationType ) { if (((ULONG)ConfigurationAddress & 0xffffff00) != INVALID_PCI_CONFIGURATION_ADDRESS) { if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { WRITE_CONFIG_Ux(ConfigurationAddress,ConfigurationType,IO_BYTE_LEN,(ULONG)ConfigurationData << (8*((ULONG)ConfigurationAddress & 0x03))); } else { WRITE_REGISTER_UCHAR(ConfigurationAddress,ConfigurationData); } } } VOID WRITE_CONFIG_USHORT( IN PVOID ConfigurationAddress, IN USHORT ConfigurationData, IN ULONG ConfigurationType ) { if (((ULONG)ConfigurationAddress & 0xffffff00) != INVALID_PCI_CONFIGURATION_ADDRESS) { if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { WRITE_CONFIG_Ux(ConfigurationAddress,ConfigurationType,IO_WORD_LEN,(ULONG)ConfigurationData << (8*((ULONG)ConfigurationAddress & 0x03))); } else { WRITE_REGISTER_USHORT(ConfigurationAddress,ConfigurationData); } } } VOID WRITE_CONFIG_ULONG( IN PVOID ConfigurationAddress, IN ULONG ConfigurationData, IN ULONG ConfigurationType ) { if (((ULONG)ConfigurationAddress & 0xffffff00) != INVALID_PCI_CONFIGURATION_ADDRESS) { if (HalpIoArchitectureType == EV5_PROCESSOR_MODULE) { WRITE_CONFIG_Ux(ConfigurationAddress,ConfigurationType,IO_LONG_LEN,ConfigurationData << (8*((ULONG)ConfigurationAddress & 0x03))); } else { WRITE_REGISTER_ULONG(ConfigurationAddress,ConfigurationData); } } } ULONG HalpPciLowLevelConfigRead( ULONG BusNumber, ULONG DeviceNumber, ULONG FunctionNumber, ULONG Register ) { PCI_SLOT_NUMBER SlotNumber; PCI_CFG_CYCLE_BITS PciCfg; ULONG ConfigurationCycleType; SlotNumber.u.AsULONG = 0; SlotNumber.u.bits.DeviceNumber = DeviceNumber; SlotNumber.u.bits.FunctionNumber = FunctionNumber; HalpPCIConfigAddr(BusNumber,SlotNumber,&PciCfg); ConfigurationCycleType = PciCfg.u.bits.Reserved1; PciCfg.u.bits.Reserved1 = 0; PciCfg.u.bits0.RegisterNumber = Register>>2; return(READ_CONFIG_ULONG((PVOID)PciCfg.u.AsULONG,ConfigurationCycleType)); }