mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-08 15:25:08 +00:00
Convert code to use u{8,16,32,64}_t and s{8,16,32,64}_t instead of C99 integer types. This handles the remaining includes and kernel, plus touching up various points that we skipped because of include dependancies. We also convert the PRI printf formatters in the arch code over to normal formatters. Jira: ZEP-2051 Change-Id: Iecbb12601a3ee4ea936fd7ddea37788a645b08b0 Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
352 lines
11 KiB
C
352 lines
11 KiB
C
/*
|
|
* Copyright (c) 2016-2017 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <soc.h>
|
|
#include <clock_control.h>
|
|
#include <system_timer.h>
|
|
#include <drivers/clock_control/nrf5_clock_control.h>
|
|
#include <arch/arm/cortex_m/cmsis.h>
|
|
|
|
/*
|
|
* 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)
|
|
#define RTC_TICKS_PER_SYS_TICK ((u32_t)((((u64_t)1000000UL / \
|
|
CONFIG_SYS_CLOCK_TICKS_PER_SEC) * \
|
|
1000000000UL) / 30517578125UL) & RTC_MASK)
|
|
|
|
extern s64_t _sys_clock_tick_count;
|
|
extern s32_t _sys_idle_elapsed_ticks;
|
|
|
|
/*
|
|
* 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
|
|
* RTC_TICKS_PER_SYS_TICK.
|
|
*/
|
|
static u32_t rtc_past;
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
/*
|
|
* Holds the maximum sys ticks the kernel expects to see in the next
|
|
* _sys_clock_tick_announce().
|
|
*/
|
|
static u32_t expected_sys_ticks;
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
/*
|
|
* Set RTC Counter Compare (CC) register to a given value in RTC ticks.
|
|
*/
|
|
static void rtc_compare_set(u32_t rtc_ticks)
|
|
{
|
|
u32_t rtc_now;
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @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)
|
|
{
|
|
u32_t rtc_now, rtc_elapsed, sys_elapsed;
|
|
|
|
/* 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;
|
|
|
|
/* Calculate how many RTC ticks elapsed since the last sys tick. */
|
|
rtc_elapsed = (rtc_now - rtc_past) & RTC_MASK;
|
|
|
|
/* If no sys ticks have elapsed, there is no point in incrementing the
|
|
* counters or announcing it.
|
|
*/
|
|
if (rtc_elapsed >= RTC_TICKS_PER_SYS_TICK) {
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
/* Calculate how many sys ticks elapsed since the last sys tick
|
|
* and notify the kernel if necessary.
|
|
*/
|
|
sys_elapsed = rtc_elapsed / RTC_TICKS_PER_SYS_TICK;
|
|
|
|
if (sys_elapsed > expected_sys_ticks) {
|
|
/* 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.
|
|
*/
|
|
sys_elapsed = expected_sys_ticks;
|
|
}
|
|
#else
|
|
/* Never announce more than one sys tick if tickless idle is not
|
|
* configured.
|
|
*/
|
|
sys_elapsed = 1;
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
/* 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 +
|
|
(sys_elapsed * RTC_TICKS_PER_SYS_TICK)
|
|
) & RTC_MASK;
|
|
|
|
_sys_idle_elapsed_ticks = sys_elapsed;
|
|
_sys_clock_tick_announce();
|
|
}
|
|
|
|
/* Set the RTC to the next sys tick */
|
|
rtc_compare_set(rtc_past + RTC_TICKS_PER_SYS_TICK);
|
|
}
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
/**
|
|
* @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
|
|
*/
|
|
void _timer_idle_enter(s32_t sys_ticks)
|
|
{
|
|
/* Restrict ticks to max supported by RTC without risking overflow. */
|
|
if ((sys_ticks < 0) ||
|
|
(sys_ticks > (RTC_HALF / RTC_TICKS_PER_SYS_TICK))) {
|
|
sys_ticks = RTC_HALF / RTC_TICKS_PER_SYS_TICK;
|
|
}
|
|
|
|
expected_sys_ticks = sys_ticks;
|
|
|
|
/* If ticks is 0, the RTC interrupt handler will be set pending
|
|
* immediately, meaning that we will not go to sleep.
|
|
*/
|
|
rtc_compare_set(rtc_past + (sys_ticks * RTC_TICKS_PER_SYS_TICK));
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @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.
|
|
*
|
|
*/
|
|
void _timer_idle_exit(void)
|
|
{
|
|
/* 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();
|
|
|
|
/* After exiting idle, the kernel no longer expects more than one sys
|
|
* ticks to have passed when _sys_clock_tick_announce() is called.
|
|
*/
|
|
expected_sys_ticks = 1;
|
|
}
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
/*
|
|
* @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.
|
|
*/
|
|
static void rtc1_nrf5_isr(void *arg)
|
|
{
|
|
ARG_UNUSED(arg);
|
|
|
|
RTC_CC_EVENT = 0;
|
|
rtc_announce_set_next();
|
|
}
|
|
|
|
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);
|
|
|
|
rtc_past = 0;
|
|
|
|
#ifdef CONFIG_TICKLESS_IDLE
|
|
expected_sys_ticks = 1;
|
|
#endif /* CONFIG_TICKLESS_IDLE */
|
|
|
|
/* TODO: replace with counter driver to access RTC */
|
|
SYS_CLOCK_RTC->PRESCALER = 0;
|
|
SYS_CLOCK_RTC->CC[0] = RTC_TICKS_PER_SYS_TICK;
|
|
SYS_CLOCK_RTC->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
|
|
SYS_CLOCK_RTC->INTENSET = RTC_INTENSET_COMPARE0_Msk;
|
|
|
|
/* Clear the event flag and possible pending interrupt */
|
|
RTC_CC_EVENT = 0;
|
|
NVIC_ClearPendingIRQ(NRF5_IRQ_RTC1_IRQn);
|
|
|
|
IRQ_CONNECT(NRF5_IRQ_RTC1_IRQn, 1, rtc1_nrf5_isr, 0, 0);
|
|
irq_enable(NRF5_IRQ_RTC1_IRQn);
|
|
|
|
SYS_CLOCK_RTC->TASKS_CLEAR = 1;
|
|
SYS_CLOCK_RTC->TASKS_START = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32_t _timer_cycle_get_32(void)
|
|
{
|
|
u32_t elapsed_cycles;
|
|
|
|
elapsed_cycles = (RTC_COUNTER -
|
|
(_sys_clock_tick_count * RTC_TICKS_PER_SYS_TICK))
|
|
& RTC_MASK;
|
|
|
|
return (_sys_clock_tick_count * sys_clock_hw_cycles_per_tick) +
|
|
elapsed_cycles;
|
|
}
|
|
|
|
#ifdef CONFIG_SYSTEM_CLOCK_DISABLE
|
|
/**
|
|
*
|
|
* @brief Stop announcing sys ticks into the kernel
|
|
*
|
|
* This routine disables the RTC1 so that timer interrupts are no
|
|
* longer delivered.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void sys_clock_disable(void)
|
|
{
|
|
unsigned int key;
|
|
|
|
key = irq_lock();
|
|
|
|
irq_disable(NRF5_IRQ_RTC1_IRQn);
|
|
|
|
SYS_CLOCK_RTC->EVTENCLR = RTC_EVTENCLR_COMPARE0_Msk;
|
|
SYS_CLOCK_RTC->INTENCLR = RTC_INTENCLR_COMPARE0_Msk;
|
|
|
|
SYS_CLOCK_RTC->TASKS_STOP = 1;
|
|
SYS_CLOCK_RTC->TASKS_CLEAR = 1;
|
|
|
|
irq_unlock(key);
|
|
|
|
/* 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 */
|