aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorMordechay Goodstein <mordechay.goodstein@intel.com>2018-12-13 16:04:51 -0500
committerLuca Coelho <luciano.coelho@intel.com>2019-02-14 04:29:45 -0500
commitf130bb75d8817c560b48c4d1a0e5279968a0859d (patch)
treed3f34bd761be323cd1c3382783e90aa8e198a7a8 /drivers/net
parentff911dcaa2e46627f5fc6a22802f72a8bfce4ab5 (diff)
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 <mordechay.goodstein@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/alive.h20
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/commands.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/file.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/img.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c54
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/ops.c18
9 files changed, 118 insertions, 0 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
index 0026e259fd87..df1bd0d2450e 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
@@ -197,4 +197,24 @@ struct iwl_card_state_notif {
197 __le32 flags; 197 __le32 flags;
198} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ 198} __packed; /* CARD_STATE_NTFY_API_S_VER_1 */
199 199
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 */
205enum 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 */
215struct iwl_fw_error_recovery_cmd {
216 __le32 flags;
217 __le32 buf_size;
218} __packed; /* ERROR_RECOVERY_CMD_HDR_API_S_VER_1 */
219
200#endif /* __iwl_fw_api_alive_h__ */ 220#endif /* __iwl_fw_api_alive_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
index 0290b333d860..4d2274bcc0b5 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
@@ -643,6 +643,11 @@ enum iwl_system_subcmd_ids {
643 * @INIT_EXTENDED_CFG_CMD: &struct iwl_init_extended_cfg_cmd 643 * @INIT_EXTENDED_CFG_CMD: &struct iwl_init_extended_cfg_cmd
644 */ 644 */
645 INIT_EXTENDED_CFG_CMD = 0x03, 645 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,
646}; 651};
647 652
648#endif /* __iwl_fw_api_commands_h__ */ 653#endif /* __iwl_fw_api_commands_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h
index 886a620e03cf..3f61dc50c2d5 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/file.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h
@@ -145,6 +145,7 @@ enum iwl_ucode_tlv_type {
145 IWL_UCODE_TLV_IML = 52, 145 IWL_UCODE_TLV_IML = 52,
146 IWL_UCODE_TLV_UMAC_DEBUG_ADDRS = 54, 146 IWL_UCODE_TLV_UMAC_DEBUG_ADDRS = 54,
147 IWL_UCODE_TLV_LMAC_DEBUG_ADDRS = 55, 147 IWL_UCODE_TLV_LMAC_DEBUG_ADDRS = 55,
148 IWL_UCODE_TLV_FW_RECOVERY_INFO = 57,
148 IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_INI_TLV_GROUP | 0x1, 149 IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_INI_TLV_GROUP | 0x1,
149 IWL_UCODE_TLV_TYPE_HCMD = IWL_UCODE_INI_TLV_GROUP | 0x2, 150 IWL_UCODE_TLV_TYPE_HCMD = IWL_UCODE_INI_TLV_GROUP | 0x2,
150 IWL_UCODE_TLV_TYPE_REGIONS = IWL_UCODE_INI_TLV_GROUP | 0x3, 151 IWL_UCODE_TLV_TYPE_REGIONS = IWL_UCODE_INI_TLV_GROUP | 0x3,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h
index 6ffa2e39a25c..f4c5a4d73206 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/img.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h
@@ -105,6 +105,8 @@ struct iwl_ucode_capabilities {
105 u32 n_scan_channels; 105 u32 n_scan_channels;
106 u32 standard_phy_calibration_size; 106 u32 standard_phy_calibration_size;
107 u32 flags; 107 u32 flags;
108 u32 error_log_addr;
109 u32 error_log_size;
108 unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)]; 110 unsigned long _api[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_API)];
109 unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)]; 111 unsigned long _capa[BITS_TO_LONGS(NUM_IWL_UCODE_TLV_CAPA)];
110}; 112};
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 784e07b648e6..91ec90e5eb67 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -1088,6 +1088,20 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
1088 return -ENOMEM; 1088 return -ENOMEM;
1089 break; 1089 break;
1090 } 1090 }
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;
1091 case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: { 1105 case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: {
1092 struct iwl_umac_debug_addrs *dbg_ptrs = 1106 struct iwl_umac_debug_addrs *dbg_ptrs =
1093 (void *)tlv_data; 1107 (void *)tlv_data;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index cf7f8c340ffe..28ef204c9cf7 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -976,6 +976,57 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm)
976} 976}
977#endif /* CONFIG_ACPI */ 977#endif /* CONFIG_ACPI */
978 978
979void 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
979static int iwl_mvm_sar_init(struct iwl_mvm *mvm) 1030static int iwl_mvm_sar_init(struct iwl_mvm *mvm)
980{ 1031{
981 int ret; 1032 int ret;
@@ -1212,6 +1263,9 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
1212 if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) 1263 if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
1213 iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); 1264 iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
1214 1265
1266 if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
1267 iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_UPDATE_DB);
1268
1215 ret = iwl_mvm_sar_init(mvm); 1269 ret = iwl_mvm_sar_init(mvm);
1216 if (ret == 0) { 1270 if (ret == 0) {
1217 ret = iwl_mvm_sar_geo_init(mvm); 1271 ret = iwl_mvm_sar_geo_init(mvm);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 9377fca39edf..c02559766712 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -1326,6 +1326,8 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
1326 /* allow transport/FW low power modes */ 1326 /* allow transport/FW low power modes */
1327 iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); 1327 iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
1328 1328
1329 iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_END_OF_RECOVERY);
1330
1329 /* 1331 /*
1330 * If we have TDLS peers, remove them. We don't know the last seqno/PN 1332 * If we have TDLS peers, remove them. We don't know the last seqno/PN
1331 * of packets the FW sent out, so we must reconnect. 1333 * of packets the FW sent out, so we must reconnect.
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 0255157378db..c70fc90680af 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -1014,6 +1014,7 @@ struct iwl_mvm {
1014 1014
1015 /* -1 for always, 0 for never, >0 for that many times */ 1015 /* -1 for always, 0 for never, >0 for that many times */
1016 s8 fw_restart; 1016 s8 fw_restart;
1017 u8 *error_recovery_buf;
1017 1018
1018#ifdef CONFIG_IWLWIFI_LEDS 1019#ifdef CONFIG_IWLWIFI_LEDS
1019 struct led_classdev led; 1020 struct led_classdev led;
@@ -1657,6 +1658,7 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
1657void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); 1658void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
1658void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm, 1659void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm,
1659 struct iwl_rx_cmd_buffer *rxb); 1660 struct iwl_rx_cmd_buffer *rxb);
1661void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags);
1660void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); 1662void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);
1661void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, 1663void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
1662 struct iwl_rx_cmd_buffer *rxb); 1664 struct iwl_rx_cmd_buffer *rxb);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
index 0c276124bf0f..f8a5a7074dc1 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c
@@ -422,6 +422,7 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = {
422static const struct iwl_hcmd_names iwl_mvm_system_names[] = { 422static const struct iwl_hcmd_names iwl_mvm_system_names[] = {
423 HCMD_NAME(SHARED_MEM_CFG_CMD), 423 HCMD_NAME(SHARED_MEM_CFG_CMD),
424 HCMD_NAME(INIT_EXTENDED_CFG_CMD), 424 HCMD_NAME(INIT_EXTENDED_CFG_CMD),
425 HCMD_NAME(FW_ERROR_RECOVERY_CMD),
425}; 426};
426 427
427/* Please keep this array *SORTED* by hex value. 428/* 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)
921 kfree(mvm->mcast_filter_cmd); 922 kfree(mvm->mcast_filter_cmd);
922 mvm->mcast_filter_cmd = NULL; 923 mvm->mcast_filter_cmd = NULL;
923 924
925 kfree(mvm->error_recovery_buf);
926 mvm->error_recovery_buf = NULL;
927
924#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) 928#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
925 kfree(mvm->d3_resume_sram); 929 kfree(mvm->d3_resume_sram);
926#endif 930#endif
@@ -1301,6 +1305,20 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
1301 /* don't let the transport/FW power down */ 1305 /* don't let the transport/FW power down */
1302 iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); 1306 iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
1303 1307
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
1304 if (fw_error && mvm->fw_restart > 0) 1322 if (fw_error && mvm->fw_restart > 0)
1305 mvm->fw_restart--; 1323 mvm->fw_restart--;
1306 set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status); 1324 set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status);