Compare commits

...

2 commits

View file

@ -7,6 +7,7 @@
#include <x86_64/cmos.h>
#include "cpu.h"
#include "sync.h"
#include "x86_64/apic.h"
#define CMOS_SEL_PORT 0x70
@ -33,133 +34,136 @@
#define STATB_BINARY 0x04
#define STATB_PERIODIC_INTEN 0x40
/* Make sure only one CPU core interacts with the CMOS chip at once. */
static spinlock_t cmos_lock = SPIN_INITIALIZER;
static uint8_t
cmos_read(uint8_t sel)
{
out8(CMOS_SEL_PORT, sel);
return in8(CMOS_DATA_PORT);
out8(CMOS_SEL_PORT, sel);
return in8(CMOS_DATA_PORT);
}
static void
cmos_write(uint8_t sel, uint8_t data)
{
out8(CMOS_SEL_PORT, sel);
out8(CMOS_DATA_PORT, data);
out8(CMOS_SEL_PORT, sel);
out8(CMOS_DATA_PORT, data);
}
void
nmi_enable(void)
{
int cpu_state = int_disable();
cmos_read(CMOS_REG_STATD);
int_restore(cpu_state);
spin_lock(&cmos_lock);
cmos_read(CMOS_REG_STATD);
spin_unlock(&cmos_lock);
}
void
nmi_disable(void)
{
int cpu_state = int_disable();
cmos_read(CMOS_REG_STATD | CMOS_NMI_DISABLE);
int_restore(cpu_state);
spin_lock(&cmos_lock);
cmos_read(CMOS_REG_STATD | CMOS_NMI_DISABLE);
spin_unlock(&cmos_lock);
}
static uint8_t
bcd_to_binary(uint8_t bcd)
{
uint8_t hi = bcd & 0xf0;
uint8_t lo = bcd & 0x0f;
return (hi >> 1) + (hi >> 3) + lo;
uint8_t hi = bcd & 0xf0;
uint8_t lo = bcd & 0x0f;
return (hi >> 1) + (hi >> 3) + lo;
}
static void
cmos_access_rtc(struct datetime *dt)
{
int cpu_state = int_disable();
spin_lock(&cmos_lock);
while (cmos_read(CMOS_REG_STATA | CMOS_NMI_DISABLE) & STATA_UPDATING) {}
while (cmos_read(CMOS_REG_STATA | CMOS_NMI_DISABLE) & STATA_UPDATING) {}
dt->seconds = cmos_read(CMOS_REG_SECONDS | CMOS_NMI_DISABLE);
dt->minutes = cmos_read(CMOS_REG_MINUTES | CMOS_NMI_DISABLE);
dt->hours = cmos_read(CMOS_REG_HOURS | CMOS_NMI_DISABLE);
dt->day = cmos_read(CMOS_REG_DAY | CMOS_NMI_DISABLE);
dt->month = cmos_read(CMOS_REG_MONTH | CMOS_NMI_DISABLE);
dt->year = cmos_read(CMOS_REG_YEAR);
dt->seconds = cmos_read(CMOS_REG_SECONDS | CMOS_NMI_DISABLE);
dt->minutes = cmos_read(CMOS_REG_MINUTES | CMOS_NMI_DISABLE);
dt->hours = cmos_read(CMOS_REG_HOURS | CMOS_NMI_DISABLE);
dt->day = cmos_read(CMOS_REG_DAY | CMOS_NMI_DISABLE);
dt->month = cmos_read(CMOS_REG_MONTH | CMOS_NMI_DISABLE);
dt->year = cmos_read(CMOS_REG_YEAR);
int_restore(cpu_state);
spin_unlock(&cmos_lock);
}
void
rtc_now(struct datetime *dt)
{
struct datetime dt1, dt2;
do {
cmos_access_rtc(&dt1);
cmos_access_rtc(&dt2);
} while (dt1.seconds != dt2.seconds ||
dt1.minutes != dt2.minutes ||
dt1.hours != dt2.hours ||
dt1.day != dt2.day ||
dt1.month != dt2.month ||
dt1.year != dt2.year);
struct datetime dt1, dt2;
do {
cmos_access_rtc(&dt1);
cmos_access_rtc(&dt2);
} while (dt1.seconds != dt2.seconds ||
dt1.minutes != dt2.minutes ||
dt1.hours != dt2.hours ||
dt1.day != dt2.day ||
dt1.month != dt2.month ||
dt1.year != dt2.year);
int cpu_state = int_disable();
uint8_t statb = cmos_read(CMOS_REG_STATB | CMOS_NMI_DISABLE);
cmos_read(0);
int_restore(cpu_state);
spin_lock(&cmos_lock);
uint8_t statb = cmos_read(CMOS_REG_STATB | CMOS_NMI_DISABLE);
cmos_read(0);
spin_unlock(&cmos_lock);
bool pm = false;
if (!(statb & STATB_24HOUR)) {
pm = dt1.hours & 0x80;
dt1.hours &= 0x7f;
}
bool pm = false;
if (!(statb & STATB_24HOUR)) {
pm = dt1.hours & 0x80;
dt1.hours &= 0x7f;
}
if (!(statb & STATB_BINARY)) {
dt1.seconds = bcd_to_binary(dt1.seconds);
dt1.minutes = bcd_to_binary(dt1.minutes);
dt1.hours = bcd_to_binary(dt1.hours);
dt1.day = bcd_to_binary(dt1.day);
dt1.month = bcd_to_binary(dt1.month);
dt1.year = bcd_to_binary(dt1.year);
}
if (!(statb & STATB_BINARY)) {
dt1.seconds = bcd_to_binary(dt1.seconds);
dt1.minutes = bcd_to_binary(dt1.minutes);
dt1.hours = bcd_to_binary(dt1.hours);
dt1.day = bcd_to_binary(dt1.day);
dt1.month = bcd_to_binary(dt1.month);
dt1.year = bcd_to_binary(dt1.year);
}
if (pm) {
dt1.hours += 12;
}
if (pm) {
dt1.hours += 12;
}
const uint8_t century = 20;
dt1.year += century * 100;
const uint8_t century = 20;
dt1.year += century * 100;
*dt = dt1;
*dt = dt1;
}
void
rtc_set_rate(int rate)
{
ASSERT(rate == 0 || (rate >= 3 && rate <= 15));
int cpu_state = int_disable();
uint8_t stata = cmos_read(CMOS_REG_STATA | CMOS_NMI_DISABLE);
stata = (stata & 0xF0) | rate;
cmos_write(CMOS_REG_STATA, stata);
int_restore(cpu_state);
ASSERT(rate == 0 || (rate >= 3 && rate <= 15));
spin_lock(&cmos_lock);
uint8_t stata = cmos_read(CMOS_REG_STATA | CMOS_NMI_DISABLE);
stata = (stata & 0xF0) | rate;
cmos_write(CMOS_REG_STATA, stata);
spin_unlock(&cmos_lock);
}
void
rtc_set_periodic(bool enable)
{
int cpu_state = int_disable();
uint8_t statb = cmos_read(CMOS_REG_STATB | CMOS_NMI_DISABLE);
statb = statb & 0x8F;
if (enable) statb |= STATB_PERIODIC_INTEN;
cmos_write(CMOS_REG_STATB, statb);
int_restore(cpu_state);
spin_lock(&cmos_lock);
uint8_t statb = cmos_read(CMOS_REG_STATB | CMOS_NMI_DISABLE);
statb = statb & 0x8F;
if (enable) statb |= STATB_PERIODIC_INTEN;
cmos_write(CMOS_REG_STATB, statb);
spin_unlock(&cmos_lock);
}
void
rtc_eoi(void)
{
int cpu_state = int_disable();
cmos_read(CMOS_REG_STATC);
int_restore(cpu_state);
spin_lock(&cmos_lock);
cmos_read(CMOS_REG_STATC);
spin_unlock(&cmos_lock);
}
void
@ -168,6 +172,6 @@ rtc_init(void)
struct datetime dt;
rtc_now(&dt);
printlinef("IT IS NOW %u-%u-%u %u:%u:%u",
dt.year, dt.month, dt.day,
dt.hours, dt.minutes, dt.seconds);
dt.year, dt.month, dt.day,
dt.hours, dt.minutes, dt.seconds);
}