source: rtems/c/src/lib/libbsp/i386/pc386/clock/ckinit.c @ 0a7fb3c

4.104.115
Last change on this file since 0a7fb3c was 0a7fb3c, checked in by Joel Sherrill <joel.sherrill@…>, on 12/11/09 at 20:54:30

2009-12-11 Joel Sherrill <joel.sherrill@…>

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