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
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
47RTEMS_INTERRUPT_LOCK_DEFINE( /* visible global variable */ ,
48   rtems_i386_i8254_access_lock, "rtems_i386_i8254_access_lock" );
49
50/*
51 * Global Variables
52 */
53volatile uint32_t         Ttimer_val;
54bool                      benchmark_timer_find_average_overhead = true;
55volatile unsigned int     fastLoop1ms, slowLoop1ms;
56
57void              (*benchmark_timer_initialize_function)(void) = 0;
58benchmark_timer_t (*benchmark_timer_read_function)(void) = 0;
59void              (*Timer_exit_function)(void) = 0;
60
61/* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
62extern void timerisr(void);
63
64void Timer_exit(void);
65
66/*
67 * Pentium optimized timer handling.
68 */
69
70/*
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.
75 */
76static void tsc_timer_exit(void)
77{
78}
79
80static void tsc_timer_initialize(void)
81{
82  static bool First = true;
83
84  if (First) {
85    First = false;
86
87    atexit(Timer_exit); /* Try not to hose the system at exit. */
88  }
89  Ttimer_val = rdtsc(); /* read starting time */
90}
91
92/*
93 * Read TSC timer value.
94 */
95static uint32_t tsc_read_timer(void)
96{
97  register uint32_t  total;
98
99  total =  (uint32_t)(rdtsc() - Ttimer_val);
100
101  if (benchmark_timer_find_average_overhead)
102    return total;
103
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 */
113#define US_PER_ISR   250  /* Number of micro-seconds per timer interruption */
114
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)
120{
121  rtems_interrupt_lock_context lock_context;
122  /*
123   * disable interrrupt at i8259 level
124   */
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
129   /* reset timer mode to standard (DOS) value */
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);
135}
136
137static void timerOn(const rtems_raw_irq_connect_data* used)
138{
139  rtems_interrupt_lock_context lock_context;
140
141  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
142
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
148  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
149
150  /*
151   * enable interrrupt at i8259 level
152   */
153  bsp_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
154}
155
156static rtems_raw_irq_connect_data timer_raw_irq_data = {
157  BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
158  timerisr,
159  timerOn,
160  timerOff,
161  NULL
162};
163
164/*
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)
171{
172  i386_delete_idt_entry (&timer_raw_irq_data);
173}
174
175extern void rtems_irq_prologue_0(void);
176static void i386_timer_initialize(void)
177{
178  static bool First = true;
179
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
189    First = false;
190    i386_delete_idt_entry (&raw_irq_data);
191
192    atexit(Timer_exit);            /* Try not to hose the system at exit. */
193    if (!i386_set_idt_entry (&timer_raw_irq_data)) {
194      printk("raw handler connection failed\n");
195      rtems_fatal_error_occurred(1);
196    }
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;
203}
204
205/*
206 * Read hardware timer value.
207 */
208static uint32_t i386_read_timer(void)
209{
210  register uint32_t         total, clicks;
211  register uint8_t          lsb, msb;
212  rtems_interrupt_lock_context lock_context;
213
214  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
215  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
216  inport_byte(TIMER_CNTR0, lsb);
217  inport_byte(TIMER_CNTR0, msb);
218  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
219
220  clicks = (msb << 8) | lsb;
221  total  = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks));
222
223  if (benchmark_timer_find_average_overhead)
224    return total;
225
226  if (total < LEAST_VALID)
227    return 0;                            /* below timer resolution */
228
229  return (total - AVG_OVERHEAD);
230}
231
232/*
233 * General timer functions using either TSC-based implementation
234 * or interrupt-based implementation
235 */
236
237void benchmark_timer_initialize(void)
238{
239  static bool First = true;
240
241  if (First) {
242    if (x86_has_tsc()) {
243#if defined(DEBUG)
244      printk("TSC: timer initialization\n");
245#endif /* DEBUG */
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 {
250#if defined(DEBUG)
251      printk("ISR: timer initialization\n");
252#endif /* DEBUG */
253      benchmark_timer_initialize_function = &i386_timer_initialize;
254      benchmark_timer_read_function = &i386_read_timer;
255      Timer_exit_function = &i386_timer_exit;
256    }
257    First = false;
258  }
259  (*benchmark_timer_initialize_function)();
260}
261
262uint32_t benchmark_timer_read(void)
263{
264  return (*benchmark_timer_read_function)();
265}
266
267void Timer_exit(void)
268{
269  if ( Timer_exit_function )
270    return (*Timer_exit_function)();
271}
272
273/*
274 * Set internal benchmark_timer_find_average_overhead flag value.
275 */
276void benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
277{
278  benchmark_timer_find_average_overhead = find_flag;
279}
280
281static unsigned short lastLoadedValue;
282
283/*
284 *  Loads timer 0 with value passed as arguemnt.
285 *
286 *  Returns: Nothing. Loaded value must be a number of clock bits...
287 */
288static void loadTimerValue( unsigned short loadedValue )
289{
290  rtems_interrupt_lock_context lock_context;
291  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
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);
296  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
297}
298
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 */
305static unsigned int readTimer0(void)
306{
307  unsigned short lsb, msb;
308  unsigned char  status;
309  unsigned int  count;
310  rtems_interrupt_lock_context lock_context;
311  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
312
313  outport_byte(
314    TIMER_MODE,
315    (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT)))
316  );
317  inport_byte(TIMER_CNTR0, status);
318  inport_byte(TIMER_CNTR0, lsb);
319  inport_byte(TIMER_CNTR0, msb);
320
321  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
322
323  count = ( msb << 8 ) | lsb ;
324  if (status & RB_OUTPUT )
325    count += lastLoadedValue;
326
327  return (2*lastLoadedValue - count);
328}
329
330static void Timer0Reset(void)
331{
332  loadTimerValue(0xffff);
333  readTimer0();
334}
335
336static void fastLoop (unsigned int loopCount)
337{
338  unsigned int i;
339  for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
340}
341
342static void slowLoop (unsigned int loopCount)
343{
344  unsigned int j;
345  for (j=0; j <100 ;  j++) {
346    fastLoop (loopCount);
347  }
348}
349
350/*
351 * #define DEBUG_CALIBRATE
352 */
353void
354Calibrate_loop_1ms(void)
355{
356  unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
357  unsigned int targetClockBits, currentClockBits;
358  unsigned int slowLoopGranularity, fastLoopGranularity;
359  rtems_interrupt_level  level;
360  int retries = 0;
361
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);
369
370retry:
371  if ( ++retries >= 5 ) {
372    printk( "Calibrate_loop_1ms: too many attempts. giving up!!\n" );
373    while (1);
374  }
375#ifdef DEBUG_CALIBRATE
376  printk("Calibrate_loop_1ms is starting,  please wait (but not too long.)\n");
377#endif
378  targetClockBits = US_TO_TICK(1000);
379  /*
380   * Fill up the cache to get a correct offset
381   */
382  Timer0Reset();
383  readTimer0();
384  /*
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) {
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;
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) {
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;
441  }
442  slowLoopGranularity = (res - offset - emptyCall)/ 10;
443
444  if (slowLoopGranularity == 0) {
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;
450  }
451
452  targetClockBits += offset;
453#ifdef DEBUG_CALIBRATE
454  printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
455  offset, emptyCall, targetClockBits);
456  printk("slowLoopGranularity = %u fastLoopGranularity =  %u\n",
457  slowLoopGranularity, fastLoopGranularity);
458#endif
459  slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
460  if (slowLoop1ms != 0) {
461    fastLoop1ms = targetClockBits % slowLoopGranularity;
462    if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
463  }
464  else
465    fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
466
467  if (slowLoop1ms != 0) {
468    /*
469     * calibrate slow loop
470     */
471
472    while(1)
473      {
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  }
501      }
502  }
503  /*
504   * calibrate fast loop
505   */
506
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) {
515  if ((currentClockBits - targetClockBits) < fastLoopGranularity)
516    break;
517  else {
518    --fastLoop1ms;
519    if (previousSign == 0) previousSign = 2;
520    if (previousSign == 1) break;
521  }
522      }
523      else {
524  if ((targetClockBits - currentClockBits) < fastLoopGranularity)
525    break;
526  else {
527    ++fastLoop1ms;
528    if (previousSign == 0) previousSign = 1;
529    if (previousSign == 2) break;
530  }
531      }
532    }
533  }
534#ifdef DEBUG_CALIBRATE
535  printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
536#endif
537  rtems_interrupt_local_enable(level);
538
539}
540
541/*
542 *  loop which waits at least timeToWait ms
543 */
544void Wait_X_ms( unsigned int timeToWait)
545{
546  unsigned int j;
547
548  for (j=0; j<timeToWait ; j++) {
549    if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
550    fastLoop(fastLoop1ms);
551  }
552}
Note: See TracBrowser for help on using the repository browser.