Back to home page

LXR

 
 

    


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

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSBSPsSPARCLEON3
0007  *
0008  * @brief This source file contains the implementation of the CPU Counter.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2014, 2023 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #include <bsp/leon3.h>
0037 #include <grlib/irqamp.h>
0038 
0039 #include <rtems/counter.h>
0040 #include <rtems/sysinit.h>
0041 #include <rtems/timecounter.h>
0042 
0043 #if defined(LEON3_HAS_ASR_22_23_UP_COUNTER) || \
0044   defined(LEON3_PROBE_ASR_22_23_UP_COUNTER)
0045 static uint32_t leon3_timecounter_get_asr_22_23_up_counter(
0046   struct timecounter *tc
0047 )
0048 {
0049   return leon3_up_counter_low();
0050 }
0051 
0052 static void leon3_counter_use_asr_22_23_up_counter(leon3_timecounter *tc)
0053 {
0054 #if !defined(LEON3_HAS_ASR_22_23_UP_COUNTER)
0055   tc->base.tc_get_timecount = leon3_timecounter_get_asr_22_23_up_counter;
0056 #endif
0057   tc->base.tc_frequency = leon3_up_counter_frequency();
0058 }
0059 #endif
0060 
0061 /*
0062  * The following code blocks provide different CPU counter implementations.
0063  * The implementation used is defined by build configuration options.  For a
0064  * particular chip, the best available hardware counter module may be selected
0065  * by build configuration options.  The default implementation tries to select
0066  * the best module at runtime.
0067  */
0068 
0069 #if defined(LEON3_HAS_ASR_22_23_UP_COUNTER)
0070 
0071 CPU_Counter_ticks _CPU_Counter_read(void)
0072 {
0073   return leon3_up_counter_low();
0074 }
0075 
0076 RTEMS_ALIAS(_CPU_Counter_read) uint32_t _SPARC_Counter_read_ISR_disabled(void);
0077 
0078 #define LEON3_GET_TIMECOUNT_INIT leon3_timecounter_get_asr_22_23_up_counter
0079 
0080 #elif defined(LEON3_DSU_BASE)
0081 
0082 /*
0083  * In general, using the Debug Support Unit (DSU) is not recommended.  Before
0084  * you use it, check that it is available in flight models and that the time
0085  * tag register is implemented in radiation hardened flip-flops.  For the
0086  * GR712RC, this is the case.
0087  */
0088 
0089 /* This value is specific to the GR712RC */
0090 #define LEON3_DSU_TIME_TAG_ZERO_BITS 2
0091 
0092 static uint32_t leon3_read_dsu_time_tag(void)
0093 {
0094   uint32_t value;
0095   volatile uint32_t *reg;
0096 
0097   /* Use a load with a forced cache miss */
0098   reg = (uint32_t *) (LEON3_DSU_BASE + 8);
0099   __asm__ volatile (
0100     "\tlda\t[%1]1, %0"
0101     : "=&r"(value)
0102     : "r"(reg)
0103   );
0104   return value << LEON3_DSU_TIME_TAG_ZERO_BITS;
0105 }
0106 
0107 static uint32_t leon3_timecounter_get_dsu_time_tag(
0108   struct timecounter *tc
0109 )
0110 {
0111   (void) tc;
0112   return leon3_read_dsu_time_tag();
0113 }
0114 
0115 CPU_Counter_ticks _CPU_Counter_read(void)
0116 {
0117   return leon3_read_dsu_time_tag();
0118 }
0119 
0120 RTEMS_ALIAS(_CPU_Counter_read) uint32_t _SPARC_Counter_read_ISR_disabled(void);
0121 
0122 static void leon3_counter_use_dsu_time_tag(leon3_timecounter *tc)
0123 {
0124   tc->base.tc_frequency =
0125     leon3_processor_local_bus_frequency() << LEON3_DSU_TIME_TAG_ZERO_BITS;
0126 }
0127 
0128 #define LEON3_GET_TIMECOUNT_INIT leon3_timecounter_get_dsu_time_tag
0129 
0130 #else /* !LEON3_HAS_ASR_22_23_UP_COUNTER && !LEON3_DSU_BASE */
0131 
0132 /*
0133  * This is a workaround for:
0134  * https://gcc.gnu.org/bugzilla//show_bug.cgi?id=69027
0135  */
0136 __asm__ (
0137   "\t.section\t\".text\"\n"
0138   "\t.align\t4\n"
0139   "\t.globl\t_CPU_Counter_read\n"
0140   "\t.globl\t_SPARC_Counter_read_ISR_disabled\n"
0141   "\t.type\t_CPU_Counter_read, #function\n"
0142   "\t.type\t_SPARC_Counter_read_ISR_disabled, #function\n"
0143   "_CPU_Counter_read:\n"
0144   "_SPARC_Counter_read_ISR_disabled:\n"
0145   "\tsethi\t%hi(leon3_timecounter_instance), %o0\n"
0146   "\tld [%o0 + %lo(leon3_timecounter_instance)], %o1\n"
0147   "\tor\t%o0, %lo(leon3_timecounter_instance), %o0\n"
0148   "\tor\t%o7, %g0, %g1\n"
0149   "\tcall\t%o1, 0\n"
0150   "\t or\t%g1, %g0, %o7\n"
0151   "\t.previous\n"
0152 );
0153 
0154 static uint32_t leon3_timecounter_get_dummy(struct timecounter *base)
0155 {
0156   leon3_timecounter *tc;
0157   uint32_t counter;
0158 
0159   tc = (leon3_timecounter *) base;
0160   counter = tc->software_counter + 1;
0161   tc->software_counter = counter;
0162   return counter;
0163 }
0164 
0165 #define LEON3_GET_TIMECOUNT_INIT leon3_timecounter_get_dummy
0166 
0167 static uint32_t leon3_timecounter_get_counter_down(struct timecounter *base)
0168 {
0169   leon3_timecounter *tc;
0170 
0171   tc = (leon3_timecounter *) base;
0172   return -(*tc->counter_register);
0173 }
0174 
0175 static void leon3_counter_use_gptimer(
0176   leon3_timecounter *tc,
0177   gptimer *gpt
0178 )
0179 {
0180   gptimer_timer *timer;
0181 
0182   timer = &gpt->timer[LEON3_COUNTER_GPTIMER_INDEX];
0183 
0184   /* Make timer free-running */
0185   grlib_store_32(&timer->trldval, 0xffffffff);
0186   grlib_store_32(&timer->tctrl, GPTIMER_TCTRL_EN | GPTIMER_TCTRL_RS);
0187 
0188   tc->counter_register = &timer->tcntval;
0189   tc->base.tc_get_timecount = leon3_timecounter_get_counter_down;
0190 #if defined(LEON3_PLB_FREQUENCY_DEFINED_BY_GPTIMER)
0191   tc->base.tc_frequency = LEON3_GPTIMER_0_FREQUENCY_SET_BY_BOOT_LOADER;
0192 #else
0193   tc->base.tc_frequency = ambapp_freq_get(ambapp_plb(), LEON3_Timer_Adev) /
0194     (grlib_load_32(&gpt->sreload) + 1);
0195 #endif
0196 }
0197 
0198 #if defined(LEON3_IRQAMP_PROBE_TIMESTAMP)
0199 
0200 static uint32_t leon3_timecounter_get_counter_up(struct timecounter *base)
0201 {
0202   leon3_timecounter *tc;
0203 
0204   tc = (leon3_timecounter *) base;
0205   return *tc->counter_register;
0206 }
0207 
0208 static void leon3_counter_use_irqamp_timestamp(
0209   leon3_timecounter *tc,
0210   irqamp_timestamp *irqmp_ts
0211 )
0212 {
0213   /* Enable interrupt timestamping for an arbitrary interrupt line */
0214   grlib_store_32(&irqmp_ts->itstmpc, IRQAMP_ITSTMPC_TSISEL(1));
0215 
0216   tc->counter_register = &irqmp_ts->itcnt;
0217   tc->base.tc_get_timecount = leon3_timecounter_get_counter_up;
0218   tc->base.tc_frequency = leon3_processor_local_bus_frequency();
0219 }
0220 
0221 #endif /* LEON3_IRQAMP_PROBE_TIMESTAMP */
0222 #endif /* LEON3_HAS_ASR_22_23_UP_COUNTER || LEON3_DSU_BASE */
0223 
0224 leon3_timecounter leon3_timecounter_instance = {
0225   .base = {
0226     .tc_get_timecount = LEON3_GET_TIMECOUNT_INIT,
0227     .tc_counter_mask = 0xffffffff,
0228     .tc_frequency = 1000000000,
0229     .tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER
0230   }
0231 };
0232 
0233 uint32_t _CPU_Counter_frequency(void)
0234 {
0235   return leon3_timecounter_instance.base.tc_frequency;
0236 }
0237 
0238 static void leon3_counter_initialize(void)
0239 {
0240 #if defined(LEON3_HAS_ASR_22_23_UP_COUNTER)
0241 
0242   leon3_up_counter_enable();
0243   leon3_counter_use_asr_22_23_up_counter(&leon3_timecounter_instance);
0244 
0245 #elif defined(LEON3_DSU_BASE)
0246 
0247   leon3_counter_use_dsu_time_tag(&leon3_timecounter_instance);
0248 
0249 #else /* !LEON3_HAS_ASR_22_23_UP_COUNTER && !LEON3_DSU_BASE */
0250 
0251   /* Try to find the best CPU counter available */
0252 
0253 #if defined(LEON3_IRQAMP_PROBE_TIMESTAMP)
0254   irqamp_timestamp *irqmp_ts;
0255 #endif
0256   gptimer *gpt;
0257   leon3_timecounter *tc;
0258 
0259   tc = &leon3_timecounter_instance;
0260 
0261 #if defined(LEON3_PROBE_ASR_22_23_UP_COUNTER)
0262   leon3_up_counter_enable();
0263 
0264   if (leon3_up_counter_is_available()) {
0265     /* Use the LEON4 up-counter if available */
0266     leon3_counter_use_asr_22_23_up_counter(tc);
0267     return;
0268   }
0269 #endif
0270 
0271 #if defined(LEON3_IRQAMP_PROBE_TIMESTAMP)
0272   irqmp_ts = irqamp_get_timestamp_registers(LEON3_IrqCtrl_Regs);
0273 
0274   if (irqmp_ts != NULL) {
0275     /* Use the interrupt controller timestamp counter if available */
0276     leon3_counter_use_irqamp_timestamp(tc, irqmp_ts);
0277     return;
0278   }
0279 #endif
0280 
0281   gpt = LEON3_Timer_Regs;
0282 
0283 #if defined(LEON3_GPTIMER_BASE)
0284   leon3_counter_use_gptimer(tc, gpt);
0285 #else
0286   if (gpt != NULL) {
0287     /* Fall back to the first GPTIMER if available */
0288     leon3_counter_use_gptimer(tc, gpt);
0289   }
0290 #endif
0291 
0292 #endif /* LEON3_HAS_ASR_22_23_UP_COUNTER || LEON3_DSU_BASE */
0293 }
0294 
0295 RTEMS_SYSINIT_ITEM(
0296   leon3_counter_initialize,
0297   RTEMS_SYSINIT_CPU_COUNTER,
0298   RTEMS_SYSINIT_ORDER_FIRST
0299 );