blob: 53b26988509df96ad8542d75911331d8abc69743 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/dif/dif_alert_handler.h"
#include <assert.h>
#include <limits.h>
#include "sw/device/lib/base/bitfield.h"
#include "alert_handler_regs.h" // Generated.
static_assert(ALERT_HANDLER_PARAM_N_CLASSES == 4,
"Expected four alert classes!");
static_assert(ALERT_HANDLER_PARAM_N_ESC_SEV == 4,
"Expected four escalation signals!");
static_assert(ALERT_HANDLER_PARAM_N_PHASES == 4,
"Expected four escalation phases!");
static_assert(ALERT_HANDLER_PARAM_N_LOC_ALERT == 7,
"Expected seven local alerts!");
// Enable, class, lock, and cause multiregs for alerts.
static_assert(ALERT_HANDLER_ALERT_EN_SHADOWED_MULTIREG_COUNT &&
ALERT_HANDLER_ALERT_EN_SHADOWED_EN_A_FIELD_WIDTH == 1 &&
ALERT_HANDLER_ALERT_EN_SHADOWED_0_EN_A_0_BIT == 0,
"Expected alert enables to be multiregs with LSB at index 0!");
static_assert(ALERT_HANDLER_ALERT_CLASS_SHADOWED_MULTIREG_COUNT &&
ALERT_HANDLER_ALERT_CLASS_SHADOWED_CLASS_A_FIELD_WIDTH == 2 &&
ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_OFFSET == 0,
"Expected alert class CSRs to be multiregs with LSB at index 0!");
static_assert(ALERT_HANDLER_ALERT_REGWEN_MULTIREG_COUNT &&
ALERT_HANDLER_ALERT_REGWEN_EN_FIELD_WIDTH == 1 &&
ALERT_HANDLER_ALERT_REGWEN_0_EN_0_BIT == 0,
"Expected alert locks to be multiregs with LSB at index 0!");
static_assert(ALERT_HANDLER_ALERT_CAUSE_MULTIREG_COUNT &&
ALERT_HANDLER_ALERT_CAUSE_A_FIELD_WIDTH == 1 &&
ALERT_HANDLER_ALERT_CAUSE_0_A_0_BIT == 0,
"Expected alert causes to be multiregs with LSB at index 0!");
// Enable, class, lock, and cause multiregs for local alerts.
static_assert(
ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_MULTIREG_COUNT &&
ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_EN_LA_FIELD_WIDTH == 1 &&
ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_0_EN_LA_0_BIT == 0,
"Expected local alert enables to be multiregs with LSB at index 0!");
static_assert(
ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_MULTIREG_COUNT &&
ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_CLASS_LA_FIELD_WIDTH == 2 &&
ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_OFFSET == 0,
"Expected local alert class CSRs to be multiregs with LSB at index 0!");
static_assert(
ALERT_HANDLER_LOC_ALERT_REGWEN_MULTIREG_COUNT &&
ALERT_HANDLER_LOC_ALERT_REGWEN_EN_FIELD_WIDTH == 1 &&
ALERT_HANDLER_LOC_ALERT_REGWEN_0_EN_0_BIT == 0,
"Expected local alert locks to be multiregs with LSB at index 0!");
static_assert(
ALERT_HANDLER_LOC_ALERT_CAUSE_MULTIREG_COUNT &&
ALERT_HANDLER_LOC_ALERT_CAUSE_LA_FIELD_WIDTH == 1 &&
ALERT_HANDLER_LOC_ALERT_CAUSE_0_LA_0_BIT == 0,
"Expected local alert causes to be multiregs with LSB at index 0!");
// Accumulator threshold field sizes.
static_assert(
ALERT_HANDLER_CLASSA_ACCUM_THRESH_SHADOWED_CLASSA_ACCUM_THRESH_SHADOWED_MASK <=
USHRT_MAX,
"Expected class A accumulator threshold field to be 16 bits.");
static_assert(
ALERT_HANDLER_CLASSB_ACCUM_THRESH_SHADOWED_CLASSB_ACCUM_THRESH_SHADOWED_MASK <=
USHRT_MAX,
"Expected class B accumulator threshold field to be 16 bits.");
static_assert(
ALERT_HANDLER_CLASSC_ACCUM_THRESH_SHADOWED_CLASSC_ACCUM_THRESH_SHADOWED_MASK <=
USHRT_MAX,
"Expected class C accumulator threshold field to be 16 bits.");
static_assert(
ALERT_HANDLER_CLASSD_ACCUM_THRESH_SHADOWED_CLASSD_ACCUM_THRESH_SHADOWED_MASK <=
USHRT_MAX,
"Expected class D accumulator threshold field to be 16 bits.");
/**
* Macro for generating the case statements for local alert cause CSRs.
*/
#define LOC_ALERT_CAUSE_REGS_CASE_(loc_alert_, value_) \
case loc_alert_: \
cause_reg_offset = ALERT_HANDLER_LOC_ALERT_CAUSE_##value_##_REG_OFFSET; \
break;
/**
* Macro for generating the case statements for local alert lock CSRs.
*/
#define LOC_ALERT_REGWENS_CASE_(loc_alert_, value_) \
case loc_alert_: \
regwen_offset = ALERT_HANDLER_LOC_ALERT_REGWEN_##value_##_REG_OFFSET; \
break;
/**
* Macro for generating the case statements for class lock CSRs.
*/
#define ALERT_CLASS_REGWENS_CASE_(class_, value_) \
case kDifAlertHandlerClass##class_: \
regwen_offset = ALERT_HANDLER_CLASS##class_##_REGWEN_REG_OFFSET; \
break;
/**
* Macro for generating the case statements for class clear lock CSRs.
*/
#define ALERT_CLASS_CLEAR_REGWENS_CASE_(class_, value_) \
case kDifAlertHandlerClass##class_: \
regwen_offset = ALERT_HANDLER_CLASS##class_##_CLR_REGWEN_REG_OFFSET; \
break;
/**
* We use this to convert the class enum to the integer value that is
* assigned to each class in auto-generated register header file. We do this
* to make this code robust against changes to the class values in the
* auto-generated register header file.
*/
OT_WARN_UNUSED_RESULT
static bool class_to_uint32(dif_alert_handler_class_t alert_class,
uint32_t *classification) {
#define ALERT_CLASS_REGS_CASE_(class_, value_) \
case kDifAlertHandlerClass##class_: \
*classification = \
ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASS##class_; \
break;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_REGS_CASE_)
default:
return false;
}
#undef ALERT_CLASS_REGS_CASE_
return true;
}
OT_WARN_UNUSED_RESULT
static bool is_valid_escalation_phase(dif_alert_handler_class_state_t phase) {
if (phase < kDifAlertHandlerClassStatePhase0 ||
phase > kDifAlertHandlerClassStatePhase3) {
return false;
}
return true;
}
/**
* NOTE: the alert ID corresponds directly to the multireg index for each CSR.
* (I.e., alert N has enable multireg N).
*/
dif_result_t dif_alert_handler_configure_alert(
const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
dif_alert_handler_class_t alert_class, dif_toggle_t enabled,
dif_toggle_t locked) {
if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS ||
!dif_is_valid_toggle(enabled) || !dif_is_valid_toggle(locked)) {
return kDifBadArg;
}
uint32_t classification;
if (!class_to_uint32(alert_class, &classification)) {
return kDifBadArg;
}
// Check if configuration is locked.
ptrdiff_t regwen_offset =
ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET + alert * sizeof(uint32_t);
if (!mmio_region_read32(alert_handler->base_addr, regwen_offset)) {
return kDifLocked;
}
// Classify the alert.
ptrdiff_t class_reg_offset = ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_REG_OFFSET +
alert * sizeof(uint32_t);
mmio_region_write32_shadowed(alert_handler->base_addr, class_reg_offset,
classification);
// Enable the alert.
ptrdiff_t enable_reg_offset =
ALERT_HANDLER_ALERT_EN_SHADOWED_0_REG_OFFSET + alert * sizeof(uint32_t);
mmio_region_write32_shadowed(alert_handler->base_addr, enable_reg_offset,
0x1);
// Lock the configuration.
if (locked == kDifToggleEnabled) {
mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
}
return kDifOk;
}
dif_result_t dif_alert_handler_configure_local_alert(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_local_alert_t local_alert,
dif_alert_handler_class_t alert_class, dif_toggle_t enabled,
dif_toggle_t locked) {
if (alert_handler == NULL || !dif_is_valid_toggle(enabled) ||
!dif_is_valid_toggle(locked)) {
return kDifBadArg;
}
uint32_t classification;
if (!class_to_uint32(alert_class, &classification)) {
return kDifBadArg;
}
#define LOC_ALERT_REGS_CASE_(loc_alert_, value_) \
case loc_alert_: \
enable_reg_offset = \
ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_##value_##_REG_OFFSET; \
class_reg_offset = \
ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_##value_##_REG_OFFSET; \
regwen_offset = ALERT_HANDLER_LOC_ALERT_REGWEN_##value_##_REG_OFFSET; \
break;
// Get register/field offsets for local alert type.
ptrdiff_t enable_reg_offset;
ptrdiff_t class_reg_offset;
ptrdiff_t regwen_offset;
switch (local_alert) {
LIST_OF_LOC_ALERTS(LOC_ALERT_REGS_CASE_)
default:
return kDifBadArg;
}
#undef LOC_ALERT_REGS_CASE_
// Check if configuration is locked.
if (!mmio_region_read32(alert_handler->base_addr, regwen_offset)) {
return kDifLocked;
}
// Classify the alert.
mmio_region_write32_shadowed(alert_handler->base_addr, class_reg_offset,
classification);
// Enable the alert.
// NOTE: the alert ID corresponds directly to the multireg index.
// (I.e., alert N has enable multireg N).
mmio_region_write32_shadowed(alert_handler->base_addr, enable_reg_offset,
0x1);
// Lock the configuration.
if (locked == kDifToggleEnabled) {
mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
}
return kDifOk;
}
dif_result_t dif_alert_handler_configure_class(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class,
dif_alert_handler_class_config_t config, dif_toggle_t enabled,
dif_toggle_t locked) {
if (alert_handler == NULL ||
!dif_is_valid_toggle(config.auto_lock_accumulation_counter) ||
(config.escalation_phases == NULL && config.escalation_phases_len != 0) ||
(config.escalation_phases != NULL && config.escalation_phases_len == 0) ||
!is_valid_escalation_phase(config.crashdump_escalation_phase) ||
!dif_is_valid_toggle(enabled) || !dif_is_valid_toggle(locked)) {
return kDifBadArg;
}
for (int i = 0; i < config.escalation_phases_len; ++i) {
switch (config.escalation_phases[i].phase) {
case kDifAlertHandlerClassStatePhase0:
case kDifAlertHandlerClassStatePhase1:
case kDifAlertHandlerClassStatePhase2:
case kDifAlertHandlerClassStatePhase3:
continue;
break;
default:
return kDifBadArg;
}
}
#define ALERT_CLASS_CONFIG_REGS_CASE_(class_, value_) \
case kDifAlertHandlerClass##class_: \
class_regwen_offset = ALERT_HANDLER_CLASS##class_##_REGWEN_REG_OFFSET; \
ctrl_reg_offset = ALERT_HANDLER_CLASS##class_##_CTRL_SHADOWED_REG_OFFSET; \
accum_thresh_reg_offset = \
ALERT_HANDLER_CLASS##class_##_ACCUM_THRESH_SHADOWED_REG_OFFSET; \
irq_deadline_reg_offset = \
ALERT_HANDLER_CLASS##class_##_TIMEOUT_CYC_SHADOWED_REG_OFFSET; \
phase0_cycles_reg_offset = \
ALERT_HANDLER_CLASS##class_##_PHASE0_CYC_SHADOWED_REG_OFFSET; \
phase1_cycles_reg_offset = \
ALERT_HANDLER_CLASS##class_##_PHASE1_CYC_SHADOWED_REG_OFFSET; \
phase2_cycles_reg_offset = \
ALERT_HANDLER_CLASS##class_##_PHASE2_CYC_SHADOWED_REG_OFFSET; \
phase3_cycles_reg_offset = \
ALERT_HANDLER_CLASS##class_##_PHASE3_CYC_SHADOWED_REG_OFFSET; \
crashdump_phase_reg_offset = \
ALERT_HANDLER_CLASS##class_##_CRASHDUMP_TRIGGER_SHADOWED_REG_OFFSET; \
break;
ptrdiff_t class_regwen_offset;
ptrdiff_t ctrl_reg_offset;
ptrdiff_t accum_thresh_reg_offset;
ptrdiff_t irq_deadline_reg_offset;
ptrdiff_t phase0_cycles_reg_offset;
ptrdiff_t phase1_cycles_reg_offset;
ptrdiff_t phase2_cycles_reg_offset;
ptrdiff_t phase3_cycles_reg_offset;
ptrdiff_t crashdump_phase_reg_offset;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_CONFIG_REGS_CASE_)
default:
return kDifBadArg;
}
#undef ALERT_CLASS_CONFIG_REGS_CASE_
// Check if class configuration is locked.
if (!mmio_region_read32(alert_handler->base_addr, class_regwen_offset)) {
return kDifLocked;
}
// NOTE: from this point on, we assume that Class A's constants are
// representative of all alert class control register layouts.
// Configure the class control register and escalation phases / cycle times.
// Note, if an escalation phase is configured, it is also enabled.
uint32_t ctrl_reg = 0;
ctrl_reg =
bitfield_bit32_write(ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_BIT,
dif_toggle_to_bool(enabled));
ctrl_reg = bitfield_bit32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_LOCK_BIT,
dif_toggle_to_bool(config.auto_lock_accumulation_counter));
for (int i = 0; i < config.escalation_phases_len; ++i) {
dif_alert_handler_class_state_t phase = config.escalation_phases[i].phase;
dif_alert_handler_escalation_signal_t signal =
config.escalation_phases[i].signal;
// Check the escalation phase is valid. The signal is checked in the case
// statement below, which will return kDifBadArg if it does not
// correspond to any of the specified cases.
if (!is_valid_escalation_phase(phase)) {
return kDifBadArg;
}
bitfield_bit32_index_t signal_enable_bit;
bitfield_field32_t signal_map_field;
switch (signal) {
// TODO: this should be rewritten to allow combinations of all signals.
// The alert handler supports the full phase -> signal mapping matrix.
// I.e., it is possible to enable signal 0 and 3 in phase 1 for
// instance. For Top Earl Grey it is for instance also recommended to
// trigger signals 1 and 2 at the same time since both trigger the same
// action in the life cycle controller and serve as redundant
// channels.
case 0:
signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT;
signal_map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_FIELD;
break;
case 1:
signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT;
signal_map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_FIELD;
break;
case 2:
signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT;
signal_map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_FIELD;
break;
case 3:
signal_enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E3_BIT;
signal_map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_FIELD;
break;
// Does not trigger any of the signals. Can be useful in cases where an
// escalation phase is just used to wait a specific amount of time.
case 0xFFFFFFFF:
break;
default:
return kDifBadArg;
}
// Clear all settings.
if (signal == 0xFFFFFFFF) {
ctrl_reg = bitfield_bit32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT, false);
ctrl_reg = bitfield_field32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_FIELD,
(uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
ctrl_reg = bitfield_bit32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT, false);
ctrl_reg = bitfield_field32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_FIELD,
(uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
ctrl_reg = bitfield_bit32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT, false);
ctrl_reg = bitfield_field32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_FIELD,
(uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
ctrl_reg = bitfield_bit32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E3_BIT, false);
ctrl_reg = bitfield_field32_write(
ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_FIELD,
(uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
} else {
ctrl_reg = bitfield_bit32_write(ctrl_reg, signal_enable_bit, true);
ctrl_reg = bitfield_field32_write(
ctrl_reg, signal_map_field,
(uint32_t)(phase - kDifAlertHandlerClassStatePhase0));
}
switch (phase) {
case kDifAlertHandlerClassStatePhase0:
mmio_region_write32_shadowed(
alert_handler->base_addr, phase0_cycles_reg_offset,
config.escalation_phases[i].duration_cycles);
break;
case kDifAlertHandlerClassStatePhase1:
mmio_region_write32_shadowed(
alert_handler->base_addr, phase1_cycles_reg_offset,
config.escalation_phases[i].duration_cycles);
break;
case kDifAlertHandlerClassStatePhase2:
mmio_region_write32_shadowed(
alert_handler->base_addr, phase2_cycles_reg_offset,
config.escalation_phases[i].duration_cycles);
break;
case kDifAlertHandlerClassStatePhase3:
mmio_region_write32_shadowed(
alert_handler->base_addr, phase3_cycles_reg_offset,
config.escalation_phases[i].duration_cycles);
break;
default:
return kDifBadArg;
}
}
// Configure the class accumulator threshold.
mmio_region_write32_shadowed(alert_handler->base_addr,
accum_thresh_reg_offset,
config.accumulator_threshold);
// Configure the class IRQ deadline.
mmio_region_write32_shadowed(alert_handler->base_addr,
irq_deadline_reg_offset,
config.irq_deadline_cycles);
// Configure the crashdump phase.
mmio_region_write32_shadowed(alert_handler->base_addr,
crashdump_phase_reg_offset,
(uint32_t)(config.crashdump_escalation_phase -
kDifAlertHandlerClassStatePhase0));
// Configure the class control register last, since this holds the enable bit.
mmio_region_write32_shadowed(alert_handler->base_addr, ctrl_reg_offset,
ctrl_reg);
// Lock the configuration.
if (locked == kDifToggleEnabled) {
mmio_region_write32(alert_handler->base_addr, class_regwen_offset, 0);
}
return kDifOk;
}
dif_result_t dif_alert_handler_configure_ping_timer(
const dif_alert_handler_t *alert_handler, uint32_t ping_timeout,
dif_toggle_t enabled, dif_toggle_t locked) {
if (alert_handler == NULL ||
ping_timeout >
ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_MASK ||
!dif_is_valid_toggle(enabled) || !dif_is_valid_toggle(locked)) {
return kDifBadArg;
}
// Check if the ping timer is locked.
if (!mmio_region_read32(alert_handler->base_addr,
ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET)) {
return kDifLocked;
}
// Set the ping timeout.
mmio_region_write32_shadowed(
alert_handler->base_addr,
ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_REG_OFFSET, ping_timeout);
// Enable the ping timer.
// Note, this must be done after the timeout has been configured above, since
// pinging will start immediately.
if (enabled == kDifToggleEnabled) {
mmio_region_write32_shadowed(
alert_handler->base_addr,
ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET, 1);
}
// Lock the configuration.
if (locked == kDifToggleEnabled) {
mmio_region_write32(alert_handler->base_addr,
ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
}
return kDifOk;
}
dif_result_t dif_alert_handler_ping_timer_set_enabled(
const dif_alert_handler_t *alert_handler, dif_toggle_t locked) {
if (alert_handler == NULL || !dif_is_valid_toggle(locked)) {
return kDifBadArg;
}
// Check if the ping timer is locked.
if (!mmio_region_read32(alert_handler->base_addr,
ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET)) {
return kDifLocked;
}
// Enable the ping timer.
mmio_region_write32_shadowed(alert_handler->base_addr,
ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET,
1);
// Lock the configuration.
if (locked == kDifToggleEnabled) {
mmio_region_write32(alert_handler->base_addr,
ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
}
return kDifOk;
}
dif_result_t dif_alert_handler_lock_alert(
const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert) {
if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
return kDifBadArg;
}
ptrdiff_t regwen_offset =
ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET + alert * sizeof(uint32_t);
mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
return kDifOk;
}
dif_result_t dif_alert_handler_is_alert_locked(
const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
bool *is_locked) {
if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS ||
is_locked == NULL) {
return kDifBadArg;
}
ptrdiff_t regwen_offset =
ALERT_HANDLER_ALERT_REGWEN_0_REG_OFFSET + alert * sizeof(uint32_t);
*is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
return kDifOk;
}
dif_result_t dif_alert_handler_lock_local_alert(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_local_alert_t local_alert) {
if (alert_handler == NULL) {
return kDifBadArg;
}
ptrdiff_t regwen_offset;
switch (local_alert) {
LIST_OF_LOC_ALERTS(LOC_ALERT_REGWENS_CASE_)
default:
return kDifBadArg;
}
mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
return kDifOk;
}
dif_result_t dif_alert_handler_is_local_alert_locked(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_local_alert_t local_alert, bool *is_locked) {
if (alert_handler == NULL || is_locked == NULL) {
return kDifBadArg;
}
ptrdiff_t regwen_offset;
switch (local_alert) {
LIST_OF_LOC_ALERTS(LOC_ALERT_REGWENS_CASE_)
default:
return kDifBadArg;
}
*is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
return kDifOk;
}
dif_result_t dif_alert_handler_lock_class(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class) {
if (alert_handler == NULL) {
return kDifBadArg;
}
ptrdiff_t regwen_offset;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_REGWENS_CASE_)
default:
return kDifBadArg;
}
mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
return kDifOk;
}
dif_result_t dif_alert_handler_is_class_locked(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class, bool *is_locked) {
if (alert_handler == NULL || is_locked == NULL) {
return kDifBadArg;
}
ptrdiff_t regwen_offset;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_REGWENS_CASE_)
default:
return kDifBadArg;
}
*is_locked = !mmio_region_read32(alert_handler->base_addr, regwen_offset);
return kDifOk;
}
dif_result_t dif_alert_handler_lock_ping_timer(
const dif_alert_handler_t *alert_handler) {
if (alert_handler == NULL) {
return kDifBadArg;
}
mmio_region_write32(alert_handler->base_addr,
ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET, 0);
return kDifOk;
}
dif_result_t dif_alert_handler_is_ping_timer_locked(
const dif_alert_handler_t *alert_handler, bool *is_locked) {
if (alert_handler == NULL || is_locked == NULL) {
return kDifBadArg;
}
*is_locked = !mmio_region_read32(alert_handler->base_addr,
ALERT_HANDLER_PING_TIMER_REGWEN_REG_OFFSET);
return kDifOk;
}
dif_result_t dif_alert_handler_alert_is_cause(
const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert,
bool *is_cause) {
if (alert_handler == NULL || is_cause == NULL ||
alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
return kDifBadArg;
}
ptrdiff_t cause_reg_offset =
ALERT_HANDLER_ALERT_CAUSE_0_REG_OFFSET + alert * sizeof(uint32_t);
*is_cause = mmio_region_read32(alert_handler->base_addr, cause_reg_offset);
return kDifOk;
}
dif_result_t dif_alert_handler_alert_acknowledge(
const dif_alert_handler_t *alert_handler, dif_alert_handler_alert_t alert) {
if (alert_handler == NULL || alert >= ALERT_HANDLER_PARAM_N_ALERTS) {
return kDifBadArg;
}
ptrdiff_t cause_reg_offset =
ALERT_HANDLER_ALERT_CAUSE_0_REG_OFFSET + alert * sizeof(uint32_t);
mmio_region_write32(alert_handler->base_addr, cause_reg_offset, 0x1);
return kDifOk;
}
dif_result_t dif_alert_handler_local_alert_is_cause(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_local_alert_t local_alert, bool *is_cause) {
if (alert_handler == NULL || is_cause == NULL) {
return kDifBadArg;
}
ptrdiff_t cause_reg_offset;
switch (local_alert) {
LIST_OF_LOC_ALERTS(LOC_ALERT_CAUSE_REGS_CASE_)
default:
return kDifBadArg;
}
*is_cause = mmio_region_read32(alert_handler->base_addr, cause_reg_offset);
return kDifOk;
}
dif_result_t dif_alert_handler_local_alert_acknowledge(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_local_alert_t local_alert) {
if (alert_handler == NULL) {
return kDifBadArg;
}
ptrdiff_t cause_reg_offset;
switch (local_alert) {
LIST_OF_LOC_ALERTS(LOC_ALERT_CAUSE_REGS_CASE_)
default:
return kDifBadArg;
}
mmio_region_write32(alert_handler->base_addr, cause_reg_offset, 0x1);
return kDifOk;
}
dif_result_t dif_alert_handler_escalation_can_clear(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class, bool *can_clear) {
if (alert_handler == NULL || can_clear == NULL) {
return kDifBadArg;
}
ptrdiff_t regwen_offset;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_CLEAR_REGWENS_CASE_)
default:
return kDifBadArg;
}
*can_clear = mmio_region_read32(alert_handler->base_addr, regwen_offset);
return kDifOk;
}
dif_result_t dif_alert_handler_escalation_disable_clearing(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class) {
if (alert_handler == NULL) {
return kDifBadArg;
}
ptrdiff_t regwen_offset;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_CLEAR_REGWENS_CASE_)
default:
return kDifBadArg;
}
mmio_region_write32(alert_handler->base_addr, regwen_offset, 0);
return kDifOk;
}
dif_result_t dif_alert_handler_escalation_clear(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class) {
if (alert_handler == NULL) {
return kDifBadArg;
}
#define ALERT_CLASS_CLEAR_CASE_(class_, value_) \
case kDifAlertHandlerClass##class_: \
reg_offset = ALERT_HANDLER_CLASS##class_##_CLR_SHADOWED_REG_OFFSET; \
break;
ptrdiff_t reg_offset;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_CLEAR_CASE_)
default:
return kDifBadArg;
}
#undef ALERT_CLASS_CLEAR_CASE_
mmio_region_write32_shadowed(alert_handler->base_addr, reg_offset, 0x1);
return kDifOk;
}
dif_result_t dif_alert_handler_get_accumulator(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class, uint16_t *num_alerts) {
if (alert_handler == NULL || num_alerts == NULL) {
return kDifBadArg;
}
#define ALERT_CLASS_ACCUM_CASE_(class_, value_) \
case kDifAlertHandlerClass##class_: \
reg_offset = ALERT_HANDLER_CLASS##class_##_ACCUM_CNT_REG_OFFSET; \
field = \
ALERT_HANDLER_CLASS##class_##_ACCUM_CNT_CLASS##class_##_ACCUM_CNT_FIELD; \
break;
ptrdiff_t reg_offset;
bitfield_field32_t field;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_ACCUM_CASE_)
default:
return kDifBadArg;
}
#undef ALERT_CLASS_ACCUM_CASE_
uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset);
*num_alerts = bitfield_field32_read(reg, field);
return kDifOk;
}
dif_result_t dif_alert_handler_get_escalation_counter(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class, uint32_t *cycles) {
if (alert_handler == NULL || cycles == NULL) {
return kDifBadArg;
}
#define ALERT_CLASS_ESC_CNT_CASE_(class_, value_) \
case kDifAlertHandlerClass##class_: \
reg_offset = ALERT_HANDLER_CLASS##class_##_ESC_CNT_REG_OFFSET; \
break;
ptrdiff_t reg_offset;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_ESC_CNT_CASE_)
default:
return kDifBadArg;
}
#undef ALERT_CLASS_ESC_CNT_CASE_
*cycles = mmio_region_read32(alert_handler->base_addr, reg_offset);
return kDifOk;
}
dif_result_t dif_alert_handler_get_class_state(
const dif_alert_handler_t *alert_handler,
dif_alert_handler_class_t alert_class,
dif_alert_handler_class_state_t *state) {
if (alert_handler == NULL || state == NULL) {
return kDifBadArg;
}
#define ALERT_CLASS_STATE_CASE_(class_, value_) \
case kDifAlertHandlerClass##class_: \
reg_offset = ALERT_HANDLER_CLASS##class_##_STATE_REG_OFFSET; \
field = ALERT_HANDLER_CLASS##class_##_STATE_CLASS##class_##_STATE_FIELD; \
break;
ptrdiff_t reg_offset;
bitfield_field32_t field;
switch (alert_class) {
LIST_OF_CLASSES(ALERT_CLASS_STATE_CASE_)
default:
return kDifBadArg;
}
#undef ALERT_CLASS_STATE_CASE_
uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset);
switch (bitfield_field32_read(reg, field)) {
case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_IDLE:
*state = kDifAlertHandlerClassStateIdle;
break;
case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_TIMEOUT:
*state = kDifAlertHandlerClassStateTimeout;
break;
case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE0:
*state = kDifAlertHandlerClassStatePhase0;
break;
case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE1:
*state = kDifAlertHandlerClassStatePhase1;
break;
case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE2:
*state = kDifAlertHandlerClassStatePhase2;
break;
case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_PHASE3:
*state = kDifAlertHandlerClassStatePhase3;
break;
case ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_VALUE_TERMINAL:
*state = kDifAlertHandlerClassStateTerminal;
break;
default:
return kDifError;
}
return kDifOk;
}