source: rtems/c/src/lib/libbsp/i386/pc386/clock/ckinit.c @ 359e537

4.104.115
Last change on this file since 359e537 was 359e537, checked in by Ralf Corsepius <ralf.corsepius@…>, on 11/30/09 at 05:09:41

Whitespace removal.

  • Property mode set to 100644
File size: 8.3 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#define READ_8254( _lsb, _msb )                               \
51  do { outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);      \
52     inport_byte(TIMER_CNTR0, _lsb);                          \
53     inport_byte(TIMER_CNTR0, _msb);                          \
54  } while (0)
55
56
57/*
58 *  Hooks which get swapped based upon which nanoseconds since last
59 *  tick method is preferred.
60 */
61void     (*Clock_driver_support_at_tick)(void) = NULL;
62uint32_t (*Clock_driver_nanoseconds_since_last_tick)(void) = NULL;
63
64/*
65 *  What do we do at each clock tick?
66 */
67void Clock_driver_support_at_tick_tsc(void)
68{
69  pc586_tsc_at_tick = rdtsc();
70}
71
72void Clock_driver_support_at_tick_empty(void)
73{
74}
75
76#define Clock_driver_support_install_isr( _new, _old ) \
77  do { \
78  } while(0)
79
80extern volatile uint32_t Clock_driver_isrs;
81
82uint32_t bsp_clock_nanoseconds_since_last_tick_tsc(void)
83{
84  /******
85   * Get nanoseconds using Pentium-compatible TSC register
86   ******/
87
88  uint64_t                 diff_nsec;
89
90  diff_nsec = rdtsc() - pc586_tsc_at_tick;
91
92  /*
93   * At this point, with a hypothetical 10 GHz CPU clock and 100 Hz tick
94   * clock, diff_nsec <= 27 bits.
95   */
96  diff_nsec *= pc586_nanoseconds_per_tick; /* <= 54 bits */
97  diff_nsec /= pc586_tsc_per_tick;
98
99  if (diff_nsec > pc586_nanoseconds_per_tick)
100    /*
101     * Hmmm... Some drift or rounding. Pin the value to 1 nanosecond before
102     * the next tick.
103     */
104    /*    diff_nsec = pc586_nanoseconds_per_tick - 1; */
105    diff_nsec = 12345;
106
107  return (uint32_t)diff_nsec;
108}
109
110uint32_t bsp_clock_nanoseconds_since_last_tick_i8254(void)
111{
112
113  /******
114   * Get nanoseconds using 8254 timer chip
115   ******/
116
117  uint32_t                 usecs, clicks, isrs;
118  uint32_t                 usecs1, usecs2;
119  uint8_t                  lsb, msb;
120  rtems_interrupt_level    level;
121
122  /*
123   * Fetch all the data in an interrupt critical section.
124   */
125  rtems_interrupt_disable(level);
126    READ_8254(lsb, msb);
127    isrs = Clock_driver_isrs;
128  rtems_interrupt_enable(level);
129
130  /*
131   *  Now do the math
132   */
133  /* convert values read into counter clicks */
134  clicks = ((msb << 8) | lsb);
135
136  /* whole ISRs we have done since the last tick */
137  usecs1 = (pc386_isrs_per_tick - isrs - 1) * pc386_microseconds_per_isr;
138
139  /* the partial ISR we in the middle of now */
140  usecs2 = pc386_microseconds_per_isr - TICK_TO_US(clicks);
141
142  /* total microseconds */
143  usecs = usecs1 + usecs2;
144  #if 0
145    printk( "usecs1=%d usecs2=%d ", usecs1, usecs2 );
146    printk( "maxclicks=%d clicks=%d ISRs=%d ISRsper=%d usersPer=%d usecs=%d\n",
147    pc386_clock_click_count, clicks,
148    Clock_driver_isrs, pc386_isrs_per_tick,
149    pc386_microseconds_per_isr, usecs );
150  #endif
151
152  /* return it in nanoseconds */
153  return usecs * 1000;
154
155}
156
157/*
158 * Calibrate CPU cycles per tick. Interrupts should be disabled.
159 */
160static void calibrate_tsc(void)
161{
162  uint64_t              begin_time;
163  uint8_t               then_lsb, then_msb, now_lsb, now_msb;
164  uint32_t              i;
165
166  pc586_nanoseconds_per_tick =
167    rtems_configuration_get_microseconds_per_tick() * 1000;
168
169  /*
170   * We just reset the timer, so we know we're at the beginning of a tick.
171   */
172
173  /*
174   * Count cycles. Watching the timer introduces a several microsecond
175   * uncertaintity, so let it cook for a while and divide by the number of
176   * ticks actually executed.
177   */
178
179  begin_time = rdtsc();
180
181  for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick;
182       i != 0; --i ) {
183    /* We know we've just completed a tick when timer goes from low to high */
184    then_lsb = then_msb = 0xff;
185    do {
186      READ_8254(now_lsb, now_msb);
187      if ((then_msb < now_msb) ||
188          ((then_msb == now_msb) && (then_lsb < now_lsb)))
189        break;
190      then_lsb = now_lsb;
191      then_msb = now_msb;
192    } while (1);
193  }
194
195  pc586_tsc_per_tick = rdtsc() - begin_time;
196
197  /* Initialize "previous tick" counters */
198  pc586_tsc_at_tick = rdtsc();
199
200#if 0
201  printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_per_tick / 1000000));
202#endif
203
204  pc586_tsc_per_tick /= rtems_clock_get_ticks_per_second();
205}
206
207static void clockOn(
208  const rtems_irq_connect_data* unused
209)
210{
211  pc386_isrs_per_tick        = 1;
212  pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick();
213
214  while (US_TO_TICK(pc386_microseconds_per_isr) > 65535) {
215    pc386_isrs_per_tick  *= 10;
216    pc386_microseconds_per_isr /= 10;
217  }
218  pc386_clock_click_count = US_TO_TICK(pc386_microseconds_per_isr);
219
220  #if 0
221    printk( "configured usecs per tick=%d \n",
222      rtems_configuration_get_microseconds_per_tick() );
223    printk( "Microseconds per ISR =%d\n", pc386_microseconds_per_isr );
224    printk( "final ISRs per=%d\n", pc386_isrs_per_tick );
225    printk( "final timer counts=%d\n", pc386_clock_click_count );
226  #endif
227
228  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
229  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff);
230  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff);
231
232  /*
233   * Now calibrate cycles per tick. Do this every time we
234   * turn the clock on in case the CPU clock speed has changed.
235   */
236  if ( x86_has_tsc() )
237    calibrate_tsc();
238}
239
240void clockOff(const rtems_irq_connect_data* unused)
241{
242  /* reset timer mode to standard (BIOS) value */
243  outport_byte(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
244  outport_byte(TIMER_CNTR0, 0);
245  outport_byte(TIMER_CNTR0, 0);
246} /* Clock_exit */
247
248int clockIsOn(const rtems_irq_connect_data* unused)
249{
250  return ((i8259s_cache & 0x1) == 0);
251}
252
253/* a bit of a hack since the ISR models do not match */
254rtems_isr Clock_isr(
255  rtems_vector_number vector
256);
257static rtems_irq_connect_data clockIrqData = {
258  BSP_PERIODIC_TIMER,
259  (void *)Clock_isr,
260  0,
261  clockOn,
262  clockOff,
263  clockIsOn
264};
265
266void Clock_driver_support_initialize_hardware(void)
267{
268  bool use_tsc = false;
269  bool use_8254 = false;
270
271  #if (CLOCK_DRIVER_USE_TSC == 1)
272    use_tsc = true;
273  #endif
274
275  #if (CLOCK_DRIVER_USE_8254 == 1)
276    use_8254 = true;
277  #endif
278
279  if ( !use_tsc && !use_8254 ) {
280    if ( x86_has_tsc() ) use_tsc  = true;
281    else                 use_8254 = true;
282  }
283
284  if ( use_8254 ) {
285    /* printk( "Use 8254\n" ); */
286    Clock_driver_support_at_tick = Clock_driver_support_at_tick_empty;
287    Clock_driver_nanoseconds_since_last_tick =
288      bsp_clock_nanoseconds_since_last_tick_i8254;
289  } else {
290    /* printk( "Use TSC\n" ); */
291    Clock_driver_support_at_tick = Clock_driver_support_at_tick_tsc;
292    Clock_driver_nanoseconds_since_last_tick =
293      bsp_clock_nanoseconds_since_last_tick_tsc;
294  }
295
296  /* Shell installs nanosecond handler before calling
297   * Clock_driver_support_initialize_hardware() :-(
298   * so we do it again now that we're ready.
299   */
300  rtems_clock_set_nanoseconds_extension(
301    Clock_driver_nanoseconds_since_last_tick
302  );
303
304  if (!BSP_install_rtems_irq_handler (&clockIrqData)) {
305    printk("Unable to initialize system clock\n");
306    rtems_fatal_error_occurred(1);
307  }
308
309}
310
311#define Clock_driver_support_shutdown_hardware() \
312  do { \
313    BSP_remove_rtems_irq_handler (&clockIrqData); \
314  } while (0)
315
316#include "../../../shared/clockdrv_shell.h"
317
Note: See TracBrowser for help on using the repository browser.