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

5
Last change on this file since 7eb606d3 was 7eb606d3, checked in by Sebastian Huber <sebastian.huber@…>, on Dec 22, 2018 at 5:31:04 PM

grlib: Move source files

Update #3678.

  • Property mode set to 100644
File size: 11.1 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 <grlib/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#ifndef RTEMS_SMP
71/* "simple timecounter" interface. Only for non-SMP. */
72static const struct ops ops_simple;
73#else
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;
80#endif
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
166/* Return a value not equal to RTEMS_SUCCESFUL to make Clock_initialize fail. */
167static rtems_device_driver tlib_clock_install_isr(rtems_isr *isr)
168{
169  int flags = 0;
170
171#ifdef RTEMS_SMP
172  /* We shall broadcast the clock interrupt to all processors. */
173  flags = TLIB_FLAGS_BROADCAST;
174#endif
175  tlib_irq_register(priv.tlib_tick, isr, NULL, flags);
176
177  return RTEMS_SUCCESSFUL;
178}
179
180#ifndef RTEMS_SMP
181/** Simple counter **/
182static uint32_t simple_tlib_tc_get(rtems_timecounter_simple *tc)
183{
184  unsigned int clicks = 0;
185
186  if (priv.tlib_tick != NULL) {
187    tlib_get_counter(priv.tlib_tick, &clicks);
188  }
189
190  return clicks;
191}
192
193static bool simple_tlib_tc_is_pending(rtems_timecounter_simple *tc)
194{
195  bool pending = false;
196
197  if (priv.tlib_tick != NULL) {
198    pending = tlib_interrupt_pending(priv.tlib_tick, 0) != 0;
199  }
200
201  return pending;
202}
203
204static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc)
205{
206  return rtems_timecounter_simple_downcounter_get(
207    tc,
208    simple_tlib_tc_get,
209    simple_tlib_tc_is_pending
210  );
211}
212
213static rtems_device_driver simple_initialize_counter(void)
214{
215  uint64_t frequency;
216  unsigned int tick_hz;
217
218  frequency = 1000000;
219  tick_hz = rtems_configuration_get_microseconds_per_tick();
220
221  rtems_timecounter_simple_install(
222    &priv.tc_simple,
223    frequency,
224    tick_hz,
225    simple_tlib_tc_get_timecount
226  );
227
228  return RTEMS_NOT_DEFINED;
229}
230
231static void simple_tlib_tc_at_tick(rtems_timecounter_simple *tc)
232{
233  /* Nothing to do */
234}
235
236/*
237 * Support for shared interrupts. Ack IRQ at source, only handle interrupts
238 * generated from the tick-timer. This is called early in Clock_isr.
239 */
240static rtems_device_driver simple_at_tick(void)
241{
242  if (tlib_interrupt_pending(priv.tlib_tick, 1) == 0) {
243    return RTEMS_NOT_DEFINED;
244  }
245  return RTEMS_SUCCESSFUL;
246}
247
248static void simple_timecounter_tick(void)
249{
250  rtems_timecounter_simple_downcounter_tick(
251    &priv.tc_simple,
252    simple_tlib_tc_get,
253    simple_tlib_tc_at_tick
254  );
255}
256
257static const struct ops ops_simple = {
258  .initialize_counter = simple_initialize_counter,
259  .at_tick            = simple_at_tick,
260  .timecounter_tick   = simple_timecounter_tick,
261  .shutdown_hardware  = NULL,
262};
263
264#else
265
266/** Subtimer as counter **/
267static uint32_t subtimer_get_timecount(struct timecounter *tc)
268{
269  unsigned int counter;
270
271  tlib_get_counter(priv.tlib_counter, &counter);
272
273  return 0xffffffff - counter;
274}
275
276static rtems_device_driver subtimer_initialize_counter(void)
277{
278  unsigned int mask;
279  unsigned int basefreq;
280
281  if (priv.tlib_counter_index == priv.tlib_tick_index) {
282    priv.tlib_counter_index = priv.tlib_tick_index + 1;
283  }
284  /* Take Timer that should be used as timecounter upcounter timer. */
285  priv.tlib_counter = tlib_open(priv.tlib_counter_index);
286  if (priv.tlib_counter == NULL) {
287    /* Timecounter timer not found */
288    return RTEMS_NOT_DEFINED;
289  }
290
291  /* Configure free running counter: GPTIMER */
292  tlib_get_freq(priv.tlib_counter, &basefreq, NULL);
293  tlib_get_widthmask(priv.tlib_counter, &mask);
294
295  priv.tc.tc_get_timecount = subtimer_get_timecount;
296  priv.tc.tc_counter_mask = mask;
297  priv.tc.tc_frequency = basefreq;
298  priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
299  rtems_timecounter_install(&priv.tc);
300  /* Start free running counter */
301  tlib_start(priv.tlib_counter, 0);
302
303  return RTEMS_SUCCESSFUL;
304}
305
306static void subtimer_timecounter_tick(void)
307{
308  rtems_timecounter_tick();
309}
310
311static void subtimer_shutdown_hardware(void)
312{
313  if (priv.tlib_counter) {
314    tlib_stop(priv.tlib_counter);
315    priv.tlib_counter = NULL;
316  }
317}
318
319static const struct ops ops_subtimer = {
320  .initialize_counter = subtimer_initialize_counter,
321  .timecounter_tick   = subtimer_timecounter_tick,
322  .shutdown_hardware  = subtimer_shutdown_hardware,
323};
324
325/** DSU timetag as counter **/
326static uint32_t timetag_get_timecount(struct timecounter *tc)
327{
328  return leon3_up_counter_low();
329}
330
331static rtems_device_driver timetag_initialize_counter(void)
332{
333  /* Configure free running counter: timetag */
334  priv.tc.tc_get_timecount = timetag_get_timecount;
335  priv.tc.tc_counter_mask = 0xffffffff;
336  priv.tc.tc_frequency = leon3_up_counter_frequency();
337  priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
338  rtems_timecounter_install(&priv.tc);
339
340  return RTEMS_SUCCESSFUL;
341}
342
343static void timetag_timecounter_tick(void)
344{
345  rtems_timecounter_tick();
346}
347
348static const struct ops ops_timetag = {
349  .initialize_counter = timetag_initialize_counter,
350  .at_tick            = NULL,
351  .timecounter_tick   = timetag_timecounter_tick,
352  .shutdown_hardware  = NULL,
353};
354
355/** IRQ(A)MP timestamp as counter **/
356static uint32_t irqamp_get_timecount(struct timecounter *tc)
357{
358  return LEON3_IrqCtrl_Regs->timestamp[0].counter;
359}
360
361static rtems_device_driver irqamp_initialize_counter(void)
362{
363  volatile struct irqmp_timestamp_regs *irqmp_ts;
364  static const uint32_t A_TSISEL_FIELD = 0xf;
365
366  /* Configure free running counter: timetag */
367  priv.tc.tc_get_timecount = irqamp_get_timecount;
368  priv.tc.tc_counter_mask = 0xffffffff;
369  priv.tc.tc_frequency = leon3_up_counter_frequency();
370  priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
371  rtems_timecounter_install(&priv.tc);
372
373  /*
374   * The counter increments whenever a TSISEL field in a Timestamp Control
375   * Register is non-zero.
376   */
377  irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
378  irqmp_ts->control = A_TSISEL_FIELD;
379
380  return RTEMS_SUCCESSFUL;
381}
382
383static void irqamp_timecounter_tick(void)
384{
385  rtems_timecounter_tick();
386}
387
388static const struct ops ops_irqamp = {
389  .initialize_counter = irqamp_initialize_counter,
390  .at_tick            = NULL,
391  .timecounter_tick   = irqamp_timecounter_tick,
392  .shutdown_hardware  = NULL,
393};
394#endif
395
396/** Interface to the Clock Driver Shell (dev/clock/clockimpl.h) **/
397#define Clock_driver_support_find_timer() \
398  do { \
399    rtems_device_driver ret; \
400    ret = tlib_clock_find_timer(); \
401    if (RTEMS_SUCCESSFUL != ret) { \
402      return ret; \
403    } \
404  } while (0)
405
406#define Clock_driver_support_install_isr( isr ) \
407  do { \
408    rtems_device_driver ret; \
409    ret = tlib_clock_install_isr( isr ); \
410    if (RTEMS_SUCCESSFUL != ret) { \
411      return ret; \
412    } \
413  } while (0)
414
415#define Clock_driver_support_set_interrupt_affinity(online_processors) \
416  /* Done by tlib_clock_install_isr() */
417
418#define Clock_driver_support_initialize_hardware() \
419  do { \
420    rtems_device_driver ret; \
421    ret = tlib_clock_initialize_hardware(); \
422    if (RTEMS_SUCCESSFUL != ret) { \
423      return ret; \
424    } \
425  } while (0)
426
427#define Clock_driver_timecounter_tick() \
428  tlib_clock_timecounter_tick()
429
430#define Clock_driver_support_at_tick() \
431  do { \
432    rtems_device_driver ret; \
433    ret = tlib_clock_at_tick(); \
434    if (RTEMS_SUCCESSFUL != ret) { \
435      return; \
436    } \
437  } while (0)
438
439#include "../../../shared/dev/clock/clockimpl.h"
440
441#endif /* RTEMS_DRVMGR_STARTUP */
442
Note: See TracBrowser for help on using the repository browser.