472 lines
13 KiB
ArmAsm
472 lines
13 KiB
ArmAsm
// "@(#) 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
|