Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:40

0001 /*
0002  * This file contains the PC386 timer package.
0003  *
0004  * Rosimildo daSilva -ConnectTel, Inc - Fixed infinite loop in the Calibration
0005  * routine. I've seen this problems with faster machines ( pentiums ). Sometimes
0006  * RTEMS just hangs at startup.
0007  *
0008  * Joel 9 May 2010: This is now seen sometimes on qemu.
0009  *
0010  *  Modifications by:
0011  *  (C) Copyright 1997 -
0012  *    NavIST Group - Real-Time Distributed Systems and Industrial Automation
0013  *    http://pandora.ist.utl.pt
0014  *    Instituto Superior Tecnico * Lisboa * PORTUGAL
0015  *
0016  *  This file is provided "AS IS" without warranty of any kind, either
0017  *  expressed or implied.
0018  *
0019  *  Based upon code by
0020  *  COPYRIGHT (c) 1989-1999.
0021  *  On-Line Applications Research Corporation (OAR).
0022  *
0023  *  The license and distribution terms for this file may be
0024  *  found in the file LICENSE in this distribution or at
0025  *  http://www.rtems.org/license/LICENSE.
0026  */
0027 
0028 #include <stdlib.h>
0029 #include <bsp.h>
0030 #include <rtems/btimer.h>
0031 #include <bsp/irq-generic.h>
0032 #include <libcpu/cpuModel.h>
0033 
0034 /*
0035  * Constants
0036  */
0037 #define AVG_OVERHEAD  0         /* 0.1 microseconds to start/stop timer. */
0038 #define LEAST_VALID   1         /* Don't trust a value lower than this.  */
0039 #define SLOW_DOWN_IO  0x80      /* io which does nothing */
0040 
0041 #define TWO_MS (uint32_t)(2000) /* TWO_MS = 2000us (sic!) */
0042 
0043 #define MSK_NULL_COUNT 0x40     /* bit counter available for reading */
0044 
0045 #define CMD_READ_BACK_STATUS 0xE2   /* command read back status */
0046 
0047 RTEMS_INTERRUPT_LOCK_DEFINE( /* visible global variable */ ,
0048    rtems_i386_i8254_access_lock, "rtems_i386_i8254_access_lock" );
0049 
0050 /*
0051  * Global Variables
0052  */
0053 volatile uint32_t         Ttimer_val;
0054 bool                      benchmark_timer_find_average_overhead = true;
0055 volatile unsigned int     fastLoop1ms, slowLoop1ms;
0056 
0057 void              (*benchmark_timer_initialize_function)(void) = 0;
0058 benchmark_timer_t (*benchmark_timer_read_function)(void) = 0;
0059 void              (*Timer_exit_function)(void) = 0;
0060 
0061 /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
0062 extern void timerisr(void);
0063 
0064 void Timer_exit(void);
0065 
0066 /*
0067  * Pentium optimized timer handling.
0068  */
0069 
0070 /*
0071  *  Timer cleanup routine at RTEMS exit.
0072  *
0073  *  NOTE: This routine is not really necessary, since there will be
0074  *        a reset at exit.
0075  */
0076 static void tsc_timer_exit(void)
0077 {
0078 }
0079 
0080 static void tsc_timer_initialize(void)
0081 {
0082   static bool First = true;
0083 
0084   if (First) {
0085     First = false;
0086 
0087     atexit(Timer_exit); /* Try not to hose the system at exit. */
0088   }
0089   Ttimer_val = rdtsc(); /* read starting time */
0090 }
0091 
0092 /*
0093  * Read TSC timer value.
0094  */
0095 static uint32_t tsc_read_timer(void)
0096 {
0097   register uint32_t  total;
0098 
0099   total =  (uint32_t)(rdtsc() - Ttimer_val);
0100 
0101   if (benchmark_timer_find_average_overhead)
0102     return total;
0103 
0104   if (total < LEAST_VALID)
0105     return 0;                 /* below timer resolution */
0106 
0107   return (total - AVG_OVERHEAD);
0108 }
0109 
0110 /*
0111  * Non-Pentium timer handling.
0112  */
0113 #define US_PER_ISR   250  /* Number of micro-seconds per timer interruption */
0114 
0115 /*
0116  * Timer cleanup routine at RTEMS exit. NOTE: This routine is
0117  * not really necessary, since there will be a reset at exit.
0118  */
0119 static void timerOff(const rtems_raw_irq_connect_data* used)
0120 {
0121   rtems_interrupt_lock_context lock_context;
0122   /*
0123    * disable interrrupt at i8259 level
0124    */
0125   bsp_interrupt_vector_disable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
0126 
0127   rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
0128 
0129    /* reset timer mode to standard (DOS) value */
0130   outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
0131   outport_byte(TIMER_CNTR0, 0);
0132   outport_byte(TIMER_CNTR0, 0);
0133 
0134   rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
0135 }
0136 
0137 static void timerOn(const rtems_raw_irq_connect_data* used)
0138 {
0139   rtems_interrupt_lock_context lock_context;
0140 
0141   rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
0142 
0143   /* load timer for US_PER_ISR microsecond period */
0144   outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
0145   outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff);
0146   outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff);
0147 
0148   rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
0149 
0150   /*
0151    * enable interrrupt at i8259 level
0152    */
0153   bsp_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
0154 }
0155 
0156 static rtems_raw_irq_connect_data timer_raw_irq_data = {
0157   BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
0158   timerisr,
0159   timerOn,
0160   timerOff,
0161   NULL
0162 };
0163 
0164 /*
0165  * Timer cleanup routine at RTEMS exit.
0166  *
0167  * NOTE: This routine is not really necessary, since there will be
0168  *       a reset at exit.
0169  */
0170 static void i386_timer_exit(void)
0171 {
0172   i386_delete_idt_entry (&timer_raw_irq_data);
0173 }
0174 
0175 extern void rtems_irq_prologue_0(void);
0176 static void i386_timer_initialize(void)
0177 {
0178   static bool First = true;
0179 
0180   if (First) {
0181     rtems_raw_irq_connect_data raw_irq_data = {
0182       BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
0183       rtems_irq_prologue_0,
0184       NULL,
0185       NULL,
0186       NULL
0187     };
0188 
0189     First = false;
0190     i386_delete_idt_entry (&raw_irq_data);
0191 
0192     atexit(Timer_exit);            /* Try not to hose the system at exit. */
0193     if (!i386_set_idt_entry (&timer_raw_irq_data)) {
0194       printk("raw handler connection failed\n");
0195       rtems_fatal_error_occurred(1);
0196     }
0197   }
0198   /* wait for ISR to be called at least once */
0199   Ttimer_val = 0;
0200   while (Ttimer_val == 0)
0201     continue;
0202   Ttimer_val = 0;
0203 }
0204 
0205 /*
0206  * Read hardware timer value.
0207  */
0208 static uint32_t i386_read_timer(void)
0209 {
0210   register uint32_t         total, clicks;
0211   register uint8_t          lsb, msb;
0212   rtems_interrupt_lock_context lock_context;
0213 
0214   rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
0215   outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
0216   inport_byte(TIMER_CNTR0, lsb);
0217   inport_byte(TIMER_CNTR0, msb);
0218   rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
0219 
0220   clicks = (msb << 8) | lsb;
0221   total  = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks));
0222 
0223   if (benchmark_timer_find_average_overhead)
0224     return total;
0225 
0226   if (total < LEAST_VALID)
0227     return 0;                            /* below timer resolution */
0228 
0229   return (total - AVG_OVERHEAD);
0230 }
0231 
0232 /*
0233  * General timer functions using either TSC-based implementation
0234  * or interrupt-based implementation
0235  */
0236 
0237 void benchmark_timer_initialize(void)
0238 {
0239   static bool First = true;
0240 
0241   if (First) {
0242     if (x86_has_tsc()) {
0243 #if defined(DEBUG)
0244       printk("TSC: timer initialization\n");
0245 #endif /* DEBUG */
0246       benchmark_timer_initialize_function = &tsc_timer_initialize;
0247       benchmark_timer_read_function = &tsc_read_timer;
0248       Timer_exit_function = &tsc_timer_exit;
0249     } else {
0250 #if defined(DEBUG)
0251       printk("ISR: timer initialization\n");
0252 #endif /* DEBUG */
0253       benchmark_timer_initialize_function = &i386_timer_initialize;
0254       benchmark_timer_read_function = &i386_read_timer;
0255       Timer_exit_function = &i386_timer_exit;
0256     }
0257     First = false;
0258   }
0259   (*benchmark_timer_initialize_function)();
0260 }
0261 
0262 uint32_t benchmark_timer_read(void)
0263 {
0264   return (*benchmark_timer_read_function)();
0265 }
0266 
0267 void Timer_exit(void)
0268 {
0269   if ( Timer_exit_function )
0270     return (*Timer_exit_function)();
0271 }
0272 
0273 /*
0274  * Set internal benchmark_timer_find_average_overhead flag value.
0275  */
0276 void benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
0277 {
0278   benchmark_timer_find_average_overhead = find_flag;
0279 }
0280 
0281 static unsigned short lastLoadedValue;
0282 
0283 /*
0284  *  Loads timer 0 with value passed as arguemnt.
0285  *
0286  *  Returns: Nothing. Loaded value must be a number of clock bits...
0287  */
0288 static void loadTimerValue( unsigned short loadedValue )
0289 {
0290   rtems_interrupt_lock_context lock_context;
0291   rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
0292   lastLoadedValue = loadedValue;
0293   outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE);
0294   outport_byte(TIMER_CNTR0, loadedValue & 0xff);
0295   outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
0296   rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
0297 }
0298 
0299 /*
0300  * Reads the current value of the timer, and converts the
0301  *  number of ticks to micro-seconds.
0302  *
0303  * Returns: number of clock bits elapsed since last load.
0304  */
0305 static unsigned int readTimer0(void)
0306 {
0307   unsigned short lsb, msb;
0308   unsigned char  status;
0309   unsigned int  count;
0310   rtems_interrupt_lock_context lock_context;
0311   rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
0312 
0313   outport_byte(
0314     TIMER_MODE,
0315     (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT)))
0316   );
0317   inport_byte(TIMER_CNTR0, status);
0318   inport_byte(TIMER_CNTR0, lsb);
0319   inport_byte(TIMER_CNTR0, msb);
0320 
0321   rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
0322 
0323   count = ( msb << 8 ) | lsb ;
0324   if (status & RB_OUTPUT )
0325     count += lastLoadedValue;
0326 
0327   return (2*lastLoadedValue - count);
0328 }
0329 
0330 static void Timer0Reset(void)
0331 {
0332   loadTimerValue(0xffff);
0333   readTimer0();
0334 }
0335 
0336 static void fastLoop (unsigned int loopCount)
0337 {
0338   unsigned int i;
0339   for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
0340 }
0341 
0342 static void slowLoop (unsigned int loopCount)
0343 {
0344   unsigned int j;
0345   for (j=0; j <100 ;  j++) {
0346     fastLoop (loopCount);
0347   }
0348 }
0349 
0350 /*
0351  * #define DEBUG_CALIBRATE
0352  */
0353 void
0354 Calibrate_loop_1ms(void)
0355 {
0356   unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
0357   unsigned int targetClockBits, currentClockBits;
0358   unsigned int slowLoopGranularity, fastLoopGranularity;
0359   rtems_interrupt_level  level;
0360   int retries = 0;
0361 
0362   /*
0363    * This code is designed to run before interrupt management
0364    * is enabled and running it on multiple CPUs and or after
0365    * secondary CPUs are bring up seems really broken.
0366    * Disabling of local interrupts is enough.
0367    */
0368   rtems_interrupt_local_disable(level);
0369 
0370 retry:
0371   if ( ++retries >= 5 ) {
0372     printk( "Calibrate_loop_1ms: too many attempts. giving up!!\n" );
0373     while (1);
0374   }
0375 #ifdef DEBUG_CALIBRATE
0376   printk("Calibrate_loop_1ms is starting,  please wait (but not too long.)\n");
0377 #endif
0378   targetClockBits = US_TO_TICK(1000);
0379   /*
0380    * Fill up the cache to get a correct offset
0381    */
0382   Timer0Reset();
0383   readTimer0();
0384   /*
0385    * Compute the minimal offset to apply due to read counter register.
0386    */
0387   offset = 0xffffffff;
0388   for (i=0; i <1000; i++) {
0389     Timer0Reset();
0390     offsetTmp = readTimer0();
0391     offset += offsetTmp;
0392   }
0393   offset = offset / 1000; /* compute average */
0394   /*
0395    * calibrate empty call
0396    */
0397   fastLoop (0);
0398   emptyCall = 0;
0399   j = 0;
0400   for (i=0; i <10; i++) {
0401     Timer0Reset();
0402     fastLoop (0);
0403     res =  readTimer0();
0404     /* res may be inferior to offset on fast
0405      * machine because we took an average for offset
0406      */
0407     if (res >  offset) {
0408       ++j;
0409       emptyCallTmp = res - offset;
0410       emptyCall += emptyCallTmp;
0411     }
0412   }
0413   if (j == 0) emptyCall = 0;
0414   else emptyCall = emptyCall / j; /* compute average */
0415   /*
0416    * calibrate fast loop
0417    */
0418   Timer0Reset();
0419   fastLoop (10000);
0420   res = readTimer0() - offset;
0421   if (res < emptyCall) {
0422     printk(
0423       "Problem #1 in offset computation in Calibrate_loop_1ms "
0424         " in file libbsp/i386/pc386/timer/timer.c\n"
0425     );
0426     goto retry;
0427   }
0428   fastLoopGranularity = (res - emptyCall) / 10000;
0429   /*
0430    * calibrate slow loop
0431    */
0432   Timer0Reset();
0433   slowLoop(10);
0434   res = readTimer0();
0435   if (res < offset + emptyCall) {
0436     printk(
0437       "Problem #2 in offset computation in Calibrate_loop_1ms "
0438         " in file libbsp/i386/pc386/timer/timer.c\n"
0439     );
0440     goto retry;
0441   }
0442   slowLoopGranularity = (res - offset - emptyCall)/ 10;
0443 
0444   if (slowLoopGranularity == 0) {
0445     printk(
0446       "Problem #3 in offset computation in Calibrate_loop_1ms "
0447         " in file libbsp/i386/pc386/timer/timer.c\n"
0448     );
0449     goto retry;
0450   }
0451 
0452   targetClockBits += offset;
0453 #ifdef DEBUG_CALIBRATE
0454   printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
0455   offset, emptyCall, targetClockBits);
0456   printk("slowLoopGranularity = %u fastLoopGranularity =  %u\n",
0457   slowLoopGranularity, fastLoopGranularity);
0458 #endif
0459   slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
0460   if (slowLoop1ms != 0) {
0461     fastLoop1ms = targetClockBits % slowLoopGranularity;
0462     if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
0463   }
0464   else
0465     fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
0466 
0467   if (slowLoop1ms != 0) {
0468     /*
0469      * calibrate slow loop
0470      */
0471 
0472     while(1)
0473       {
0474  int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
0475  Timer0Reset();
0476  slowLoop(slowLoop1ms);
0477  currentClockBits = readTimer0();
0478  if (currentClockBits > targetClockBits) {
0479    if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
0480      /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
0481      --slowLoop1ms;
0482      break;
0483    }
0484    else {
0485      --slowLoop1ms;
0486      if (slowLoop1ms == 0) break;
0487      if (previousSign == 0) previousSign = 2;
0488      if (previousSign == 1) break;
0489    }
0490  }
0491  else {
0492    if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
0493       break;
0494     }
0495     else {
0496       ++slowLoop1ms;
0497       if (previousSign == 0) previousSign = 1;
0498       if (previousSign == 2) break;
0499     }
0500   }
0501       }
0502   }
0503   /*
0504    * calibrate fast loop
0505    */
0506 
0507   if (fastLoopGranularity != 0 ) {
0508     while(1) {
0509       int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
0510       Timer0Reset();
0511       if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
0512       fastLoop(fastLoop1ms);
0513       currentClockBits = readTimer0();
0514       if (currentClockBits > targetClockBits) {
0515   if ((currentClockBits - targetClockBits) < fastLoopGranularity)
0516     break;
0517   else {
0518     --fastLoop1ms;
0519     if (previousSign == 0) previousSign = 2;
0520     if (previousSign == 1) break;
0521   }
0522       }
0523       else {
0524   if ((targetClockBits - currentClockBits) < fastLoopGranularity)
0525     break;
0526   else {
0527     ++fastLoop1ms;
0528     if (previousSign == 0) previousSign = 1;
0529     if (previousSign == 2) break;
0530   }
0531       }
0532     }
0533   }
0534 #ifdef DEBUG_CALIBRATE
0535   printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
0536 #endif
0537   rtems_interrupt_local_enable(level);
0538 
0539 }
0540 
0541 /*
0542  *  loop which waits at least timeToWait ms
0543  */
0544 void Wait_X_ms( unsigned int timeToWait)
0545 {
0546   unsigned int j;
0547 
0548   for (j=0; j<timeToWait ; j++) {
0549     if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
0550     fastLoop(fastLoop1ms);
0551   }
0552 }