Read RTC date and time

This commit is contained in:
Josh Holtrop 2022-12-18 22:29:12 -05:00
parent 4081a220ef
commit 7c8744d1e0

View File

@ -13,32 +13,67 @@ struct rtc
private enum ubyte DISABLE_NMI = 0x80u; private enum ubyte DISABLE_NMI = 0x80u;
private enum ubyte SR_A = 0xAu; private enum ubyte REG_RTC_SECONDS = 0x0u;
private enum ubyte SR_B = 0xBu; private enum ubyte REG_RTC_MINUTES = 0x2u;
private enum ubyte SR_C = 0xCu; private enum ubyte REG_RTC_HOURS = 0x4u;
private enum ubyte REG_RTC_DAY = 0x7u;
private enum ubyte REG_RTC_MONTH = 0x8u;
private enum ubyte REG_RTC_YEAR = 0x9u;
private enum ubyte REG_SR_A = 0xAu;
private enum ubyte REG_SR_B = 0xBu;
private enum ubyte REG_SR_C = 0xCu;
private enum ubyte SR_A_RTC_UPDATE_IN_PROGRESS = 0x80u;
private enum ubyte SR_B_24HOUR = 0x02u;
private enum ubyte SR_B_BINARY = 0x04u;
private enum ubyte SR_B_ENABLE_IRQ = 0x40u; private enum ubyte SR_B_ENABLE_IRQ = 0x40u;
private static __gshared bool rtc_24hour_mode;
private static __gshared bool rtc_binary_mode;
static struct time
{
public ubyte year;
public ubyte month;
public ubyte day;
public ubyte hour;
public ubyte minute;
public ubyte second;
}
public static void initialize() public static void initialize()
{ {
/* Enable IRQ 8 to receive RTC interrupts. */ time t = read_rtc_time();
out8(PORT_SELECT, DISABLE_NMI | SR_B); klog.writefln("System time is 20%02u-%02u-%02u %02u:%02u:%02u",
ubyte sr_b = in8(PORT_DATA); t.year, t.month, t.day, t.hour, t.minute, t.second);
out8(PORT_SELECT, DISABLE_NMI | SR_B);
out8(PORT_DATA, sr_b | SR_B_ENABLE_IRQ);
out8(PORT_SELECT, DISABLE_NMI | SR_A); /* Enable IRQ 8 to receive RTC interrupts. */
klog.writefln("SR_A = 0x%x", in8(PORT_DATA)); ubyte sr_b = read_register(REG_SR_B, true);
write_register(REG_SR_B, sr_b | SR_B_ENABLE_IRQ, true);
rtc_24hour_mode = (sr_b & SR_B_24HOUR) != 0u;
rtc_binary_mode = (sr_b & SR_B_BINARY) != 0u;
/* Send EOI to enable more RTC interrupts and re-enable NMIs. */ /* Send EOI to enable more RTC interrupts and re-enable NMIs. */
eoi(); eoi();
} }
private static ubyte read_register(ubyte register, bool disable_nmi = false)
{
out8(PORT_SELECT, register | (disable_nmi ? DISABLE_NMI : 0u));
return in8(PORT_DATA);
}
private static void write_register(ubyte register, ubyte value, bool disable_nmi = false)
{
out8(PORT_SELECT, register | (disable_nmi ? DISABLE_NMI : 0u));
out8(PORT_DATA, value);
}
private static void eoi() private static void eoi()
{ {
/* Read from status register C to clear the interrupt. */ /* Read from status register C to clear the interrupt. */
out8(PORT_SELECT, SR_C); read_register(REG_SR_C);
in8(PORT_DATA);
} }
public static void isr() public static void isr()
@ -53,4 +88,57 @@ struct rtc
} }
eoi(); eoi();
} }
private static time read_rtc_time()
{
time[2] times;
size_t reads;
do
{
times[reads & 1] = read_rtc_time_unsafe();
reads++;
} while ((reads < 2u) || (times[0] != times[1]));
return times[0];
}
private static time read_rtc_time_unsafe()
{
time t;
while ((read_register(REG_SR_A) & SR_A_RTC_UPDATE_IN_PROGRESS) != 0u)
{
}
t.year = read_rtc_time_register(REG_RTC_YEAR);
t.month = read_rtc_time_register(REG_RTC_MONTH);
t.day = read_rtc_time_register(REG_RTC_DAY);
t.hour = read_register(REG_RTC_HOURS);
t.minute = read_rtc_time_register(REG_RTC_MINUTES);
t.second = read_rtc_time_register(REG_RTC_SECONDS);
if (!rtc_24hour_mode)
{
ubyte pm = (t.hour & 0x80u) >> 7u;
t.hour &= 0x7Fu;
if (!rtc_binary_mode)
{
t.hour = bcd_to_binary(t.hour);
}
/* Hour is in 12-hour format; convert to 24-hour format. */
t.hour = cast(ubyte)((t.hour % 12u) + pm * 12u);
}
return t;
}
private static ubyte read_rtc_time_register(ubyte register)
{
ubyte value = read_register(register);
if (!rtc_binary_mode)
{
value = bcd_to_binary(value);
}
return value;
}
private static ubyte bcd_to_binary(ubyte bcd)
{
return (bcd & 0xFu) + ((bcd & 0xF0u) >> 4u) * 10u;
}
} }