2016-11-22 17:03:32 +01:00
|
|
|
/*
|
2017-02-13 16:31:53 +01:00
|
|
|
* Copyright (c) 2016-2017 Nordic Semiconductor ASA
|
2016-11-22 17:03:32 +01:00
|
|
|
*
|
2017-01-19 02:01:01 +01:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-11-22 17:03:32 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <soc.h>
|
|
|
|
#include <clock_control.h>
|
|
|
|
#include <system_timer.h>
|
|
|
|
#include <drivers/clock_control/nrf5_clock_control.h>
|
kernel: tickless: Add tickless kernel support
Adds event based scheduling logic to the kernel. Updates
management of timeouts, timers, idling etc. based on
time tracked at events rather than periodic ticks. Provides
interfaces for timers to announce and get next timer expiry
based on kernel scheduling decisions involving time slicing
of threads, timeouts and idling. Uses wall time units instead
of ticks in all scheduling activities.
The implementation involves changes in the following areas
1. Management of time in wall units like ms/us instead of ticks
The existing implementation already had an option to configure
number of ticks in a second. The new implementation builds on
top of that feature and provides option to set the size of the
scheduling granurality to mili seconds or micro seconds. This
allows most of the current implementation to be reused. Due to
this re-use and co-existence with tick based kernel, the names
of variables may contain the word "tick". However, in the
tickless kernel implementation, it represents the currently
configured time unit, which would be be mili seconds or
micro seconds. The APIs that take time as a parameter are not
impacted and they continue to pass time in mili seconds.
2. Timers would not be programmed in periodic mode
generating ticks. Instead they would be programmed in one
shot mode to generate events at the time the kernel scheduler
needs to gain control for its scheduling activities like
timers, timeouts, time slicing, idling etc.
3. The scheduler provides interfaces that the timer drivers
use to announce elapsed time and get the next time the scheduler
needs a timer event. It is possible that the scheduler may not
need another timer event, in which case the system would wait
for a non-timer event to wake it up if it is idling.
4. New APIs are defined to be implemented by timer drivers. Also
they need to handler timer events differently. These changes
have been done in the HPET timer driver. In future other timers
that support tickles kernel should implement these APIs as well.
These APIs are to re-program the timer, update and announce
elapsed time.
5. Philosopher and timer_api applications have been enabled to
test tickless kernel. Separate configuration files are created
which define the necessary CONFIG flags. Run these apps using
following command
make pristine && make BOARD=qemu_x86 CONF_FILE=prj_tickless.conf qemu
Jira: ZEP-339 ZEP-1946 ZEP-948
Change-Id: I7d950c31bf1ff929a9066fad42c2f0559a2e5983
Signed-off-by: Ramesh Thomas <ramesh.thomas@intel.com>
2017-02-06 04:37:19 +01:00
|
|
|
#include <sys_clock.h>
|
2016-11-22 17:03:32 +01:00
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/*
|
|
|
|
* Convenience defines.
|
|
|
|
*/
|
|
|
|
#define SYS_CLOCK_RTC NRF_RTC1
|
|
|
|
#define RTC_COUNTER SYS_CLOCK_RTC->COUNTER
|
|
|
|
#define RTC_CC_VALUE SYS_CLOCK_RTC->CC[0]
|
|
|
|
#define RTC_CC_EVENT SYS_CLOCK_RTC->EVENTS_COMPARE[0]
|
|
|
|
|
|
|
|
/* Minimum delta between current counter and CC register that the RTC is able
|
|
|
|
* to handle
|
|
|
|
*/
|
|
|
|
#define RTC_MIN_DELTA 2
|
|
|
|
#define RTC_MASK 0x00FFFFFF
|
|
|
|
/* Maximum difference for RTC counter values used. Half the maximum value is
|
|
|
|
* selected to be able to detect overflow (a negative value has the same
|
|
|
|
* representation as a large positive value).
|
|
|
|
*/
|
|
|
|
#define RTC_HALF (RTC_MASK / 2)
|
2017-05-08 07:59:37 +02:00
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/*
|
|
|
|
* rtc_past holds the value of RTC_COUNTER at the time the last sys tick was
|
|
|
|
* announced, in RTC ticks. It is therefore always a multiple of
|
2018-06-07 12:51:39 +02:00
|
|
|
* sys_clock_hw_cycles_per_tick.
|
2017-02-13 16:31:53 +01:00
|
|
|
*/
|
2017-04-21 17:03:20 +02:00
|
|
|
static u32_t rtc_past;
|
2016-11-22 17:03:32 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
2017-02-13 16:31:53 +01:00
|
|
|
/*
|
|
|
|
* Holds the maximum sys ticks the kernel expects to see in the next
|
|
|
|
* _sys_clock_tick_announce().
|
|
|
|
*/
|
2017-04-21 17:03:20 +02:00
|
|
|
static u32_t expected_sys_ticks;
|
2016-12-13 11:55:22 +01:00
|
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
|
2017-05-08 07:59:37 +02:00
|
|
|
#ifdef CONFIG_TICKLESS_KERNEL
|
2018-01-01 21:20:29 +01:00
|
|
|
s32_t _get_max_clock_time(void);
|
2017-05-08 07:59:37 +02:00
|
|
|
#endif /* CONFIG_TICKLESS_KERNEL */
|
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/*
|
|
|
|
* Set RTC Counter Compare (CC) register to a given value in RTC ticks.
|
|
|
|
*/
|
2017-04-21 17:03:20 +02:00
|
|
|
static void rtc_compare_set(u32_t rtc_ticks)
|
2016-12-13 11:55:22 +01:00
|
|
|
{
|
2017-04-21 17:03:20 +02:00
|
|
|
u32_t rtc_now;
|
2017-02-13 16:31:53 +01:00
|
|
|
|
|
|
|
/* Try to set CC value. We assume the procedure is always successful. */
|
|
|
|
RTC_CC_VALUE = rtc_ticks;
|
|
|
|
rtc_now = RTC_COUNTER;
|
|
|
|
|
|
|
|
/* The following checks if the CC register was set to a valid value.
|
|
|
|
* The first test checks if the distance between the current RTC counter
|
|
|
|
* and the value (in the future) set in the CC register is too small to
|
|
|
|
* guarantee a compare event being triggered.
|
|
|
|
* The second test checks if the current RTC counter is higher than the
|
|
|
|
* value written to the CC register, i.e. the CC value is in the past,
|
|
|
|
* by checking if the unsigned subtraction wraps around.
|
|
|
|
* If either of the above are true then instead of waiting for the CC
|
|
|
|
* event to trigger in the form of an interrupt, trigger it directly
|
|
|
|
* using the NVIC.
|
|
|
|
*/
|
|
|
|
if ((((rtc_ticks - rtc_now) & RTC_MASK) < RTC_MIN_DELTA) ||
|
|
|
|
(((rtc_ticks - rtc_now) & RTC_MASK) > RTC_HALF)) {
|
|
|
|
NVIC_SetPendingIRQ(NRF5_IRQ_RTC1_IRQn);
|
|
|
|
}
|
|
|
|
}
|
2017-05-08 07:59:37 +02:00
|
|
|
#ifndef CONFIG_TICKLESS_KERNEL
|
2017-02-13 16:31:53 +01:00
|
|
|
/*
|
|
|
|
* @brief Announces the number of sys ticks, if any, that have passed since the
|
|
|
|
* last announcement, and programs the RTC to trigger the interrupt on the next
|
|
|
|
* sys tick.
|
|
|
|
*
|
|
|
|
* This function is not reentrant. It is called from:
|
|
|
|
*
|
|
|
|
* * _timer_idle_exit(), which in turn is called with interrupts disabled when
|
|
|
|
* an interrupt fires.
|
|
|
|
* * rtc1_nrf5_isr(), which runs with interrupts enabled but at that time the
|
|
|
|
* device cannot be idle and hence _timer_idle_exit() cannot be called.
|
|
|
|
*
|
|
|
|
* Since this function can be preempted, we need to take some provisions to
|
|
|
|
* announce all expected sys ticks that have passed.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static void rtc_announce_set_next(void)
|
|
|
|
{
|
2017-04-21 17:03:20 +02:00
|
|
|
u32_t rtc_now, rtc_elapsed, sys_elapsed;
|
2016-11-22 17:03:32 +01:00
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/* Read the RTC counter one single time in the beginning, so that an
|
|
|
|
* increase in the counter during this procedure leads to no race
|
|
|
|
* conditions.
|
|
|
|
*/
|
|
|
|
rtc_now = RTC_COUNTER;
|
2016-12-13 11:55:22 +01:00
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/* Calculate how many RTC ticks elapsed since the last sys tick. */
|
|
|
|
rtc_elapsed = (rtc_now - rtc_past) & RTC_MASK;
|
2016-12-13 11:55:22 +01:00
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/* If no sys ticks have elapsed, there is no point in incrementing the
|
|
|
|
* counters or announcing it.
|
|
|
|
*/
|
2018-06-07 12:51:39 +02:00
|
|
|
if (rtc_elapsed >= sys_clock_hw_cycles_per_tick) {
|
2017-02-20 18:15:30 +01:00
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
2017-02-13 16:31:53 +01:00
|
|
|
/* Calculate how many sys ticks elapsed since the last sys tick
|
|
|
|
* and notify the kernel if necessary.
|
|
|
|
*/
|
2018-06-07 12:51:39 +02:00
|
|
|
sys_elapsed = rtc_elapsed / sys_clock_hw_cycles_per_tick;
|
2016-12-13 11:55:22 +01:00
|
|
|
|
2017-02-20 18:15:30 +01:00
|
|
|
if (sys_elapsed > expected_sys_ticks) {
|
2017-02-13 16:31:53 +01:00
|
|
|
/* Never announce more sys ticks than the kernel asked
|
|
|
|
* to be idle for. The remainder will be announced when
|
|
|
|
* the RTC ISR runs after rtc_compare_set() is called
|
|
|
|
* after the first announcement.
|
|
|
|
*/
|
2017-02-20 18:15:30 +01:00
|
|
|
sys_elapsed = expected_sys_ticks;
|
2016-12-13 11:55:22 +01:00
|
|
|
}
|
2017-02-20 18:15:30 +01:00
|
|
|
#else
|
|
|
|
/* Never announce more than one sys tick if tickless idle is not
|
|
|
|
* configured.
|
|
|
|
*/
|
|
|
|
sys_elapsed = 1;
|
2017-02-13 16:31:53 +01:00
|
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
2016-12-13 11:55:22 +01:00
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/* Store RTC_COUNTER floored to the last sys tick. This is
|
|
|
|
* done, so that ISR can properly calculate that 1 sys tick
|
|
|
|
* has passed.
|
|
|
|
*/
|
|
|
|
rtc_past = (rtc_past +
|
2018-06-07 12:51:39 +02:00
|
|
|
(sys_elapsed * sys_clock_hw_cycles_per_tick)
|
2017-02-13 16:31:53 +01:00
|
|
|
) & RTC_MASK;
|
|
|
|
|
|
|
|
_sys_idle_elapsed_ticks = sys_elapsed;
|
|
|
|
_sys_clock_tick_announce();
|
2016-12-13 11:55:22 +01:00
|
|
|
}
|
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/* Set the RTC to the next sys tick */
|
2018-06-07 12:51:39 +02:00
|
|
|
rtc_compare_set(rtc_past + sys_clock_hw_cycles_per_tick);
|
2016-12-13 11:55:22 +01:00
|
|
|
}
|
2017-05-08 07:59:37 +02:00
|
|
|
#endif
|
2016-12-13 11:55:22 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
2017-02-13 16:31:53 +01:00
|
|
|
/**
|
|
|
|
* @brief Place system timer into idle state.
|
|
|
|
*
|
|
|
|
* Re-program the timer to enter into the idle state for the given number of
|
|
|
|
* sys ticks, counted from the previous sys tick. The timer will fire in the
|
|
|
|
* number of sys ticks supplied or the maximum number of sys ticks (converted
|
|
|
|
* to RTC ticks) that can be programmed into the hardware.
|
|
|
|
*
|
|
|
|
* This will only be called from idle context, with IRQs disabled.
|
|
|
|
*
|
|
|
|
* A value of -1 will result in the maximum number of sys ticks.
|
|
|
|
*
|
|
|
|
* Example 1: Idle sleep is entered:
|
|
|
|
*
|
|
|
|
* sys tick timeline: (1) (2) (3) (4) (5) (6)
|
|
|
|
* rtc tick timeline : 0----100----200----300----400----500----600
|
|
|
|
* ******************
|
|
|
|
* 150
|
|
|
|
*
|
|
|
|
* a) The last sys tick was announced at 100
|
|
|
|
* b) The idle context enters sleep at 150, between sys tick 1 and 2, with
|
|
|
|
* sys_ticks = 3.
|
|
|
|
* c) The RTC is programmed to fire at sys tick 1 + 3 = 4 (RTC tick 400)
|
|
|
|
*
|
|
|
|
* @return N/A
|
|
|
|
*/
|
2017-04-21 17:03:20 +02:00
|
|
|
void _timer_idle_enter(s32_t sys_ticks)
|
2016-11-22 17:03:32 +01:00
|
|
|
{
|
2017-05-08 07:59:37 +02:00
|
|
|
#ifdef CONFIG_TICKLESS_KERNEL
|
|
|
|
if (sys_ticks != K_FOREVER) {
|
|
|
|
/* Need to reprograme timer if current program is smaller*/
|
|
|
|
if (sys_ticks > expected_sys_ticks) {
|
|
|
|
_set_time(sys_ticks);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
expected_sys_ticks = 0;
|
|
|
|
/* Set time to largest possile RTC Tick*/
|
|
|
|
_set_time(_get_max_clock_time());
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
/* Restrict ticks to max supported by RTC without risking overflow*/
|
2017-02-13 16:31:53 +01:00
|
|
|
if ((sys_ticks < 0) ||
|
2018-06-07 12:51:39 +02:00
|
|
|
(sys_ticks > (RTC_HALF / sys_clock_hw_cycles_per_tick))) {
|
|
|
|
sys_ticks = RTC_HALF / sys_clock_hw_cycles_per_tick;
|
2016-11-22 17:03:32 +01:00
|
|
|
}
|
|
|
|
|
2017-02-20 18:15:30 +01:00
|
|
|
expected_sys_ticks = sys_ticks;
|
2017-02-13 16:31:53 +01:00
|
|
|
|
|
|
|
/* If ticks is 0, the RTC interrupt handler will be set pending
|
|
|
|
* immediately, meaning that we will not go to sleep.
|
|
|
|
*/
|
2018-06-07 12:51:39 +02:00
|
|
|
rtc_compare_set(rtc_past + (sys_ticks * sys_clock_hw_cycles_per_tick));
|
2017-05-08 07:59:37 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_TICKLESS_KERNEL
|
|
|
|
|
|
|
|
/**
|
2017-10-18 00:55:47 +02:00
|
|
|
* @brief provides total systicks programmed.
|
2017-05-08 07:59:37 +02:00
|
|
|
*
|
2017-10-18 00:55:47 +02:00
|
|
|
* returns : total number of sys ticks programmed.
|
2017-05-08 07:59:37 +02:00
|
|
|
*/
|
|
|
|
|
2018-01-01 21:20:29 +01:00
|
|
|
u32_t _get_program_time(void)
|
2017-05-08 07:59:37 +02:00
|
|
|
{
|
|
|
|
return expected_sys_ticks;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-10-18 00:55:47 +02:00
|
|
|
* @brief provides total systicks remaining since last programming of RTC.
|
2017-05-08 07:59:37 +02:00
|
|
|
*
|
2017-10-18 00:55:47 +02:00
|
|
|
* returns : total number of sys ticks remaining since last RTC programming.
|
2017-05-08 07:59:37 +02:00
|
|
|
*/
|
|
|
|
|
2018-01-01 21:20:29 +01:00
|
|
|
u32_t _get_remaining_program_time(void)
|
2017-05-08 07:59:37 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
if (!expected_sys_ticks) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (expected_sys_ticks - _get_elapsed_program_time());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-10-18 00:55:47 +02:00
|
|
|
* @brief provides total systicks passed since last programming of RTC.
|
2017-05-08 07:59:37 +02:00
|
|
|
*
|
2017-10-18 00:55:47 +02:00
|
|
|
* returns : total number of sys ticks passed since last RTC programming.
|
2017-05-08 07:59:37 +02:00
|
|
|
*/
|
|
|
|
|
2018-01-01 21:20:29 +01:00
|
|
|
u32_t _get_elapsed_program_time(void)
|
2017-05-08 07:59:37 +02:00
|
|
|
{
|
|
|
|
u32_t rtc_now, rtc_prev, rtc_elapsed;
|
|
|
|
|
|
|
|
if (!expected_sys_ticks) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
rtc_now = RTC_COUNTER;
|
|
|
|
|
|
|
|
/* Discard value of RTC_COUNTER read at LFCLK transition */
|
|
|
|
do {
|
|
|
|
/* Number of RTC cycles passed since last RTC Programing*/
|
|
|
|
rtc_elapsed = (rtc_now - rtc_past) & RTC_MASK;
|
|
|
|
rtc_prev = rtc_now;
|
|
|
|
rtc_now = RTC_COUNTER;
|
|
|
|
} while (rtc_prev != rtc_now);
|
|
|
|
|
|
|
|
/*Convert number of Machine cycles to SYS TICS*/
|
2018-06-07 12:51:39 +02:00
|
|
|
return (rtc_elapsed / sys_clock_hw_cycles_per_tick);
|
2017-05-08 07:59:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Sets interrupt for rtc compare value for systick time.
|
|
|
|
*
|
|
|
|
* This Function does following:-
|
|
|
|
* 1. Updates expected_sys_ticks equal to time.
|
|
|
|
* 2. Update kernel book keeping for time passed since device bootup.
|
|
|
|
* 3. Calls routine to set rtc interrupt.
|
|
|
|
*/
|
|
|
|
|
2018-01-01 21:20:29 +01:00
|
|
|
void _set_time(u32_t time)
|
2017-05-08 07:59:37 +02:00
|
|
|
{
|
|
|
|
if (!time) {
|
|
|
|
expected_sys_ticks = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update expected_sys_ticls to time to programe*/
|
|
|
|
expected_sys_ticks = time;
|
|
|
|
_sys_clock_tick_count = _get_elapsed_clock_time();
|
|
|
|
/* Update rtc_past to track rtc timer count*/
|
2018-06-07 12:51:39 +02:00
|
|
|
rtc_past = (_sys_clock_tick_count * sys_clock_hw_cycles_per_tick) & RTC_MASK;
|
2017-05-08 07:59:37 +02:00
|
|
|
|
|
|
|
expected_sys_ticks = expected_sys_ticks > _get_max_clock_time() ?
|
|
|
|
_get_max_clock_time() : expected_sys_ticks;
|
|
|
|
|
|
|
|
/* Programe RTC compare register to generate interrupt*/
|
|
|
|
rtc_compare_set(rtc_past +
|
2018-06-07 12:51:39 +02:00
|
|
|
(expected_sys_ticks * sys_clock_hw_cycles_per_tick));
|
2017-05-08 07:59:37 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief provides time remaining to reach rtc count overflow.
|
|
|
|
*
|
|
|
|
* This function returns how many sys RTC remaining for rtc to overflow.
|
2017-10-18 00:55:47 +02:00
|
|
|
* This will be required when we will program RTC compare value to maximum
|
2017-05-08 07:59:37 +02:00
|
|
|
* possible value.
|
|
|
|
*
|
|
|
|
* returns : difference between current systick and Maximum possible systick.
|
|
|
|
*/
|
2018-01-01 21:20:29 +01:00
|
|
|
s32_t _get_max_clock_time(void)
|
2017-05-08 07:59:37 +02:00
|
|
|
{
|
|
|
|
u32_t rtc_now, rtc_prev, rtc_away, sys_away = 0;
|
|
|
|
|
|
|
|
rtc_now = RTC_COUNTER;
|
|
|
|
/* Discard value of RTC_COUNTER read at LFCLK transition */
|
|
|
|
do {
|
|
|
|
rtc_away = (RTC_MASK - rtc_now);
|
|
|
|
rtc_away = rtc_away > RTC_HALF ? RTC_HALF : rtc_away;
|
|
|
|
rtc_prev = rtc_now;
|
|
|
|
/* Calculate time to programe into RTC*/
|
|
|
|
rtc_now = RTC_COUNTER;
|
|
|
|
} while (rtc_now != rtc_prev);
|
|
|
|
|
|
|
|
/* Convert RTC Ticks to SYS TICKS*/
|
2018-06-07 12:51:39 +02:00
|
|
|
if (rtc_away >= sys_clock_hw_cycles_per_tick) {
|
|
|
|
sys_away = rtc_away / sys_clock_hw_cycles_per_tick;
|
2017-05-08 07:59:37 +02:00
|
|
|
}
|
|
|
|
return sys_away;
|
2016-11-22 17:03:32 +01:00
|
|
|
}
|
|
|
|
|
2017-05-08 07:59:37 +02:00
|
|
|
/**
|
|
|
|
* @brief Enable sys Clock.
|
|
|
|
*
|
2017-10-18 00:55:47 +02:00
|
|
|
* This is used to program RTC clock to maximum Clock time in case Clock to
|
2017-05-08 07:59:37 +02:00
|
|
|
* remain On.
|
|
|
|
*/
|
|
|
|
void _enable_sys_clock(void)
|
|
|
|
{
|
|
|
|
if (!expected_sys_ticks) {
|
|
|
|
/* Programe sys tick to Maximum possible value*/
|
|
|
|
_set_time(_get_max_clock_time());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief provides total systicks passed since device bootup.
|
|
|
|
*
|
|
|
|
* returns : total number of sys ticks passed since device bootup.
|
|
|
|
*/
|
|
|
|
|
2018-01-01 21:20:29 +01:00
|
|
|
u64_t _get_elapsed_clock_time(void)
|
2017-05-08 07:59:37 +02:00
|
|
|
{
|
|
|
|
u64_t elapsed;
|
|
|
|
u32_t rtc_now, rtc_elapsed, rtc_prev, sys_elapsed;
|
|
|
|
|
|
|
|
rtc_now = RTC_COUNTER;
|
|
|
|
|
|
|
|
/* Discard value of RTC_COUNTER read at LFCLK transition */
|
|
|
|
do {
|
|
|
|
/* Calculate number of rtc cycles elapsed since RTC programing*/
|
|
|
|
rtc_elapsed = (rtc_now - rtc_past) & RTC_MASK;
|
|
|
|
elapsed = _sys_clock_tick_count;
|
|
|
|
rtc_prev = rtc_now;
|
|
|
|
rtc_now = RTC_COUNTER;
|
|
|
|
} while (rtc_now != rtc_prev);
|
|
|
|
|
2018-06-07 12:51:39 +02:00
|
|
|
if (rtc_elapsed >= sys_clock_hw_cycles_per_tick) {
|
2017-05-08 07:59:37 +02:00
|
|
|
/* Convert RTC cycles to SYS TICKS*/
|
2018-06-07 12:51:39 +02:00
|
|
|
sys_elapsed = rtc_elapsed / sys_clock_hw_cycles_per_tick;
|
2017-05-08 07:59:37 +02:00
|
|
|
/* Update total number of SYS_TICKS passed*/
|
|
|
|
elapsed += sys_elapsed;
|
|
|
|
}
|
|
|
|
return elapsed;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @brief Handling of tickless idle when interrupted
|
|
|
|
*
|
|
|
|
* The function will be called by _sys_power_save_idle_exit(), called from
|
|
|
|
* _arch_isr_direct_pm() for 'direct' interrupts, or from _isr_wrapper for
|
|
|
|
* regular ones, which is called on every IRQ handler if the device was
|
|
|
|
* idle, and optionally called when a 'direct' IRQ handler executes if the
|
|
|
|
* device was idle.
|
|
|
|
*
|
|
|
|
* Example 1: Idle sleep is interrupted before time:
|
|
|
|
*
|
|
|
|
* sys tick timeline: (1) (2) (3) (4) (5) (6)
|
|
|
|
* rtc tick timeline : 0----100----200----300----400----500----600
|
|
|
|
* **************!***
|
|
|
|
* 150 350
|
|
|
|
*
|
|
|
|
* Assume that _timer_idle_enter() is called at 150 (1) to sleep for 3
|
|
|
|
* sys ticks. The last sys tick was announced at 100.
|
|
|
|
*
|
|
|
|
* On wakeup (non-RTC IRQ at 350):
|
|
|
|
*
|
|
|
|
* a) Notify how many sys ticks have passed, i.e., 350 - 150 / 100 = 2.
|
|
|
|
* b) Schedule next sys tick at 400.
|
|
|
|
*
|
|
|
|
*/
|
2016-11-22 17:03:32 +01:00
|
|
|
void _timer_idle_exit(void)
|
|
|
|
{
|
2017-05-08 07:59:37 +02:00
|
|
|
#ifdef CONFIG_TICKLESS_KERNEL
|
|
|
|
if (!expected_sys_ticks && _sys_clock_always_on) {
|
|
|
|
_set_time(_get_max_clock_time());
|
|
|
|
}
|
|
|
|
#else
|
2017-02-13 16:31:53 +01:00
|
|
|
/* Clear the event flag and interrupt in case we woke up on the RTC
|
|
|
|
* interrupt. No need to run the RTC ISR since everything that needs
|
|
|
|
* to run in the ISR will be done in this call.
|
|
|
|
*/
|
|
|
|
RTC_CC_EVENT = 0;
|
|
|
|
NVIC_ClearPendingIRQ(NRF5_IRQ_RTC1_IRQn);
|
|
|
|
|
|
|
|
rtc_announce_set_next();
|
|
|
|
|
2017-02-20 18:15:30 +01:00
|
|
|
/* After exiting idle, the kernel no longer expects more than one sys
|
|
|
|
* ticks to have passed when _sys_clock_tick_announce() is called.
|
2017-02-13 16:31:53 +01:00
|
|
|
*/
|
2017-02-20 18:15:30 +01:00
|
|
|
expected_sys_ticks = 1;
|
2017-05-08 07:59:37 +02:00
|
|
|
#endif
|
2016-11-22 17:03:32 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
/*
|
|
|
|
* @brief Announces the number of sys ticks that have passed since the last
|
|
|
|
* announcement, if any, and programs the RTC to trigger the interrupt on the
|
|
|
|
* next sys tick.
|
|
|
|
*
|
|
|
|
* The ISR is set pending due to a regular sys tick and after exiting idle mode
|
|
|
|
* as scheduled.
|
|
|
|
*
|
|
|
|
* Since this ISR can be preempted, we need to take some provisions to announce
|
|
|
|
* all expected sys ticks that have passed.
|
|
|
|
*
|
|
|
|
* Consider the following example:
|
|
|
|
*
|
|
|
|
* sys tick timeline: (1) (2) (3) (4) (5) (6)
|
|
|
|
* rtc tick timeline : 0----100----200----300----400----500----600
|
|
|
|
* !**********
|
|
|
|
* 450
|
|
|
|
*
|
|
|
|
* The last sys tick was anounced at 200, i.e, rtc_past = 200. The ISR is
|
|
|
|
* executed at the next sys tick, i.e. 300. The following sys tick is due at
|
|
|
|
* 400. However, the ISR is preempted for a number of sys ticks, until 450 in
|
|
|
|
* this example. The ISR will then announce the number of sys ticks it was
|
|
|
|
* delayed (2), and schedule the next sys tick (5) at 500.
|
|
|
|
*/
|
2018-04-04 17:45:46 +02:00
|
|
|
void rtc1_nrf5_isr(void *arg)
|
2016-11-22 17:03:32 +01:00
|
|
|
{
|
|
|
|
|
2017-05-08 07:59:37 +02:00
|
|
|
ARG_UNUSED(arg);
|
2017-02-13 16:31:53 +01:00
|
|
|
RTC_CC_EVENT = 0;
|
2017-05-08 07:59:37 +02:00
|
|
|
|
2017-08-31 16:52:18 +02:00
|
|
|
#ifdef CONFIG_EXECUTION_BENCHMARKING
|
|
|
|
extern void read_timer_start_of_tick_handler(void);
|
|
|
|
read_timer_start_of_tick_handler();
|
|
|
|
#endif
|
|
|
|
|
2017-05-08 07:59:37 +02:00
|
|
|
#ifdef CONFIG_TICKLESS_KERNEL
|
|
|
|
_sys_idle_elapsed_ticks = expected_sys_ticks;
|
|
|
|
/* Initialize expected sys tick,
|
|
|
|
* It will be later updated based on next timeout.
|
|
|
|
*/
|
|
|
|
expected_sys_ticks = 0;
|
|
|
|
/* Anounce elapsed of _sys_idle_elapsed_ticks systicks*/
|
|
|
|
_sys_clock_tick_announce();
|
|
|
|
#else
|
2017-02-13 16:31:53 +01:00
|
|
|
rtc_announce_set_next();
|
2017-05-08 07:59:37 +02:00
|
|
|
#endif
|
2017-08-31 16:52:18 +02:00
|
|
|
|
|
|
|
#ifdef CONFIG_EXECUTION_BENCHMARKING
|
|
|
|
extern void read_timer_end_of_tick_handler(void);
|
|
|
|
read_timer_end_of_tick_handler();
|
|
|
|
#endif
|
|
|
|
|
2016-11-22 17:03:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int _sys_clock_driver_init(struct device *device)
|
|
|
|
{
|
|
|
|
struct device *clock;
|
|
|
|
|
|
|
|
ARG_UNUSED(device);
|
|
|
|
|
|
|
|
clock = device_get_binding(CONFIG_CLOCK_CONTROL_NRF5_K32SRC_DRV_NAME);
|
|
|
|
if (!clock) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
clock_control_on(clock, (void *)CLOCK_CONTROL_NRF5_K32SRC);
|
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
rtc_past = 0;
|
|
|
|
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
2017-02-20 18:15:30 +01:00
|
|
|
expected_sys_ticks = 1;
|
2017-02-13 16:31:53 +01:00
|
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
|
2016-11-22 17:03:32 +01:00
|
|
|
/* TODO: replace with counter driver to access RTC */
|
2017-02-13 16:31:53 +01:00
|
|
|
SYS_CLOCK_RTC->PRESCALER = 0;
|
2018-06-07 12:51:39 +02:00
|
|
|
SYS_CLOCK_RTC->CC[0] = sys_clock_hw_cycles_per_tick;
|
2017-02-13 16:31:53 +01:00
|
|
|
SYS_CLOCK_RTC->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
|
|
|
|
SYS_CLOCK_RTC->INTENSET = RTC_INTENSET_COMPARE0_Msk;
|
2016-11-22 17:03:32 +01:00
|
|
|
|
2017-02-28 15:41:18 +01:00
|
|
|
/* Clear the event flag and possible pending interrupt */
|
|
|
|
RTC_CC_EVENT = 0;
|
|
|
|
NVIC_ClearPendingIRQ(NRF5_IRQ_RTC1_IRQn);
|
|
|
|
|
2016-11-22 17:03:32 +01:00
|
|
|
IRQ_CONNECT(NRF5_IRQ_RTC1_IRQn, 1, rtc1_nrf5_isr, 0, 0);
|
|
|
|
irq_enable(NRF5_IRQ_RTC1_IRQn);
|
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
SYS_CLOCK_RTC->TASKS_CLEAR = 1;
|
|
|
|
SYS_CLOCK_RTC->TASKS_START = 1;
|
2016-11-22 17:03:32 +01:00
|
|
|
|
2016-11-28 04:35:52 +01:00
|
|
|
return 0;
|
2016-11-22 17:03:32 +01:00
|
|
|
}
|
|
|
|
|
2017-04-21 17:55:34 +02:00
|
|
|
u32_t _timer_cycle_get_32(void)
|
2016-11-22 17:03:32 +01:00
|
|
|
{
|
2017-04-21 17:03:20 +02:00
|
|
|
u32_t elapsed_cycles;
|
2017-06-01 23:43:09 +02:00
|
|
|
u32_t sys_clock_tick_count;
|
|
|
|
u32_t rtc_prev;
|
|
|
|
u32_t rtc_now;
|
2016-11-22 17:03:32 +01:00
|
|
|
|
2017-06-01 23:43:09 +02:00
|
|
|
rtc_now = RTC_COUNTER;
|
2017-05-08 07:59:37 +02:00
|
|
|
/* Discard value of RTC_COUNTER read at LFCLK transition */
|
2017-06-01 23:43:09 +02:00
|
|
|
do {
|
|
|
|
sys_clock_tick_count = _sys_clock_tick_count;
|
|
|
|
elapsed_cycles = (rtc_now - (sys_clock_tick_count *
|
2018-06-07 12:51:39 +02:00
|
|
|
sys_clock_hw_cycles_per_tick)) &
|
2017-06-01 23:43:09 +02:00
|
|
|
RTC_MASK;
|
|
|
|
rtc_prev = rtc_now;
|
|
|
|
rtc_now = RTC_COUNTER;
|
|
|
|
} while (rtc_now != rtc_prev);
|
|
|
|
|
|
|
|
return (sys_clock_tick_count * sys_clock_hw_cycles_per_tick) +
|
2016-11-22 17:03:32 +01:00
|
|
|
elapsed_cycles;
|
|
|
|
}
|
2016-11-30 17:34:28 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_SYSTEM_CLOCK_DISABLE
|
|
|
|
/**
|
|
|
|
*
|
2017-02-13 16:31:53 +01:00
|
|
|
* @brief Stop announcing sys ticks into the kernel
|
2016-11-30 17:34:28 +01:00
|
|
|
*
|
|
|
|
* This routine disables the RTC1 so that timer interrupts are no
|
|
|
|
* longer delivered.
|
|
|
|
*
|
|
|
|
* @return N/A
|
|
|
|
*/
|
|
|
|
void sys_clock_disable(void)
|
|
|
|
{
|
2017-02-28 15:41:18 +01:00
|
|
|
unsigned int key;
|
|
|
|
|
|
|
|
key = irq_lock();
|
|
|
|
|
2016-11-30 17:34:28 +01:00
|
|
|
irq_disable(NRF5_IRQ_RTC1_IRQn);
|
|
|
|
|
2017-02-28 15:41:18 +01:00
|
|
|
SYS_CLOCK_RTC->EVTENCLR = RTC_EVTENCLR_COMPARE0_Msk;
|
|
|
|
SYS_CLOCK_RTC->INTENCLR = RTC_INTENCLR_COMPARE0_Msk;
|
|
|
|
|
2017-02-13 16:31:53 +01:00
|
|
|
SYS_CLOCK_RTC->TASKS_STOP = 1;
|
2017-02-28 15:41:18 +01:00
|
|
|
SYS_CLOCK_RTC->TASKS_CLEAR = 1;
|
|
|
|
|
|
|
|
irq_unlock(key);
|
2016-11-30 17:34:28 +01:00
|
|
|
|
|
|
|
/* TODO: turn off (release) 32 KHz clock source.
|
|
|
|
* Turning off of 32 KHz clock source is not implemented in clock
|
|
|
|
* driver.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_SYSTEM_CLOCK_DISABLE */
|