reactos/hal/halx86/apic/tsc.c

178 lines
4.4 KiB
C

/*
* PROJECT: ReactOS HAL
* LICENSE: GPL - See COPYING in the top level directory
* FILE: hal/halx86/apic/tsc.c
* PURPOSE: HAL Routines for TSC handling
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <hal.h>
#include "tsc.h"
#include "apicp.h"
#define NDEBUG
#include <debug.h>
LARGE_INTEGER HalpCpuClockFrequency = {{INITIAL_STALL_COUNT * 1000000}};
UCHAR TscCalibrationPhase;
ULONG64 TscCalibrationArray[NUM_SAMPLES];
#define RTC_MODE 6 /* Mode 6 is 1024 Hz */
#define SAMPLE_FREQUENCY ((32768 << 1) >> RTC_MODE)
/* PRIVATE FUNCTIONS *********************************************************/
static
ULONG64
DoLinearRegression(
ULONG XMax,
ULONG64 *ArrayY)
{
ULONG X, SumXX;
ULONG64 SumXY;
/* Calculate the sum of the squares of X */
SumXX = (XMax * (XMax + 1) * (2*XMax + 1)) / 6;
/* Calculate the sum of the differences to the first value
weighted by x */
for (SumXY = 0, X = 1; X <= XMax; X++)
{
SumXY += X * (ArrayY[X] - ArrayY[0]);
}
/* Account for sample frequency */
SumXY *= SAMPLE_FREQUENCY;
/* Return the quotient of the sums */
return (SumXY + (SumXX/2)) / SumXX;
}
VOID
NTAPI
HalpInitializeTsc(VOID)
{
ULONG_PTR Flags;
PVOID PreviousHandler;
UCHAR RegisterA, RegisterB;
/* Check if the CPU supports RDTSC */
if (!(KeGetCurrentPrcb()->FeatureBits & KF_RDTSC))
{
KeBugCheck(HAL_INITIALIZATION_FAILED);
}
/* Save flags and disable interrupts */
Flags = __readeflags();
_disable();
/* Enable the periodic interrupt in the CMOS */
RegisterB = HalpReadCmos(RTC_REGISTER_B);
HalpWriteCmos(RTC_REGISTER_B, RegisterB | RTC_REG_B_PI);
/* Modify register A to RTC_MODE to get SAMPLE_FREQUENCY */
RegisterA = HalpReadCmos(RTC_REGISTER_A);
RegisterA = (RegisterA & 0xF0) | RTC_MODE;
HalpWriteCmos(RTC_REGISTER_A, RegisterA);
/* Save old IDT entry */
PreviousHandler = KeQueryInterruptHandler(APIC_CLOCK_VECTOR);
/* Set the calibration ISR */
KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, TscCalibrationISR);
/* Reset TSC value to 0 */
__writemsr(MSR_RDTSC, 0);
/* Enable the timer interrupt */
HalEnableSystemInterrupt(APIC_CLOCK_VECTOR, CLOCK_LEVEL, Latched);
/* Read register C, so that the next interrupt can happen */
HalpReadCmos(RTC_REGISTER_C);
/* Wait for completion */
_enable();
while (TscCalibrationPhase < NUM_SAMPLES) _ReadWriteBarrier();
_disable();
/* Disable the periodic interrupt in the CMOS */
HalpWriteCmos(RTC_REGISTER_B, RegisterB & ~RTC_REG_B_PI);
/* Disable the timer interrupt */
HalDisableSystemInterrupt(APIC_CLOCK_VECTOR, CLOCK_LEVEL);
/* Restore the previous handler */
KeRegisterInterruptHandler(APIC_CLOCK_VECTOR, PreviousHandler);
/* Calculate an average, using simplified linear regression */
HalpCpuClockFrequency.QuadPart = DoLinearRegression(NUM_SAMPLES - 1,
TscCalibrationArray);
/* Restore flags */
__writeeflags(Flags);
}
VOID
NTAPI
HalpCalibrateStallExecution(VOID)
{
// Timer interrupt is now active
HalpInitializeTsc();
KeGetPcr()->StallScaleFactor = (ULONG)(HalpCpuClockFrequency.QuadPart / 1000000);
}
/* PUBLIC FUNCTIONS ***********************************************************/
LARGE_INTEGER
NTAPI
KeQueryPerformanceCounter(
OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
{
LARGE_INTEGER Result;
/* Make sure it's calibrated */
ASSERT(HalpCpuClockFrequency.QuadPart != 0);
/* Does the caller want the frequency? */
if (PerformanceFrequency)
{
/* Return tsc frequency */
*PerformanceFrequency = HalpCpuClockFrequency;
}
/* Return the current value */
Result.QuadPart = __rdtsc();
return Result;
}
VOID
NTAPI
KeStallExecutionProcessor(ULONG MicroSeconds)
{
ULONG64 StartTime, EndTime;
/* Get the initial time */
StartTime = __rdtsc();
/* Calculate the ending time */
EndTime = StartTime + KeGetPcr()->StallScaleFactor * MicroSeconds;
/* Loop until time is elapsed */
while (__rdtsc() < EndTime);
}
VOID
NTAPI
HalCalibratePerformanceCounter(
IN volatile PLONG Count,
IN ULONGLONG NewCount)
{
UNIMPLEMENTED;
ASSERT(FALSE);
}