source: rtems/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c @ f600458d

Last change on this file since f600458d was f600458d, checked in by Martin Aberg <maberg@…>, on Feb 23, 2017 at 3:33:21 PM

leon, clock: new driver manager clock driver

  • Compatible with SMP
  • Selects timecounter depending on features available
  • Fixes problem with time going to fast on SMP

This is an implementation of the RTEMS "clockdrv_shell" interface for
LEON2/3/4 systems using the Driver Manager. It is clock hardware agnostic
and compatible with SMP and UP. Availability of free running counters is
probed and selected as needed.

GR740:

RTEMS TESTSUITE FAILURE SUMMARY

Result Test ExecRes? ConsoleRes? ExitCode1 ExitCode2
FAIL: ./fstests/imfs_fsscandir01 OK FAIL 5 0
FAIL: ./fstests/jffs2_fsscandir01 OK FAIL 5 0
FAIL: ./fstests/mdosfs_fsscandir01 OK FAIL 5 0
FAIL: ./fstests/mimfs_fsscandir01 OK FAIL 5 0
FAIL: ./fstests/mrfs_fsscandir01 OK FAIL 5 0
FAIL: ./psxtests/psxshm01 FAIL FAIL N/A N/A
FAIL: ./psxtests/psxshm02 FAIL FAIL N/A N/A
FAIL: ./sptests/spinternalerror01 OK N/A -559038737 1611526157
FAIL: ./sptests/sptimecounter01 OK N/A 5 0

SUMMARY

Tests failing: 9
Tests successful: 578

---
GR712RC:

RTEMS TESTSUITE FAILURE SUMMARY

Result Test ExecRes? ConsoleRes? ExitCode1 ExitCode2
FAIL: ./smptests/smpipi01 FAIL FAIL N/A N/A
FAIL: ./smptests/smpthreadlife01 FAIL FAIL N/A N/A
FAIL: ./fstests/imfs_fsscandir01 OK FAIL 5 0
FAIL: ./fstests/jffs2_fsscandir01 OK FAIL 5 0
FAIL: ./fstests/mdosfs_fsscandir01 OK FAIL 5 0
FAIL: ./fstests/mimfs_fsscandir01 OK FAIL 5 0
FAIL: ./fstests/mrfs_fsscandir01 OK FAIL 5 0
FAIL: ./psxtests/psxshm01 FAIL FAIL N/A N/A
FAIL: ./psxtests/psxshm02 FAIL FAIL N/A N/A
FAIL: ./sptests/spinternalerror01 OK N/A -559038737 1611526157
FAIL: ./sptests/sptimecounter01 OK N/A 5 0

SUMMARY

Tests failing: 11
Tests successful: 576

  • Property mode set to 100644
