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 Dec 5, 2019 at 6:22:33 PM

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
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
166static void tlib_clock_install_isr(rtems_isr *isr)
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;
173#endif
174  tlib_irq_register(priv.tlib_tick, isr, NULL, flags);
175}
176
177#ifndef RTEMS_SMP
178/** Simple counter **/
179static uint32_t simple_tlib_tc_get(rtems_timecounter_simple *tc)
180{
181  unsigned int clicks = 0;
182
183  if (priv.tlib_tick != NULL) {
184    tlib_get_counter(priv.tlib_tick, &clicks);
185  }
186
187  return clicks;
188}
189
190static bool simple_tlib_tc_is_pending(rtems_timecounter_simple *tc)
191{
192  bool pending = false;
193
194  if (priv.tlib_tick != NULL) {
195    pending = tlib_interrupt_pending(priv.tlib_tick, 0) != 0;
196  }
197
198  return pending;
199}
200
201static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc)
202{
203  return rtems_timecounter_simple_downcounter_get(
204    tc,
205    simple_tlib_tc_get,
206    simple_tlib_tc_is_pending
207  );
208}
209
210static rtems_device_driver simple_initialize_counter(void)
211{
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;
226}
227
228static void simple_tlib_tc_at_tick(rtems_timecounter_simple *tc)
229{
230  /* Nothing to do */
231}
232
233/*
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.
236 */
237static rtems_device_driver simple_at_tick(void)
238{
239  if (tlib_interrupt_pending(priv.tlib_tick, 1) == 0) {
240    return RTEMS_NOT_DEFINED;
241  }
242  return RTEMS_SUCCESSFUL;
243}
244
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}
253
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};
260
261#else
262
263/** Subtimer as counter **/
264static uint32_t subtimer_get_timecount(struct timecounter *tc)
265{
266  unsigned int counter;
267
268  tlib_get_counter(priv.tlib_counter, &counter);
269
270  return 0xffffffff - counter;
271}
272
273static rtems_device_driver subtimer_initialize_counter(void)
274{
275  unsigned int mask;
276  unsigned int basefreq;
277
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  }
287
288  /* Configure free running counter: GPTIMER */
289  tlib_get_freq(priv.tlib_counter, &basefreq, NULL);
290  tlib_get_widthmask(priv.tlib_counter, &mask);
291
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);
299
300  return RTEMS_SUCCESSFUL;
301}
302
303static void subtimer_timecounter_tick(void)
304{
305  rtems_timecounter_tick();
306}
307
308static void subtimer_shutdown_hardware(void)
309{
310  if (priv.tlib_counter) {
311    tlib_stop(priv.tlib_counter);
312    priv.tlib_counter = NULL;
313  }
314}
315
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};
321
322/** DSU timetag as counter **/
323static uint32_t timetag_get_timecount(struct timecounter *tc)
324{
325  return leon3_up_counter_low();
326}
327
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);
336
337  return RTEMS_SUCCESSFUL;
338}
339
340static void timetag_timecounter_tick(void)
341{
342  rtems_timecounter_tick();
343}
344
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};
351
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}
357
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;
362
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);
369
370  /*
371   * The counter increments whenever a TSISEL field in a Timestamp Control
372   * Register is non-zero.
373   */
374  irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
375  irqmp_ts->control = A_TSISEL_FIELD;
376
377  return RTEMS_SUCCESSFUL;
378}
379
380static void irqamp_timecounter_tick(void)
381{
382  rtems_timecounter_tick();
383}
384
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};
391#endif
392
393/** Interface to the Clock Driver Shell (dev/clock/clockimpl.h) **/
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
403#define Clock_driver_support_install_isr( isr ) \
404  tlib_clock_install_isr( isr )
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
430#include "../../../shared/dev/clock/clockimpl.h"
431
432#endif /* RTEMS_DRVMGR_STARTUP */
433
Note: See TracBrowser for help on using the repository browser.