timer: xtensa_sys_timer: Tickless Kernel Implementation for Xtensa

Implement Tickless Kernel support for Xtensa Architecture.

Signed-off-by: Youvedeep Singh <youvedeep.singh@intel.com>
This commit is contained in:
Youvedeep Singh 2017-10-27 21:38:42 +05:30 committed by Anas Nashif
commit 833025dd94
3 changed files with 182 additions and 6 deletions

View file

@ -128,7 +128,12 @@
* RTOS may optionally define XT_TICK_PER_SEC in its own way (eg. macro).
*/
#define XT_RTOS_TIMER_INT _zxt_timer_int
#define XT_TICK_PER_SEC CONFIG_SYS_CLOCK_TICKS_PER_SEC
#if CONFIG_TICKLESS_KERNEL
#define XT_TICK_PER_SEC 1000
#else
#define XT_TICK_PER_SEC CONFIG_SYS_CLOCK_TICKS_PER_SEC
#endif /* CONFIG_TICKLESS_KERNEL */
/*
* Return in a15 the base address of the co-processor state save area for the

View file

@ -127,8 +127,12 @@
* "-DXT_TICK_PER_SEC=<value>" where <value> is a suitable number.
*/
#ifndef XT_TICK_PER_SEC
#define XT_TICK_PER_SEC CONFIG_SYS_CLOCK_TICKS_PER_SEC
#endif
#if CONFIG_TICKLESS_KERNEL
#define XT_TICK_PER_SEC 1000 /* In tickless kernel 1TICK = 1msec */
#else
#define XT_TICK_PER_SEC CONFIG_SYS_CLOCK_TICKS_PER_SEC
#endif /* CONFIG_TICKLESS_KERNEL */
#endif /* XT_TICK_PER_SEC */
/*
* Derivation of clock divisor for timer tick and interrupt (one per tick).

View file

@ -31,6 +31,7 @@
* according to his HW.
*/
#define MAX_TIMER_CYCLES 0xFFFFFFFF
/* Abstraction macros to access the timer fire time register */
#if CONFIG_XTENSA_INTERNAL_TIMER || (CONFIG_XTENSA_TIMER_IRQ < 0)
#define _XT_SR_CCOMPARE(op, idx) XT_##op##SR_CCOMPARE##idx
@ -99,14 +100,142 @@ static u32_t __noinit cycles_per_tick;
static u32_t __noinit max_system_ticks;
static u32_t idle_original_ticks;
static u32_t __noinit max_load_value;
#ifdef CONFIG_TICKLESS_KERNEL
static u32_t last_timer_value;
#else
static unsigned char timer_mode = TIMER_MODE_PERIODIC;
static unsigned char idle_mode = IDLE_NOT_TICKLESS;
#endif /* CONFIG_TICKLESS_KERNEL */
#ifdef CONFIG_TICKLESS_KERNEL
/* provides total programmed in tick count. */
u32_t _get_program_time(void)
{
return idle_original_ticks;
}
/* Timer Clock Ticks remaining for timer to expire. */
u32_t _get_remaining_program_time(void)
{
u32_t c; /* Current time (time within this function execution) */
u32_t f; /* Idle timer programmed fire time */
u32_t r; /*remaining time to the timer to expire */
if (!idle_original_ticks) {
return 0;
}
f = GET_TIMER_FIRE_TIME();
c = GET_TIMER_CURRENT_TIME();
r = f > c ? (f - c) / cycles_per_tick : 0;
return r;
}
/* Total number of timer ticks passed since last Timer program. */
u32_t _get_elapsed_program_time(void)
{
if (!idle_original_ticks) {
return 0;
}
return (_get_program_time() - _get_remaining_program_time());
}
/* Returns number of clocks Cycles remaining for timer to overflow. */
static inline int32_t _get_max_clock_time(void)
{
u32_t C;
C = GET_TIMER_CURRENT_TIME();
return (MAX_TIMER_CYCLES - C);
}
static inline void _set_max_clock_time(void)
{
int key;
key = irq_lock();
_sys_clock_tick_count = _get_elapsed_clock_time();
last_timer_value = GET_TIMER_CURRENT_TIME();
irq_unlock(key);
SET_TIMER_FIRE_TIME(MAX_TIMER_CYCLES); /* Program timer to max value */
}
/*
* This Function does following:-
* 1. Updates expected system ticks equal to time.
* 2. Update kernel time book keeping for time passed since device bootup.
* 3. Calls routine to set interrupt.
*/
void _set_time(u32_t time)
{
u32_t C; /* (current) time */
u32_t F; /* Time to program */
int key;
if (!time) {
idle_original_ticks = 0;
return;
}
key = irq_lock();
/* Update System Level Ticks Time Keeping */
_sys_clock_tick_count = _get_elapsed_clock_time();
C = GET_TIMER_CURRENT_TIME();
last_timer_value = C;
irq_unlock(key);
/* Track TICKs to program, this is required at next timer interrupt */
idle_original_ticks = time;
/* Track timer Overflow Case */
if (idle_original_ticks >= (_get_max_clock_time() / cycles_per_tick)) {
F = MAX_TIMER_CYCLES;
idle_original_ticks = (F - C) / cycles_per_tick;
}
/* Calculate tiring time */
F = (C + (idle_original_ticks * cycles_per_tick));
/* Program firing timer */
SET_TIMER_FIRE_TIME(F);
}
/*
* This is used to program Timer clock to maximum Clock cycles in case Clock to
* remain On.
*/
void _enable_sys_clock(void)
{
if (!idle_original_ticks) {
/* Program sys tick to maximum possible value */
_set_time(_get_max_clock_time());
}
}
/* Total number of ticks passed since device bootup. */
u64_t _get_elapsed_clock_time(void)
{
u32_t C;
int key;
u64_t total;
u32_t elapsed;
key = irq_lock();
C = GET_TIMER_CURRENT_TIME();
elapsed = (last_timer_value <= C) ? (C - last_timer_value) :
(MAX_TIMER_CYCLES - last_timer_value) + C;
total = (_sys_clock_tick_count + (elapsed / cycles_per_tick));
irq_unlock(key);
return total;
}
#endif /* CONFIG_TICKLESS_KERNEL */
static ALWAYS_INLINE void tickless_idle_init(void)
{
cycles_per_tick = sys_clock_hw_cycles_per_tick;
/* calculate the max number of ticks with this 32-bit H/W counter */
max_system_ticks = 0xffffffff / cycles_per_tick;
max_system_ticks = MAX_TIMER_CYCLES / cycles_per_tick;
max_load_value = max_system_ticks * cycles_per_tick;
}
@ -122,6 +251,18 @@ static ALWAYS_INLINE void tickless_idle_init(void)
void _timer_idle_enter(s32_t ticks)
{
#ifdef CONFIG_TICKLESS_KERNEL
if (idle_original_ticks != K_FOREVER) {
/* Need to reprograme timer if current program is smaller */
if (ticks > idle_original_ticks) {
_set_time(ticks);
}
} else {
idle_original_ticks = 0;
/* Set time to largest possile Timer Tick */
_set_max_clock_time();
}
#else
u32_t P; /* Programming (current) time */
u32_t F; /* Idle timer fire time */
u32_t f; /* Last programmed timer fire time */
@ -161,6 +302,7 @@ void _timer_idle_enter(s32_t ticks)
F = f + idle_original_ticks * cycles_per_tick;
/* Program the timer register to fire at the right time */
SET_TIMER_FIRE_TIME(F);
#endif /* CONFIG_TICKLESS_KERNEL */
}
/**
@ -178,6 +320,11 @@ void _timer_idle_enter(s32_t ticks)
*/
void _timer_idle_exit(void)
{
#ifdef CONFIG_TICKLESS_KERNEL
if (!idle_original_ticks) {
_set_max_clock_time();
}
#else
u32_t C; /* Current time (time within this function execution) */
u32_t F; /* Idle timer programmed fire time */
u32_t s; /* Requested idle timer sleep time */
@ -259,6 +406,7 @@ void _timer_idle_exit(void)
/* Exit timer idle mode */
idle_mode = IDLE_NOT_TICKLESS;
timer_mode = TIMER_MODE_PERIODIC;
#endif /* CONFIG_TICKLESS_KERNEL */
}
#endif /* CONFIG_TICKLESS_IDLE */
@ -307,8 +455,27 @@ void _timer_int_handler(void *params)
extern void _sys_k_event_logger_interrupt(void);
_sys_k_event_logger_interrupt();
#endif
#ifdef CONFIG_TICKLESS_KERNEL
if (!idle_original_ticks) {
_set_max_clock_time();
return;
}
_sys_idle_elapsed_ticks = idle_original_ticks;
idle_original_ticks = 0;
/* Anounce elapsed of _sys_idle_elapsed_ticks systicks */
_sys_clock_tick_announce();
/* Program timer incase it is not Prgrammed */
if (!idle_original_ticks) {
_set_max_clock_time();
return;
}
#else
/* Announce the tick event to the kernel. */
_sys_clock_final_tick_announce();
#endif /* CONFIG_TICKLESS_KERNEL */
}
@ -349,7 +516,7 @@ int _sys_clock_driver_init(struct device *device)
volatile u32_t *p_mmio = (u32_t *) 0xC0000000; /* start HW reg */
u32_t interrupt = 0x00000000;
/* Start the timer: Trigger the interrupt source drivers */
*p_mmio = 0xFFFFFFFF;
*p_mmio = MAX_TIMER_CYCLES;
*p_mmio = interrupt;
/*
* Code above is example code, it is kept here on purpose to let users
@ -363,7 +530,7 @@ int _sys_clock_driver_init(struct device *device)
#endif /* CONFIG_XTENSA_INTERNAL_TIMER || (CONFIG_XTENSA_TIMER_IRQ < 0) */
#if CONFIG_TICKLESS_IDLE
tickless_idle_init();
#endif
#endif /* CONFIG_TICKLESS_KERNEL */
return 0;
}