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