Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:06

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  *  Clock Tick Device Driver using Timer Library implemented
0005  *  by the GRLIB GPTIMER / LEON2 Timer drivers.
0006  *
0007  *  COPYRIGHT (c) 2010 - 2017.
0008  *  Cobham Gaisler AB.
0009  *
0010  * Redistribution and use in source and binary forms, with or without
0011  * modification, are permitted provided that the following conditions
0012  * are met:
0013  * 1. Redistributions of source code must retain the above copyright
0014  *    notice, this list of conditions and the following disclaimer.
0015  * 2. Redistributions in binary form must reproduce the above copyright
0016  *    notice, this list of conditions and the following disclaimer in the
0017  *    documentation and/or other materials provided with the distribution.
0018  *
0019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0020  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0022  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0023  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0024  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0025  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0026  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0027  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0028  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0029  * POSSIBILITY OF SUCH DAMAGE.
0030  *
0031  */
0032 
0033 /*
0034  * This is an implementation of the RTEMS "clockdrv_shell" interface for
0035  * LEON2/3/4 systems using the Driver Manager. It is clock hardware agnostic
0036  * and compatible with SMP and UP. Availability of free running counters is
0037  * probed and selected as needed.
0038  */
0039 #include <rtems.h>
0040 #include <rtems/timecounter.h>
0041 #include <rtems/clockdrv.h>
0042 #include <stdlib.h>
0043 #include <bsp.h>
0044 #include <grlib/tlib.h>
0045 
0046 #ifdef RTEMS_DRVMGR_STARTUP
0047 
0048 #if defined(LEON3)
0049 #include <leon.h>
0050 #endif
0051 
0052 struct ops {
0053   /*
0054    * Set up the free running counter using the Timecounter or Simple
0055    * Timecounter interface.
0056    */
0057   rtems_device_driver (*initialize_counter)(void);
0058 
0059   /*
0060    * Hardware-specific support at tick interrupt which runs early in Clock_isr.
0061    * It can for example be used to check if interrupt was actually caused by
0062    * the timer hardware. If return value is not RTEMS_SUCCESSFUL then Clock_isr
0063    * returns immediately. at_tick can be initialized with NULL.
0064    */
0065   rtems_device_driver (*at_tick)(void);
0066 
0067   /*
0068    * Typically calls rtems_timecounter_tick(). A specialized clock driver may
0069    * use for example rtems_timecounter_tick_simple() instead.
0070    */
0071   void (*timecounter_tick)(void);
0072 
0073   /*
0074    * Called when the clock driver exits. It can be used to stop functionality
0075    * started by initialize_counter. The tick timer is stopped by default.
0076    * shutdown_hardware can be initialized with NULL
0077    */
0078   void (*shutdown_hardware)(void);
0079 };
0080 
0081 /*
0082  * Different implementation depending on available free running counter for the
0083  * timecounter.
0084  *
0085  * NOTE: The clock interface is not compatible with shared interrupts on the
0086  * clock (tick) timer in SMP configuration.
0087  */
0088 
0089 #ifndef RTEMS_SMP
0090 /* "simple timecounter" interface. Only for non-SMP. */
0091 static const struct ops ops_simple;
0092 #else
0093 /* Hardware support up-counter using LEON3 %asr23. */
0094 static const struct ops ops_timetag;
0095 /* Timestamp counter available in some IRQ(A)MP instantiations. */
0096 static const struct ops ops_irqamp;
0097 /* Separate GPTIMER subtimer as timecounter */
0098 static const struct ops ops_subtimer;
0099 #endif
0100 
0101 struct clock_priv {
0102   const struct ops *ops;
0103   /*
0104    * Timer number in Timer Library for tick timer used by this interface.
0105    * Defaults to the first Timer in the System.
0106    */
0107   int tlib_tick_index;
0108   /* Timer number for timecounter timer if separate GPTIMER subtimer is used */
0109   int tlib_counter_index;
0110   void *tlib_tick;
0111   void *tlib_counter;
0112   rtems_timecounter_simple tc_simple;
0113   struct timecounter tc;
0114 };
0115 static struct clock_priv priv;
0116 
0117 /** Common interface **/
0118 
0119 /* Set system clock timer instance */
0120 void Clock_timer_register(int timer_number)
0121 {
0122   priv.tlib_tick_index = timer_number;
0123   priv.tlib_counter_index = timer_number + 1;
0124 }
0125 
0126 static rtems_device_driver tlib_clock_find_timer(void)
0127 {
0128   /* Take Timer that should be used as system timer. */
0129   priv.tlib_tick = tlib_open(priv.tlib_tick_index);
0130   if (priv.tlib_tick == NULL) {
0131     /* System Clock Timer not found */
0132     return RTEMS_NOT_DEFINED;
0133   }
0134 
0135   /* Select which operation set to use */
0136 #ifndef RTEMS_SMP
0137   priv.ops = &ops_simple;
0138 #else
0139   /* When on LEON3 try to use dedicated hardware free running counter. */
0140   leon3_up_counter_enable();
0141   if (leon3_up_counter_is_available()) {
0142     priv.ops = &ops_timetag;
0143     return RTEMS_SUCCESSFUL;
0144   } else {
0145     volatile struct irqmp_timestamp_regs *irqmp_ts;
0146 
0147     irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
0148     if (irqmp_has_timestamp(irqmp_ts)) {
0149       priv.ops = &ops_irqamp;
0150       return RTEMS_SUCCESSFUL;
0151     }
0152   }
0153 
0154   /* Take another subtimer as the final option. */
0155   priv.ops = &ops_subtimer;
0156 #endif
0157 
0158   return RTEMS_SUCCESSFUL;
0159 }
0160 
0161 static rtems_device_driver tlib_clock_initialize_hardware(void)
0162 {
0163   /* Set tick rate in number of "Base-Frequency ticks" */
0164   tlib_set_freq(priv.tlib_tick, rtems_configuration_get_microseconds_per_tick());
0165   priv.ops->initialize_counter();
0166   tlib_start(priv.tlib_tick, 0);
0167 
0168   return RTEMS_SUCCESSFUL;
0169 }
0170 
0171 static rtems_device_driver tlib_clock_at_tick(void)
0172 {
0173   if (priv.ops->at_tick) {
0174     return priv.ops->at_tick();
0175   }
0176 
0177   return RTEMS_SUCCESSFUL;
0178 }
0179 
0180 static void tlib_clock_timecounter_tick(void)
0181 {
0182   priv.ops->timecounter_tick();
0183 }
0184 
0185 static void tlib_clock_install_isr(rtems_isr *isr)
0186 {
0187   int flags = 0;
0188 
0189 #ifdef RTEMS_SMP
0190   /* We shall broadcast the clock interrupt to all processors. */
0191   flags = TLIB_FLAGS_BROADCAST;
0192 #endif
0193   tlib_irq_register(priv.tlib_tick, isr, NULL, flags);
0194 }
0195 
0196 #ifndef RTEMS_SMP
0197 /** Simple counter **/
0198 static uint32_t simple_tlib_tc_get(rtems_timecounter_simple *tc)
0199 {
0200   unsigned int clicks = 0;
0201 
0202   if (priv.tlib_tick != NULL) {
0203     tlib_get_counter(priv.tlib_tick, &clicks);
0204   }
0205 
0206   return clicks;
0207 }
0208 
0209 static bool simple_tlib_tc_is_pending(rtems_timecounter_simple *tc)
0210 {
0211   bool pending = false;
0212 
0213   if (priv.tlib_tick != NULL) {
0214     pending = tlib_interrupt_pending(priv.tlib_tick, 0) != 0;
0215   }
0216 
0217   return pending;
0218 }
0219 
0220 static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc)
0221 {
0222   return rtems_timecounter_simple_downcounter_get(
0223     tc,
0224     simple_tlib_tc_get,
0225     simple_tlib_tc_is_pending
0226   );
0227 }
0228 
0229 static rtems_device_driver simple_initialize_counter(void)
0230 {
0231   unsigned int tick_hz, frequency;
0232 
0233   tlib_get_freq(priv.tlib_tick, &frequency, NULL);
0234   tick_hz = rtems_configuration_get_microseconds_per_tick();
0235 
0236   rtems_timecounter_simple_install(
0237     &priv.tc_simple,
0238     (uint64_t)frequency,
0239     tick_hz,
0240     simple_tlib_tc_get_timecount
0241   );
0242 
0243   return RTEMS_NOT_DEFINED;
0244 }
0245 
0246 static void simple_tlib_tc_at_tick(rtems_timecounter_simple *tc)
0247 {
0248   /* Nothing to do */
0249 }
0250 
0251 /*
0252  * Support for shared interrupts. Ack IRQ at source, only handle interrupts
0253  * generated from the tick-timer. This is called early in Clock_isr.
0254  */
0255 static rtems_device_driver simple_at_tick(void)
0256 {
0257   if (tlib_interrupt_pending(priv.tlib_tick, 1) == 0) {
0258     return RTEMS_NOT_DEFINED;
0259   }
0260   return RTEMS_SUCCESSFUL;
0261 }
0262 
0263 static void simple_timecounter_tick(void)
0264 {
0265   rtems_timecounter_simple_downcounter_tick(
0266     &priv.tc_simple,
0267     simple_tlib_tc_get,
0268     simple_tlib_tc_at_tick
0269   );
0270 }
0271 
0272 static const struct ops ops_simple = {
0273   .initialize_counter = simple_initialize_counter,
0274   .at_tick            = simple_at_tick,
0275   .timecounter_tick   = simple_timecounter_tick,
0276   .shutdown_hardware  = NULL,
0277 };
0278 
0279 #else
0280 
0281 /** Subtimer as counter **/
0282 static uint32_t subtimer_get_timecount(struct timecounter *tc)
0283 {
0284   unsigned int counter;
0285 
0286   tlib_get_counter(priv.tlib_counter, &counter);
0287 
0288   return 0xffffffff - counter;
0289 }
0290 
0291 static rtems_device_driver subtimer_initialize_counter(void)
0292 {
0293   unsigned int mask;
0294   unsigned int basefreq;
0295 
0296   if (priv.tlib_counter_index == priv.tlib_tick_index) {
0297     priv.tlib_counter_index = priv.tlib_tick_index + 1;
0298   }
0299   /* Take Timer that should be used as timecounter upcounter timer. */
0300   priv.tlib_counter = tlib_open(priv.tlib_counter_index);
0301   if (priv.tlib_counter == NULL) {
0302     /* Timecounter timer not found */
0303     return RTEMS_NOT_DEFINED;
0304   }
0305 
0306   /* Configure free running counter: GPTIMER */
0307   tlib_get_freq(priv.tlib_counter, &basefreq, NULL);
0308   tlib_get_widthmask(priv.tlib_counter, &mask);
0309 
0310   priv.tc.tc_get_timecount = subtimer_get_timecount;
0311   priv.tc.tc_counter_mask = mask;
0312   priv.tc.tc_frequency = basefreq;
0313   priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
0314   rtems_timecounter_install(&priv.tc);
0315   /* Start free running counter */
0316   tlib_start(priv.tlib_counter, 0);
0317 
0318   return RTEMS_SUCCESSFUL;
0319 }
0320 
0321 static void subtimer_timecounter_tick(void)
0322 {
0323   rtems_timecounter_tick();
0324 }
0325 
0326 static void subtimer_shutdown_hardware(void)
0327 {
0328   if (priv.tlib_counter) {
0329     tlib_stop(priv.tlib_counter);
0330     priv.tlib_counter = NULL;
0331   }
0332 }
0333 
0334 static const struct ops ops_subtimer = {
0335   .initialize_counter = subtimer_initialize_counter,
0336   .timecounter_tick   = subtimer_timecounter_tick,
0337   .shutdown_hardware  = subtimer_shutdown_hardware,
0338 };
0339 
0340 /** DSU timetag as counter **/
0341 static uint32_t timetag_get_timecount(struct timecounter *tc)
0342 {
0343   return leon3_up_counter_low();
0344 }
0345 
0346 static rtems_device_driver timetag_initialize_counter(void)
0347 {
0348   /* Configure free running counter: timetag */
0349   priv.tc.tc_get_timecount = timetag_get_timecount;
0350   priv.tc.tc_counter_mask = 0xffffffff;
0351   priv.tc.tc_frequency = leon3_up_counter_frequency();
0352   priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
0353   rtems_timecounter_install(&priv.tc);
0354 
0355   return RTEMS_SUCCESSFUL;
0356 }
0357 
0358 static void timetag_timecounter_tick(void)
0359 {
0360   rtems_timecounter_tick();
0361 }
0362 
0363 static const struct ops ops_timetag = {
0364   .initialize_counter = timetag_initialize_counter,
0365   .at_tick            = NULL,
0366   .timecounter_tick   = timetag_timecounter_tick,
0367   .shutdown_hardware  = NULL,
0368 };
0369 
0370 /** IRQ(A)MP timestamp as counter **/
0371 static uint32_t irqamp_get_timecount(struct timecounter *tc)
0372 {
0373   return LEON3_IrqCtrl_Regs->timestamp[0].counter;
0374 }
0375 
0376 static rtems_device_driver irqamp_initialize_counter(void)
0377 {
0378   volatile struct irqmp_timestamp_regs *irqmp_ts;
0379   static const uint32_t A_TSISEL_FIELD = 0xf;
0380 
0381   /* Configure free running counter: timetag */
0382   priv.tc.tc_get_timecount = irqamp_get_timecount;
0383   priv.tc.tc_counter_mask = 0xffffffff;
0384   priv.tc.tc_frequency = leon3_up_counter_frequency();
0385   priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
0386   rtems_timecounter_install(&priv.tc);
0387 
0388   /*
0389    * The counter increments whenever a TSISEL field in a Timestamp Control
0390    * Register is non-zero.
0391    */
0392   irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
0393   irqmp_ts->control = A_TSISEL_FIELD;
0394 
0395   return RTEMS_SUCCESSFUL;
0396 }
0397 
0398 static void irqamp_timecounter_tick(void)
0399 {
0400   rtems_timecounter_tick();
0401 }
0402 
0403 static const struct ops ops_irqamp = {
0404   .initialize_counter = irqamp_initialize_counter,
0405   .at_tick            = NULL,
0406   .timecounter_tick   = irqamp_timecounter_tick,
0407   .shutdown_hardware  = NULL,
0408 };
0409 #endif
0410 
0411 /** Interface to the Clock Driver Shell (dev/clock/clockimpl.h) **/
0412 #define Clock_driver_support_find_timer() \
0413   do { \
0414     rtems_device_driver ret; \
0415     ret = tlib_clock_find_timer(); \
0416     if (RTEMS_SUCCESSFUL != ret) { \
0417       return ret; \
0418     } \
0419   } while (0)
0420 
0421 #define Clock_driver_support_install_isr( isr ) \
0422   tlib_clock_install_isr( isr )
0423 
0424 #define Clock_driver_support_set_interrupt_affinity(online_processors) \
0425   /* Done by tlib_clock_install_isr() */
0426 
0427 #define Clock_driver_support_initialize_hardware() \
0428   do { \
0429     rtems_device_driver ret; \
0430     ret = tlib_clock_initialize_hardware(); \
0431     if (RTEMS_SUCCESSFUL != ret) { \
0432       return ret; \
0433     } \
0434   } while (0)
0435 
0436 #define Clock_driver_timecounter_tick(arg) \
0437   tlib_clock_timecounter_tick()
0438 
0439 #define Clock_driver_support_at_tick(arg) \
0440   do { \
0441     rtems_device_driver ret; \
0442     ret = tlib_clock_at_tick(); \
0443     if (RTEMS_SUCCESSFUL != ret) { \
0444       return; \
0445     } \
0446   } while (0)
0447 
0448 #include "../../../shared/dev/clock/clockimpl.h"
0449 
0450 #endif /* RTEMS_DRVMGR_STARTUP */
0451