2011-09-06 21:01:49 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: ReactOS HAL
|
|
|
|
* LICENSE: GNU GPL - See COPYING in the top level directory
|
2015-11-04 13:30:52 +00:00
|
|
|
* FILE: hal/halx86/apic/rtctimer.c
|
2011-09-06 21:01:49 +00:00
|
|
|
* PURPOSE: HAL APIC Management and Control Code
|
|
|
|
* PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
|
2019-12-31 08:39:55 +00:00
|
|
|
* REFERENCES: https://wiki.osdev.org/RTC
|
|
|
|
* https://forum.osdev.org/viewtopic.php?f=13&t=20825&start=0
|
|
|
|
* http://www.bioscentral.com/misc/cmosmap.htm
|
2011-09-06 21:01:49 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
|
|
|
|
#include <hal.h>
|
2021-07-07 19:58:23 +00:00
|
|
|
#include "apicp.h"
|
2024-02-24 10:05:12 +00:00
|
|
|
#include <smp.h>
|
2011-09-06 21:01:49 +00:00
|
|
|
#define NDEBUG
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
|
2021-07-16 09:42:45 +00:00
|
|
|
static const UCHAR RtcMinimumClockRate = 6; /* Minimum rate 6: 1024 Hz / 0.97 ms */
|
|
|
|
static const UCHAR RtcMaximumClockRate = 10; /* Maximum rate 10: 64 Hz / 15.6 ms */
|
|
|
|
static UCHAR HalpCurrentClockRate = 10; /* Initial rate 10: 64 Hz / 15.6 ms */
|
|
|
|
static ULONG HalpCurrentTimeIncrement;
|
|
|
|
static ULONG HalpMinimumTimeIncrement;
|
|
|
|
static ULONG HalpMaximumTimeIncrement;
|
|
|
|
static ULONG HalpCurrentFractionalIncrement;
|
|
|
|
static ULONG HalpRunningFraction;
|
|
|
|
static BOOLEAN HalpSetClockRate;
|
|
|
|
static UCHAR HalpNextClockRate;
|
2011-09-06 21:01:49 +00:00
|
|
|
|
2019-12-31 08:39:55 +00:00
|
|
|
/*!
|
2024-05-02 10:21:13 +00:00
|
|
|
\brief Converts the CMOS RTC rate into the time increment in 0.1ns intervals.
|
2011-09-06 21:01:49 +00:00
|
|
|
|
2021-07-16 09:42:45 +00:00
|
|
|
Rate Frequency Interval (ms) Precise increment (0.1ns)
|
|
|
|
------------------------------------------------------
|
2019-12-31 08:39:55 +00:00
|
|
|
0 disabled
|
2021-07-16 09:42:45 +00:00
|
|
|
1 32768 0.03052 305,175
|
|
|
|
2 16384 0.06103 610,351
|
|
|
|
3 8192 0.12207 1,220,703
|
|
|
|
4 4096 0.24414 2,441,406
|
|
|
|
5 2048 0.48828 4,882,812
|
|
|
|
6 1024 0.97656 9,765,625 <- minimum
|
|
|
|
7 512 1.95313 19,531,250
|
|
|
|
8 256 3.90625 39,062,500
|
|
|
|
9 128 7.8125 78,125,000
|
|
|
|
10 64 15.6250 156,250,000 <- maximum / default
|
|
|
|
11 32 31.25 312,500,000
|
|
|
|
12 16 62.5 625,000,000
|
|
|
|
13 8 125 1,250,000,000
|
|
|
|
14 4 250 2,500,000,000
|
|
|
|
15 2 500 5,000,000,000
|
2019-12-31 08:39:55 +00:00
|
|
|
|
|
|
|
*/
|
2011-09-06 21:01:49 +00:00
|
|
|
FORCEINLINE
|
2014-05-04 09:39:44 +00:00
|
|
|
ULONG
|
2021-07-16 09:42:45 +00:00
|
|
|
RtcClockRateToPreciseIncrement(UCHAR Rate)
|
2011-09-06 21:01:49 +00:00
|
|
|
{
|
2019-12-31 08:39:55 +00:00
|
|
|
/* Calculate frequency */
|
2021-07-16 09:42:45 +00:00
|
|
|
ULONG Frequency = 32768 >> (Rate - 1);
|
2019-12-31 08:39:55 +00:00
|
|
|
|
2021-07-16 09:42:45 +00:00
|
|
|
/* Calculate interval in 0.1ns interval: Interval = (1 / Frequency) * 10,000,000,000 */
|
|
|
|
return 10000000000ULL / Frequency;
|
2011-09-06 21:01:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
RtcSetClockRate(UCHAR ClockRate)
|
|
|
|
{
|
|
|
|
UCHAR RegisterA;
|
2021-07-16 09:42:45 +00:00
|
|
|
ULONG PreciseIncrement;
|
2011-09-06 21:01:49 +00:00
|
|
|
|
2011-09-09 15:42:59 +00:00
|
|
|
/* Update the global values */
|
2021-07-16 09:42:45 +00:00
|
|
|
HalpCurrentClockRate = ClockRate;
|
|
|
|
PreciseIncrement = RtcClockRateToPreciseIncrement(ClockRate);
|
|
|
|
HalpCurrentTimeIncrement = PreciseIncrement / 1000;
|
|
|
|
HalpCurrentFractionalIncrement = PreciseIncrement % 1000;
|
2011-09-09 15:42:59 +00:00
|
|
|
|
2011-09-07 21:41:50 +00:00
|
|
|
/* Acquire CMOS lock */
|
|
|
|
HalpAcquireCmosSpinLock();
|
|
|
|
|
2011-09-06 21:01:49 +00:00
|
|
|
// TODO: disable NMI
|
|
|
|
|
|
|
|
/* Read value of register A */
|
|
|
|
RegisterA = HalpReadCmos(RTC_REGISTER_A);
|
|
|
|
|
|
|
|
/* Change lower 4 bits to new rate */
|
|
|
|
RegisterA &= 0xF0;
|
|
|
|
RegisterA |= ClockRate;
|
|
|
|
|
|
|
|
/* Write the new value */
|
|
|
|
HalpWriteCmos(RTC_REGISTER_A, RegisterA);
|
|
|
|
|
2011-09-07 21:41:50 +00:00
|
|
|
/* Release CMOS lock */
|
|
|
|
HalpReleaseCmosSpinLock();
|
2011-09-06 21:01:49 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 19:44:01 +00:00
|
|
|
CODE_SEG("INIT")
|
2011-09-06 21:01:49 +00:00
|
|
|
VOID
|
|
|
|
NTAPI
|
|
|
|
HalpInitializeClock(VOID)
|
|
|
|
{
|
2011-09-11 09:34:50 +00:00
|
|
|
ULONG_PTR EFlags;
|
2011-09-06 21:01:49 +00:00
|
|
|
UCHAR RegisterB;
|
2011-09-11 09:34:50 +00:00
|
|
|
|
|
|
|
/* Save EFlags and disable interrupts */
|
|
|
|
EFlags = __readeflags();
|
|
|
|
_disable();
|
|
|
|
|
2011-09-06 21:01:49 +00:00
|
|
|
// TODO: disable NMI
|
|
|
|
|
2011-09-07 21:41:50 +00:00
|
|
|
/* Acquire CMOS lock */
|
|
|
|
HalpAcquireCmosSpinLock();
|
|
|
|
|
2011-09-06 21:01:49 +00:00
|
|
|
/* Enable the periodic interrupt in the CMOS */
|
|
|
|
RegisterB = HalpReadCmos(RTC_REGISTER_B);
|
|
|
|
HalpWriteCmos(RTC_REGISTER_B, RegisterB | RTC_REG_B_PI);
|
|
|
|
|
2011-09-07 21:41:50 +00:00
|
|
|
/* Release CMOS lock */
|
|
|
|
HalpReleaseCmosSpinLock();
|
|
|
|
|
2011-09-09 15:42:59 +00:00
|
|
|
/* Set initial rate */
|
2021-07-16 09:42:45 +00:00
|
|
|
RtcSetClockRate(HalpCurrentClockRate);
|
2011-09-08 08:15:39 +00:00
|
|
|
|
2011-09-11 09:34:50 +00:00
|
|
|
/* Restore interrupt state */
|
|
|
|
__writeeflags(EFlags);
|
|
|
|
|
2021-07-16 09:42:45 +00:00
|
|
|
/* Calculate minumum and maximum increment */
|
|
|
|
HalpMinimumTimeIncrement = RtcClockRateToPreciseIncrement(RtcMinimumClockRate) / 1000;
|
|
|
|
HalpMaximumTimeIncrement = RtcClockRateToPreciseIncrement(RtcMaximumClockRate) / 1000;
|
|
|
|
|
2011-09-09 15:42:59 +00:00
|
|
|
/* Notify the kernel about the maximum and minimum increment */
|
2021-07-16 09:42:45 +00:00
|
|
|
KeSetTimeIncrement(HalpMaximumTimeIncrement, HalpMinimumTimeIncrement);
|
2011-09-09 15:42:59 +00:00
|
|
|
|
2021-07-16 09:45:39 +00:00
|
|
|
/* Enable the timer interrupt */
|
|
|
|
HalEnableSystemInterrupt(APIC_CLOCK_VECTOR, CLOCK_LEVEL, Latched);
|
2011-09-11 09:34:50 +00:00
|
|
|
|
2011-09-08 08:15:39 +00:00
|
|
|
DPRINT1("Clock initialized\n");
|
2011-09-06 21:01:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
|
|
FASTCALL
|
|
|
|
HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
|
|
|
|
{
|
|
|
|
ULONG LastIncrement;
|
|
|
|
KIRQL Irql;
|
|
|
|
|
|
|
|
/* Enter trap */
|
|
|
|
KiEnterInterruptTrap(TrapFrame);
|
2011-09-16 09:39:41 +00:00
|
|
|
#ifdef _M_AMD64
|
|
|
|
/* This is for debugging */
|
|
|
|
TrapFrame->ErrorCode = 0xc10c4;
|
|
|
|
#endif
|
2011-09-07 21:41:50 +00:00
|
|
|
|
2011-09-06 21:01:49 +00:00
|
|
|
/* Start the interrupt */
|
2021-07-07 19:58:23 +00:00
|
|
|
if (!HalBeginSystemInterrupt(CLOCK_LEVEL, APIC_CLOCK_VECTOR, &Irql))
|
2011-09-06 21:01:49 +00:00
|
|
|
{
|
2011-09-16 09:39:41 +00:00
|
|
|
/* Spurious, just end the interrupt */
|
2024-12-05 14:51:26 +00:00
|
|
|
#ifdef _M_IX86
|
2011-09-16 09:39:41 +00:00
|
|
|
KiEoiHelper(TrapFrame);
|
2024-12-05 14:51:26 +00:00
|
|
|
#endif
|
|
|
|
return;
|
2011-09-16 09:39:41 +00:00
|
|
|
}
|
2011-09-06 21:01:49 +00:00
|
|
|
|
2011-09-16 09:39:41 +00:00
|
|
|
/* Read register C, so that the next interrupt can happen */
|
2016-02-04 20:42:07 +00:00
|
|
|
HalpReadCmos(RTC_REGISTER_C);
|
2011-09-06 21:01:49 +00:00
|
|
|
|
2011-09-16 09:39:41 +00:00
|
|
|
/* Save increment */
|
|
|
|
LastIncrement = HalpCurrentTimeIncrement;
|
2011-09-06 21:01:49 +00:00
|
|
|
|
2021-07-16 09:42:45 +00:00
|
|
|
/* Check if the running fraction has accounted for 100 ns */
|
|
|
|
HalpRunningFraction += HalpCurrentFractionalIncrement;
|
|
|
|
if (HalpRunningFraction >= 1000)
|
|
|
|
{
|
|
|
|
LastIncrement++;
|
|
|
|
HalpRunningFraction -= 1000;
|
|
|
|
}
|
|
|
|
|
2011-09-16 09:39:41 +00:00
|
|
|
/* Check if someone changed the time rate */
|
2021-07-16 09:42:45 +00:00
|
|
|
if (HalpSetClockRate)
|
2011-09-16 09:39:41 +00:00
|
|
|
{
|
|
|
|
/* Set new clock rate */
|
2021-07-16 09:42:45 +00:00
|
|
|
RtcSetClockRate(HalpNextClockRate);
|
2011-09-06 21:01:49 +00:00
|
|
|
|
2011-09-16 09:39:41 +00:00
|
|
|
/* We're done */
|
2021-07-16 09:42:45 +00:00
|
|
|
HalpSetClockRate = FALSE;
|
2011-09-06 21:01:49 +00:00
|
|
|
}
|
|
|
|
|
2024-02-24 10:05:12 +00:00
|
|
|
/* Send the clock IPI to all other CPUs */
|
|
|
|
HalpBroadcastClockIpi(CLOCK_IPI_VECTOR);
|
|
|
|
|
2011-09-16 09:39:41 +00:00
|
|
|
/* Update the system time -- on x86 the kernel will exit this trap */
|
|
|
|
KeUpdateSystemTime(TrapFrame, LastIncrement, Irql);
|
2024-12-05 14:53:26 +00:00
|
|
|
|
|
|
|
/* End the interrupt */
|
|
|
|
KiEndInterrupt(Irql, TrapFrame);
|
2011-09-06 21:01:49 +00:00
|
|
|
}
|
|
|
|
|
2024-02-24 10:05:12 +00:00
|
|
|
VOID
|
|
|
|
FASTCALL
|
|
|
|
HalpClockIpiHandler(IN PKTRAP_FRAME TrapFrame)
|
|
|
|
{
|
|
|
|
KIRQL Irql;
|
|
|
|
|
|
|
|
/* Enter trap */
|
|
|
|
KiEnterInterruptTrap(TrapFrame);
|
|
|
|
#ifdef _M_AMD64
|
|
|
|
/* This is for debugging */
|
|
|
|
TrapFrame->ErrorCode = 0xc10c4;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Start the interrupt */
|
|
|
|
if (!HalBeginSystemInterrupt(CLOCK_LEVEL, CLOCK_IPI_VECTOR, &Irql))
|
|
|
|
{
|
|
|
|
/* Spurious, just end the interrupt */
|
2024-12-05 14:51:26 +00:00
|
|
|
#ifdef _M_IX86
|
2024-02-24 10:05:12 +00:00
|
|
|
KiEoiHelper(TrapFrame);
|
2024-12-05 14:51:26 +00:00
|
|
|
#endif
|
|
|
|
return;
|
2024-02-24 10:05:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Call the kernel to update runtimes */
|
|
|
|
KeUpdateRunTime(TrapFrame, Irql);
|
|
|
|
|
|
|
|
/* End the interrupt */
|
|
|
|
KiEndInterrupt(Irql, TrapFrame);
|
|
|
|
}
|
|
|
|
|
2011-09-06 21:01:49 +00:00
|
|
|
ULONG
|
|
|
|
NTAPI
|
|
|
|
HalSetTimeIncrement(IN ULONG Increment)
|
|
|
|
{
|
|
|
|
UCHAR Rate;
|
2024-05-02 10:21:13 +00:00
|
|
|
ULONG NextIncrement;
|
2011-09-06 21:01:49 +00:00
|
|
|
|
|
|
|
/* Lookup largest value below given Increment */
|
2024-05-02 10:21:13 +00:00
|
|
|
for (Rate = RtcMinimumClockRate; Rate < RtcMaximumClockRate; Rate++)
|
2011-09-06 21:01:49 +00:00
|
|
|
{
|
|
|
|
/* Check if this is the largest rate possible */
|
2024-05-02 10:21:13 +00:00
|
|
|
NextIncrement = RtcClockRateToPreciseIncrement(Rate + 1) / 1000;
|
|
|
|
if (NextIncrement > Increment)
|
|
|
|
break;
|
2011-09-06 21:01:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the rate and tell HAL we want to change it */
|
2021-07-16 09:42:45 +00:00
|
|
|
HalpNextClockRate = Rate;
|
|
|
|
HalpSetClockRate = TRUE;
|
2011-09-06 21:01:49 +00:00
|
|
|
|
|
|
|
/* Return the real increment */
|
2021-07-16 09:42:45 +00:00
|
|
|
return RtcClockRateToPreciseIncrement(Rate) / 1000;
|
2011-09-06 21:01:49 +00:00
|
|
|
}
|