// "@(#) NEC r98clock.s 1.10 95/02/20 17:04:37" // TITLE("Interval and Profile Clock Interrupts") //++ // // Copyright (c) 1991-1994 Microsoft Corporation // // Module Name: // // r98clock.s // // Abstract: // // This module implements the code necessary to field and process the // interval and profile clock interrupts on a MIPS R4000 system. // //-- // // Original source: Build Number 1.612 // // Modify for R98(MIPS/R4400) // //*********************************************************************** // // M001 94.03/16-5/31 T.Samezima // // change header file from duo to r98 // change value of use to update of parformance counter // initial value of count register // // del clear interrupt // //*********************************************************************** // // S002 94.6/13 T.Samezima // // Del Compile err // // //*********************************************************************** // // S003 94.7/19 T.Samezima // // Chg Function interface change // //*********************************************************************** // // S004 94.7/19 T.Samezima // // Bug PMC register address is not change of KSEG1_BASE // //*********************************************************************** // // S005 94.10/12 T.Samezima // // Fix Version Up at build807 // // S006 '94.10/14 T.Samezima // Chg Logic of set interval count // // S007 '94.01/11 T.Samezima // Del delete 'if NT_UP'. // // S008 '94.01/16 T.Samezima // Add Check ECC 1bit err flag and enable ECC 1bit err. // // #include "halmips.h" // Start M001 #if defined(_R98_) #include "r98def.h" // #include "r98reg.h" // S002 #else // if defined(_R98_) #if defined(_DUO_) #include "duodef.h" #endif #if defined(_JAZZ_) #include "jazzdef.h" #endif #endif // #if !defined(_R98_) // End M001 #define ECC_ERROR_COUNT_LIMIT 1 // S008 SBTTL("System Clock Interrupt - Processor 0") //++ // // Routine Description: // // This routine is entered as the result of an interrupt generated by // the interval timer. Its function is to acknowledge the interrupt and // transfer control to the standard system routine to update the system // time and the execution time of the current thread and process. // // Arguments: // // a0 - Supplies a pointer to a trap frame. // S003 // // Return Value: // // None. // //-- .struct 0 CiArgs: .space 4 * 4 // saved arguments .space 3 * 4 // fill CiRa: .space 4 // saved return address CiFrameLength: // NESTED_ENTRY(HalpClockInterrupt0, CiFrameLength, zero) subu sp,sp,CiFrameLength // allocate stack frame sw ra,CiRa(sp) // save return address PROLOGUE_END // Start M001 #if !defined(_R98_) .set noreorder #if defined(_DUO_) lw t0,DMA_VIRTUAL_BASE + 0x58 // acknowledge timer interrupt #endif #if defined(_JAZZ_) lw t0,DMA_VIRTUAL_BASE + 0x230 // acknowledge timer interrupt #endif .set reorder #endif // #if !defined(_R98_) // End M001 // S003 // move a0,s8 // set address of trap frame lw a1,HalpCurrentTimeIncrement // set current time increment lw t0,__imp_KeUpdateSystemTime // update system time // S005 jal t0 // // S005 // // The following code is a work around for a bug in the Fusion machines // where the clock interrupt is not dismissed by reading the acknowledge // register. // // Start M001 #if !defined(_R98_) #if defined(_JAZZ_) .set noreorder .set noat mfc0 t0,cause // read the cause register lw t1,HalpEisaControlBase // get EISA control base address sll t0,t0,31 - (CAUSE_INTPEND + CLOCK_LEVEL - 1) // isolate clock bit bgez t0,10f // if gez, no clock interrupt pending li t2,0x2 // get NMI port enable bit lb t3,0x70(t1) // save EISA NMI interrupt disable lb t4,0x461(t1) // save EISA extended NMI status sb zero,0x70(t1) // clear EISA NMI interrupt disable sb t2,0x461(t1) // set EISA NMI port enable sb zero,0x462(t1) // generate EISA NMI interrupt sb zero,0x461(t1) // clear EISA extended NMI status sb t2,0x461(t1) // lb zero,0x461(t1) // synchronize clear operatin sb t3,0x70(t1) // restore EISA NMI interupt disable sb t4,0x461(t1) // restore EISA exteneed NMI status lb zero,0x461(t1) // synchronize restore operation .set at .set reorder 10: // #endif #endif // #if !defined(_R98_) // End M001 // S008 vvv // // Check ECC 1bit error flag. // lw t0,HalpECC1bitDisableTime // get value of disable time beq zero,t0,10f // if ne, check ecc 1bit lw t1,HalpCurrentTimeIncrement // get current time increment subu t0,t0,t1 // declement disable time sw t0,HalpECC1bitDisableTime // blez t0,5f // if lez, enable ecc 1bit beq zero,zero,10f // not lez, 5: sw zero,HalpECC1bitDisableTime // clear disable time li t0,ECC_ERROR_COUNT_LIMIT // set new flag sw t0,HalpECC1bitDisableFlag // la t1,KSEG1_BASE+SIC_PHYSICAL_BASE+SIC_DATA_OFFSET+SIC_SET0_OFFSET sw zero,0x0(t1) // enable ECC 1bit error lw t0,HalpECC1bitScfrBuffer // check connect to SIC1 andi t0,SCFR_SIC_SET1_CONNECT // bne t0,zero,10f // la t1,KSEG1_BASE+SIC_PHYSICAL_BASE+SIC_DATA_OFFSET+SIC_SET1_OFFSET sw zero,0x0(t1) // enable ECC 1bit error // S008 ^^^ // // At each clock interrupt the next time increment is moved to the current // time increment to "pipeline" the update of the current increment at the // correct time. If the next interval count is nonzero, then the new time // increment is moved to the next time increment and the next interval count // register is loaded with the specified interval count minus one (i.e., ms). // 10: lw t0,KdDebuggerEnabled // get address of debugger enable // S008 lw t1,HalpNextIntervalCount // get next interval count lw t2,HalpNextTimeIncrement // get the next increment value lbu t0,0(t0) // get debugger enable flag lw t3,HalpNewTimeIncrement // get new new time increment value lw ra,CiRa(sp) // restore return address or t4,t1,t0 // set interval count or debugger? sw t2,HalpCurrentTimeIncrement // set current increment value bne zero,t4,20f // if ne, interval change or debugger addu sp,sp,CiFrameLength // deallocate stack frame j ra // return // // The interval count must be changed or the debugger is enabled. // 20: sw zero,HalpNextIntervalCount // clear next interval count beq zero,t1,30f // if eq, not interval count change subu t1,t1,1 // compute millisecond interval count // Start M001 .set noreorder #if !defined(_R98_) #if defined(_DUO_) sw t1,DMA_VIRTUAL_BASE + 0x1a8 // set next interval count #endif #if defined(_JAZZ_) sw t1,DMA_VIRTUAL_BASE + 0x228 // set next interval count #endif # else // #if !defined(_R98_) sw t1,KSEG1_BASE+PMC_PHYSICAL_BASE1+PMC_LOCAL_OFFSET+0x40(zero) // S004 #endif // #if !defined(_R98_) .set reorder // End M001 sw t3,HalpNextTimeIncrement // set next time increment value 30: beq zero,t0,40f // if eq, debugger not enabled jal KdPollBreakIn // check if breakin is requested beq zero,v0,40f // if eq, no breakin requested li a0,DBG_STATUS_CONTROL_C // break in and send jal DbgBreakPointWithStatus // status to debugger 40: lw ra,CiRa(sp) // restore return address addu sp,sp,CiFrameLength // deallocate stack frame j ra // return .end HalpClockInterrupt0 SBTTL("System Clock Interrupt - Processor N") //++ // // Routine Description: // // This routine is entered as the result of an interrupt generated by // the interval timer. Its function is to acknowledge the interrupt // and transfer control to the standard system routine to update the // execution time of the current thread and process. // // Arguments: // // a0 - Supplies a pointer to a trap frame. // S003 // // Return Value: // // None. // //-- LEAF_ENTRY(HalpClockInterrupt1) #if !defined(_R98_) #if defined(_DUO_) lw t0,DMA_VIRTUAL_BASE + 0x58 // acknowledge timer interrupt move a0,s8 // set address of trap frame j KeUpdateRunTime // update system time #endif #else // #if !defined(_R98_) lw t1,KiPcr + PcPrcb(zero) // get current processor block address // S006 vvv la t2,HalpChangeIntervalFlg // get change flag of timer interval lbu t1,PbNumber(t1) // get processor number add t2,t1,t2 // check flag lb t1,0x0(t2) // get change flag of this CPU beq t1,zero,10f // if eq, no change timer interval sb zero,0x0(t2) // clear change flag of timer interval // S006 ^^^ lw t1,HalpChangeIntervalCount // get next interval count sw t1,KSEG1_BASE+PMC_PHYSICAL_BASE1+PMC_LOCAL_OFFSET+0x40(zero) // S004 // Start S003, S005 //10: move a0,s8 // set address of trap frame 10: lw t1,__imp_KeUpdateRunTime // update system runtime j t1 // // End S003, S005 #endif // #if !defined(_R98_) .end HalpClockInterrupt1 SBTTL("Profile Clock Interrupt") //++ // // Routine Description: // // This routine is entered as the result of an interrupt generated by the // profile clock. Its function is to acknowledge the profile interrupt, // compute the next compare value, update the performance counter, and // transfer control to the standard system routine to process any active // profiles. // // Arguments: // // a0 - Supplies a pointer to a trap frame. // S003 // // Return Value: // // None. // //-- LEAF_ENTRY(HalpProfileInterrupt) .set noreorder .set noat // Start M001 mfc0 t0,count // get current count value addu t1,zero,3 // set initial count value mtc0 t1,count // set new count register value // End M001 .set at .set reorder #if 0 // S007 //#if defined(NT_UP) la t1,HalpPerformanceCounter // get performance counter address //#else #endif lw t1,KiPcr + PcPrcb(zero) // get current processor block address la t2,HalpPerformanceCounter // get performance counter address lbu t1,PbNumber(t1) // get processor number sll t1,t1,3 // compute address of performance count addu t1,t1,t2 // // #endif // S007 lw t2,LiLowPart(t1) // get low part of performance count lw t3,LiHighPart(t1) // get high part of performance count addu t2,t2,t0 // update low part of performance count sw t2,LiLowPart(t1) // store low part of performance count sltu t4,t2,t0 // generate carry into high part addu t3,t3,t4 // update high part of performance count sw t3,LiHighPart(t1) // store high part of performance count // move a0,s8 // set address of trap frame // S003 lw t4,__imp_KeProfileInterrupt // process profile interrupt // S005 j t4 // // S005 .end HalpProfileInterrupt SBTTL("Read Count Register") //++ // // ULONG // HalpReadCountRegister ( // VOID // ); // // Routine Description: // // This routine reads the current value of the count register and // returns the value. // // Arguments: // // None. // // Return Value: // // Current value of the count register. // //-- LEAF_ENTRY(HalpReadCountRegister) .set noreorder .set noat mfc0 v0,count // get count register value .set at .set reorder j ra // return .end HalpReadCountRegister SBTTL("Write Compare Register And Clear") //++ // // ULONG // HalpWriteCompareRegisterAndClear ( // IN ULONG Value // ); // // Routine Description: // // This routine reads the current value of the count register, writes // the value of the compare register, clears the count register, and // returns the previous value of the count register. // // Arguments: // // Value - Supplies the value written to the compare register. // // Return Value: // // Previous value of the count register. // //-- LEAF_ENTRY(HalpWriteCompareRegisterAndClear) .set noreorder .set noat mfc0 v0,count // get count register value mtc0 a0,compare // set compare register value li t0,7 // set lost cycle count mtc0 t0,count // set count register to zero .set at .set reorder j ra // return .end HalpWriteCompareRegisterAndClear