diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index c44acda2ae6c6..dfad6c9f27302 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -31,6 +31,7 @@ zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c) zephyr_library_sources_ifdef(CONFIG_RV32M1_LPTMR_TIMER rv32m1_lptmr_timer.c) zephyr_library_sources_ifdef(CONFIG_SAM0_RTC_TIMER sam0_rtc_timer.c) zephyr_library_sources_ifdef(CONFIG_STM32_LPTIM_TIMER stm32_lptim_timer.c) +zephyr_library_sources_ifdef(CONFIG_TI_DM_TIMER ti_dmtimer.c) zephyr_library_sources_ifdef(CONFIG_XLNX_PSTTC_TIMER xlnx_psttc_timer.c) zephyr_library_sources_ifdef(CONFIG_XTENSA_TIMER xtensa_sys_timer.c) zephyr_library_sources_ifdef(CONFIG_SMARTBOND_TIMER smartbond_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 2ddfe321e77e4..38bb3c575de1d 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -92,6 +92,7 @@ source "drivers/timer/Kconfig.rv32m1_lptmr" source "drivers/timer/Kconfig.sam0_rtc" source "drivers/timer/Kconfig.smartbond" source "drivers/timer/Kconfig.stm32_lptim" +source "drivers/timer/Kconfig.ti_dm_timer" source "drivers/timer/Kconfig.xlnx_psttc" source "drivers/timer/Kconfig.xtensa" diff --git a/drivers/timer/Kconfig.ti_dm_timer b/drivers/timer/Kconfig.ti_dm_timer new file mode 100644 index 0000000000000..42b86e5c6ef8f --- /dev/null +++ b/drivers/timer/Kconfig.ti_dm_timer @@ -0,0 +1,13 @@ +# Copyright (C) 2023 BeagleBoard.org Foundation +# Copyright (C) 2023 S Prashanth +# +# SPDX-License-Identifier: Apache-2.0 + +config TI_DM_TIMER + bool "TI Dual-Mode Timer" + default y + depends on DT_HAS_TI_AM654_TIMER_ENABLED + select TICKLESS_CAPABLE + help + This module implements a kernel device driver for TI Dual-Mode timer. This + driver provides system tick interface. diff --git a/drivers/timer/ti_dmtimer.c b/drivers/timer/ti_dmtimer.c new file mode 100644 index 0000000000000..7b04b92788a21 --- /dev/null +++ b/drivers/timer/ti_dmtimer.c @@ -0,0 +1,167 @@ +/* Copyright (C) 2023 BeagleBoard.org Foundation + * Copyright (C) 2023 S Prashanth + * Copyright (c) 2024 Texas Instruments Incorporated + * Andrew Davis + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define DT_DRV_COMPAT ti_am654_timer + +#define TIMER_BASE_ADDR DT_INST_REG_ADDR(0) + +#define TIMER_IRQ_NUM DT_INST_IRQN(0) +#define TIMER_IRQ_PRIO DT_INST_IRQ(0, priority) +#define TIMER_IRQ_FLAGS DT_INST_IRQ(0, flags) + +#define CYC_PER_TICK ((uint32_t)(sys_clock_hw_cycles_per_sec() \ + / CONFIG_SYS_CLOCK_TICKS_PER_SEC)) + +#define MAX_TICKS ((k_ticks_t)(UINT32_MAX / CYC_PER_TICK) - 1) + +static struct k_spinlock lock; + +static uint32_t last_cycle; + +#define TI_DM_TIMER_READ(reg) sys_read32(TIMER_BASE_ADDR + TI_DM_TIMER_ ## reg) + +#define TI_DM_TIMER_MASK(reg) TI_DM_TIMER_ ## reg ## _MASK +#define TI_DM_TIMER_SHIFT(reg) TI_DM_TIMER_ ## reg ## _SHIFT +#define TI_DM_TIMER_WRITE(data, reg, bits) \ + ti_dm_timer_write_masks(data, \ + TIMER_BASE_ADDR + TI_DM_TIMER_ ## reg, \ + TI_DM_TIMER_MASK(reg ## _ ## bits), \ + TI_DM_TIMER_SHIFT(reg ## _ ## bits)) + +static void ti_dm_timer_write_masks(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 << shift); + sys_write32(reg_val, reg); +} + +static void ti_dmtimer_isr(void *data) +{ + /* If no pending event */ + if (!TI_DM_TIMER_READ(IRQSTATUS)) + return; + + k_spinlock_key_t key = k_spin_lock(&lock); + + uint32_t curr_cycle = TI_DM_TIMER_READ(TCRR); + uint32_t delta_cycles = curr_cycle - last_cycle; + uint32_t delta_ticks = delta_cycles / CYC_PER_TICK; + + last_cycle = curr_cycle; + + /* ACK match interrupt */ + TI_DM_TIMER_WRITE(1, IRQSTATUS, MAT_IT_FLAG); + + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + /* Setup next match time */ + uint64_t next_cycle = curr_cycle + CYC_PER_TICK; + + TI_DM_TIMER_WRITE(next_cycle, TMAR, COMPARE_VALUE); + } + + k_spin_unlock(&lock, key); + + sys_clock_announce(delta_ticks); +} + +void sys_clock_set_timeout(int32_t ticks, bool idle) +{ + ARG_UNUSED(idle); + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + /* Not supported on tickful kernels */ + return; + } + + ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : ticks; + ticks = CLAMP(ticks, 1, (int32_t)MAX_TICKS); + + k_spinlock_key_t key = k_spin_lock(&lock); + + /* Setup next match time */ + uint32_t curr_cycle = TI_DM_TIMER_READ(TCRR); + uint32_t next_cycle = curr_cycle + (ticks * CYC_PER_TICK); + + TI_DM_TIMER_WRITE(next_cycle, TMAR, COMPARE_VALUE); + + k_spin_unlock(&lock, key); +} + +uint32_t sys_clock_cycle_get_32(void) +{ + k_spinlock_key_t key = k_spin_lock(&lock); + + uint32_t curr_cycle = TI_DM_TIMER_READ(TCRR); + + k_spin_unlock(&lock, key); + + return curr_cycle; +} + +unsigned int sys_clock_elapsed(void) +{ + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + /* Always return 0 for tickful kernel system */ + return 0; + } + + k_spinlock_key_t key = k_spin_lock(&lock); + + uint32_t curr_cycle = TI_DM_TIMER_READ(TCRR); + uint32_t delta_cycles = curr_cycle - last_cycle; + uint32_t delta_ticks = delta_cycles / CYC_PER_TICK; + + k_spin_unlock(&lock, key); + + return delta_ticks; +} + +static int sys_clock_driver_init(void) +{ + last_cycle = 0; + + IRQ_CONNECT(TIMER_IRQ_NUM, TIMER_IRQ_PRIO, ti_dmtimer_isr, NULL, TIMER_IRQ_FLAGS); + + /* Select autoreload mode */ + TI_DM_TIMER_WRITE(1, TCLR, AR); + + /* Enable match interrupt */ + TI_DM_TIMER_WRITE(1, IRQENABLE_SET, MAT_EN_FLAG); + + /* Load timer counter value */ + TI_DM_TIMER_WRITE(0, TCRR, TIMER_COUNTER); + + /* Load timer load value */ + TI_DM_TIMER_WRITE(0, TLDR, LOAD_VALUE); + + /* Load timer compare value */ + TI_DM_TIMER_WRITE(CYC_PER_TICK, TMAR, COMPARE_VALUE); + + /* Enable compare mode */ + TI_DM_TIMER_WRITE(1, TCLR, CE); + + /* Start the timer */ + TI_DM_TIMER_WRITE(1, TCLR, ST); + + irq_enable(TIMER_IRQ_NUM); + + return 0; +} + +SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, + CONFIG_SYSTEM_CLOCK_INIT_PRIORITY); diff --git a/dts/bindings/timer/ti,am654-dmtimer.yaml b/dts/bindings/timer/ti,am654-dmtimer.yaml new file mode 100644 index 0000000000000..f2fc8167aba51 --- /dev/null +++ b/dts/bindings/timer/ti,am654-dmtimer.yaml @@ -0,0 +1,16 @@ +# Copyright (C) 2023 BeagleBoard.org Foundation +# Copyright (C) 2023 S Prashanth +# SPDX-License-Identifier: Apache-2.0 + +description: TI Dual-Mode Timer + +compatible: "ti,am654-timer" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/include/zephyr/drivers/timer/ti_dmtimer.h b/include/zephyr/drivers/timer/ti_dmtimer.h new file mode 100644 index 0000000000000..95f39390fc1f0 --- /dev/null +++ b/include/zephyr/drivers/timer/ti_dmtimer.h @@ -0,0 +1,104 @@ +/* Copyright (C) 2023 BeagleBoard.org Foundation + * Copyright (C) 2023 S Prashanth + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_TIMERS_TI_DMTIMER_H_ +#define ZEPHYR_DRIVERS_TIMERS_TI_DMTIMER_H_ + +#include + +#define TI_DM_TIMER_TIDR (0x00) +#define TI_DM_TIMER_TIOCP_CFG (0x10) +#define TI_DM_TIMER_IRQ_EOI (0x20) +#define TI_DM_TIMER_IRQSTATUS_RAW (0x24) +#define TI_DM_TIMER_IRQSTATUS (0x28) /* Interrupt status register */ +#define TI_DM_TIMER_IRQENABLE_SET (0x2c) /* Interrupt enable register */ +#define TI_DM_TIMER_IRQENABLE_CLR (0x30) /* Interrupt disable register */ +#define TI_DM_TIMER_IRQWAKEEN (0x34) +#define TI_DM_TIMER_TCLR (0x38) /* Control register */ +#define TI_DM_TIMER_TCRR (0x3c) /* Counter register */ +#define TI_DM_TIMER_TLDR (0x40) /* Load register */ +#define TI_DM_TIMER_TTGR (0x44) +#define TI_DM_TIMER_TWPS (0x48) +#define TI_DM_TIMER_TMAR (0x4c) /* Match register */ +#define TI_DM_TIMER_TCAR1 (0x50) +#define TI_DM_TIMER_TSICR (0x54) +#define TI_DM_TIMER_TCAR2 (0x58) +#define TI_DM_TIMER_TPIR (0x5c) +#define TI_DM_TIMER_TNIR (0x60) +#define TI_DM_TIMER_TCVR (0x64) +#define TI_DM_TIMER_TOCR (0x68) +#define TI_DM_TIMER_TOWR (0x6c) + +#define TI_DM_TIMER_IRQSTATUS_MAT_IT_FLAG_SHIFT (0) +#define TI_DM_TIMER_IRQSTATUS_MAT_IT_FLAG_MASK (0x00000001) + +#define TI_DM_TIMER_IRQSTATUS_OVF_IT_FLAG_SHIFT (1) +#define TI_DM_TIMER_IRQSTATUS_OVF_IT_FLAG_MASK (0x00000002) + +#define TI_DM_TIMER_IRQSTATUS_TCAR_IT_FLAG_SHIFT (2) +#define TI_DM_TIMER_IRQSTATUS_TCAR_IT_FLAG_MASK (0x00000004) + +#define TI_DM_TIMER_IRQENABLE_SET_MAT_EN_FLAG_SHIFT (0) +#define TI_DM_TIMER_IRQENABLE_SET_MAT_EN_FLAG_MASK (0x00000001) + +#define TI_DM_TIMER_IRQENABLE_SET_OVF_EN_FLAG_SHIFT (1) +#define TI_DM_TIMER_IRQENABLE_SET_OVF_EN_FLAG_MASK (0x00000002) + +#define TI_DM_TIMER_IRQENABLE_SET_TCAR_EN_FLAG_SHIFT (2) +#define TI_DM_TIMER_IRQENABLE_SET_TCAR_EN_FLAG_MASK (0x00000004) + +#define TI_DM_TIMER_IRQENABLE_CLR_MAT_EN_FLAG_SHIFT (0) +#define TI_DM_TIMER_IRQENABLE_CLR_MAT_EN_FLAG_MASK (0x00000001) + +#define TI_DM_TIMER_IRQENABLE_CLR_OVF_EN_FLAG_SHIFT (1) +#define TI_DM_TIMER_IRQENABLE_CLR_OVF_EN_FLAG_MASK (0x00000002) + +#define TI_DM_TIMER_IRQENABLE_CLR_TCAR_EN_FLAG_SHIFT (2) +#define TI_DM_TIMER_IRQENABLE_CLR_TCAR_EN_FLAG_MASK (0x00000004) + +#define TI_DM_TIMER_TCLR_ST_SHIFT (0) +#define TI_DM_TIMER_TCLR_ST_MASK (0x00000001) + +#define TI_DM_TIMER_TCLR_AR_SHIFT (1) +#define TI_DM_TIMER_TCLR_AR_MASK (0x00000002) + +#define TI_DM_TIMER_TCLR_PTV_SHIFT (2) +#define TI_DM_TIMER_TCLR_PTV_MASK (0x0000001c) + +#define TI_DM_TIMER_TCLR_PRE_SHIFT (5) +#define TI_DM_TIMER_TCLR_PRE_MASK (0x00000020) + +#define TI_DM_TIMER_TCLR_CE_SHIFT (6) +#define TI_DM_TIMER_TCLR_CE_MASK (0x00000040) + +#define TI_DM_TIMER_TCLR_SCPWM_SHIFT (7) +#define TI_DM_TIMER_TCLR_SCPWM_MASK (0x00000080) + +#define TI_DM_TIMER_TCLR_TCM_SHIFT (8) +#define TI_DM_TIMER_TCLR_TCM_MASK (0x00000300) + +#define TI_DM_TIMER_TCLR_TRG_SHIFT (10) +#define TI_DM_TIMER_TCLR_TRG_MASK (0x00000c00) + +#define TI_DM_TIMER_TCLR_PT_SHIFT (12) +#define TI_DM_TIMER_TCLR_PT_MASK (0x00001000) + +#define TI_DM_TIMER_TCLR_CAPT_MODE_SHIFT (13) +#define TI_DM_TIMER_TCLR_CAPT_MODE_MASK (0x00002000) + +#define TI_DM_TIMER_TCLR_GPO_CFG_SHIFT (14) +#define TI_DM_TIMER_TCLR_GPO_CFG_MASK (0x00004000) + +#define TI_DM_TIMER_TCRR_TIMER_COUNTER_SHIFT (0) +#define TI_DM_TIMER_TCRR_TIMER_COUNTER_MASK (0xffffffff) + +#define TI_DM_TIMER_TLDR_LOAD_VALUE_SHIFT (0) +#define TI_DM_TIMER_TLDR_LOAD_VALUE_MASK (0xffffffff) + +#define TI_DM_TIMER_TMAR_COMPARE_VALUE_SHIFT (0) +#define TI_DM_TIMER_TMAR_COMPARE_VALUE_MASK (0xffffffff) + +#endif /* ZEPHYR_DRIVERS_TIMERS_TI_DMTIMER_H_ */