source: rtems/bsps/i386/pc386/clock/ckinit.c @ 1a971d8

Last change on this file since 1a971d8 was 1a971d8, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 18, 2019 at 5:38:13 AM

score: Remove SMP_MESSAGE_CLOCK_TICK

Use _SMP_Multicast_action() instead.

  • Property mode set to 100644
File size: 6.8 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-generic.h>
28#include <bspopts.h>
29#include <libcpu/cpuModel.h>
30#include <assert.h>
31#include <rtems/timecounter.h>
32#ifdef RTEMS_SMP
33#include <rtems/score/smpimpl.h>
34#endif
35
36#define CLOCK_VECTOR 0
37
38volatile uint32_t pc386_microseconds_per_isr;
39volatile uint32_t pc386_isrs_per_tick;
40uint32_t pc386_clock_click_count;
41
42/* forward declaration */
43void Clock_isr(void *param);
44static void Clock_isr_handler(void *param);
45
46/*
47 * Roughly the number of cycles per second. Note that these
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 */
53static uint64_t pc586_tsc_frequency;
54
55static struct timecounter pc386_tc;
56
57/* this driver may need to count ISRs per tick */
58#define CLOCK_DRIVER_ISRS_PER_TICK       1
59#define CLOCK_DRIVER_ISRS_PER_TICK_VALUE pc386_isrs_per_tick
60
61extern volatile uint32_t Clock_driver_ticks;
62
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
70#ifdef RTEMS_SMP
71#define Clock_driver_support_at_tick() \
72  do {                                                              \
73    Processor_mask targets;                                         \
74    _Processor_mask_Assign(&targets, _SMP_Get_online_processors()); \
75    _Processor_mask_Clear(&targets, _SMP_Get_current_processor());  \
76    _SMP_Multicast_action(&targets, Clock_isr, NULL);               \
77  } while (0)
78#endif
79
80static uint32_t pc386_get_timecount_tsc(struct timecounter *tc)
81{
82  return (uint32_t)rdtsc();
83}
84
85static uint32_t pc386_get_timecount_i8254(struct timecounter *tc)
86{
87  uint32_t                 irqs;
88  uint8_t                  lsb, msb;
89  rtems_interrupt_lock_context lock_context;
90
91  /*
92   * Fetch all the data in an interrupt critical section.
93   */
94
95  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
96
97    READ_8254(lsb, msb);
98    irqs = Clock_driver_ticks;
99
100  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
101
102  return (irqs + 1) * pc386_microseconds_per_isr - ((msb << 8) | lsb);
103}
104
105/*
106 * Calibrate CPU cycles per tick. Interrupts should be disabled.
107 */
108static void calibrate_tsc(void)
109{
110  uint64_t              begin_time;
111  uint8_t               then_lsb, then_msb, now_lsb, now_msb;
112  uint32_t              i;
113
114  /*
115   * We just reset the timer, so we know we're at the beginning of a tick.
116   */
117
118  /*
119   * Count cycles. Watching the timer introduces a several microsecond
120   * uncertaintity, so let it cook for a while and divide by the number of
121   * ticks actually executed.
122   */
123
124  begin_time = rdtsc();
125
126  for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick;
127       i != 0; --i ) {
128    /* We know we've just completed a tick when timer goes from low to high */
129    then_lsb = then_msb = 0xff;
130    do {
131      READ_8254(now_lsb, now_msb);
132      if ((then_msb < now_msb) ||
133          ((then_msb == now_msb) && (then_lsb < now_lsb)))
134        break;
135      then_lsb = now_lsb;
136      then_msb = now_msb;
137    } while (1);
138  }
139
140  pc586_tsc_frequency = rdtsc() - begin_time;
141
142#if 0
143  printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_frequency / 1000000));
144#endif
145}
146
147static void clockOn(void)
148{
149  rtems_interrupt_lock_context lock_context;
150  pc386_isrs_per_tick        = 1;
151  pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick();
152
153  while (US_TO_TICK(pc386_microseconds_per_isr) > 65535) {
154    pc386_isrs_per_tick  *= 10;
155    pc386_microseconds_per_isr /= 10;
156  }
157  pc386_clock_click_count = US_TO_TICK(pc386_microseconds_per_isr);
158
159  #if 0
160    printk( "configured usecs per tick=%d \n",
161      rtems_configuration_get_microseconds_per_tick() );
162    printk( "Microseconds per ISR =%d\n", pc386_microseconds_per_isr );
163    printk( "final ISRs per=%d\n", pc386_isrs_per_tick );
164    printk( "final timer counts=%d\n", pc386_clock_click_count );
165  #endif
166
167  rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
168  outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
169  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff);
170  outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff);
171  rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
172
173  bsp_interrupt_vector_enable( BSP_PERIODIC_TIMER - BSP_IRQ_VECTOR_BASE );
174
175  /*
176   * Now calibrate cycles per tick. Do this every time we
177   * turn the clock on in case the CPU clock speed has changed.
178   */
179  if ( x86_has_tsc() )
180    calibrate_tsc();
181}
182
183bool Clock_isr_enabled = false;
184static void Clock_isr_handler(void *param)
185{
186  if ( Clock_isr_enabled )
187    Clock_isr( param );
188}
189
190void Clock_driver_install_handler(void)
191{
192  rtems_status_code status;
193
194  status = rtems_interrupt_handler_install(
195    BSP_PERIODIC_TIMER,
196    "ckinit",
197    RTEMS_INTERRUPT_UNIQUE,
198    Clock_isr_handler,
199    NULL
200  );
201  assert(status == RTEMS_SUCCESSFUL);
202  clockOn();
203}
204
205#define Clock_driver_support_set_interrupt_affinity(online_processors) \
206  do { \
207    /* FIXME: Is there a way to do this on x86? */ \
208    (void) online_processors; \
209  } while (0)
210
211void Clock_driver_support_initialize_hardware(void)
212{
213  bool use_tsc = false;
214  bool use_8254 = false;
215
216  #if (CLOCK_DRIVER_USE_TSC == 1)
217    use_tsc = true;
218  #endif
219
220  #if (CLOCK_DRIVER_USE_8254 == 1)
221    use_8254 = true;
222  #endif
223
224  if ( !use_tsc && !use_8254 ) {
225    if ( x86_has_tsc() ) use_tsc  = true;
226    else                 use_8254 = true;
227  }
228
229  if ( use_8254 ) {
230    /* printk( "Use 8254\n" ); */
231    pc386_tc.tc_get_timecount = pc386_get_timecount_i8254;
232    pc386_tc.tc_counter_mask = 0xffffffff;
233    pc386_tc.tc_frequency = TIMER_TICK;
234  } else {
235    /* printk( "Use TSC\n" ); */
236    pc386_tc.tc_get_timecount = pc386_get_timecount_tsc;
237    pc386_tc.tc_counter_mask = 0xffffffff;
238    pc386_tc.tc_frequency = pc586_tsc_frequency;
239  }
240
241  pc386_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
242  rtems_timecounter_install(&pc386_tc);
243  Clock_isr_enabled = true;
244}
245
246#include "../../../shared/dev/clock/clockimpl.h"
Note: See TracBrowser for help on using the repository browser.