diff options
Diffstat (limited to 'bsps/shared')
-rw-r--r-- | bsps/shared/dev/clock/xil-ttc.c | 255 |
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" |