summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/i386/pc386/timer
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2000-12-05 16:49:23 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2000-12-05 16:49:23 +0000
commitd57c04e1f3a2c704d3479f85de706ad7de65b8b0 (patch)
treec950d8370cad1952893da1a3331be486a4c9d308 /c/src/lib/libbsp/i386/pc386/timer
parent2000-12-05 Joel Sherrill <joel@OARcorp.com> (diff)
downloadrtems-d57c04e1f3a2c704d3479f85de706ad7de65b8b0.tar.bz2
2000-12-05 Eric Valette <valette@crf.canon.fr>
* console/inch.c, console/keyboard.c, console/pc_keyb.c, console/vt.c, include/bsp.h: Correct incorrect interrupt level handling in new keyboard management code. Correct BSP_poll_char initialization routine. * start/start.S, startup/bspstart.c: Correct when the video is initialized. * timer/timer.c (Calibrate_1ms_loop): Address problem where this did not work correctly on all PC speeds. The new calibrate routine has been tested on Pentium 166, pentium II 200, pentium III 300 Mhz and does work as expected.
Diffstat (limited to 'c/src/lib/libbsp/i386/pc386/timer')
-rw-r--r--c/src/lib/libbsp/i386/pc386/timer/timer.c298
1 files changed, 198 insertions, 100 deletions
diff --git a/c/src/lib/libbsp/i386/pc386/timer/timer.c b/c/src/lib/libbsp/i386/pc386/timer/timer.c
index 1095061bea..8d5a36055e 100644
--- a/c/src/lib/libbsp/i386/pc386/timer/timer.c
+++ b/c/src/lib/libbsp/i386/pc386/timer/timer.c
@@ -60,8 +60,8 @@
| Global Variables
+--------------------------------------------------------------------------*/
volatile rtems_unsigned32 Ttimer_val;
- rtems_boolean Timer_driver_Find_average_overhead = TRUE;
- unsigned int loop1ms;
+rtems_boolean Timer_driver_Find_average_overhead = TRUE;
+volatile unsigned int fastLoop1ms, slowLoop1ms;
/*-------------------------------------------------------------------------+
| External Prototypes
@@ -305,137 +305,233 @@ Set_find_average_overhead(rtems_boolean find_flag)
Timer_driver_Find_average_overhead = find_flag;
} /* Set_find_average_overhead */
-
+static unsigned short lastLoadedValue;
/*-------------------------------------------------------------------------+
| Description: Loads timer 0 with value passed as arguemnt.
-| Returns: Nothing.
+| Returns: Nothing. Loaded value must be a number of clock bits...
+--------------------------------------------------------------------------*/
-inline void loadTimerValue( unsigned short loadedValue )
+void loadTimerValue( unsigned short loadedValue )
{
- outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
- outport_byte(TIMER_CNTR0, loadedValue >> 0 & 0xff);
- outport_byte(TIMER_CNTR0, loadedValue >> 8 & 0xff);
-}
-
-
-/*-------------------------------------------------------------------------+
-| Description: Waits until the counter on timer 0 reaches 0.
-| Returns: Nothing.
-+--------------------------------------------------------------------------*/
-inline void waitTimerStatus( void )
-{
- unsigned char status;
- outport_byte(TIMER_MODE, CMD_READ_BACK_STATUS); /* read Status counter 0 */
- inport_byte(TIMER_CNTR0, status);
- while (status & MSK_NULL_COUNT){ /* wait for counter ready */
- outport_byte(TIMER_MODE, CMD_READ_BACK_STATUS);
- inport_byte(TIMER_CNTR0, status);
- }
+ lastLoadedValue = loadedValue;
+ outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE);
+ outport_byte(TIMER_CNTR0, loadedValue & 0xff);
+ outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
}
/*-------------------------------------------------------------------------+
| Description: Reads the current value of the timer, and converts the
| number of ticks to micro-seconds.
-| Returns: current number of microseconds since last value loaded..
+| Returns: number of clock bits elapsed since last load.
+--------------------------------------------------------------------------*/
-inline unsigned short readCurrentTimer()
+unsigned int readTimer0()
{
unsigned short lsb, msb;
- outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
+ unsigned char status;
+ unsigned int count;
+
+ outport_byte(TIMER_MODE, (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT))));
+ inport_byte(TIMER_CNTR0, status);
inport_byte(TIMER_CNTR0, lsb);
inport_byte(TIMER_CNTR0, msb);
- return TICK_TO_US( ( msb << 8 ) | lsb );
+ count = ( msb << 8 ) | lsb ;
+ if (status & RB_OUTPUT )
+ count += lastLoadedValue;
+
+ return (2*lastLoadedValue - count);
}
+void Timer0Reset()
+{
+ loadTimerValue(0xffff);
+ readTimer0();
+}
-/*-------------------------------------------------------------------------+
- * clockbits - Read low order bits of timer 0 (the TOD clock)
- * This works only for the 8254 chips used in ATs and 386s.
- *
- * The timer runs in mode 3 (square wave mode), counting down
- * by twos, twice for each cycle. So it is necessary to read back the
- * OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
- * pin forms the most significant bit of the count. Unfortunately,
- * the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
- *
- * The PC's clock design is soooo brain damaged...
- *
- * Rosimildo - I've got this routine from the KA9Q32 distribution and
- * have updated it for the RTEMS environment.
- +--------------------------------------------------------------------------*/
-unsigned int clockbits(void)
+void fastLoop (unsigned int loopCount)
{
- unsigned int stat,count1, count;
-
- do
- {
- outport_byte( 0x43, 0xc2 ); /* latch timer 0 count and status for reading */
- inport_byte( 0x40, stat ); /* get status of timer 0 */
- inport_byte( 0x40, count1 ); /* lsb of count */
- inport_byte( 0x40, count ); /* msb of count */
- count = count1 | ( count << 8 );
- } while(stat & 0x40); /* reread if NULL COUNT bit set */
- stat = (stat & 0x80) << 8; /* Shift OUTPUT to msb of 16-bit word */
- if(count == 0)
- return stat ^ 0x8000; /* return complement of OUTPUT bit */
- else
- return count | stat; /* Combine OUTPUT with counter */
+ unsigned int i;
+ for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
}
+void slowLoop (unsigned int loopCount)
+{
+ unsigned int j;
+ for (j=0; j <100 ; j++) {
+ fastLoop (loopCount);
+ }
+}
-/*-------------------------------------------------------------------------+
-| Function: Calibrate_loop_1ms
-| Description: Set loop variable to calibrate a 1ms loop
-| Global Variables: loop1ms
-| Arguments: none
-| Returns: Nothing.
-+--------------------------------------------------------------------------*/
+/*
+ * #define DEBUG_CALIBRATE
+ */
void
Calibrate_loop_1ms(void)
{
- unsigned int i;
- unsigned short loadedValue, offset;
- unsigned int timerValue, t1_ref, t2_ref=0;
+ unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
+ unsigned int targetClockBits, currentClockBits;
+ unsigned int slowLoopGranularity, fastLoopGranularity;
rtems_interrupt_level level;
+#ifdef DEBUG_CALIBRATE
+ printk( "Calibrate_loop_1ms is starting, please wait ( but not too loooong. )\n" );
+#endif
+ targetClockBits = US_TO_TICK(1000);
+
+ rtems_interrupt_disable(level);
+ /*
+ * Fill up the cache to get a correct offset
+ */
+ Timer0Reset();
+ readTimer0();
+ /*
+ * Compute the minimal offset to apply due to read counter register.
+ */
+ offset = 0xffffffff;
+ for (i=0; i <1000; i++) {
+ Timer0Reset();
+ offsetTmp = readTimer0();
+ offset += offsetTmp;
+ }
+ offset = offset / 1000; /* compute average */
+ /*
+ * calibrate empty call
+ */
+ fastLoop (0);
+ emptyCall = 0;
+ j = 0;
+ for (i=0; i <10; i++) {
+ Timer0Reset();
+ fastLoop (0);
+ res = readTimer0();
+ /* res may be inferior to offset on fast
+ * machine because we took an average for offset
+ */
+ if (res > offset) {
+ ++j;
+ emptyCallTmp = res - offset;
+ emptyCall += emptyCallTmp;
+ }
+ }
+ if (j == 0) emptyCall = 0;
+ else emptyCall = emptyCall / j; /* compute average */
+ /*
+ * calibrate fast loop
+ */
+ Timer0Reset();
+ fastLoop (10000);
+ res = readTimer0() - offset;
+ if (res < emptyCall) {
+ printk("Problem #1 in offset computation in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
+ while (1);
+ }
+ fastLoopGranularity = (res - emptyCall) / 10000;
+ /*
+ * calibrate slow loop
+ */
+ Timer0Reset();
+ slowLoop(10);
+ res = readTimer0();
+ if (res < offset + emptyCall) {
+ printk("Problem #2 in offset computation in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
+ while (1);
+ }
+ slowLoopGranularity = (res - offset - emptyCall)/ 10;
+
+ if (slowLoopGranularity == 0) {
+ printk("Problem #3 in Calibrate_loop_1ms in file libbsp/i386/pc386/timer/timer.c\n");
+ while (1);
+ }
- printk( "Calibrate_loop_1ms is starting, please wait ( but not too loooong. )\n" );
-
- loop1ms = 200;
- timerValue = 0;
+ targetClockBits += offset;
+#ifdef DEBUG_CALIBRATE
+ printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
+ offset, emptyCall, targetClockBits);
+ printk("slowLoopGranularity = %u fastLoopGranularity = %u\n",
+ slowLoopGranularity, fastLoopGranularity);
+#endif
+ slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
+ if (slowLoop1ms != 0) {
+ fastLoop1ms = targetClockBits % slowLoopGranularity;
+ if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
+ }
+ else
+ fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
- /* Let's load the timer with 2ms, initially */
- loadedValue = US_TO_TICK( 2000 );
+ if (slowLoop1ms != 0) {
+ /*
+ * calibrate slow loop
+ */
- rtems_interrupt_disable(level);
+ while(1)
+ {
+ int previousSign = 0; /* 0 = unset, 1 = incrementing, 2 = decrementing */
+ Timer0Reset();
+ slowLoop(slowLoop1ms);
+ currentClockBits = readTimer0();
+ if (currentClockBits > targetClockBits) {
+ if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
+ /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
+ --slowLoop1ms;
+ break;
+ }
+ else {
+ --slowLoop1ms;
+ if (slowLoop1ms == 0) break;
+ if (previousSign == 0) previousSign = 2;
+ if (previousSign == 1) break;
+ }
+ }
+ else {
+ if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
+ break;
+ }
+ else {
+ ++slowLoop1ms;
+ if (previousSign == 0) previousSign = 1;
+ if (previousSign == 2) break;
+ }
+ }
+ }
+ }
/*
- * Compute the offset to apply due to read counter register
+ * calibrate fast loop
*/
- offset = 0;
- loadTimerValue( loadedValue + offset );
- waitTimerStatus();
- t1_ref = clockbits();
- offset = loadedValue - readCurrentTimer();
- while( timerValue < 1000 )
- {
- loop1ms++;
- loadTimerValue( loadedValue + offset );
- waitTimerStatus();
- t1_ref = clockbits();
- for( i=0; i < loop1ms; i++ )
- outport_byte( SLOW_DOWN_IO, 0 ); /* write is # 1us */
- t2_ref = clockbits();
- timerValue = TICK_TO_US( t1_ref - t2_ref ); /* timer0 is decrementing number of ticks */
+
+ if (fastLoopGranularity != 0 ) {
+ while(1) {
+ int previousSign = 0; /* 0 = unset, 1 = incrementing, 2 = decrementing */
+ Timer0Reset();
+ if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
+ fastLoop(fastLoop1ms);
+ currentClockBits = readTimer0();
+ if (currentClockBits > targetClockBits) {
+ if ((currentClockBits - targetClockBits) < fastLoopGranularity)
+ break;
+ else {
+ --fastLoop1ms;
+ if (previousSign == 0) previousSign = 2;
+ if (previousSign == 1) break;
+ }
+ }
+ else {
+ if ((targetClockBits - currentClockBits) < fastLoopGranularity)
+ break;
+ else {
+ ++fastLoop1ms;
+ if (previousSign == 0) previousSign = 1;
+ if (previousSign == 2) break;
+ }
+ }
+ }
}
- printk( "Calibrate_loop_1ms timerValue=%d, loop1ms=%d, t1_ref=%x, clockbits=%x, delta=%d\n",
- timerValue, loop1ms, t1_ref, t2_ref, t1_ref - t2_ref );
+#ifdef DEBUG_CALIBRATE
+ printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
+#endif
rtems_interrupt_enable(level);
+
}
-
-
/*-------------------------------------------------------------------------+
| Function: Wait_X_1ms
| Description: loop which waits at least timeToWait ms
@@ -446,11 +542,13 @@ Calibrate_loop_1ms(void)
void
Wait_X_ms( unsigned int timeToWait){
- unsigned int i, j;
+ unsigned int j;
+
+ for (j=0; j<timeToWait ; j++) {
+ if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
+ fastLoop(fastLoop1ms);
+ }
- for (j=0; j<timeToWait ; j++)
- for (i=0; i<loop1ms; i++)
- outport_byte(SLOW_DOWN_IO, 0); /* write is # 1us */
}