| /* |
| * 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> |
| #include <sys_clock.h> |
| |
| extern void youve_print(void); |
| /* |
| * 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 / \ |
| sys_clock_ticks_per_sec) * \ |
| 1000000000UL) / 30517578125UL) & RTC_MASK) |
| |
| 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 */ |
| |
| #ifdef CONFIG_TICKLESS_KERNEL |
| int32_t _get_max_clock_time(void); |
| #endif /* CONFIG_TICKLESS_KERNEL */ |
| |
| /* |
| * 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); |
| } |
| } |
| #ifndef CONFIG_TICKLESS_KERNEL |
| /* |
| * @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); |
| } |
| #endif |
| |
| #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) |
| { |
| #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*/ |
| 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)); |
| #endif |
| } |
| |
| |
| #ifdef CONFIG_TICKLESS_KERNEL |
| |
| /** |
| * @brief provides total systicks programmed. |
| * |
| * returns : total number of sys ticks programmed. |
| */ |
| |
| uint32_t _get_program_time(void) |
| { |
| return expected_sys_ticks; |
| } |
| |
| /** |
| * @brief provides total systicks remaining since last programming of RTC. |
| * |
| * returns : total number of sys ticks remaining since last RTC programming. |
| */ |
| |
| uint32_t _get_remaining_program_time(void) |
| { |
| |
| if (!expected_sys_ticks) { |
| return 0; |
| } |
| |
| return (expected_sys_ticks - _get_elapsed_program_time()); |
| } |
| |
| /** |
| * @brief provides total systicks passed since last programming of RTC. |
| * |
| * returns : total number of sys ticks passed since last RTC programming. |
| */ |
| |
| uint32_t _get_elapsed_program_time(void) |
| { |
| 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*/ |
| return (rtc_elapsed / RTC_TICKS_PER_SYS_TICK); |
| } |
| |
| |
| /** |
| * @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. |
| */ |
| |
| void _set_time(uint32_t time) |
| { |
| 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*/ |
| rtc_past = (_sys_clock_tick_count * RTC_TICKS_PER_SYS_TICK) & RTC_MASK; |
| |
| 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 + |
| (expected_sys_ticks * RTC_TICKS_PER_SYS_TICK)); |
| |
| } |
| |
| /** |
| * @brief provides time remaining to reach rtc count overflow. |
| * |
| * This function returns how many sys RTC remaining for rtc to overflow. |
| * This will be required when we will program RTC compare value to maximum |
| * possible value. |
| * |
| * returns : difference between current systick and Maximum possible systick. |
| */ |
| int32_t _get_max_clock_time(void) |
| { |
| 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*/ |
| if (rtc_away >= RTC_TICKS_PER_SYS_TICK) { |
| sys_away = rtc_away / RTC_TICKS_PER_SYS_TICK; |
| } |
| return sys_away; |
| } |
| |
| /** |
| * @brief Enable sys Clock. |
| * |
| * This is used to program RTC clock to maximum Clock time in case Clock to |
| * 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. |
| */ |
| |
| uint64_t _get_elapsed_clock_time(void) |
| { |
| 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); |
| |
| if (rtc_elapsed >= RTC_TICKS_PER_SYS_TICK) { |
| /* Convert RTC cycles to SYS TICKS*/ |
| sys_elapsed = rtc_elapsed / RTC_TICKS_PER_SYS_TICK; |
| /* Update total number of SYS_TICKS passed*/ |
| elapsed += sys_elapsed; |
| } |
| return elapsed; |
| } |
| #endif |
| |
| |
| /** |
| * |
| * @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) |
| { |
| #ifdef CONFIG_TICKLESS_KERNEL |
| if (!expected_sys_ticks && _sys_clock_always_on) { |
| _set_time(_get_max_clock_time()); |
| } |
| #else |
| /* 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 |
| } |
| #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; |
| |
| #ifdef CONFIG_EXECUTION_BENCHMARKING |
| extern void read_timer_start_of_tick_handler(void); |
| read_timer_start_of_tick_handler(); |
| #endif |
| |
| #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 |
| rtc_announce_set_next(); |
| #endif |
| |
| #ifdef CONFIG_EXECUTION_BENCHMARKING |
| extern void read_timer_end_of_tick_handler(void); |
| read_timer_end_of_tick_handler(); |
| #endif |
| |
| } |
| |
| 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; |
| u32_t sys_clock_tick_count; |
| u32_t rtc_prev; |
| u32_t rtc_now; |
| |
| rtc_now = RTC_COUNTER; |
| /* Discard value of RTC_COUNTER read at LFCLK transition */ |
| do { |
| sys_clock_tick_count = _sys_clock_tick_count; |
| elapsed_cycles = (rtc_now - (sys_clock_tick_count * |
| RTC_TICKS_PER_SYS_TICK)) & |
| 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) + |
| 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 */ |