source: rtems/c/src/lib/libbsp/i386/pc386/clock/ckinit.c @ 441b90e

4.115
Last change on this file since 441b90e 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
RevLine 
[b62009c3]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 */
[7150f00f]23
24#include <bsp.h>
[6daada6]25#include <bsp/irq.h>
[b62009c3]26#include <bspopts.h>
[959f887a]27#include <libcpu/cpuModel.h>
[7150f00f]28
[b62009c3]29#define CLOCK_VECTOR 0
[7150f00f]30
[b62009c3]31volatile uint32_t pc386_microseconds_per_isr;
32volatile uint32_t pc386_isrs_per_tick;
33uint32_t pc386_clock_click_count;
[7150f00f]34
[959f887a]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;
[7150f00f]44
[959f887a]45uint64_t pc586_tsc_at_tick;
[7150f00f]46
[959f887a]47/* this driver may need to count ISRs per tick */
[b62009c3]48#define CLOCK_DRIVER_ISRS_PER_TICK pc386_isrs_per_tick
[7150f00f]49
[ee07b997]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
[959f887a]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{
[ee07b997]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   */
[959f887a]84  pc586_tsc_at_tick = rdtsc();
[ee07b997]85#endif
[959f887a]86}
87
88void Clock_driver_support_at_tick_empty(void)
89{
90}
[7150f00f]91
[b62009c3]92#define Clock_driver_support_install_isr( _new, _old ) \
93  do { \
[0a7fb3c]94    _old = NULL; \
[b62009c3]95  } while(0)
[7150f00f]96
[e3a1d425]97extern volatile uint32_t Clock_driver_isrs;
98
[959f887a]99uint32_t bsp_clock_nanoseconds_since_last_tick_tsc(void)
[67a2288]100{
[959f887a]101  /******
102   * Get nanoseconds using Pentium-compatible TSC register
103   ******/
[359e537]104
[959f887a]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}
[359e537]126
[959f887a]127uint32_t bsp_clock_nanoseconds_since_last_tick_i8254(void)
128{
129
130  /******
131   * Get nanoseconds using 8254 timer chip
132   ******/
133
[b62009c3]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);
[959f887a]143    READ_8254(lsb, msb);
[b62009c3]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;
[959f887a]171
[67a2288]172}
173
[959f887a]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;
[359e537]199       i != 0; --i ) {
[959f887a]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
[88ef1655]217#if 0
[959f887a]218  printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_per_tick / 1000000));
219#endif
[359e537]220
[959f887a]221  pc586_tsc_per_tick /= rtems_clock_get_ticks_per_second();
222}
[b62009c3]223
224static void clockOn(
225  const rtems_irq_connect_data* unused
226)
[7150f00f]227{
[b62009c3]228  pc386_isrs_per_tick        = 1;
229  pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick();
[67a2288]230
[b62009c3]231  while (US_TO_TICK(pc386_microseconds_per_isr) > 65535) {
232    pc386_isrs_per_tick  *= 10;
233    pc386_microseconds_per_isr /= 10;
[67a2288]234  }
[b62009c3]235  pc386_clock_click_count = US_TO_TICK(pc386_microseconds_per_isr);
236
[8a7ed82]237  BSP_irq_enable_at_i8259s( BSP_PERIODIC_TIMER - BSP_IRQ_VECTOR_BASE );
238
[b62009c3]239  #if 0
[359e537]240    printk( "configured usecs per tick=%d \n",
[b62009c3]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);
[959f887a]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();
[b62009c3]257}
[7150f00f]258
[b62009c3]259void clockOff(const rtems_irq_connect_data* unused)
[7150f00f]260{
[b62009c3]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 */
[7150f00f]266
[b62009c3]267int clockIsOn(const rtems_irq_connect_data* unused)
[67a2288]268{
[b62009c3]269  return ((i8259s_cache & 0x1) == 0);
[67a2288]270}
[7150f00f]271
[b62009c3]272/* a bit of a hack since the ISR models do not match */
273rtems_isr Clock_isr(
274  rtems_vector_number vector
275);
[8a7ed82]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
[b62009c3]286static rtems_irq_connect_data clockIrqData = {
287  BSP_PERIODIC_TIMER,
[8a7ed82]288  Clock_isr_handler,
[b62009c3]289  0,
290  clockOn,
291  clockOff,
292  clockIsOn
293};
294
[8a7ed82]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
[88ef1655]303void Clock_driver_support_initialize_hardware(void)
[959f887a]304{
305  bool use_tsc = false;
306  bool use_8254 = false;
[359e537]307
[959f887a]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
[359e537]315
[959f887a]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 ) {
[88ef1655]322    /* printk( "Use 8254\n" ); */
[959f887a]323    Clock_driver_support_at_tick = Clock_driver_support_at_tick_empty;
[359e537]324    Clock_driver_nanoseconds_since_last_tick =
[446a1e81]325      bsp_clock_nanoseconds_since_last_tick_i8254;
[959f887a]326  } else {
[88ef1655]327    /* printk( "Use TSC\n" ); */
[959f887a]328    Clock_driver_support_at_tick = Clock_driver_support_at_tick_tsc;
[359e537]329    Clock_driver_nanoseconds_since_last_tick =
[446a1e81]330      bsp_clock_nanoseconds_since_last_tick_tsc;
[959f887a]331  }
332
[446a1e81]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
[8a7ed82]341  Clock_isr_enabled = true;
[959f887a]342}
[b62009c3]343
344#define Clock_driver_support_shutdown_hardware() \
345  do { \
346    BSP_remove_rtems_irq_handler (&clockIrqData); \
347  } while (0)
348
[05d1c82]349#include "../../../shared/clockdrv_shell.h"
[b62009c3]350
Note: See TracBrowser for help on using the repository browser.