175 lines
5.5 KiB
C
175 lines
5.5 KiB
C
// HAL RTC support. This file reads the CMOS clock as the fallback wall-clock source.
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <intrin.h>
|
|
#include <ntlite/status.h>
|
|
#include <ntlite/ddi/hal_api.h>
|
|
#include <kernel/hal/hal_debug.h>
|
|
|
|
#define CMOS_INDEX_PORT 0x70
|
|
#define CMOS_DATA_PORT 0x71
|
|
|
|
#define RTC_REG_SECONDS 0x00
|
|
#define RTC_REG_MINUTES 0x02
|
|
#define RTC_REG_HOURS 0x04
|
|
#define RTC_REG_WEEKDAY 0x06
|
|
#define RTC_REG_DAY 0x07
|
|
#define RTC_REG_MONTH 0x08
|
|
#define RTC_REG_YEAR 0x09
|
|
#define RTC_REG_STATUS_A 0x0A
|
|
#define RTC_REG_STATUS_B 0x0B
|
|
|
|
static uint8_t gCenturyRegister;
|
|
|
|
// Implements the rtc Read operation.
|
|
static uint8_t RtcRead(uint8_t reg) {
|
|
__outbyte(CMOS_INDEX_PORT, (uint8_t)(reg | 0x80u)); // keep NMI masked while selecting.
|
|
return __inbyte(CMOS_DATA_PORT);
|
|
}
|
|
|
|
// Implements the rtc Update In Progress operation.
|
|
static bool RtcUpdateInProgress(void) {
|
|
return (RtcRead(RTC_REG_STATUS_A) & 0x80u) != 0;
|
|
}
|
|
|
|
// Implements the rtc Bcd To Binary operation.
|
|
static uint8_t RtcBcdToBinary(uint8_t value) {
|
|
return (uint8_t)(((value >> 4) * 10u) + (value & 0x0Fu));
|
|
}
|
|
|
|
// Implements the rtc Read Stable operation.
|
|
static bool RtcReadStable(HAL_RTC_TIME* out) {
|
|
if (!out) return false;
|
|
|
|
uint8_t sec1 = 0, min1 = 0, hour1 = 0, day1 = 0, month1 = 0, year1 = 0, century1 = 0;
|
|
uint8_t sec2 = 0, min2 = 0, hour2 = 0, day2 = 0, month2 = 0, year2 = 0, century2 = 0;
|
|
bool stable = false;
|
|
|
|
for (uint32_t tries = 0; tries < 100000u; tries++) {
|
|
while (RtcUpdateInProgress()) _mm_pause();
|
|
|
|
sec1 = RtcRead(RTC_REG_SECONDS);
|
|
min1 = RtcRead(RTC_REG_MINUTES);
|
|
hour1 = RtcRead(RTC_REG_HOURS);
|
|
day1 = RtcRead(RTC_REG_DAY);
|
|
month1 = RtcRead(RTC_REG_MONTH);
|
|
year1 = RtcRead(RTC_REG_YEAR);
|
|
century1 = gCenturyRegister ? RtcRead(gCenturyRegister) : 0;
|
|
|
|
while (RtcUpdateInProgress()) _mm_pause();
|
|
|
|
sec2 = RtcRead(RTC_REG_SECONDS);
|
|
min2 = RtcRead(RTC_REG_MINUTES);
|
|
hour2 = RtcRead(RTC_REG_HOURS);
|
|
day2 = RtcRead(RTC_REG_DAY);
|
|
month2 = RtcRead(RTC_REG_MONTH);
|
|
year2 = RtcRead(RTC_REG_YEAR);
|
|
century2 = gCenturyRegister ? RtcRead(gCenturyRegister) : 0;
|
|
|
|
if (sec1 == sec2 && min1 == min2 && hour1 == hour2 && day1 == day2 &&
|
|
month1 == month2 && year1 == year2 && century1 == century2) {
|
|
stable = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!stable) return false;
|
|
|
|
uint8_t statusB = RtcRead(RTC_REG_STATUS_B);
|
|
bool binary = (statusB & 0x04u) != 0;
|
|
bool hour24 = (statusB & 0x02u) != 0;
|
|
bool pm = (!hour24 && (hour1 & 0x80u) != 0);
|
|
|
|
if (!binary) {
|
|
sec1 = RtcBcdToBinary(sec1);
|
|
min1 = RtcBcdToBinary(min1);
|
|
hour1 = (uint8_t)((hour1 & 0x80u) | RtcBcdToBinary((uint8_t)(hour1 & 0x7Fu)));
|
|
day1 = RtcBcdToBinary(day1);
|
|
month1 = RtcBcdToBinary(month1);
|
|
year1 = RtcBcdToBinary(year1);
|
|
if (gCenturyRegister) century1 = RtcBcdToBinary(century1);
|
|
}
|
|
|
|
if (!hour24) {
|
|
hour1 = (uint8_t)(hour1 & 0x7Fu);
|
|
if (pm && hour1 < 12u) hour1 = (uint8_t)(hour1 + 12u);
|
|
if (!pm && hour1 == 12u) hour1 = 0;
|
|
}
|
|
|
|
uint16_t year;
|
|
if (gCenturyRegister && century1) {
|
|
year = (uint16_t)((uint16_t)century1 * 100u + year1);
|
|
} else {
|
|
year = (uint16_t)((year1 < 70u) ? (2000u + year1) : (1900u + year1));
|
|
}
|
|
|
|
out->Year = year;
|
|
out->Month = month1;
|
|
out->Day = day1;
|
|
out->Hour = hour1;
|
|
out->Minute = min1;
|
|
out->Second = sec1;
|
|
out->Nanosecond = 0;
|
|
out->Flags = HAL_RTC_TIME_FLAG_VALID | HAL_RTC_TIME_FLAG_RTC;
|
|
out->Reserved = 0;
|
|
return true;
|
|
}
|
|
|
|
// Implements the rtc Leap Year operation.
|
|
static bool RtcLeapYear(uint16_t year) {
|
|
return ((year % 4u) == 0u && (year % 100u) != 0u) || ((year % 400u) == 0u);
|
|
}
|
|
|
|
// Implements the rtc Days In Month operation.
|
|
static uint8_t RtcDaysInMonth(uint16_t year, uint8_t month) {
|
|
static const uint8_t days[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
|
|
if (month < 1u || month > 12u) return 0;
|
|
if (month == 2u && RtcLeapYear(year)) return 29;
|
|
return days[month - 1u];
|
|
}
|
|
|
|
// Implements the rtc Valid operation.
|
|
static bool RtcValid(const HAL_RTC_TIME* t) {
|
|
if (!t) return false;
|
|
if (t->Year < 1970u || t->Year > 9999u) return false;
|
|
if (t->Month < 1u || t->Month > 12u) return false;
|
|
uint8_t dim = RtcDaysInMonth(t->Year, t->Month);
|
|
if (t->Day < 1u || t->Day > dim) return false;
|
|
if (t->Hour > 23u || t->Minute > 59u || t->Second > 59u) return false;
|
|
return true;
|
|
}
|
|
|
|
// Implements the rtc Query Time operation.
|
|
static KSTATUS HalRtcQueryTime(HAL_RTC_TIME* outTime) {
|
|
if (!outTime) return STATUS_INVALID_PARAMETER;
|
|
|
|
HAL_RTC_TIME t;
|
|
memset(&t, 0, sizeof(t));
|
|
if (!RtcReadStable(&t) || !RtcValid(&t))
|
|
return STATUS_DEVICE_ERROR;
|
|
|
|
*outTime = t;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Implements the rtc Set Century Register operation.
|
|
void HalRtcSetCenturyRegister(uint8_t centuryRegister) {
|
|
gCenturyRegister = centuryRegister;
|
|
}
|
|
|
|
// Implements the rtc Initialize operation.
|
|
void HalRtcInitialize(HAL_RTC* out) {
|
|
if (!out) return;
|
|
out->QueryTime = HalRtcQueryTime;
|
|
|
|
HAL_RTC_TIME t;
|
|
if (HalRtcQueryTime(&t) == STATUS_SUCCESS) {
|
|
HalDbgPrint("HAL: RTC %04u-%02u-%02u %02u:%02u:%02u century-reg=0x%02x\n",
|
|
t.Year, t.Month, t.Day, t.Hour, t.Minute, t.Second,
|
|
gCenturyRegister);
|
|
} else {
|
|
HalDbgPrint("HAL: RTC unavailable or invalid\n");
|
|
}
|
|
}
|