blob: 7e2a2664d5ea857cd446a4d832669ea8c961c00f [file]
/* 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);