4 * @brief RTC & CMOS abstraction. Reference: MC146818A & Intel Series 500 PCH
9 * @copyright Copyright (c) 2022
13 #include <lunaix/mm/valloc.h>
14 #include <lunaix/status.h>
15 #include <lunaix/hart_state.h>
17 #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 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
43 #define RTC_BIN (1 << 2)
44 #define RTC_HOURFORM24 (1 << 1)
46 #define RTC_BIN_ENCODED(reg) (reg & 0x04)
47 #define RTC_24HRS_ENCODED(reg) (reg & 0x02)
49 #define RTC_TIMER_BASE_FREQUENCY 1024
50 #define RTC_TIMER_ON 0x40
52 #define RTC_FREQUENCY_1024HZ 0b110
53 #define RTC_DIVIDER_33KHZ (0b010 << 4)
55 #define PC_AT_IRQ_RTC 8
59 struct hwrtc_potens* rtc_context;
63 #define rtc_state(data) ((struct mc146818*)(data))
66 rtc_read_reg(u8_t reg_selector)
68 port_wrbyte(RTC_INDEX_PORT, reg_selector);
69 return port_rdbyte(RTC_TARGET_PORT);
73 rtc_write_reg(u8_t reg_selector, u8_t val)
75 port_wrbyte(RTC_INDEX_PORT, reg_selector);
76 port_wrbyte(RTC_TARGET_PORT, val);
82 u8_t regB = rtc_read_reg(RTC_REG_B);
83 rtc_write_reg(RTC_REG_B, regB | RTC_TIMER_ON);
89 u8_t regB = rtc_read_reg(RTC_REG_B);
90 rtc_write_reg(RTC_REG_B, regB & ~RTC_TIMER_ON);
94 rtc_getwalltime(struct hwrtc_potens* rtc, datetime_t* datetime)
99 while (rtc_read_reg(RTC_REG_A) & 0x80)
101 memcpy(¤t, datetime, sizeof(datetime_t));
103 datetime->year = rtc_read_reg(RTC_REG_YRS);
104 datetime->month = rtc_read_reg(RTC_REG_MTH);
105 datetime->day = rtc_read_reg(RTC_REG_DAY);
106 datetime->weekday = rtc_read_reg(RTC_REG_WDY);
107 datetime->hour = rtc_read_reg(RTC_REG_HRS);
108 datetime->minute = rtc_read_reg(RTC_REG_MIN);
109 datetime->second = rtc_read_reg(RTC_REG_SEC);
110 } while (!datatime_eq(datetime, ¤t));
112 datetime->year += RTC_CURRENT_CENTRY * 100;
116 rtc_setwalltime(struct hwrtc_potens* rtc, datetime_t* datetime)
118 u8_t reg = rtc_read_reg(RTC_REG_B);
119 reg = reg & ~RTC_SET;
121 rtc_write_reg(RTC_REG_B, reg | RTC_SET);
123 rtc_write_reg(RTC_REG_YRS, datetime->year - RTC_CURRENT_CENTRY * 100);
124 rtc_write_reg(RTC_REG_MTH, datetime->month);
125 rtc_write_reg(RTC_REG_DAY, datetime->day);
126 rtc_write_reg(RTC_REG_WDY, datetime->weekday);
127 rtc_write_reg(RTC_REG_HRS, datetime->hour);
128 rtc_write_reg(RTC_REG_MIN, datetime->minute);
129 rtc_write_reg(RTC_REG_SEC, datetime->second);
131 rtc_write_reg(RTC_REG_B, reg & ~RTC_SET);
135 __rtc_tick(irq_t irq, const struct hart_state* hstate)
137 struct mc146818* state;
139 state = irq_payload(irq, struct mc146818);
140 state->rtc_context->live++;
142 (void)rtc_read_reg(RTC_REG_C);
146 rtc_set_proactive(struct hwrtc_potens* pot, bool proactive)
153 pot->state = RTC_STATE_MASKED;
159 rtc_chfreq(struct hwrtc_potens* rtc, int freq)
165 __rtc_calibrate(struct hwrtc_potens* pot)
167 struct mc146818* state;
168 struct device* rtc_dev;
171 rtc_dev = potens_dev(pot);
173 reg = rtc_read_reg(RTC_REG_A);
174 reg = (reg & ~0x7f) | RTC_FREQUENCY_1024HZ | RTC_DIVIDER_33KHZ;
175 rtc_write_reg(RTC_REG_A, reg);
177 reg = RTC_BIN | RTC_HOURFORM24;
178 rtc_write_reg(RTC_REG_B, reg);
180 // Make sure the rtc timer is disabled by default
183 pot->base_freq = RTC_TIMER_BASE_FREQUENCY;
185 state = (struct mc146818*)rtc_dev->underlay;
187 state->irq = irq_declare_line(__rtc_tick, PC_AT_IRQ_RTC);
188 irq_set_payload(state->irq, state);
190 irq_assign(irq_owning_domain(rtc_dev), state->irq, NULL);
195 static struct hwrtc_potens_ops ops = {
196 .get_walltime = rtc_getwalltime,
197 .set_walltime = rtc_setwalltime,
198 .set_proactive = rtc_set_proactive,
199 .chfreq = rtc_chfreq,
200 .calibrate = __rtc_calibrate
204 rtc_load(struct device_def* devdef)
207 struct mc146818* state;
208 struct hwrtc_potens* pot;
210 state = valloc(sizeof(struct mc146818));
211 dev = device_allocsys(NULL, state);
213 pot = hwrtc_attach_potens(dev, &ops);
218 pot->state = RTC_STATE_MASKED;
219 state->rtc_context = pot;
221 register_device(dev, &devdef->class, "mc146818");
226 static struct device_def devrtc_mc146818 = {
227 def_device_class(INTEL, TIME, RTC),
228 def_device_name("x86 legacy RTC"),
229 def_on_load(rtc_load)
231 EXPORT_DEVICE(mc146818, &devrtc_mc146818, load_sysconf);