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

Last change on this file since beefa112 was beefa112, checked in by Chris Johns <chrisj@…>, on May 6, 2016 at 7:51:26 AM

bsp/pc386: Use irq-generic.

  • Property mode set to 100644
File size: 12.7 KB
Line 
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
24 *  found in the file LICENSE in this distribution or at
25 *  http://www.rtems.org/license/LICENSE.
26 */
27
28#include <stdlib.h>
29#include <bsp.h>
30#include <rtems/btimer.h>
31#include <bsp/irq-generic.h>
32#include <libcpu/cpuModel.h>
33
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 */
40
41#define TWO_MS (uint32_t)(2000) /* TWO_MS = 2000us (sic!) */
42
43#define MSK_NULL_COUNT 0x40     /* bit counter available for reading */
44
45#define CMD_READ_BACK_STATUS 0xE2   /* command read back status */
46
47/*
48 * Global Variables
49 */
50volatile uint32_t         Ttimer_val;
51bool                      benchmark_timer_find_average_overhead = true;
52volatile unsigned int     fastLoop1ms, slowLoop1ms;
53
54void              (*benchmark_timer_initialize_function)(void) = 0;
55benchmark_timer_t (*benchmark_timer_read_function)(void) = 0;
56void              (*Timer_exit_function)(void) = 0;
57
58/* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
59extern void timerisr(void);
60
61void Timer_exit(void);
62
63/*
64 * Pentium optimized timer handling.
65 */
66
67/*
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.
72 */
73static void tsc_timer_exit(void)
74{
75}
76
77static void tsc_timer_initialize(void)
78{
79  static bool First = true;
80
81  if (First) {
82    First = false;
83
84    atexit(Timer_exit); /* Try not to hose the system at exit. */
85  }
86  Ttimer_val = rdtsc(); /* read starting time */
87}
88
89/*
90 * Read TSC timer value.
91 */
92static uint32_t tsc_read_timer(void)
93{
94  register uint32_t  total;
95
96  total =  (uint32_t)(rdtsc() - Ttimer_val);
97
98  if (benchmark_timer_find_average_overhead)
99    return total;
100
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 */
110#define US_PER_ISR   250  /* Number of micro-seconds per timer interruption */
111
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)
117{
118  /*
119   * disable interrrupt at i8259 level
120   */
121   bsp_interrupt_vector_disable(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)
129{
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_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
139}
140
141static int timerIsOn(const rtems_raw_irq_connect_data *used)
142{
143  return bsp_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
144}
145
146static rtems_raw_irq_connect_data timer_raw_irq_data = {
147  BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
148  timerisr,
149  timerOn,
150  timerOff,
151  timerIsOn
152};
153
154/*
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)
161{
162  i386_delete_idt_entry (&timer_raw_irq_data);
163}
164
165extern void rtems_irq_prologue_0(void);
166static void i386_timer_initialize(void)
167{
168  static bool First = true;
169
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
179    First = false;
180    i386_delete_idt_entry (&raw_irq_data);
181
182    atexit(Timer_exit);            /* Try not to hose the system at exit. */
183    if (!i386_set_idt_entry (&timer_raw_irq_data)) {
184      printk("raw handler connection failed\n");
185      rtems_fatal_error_occurred(1);
186    }
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;
193}
194
195/*
196 * Read hardware timer value.
197 */
198static uint32_t i386_read_timer(void)
199{
200  register uint32_t         total, clicks;
201  register uint8_t          lsb, msb;
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
209  if (benchmark_timer_find_average_overhead)
210    return total;
211
212  if (total < LEAST_VALID)
213    return 0;                            /* below timer resolution */
214
215  return (total - AVG_OVERHEAD);
216}
217
218/*
219 * General timer functions using either TSC-based implementation
220 * or interrupt-based implementation
221 */
222
223void benchmark_timer_initialize(void)
224{
225  static bool First = true;
226
227  if (First) {
228    if (x86_has_tsc()) {
229#if defined(DEBUG)
230      printk("TSC: timer initialization\n");
231#endif /* DEBUG */
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 {
236#if defined(DEBUG)
237      printk("ISR: timer initialization\n");
238#endif /* DEBUG */
239      benchmark_timer_initialize_function = &i386_timer_initialize;
240      benchmark_timer_read_function = &i386_read_timer;
241      Timer_exit_function = &i386_timer_exit;
242    }
243    First = false;
244  }
245  (*benchmark_timer_initialize_function)();
246}
247
248uint32_t benchmark_timer_read(void)
249{
250  return (*benchmark_timer_read_function)();
251}
252
253void Timer_exit(void)
254{
255  if ( Timer_exit_function )
256    return (*Timer_exit_function)();
257}
258
259/*
260 * Set internal benchmark_timer_find_average_overhead flag value.
261 */
262void benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
263{
264  benchmark_timer_find_average_overhead = find_flag;
265}
266
267static unsigned short lastLoadedValue;
268
269/*
270 *  Loads timer 0 with value passed as arguemnt.
271 *
272 *  Returns: Nothing. Loaded value must be a number of clock bits...
273 */
274static void loadTimerValue( unsigned short loadedValue )
275{
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);
280}
281
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 */
288static unsigned int readTimer0(void)
289{
290  unsigned short lsb, msb;
291  unsigned char  status;
292  unsigned int  count;
293
294  outport_byte(
295    TIMER_MODE,
296    (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT)))
297  );
298  inport_byte(TIMER_CNTR0, status);
299  inport_byte(TIMER_CNTR0, lsb);
300  inport_byte(TIMER_CNTR0, msb);
301  count = ( msb << 8 ) | lsb ;
302  if (status & RB_OUTPUT )
303    count += lastLoadedValue;
304
305  return (2*lastLoadedValue - count);
306}
307
308static void Timer0Reset(void)
309{
310  loadTimerValue(0xffff);
311  readTimer0();
312}
313
314static void fastLoop (unsigned int loopCount)
315{
316  unsigned int i;
317  for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
318}
319
320static void slowLoop (unsigned int loopCount)
321{
322  unsigned int j;
323  for (j=0; j <100 ;  j++) {
324    fastLoop (loopCount);
325  }
326}
327
328/*
329 * #define DEBUG_CALIBRATE
330 */
331void
332Calibrate_loop_1ms(void)
333{
334  unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
335  unsigned int targetClockBits, currentClockBits;
336  unsigned int slowLoopGranularity, fastLoopGranularity;
337  rtems_interrupt_level  level;
338  int retries = 0;
339
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  }
347#ifdef DEBUG_CALIBRATE
348  printk("Calibrate_loop_1ms is starting,  please wait (but not too long.)\n");
349#endif
350  targetClockBits = US_TO_TICK(1000);
351  /*
352   * Fill up the cache to get a correct offset
353   */
354  Timer0Reset();
355  readTimer0();
356  /*
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) {
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;
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) {
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;
413  }
414  slowLoopGranularity = (res - offset - emptyCall)/ 10;
415
416  if (slowLoopGranularity == 0) {
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;
422  }
423
424  targetClockBits += offset;
425#ifdef DEBUG_CALIBRATE
426  printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
427  offset, emptyCall, targetClockBits);
428  printk("slowLoopGranularity = %u fastLoopGranularity =  %u\n",
429  slowLoopGranularity, fastLoopGranularity);
430#endif
431  slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
432  if (slowLoop1ms != 0) {
433    fastLoop1ms = targetClockBits % slowLoopGranularity;
434    if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
435  }
436  else
437    fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
438
439  if (slowLoop1ms != 0) {
440    /*
441     * calibrate slow loop
442     */
443
444    while(1)
445      {
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  }
473      }
474  }
475  /*
476   * calibrate fast loop
477   */
478
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) {
487  if ((currentClockBits - targetClockBits) < fastLoopGranularity)
488    break;
489  else {
490    --fastLoop1ms;
491    if (previousSign == 0) previousSign = 2;
492    if (previousSign == 1) break;
493  }
494      }
495      else {
496  if ((targetClockBits - currentClockBits) < fastLoopGranularity)
497    break;
498  else {
499    ++fastLoop1ms;
500    if (previousSign == 0) previousSign = 1;
501    if (previousSign == 2) break;
502  }
503      }
504    }
505  }
506#ifdef DEBUG_CALIBRATE
507  printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
508#endif
509  rtems_interrupt_enable(level);
510
511}
512
513/*
514 *  loop which waits at least timeToWait ms
515 */
516void Wait_X_ms( unsigned int timeToWait)
517{
518  unsigned int j;
519
520  for (j=0; j<timeToWait ; j++) {
521    if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
522    fastLoop(fastLoop1ms);
523  }
524}
Note: See TracBrowser for help on using the repository browser.