source: rtems/bsps/shared/grlib/btimer/tlib_ckinit.c @ bb99cd0d

5
Last change on this file since bb99cd0d was bb99cd0d, checked in by Sebastian Huber <sebastian.huber@…>, on 12/05/19 at 18:22:33

clock: Simplify driver initialization

Use a system initialization handler instead of a legacy IO driver.

Update #3834.

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