1 /*
2  * Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _PICO_TIME_ADAPTER_H
8 #define _PICO_TIME_ADAPTER_H
9 
10 #include "hardware/irq.h"
11 #include "hardware/timer.h"
12 #include "pico/assert.h"
13 
14 #define TA_NUM_TIMERS NUM_GENERIC_TIMERS
15 #define TA_NUM_TIMER_ALARMS NUM_ALARMS
16 
17 #define timer_hw_from_timer(t) ((timer_hw_t *)(t))
18 
ta_force_irq(alarm_pool_timer_t * timer,uint alarm_num)19 static inline void ta_force_irq(alarm_pool_timer_t *timer, uint alarm_num) {
20     hw_set_bits(&timer_hw_from_timer(timer)->intf, 1u << alarm_num);
21 }
22 
ta_clear_force_irq(alarm_pool_timer_t * timer,uint alarm_num)23 static inline void ta_clear_force_irq(alarm_pool_timer_t *timer, uint alarm_num) {
24     hw_clear_bits(&timer_hw_from_timer(timer)->intf, 1u << alarm_num);
25 }
26 
ta_clear_irq(alarm_pool_timer_t * timer,uint alarm_num)27 static inline void ta_clear_irq(alarm_pool_timer_t *timer, uint alarm_num) {
28     timer_hw_from_timer(timer)->intr = 1u << alarm_num;
29 }
30 
ta_from_current_irq(uint * alarm_num)31 static inline alarm_pool_timer_t *ta_from_current_irq(uint *alarm_num) {
32     uint irq_num = __get_current_exception() - VTABLE_FIRST_IRQ;
33     alarm_pool_timer_t *timer = timer_get_instance(TIMER_NUM_FROM_IRQ(irq_num));
34     *alarm_num = TIMER_ALARM_NUM_FROM_IRQ(irq_num);
35     return timer;
36 }
37 
ta_set_timeout(alarm_pool_timer_t * timer,uint alarm_num,int64_t target)38 static inline void ta_set_timeout(alarm_pool_timer_t *timer, uint alarm_num, int64_t target) {
39     // We never want to set the timeout to be later than our current one.
40     uint32_t current = timer_time_us_32(timer_hw_from_timer(timer));
41     uint32_t time_til_target = (uint32_t) target - current;
42     uint32_t time_til_alarm = timer_hw_from_timer(timer)->alarm[alarm_num] - current;
43     // Note: we are only dealing with the low 32 bits of the timer values,
44     // so there is some opportunity to make wrap-around errors.
45     //
46     // 1. If we just passed the alarm time, then time_til_alarm will be high, meaning we will
47     //    likely do the update, but this is OK since the alarm will have just fired
48     // 2. If we just passed the target time, then time_til_target will be high, meaning we will
49     //    likely not do the update, but this is OK since the caller who has the full 64 bits
50     //    must check if the target time has passed when we return anyway to avoid races.
51     if (time_til_target < time_til_alarm) {
52         timer_hw_from_timer(timer)->alarm[alarm_num] = (uint32_t) target;
53     }
54 }
55 
ta_wakes_up_on_or_before(alarm_pool_timer_t * timer,uint alarm_num,int64_t target)56 static inline bool ta_wakes_up_on_or_before(alarm_pool_timer_t *timer, uint alarm_num, int64_t target) {
57     uint32_t current = timer_time_us_32(timer_hw_from_timer(timer));
58     uint32_t time_til_target = (uint32_t) target - current;
59     uint32_t time_til_alarm = timer_hw_from_timer(timer)->alarm[alarm_num] - current;
60     return time_til_alarm <= time_til_target;
61 }
62 
ta_time_us_64(alarm_pool_timer_t * timer)63 static inline uint64_t ta_time_us_64(alarm_pool_timer_t *timer) {
64     return timer_time_us_64(timer_hw_from_timer(timer));
65 }
66 
ta_enable_irq_handler(alarm_pool_timer_t * timer,uint alarm_num,irq_handler_t irq_handler)67 static inline void ta_enable_irq_handler(alarm_pool_timer_t *timer, uint alarm_num, irq_handler_t irq_handler) {
68     // disarm the timer
69     uint irq_num = timer_hardware_alarm_get_irq_num(timer, alarm_num);
70     timer_hw_from_timer(timer)->armed = 1u << alarm_num;
71     irq_set_exclusive_handler(irq_num, irq_handler);
72     irq_set_enabled(irq_num, true);
73     hw_set_bits(&timer_hw_from_timer(timer)->inte, 1u << alarm_num);
74 }
75 
ta_disable_irq_handler(alarm_pool_timer_t * timer,uint alarm_num,irq_handler_t irq_handler)76 static inline void ta_disable_irq_handler(alarm_pool_timer_t *timer, uint alarm_num, irq_handler_t irq_handler) {
77     uint irq_num = timer_hardware_alarm_get_irq_num(timer, alarm_num);
78     hw_clear_bits(&timer_hw_from_timer(timer)->inte, 1u << alarm_num);
79     irq_set_enabled(irq_num, true);
80     irq_remove_handler(irq_num, irq_handler);
81     hardware_alarm_unclaim(alarm_num);
82 }
83 
ta_hardware_alarm_claim(alarm_pool_timer_t * timer,uint hardware_alaram_num)84 static inline void ta_hardware_alarm_claim(alarm_pool_timer_t *timer, uint hardware_alaram_num) {
85     timer_hardware_alarm_claim(timer_hw_from_timer(timer), hardware_alaram_num);
86 }
87 
ta_hardware_alarm_claim_unused(alarm_pool_timer_t * timer,bool required)88 static inline int ta_hardware_alarm_claim_unused(alarm_pool_timer_t *timer, bool required) {
89     return timer_hardware_alarm_claim_unused(timer, required);
90 }
91 
ta_timer_instance(uint timer_num)92 static inline alarm_pool_timer_t *ta_timer_instance(uint timer_num) {
93     return timer_get_instance(timer_num);
94 }
95 
ta_timer_num(alarm_pool_timer_t * timer)96 static inline uint ta_timer_num(alarm_pool_timer_t *timer) {
97     return timer_get_index(timer_hw_from_timer(timer));
98 }
99 
ta_default_timer_instance(void)100 static inline alarm_pool_timer_t *ta_default_timer_instance(void) {
101     return PICO_DEFAULT_TIMER_INSTANCE();
102 }
103 #endif
104