Skip to content

Commit f130bb7

Browse files
gomordlucacoelho
authored andcommitted
iwlwifi: add FW recovery flow
Add new API and TLV for the ability to send commands in the beginning and end of reset flow. The full flow of recovery is: 1. While loading FW, get address (from the TLV) of target buffer to read in case of reset 2. If an error/assert happens read the address data from step 1. 3. Reset the HW and load the FW. 4. Send the data read in step 2. 5. Add station keys 6. Send notification to FW that reset flow is done. The main use of the recovery flow is for support in PN/SN recovery when offloaded Signed-off-by: Mordechay Goodstein <[email protected]> Signed-off-by: Luca Coelho <[email protected]>
1 parent ff911dc commit f130bb7

File tree

9 files changed

+118
-0
lines changed

9 files changed

+118
-0
lines changed

drivers/net/wireless/intel/iwlwifi/fw/api/alive.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,24 @@ struct iwl_card_state_notif {
197197
__le32 flags;
198198
} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
199199

200+
/**
201+
* enum iwl_error_recovery_flags - flags for error recovery cmd
202+
* @ERROR_RECOVERY_UPDATE_DB: update db from blob sent
203+
* @ERROR_RECOVERY_END_OF_RECOVERY: end of recovery
204+
*/
205+
enum iwl_error_recovery_flags {
206+
ERROR_RECOVERY_UPDATE_DB = BIT(0),
207+
ERROR_RECOVERY_END_OF_RECOVERY = BIT(1),
208+
};
209+
210+
/**
211+
* struct iwl_fw_error_recovery_cmd - recovery cmd sent upon assert
212+
* @flags: &enum iwl_error_recovery_flags
213+
* @buf_size: db buffer size in bytes
214+
*/
215+
struct iwl_fw_error_recovery_cmd {
216+
__le32 flags;
217+
__le32 buf_size;
218+
} __packed; /* ERROR_RECOVERY_CMD_HDR_API_S_VER_1 */
219+
200220
#endif /* __iwl_fw_api_alive_h__ */

drivers/net/wireless/intel/iwlwifi/fw/api/commands.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,11 @@ enum iwl_system_subcmd_ids {
643643
* @INIT_EXTENDED_CFG_CMD: &struct iwl_init_extended_cfg_cmd
644644
*/
645645
INIT_EXTENDED_CFG_CMD = 0x03,
646+
647+
/**
648+
* @FW_ERROR_RECOVERY_CMD: &struct iwl_fw_error_recovery_cmd
649+
*/
650+
FW_ERROR_RECOVERY_CMD = 0x7,
646651
};
647652

648653
#endif /* __iwl_fw_api_commands_h__ */

drivers/net/wireless/intel/iwlwifi/fw/file.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ enum iwl_ucode_tlv_type {
145145
IWL_UCODE_TLV_IML = 52,
146146
IWL_UCODE_TLV_UMAC_DEBUG_ADDRS = 54,
147147
IWL_UCODE_TLV_LMAC_DEBUG_ADDRS = 55,
148+
IWL_UCODE_TLV_FW_RECOVERY_INFO = 57,
148149
IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_INI_TLV_GROUP | 0x1,
149150
IWL_UCODE_TLV_TYPE_HCMD = IWL_UCODE_INI_TLV_GROUP | 0x2,
150151
IWL_UCODE_TLV_TYPE_REGIONS = IWL_UCODE_INI_TLV_GROUP | 0x3,

drivers/net/wireless/intel/iwlwifi/fw/img.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ struct iwl_ucode_capabilities {
105105
u32 n_scan_channels;
106106
u32 standard_phy_calibration_size;
107107
u32 flags;
108+
u32 error_log_addr;
109+
u32 error_log_size;
108110
unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)];
109111
unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)];
110112
};

drivers/net/wireless/intel/iwlwifi/iwl-drv.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,20 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
10881088
return -ENOMEM;
10891089
break;
10901090
}
1091+
case IWL_UCODE_TLV_FW_RECOVERY_INFO: {
1092+
struct {
1093+
__le32 buf_addr;
1094+
__le32 buf_size;
1095+
} *recov_info = (void *)tlv_data;
1096+
1097+
if (tlv_len != sizeof(*recov_info))
1098+
goto invalid_tlv_len;
1099+
capa->error_log_addr =
1100+
le32_to_cpu(recov_info->buf_addr);
1101+
capa->error_log_size =
1102+
le32_to_cpu(recov_info->buf_size);
1103+
}
1104+
break;
10911105
case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: {
10921106
struct iwl_umac_debug_addrs *dbg_ptrs =
10931107
(void *)tlv_data;

drivers/net/wireless/intel/iwlwifi/mvm/fw.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,57 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
976976
}
977977
#endif /* CONFIG_ACPI */
978978

