feat: better rtc framework which aims to remove single rtc restrictions.
[lunaix-os.git] / lunaix-os / hal / rtc / mc146818a.c
1 /**
2  * @file rtc.c
3  * @author Lunaixsky
4  * @brief RTC & CMOS abstraction. Reference: MC146818A & Intel Series 500 PCH
5  * datasheet
6  * @version 0.1
7  * @date 2022-03-07
8  *
9  * @copyright Copyright (c) 2022
10  *
11  */
12
13 #include <lunaix/isrm.h>
14 #include <lunaix/mm/valloc.h>
15 #include <lunaix/status.h>
16
17 #include <hal/rtc/mc146818a.h>
18
19 #include <klibc/string.h>
20
21 #include <sys/interrupts.h>
22 #include <sys/port_io.h>
23
24 #define RTC_INDEX_PORT 0x70
25 #define RTC_TARGET_PORT 0x71
26
27 #define WITH_NMI_DISABLED 0x80
28
29 #define RTC_CURRENT_CENTRY 20
30
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
38
39 #define RTC_REG_A 0xA
40 #define RTC_REG_B 0xB
41 #define RTC_REG_C 0xC
42 #define RTC_REG_D 0xD
43
44 #define RTC_SET 0x80
45 #define RTC_BIN (1 << 2)
46 #define RTC_HOURFORM24 (1 << 1)
47
48 #define RTC_BIN_ENCODED(reg) (reg & 0x04)
49 #define RTC_24HRS_ENCODED(reg) (reg & 0x02)
50
51 #define RTC_TIMER_BASE_FREQUENCY 1024
52 #define RTC_TIMER_ON 0x40
53
54 #define RTC_FREQUENCY_1024HZ 0b110
55 #define RTC_DIVIDER_33KHZ (0b010 << 4)
56
57 struct mc146818
58 {
59     struct hwrtc* rtc_context;
60     u32_t rtc_iv;
61     u32_t tick_counts;
62 };
63
64 #define rtc_state(data) ((struct mc146818*)(data))
65
66 static u8_t
67 rtc_read_reg(u8_t reg_selector)
68 {
69     port_wrbyte(RTC_INDEX_PORT, reg_selector);
70     return port_rdbyte(RTC_TARGET_PORT);
71 }
72
73 static void
74 rtc_write_reg(u8_t reg_selector, u8_t val)
75 {
76     port_wrbyte(RTC_INDEX_PORT, reg_selector);
77     port_wrbyte(RTC_TARGET_PORT, val);
78 }
79
80 static void
81 rtc_enable_timer()
82 {
83     u8_t regB = rtc_read_reg(RTC_REG_B);
84     rtc_write_reg(RTC_REG_B, regB | RTC_TIMER_ON);
85 }
86
87 static void
88 rtc_disable_timer()
89 {
90     u8_t regB = rtc_read_reg(RTC_REG_B);
91     rtc_write_reg(RTC_REG_B, regB & ~RTC_TIMER_ON);
92 }
93
94 static void
95 rtc_getwalltime(struct hwrtc* rtc, datetime_t* datetime)
96 {
97     datetime_t current;
98
99     do {
100         while (rtc_read_reg(RTC_REG_A) & 0x80)
101             ;
102         memcpy(&current, datetime, sizeof(datetime_t));
103
104         datetime->year = rtc_read_reg(RTC_REG_YRS);
105         datetime->month = rtc_read_reg(RTC_REG_MTH);
106         datetime->day = rtc_read_reg(RTC_REG_DAY);
107         datetime->weekday = rtc_read_reg(RTC_REG_WDY);
108         datetime->hour = rtc_read_reg(RTC_REG_HRS);
109         datetime->minute = rtc_read_reg(RTC_REG_MIN);
110         datetime->second = rtc_read_reg(RTC_REG_SEC);
111     } while (!datatime_eq(datetime, &current));
112
113     datetime->year += RTC_CURRENT_CENTRY * 100;
114 }
115
116 static void
117 rtc_setwalltime(struct hwrtc* rtc, datetime_t* datetime)
118 {
119     u8_t reg = rtc_read_reg(RTC_REG_B);
120     reg = reg & ~RTC_SET;
121
122     rtc_write_reg(RTC_REG_B, reg | RTC_SET);
123
124     rtc_write_reg(RTC_REG_YRS, datetime->year - RTC_CURRENT_CENTRY * 100);
125     rtc_write_reg(RTC_REG_MTH, datetime->month);
126     rtc_write_reg(RTC_REG_DAY, datetime->day);
127     rtc_write_reg(RTC_REG_WDY, datetime->weekday);
128     rtc_write_reg(RTC_REG_HRS, datetime->hour);
129     rtc_write_reg(RTC_REG_MIN, datetime->minute);
130     rtc_write_reg(RTC_REG_SEC, datetime->second);
131
132     rtc_write_reg(RTC_REG_B, reg & ~RTC_SET);
133 }
134
135 static int
136 mc146818_check_support(struct hwrtc* rtc)
137 {
138 #if __ARCH__ == i386
139     return 1;
140 #else
141     return 0;
142 #endif
143 }
144
145 static void
146 __rtc_tick(const isr_param* param)
147 {
148     struct mc146818* state =
149       (struct mc146818*)isrm_get_payload(param->execp->vector);
150
151     state->tick_counts++;
152
153     (void)rtc_read_reg(RTC_REG_C);
154 }
155
156 static void
157 rtc_set_mask(struct hwrtc* rtc)
158 {
159     rtc->state = RTC_STATE_MASKED;
160     rtc_disable_timer();
161 }
162
163 static void
164 rtc_cls_mask(struct hwrtc* rtc)
165 {
166     struct mc146818* state = rtc_state(rtc->data);
167
168     rtc->state = 0;
169     state->tick_counts = 0;
170     rtc_enable_timer();
171 }
172
173 static int
174 rtc_chfreq(struct hwrtc* rtc, int freq)
175 {
176     return ENOTSUP;
177 }
178
179 static int
180 rtc_getcnt(struct hwrtc* rtc)
181 {
182     struct mc146818* state = rtc_state(rtc->data);
183     return state->tick_counts;
184 }
185
186 static void
187 rtc_init()
188 {
189     u8_t reg = rtc_read_reg(RTC_REG_A);
190     reg = (reg & ~0x7f) | RTC_FREQUENCY_1024HZ | RTC_DIVIDER_33KHZ;
191     rtc_write_reg(RTC_REG_A, reg);
192
193     reg = RTC_BIN | RTC_HOURFORM24;
194     rtc_write_reg(RTC_REG_B, reg);
195
196     // Make sure the rtc timer is disabled by default
197     rtc_disable_timer();
198
199     struct hwrtc* rtc = hwrtc_alloc_new("mc146818");
200     struct mc146818* state = valloc(sizeof(struct mc146818));
201
202     state->rtc_context = rtc;
203     state->rtc_iv = isrm_bindirq(PC_AT_IRQ_RTC, __rtc_tick);
204     isrm_set_payload(state->rtc_iv, (ptr_t)state);
205
206     rtc->state = RTC_STATE_MASKED;
207     rtc->data = state;
208     rtc->base_freq = RTC_TIMER_BASE_FREQUENCY;
209     rtc->get_walltime = rtc_getwalltime;
210     rtc->set_walltime = rtc_setwalltime;
211     rtc->set_mask = rtc_set_mask;
212     rtc->cls_mask = rtc_cls_mask;
213     rtc->get_counts = rtc_getcnt;
214     rtc->chfreq = rtc_chfreq;
215 }
216 EXPORT_RTC_DEVICE(mc146818, rtc_init);