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

4.104.114.84.95
Last change on this file since 5cfcd7e was 5cfcd7e, checked in by Joel Sherrill <joel.sherrill@…>, on 03/31/99 at 22:59:13

Removed warnings.

  • Property mode set to 100644
File size: 15.4 KB
Line 
1/*-------------------------------------------------------------------------+
2| timer.c v1.1 - PC386 BSP - 1997/08/07
3+--------------------------------------------------------------------------+
4| This file contains the PC386 timer package.
5+--------------------------------------------------------------------------+
6| NOTE: It is important that the timer start/stop overhead be determined
7|       when porting or modifying this code.
8+--------------------------------------------------------------------------+
9| (C) Copyright 1997 -
10| - NavIST Group - Real-Time Distributed Systems and Industrial Automation
11|
12| http://pandora.ist.utl.pt
13|
14| Instituto Superior Tecnico * Lisboa * PORTUGAL
15+--------------------------------------------------------------------------+
16| Disclaimer:
17|
18| This file is provided "AS IS" without warranty of any kind, either
19| expressed or implied.
20+--------------------------------------------------------------------------+
21| This code is base on:
22|   timer.c,v 1.7 1995/12/19 20:07:43 joel Exp - go32 BSP
23|
24| Rosimildo daSilva -ConnectTel, Inc - Fixed infinite loop in the Calibration
25| routine. I've seen this problems with faster machines ( pentiums ). Sometimes
26| RTEMS just hangs at startup.
27|
28| With the following copyright notice:
29| **************************************************************************
30| *  COPYRIGHT (c) 1989-1998.
31| *  On-Line Applications Research Corporation (OAR).
32| *  Copyright assigned to U.S. Government, 1994.
33| *
34| *  The license and distribution terms for this file may be
35| *  found in found in the file LICENSE in this distribution or at
36| *  http://www.OARcorp.com/rtems/license.html.
37| **************************************************************************
38|
39|  $Id$
40+--------------------------------------------------------------------------*/
41
42
43#include <stdlib.h>
44
45#include <bsp.h>
46#include <irq.h>
47
48/*-------------------------------------------------------------------------+
49| Constants
50+--------------------------------------------------------------------------*/
51#define AVG_OVERHEAD  0              /* 0.1 microseconds to start/stop timer. */
52#define LEAST_VALID   1              /* Don't trust a value lower than this.  */
53#define SLOW_DOWN_IO  0x80      /* io which does nothing */
54
55#define TWO_MS  (rtems_unsigned32)(2000)     /* TWO_MS = 2000us (sic!) */
56
57#define MSK_NULL_COUNT 0x40     /* bit counter available for reading */
58
59#define CMD_READ_BACK_STATUS 0xE2   /* command read back status */
60/*-------------------------------------------------------------------------+
61| Global Variables
62+--------------------------------------------------------------------------*/
63volatile rtems_unsigned32 Ttimer_val;
64         rtems_boolean    Timer_driver_Find_average_overhead = TRUE;
65         unsigned int     loop1ms;
66
67/*-------------------------------------------------------------------------+
68| External Prototypes
69+--------------------------------------------------------------------------*/
70extern void timerisr(void);
71       /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
72
73/*-------------------------------------------------------------------------+
74| Pentium optimized timer handling.
75+--------------------------------------------------------------------------*/
76#if defined(pentium)
77
78/*-------------------------------------------------------------------------+
79|         Function: rdtsc
80|      Description: Read the value of PENTIUM on-chip cycle counter.
81| Global Variables: None.
82|        Arguments: None.
83|          Returns: Value of PENTIUM on-chip cycle counter.
84+--------------------------------------------------------------------------*/
85static inline unsigned long long
86rdtsc(void)
87{
88  /* Return the value of the on-chip cycle counter. */
89  unsigned long long result;
90  asm volatile(".byte 0x0F, 0x31" : "=A" (result));
91  return result;
92} /* rdtsc */
93
94
95/*-------------------------------------------------------------------------+
96|         Function: Timer_exit
97|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
98|                   not really necessary, since there will be a reset at exit.
99| Global Variables: None.
100|        Arguments: None.
101|          Returns: Nothing.
102+--------------------------------------------------------------------------*/
103void
104Timer_exit(void)
105{
106} /* Timer_exit */
107
108
109/*-------------------------------------------------------------------------+
110|         Function: Timer_initialize
111|      Description: Timer initialization routine.
112| Global Variables: Ttimer_val.
113|        Arguments: None.
114|          Returns: Nothing.
115+--------------------------------------------------------------------------*/
116void
117Timer_initialize(void)
118{
119  static rtems_boolean First = TRUE;
120
121  if (First)
122  {
123    First = FALSE;
124
125    atexit(Timer_exit); /* Try not to hose the system at exit. */
126  }
127  Ttimer_val = rdtsc(); /* read starting time */
128} /* Timer_initialize */
129
130
131/*-------------------------------------------------------------------------+
132|         Function: Read_timer
133|      Description: Read hardware timer value.
134| Global Variables: Ttimer_val, Timer_driver_Find_average_overhead.
135|        Arguments: None.
136|          Returns: Nothing.
137+--------------------------------------------------------------------------*/
138rtems_unsigned32
139Read_timer(void)
140{
141  register rtems_unsigned32 total;
142
143  total =  (rtems_unsigned32)(rdtsc() - Ttimer_val);
144
145  if (Timer_driver_Find_average_overhead)
146    return total;
147  else if (total < LEAST_VALID)
148    return 0; /* below timer resolution */
149  else
150    return (total - AVG_OVERHEAD);
151} /* Read_timer */
152
153#else /* pentium */
154
155/*-------------------------------------------------------------------------+
156| Non-Pentium timer handling.
157+--------------------------------------------------------------------------*/
158#define US_PER_ISR   250  /* Number of micro-seconds per timer interruption */
159
160
161/*-------------------------------------------------------------------------+
162|         Function: Timer_exit
163|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
164|                   not really necessary, since there will be a reset at exit.
165| Global Variables: None.
166|        Arguments: None.
167|          Returns: Nothing.
168+--------------------------------------------------------------------------*/
169static void
170timerOff(const rtems_raw_irq_connect_data* used)
171{
172    /*
173     * disable interrrupt at i8259 level
174     */
175     BSP_irq_disable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
176     /* reset timer mode to standard (DOS) value */
177     outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
178     outport_byte(TIMER_CNTR0, 0);
179     outport_byte(TIMER_CNTR0, 0);
180} /* Timer_exit */
181
182
183static void
184timerOn(const rtems_raw_irq_connect_data* used)
185{
186     /* load timer for US_PER_ISR microsecond period */
187     outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
188     outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff);
189     outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff);
190    /*
191     * enable interrrupt at i8259 level
192     */
193     BSP_irq_enable_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);
194}
195
196static int
197timerIsOn(const rtems_raw_irq_connect_data *used)
198{
199     return BSP_irq_enabled_at_i8259s(used->idtIndex - BSP_IRQ_VECTOR_BASE);}
200
201static rtems_raw_irq_connect_data timer_raw_irq_data = {
202  BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
203  timerisr,
204  timerOn,
205  timerOff,
206  timerIsOn
207};
208
209/*-------------------------------------------------------------------------+
210|         Function: Timer_exit
211|      Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is
212|                   not really necessary, since there will be a reset at exit.
213| Global Variables: None.
214|        Arguments: None.
215|          Returns: Nothing.
216+--------------------------------------------------------------------------*/
217void
218Timer_exit(void)
219{
220  i386_delete_idt_entry (&timer_raw_irq_data);
221} /* Timer_exit */
222
223/*-------------------------------------------------------------------------+
224|         Function: Timer_initialize
225|      Description: Timer initialization routine.
226| Global Variables: Ttimer_val.
227|        Arguments: None.
228|          Returns: Nothing.
229+--------------------------------------------------------------------------*/
230void
231Timer_initialize(void)
232{
233  static rtems_boolean First = TRUE;
234
235  if (First)
236  {
237    First = FALSE;
238
239    atexit(Timer_exit); /* Try not to hose the system at exit. */
240    if (!i386_set_idt_entry (&timer_raw_irq_data)) {
241      printk("raw handler connexion failed\n");
242      rtems_fatal_error_occurred(1);
243    }
244  }
245  /* wait for ISR to be called at least once */
246  Ttimer_val = 0;
247  while (Ttimer_val == 0)
248    continue;
249  Ttimer_val = 0;
250} /* Timer_initialize */
251
252
253/*-------------------------------------------------------------------------+
254|         Function: Read_timer
255|      Description: Read hardware timer value.
256| Global Variables: Ttimer_val, Timer_driver_Find_average_overhead.
257|        Arguments: None.
258|          Returns: Nothing.
259+--------------------------------------------------------------------------*/
260rtems_unsigned32
261Read_timer(void)
262{
263  register rtems_unsigned32 total, clicks;
264  register rtems_unsigned8  lsb, msb;
265
266  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
267  inport_byte(TIMER_CNTR0, lsb);
268  inport_byte(TIMER_CNTR0, msb);
269  clicks = (msb << 8) | lsb;
270  total  = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks));
271
272  if (Timer_driver_Find_average_overhead)
273    return total;
274  else if (total < LEAST_VALID)
275    return 0; /* below timer resolution */
276  else
277    return (total - AVG_OVERHEAD);
278}
279
280#endif /* pentium */
281
282
283/*-------------------------------------------------------------------------+
284|         Function: Empty_function
285|      Description: Empty function used in time tests.
286| Global Variables: None.
287|        Arguments: None.
288|          Returns: Nothing.
289+--------------------------------------------------------------------------*/
290rtems_status_code Empty_function(void)
291{
292  return RTEMS_SUCCESSFUL;
293} /* Empty function */
294 
295
296/*-------------------------------------------------------------------------+
297|         Function: Set_find_average_overhead
298|      Description: Set internal Timer_driver_Find_average_overhead flag value.
299| Global Variables: Timer_driver_Find_average_overhead.
300|        Arguments: find_flag - new value of the flag.
301|          Returns: Nothing.
302+--------------------------------------------------------------------------*/
303void
304Set_find_average_overhead(rtems_boolean find_flag)
305{
306  Timer_driver_Find_average_overhead = find_flag;
307} /* Set_find_average_overhead */
308
309
310
311/*-------------------------------------------------------------------------+
312| Description: Loads timer 0 with value passed as arguemnt.
313| Returns: Nothing.
314+--------------------------------------------------------------------------*/
315inline void loadTimerValue( unsigned short loadedValue )
316{
317  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
318  outport_byte(TIMER_CNTR0, loadedValue >> 0 & 0xff);
319  outport_byte(TIMER_CNTR0, loadedValue >> 8 & 0xff);
320}
321
322
323/*-------------------------------------------------------------------------+
324| Description: Waits until the counter on timer 0 reaches 0.
325| Returns: Nothing.
326+--------------------------------------------------------------------------*/
327inline void waitTimerStatus( void )
328{
329  unsigned char status;
330  outport_byte(TIMER_MODE, CMD_READ_BACK_STATUS); /* read Status counter 0 */
331  inport_byte(TIMER_CNTR0, status);
332  while (status & MSK_NULL_COUNT){      /* wait for counter ready */   
333    outport_byte(TIMER_MODE, CMD_READ_BACK_STATUS);
334    inport_byte(TIMER_CNTR0, status);
335  }
336}
337
338
339/*-------------------------------------------------------------------------+
340| Description: Reads the current value of the timer, and converts the
341|                          number of ticks to micro-seconds.   
342| Returns: current number of microseconds since last value loaded..
343+--------------------------------------------------------------------------*/
344inline unsigned short readCurrentTimer()
345{
346  unsigned short lsb, msb;
347  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
348  inport_byte(TIMER_CNTR0, lsb);
349  inport_byte(TIMER_CNTR0, msb);
350  return TICK_TO_US( ( msb << 8 ) | lsb );
351}
352
353
354/*-------------------------------------------------------------------------+
355 * clockbits - Read low order bits of timer 0 (the TOD clock)
356 * This works only for the 8254 chips used in ATs and 386s.
357 *
358 * The timer runs in mode 3 (square wave mode), counting down
359 * by twos, twice for each cycle. So it is necessary to read back the
360 * OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
361 * pin forms the most significant bit of the count. Unfortunately,
362 * the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
363 *
364 * The PC's clock design is soooo brain damaged...
365 *
366 * Rosimildo - I've got this routine from the KA9Q32 distribution and
367 *                         have updated it for the RTEMS environment.
368 +--------------------------------------------------------------------------*/
369unsigned int clockbits(void)
370{
371        unsigned int stat,count1, count;
372
373        do
374        {
375          outport_byte( 0x43, 0xc2 );    /* latch timer 0 count and status for reading */
376          inport_byte( 0x40, stat );     /* get status of timer 0 */
377          inport_byte( 0x40, count1 ); /* lsb of count */
378          inport_byte( 0x40, count ); /* msb of count */
379          count = count1 | ( count << 8 );
380        } while(stat & 0x40);            /* reread if NULL COUNT bit set */
381        stat = (stat & 0x80) << 8;       /* Shift OUTPUT to msb of 16-bit word */
382        if(count == 0)
383                return stat ^ 0x8000;   /* return complement of OUTPUT bit */
384        else
385                return count | stat;    /* Combine OUTPUT with counter */
386}
387
388
389/*-------------------------------------------------------------------------+
390|         Function: Calibrate_loop_1ms
391|      Description: Set loop variable to calibrate a 1ms loop
392| Global Variables: loop1ms
393|        Arguments: none
394|          Returns: Nothing.
395+--------------------------------------------------------------------------*/
396void
397Calibrate_loop_1ms(void)
398{
399  unsigned int i;
400  unsigned short loadedValue, offset;
401  unsigned int timerValue, t1_ref, t2_ref=0;
402  rtems_interrupt_level  level;
403 
404
405  printk( "Calibrate_loop_1ms is starting,  please wait ( but not too loooong. )\n" );
406 
407  loop1ms = 200;
408  timerValue = 0;
409
410  /* Let's load the timer with 2ms, initially */
411  loadedValue = US_TO_TICK( 2000 );
412 
413  rtems_interrupt_disable(level);
414  /*
415   * Compute the offset to apply due to read counter register
416   */
417  offset = 0;
418  loadTimerValue( loadedValue + offset );
419  waitTimerStatus();
420  t1_ref = clockbits();
421  offset = loadedValue - readCurrentTimer();
422  while( timerValue < 1000 )
423  {
424    loop1ms++;
425    loadTimerValue( loadedValue + offset );
426    waitTimerStatus();
427    t1_ref = clockbits();
428    for( i=0; i < loop1ms; i++ )
429       outport_byte( SLOW_DOWN_IO, 0 ); /* write is # 1us */
430    t2_ref = clockbits();
431    timerValue = TICK_TO_US( t1_ref - t2_ref );  /* timer0 is decrementing number of ticks  */
432  }
433  printk( "Calibrate_loop_1ms timerValue=%d, loop1ms=%d, t1_ref=%x, clockbits=%x, delta=%d\n",
434                   timerValue, loop1ms, t1_ref, t2_ref, t1_ref - t2_ref );
435  rtems_interrupt_enable(level);
436}
437
438
439
440/*-------------------------------------------------------------------------+
441|         Function: Wait_X_1ms
442|      Description: loop which waits at least timeToWait ms
443| Global Variables: loop1ms
444|        Arguments: timeToWait
445|          Returns: Nothing.
446+--------------------------------------------------------------------------*/
447void
448Wait_X_ms( unsigned int timeToWait){
449
450  unsigned int i, j;
451
452  for (j=0; j<timeToWait ; j++)
453    for (i=0; i<loop1ms; i++)
454      outport_byte(SLOW_DOWN_IO, 0);    /* write is # 1us */
455}
456
457
458
459
460
461
Note: See TracBrowser for help on using the repository browser.