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);
118 /****************************************************************
120 ****************************************************************/
125 // timer0: binary count, 16bit count, mode 2
126 outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE);
127 // maximum count of 0000H = 18.2Hz
128 outb(0x0, PORT_PIT_COUNTER0);
129 outb(0x0, PORT_PIT_COUNTER0);
135 return (val & 0xf) + ((val >> 4) * 10);
141 dprintf(3, "init timer\n");
145 u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
146 u32 ticks = (seconds * 18206507) / 1000000;
147 u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
148 ticks += (minutes * 10923904) / 10000;
149 u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
150 ticks += (hours * 65543427) / 1000;
151 SET_BDA(timer_counter, ticks);
152 SET_BDA(timer_rollover, 0);
154 enable_hwirq(0, entry_08);
155 enable_hwirq(8, entry_70);
161 outb_cmos(0x26, CMOS_STATUS_A);
162 outb_cmos(0x02, CMOS_STATUS_B);
163 inb_cmos(CMOS_STATUS_C);
164 inb_cmos(CMOS_STATUS_D);
168 /****************************************************************
169 * Standard clock functions
170 ****************************************************************/
175 // This function checks to see if the update-in-progress bit
176 // is set in CMOS Status Register A. If not, it returns 0.
177 // If it is set, it tries to wait until there is a transition
178 // to 0, and will return 0 if such a transition occurs. A 1
179 // is returned only after timing out. The maximum period
180 // that this bit should be set is constrained to 244useconds.
181 // The count I use below guarantees coverage or more than
182 // this time, with any reasonable IPS setting.
185 while (--count != 0) {
186 if ( (inb_cmos(CMOS_STATUS_A) & 0x80) == 0 )
189 return 1; // update-in-progress never transitioned to 0
192 // get current clock count
194 handle_1a00(struct bregs *regs)
196 u32 ticks = GET_BDA(timer_counter);
197 regs->cx = ticks >> 16;
199 regs->al = GET_BDA(timer_rollover);
200 SET_BDA(timer_rollover, 0); // reset flag
204 // Set Current Clock Count
206 handle_1a01(struct bregs *regs)
208 u32 ticks = (regs->cx << 16) | regs->dx;
209 SET_BDA(timer_counter, ticks);
210 SET_BDA(timer_rollover, 0); // reset flag
217 handle_1a02(struct bregs *regs)
219 if (rtc_updating()) {
224 regs->dh = inb_cmos(CMOS_RTC_SECONDS);
225 regs->cl = inb_cmos(CMOS_RTC_MINUTES);
226 regs->ch = inb_cmos(CMOS_RTC_HOURS);
227 regs->dl = inb_cmos(CMOS_STATUS_B) & 0x01;
235 handle_1a03(struct bregs *regs)
237 // Using a debugger, I notice the following masking/setting
238 // of bits in Status Register B, by setting Reg B to
239 // a few values and getting its value after INT 1A was called.
242 // before 1111 1101 0111 1101 0000 0000
243 // after 0110 0010 0110 0010 0000 0010
245 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
246 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
247 if (rtc_updating()) {
249 // fall through as if an update were not in progress
251 outb_cmos(regs->dh, CMOS_RTC_SECONDS);
252 outb_cmos(regs->cl, CMOS_RTC_MINUTES);
253 outb_cmos(regs->ch, CMOS_RTC_HOURS);
254 // Set Daylight Savings time enabled bit to requested value
255 u8 val8 = (inb_cmos(CMOS_STATUS_B) & 0x60) | 0x02 | (regs->dl & 0x01);
256 outb_cmos(val8, CMOS_STATUS_B);
258 regs->al = val8; // val last written to Reg B
264 handle_1a04(struct bregs *regs)
267 if (rtc_updating()) {
271 regs->cl = inb_cmos(CMOS_RTC_YEAR);
272 regs->dh = inb_cmos(CMOS_RTC_MONTH);
273 regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH);
274 regs->ch = inb_cmos(CMOS_CENTURY);
281 handle_1a05(struct bregs *regs)
283 // Using a debugger, I notice the following masking/setting
284 // of bits in Status Register B, by setting Reg B to
285 // a few values and getting its value after INT 1A was called.
287 // try#1 try#2 try#3 try#4
288 // before 1111 1101 0111 1101 0000 0010 0000 0000
289 // after 0110 1101 0111 1101 0000 0010 0000 0000
291 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
292 // My assumption: RegB = (RegB & 01111111b)
293 if (rtc_updating()) {
298 outb_cmos(regs->cl, CMOS_RTC_YEAR);
299 outb_cmos(regs->dh, CMOS_RTC_MONTH);
300 outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH);
301 outb_cmos(regs->ch, CMOS_CENTURY);
302 // clear halt-clock bit
303 u8 val8 = inb_cmos(CMOS_STATUS_B) & ~RTC_B_SET;
304 outb_cmos(val8, CMOS_STATUS_B);
306 regs->al = val8; // AL = val last written to Reg B
310 // Set Alarm Time in CMOS
312 handle_1a06(struct bregs *regs)
314 // Using a debugger, I notice the following masking/setting
315 // of bits in Status Register B, by setting Reg B to
316 // a few values and getting its value after INT 1A was called.
319 // before 1101 1111 0101 1111 0000 0000
320 // after 0110 1111 0111 1111 0010 0000
322 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
323 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
324 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
327 // Alarm interrupt enabled already
331 if (rtc_updating()) {
333 // fall through as if an update were not in progress
335 outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM);
336 outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM);
337 outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM);
338 // enable Status Reg B alarm bit, clear halt clock bit
339 outb_cmos((val8 & ~RTC_B_SET) | RTC_B_AIE, CMOS_STATUS_B);
345 handle_1a07(struct bregs *regs)
347 // Using a debugger, I notice the following masking/setting
348 // of bits in Status Register B, by setting Reg B to
349 // a few values and getting its value after INT 1A was called.
351 // try#1 try#2 try#3 try#4
352 // before 1111 1101 0111 1101 0010 0000 0010 0010
353 // after 0100 0101 0101 0101 0000 0000 0000 0010
355 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
356 // My assumption: RegB = (RegB & 01010111b)
357 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
358 // clear clock-halt bit, disable alarm bit
359 outb_cmos(val8 & ~(RTC_B_SET|RTC_B_AIE), CMOS_STATUS_B);
361 regs->al = val8; // val last written to Reg B
367 handle_1aXX(struct bregs *regs)
372 // INT 1Ah Time-of-day Service Entry Point
374 handle_1a(struct bregs *regs)
376 debug_enter(regs, DEBUG_HDL_1a);
378 case 0x00: handle_1a00(regs); break;
379 case 0x01: handle_1a01(regs); break;
380 case 0x02: handle_1a02(regs); break;
381 case 0x03: handle_1a03(regs); break;
382 case 0x04: handle_1a04(regs); break;
383 case 0x05: handle_1a05(regs); break;
384 case 0x06: handle_1a06(regs); break;
385 case 0x07: handle_1a07(regs); break;
386 case 0xb1: handle_1ab1(regs); break;
387 default: handle_1aXX(regs); break;
395 debug_isr(DEBUG_ISR_1c);
398 // INT 08h System Timer ISR Entry Point
402 debug_isr(DEBUG_ISR_08);
407 u32 counter = GET_BDA(timer_counter);
409 // compare to one days worth of timer ticks at 18.2 hz
410 if (counter >= 0x001800B0) {
411 // there has been a midnight rollover at this point
413 SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
416 SET_BDA(timer_counter, counter);
418 // chain to user timer tick INT #0x1c
420 memset(&br, 0, sizeof(br));
421 call16_int(0x1c, &br);
429 /****************************************************************
431 ****************************************************************/
434 set_usertimer(u32 usecs, u16 seg, u16 offset)
436 if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)
439 // Interval not already set.
440 SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
441 SET_BDA(ptr_user_wait_complete_flag, (seg << 16) | offset);
442 SET_BDA(user_wait_timeout, usecs);
444 // Turn on the Periodic Interrupt timer
445 u8 bRegister = inb_cmos(CMOS_STATUS_B);
446 outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
454 // Turn off status byte.
455 SET_BDA(rtc_wait_flag, 0);
456 // Clear the Periodic Interrupt.
457 u8 bRegister = inb_cmos(CMOS_STATUS_B);
458 outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
461 #define RET_ECLOCKINUSE 0x83
463 // Wait for CX:DX microseconds
465 handle_1586(struct bregs *regs)
467 // Use the rtc to wait for the specified time.
469 u32 count = (regs->cx << 16) | regs->dx;
470 int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag);
472 set_code_fail(regs, RET_ECLOCKINUSE);
482 // Set Interval requested.
484 handle_158300(struct bregs *regs)
486 int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx);
488 // Interval already set.
489 set_code_fail(regs, RET_EUNSUPPORTED);
494 // Clear interval requested
496 handle_158301(struct bregs *regs)
503 handle_1583XX(struct bregs *regs)
505 set_code_fail(regs, RET_EUNSUPPORTED);
510 handle_1583(struct bregs *regs)
513 case 0x00: handle_158300(regs); break;
514 case 0x01: handle_158301(regs); break;
515 default: handle_1583XX(regs); break;
519 // int70h: IRQ8 - CMOS RTC
523 debug_isr(DEBUG_ISR_70);
525 // Check which modes are enabled and have occurred.
526 u8 registerB = inb_cmos(CMOS_STATUS_B);
527 u8 registerC = inb_cmos(CMOS_STATUS_C);
529 if (!(registerB & (RTC_B_PIE|RTC_B_AIE)))
531 if (registerC & 0x20) {
532 // Handle Alarm Interrupt.
534 memset(&br, 0, sizeof(br));
535 call16_int(0x4a, &br);
538 if (!(registerC & 0x40))
541 // Handle Periodic Interrupt.
543 if (!GET_BDA(rtc_wait_flag))
546 // Wait Interval (Int 15, AH=83) active.
547 u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds.
549 // Done waiting - write to specified flag byte.
550 u32 segoff = GET_BDA(ptr_user_wait_complete_flag);
551 u16 segment = segoff >> 16;
552 u16 offset = segoff & 0xffff;
553 u8 oldval = GET_FARVAR(segment, *(u8*)(offset+0));
554 SET_FARVAR(segment, *(u8*)(offset+0), oldval | 0x80);
560 SET_BDA(user_wait_timeout, time);