__le32 flags;
} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
+/**
+ * enum iwl_error_recovery_flags - flags for error recovery cmd
+ * @ERROR_RECOVERY_UPDATE_DB: update db from blob sent
+ * @ERROR_RECOVERY_END_OF_RECOVERY: end of recovery
+ */
+enum iwl_error_recovery_flags {
+ ERROR_RECOVERY_UPDATE_DB = BIT(0),
+ ERROR_RECOVERY_END_OF_RECOVERY = BIT(1),
+};
+
+/**
+ * struct iwl_fw_error_recovery_cmd - recovery cmd sent upon assert
+ * @flags: &enum iwl_error_recovery_flags
+ * @buf_size: db buffer size in bytes
+ */
+struct iwl_fw_error_recovery_cmd {
+ __le32 flags;
+ __le32 buf_size;
+} __packed; /* ERROR_RECOVERY_CMD_HDR_API_S_VER_1 */
+
#endif /* __iwl_fw_api_alive_h__ */
* @INIT_EXTENDED_CFG_CMD: &struct iwl_init_extended_cfg_cmd
*/
INIT_EXTENDED_CFG_CMD = 0x03,
+
+ /**
+ * @FW_ERROR_RECOVERY_CMD: &struct iwl_fw_error_recovery_cmd
+ */
+ FW_ERROR_RECOVERY_CMD = 0x7,
};
#endif /* __iwl_fw_api_commands_h__ */
IWL_UCODE_TLV_IML = 52,
IWL_UCODE_TLV_UMAC_DEBUG_ADDRS = 54,
IWL_UCODE_TLV_LMAC_DEBUG_ADDRS = 55,
+ IWL_UCODE_TLV_FW_RECOVERY_INFO = 57,
IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_INI_TLV_GROUP | 0x1,
IWL_UCODE_TLV_TYPE_HCMD = IWL_UCODE_INI_TLV_GROUP | 0x2,
IWL_UCODE_TLV_TYPE_REGIONS = IWL_UCODE_INI_TLV_GROUP | 0x3,
u32 n_scan_channels;
u32 standard_phy_calibration_size;
u32 flags;
+ u32 error_log_addr;
+ u32 error_log_size;
unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)];
unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)];
};
return -ENOMEM;
break;
}
+ case IWL_UCODE_TLV_FW_RECOVERY_INFO: {
+ struct {
+ __le32 buf_addr;
+ __le32 buf_size;
+ } *recov_info = (void *)tlv_data;
+
+ if (tlv_len != sizeof(*recov_info))
+ goto invalid_tlv_len;
+ capa->error_log_addr =
+ le32_to_cpu(recov_info->buf_addr);
+ capa->error_log_size =
+ le32_to_cpu(recov_info->buf_size);
+ }
+ break;
case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: {
struct iwl_umac_debug_addrs *dbg_ptrs =
(void *)tlv_data;
}
#endif /* CONFIG_ACPI */
+void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags)
+{
+ u32 error_log_size = mvm->fw->ucode_capa.error_log_size;
+ int ret;
+ u32 resp;
+
+ struct iwl_fw_error_recovery_cmd recovery_cmd = {
+ .flags = cpu_to_le32(flags),
+ .buf_size = 0,
+ };
+ struct iwl_host_cmd host_cmd = {
+ .id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD),
+ .flags = CMD_WANT_SKB,
+ .data = {&recovery_cmd, },
+ .len = {sizeof(recovery_cmd), },
+ };
+
+ /* no error log was defined in TLV */
+ if (!error_log_size)
+ return;
+
+ if (flags & ERROR_RECOVERY_UPDATE_DB) {
+ /* no buf was allocated while HW reset */
+ if (!mvm->error_recovery_buf)
+ return;
+
+ host_cmd.data[1] = mvm->error_recovery_buf;
+ host_cmd.len[1] = error_log_size;
+ host_cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
+ recovery_cmd.buf_size = cpu_to_le32(error_log_size);
+ }
+
+ ret = iwl_mvm_send_cmd(mvm, &host_cmd);
+ kfree(mvm->error_recovery_buf);
+ mvm->error_recovery_buf = NULL;
+
+ if (ret) {
+ IWL_ERR(mvm, "Failed to send recovery cmd %d\n", ret);
+ return;
+ }
+
+ /* skb respond is only relevant in ERROR_RECOVERY_UPDATE_DB */
+ if (flags & ERROR_RECOVERY_UPDATE_DB) {
+ resp = le32_to_cpu(*(__le32 *)host_cmd.resp_pkt->data);
+ if (resp)
+ IWL_ERR(mvm,
+ "Failed to send recovery cmd blob was invalid %d\n",
+ resp);
+ }
+}
+
static int iwl_mvm_sar_init(struct iwl_mvm *mvm)
{
int ret;
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+ iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_UPDATE_DB);
+
ret = iwl_mvm_sar_init(mvm);
if (ret == 0) {
ret = iwl_mvm_sar_geo_init(mvm);
/* allow transport/FW low power modes */
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_END_OF_RECOVERY);
+
/*
* If we have TDLS peers, remove them. We don't know the last seqno/PN
* of packets the FW sent out, so we must reconnect.
/* -1 for always, 0 for never, >0 for that many times */
s8 fw_restart;
+ u8 *error_recovery_buf;
#ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led;
void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags);
void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb);
static const struct iwl_hcmd_names iwl_mvm_system_names[] = {
HCMD_NAME(SHARED_MEM_CFG_CMD),
HCMD_NAME(INIT_EXTENDED_CFG_CMD),
+ HCMD_NAME(FW_ERROR_RECOVERY_CMD),
};
/* Please keep this array *SORTED* by hex value.
kfree(mvm->mcast_filter_cmd);
mvm->mcast_filter_cmd = NULL;
+ kfree(mvm->error_recovery_buf);
+ mvm->error_recovery_buf = NULL;
+
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
kfree(mvm->d3_resume_sram);
#endif
/* don't let the transport/FW power down */
iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ if (mvm->fw->ucode_capa.error_log_size) {
+ u32 src_size = mvm->fw->ucode_capa.error_log_size;
+ u32 src_addr = mvm->fw->ucode_capa.error_log_addr;
+ u8 *recover_buf = kzalloc(src_size, GFP_ATOMIC);
+
+ if (recover_buf) {
+ mvm->error_recovery_buf = recover_buf;
+ iwl_trans_read_mem_bytes(mvm->trans,
+ src_addr,
+ recover_buf,
+ src_size);
+ }
+ }
+
if (fw_error && mvm->fw_restart > 0)
mvm->fw_restart--;
set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status);