source: rtems/c/src/lib/libbsp/i386/pc386/clock/ckinit.c @ 864dd9ad

4.115
Last change on this file since 864dd9ad was ee07b997, checked in by Joel Sherrill <joel.sherrill@…>, on 03/10/10 at 17:16:02

2010-03-10 Gedare Bloom <gedare@…>

PR 1495/bsp

  • clock/ckinit.c, make/custom/pc386.cfg: Calling rtems_clock_get_uptime() in a tight loop sometimes showed time moving backwards.
  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*
2 *  Clock Tick Device Driver
3 *
4 *  History:
5 *    + Original driver was go32 clock by Joel Sherrill
6 *    + go32 clock driver hardware code was inserted into new
7 *      boilerplate when the pc386 BSP by:
8 *        Pedro Miguel Da Cruz Neto Romano <pmcnr@camoes.rnl.ist.utl.pt>
9 *        Jose Rufino <ruf@asterix.ist.utl.pt>
10 *    + Reworked by Joel Sherrill to use clock driver template.
11 *      This removes all boilerplate and leave original hardware
12 *      code I developed for the go32 BSP.
13 *
14 *  COPYRIGHT (c) 1989-2008.
15 *  On-Line Applications Research Corporation (OAR).
16 *
17 *  The license and distribution terms for this file may be
18 *  found in the file LICENSE in this distribution or at
19 *  http://www.rtems.com/license/LICENSE.
20 *
21 *  $Id$
22 */
23
24#include <bsp.h>
25#include <bsp/irq.h>
26#include <bspopts.h>
27#include <libcpu/cpuModel.h>
28
29#define CLOCK_VECTOR 0
30
31volatile uint32_t pc386_microseconds_per_isr;
32volatile uint32_t pc386_isrs_per_tick;
33uint32_t pc386_clock_click_count;
34
35/*
36 * Roughly the number of cycles per tick and per nanosecond. Note that these
37 * will be wildly inaccurate if the chip speed changes due to power saving
38 * or thermal modes.
39 *
40 * NOTE: These are only used when the TSC method is used.
41 */
42uint64_t pc586_tsc_per_tick;
43uint64_t pc586_nanoseconds_per_tick;
44
45uint64_t pc586_tsc_at_tick;
46
47/* this driver may need to count ISRs per tick */
48#define CLOCK_DRIVER_ISRS_PER_TICK pc386_isrs_per_tick
49
50/* if so, the driver may use the count in Clock_driver_support_at_tick */
51#ifdef CLOCK_DRIVER_ISRS_PER_TICK
52extern volatile uint32_t Clock_driver_isrs;
53#endif
54
55#define READ_8254( _lsb, _msb )                               \
56  do { outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);      \
57     inport_byte(TIMER_CNTR0, _lsb);                          \
58     inport_byte(TIMER_CNTR0, _msb);                          \
59  } while (0)
60
61
62/*
63 *  Hooks which get swapped based upon which nanoseconds since last
64 *  tick method is preferred.
65 */
66void     (*Clock_driver_support_at_tick)(void) = NULL;
67uint32_t (*Clock_driver_nanoseconds_since_last_tick)(void) = NULL;
68
69/*
70 *  What do we do at each clock tick?
71 */
72void Clock_driver_support_at_tick_tsc(void)
73{
74#ifdef CLOCK_DRIVER_ISRS_PER_TICK
75  /*
76   *  The driver is multiple ISRs per clock tick.
77  */
78  if (!Clock_driver_isrs)
79    pc586_tsc_at_tick = rdtsc();
80#else
81  /*
82   *  The driver is one ISR per clock tick.
83   */
84  pc586_tsc_at_tick = rdtsc();
85#endif
86}
87
88void Clock_driver_support_at_tick_empty(void)
89{
90}
91
92#define Clock_driver_support_install_isr( _new, _old ) \
93  do { \
94    _old = NULL; \
95  } while(0)
96
97extern volatile uint32_t Clock_driver_isrs;
98
99uint32_t bsp_clock_nanoseconds_since_last_tick_tsc(void)
100{
101  /******
102   * Get nanoseconds using Pentium-compatible TSC register
103   ******/
104
105  uint64_t                 diff_nsec;
106
107  diff_nsec = rdtsc() - pc586_tsc_at_tick;
108
109  /*
110   * At this point, with a hypothetical 10 GHz CPU clock and 100 Hz tick
111   * clock, diff_nsec <= 27 bits.
112   */
113  diff_nsec *= pc586_nanoseconds_per_tick; /* <= 54 bits */
114  diff_nsec /= pc586_tsc_per_tick;
115
116  if (diff_nsec > pc586_nanoseconds_per_tick)
117    /*
118     * Hmmm... Some drift or rounding. Pin the value to 1 nanosecond before
119     * the next tick.
120     */
121    /*    diff_nsec = pc586_nanoseconds_per_tick - 1; */
122    diff_nsec = 12345;
123
124  return (uint32_t)diff_nsec;
125}
126
127uint32_t bsp_clock_nanoseconds_since_last_tick_i8254(void)
128{
129
130  /******
131   * Get nanoseconds using 8254 timer chip
132   ******/
133
134  uint32_t                 usecs, clicks, isrs;
135  uint32_t                 usecs1, usecs2;
136  uint8_t                  lsb, msb;
137  rtems_interrupt_level    level;
138
139  /*
140   * Fetch all the data in an interrupt critical section.
141   */
142  rtems_interrupt_disable(level);
143    READ_8254(lsb, msb);
144    isrs = Clock_driver_isrs;
145  rtems_interrupt_enable(level);
146
147  /*
148   *  Now do the math
149   */
150  /* convert values read into counter clicks */
151  clicks = ((msb << 8) | lsb);
152
153  /* whole ISRs we have done since the last tick */
154  usecs1 = (pc386_isrs_per_tick - isrs - 1) * pc386_microseconds_per_isr;
155
156  /* the partial ISR we in the middle of now */
157  usecs2 = pc386_microseconds_per_isr - TICK_TO_US(clicks);
158
159  /* total microseconds */
160  usecs = usecs1 + usecs2;
161  #if 0
162    printk( "usecs1=%d usecs2=%d ", usecs1, usecs2 );
163    printk( "maxclicks=%d clicks=%d ISRs=%d ISRsper=%d usersPer=%d usecs=%d\n",
164    pc386_clock_click_count, clicks,
165    Clock_driver_isrs, pc386_isrs_per_tick,
166    pc386_microseconds_per_isr, usecs );
167  #endif
168
169  /* return it in nanoseconds */
170  return usecs * 1000;
171
172}
173
174/*
175 * Calibrate CPU cycles per tick. Interrupts should be disabled.
176 */
177static void calibrate_tsc(void)
178{
179  uint64_t              begin_time;
180  uint8_t               then_lsb, then_msb, now_lsb, now_msb;
181  uint32_t              i;
182
183  pc586_nanoseconds_per_tick =
184    rtems_configuration_get_microseconds_per_tick() * 1000;
185
186  /*
187   * We just reset the timer, so we know we're at the beginning of a tick.
188   */
189
190  /*
191   * Count cycles. Watching the timer introduces a several microsecond
192   * uncertaintity, so let it cook for a while and divide by the number of
193   * ticks actually executed.
194   */
195
196  begin_time = rdtsc();
197
198  for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick;
199       i != 0; --i ) {
200    /* We know we've just completed a tick when timer goes from low to high */
201    then_lsb = then_msb = 0xff;
202    do {
203      READ_8254(now_lsb, now_msb);
204      if ((then_msb < now_msb) ||
205          ((then_msb == now_msb) && (then_lsb < now_lsb)))
206        break;
207      then_lsb = now_lsb;
208      then_msb = now_msb;
209    } while (1);
210  }
211
212  pc586_tsc_per_tick = rdtsc() - begin_time;
213
214  /* Initialize "previous tick" counters */
215  pc586_tsc_at_tick = rdtsc();
216
217#if 0
218  printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_per_tick / 1000000));
219#endif
220
221  pc586_tsc_per_tick /= rtems_clock_get_ticks_per_second();
222}
223
224static void clockOn(
225  const rtems_irq_connect_data* unused
226)
227{
228  pc386_isrs_per_tick        = 1;
229  pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick();
230
231  while (US_TO_TICK(pc386_microseconds_per_isr) > 65535) {
232    pc386_isrs_per_tick  *= 10;
233    pc386_microseconds_per_isr /= 10;
234  }
235  pc386_clock_click_count = US_TO_TICK(pc386_microseconds_per_isr);
236
237  #if 0
238    printk( "configured usecs per tick=%d \n",
239      rtems_configuration_get_microseconds_per_tick() );
240    printk( "Microseconds per ISR =%d\n", pc386_microseconds_per_isr );
241    printk( "final ISRs per=%d\n", pc386_isrs_per_tick );
242    printk( "final timer counts=%d\n", pc386_clock_click_count );
243  #endif
244
245  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
246  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff);
247  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff);
248
249  /*
250   * Now calibrate cycles per tick. Do this every time we
251   * turn the clock on in case the CPU clock speed has changed.
252   */
253  if ( x86_has_tsc() )
254    calibrate_tsc();
255}
256
257void clockOff(const rtems_irq_connect_data* unused)
258{
259  /* reset timer mode to standard (BIOS) value */
260  outport_byte(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
261  outport_byte(TIMER_CNTR0, 0);
262  outport_byte(TIMER_CNTR0, 0);
263} /* Clock_exit */
264
265int clockIsOn(const rtems_irq_connect_data* unused)
266{
267  return ((i8259s_cache & 0x1) == 0);
268}
269
270/* a bit of a hack since the ISR models do not match */
271rtems_isr Clock_isr(
272  rtems_vector_number vector
273);
274static rtems_irq_connect_data clockIrqData = {
275  BSP_PERIODIC_TIMER,
276  (void *)Clock_isr,
277  0,
278  clockOn,
279  clockOff,
280  clockIsOn
281};
282
283void Clock_driver_support_initialize_hardware(void)
284{
285  bool use_tsc = false;
286  bool use_8254 = false;
287
288  #if (CLOCK_DRIVER_USE_TSC == 1)
289    use_tsc = true;
290  #endif
291
292  #if (CLOCK_DRIVER_USE_8254 == 1)
293    use_8254 = true;
294  #endif
295
296  if ( !use_tsc && !use_8254 ) {
297    if ( x86_has_tsc() ) use_tsc  = true;
298    else                 use_8254 = true;
299  }
300
301  if ( use_8254 ) {
302    /* printk( "Use 8254\n" ); */
303    Clock_driver_support_at_tick = Clock_driver_support_at_tick_empty;
304    Clock_driver_nanoseconds_since_last_tick =
305      bsp_clock_nanoseconds_since_last_tick_i8254;
306  } else {
307    /* printk( "Use TSC\n" ); */
308    Clock_driver_support_at_tick = Clock_driver_support_at_tick_tsc;
309    Clock_driver_nanoseconds_since_last_tick =
310      bsp_clock_nanoseconds_since_last_tick_tsc;
311  }
312
313  /* Shell installs nanosecond handler before calling
314   * Clock_driver_support_initialize_hardware() :-(
315   * so we do it again now that we're ready.
316   */
317  rtems_clock_set_nanoseconds_extension(
318    Clock_driver_nanoseconds_since_last_tick
319  );
320
321  if (!BSP_install_rtems_irq_handler (&clockIrqData)) {
322    printk("Unable to initialize system clock\n");
323    rtems_fatal_error_occurred(1);
324  }
325
326}
327
328#define Clock_driver_support_shutdown_hardware() \
329  do { \
330    BSP_remove_rtems_irq_handler (&clockIrqData); \
331  } while (0)
332
333#include "../../../shared/clockdrv_shell.h"
334
Note: See TracBrowser for help on using the repository browser.