blob: f13d24996078a5c47c9c096b07c4908f4687defc [file] [log] [blame]
Austin Schuh208337d2022-01-01 14:29:11 -08001/*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include "hardware/timer.h"
8#include "hardware/irq.h"
9#include "hardware/sync.h"
10#include "hardware/claim.h"
11
12check_hw_layout(timer_hw_t, ints, TIMER_INTS_OFFSET);
13
14static hardware_alarm_callback_t alarm_callbacks[NUM_TIMERS];
15static uint32_t target_hi[NUM_TIMERS];
16static uint8_t timer_callbacks_pending;
17
18static_assert(NUM_TIMERS <= 4, "");
19static uint8_t claimed;
20
21void hardware_alarm_claim(uint alarm_num) {
22 check_hardware_alarm_num_param(alarm_num);
23 hw_claim_or_assert(&claimed, alarm_num, "Hardware alarm %d already claimed");
24}
25
26void hardware_alarm_unclaim(uint alarm_num) {
27 check_hardware_alarm_num_param(alarm_num);
28 hw_claim_clear(&claimed, alarm_num);
29}
30
31bool hardware_alarm_is_claimed(uint alarm_num) {
32 check_hardware_alarm_num_param(alarm_num);
33 return hw_is_claimed(&claimed, alarm_num);
34}
35
36/// tag::time_us_64[]
37uint64_t time_us_64() {
38 // Need to make sure that the upper 32 bits of the timer
39 // don't change, so read that first
40 uint32_t hi = timer_hw->timerawh;
41 uint32_t lo;
42 do {
43 // Read the lower 32 bits
44 lo = timer_hw->timerawl;
45 // Now read the upper 32 bits again and
46 // check that it hasn't incremented. If it has loop around
47 // and read the lower 32 bits again to get an accurate value
48 uint32_t next_hi = timer_hw->timerawh;
49 if (hi == next_hi) break;
50 hi = next_hi;
51 } while (true);
52 return ((uint64_t) hi << 32u) | lo;
53}
54/// end::time_us_64[]
55
56/// \tag::busy_wait[]
57void busy_wait_us_32(uint32_t delay_us) {
58 if (0 <= (int32_t)delay_us) {
59 // we only allow 31 bits, otherwise we could have a race in the loop below with
60 // values very close to 2^32
61 uint32_t start = timer_hw->timerawl;
62 while (timer_hw->timerawl - start < delay_us) {
63 tight_loop_contents();
64 }
65 } else {
66 busy_wait_us(delay_us);
67 }
68}
69
70void busy_wait_us(uint64_t delay_us) {
71 uint64_t base = time_us_64();
72 uint64_t target = base + delay_us;
73 if (target < base) {
74 target = (uint64_t)-1;
75 }
76 absolute_time_t t;
77 update_us_since_boot(&t, target);
78 busy_wait_until(t);
79}
80
81void busy_wait_ms(uint32_t delay_ms)
82{
83 if (delay_ms <= 0x7fffffffu / 1000) {
84 busy_wait_us_32(delay_ms * 1000);
85 } else {
86 busy_wait_us(delay_ms * 1000ull);
87 }
88}
89
90void busy_wait_until(absolute_time_t t) {
91 uint64_t target = to_us_since_boot(t);
92 uint32_t hi_target = (uint32_t)(target >> 32u);
93 uint32_t hi = timer_hw->timerawh;
94 while (hi < hi_target) {
95 hi = timer_hw->timerawh;
96 tight_loop_contents();
97 }
98 while (hi == hi_target && timer_hw->timerawl < (uint32_t) target) {
99 hi = timer_hw->timerawh;
100 tight_loop_contents();
101 }
102}
103/// \end::busy_wait[]
104
105static inline uint harware_alarm_irq_number(uint alarm_num) {
106 return TIMER_IRQ_0 + alarm_num;
107}
108
109static void hardware_alarm_irq_handler(void) {
110 // Determine which timer this IRQ is for
111 uint32_t ipsr;
112 __asm volatile ("mrs %0, ipsr" : "=r" (ipsr)::);
113 uint alarm_num = (ipsr & 0x3fu) - 16 - TIMER_IRQ_0;
114 check_hardware_alarm_num_param(alarm_num);
115
116 hardware_alarm_callback_t callback = NULL;
117
118 spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_TIMER);
119 uint32_t save = spin_lock_blocking(lock);
120 // Clear the timer IRQ (inside lock, because we check whether we have handled the IRQ yet in alarm_set by looking at the interrupt status
121 timer_hw->intr = 1u << alarm_num;
122
123 // make sure the IRQ is still valid
124 if (timer_callbacks_pending & (1u << alarm_num)) {
125 // Now check whether we have a timer event to handle that isn't already obsolete (this could happen if we
126 // were already in the IRQ handler before someone else changed the timer setup
127 if (timer_hw->timerawh >= target_hi[alarm_num]) {
128 // we have reached the right high word as well as low word value
129 callback = alarm_callbacks[alarm_num];
130 timer_callbacks_pending &= (uint8_t)~(1u << alarm_num);
131 } else {
132 // try again in 2^32 us
133 timer_hw->alarm[alarm_num] = timer_hw->alarm[alarm_num]; // re-arm the timer
134 }
135 }
136
137 spin_unlock(lock, save);
138
139 if (callback) {
140 callback(alarm_num);
141 }
142}
143
144void hardware_alarm_set_callback(uint alarm_num, hardware_alarm_callback_t callback) {
145 // todo check current core owner
146 // note this should probably be subsumed by irq_set_exclusive_handler anyway, since that
147 // should disallow IRQ handlers on both cores
148 check_hardware_alarm_num_param(alarm_num);
149 uint irq_num = harware_alarm_irq_number(alarm_num);
150 spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_TIMER);
151 uint32_t save = spin_lock_blocking(lock);
152 if (callback) {
153 if (hardware_alarm_irq_handler != irq_get_vtable_handler(irq_num)) {
154 // note that set_exclusive will silently allow you to set the handler to the same thing
155 // since it is idempotent, which means we don't need to worry about locking ourselves
156 irq_set_exclusive_handler(irq_num, hardware_alarm_irq_handler);
157 irq_set_enabled(irq_num, true);
158 // Enable interrupt in block and at processor
159 hw_set_bits(&timer_hw->inte, 1u << alarm_num);
160 }
161 alarm_callbacks[alarm_num] = callback;
162 } else {
163 alarm_callbacks[alarm_num] = NULL;
164 timer_callbacks_pending &= (uint8_t)~(1u << alarm_num);
165 irq_remove_handler(irq_num, hardware_alarm_irq_handler);
166 irq_set_enabled(irq_num, false);
167 }
168 spin_unlock(lock, save);
169}
170
171bool hardware_alarm_set_target(uint alarm_num, absolute_time_t target) {
172 bool missed;
173 uint64_t now = time_us_64();
174 uint64_t t = to_us_since_boot(target);
175 if (now >= t) {
176 missed = true;
177 } else {
178 missed = false;
179
180 // 1) actually set the hardware timer
181 spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_TIMER);
182 uint32_t save = spin_lock_blocking(lock);
183 uint8_t old_timer_callbacks_pending = timer_callbacks_pending;
184 timer_callbacks_pending |= (uint8_t)(1u << alarm_num);
185 timer_hw->intr = 1u << alarm_num; // clear any IRQ
186 timer_hw->alarm[alarm_num] = (uint32_t) t;
187 // Set the alarm. Writing time should arm it
188 target_hi[alarm_num] = (uint32_t)(t >> 32u);
189
190 // 2) check for races
191 if (!(timer_hw->armed & 1u << alarm_num)) {
192 // not armed, so has already fired .. IRQ must be pending (we are still under lock)
193 assert(timer_hw->ints & 1u << alarm_num);
194 } else {
195 if (time_us_64() >= t) {
196 // we are already at or past the right time; there is no point in us racing against the IRQ
197 // we are about to generate. note however that, if there was already a timer pending before,
198 // then we still let the IRQ fire, as whatever it was, is not handled by our setting missed=true here
199 missed = true;
200 if (timer_callbacks_pending != old_timer_callbacks_pending) {
201 // disarm the timer
202 timer_hw->armed = 1u << alarm_num;
203 // clear the IRQ...
204 timer_hw->intr = 1u << alarm_num;
205 // ... including anything pending on the processor - perhaps unnecessary, but
206 // our timer flag says we aren't expecting anything.
207 irq_clear(harware_alarm_irq_number(alarm_num));
208 // and clear our flag so that if the IRQ handler is already active (because it is on
209 // the other core) it will also skip doing anything
210 timer_callbacks_pending = old_timer_callbacks_pending;
211 }
212 }
213 }
214 spin_unlock(lock, save);
215 // note at this point any pending timer IRQ can likely run
216 }
217 return missed;
218}
219
220void hardware_alarm_cancel(uint alarm_num) {
221 check_hardware_alarm_num_param(alarm_num);
222
223 spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_TIMER);
224 uint32_t save = spin_lock_blocking(lock);
225 timer_hw->armed = 1u << alarm_num;
226 timer_callbacks_pending &= (uint8_t)~(1u << alarm_num);
227 spin_unlock(lock, save);
228}
229
230