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 "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 0x1);
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 Earl Grey 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_configure_ping_timer(
460 const dif_alert_handler_t *alert_handler, uint32_t ping_timeout,
461 dif_toggle_t enabled, dif_toggle_t locked) {
462 if (alert_handler == NULL ||
463 ping_timeout >
464 ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_MASK ||
465 !dif_is_valid_toggle(enabled) || !dif_is_valid_toggle(locked)) {
466 return kDifBadArg;
467 }
468
469 // Check if the ping timer is locked.
470 if (!mmio_region_read32(alert_handler->base_addr,
471 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET)) {
472 return kDifLocked;
473 }
474
475 // Set the ping timeout.
476 mmio_region_write32_shadowed(
477 alert_handler->base_addr,
478 ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_REG_OFFSET, ping_timeout);
479
480 // Enable the ping timer.
481 // Note, this must be done after the timeout has been configured above, since
482 // pinging will start immediately.
483 if (enabled == kDifToggleEnabled) {
484 mmio_region_write32_shadowed(
485 alert_handler->base_addr,
486 ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET, 1);
487 }
488
489 // Lock the configuration.
490 if (locked == kDifToggleEnabled) {
491 mmio_region_write32(alert_handler->base_addr,
492 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
493 }
494
495 return kDifOk;
496}
497
498dif_result_t dif_alert_handler_ping_timer_set_enabled(
499 const dif_alert_handler_t *alert_handler, dif_toggle_t locked) {
500 if (alert_handler == NULL || !dif_is_valid_toggle(locked)) {
501 return kDifBadArg;
502 }
503
504 // Check if the ping timer is locked.
505 if (!mmio_region_read32(alert_handler->base_addr,
506 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET)) {
507 return kDifLocked;
508 }
509
510 // Enable the ping timer.
511 mmio_region_write32_shadowed(alert_handler->base_addr,
512 ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET,
513 1);
514
515 // Lock the configuration.
516 if (locked == kDifToggleEnabled) {
517 mmio_region_write32(alert_handler->base_addr,
518 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
519 }
520
521 return kDifOk;
522}
523
524dif_result_t dif_alert_handler_lock_alert(
525 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert) {
526 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
527 return kDifBadArg;
528 }
529
530 ptrdiff_t regwen_offset = ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET +
531 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
532 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
533
534 return kDifOk;
535}
536
537dif_result_t dif_alert_handler_is_alert_locked(
538 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
539 bool *is_locked) {
540 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS ||
541 is_locked == NULL) {
542 return kDifBadArg;
543 }
544
545 ptrdiff_t regwen_offset = ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET +
546 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
547 *is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
548
549 return kDifOk;
550}
551
552dif_result_t dif_alert_handler_lock_local_alert(
553 const dif_alert_handler_t *alert_handler,
555 if (alert_handler == NULL) {
556 return kDifBadArg;
557 }
558
559 ptrdiff_t regwen_offset;
560 switch (local_alert) {
561 LIST_OF_LOC_ALERTS(LOC_ALERT_REGWENS_CASE_)
562 default:
563 return kDifBadArg;
564 }
565
566 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
567
568 return kDifOk;
569}
570
571dif_result_t dif_alert_handler_is_local_alert_locked(
572 const dif_alert_handler_t *alert_handler,
573 dif_alert_handler_local_alert_t local_alert, bool *is_locked) {
574 if (alert_handler == NULL || is_locked == NULL) {
575 return kDifBadArg;
576 }
577
578 ptrdiff_t regwen_offset;
579 switch (local_alert) {
580 LIST_OF_LOC_ALERTS(LOC_ALERT_REGWENS_CASE_)
581 default:
582 return kDifBadArg;
583 }
584
585 *is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
586
587 return kDifOk;
588}
589
590dif_result_t dif_alert_handler_lock_class(
591 const dif_alert_handler_t *alert_handler,
592 dif_alert_handler_class_t alert_class) {
593 if (alert_handler == NULL) {
594 return kDifBadArg;
595 }
596
597 ptrdiff_t regwen_offset;
598 switch (alert_class) {
599 LIST_OF_CLASSES(ALERT_CLASS_REGWENS_CASE_)
600 default:
601 return kDifBadArg;
602 }
603
604 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
605
606 return kDifOk;
607}
608
609dif_result_t dif_alert_handler_is_class_locked(
610 const dif_alert_handler_t *alert_handler,
611 dif_alert_handler_class_t alert_class, bool *is_locked) {
612 if (alert_handler == NULL || is_locked == NULL) {
613 return kDifBadArg;
614 }
615
616 ptrdiff_t regwen_offset;
617 switch (alert_class) {
618 LIST_OF_CLASSES(ALERT_CLASS_REGWENS_CASE_)
619 default:
620 return kDifBadArg;
621 }
622
623 *is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
624
625 return kDifOk;
626}
627
628dif_result_t dif_alert_handler_lock_ping_timer(
629 const dif_alert_handler_t *alert_handler) {
630 if (alert_handler == NULL) {
631 return kDifBadArg;
632 }
633
634 mmio_region_write32(alert_handler->base_addr,
635 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
636
637 return kDifOk;
638}
639
640dif_result_t dif_alert_handler_is_ping_timer_locked(
641 const dif_alert_handler_t *alert_handler, bool *is_locked) {
642 if (alert_handler == NULL || is_locked == NULL) {
643 return kDifBadArg;
644 }
645
646 *is_locked = !mmio_region_read32(alert_handler->base_addr,
647 ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET);
648
649 return kDifOk;
650}
651
652dif_result_t dif_alert_handler_alert_is_cause(
653 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
654 bool *is_cause) {
655 if (alert_handler == NULL || is_cause == NULL ||
656 alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
657 return kDifBadArg;
658 }
659
660 ptrdiff_t cause_reg_offset = ALERT_HANDLER_ALERT_CAUSE_0_REG_OFFSET +
661 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
662 *is_cause = mmio_region_read32(alert_handler->base_addr, cause_reg_offset);
663
664 return kDifOk;
665}
666
667dif_result_t dif_alert_handler_alert_acknowledge(
668 const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert) {
669 if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
670 return kDifBadArg;
671 }
672
673 ptrdiff_t cause_reg_offset = ALERT_HANDLER_ALERT_CAUSE_0_REG_OFFSET +
674 (ptrdiff_t)alert * (ptrdiff_t)sizeof(uint32_t);
675 mmio_region_write32(alert_handler->base_addr, cause_reg_offset, 0x1);
676
677 return kDifOk;
678}
679
680dif_result_t dif_alert_handler_local_alert_is_cause(
681 const dif_alert_handler_t *alert_handler,
682 dif_alert_handler_local_alert_t local_alert, bool *is_cause) {
683 if (alert_handler == NULL || is_cause == NULL) {
684 return kDifBadArg;
685 }
686
687 ptrdiff_t cause_reg_offset;
688 switch (local_alert) {
689 LIST_OF_LOC_ALERTS(LOC_ALERT_CAUSE_REGS_CASE_)
690 default:
691 return kDifBadArg;
692 }
693
694 *is_cause = mmio_region_read32(alert_handler->base_addr, cause_reg_offset);
695
696 return kDifOk;
697}
698
699dif_result_t dif_alert_handler_local_alert_acknowledge(
700 const dif_alert_handler_t *alert_handler,
702 if (alert_handler == NULL) {
703 return kDifBadArg;
704 }
705
706 ptrdiff_t cause_reg_offset;
707 switch (local_alert) {
708 LIST_OF_LOC_ALERTS(LOC_ALERT_CAUSE_REGS_CASE_)
709 default:
710 return kDifBadArg;
711 }
712
713 mmio_region_write32(alert_handler->base_addr, cause_reg_offset, 0x1);
714
715 return kDifOk;
716}
717
718dif_result_t dif_alert_handler_escalation_can_clear(
719 const dif_alert_handler_t *alert_handler,
720 dif_alert_handler_class_t alert_class, bool *can_clear) {
721 if (alert_handler == NULL || can_clear == NULL) {
722 return kDifBadArg;
723 }
724
725 ptrdiff_t regwen_offset;
726 switch (alert_class) {
727 LIST_OF_CLASSES(ALERT_CLASS_CLEAR_REGWENS_CASE_)
728 default:
729 return kDifBadArg;
730 }
731
732 *can_clear = mmio_region_read32(alert_handler->base_addr, regwen_offset);
733
734 return kDifOk;
735}
736
737dif_result_t dif_alert_handler_escalation_disable_clearing(
738 const dif_alert_handler_t *alert_handler,
739 dif_alert_handler_class_t alert_class) {
740 if (alert_handler == NULL) {
741 return kDifBadArg;
742 }
743
744 ptrdiff_t regwen_offset;
745 switch (alert_class) {
746 LIST_OF_CLASSES(ALERT_CLASS_CLEAR_REGWENS_CASE_)
747 default:
748 return kDifBadArg;
749 }
750
751 mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
752
753 return kDifOk;
754}
755
756dif_result_t dif_alert_handler_escalation_clear(
757 const dif_alert_handler_t *alert_handler,
758 dif_alert_handler_class_t alert_class) {
759 if (alert_handler == NULL) {
760 return kDifBadArg;
761 }
762
763#define ALERT_CLASS_CLEAR_CASE_(class_, value_) \
764 case kDifAlertHandlerClass##class_: \
765 reg_offset = ALERT_HANDLER_CLASS##class_##_CLR_SHADOWED_REG_OFFSET; \
766 break;
767
768 ptrdiff_t reg_offset;
769 switch (alert_class) {
770 LIST_OF_CLASSES(ALERT_CLASS_CLEAR_CASE_)
771 default:
772 return kDifBadArg;
773 }
774
775#undef ALERT_CLASS_CLEAR_CASE_
776
777 mmio_region_write32_shadowed(alert_handler->base_addr, reg_offset, 0x1);
778
779 return kDifOk;
780}
781
782dif_result_t dif_alert_handler_get_accumulator(
783 const dif_alert_handler_t *alert_handler,
784 dif_alert_handler_class_t alert_class, uint16_t *num_alerts) {
785 if (alert_handler == NULL || num_alerts == NULL) {
786 return kDifBadArg;
787 }
788
789#define ALERT_CLASS_ACCUM_CASE_(class_, value_) \
790 case kDifAlertHandlerClass##class_: \
791 reg_offset = ALERT_HANDLER_CLASS##class_##_ACCUM_CNT_REG_OFFSET; \
792 field = \
793 ALERT_HANDLER_CLASS##class_##_ACCUM_CNT_CLASS##class_##_ACCUM_CNT_FIELD; \
794 break;
795
796 ptrdiff_t reg_offset;
797 bitfield_field32_t field;
798 switch (alert_class) {
799 LIST_OF_CLASSES(ALERT_CLASS_ACCUM_CASE_)
800 default:
801 return kDifBadArg;
802 }
803
804#undef ALERT_CLASS_ACCUM_CASE_
805
806 uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset);
807 *num_alerts = (uint16_t)bitfield_field32_read(reg, field);
808
809 return kDifOk;
810}
811
812dif_result_t dif_alert_handler_get_escalation_counter(
813 const dif_alert_handler_t *alert_handler,
814 dif_alert_handler_class_t alert_class, uint32_t *cycles) {
815 if (alert_handler == NULL || cycles == NULL) {
816 return kDifBadArg;
817 }
818
819#define ALERT_CLASS_ESC_CNT_CASE_(class_, value_) \
820 case kDifAlertHandlerClass##class_: \
821 reg_offset = ALERT_HANDLER_CLASS##class_##_ESC_CNT_REG_OFFSET; \
822 break;
823
824 ptrdiff_t reg_offset;
825 switch (alert_class) {
826 LIST_OF_CLASSES(ALERT_CLASS_ESC_CNT_CASE_)
827 default:
828 return kDifBadArg;
829 }
830
831#undef ALERT_CLASS_ESC_CNT_CASE_
832
833 *cycles = mmio_region_read32(alert_handler->base_addr, reg_offset);
834
835 return kDifOk;
836}
837
838dif_result_t dif_alert_handler_get_class_state(
839 const dif_alert_handler_t *alert_handler,
840 dif_alert_handler_class_t alert_class,
842 if (alert_handler == NULL || state == NULL) {
843 return kDifBadArg;
844 }
845
846#define ALERT_CLASS_STATE_CASE_(class_, value_) \
847 case kDifAlertHandlerClass##class_: \
848 reg_offset = ALERT_HANDLER_CLASS##class_##_STATE_REG_OFFSET; \
849 field = ALERT_HANDLER_CLASS##class_##_STATE_CLASS##class_##_STATE_FIELD; \
850 break;
851
852 ptrdiff_t reg_offset;
853 bitfield_field32_t field;
854 switch (alert_class) {
855 LIST_OF_CLASSES(ALERT_CLASS_STATE_CASE_)
856 default:
857 return kDifBadArg;
858 }
859
860#undef ALERT_CLASS_STATE_CASE_
861
862 uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset);
863 switch (bitfield_field32_read(reg, field)) {
864 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_IDLE:
866 break;
867 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_TIMEOUT:
869 break;
870 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_FSMERROR:
872 break;
873 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_TERMINAL:
875 break;
876 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE0:
878 break;
879 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE1:
881 break;
882 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE2:
884 break;
885 case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE3:
887 break;
888 default:
889 return kDifError;
890 }
891
892 return kDifOk;
893}