driver: timer: npcx: fix announce/set timer timeout tick

The timer driver doesn't annouce/set the timeout at the tick boundary
but at the absolute next expiration time.
It will cause the accumatlation of the tick drift and cannot pass the
kernel/timer/timer_behavior test suite.
This commit fixes the tick drift problem by annouce the time at the tick
bouandry.

Fixes #59594

Signed-off-by: Jun Lin <CHLin56@nuvoton.com>
This commit is contained in:
Jun Lin 2023-10-30 15:14:12 +08:00 committed by Fabio Baltieri
parent dfc747d53a
commit 4cfd4b5379

View file

@ -58,6 +58,7 @@ LOG_MODULE_REGISTER(itim, LOG_LEVEL_ERR);
#define NPCX_ITIM_CLK_SEL_DELAY 92 /* Delay for clock selection (Unit:us) */
/* Timeout for enabling ITIM module: 100us (Unit:cycles) */
#define NPCX_ITIM_EN_TIMEOUT_CYCLES (100 * SYS_CYCLES_PER_USEC)
#define SYS_CYC_PER_EVT_CYC (sys_clock_hw_cycles_per_sec() / EVT_CYCLES_PER_SEC)
/* Instance of system and event timers */
static struct itim64_reg *const sys_tmr = (struct itim64_reg *)
@ -70,6 +71,9 @@ static const struct npcx_clk_cfg itim_clk_cfg[] = NPCX_DT_CLK_CFG_ITEMS_LIST(0);
static struct k_spinlock lock;
/* Announced cycles in system timer before executing sys_clock_announce() */
static uint64_t cyc_sys_announced;
static uint64_t last_ticks;
static uint32_t last_elapsed;
/* Current target cycles of time-out signal in event timer */
static uint32_t cyc_evt_timeout;
/* Total cycles of system timer stopped in "sleep/deep sleep" mode */
@ -136,18 +140,33 @@ static inline void npcx_itim_evt_disable(void)
/* ITIM local functions */
static int npcx_itim_start_evt_tmr_by_tick(int32_t ticks)
{
k_spinlock_key_t key = k_spin_lock(&lock);
/*
* Get desired cycles of event timer from the requested ticks which
* round up to next tick boundary.
*/
if (ticks == K_TICKS_FOREVER) {
cyc_evt_timeout = NPCX_ITIM32_MAX_CNT;
} else {
uint64_t next_cycs;
uint64_t curr = npcx_itim_get_sys_cyc64();
uint32_t dcycles;
if (ticks <= 0) {
ticks = 1;
}
cyc_evt_timeout = MIN(EVT_CYCLES_FROM_TICKS(ticks),
NPCX_ITIM32_MAX_CNT);
next_cycs = (last_ticks + last_elapsed + ticks) * SYS_CYCLES_PER_TICK;
if (unlikely(next_cycs <= curr)) {
cyc_evt_timeout = 1;
} else {
dcycles = next_cycs - curr;
cyc_evt_timeout =
CLAMP((dcycles / SYS_CYC_PER_EVT_CYC), 1, NPCX_ITIM32_MAX_CNT);
}
}
LOG_DBG("ticks %x, cyc_evt_timeout %x", ticks, cyc_evt_timeout);
@ -159,9 +178,9 @@ static int npcx_itim_start_evt_tmr_by_tick(int32_t ticks)
/* Upload counter of event timer */
evt_tmr->ITCNT32 = MAX(cyc_evt_timeout - 1, 1);
k_spin_unlock(&lock, key);
/* Enable event timer and start ticking */
return npcx_itim_evt_enable();
}
static void npcx_itim_evt_isr(const struct device *dev)
@ -175,11 +194,12 @@ static void npcx_itim_evt_isr(const struct device *dev)
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
k_spinlock_key_t key = k_spin_lock(&lock);
uint32_t delta_ticks = (uint32_t)((npcx_itim_get_sys_cyc64() -
cyc_sys_announced) / SYS_CYCLES_PER_TICK);
uint64_t curr = npcx_itim_get_sys_cyc64();
uint32_t delta_ticks = (uint32_t)((curr - cyc_sys_announced) / SYS_CYCLES_PER_TICK);
/* Store announced cycles of system timer */
cyc_sys_announced = npcx_itim_get_sys_cyc64();
cyc_sys_announced += delta_ticks * SYS_CYCLES_PER_TICK;
last_ticks += delta_ticks;
last_elapsed = 0;
k_spin_unlock(&lock, key);
/* Informs kernel that specified number of ticks have elapsed */
@ -252,11 +272,13 @@ uint32_t sys_clock_elapsed(void)
k_spinlock_key_t key = k_spin_lock(&lock);
uint64_t delta_cycle = npcx_itim_get_sys_cyc64() - cyc_sys_announced;
uint32_t delta_ticks = (uint32_t)delta_cycle / SYS_CYCLES_PER_TICK;
last_elapsed = delta_ticks;
k_spin_unlock(&lock, key);
/* Return how many ticks elapsed since last sys_clock_announce() call */
return (uint32_t)(delta_cycle / SYS_CYCLES_PER_TICK);
return delta_ticks;
}
uint32_t sys_clock_cycle_get_32(void)