879b4f5c2ced393e1c32a9b093557a433fccc404
[lunaix-os.git] / lunaix-os / arch / x86 / hal / 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 <asm-generic/isrm.h>
14 #include <lunaix/mm/valloc.h>
15 #include <lunaix/status.h>
16 #include <lunaix/hart_state.h>
17
18 #include <hal/hwrtc.h>
19
20 #include <klibc/string.h>
21
22 #include <asm/x86_pmio.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 #define PC_AT_IRQ_RTC                   8
58
59 struct mc146818
60 {
61     struct hwrtc* rtc_context;
62     u32_t rtc_iv;
63     u32_t tick_counts;
64 };
65
66 #define rtc_state(data) ((struct mc146818*)(data))
67
68 static u8_t
69 rtc_read_reg(u8_t reg_selector)
70 {
71     port_wrbyte(RTC_INDEX_PORT, reg_selector);
72     return port_rdbyte(RTC_TARGET_PORT);
73 }
74
75 static void
76 rtc_write_reg(u8_t reg_selector, u8_t val)
77 {
78     port_wrbyte(RTC_INDEX_PORT, reg_selector);
79     port_wrbyte(RTC_TARGET_PORT, val);
80 }
81
82 static void
83 rtc_enable_timer()
84 {
85     u8_t regB = rtc_read_reg(RTC_REG_B);
86     rtc_write_reg(RTC_REG_B, regB | RTC_TIMER_ON);
87 }
88
89 static void
90 rtc_disable_timer()
91 {
92     u8_t regB = rtc_read_reg(RTC_REG_B);
93     rtc_write_reg(RTC_REG_B, regB & ~RTC_TIMER_ON);
94 }
95
96 static void
97 rtc_getwalltime(struct hwrtc* rtc, datetime_t* datetime)
98 {
99     datetime_t current;
100
101     do {
102         while (rtc_read_reg(RTC_REG_A) & 0x80)
103             ;
104         memcpy(&current, datetime, sizeof(datetime_t));
105
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, &current));
114
115     datetime->year += RTC_CURRENT_CENTRY * 100;
116 }
117
118 static void
119 rtc_setwalltime(struct hwrtc* rtc, datetime_t* datetime)
120 {
121     u8_t reg = rtc_read_reg(RTC_REG_B);
122     reg = reg & ~RTC_SET;
123
124     rtc_write_reg(RTC_REG_B, reg | RTC_SET);
125
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);
133
134     rtc_write_reg(RTC_REG_B, reg & ~RTC_SET);
135 }
136
137 static int
138 mc146818_check_support(struct hwrtc* rtc)
139 {
140 #if __ARCH__ == i386
141     return 1;
142 #else
143     return 0;
144 #endif
145 }
146
147 static void
148 __rtc_tick(const struct hart_state* hstate)
149 {
150     struct mc146818* state = (struct mc146818*)isrm_get_payload(hstate);
151
152     state->tick_counts++;
153
154     (void)rtc_read_reg(RTC_REG_C);
155 }
156
157 static void
158 rtc_set_mask(struct hwrtc* rtc)
159 {
160     rtc->state = RTC_STATE_MASKED;
161     rtc_disable_timer();
162 }
163
164 static void
165 rtc_cls_mask(struct hwrtc* rtc)
166 {
167     struct mc146818* state = rtc_state(rtc->data);
168
169     rtc->state = 0;
170     state->tick_counts = 0;
171     rtc_enable_timer();
172 }
173
174 static int
175 rtc_chfreq(struct hwrtc* rtc, int freq)
176 {
177     return ENOTSUP;
178 }
179
180 static int
181 rtc_getcnt(struct hwrtc* rtc)
182 {
183     struct mc146818* state = rtc_state(rtc->data);
184     return state->tick_counts;
185 }
186
187 static int
188 rtc_init(struct device_def* devdef)
189 {
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);
193
194     reg = RTC_BIN | RTC_HOURFORM24;
195     rtc_write_reg(RTC_REG_B, reg);
196
197     // Make sure the rtc timer is disabled by default
198     rtc_disable_timer();
199
200     struct hwrtc* rtc = hwrtc_alloc_new("mc146818");
201     struct mc146818* state = valloc(sizeof(struct mc146818));
202
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);
206
207     rtc->state = RTC_STATE_MASKED;
208     rtc->data = state;
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;
216
217     hwrtc_register(&devdef->class, rtc);
218
219     return 0;
220 }
221
222 static struct device_def devrtc_mc146818 = {
223     .name = "MC146818 RTC",
224     .class = DEVCLASS(DEVIF_SOC, DEVFN_TIME, DEV_RTC),
225     .init = rtc_init
226 };
227 EXPORT_DEVICE(mc146818, &devrtc_mc146818, load_timedev);