Software APIs
dif_aon_timer.c
1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
6
7#include <assert.h>
8#include <stddef.h>
9
13
14#include "aon_timer_regs.h" // Generated.
15
16static_assert(AON_TIMER_INTR_STATE_WKUP_TIMER_EXPIRED_BIT ==
17 AON_TIMER_INTR_TEST_WKUP_TIMER_EXPIRED_BIT,
18 "Wake-up IRQ have different indexes in different registers!");
19static_assert(AON_TIMER_INTR_STATE_WDOG_TIMER_BARK_BIT ==
20 AON_TIMER_INTR_TEST_WDOG_TIMER_BARK_BIT,
21 "Watchdog IRQ have different indexes in different registers!");
22
23// Note count is a 64-bit value that cannot be set atomically. It is recommened
24// the counter is only cleared when the wakeup timer is disabled.
25static void aon_timer_wakeup_clear_counter(const dif_aon_timer_t *aon) {
26 mmio_region_write32(aon->base_addr, AON_TIMER_WKUP_COUNT_LO_REG_OFFSET, 0);
27 mmio_region_write32(aon->base_addr, AON_TIMER_WKUP_COUNT_HI_REG_OFFSET, 0);
28}
29
30static void aon_timer_wakeup_toggle(const dif_aon_timer_t *aon, bool enable) {
31 uint32_t reg =
32 mmio_region_read32(aon->base_addr, AON_TIMER_WKUP_CTRL_REG_OFFSET);
33 reg = bitfield_bit32_write(reg, AON_TIMER_WKUP_CTRL_ENABLE_BIT, enable);
34 mmio_region_write32(aon->base_addr, AON_TIMER_WKUP_CTRL_REG_OFFSET, reg);
35}
36
37static void aon_timer_watchdog_clear_counter(const dif_aon_timer_t *aon) {
38 mmio_region_write32(aon->base_addr, AON_TIMER_WDOG_COUNT_REG_OFFSET, 0);
39}
40
41static void aon_timer_watchdog_toggle(const dif_aon_timer_t *aon, bool enable) {
42 uint32_t reg =
43 mmio_region_read32(aon->base_addr, AON_TIMER_WDOG_CTRL_REG_OFFSET);
44 reg = bitfield_bit32_write(reg, AON_TIMER_WDOG_CTRL_ENABLE_BIT, enable);
45 mmio_region_write32(aon->base_addr, AON_TIMER_WDOG_CTRL_REG_OFFSET, reg);
46}
47
48static void aon_timer_watchdog_lock(const dif_aon_timer_t *aon) {
49 // Clear bit to lock the watchdog configuration register until the next reset.
50 // Write one to clear the bit.
51 mmio_region_write32(aon->base_addr, AON_TIMER_WDOG_REGWEN_REG_OFFSET,
52 0x00000001);
53}
54
55static bool aon_timer_watchdog_is_locked(const dif_aon_timer_t *aon) {
56 uint32_t reg =
57 mmio_region_read32(aon->base_addr, AON_TIMER_WDOG_REGWEN_REG_OFFSET);
58
59 // Locked when bit is cleared.
60 return !bitfield_bit32_read(reg, AON_TIMER_WDOG_REGWEN_REGWEN_BIT);
61}
62
63dif_result_t dif_aon_timer_wakeup_start(const dif_aon_timer_t *aon,
64 uint64_t threshold,
65 uint32_t prescaler) {
66 uint64_t threshold_dec;
67
68 if (aon == NULL || prescaler > AON_TIMER_WKUP_CTRL_PRESCALER_MASK) {
69 return kDifBadArg;
70 }
71
72 // The timer should be stopped first, otherwise it will continue counting up.
73 aon_timer_wakeup_toggle(aon, false);
74 aon_timer_wakeup_clear_counter(aon);
75
76 threshold_dec = threshold - 1;
77
78 // As AON_TIMER spends one more cycle to create the interrupt, subtract
79 // cycles by 1 here.
80 mmio_region_write32(aon->base_addr, AON_TIMER_WKUP_THOLD_LO_REG_OFFSET,
81 (uint32_t)(threshold_dec & 0xffffffff));
82 mmio_region_write32(aon->base_addr, AON_TIMER_WKUP_THOLD_HI_REG_OFFSET,
83 (uint32_t)(threshold_dec >> 32));
84
85 uint32_t reg =
86 bitfield_field32_write(0, AON_TIMER_WKUP_CTRL_PRESCALER_FIELD, prescaler);
87 reg = bitfield_bit32_write(reg, AON_TIMER_WKUP_CTRL_ENABLE_BIT, true);
88 mmio_region_write32(aon->base_addr, AON_TIMER_WKUP_CTRL_REG_OFFSET, reg);
89
90 return kDifOk;
91}
92
93dif_result_t dif_aon_timer_wakeup_stop(const dif_aon_timer_t *aon) {
94 if (aon == NULL) {
95 return kDifBadArg;
96 }
97
98 aon_timer_wakeup_toggle(aon, false);
99
100 return kDifOk;
101}
102
103dif_result_t dif_aon_timer_wakeup_restart(const dif_aon_timer_t *aon) {
104 if (aon == NULL) {
105 return kDifBadArg;
106 }
107
108 aon_timer_wakeup_toggle(aon, false);
109 aon_timer_wakeup_clear_counter(aon);
110 aon_timer_wakeup_toggle(aon, true);
111
112 return kDifOk;
113}
114
115dif_result_t dif_aon_timer_wakeup_is_enabled(const dif_aon_timer_t *aon,
116 bool *is_enabled) {
117 if (aon == NULL || is_enabled == NULL) {
118 return kDifBadArg;
119 }
120
121 uint32_t reg =
122 mmio_region_read32(aon->base_addr, AON_TIMER_WKUP_CTRL_REG_OFFSET);
123
124 *is_enabled = bitfield_bit32_read(reg, AON_TIMER_WKUP_CTRL_ENABLE_BIT);
125
126 return kDifOk;
127}
128
129dif_result_t dif_aon_timer_get_wakeup_cause(const dif_aon_timer_t *aon,
130 bool *cause) {
131 if (aon == NULL || cause == NULL) {
132 return kDifBadArg;
133 }
134 uint32_t reg =
135 mmio_region_read32(aon->base_addr, AON_TIMER_WKUP_CAUSE_REG_OFFSET);
136 *cause = bitfield_bit32_read(reg, AON_TIMER_WKUP_CAUSE_CAUSE_BIT);
137 return kDifOk;
138}
139
140dif_result_t dif_aon_timer_clear_wakeup_cause(const dif_aon_timer_t *aon) {
141 if (aon == NULL) {
142 return kDifBadArg;
143 }
144 mmio_region_write32(aon->base_addr, AON_TIMER_WKUP_CAUSE_REG_OFFSET, 0);
145 return kDifOk;
146}
147
148dif_result_t dif_aon_timer_wakeup_get_count(const dif_aon_timer_t *aon,
149 uint64_t *count) {
150 uint32_t count_lo;
151 uint32_t count_hi;
152 uint32_t count_hi_2;
153
154 if (aon == NULL || count == NULL) {
155 return kDifBadArg;
156 }
157
158 count_hi =
159 mmio_region_read32(aon->base_addr, AON_TIMER_WKUP_COUNT_HI_REG_OFFSET);
160 count_lo =
161 mmio_region_read32(aon->base_addr, AON_TIMER_WKUP_COUNT_LO_REG_OFFSET);
162 count_hi_2 =
163 mmio_region_read32(aon->base_addr, AON_TIMER_WKUP_COUNT_HI_REG_OFFSET);
164
165 // If second WKUP_COUNT_HI read differs from the first WKUP_COUNT_LO has
166 // overflowed due to counter increment so read new WKUP_COUNT_LO value and use
167 // second WKUP_COUNT_HI read as top 32-bit value.
168 if (count_hi_2 != count_hi) {
169 count_lo =
170 mmio_region_read32(aon->base_addr, AON_TIMER_WKUP_COUNT_LO_REG_OFFSET);
171 count_hi = count_hi_2;
172 }
173
174 *count = (uint64_t)count_lo | (((uint64_t)count_hi) << 32);
175
176 return kDifOk;
177}
178
179dif_result_t dif_aon_timer_watchdog_start(const dif_aon_timer_t *aon,
180 uint32_t bark_threshold,
181 uint32_t bite_threshold,
182 bool pause_in_sleep, bool lock) {
183 if (aon == NULL) {
184 return kDifBadArg;
185 }
186
187 if (aon_timer_watchdog_is_locked(aon)) {
188 return kDifLocked;
189 }
190
191 // The timer should be stopped first, otherwise it will continue counting up.
192 aon_timer_watchdog_toggle(aon, false);
193 aon_timer_watchdog_clear_counter(aon);
194
195 // As AON_TIMER spends one more cycle to create the interrupt, subtract
196 // cycles by 1 here.
197 mmio_region_write32(aon->base_addr, AON_TIMER_WDOG_BARK_THOLD_REG_OFFSET,
198 bark_threshold - 1);
199 mmio_region_write32(aon->base_addr, AON_TIMER_WDOG_BITE_THOLD_REG_OFFSET,
200 bite_threshold - 1);
201
202 uint32_t reg = bitfield_bit32_write(0, AON_TIMER_WDOG_CTRL_ENABLE_BIT, true);
203 if (pause_in_sleep) {
204 reg =
205 bitfield_bit32_write(reg, AON_TIMER_WDOG_CTRL_PAUSE_IN_SLEEP_BIT, true);
206 }
207 mmio_region_write32(aon->base_addr, AON_TIMER_WDOG_CTRL_REG_OFFSET, reg);
208
209 // Watchdog control register should only be locked after the last
210 // control register access.
211 if (lock) {
212 aon_timer_watchdog_lock(aon);
213 }
214
215 return kDifOk;
216}
217
218dif_result_t dif_aon_timer_watchdog_stop(const dif_aon_timer_t *aon) {
219 if (aon == NULL) {
220 return kDifBadArg;
221 }
222
223 if (aon_timer_watchdog_is_locked(aon)) {
224 return kDifLocked;
225 }
226
227 aon_timer_watchdog_toggle(aon, false);
228
229 return kDifOk;
230}
231
232dif_result_t dif_aon_timer_watchdog_restart(const dif_aon_timer_t *aon) {
233 if (aon == NULL) {
234 return kDifBadArg;
235 }
236
237 if (aon_timer_watchdog_is_locked(aon)) {
238 return kDifLocked;
239 }
240
241 aon_timer_watchdog_clear_counter(aon);
242 aon_timer_watchdog_toggle(aon, true);
243
244 return kDifOk;
245}
246
247dif_result_t dif_aon_timer_watchdog_is_enabled(const dif_aon_timer_t *aon,
248 bool *is_enabled) {
249 if (aon == NULL || is_enabled == NULL) {
250 return kDifBadArg;
251 }
252
253 uint32_t reg =
254 mmio_region_read32(aon->base_addr, AON_TIMER_WDOG_CTRL_REG_OFFSET);
255
256 *is_enabled = bitfield_bit32_read(reg, AON_TIMER_WDOG_CTRL_ENABLE_BIT);
257
258 return kDifOk;
259}
260
261dif_result_t dif_aon_timer_watchdog_get_count(const dif_aon_timer_t *aon,
262 uint32_t *count) {
263 if (aon == NULL || count == NULL) {
264 return kDifBadArg;
265 }
266
267 *count = mmio_region_read32(aon->base_addr, AON_TIMER_WDOG_COUNT_REG_OFFSET);
268
269 return kDifOk;
270}
271
272dif_result_t dif_aon_timer_watchdog_pet(const dif_aon_timer_t *aon) {
273 if (aon == NULL) {
274 return kDifBadArg;
275 }
276
277 aon_timer_watchdog_clear_counter(aon);
278
279 return kDifOk;
280}
281
282dif_result_t dif_aon_timer_watchdog_lock(const dif_aon_timer_t *aon) {
283 if (aon == NULL) {
284 return kDifBadArg;
285 }
286
287 aon_timer_watchdog_lock(aon);
288
289 return kDifOk;
290}
291
292dif_result_t dif_aon_timer_watchdog_is_locked(const dif_aon_timer_t *aon,
293 bool *is_locked) {
294 if (aon == NULL || is_locked == NULL) {
295 return kDifBadArg;
296 }
297
298 *is_locked = aon_timer_watchdog_is_locked(aon);
299
300 return kDifOk;
301}