Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

433 lines
8.2 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
pmtimer.c
Abstract:
This module implements the code for ACPI-related timer
functions.
Author:
Jake Oshins (jakeo) March 28, 1997
Environment:
Kernel mode only.
Revision History:
Split from pmclock.asm due to PIIX4 bugs.
Forrest Foltz (forrestf) 23-Oct-2000
Ported from pmtimer.asm to pmtimer.c
--*/
#include "halcmn.h"
#define TSC 0x10
#define PM_TMR_FREQ 3579545
#define TIMER_ROUNDING 10000
#define __1MHz 1000000
//
// HalpCurrentTime is the value of the hardware timer. It is updated at
// every timer tick.
//
volatile ULONG64 HalpCurrentTime;
volatile ULONG HalpGlobalVolatile;
//
// HalpHardwareTimeRollover represents the maximum count + 1 of the
// hardware timer.
//
// The hardware is either 24- or 32-bits. HalpHardwareTimeRollover will
// therefore have a vale of either 0x1000000 or 0x100000000.
//
// ACPI generates an interrupt whenever the MSb of the hardware timer
// changes.
//
ULONG64 HalpHardwareTimeRollover;
ULONG64 HalpTimeBias = 0;
//
// HalpCurrentTimePort is the port number of the 32-bit hardware timer.
//
ULONG HalpCurrentTimePort;
FORCEINLINE
ULONG
HalpReadPmTimer (
VOID
)
{
ULONG value;
ASSERT(HalpCurrentTimePort != 0);
value = READ_PORT_ULONG(UlongToPtr(HalpCurrentTimePort));
return value;
}
ULONG64
HalpQueryPerformanceCounter (
VOID
)
{
ULONG64 currentTime;
ULONG hardwareTime;
ULONG lastHardwareTime;
//
// Get a local copy of HalpCurrentTime and the value of the hardware
// timer, in that order.
//
currentTime = HalpCurrentTime;
hardwareTime = HalpReadPmTimer();
//
// Extract the hardware portion of the currentTime.
//
lastHardwareTime = (ULONG)(currentTime & (HalpHardwareTimeRollover - 1));
//
// Replace the lastHardwareTime component of currentTime with the
// current hardware time.
//
currentTime ^= lastHardwareTime;
currentTime |= hardwareTime;
//
// Check and compensate for hardware timer rollover
//
if (lastHardwareTime > hardwareTime) {
currentTime += HalpHardwareTimeRollover;
}
return currentTime;
}
LARGE_INTEGER
KeQueryPerformanceCounter (
OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
)
{
LARGE_INTEGER value;
if (ARGUMENT_PRESENT(PerformanceFrequency)) {
PerformanceFrequency->QuadPart = PM_TMR_FREQ;
}
value.QuadPart = HalpQueryPerformanceCounter() + HalpTimeBias;
return value;
}
VOID
HalAcpiTimerCarry (
VOID
)
/*++
Routine Description:
This routine is called to service the PM timer carry interrupt
N.B. This function is called at interrupt time and assumes the
caller clears the interrupt
Arguments:
None
Return Value:
None
--*/
{
ULONG hardwareTime;
ULONG64 currentTime;
ULONG64 halfRollover;
currentTime = HalpCurrentTime;
hardwareTime = HalpReadPmTimer();
//
// ACPI generates an interrupt whenever the MSb of the hardware timer
// changes. Each interrupt represents, therefore, half a rollover.
//
halfRollover = HalpHardwareTimeRollover / 2;
currentTime += halfRollover;
//
// Make sure the MSb of the hardware matches the software MSb. Breaking
// into the debugger might have gotten these out of sync.
//
currentTime += halfRollover & (currentTime ^ hardwareTime);
//
// Finally, store the new current time back into the global
//
HalpCurrentTime = currentTime;
}
VOID
HalaAcpiTimerInit(
IN ULONG TimerPort,
IN BOOLEAN TimerValExt
)
{
HalpCurrentTimePort = TimerPort;
if (TimerValExt) {
HalpHardwareTimeRollover = 0x100000000;
} else {
HalpHardwareTimeRollover = 0x1000000;
}
}
VOID
HalpPmTimerSpecialStall(
IN ULONG Ticks
)
/*++
Routine Description:
Arguments:
Ticks - Number of PM timer ticks to stall
Return Value:
TRUE if we were able to stall for the correct interval,
otherwise FALSE
--*/
{
ULONG64 currentCounter;
ULONG64 lastCounter;
ULONG64 baseTime;
ULONG64 currentTime;
ULONG64 endTime;
ULONG64 newCurrentTime;
ASSERT(HalpHardwareTimeRollover != 0);
baseTime = 0;
lastCounter = HalpReadPmTimer();
endTime = lastCounter + Ticks;
do {
currentCounter = HalpReadPmTimer();
if (currentCounter < lastCounter) {
baseTime += HalpHardwareTimeRollover;
}
lastCounter = currentCounter;
currentTime = baseTime + currentCounter;
if (currentTime >= endTime) {
break;
}
HalpWasteTime(200);
} while (TRUE);
}
BOOLEAN
HalpPmTimerScaleTimers(
VOID
)
/*++
Routine Description:
Determines the frequency of the APIC timer, this routine is run
during initialization
Arguments:
None
Return Value:
None
--*/
{
ULONG eflags;
PHALPCR HalPCR;
PKPCR PCR;
ULONG ApicHz;
ULONGLONG TscHz;
ULONG RoundApicHz;
ULONGLONG RoundTscHz;
ULONGLONG RoundTscMhz;
ULONGLONG currentTime;
ULONGLONG startTime;
ULONGLONG endTime;
//
// Disable interrupts on this processor
//
eflags = HalpDisableInterrupts();
PCR = KeGetPcr();
HalPCR = HalpGetCurrentHalPcr();
//
// Configure APIC timer
//
LOCAL_APIC(LU_TIMER_VECTOR) = INTERRUPT_MASKED |
PERIODIC_TIMER |
APIC_PROFILE_VECTOR;
LOCAL_APIC(LU_DIVIDER_CONFIG) = LU_DIVIDE_BY_1;
//
// Make sure the write has completed, zero the perf counter,
// and insert a processor fence
//
HalPCR->PerfCounter = 0;
LOCAL_APIC(LU_DIVIDER_CONFIG);
PROCESSOR_FENCE;
//
// Reset APIC counter and TSC
//
LOCAL_APIC(LU_INITIAL_COUNT) = 0xFFFFFFFF;
WRMSR(TSC, 0);
//
// Stall for an eighth of a second
//
HalpPmTimerSpecialStall(PM_TMR_FREQ / 8);
TscHz = ReadTimeStampCounter() * 8;
ApicHz = (0 - LOCAL_APIC(LU_CURRENT_COUNT)) * 8;
//
// Round APIC frequency
//
RoundApicHz = ((ApicHz + (TIMER_ROUNDING / 2)) / TIMER_ROUNDING) *
TIMER_ROUNDING;
HalPCR->ApicClockFreqHz = RoundApicHz;
//
// Round TSC frequency
//
RoundTscHz = ((TscHz + (TIMER_ROUNDING / 2)) / TIMER_ROUNDING) *
TIMER_ROUNDING;
HalPCR->TSCHz = RoundTscHz;
//
// Convert TSC frequency to MHz
//
RoundTscMhz = (RoundTscHz + (__1MHz / 2)) / __1MHz;
PCR->StallScaleFactor = (ULONG)RoundTscMhz;
HalPCR->ProfileCountDown = RoundApicHz;
LOCAL_APIC(LU_INITIAL_COUNT) = RoundApicHz;
//
// Restore interrupt state
//
HalpRestoreInterrupts(eflags);
return TRUE;
}
VOID
HalpWasteTime (
ULONG Ticks
)
{
ULONG i;
for (i = 0; i < Ticks; i++) {
HalpGlobalVolatile *= i;
}
}
VOID
#if defined(MMTIMER)
HalpPmTimerCalibratePerfCount (
#else
HalCalibratePerformanceCounter (
#endif
IN LONG volatile *Number,
IN ULONGLONG NewCount
)
/*++
Routine Description:
This routine sets the performance counter value for the current
processor to the specified value. The reset is done such that the
resulting value is closely synchronized with other processors in
the configuration.
Arguments:
Number - Supplies a pointer to count of the number of processors in
the configuration.
NewCount - Supplies the value to synchronize the counter to
Return Value:
None.
--*/
{
ULONG64 CurrentTime;
if(KeGetCurrentProcessorNumber() == 0) {
CurrentTime = HalpQueryPerformanceCounter();
HalpTimeBias = NewCount - CurrentTime;
}
InterlockedDecrement(Number);
//
// Wait for all processors to signal
//
while (*Number > 0) {
}
}