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