diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/nthals/halalpha/perf8254.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/nthals/halalpha/perf8254.c')
-rw-r--r-- | private/ntos/nthals/halalpha/perf8254.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/private/ntos/nthals/halalpha/perf8254.c b/private/ntos/nthals/halalpha/perf8254.c new file mode 100644 index 000000000..7a2160408 --- /dev/null +++ b/private/ntos/nthals/halalpha/perf8254.c @@ -0,0 +1,457 @@ +/*++ + +Copyright (c) 1992, 1993 Digital Equipment Corporation + +Module Name: + + perf82454.c + +Abstract: + + This module implements the interfaces that access the system + performance counter and the calibrated stall for systems that + use an external 8254 timer chip to implement the performance counter. + This module is suitable for use in multiprocessor systems. + +Author: + + Joe Notarangelo 14-Mar-1994 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "halp.h" +#include "eisa.h" + +// +// Declare the global that contains the current value of the performance +// counter. +// + +ULONG HalpLastTimer; +ULONGLONG HalpTimerWrapCount; + +// +// Declare globals used to control the access to and initialization of +// the performance counter. +// + +BOOLEAN HalpPerformanceCounterInitialized = FALSE; +ULONG HalpPerformanceCounterFrequency; +KSPIN_LOCK HalpPerformanceCounterSpinLock; + +#define TIMER_START_VALUE (0xffff) + +// +// Define local routine prototypes. +// + +VOID +HalpStartTimer( + VOID + ); + +ULONG +HalpReadTimerValue( + VOID + ); + +VOID +HalCalibratePerformanceCounter ( + IN volatile PLONG Number + ) + +/*++ + +Routine Description: + + This routine is responsible for synchronizing the performance + counter across all processors in the system configuration. + For an 8254-based performance counter all that is necessary is that + the counter be initialized. + +Arguments: + + Number - Supplies a pointer to count of the number of processors in + the configuration. + +Return Value: + + None. + +--*/ + +{ + PKPRCB Prcb = PCR->Prcb; + + // + // If this isn't the primary processor, then return. + // + + if( Prcb->Number != HAL_PRIMARY_PROCESSOR ){ + return; + } + + // + // If the counter has already been initialized then simply return. + // + + if( HalpPerformanceCounterInitialized == TRUE ){ + return; + } + + // + // Initialize the spinlock for controlling access to the counter. + // + + KeInitializeSpinLock( &HalpPerformanceCounterSpinLock ); + + // + // Set the frequency of the counter. + // + + HalpPerformanceCounterFrequency = TIMER_CLOCK_IN; + + // + // Initialize the wrap count. + // + + HalpTimerWrapCount = 0; + + // + // Initialize the counter and start it. + // + + HalpStartTimer(); + + HalpLastTimer = HalpReadTimerValue(); + + // + // Mark the counter as initialized. + // + + HalpPerformanceCounterInitialized = TRUE; + +} + + +LARGE_INTEGER +KeQueryPerformanceCounter ( + OUT PLARGE_INTEGER Frequency OPTIONAL + ) + +/*++ + +Routine Description: + + This routine returns the current performance counter value and the + performance counter frequency. + +Arguments: + + Frequency - Supplies an optional pointer to a variable which receives + the performance counter frequency in Hertz. + +Return Value: + + The current performance counter value is returned as the function + value. + +--*/ + +{ + + LARGE_INTEGER LocalPerformanceCount; + KIRQL OldIrql; + ULONG TimerValue; + + // + // Return 0 if the performance counter has not been initialized yet. + // + + if( HalpPerformanceCounterInitialized == FALSE ){ + LocalPerformanceCount.QuadPart = 0; + return LocalPerformanceCount; + } + + // + // Synchronize the calculation of the performance counter with the + // clock routine executing on the boot processor. + // + + KeRaiseIrql( CLOCK_LEVEL, &OldIrql ); + KiAcquireSpinLock( &HalpPerformanceCounterSpinLock ); + + // + // Read the current value of the timer count. + // + + TimerValue = HalpReadTimerValue(); + + // + // If the timer is greater than the last timer value then the timer + // has wrapped since the last time we have read it. + // + + if( TimerValue > HalpLastTimer ){ + + HalpTimerWrapCount += (1 << 15); + } + + HalpLastTimer = TimerValue; + + LocalPerformanceCount.QuadPart = HalpTimerWrapCount + + (TIMER_START_VALUE - TimerValue)/2; + + // + // Once the value is calculated synchronization is no longer + // required. + // + + KiReleaseSpinLock( &HalpPerformanceCounterSpinLock ); + KeLowerIrql( OldIrql ); + + // + // If the frequency parameter is specified, then return the frequency + // of the 8254 counter. + // + + if (ARGUMENT_PRESENT(Frequency) != FALSE) { + Frequency->LowPart = HalpPerformanceCounterFrequency; + Frequency->HighPart = 0; + } + + // + // Return the calculated performance count. + // + + return LocalPerformanceCount; + +} + + +VOID +HalpCheckPerformanceCounter( + VOID + ) +/*++ + +Routine Description: + + This function is called every system clock interrupt in order to + check for wrap of the performance counter. The function must handle + a wrap if it is detected. + + N.B. - This function must be called at CLOCK_LEVEL. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + + ULONG TimerValue; + + // + // Synchronize the performance counter check with any possible + // readers. + // + + KiAcquireSpinLock( &HalpPerformanceCounterSpinLock ); + + // + // Read the current value of the timer count. + // + + TimerValue = HalpReadTimerValue(); + + // + // If the timer is greater than the last timer value then the timer + // has wrapped since the last time we have read it. + // + + if( TimerValue > HalpLastTimer ){ + + HalpTimerWrapCount += (1 << 15); + } + + HalpLastTimer = TimerValue; + + KiReleaseSpinLock( &HalpPerformanceCounterSpinLock ); + + return; + +} + + +VOID +KeStallExecutionProcessor ( + IN ULONG MicroSeconds + ) + +/*++ + +Routine Description: + + This function stalll execution of the current processor for the specified + number of microseconds. + +Arguments: + + MicroSeconds - Supplies the number of microseconds that execution is to + be stalled. + +Return Value: + + None. + +--*/ + +{ + + LONG StallCyclesRemaining; // signed value + ULONG PreviousRpcc, CurrentRpcc; + + // + // Get the value of the RPCC as soon as we enter + // + + PreviousRpcc = HalpRpcc(); + + // + // Compute the number of cycles to stall + // + + StallCyclesRemaining = MicroSeconds * HalpClockMegaHertz; + + // + // Wait while there are stall cycles remaining. + // The accuracy of this routine is limited by the + // length of this while loop. + // + + while (StallCyclesRemaining > 0) { + + CurrentRpcc = HalpRpcc(); + + // + // The subtraction always works because the Rpcc + // is a wrapping long-word. If it wraps, we still + // get the positive number we want. + // + + StallCyclesRemaining -= (CurrentRpcc - PreviousRpcc); + + // + // remember this RPCC value + // + + PreviousRpcc = CurrentRpcc; + } + +} + + +VOID +HalpStartTimer( + VOID + ) +/*++ + +Routine Description: + + Start the timer used to maintain the performance count. The timer used + is counter 0 in timer 1 - it is an 8254-compatible timer. + +Arguments: + + None. + +Return Value: + + None. + +--*/ +{ + TIMER_CONTROL TimerControl; + + // + // Set the timer for counter 0 in binary mode. Write the counter + // with the LSB then MSB of the TIMER_START_VALUE. + // + + TimerControl.BcdMode = 0; + TimerControl.Mode = TM_SQUARE_WAVE; + TimerControl.SelectByte = SB_LSB_THEN_MSB; + TimerControl.SelectCounter = SELECT_COUNTER_0; + + WRITE_PORT_UCHAR( &((PEISA_CONTROL)HalpEisaControlBase)->CommandMode1, + *(PUCHAR)&TimerControl ); + + WRITE_PORT_UCHAR( &((PEISA_CONTROL)HalpEisaControlBase)->Timer1, + TIMER_START_VALUE & 0xff ); + + WRITE_PORT_UCHAR( &((PEISA_CONTROL)HalpEisaControlBase)->Timer1, + (TIMER_START_VALUE >> 8) & 0xff ); + + + return; + +} + + +ULONG +HalpReadTimerValue( + VOID + ) +/*++ + +Routine Description: + + Read the current value from the timer used to maintain the performance + count. The timer used is counter 0 in timer - it is an 8254-compatible + timer. + +Arguments: + + None. + +Return Value: + + The current count value of the timer is returned. + +--*/ +{ + UCHAR Lsb; + UCHAR Msb; + TIMER_CONTROL TimerControl; + + // + // Set the counter for a latched read, read it Lsb then Msb. + // + + TimerControl.BcdMode = 0; + TimerControl.Mode = 0; + TimerControl.SelectByte = SB_COUNTER_LATCH; + TimerControl.SelectCounter = SELECT_COUNTER_0; + + WRITE_PORT_UCHAR( &((PEISA_CONTROL)HalpEisaControlBase)->CommandMode1, + *(PUCHAR)&TimerControl ); + + Lsb = READ_PORT_UCHAR( &((PEISA_CONTROL)HalpEisaControlBase)->Timer1 ); + + Msb = READ_PORT_UCHAR( &((PEISA_CONTROL)HalpEisaControlBase)->Timer1 ); + + return (ULONG)( (Msb << 8) | Lsb ); + +} |