| // 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 "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!"); |
| |
| /** |
| * 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) { |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| *classification = |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSA; |
| break; |
| case kDifAlertHandlerClassB: |
| *classification = |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSB; |
| break; |
| case kDifAlertHandlerClassC: |
| *classification = |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSC; |
| break; |
| case kDifAlertHandlerClassD: |
| *classification = |
| ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_VALUE_CLASSD; |
| break; |
| default: |
| 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; |
| } |
| |
| // Enable the alert. |
| ptrdiff_t enable_reg_offset = |
| ALERT_HANDLER_ALERT_EN_SHADOWED_0_REG_OFFSET + alert * sizeof(uint32_t); |
| uint32_t enable_reg = bitfield_bit32_write( |
| 0, ALERT_HANDLER_ALERT_EN_SHADOWED_0_EN_A_0_BIT, true); |
| mmio_region_write32_shadowed(alert_handler->base_addr, enable_reg_offset, |
| enable_reg); |
| |
| // Classify the alert. |
| ptrdiff_t class_reg_offset = ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_REG_OFFSET + |
| alert * sizeof(uint32_t); |
| uint32_t class_reg = bitfield_field32_write( |
| 0, ALERT_HANDLER_ALERT_CLASS_SHADOWED_0_CLASS_A_0_FIELD, classification); |
| mmio_region_write32_shadowed(alert_handler->base_addr, class_reg_offset, |
| class_reg); |
| |
| // Lock the configuration. |
| if (locked == kDifToggleEnabled) { |
| mmio_region_write32(alert_handler->base_addr, regwen_offset, 0); |
| } |
| |
| return kDifOk; |
| } |
| |
| /** |
| * Classifies alerts for a single alert class. Returns `false` if any of the |
| * provided configuration is invalid. |
| */ |
| // TODO(#9899): support locking the alert class configuration. |
| OT_WARN_UNUSED_RESULT |
| static bool classify_alerts(const dif_alert_handler_t *alert_handler, |
| const dif_alert_handler_class_config_t *class) { |
| if (class->alerts == NULL && class->alerts_len != 0) { |
| return false; |
| } |
| |
| for (int i = 0; i < class->alerts_len; ++i) { |
| if (dif_alert_handler_configure_alert(alert_handler, class->alerts[i], |
| class->alert_class, kDifToggleEnabled, |
| kDifToggleDisabled) != kDifOk) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Classifies local alerts for a single alert class. Returns `false` if any of |
| * the provided configuration is invalid. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static bool classify_local_alerts( |
| const dif_alert_handler_t *alert_handler, |
| const dif_alert_handler_class_config_t *class) { |
| if (class->local_alerts == NULL && class->local_alerts_len != 0) { |
| return false; |
| } |
| |
| for (int i = 0; i < class->local_alerts_len; ++i) { |
| uint32_t classification; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| classification = |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSA; |
| break; |
| case kDifAlertHandlerClassB: |
| classification = |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSB; |
| break; |
| case kDifAlertHandlerClassC: |
| classification = |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSC; |
| break; |
| case kDifAlertHandlerClassD: |
| classification = |
| ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_VALUE_CLASSD; |
| break; |
| default: |
| return false; |
| } |
| |
| ptrdiff_t enable_reg_offset; |
| bitfield_bit32_index_t enable_bit; |
| ptrdiff_t class_reg_offset; |
| bitfield_field32_t class_field; |
| switch (class->local_alerts[i]) { |
| case kDifAlertHandlerLocalAlertAlertPingFail: |
| enable_reg_offset = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_0_REG_OFFSET; |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_0_EN_LA_0_BIT; |
| class_reg_offset = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_REG_OFFSET; |
| class_field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_0_CLASS_LA_0_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationPingFail: |
| enable_reg_offset = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_1_REG_OFFSET; |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_1_EN_LA_1_BIT; |
| class_reg_offset = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_1_REG_OFFSET; |
| class_field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_1_CLASS_LA_1_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertAlertIntegrityFail: |
| enable_reg_offset = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_2_REG_OFFSET; |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_2_EN_LA_2_BIT; |
| class_reg_offset = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_2_REG_OFFSET; |
| class_field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_2_CLASS_LA_2_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationIntegrityFail: |
| enable_reg_offset = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_3_REG_OFFSET; |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_3_EN_LA_3_BIT; |
| class_reg_offset = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_3_REG_OFFSET; |
| class_field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_3_CLASS_LA_3_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertBusIntegrityFail: |
| enable_reg_offset = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_4_REG_OFFSET; |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_4_EN_LA_4_BIT; |
| class_reg_offset = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_4_REG_OFFSET; |
| class_field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_4_CLASS_LA_4_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedUpdateError: |
| enable_reg_offset = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_5_REG_OFFSET; |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_5_EN_LA_5_BIT; |
| class_reg_offset = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_5_REG_OFFSET; |
| class_field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_5_CLASS_LA_5_FIELD; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedStorageError: |
| enable_reg_offset = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_6_REG_OFFSET; |
| enable_bit = ALERT_HANDLER_LOC_ALERT_EN_SHADOWED_6_EN_LA_6_BIT; |
| class_reg_offset = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_6_REG_OFFSET; |
| class_field = ALERT_HANDLER_LOC_ALERT_CLASS_SHADOWED_6_CLASS_LA_6_FIELD; |
| break; |
| default: |
| return false; |
| } |
| |
| uint32_t enable_reg = |
| mmio_region_read32(alert_handler->base_addr, enable_reg_offset); |
| uint32_t class_reg = |
| mmio_region_read32(alert_handler->base_addr, class_reg_offset); |
| enable_reg = bitfield_bit32_write(enable_reg, enable_bit, true); |
| class_reg = bitfield_field32_write(class_reg, class_field, classification); |
| mmio_region_write32_shadowed(alert_handler->base_addr, enable_reg_offset, |
| enable_reg); |
| mmio_region_write32_shadowed(alert_handler->base_addr, class_reg_offset, |
| class_reg); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Configures the control registers of a particular alert handler class. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static bool configure_class(const dif_alert_handler_t *alert_handler, |
| const dif_alert_handler_class_config_t *class) { |
| ptrdiff_t reg_offset; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_CTRL_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_CTRL_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_CTRL_SHADOWED_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| |
| // NOTE: from this point on, we assume that Class A's constants are |
| // representative of all alert class control register layouts. |
| |
| // We're going to configure the entire register, so there's no point in |
| // reading it. In particular, this makes sure that the enable bits for each |
| // escalation signal default to disabled. |
| uint32_t ctrl_reg = 0; |
| |
| // Configure the escalation protocol enable flag. |
| ctrl_reg = |
| bitfield_bit32_write(ctrl_reg, ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_BIT, |
| dif_toggle_to_bool(class->use_escalation_protocol)); |
| |
| // Configure the escalation protocol auto-lock flag. |
| ctrl_reg = bitfield_bit32_write(ctrl_reg, |
| ALERT_HANDLER_CLASSA_CTRL_SHADOWED_LOCK_BIT, |
| dif_toggle_to_bool(class->automatic_locking)); |
| |
| if (class->phase_signals == NULL && class->phase_signals_len != 0) { |
| return false; |
| } |
| // Configure the escalation signals for each escalation phase. In particular, |
| // if an escalation phase is configured, it is also enabled. |
| for (int i = 0; i < class->phase_signals_len; ++i) { |
| if (class->phase_signals[i].signal >= ALERT_HANDLER_PARAM_N_ESC_SEV) { |
| return false; |
| } |
| |
| bitfield_bit32_index_t enable_bit; |
| bitfield_field32_t map_field; |
| switch (class->phase_signals[i].phase) { |
| case kDifAlertHandlerClassStatePhase0: |
| enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E0_BIT; |
| map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E0_FIELD; |
| break; |
| case kDifAlertHandlerClassStatePhase1: |
| enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E1_BIT; |
| map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E1_FIELD; |
| break; |
| case kDifAlertHandlerClassStatePhase2: |
| enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E2_BIT; |
| map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E2_FIELD; |
| break; |
| case kDifAlertHandlerClassStatePhase3: |
| enable_bit = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_EN_E3_BIT; |
| map_field = ALERT_HANDLER_CLASSA_CTRL_SHADOWED_MAP_E3_FIELD; |
| break; |
| default: |
| return false; |
| } |
| |
| ctrl_reg = bitfield_bit32_write(ctrl_reg, enable_bit, true); |
| ctrl_reg = bitfield_field32_write(ctrl_reg, map_field, |
| class->phase_signals[i].signal); |
| } |
| |
| mmio_region_write32_shadowed(alert_handler->base_addr, reg_offset, ctrl_reg); |
| |
| // Configure the class accumulator threshold. |
| ptrdiff_t acc_offset; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| acc_offset = ALERT_HANDLER_CLASSA_ACCUM_THRESH_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| acc_offset = ALERT_HANDLER_CLASSB_ACCUM_THRESH_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| acc_offset = ALERT_HANDLER_CLASSC_ACCUM_THRESH_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| acc_offset = ALERT_HANDLER_CLASSD_ACCUM_THRESH_SHADOWED_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| mmio_region_write32_shadowed(alert_handler->base_addr, acc_offset, |
| class->accumulator_threshold); |
| |
| // Configure the class IRQ deadline. |
| ptrdiff_t deadline_offset; |
| switch (class->alert_class) { |
| case kDifAlertHandlerClassA: |
| deadline_offset = ALERT_HANDLER_CLASSA_TIMEOUT_CYC_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| deadline_offset = ALERT_HANDLER_CLASSB_TIMEOUT_CYC_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| deadline_offset = ALERT_HANDLER_CLASSC_TIMEOUT_CYC_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| deadline_offset = ALERT_HANDLER_CLASSD_TIMEOUT_CYC_SHADOWED_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| mmio_region_write32_shadowed(alert_handler->base_addr, deadline_offset, |
| class->irq_deadline_cycles); |
| |
| return true; |
| } |
| |
| /** |
| * Configures phase durations of a particular class. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static bool configure_phase_durations( |
| const dif_alert_handler_t *alert_handler, |
| const dif_alert_handler_class_config_t *class) { |
| if (class->phase_durations == NULL && class->phase_durations_len != 0) { |
| return false; |
| } |
| |
| for (int i = 0; i < class->phase_durations_len; ++i) { |
| // To save on writing a fairly ridiculous `if` chain, we use a lookup table |
| // that leverages the numeric values of enum constants. |
| static const ptrdiff_t kRegOffsets |
| [ALERT_HANDLER_PARAM_N_CLASSES][ALERT_HANDLER_PARAM_N_PHASES] = { |
| [kDifAlertHandlerClassA] = |
| { |
| ALERT_HANDLER_CLASSA_PHASE0_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSA_PHASE1_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSA_PHASE2_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSA_PHASE3_CYC_SHADOWED_REG_OFFSET, |
| }, |
| [kDifAlertHandlerClassB] = |
| { |
| ALERT_HANDLER_CLASSB_PHASE0_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSB_PHASE1_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSB_PHASE2_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSB_PHASE3_CYC_SHADOWED_REG_OFFSET, |
| }, |
| [kDifAlertHandlerClassC] = |
| { |
| ALERT_HANDLER_CLASSC_PHASE0_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSC_PHASE1_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSC_PHASE2_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSC_PHASE3_CYC_SHADOWED_REG_OFFSET, |
| }, |
| [kDifAlertHandlerClassD] = |
| { |
| ALERT_HANDLER_CLASSD_PHASE0_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSD_PHASE1_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSD_PHASE2_CYC_SHADOWED_REG_OFFSET, |
| ALERT_HANDLER_CLASSD_PHASE3_CYC_SHADOWED_REG_OFFSET, |
| }, |
| }; |
| |
| if (class->alert_class >= ALERT_HANDLER_PARAM_N_CLASSES) { |
| return false; |
| } |
| |
| // NOTE: phase need not be an escalation phase! |
| dif_alert_handler_class_state_t phase = class->phase_durations[i].phase; |
| if (phase < kDifAlertHandlerClassStatePhase0 || |
| phase > kDifAlertHandlerClassStatePhase3) { |
| return false; |
| } |
| ptrdiff_t reg_offset = |
| kRegOffsets[class->alert_class] |
| [phase - kDifAlertHandlerClassStatePhase0]; |
| |
| mmio_region_write32_shadowed(alert_handler->base_addr, reg_offset, |
| class->phase_durations[i].cycles); |
| } |
| |
| return true; |
| } |
| |
| dif_result_t dif_alert_handler_configure( |
| const dif_alert_handler_t *alert_handler, |
| dif_alert_handler_config_t config) { |
| if (alert_handler == NULL) { |
| return kDifBadArg; |
| } |
| // Check that the provided ping timeout actually fits in the timeout register, |
| // which is smaller than a native word length. |
| if (config.ping_timeout > |
| ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_MASK) { |
| return kDifBadArg; |
| } |
| if (config.classes == NULL && config.classes_len != 0) { |
| return kDifBadArg; |
| } |
| |
| for (int i = 0; i < config.classes_len; ++i) { |
| if (!classify_alerts(alert_handler, &config.classes[i])) { |
| return kDifError; |
| } |
| if (!classify_local_alerts(alert_handler, &config.classes[i])) { |
| return kDifError; |
| } |
| if (!configure_class(alert_handler, &config.classes[i])) { |
| return kDifError; |
| } |
| if (!configure_phase_durations(alert_handler, &config.classes[i])) { |
| return kDifError; |
| } |
| } |
| |
| // Check if the ping timer is locked. |
| bool is_ping_timer_locked; |
| dif_result_t result = dif_alert_handler_is_ping_timer_locked( |
| alert_handler, &is_ping_timer_locked); |
| if (result != kDifOk) { |
| return result; |
| } |
| if (is_ping_timer_locked) { |
| return kDifLocked; |
| } |
| |
| // Configure the ping timer. |
| uint32_t ping_timeout_reg = bitfield_field32_write( |
| 0, |
| ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_FIELD, |
| config.ping_timeout); |
| mmio_region_write32_shadowed( |
| alert_handler->base_addr, |
| ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_REG_OFFSET, ping_timeout_reg); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_alert_handler_lock_ping_timer( |
| const dif_alert_handler_t *alert_handler) { |
| if (alert_handler == NULL) { |
| return kDifBadArg; |
| } |
| |
| uint32_t reg = bitfield_bit32_write( |
| 1, ALERT_HANDLER_PING_TIMER_EN_SHADOWED_PING_TIMER_EN_SHADOWED_BIT, true); |
| mmio_region_write32_shadowed(alert_handler->base_addr, |
| ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET, |
| reg); |
| |
| 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; |
| } |
| uint32_t reg = |
| mmio_region_read32(alert_handler->base_addr, |
| ALERT_HANDLER_PING_TIMER_EN_SHADOWED_REG_OFFSET); |
| *is_locked = bitfield_bit32_read( |
| reg, ALERT_HANDLER_PING_TIMER_EN_SHADOWED_PING_TIMER_EN_SHADOWED_BIT); |
| |
| 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); |
| uint32_t cause_reg = |
| mmio_region_read32(alert_handler->base_addr, cause_reg_offset); |
| // NOTE: assuming all cause registers across all alerts use the same bit index |
| // for the cause bit |
| *is_cause = |
| bitfield_bit32_read(cause_reg, ALERT_HANDLER_ALERT_CAUSE_0_A_0_BIT); |
| |
| 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); |
| // NOTE: assuming all cause registers across all alerts use the same bit index |
| // for the cause bit |
| uint32_t cause_reg = |
| bitfield_bit32_write(0, ALERT_HANDLER_ALERT_CAUSE_0_A_0_BIT, true); |
| mmio_region_write32(alert_handler->base_addr, cause_reg_offset, cause_reg); |
| |
| return kDifOk; |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| static bool loc_alert_cause_reg_offset(dif_alert_handler_local_alert_t alert, |
| ptrdiff_t *offset) { |
| switch (alert) { |
| case kDifAlertHandlerLocalAlertAlertPingFail: |
| *offset = ALERT_HANDLER_LOC_ALERT_CAUSE_0_REG_OFFSET; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationPingFail: |
| *offset = ALERT_HANDLER_LOC_ALERT_CAUSE_1_REG_OFFSET; |
| break; |
| case kDifAlertHandlerLocalAlertAlertIntegrityFail: |
| *offset = ALERT_HANDLER_LOC_ALERT_CAUSE_2_REG_OFFSET; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationIntegrityFail: |
| *offset = ALERT_HANDLER_LOC_ALERT_CAUSE_3_REG_OFFSET; |
| break; |
| case kDifAlertHandlerLocalAlertBusIntegrityFail: |
| *offset = ALERT_HANDLER_LOC_ALERT_CAUSE_4_REG_OFFSET; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedUpdateError: |
| *offset = ALERT_HANDLER_LOC_ALERT_CAUSE_5_REG_OFFSET; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedStorageError: |
| *offset = ALERT_HANDLER_LOC_ALERT_CAUSE_6_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| static bool loc_alert_cause_bit_index(dif_alert_handler_local_alert_t alert, |
| bitfield_bit32_index_t *index) { |
| switch (alert) { |
| case kDifAlertHandlerLocalAlertAlertPingFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_0_LA_0_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationPingFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_1_LA_1_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertAlertIntegrityFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_2_LA_2_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertEscalationIntegrityFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_3_LA_3_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertBusIntegrityFail: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_4_LA_4_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedUpdateError: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_5_LA_5_BIT; |
| break; |
| case kDifAlertHandlerLocalAlertShadowedStorageError: |
| *index = ALERT_HANDLER_LOC_ALERT_CAUSE_6_LA_6_BIT; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| dif_result_t dif_alert_handler_local_alert_is_cause( |
| const dif_alert_handler_t *alert_handler, |
| dif_alert_handler_local_alert_t alert, bool *is_cause) { |
| if (alert_handler == NULL || is_cause == NULL) { |
| return kDifBadArg; |
| } |
| |
| // Get offset of cause register. |
| ptrdiff_t offset; |
| if (!loc_alert_cause_reg_offset(alert, &offset)) { |
| return kDifBadArg; |
| } |
| |
| // Get bit index within cause register. |
| bitfield_bit32_index_t index; |
| if (!loc_alert_cause_bit_index(alert, &index)) { |
| return kDifBadArg; |
| } |
| |
| // Read the cause register. |
| uint32_t reg = mmio_region_read32(alert_handler->base_addr, offset); |
| *is_cause = bitfield_bit32_read(reg, index); |
| |
| return kDifOk; |
| } |
| |
| dif_result_t dif_alert_handler_local_alert_acknowledge( |
| const dif_alert_handler_t *alert_handler, |
| dif_alert_handler_local_alert_t alert) { |
| if (alert_handler == NULL) { |
| return kDifBadArg; |
| } |
| |
| // Get offset of cause register. |
| ptrdiff_t offset; |
| if (!loc_alert_cause_reg_offset(alert, &offset)) { |
| return kDifBadArg; |
| } |
| |
| // Get bit index within cause register. |
| bitfield_bit32_index_t index; |
| if (!loc_alert_cause_bit_index(alert, &index)) { |
| return kDifBadArg; |
| } |
| |
| // Clear the cause register by writing setting the index bit. |
| uint32_t reg = bitfield_bit32_write(0, index, true); |
| mmio_region_write32(alert_handler->base_addr, offset, reg); |
| |
| return kDifOk; |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| static bool get_clear_enable_reg_offset(dif_alert_handler_class_t class, |
| ptrdiff_t *reg_offset) { |
| switch (class) { |
| case kDifAlertHandlerClassA: |
| *reg_offset = ALERT_HANDLER_CLASSA_CLR_REGWEN_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| *reg_offset = ALERT_HANDLER_CLASSB_CLR_REGWEN_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| *reg_offset = ALERT_HANDLER_CLASSC_CLR_REGWEN_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| *reg_offset = ALERT_HANDLER_CLASSD_CLR_REGWEN_REG_OFFSET; |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| 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 reg_offset; |
| if (!get_clear_enable_reg_offset(alert_class, ®_offset)) { |
| return kDifBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset); |
| *can_clear = bitfield_bit32_read( |
| reg, ALERT_HANDLER_CLASSA_CLR_REGWEN_CLASSA_CLR_REGWEN_BIT); |
| |
| 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 reg_offset; |
| if (!get_clear_enable_reg_offset(alert_class, ®_offset)) { |
| return kDifBadArg; |
| } |
| |
| uint32_t reg = bitfield_bit32_write( |
| 0, ALERT_HANDLER_CLASSA_CLR_REGWEN_CLASSA_CLR_REGWEN_BIT, true); |
| mmio_region_write32(alert_handler->base_addr, reg_offset, reg); |
| |
| 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; |
| } |
| |
| ptrdiff_t reg_offset; |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_CLR_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_CLR_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_CLR_SHADOWED_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_CLR_SHADOWED_REG_OFFSET; |
| break; |
| default: |
| return kDifBadArg; |
| } |
| |
| uint32_t reg = bitfield_bit32_write( |
| 0, ALERT_HANDLER_CLASSA_CLR_SHADOWED_CLASSA_CLR_SHADOWED_BIT, true); |
| mmio_region_write32_shadowed(alert_handler->base_addr, reg_offset, reg); |
| |
| 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 *alerts) { |
| if (alert_handler == NULL || alerts == NULL) { |
| return kDifBadArg; |
| } |
| |
| ptrdiff_t reg_offset; |
| bitfield_field32_t field; |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_ACCUM_CNT_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSA_ACCUM_CNT_CLASSA_ACCUM_CNT_FIELD; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_ACCUM_CNT_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSB_ACCUM_CNT_CLASSB_ACCUM_CNT_FIELD; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_ACCUM_CNT_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSC_ACCUM_CNT_CLASSC_ACCUM_CNT_FIELD; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_ACCUM_CNT_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSD_ACCUM_CNT_CLASSD_ACCUM_CNT_FIELD; |
| break; |
| default: |
| return kDifBadArg; |
| } |
| |
| uint32_t reg = mmio_region_read32(alert_handler->base_addr, reg_offset); |
| *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; |
| } |
| |
| ptrdiff_t reg_offset; |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_ESC_CNT_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_ESC_CNT_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_ESC_CNT_REG_OFFSET; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_ESC_CNT_REG_OFFSET; |
| break; |
| default: |
| return kDifBadArg; |
| } |
| |
| *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; |
| } |
| |
| ptrdiff_t reg_offset; |
| bitfield_field32_t field; |
| switch (alert_class) { |
| case kDifAlertHandlerClassA: |
| reg_offset = ALERT_HANDLER_CLASSA_STATE_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSA_STATE_CLASSA_STATE_FIELD; |
| break; |
| case kDifAlertHandlerClassB: |
| reg_offset = ALERT_HANDLER_CLASSB_STATE_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSB_STATE_CLASSB_STATE_FIELD; |
| break; |
| case kDifAlertHandlerClassC: |
| reg_offset = ALERT_HANDLER_CLASSC_STATE_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSC_STATE_CLASSC_STATE_FIELD; |
| break; |
| case kDifAlertHandlerClassD: |
| reg_offset = ALERT_HANDLER_CLASSD_STATE_REG_OFFSET; |
| field = ALERT_HANDLER_CLASSD_STATE_CLASSD_STATE_FIELD; |
| break; |
| default: |
| return kDifBadArg; |
| } |
| |
| 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; |
| } |