summaryrefslogtreecommitdiffstats
path: root/bsps/shared
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/shared')
-rw-r--r--bsps/shared/dev/clock/xil-ttc.c255
1 files changed, 122 insertions, 133 deletions
diff --git a/bsps/shared/dev/clock/xil-ttc.c b/bsps/shared/dev/clock/xil-ttc.c
index 35f176b825..624845d71c 100644
--- a/bsps/shared/dev/clock/xil-ttc.c
+++ b/bsps/shared/dev/clock/xil-ttc.c
@@ -1,14 +1,16 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
/**
* @file
*
- * @ingroup RTEMSBSPsARMZynqMP
+ * @ingroup RTEMSDriverClockXilTTC
*
- * @brief Triple Timer Counter clock functions definitions.
+ * @brief This source file contains a Clock Driver implementation using the
+ * Xilinx Triple Timer Counter (TTC).
*/
/*
- * SPDX-License-Identifier: BSD-2-Clause
- *
+ * Copyright (C) 2024 embedded brains GmbH & Co. KG
* Copyright (C) 2023 Reflex Aerospace GmbH
*
* Written by Philip Kirkpatrick <p.kirkpatrick@reflexaerospace.com>
@@ -38,175 +40,160 @@
#include <bsp.h>
#include <bsp/irq.h>
#include <bsp/fatal.h>
-#include <peripheral_maps/xilinx_zynqmp.h>
#include <dev/clock/xttcps_hw.h>
+#include <rtems/sysinit.h>
#include <rtems/timecounter.h>
-typedef struct {
- struct timecounter ttc_tc;
- uint32_t irq_match_interval;
- uint32_t tick_miss;
-} ttc_clock_context;
-
-static ttc_clock_context ttc_clock_instance = {0, };
+#if XTTCPS_COUNT_VALUE_MASK != UINT32_MAX
+#error "unexpected XTTCPS_COUNT_VALUE_MASK value"
+#endif
-#define TTC_REFERENCE_CLOCK 100000000
+/**
+ * @defgroup RTEMSDriverClockXilTTC \
+ * Xilinx Triple Timer Counter (TTC) Clock Driver
+ *
+ * @ingroup RTEMSDriverClockImpl
+ *
+ * @brief This group contains the Xilinx Triple Timer Counter (TTC) Clock
+ * Driver implementation.
+ *
+ * @{
+ */
uint32_t _CPU_Counter_frequency( void )
{
- return ttc_clock_instance.ttc_tc.tc_frequency;
+ return XIL_CLOCK_TTC_REFERENCE_CLOCK;
}
-static uint32_t zynqmp_ttc_get_timecount(struct timecounter *tc)
+CPU_Counter_ticks _CPU_Counter_read(void)
{
- uint32_t time;
- time = XTtcPs_ReadReg(BSP_SELECTED_TTC_ADDR, XTTCPS_COUNT_VALUE_OFFSET);
- return time;
+ return XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_COUNT_VALUE_OFFSET);
}
-CPU_Counter_ticks _CPU_Counter_read(void)
+static void xil_ttc_initialize(void)
{
- return zynqmp_ttc_get_timecount(&ttc_clock_instance.ttc_tc);
+ /* Do not use a prescaler to get a high resolution time source */
+ XTtcPs_WriteReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_CLK_CNTRL_OFFSET, 0);
+
+ /* Disable interupts */
+ XTtcPs_WriteReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_IER_OFFSET, 0);
+
+ /*
+ * Enable the timer, do not enable waveform output, increment up, use
+ * overflow mode, enable match mode.
+ */
+ XTtcPs_WriteReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_CNT_CNTRL_OFFSET,
+ XTTCPS_CNT_CNTRL_EN_WAVE_MASK | XTTCPS_CNT_CNTRL_MATCH_MASK);
}
-/**
- * @brief Initialize the HW peripheral for clock driver
- *
- * Clock driver is implemented by RTI module
- *
- * @retval Void
- */
-static void zynqmp_ttc_clock_driver_support_initialize_hardware(void)
-{
+RTEMS_SYSINIT_ITEM(
+ xil_ttc_initialize,
+ RTEMS_SYSINIT_CPU_COUNTER,
+ RTEMS_SYSINIT_ORDER_MIDDLE
+);
+
+typedef struct {
+ struct timecounter base;
+ uint32_t irq_match_interval;
+} xil_ttc_timecounter;
- uint32_t microsec_per_tick;
- uint16_t clock_ratio;
- uint8_t index;
- uint32_t frequency;
- uint32_t prescaler;
- uint32_t tmp_reg_val;
+static xil_ttc_timecounter xil_ttc_clock_instance;
- microsec_per_tick = rtems_configuration_get_microseconds_per_tick();
+static uint32_t xil_ttc_get_timecount(struct timecounter *tc)
+{
+ (void) tc;
+ return XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_COUNT_VALUE_OFFSET);
+}
- /* Check the TTC is OFF before reconfiguring */
- XTtcPs_WriteReg(BSP_SELECTED_TTC_ADDR, XTTCPS_CNT_CNTRL_OFFSET,
- /* Don't enable waveform output */
- XTTCPS_CNT_CNTRL_DIS_MASK | XTTCPS_CNT_CNTRL_EN_WAVE_MASK);
+static void xil_ttc_clock_driver_support_initialize_hardware(void)
+{
+ xil_ttc_timecounter *tc;
+ uint64_t frequency;
+ uint32_t irq_match_interval;
+ uint32_t count;
- /* Prescaler value is 2^(N + 1)
- * Divide down the clock as much as possible while still retaining a
- * frequency that is an integer multiple of 1MHz. This maximizes time to
- * overflow while minimizing rounding errors in 1us periods
- */
- clock_ratio = TTC_REFERENCE_CLOCK / 1000000;
- /* Search for the highest set bit. This is effectively min(log2(ratio)) */
- for(index = sizeof(clock_ratio) * 8 - 1; index > 0; index--) {
- if((clock_ratio >> (index)) & 0x01) {
- break;
- }
- }
- if(index == 0 && (clock_ratio & 0x01) == 0) {
- /* No prescaler */
- frequency = TTC_REFERENCE_CLOCK;
- XTtcPs_WriteReg(BSP_SELECTED_TTC_ADDR, XTTCPS_CLK_CNTRL_OFFSET, 0);
- } else {
- prescaler = index - 1;
- frequency = TTC_REFERENCE_CLOCK / (1 << (prescaler + 1));
- XTtcPs_WriteReg(BSP_SELECTED_TTC_ADDR, XTTCPS_CLK_CNTRL_OFFSET,
- prescaler << XTTCPS_CLK_CNTRL_PS_VAL_SHIFT |
- XTTCPS_CLK_CNTRL_PS_EN_MASK);
- }
+ tc = &xil_ttc_clock_instance;
+ frequency = XIL_CLOCK_TTC_REFERENCE_CLOCK;
+ irq_match_interval = (uint32_t)
+ ((frequency * rtems_configuration_get_microseconds_per_tick()) / 1000000);
- /* Max out the counter interval */
- tmp_reg_val = XTTCPS_INTERVAL_VAL_MASK;
- XTtcPs_WriteReg(BSP_SELECTED_TTC_ADDR, XTTCPS_INTERVAL_VAL_OFFSET,
- tmp_reg_val);
+ /* Setup match register to generate clock interrupts */
+ count = XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_COUNT_VALUE_OFFSET);
+ XTtcPs_WriteReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_MATCH_0_OFFSET,
+ count + irq_match_interval);
- /* Setup match register to generate tick IRQ */
- ttc_clock_instance.irq_match_interval =
- (uint32_t) (((uint64_t)frequency * microsec_per_tick) / 1000000);
- XTtcPs_WriteReg(BSP_SELECTED_TTC_ADDR, XTTCPS_MATCH_0_OFFSET,
- ttc_clock_instance.irq_match_interval);
/* Clear interupts (clear on read) */
- XTtcPs_ReadReg(BSP_SELECTED_TTC_ADDR, XTTCPS_ISR_OFFSET);
+ (void) XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_ISR_OFFSET);
+
/* Enable interupt for match register */
- XTtcPs_WriteReg(BSP_SELECTED_TTC_ADDR, XTTCPS_IER_OFFSET,
+ XTtcPs_WriteReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_IER_OFFSET,
XTTCPS_IXR_MATCH_0_MASK);
- /* Configure, reset, and enable counter */
- XTtcPs_WriteReg(BSP_SELECTED_TTC_ADDR, XTTCPS_CNT_CNTRL_OFFSET,
- XTTCPS_CNT_CNTRL_EN_WAVE_MASK | /* Don't enable waveform output */
- XTTCPS_CNT_CNTRL_RST_MASK | /* Reset count and start counter */
- XTTCPS_CNT_CNTRL_MATCH_MASK /* Enable match mode */
- /* Increment mode */
- /* Overflow mode */
- /* Not disabled */
- );
-
- /* set timecounter */
- ttc_clock_instance.ttc_tc.tc_get_timecount = zynqmp_ttc_get_timecount;
- ttc_clock_instance.ttc_tc.tc_counter_mask = XTTCPS_COUNT_VALUE_MASK;
- ttc_clock_instance.ttc_tc.tc_frequency = frequency;
- ttc_clock_instance.ttc_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
- rtems_timecounter_install(&ttc_clock_instance.ttc_tc);
+
+ /* Install timecounter */
+ tc->irq_match_interval = irq_match_interval;
+ tc->base.tc_counter_mask = UINT32_MAX;
+ tc->base.tc_frequency = XIL_CLOCK_TTC_REFERENCE_CLOCK;
+ tc->base.tc_get_timecount = xil_ttc_get_timecount;
+ tc->base.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
+ rtems_timecounter_install(&tc->base);
}
-/**
- * @brief Clears interrupt source
- *
- * @retval Void
- */
-static void zynqmp_ttc_clock_driver_support_at_tick(ttc_clock_context *tc)
+static void xil_ttc_clock_driver_support_at_tick(xil_ttc_timecounter *tc)
{
- uint32_t irq_flags;
- uint32_t cval;
- uint32_t now;
- uint32_t delta;
-
- /* Get and clear interupts (clear on read) */
- irq_flags = XTtcPs_ReadReg(BSP_SELECTED_TTC_ADDR, XTTCPS_ISR_OFFSET);
-
- if(irq_flags & XTTCPS_IXR_MATCH_0_MASK) {
- /* Update match */
- cval = XTtcPs_ReadReg(BSP_SELECTED_TTC_ADDR, XTTCPS_MATCH_0_OFFSET);
- /* Check that the match for the next tick is in the future
- * If no, then set the match for one irq interval from now
- * This will have the effect that your timebase will slip but
- * won't hang waiting for the counter to wrap around.
- * If this happens during normal operation, there is a problem
- * causing this interrupt to not be serviced quickly enough
- * If this happens during debugging, that is normal and expected
- * because the TTC does NOT pause when the CPU is halted on a breakpoint
+ uint32_t irq_match_interval;
+ uint32_t count;
+ uint32_t match;
+
+ irq_match_interval = tc->irq_match_interval;
+
+ /* Update match register */
+ match = XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_MATCH_0_OFFSET);
+ match += irq_match_interval;
+ XTtcPs_WriteReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_MATCH_0_OFFSET, match);
+
+ /* Clear interupts (clear on read) */
+ (void) XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_ISR_OFFSET);
+
+ /* Check that the new match value is in the future */
+ count = XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_COUNT_VALUE_OFFSET);
+
+ while (RTEMS_PREDICT_FALSE(match - count > irq_match_interval)) {
+ /*
+ * Tick misses may happen if interrupts are disabled for an extremly long
+ * period or while debugging.
*/
- now = XTtcPs_ReadReg(BSP_SELECTED_TTC_ADDR, XTTCPS_COUNT_VALUE_OFFSET);
- delta = now - cval;
- if(delta > tc->irq_match_interval) {
- cval = now;
- tc->tick_miss++;
- }
- cval += tc->irq_match_interval;
- XTtcPs_WriteReg(BSP_SELECTED_TTC_ADDR, XTTCPS_MATCH_0_OFFSET, cval);
+ rtems_timecounter_tick();
+
+ /* Update match register */
+ match += irq_match_interval;
+ XTtcPs_WriteReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_MATCH_0_OFFSET, match);
+
+ /* Clear interupts (clear on read) */
+ (void) XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_ISR_OFFSET);
+
+ /* Maybe the new match value is now in the future */
+ count = XTtcPs_ReadReg(XIL_CLOCK_TTC_BASE_ADDR, XTTCPS_COUNT_VALUE_OFFSET);
}
- /* Else, something is set up wrong, only match should be enabled */
}
-static rtems_interrupt_entry zynqmp_ttc_interrupt_entry;
+static rtems_interrupt_entry xil_ttc_interrupt_entry;
-static void zynqmp_ttc_clock_driver_support_install_isr(
+static void xil_ttc_clock_driver_support_install_isr(
rtems_interrupt_handler handler
)
{
rtems_status_code sc;
rtems_interrupt_entry_initialize(
- &zynqmp_ttc_interrupt_entry,
+ &xil_ttc_interrupt_entry,
handler,
- &ttc_clock_instance,
+ &xil_ttc_clock_instance,
"Clock"
);
sc = rtems_interrupt_entry_install(
- BSP_SELECTED_TTC_IRQ,
+ XIL_CLOCK_TTC_IRQ,
RTEMS_INTERRUPT_UNIQUE,
- &zynqmp_ttc_interrupt_entry
+ &xil_ttc_interrupt_entry
);
if ( sc != RTEMS_SUCCESSFUL ) {
bsp_fatal(XIL_FATAL_TTC_IRQ_INSTALL);
@@ -214,12 +201,14 @@ static void zynqmp_ttc_clock_driver_support_install_isr(
}
#define Clock_driver_support_at_tick(arg) \
- zynqmp_ttc_clock_driver_support_at_tick(arg)
+ xil_ttc_clock_driver_support_at_tick(arg)
#define Clock_driver_support_initialize_hardware \
- zynqmp_ttc_clock_driver_support_initialize_hardware
+ xil_ttc_clock_driver_support_initialize_hardware
#define Clock_driver_support_install_isr(isr) \
- zynqmp_ttc_clock_driver_support_install_isr(isr)
+ xil_ttc_clock_driver_support_install_isr(isr)
+
+/** @} */
#include "../../../shared/dev/clock/clockimpl.h"