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