Files
MentOS/mentos/src/drivers/rtc.c
T
2022-12-13 14:25:16 -05:00

164 lines
4.4 KiB
C

/// @file rtc.c
/// @brief Real Time Clock (RTC) driver.
/// @copyright (c) 2014-2022 This file is distributed under the MIT License.
/// See LICENSE.md for details.
/// @addtogroup rtc
/// @{
// Include the kernel log levels.
#include "sys/kernel_levels.h"
/// Change the header.
#define __DEBUG_HEADER__ "[RTC ]"
/// Set the log level.
#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE
#include "drivers/rtc.h"
#include "hardware/pic8259.h"
#include "string.h"
#include "io/port_io.h"
#include "kernel.h"
#include "descriptor_tables/isr.h"
#define CMOS_ADDR 0x70 ///< Addess where we need to write the Address.
#define CMOS_DATA 0x71 ///< Addess where we need to write the Data.
/// Current global time.
tm_t global_time;
/// Previous global time.
tm_t previous_global_time;
/// Data type is BCD.
int is_bcd;
static inline unsigned int rtc_are_different(tm_t *t0, tm_t *t1)
{
if (t0->tm_sec != t1->tm_sec)
return 1;
if (t0->tm_min != t1->tm_min)
return 1;
if (t0->tm_hour != t1->tm_hour)
return 1;
if (t0->tm_mon != t1->tm_mon)
return 1;
if (t0->tm_year != t1->tm_year)
return 1;
if (t0->tm_wday != t1->tm_wday)
return 1;
if (t0->tm_mday != t1->tm_mday)
return 1;
return 0;
}
/// @brief Check if rtc is updating time currently.
static inline unsigned int is_updating_rtc()
{
outportb(CMOS_ADDR, 0x0A);
uint32_t status = inportb(CMOS_DATA);
return (status & 0x80U);
}
static inline unsigned char read_register(unsigned char reg)
{
outportb(CMOS_ADDR, reg);
return inportb(CMOS_DATA);
}
static inline void write_register(unsigned char reg, unsigned char value)
{
outportb(CMOS_ADDR, reg);
outportb(CMOS_DATA, value);
}
static inline unsigned char bcd2bin(unsigned char bcd)
{
return ((bcd >> 4u) * 10) + (bcd & 0x0Fu);
}
static inline void rtc_read_datetime()
{
if (read_register(0x0Cu) & 0x10u) {
if (is_bcd) {
global_time.tm_sec = bcd2bin(read_register(0x00));
global_time.tm_min = bcd2bin(read_register(0x02));
global_time.tm_hour = bcd2bin(read_register(0x04)) + 2;
global_time.tm_mon = bcd2bin(read_register(0x08));
global_time.tm_year = bcd2bin(read_register(0x09)) + 2000;
global_time.tm_wday = bcd2bin(read_register(0x06));
global_time.tm_mday = bcd2bin(read_register(0x07));
} else {
global_time.tm_sec = read_register(0x00);
global_time.tm_min = read_register(0x02);
global_time.tm_hour = read_register(0x04) + 2;
global_time.tm_mon = read_register(0x08);
global_time.tm_year = read_register(0x09) + 2000;
global_time.tm_wday = read_register(0x06);
global_time.tm_mday = read_register(0x07);
}
}
}
static inline void rtc_update_datetime()
{
static unsigned int first_update = 1;
// Wait until rtc is not updating.
while (is_updating_rtc()) {}
// Read the values.
rtc_read_datetime();
if (first_update) {
do {
// Save the previous global time.
previous_global_time = global_time;
// Wait until rtc is not updating.
while (is_updating_rtc()) {}
// Read the values.
rtc_read_datetime();
} while (!rtc_are_different(&previous_global_time, &global_time));
first_update = 0;
}
}
static inline void rtc_handler_isr(pt_regs *f)
{
rtc_update_datetime();
}
void gettime(tm_t *time)
{
// Copy the update time.
memcpy(time, &global_time, sizeof(tm_t));
}
int rtc_initialize()
{
unsigned char status;
status = read_register(0x0B);
status |= 0x02u; // 24 hour clock
status |= 0x10u; // update ended interrupts
status &= ~0x20u; // no alarm interrupts
status &= ~0x40u; // no periodic interrupt
is_bcd = !(status & 0x04u); // check if data type is BCD
write_register(0x0B, status);
read_register(0x0C);
// Install the IRQ.
irq_install_handler(IRQ_REAL_TIME_CLOCK, rtc_handler_isr, "Real Time Clock (RTC)");
// Enable the IRQ.
pic8259_irq_enable(IRQ_REAL_TIME_CLOCK);
// Wait until rtc is ready.
rtc_update_datetime();
return 0;
}
int rtc_finalize()
{
// Uninstall the IRQ.
irq_uninstall_handler(IRQ_REAL_TIME_CLOCK, rtc_handler_isr);
// Disable the IRQ.
pic8259_irq_disable(IRQ_REAL_TIME_CLOCK);
return 0;
}
/// @}