RTEMS
ckinit.c
1 /*
2  * Clock Tick Device Driver
3  *
4  * This routine initializes LEON timer 1 which used for the clock tick.
5  *
6  * The tick frequency is directly programmed to the configured number of
7  * microseconds per tick.
8  *
9  * COPYRIGHT (c) 1989-2006.
10  * On-Line Applications Research Corporation (OAR).
11  *
12  * Modified for LEON3 BSP.
13  * COPYRIGHT (c) 2004.
14  * Gaisler Research.
15  *
16  * Copyright (c) 2014, 2018 embedded brains GmbH
17  *
18  * The license and distribution terms for this file may be
19  * found in the file LICENSE in this distribution or at
20  * http://www.rtems.org/license/LICENSE.
21  */
22 
23 #include <bsp.h>
24 #include <bsp/irq.h>
25 #include <bspopts.h>
26 #include <bsp/fatal.h>
27 #include <rtems/rtems/intr.h>
28 #include <grlib/ambapp.h>
29 #include <rtems/score/profiling.h>
30 #include <rtems/score/sparcimpl.h>
31 #include <rtems/timecounter.h>
32 
33 /* The LEON3 BSP Timer driver can rely on the Driver Manager if the
34  * DrvMgr is initialized during startup. Otherwise the classic driver
35  * must be used.
36  *
37  * The DrvMgr Clock driver is located in the shared/timer directory
38  */
39 #ifndef RTEMS_DRVMGR_STARTUP
40 
41 /* LEON3 Timer system interrupt number */
42 static int clkirq;
43 
44 static void (*leon3_tc_tick)(void);
45 
46 static struct timecounter leon3_tc;
47 
48 #ifdef RTEMS_PROFILING
49 #define IRQMP_TIMESTAMP_S1_S2 ((1U << 25) | (1U << 26))
50 
51 static void leon3_tc_tick_irqmp_timestamp(void)
52 {
53  volatile struct irqmp_timestamp_regs *irqmp_ts =
54  &LEON3_IrqCtrl_Regs->timestamp[0];
55  unsigned int first = irqmp_ts->assertion;
56  unsigned int second = irqmp_ts->counter;
57 
58  irqmp_ts->control |= IRQMP_TIMESTAMP_S1_S2;
59 
60  _Profiling_Update_max_interrupt_delay(_Per_CPU_Get(), second - first);
61 
63 }
64 #endif
65 
66 static void leon3_tc_tick_irqmp_timestamp_init(void)
67 {
68 #ifdef RTEMS_PROFILING
69  /*
70  * Ignore the first clock interrupt, since it contains the sequential system
71  * initialization time. Do the timestamp initialization on the fly.
72  */
73 
74 #ifdef RTEMS_SMP
75  static Atomic_Uint counter = ATOMIC_INITIALIZER_UINT(0);
76 
77  bool done =
78  _Atomic_Fetch_add_uint(&counter, 1, ATOMIC_ORDER_RELAXED)
80 #else
81  bool done = true;
82 #endif
83 
84  volatile struct irqmp_timestamp_regs *irqmp_ts =
85  &LEON3_IrqCtrl_Regs->timestamp[0];
86  unsigned int ks = 1U << 5;
87 
88  irqmp_ts->control = ks | IRQMP_TIMESTAMP_S1_S2 | (unsigned int) clkirq;
89 
90  if (done) {
91  leon3_tc_tick = leon3_tc_tick_irqmp_timestamp;
92  }
93 #endif
94 
96 }
97 
98 static void leon3_tc_tick_default(void)
99 {
100 #ifndef RTEMS_SMP
101  SPARC_Counter *counter;
102  rtems_interrupt_level level;
103 
104  counter = &_SPARC_Counter_mutable;
106 
107  LEON3_IrqCtrl_Regs->iclear = counter->pending_mask;
108  counter->accumulated += counter->interval;
109 
111 #endif
112 
114 }
115 
116 static void leon3_tc_do_tick(void)
117 {
118  (*leon3_tc_tick)();
119 }
120 
121 #define Adjust_clkirq_for_node() do { clkirq += LEON3_CLOCK_INDEX; } while(0)
122 
123 #define Clock_driver_support_find_timer() \
124  do { \
125  /* Assume timer found during BSP initialization */ \
126  if (LEON3_Timer_Regs) { \
127  clkirq = (LEON3_Timer_Regs->cfg & 0xf8) >> 3; \
128  \
129  Adjust_clkirq_for_node(); \
130  } \
131  } while (0)
132 
133 #define Clock_driver_support_install_isr( _new ) \
134  bsp_clock_handler_install(_new)
135 
136 static void bsp_clock_handler_install(rtems_isr *new)
137 {
139 
141  clkirq,
142  "Clock",
144  new,
145  NULL
146  );
147  if (sc != RTEMS_SUCCESSFUL) {
148  rtems_fatal(RTEMS_FATAL_SOURCE_BSP, LEON3_FATAL_CLOCK_INITIALIZATION);
149  }
150 }
151 
152 #define Clock_driver_support_set_interrupt_affinity(online_processors) \
153  bsp_interrupt_set_affinity(clkirq, online_processors)
154 
155 static void leon3_clock_initialize(void)
156 {
157  volatile struct irqmp_timestamp_regs *irqmp_ts;
158  volatile struct gptimer_regs *gpt;
159  struct timecounter *tc;
160 
161  irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
162  gpt = LEON3_Timer_Regs;
163  tc = &leon3_tc;
164 
165  gpt->timer[LEON3_CLOCK_INDEX].reload =
167  gpt->timer[LEON3_CLOCK_INDEX].ctrl =
168  GPTIMER_TIMER_CTRL_EN | GPTIMER_TIMER_CTRL_RS |
169  GPTIMER_TIMER_CTRL_LD | GPTIMER_TIMER_CTRL_IE;
170 
171  leon3_up_counter_enable();
172 
173  if (leon3_up_counter_is_available()) {
174  /* Use the LEON4 up-counter if available */
175  tc->tc_get_timecount = _SPARC_Get_timecount_asr23;
176  tc->tc_frequency = leon3_up_counter_frequency();
177 
178 #ifdef RTEMS_PROFILING
179  if (!leon3_irqmp_has_timestamp(irqmp_ts)) {
180  bsp_fatal(LEON3_FATAL_CLOCK_NO_IRQMP_TIMESTAMP_SUPPORT);
181  }
182 #endif
183 
184  leon3_tc_tick = leon3_tc_tick_irqmp_timestamp_init;
185  } else if (leon3_irqmp_has_timestamp(irqmp_ts)) {
186  /* Use the interrupt controller timestamp counter if available */
187  tc->tc_get_timecount = _SPARC_Get_timecount_up;
188  tc->tc_frequency = ambapp_freq_get(&ambapp_plb, LEON3_Timer_Adev);
189 
190  leon3_tc_tick = leon3_tc_tick_irqmp_timestamp_init;
191 
192  /*
193  * At least one TSISEL field must be non-zero to enable the timestamp
194  * counter. Use an arbitrary interrupt source.
195  */
196  irqmp_ts->control = 0x1;
197  } else {
198 #ifdef RTEMS_SMP
199  /*
200  * The GR712RC for example has no timestamp unit in the interrupt
201  * controller. At least on SMP configurations we must use a second timer
202  * in free running mode for the timecounter.
203  */
204  gpt->timer[LEON3_COUNTER_GPTIMER_INDEX].ctrl =
205  GPTIMER_TIMER_CTRL_EN | GPTIMER_TIMER_CTRL_IE;
206 
207  tc->tc_get_timecount = _SPARC_Get_timecount_down;
208 #else
209  SPARC_Counter *counter;
210 
211  counter = &_SPARC_Counter_mutable;
212  counter->read_isr_disabled = _SPARC_Counter_read_clock_isr_disabled;
213  counter->read = _SPARC_Counter_read_clock;
214  counter->counter_register = &gpt->timer[LEON3_CLOCK_INDEX].value;
215  counter->pending_register = &LEON3_IrqCtrl_Regs->ipend;
216  counter->pending_mask = UINT32_C(1) << clkirq;
217  counter->accumulated = rtems_configuration_get_microseconds_per_tick();
218  counter->interval = rtems_configuration_get_microseconds_per_tick();
219 
220  tc->tc_get_timecount = _SPARC_Get_timecount_clock;
221 #endif
222 
223  tc->tc_frequency = LEON3_GPTIMER_0_FREQUENCY_SET_BY_BOOT_LOADER,
224  leon3_tc_tick = leon3_tc_tick_default;
225  }
226 
227  tc->tc_counter_mask = 0xffffffff;
230 }
231 
232 #define Clock_driver_support_initialize_hardware() \
233  leon3_clock_initialize()
234 
235 #define Clock_driver_timecounter_tick() leon3_tc_do_tick()
236 
237 #include "../../../shared/dev/clock/clockimpl.h"
238 
239 #endif
static RTEMS_NO_RETURN void rtems_fatal(rtems_fatal_source fatal_source, rtems_fatal_code error_code)
%
Definition: fatal.h:162
Profiling Support API.
Timecounter API.
rtems_status_code rtems_interrupt_handler_install(rtems_vector_number vector, const char *info, rtems_option options, rtems_interrupt_handler handler, void *arg)
Installs the interrupt handler routine handler for the interrupt vector with number vector...
Definition: irq-generic.c:520
static __inline__ void rtems_timecounter_install(struct timecounter *tc)
Installs the timecounter.
Definition: timecounter.h:73
This status code indicates successful completion.
Definition: status.h:86
#define rtems_interrupt_local_enable(_isr_cookie)
%
Definition: intr.h:234
ISR_Level rtems_interrupt_level
%
Definition: intr.h:211
rtems_status_code
This enumeration provides status codes for directives of the Classic API.
Definition: status.h:82
#define RTEMS_INTERRUPT_UNIQUE
Makes the interrupt handler unique. Prevents other handler from using the same interrupt vector...
Definition: irq-extension.h:44
This header file defines the Interrupt Manager API.
static void _Profiling_Update_max_interrupt_delay(Per_CPU_Control *cpu, CPU_Counter_ticks interrupt_delay)
Updates the maximum interrupt delay.
Definition: profiling.h:141
#define rtems_configuration_get_microseconds_per_tick()
Returns the number of microseconds per clock tick configured for this application.
Definition: config.h:205
#define rtems_scheduler_get_processor_maximum()
Returns the processor maximum supported by the system.
Definition: tasks.h:350
Fatal source for BSP errors.
Definition: interr.h:94
static __inline__ void rtems_timecounter_tick(void)
Performs a timecounter tick.
Definition: timecounter.h:83
ISR_Handler rtems_isr
%
Definition: intr.h:202
LEON3 generic shared IRQ setup.
#define RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER
Timecounter quality for the clock drivers.
Definition: timecounter.h:47
#define rtems_interrupt_local_disable(_isr_cookie)
%
Definition: intr.h:222