NT4/private/ntos/nthals/halalpha/perf8254.c
2020-09-30 17:12:29 +02:00

458 lines
8.5 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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 );
}