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