source: rtems/c/src/lib/libbsp/i386/pc386/clock/ckinit.c @ 8a7ed82

4.115
Last change on this file since 8a7ed82 was 8a7ed82, checked in by Jennifer Averett <Jennifer.Averett@…>, on 08/01/11 at 13:41:37

2011-08-01 Jennifer Averett <Jennifer.Averett@…>

PR 1802

  • Makefile.am, configure.ac, preinstall.am, clock/ckinit.c, start/start16.S, startup/bspstart.c, startup/ldsegs.S: Add SMP support for i386.
  • Property mode set to 100644
File size: 9.0 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  BSP_irq_enable_at_i8259s( BSP_PERIODIC_TIMER - BSP_IRQ_VECTOR_BASE );
238
239  #if 0
240    printk( "configured usecs per tick=%d \n",
241      rtems_configuration_get_microseconds_per_tick() );
242    printk( "Microseconds per ISR =%d\n", pc386_microseconds_per_isr );
243    printk( "final ISRs per=%d\n", pc386_isrs_per_tick );
244    printk( "final timer counts=%d\n", pc386_clock_click_count );
245  #endif
246
247  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
248  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff);
249  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff);
250
251  /*
252   * Now calibrate cycles per tick. Do this every time we
253   * turn the clock on in case the CPU clock speed has changed.
254   */
255  if ( x86_has_tsc() )
256    calibrate_tsc();
257}
258
259void clockOff(const rtems_irq_connect_data* unused)
260{
261  /* reset timer mode to standard (BIOS) value */
262  outport_byte(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
263  outport_byte(TIMER_CNTR0, 0);
264  outport_byte(TIMER_CNTR0, 0);
265} /* Clock_exit */
266
267int clockIsOn(const rtems_irq_connect_data* unused)
268{
269  return ((i8259s_cache & 0x1) == 0);
270}
271
272/* a bit of a hack since the ISR models do not match */
273rtems_isr Clock_isr(
274  rtems_vector_number vector
275);
276
277bool Clock_isr_enabled = false;
278void Clock_isr_handler(
279  rtems_irq_hdl_param param
280)
281{
282  if ( Clock_isr_enabled )
283    Clock_isr( 0 );
284}
285
286static rtems_irq_connect_data clockIrqData = {
287  BSP_PERIODIC_TIMER,
288  Clock_isr_handler,
289  0,
290  clockOn,
291  clockOff,
292  clockIsOn
293};
294
295void Clock_driver_install_handler(void)
296{
297  if (!BSP_install_rtems_irq_handler (&clockIrqData)) {
298    printk("Unable to install system clock ISR handler\n");
299    rtems_fatal_error_occurred(1);
300  }
301}
302
303void Clock_driver_support_initialize_hardware(void)
304{
305  bool use_tsc = false;
306  bool use_8254 = false;
307
308  #if (CLOCK_DRIVER_USE_TSC == 1)
309    use_tsc = true;
310  #endif
311
312  #if (CLOCK_DRIVER_USE_8254 == 1)
313    use_8254 = true;
314  #endif
315
316  if ( !use_tsc && !use_8254 ) {
317    if ( x86_has_tsc() ) use_tsc  = true;
318    else                 use_8254 = true;
319  }
320
321  if ( use_8254 ) {
322    /* printk( "Use 8254\n" ); */
323    Clock_driver_support_at_tick = Clock_driver_support_at_tick_empty;
324    Clock_driver_nanoseconds_since_last_tick =
325      bsp_clock_nanoseconds_since_last_tick_i8254;
326  } else {
327    /* printk( "Use TSC\n" ); */
328    Clock_driver_support_at_tick = Clock_driver_support_at_tick_tsc;
329    Clock_driver_nanoseconds_since_last_tick =
330      bsp_clock_nanoseconds_since_last_tick_tsc;
331  }
332
333  /* Shell installs nanosecond handler before calling
334   * Clock_driver_support_initialize_hardware() :-(
335   * so we do it again now that we're ready.
336   */
337  rtems_clock_set_nanoseconds_extension(
338    Clock_driver_nanoseconds_since_last_tick
339  );
340
341  Clock_isr_enabled = true;
342}
343
344#define Clock_driver_support_shutdown_hardware() \
345  do { \
346    BSP_remove_rtems_irq_handler (&clockIrqData); \
347  } while (0)
348
349#include "../../../shared/clockdrv_shell.h"
350
Note: See TracBrowser for help on using the repository browser.