|  | /* | 
|  | * Copyright (c) 2017 Oticon A/S | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * Driver for the timer model of the POSIX native_posix board | 
|  | * It provides the interfaces required by the kernel and the sanity testcases | 
|  | * It also provides a custom k_busy_wait() which can be used with the | 
|  | * POSIX arch and InfClock SOC | 
|  | */ | 
|  |  | 
|  | #include "zephyr/types.h" | 
|  | #include "irq.h" | 
|  | #include "device.h" | 
|  | #include <drivers/timer/system_timer.h> | 
|  | #include "sys_clock.h" | 
|  | #include "timer_model.h" | 
|  | #include "soc.h" | 
|  | #include "posix_trace.h" | 
|  |  | 
|  | #include "legacy_api.h" | 
|  |  | 
|  | static u64_t tick_period; /* System tick period in number of hw cycles */ | 
|  | static s64_t silent_ticks; | 
|  | static s32_t _sys_idle_elapsed_ticks = 1; | 
|  |  | 
|  | /** | 
|  | * Return the current HW cycle counter | 
|  | * (number of microseconds since boot in 32bits) | 
|  | */ | 
|  | u32_t z_timer_cycle_get_32(void) | 
|  | { | 
|  | return hwm_get_time(); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_TICKLESS_IDLE | 
|  |  | 
|  | /* | 
|  | * Do not raise another ticker interrupt until the sys_ticks'th one | 
|  | * e.g. if sys_ticks is 10, do not raise the next 9 ones | 
|  | * | 
|  | * if sys_ticks is K_FOREVER (or another negative number), | 
|  | * we will effectively silence the tick interrupts forever | 
|  | */ | 
|  | void z_timer_idle_enter(s32_t sys_ticks) | 
|  | { | 
|  | if (silent_ticks > 0) { /* LCOV_EXCL_BR_LINE */ | 
|  | /* LCOV_EXCL_START */ | 
|  | posix_print_warning("native timer: Re-entering idle mode with " | 
|  | "%i ticks pending\n", | 
|  | silent_ticks); | 
|  | z_clock_idle_exit(); | 
|  | /* LCOV_EXCL_STOP */ | 
|  | } | 
|  | if (sys_ticks < 0) { | 
|  | silent_ticks = INT64_MAX; | 
|  | } else if (sys_ticks > 0) { | 
|  | silent_ticks = sys_ticks - 1; | 
|  | } else { | 
|  | silent_ticks = 0; | 
|  | } | 
|  | hwtimer_set_silent_ticks(silent_ticks); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Exit from idle mode | 
|  | * | 
|  | * If we have been silent for a number of ticks, announce immediately to the | 
|  | * kernel how many silent ticks have passed. | 
|  | * If this is called due to the 1st non silent timer interrupt, sp_timer_isr() | 
|  | * will be called right away, which will announce that last (non silent) one. | 
|  | * | 
|  | * Note that we do not assume this function is called before the interrupt is | 
|  | * raised (the interrupt can handle it announcing all ticks) | 
|  | */ | 
|  | void z_clock_idle_exit(void) | 
|  | { | 
|  | silent_ticks -= hwtimer_get_pending_silent_ticks(); | 
|  | if (silent_ticks > 0) { | 
|  | _sys_idle_elapsed_ticks = silent_ticks; | 
|  | z_clock_announce(_sys_idle_elapsed_ticks); | 
|  | } | 
|  | silent_ticks = 0; | 
|  | hwtimer_set_silent_ticks(0); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /** | 
|  | * Interrupt handler for the timer interrupt | 
|  | * Announce to the kernel that a tick has passed | 
|  | */ | 
|  | static void sp_timer_isr(void *arg) | 
|  | { | 
|  | ARG_UNUSED(arg); | 
|  | _sys_idle_elapsed_ticks = silent_ticks + 1; | 
|  | silent_ticks = 0; | 
|  | z_clock_announce(_sys_idle_elapsed_ticks); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Enable the hw timer, setting its tick period, and setup its interrupt | 
|  | */ | 
|  | int z_clock_driver_init(struct device *device) | 
|  | { | 
|  | ARG_UNUSED(device); | 
|  |  | 
|  | tick_period = 1000000ul / CONFIG_SYS_CLOCK_TICKS_PER_SEC; | 
|  |  | 
|  | hwtimer_enable(tick_period); | 
|  |  | 
|  | IRQ_CONNECT(TIMER_TICK_IRQ, 1, sp_timer_isr, 0, 0); | 
|  | irq_enable(TIMER_TICK_IRQ); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | #if defined(CONFIG_ARCH_HAS_CUSTOM_BUSY_WAIT) | 
|  | /** | 
|  | * Replacement to the kernel k_busy_wait() | 
|  | * Will block this thread (and therefore the whole zephyr) during usec_to_wait | 
|  | * | 
|  | * Note that interrupts may be received in the meanwhile and that therefore this | 
|  | * thread may loose context | 
|  | */ | 
|  | void z_arch_busy_wait(u32_t usec_to_wait) | 
|  | { | 
|  | u64_t time_end = hwm_get_time() + usec_to_wait; | 
|  |  | 
|  | while (hwm_get_time() < time_end) { | 
|  | /*There may be wakes due to other interrupts*/ | 
|  | hwtimer_wake_in_time(time_end); | 
|  | posix_halt_cpu(); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if defined(CONFIG_SYSTEM_CLOCK_DISABLE) | 
|  | /** | 
|  | * | 
|  | * @brief Stop announcing sys ticks into the kernel | 
|  | * | 
|  | * Disable the system ticks generation | 
|  | * | 
|  | * @return N/A | 
|  | */ | 
|  | void sys_clock_disable(void) | 
|  | { | 
|  | irq_disable(TIMER_TICK_IRQ); | 
|  | hwtimer_set_silent_ticks(INT64_MAX); | 
|  | } | 
|  | #endif /* CONFIG_SYSTEM_CLOCK_DISABLE */ |