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 
16 static_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!");
19 static_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.
25 static 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 
30 static 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 
37 static 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 
41 static 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 
48 static 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 
55 static 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 
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 
94  if (aon == NULL) {
95  return kDifBadArg;
96  }
97 
98  aon_timer_wakeup_toggle(aon, false);
99 
100  return kDifOk;
101 }
102 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
273  if (aon == NULL) {
274  return kDifBadArg;
275  }
276 
277  aon_timer_watchdog_clear_counter(aon);
278 
279  return kDifOk;
280 }
281 
283  if (aon == NULL) {
284  return kDifBadArg;
285  }
286 
287  aon_timer_watchdog_lock(aon);
288 
289  return kDifOk;
290 }
291 
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 }