| /* Copyright (c) 2025 Texas Instruments Incorporated |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/timer/system_timer.h> |
| #include <zephyr/irq.h> |
| #include <zephyr/sys/clock.h> |
| #include <zephyr/kernel.h> |
| |
| #include "ti_rti_timer.h" |
| |
| #define DT_DRV_COMPAT ti_am26_rtitimer |
| |
| BUILD_ASSERT(DT_HAS_ALIAS(systick_timer), "No alias named systick_timer found in the device tree"); |
| BUILD_ASSERT(DT_NODE_HAS_STATUS(DT_ALIAS(systick_timer), okay), |
| "systick_timer NOT enabled in the device tree"); |
| #if defined(CONFIG_TICKLESS_KERNEL) |
| BUILD_ASSERT(0, "RTI Timer Implementation DOES NOT support tickless kernel!"); |
| #endif |
| |
| #define SYSTICK_NODE DT_ALIAS(systick_timer) |
| #define TIMER_IRQ_NUM DT_IRQN(SYSTICK_NODE) |
| #define TIMER_IRQ_PRIO DT_IRQ(SYSTICK_NODE, priority) |
| #define TIMER_IRQ_FLAGS DT_IRQ(SYSTICK_NODE, flags) |
| |
| #if defined(CONFIG_TEST) |
| const int32_t z_sys_timer_irq_for_test = TIMER_IRQ_NUM; |
| #endif |
| |
| #define CYC_PER_TICK ((uint32_t)(sys_clock_hw_cycles_per_sec() / CONFIG_SYS_CLOCK_TICKS_PER_SEC)) |
| |
| struct ti_rti_timer_config { |
| DEVICE_MMIO_ROM; |
| }; |
| |
| struct ti_rti_timer_data { |
| DEVICE_MMIO_RAM; |
| }; |
| |
| static const struct device *systick_timer_dev; |
| |
| #define TI_RTI_TIMER_MASK(reg) TI_RTI_TIMER_##reg##_MASK |
| #define TI_RTI_TIMER_SHIFT(reg) TI_RTI_TIMER_##reg##_SHIFT |
| |
| #define RTI_REG(reg) (DEVICE_MMIO_GET(systick_timer_dev) + TI_RTI_TIMER_##reg) |
| |
| static void ti_rti_timer_write_reg_fields(uint32_t data, uint32_t reg, uint32_t mask, |
| uint32_t shift) |
| { |
| uint32_t reg_val; |
| |
| reg_val = sys_read32(reg); |
| reg_val = (reg_val & ~(mask)) | ((data & mask) << shift); |
| sys_write32(reg_val, reg); |
| } |
| |
| static void ti_rti_timer_isr(void *param) |
| { |
| ARG_UNUSED(param); |
| |
| /* If no pending event */ |
| if (!sys_read32(RTI_REG(RTIINTFLAG))) { |
| return; |
| } |
| |
| /* ACK match interrupt (INT0 for an RTI) */ |
| sys_write32(0x1u, RTI_REG(RTIINTFLAG)); |
| |
| /* Clear counter 0 overflow interrupt if high */ |
| if (sys_read32(RTI_REG(RTIINTFLAG)) & TI_RTI_TIMER_MASK(RTIINTFLAG_OVL0INT)) { |
| sys_write32((0x1u << TI_RTI_TIMER_SHIFT(RTIINTFLAG_OVL0INT)), RTI_REG(RTIINTFLAG)); |
| } |
| |
| /* The timer ISR is generated per counter increment, announce that 1 tick has elapsed */ |
| sys_clock_announce(1); |
| } |
| |
| void sys_clock_set_timeout(uint32_t ticks, bool idle) |
| { |
| ARG_UNUSED(idle); |
| ARG_UNUSED(ticks); |
| /* This driver doesn't support tickless kernel, return immediately */ |
| } |
| |
| uint32_t sys_clock_cycle_get_32(void) |
| { |
| return (uint32_t)sys_read32(RTI_REG(RTIFRC0)); |
| } |
| |
| unsigned int sys_clock_elapsed(void) |
| { |
| /* This driver doesn't support tickless kernel, return 0 for tickful kernel */ |
| return 0; |
| } |
| |
| static int sys_clock_driver_init(void) |
| { |
| |
| systick_timer_dev = DEVICE_DT_GET(DT_ALIAS(systick_timer)); |
| |
| DEVICE_MMIO_MAP(systick_timer_dev, K_MEM_CACHE_NONE); |
| |
| irq_disable(TIMER_IRQ_NUM); |
| |
| IRQ_CONNECT(TIMER_IRQ_NUM, TIMER_IRQ_PRIO, ti_rti_timer_isr, NULL, TIMER_IRQ_FLAGS); |
| |
| /* Disable Continue on Suspend */ |
| ti_rti_timer_write_reg_fields(0, RTI_REG(RTIGCTRL), TI_RTI_TIMER_MASK(RTIGCTRL_COS), |
| TI_RTI_TIMER_SHIFT(RTIGCTRL_COS)); |
| |
| /* COUNTER CONFIGURATION START */ |
| |
| /* Disable the counter block */ |
| ti_rti_timer_write_reg_fields(0, RTI_REG(RTIGCTRL), TI_RTI_TIMER_MASK(RTIGCTRL_CNT0EN), |
| TI_RTI_TIMER_SHIFT(RTIGCTRL_CNT0EN)); |
| |
| /* Clear free-running counter */ |
| sys_write32(0, RTI_REG(RTIFRC0)); |
| |
| /* let up-counter be clock source to free-running counter */ |
| ti_rti_timer_write_reg_fields(0, RTI_REG(RTITBCTRL), TI_RTI_TIMER_MASK(RTITBCTRL_TBEXT), |
| TI_RTI_TIMER_SHIFT(RTITBCTRL_TBEXT)); |
| |
| /* Reset up-counter 0 */ |
| sys_write32(0, RTI_REG(RTIUC0)); |
| |
| /* Configure the up counter prescaler, defaulted to 1 */ |
| uint32_t prescaler = 1; |
| |
| sys_write32(prescaler, RTI_REG(RTICPUC0)); |
| |
| /* COUNTER CONFIGURATION END */ |
| |
| /* Load timer compare value to compare block 0*/ |
| sys_write32((CYC_PER_TICK - 1), RTI_REG(RTICOMP0)); |
| |
| /* Load counter compare update value to compare block 0*/ |
| sys_write32((CYC_PER_TICK), RTI_REG(RTIUDCP0)); |
| |
| /* Use Compare block 0 for interrupt / tick generation */ |
| ti_rti_timer_write_reg_fields(0, RTI_REG(RTICOMPCTRL), |
| TI_RTI_TIMER_MASK(RTICOMPCTRL_COMP0SEL), |
| TI_RTI_TIMER_SHIFT(RTICOMPCTRL_COMP0SEL)); |
| |
| /* Clear Interrupt0 status */ |
| /* NOTE: Uses RTI Interrupt 0 (Interrupt generated by Compare Block 0) by default */ |
| ti_rti_timer_write_reg_fields(0, RTI_REG(RTIINTFLAG), TI_RTI_TIMER_MASK(RTIINTFLAG_INT0), |
| TI_RTI_TIMER_SHIFT(RTIINTFLAG_INT0)); |
| |
| /* Enable Interrupt0 */ |
| ti_rti_timer_write_reg_fields(1, RTI_REG(RTISETINT), TI_RTI_TIMER_MASK(RTISETINT_SETINT0), |
| TI_RTI_TIMER_SHIFT(RTISETINT_SETINT0)); |
| |
| /* Start the timer */ |
| ti_rti_timer_write_reg_fields(1, RTI_REG(RTIGCTRL), TI_RTI_TIMER_MASK(RTIGCTRL_CNT0EN), |
| TI_RTI_TIMER_SHIFT(RTIGCTRL_CNT0EN)); |
| |
| irq_enable(TIMER_IRQ_NUM); |
| |
| return 0; |
| } |
| |
| #define TI_RTI_TIMER(n) \ |
| static struct ti_rti_timer_data ti_rti_timer_data_##n; \ |
| static const struct ti_rti_timer_config ti_rti_timer_config_##n = { \ |
| DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ |
| }; \ |
| DEVICE_DT_INST_DEFINE(n, NULL, NULL, &ti_rti_timer_data_##n, &ti_rti_timer_config_##n, \ |
| PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY, NULL); |
| |
| DT_INST_FOREACH_STATUS_OKAY(TI_RTI_TIMER); |
| |
| SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); |