Software APIs
dif_rstmgr.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 <stdint.h>
9
13#include "sw/device/lib/base/multibits.h"
15
16#include "hw/top/rstmgr_regs.h" // Generated.
17
18// This macro simplifies the `static_assert` check to make sure that the
19// public reset info register bitfield matches register bits.
20#define RSTMGR_RESET_INFO_CHECK(pub_name, priv_name) \
21 static_assert(kDifRstmgrResetInfo##pub_name == \
22 (0x1 << RSTMGR_RESET_INFO_##priv_name##_BIT), \
23 "kDifRstmgrResetInfo" #pub_name \
24 " must match the register definition!")
25
26// These assertions are only defined for the Earl Grey chip.
27#if defined(OPENTITAN_IS_EARLGREY)
28
29RSTMGR_RESET_INFO_CHECK(Por, POR);
30RSTMGR_RESET_INFO_CHECK(LowPowerExit, LOW_POWER_EXIT);
31RSTMGR_RESET_INFO_CHECK(Sw, SW_RESET);
32
33static_assert(kDifRstmgrResetInfoHwReq == (RSTMGR_RESET_INFO_HW_REQ_MASK
34 << RSTMGR_RESET_INFO_HW_REQ_OFFSET),
35 "kDifRstmgrResetInfoHwReq must match the register definition!");
36
37// The Reset Manager implementation will have to be updated if the number
38// of software resets grows, as it would span across multiple registers, so
39// there will be multiple of Reset Enable and Reset Control registers. The
40// appropriate offset from the peripheral base would then have to be
41// calculated.
42static_assert(
43 RSTMGR_PARAM_NUM_SW_RESETS <= 32,
44 "Reset Enable and Control registers span across multiple registers!");
45
46// Make sure that the public alert info crash dump size matches the HW.
47// Note that `RSTMGR_ALERT_INFO_CTRL_INDEX_MASK` implies 16 indexes ( 0 - 15
48// inclusive). However, in reality it only supports 15, as
49// `RSTMGR_ALERT_INFO_ATTR_CNT_AVAIL_MASK` is of the same size, but value of
50// 0 indicates that there is no alert info crash dump.
51static_assert(
52 DIF_RSTMGR_ALERT_INFO_MAX_SIZE == RSTMGR_ALERT_INFO_CTRL_INDEX_MASK,
53 "Alert info dump max size has grown, please update the public define!");
54#elif defined(OPENTITAN_IS_DARJEELING)
55// TODO: equivalent assertions are not yet defined for Darjeeling
56#else
57#error "dif_rstmgr does not support this top"
58#endif
59
60/**
61 * Checks whether alert_info capture is disabled.
62 */
63static bool alert_capture_is_locked(mmio_region_t base_addr) {
64 uint32_t bitfield =
65 mmio_region_read32(base_addr, RSTMGR_ALERT_REGWEN_REG_OFFSET);
66
67 // When bit is cleared, alert capture is disabled.
68 return !bitfield_bit32_read(bitfield, RSTMGR_ALERT_REGWEN_EN_BIT);
69}
70
71/**
72 * Checks whether CPU info capture is disabled.
73 */
74static bool cpu_capture_is_locked(mmio_region_t base_addr) {
75 uint32_t bitfield =
76 mmio_region_read32(base_addr, RSTMGR_CPU_REGWEN_REG_OFFSET);
77
78 // When bit is cleared, APU capture is disabled.
79 return !bitfield_bit32_read(bitfield, RSTMGR_CPU_REGWEN_EN_BIT);
80}
81
82static inline ptrdiff_t get_regwen_reg_offset(dif_rstmgr_peripheral_t p) {
83#if RSTMGR_SW_RST_REGWEN_MULTIREG_COUNT > 1
84 return RSTMGR_SW_RST_REGWEN_0_REG_OFFSET + 4 * (ptrdiff_t)p;
85#else
86 return RSTMGR_SW_RST_REGWEN_REG_OFFSET + 4 * (ptrdiff_t)p;
87#endif
88}
89
90/**
91 * Checks whether the software reset is disabled for a `peripheral`.
92 */
93static bool rstmgr_software_reset_is_locked(
94 mmio_region_t base_addr, dif_rstmgr_peripheral_t peripheral) {
95 return !mmio_region_read32(base_addr, get_regwen_reg_offset(peripheral));
96}
97
98static inline ptrdiff_t get_ctrl_n_reg_offset(dif_rstmgr_peripheral_t p) {
99#if RSTMGR_SW_RST_CTRL_N_MULTIREG_COUNT > 1
100 return RSTMGR_SW_RST_CTRL_N_0_REG_OFFSET + 4 * (ptrdiff_t)p;
101#else
102 return RSTMGR_SW_RST_CTRL_N_REG_OFFSET + 4 * (ptrdiff_t)p;
103#endif
104}
105
106/**
107 * Holds or releases a `peripheral` in/from the reset state.
108 */
109static void rstmgr_software_reset_hold(mmio_region_t base_addr,
110 dif_rstmgr_peripheral_t peripheral,
111 bool hold) {
112 bool value = hold ? false : true;
113 mmio_region_write32(base_addr, get_ctrl_n_reg_offset(peripheral), value);
114}
115
116/**
117 * Clears entire reset info register.
118 *
119 * Normal "Power On Reset" cause is also cleared. Set bit to clear.
120 */
121static void rstmgr_reset_info_clear(mmio_region_t base_addr) {
122 mmio_region_write32(base_addr, RSTMGR_RESET_INFO_REG_OFFSET, UINT32_MAX);
123}
124
125dif_result_t dif_rstmgr_reset(const dif_rstmgr_t *handle) {
126 if (handle == NULL) {
127 return kDifBadArg;
128 }
129
130 mmio_region_t base_addr = handle->base_addr;
131
132 rstmgr_reset_info_clear(base_addr);
133
134 // Set bits to stop holding all peripherals in the reset state.
135 for (uint32_t i = 0; i < RSTMGR_PARAM_NUM_SW_RESETS; i++) {
136 mmio_region_write32(base_addr, get_ctrl_n_reg_offset(i), UINT32_MAX);
137 }
138
139 return kDifOk;
140}
141
142dif_result_t dif_rstmgr_reset_lock(const dif_rstmgr_t *handle,
143 dif_rstmgr_peripheral_t peripheral) {
144 if (handle == NULL || peripheral >= RSTMGR_PARAM_NUM_SW_RESETS) {
145 return kDifBadArg;
146 }
147
148 mmio_region_t base_addr = handle->base_addr;
149
150 mmio_region_write32(base_addr, get_regwen_reg_offset(peripheral), 0);
151
152 return kDifOk;
153}
154
155dif_result_t dif_rstmgr_reset_is_locked(const dif_rstmgr_t *handle,
156 dif_rstmgr_peripheral_t peripheral,
157 bool *is_locked) {
158 if (handle == NULL || is_locked == NULL ||
159 peripheral >= RSTMGR_PARAM_NUM_SW_RESETS) {
160 return kDifBadArg;
161 }
162
163 mmio_region_t base_addr = handle->base_addr;
164 *is_locked = rstmgr_software_reset_is_locked(base_addr, peripheral);
165
166 return kDifOk;
167}
168
169dif_result_t dif_rstmgr_reset_info_get(const dif_rstmgr_t *handle,
171 if (handle == NULL || info == NULL) {
172 return kDifBadArg;
173 }
174
175 mmio_region_t base_addr = handle->base_addr;
176 *info = mmio_region_read32(base_addr, RSTMGR_RESET_INFO_REG_OFFSET);
177
178 return kDifOk;
179}
180
181dif_result_t dif_rstmgr_reset_info_clear(const dif_rstmgr_t *handle) {
182 if (handle == NULL) {
183 return kDifBadArg;
184 }
185
186 mmio_region_t base_addr = handle->base_addr;
187
188 rstmgr_reset_info_clear(base_addr);
189
190 return kDifOk;
191}
192
193dif_result_t dif_rstmgr_alert_info_set_enabled(const dif_rstmgr_t *handle,
194 dif_toggle_t state) {
195 if (handle == NULL) {
196 return kDifBadArg;
197 }
198
199 mmio_region_t base_addr = handle->base_addr;
200
201 if (alert_capture_is_locked(base_addr)) {
202 return kDifLocked;
203 }
204
205 uint32_t enabled = (state == kDifToggleEnabled) ? 0x1 : 0x0;
206
207 // This will clobber the `ALERT_INFO_CTRL.INDEX` field. However, the index
208 // field is only relevant during the crash dump read operation, and is
209 // set by the caller and not the hardware, so it is safe to clobber it.
210 mmio_region_write32(base_addr, RSTMGR_ALERT_INFO_CTRL_REG_OFFSET, enabled);
211
212 return kDifOk;
213}
214
215dif_result_t dif_rstmgr_alert_info_get_enabled(const dif_rstmgr_t *handle,
216 dif_toggle_t *state) {
217 if (handle == NULL || state == NULL) {
218 return kDifBadArg;
219 }
220
221 mmio_region_t base_addr = handle->base_addr;
222
223 uint32_t reg =
224 mmio_region_read32(base_addr, RSTMGR_ALERT_INFO_CTRL_REG_OFFSET);
225 bool enabled = bitfield_bit32_read(reg, RSTMGR_ALERT_INFO_CTRL_EN_BIT);
226
227 *state = enabled ? kDifToggleEnabled : kDifToggleDisabled;
228
229 return kDifOk;
230}
231
232dif_result_t dif_rstmgr_alert_info_get_size(const dif_rstmgr_t *handle,
233 size_t *size) {
234 if (handle == NULL || size == NULL) {
235 return kDifBadArg;
236 }
237
238 mmio_region_t base_addr = handle->base_addr;
239 *size = mmio_region_read32(base_addr, RSTMGR_ALERT_INFO_ATTR_REG_OFFSET);
240 return kDifOk;
241}
242
243dif_result_t dif_rstmgr_alert_info_dump_read(
245 size_t dump_size, size_t *segments_read) {
246 if (handle == NULL || dump == NULL || segments_read == NULL) {
247 return kDifBadArg;
248 }
249
250 mmio_region_t base_addr = handle->base_addr;
251
252 // The actual crash dump size (can be smaller than `dump_size`).
253 size_t dump_size_actual =
254 mmio_region_read32(base_addr, RSTMGR_ALERT_INFO_ATTR_REG_OFFSET);
255
256 // Partial crash dump read is not allowed.
257 if (dump_size < dump_size_actual) {
258 return kDifError;
259 }
260
261 uint32_t control_reg =
262 mmio_region_read32(base_addr, RSTMGR_ALERT_INFO_CTRL_REG_OFFSET);
263
264 // Read the entire alert info crash dump, one 32bit data segment at the time.
265 for (uint32_t i = 0; i < dump_size_actual; ++i) {
266 control_reg = bitfield_field32_write(control_reg,
267 RSTMGR_ALERT_INFO_CTRL_INDEX_FIELD, i);
268
269 // Set the index of the 32bit data segment to be read at `i`.
270 mmio_region_write32(base_addr, RSTMGR_ALERT_INFO_CTRL_REG_OFFSET,
271 control_reg);
272
273 // Read the alert info crash dump 32bit data segment.
274 dump[i] = mmio_region_read32(base_addr, RSTMGR_ALERT_INFO_REG_OFFSET);
275 }
276
277 *segments_read = dump_size_actual;
278
279 return kDifOk;
280}
281
282dif_result_t dif_rstmgr_cpu_info_set_enabled(const dif_rstmgr_t *handle,
283 dif_toggle_t state) {
284 if (handle == NULL) {
285 return kDifBadArg;
286 }
287
288 mmio_region_t base_addr = handle->base_addr;
289
290 if (cpu_capture_is_locked(base_addr)) {
291 return kDifLocked;
292 }
293
294 uint32_t enabled = (state == kDifToggleEnabled) ? 0x1 : 0x0;
295
296 // This will clobber the `CPU_INFO_CTRL.INDEX` field. However, the index
297 // field is only relevant during the crash dump read operation, and is
298 // set by the caller and not the hardware, so it is safe to clobber it.
299 mmio_region_write32(base_addr, RSTMGR_CPU_INFO_CTRL_REG_OFFSET, enabled);
300
301 return kDifOk;
302}
303
304dif_result_t dif_rstmgr_cpu_info_get_enabled(const dif_rstmgr_t *handle,
305 dif_toggle_t *state) {
306 if (handle == NULL || state == NULL) {
307 return kDifBadArg;
308 }
309
310 mmio_region_t base_addr = handle->base_addr;
311
312 uint32_t reg = mmio_region_read32(base_addr, RSTMGR_CPU_INFO_CTRL_REG_OFFSET);
313 bool enabled = bitfield_bit32_read(reg, RSTMGR_CPU_INFO_CTRL_EN_BIT);
314
315 *state = enabled ? kDifToggleEnabled : kDifToggleDisabled;
316
317 return kDifOk;
318}
319
320dif_result_t dif_rstmgr_cpu_info_get_size(const dif_rstmgr_t *handle,
321 size_t *size) {
322 if (handle == NULL || size == NULL) {
323 return kDifBadArg;
324 }
325
326 mmio_region_t base_addr = handle->base_addr;
327 *size = mmio_region_read32(base_addr, RSTMGR_CPU_INFO_ATTR_REG_OFFSET);
328 return kDifOk;
329}
330
331dif_result_t dif_rstmgr_cpu_info_dump_read(
333 size_t dump_size, size_t *segments_read) {
334 if (handle == NULL || dump == NULL || segments_read == NULL) {
335 return kDifBadArg;
336 }
337
338 mmio_region_t base_addr = handle->base_addr;
339
340 // The actual crash dump size (can be smaller than `dump_size`).
341 size_t dump_size_actual =
342 mmio_region_read32(base_addr, RSTMGR_CPU_INFO_ATTR_REG_OFFSET);
343
344 // Partial crash dump read is not allowed.
345 if (dump_size < dump_size_actual) {
346 return kDifError;
347 }
348
349 uint32_t control_reg =
350 mmio_region_read32(base_addr, RSTMGR_CPU_INFO_CTRL_REG_OFFSET);
351
352 // Read the entire cpu info crash dump, one 32bit data segment at the time.
353 for (uint32_t i = 0; i < dump_size_actual; ++i) {
354 control_reg = bitfield_field32_write(control_reg,
355 RSTMGR_CPU_INFO_CTRL_INDEX_FIELD, i);
356
357 // Set the index of the 32bit data segment to be read at `i`.
358 mmio_region_write32(base_addr, RSTMGR_CPU_INFO_CTRL_REG_OFFSET,
359 control_reg);
360
361 // Read the cpu info crash dump 32bit data segment.
362 dump[i] = mmio_region_read32(base_addr, RSTMGR_CPU_INFO_REG_OFFSET);
363 }
364
365 *segments_read = dump_size_actual;
366
367 return kDifOk;
368}
369
371 dif_rstmgr_peripheral_t peripheral,
373 if (handle == NULL || peripheral >= RSTMGR_PARAM_NUM_SW_RESETS) {
374 return kDifBadArg;
375 }
376
377 mmio_region_t base_addr = handle->base_addr;
378 if (rstmgr_software_reset_is_locked(base_addr, peripheral)) {
379 return kDifLocked;
380 }
381
382 switch (reset) {
384 rstmgr_software_reset_hold(base_addr, peripheral, true);
385 rstmgr_software_reset_hold(base_addr, peripheral, false);
386 break;
388 rstmgr_software_reset_hold(base_addr, peripheral, true);
389 break;
391 rstmgr_software_reset_hold(base_addr, peripheral, false);
392 break;
393 default:
394 return kDifError;
395 }
396
397 return kDifOk;
398}
399
400dif_result_t dif_rstmgr_software_reset_is_held(
401 const dif_rstmgr_t *handle, dif_rstmgr_peripheral_t peripheral,
402 bool *asserted) {
403 if (handle == NULL || asserted == NULL ||
404 peripheral >= RSTMGR_PARAM_NUM_SW_RESETS) {
405 return kDifBadArg;
406 }
407
408 // When the bit is cleared - peripheral is held in reset.
409 *asserted =
410 !mmio_region_read32(handle->base_addr, get_ctrl_n_reg_offset(peripheral));
411
412 return kDifOk;
413}
414
415dif_result_t dif_rstmgr_software_device_reset(const dif_rstmgr_t *handle) {
416 if (handle == NULL) {
417 return kDifBadArg;
418 }
419
420 mmio_region_write32(handle->base_addr, RSTMGR_RESET_REQ_REG_OFFSET,
421 kMultiBitBool4True);
422
423 return kDifOk;
424}
425
426dif_result_t dif_rstmgr_get_sw_reset_index(dt_rstmgr_t dt, dt_reset_t reset,
427 size_t *sw_rst_idx) {
428 size_t sw_reset_count = dt_rstmgr_sw_reset_count(dt);
429 for (*sw_rst_idx = 0; *sw_rst_idx < sw_reset_count; ++(*sw_rst_idx)) {
430 if (dt_rstmgr_sw_reset(dt, *sw_rst_idx) == reset) {
431 return kDifOk;
432 }
433 }
434 return kDifBadArg;
435}
436
437dif_result_t dif_rstmgr_fatal_err_code_get_codes(
438 const dif_rstmgr_t *rstmgr, dif_rstmgr_fatal_err_codes_t *codes) {
439 if (rstmgr == NULL || codes == NULL) {
440 return kDifBadArg;
441 }
442 *codes = mmio_region_read32(rstmgr->base_addr, RSTMGR_ERR_CODE_REG_OFFSET);
443 return kDifOk;
444}