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

5
Last change on this file since 6b54dcb was 6b54dcb, checked in by Pavel Pisa <pisa@…>, on 10/12/16 at 07:40:41

bsps/i386: replace global interrupt disable by SMP build supporting locking.

  • Property mode set to 100644
File size: 7.3 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>
[7150f00f]32
[b62009c3]33#define CLOCK_VECTOR 0
[7150f00f]34
[b62009c3]35volatile uint32_t pc386_microseconds_per_isr;
36volatile uint32_t pc386_isrs_per_tick;
37uint32_t pc386_clock_click_count;
[7150f00f]38
[eb7c6a84]39/* forward declaration */
40void Clock_isr(void *param);
[2bdcf4fd]41static void clockOff(void);
42static void Clock_isr_handler(void *param);
[eb7c6a84]43
[959f887a]44/*
[75acd9e]45 * Roughly the number of cycles per second. Note that these
[959f887a]46 * will be wildly inaccurate if the chip speed changes due to power saving
47 * or thermal modes.
48 *
49 * NOTE: These are only used when the TSC method is used.
50 */
[75acd9e]51static uint64_t pc586_tsc_frequency;
[7150f00f]52
[75acd9e]53static struct timecounter pc386_tc;
[7150f00f]54
[959f887a]55/* this driver may need to count ISRs per tick */
[3109857c]56#define CLOCK_DRIVER_ISRS_PER_TICK       1
57#define CLOCK_DRIVER_ISRS_PER_TICK_VALUE pc386_isrs_per_tick
[7150f00f]58
[75acd9e]59extern volatile uint32_t Clock_driver_ticks;
[ee07b997]60
[959f887a]61#define READ_8254( _lsb, _msb )                               \
62  do { outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);      \
63     inport_byte(TIMER_CNTR0, _lsb);                          \
64     inport_byte(TIMER_CNTR0, _msb);                          \
65  } while (0)
66
67
[90d8567]68#ifdef RTEMS_SMP
69#define Clock_driver_support_at_tick() \
70  _SMP_Send_message_broadcast(SMP_MESSAGE_CLOCK_TICK)
71#endif
[7150f00f]72
[b62009c3]73#define Clock_driver_support_install_isr( _new, _old ) \
74  do { \
[0a7fb3c]75    _old = NULL; \
[b62009c3]76  } while(0)
[7150f00f]77
[75acd9e]78static uint32_t pc386_get_timecount_tsc(struct timecounter *tc)
[67a2288]79{
[75acd9e]80  return (uint32_t)rdtsc();
[959f887a]81}
[359e537]82
[75acd9e]83static uint32_t pc386_get_timecount_i8254(struct timecounter *tc)
[959f887a]84{
[75acd9e]85  uint32_t                 irqs;
[b62009c3]86  uint8_t                  lsb, msb;
[6b54dcb]87  rtems_interrupt_lock_context lock_context;
[b62009c3]88
89  /*
90   * Fetch all the data in an interrupt critical section.
91   */
[6b54dcb]92
93  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
94
[959f887a]95    READ_8254(lsb, msb);
[75acd9e]96    irqs = Clock_driver_ticks;
[6b54dcb]97
98  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
[b62009c3]99
[75acd9e]100  return (irqs + 1) * pc386_microseconds_per_isr - ((msb << 8) | lsb);
[67a2288]101}
102
[959f887a]103/*
104 * Calibrate CPU cycles per tick. Interrupts should be disabled.
105 */
106static void calibrate_tsc(void)
107{
108  uint64_t              begin_time;
109  uint8_t               then_lsb, then_msb, now_lsb, now_msb;
110  uint32_t              i;
111
112  /*
113   * We just reset the timer, so we know we're at the beginning of a tick.
114   */
115
116  /*
117   * Count cycles. Watching the timer introduces a several microsecond
118   * uncertaintity, so let it cook for a while and divide by the number of
119   * ticks actually executed.
120   */
121
122  begin_time = rdtsc();
123
124  for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick;
[359e537]125       i != 0; --i ) {
[959f887a]126    /* We know we've just completed a tick when timer goes from low to high */
127    then_lsb = then_msb = 0xff;
128    do {
129      READ_8254(now_lsb, now_msb);
130      if ((then_msb < now_msb) ||
131          ((then_msb == now_msb) && (then_lsb < now_lsb)))
132        break;
133      then_lsb = now_lsb;
134      then_msb = now_msb;
135    } while (1);
136  }
137
[75acd9e]138  pc586_tsc_frequency = rdtsc() - begin_time;
[959f887a]139
[88ef1655]140#if 0
[75acd9e]141  printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_frequency / 1000000));
[959f887a]142#endif
143}
[b62009c3]144
[2bdcf4fd]145static void clockOn(void)
[7150f00f]146{
[6b54dcb]147  rtems_interrupt_lock_context lock_context;
[b62009c3]148  pc386_isrs_per_tick        = 1;
149  pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick();
[67a2288]150
[b62009c3]151  while (US_TO_TICK(pc386_microseconds_per_isr) > 65535) {
152    pc386_isrs_per_tick  *= 10;
153    pc386_microseconds_per_isr /= 10;
[67a2288]154  }
[b62009c3]155  pc386_clock_click_count = US_TO_TICK(pc386_microseconds_per_isr);
156
157  #if 0
[359e537]158    printk( "configured usecs per tick=%d \n",
[b62009c3]159      rtems_configuration_get_microseconds_per_tick() );
160    printk( "Microseconds per ISR =%d\n", pc386_microseconds_per_isr );
161    printk( "final ISRs per=%d\n", pc386_isrs_per_tick );
162    printk( "final timer counts=%d\n", pc386_clock_click_count );
163  #endif
164
[6b54dcb]165  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
[b62009c3]166  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
167  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff);
168  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff);
[6b54dcb]169  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
170
171  bsp_interrupt_vector_enable( BSP_PERIODIC_TIMER - BSP_IRQ_VECTOR_BASE );
[959f887a]172
173  /*
174   * Now calibrate cycles per tick. Do this every time we
175   * turn the clock on in case the CPU clock speed has changed.
176   */
177  if ( x86_has_tsc() )
178    calibrate_tsc();
[b62009c3]179}
[7150f00f]180
[2bdcf4fd]181static void clockOff(void)
[7150f00f]182{
[6b54dcb]183  rtems_interrupt_lock_context lock_context;
184  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
[b62009c3]185  /* reset timer mode to standard (BIOS) value */
186  outport_byte(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
187  outport_byte(TIMER_CNTR0, 0);
188  outport_byte(TIMER_CNTR0, 0);
[6b54dcb]189  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
[b62009c3]190} /* Clock_exit */
[7150f00f]191
[8a7ed82]192bool Clock_isr_enabled = false;
[2bdcf4fd]193static void Clock_isr_handler(void *param)
[8a7ed82]194{
195  if ( Clock_isr_enabled )
[eb7c6a84]196    Clock_isr( param );
[8a7ed82]197}
198
199void Clock_driver_install_handler(void)
200{
[2bdcf4fd]201  rtems_status_code status;
202
203  status = rtems_interrupt_handler_install(
204    BSP_PERIODIC_TIMER,
205    "ckinit",
206    RTEMS_INTERRUPT_UNIQUE,
207    Clock_isr_handler,
208    NULL
209  );
210  assert(status == RTEMS_SUCCESSFUL);
211  clockOn();
[8a7ed82]212}
213
[90d8567]214#define Clock_driver_support_set_interrupt_affinity(online_processors) \
215  do { \
216    /* FIXME: Is there a way to do this on x86? */ \
217    (void) online_processors; \
218  } while (0)
219
[88ef1655]220void Clock_driver_support_initialize_hardware(void)
[959f887a]221{
222  bool use_tsc = false;
223  bool use_8254 = false;
[359e537]224
[959f887a]225  #if (CLOCK_DRIVER_USE_TSC == 1)
226    use_tsc = true;
227  #endif
228
229  #if (CLOCK_DRIVER_USE_8254 == 1)
230    use_8254 = true;
231  #endif
[359e537]232
[959f887a]233  if ( !use_tsc && !use_8254 ) {
234    if ( x86_has_tsc() ) use_tsc  = true;
235    else                 use_8254 = true;
236  }
237
238  if ( use_8254 ) {
[88ef1655]239    /* printk( "Use 8254\n" ); */
[75acd9e]240    pc386_tc.tc_get_timecount = pc386_get_timecount_i8254;
241    pc386_tc.tc_counter_mask = 0xffffffff;
242    pc386_tc.tc_frequency = TIMER_TICK;
[959f887a]243  } else {
[88ef1655]244    /* printk( "Use TSC\n" ); */
[75acd9e]245    pc386_tc.tc_get_timecount = pc386_get_timecount_tsc;
246    pc386_tc.tc_counter_mask = 0xffffffff;
247    pc386_tc.tc_frequency = pc586_tsc_frequency;
[959f887a]248  }
249
[75acd9e]250  pc386_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
251  rtems_timecounter_install(&pc386_tc);
[8a7ed82]252  Clock_isr_enabled = true;
[959f887a]253}
[b62009c3]254
255#define Clock_driver_support_shutdown_hardware() \
256  do { \
[2bdcf4fd]257    rtems_status_code status; \
258    clockOff(); \
259    status = rtems_interrupt_handler_remove(  \
260      BSP_PERIODIC_TIMER, \
261      Clock_isr_handler,  \
262      NULL  \
263    );  \
264    assert(status == RTEMS_SUCCESSFUL); \
[b62009c3]265  } while (0)
266
[05d1c82]267#include "../../../shared/clockdrv_shell.h"
Note: See TracBrowser for help on using the repository browser.