4 * @brief RTC & CMOS abstraction. Reference: MC146818A & Intel Series 500 PCH
9 * @copyright Copyright (c) 2022
13 #include <asm-generic/isrm.h>
14 #include <lunaix/mm/valloc.h>
15 #include <lunaix/status.h>
16 #include <lunaix/hart_state.h>
18 #include <hal/hwrtc.h>
20 #include <klibc/string.h>
22 #include <asm/x86_pmio.h>
24 #define RTC_INDEX_PORT 0x70
25 #define RTC_TARGET_PORT 0x71
27 #define WITH_NMI_DISABLED 0x80
29 #define RTC_CURRENT_CENTRY 20
31 #define RTC_REG_YRS 0x9
32 #define RTC_REG_MTH 0x8
33 #define RTC_REG_DAY 0x7
34 #define RTC_REG_WDY 0x6
35 #define RTC_REG_HRS 0x4
36 #define RTC_REG_MIN 0x2
37 #define RTC_REG_SEC 0x0
45 #define RTC_BIN (1 << 2)
46 #define RTC_HOURFORM24 (1 << 1)
48 #define RTC_BIN_ENCODED(reg) (reg & 0x04)
49 #define RTC_24HRS_ENCODED(reg) (reg & 0x02)
51 #define RTC_TIMER_BASE_FREQUENCY 1024
52 #define RTC_TIMER_ON 0x40
54 #define RTC_FREQUENCY_1024HZ 0b110
55 #define RTC_DIVIDER_33KHZ (0b010 << 4)
57 #define PC_AT_IRQ_RTC 8
61 struct hwrtc* rtc_context;
66 #define rtc_state(data) ((struct mc146818*)(data))
69 rtc_read_reg(u8_t reg_selector)
71 port_wrbyte(RTC_INDEX_PORT, reg_selector);
72 return port_rdbyte(RTC_TARGET_PORT);
76 rtc_write_reg(u8_t reg_selector, u8_t val)
78 port_wrbyte(RTC_INDEX_PORT, reg_selector);
79 port_wrbyte(RTC_TARGET_PORT, val);
85 u8_t regB = rtc_read_reg(RTC_REG_B);
86 rtc_write_reg(RTC_REG_B, regB | RTC_TIMER_ON);
92 u8_t regB = rtc_read_reg(RTC_REG_B);
93 rtc_write_reg(RTC_REG_B, regB & ~RTC_TIMER_ON);
97 rtc_getwalltime(struct hwrtc* rtc, datetime_t* datetime)
102 while (rtc_read_reg(RTC_REG_A) & 0x80)
104 memcpy(¤t, datetime, sizeof(datetime_t));
106 datetime->year = rtc_read_reg(RTC_REG_YRS);
107 datetime->month = rtc_read_reg(RTC_REG_MTH);
108 datetime->day = rtc_read_reg(RTC_REG_DAY);
109 datetime->weekday = rtc_read_reg(RTC_REG_WDY);
110 datetime->hour = rtc_read_reg(RTC_REG_HRS);
111 datetime->minute = rtc_read_reg(RTC_REG_MIN);
112 datetime->second = rtc_read_reg(RTC_REG_SEC);
113 } while (!datatime_eq(datetime, ¤t));
115 datetime->year += RTC_CURRENT_CENTRY * 100;
119 rtc_setwalltime(struct hwrtc* rtc, datetime_t* datetime)
121 u8_t reg = rtc_read_reg(RTC_REG_B);
122 reg = reg & ~RTC_SET;
124 rtc_write_reg(RTC_REG_B, reg | RTC_SET);
126 rtc_write_reg(RTC_REG_YRS, datetime->year - RTC_CURRENT_CENTRY * 100);
127 rtc_write_reg(RTC_REG_MTH, datetime->month);
128 rtc_write_reg(RTC_REG_DAY, datetime->day);
129 rtc_write_reg(RTC_REG_WDY, datetime->weekday);
130 rtc_write_reg(RTC_REG_HRS, datetime->hour);
131 rtc_write_reg(RTC_REG_MIN, datetime->minute);
132 rtc_write_reg(RTC_REG_SEC, datetime->second);
134 rtc_write_reg(RTC_REG_B, reg & ~RTC_SET);
138 mc146818_check_support(struct hwrtc* rtc)
148 __rtc_tick(const struct hart_state* hstate)
150 struct mc146818* state = (struct mc146818*)isrm_get_payload(hstate);
152 state->tick_counts++;
154 (void)rtc_read_reg(RTC_REG_C);
158 rtc_set_mask(struct hwrtc* rtc)
160 rtc->state = RTC_STATE_MASKED;
165 rtc_cls_mask(struct hwrtc* rtc)
167 struct mc146818* state = rtc_state(rtc->data);
170 state->tick_counts = 0;
175 rtc_chfreq(struct hwrtc* rtc, int freq)
181 rtc_getcnt(struct hwrtc* rtc)
183 struct mc146818* state = rtc_state(rtc->data);
184 return state->tick_counts;
188 rtc_init(struct device_def* devdef)
190 u8_t reg = rtc_read_reg(RTC_REG_A);
191 reg = (reg & ~0x7f) | RTC_FREQUENCY_1024HZ | RTC_DIVIDER_33KHZ;
192 rtc_write_reg(RTC_REG_A, reg);
194 reg = RTC_BIN | RTC_HOURFORM24;
195 rtc_write_reg(RTC_REG_B, reg);
197 // Make sure the rtc timer is disabled by default
200 struct hwrtc* rtc = hwrtc_alloc_new("mc146818");
201 struct mc146818* state = valloc(sizeof(struct mc146818));
203 state->rtc_context = rtc;
204 state->rtc_iv = isrm_bindirq(PC_AT_IRQ_RTC, __rtc_tick);
205 isrm_set_payload(state->rtc_iv, (ptr_t)state);
207 rtc->state = RTC_STATE_MASKED;
209 rtc->base_freq = RTC_TIMER_BASE_FREQUENCY;
210 rtc->get_walltime = rtc_getwalltime;
211 rtc->set_walltime = rtc_setwalltime;
212 rtc->set_mask = rtc_set_mask;
213 rtc->cls_mask = rtc_cls_mask;
214 rtc->get_counts = rtc_getcnt;
215 rtc->chfreq = rtc_chfreq;
217 hwrtc_register(&devdef->class, rtc);
222 static struct device_def devrtc_mc146818 = {
223 .name = "MC146818 RTC",
224 .class = DEVCLASS(DEVIF_SOC, DEVFN_TIME, DEV_RTC),
227 EXPORT_DEVICE(mc146818, &devrtc_mc146818, load_timedev);