1 // 16bit code to handle system clocks.
3 // Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002 MandrakeSoft S.A.
6 // This file may be distributed under the terms of the GNU GPLv3 license.
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" // eoi_pic1
13 #include "bregs.h" // struct bregs
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
22 // Bits for PORT_PS2_CTRLB
23 #define PPCB_T2GATE (1<<0)
24 #define PPCB_SPKR (1<<1)
25 #define PPCB_T2OUT (1<<5)
27 // Bits for PORT_PIT_MODE
28 #define PM_SEL_TIMER0 (0<<6)
29 #define PM_SEL_TIMER1 (1<<6)
30 #define PM_SEL_TIMER2 (2<<6)
31 #define PM_SEL_READBACK (3<<6)
32 #define PM_ACCESS_LATCH (0<<4)
33 #define PM_ACCESS_LOBYTE (1<<4)
34 #define PM_ACCESS_HIBYTE (2<<4)
35 #define PM_ACCESS_WORD (3<<4)
36 #define PM_MODE0 (0<<1)
37 #define PM_MODE1 (1<<1)
38 #define PM_MODE2 (2<<1)
39 #define PM_MODE3 (3<<1)
40 #define PM_MODE4 (4<<1)
41 #define PM_MODE5 (5<<1)
42 #define PM_CNT_BINARY (0<<0)
43 #define PM_CNT_BCD (1<<0)
46 /****************************************************************
48 ****************************************************************/
50 #define PIT_TICK_RATE 1193182 // Underlying HZ of PIT
51 #define CALIBRATE_COUNT 0x800 // Approx 1.7ms
55 u32 cpu_khz VISIBLE16;
62 u8 orig = inb(PORT_PS2_CTRLB);
63 outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB);
64 /* binary, mode 0, LSB/MSB, Ch 2 */
65 outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE);
67 outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2);
69 outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2);
71 u64 start = rdtscll();
72 while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0)
76 // Restore PORT_PS2_CTRLB
77 outb(orig, PORT_PS2_CTRLB);
79 // Store calibrated cpu khz.
80 u64 diff = end - start;
81 dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n"
82 , (u32)start, (u32)end, (u32)diff);
83 u32 hz = diff * PIT_TICK_RATE / CALIBRATE_COUNT;
84 SET_VAR(CS, cpu_khz, hz / 1000);
86 dprintf(1, "CPU Mhz=%u\n", hz / 1000000);
92 u64 start = rdtscll();
93 u64 end = start + diff;
94 while (rdtscll() < end)
101 u32 khz = GET_VAR(CS, cpu_khz);
102 tscsleep(count * khz / 1000000);
107 u32 khz = GET_VAR(CS, cpu_khz);
108 tscsleep(count * khz / 1000);
113 u32 khz = GET_VAR(CS, cpu_khz);
114 tscsleep(count * khz);
117 // Return the TSC value that is 'msecs' time in the future.
119 calc_future_tsc(u32 msecs)
121 u32 khz = GET_VAR(CS, cpu_khz);
122 return rdtscll() + ((u64)khz * msecs);
126 /****************************************************************
128 ****************************************************************/
133 // This function checks to see if the update-in-progress bit
134 // is set in CMOS Status Register A. If not, it returns 0.
135 // If it is set, it tries to wait until there is a transition
136 // to 0, and will return 0 if such a transition occurs. A -1
137 // is returned only after timing out. The maximum period
138 // that this bit should be set is constrained to 244useconds, so
139 // we wait for 1 msec max.
141 if ((inb_cmos(CMOS_STATUS_A) & 0x80) == 0)
143 u64 end = calc_future_tsc(1);
145 if ((inb_cmos(CMOS_STATUS_A) & 0x80) == 0)
147 } while (rdtscll() <= end);
149 // update-in-progress never transitioned to 0
156 // timer0: binary count, 16bit count, mode 2
157 outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE);
158 // maximum count of 0000H = 18.2Hz
159 outb(0x0, PORT_PIT_COUNTER0);
160 outb(0x0, PORT_PIT_COUNTER0);
166 return (val & 0xf) + ((val >> 4) * 10);
172 dprintf(3, "init timer\n");
177 u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
178 u32 ticks = (seconds * 18206507) / 1000000;
179 u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
180 ticks += (minutes * 10923904) / 10000;
181 u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
182 ticks += (hours * 65543427) / 1000;
183 SET_BDA(timer_counter, ticks);
184 SET_BDA(timer_rollover, 0);
186 enable_hwirq(0, entry_08);
187 enable_hwirq(8, entry_70);
193 outb_cmos(0x26, CMOS_STATUS_A);
194 outb_cmos(0x02, CMOS_STATUS_B);
195 inb_cmos(CMOS_STATUS_C);
196 inb_cmos(CMOS_STATUS_D);
200 /****************************************************************
201 * Standard clock functions
202 ****************************************************************/
204 // get current clock count
206 handle_1a00(struct bregs *regs)
208 u32 ticks = GET_BDA(timer_counter);
209 regs->cx = ticks >> 16;
211 regs->al = GET_BDA(timer_rollover);
212 SET_BDA(timer_rollover, 0); // reset flag
216 // Set Current Clock Count
218 handle_1a01(struct bregs *regs)
220 u32 ticks = (regs->cx << 16) | regs->dx;
221 SET_BDA(timer_counter, ticks);
222 SET_BDA(timer_rollover, 0); // reset flag
229 handle_1a02(struct bregs *regs)
231 if (rtc_updating()) {
236 regs->dh = inb_cmos(CMOS_RTC_SECONDS);
237 regs->cl = inb_cmos(CMOS_RTC_MINUTES);
238 regs->ch = inb_cmos(CMOS_RTC_HOURS);
239 regs->dl = inb_cmos(CMOS_STATUS_B) & 0x01;
247 handle_1a03(struct bregs *regs)
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.
254 // before 1111 1101 0111 1101 0000 0000
255 // after 0110 0010 0110 0010 0000 0010
257 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
258 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
259 if (rtc_updating()) {
261 // fall through as if an update were not in progress
263 outb_cmos(regs->dh, CMOS_RTC_SECONDS);
264 outb_cmos(regs->cl, CMOS_RTC_MINUTES);
265 outb_cmos(regs->ch, CMOS_RTC_HOURS);
266 // Set Daylight Savings time enabled bit to requested value
267 u8 val8 = (inb_cmos(CMOS_STATUS_B) & 0x60) | 0x02 | (regs->dl & 0x01);
268 outb_cmos(val8, CMOS_STATUS_B);
270 regs->al = val8; // val last written to Reg B
276 handle_1a04(struct bregs *regs)
279 if (rtc_updating()) {
283 regs->cl = inb_cmos(CMOS_RTC_YEAR);
284 regs->dh = inb_cmos(CMOS_RTC_MONTH);
285 regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH);
286 regs->ch = inb_cmos(CMOS_CENTURY);
293 handle_1a05(struct bregs *regs)
295 // Using a debugger, I notice the following masking/setting
296 // of bits in Status Register B, by setting Reg B to
297 // a few values and getting its value after INT 1A was called.
299 // try#1 try#2 try#3 try#4
300 // before 1111 1101 0111 1101 0000 0010 0000 0000
301 // after 0110 1101 0111 1101 0000 0010 0000 0000
303 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
304 // My assumption: RegB = (RegB & 01111111b)
305 if (rtc_updating()) {
310 outb_cmos(regs->cl, CMOS_RTC_YEAR);
311 outb_cmos(regs->dh, CMOS_RTC_MONTH);
312 outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH);
313 outb_cmos(regs->ch, CMOS_CENTURY);
314 // clear halt-clock bit
315 u8 val8 = inb_cmos(CMOS_STATUS_B) & ~RTC_B_SET;
316 outb_cmos(val8, CMOS_STATUS_B);
318 regs->al = val8; // AL = val last written to Reg B
322 // Set Alarm Time in CMOS
324 handle_1a06(struct bregs *regs)
326 // Using a debugger, I notice the following masking/setting
327 // of bits in Status Register B, by setting Reg B to
328 // a few values and getting its value after INT 1A was called.
331 // before 1101 1111 0101 1111 0000 0000
332 // after 0110 1111 0111 1111 0010 0000
334 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
335 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
336 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
339 // Alarm interrupt enabled already
343 if (rtc_updating()) {
345 // fall through as if an update were not in progress
347 outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM);
348 outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM);
349 outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM);
350 // enable Status Reg B alarm bit, clear halt clock bit
351 outb_cmos((val8 & ~RTC_B_SET) | RTC_B_AIE, CMOS_STATUS_B);
357 handle_1a07(struct bregs *regs)
359 // Using a debugger, I notice the following masking/setting
360 // of bits in Status Register B, by setting Reg B to
361 // a few values and getting its value after INT 1A was called.
363 // try#1 try#2 try#3 try#4
364 // before 1111 1101 0111 1101 0010 0000 0010 0010
365 // after 0100 0101 0101 0101 0000 0000 0000 0010
367 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
368 // My assumption: RegB = (RegB & 01010111b)
369 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
370 // clear clock-halt bit, disable alarm bit
371 outb_cmos(val8 & ~(RTC_B_SET|RTC_B_AIE), CMOS_STATUS_B);
373 regs->al = val8; // val last written to Reg B
379 handle_1aXX(struct bregs *regs)
384 // INT 1Ah Time-of-day Service Entry Point
386 handle_1a(struct bregs *regs)
388 debug_enter(regs, DEBUG_HDL_1a);
390 case 0x00: handle_1a00(regs); break;
391 case 0x01: handle_1a01(regs); break;
392 case 0x02: handle_1a02(regs); break;
393 case 0x03: handle_1a03(regs); break;
394 case 0x04: handle_1a04(regs); break;
395 case 0x05: handle_1a05(regs); break;
396 case 0x06: handle_1a06(regs); break;
397 case 0x07: handle_1a07(regs); break;
398 case 0xb1: handle_1ab1(regs); break;
399 default: handle_1aXX(regs); break;
407 debug_isr(DEBUG_ISR_1c);
410 // INT 08h System Timer ISR Entry Point
414 debug_isr(DEBUG_ISR_08);
419 u32 counter = GET_BDA(timer_counter);
421 // compare to one days worth of timer ticks at 18.2 hz
422 if (counter >= 0x001800B0) {
423 // there has been a midnight rollover at this point
425 SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
428 SET_BDA(timer_counter, counter);
430 // chain to user timer tick INT #0x1c
432 memset(&br, 0, sizeof(br));
433 call16_int(0x1c, &br);
441 /****************************************************************
443 ****************************************************************/
446 set_usertimer(u32 usecs, u16 seg, u16 offset)
448 if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)
451 // Interval not already set.
452 SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
453 SET_BDA(ptr_user_wait_complete_flag, (seg << 16) | offset);
454 SET_BDA(user_wait_timeout, usecs);
456 // Turn on the Periodic Interrupt timer
457 u8 bRegister = inb_cmos(CMOS_STATUS_B);
458 outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
466 // Turn off status byte.
467 SET_BDA(rtc_wait_flag, 0);
468 // Clear the Periodic Interrupt.
469 u8 bRegister = inb_cmos(CMOS_STATUS_B);
470 outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
473 #define RET_ECLOCKINUSE 0x83
475 // Wait for CX:DX microseconds
477 handle_1586(struct bregs *regs)
479 // Use the rtc to wait for the specified time.
481 u32 count = (regs->cx << 16) | regs->dx;
482 int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag);
484 set_code_fail(regs, RET_ECLOCKINUSE);
494 // Set Interval requested.
496 handle_158300(struct bregs *regs)
498 int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx);
500 // Interval already set.
501 set_code_fail(regs, RET_EUNSUPPORTED);
506 // Clear interval requested
508 handle_158301(struct bregs *regs)
515 handle_1583XX(struct bregs *regs)
517 set_code_fail(regs, RET_EUNSUPPORTED);
522 handle_1583(struct bregs *regs)
525 case 0x00: handle_158300(regs); break;
526 case 0x01: handle_158301(regs); break;
527 default: handle_1583XX(regs); break;
531 // int70h: IRQ8 - CMOS RTC
535 debug_isr(DEBUG_ISR_70);
537 // Check which modes are enabled and have occurred.
538 u8 registerB = inb_cmos(CMOS_STATUS_B);
539 u8 registerC = inb_cmos(CMOS_STATUS_C);
541 if (!(registerB & (RTC_B_PIE|RTC_B_AIE)))
543 if (registerC & 0x20) {
544 // Handle Alarm Interrupt.
546 memset(&br, 0, sizeof(br));
547 call16_int(0x4a, &br);
550 if (!(registerC & 0x40))
553 // Handle Periodic Interrupt.
555 if (!GET_BDA(rtc_wait_flag))
558 // Wait Interval (Int 15, AH=83) active.
559 u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds.
561 // Done waiting - write to specified flag byte.
562 u32 segoff = GET_BDA(ptr_user_wait_complete_flag);
563 u16 segment = segoff >> 16;
564 u16 offset = segoff & 0xffff;
565 u8 oldval = GET_FARVAR(segment, *(u8*)(offset+0));
566 SET_FARVAR(segment, *(u8*)(offset+0), oldval | 0x80);
572 SET_BDA(user_wait_timeout, time);