source: rtems/c/src/lib/libbsp/i386/pc386/timer/timer.c @ 4977f07e

4.115
Last change on this file since 4977f07e was 4977f07e, checked in by Joel Sherrill <joel.sherrill@…>, on 10/09/14 at 17:56:18

i386/pc386: Eliminate multiple warnings

  • Property mode set to 100644
File size: 12.7 KB
RevLine 
[171f834]1/*
2 * This file contains the PC386 timer package.
3 *
4 * Rosimildo daSilva -ConnectTel, Inc - Fixed infinite loop in the Calibration
5 * routine. I've seen this problems with faster machines ( pentiums ). Sometimes
6 * RTEMS just hangs at startup.
7 *
8 * Joel 9 May 2010: This is now seen sometimes on qemu.
9 *
10 *  Modifications by:
11 *  (C) Copyright 1997 -
12 *    NavIST Group - Real-Time Distributed Systems and Industrial Automation
13 *    http://pandora.ist.utl.pt
14 *    Instituto Superior Tecnico * Lisboa * PORTUGAL
15 *
16 *  This file is provided "AS IS" without warranty of any kind, either
17 *  expressed or implied.
18 *
19 *  Based upon code by
20 *  COPYRIGHT (c) 1989-1999.
21 *  On-Line Applications Research Corporation (OAR).
22 *
23 *  The license and distribution terms for this file may be
[ba64905]24 *  found in the file LICENSE in this distribution or at
[c499856]25 *  http://www.rtems.org/license/LICENSE.
[171f834]26 */
[7150f00f]27
28#include <stdlib.h>
29#include <bsp.h>
[d085040]30#include <rtems/btimer.h>
[6daada6]31#include <bsp/irq.h>
[959f887a]32#include <libcpu/cpuModel.h>
[7150f00f]33
[171f834]34/*
35 * Constants
36 */
37#define AVG_OVERHEAD  0         /* 0.1 microseconds to start/stop timer. */
38#define LEAST_VALID   1         /* Don't trust a value lower than this.  */
39#define SLOW_DOWN_IO  0x80      /* io which does nothing */
[7150f00f]40
[171f834]41#define TWO_MS (uint32_t)(2000) /* TWO_MS = 2000us (sic!) */
[eff217e]42
[171f834]43#define MSK_NULL_COUNT 0x40     /* bit counter available for reading */
[eff217e]44
45#define CMD_READ_BACK_STATUS 0xE2   /* command read back status */
[171f834]46
47/*
48 * Global Variables
49 */
[6fda59f]50volatile uint32_t         Ttimer_val;
[171f834]51bool                      benchmark_timer_find_average_overhead = true;
[d57c04e]52volatile unsigned int     fastLoop1ms, slowLoop1ms;
[959f887a]53
[8fbe2e6]54void              (*benchmark_timer_initialize_function)(void) = 0;
55benchmark_timer_t (*benchmark_timer_read_function)(void) = 0;
56void              (*Timer_exit_function)(void) = 0;
[7150f00f]57
[171f834]58/* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
[2d7d605]59extern void timerisr(void);
[171f834]60
61void Timer_exit(void);
[1131dfe]62
63/*
[171f834]64 * Pentium optimized timer handling.
[1131dfe]65 */
66
[171f834]67/*
[4977f07e]68 *  Timer cleanup routine at RTEMS exit.
69 *
70 *  NOTE: This routine is not really necessary, since there will be
71 *        a reset at exit.
[171f834]72 */
[4977f07e]73static void tsc_timer_exit(void)
[7150f00f]74{
[171f834]75}
76
[4977f07e]77static void tsc_timer_initialize(void)
[7150f00f]78{
[78b05d3]79  static bool First = true;
[7150f00f]80
[171f834]81  if (First) {
[78b05d3]82    First = false;
[7150f00f]83
84    atexit(Timer_exit); /* Try not to hose the system at exit. */
85  }
86  Ttimer_val = rdtsc(); /* read starting time */
[171f834]87}
88
89/*
[4977f07e]90 * Read TSC timer value.
[171f834]91 */
[4977f07e]92static uint32_t tsc_read_timer(void)
[7150f00f]93{
[171f834]94  register uint32_t  total;
[7150f00f]95
[6fda59f]96  total =  (uint32_t)(rdtsc() - Ttimer_val);
[7150f00f]97
[25c62b0]98  if (benchmark_timer_find_average_overhead)
[7150f00f]99    return total;
100
[171f834]101  if (total < LEAST_VALID)
102    return 0;                 /* below timer resolution */
103
104  return (total - AVG_OVERHEAD);
105}
106
107/*
108 * Non-Pentium timer handling.
109 */
[7150f00f]110#define US_PER_ISR   250  /* Number of micro-seconds per timer interruption */
111
[171f834]112/*
113 * Timer cleanup routine at RTEMS exit. NOTE: This routine is
114 * not really necessary, since there will be a reset at exit.
115 */
116static void timerOff(const rtems_raw_irq_connect_data* used)
[67a2288]117{
[171f834]118  /*
119   * disable interrrupt at i8259 level
120   */
121   BSP_irq_disable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
122   /* reset timer mode to standard (DOS) value */
123   outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
124   outport_byte(TIMER_CNTR0, 0);
125   outport_byte(TIMER_CNTR0, 0);
126}
127
128static void timerOn(const rtems_raw_irq_connect_data* used)
[67a2288]129{
[171f834]130  /* load timer for US_PER_ISR microsecond period */
131  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
132  outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff);
133  outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff);
134
135  /*
136   * enable interrrupt at i8259 level
137   */
138  BSP_irq_enable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
[67a2288]139}
140
[171f834]141static int timerIsOn(const rtems_raw_irq_connect_data *used)
[2d7d605]142{
[171f834]143  return BSP_irq_enabled_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
[68f4e5f]144}
[2d7d605]145
[67a2288]146static rtems_raw_irq_connect_data timer_raw_irq_data = {
[0ebbf66]147  BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
[67a2288]148  timerisr,
149  timerOn,
150  timerOff,
[2d7d605]151  timerIsOn
[67a2288]152};
[7150f00f]153
[171f834]154/*
[4977f07e]155 * Timer cleanup routine at RTEMS exit.
156 *
157 * NOTE: This routine is not really necessary, since there will be
158 *       a reset at exit.
159 */
160static void i386_timer_exit(void)
[7150f00f]161{
[67a2288]162  i386_delete_idt_entry (&timer_raw_irq_data);
[171f834]163}
164
165extern void rtems_irq_prologue_0(void);
[4977f07e]166static void i386_timer_initialize(void)
[7150f00f]167{
[0bbd74de]168  static bool First = true;
[7150f00f]169
[171f834]170  if (First) {
171    rtems_raw_irq_connect_data raw_irq_data = {
172      BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
173      rtems_irq_prologue_0,
174      NULL,
175      NULL,
176      NULL
177    };
178
[0bbd74de]179    First = false;
[171f834]180    i386_delete_idt_entry (&raw_irq_data);
[7150f00f]181
[171f834]182    atexit(Timer_exit);            /* Try not to hose the system at exit. */
[67a2288]183    if (!i386_set_idt_entry (&timer_raw_irq_data)) {
[171f834]184      printk("raw handler connection failed\n");
[67a2288]185      rtems_fatal_error_occurred(1);
186    }
[7150f00f]187  }
188  /* wait for ISR to be called at least once */
189  Ttimer_val = 0;
190  while (Ttimer_val == 0)
191    continue;
192  Ttimer_val = 0;
[171f834]193}
194
195/*
196 * Read hardware timer value.
197 */
[4977f07e]198static uint32_t i386_read_timer(void)
[7150f00f]199{
[6fda59f]200  register uint32_t         total, clicks;
201  register uint8_t          lsb, msb;
[7150f00f]202
203  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
204  inport_byte(TIMER_CNTR0, lsb);
205  inport_byte(TIMER_CNTR0, msb);
206  clicks = (msb << 8) | lsb;
207  total  = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks));
208
[25c62b0]209  if (benchmark_timer_find_average_overhead)
[7150f00f]210    return total;
[171f834]211
212  if (total < LEAST_VALID)
213    return 0;                            /* below timer resolution */
214
215  return (total - AVG_OVERHEAD);
[7150f00f]216}
217
[1131dfe]218/*
219 * General timer functions using either TSC-based implementation
220 * or interrupt-based implementation
221 */
222
[171f834]223void benchmark_timer_initialize(void)
[1131dfe]224{
[171f834]225  static bool First = true;
[1131dfe]226
[171f834]227  if (First) {
228    if (x86_has_tsc()) {
[1131dfe]229#if defined(DEBUG)
[171f834]230      printk("TSC: timer initialization\n");
[3e21ecf]231#endif /* DEBUG */
[171f834]232      benchmark_timer_initialize_function = &tsc_timer_initialize;
233      benchmark_timer_read_function = &tsc_read_timer;
234      Timer_exit_function = &tsc_timer_exit;
235    } else {
[1131dfe]236#if defined(DEBUG)
[171f834]237      printk("ISR: timer initialization\n");
[3e21ecf]238#endif /* DEBUG */
[171f834]239      benchmark_timer_initialize_function = &i386_timer_initialize;
240      benchmark_timer_read_function = &i386_read_timer;
241      Timer_exit_function = &i386_timer_exit;
[1131dfe]242    }
[171f834]243    First = false;
244  }
245  (*benchmark_timer_initialize_function)();
[1131dfe]246}
247
[171f834]248uint32_t benchmark_timer_read(void)
[1131dfe]249{
[171f834]250  return (*benchmark_timer_read_function)();
[1131dfe]251}
252
[171f834]253void Timer_exit(void)
[1131dfe]254{
[59211c6]255  if ( Timer_exit_function )
256    return (*Timer_exit_function)();
[1131dfe]257}
[7150f00f]258
[171f834]259/*
260 * Set internal benchmark_timer_find_average_overhead flag value.
261 */
262void benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
[7150f00f]263{
[25c62b0]264  benchmark_timer_find_average_overhead = find_flag;
[171f834]265}
[eff217e]266
[d57c04e]267static unsigned short lastLoadedValue;
[23291e99]268
[171f834]269/*
270 *  Loads timer 0 with value passed as arguemnt.
271 *
272 *  Returns: Nothing. Loaded value must be a number of clock bits...
273 */
[4977f07e]274static void loadTimerValue( unsigned short loadedValue )
[23291e99]275{
[d57c04e]276  lastLoadedValue = loadedValue;
277  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE);
278  outport_byte(TIMER_CNTR0, loadedValue & 0xff);
279  outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
[23291e99]280}
281
[171f834]282/*
283 * Reads the current value of the timer, and converts the
284 *  number of ticks to micro-seconds.
285 *
286 * Returns: number of clock bits elapsed since last load.
287 */
[4977f07e]288static unsigned int readTimer0(void)
[23291e99]289{
290  unsigned short lsb, msb;
[d57c04e]291  unsigned char  status;
[171f834]292  unsigned int  count;
[d57c04e]293
[171f834]294  outport_byte(
295    TIMER_MODE,
296    (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT)))
297  );
[d57c04e]298  inport_byte(TIMER_CNTR0, status);
[23291e99]299  inport_byte(TIMER_CNTR0, lsb);
300  inport_byte(TIMER_CNTR0, msb);
[d57c04e]301  count = ( msb << 8 ) | lsb ;
302  if (status & RB_OUTPUT )
[171f834]303    count += lastLoadedValue;
[6128a4a]304
[d57c04e]305  return (2*lastLoadedValue - count);
[23291e99]306}
307
[4977f07e]308static void Timer0Reset(void)
[d57c04e]309{
310  loadTimerValue(0xffff);
311  readTimer0();
312}
[23291e99]313
[4977f07e]314static void fastLoop (unsigned int loopCount)
[23291e99]315{
[d57c04e]316  unsigned int i;
317  for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
[23291e99]318}
319
[4977f07e]320static void slowLoop (unsigned int loopCount)
[d57c04e]321{
322  unsigned int j;
323  for (j=0; j <100 ;  j++) {
324    fastLoop (loopCount);
325  }
326}
[23291e99]327
[d57c04e]328/*
329 * #define DEBUG_CALIBRATE
330 */
[eff217e]331void
[23291e99]332Calibrate_loop_1ms(void)
333{
[d57c04e]334  unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
335  unsigned int targetClockBits, currentClockBits;
336  unsigned int slowLoopGranularity, fastLoopGranularity;
[eff217e]337  rtems_interrupt_level  level;
[13e4ab64]338  int retries = 0;
[6128a4a]339
[13e4ab64]340  rtems_interrupt_disable(level);
341
342retry:
343  if ( ++retries >= 5 ) {
344    printk( "Calibrate_loop_1ms: too many attempts. giving up!!\n" );
345    while (1);
346  }
[d57c04e]347#ifdef DEBUG_CALIBRATE
[171f834]348  printk("Calibrate_loop_1ms is starting,  please wait (but not too long.)\n");
[6128a4a]349#endif
[d57c04e]350  targetClockBits = US_TO_TICK(1000);
351  /*
352   * Fill up the cache to get a correct offset
353   */
354  Timer0Reset();
355  readTimer0();
[6128a4a]356  /*
[d57c04e]357   * Compute the minimal offset to apply due to read counter register.
358   */
359  offset = 0xffffffff;
360  for (i=0; i <1000; i++) {
361    Timer0Reset();
362    offsetTmp = readTimer0();
363    offset += offsetTmp;
364  }
365  offset = offset / 1000; /* compute average */
366  /*
367   * calibrate empty call
368   */
369  fastLoop (0);
370  emptyCall = 0;
371  j = 0;
372  for (i=0; i <10; i++) {
373    Timer0Reset();
374    fastLoop (0);
375    res =  readTimer0();
376    /* res may be inferior to offset on fast
377     * machine because we took an average for offset
378     */
379    if (res >  offset) {
380      ++j;
381      emptyCallTmp = res - offset;
382      emptyCall += emptyCallTmp;
383    }
384  }
385  if (j == 0) emptyCall = 0;
386  else emptyCall = emptyCall / j; /* compute average */
387  /*
388   * calibrate fast loop
389   */
390  Timer0Reset();
391  fastLoop (10000);
392  res = readTimer0() - offset;
393  if (res < emptyCall) {
[13e4ab64]394    printk(
395      "Problem #1 in offset computation in Calibrate_loop_1ms "
396        " in file libbsp/i386/pc386/timer/timer.c\n"
397    );
398    goto retry;
[d57c04e]399  }
400  fastLoopGranularity = (res - emptyCall) / 10000;
401  /*
402   * calibrate slow loop
403   */
404  Timer0Reset();
405  slowLoop(10);
406  res = readTimer0();
407  if (res < offset + emptyCall) {
[13e4ab64]408    printk(
409      "Problem #2 in offset computation in Calibrate_loop_1ms "
410        " in file libbsp/i386/pc386/timer/timer.c\n"
411    );
412    goto retry;
[d57c04e]413  }
414  slowLoopGranularity = (res - offset - emptyCall)/ 10;
[6128a4a]415
[d57c04e]416  if (slowLoopGranularity == 0) {
[13e4ab64]417    printk(
418      "Problem #3 in offset computation in Calibrate_loop_1ms "
419        " in file libbsp/i386/pc386/timer/timer.c\n"
420    );
421    goto retry;
[d57c04e]422  }
[23291e99]423
[d57c04e]424  targetClockBits += offset;
[6128a4a]425#ifdef DEBUG_CALIBRATE
[d57c04e]426  printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
[171f834]427  offset, emptyCall, targetClockBits);
[d57c04e]428  printk("slowLoopGranularity = %u fastLoopGranularity =  %u\n",
[171f834]429  slowLoopGranularity, fastLoopGranularity);
[6128a4a]430#endif
[d57c04e]431  slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
432  if (slowLoop1ms != 0) {
433    fastLoop1ms = targetClockBits % slowLoopGranularity;
434    if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
[6128a4a]435  }
[d57c04e]436  else
437    fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
[23291e99]438
[d57c04e]439  if (slowLoop1ms != 0) {
440    /*
441     * calibrate slow loop
442     */
[6128a4a]443
[d57c04e]444    while(1)
445      {
[171f834]446 int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
447 Timer0Reset();
448 slowLoop(slowLoop1ms);
449 currentClockBits = readTimer0();
450 if (currentClockBits > targetClockBits) {
451   if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
452     /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
453     --slowLoop1ms;
454     break;
455   }
456   else {
457     --slowLoop1ms;
458     if (slowLoop1ms == 0) break;
459     if (previousSign == 0) previousSign = 2;
460     if (previousSign == 1) break;
461   }
462 }
463 else {
464   if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
465      break;
466    }
467    else {
468      ++slowLoop1ms;
469      if (previousSign == 0) previousSign = 1;
470      if (previousSign == 2) break;
471    }
472  }
[d57c04e]473      }
474  }
[eff217e]475  /*
[d57c04e]476   * calibrate fast loop
[eff217e]477   */
[6128a4a]478
[d57c04e]479  if (fastLoopGranularity != 0 ) {
480    while(1) {
481      int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
482      Timer0Reset();
483      if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
484      fastLoop(fastLoop1ms);
485      currentClockBits = readTimer0();
486      if (currentClockBits > targetClockBits) {
[171f834]487  if ((currentClockBits - targetClockBits) < fastLoopGranularity)
488    break;
489  else {
490    --fastLoop1ms;
491    if (previousSign == 0) previousSign = 2;
492    if (previousSign == 1) break;
493  }
[d57c04e]494      }
495      else {
[171f834]496  if ((targetClockBits - currentClockBits) < fastLoopGranularity)
497    break;
498  else {
499    ++fastLoop1ms;
500    if (previousSign == 0) previousSign = 1;
501    if (previousSign == 2) break;
502  }
[d57c04e]503      }
504    }
[eff217e]505  }
[6128a4a]506#ifdef DEBUG_CALIBRATE
[d57c04e]507  printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
[6128a4a]508#endif
[eff217e]509  rtems_interrupt_enable(level);
[6128a4a]510
[eff217e]511}
512
[171f834]513/*
514 *  loop which waits at least timeToWait ms
515 */
516void Wait_X_ms( unsigned int timeToWait)
517{
[d57c04e]518  unsigned int j;
519
520  for (j=0; j<timeToWait ; j++) {
521    if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
522    fastLoop(fastLoop1ms);
523  }
[eff217e]524}
Note: See TracBrowser for help on using the repository browser.