From 243d6a2ed1aab4e809cae93f5c3bb04092bfe40f Mon Sep 17 00:00:00 2001 From: Mahesh Mahadevan Date: Mon, 1 Mar 2021 10:57:23 -0600 Subject: [PATCH] drivers: timer: Add MCUX OS timer Add OS timer for the MCUX SoC's Signed-off-by: Mahesh Mahadevan --- drivers/timer/CMakeLists.txt | 1 + drivers/timer/Kconfig | 11 +++ drivers/timer/mcux_os_timer.c | 126 ++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 drivers/timer/mcux_os_timer.c diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt index 284ce1ef3d6..54289de7183 100644 --- a/drivers/timer/CMakeLists.txt +++ b/drivers/timer/CMakeLists.txt @@ -22,3 +22,4 @@ zephyr_sources_ifdef(CONFIG_CC13X2_CC26X2_RTC_TIMER cc13x2_cc26x2_rtc_timer.c) zephyr_sources_ifdef(CONFIG_CAVS_TIMER cavs_timer.c) zephyr_sources_ifdef(CONFIG_LEON_GPTIMER leon_gptimer.c) zephyr_sources_ifdef(CONFIG_NPCX_ITIM_TIMER npcx_itim_timer.c) +zephyr_sources_ifdef(CONFIG_MCUX_OS_TIMER mcux_os_timer.c) diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 5ab5e6ce7a8..ce572545b7e 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -351,4 +351,15 @@ config TICKLESS_CAPABLE sys_clock_announce() (really, not to produce an interrupt at all) until the specified expiration. +DT_COMPAT_NXP_OS_TIMER := nxp,os-timer + +config MCUX_OS_TIMER + bool "MCUX OS Event timer" + depends on HAS_MCUX_OS_TIMER + default $(dt_compat_enabled,$(DT_COMPAT_NXP_OS_TIMER)) + select TICKLESS_CAPABLE + help + This module implements a kernel device driver for the NXP OS + event timer and provides the standard "system clock driver" interfaces. + endmenu diff --git a/drivers/timer/mcux_os_timer.c b/drivers/timer/mcux_os_timer.c new file mode 100644 index 00000000000..91c4623c7f6 --- /dev/null +++ b/drivers/timer/mcux_os_timer.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_os_timer + +#include +#include +#include +#include "fsl_ostimer.h" + +#define CYC_PER_TICK ((uint32_t)((uint64_t)sys_clock_hw_cycles_per_sec() \ + / (uint64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC)) +#define MAX_CYC INT_MAX +#define MAX_TICKS ((MAX_CYC - CYC_PER_TICK) / CYC_PER_TICK) +#define MIN_DELAY 1000 + +#define TICKLESS IS_ENABLED(CONFIG_TICKLESS_KERNEL) + +static struct k_spinlock lock; +static uint64_t last_count; +static OSTIMER_Type *base; + +static void mcux_lpc_ostick_isr(void *arg) +{ + ARG_UNUSED(arg); + + k_spinlock_key_t key = k_spin_lock(&lock); + uint64_t now = OSTIMER_GetCurrentTimerValue(base); + uint32_t dticks = (uint32_t)((now - last_count) / CYC_PER_TICK); + + /* Clear interrupt flag by writing 1. */ + base->OSEVENT_CTRL &= ~OSTIMER_OSEVENT_CTRL_OSTIMER_INTENA_MASK; + + last_count += dticks * CYC_PER_TICK; + + if (!TICKLESS) { + uint64_t next = last_count + CYC_PER_TICK; + + if ((int64_t)(next - now) < MIN_DELAY) { + next += CYC_PER_TICK; + } + OSTIMER_SetMatchValue(base, next, NULL); + } + + k_spin_unlock(&lock, key); + sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? dticks : 1); +} + +int sys_clock_driver_init(const struct device *device) +{ + ARG_UNUSED(device); + + /* Configure event timer's ISR */ + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), + mcux_lpc_ostick_isr, NULL, 0); + + base = (OSTIMER_Type *)DT_INST_REG_ADDR(0); + + /* Initialize the OS timer, setting clock configuration. */ + OSTIMER_Init(base); + + last_count = OSTIMER_GetCurrentTimerValue(base); + OSTIMER_SetMatchValue(base, last_count + CYC_PER_TICK, NULL); + + /* Enable event timer interrupt */ + irq_enable(DT_INST_IRQN(0)); + + return 0; +} + +void sys_clock_set_timeout(int32_t ticks, bool idle) +{ + ARG_UNUSED(idle); + + if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { + /* Only for tickless kernel system */ + return; + } + + ticks = ticks == K_TICKS_FOREVER ? MAX_TICKS : ticks; + ticks = CLAMP(ticks - 1, 0, (int32_t)MAX_TICKS); + + k_spinlock_key_t key = k_spin_lock(&lock); + uint64_t now = OSTIMER_GetCurrentTimerValue(base); + uint32_t adj, cyc = ticks * CYC_PER_TICK; + + /* Round up to next tick boundary. */ + adj = (uint32_t)(now - last_count) + (CYC_PER_TICK - 1); + if (cyc <= MAX_CYC - adj) { + cyc += adj; + } else { + cyc = MAX_CYC; + } + cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK; + + if ((int32_t)(cyc + last_count - now) < MIN_DELAY) { + cyc += CYC_PER_TICK; + } + + OSTIMER_SetMatchValue(base, cyc + last_count, NULL); + + k_spin_unlock(&lock, key); +} + +uint32_t 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 ret = ((uint32_t)OSTIMER_GetCurrentTimerValue(base) - + (uint32_t)last_count) / CYC_PER_TICK; + + k_spin_unlock(&lock, key); + return ret; +} + +uint32_t sys_clock_cycle_get_32(void) +{ + return (uint32_t)OSTIMER_GetCurrentTimerValue(base); +}