X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fclock.c;h=fcdc698bb15b8c9a0cad8a950312fa1b3b35e965;hb=c990f27298104d23a0a63fc340385310e598fe00;hp=c95f0a142ebb4b7ac4d8c0e0650b91065ded0048;hpb=372e071ed4b6a66fb371cf13b6f6d14ddd00837a;p=seabios.git diff --git a/src/clock.c b/src/clock.c index c95f0a1..fcdc698 100644 --- a/src/clock.c +++ b/src/clock.c @@ -1,6 +1,6 @@ // 16bit code to handle system clocks. // -// Copyright (C) 2008 Kevin O'Connor +// Copyright (C) 2008-2010 Kevin O'Connor // Copyright (C) 2002 MandrakeSoft S.A. // // This file may be distributed under the terms of the GNU LGPLv3 license. @@ -12,6 +12,7 @@ #include "pic.h" // eoi_pic1 #include "bregs.h" // struct bregs #include "biosvar.h" // GET_GLOBAL +#include "usb-hid.h" // usb_check_event // RTC register flags #define RTC_A_UIP 0x80 @@ -53,13 +54,12 @@ * TSC timer ****************************************************************/ -#define PIT_TICK_RATE 1193182 // Underlying HZ of PIT -#define CALIBRATE_COUNT 0x800 // Approx 1.7ms +#define CALIBRATE_COUNT 0x800 // Approx 1.7ms u32 cpu_khz VAR16VISIBLE; static void -calibrate_tsc() +calibrate_tsc(void) { // Setup "timer2" u8 orig = inb(PORT_PS2_CTRLB); @@ -90,31 +90,41 @@ calibrate_tsc() } static void -tscsleep(u64 diff) +tscdelay(u64 diff) { u64 start = rdtscll(); u64 end = start + diff; - while (rdtscll() <= end) + while (!check_tsc(end)) cpu_relax(); } -void -ndelay(u32 count) +static void +tscsleep(u64 diff) { - u32 khz = GET_GLOBAL(cpu_khz); - tscsleep(count * khz / 1000000); + u64 start = rdtscll(); + u64 end = start + diff; + while (!check_tsc(end)) + yield(); } -void -udelay(u32 count) -{ - u32 khz = GET_GLOBAL(cpu_khz); - tscsleep(count * khz / 1000); + +void ndelay(u32 count) { + tscdelay(count * GET_GLOBAL(cpu_khz) / 1000000); } -void -mdelay(u32 count) -{ - u32 khz = GET_GLOBAL(cpu_khz); - tscsleep(count * khz); +void udelay(u32 count) { + tscdelay(count * GET_GLOBAL(cpu_khz) / 1000); +} +void mdelay(u32 count) { + tscdelay(count * GET_GLOBAL(cpu_khz)); +} + +void nsleep(u32 count) { + tscsleep(count * GET_GLOBAL(cpu_khz) / 1000000); +} +void usleep(u32 count) { + tscsleep(count * GET_GLOBAL(cpu_khz) / 1000); +} +void msleep(u32 count) { + tscsleep(count * GET_GLOBAL(cpu_khz)); } // Return the TSC value that is 'msecs' time in the future. @@ -124,6 +134,12 @@ calc_future_tsc(u32 msecs) u32 khz = GET_GLOBAL(cpu_khz); return rdtscll() + ((u64)khz * msecs); } +u64 +calc_future_tsc_usec(u32 usecs) +{ + u32 khz = GET_GLOBAL(cpu_khz); + return rdtscll() + ((u64)(khz/1000) * usecs); +} /**************************************************************** @@ -131,7 +147,7 @@ calc_future_tsc(u32 msecs) ****************************************************************/ static int -rtc_updating() +rtc_updating(void) { // This function checks to see if the update-in-progress bit // is set in CMOS Status Register A. If not, it returns 0. @@ -139,22 +155,23 @@ rtc_updating() // to 0, and will return 0 if such a transition occurs. A -1 // is returned only after timing out. The maximum period // that this bit should be set is constrained to (1984+244) - // useconds, so we wait for 3 msec max. + // useconds, but we wait for longer just to be sure. if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0) return 0; - u64 end = calc_future_tsc(3); - do { + u64 end = calc_future_tsc(15); + for (;;) { if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0) return 0; - } while (rdtscll() <= end); - - // update-in-progress never transitioned to 0 - return -1; + if (check_tsc(end)) + // update-in-progress never transitioned to 0 + return -1; + yield(); + } } static void -pit_setup() +pit_setup(void) { // timer0: binary count, 16bit count, mode 2 outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE); @@ -164,7 +181,7 @@ pit_setup() } static void -init_rtc() +init_rtc(void) { outb_cmos(0x26, CMOS_STATUS_A); // 32,768Khz src, 976.5625us updates u8 regB = inb_cmos(CMOS_STATUS_B); @@ -180,7 +197,7 @@ bcd2bin(u8 val) } void -timer_setup() +timer_setup(void) { dprintf(3, "init timer\n"); calibrate_tsc(); @@ -189,16 +206,14 @@ timer_setup() init_rtc(); rtc_updating(); u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS)); - u32 ticks = (seconds * 18206507) / 1000000; u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES)); - ticks += (minutes * 10923904) / 10000; u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS)); - ticks += (hours * 65543427) / 1000; + u32 ticks = (hours * 60 + minutes) * 60 + seconds; + ticks = ((u64)ticks * PIT_TICK_RATE) / PIT_TICK_INTERVAL; SET_BDA(timer_counter, ticks); - SET_BDA(timer_rollover, 0); - enable_hwirq(0, entry_08); - enable_hwirq(8, entry_70); + enable_hwirq(0, FUNC16(entry_08)); + enable_hwirq(8, FUNC16(entry_70)); } @@ -206,10 +221,40 @@ timer_setup() * Standard clock functions ****************************************************************/ +#define TICKS_PER_DAY (u32)((u64)60*60*24*PIT_TICK_RATE / PIT_TICK_INTERVAL) + +// Calculate the timer value at 'count' number of full timer ticks in +// the future. +u32 +calc_future_timer_ticks(u32 count) +{ + return (GET_BDA(timer_counter) + count + 1) % TICKS_PER_DAY; +} + +// Return the timer value that is 'msecs' time in the future. +u32 +calc_future_timer(u32 msecs) +{ + if (!msecs) + return GET_BDA(timer_counter); + u32 kticks = DIV_ROUND_UP((u64)msecs * PIT_TICK_RATE, PIT_TICK_INTERVAL); + u32 ticks = DIV_ROUND_UP(kticks, 1000); + return calc_future_timer_ticks(ticks); +} + +// Check if the given timer value has passed. +int +check_timer(u32 end) +{ + return (((GET_BDA(timer_counter) + TICKS_PER_DAY - end) % TICKS_PER_DAY) + < (TICKS_PER_DAY/2)); +} + // get current clock count static void handle_1a00(struct bregs *regs) { + yield(); u32 ticks = GET_BDA(timer_counter); regs->cx = ticks >> 16; regs->dx = ticks; @@ -235,7 +280,7 @@ static void handle_1a02(struct bregs *regs) { if (rtc_updating()) { - set_fail(regs); + set_invalid(regs); return; } @@ -284,7 +329,7 @@ handle_1a04(struct bregs *regs) { regs->ah = 0; if (rtc_updating()) { - set_fail(regs); + set_invalid(regs); return; } regs->cl = inb_cmos(CMOS_RTC_YEAR); @@ -318,7 +363,7 @@ handle_1a05(struct bregs *regs) // My assumption: RegB = (RegB & 01111111b) if (rtc_updating()) { init_rtc(); - set_fail(regs); + set_invalid(regs); return; } outb_cmos(regs->cl, CMOS_RTC_YEAR); @@ -352,7 +397,7 @@ handle_1a06(struct bregs *regs) regs->ax = 0; if (val8 & RTC_B_AIE) { // Alarm interrupt enabled already - set_fail(regs); + set_invalid(regs); return; } if (rtc_updating()) { @@ -393,7 +438,7 @@ handle_1a07(struct bregs *regs) static void handle_1aXX(struct bregs *regs) { - set_fail(regs); + set_unimplemented(regs); } // INT 1Ah Time-of-day Service Entry Point @@ -417,7 +462,7 @@ handle_1a(struct bregs *regs) // INT 08h System Timer ISR Entry Point void VISIBLE16 -handle_08() +handle_08(void) { debug_isr(DEBUG_ISR_08); @@ -426,7 +471,7 @@ handle_08() u32 counter = GET_BDA(timer_counter); counter++; // compare to one days worth of timer ticks at 18.2 hz - if (counter >= 0x001800B0) { + if (counter >= TICKS_PER_DAY) { // there has been a midnight rollover at this point counter = 0; SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1); @@ -434,6 +479,8 @@ handle_08() SET_BDA(timer_counter, counter); + usb_check_event(); + // chain to user timer tick INT #0x1c u32 eax=0, flags; call16_simpint(0x1c, &eax, &flags); @@ -446,6 +493,32 @@ handle_08() * Periodic timer ****************************************************************/ +void +useRTC(void) +{ + u16 ebda_seg = get_ebda_seg(); + int count = GET_EBDA2(ebda_seg, RTCusers); + SET_EBDA2(ebda_seg, RTCusers, count+1); + if (count) + return; + // Turn on the Periodic Interrupt timer + u8 bRegister = inb_cmos(CMOS_STATUS_B); + outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B); +} + +void +releaseRTC(void) +{ + u16 ebda_seg = get_ebda_seg(); + int count = GET_EBDA2(ebda_seg, RTCusers); + SET_EBDA2(ebda_seg, RTCusers, count-1); + if (count != 1) + return; + // Clear the Periodic Interrupt. + u8 bRegister = inb_cmos(CMOS_STATUS_B); + outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B); +} + static int set_usertimer(u32 usecs, u16 seg, u16 offset) { @@ -454,24 +527,20 @@ set_usertimer(u32 usecs, u16 seg, u16 offset) // Interval not already set. SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte. - SET_BDA(ptr_user_wait_complete_flag, (seg << 16) | offset); + SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset)); SET_BDA(user_wait_timeout, usecs); - - // Turn on the Periodic Interrupt timer - u8 bRegister = inb_cmos(CMOS_STATUS_B); - outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B); - + useRTC(); return 0; } static void -clear_usertimer() +clear_usertimer(void) { + if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)) + return; // Turn off status byte. SET_BDA(rtc_wait_flag, 0); - // Clear the Periodic Interrupt. - u8 bRegister = inb_cmos(CMOS_STATUS_B); - outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B); + releaseRTC(); } #define RET_ECLOCKINUSE 0x83 @@ -485,13 +554,11 @@ handle_1586(struct bregs *regs) u32 count = (regs->cx << 16) | regs->dx; int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag); if (ret) { - set_code_fail(regs, RET_ECLOCKINUSE); + set_code_invalid(regs, RET_ECLOCKINUSE); return; } - irq_enable(); while (!statusflag) - cpu_relax(); - irq_disable(); + wait_irq(); set_success(regs); } @@ -502,7 +569,7 @@ handle_158300(struct bregs *regs) int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx); if (ret) // Interval already set. - set_code_fail(regs, RET_EUNSUPPORTED); + set_code_invalid(regs, RET_EUNSUPPORTED); else set_success(regs); } @@ -518,7 +585,7 @@ handle_158301(struct bregs *regs) static void handle_1583XX(struct bregs *regs) { - set_code_fail(regs, RET_EUNSUPPORTED); + set_code_unimplemented(regs, RET_EUNSUPPORTED); regs->al--; } @@ -532,9 +599,11 @@ handle_1583(struct bregs *regs) } } +#define USEC_PER_RTC DIV_ROUND_CLOSEST(1000000, 1024) + // int70h: IRQ8 - CMOS RTC void VISIBLE16 -handle_70() +handle_70(void) { debug_isr(DEBUG_ISR_70); @@ -554,23 +623,25 @@ handle_70() // Handle Periodic Interrupt. + check_preempt(); + if (!GET_BDA(rtc_wait_flag)) goto done; // Wait Interval (Int 15, AH=83) active. u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds. - if (time < 0x3D1) { + if (time < USEC_PER_RTC) { // Done waiting - write to specified flag byte. - u32 segoff = GET_BDA(ptr_user_wait_complete_flag); - u16 segment = segoff >> 16; - u16 offset = segoff & 0xffff; - u8 oldval = GET_FARVAR(segment, *(u8*)(offset+0)); - SET_FARVAR(segment, *(u8*)(offset+0), oldval | 0x80); + struct segoff_s segoff = GET_BDA(user_wait_complete_flag); + u16 ptr_seg = segoff.seg; + u8 *ptr_far = (u8*)(segoff.offset+0); + u8 oldval = GET_FARVAR(ptr_seg, *ptr_far); + SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80); clear_usertimer(); } else { // Continue waiting. - time -= 977; + time -= USEC_PER_RTC; SET_BDA(user_wait_timeout, time); }