File size: 11.4 KB
Line 
1/*
2 *  Clock Tick Device Driver using Timer Library implemented
3 *  by the GRLIB GPTIMER / LEON2 Timer drivers.
4 *
5 *  COPYRIGHT (c) 2010 - 2017.
6 *  Cobham Gaisler AB.
7 *
8 *  The license and distribution terms for this file may be
9 *  found in the file LICENSE in this distribution or at
10 *  http://www.rtems.org/license/LICENSE.
11 *
12 */
13
14/*
15 * This is an implementation of the RTEMS "clockdrv_shell" interface for
16 * LEON2/3/4 systems using the Driver Manager. It is clock hardware agnostic
17 * and compatible with SMP and UP. Availability of free running counters is
18 * probed and selected as needed.
19 */
20#include <rtems.h>
21#include <rtems/timecounter.h>
22#include <rtems/clockdrv.h>
23#include <stdlib.h>
24#include <bsp.h>
25#include <bsp/tlib.h>
26
27#ifdef RTEMS_DRVMGR_STARTUP
28
29#if defined(LEON3)
30#include <leon.h>
31#endif
32
33struct ops {
34  /*
35   * Set up the free running counter using the Timecounter or Simple
36   * Timecounter interface.
37   */
38  rtems_device_driver (*initialize_counter)(void);
39
40  /*
41   * Hardware-specific support at tick interrupt which runs early in Clock_isr.
42   * It can for example be used to check if interrupt was actually caused by
43   * the timer hardware. If return value is not RTEMS_SUCCESSFUL then Clock_isr
44   * returns immediately. at_tick can be initialized with NULL.
45   */
46  rtems_device_driver (*at_tick)(void);
47
48  /*
49   * Typically calls rtems_timecounter_tick(). A specialized clock driver may
50   * use for example rtems_timecounter_tick_simple() instead.
51   */
52  void (*timecounter_tick)(void);
53
54  /*
55   * Called when the clock driver exits. It can be used to stop functionality
56   * started by initialize_counter. The tick timer is stopped by default.
57   * shutdown_hardware can be initialized with NULL
58   */
59  void (*shutdown_hardware)(void);
60};
61
62/*
63 * Different implementation depending on available free running counter for the
64 * timecounter.
65 *
66 * NOTE: The clock interface is not compatible with shared interrupts on the
67 * clock (tick) timer in SMP configuration.
68 */
69
70/* "simple timecounter" interface. Only for non-SMP. */
71static const struct ops ops_simple;
72/* Hardware support up-counter using LEON3 %asr23. */
73static const struct ops ops_timetag;
74/* Timestamp counter available in some IRQ(A)MP instantiations. */
75static const struct ops ops_irqamp;
76/* Separate GPTIMER subtimer as timecounter */
77static const struct ops ops_subtimer;
78
79struct clock_priv {
80  const struct ops *ops;
81  /*
82   * Timer number in Timer Library for tick timer used by this interface.
83   * Defaults to the first Timer in the System.
84   */
85  int tlib_tick_index;
86  /* Timer number for timecounter timer if separate GPTIMER subtimer is used */
87  int tlib_counter_index;
88  void *tlib_tick;
89  void *tlib_counter;
90  rtems_timecounter_simple tc_simple;
91  struct timecounter tc;
92};
93static struct clock_priv priv;
94
95/** Common interface **/
96
97/* Set system clock timer instance */
98void Clock_timer_register(int timer_number)
99{
100  priv.tlib_tick_index = timer_number;
101  priv.tlib_counter_index = timer_number + 1;
102}
103
104static rtems_device_driver tlib_clock_find_timer(void)
105{
106  /* Take Timer that should be used as system timer. */
107  priv.tlib_tick = tlib_open(priv.tlib_tick_index);
108  if (priv.tlib_tick == NULL) {
109    /* System Clock Timer not found */
110    return RTEMS_NOT_DEFINED;
111  }
112
113  /* Select which operation set to use */
114#ifndef RTEMS_SMP
115  priv.ops = &ops_simple;
116#else
117  /* When on LEON3 try to use dedicated hardware free running counter. */
118  leon3_up_counter_enable();
119  if (leon3_up_counter_is_available()) {
120    priv.ops = &ops_timetag;
121    return RTEMS_SUCCESSFUL;
122  } else {
123    volatile struct irqmp_timestamp_regs *irqmp_ts;
124
125    irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
126    if (leon3_irqmp_has_timestamp(irqmp_ts)) {
127      priv.ops = &ops_irqamp;
128      return RTEMS_SUCCESSFUL;
129    }
130  }
131
132  /* Take another subtimer as the final option. */
133  priv.ops = &ops_subtimer;
134#endif
135
136  return RTEMS_SUCCESSFUL;
137}
138
139static rtems_device_driver tlib_clock_initialize_hardware(void)
140{
141  /* Set tick rate in number of "Base-Frequency ticks" */
142  tlib_set_freq(priv.tlib_tick, rtems_configuration_get_microseconds_per_tick());
143  priv.ops->initialize_counter();
144  tlib_start(priv.tlib_tick, 0);
145
146  return RTEMS_SUCCESSFUL;
147}
148
149static rtems_device_driver tlib_clock_at_tick(void)
150{
151  if (priv.ops->at_tick) {
152    return priv.ops->at_tick();
153  }
154
155  return RTEMS_SUCCESSFUL;
156}
157
158static void tlib_clock_timecounter_tick(void)
159{
160  priv.ops->timecounter_tick();
161}
162
163/* Return a value not equal to RTEMS_SUCCESFUL to make Clock_initialize fail. */
164static rtems_device_driver tlib_clock_install_isr(rtems_isr *isr)
165{
166  int flags = 0;
167
168#ifdef RTEMS_SMP
169  /* We shall broadcast the clock interrupt to all processors. */
170  flags = TLIB_FLAGS_BROADCAST;
171#endif
172  tlib_irq_register(priv.tlib_tick, isr, NULL, flags);
173
174  return RTEMS_SUCCESSFUL;
175}
176
177static void tlib_clock_shutdown_hardware(void)
178{
179  if (priv.tlib_tick) {
180    tlib_stop(priv.tlib_tick);
181    priv.tlib_tick = NULL;
182  }
183  if (priv.ops->shutdown_hardware) {
184    priv.ops->shutdown_hardware();
185  }
186}
187
188/** Simple counter **/
189static uint32_t simple_tlib_tc_get(rtems_timecounter_simple *tc)
190{
191  unsigned int clicks = 0;
192
193  if (priv.tlib_tick != NULL) {
194    tlib_get_counter(priv.tlib_tick, &clicks);
195  }
196
197  return clicks;
198}
199
200static bool simple_tlib_tc_is_pending(rtems_timecounter_simple *tc)
201{
202  bool pending = false;
203
204  if (priv.tlib_tick != NULL) {
205    pending = tlib_interrupt_pending(priv.tlib_tick, 0) != 0;
206  }
207
208  return pending;
209}
210
211static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc)
212{
213  return rtems_timecounter_simple_downcounter_get(
214    tc,
215    simple_tlib_tc_get,
216    simple_tlib_tc_is_pending
217  );
218}
219
220static rtems_device_driver simple_initialize_counter(void)
221{
222  uint64_t frequency;
223  unsigned int tick_hz;
224
225  frequency = 1000000;
226  tick_hz = rtems_configuration_get_microseconds_per_tick();
227
228  rtems_timecounter_simple_install(
229    &priv.tc_simple,
230    frequency,
231    tick_hz,
232    simple_tlib_tc_get_timecount
233  );
234
235  return RTEMS_NOT_DEFINED;
236}
237
238static void simple_tlib_tc_at_tick(rtems_timecounter_simple *tc)
239{
240  /* Nothing to do */
241}
242
243/*
244 * Support for shared interrupts. Ack IRQ at source, only handle interrupts
245 * generated from the tick-timer. This is called early in Clock_isr.
246 */
247static rtems_device_driver simple_at_tick(void)
248{
249  if (tlib_interrupt_pending(priv.tlib_tick, 1) == 0) {
250    return RTEMS_NOT_DEFINED;
251  }
252  return RTEMS_SUCCESSFUL;
253}
254
255static void simple_timecounter_tick(void)
256{
257  rtems_timecounter_simple_downcounter_tick(
258    &priv.tc_simple,
259    simple_tlib_tc_get,
260    simple_tlib_tc_at_tick
261  );
262}
263
264static const struct ops ops_simple = {
265  .initialize_counter = simple_initialize_counter,
266  .at_tick            = simple_at_tick,
267  .timecounter_tick   = simple_timecounter_tick,
268  .shutdown_hardware  = NULL,
269};
270
271/** Subtimer as counter **/
272static uint32_t subtimer_get_timecount(struct timecounter *tc)
273{
274  unsigned int counter;
275
276  tlib_get_counter(priv.tlib_counter, &counter);
277
278  return 0xffffffff - counter;
279}
280
281static rtems_device_driver subtimer_initialize_counter(void)
282{
283  unsigned int mask;
284  unsigned int basefreq;
285
286  if (priv.tlib_counter_index == priv.tlib_tick_index) {
287    priv.tlib_counter_index = priv.tlib_tick_index + 1;
288  }
289  /* Take Timer that should be used as timecounter upcounter timer. */
290  priv.tlib_counter = tlib_open(priv.tlib_counter_index);
291  if (priv.tlib_counter == NULL) {
292    /* Timecounter timer not found */
293    return RTEMS_NOT_DEFINED;
294  }
295
296  /* Configure free running counter: GPTIMER */
297  tlib_get_freq(priv.tlib_counter, &basefreq, NULL);
298  tlib_get_widthmask(priv.tlib_counter, &mask);
299
300  priv.tc.tc_get_timecount = subtimer_get_timecount;
301  priv.tc.tc_counter_mask = mask;
302  priv.tc.tc_frequency = basefreq;
303  priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
304  rtems_timecounter_install(&priv.tc);
305  /* Start free running counter */
306  tlib_start(priv.tlib_counter, 0);
307
308  return RTEMS_SUCCESSFUL;
309}
310
311static void subtimer_timecounter_tick(void)
312{
313  rtems_timecounter_tick();
314}
315
316static void subtimer_shutdown_hardware(void)
317{
318  if (priv.tlib_counter) {
319    tlib_stop(priv.tlib_counter);
320    priv.tlib_counter = NULL;
321  }
322}
323
324static const struct ops ops_subtimer = {
325  .initialize_counter = subtimer_initialize_counter,
326  .timecounter_tick   = subtimer_timecounter_tick,
327  .shutdown_hardware  = subtimer_shutdown_hardware,
328};
329
330#if defined(LEON3)
331/** DSU timetag as counter **/
332static uint32_t timetag_get_timecount(struct timecounter *tc)
333{
334  return leon3_up_counter_low();
335}
336
337static rtems_device_driver timetag_initialize_counter(void)
338{
339  /* Configure free running counter: timetag */
340  priv.tc.tc_get_timecount = timetag_get_timecount;
341  priv.tc.tc_counter_mask = 0xffffffff;
342  priv.tc.tc_frequency = leon3_up_counter_frequency();
343  priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
344  rtems_timecounter_install(&priv.tc);
345
346  return RTEMS_SUCCESSFUL;
347}
348
349static void timetag_timecounter_tick(void)
350{
351  rtems_timecounter_tick();
352}
353
354static const struct ops ops_timetag = {
355  .initialize_counter = timetag_initialize_counter,
356  .at_tick            = NULL,
357  .timecounter_tick   = timetag_timecounter_tick,
358  .shutdown_hardware  = NULL,
359};
360#endif
361
362#if defined(LEON3)
363/** IRQ(A)MP timestamp as counter **/
364static uint32_t irqamp_get_timecount(struct timecounter *tc)
365{
366  return LEON3_IrqCtrl_Regs->timestamp[0].counter;
367}
368
369static rtems_device_driver irqamp_initialize_counter(void)
370{
371  volatile struct irqmp_timestamp_regs *irqmp_ts;
372  static const uint32_t A_TSISEL_FIELD = 0xf;
373
374  /* Configure free running counter: timetag */
375  priv.tc.tc_get_timecount = irqamp_get_timecount;
376  priv.tc.tc_counter_mask = 0xffffffff;
377  priv.tc.tc_frequency = leon3_up_counter_frequency();
378  priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
379  rtems_timecounter_install(&priv.tc);
380
381  /*
382   * The counter increments whenever a TSISEL field in a Timestamp Control
383   * Register is non-zero.
384   */
385  irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
386  irqmp_ts->control = A_TSISEL_FIELD;
387
388  return RTEMS_SUCCESSFUL;
389}
390
391static void irqamp_timecounter_tick(void)
392{
393  rtems_timecounter_tick();
394}
395
396static const struct ops ops_irqamp = {
397  .initialize_counter = irqamp_initialize_counter,
398  .at_tick            = NULL,
399  .timecounter_tick   = irqamp_timecounter_tick,
400  .shutdown_hardware  = NULL,
401};
402#endif
403
404/** Interface to the Clock Driver Shell (clockdrv_shell.h) **/
405#define Clock_driver_support_find_timer() \
406  do { \
407    rtems_device_driver ret; \
408    ret = tlib_clock_find_timer(); \
409    if (RTEMS_SUCCESSFUL != ret) { \
410      return ret; \
411    } \
412  } while (0)
413
414#define Clock_driver_support_install_isr( isr, old ) \
415  do { \
416    rtems_device_driver ret; \
417    ret = tlib_clock_install_isr( isr ); \
418    if (RTEMS_SUCCESSFUL != ret) { \
419      return ret; \
420    } \
421  } while (0)
422
423#define Clock_driver_support_set_interrupt_affinity(online_processors) \
424  /* Done by tlib_clock_install_isr() */
425
426#define Clock_driver_support_initialize_hardware() \
427  do { \
428    rtems_device_driver ret; \
429    ret = tlib_clock_initialize_hardware(); \
430    if (RTEMS_SUCCESSFUL != ret) { \
431      return ret; \
432    } \
433  } while (0)
434
435#define Clock_driver_support_shutdown_hardware() \
436  tlib_clock_shutdown_hardware()
437
438#define Clock_driver_timecounter_tick() \
439  tlib_clock_timecounter_tick()
440
441#define Clock_driver_support_at_tick() \
442  do { \
443    rtems_device_driver ret; \
444    ret = tlib_clock_at_tick(); \
445    if (RTEMS_SUCCESSFUL != ret) { \
446      return; \
447    } \
448  } while (0)
449
450#include "../../../shared/clockdrv_shell.h"
451
452#endif /* RTEMS_DRVMGR_STARTUP */
453
Note: See TracBrowser for help on using the repository browser.