diff options
Diffstat (limited to 'private/ntos/nthals/halfire/ppc/phprods.c')
-rw-r--r-- | private/ntos/nthals/halfire/ppc/phprods.c | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/private/ntos/nthals/halfire/ppc/phprods.c b/private/ntos/nthals/halfire/ppc/phprods.c new file mode 100644 index 000000000..cc09923f2 --- /dev/null +++ b/private/ntos/nthals/halfire/ppc/phprods.c @@ -0,0 +1,794 @@ +/* + * Copyright (c) 1995 FirePower Systems, Inc. + * DO NOT DISTRIBUTE without permission + * + * $RCSfile: phprods.c $ + * $Revision: 1.66 $ + * $Date: 1996/05/14 02:33:18 $ + * $Locker: $ + */ + +#include "fpdebug.h" +#include "halp.h" +#include "eisa.h" +#include "pxsiosup.h" +#include "pxpcisup.h" +#include "pxmemctl.h" +#include "bugcodes.h" + +#include "phsystem.h" +#include "fpio.h" +#include "fpcpu.h" +#include <pci.h> +#include "pcip.h" +#include <arccodes.h> // for ESUCCESS + + +// +// Define the context structure for use by the interrupt routine. +// + +typedef BOOLEAN (*PSECONDARY_DISPATCH)( + PVOID InterruptRoutine, + PVOID ServiceContext, + PVOID TrapFrame + ); + +VOID HalpHandleDecrementerInterrupt1( PKINTERRUPT , PVOID , PVOID ); + +HalState Dispatch; + + +extern BOOLEAN HalpHandleMachineCheck(PKINTERRUPT, PVOID); +extern KINTERRUPT HalpMachineCheckInterrupt; +extern ULONG HalpSetIntPriorityMask( VOID ); +extern ULONG Irql2Mask[]; + +KINTERRUPT HalpPciErrorInt; +KINTERRUPT HalpBusErrorInt; +KINTERRUPT HalpMemoryErrorInt; + +KINTERRUPT HalpHandleClockInterrruptOnOther; + + + +extern ULONG registeredInts[]; + +extern VOID KiDispatchSoftwareInterrupt( VOID); +extern BOOLEAN HalAcknowledgeIpi (VOID); +ULONG HalpGetHighVector(ULONG); + + +extern ULONG HalpSpuriousInterruptCount; +extern UCHAR HalpSioInterrupt1Mask,HalpSioInterrupt2Mask; +extern UCHAR HalpSioInterrupt1Level, HalpSioInterrupt2Level; +extern ULONG Vector2Irql[]; +ULONG HalpSpuriousInts = 0; + + +/* + * HalpHandleExternalInterrupt + * + * Description: + * + * This is the main external interrupt handler. It is called whenever + * an external interrupt occurs. It interfaces to the ASICs that + * cause the external interrupt and vectors to the corresponding + * interrupt handling routine. + * + * Issues: + * + * Not implemented Yet (sfk 8/26/95). + * The returnValue of the driver should be checked and if the driver + * did not handle the interrupt, we should consider clearing the interrupt + * and broadcasting an interrupt error message. + */ +BOOLEAN +HalpHandleExternalInterrupt( + IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext, + IN PVOID TrapFrame + ) +{ + PSECONDARY_DISPATCH SioHandler; + PKINTERRUPT SioInterrupt; + ULONG TmpSysVector; + USHORT interruptVector; + BOOLEAN returnValue; + KIRQL OldIrql; + ULONG OldMask; + UCHAR Irql, i; + register UCHAR CpuId; + + // + // Assert that interrupts are disabled (or just disable them for now) + // + HASSERT(!MSR(EE)); + + // + // Use a local variable for CPU. + // + CpuId = (UCHAR) GetCpuId(); + + // + // indicate are in interrupt handler... + // + SET_LEDS(0xf0 & ~(LED_1)); + + // + // Get the value out of the (ESCC/TIO) register + // Compute interrupt vector number. + // + TmpSysVector = RInterruptPending(CpuId); + if (TmpSysVector == 0) { + // For a spurious interrupt, simply return +#if defined(HALDEBUG_ON) + HalpDebugPrint("HalpHandleExternalInterrupt: Spurious Interrupt"); +#endif + return FALSE; + } + // + // We must now find the highest priority interrupt to service. + // Since the Pending register is not ordered in correct + // priority order, we muse "find" the highest priority + // interrupt. Servicing a lower priority interrupt will cause us + // to nest too deeply on the interrupt stack and panic. + // + for (i = HIGH_LEVEL; i > DISPATCH_LEVEL; i--) { + if ((Irql2Mask[i] & TmpSysVector) != 0) { + break; + } + } + HASSERT(i >= PCR->CurrentIrql); + TmpSysVector &= Irql2Mask[i]; + + // + // Now find any single bit of the bits left. + // + interruptVector = (USHORT) HalpGetHighVector(TmpSysVector); + + // + // Turns off ASIC Interrupts (ESCC/TIO). + // Need more protection than KeRaisIrql currently provides. + // + OldIrql = PCR->CurrentIrql; + Irql = (UCHAR) Vector2Irql[interruptVector]; + HASSERT(Irql > i); + PCR->CurrentIrql = Irql; + OldMask = RInterruptMask(CpuId); + + // + // Mask Handling has varied over time based on how well + // nesting works. The proper answer is to mask off all of the + // interrupts that are a lesser priority than the one + // we are currently handling. Using the same code as KeRaiseIrql + // does. + // + RInterruptMask(CpuId) = (Irql2Mask[Irql]®isteredInts[CpuId]); + WaitForRInterruptMask(CpuId); + HASSERT((RInterruptMask(CpuId) & (1 << interruptVector)) == 0); + + // + // Clear the interrupt out of the request register by writing a one + // for the handled interrupt. + // + rInterruptRequest = (1 << interruptVector); + FireSyncRegister(); + + // + // if the new IRQL level is lower than clock2_level, restore + // system interrupts to allow decrementer interrupts: + // Restoring the interrupt bit in the MSR here allows the + // debugger to break into this routine (or a driver ISR) if + // the system hangs + // + if (Irql < CLOCK2_LEVEL) { + HalpEnableInterrupts(); + } + + // + // Dispatch to the secondary interrupt service routine. + // + SioHandler = (PSECONDARY_DISPATCH) + PCR->InterruptRoutine[DEVICE_VECTORS + interruptVector]; + + // + // A small bit of hack logic. We need a way to make sure that a + // "valid" registration has occured for this interrupt. We compare + // the handler with the "known" Unexpected interrupt handler routine. + // Use location 255 to get it. + // + if (SioHandler == (PSECONDARY_DISPATCH) PCR->InterruptRoutine[255]) { + HDBG(DBG_INTERRUPTS, + HalpDebugPrint("HalpHandleExternalInterrupt: %d not registered\n", + interruptVector);); + returnValue = FALSE; + } else { + SioInterrupt = CONTAINING_RECORD(SioHandler, + KINTERRUPT, DispatchCode[0]); + returnValue = SioHandler(SioInterrupt, + SioInterrupt->ServiceContext, + TrapFrame); + } + + // + // Now disable the PowerPC interrupts to provide protection + // for the exit portion of the interrupt handling. + // + HalpDisableInterrupts(); + + // + // indicate are exiting the interrupt handler... + // + SET_LEDS(0xf0 & ~(0x1)); + + // + // Now lower the IRQL + // + PCR->CurrentIrql = OldIrql; + RInterruptMask(CpuId) = OldMask; + WaitForRInterruptMask(CpuId); + HASSERT(RInterruptMask(CpuId) == + (Irql2Mask[PCR->CurrentIrql] & registeredInts[CpuId])); + return(returnValue); +} + +/* + * HalpHandleIpiInterrupt + * + * Clear the IPI and call the kernel's handler + */ +BOOLEAN +HalpHandleIpiInterrupt( + IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext, + IN PVOID TrapFrame + ) +{ + if (HalAcknowledgeIpi()) { + KeIpiInterrupt(TrapFrame); + return TRUE; + } + return FALSE; +} + + +int +PHalpInterruptSetup( VOID ) +{ + UCHAR DataByte,Isr; + + HalpSetIntPriorityMask(); + DataByte = 0; + ((PINITIALIZATION_COMMAND_1) &DataByte)->Icw4Needed = 1; + ((PINITIALIZATION_COMMAND_1) &DataByte)->InitializationFlag = 1; + + rMasterIntPort0 = DataByte; + rSlaveIntPort0 = DataByte; + + // + // The second intitialization control word sets the iterrupt vector to + // 0-15. + // + + DataByte = 0; + rMasterIntPort1 = DataByte; + FireSyncRegister(); + + DataByte = 0x08; + rSlaveIntPort1 = DataByte; + FireSyncRegister(); + + // + // The third initialization control word set the slave mode. + // The master ICW3 uses bit position and the slave ICW3 uses a number. + // + DataByte = 1 << SLAVE_IRQL_LEVEL; + rMasterIntPort1 = DataByte; + FireSyncRegister(); + + DataByte = SLAVE_IRQL_LEVEL; + rSlaveIntPort1 = DataByte; + FireSyncRegister(); + // + // The fourth initialization control word is used to specify normal + // end-of-interrupt mode and not special-fully-nested mode. + // + DataByte = 0; + ((PINITIALIZATION_COMMAND_4) &DataByte)->I80x86Mode = 1; + + // + // setup for auto end of interrupt mode in case of firepower + // + ((PINITIALIZATION_COMMAND_4) &DataByte)->AutoEndOfInterruptMode = 1; + + + rMasterIntPort1 = DataByte; + rSlaveIntPort1 = DataByte; + FireSyncRegister(); + + // + // Disable all of the interrupts except the slave. + // + + HalpSioInterrupt1Mask = (UCHAR)~(1 << SLAVE_IRQL_LEVEL); + + rMasterIntPort1 = DataByte; + + HalpSioInterrupt2Mask = 0xFF; + + rSlaveIntPort1 = DataByte; + +// define priority specifically IRQ 7 is set to priority 15, while IRQ 0 is set +// to 1. So, tell the SIO to affix the lowest priority to IRQ 7: 110 00 111 +// should do it: this sets the command to "set priority" and says IRQ 7 is +// the lowest priority [ 111 ]. + + DataByte=0xc7; + rMasterIntPort0 = DataByte; + rSlaveIntPort0 = DataByte; + +// Read the IRR: + DataByte=0x0a; + rMasterIntPort0 = DataByte; + Isr = rMasterIntPort0; + HDBG(DBG_GENERAL, HalpDebugPrint("Master: IRR is %x .. ",Isr);); + +// Read the ISR + DataByte=0x0b; + rMasterIntPort0 = DataByte; + Isr = rMasterIntPort0; + HDBG(DBG_GENERAL, HalpDebugPrint("ISR is %x .. \n",Isr);); + + // + // Set up the system error registers for use: Mask off Video ints, clear + // anything pending: + // + + // + // Clear the PCI Bus error cause register and mask-enable pci error ints + // + rPCIBusErrorCauseSet = 0x0; + rPCIBusErrorCause = 0xffffffff; + FireSyncRegister(); + + // + // Clear the Memory Bus error cause register and mask-enable memory + // error ints. Note: (breeze 3/6/95 ) I removed the earlier comments. + // + rErrorStatus0Set = 0x0; + rErrorStatus0 = 0xffffffff; + rErrorMask = + (ECC_CORRECTED|ECC_FAILED|ADDRESS_PARITY_ERROR|DATA_PARITY_ERROR| + MEM_PARITY_ERROR|INVALID_XACT); + + // + // IBM 604 Processors (revision 3.3) have parity problems. For these + // processors we unset DATA_PARITY to avoid dying from a false + // parity error. ES machines only used the Motorola processors + // so check for PowerTOP. + // + if ((SystemType == SYS_POWERTOP) && (ProcessorType == PPC_604)) { + if (HalpGetProcessorVersion() == 0x00040303) { + rErrorMask &= ~DATA_PARITY_ERROR; + } + } + FireSyncRegister(); + + // + // Clear the Video error register and disable video ints + // + rVidInt = 0xffffffff; + rVidIntMask = 0x00000000; + FireSyncRegister(); + + if (!HalpEnableInterruptHandler(&HalpPciErrorInt, + (PKSERVICE_ROUTINE) HalpMemoryHandler, + NULL, + NULL, + DEVICE_VECTORS + PCI_ERROR_NUM, + 28, + 28, + Latched, + FALSE, + 0, // Processor Number + FALSE, + InternalUsage, + DEVICE_VECTORS + PCI_ERROR_NUM)) { + KeBugCheck(HAL_INITIALIZATION_FAILED); + } + if (!HalpEnableInterruptHandler(&HalpBusErrorInt, + (PKSERVICE_ROUTINE) HalpMemoryHandler, + NULL, + NULL, + DEVICE_VECTORS + CPU_BUS_ERROR_NUM, + 28, + 28, + Latched, + FALSE, + 0, // Processor Number + FALSE, + InternalUsage, + DEVICE_VECTORS + CPU_BUS_ERROR_NUM)) { + KeBugCheck(HAL_INITIALIZATION_FAILED); + } + + if (SystemType == SYS_POWERPRO) { + if (!HalpEnableInterruptHandler(&HalpMemoryErrorInt, + (PKSERVICE_ROUTINE) HalpMemoryHandler, + NULL, + NULL, + DEVICE_VECTORS + MEMORY_ERROR_VIDEO_NUM, + 28, + 28, + Latched, + TRUE, // share with the video-in ISR... + // FALSE, + 0, // Processor Number + FALSE, + InternalUsage, + DEVICE_VECTORS + + MEMORY_ERROR_VIDEO_NUM)) { + KeBugCheck(HAL_INITIALIZATION_FAILED); + } + } + + return(0); +} + + + +// +// Initialize interrupts on processors other than the main (boot) processor. +// +// +ULONG +HalpInitInts( ULONG ProcessorNumber ) +{ + + ULONG Mask; + + HDBG(DBG_INTERNAL, + HalpDebugPrint("HalpInitInts: entered Cpu (%d)\n", ProcessorNumber)); + + // + // Make sure all the interrupts are masked off and any potential + // interrupts are cleared from the pending register. This is a percpu + // action and makes sure that this cpu starts off with a known + // interrupt state. + // + HalpDisableInterrupts(); + RInterruptMask(GetCpuId()) = ALL_INTS_OFF; + WaitForRInterruptMask(GetCpuId()); + + // + // Connect the external interrupt handler first, then the other + // handlers for external events may be installed.... + // + + PCR->InterruptRoutine[EXTERNAL_INTERRUPT_VECTOR] = + (PKINTERRUPT_ROUTINE) HalpHandleExternalInterrupt; + + // + // register the interrupt vector with the HAL + // + + HalpRegisterVector(InternalUsage, + EXTERNAL_INTERRUPT_VECTOR, + EXTERNAL_INTERRUPT_VECTOR, + HIGH_LEVEL); + + // + // Connect the IPI interrupt handler directly to the CPU dispatch table + // without registering with the kernel. The IPI interrupt has + // already been registered with the HAL by CPU 0 in HalpCreateSioStructures + // so we don't need to do it again here. + // + + PCR->InterruptRoutine[DEVICE_VECTORS + 31] = + (PKINTERRUPT_ROUTINE) HalpHandleIpiInterrupt; + + // + // Now enable the IPI interrupt on this CPU; the HAL must do this + // directly since we did not register with the kernel. + // It should be alright to call this routine directly rather than + // HalEnableSystemInterrupt because interrupts are disabled. + // + HalpEnableSioInterrupt(DEVICE_VECTORS + 31, Latched); + + HDBG(DBG_GENERAL, + HalpDebugPrint("HalpInitInts: Enabled IPI handler \n");); + + // + // Initialize the Machine Check interrupt handler + // + if (HalpEnableInterruptHandler(&HalpMachineCheckInterrupt, + HalpHandleMachineCheck, + NULL, + NULL, + MACHINE_CHECK_VECTOR, + MACHINE_CHECK_LEVEL, + MACHINE_CHECK_LEVEL, + Latched, + FALSE, + (CCHAR) ProcessorNumber, + FALSE, + InternalUsage, + MACHINE_CHECK_VECTOR + ) == FALSE) { + KeBugCheck(HAL_INITIALIZATION_FAILED); + } + + // Connect directly to the decrementer handler. This is done + // directly rather than thru HalpEnableInterruptHandler due to + // special handling required because the handler calls KdPollBreakIn(). + // + + + PCR->InterruptRoutine[DECREMENT_VECTOR] = + (PKINTERRUPT_ROUTINE) HalpHandleDecrementerInterrupt1; + + HalpUpdateDecrementer(1000); // Get those decrementer ticks going + + // + // Make sure the mask is set correctly at the mask register for this cpu: + // + Mask = RInterruptMask(GetCpuId()); + if (Mask != CPU_MESSAGE_INT) { + HalpDebugPrint("HalpInitInts: no CPU_MESSAGE_INT\n"); + } + + HDBG(DBG_INTERNAL, HalpDebugPrint("HalpInitInts: exit\n");); + return (0); +} + +// +// Some quick code for handling memory bus errors +// +BOOLEAN +HalpMemoryHandler( + IN PKINTERRUPT Interrupt, + IN PVOID ServiceContext, + IN PVOID TrapFrame + ) +{ + ULONG causeValue; + + // + // Proper handling of Memory, CPU and PCI errors. + // + switch ((Interrupt->Vector - DEVICE_VECTORS)) { + case MEMORY_ERROR_VIDEO_NUM: { + ULONG addr; + + // + // Read the register so that we can see it on a logic analyzer + // + causeValue = rErrorStatus0; + + // + // check for valid video-in interrupt if on a powertop system. If + // there is a valid video-in interrupt AND no valid cause other than + // a memory int, then return false and let the video driver handle + // this. + // If there is a cause, then handle the memory error cause + // regardless of the video-in case: + // + if ( causeValue == 0x0 ) { + return FALSE; // return false in any case since no + // cause was found!! + } + + // + // This is not a true error, just record it in the registry + // (recording in registry not implemented yet - sfk 2/20/95 - XXX) + // + // Don't bother checking for video-in interrupt here. If there is + // a video interrupt awaiting action, it should be picked up when + // the hal returns from interrupt since the uncleared int will be + // automatically re-asserted when cpu ints are re-enabled via the + // MSR(EE) action. + // + if (causeValue&ERROR_ECC_CORRECTED) { + ULONG rc; + ULONG count = 0, total = 0; + const PCHAR varname = "PARITY_ERROR"; + CHAR buf[80]; + + rErrorStatus0 |= ERROR_ECC_CORRECTED; + FireSyncRegister(); + + // + // record the error count to NVRAM as: + // # errors this boot, # errors forever + // + MemoryParityErrorsThisBoot++; + MemoryParityErrorsForever++; + sprintf(buf, "%d,%d", + MemoryParityErrorsThisBoot, + MemoryParityErrorsForever + ); + rc = HalSetEnvironmentVariable(MemoryParityErrorsVarName, buf); + if (ESUCCESS != rc) { + // what to do? + } + return TRUE; + } + // + // Now we panic: Make sure that in the process we don't fall + // into a loop of parity errors, so turn off memory parity errors, + // and the other errors as well: + // + rErrorMask &= ~(MEM_PARITY_ERROR | + INVALID_XACT + ); + + addr = (rErrorAddr0 & 0xff000000); + addr |= ((rErrorAddr1 & 0xff000000) >> 8); + addr |= ((rErrorAddr2 & 0xff000000) >> 16); + addr |= ((rErrorAddr3 & 0xff000000) >> 24); + + // + // Show the user what happened before we die + // + HalpForceDisplay("MEMORY_ERROR cause(0x%08x) at 0x%08x\n", + causeValue&0xff000000, addr); + + // + // Decode the cause into ascii strings for the user impatiently + // waiting for enlightenment from the dead machine: Before + // reincarnation, what major transgression did we commit? + // + + if( causeValue&ERROR_DATA_PARITY) { + HalpForceDisplay("DATA PARITY"); + } + if (causeValue&ERROR_MEM_PARITY) { + HalpForceDisplay(", MEMORY PARITY"); + } + if (causeValue&ERROR_ECC_FAILED) { + HalpForceDisplay(", ECC FAILED"); + } + if (causeValue&ERROR_INVALID_XACT) { + HalpForceDisplay(", Bad bus transaction: burst access to rom or io space?"); + } + HalpForceDisplay("\n\n"); + + // + // Clear the Memory Error. Since we only want one error + // of each type (max.), we mask off the error we just + // received. + // + rErrorMask &= ~causeValue; + rErrorStatus0 = causeValue; + FireSyncRegister(); + break; + + } + + case PCI_ERROR_NUM: + // + // Read the register so that we can see it on a logic analyzer + // + causeValue = rPCIBusErrorCause; + + // + // This is not a true error, just record it in the registry + // (recording in registry not implemented yet - sfk 2/20/95 - XXX) + // + if (causeValue&PCI_ERROR_DEV_TIMEOUT) { + rPCIBusErrorCause = PCI_ERROR_DEV_TIMEOUT; + FireSyncRegister(); + return TRUE; + } + + // This also may not be a true error; we must check the Pci status + // registers + if( causeValue& PCI_ERROR_TARGET_ABORT) { + // Scan the Primary Pci status registers + ULONG devs; + UCHAR buffer[PCI_COMMON_HDR_LENGTH]; + PPCI_COMMON_CONFIG PciData; + + PciData = (PPCI_COMMON_CONFIG) buffer; + for (devs = 0; devs < MAXIMUM_PCI_SLOTS; devs++) { + HalGetBusData ( + PCIConfiguration, + 0, + devs, + PciData, + PCI_COMMON_HDR_LENGTH + ); + if (PciData->VendorID == PCI_INVALID_VENDORID || + PCI_CONFIG_TYPE (PciData) != 0) { + continue; + } + + if (PciData->Status & PCI_STATUS_SIGNALED_TARGET_ABORT) { + // We can ignore this one; clear the error bits + rPCIBusErrorCause = PCI_ERROR_TARGET_ABORT; + PciData->Status = PCI_STATUS_SIGNALED_TARGET_ABORT; + HalSetBusDataByOffset( + PCIConfiguration, + 0, + devs, + &PciData->Status, + FIELD_OFFSET(PCI_COMMON_CONFIG, + Status), + 1); + FireSyncRegister(); + return TRUE; + } + } + } + + // + // Show the user what happened before we die + // + HalpForceDisplay("\nPCI_ERROR cause(0x%08x) at 0x%08x\n", + causeValue, rPCIBusErrorAddressRegister); + + if( causeValue& PCI_ERROR_SIGNALED_SYS) { + HalpForceDisplay("Address parity error on PCI bus\n"); + } + if( causeValue& PCI_ERROR_DATA_PARITY) { + HalpForceDisplay("Data parity error on PCI bus\n"); + } + if( causeValue& PCI_ERROR_DEV_TIMEOUT) { + HalpForceDisplay("Device Timeout: Master Aborted\n"); + } + if( causeValue& PCI_ERROR_TARGET_ABORT) { + HalpForceDisplay("Target Aborted or Master experienced a fatal error\n"); + } + HalpForceDisplay("\n\n"); + + // + // Clear the PCI Error + // + rPCIBusErrorCause = causeValue; + FireSyncRegister(); + break; + + case CPU_BUS_ERROR_NUM: + // + // Read the register so that we can see it on a logic analyzer + // + causeValue = rCPUBusErrorCause; + + // + // Show the user what happened before we die + // + HalpForceDisplay("CPU_ERROR cause(0x%08x) at 0x%08x\n", + causeValue, rCPUBusErrorAddressRegister); + + // + // Clear the Bus Error + // + rCPUBusErrorCause = causeValue; + FireSyncRegister(); + break; + + default: + // + // We should not be here, bug check because things are not sane + // if we got this far. + // + HalpForceDisplay("Unknown Bus Error (%d)\n", + (Interrupt->Vector - DEVICE_VECTORS)); + break; + + } // end of switch statement..... + + // + // If we make it to here, we just stop the system (cold). + // + KeBugCheck(NMI_HARDWARE_FAILURE); + + // + // This should never return but just in case + // + HalpDisableInterrupts(); +here: + goto here; + +} |