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

4.115
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

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