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

5
Last change on this file since c6810c8 was c6810c8, checked in by Sebastian Huber <sebastian.huber@…>, on 06/19/17 at 12:09:28

bsps: Improve interrupt vector enable/disable API

Change bsp_interrupt_vector_enable() and bsp_interrupt_vector_disable()
to not return a status code. Add bsp_interrupt_assert() and use it to
validate the vector number in the vector enable/disable implementations.

  • Property mode set to 100644
File size: 13.9 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 *
[beefa112]19 *  Based upon code by
[171f834]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>
[beefa112]31#include <bsp/irq-generic.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
[6b54dcb]47RTEMS_INTERRUPT_LOCK_DEFINE( /* visible global variable */ ,
48   rtems_i386_i8254_access_lock, "rtems_i386_i8254_access_lock" );
49
[171f834]50/*
51 * Global Variables
52 */
[6fda59f]53volatile uint32_t         Ttimer_val;
[171f834]54bool                      benchmark_timer_find_average_overhead = true;
[d57c04e]55volatile unsigned int     fastLoop1ms, slowLoop1ms;
[959f887a]56
[8fbe2e6]57void              (*benchmark_timer_initialize_function)(void) = 0;
58benchmark_timer_t (*benchmark_timer_read_function)(void) = 0;
59void              (*Timer_exit_function)(void) = 0;
[7150f00f]60
[171f834]61/* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
[2d7d605]62extern void timerisr(void);
[171f834]63
64void Timer_exit(void);
[1131dfe]65
66/*
[171f834]67 * Pentium optimized timer handling.
[1131dfe]68 */
69
[171f834]70/*
[4977f07e]71 *  Timer cleanup routine at RTEMS exit.
72 *
73 *  NOTE: This routine is not really necessary, since there will be
74 *        a reset at exit.
[171f834]75 */
[4977f07e]76static void tsc_timer_exit(void)
[7150f00f]77{
[171f834]78}
79
[4977f07e]80static void tsc_timer_initialize(void)
[7150f00f]81{
[78b05d3]82  static bool First = true;
[7150f00f]83
[171f834]84  if (First) {
[78b05d3]85    First = false;
[7150f00f]86
87    atexit(Timer_exit); /* Try not to hose the system at exit. */
88  }
89  Ttimer_val = rdtsc(); /* read starting time */
[171f834]90}
91
92/*
[4977f07e]93 * Read TSC timer value.
[171f834]94 */
[4977f07e]95static uint32_t tsc_read_timer(void)
[7150f00f]96{
[171f834]97  register uint32_t  total;
[7150f00f]98
[6fda59f]99  total =  (uint32_t)(rdtsc() - Ttimer_val);
[7150f00f]100
[25c62b0]101  if (benchmark_timer_find_average_overhead)
[7150f00f]102    return total;
103
[171f834]104  if (total < LEAST_VALID)
105    return 0;                 /* below timer resolution */
106
107  return (total - AVG_OVERHEAD);
108}
109
110/*
111 * Non-Pentium timer handling.
112 */
[7150f00f]113#define US_PER_ISR   250  /* Number of micro-seconds per timer interruption */
114
[171f834]115/*
116 * Timer cleanup routine at RTEMS exit. NOTE: This routine is
117 * not really necessary, since there will be a reset at exit.
118 */
119static void timerOff(const rtems_raw_irq_connect_data* used)
[67a2288]120{
[6b54dcb]121  rtems_interrupt_lock_context lock_context;
[171f834]122  /*
123   * disable interrrupt at i8259 level
124   */
[6b54dcb]125  bsp_interrupt_vector_disable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
126
127  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
128
[171f834]129   /* reset timer mode to standard (DOS) value */
[6b54dcb]130  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
131  outport_byte(TIMER_CNTR0, 0);
132  outport_byte(TIMER_CNTR0, 0);
133
134  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
[171f834]135}
136
137static void timerOn(const rtems_raw_irq_connect_data* used)
[67a2288]138{
[6b54dcb]139  rtems_interrupt_lock_context lock_context;
140
141  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
142
[171f834]143  /* load timer for US_PER_ISR microsecond period */
144  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
145  outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff);
146  outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff);
147
[6b54dcb]148  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
149
[171f834]150  /*
151   * enable interrrupt at i8259 level
152   */
[beefa112]153  bsp_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
[67a2288]154}
155
156static rtems_raw_irq_connect_data timer_raw_irq_data = {
[0ebbf66]157  BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
[67a2288]158  timerisr,
159  timerOn,
160  timerOff,
[c6810c8]161  NULL
[67a2288]162};
[7150f00f]163
[171f834]164/*
[4977f07e]165 * Timer cleanup routine at RTEMS exit.
166 *
167 * NOTE: This routine is not really necessary, since there will be
168 *       a reset at exit.
169 */
170static void i386_timer_exit(void)
[7150f00f]171{
[67a2288]172  i386_delete_idt_entry (&timer_raw_irq_data);
[171f834]173}
174
175extern void rtems_irq_prologue_0(void);
[4977f07e]176static void i386_timer_initialize(void)
[7150f00f]177{
[0bbd74de]178  static bool First = true;
[7150f00f]179
[171f834]180  if (First) {
181    rtems_raw_irq_connect_data raw_irq_data = {
182      BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
183      rtems_irq_prologue_0,
184      NULL,
185      NULL,
186      NULL
187    };
188
[0bbd74de]189    First = false;
[171f834]190    i386_delete_idt_entry (&raw_irq_data);
[7150f00f]191
[171f834]192    atexit(Timer_exit);            /* Try not to hose the system at exit. */
[67a2288]193    if (!i386_set_idt_entry (&timer_raw_irq_data)) {
[171f834]194      printk("raw handler connection failed\n");
[67a2288]195      rtems_fatal_error_occurred(1);
196    }
[7150f00f]197  }
198  /* wait for ISR to be called at least once */
199  Ttimer_val = 0;
200  while (Ttimer_val == 0)
201    continue;
202  Ttimer_val = 0;
[171f834]203}
204
205/*
206 * Read hardware timer value.
207 */
[4977f07e]208static uint32_t i386_read_timer(void)
[7150f00f]209{
[6fda59f]210  register uint32_t         total, clicks;
211  register uint8_t          lsb, msb;
[6b54dcb]212  rtems_interrupt_lock_context lock_context;
[7150f00f]213
[6b54dcb]214  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
[7150f00f]215  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
216  inport_byte(TIMER_CNTR0, lsb);
217  inport_byte(TIMER_CNTR0, msb);
[6b54dcb]218  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
219
[7150f00f]220  clicks = (msb << 8) | lsb;
221  total  = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks));
222
[25c62b0]223  if (benchmark_timer_find_average_overhead)
[7150f00f]224    return total;
[171f834]225
226  if (total < LEAST_VALID)
227    return 0;                            /* below timer resolution */
228
229  return (total - AVG_OVERHEAD);
[7150f00f]230}
231
[1131dfe]232/*
233 * General timer functions using either TSC-based implementation
234 * or interrupt-based implementation
235 */
236
[171f834]237void benchmark_timer_initialize(void)
[1131dfe]238{
[171f834]239  static bool First = true;
[1131dfe]240
[171f834]241  if (First) {
242    if (x86_has_tsc()) {
[1131dfe]243#if defined(DEBUG)
[171f834]244      printk("TSC: timer initialization\n");
[3e21ecf]245#endif /* DEBUG */
[171f834]246      benchmark_timer_initialize_function = &tsc_timer_initialize;
247      benchmark_timer_read_function = &tsc_read_timer;
248      Timer_exit_function = &tsc_timer_exit;
249    } else {
[1131dfe]250#if defined(DEBUG)
[171f834]251      printk("ISR: timer initialization\n");
[3e21ecf]252#endif /* DEBUG */
[171f834]253      benchmark_timer_initialize_function = &i386_timer_initialize;
254      benchmark_timer_read_function = &i386_read_timer;
255      Timer_exit_function = &i386_timer_exit;
[1131dfe]256    }
[171f834]257    First = false;
258  }
259  (*benchmark_timer_initialize_function)();
[1131dfe]260}
261
[171f834]262uint32_t benchmark_timer_read(void)
[1131dfe]263{
[171f834]264  return (*benchmark_timer_read_function)();
[1131dfe]265}
266
[171f834]267void Timer_exit(void)
[1131dfe]268{
[59211c6]269  if ( Timer_exit_function )
270    return (*Timer_exit_function)();
[1131dfe]271}
[7150f00f]272
[171f834]273/*
274 * Set internal benchmark_timer_find_average_overhead flag value.
275 */
276void benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
[7150f00f]277{
[25c62b0]278  benchmark_timer_find_average_overhead = find_flag;
[171f834]279}
[eff217e]280
[d57c04e]281static unsigned short lastLoadedValue;
[23291e99]282
[171f834]283/*
284 *  Loads timer 0 with value passed as arguemnt.
285 *
286 *  Returns: Nothing. Loaded value must be a number of clock bits...
287 */
[4977f07e]288static void loadTimerValue( unsigned short loadedValue )
[23291e99]289{
[6b54dcb]290  rtems_interrupt_lock_context lock_context;
291  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
[d57c04e]292  lastLoadedValue = loadedValue;
293  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE);
294  outport_byte(TIMER_CNTR0, loadedValue & 0xff);
295  outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
[6b54dcb]296  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
[23291e99]297}
298
[171f834]299/*
300 * Reads the current value of the timer, and converts the
301 *  number of ticks to micro-seconds.
302 *
303 * Returns: number of clock bits elapsed since last load.
304 */
[4977f07e]305static unsigned int readTimer0(void)
[23291e99]306{
307  unsigned short lsb, msb;
[d57c04e]308  unsigned char  status;
[171f834]309  unsigned int  count;
[6b54dcb]310  rtems_interrupt_lock_context lock_context;
311  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
[d57c04e]312
[171f834]313  outport_byte(
314    TIMER_MODE,
315    (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT)))
316  );
[d57c04e]317  inport_byte(TIMER_CNTR0, status);
[23291e99]318  inport_byte(TIMER_CNTR0, lsb);
319  inport_byte(TIMER_CNTR0, msb);
[6b54dcb]320
321  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
322
[d57c04e]323  count = ( msb << 8 ) | lsb ;
324  if (status & RB_OUTPUT )
[171f834]325    count += lastLoadedValue;
[6128a4a]326
[d57c04e]327  return (2*lastLoadedValue - count);
[23291e99]328}
329
[4977f07e]330static void Timer0Reset(void)
[d57c04e]331{
332  loadTimerValue(0xffff);
333  readTimer0();
334}
[23291e99]335
[4977f07e]336static void fastLoop (unsigned int loopCount)
[23291e99]337{
[d57c04e]338  unsigned int i;
339  for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
[23291e99]340}
341
[4977f07e]342static void slowLoop (unsigned int loopCount)
[d57c04e]343{
344  unsigned int j;
345  for (j=0; j <100 ;  j++) {
346    fastLoop (loopCount);
347  }
348}
[23291e99]349
[d57c04e]350/*
351 * #define DEBUG_CALIBRATE
352 */
[eff217e]353void
[23291e99]354Calibrate_loop_1ms(void)
355{
[d57c04e]356  unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
357  unsigned int targetClockBits, currentClockBits;
358  unsigned int slowLoopGranularity, fastLoopGranularity;
[eff217e]359  rtems_interrupt_level  level;
[13e4ab64]360  int retries = 0;
[6128a4a]361
[6b54dcb]362  /*
363   * This code is designed to run before interrupt management
364   * is enabled and running it on multiple CPUs and or after
365   * secondary CPUs are bring up seems really broken.
366   * Disabling of local interrupts is enough.
367   */
368  rtems_interrupt_local_disable(level);
[13e4ab64]369
370retry:
371  if ( ++retries >= 5 ) {
372    printk( "Calibrate_loop_1ms: too many attempts. giving up!!\n" );
373    while (1);
374  }
[d57c04e]375#ifdef DEBUG_CALIBRATE
[171f834]376  printk("Calibrate_loop_1ms is starting,  please wait (but not too long.)\n");
[6128a4a]377#endif
[d57c04e]378  targetClockBits = US_TO_TICK(1000);
379  /*
380   * Fill up the cache to get a correct offset
381   */
382  Timer0Reset();
383  readTimer0();
[6128a4a]384  /*
[d57c04e]385   * Compute the minimal offset to apply due to read counter register.
386   */
387  offset = 0xffffffff;
388  for (i=0; i <1000; i++) {
389    Timer0Reset();
390    offsetTmp = readTimer0();
391    offset += offsetTmp;
392  }
393  offset = offset / 1000; /* compute average */
394  /*
395   * calibrate empty call
396   */
397  fastLoop (0);
398  emptyCall = 0;
399  j = 0;
400  for (i=0; i <10; i++) {
401    Timer0Reset();
402    fastLoop (0);
403    res =  readTimer0();
404    /* res may be inferior to offset on fast
405     * machine because we took an average for offset
406     */
407    if (res >  offset) {
408      ++j;
409      emptyCallTmp = res - offset;
410      emptyCall += emptyCallTmp;
411    }
412  }
413  if (j == 0) emptyCall = 0;
414  else emptyCall = emptyCall / j; /* compute average */
415  /*
416   * calibrate fast loop
417   */
418  Timer0Reset();
419  fastLoop (10000);
420  res = readTimer0() - offset;
421  if (res < emptyCall) {
[13e4ab64]422    printk(
423      "Problem #1 in offset computation in Calibrate_loop_1ms "
424        " in file libbsp/i386/pc386/timer/timer.c\n"
425    );
426    goto retry;
[d57c04e]427  }
428  fastLoopGranularity = (res - emptyCall) / 10000;
429  /*
430   * calibrate slow loop
431   */
432  Timer0Reset();
433  slowLoop(10);
434  res = readTimer0();
435  if (res < offset + emptyCall) {
[13e4ab64]436    printk(
437      "Problem #2 in offset computation in Calibrate_loop_1ms "
438        " in file libbsp/i386/pc386/timer/timer.c\n"
439    );
440    goto retry;
[d57c04e]441  }
442  slowLoopGranularity = (res - offset - emptyCall)/ 10;
[6128a4a]443
[d57c04e]444  if (slowLoopGranularity == 0) {
[13e4ab64]445    printk(
446      "Problem #3 in offset computation in Calibrate_loop_1ms "
447        " in file libbsp/i386/pc386/timer/timer.c\n"
448    );
449    goto retry;
[d57c04e]450  }
[23291e99]451
[d57c04e]452  targetClockBits += offset;
[6128a4a]453#ifdef DEBUG_CALIBRATE
[d57c04e]454  printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
[171f834]455  offset, emptyCall, targetClockBits);
[d57c04e]456  printk("slowLoopGranularity = %u fastLoopGranularity =  %u\n",
[171f834]457  slowLoopGranularity, fastLoopGranularity);
[6128a4a]458#endif
[d57c04e]459  slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
460  if (slowLoop1ms != 0) {
461    fastLoop1ms = targetClockBits % slowLoopGranularity;
462    if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
[6128a4a]463  }
[d57c04e]464  else
465    fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
[23291e99]466
[d57c04e]467  if (slowLoop1ms != 0) {
468    /*
469     * calibrate slow loop
470     */
[6128a4a]471
[d57c04e]472    while(1)
473      {
[171f834]474 int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
475 Timer0Reset();
476 slowLoop(slowLoop1ms);
477 currentClockBits = readTimer0();
478 if (currentClockBits > targetClockBits) {
479   if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
480     /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
481     --slowLoop1ms;
482     break;
483   }
484   else {
485     --slowLoop1ms;
486     if (slowLoop1ms == 0) break;
487     if (previousSign == 0) previousSign = 2;
488     if (previousSign == 1) break;
489   }
490 }
491 else {
492   if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
493      break;
494    }
495    else {
496      ++slowLoop1ms;
497      if (previousSign == 0) previousSign = 1;
498      if (previousSign == 2) break;
499    }
500  }
[d57c04e]501      }
502  }
[eff217e]503  /*
[d57c04e]504   * calibrate fast loop
[eff217e]505   */
[6128a4a]506
[d57c04e]507  if (fastLoopGranularity != 0 ) {
508    while(1) {
509      int previousSign = 0; /* 0 = unset, 1 = incrementing,  2 = decrementing */
510      Timer0Reset();
511      if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
512      fastLoop(fastLoop1ms);
513      currentClockBits = readTimer0();
514      if (currentClockBits > targetClockBits) {
[171f834]515  if ((currentClockBits - targetClockBits) < fastLoopGranularity)
516    break;
517  else {
518    --fastLoop1ms;
519    if (previousSign == 0) previousSign = 2;
520    if (previousSign == 1) break;
521  }
[d57c04e]522      }
523      else {
[171f834]524  if ((targetClockBits - currentClockBits) < fastLoopGranularity)
525    break;
526  else {
527    ++fastLoop1ms;
528    if (previousSign == 0) previousSign = 1;
529    if (previousSign == 2) break;
530  }
[d57c04e]531      }
532    }
[eff217e]533  }
[6128a4a]534#ifdef DEBUG_CALIBRATE
[d57c04e]535  printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
[6128a4a]536#endif
[6b54dcb]537  rtems_interrupt_local_enable(level);
[6128a4a]538
[eff217e]539}
540
[171f834]541/*
542 *  loop which waits at least timeToWait ms
543 */
544void Wait_X_ms( unsigned int timeToWait)
545{
[d57c04e]546  unsigned int j;
547
548  for (j=0; j<timeToWait ; j++) {
549    if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
550    fastLoop(fastLoop1ms);
551  }
[eff217e]552}
Note: See TracBrowser for help on using the repository browser.