Pavona Software APIs
dif_alert_handler.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 <limits.h>
9
11
12#include "hw/top/alert_handler_regs.h" // Generated.
13
14static_assert(ALERT_HANDLER_PARAM_N_CLASSES == 4,
15 "Expected four alert classes!");
16static_assert(ALERT_HANDLER_PARAM_N_ESC_SEV == 4,
17 "Expected four escalation signals!");
18static_assert(ALERT_HANDLER_PARAM_N_PHASES == 4,
19 "Expected four escalation phases!");
20static_assert(ALERT_HANDLER_PARAM_N_LOC_ALERT == 7,
21 "Expected seven local alerts!");
22
23// Enable, class, lock, and cause multiregs for alerts.
24static_assert(ALERT_HANDLER_ALERT_EN_SHADOWED_MULTIREG_COUNT &&
25 ALERT_HANDLER_ALERT_EN_SHADOWED_EN_A_FIELD_WIDTH == 1 &&
26 ALERT_HANDLER_ALERT_EN_SHADOWED_0_EN_A_0_BIT == 0,
27 "Expected alert enables to be multiregs with LSB at index 0!");
28static_assert(ALERT_HANDLER_ALERT_CLASS_SHADOWED_MULTIREG_COUNT &&
29 ALERT_HANDLER_ALERT_CLASS_SHADOWED_CLASS_A_FIELD_WIDTH == 2 &&
30 ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_OFFSET == 0,
31 "Expected alert class CSRs to be multiregs with LSB at index 0!");
32static_assert(ALERT_HANDLER_ALERT_REGWEN_MULTIREG_COUNT &&
33 ALERT_HANDLER_ALERT_REGWEN_EN_FIELD_WIDTH == 1 &&
34 ALERT_HANDLER_ALERT_REGWEN_0_EN_0_BIT == 0,
35 "Expected alert locks to be multiregs with LSB at index 0!");
36static_assert(ALERT_HANDLER_ALERT_CAUSE_MULTIREG_COUNT &&
37 ALERT_HANDLER_ALERT_CAUSE_A_FIELD_WIDTH == 1 &&
38 ALERT_HANDLER_ALERT_CAUSE_0_A_0_BIT == 0,
39 "Expected alert causes to be multiregs with LSB at index 0!");
40
41// Enable, class, lock, and cause multiregs for local alerts.
42static_assert(
43 ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_MULTIREG_COUNT &&
44 ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_EN_LA_FIELD_WIDTH == 1 &&
45 ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_0_EN_LA_0_BIT == 0,
46 "Expected local alert enables to be multiregs with LSB at index 0!");
47static_assert(
48 ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_MULTIREG_COUNT &&
49 ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_CLASS_LA_FIELD_WIDTH == 2 &&
50 ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_OFFSET == 0,
51 "Expected local alert class CSRs to be multiregs with LSB at index 0!");
52static_assert(
53 ALERT_HANDLER_LOC_ALERT_REGWEN_MULTIREG_COUNT &&
54 ALERT_HANDLER_LOC_ALERT_REGWEN_EN_FIELD_WIDTH == 1 &&
55 ALERT_HANDLER_LOC_ALERT_REGWEN_0_EN_0_BIT == 0,
56 "Expected local alert locks to be multiregs with LSB at index 0!");
57static_assert(
58 ALERT_HANDLER_LOC_ALERT_CAUSE_MULTIREG_COUNT &&
59 ALERT_HANDLER_LOC_ALERT_CAUSE_LA_FIELD_WIDTH == 1 &&
60 ALERT_HANDLER_LOC_ALERT_CAUSE_0_LA_0_BIT == 0,
61 "Expected local alert causes to be multiregs with LSB at index 0!");
62
63// Accumulator threshold field sizes.
64static_assert(
65 ALERT_HANDLER_CLASSA_ACCUM_THRESH_SHADOWED_CLASSA_ACCUM_THRESH_SHADOWED_MASK <=
66 USHRT_MAX,
67 "Expected class A accumulator threshold field to be 16 bits.");
68static_assert(
69 ALERT_HANDLER_CLASSB_ACCUM_THRESH_SHADOWED_CLASSB_ACCUM_THRESH_SHADOWED_MASK <=
70 USHRT_MAX,
71 "Expected class B accumulator threshold field to be 16 bits.");
72static_assert(
73 ALERT_HANDLER_CLASSC_ACCUM_THRESH_SHADOWED_CLASSC_ACCUM_THRESH_SHADOWED_MASK <=
74 USHRT_MAX,
75 "Expected class C accumulator threshold field to be 16 bits.");
76static_assert(
77 ALERT_HANDLER_CLASSD_ACCUM_THRESH_SHADOWED_CLASSD_ACCUM_THRESH_SHADOWED_MASK <=
78 USHRT_MAX,
79 "Expected class D accumulator threshold field to be 16 bits.");
80
81/**
82 * Macro for generating the case statements for local alert cause CSRs.
83 */
84#define LOC_ALERT_CAUSE_REGS_CASE_(loc_alert_, value_) \
85 case loc_alert_: \
86 cause_reg_offset = ALERT_HANDLER_LOC_ALERT_CAUSE_##value_##_REG_OFFSET; \
87 break;
88
89/**
90 * Macro for generating the case statements for local alert lock CSRs.
91 */
92#define LOC_ALERT_REGWENS_CASE_(loc_alert_, value_) \
93 case loc_alert_: \
94 regwen_offset = ALERT_HANDLER_LOC_ALERT_REGWEN_##value_##_REG_OFFSET; \
95 break;
96
97/**
98 * Macro for generating the case statements for class lock CSRs.
99 */
100#define ALERT_CLASS_REGWENS_CASE_(class_, value_) \
101 case kDifAlertHandlerClass##class_: \
102 regwen_offset = ALERT_HANDLER_CLASS##class_##_REGWEN_REG_OFFSET; \
103 break;
104
105/**
106 * Macro for generating the case statements for class clear lock CSRs.
107 */
108#define ALERT_CLASS_CLEAR_REGWENS_CASE_(class_, value_) \
109 case kDifAlertHandlerClass##class_: \
110 regwen_offset = ALERT_HANDLER_CLASS##class_##_CLR_REGWEN_REG_OFFSET; \
111 break;
112
113/**
114 * We use this to convert the class enum to the integer value that is
115 * assigned to each class in auto-generated register header file. We do this
116 * to make this code robust against changes to the class values in the
117 * auto-generated register header file.
118 */
120static bool class_to_uint32(dif_alert_handler_class_t alert_class,
121 uint32_t *classification) {
122#define ALERT_CLASS_REGS_CASE_(class_, value_) \
123 case kDifAlertHandlerClass##class_: \
124 *classification = \
125 ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASS##class_; \
126 break;
127 switch (alert_class) {
128 LIST_OF_CLASSES(ALERT_CLASS_REGS_CASE_)
129 default:
130 return false;
131 }
132
133#undef ALERT_CLASS_REGS_CASE_
134
135 return true;
136}
137
139static bool is_valid_escalation_phase(dif_alert_handler_class_state_t phase) {
142 return false;
143 }
144 return true;
145}
146
147/**
148 * NOTE: the alert ID corresponds directly to the multireg index for each CSR.
149 * (I.e., alert N has enable multireg N).
150 */
151dif_result_t dif_alert_handler_configure_alert(
152 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
153 dif_alert_handler_class_t alert_class, dif_toggle_t enabled,
154 dif_toggle_t locked) {
155 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS ||
156 !dif_is_valid_toggle(enabled) || !dif_is_valid_toggle(locked)) {
157 return kDifBadArg;
158 }
159 uint32_t classification;
160 if (!class_to_uint32(alert_class, &classification)) {
161 return kDifBadArg;
162 }
163
164 // Check if configuration is locked.
165 ptrdiff_t regwen_offset = ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET +
166 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
167 if (!mmio_region_read32(alert_handler->base_addr, regwen_offset)) {
168 return kDifLocked;
169 }
170
171 // Classify the alert.
172 ptrdiff_t class_reg_offset = ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_REG_OFFSET +
173 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
174 mmio_region_write32_shadowed(alert_handler->base_addr, class_reg_offset,
175 classification);
176
177 // Enable the alert.
178 ptrdiff_t enable_reg_offset = ALERT_HANDLER_ALERT_EN_SHADOWED_0_REG_OFFSET +
179 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
180 mmio_region_write32_shadowed(alert_handler->base_addr, enable_reg_offset,
181 dif_toggle_to_bool(enabled));
182
183 // Lock the configuration.
184 if (locked == kDifToggleEnabled) {
185 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
186 }
187
188 return kDifOk;
189}
190
191dif_result_t dif_alert_handler_configure_local_alert(
192 const dif_alert_handler_t *alert_handler,
194 dif_alert_handler_class_t alert_class, dif_toggle_t enabled,
195 dif_toggle_t locked) {
196 if (alert_handler == NULL || !dif_is_valid_toggle(enabled) ||
197 !dif_is_valid_toggle(locked)) {
198 return kDifBadArg;
199 }
200 uint32_t classification;
201 if (!class_to_uint32(alert_class, &classification)) {
202 return kDifBadArg;
203 }
204
205#define LOC_ALERT_REGS_CASE_(loc_alert_, value_) \
206 case loc_alert_: \
207 enable_reg_offset = \
208 ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_##value_##_REG_OFFSET; \
209 class_reg_offset = \
210 ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_##value_##_REG_OFFSET; \
211 regwen_offset = ALERT_HANDLER_LOC_ALERT_REGWEN_##value_##_REG_OFFSET; \
212 break;
213
214 // Get register/field offsets for local alert type.
215 ptrdiff_t enable_reg_offset;
216 ptrdiff_t class_reg_offset;
217 ptrdiff_t regwen_offset;
218 switch (local_alert) {
219 LIST_OF_LOC_ALERTS(LOC_ALERT_REGS_CASE_)
220 default:
221 return kDifBadArg;
222 }
223
224#undef LOC_ALERT_REGS_CASE_
225
226 // Check if configuration is locked.
227 if (!mmio_region_read32(alert_handler->base_addr, regwen_offset)) {
228 return kDifLocked;
229 }
230
231 // Classify the alert.
232 mmio_region_write32_shadowed(alert_handler->base_addr, class_reg_offset,
233 classification);
234
235 // Enable the alert.
236 // NOTE: the alert ID corresponds directly to the multireg index.
237 // (I.e., alert N has enable multireg N).
238 mmio_region_write32_shadowed(alert_handler->base_addr, enable_reg_offset,
239 0x1);
240
241 // Lock the configuration.
242 if (locked == kDifToggleEnabled) {
243 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
244 }
245
246 return kDifOk;
247}
248
249dif_result_t dif_alert_handler_configure_class(
250 const dif_alert_handler_t *alert_handler,
251 dif_alert_handler_class_t alert_class,
253 dif_toggle_t locked) {
254 if (alert_handler == NULL ||
255 !dif_is_valid_toggle(config.auto_lock_accumulation_counter) ||
256 (config.escalation_phases == NULL && config.escalation_phases_len != 0) ||
257 (config.escalation_phases != NULL && config.escalation_phases_len == 0) ||
258 !is_valid_escalation_phase(config.crashdump_escalation_phase) ||
259 !dif_is_valid_toggle(enabled) || !dif_is_valid_toggle(locked)) {
260 return kDifBadArg;
261 }
262 for (int i = 0; i < config.escalation_phases_len; ++i) {
263 switch (config.escalation_phases[i].phase) {
268 continue;
269 break;
270 default:
271 return kDifBadArg;
272 }
273 }
274
275#define ALERT_CLASS_CONFIG_REGS_CASE_(class_, value_) \
276 case kDifAlertHandlerClass##class_: \
277 class_regwen_offset = ALERT_HANDLER_CLASS##class_##_REGWEN_REG_OFFSET; \
278 ctrl_reg_offset = ALERT_HANDLER_CLASS##class_##_CTRL_SHADOWED_REG_OFFSET; \
279 accum_thresh_reg_offset = \
280 ALERT_HANDLER_CLASS##class_##_ACCUM_THRESH_SHADOWED_REG_OFFSET; \
281 irq_deadline_reg_offset = \
282 ALERT_HANDLER_CLASS##class_##_TIMEOUT_CYC_SHADOWED_REG_OFFSET; \
283 phase0_cycles_reg_offset = \
284 ALERT_HANDLER_CLASS##class_##_PHASE0_CYC_SHADOWED_REG_OFFSET; \
285 phase1_cycles_reg_offset = \
286 ALERT_HANDLER_CLASS##class_##_PHASE1_CYC_SHADOWED_REG_OFFSET; \
287 phase2_cycles_reg_offset = \
288 ALERT_HANDLER_CLASS##class_##_PHASE2_CYC_SHADOWED_REG_OFFSET; \
289 phase3_cycles_reg_offset = \
290 ALERT_HANDLER_CLASS##class_##_PHASE3_CYC_SHADOWED_REG_OFFSET; \
291 crashdump_phase_reg_offset = \
292 ALERT_HANDLER_CLASS##class_##_CRASHDUMP_TRIGGER_SHADOWED_REG_OFFSET; \
293 break;
294
295 ptrdiff_t class_regwen_offset;
296 ptrdiff_t ctrl_reg_offset;
297 ptrdiff_t accum_thresh_reg_offset;
298 ptrdiff_t irq_deadline_reg_offset;
299 ptrdiff_t phase0_cycles_reg_offset;
300 ptrdiff_t phase1_cycles_reg_offset;
301 ptrdiff_t phase2_cycles_reg_offset;
302 ptrdiff_t phase3_cycles_reg_offset;
303 ptrdiff_t crashdump_phase_reg_offset;
304 switch (alert_class) {
305 LIST_OF_CLASSES(ALERT_CLASS_CONFIG_REGS_CASE_)
306 default:
307 return kDifBadArg;
308 }
309
310#undef ALERT_CLASS_CONFIG_REGS_CASE_
311
312 // Check if class configuration is locked.
313 if (!mmio_region_read32(alert_handler->base_addr, class_regwen_offset)) {
314 return kDifLocked;
315 }
316
317 // NOTE: from this point on, we assume that Class A's constants are
318 // representative of all alert class control register layouts.
319
320 // Configure the class control register and escalation phases / cycle times.
321 // Note, if an escalation phase is configured, it is also enabled.
322 uint32_t ctrl_reg = 0;
323 ctrl_reg =
324 bitfield_bit32_write(ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_BIT,
325 dif_toggle_to_bool(enabled));
326 ctrl_reg = bitfield_bit32_write(
327 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_LOCK_BIT,
328 dif_toggle_to_bool(config.auto_lock_accumulation_counter));
329
330 for (int i = 0; i < config.escalation_phases_len; ++i) {
333 config.escalation_phases[i].signal;
334
335 // Check the escalation phase is valid. The signal is checked in the case
336 // statement below, which will return kDifBadArg if it does not
337 // correspond to any of the specified cases.
338 if (!is_valid_escalation_phase(phase)) {
339 return kDifBadArg;
340 }
341
342 bitfield_bit32_index_t signal_enable_bit;
343 bitfield_field32_t signal_map_field;
344 switch (signal) {
345 // TODO: this should be rewritten to allow combinations of all signals.
346 // The alert handler supports the full phase -> signal mapping matrix.
347 // I.e., it is possible to enable signal 0 and 3 in phase 1 for
348 // instance. For Top Egret it is for instance also recommended to
349 // trigger signals 1 and 2 at the same time since both trigger the same
350 // action in the life cycle controller and serve as redundant
351 // channels.
352 case 0:
353 signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT;
354 signal_map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_FIELD;
355 break;
356 case 1:
357 signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT;
358 signal_map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_FIELD;
359 break;
360 case 2:
361 signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT;
362 signal_map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_FIELD;
363 break;
364 case 3:
365 signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E3_BIT;
366 signal_map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_FIELD;
367 break;
368 // Does not trigger any of the signals. Can be useful in cases where an
369 // escalation phase is just used to wait a specific amount of time.
370 case 0xFFFFFFFF:
371 break;
372 default:
373 return kDifBadArg;
374 }
375
376 // Clear all settings.
377 if (signal == 0xFFFFFFFF) {
378 ctrl_reg = bitfield_bit32_write(
379 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT, false);
380 ctrl_reg = bitfield_field32_write(
381 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_FIELD,
382 (uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
383 ctrl_reg = bitfield_bit32_write(
384 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT, false);
385 ctrl_reg = bitfield_field32_write(
386 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_FIELD,
387 (uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
388 ctrl_reg = bitfield_bit32_write(
389 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT, false);
390 ctrl_reg = bitfield_field32_write(
391 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_FIELD,
392 (uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
393 ctrl_reg = bitfield_bit32_write(
394 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E3_BIT, false);
395 ctrl_reg = bitfield_field32_write(
396 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_FIELD,
397 (uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
398 } else {
399 ctrl_reg = bitfield_bit32_write(ctrl_reg, signal_enable_bit, true);
400 ctrl_reg = bitfield_field32_write(
401 ctrl_reg, signal_map_field,
402 (uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
403 }
404
405 switch (phase) {
407 mmio_region_write32_shadowed(
408 alert_handler->base_addr, phase0_cycles_reg_offset,
410 break;
412 mmio_region_write32_shadowed(
413 alert_handler->base_addr, phase1_cycles_reg_offset,
415 break;
417 mmio_region_write32_shadowed(
418 alert_handler->base_addr, phase2_cycles_reg_offset,
420 break;
422 mmio_region_write32_shadowed(
423 alert_handler->base_addr, phase3_cycles_reg_offset,
425 break;
426 default:
427 return kDifBadArg;
428 }
429 }
430
431 // Configure the class accumulator threshold.
432 mmio_region_write32_shadowed(alert_handler->base_addr,
433 accum_thresh_reg_offset,
434 config.accumulator_threshold);
435
436 // Configure the class IRQ deadline.
437 mmio_region_write32_shadowed(alert_handler->base_addr,
438 irq_deadline_reg_offset,
439 config.irq_deadline_cycles);
440
441 // Configure the crashdump phase.
442 mmio_region_write32_shadowed(alert_handler->base_addr,
443 crashdump_phase_reg_offset,
444 (uint32_t)(config.crashdump_escalation_phase -
446
447 // Configure the class control register last, since this holds the enable bit.
448 mmio_region_write32_shadowed(alert_handler->base_addr, ctrl_reg_offset,
449 ctrl_reg);
450
451 // Lock the configuration.
452 if (locked == kDifToggleEnabled) {
453 mmio_region_write32(alert_handler->base_addr, class_regwen_offset, 0);
454 }
455
456 return kDifOk;
457}
458
459dif_result_t dif_alert_handler_get_class_configuration(
460 const dif_alert_handler_t *alert_handler,
461 dif_alert_handler_class_t alert_class,
463 dif_alert_handler_escalation_phase_t *escalation_phases,
464 dif_toggle_t *enabled, dif_toggle_t *locked) {
465 if (alert_handler == NULL || config == NULL || enabled == NULL ||
466 locked == NULL) {
467 return kDifBadArg;
468 }
469 if (escalation_phases == NULL) {
470 return kDifBadArg;
471 }
472
473#define ALERT_CLASS_CONFIG_REGS_CASE_(class_, value_) \
474 case kDifAlertHandlerClass##class_: \
475 class_regwen_offset = ALERT_HANDLER_CLASS##class_##_REGWEN_REG_OFFSET; \
476 ctrl_reg_offset = ALERT_HANDLER_CLASS##class_##_CTRL_SHADOWED_REG_OFFSET; \
477 accum_thresh_reg_offset = \
478 ALERT_HANDLER_CLASS##class_##_ACCUM_THRESH_SHADOWED_REG_OFFSET; \
479 irq_deadline_reg_offset = \
480 ALERT_HANDLER_CLASS##class_##_TIMEOUT_CYC_SHADOWED_REG_OFFSET; \
481 phase0_cycles_reg_offset = \
482 ALERT_HANDLER_CLASS##class_##_PHASE0_CYC_SHADOWED_REG_OFFSET; \
483 phase1_cycles_reg_offset = \
484 ALERT_HANDLER_CLASS##class_##_PHASE1_CYC_SHADOWED_REG_OFFSET; \
485 phase2_cycles_reg_offset = \
486 ALERT_HANDLER_CLASS##class_##_PHASE2_CYC_SHADOWED_REG_OFFSET; \
487 phase3_cycles_reg_offset = \
488 ALERT_HANDLER_CLASS##class_##_PHASE3_CYC_SHADOWED_REG_OFFSET; \
489 break;
490
491 ptrdiff_t class_regwen_offset;
492 ptrdiff_t ctrl_reg_offset;
493 ptrdiff_t accum_thresh_reg_offset;
494 ptrdiff_t irq_deadline_reg_offset;
495 ptrdiff_t phase0_cycles_reg_offset;
496 ptrdiff_t phase1_cycles_reg_offset;
497 ptrdiff_t phase2_cycles_reg_offset;
498 ptrdiff_t phase3_cycles_reg_offset;
499 switch (alert_class) {
500 LIST_OF_CLASSES(ALERT_CLASS_CONFIG_REGS_CASE_)
501 default:
502 return kDifBadArg;
503 }
504
505#undef ALERT_CLASS_CONFIG_REGS_CASE_
506
507 *locked = dif_bool_to_toggle(
508 mmio_region_read32(alert_handler->base_addr, class_regwen_offset));
509 uint32_t ctrl_reg =
510 mmio_region_read32(alert_handler->base_addr, ctrl_reg_offset);
511 *enabled = dif_bool_to_toggle(
512 bitfield_bit32_read(ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_BIT));
513
514 // NOTE: from this point on, we assume that Class A's constants are
515 // representative of all alert class control register layouts.
516
517 // Configure the class control register and escalation phases / cycle times.
518 // Note, if an escalation phase is configured, it is also enabled.
520 dif_toggle_to_bool(bitfield_bit32_read(
521 ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_LOCK_BIT));
522
523 config->accumulator_threshold = (uint16_t)mmio_region_read32(
524 alert_handler->base_addr, accum_thresh_reg_offset);
525 config->irq_deadline_cycles =
526 mmio_region_read32(alert_handler->base_addr, irq_deadline_reg_offset);
527
528#define LIST_OF_ESCALATION_SEVERITIES(X_) \
529 X_(0) \
530 X_(1) \
531 X_(2) \
532 X_(3)
533
534#define ALERT_CLASS_ESCALATION_CNTRL_REGS_CASE_(value_) \
535 case value_: \
536 signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E##value_##_BIT; \
537 signal_map_field = \
538 ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E##value_##_FIELD; \
539 break;
540
541 bitfield_bit32_index_t signal_enable_bit;
542 bitfield_field32_t signal_map_field;
543
544 for (int i = 0; i < ALERT_HANDLER_PARAM_N_ESC_SEV; ++i) {
545 switch (i) {
546 LIST_OF_ESCALATION_SEVERITIES(ALERT_CLASS_ESCALATION_CNTRL_REGS_CASE_)
547 default:
548 // Not possible
549 break;
550 }
551
552 escalation_phases[i].phase =
553 bitfield_bit32_read(ctrl_reg, signal_enable_bit);
554 escalation_phases[i].signal =
555 bitfield_field32_read(ctrl_reg, signal_map_field);
556 escalation_phases[0].duration_cycles =
557 mmio_region_read32(alert_handler->base_addr, phase0_cycles_reg_offset);
558 escalation_phases[1].duration_cycles =
559 mmio_region_read32(alert_handler->base_addr, phase1_cycles_reg_offset);
560 escalation_phases[2].duration_cycles =
561 mmio_region_read32(alert_handler->base_addr, phase2_cycles_reg_offset);
562 escalation_phases[3].duration_cycles =
563 mmio_region_read32(alert_handler->base_addr, phase3_cycles_reg_offset);
564 }
565 return kDifOk;
566}
567
568dif_result_t dif_alert_handler_crash_dump_trigger_set(
569 const dif_alert_handler_t *alert_handler,
570 dif_alert_handler_class_t alert_class,
571 dif_alert_handler_class_state_t crashdump_phase) {
572 if (alert_handler == NULL ||
573 crashdump_phase < kDifAlertHandlerClassStatePhase0 ||
574 crashdump_phase > kDifAlertHandlerClassStatePhase3) {
575 return kDifBadArg;
576 }
577#define ALERT_CLASS_CONFIG_REGS_CASE_(class_, value_) \
578 case kDifAlertHandlerClass##class_: \
579 class_regwen_offset = ALERT_HANDLER_CLASS##class_##_REGWEN_REG_OFFSET; \
580 crashdump_phase_reg_offset = \
581 ALERT_HANDLER_CLASS##class_##_CRASHDUMP_TRIGGER_SHADOWED_REG_OFFSET; \
582 break;
583
584 ptrdiff_t class_regwen_offset;
585 ptrdiff_t crashdump_phase_reg_offset;
586 switch (alert_class) {
587 LIST_OF_CLASSES(ALERT_CLASS_CONFIG_REGS_CASE_)
588 default:
589 return kDifBadArg;
590 }
591#undef ALERT_CLASS_CONFIG_REGS_CASE_
592
593 // Check if class configuration is locked.
594 if (!mmio_region_read32(alert_handler->base_addr, class_regwen_offset)) {
595 return kDifLocked;
596 }
597
598 // Configure the crashdump phase.
599 mmio_region_write32_shadowed(
600 alert_handler->base_addr, crashdump_phase_reg_offset,
601 (uint32_t)(crashdump_phase - kDifAlertHandlerClassStatePhase0));
602 return kDifOk;
603}
604
605dif_result_t dif_alert_handler_configure_ping_timer(
606 const dif_alert_handler_t *alert_handler, uint32_t ping_timeout,
607 dif_toggle_t enabled, dif_toggle_t locked) {
608 if (alert_handler == NULL ||
609 ping_timeout >
610 ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_MASK ||
611 !dif_is_valid_toggle(enabled) || !dif_is_valid_toggle(locked)) {
612 return kDifBadArg;
613 }
614
615 // Check if the ping timer is locked.
616 if (!mmio_region_read32(alert_handler->base_addr,
617 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET)) {
618 return kDifLocked;
619 }
620
621 // Set the ping timeout.
622 mmio_region_write32_shadowed(
623 alert_handler->base_addr,
624 ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_REG_OFFSET, ping_timeout);
625
626 // Enable the ping timer.
627 // Note, this must be done after the timeout has been configured above, since
628 // pinging will start immediately.
629 if (enabled == kDifToggleEnabled) {
630 mmio_region_write32_shadowed(
631 alert_handler->base_addr,
632 ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET, 1);
633 }
634
635 // Lock the configuration.
636 if (locked == kDifToggleEnabled) {
637 mmio_region_write32(alert_handler->base_addr,
638 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
639 }
640
641 return kDifOk;
642}
643
644dif_result_t dif_alert_handler_ping_timer_set_enabled(
645 const dif_alert_handler_t *alert_handler, dif_toggle_t locked) {
646 if (alert_handler == NULL || !dif_is_valid_toggle(locked)) {
647 return kDifBadArg;
648 }
649
650 // Check if the ping timer is locked.
651 if (!mmio_region_read32(alert_handler->base_addr,
652 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET)) {
653 return kDifLocked;
654 }
655
656 // Enable the ping timer.
657 mmio_region_write32_shadowed(alert_handler->base_addr,
658 ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET,
659 1);
660
661 // Lock the configuration.
662 if (locked == kDifToggleEnabled) {
663 mmio_region_write32(alert_handler->base_addr,
664 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
665 }
666
667 return kDifOk;
668}
669
670dif_result_t dif_alert_handler_lock_alert(
671 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert) {
672 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
673 return kDifBadArg;
674 }
675
676 ptrdiff_t regwen_offset = ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET +
677 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
678 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
679
680 return kDifOk;
681}
682
683dif_result_t dif_alert_handler_is_alert_locked(
684 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
685 bool *is_locked) {
686 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS ||
687 is_locked == NULL) {
688 return kDifBadArg;
689 }
690
691 ptrdiff_t regwen_offset = ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET +
692 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
693 *is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
694
695 return kDifOk;
696}
697
698dif_result_t dif_alert_handler_lock_local_alert(
699 const dif_alert_handler_t *alert_handler,
701 if (alert_handler == NULL) {
702 return kDifBadArg;
703 }
704
705 ptrdiff_t regwen_offset;
706 switch (local_alert) {
707 LIST_OF_LOC_ALERTS(LOC_ALERT_REGWENS_CASE_)
708 default:
709 return kDifBadArg;
710 }
711
712 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
713
714 return kDifOk;
715}
716
717dif_result_t dif_alert_handler_is_local_alert_locked(
718 const dif_alert_handler_t *alert_handler,
719 dif_alert_handler_local_alert_t local_alert, bool *is_locked) {
720 if (alert_handler == NULL || is_locked == NULL) {
721 return kDifBadArg;
722 }
723
724 ptrdiff_t regwen_offset;
725 switch (local_alert) {
726 LIST_OF_LOC_ALERTS(LOC_ALERT_REGWENS_CASE_)
727 default:
728 return kDifBadArg;
729 }
730
731 *is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
732
733 return kDifOk;
734}
735
736dif_result_t dif_alert_handler_lock_class(
737 const dif_alert_handler_t *alert_handler,
738 dif_alert_handler_class_t alert_class) {
739 if (alert_handler == NULL) {
740 return kDifBadArg;
741 }
742
743 ptrdiff_t regwen_offset;
744 switch (alert_class) {
745 LIST_OF_CLASSES(ALERT_CLASS_REGWENS_CASE_)
746 default:
747 return kDifBadArg;
748 }
749
750 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
751
752 return kDifOk;
753}
754
755dif_result_t dif_alert_handler_is_class_locked(
756 const dif_alert_handler_t *alert_handler,
757 dif_alert_handler_class_t alert_class, bool *is_locked) {
758 if (alert_handler == NULL || is_locked == NULL) {
759 return kDifBadArg;
760 }
761
762 ptrdiff_t regwen_offset;
763 switch (alert_class) {
764 LIST_OF_CLASSES(ALERT_CLASS_REGWENS_CASE_)
765 default:
766 return kDifBadArg;
767 }
768
769 *is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
770
771 return kDifOk;
772}
773
774dif_result_t dif_alert_handler_lock_ping_timer(
775 const dif_alert_handler_t *alert_handler) {
776 if (alert_handler == NULL) {
777 return kDifBadArg;
778 }
779
780 mmio_region_write32(alert_handler->base_addr,
781 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
782
783 return kDifOk;
784}
785
786dif_result_t dif_alert_handler_is_ping_timer_locked(
787 const dif_alert_handler_t *alert_handler, bool *is_locked) {
788 if (alert_handler == NULL || is_locked == NULL) {
789 return kDifBadArg;
790 }
791
792 *is_locked = !mmio_region_read32(alert_handler->base_addr,
793 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET);
794
795 return kDifOk;
796}
797
798dif_result_t dif_alert_handler_alert_is_cause(
799 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
800 bool *is_cause) {
801 if (alert_handler == NULL || is_cause == NULL ||
802 alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
803 return kDifBadArg;
804 }
805
806 ptrdiff_t cause_reg_offset = ALERT_HANDLER_ALERT_CAUSE_0_REG_OFFSET +
807 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
808 *is_cause = mmio_region_read32(alert_handler->base_addr, cause_reg_offset);
809
810 return kDifOk;
811}
812
813dif_result_t dif_alert_handler_alert_acknowledge(
814 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert) {
815 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
816 return kDifBadArg;
817 }
818
819 ptrdiff_t cause_reg_offset = ALERT_HANDLER_ALERT_CAUSE_0_REG_OFFSET +
820 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
821 mmio_region_write32(alert_handler->base_addr, cause_reg_offset, 0x1);
822
823 return kDifOk;
824}
825
826dif_result_t dif_alert_handler_local_alert_is_cause(
827 const dif_alert_handler_t *alert_handler,
828 dif_alert_handler_local_alert_t local_alert, bool *is_cause) {
829 if (alert_handler == NULL || is_cause == NULL) {
830 return kDifBadArg;
831 }
832
833 ptrdiff_t cause_reg_offset;
834 switch (local_alert) {
835 LIST_OF_LOC_ALERTS(LOC_ALERT_CAUSE_REGS_CASE_)
836 default:
837 return kDifBadArg;
838 }
839
840 *is_cause = mmio_region_read32(alert_handler->base_addr, cause_reg_offset);
841
842 return kDifOk;
843}
844
845dif_result_t dif_alert_handler_local_alert_acknowledge(
846 const dif_alert_handler_t *alert_handler,
848 if (alert_handler == NULL) {
849 return kDifBadArg;
850 }
851
852 ptrdiff_t cause_reg_offset;
853 switch (local_alert) {
854 LIST_OF_LOC_ALERTS(LOC_ALERT_CAUSE_REGS_CASE_)
855 default:
856 return kDifBadArg;
857 }
858
859 mmio_region_write32(alert_handler->base_addr, cause_reg_offset, 0x1);
860
861 return kDifOk;
862}
863
864dif_result_t dif_alert_handler_escalation_can_clear(
865 const dif_alert_handler_t *alert_handler,
866 dif_alert_handler_class_t alert_class, bool *can_clear) {
867 if (alert_handler == NULL || can_clear == NULL) {
868 return kDifBadArg;
869 }
870
871 ptrdiff_t regwen_offset;
872 switch (alert_class) {
873 LIST_OF_CLASSES(ALERT_CLASS_CLEAR_REGWENS_CASE_)
874 default:
875 return kDifBadArg;
876 }
877
878 *can_clear = mmio_region_read32(alert_handler->base_addr, regwen_offset);
879
880 return kDifOk;
881}
882
883dif_result_t dif_alert_handler_escalation_disable_clearing(
884 const dif_alert_handler_t *alert_handler,
885 dif_alert_handler_class_t alert_class) {
886 if (alert_handler == NULL) {
887 return kDifBadArg;
888 }
889
890 ptrdiff_t regwen_offset;
891 switch (alert_class) {
892 LIST_OF_CLASSES(ALERT_CLASS_CLEAR_REGWENS_CASE_)
893 default:
894 return kDifBadArg;
895 }
896
897 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
898
899 return kDifOk;
900}
901
902dif_result_t dif_alert_handler_escalation_clear(
903 const dif_alert_handler_t *alert_handler,
904 dif_alert_handler_class_t alert_class) {
905 if (alert_handler == NULL) {
906 return kDifBadArg;
907 }
908
909#define ALERT_CLASS_CLEAR_CASE_(class_, value_) \
910 case kDifAlertHandlerClass##class_: \
911 reg_offset = ALERT_HANDLER_CLASS##class_##_CLR_SHADOWED_REG_OFFSET; \
912 break;
913
914 ptrdiff_t reg_offset;
915 switch (alert_class) {
916 LIST_OF_CLASSES(ALERT_CLASS_CLEAR_CASE_)
917 default:
918 return kDifBadArg;
919 }
920
921#undef ALERT_CLASS_CLEAR_CASE_
922
923 mmio_region_write32_shadowed(alert_handler->base_addr, reg_offset, 0x1);
924
925 return kDifOk;
926}
927
928dif_result_t dif_alert_handler_get_accumulator(
929 const dif_alert_handler_t *alert_handler,
930 dif_alert_handler_class_t alert_class, uint16_t *num_alerts) {
931 if (alert_handler == NULL || num_alerts == NULL) {
932 return kDifBadArg;
933 }
934
935#define ALERT_CLASS_ACCUM_CASE_(class_, value_) \
936 case kDifAlertHandlerClass##class_: \
937 reg_offset = ALERT_HANDLER_CLASS##class_##_ACCUM_CNT_REG_OFFSET; \
938 field = \
939 ALERT_HANDLER_CLASS##class_##_ACCUM_CNT_CLASS##class_##_ACCUM_CNT_FIELD; \
940 break;
941
942 ptrdiff_t reg_offset;
943 bitfield_field32_t field;
944 switch (alert_class) {
945 LIST_OF_CLASSES(ALERT_CLASS_ACCUM_CASE_)
946 default:
947 return kDifBadArg;
948 }
949
950#undef ALERT_CLASS_ACCUM_CASE_
951
952 uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset);
953 *num_alerts = (uint16_t)bitfield_field32_read(reg, field);
954
955 return kDifOk;
956}
957
958dif_result_t dif_alert_handler_get_escalation_counter(
959 const dif_alert_handler_t *alert_handler,
960 dif_alert_handler_class_t alert_class, uint32_t *cycles) {
961 if (alert_handler == NULL || cycles == NULL) {
962 return kDifBadArg;
963 }
964
965#define ALERT_CLASS_ESC_CNT_CASE_(class_, value_) \
966 case kDifAlertHandlerClass##class_: \
967 reg_offset = ALERT_HANDLER_CLASS##class_##_ESC_CNT_REG_OFFSET; \
968 break;
969
970 ptrdiff_t reg_offset;
971 switch (alert_class) {
972 LIST_OF_CLASSES(ALERT_CLASS_ESC_CNT_CASE_)
973 default:
974 return kDifBadArg;
975 }
976
977#undef ALERT_CLASS_ESC_CNT_CASE_
978
979 *cycles = mmio_region_read32(alert_handler->base_addr, reg_offset);
980
981 return kDifOk;
982}
983
984dif_result_t dif_alert_handler_is_class_enabled(
985 const dif_alert_handler_t *alert_handler,
986 dif_alert_handler_class_t alert_class, bool *is_enabled) {
987 if (alert_handler == NULL || is_enabled == NULL) {
988 return kDifBadArg;
989 }
990#define ALERT_CLASS_CTRL_CASE_(class_, value_) \
991 case kDifAlertHandlerClass##class_: \
992 reg_offset = ALERT_HANDLER_CLASS##class_##_CTRL_SHADOWED_REG_OFFSET; \
993 break;
994
995 ptrdiff_t reg_offset;
996 switch (alert_class) {
997 LIST_OF_CLASSES(ALERT_CLASS_CTRL_CASE_)
998 default:
999 return kDifBadArg;
1000 }
1001#undef ALERT_CLASS_CTRL_CASE_
1002
1003 uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset);
1004 *is_enabled =
1005 bitfield_bit32_read(reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_BIT);
1006
1007 return kDifOk;
1008}
1009
1010dif_result_t dif_alert_handler_get_class_state(
1011 const dif_alert_handler_t *alert_handler,
1012 dif_alert_handler_class_t alert_class,
1014 if (alert_handler == NULL || state == NULL) {
1015 return kDifBadArg;
1016 }
1017
1018#define ALERT_CLASS_STATE_CASE_(class_, value_) \
1019 case kDifAlertHandlerClass##class_: \
1020 reg_offset = ALERT_HANDLER_CLASS##class_##_STATE_REG_OFFSET; \
1021 field = ALERT_HANDLER_CLASS##class_##_STATE_CLASS##class_##_STATE_FIELD; \
1022 break;
1023
1024 ptrdiff_t reg_offset;
1025 bitfield_field32_t field;
1026 switch (alert_class) {
1027 LIST_OF_CLASSES(ALERT_CLASS_STATE_CASE_)
1028 default:
1029 return kDifBadArg;
1030 }
1031
1032#undef ALERT_CLASS_STATE_CASE_
1033
1034 uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset);
1035 switch (bitfield_field32_read(reg, field)) {
1036 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_IDLE:
1038 break;
1039 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_TIMEOUT:
1041 break;
1042 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_FSMERROR:
1044 break;
1045 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_TERMINAL:
1047 break;
1048 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE0:
1050 break;
1051 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE1:
1053 break;
1054 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE2:
1056 break;
1057 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE3:
1059 break;
1060 default:
1061 return kDifError;
1062 }
1063
1064 return kDifOk;
1065}
1066
1067dif_result_t dif_alert_handler_alert_is_enabled(
1068 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
1069 dif_toggle_t *is_enabled) {
1070 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS ||
1071 is_enabled == NULL) {
1072 return kDifBadArg;
1073 }
1074
1075 // Enable the alert.
1076 ptrdiff_t enable_reg_offset = ALERT_HANDLER_ALERT_EN_SHADOWED_0_REG_OFFSET +
1077 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
1078 bool enabled =
1079 mmio_region_read32(alert_handler->base_addr, enable_reg_offset);
1080 *is_enabled = dif_bool_to_toggle(enabled);
1081
1082 return kDifOk;
1083}
1084
1085dif_result_t dif_alert_handler_alert_set_enabled(
1086 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
1087 dif_toggle_t enabled) {
1088 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS ||
1089 !dif_is_valid_toggle(enabled)) {
1090 return kDifBadArg;
1091 }
1092
1093 // Check if configuration is locked.
1094 ptrdiff_t regwen_offset = ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET +
1095 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
1096 if (!mmio_region_read32(alert_handler->base_addr, regwen_offset)) {
1097 return kDifLocked;
1098 }
1099
1100 // Enable the alert.
1101 ptrdiff_t enable_reg_offset = ALERT_HANDLER_ALERT_EN_SHADOWED_0_REG_OFFSET +
1102 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
1103 mmio_region_write32_shadowed(alert_handler->base_addr, enable_reg_offset,
1104 dif_toggle_to_bool(enabled));
1105
1106 return kDifOk;
1107}