Always define macro MODE16 - that way it can be used in C conditionals.
[seabios.git] / src / clock.c
1 // 16bit code to handle system clocks.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "biosvar.h" // SET_BDA
9 #include "util.h" // debug_enter
10 #include "disk.h" // floppy_tick
11 #include "cmos.h" // inb_cmos
12 #include "pic.h" // unmask_pic1
13 #include "bregs.h" // struct bregs
14
15 // RTC register flags
16 #define RTC_A_UIP 0x80
17 #define RTC_B_SET 0x80
18 #define RTC_B_PIE 0x40
19 #define RTC_B_AIE 0x20
20 #define RTC_B_UIE 0x10
21
22
23 /****************************************************************
24  * Init
25  ****************************************************************/
26
27 static void
28 pit_setup()
29 {
30     // timer0: binary count, 16bit count, mode 2
31     outb(0x34, PORT_PIT_MODE);
32     // maximum count of 0000H = 18.2Hz
33     outb(0x0, PORT_PIT_COUNTER0);
34     outb(0x0, PORT_PIT_COUNTER0);
35 }
36
37 static u32
38 bcd2bin(u8 val)
39 {
40     return (val & 0xf) + ((val >> 4) * 10);
41 }
42
43 void
44 timer_setup()
45 {
46     dprintf(3, "init timer\n");
47     pit_setup();
48
49     u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
50     u32 ticks = (seconds * 18206507) / 1000000;
51     u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
52     ticks += (minutes * 10923904) / 10000;
53     u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
54     ticks += (hours * 65543427) / 1000;
55     SET_BDA(timer_counter, ticks);
56     SET_BDA(timer_rollover, 0);
57
58     // Enable IRQ0 (handle_08)
59     unmask_pic1(PIC1_IRQ0);
60 }
61
62 static void
63 init_rtc()
64 {
65     outb_cmos(0x26, CMOS_STATUS_A);
66     outb_cmos(0x02, CMOS_STATUS_B);
67     inb_cmos(CMOS_STATUS_C);
68     inb_cmos(CMOS_STATUS_D);
69 }
70
71
72 /****************************************************************
73  * Standard clock functions
74  ****************************************************************/
75
76 static u8
77 rtc_updating()
78 {
79     // This function checks to see if the update-in-progress bit
80     // is set in CMOS Status Register A.  If not, it returns 0.
81     // If it is set, it tries to wait until there is a transition
82     // to 0, and will return 0 if such a transition occurs.  A 1
83     // is returned only after timing out.  The maximum period
84     // that this bit should be set is constrained to 244useconds.
85     // The count I use below guarantees coverage or more than
86     // this time, with any reasonable IPS setting.
87
88     u16 count = 25000;
89     while (--count != 0) {
90         if ( (inb_cmos(CMOS_STATUS_A) & 0x80) == 0 )
91             return 0;
92     }
93     return 1; // update-in-progress never transitioned to 0
94 }
95
96 // get current clock count
97 static void
98 handle_1a00(struct bregs *regs)
99 {
100     u32 ticks = GET_BDA(timer_counter);
101     regs->cx = ticks >> 16;
102     regs->dx = ticks;
103     regs->al = GET_BDA(timer_rollover);
104     SET_BDA(timer_rollover, 0); // reset flag
105     set_success(regs);
106 }
107
108 // Set Current Clock Count
109 static void
110 handle_1a01(struct bregs *regs)
111 {
112     u32 ticks = (regs->cx << 16) | regs->dx;
113     SET_BDA(timer_counter, ticks);
114     SET_BDA(timer_rollover, 0); // reset flag
115     regs->ah = 0;
116     set_success(regs);
117 }
118
119 // Read CMOS Time
120 static void
121 handle_1a02(struct bregs *regs)
122 {
123     if (rtc_updating()) {
124         set_fail(regs);
125         return;
126     }
127
128     regs->dh = inb_cmos(CMOS_RTC_SECONDS);
129     regs->cl = inb_cmos(CMOS_RTC_MINUTES);
130     regs->ch = inb_cmos(CMOS_RTC_HOURS);
131     regs->dl = inb_cmos(CMOS_STATUS_B) & 0x01;
132     regs->ah = 0;
133     regs->al = regs->ch;
134     set_success(regs);
135 }
136
137 // Set CMOS Time
138 static void
139 handle_1a03(struct bregs *regs)
140 {
141     // Using a debugger, I notice the following masking/setting
142     // of bits in Status Register B, by setting Reg B to
143     // a few values and getting its value after INT 1A was called.
144     //
145     //        try#1       try#2       try#3
146     // before 1111 1101   0111 1101   0000 0000
147     // after  0110 0010   0110 0010   0000 0010
148     //
149     // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
150     // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
151     if (rtc_updating()) {
152         init_rtc();
153         // fall through as if an update were not in progress
154     }
155     outb_cmos(regs->dh, CMOS_RTC_SECONDS);
156     outb_cmos(regs->cl, CMOS_RTC_MINUTES);
157     outb_cmos(regs->ch, CMOS_RTC_HOURS);
158     // Set Daylight Savings time enabled bit to requested value
159     u8 val8 = (inb_cmos(CMOS_STATUS_B) & 0x60) | 0x02 | (regs->dl & 0x01);
160     outb_cmos(val8, CMOS_STATUS_B);
161     regs->ah = 0;
162     regs->al = val8; // val last written to Reg B
163     set_success(regs);
164 }
165
166 // Read CMOS Date
167 static void
168 handle_1a04(struct bregs *regs)
169 {
170     regs->ah = 0;
171     if (rtc_updating()) {
172         set_fail(regs);
173         return;
174     }
175     regs->cl = inb_cmos(CMOS_RTC_YEAR);
176     regs->dh = inb_cmos(CMOS_RTC_MONTH);
177     regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH);
178     regs->ch = inb_cmos(CMOS_CENTURY);
179     regs->al = regs->ch;
180     set_success(regs);
181 }
182
183 // Set CMOS Date
184 static void
185 handle_1a05(struct bregs *regs)
186 {
187     // Using a debugger, I notice the following masking/setting
188     // of bits in Status Register B, by setting Reg B to
189     // a few values and getting its value after INT 1A was called.
190     //
191     //        try#1       try#2       try#3       try#4
192     // before 1111 1101   0111 1101   0000 0010   0000 0000
193     // after  0110 1101   0111 1101   0000 0010   0000 0000
194     //
195     // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
196     // My assumption: RegB = (RegB & 01111111b)
197     if (rtc_updating()) {
198         init_rtc();
199         set_fail(regs);
200         return;
201     }
202     outb_cmos(regs->cl, CMOS_RTC_YEAR);
203     outb_cmos(regs->dh, CMOS_RTC_MONTH);
204     outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH);
205     outb_cmos(regs->ch, CMOS_CENTURY);
206     // clear halt-clock bit
207     u8 val8 = inb_cmos(CMOS_STATUS_B) & ~RTC_B_SET;
208     outb_cmos(val8, CMOS_STATUS_B);
209     regs->ah = 0;
210     regs->al = val8; // AL = val last written to Reg B
211     set_success(regs);
212 }
213
214 // Set Alarm Time in CMOS
215 static void
216 handle_1a06(struct bregs *regs)
217 {
218     // Using a debugger, I notice the following masking/setting
219     // of bits in Status Register B, by setting Reg B to
220     // a few values and getting its value after INT 1A was called.
221     //
222     //        try#1       try#2       try#3
223     // before 1101 1111   0101 1111   0000 0000
224     // after  0110 1111   0111 1111   0010 0000
225     //
226     // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
227     // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
228     u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
229     regs->ax = 0;
230     if (val8 & 0x20) {
231         // Alarm interrupt enabled already
232         set_fail(regs);
233         return;
234     }
235     if (rtc_updating()) {
236         init_rtc();
237         // fall through as if an update were not in progress
238     }
239     outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM);
240     outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM);
241     outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM);
242     unmask_pic2(PIC2_IRQ8); // enable IRQ 8
243     // enable Status Reg B alarm bit, clear halt clock bit
244     outb_cmos((val8 & ~RTC_B_SET) | RTC_B_AIE, CMOS_STATUS_B);
245     set_success(regs);
246 }
247
248 // Turn off Alarm
249 static void
250 handle_1a07(struct bregs *regs)
251 {
252     // Using a debugger, I notice the following masking/setting
253     // of bits in Status Register B, by setting Reg B to
254     // a few values and getting its value after INT 1A was called.
255     //
256     //        try#1       try#2       try#3       try#4
257     // before 1111 1101   0111 1101   0010 0000   0010 0010
258     // after  0100 0101   0101 0101   0000 0000   0000 0010
259     //
260     // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
261     // My assumption: RegB = (RegB & 01010111b)
262     u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
263     // clear clock-halt bit, disable alarm bit
264     outb_cmos(val8 & ~(RTC_B_SET|RTC_B_AIE), CMOS_STATUS_B);
265     regs->ah = 0;
266     regs->al = val8; // val last written to Reg B
267     set_success(regs);
268 }
269
270 // Unsupported
271 static void
272 handle_1aXX(struct bregs *regs)
273 {
274     set_fail(regs);
275 }
276
277 // INT 1Ah Time-of-day Service Entry Point
278 void VISIBLE16
279 handle_1a(struct bregs *regs)
280 {
281     debug_enter(regs, DEBUG_HDL_1a);
282     switch (regs->ah) {
283     case 0x00: handle_1a00(regs); break;
284     case 0x01: handle_1a01(regs); break;
285     case 0x02: handle_1a02(regs); break;
286     case 0x03: handle_1a03(regs); break;
287     case 0x04: handle_1a04(regs); break;
288     case 0x05: handle_1a05(regs); break;
289     case 0x06: handle_1a06(regs); break;
290     case 0x07: handle_1a07(regs); break;
291     case 0xb1: handle_1ab1(regs); break;
292     default:   handle_1aXX(regs); break;
293     }
294 }
295
296 // User Timer Tick
297 void VISIBLE16
298 handle_1c()
299 {
300     debug_isr(DEBUG_ISR_1c);
301 }
302
303 // INT 08h System Timer ISR Entry Point
304 void VISIBLE16
305 handle_08()
306 {
307     debug_isr(DEBUG_ISR_08);
308     irq_enable();
309
310     floppy_tick();
311
312     u32 counter = GET_BDA(timer_counter);
313     counter++;
314     // compare to one days worth of timer ticks at 18.2 hz
315     if (counter >= 0x001800B0) {
316         // there has been a midnight rollover at this point
317         counter = 0;
318         SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
319     }
320
321     SET_BDA(timer_counter, counter);
322
323     // chain to user timer tick INT #0x1c
324     struct bregs br;
325     memset(&br, 0, sizeof(br));
326     call16_int(0x1c, &br);
327
328     irq_disable();
329
330     eoi_pic1();
331 }
332
333
334 /****************************************************************
335  * Periodic timer
336  ****************************************************************/
337
338 static int
339 set_usertimer(u32 usecs, u16 seg, u16 offset)
340 {
341     if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)
342         return -1;
343
344     // Interval not already set.
345     SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING);  // Set status byte.
346     SET_BDA(ptr_user_wait_complete_flag, (seg << 16) | offset);
347     SET_BDA(user_wait_timeout, usecs);
348
349     // Unmask IRQ8 so INT70 will get through.
350     unmask_pic2(PIC2_IRQ8);
351     // Turn on the Periodic Interrupt timer
352     u8 bRegister = inb_cmos(CMOS_STATUS_B);
353     outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
354
355     return 0;
356 }
357
358 static void
359 clear_usertimer()
360 {
361     // Turn off status byte.
362     SET_BDA(rtc_wait_flag, 0);
363     // Clear the Periodic Interrupt.
364     u8 bRegister = inb_cmos(CMOS_STATUS_B);
365     outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
366 }
367
368 // Sleep for n microseconds.
369 int
370 usleep(u32 count)
371 {
372     if (MODE16) {
373         // In 16bit mode, use the rtc to wait for the specified time.
374         u8 statusflag = 0;
375         int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag);
376         if (ret)
377             return -1;
378         irq_enable();
379         while (!statusflag)
380             cpu_relax();
381         irq_disable();
382         return 0;
383     } else {
384         // In 32bit mode, we need to call into 16bit mode to sleep.
385         struct bregs br;
386         memset(&br, 0, sizeof(br));
387         br.ah = 0x86;
388         br.cx = count >> 16;
389         br.dx = count;
390         call16_int(0x15, &br);
391         if (br.flags & F_CF)
392             return -1;
393         return 0;
394     }
395 }
396
397 #define RET_ECLOCKINUSE  0x83
398
399 // Wait for CX:DX microseconds. currently using the
400 // refresh request port 0x61 bit4, toggling every 15usec
401 void
402 handle_1586(struct bregs *regs)
403 {
404     int ret = usleep((regs->cx << 16) | regs->dx);
405     if (ret)
406         set_code_fail(regs, RET_ECLOCKINUSE);
407     else
408         set_success(regs);
409 }
410
411 // Set Interval requested.
412 static void
413 handle_158300(struct bregs *regs)
414 {
415     int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx);
416     if (ret)
417         // Interval already set.
418         set_code_fail(regs, RET_EUNSUPPORTED);
419     else
420         set_success(regs);
421 }
422
423 // Clear interval requested
424 static void
425 handle_158301(struct bregs *regs)
426 {
427     clear_usertimer();
428     set_success(regs);
429 }
430
431 static void
432 handle_1583XX(struct bregs *regs)
433 {
434     set_code_fail(regs, RET_EUNSUPPORTED);
435     regs->al--;
436 }
437
438 void
439 handle_1583(struct bregs *regs)
440 {
441     switch (regs->al) {
442     case 0x00: handle_158300(regs); break;
443     case 0x01: handle_158301(regs); break;
444     default:   handle_1583XX(regs); break;
445     }
446 }
447
448 // int70h: IRQ8 - CMOS RTC
449 void VISIBLE16
450 handle_70()
451 {
452     debug_isr(DEBUG_ISR_70);
453
454     // Check which modes are enabled and have occurred.
455     u8 registerB = inb_cmos(CMOS_STATUS_B);
456     u8 registerC = inb_cmos(CMOS_STATUS_C);
457
458     if (!(registerB & (RTC_B_PIE|RTC_B_AIE)))
459         goto done;
460     if (registerC & 0x20) {
461         // Handle Alarm Interrupt.
462         struct bregs br;
463         memset(&br, 0, sizeof(br));
464         call16_int(0x4a, &br);
465         irq_disable();
466     }
467     if (!(registerC & 0x40))
468         goto done;
469
470     // Handle Periodic Interrupt.
471
472     if (!GET_BDA(rtc_wait_flag))
473         goto done;
474
475     // Wait Interval (Int 15, AH=83) active.
476     u32 time = GET_BDA(user_wait_timeout);  // Time left in microseconds.
477     if (time < 0x3D1) {
478         // Done waiting - write to specified flag byte.
479         u32 segoff = GET_BDA(ptr_user_wait_complete_flag);
480         u16 segment = segoff >> 16;
481         u16 offset = segoff & 0xffff;
482         u8 oldval = GET_FARVAR(segment, *(u8*)(offset+0));
483         SET_FARVAR(segment, *(u8*)(offset+0), oldval | 0x80);
484
485         clear_usertimer();
486     } else {
487         // Continue waiting.
488         time -= 0x3D1;
489         SET_BDA(user_wait_timeout, time);
490     }
491
492 done:
493     eoi_pic2();
494 }