source: rtems/bsps/i386/pc386/clock/ckinit.c @ 58adad4

5
Last change on this file since 58adad4 was 7632906, checked in by Sebastian Huber <sebastian.huber@…>, on 04/19/18 at 04:35:52

bsps: Move clock drivers to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

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