979+
void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags)
980+
{
981+
u32 error_log_size = mvm->fw->ucode_capa.error_log_size;
982+
int ret;
983+
u32 resp;
984+
985+
struct iwl_fw_error_recovery_cmd recovery_cmd = {
986+
.flags = cpu_to_le32(flags),
987+
.buf_size = 0,
988+
};
989+
struct iwl_host_cmd host_cmd = {
990+
.id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD),
991+
.flags = CMD_WANT_SKB,
992+
.data = {&recovery_cmd, },
993+
.len = {sizeof(recovery_cmd), },
994+
};
995+
996+
/* no error log was defined in TLV */
997+
if (!error_log_size)
998+
return;
999+
1000+
if (flags & ERROR_RECOVERY_UPDATE_DB) {
1001+
/* no buf was allocated while HW reset */
1002+
if (!mvm->error_recovery_buf)
1003+
return;
1004+
1005+
host_cmd.data[1] = mvm->error_recovery_buf;
1006+
host_cmd.len[1] = error_log_size;
1007+
host_cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY;
1008+
recovery_cmd.buf_size = cpu_to_le32(error_log_size);
1009+
}
1010+
1011+
ret = iwl_mvm_send_cmd(mvm, &host_cmd);
1012+
kfree(mvm->error_recovery_buf);
1013+
mvm->error_recovery_buf = NULL;
1014+
1015+
if (ret) {
1016+
IWL_ERR(mvm, "Failed to send recovery cmd %d\n", ret);
1017+
return;
1018+
}
1019+
1020+
/* skb respond is only relevant in ERROR_RECOVERY_UPDATE_DB */
1021+
if (flags & ERROR_RECOVERY_UPDATE_DB) {
1022+
resp = le32_to_cpu(*(__le32 *)host_cmd.resp_pkt->data);
1023+
if (resp)
1024+
IWL_ERR(mvm,
1025+
"Failed to send recovery cmd blob was invalid %d\n",
1026+
resp);
1027+
}
1028+
}
1029+
9791030
static int iwl_mvm_sar_init(struct iwl_mvm *mvm)
9801031
{
9811032
int ret;
@@ -1212,6 +1263,9 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
12121263
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
12131264
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
12141265

1266+
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
1267+
iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_UPDATE_DB);
1268+
12151269
ret = iwl_mvm_sar_init(mvm);
12161270
if (ret == 0) {
12171271
ret = iwl_mvm_sar_geo_init(mvm);

drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,8 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
13261326
/* allow transport/FW low power modes */
13271327
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
13281328

1329+
iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_END_OF_RECOVERY);
1330+
13291331
/*
13301332
* If we have TDLS peers, remove them. We don't know the last seqno/PN
13311333
* of packets the FW sent out, so we must reconnect.

drivers/net/wireless/intel/iwlwifi/mvm/mvm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,7 @@ struct iwl_mvm {
10141014

10151015
/* -1 for always, 0 for never, >0 for that many times */
10161016
s8 fw_restart;
1017+
u8 *error_recovery_buf;
10171018

10181019
#ifdef CONFIG_IWLWIFI_LEDS
10191020
struct led_classdev led;
@@ -1657,6 +1658,7 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
16571658
void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
16581659
void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm,
16591660
struct iwl_rx_cmd_buffer *rxb);
1661+
void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags);
16601662
void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
16611663
void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
16621664
struct iwl_rx_cmd_buffer *rxb);

drivers/net/wireless/intel/iwlwifi/mvm/ops.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = {
422422
static const struct iwl_hcmd_names iwl_mvm_system_names[] = {
423423
HCMD_NAME(SHARED_MEM_CFG_CMD),
424424
HCMD_NAME(INIT_EXTENDED_CFG_CMD),
425+
HCMD_NAME(FW_ERROR_RECOVERY_CMD),
425426
};
426427

427428
/* Please keep this array *SORTED* by hex value.
@@ -921,6 +922,9 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
921922
kfree(mvm->mcast_filter_cmd);
922923
mvm->mcast_filter_cmd = NULL;
923924

925+
kfree(mvm->error_recovery_buf);
926+
mvm->error_recovery_buf = NULL;
927+
924928
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
925929
kfree(mvm->d3_resume_sram);
926930
#endif
@@ -1301,6 +1305,20 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
13011305
/* don't let the transport/FW power down */
13021306
iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
13031307

1308+
if (mvm->fw->ucode_capa.error_log_size) {
1309+
u32 src_size = mvm->fw->ucode_capa.error_log_size;
1310+
u32 src_addr = mvm->fw->ucode_capa.error_log_addr;
1311+
u8 *recover_buf = kzalloc(src_size, GFP_ATOMIC);
1312+
1313+
if (recover_buf) {
1314+
mvm->error_recovery_buf = recover_buf;
1315+
iwl_trans_read_mem_bytes(mvm->trans,
1316+
src_addr,
1317+
recover_buf,
1318+
src_size);
1319+
}
1320+
}
1321+
13041322
if (fw_error && mvm->fw_restart > 0)
13051323
mvm->fw_restart--;
13061324
set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status);

0 commit comments

Comments
 (0)