/* * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include void _ExcExit(void); /* Minimum cycles in the future to try to program. */ #define MIN_DELAY 512 #define COUNTER_MAX 0x00ffffff #define TIMER_STOPPED 0xff000000 #define CYC_PER_TICK (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC \ / CONFIG_SYS_CLOCK_TICKS_PER_SEC) #define MAX_TICKS ((COUNTER_MAX / CYC_PER_TICK) - 1) #define MAX_CYCLES (MAX_TICKS * CYC_PER_TICK) #define TICKLESS (IS_ENABLED(CONFIG_TICKLESS_KERNEL) && \ !IS_ENABLED(CONFIG_QEMU_TICKLESS_WORKAROUND)) static struct k_spinlock lock; static u32_t last_load; static u32_t cycle_count; static u32_t announced_cycles; static volatile u32_t ctrl_cache; /* overflow bit clears on read! */ static u32_t elapsed(void) { u32_t val, ov; do { val = SysTick->VAL & COUNTER_MAX; ctrl_cache |= SysTick->CTRL; } while (SysTick->VAL > val); ov = (ctrl_cache & SysTick_CTRL_COUNTFLAG_Msk) ? last_load : 0; return (last_load - val) + ov; } /* Callout out of platform assembly, not hooked via IRQ_CONNECT... */ void _timer_int_handler(void *arg) { ARG_UNUSED(arg); u32_t dticks; cycle_count += last_load; dticks = (cycle_count - announced_cycles) / CYC_PER_TICK; announced_cycles += dticks * CYC_PER_TICK; ctrl_cache = SysTick->CTRL; /* Reset overflow flag */ ctrl_cache = 0U; z_clock_announce(TICKLESS ? dticks : 1); _ExcExit(); } int z_clock_driver_init(struct device *device) { NVIC_SetPriority(SysTick_IRQn, _IRQ_PRIO_OFFSET); last_load = CYC_PER_TICK; SysTick->LOAD = last_load; SysTick->VAL = 0; /* resets timer to last_load */ SysTick->CTRL |= (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_CLKSOURCE_Msk); return 0; } void z_clock_set_timeout(s32_t ticks, bool idle) { /* Fast CPUs and a 24 bit counter mean that even idle systems * need to wake up multiple times per second. If the kernel * allows us to miss tick announcements in idle, then shut off * the counter. (Note: we can assume if idle==true that * interrupts are already disabled) */ if (IS_ENABLED(CONFIG_TICKLESS_IDLE) && idle && ticks == K_FOREVER) { SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; last_load = TIMER_STOPPED; return; } #if defined(CONFIG_TICKLESS_KERNEL) && !defined(CONFIG_QEMU_TICKLESS_WORKAROUND) u32_t delay; ticks = min(MAX_TICKS, max(ticks - 1, 0)); /* Desired delay in the future */ delay = (ticks == 0) ? MIN_DELAY : ticks * CYC_PER_TICK; k_spinlock_key_t key = k_spin_lock(&lock); cycle_count += elapsed(); /* Round delay up to next tick boundary */ delay = delay + (cycle_count - announced_cycles); delay = ((delay + CYC_PER_TICK - 1) / CYC_PER_TICK) * CYC_PER_TICK; last_load = delay - (cycle_count - announced_cycles); SysTick->LOAD = last_load; SysTick->VAL = 0; /* resets timer to last_load */ k_spin_unlock(&lock, key); #endif } u32_t z_clock_elapsed(void) { if (!TICKLESS) { return 0; } k_spinlock_key_t key = k_spin_lock(&lock); u32_t cyc = elapsed() + cycle_count - announced_cycles; k_spin_unlock(&lock, key); return cyc / CYC_PER_TICK; } u32_t _timer_cycle_get_32(void) { k_spinlock_key_t key = k_spin_lock(&lock); u32_t ret = elapsed() + cycle_count; k_spin_unlock(&lock, key); return ret; } void z_clock_idle_exit(void) { if (last_load == TIMER_STOPPED) { SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; } } void sys_clock_disable(void) { SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; }