diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/ops.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/ops.c | 83 |
1 files changed, 54 insertions, 29 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 97dfba50c682..2dffc3600ed3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c | |||
@@ -84,15 +84,8 @@ | |||
84 | #include "time-event.h" | 84 | #include "time-event.h" |
85 | #include "iwl-fw-error-dump.h" | 85 | #include "iwl-fw-error-dump.h" |
86 | 86 | ||
87 | /* | ||
88 | * module name, copyright, version, etc. | ||
89 | */ | ||
90 | #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" | 87 | #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" |
91 | |||
92 | #define DRV_VERSION IWLWIFI_VERSION | ||
93 | |||
94 | MODULE_DESCRIPTION(DRV_DESCRIPTION); | 88 | MODULE_DESCRIPTION(DRV_DESCRIPTION); |
95 | MODULE_VERSION(DRV_VERSION); | ||
96 | MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); | 89 | MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); |
97 | MODULE_LICENSE("GPL"); | 90 | MODULE_LICENSE("GPL"); |
98 | 91 | ||
@@ -100,6 +93,7 @@ static const struct iwl_op_mode_ops iwl_mvm_ops; | |||
100 | 93 | ||
101 | struct iwl_mvm_mod_params iwlmvm_mod_params = { | 94 | struct iwl_mvm_mod_params iwlmvm_mod_params = { |
102 | .power_scheme = IWL_POWER_SCHEME_BPS, | 95 | .power_scheme = IWL_POWER_SCHEME_BPS, |
96 | .tfd_q_hang_detect = true | ||
103 | /* rest of fields are 0 by default */ | 97 | /* rest of fields are 0 by default */ |
104 | }; | 98 | }; |
105 | 99 | ||
@@ -109,6 +103,10 @@ MODULE_PARM_DESC(init_dbg, | |||
109 | module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO); | 103 | module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO); |
110 | MODULE_PARM_DESC(power_scheme, | 104 | MODULE_PARM_DESC(power_scheme, |
111 | "power management scheme: 1-active, 2-balanced, 3-low power, default: 2"); | 105 | "power management scheme: 1-active, 2-balanced, 3-low power, default: 2"); |
106 | module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect, | ||
107 | bool, S_IRUGO); | ||
108 | MODULE_PARM_DESC(tfd_q_hang_detect, | ||
109 | "TFD queues hang detection (default: true"); | ||
112 | 110 | ||
113 | /* | 111 | /* |
114 | * module init and exit functions | 112 | * module init and exit functions |
@@ -146,13 +144,14 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) | |||
146 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); | 144 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); |
147 | u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; | 145 | u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; |
148 | u32 reg_val = 0; | 146 | u32 reg_val = 0; |
147 | u32 phy_config = iwl_mvm_get_phy_config(mvm); | ||
149 | 148 | ||
150 | radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >> | 149 | radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >> |
151 | FW_PHY_CFG_RADIO_TYPE_POS; | 150 | FW_PHY_CFG_RADIO_TYPE_POS; |
152 | radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >> | 151 | radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >> |
153 | FW_PHY_CFG_RADIO_STEP_POS; | 152 | FW_PHY_CFG_RADIO_STEP_POS; |
154 | radio_cfg_dash = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_DASH) >> | 153 | radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >> |
155 | FW_PHY_CFG_RADIO_DASH_POS; | 154 | FW_PHY_CFG_RADIO_DASH_POS; |
156 | 155 | ||
157 | /* SKU control */ | 156 | /* SKU control */ |
158 | reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << | 157 | reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << |
@@ -240,6 +239,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { | |||
240 | 239 | ||
241 | RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), | 240 | RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), |
242 | RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true), | 241 | RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true), |
242 | RX_HANDLER(SCAN_ITERATION_COMPLETE, | ||
243 | iwl_mvm_rx_scan_offload_iter_complete_notif, false), | ||
243 | RX_HANDLER(SCAN_OFFLOAD_COMPLETE, | 244 | RX_HANDLER(SCAN_OFFLOAD_COMPLETE, |
244 | iwl_mvm_rx_scan_offload_complete_notif, true), | 245 | iwl_mvm_rx_scan_offload_complete_notif, true), |
245 | RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results, | 246 | RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results, |
@@ -274,6 +275,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { | |||
274 | CMD(MGMT_MCAST_KEY), | 275 | CMD(MGMT_MCAST_KEY), |
275 | CMD(TX_CMD), | 276 | CMD(TX_CMD), |
276 | CMD(TXPATH_FLUSH), | 277 | CMD(TXPATH_FLUSH), |
278 | CMD(SHARED_MEM_CFG), | ||
277 | CMD(MAC_CONTEXT_CMD), | 279 | CMD(MAC_CONTEXT_CMD), |
278 | CMD(TIME_EVENT_CMD), | 280 | CMD(TIME_EVENT_CMD), |
279 | CMD(TIME_EVENT_NOTIFICATION), | 281 | CMD(TIME_EVENT_NOTIFICATION), |
@@ -476,17 +478,19 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
476 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) | 478 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) |
477 | trans_cfg.bc_table_dword = true; | 479 | trans_cfg.bc_table_dword = true; |
478 | 480 | ||
479 | if (!iwlwifi_mod_params.wd_disable) | ||
480 | trans_cfg.queue_watchdog_timeout = cfg->base_params->wd_timeout; | ||
481 | else | ||
482 | trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; | ||
483 | |||
484 | trans_cfg.command_names = iwl_mvm_cmd_strings; | 481 | trans_cfg.command_names = iwl_mvm_cmd_strings; |
485 | 482 | ||
486 | trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; | 483 | trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; |
487 | trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; | 484 | trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; |
488 | trans_cfg.scd_set_active = true; | 485 | trans_cfg.scd_set_active = true; |
489 | 486 | ||
487 | trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; | ||
488 | |||
489 | /* Set a short watchdog for the command queue */ | ||
490 | trans_cfg.cmd_q_wdg_timeout = | ||
491 | iwlmvm_mod_params.tfd_q_hang_detect ? IWL_DEF_WD_TIMEOUT : | ||
492 | IWL_WATCHDOG_DISABLED; | ||
493 | |||
490 | snprintf(mvm->hw->wiphy->fw_version, | 494 | snprintf(mvm->hw->wiphy->fw_version, |
491 | sizeof(mvm->hw->wiphy->fw_version), | 495 | sizeof(mvm->hw->wiphy->fw_version), |
492 | "%s", fw->fw_version); | 496 | "%s", fw->fw_version); |
@@ -517,10 +521,15 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
517 | min_backoff = calc_min_backoff(trans, cfg); | 521 | min_backoff = calc_min_backoff(trans, cfg); |
518 | iwl_mvm_tt_initialize(mvm, min_backoff); | 522 | iwl_mvm_tt_initialize(mvm, min_backoff); |
519 | /* set the nvm_file_name according to priority */ | 523 | /* set the nvm_file_name according to priority */ |
520 | if (iwlwifi_mod_params.nvm_file) | 524 | if (iwlwifi_mod_params.nvm_file) { |
521 | mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; | 525 | mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; |
522 | else | 526 | } else { |
523 | mvm->nvm_file_name = mvm->cfg->default_nvm_file; | 527 | if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) && |
528 | (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)) | ||
529 | mvm->nvm_file_name = mvm->cfg->default_nvm_file_8000A; | ||
530 | else | ||
531 | mvm->nvm_file_name = mvm->cfg->default_nvm_file; | ||
532 | } | ||
524 | 533 | ||
525 | if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, | 534 | if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, |
526 | "not allowing power-up and not having nvm_file\n")) | 535 | "not allowing power-up and not having nvm_file\n")) |
@@ -559,6 +568,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, | |||
559 | if (!mvm->scan_cmd) | 568 | if (!mvm->scan_cmd) |
560 | goto out_free; | 569 | goto out_free; |
561 | 570 | ||
571 | /* Set EBS as successful as long as not stated otherwise by the FW. */ | ||
572 | mvm->last_ebs_successful = true; | ||
573 | |||
562 | err = iwl_mvm_mac_setup_register(mvm); | 574 | err = iwl_mvm_mac_setup_register(mvm); |
563 | if (err) | 575 | if (err) |
564 | goto out_free; | 576 | goto out_free; |
@@ -817,9 +829,20 @@ static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) | |||
817 | struct iwl_mvm *mvm = | 829 | struct iwl_mvm *mvm = |
818 | container_of(work, struct iwl_mvm, fw_error_dump_wk); | 830 | container_of(work, struct iwl_mvm, fw_error_dump_wk); |
819 | 831 | ||
832 | if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT)) | ||
833 | return; | ||
834 | |||
820 | mutex_lock(&mvm->mutex); | 835 | mutex_lock(&mvm->mutex); |
821 | iwl_mvm_fw_error_dump(mvm); | 836 | iwl_mvm_fw_error_dump(mvm); |
837 | |||
838 | /* start recording again if the firmware is not crashed */ | ||
839 | WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) && | ||
840 | mvm->fw->dbg_dest_tlv && | ||
841 | iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf)); | ||
842 | |||
822 | mutex_unlock(&mvm->mutex); | 843 | mutex_unlock(&mvm->mutex); |
844 | |||
845 | iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT); | ||
823 | } | 846 | } |
824 | 847 | ||
825 | void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) | 848 | void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) |
@@ -855,7 +878,10 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) | |||
855 | * If WoWLAN fw asserted, don't restart either, mac80211 | 878 | * If WoWLAN fw asserted, don't restart either, mac80211 |
856 | * can't recover this since we're already half suspended. | 879 | * can't recover this since we're already half suspended. |
857 | */ | 880 | */ |
858 | if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { | 881 | if (!mvm->restart_fw && fw_error) { |
882 | schedule_work(&mvm->fw_error_dump_wk); | ||
883 | } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, | ||
884 | &mvm->status)) { | ||
859 | struct iwl_mvm_reprobe *reprobe; | 885 | struct iwl_mvm_reprobe *reprobe; |
860 | 886 | ||
861 | IWL_ERR(mvm, | 887 | IWL_ERR(mvm, |
@@ -879,16 +905,13 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) | |||
879 | reprobe->dev = mvm->trans->dev; | 905 | reprobe->dev = mvm->trans->dev; |
880 | INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); | 906 | INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); |
881 | schedule_work(&reprobe->work); | 907 | schedule_work(&reprobe->work); |
882 | } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && | 908 | } else if (mvm->cur_ucode == IWL_UCODE_REGULAR) { |
883 | (!fw_error || mvm->restart_fw)) { | ||
884 | /* don't let the transport/FW power down */ | 909 | /* don't let the transport/FW power down */ |
885 | iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); | 910 | iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); |
886 | 911 | ||
887 | if (fw_error && mvm->restart_fw > 0) | 912 | if (fw_error && mvm->restart_fw > 0) |
888 | mvm->restart_fw--; | 913 | mvm->restart_fw--; |
889 | ieee80211_restart_hw(mvm->hw); | 914 | ieee80211_restart_hw(mvm->hw); |
890 | } else if (fw_error) { | ||
891 | schedule_work(&mvm->fw_error_dump_wk); | ||
892 | } | 915 | } |
893 | } | 916 | } |
894 | 917 | ||
@@ -1031,7 +1054,8 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, | |||
1031 | out: | 1054 | out: |
1032 | rcu_read_unlock(); | 1055 | rcu_read_unlock(); |
1033 | } | 1056 | } |
1034 | static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) | 1057 | |
1058 | int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) | ||
1035 | { | 1059 | { |
1036 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); | 1060 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); |
1037 | u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; | 1061 | u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; |
@@ -1047,6 +1071,7 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) | |||
1047 | }; | 1071 | }; |
1048 | struct iwl_d3_manager_config d3_cfg_cmd = { | 1072 | struct iwl_d3_manager_config d3_cfg_cmd = { |
1049 | .min_sleep_time = cpu_to_le32(1000), | 1073 | .min_sleep_time = cpu_to_le32(1000), |
1074 | .wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR), | ||
1050 | }; | 1075 | }; |
1051 | 1076 | ||
1052 | IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); | 1077 | IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); |
@@ -1146,7 +1171,7 @@ void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) | |||
1146 | 1171 | ||
1147 | if (mvm->d0i3_offloading && qos_seq) { | 1172 | if (mvm->d0i3_offloading && qos_seq) { |
1148 | /* update qos seq numbers if offloading was enabled */ | 1173 | /* update qos seq numbers if offloading was enabled */ |
1149 | mvm_ap_sta = (struct iwl_mvm_sta *)sta->drv_priv; | 1174 | mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta); |
1150 | for (i = 0; i < IWL_MAX_TID_COUNT; i++) { | 1175 | for (i = 0; i < IWL_MAX_TID_COUNT; i++) { |
1151 | u16 seq = le16_to_cpu(qos_seq[i]); | 1176 | u16 seq = le16_to_cpu(qos_seq[i]); |
1152 | /* firmware stores last-used one, we store next one */ | 1177 | /* firmware stores last-used one, we store next one */ |
@@ -1245,7 +1270,7 @@ out: | |||
1245 | return ret; | 1270 | return ret; |
1246 | } | 1271 | } |
1247 | 1272 | ||
1248 | static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) | 1273 | int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) |
1249 | { | 1274 | { |
1250 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); | 1275 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); |
1251 | 1276 | ||