Files
2020-02-04 12:45:15 +02:00

101 lines
2.8 KiB
C

#include "arch/amd64/hw/rtc.h"
#include "arch/amd64/hw/irq.h"
#include "arch/amd64/hw/io.h"
#include "sys/debug.h"
#include "sys/string.h"
#define RTC_CMOS_REG_PORT 0x70
#define RTC_CMOS_DATA_PORT 0x71
#define RTC_DISABLE_NMI 0x80
#define RTC_REGA_STATUS 0x0A
#define RTC_REGB_STATUS 0x0B
#define RTC_REGC_STATUS 0x0C
#define RTC_REG_SECOND 0x00
#define RTC_REG_MINUTE 0x02
#define RTC_REG_HOUR 0x04
#define RTC_REG_WEEKDAY 0x06
#define RTC_REG_DAY 0x07
#define RTC_REG_MONTH 0x08
#define RTC_REG_YEAR 0x09
#define RTC_REGB_BCD 0x04
#define RTC_REGB_AMPM 0x02
#define FROMBCD(bcd) ((((bcd) / 16) * 10) + \
((bcd & 0xF)))
// [s]Please change this in Y3K[/s]I hope x86 dies by 3000
static uint8_t rtc_century = 20;
static inline void cmos_outb(uint8_t reg, uint8_t val) {
outb(RTC_CMOS_REG_PORT, RTC_DISABLE_NMI | reg);
for (size_t i = 0; i < 1000000; ++i);
outb(RTC_CMOS_DATA_PORT, val);
}
static inline uint8_t cmos_inb(uint8_t reg) {
outb(RTC_CMOS_REG_PORT, RTC_DISABLE_NMI | reg);
for (size_t i = 0; i < 1000000; ++i);
return inb(RTC_CMOS_DATA_PORT);
// TODO; maybe reenable NMI?
}
static inline uint8_t cmos_read_time(uint8_t reg, uint8_t reg_b) {
if (!(reg_b & RTC_REGB_BCD)) {
return FROMBCD(cmos_inb(reg));
} else {
return cmos_inb(reg);
}
}
void rtc_read(struct tm *time) {
uint8_t reg_b = cmos_inb(RTC_REGB_STATUS);
time->tm_sec = cmos_read_time(RTC_REG_SECOND, reg_b);
time->tm_min = cmos_read_time(RTC_REG_MINUTE, reg_b);
time->tm_hour = cmos_read_time(RTC_REG_HOUR, reg_b);
//time->tm_wday = cmos_read_time(RTC_REG_WEEKDAY, reg_b);
time->tm_mday = cmos_read_time(RTC_REG_DAY, reg_b);
time->tm_mon = cmos_read_time(RTC_REG_MONTH, reg_b);
time->tm_year = cmos_read_time(RTC_REG_YEAR, reg_b) + 100 * rtc_century;
}
static uint32_t rtc_irq(void *ctx) {
uint8_t reg_c = cmos_inb(RTC_REGC_STATUS);
if (reg_c == 0) {
return IRQ_UNHANDLED;
}
// Do something here?
return IRQ_HANDLED;
}
static void rtc_enable_irq(void) {
uint8_t prev;
// Gives IRQs approximately every 0.5s
prev = cmos_inb(RTC_REGA_STATUS);
prev &= 0xF0;
prev |= 0xF;
cmos_outb(RTC_REGA_STATUS, prev);
// Enable IRQs
prev = cmos_inb(RTC_REGB_STATUS);
cmos_outb(RTC_REGB_STATUS, prev | 0x40);
}
void rtc_set_century(uint8_t century) {
uint8_t reg_b = cmos_inb(RTC_REGB_STATUS);
uint8_t century_val = cmos_read_time(century, reg_b);
kdebug("Setting century: %d\n", century_val * 100);
rtc_century = century_val;
}
void rtc_init(void) {
cmos_inb(RTC_REGC_STATUS);
irq_add_leg_handler(8, rtc_irq, NULL);
rtc_enable_irq();
}