4 * @brief RTC & CMOS abstraction. Reference: MC146818A & Intel Series 500 PCH
9 * @copyright Copyright (c) 2022
13 #include <lunaix/isrm.h>
15 #include <hal/rtc/mc146818a.h>
17 #include <klibc/string.h>
19 #include <sys/interrupts.h>
20 #include <sys/port_io.h>
22 #define RTC_INDEX_PORT 0x70
23 #define RTC_TARGET_PORT 0x71
25 #define WITH_NMI_DISABLED 0x80
27 #define RTC_CURRENT_CENTRY 20
29 #define RTC_REG_YRS 0x9
30 #define RTC_REG_MTH 0x8
31 #define RTC_REG_DAY 0x7
32 #define RTC_REG_WDY 0x6
33 #define RTC_REG_HRS 0x4
34 #define RTC_REG_MIN 0x2
35 #define RTC_REG_SEC 0x0
42 #define RTC_BIN_ENCODED(reg) (reg & 0x04)
43 #define RTC_24HRS_ENCODED(reg) (reg & 0x02)
45 #define RTC_TIMER_BASE_FREQUENCY 1024
46 #define RTC_TIMER_ON 0x40
48 #define RTC_FREQUENCY_1024HZ 0b110
49 #define RTC_DIVIDER_33KHZ (0b010 << 4)
53 struct hwrtc* rtc_context;
56 void (*on_tick_cb)(const struct hwrtc*);
59 #define rtc_state(data) ((struct mc146818*)(data))
62 rtc_read_reg(u8_t reg_selector)
64 port_wrbyte(RTC_INDEX_PORT, reg_selector);
65 return port_rdbyte(RTC_TARGET_PORT);
69 rtc_write_reg(u8_t reg_selector, u8_t val)
71 port_wrbyte(RTC_INDEX_PORT, reg_selector);
72 port_wrbyte(RTC_TARGET_PORT, val);
78 return ((bcd & 0xF0) >> 1) + ((bcd & 0xF0) >> 3) + (bcd & 0xf);
84 u8_t regB = rtc_read_reg(RTC_REG_B | WITH_NMI_DISABLED);
85 rtc_write_reg(RTC_REG_B | WITH_NMI_DISABLED, regB | RTC_TIMER_ON);
91 u8_t regB = rtc_read_reg(RTC_REG_B | WITH_NMI_DISABLED);
92 rtc_write_reg(RTC_REG_B | WITH_NMI_DISABLED, regB & ~RTC_TIMER_ON);
96 clock_walltime(struct hwrtc* rtc, datetime_t* datetime)
101 while (rtc_read_reg(RTC_REG_A) & 0x80)
103 memcpy(¤t, datetime, sizeof(datetime_t));
105 datetime->year = rtc_read_reg(RTC_REG_YRS);
106 datetime->month = rtc_read_reg(RTC_REG_MTH);
107 datetime->day = rtc_read_reg(RTC_REG_DAY);
108 datetime->weekday = rtc_read_reg(RTC_REG_WDY);
109 datetime->hour = rtc_read_reg(RTC_REG_HRS);
110 datetime->minute = rtc_read_reg(RTC_REG_MIN);
111 datetime->second = rtc_read_reg(RTC_REG_SEC);
112 } while (!datatime_eq(datetime, ¤t));
114 u8_t regbv = rtc_read_reg(RTC_REG_B);
116 // Convert from bcd to binary when needed
117 if (!RTC_BIN_ENCODED(regbv)) {
118 datetime->year = bcd2dec(datetime->year);
119 datetime->month = bcd2dec(datetime->month);
120 datetime->day = bcd2dec(datetime->day);
121 datetime->hour = bcd2dec(datetime->hour);
122 datetime->minute = bcd2dec(datetime->minute);
123 datetime->second = bcd2dec(datetime->second);
127 if (!RTC_24HRS_ENCODED(regbv) && (datetime->hour >> 7)) {
128 datetime->hour = 12 + (datetime->hour & 0x80);
131 datetime->year += RTC_CURRENT_CENTRY * 100;
135 mc146818_check_support(struct hwrtc* rtc)
145 rtc_init(struct hwrtc* rtc)
147 u8_t regA = rtc_read_reg(RTC_REG_A | WITH_NMI_DISABLED);
148 regA = (regA & ~0x7f) | RTC_FREQUENCY_1024HZ | RTC_DIVIDER_33KHZ;
149 rtc_write_reg(RTC_REG_A | WITH_NMI_DISABLED, regA);
151 rtc_state(rtc->data)->rtc_context = rtc;
153 // Make sure the rtc timer is disabled by default
157 static struct mc146818 rtc_state = { .ticking = 0 };
160 __rtc_tick(const isr_param* param)
162 rtc_state.on_tick_cb(rtc_state.rtc_context);
164 (void)rtc_read_reg(RTC_REG_C);
168 rtc_do_ticking(struct hwrtc* rtc, void (*on_tick)())
170 if (!on_tick || rtc_state(rtc->data)->ticking) {
174 struct mc146818* state = rtc_state(rtc->data);
176 state->on_tick_cb = on_tick;
178 /* We realise that using rtc to tick something has an extremely rare use
179 * case (e.g., calibrating some timer). Therefore, we will release this
180 * allocated IV when rtc ticking is no longer required to save IV
183 state->rtc_iv = isrm_bindirq(PC_AT_IRQ_RTC, __rtc_tick);
189 rtc_end_ticking(struct hwrtc* rtc)
191 if (!rtc_state(rtc->data)->ticking) {
197 // do some delay, ensure there is no more interrupt from rtc before we
201 isrm_ivfree(rtc_state(rtc->data)->rtc_iv);
203 rtc_state(rtc->data)->ticking = 0;
206 static struct hwrtc hwrtc_mc146818a = { .name = "mc146818a",
209 .base_freq = RTC_TIMER_BASE_FREQUENCY,
210 .supported = mc146818_check_support,
211 .get_walltime = clock_walltime,
212 .do_ticking = rtc_do_ticking,
213 .end_ticking = rtc_end_ticking };
216 mc146818a_rtc_context()
218 return &hwrtc_mc146818a;