source: rtems/bsps/i386/pc386/clock/ckinit.c @ 4925ab4

5
Last change on this file since 4925ab4 was 4925ab4, checked in by Jan Sommer <jan.sommer@…>, on 06/11/21 at 07:35:30

bsps/i386: Update calibration of TSC to be more accurate

Closes #4456

  • Property mode set to 100644
File size: 7.0 KB
RevLine 
[eb7c6a84]1/**
2 *  @file
3 *
[b62009c3]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.
[eb7c6a84]15 */
16
17/*
18 *  COPYRIGHT (c) 1989-2012.
[b62009c3]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
[c499856]23 *  http://www.rtems.org/license/LICENSE.
[b62009c3]24 */
[7150f00f]25
26#include <bsp.h>
[beefa112]27#include <bsp/irq-generic.h>
[b62009c3]28#include <bspopts.h>
[959f887a]29#include <libcpu/cpuModel.h>
[2bdcf4fd]30#include <assert.h>
[75acd9e]31#include <rtems/timecounter.h>
[1a971d8]32#ifdef RTEMS_SMP
33#include <rtems/score/smpimpl.h>
34#endif
[7150f00f]35
[b62009c3]36#define CLOCK_VECTOR 0
[7150f00f]37
[b62009c3]38volatile uint32_t pc386_microseconds_per_isr;
39volatile uint32_t pc386_isrs_per_tick;
40uint32_t pc386_clock_click_count;
[7150f00f]41
[eb7c6a84]42/* forward declaration */
43void Clock_isr(void *param);
[2bdcf4fd]44static void Clock_isr_handler(void *param);
[eb7c6a84]45
[959f887a]46/*
[75acd9e]47 * Roughly the number of cycles per second. Note that these
[959f887a]48 * will be wildly inaccurate if the chip speed changes due to power saving
49 * or thermal modes.
50 *
51 * NOTE: These are only used when the TSC method is used.
52 */
[75acd9e]53static uint64_t pc586_tsc_frequency;
[7150f00f]54
[75acd9e]55static struct timecounter pc386_tc;
[7150f00f]56
[959f887a]57/* this driver may need to count ISRs per tick */
[3109857c]58#define CLOCK_DRIVER_ISRS_PER_TICK       1
59#define CLOCK_DRIVER_ISRS_PER_TICK_VALUE pc386_isrs_per_tick
[7150f00f]60
[75acd9e]61extern volatile uint32_t Clock_driver_ticks;
[ee07b997]62
[959f887a]63#define READ_8254( _lsb, _msb )                               \
64  do { outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);      \
65     inport_byte(TIMER_CNTR0, _lsb);                          \
66     inport_byte(TIMER_CNTR0, _msb);                          \
67  } while (0)
68
69
[90d8567]70#ifdef RTEMS_SMP
71#define Clock_driver_support_at_tick() \
[1a971d8]72  do {                                                              \
73    Processor_mask targets;                                         \
74    _Processor_mask_Assign(&targets, _SMP_Get_online_processors()); \
75    _Processor_mask_Clear(&targets, _SMP_Get_current_processor());  \
[c954003]76    _SMP_Multicast_action(&targets, rtems_timecounter_tick, NULL);               \
[1a971d8]77  } while (0)
[90d8567]78#endif
[7150f00f]79
[75acd9e]80static uint32_t pc386_get_timecount_tsc(struct timecounter *tc)
[67a2288]81{
[75acd9e]82  return (uint32_t)rdtsc();
[959f887a]83}
[359e537]84
[75acd9e]85static uint32_t pc386_get_timecount_i8254(struct timecounter *tc)
[959f887a]86{
[75acd9e]87  uint32_t                 irqs;
[b62009c3]88  uint8_t                  lsb, msb;
[6b54dcb]89  rtems_interrupt_lock_context lock_context;
[b62009c3]90
91  /*
92   * Fetch all the data in an interrupt critical section.
93   */
[6b54dcb]94
95  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
96
[959f887a]97    READ_8254(lsb, msb);
[75acd9e]98    irqs = Clock_driver_ticks;
[6b54dcb]99
100  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
[b62009c3]101
[75acd9e]102  return (irqs + 1) * pc386_microseconds_per_isr - ((msb << 8) | lsb);
[67a2288]103}
104
[959f887a]105/*
106 * Calibrate CPU cycles per tick. Interrupts should be disabled.
[4925ab4]107 * Will also set the PIT, so call this before registering the
108 * periodic timer for rtems tick generation
[959f887a]109 */
110static void calibrate_tsc(void)
111{
112  uint64_t              begin_time;
[4925ab4]113  uint8_t               lsb, msb;
114  uint32_t              max_timer_value;
115  uint32_t              last_tick, cur_tick;
116  int32_t               diff, remaining;
117
118  /* Set the timer to free running mode */
119  outport_byte(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_INTTC);
120  /* Reset the 16 timer reload value, first LSB, then MSB */
121  outport_byte(TIMER_CNTR0, 0);
122  outport_byte(TIMER_CNTR0, 0);
123  /* We use the full 16 bit */
124  max_timer_value = 0xffff;
125  /* Calibrate for 1s, i.e. TIMER_TICK PIT ticks */
126  remaining = TIMER_TICK;
[959f887a]127
128  begin_time = rdtsc();
[4925ab4]129  READ_8254(lsb, msb);
130  last_tick = (msb << 8) | lsb;
131  while(remaining > 0) {
132    READ_8254(lsb, msb);
133    cur_tick = (msb << 8) | lsb;
134    /* PIT counts down, so subtract cur from last */
135    diff = last_tick - cur_tick;
136    last_tick = cur_tick;
137    if (diff < 0) {
138        diff += max_timer_value;
139    }
140    remaining -= diff;
[959f887a]141  }
142
[75acd9e]143  pc586_tsc_frequency = rdtsc() - begin_time;
[959f887a]144
[88ef1655]145#if 0
[4925ab4]146  printk( "CPU clock at %u Hz\n", (uint32_t)(pc586_tsc_frequency ));
[959f887a]147#endif
148}
[b62009c3]149
[2bdcf4fd]150static void clockOn(void)
[7150f00f]151{
[4925ab4]152
153  /*
154   * First calibrate the TSC. Do this every time we
155   * turn the clock on in case the CPU clock speed has changed.
156   */
157  if ( x86_has_tsc() ) {
158    calibrate_tsc();
159  }
160
[6b54dcb]161  rtems_interrupt_lock_context lock_context;
[b62009c3]162  pc386_isrs_per_tick        = 1;
163  pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick();
[67a2288]164
[b62009c3]165  while (US_TO_TICK(pc386_microseconds_per_isr) > 65535) {
166    pc386_isrs_per_tick  *= 10;
167    pc386_microseconds_per_isr /= 10;
[67a2288]168  }
[b62009c3]169  pc386_clock_click_count = US_TO_TICK(pc386_microseconds_per_isr);
170
171  #if 0
[359e537]172    printk( "configured usecs per tick=%d \n",
[b62009c3]173      rtems_configuration_get_microseconds_per_tick() );
174    printk( "Microseconds per ISR =%d\n", pc386_microseconds_per_isr );
175    printk( "final ISRs per=%d\n", pc386_isrs_per_tick );
176    printk( "final timer counts=%d\n", pc386_clock_click_count );
177  #endif
178
[6b54dcb]179  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
[b62009c3]180  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
181  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff);
182  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff);
[6b54dcb]183  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
184
[2220a53]185  bsp_interrupt_vector_enable( BSP_PERIODIC_TIMER );
[b62009c3]186}
[7150f00f]187
[8a7ed82]188bool Clock_isr_enabled = false;
[2bdcf4fd]189static void Clock_isr_handler(void *param)
[8a7ed82]190{
191  if ( Clock_isr_enabled )
[eb7c6a84]192    Clock_isr( param );
[8a7ed82]193}
194
195void Clock_driver_install_handler(void)
196{
[2bdcf4fd]197  rtems_status_code status;
198
199  status = rtems_interrupt_handler_install(
200    BSP_PERIODIC_TIMER,
201    "ckinit",
202    RTEMS_INTERRUPT_UNIQUE,
203    Clock_isr_handler,
204    NULL
205  );
206  assert(status == RTEMS_SUCCESSFUL);
207  clockOn();
[8a7ed82]208}
209
[90d8567]210#define Clock_driver_support_set_interrupt_affinity(online_processors) \
211  do { \
212    /* FIXME: Is there a way to do this on x86? */ \
213    (void) online_processors; \
214  } while (0)
215
[88ef1655]216void Clock_driver_support_initialize_hardware(void)
[959f887a]217{
218  bool use_tsc = false;
219  bool use_8254 = false;
[359e537]220
[959f887a]221  #if (CLOCK_DRIVER_USE_TSC == 1)
222    use_tsc = true;
223  #endif
224
225  #if (CLOCK_DRIVER_USE_8254 == 1)
226    use_8254 = true;
227  #endif
[359e537]228
[959f887a]229  if ( !use_tsc && !use_8254 ) {
230    if ( x86_has_tsc() ) use_tsc  = true;
231    else                 use_8254 = true;
232  }
233
234  if ( use_8254 ) {
[88ef1655]235    /* printk( "Use 8254\n" ); */
[75acd9e]236    pc386_tc.tc_get_timecount = pc386_get_timecount_i8254;
237    pc386_tc.tc_counter_mask = 0xffffffff;
238    pc386_tc.tc_frequency = TIMER_TICK;
[959f887a]239  } else {
[88ef1655]240    /* printk( "Use TSC\n" ); */
[75acd9e]241    pc386_tc.tc_get_timecount = pc386_get_timecount_tsc;
242    pc386_tc.tc_counter_mask = 0xffffffff;
243    pc386_tc.tc_frequency = pc586_tsc_frequency;
[959f887a]244  }
245
[75acd9e]246  pc386_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
247  rtems_timecounter_install(&pc386_tc);
[8a7ed82]248  Clock_isr_enabled = true;
[959f887a]249}
[b62009c3]250
[7632906]251#include "../../../shared/dev/clock/clockimpl.h"
Note: See TracBrowser for help on using the repository browser.