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