Merge branch 'iwlwifi-fixes' into iwlwifi-next
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / iwlwifi / mvm / mac80211.c
index 20915587c8207a46bf7525db41df3af98c46404a..302c8cc50f25a695c40ce59555f8a05bf58cae2d 100644 (file)
@@ -85,6 +85,8 @@
 #include "testmode.h"
 #include "iwl-fw-error-dump.h"
 #include "iwl-prph.h"
+#include "iwl-csr.h"
+#include "iwl-nvm-parse.h"
 
 static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
        {
@@ -105,7 +107,7 @@ static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
 
 static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
        {
-               .num_different_channels = 1,
+               .num_different_channels = 2,
                .max_interfaces = 3,
                .limits = iwl_mvm_limits,
                .n_limits = ARRAY_SIZE(iwl_mvm_limits),
@@ -300,6 +302,109 @@ static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
        }
 }
 
+struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy,
+                                                 const char *alpha2,
+                                                 enum iwl_mcc_source src_id,
+                                                 bool *changed)
+{
+       struct ieee80211_regdomain *regd = NULL;
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mcc_update_resp *resp;
+
+       IWL_DEBUG_LAR(mvm, "Getting regdomain data for %s from FW\n", alpha2);
+
+       lockdep_assert_held(&mvm->mutex);
+
+       resp = iwl_mvm_update_mcc(mvm, alpha2, src_id);
+       if (IS_ERR_OR_NULL(resp)) {
+               IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n",
+                             PTR_RET(resp));
+               goto out;
+       }
+
+       if (changed)
+               *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE);
+
+       regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg,
+                                     __le32_to_cpu(resp->n_channels),
+                                     resp->channels,
+                                     __le16_to_cpu(resp->mcc));
+       /* Store the return source id */
+       src_id = resp->source_id;
+       kfree(resp);
+       if (IS_ERR_OR_NULL(regd)) {
+               IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n",
+                             PTR_RET(regd));
+               goto out;
+       }
+
+       IWL_DEBUG_LAR(mvm, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n",
+                     regd->alpha2, regd->alpha2[0], regd->alpha2[1], src_id);
+       mvm->lar_regdom_set = true;
+       mvm->mcc_src = src_id;
+
+out:
+       return regd;
+}
+
+void iwl_mvm_update_changed_regdom(struct iwl_mvm *mvm)
+{
+       bool changed;
+       struct ieee80211_regdomain *regd;
+
+       if (!iwl_mvm_is_lar_supported(mvm))
+               return;
+
+       regd = iwl_mvm_get_current_regdomain(mvm, &changed);
+       if (!IS_ERR_OR_NULL(regd)) {
+               /* only update the regulatory core if changed */
+               if (changed)
+                       regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+
+               kfree(regd);
+       }
+}
+
+struct ieee80211_regdomain *iwl_mvm_get_current_regdomain(struct iwl_mvm *mvm,
+                                                         bool *changed)
+{
+       return iwl_mvm_get_regdomain(mvm->hw->wiphy, "ZZ",
+                                    iwl_mvm_is_wifi_mcc_supported(mvm) ?
+                                    MCC_SOURCE_GET_CURRENT :
+                                    MCC_SOURCE_OLD_FW, changed);
+}
+
+int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
+{
+       enum iwl_mcc_source used_src;
+       struct ieee80211_regdomain *regd;
+       const struct ieee80211_regdomain *r =
+                       rtnl_dereference(mvm->hw->wiphy->regd);
+
+       if (!r)
+               return 0;
+
+       /* save the last source in case we overwrite it below */
+       used_src = mvm->mcc_src;
+       if (iwl_mvm_is_wifi_mcc_supported(mvm)) {
+               /* Notify the firmware we support wifi location updates */
+               regd = iwl_mvm_get_current_regdomain(mvm, NULL);
+               if (!IS_ERR_OR_NULL(regd))
+                       kfree(regd);
+       }
+
+       /* Now set our last stored MCC and source */
+       regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, r->alpha2, used_src, NULL);
+       if (IS_ERR_OR_NULL(regd))
+               return -EIO;
+
+       regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+       kfree(regd);
+
+       return 0;
+}
+
 int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 {
        struct ieee80211_hw *hw = mvm->hw;
@@ -326,6 +431,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
                IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
        hw->rate_control_algorithm = "iwl-mvm-rs";
+       hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
+       hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
 
        /*
         * Enable 11w if advertised by firmware and software crypto
@@ -336,20 +443,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            !iwlwifi_mod_params.sw_crypto)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
-           !iwlwifi_mod_params.uapsd_disable) {
-               hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
-               hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
-               hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
-       }
-
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
-           mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
-               hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
-               hw->wiphy->features |=
-                       NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
-                       NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
-       }
+       hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+       hw->wiphy->features |=
+               NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+               NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
 
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
@@ -363,8 +460,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
                BIT(NL80211_IFTYPE_ADHOC);
 
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
-       hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
-                                      REGULATORY_DISABLE_BEACON_HINTS;
+       hw->wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;
+       if (iwl_mvm_is_lar_supported(mvm))
+               hw->wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+       else
+               hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+                                              REGULATORY_DISABLE_BEACON_HINTS;
 
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
                hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
@@ -377,6 +478,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        hw->wiphy->max_remain_on_channel_duration = 10000;
        hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL;
+       /* we can compensate an offset of up to 3 channels = 15 MHz */
+       hw->wiphy->max_adj_channel_rssi_comp = 3 * 5;
 
        /* Extract MAC address */
        memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN);
@@ -403,10 +506,18 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
                hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
                        &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
-       if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels)
+       if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) {
                hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
                        &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
 
+               if ((mvm->fw->ucode_capa.capa[0] &
+                    IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
+                   (mvm->fw->ucode_capa.api[0] &
+                    IWL_UCODE_TLV_API_LQ_SS_PARAMS))
+                       hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |=
+                               IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+       }
+
        hw->wiphy->hw_version = mvm->trans->hw_id;
 
        if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
@@ -459,15 +570,17 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
            device_can_wakeup(mvm->trans->dev)) {
                mvm->wowlan.flags = WIPHY_WOWLAN_ANY;
                hw->wiphy->wowlan = &mvm->wowlan;
-       } else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
+       }
+
+       if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
-               mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
-                                   WIPHY_WOWLAN_DISCONNECT |
-                                   WIPHY_WOWLAN_EAP_IDENTITY_REQ |
-                                   WIPHY_WOWLAN_RFKILL_RELEASE |
-                                   WIPHY_WOWLAN_NET_DETECT;
+               mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT |
+                                    WIPHY_WOWLAN_DISCONNECT |
+                                    WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+                                    WIPHY_WOWLAN_RFKILL_RELEASE |
+                                    WIPHY_WOWLAN_NET_DETECT;
                if (!iwlwifi_mod_params.sw_crypto)
                        mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
                                             WIPHY_WOWLAN_GTK_REKEY_FAILURE |
@@ -707,9 +820,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
        mvmvif->uploaded = false;
        mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
 
-       /* does this make sense at all? */
-       mvmvif->color++;
-
        spin_lock_bh(&mvm->time_event_lock);
        iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data);
        spin_unlock_bh(&mvm->time_event_lock);
@@ -761,44 +871,234 @@ static void iwl_mvm_free_coredump(const void *data)
        kfree(fw_error_dump);
 }
 
+static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
+                              struct iwl_fw_error_dump_data **dump_data)
+{
+       struct iwl_fw_error_dump_fifo *fifo_hdr;
+       u32 *fifo_data;
+       u32 fifo_len;
+       unsigned long flags;
+       int i, j;
+
+       if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags))
+               return;
+
+       /* Pull RXF data from all RXFs */
+       for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) {
+               /*
+                * Keep aside the additional offset that might be needed for
+                * next RXF
+                */
+               u32 offset_diff = RXF_DIFF_FROM_PREV * i;
+
+               fifo_hdr = (void *)(*dump_data)->data;
+               fifo_data = (void *)fifo_hdr->data;
+               fifo_len = mvm->shared_mem_cfg.rxfifo_size[i];
+
+               /* No need to try to read the data if the length is 0 */
+               if (fifo_len == 0)
+                       continue;
+
+               /* Add a TLV for the RXF */
+               (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+               (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
+
+               fifo_hdr->fifo_num = cpu_to_le32(i);
+               fifo_hdr->available_bytes =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_RD_D_SPACE +
+                                                       offset_diff));
+               fifo_hdr->wr_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_RD_WR_PTR +
+                                                       offset_diff));
+               fifo_hdr->rd_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_RD_RD_PTR +
+                                                       offset_diff));
+               fifo_hdr->fence_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_RD_FENCE_PTR +
+                                                       offset_diff));
+               fifo_hdr->fence_mode =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_SET_FENCE_MODE +
+                                                       offset_diff));
+
+               /* Lock fence */
+               iwl_trans_write_prph(mvm->trans,
+                                    RXF_SET_FENCE_MODE + offset_diff, 0x1);
+               /* Set fence pointer to the same place like WR pointer */
+               iwl_trans_write_prph(mvm->trans,
+                                    RXF_LD_WR2FENCE + offset_diff, 0x1);
+               /* Set fence offset */
+               iwl_trans_write_prph(mvm->trans,
+                                    RXF_LD_FENCE_OFFSET_ADDR + offset_diff,
+                                    0x0);
+
+               /* Read FIFO */
+               fifo_len /= sizeof(u32); /* Size in DWORDS */
+               for (j = 0; j < fifo_len; j++)
+                       fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+                                                        RXF_FIFO_RD_FENCE_INC +
+                                                        offset_diff);
+               *dump_data = iwl_fw_error_next_data(*dump_data);
+       }
+
+       /* Pull TXF data from all TXFs */
+       for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) {
+               /* Mark the number of TXF we're pulling now */
+               iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i);
+
+               fifo_hdr = (void *)(*dump_data)->data;
+               fifo_data = (void *)fifo_hdr->data;
+               fifo_len = mvm->shared_mem_cfg.txfifo_size[i];
+
+               /* No need to try to read the data if the length is 0 */
+               if (fifo_len == 0)
+                       continue;
+
+               /* Add a TLV for the FIFO */
+               (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
+               (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr));
+
+               fifo_hdr->fifo_num = cpu_to_le32(i);
+               fifo_hdr->available_bytes =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_FIFO_ITEM_CNT));
+               fifo_hdr->wr_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_WR_PTR));
+               fifo_hdr->rd_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_RD_PTR));
+               fifo_hdr->fence_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_FENCE_PTR));
+               fifo_hdr->fence_mode =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_LOCK_FENCE));
+
+               /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
+               iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR,
+                                    TXF_WR_PTR);
+
+               /* Dummy-read to advance the read pointer to the head */
+               iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA);
+
+               /* Read FIFO */
+               fifo_len /= sizeof(u32); /* Size in DWORDS */
+               for (j = 0; j < fifo_len; j++)
+                       fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+                                                         TXF_READ_MODIFY_DATA);
+               *dump_data = iwl_fw_error_next_data(*dump_data);
+       }
+
+       iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+
+void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
+{
+       if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert ||
+           !mvm->fw_dump_desc)
+               return;
+
+       kfree(mvm->fw_dump_desc);
+       mvm->fw_dump_desc = NULL;
+}
+
 void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 {
        struct iwl_fw_error_dump_file *dump_file;
        struct iwl_fw_error_dump_data *dump_data;
        struct iwl_fw_error_dump_info *dump_info;
+       struct iwl_fw_error_dump_mem *dump_mem;
+       struct iwl_fw_error_dump_trigger_desc *dump_trig;
        struct iwl_mvm_dump_ptrs *fw_error_dump;
-       const struct fw_img *img;
        u32 sram_len, sram_ofs;
-       u32 file_len, rxf_len;
-       unsigned long flags;
-       int reg_val;
+       u32 file_len, fifo_data_len = 0;
+       u32 smem_len = mvm->cfg->smem_len;
+       u32 sram2_len = mvm->cfg->dccm2_len;
 
        lockdep_assert_held(&mvm->mutex);
 
+       /* W/A for 8000 HW family A-step */
+       if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+           CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) {
+               if (smem_len)
+                       smem_len = 0x38000;
+
+               if (sram2_len)
+                       sram2_len = 0x10000;
+       }
+
        fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
        if (!fw_error_dump)
                return;
 
-       img = &mvm->fw->img[mvm->cur_ucode];
-       sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
-       sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       /* SRAM - include stack CCM if driver knows the values for it */
+       if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) {
+               const struct fw_img *img;
+
+               img = &mvm->fw->img[mvm->cur_ucode];
+               sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+               sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+       } else {
+               sram_ofs = mvm->cfg->dccm_offset;
+               sram_len = mvm->cfg->dccm_len;
+       }
+
+       /* reading RXF/TXF sizes */
+       if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
+               struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
+               int i;
+
+               fifo_data_len = 0;
+
+               /* Count RXF size */
+               for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
+                       if (!mem_cfg->rxfifo_size[i])
+                               continue;
 
-       /* reading buffer size */
-       reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
-       rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
+                       /* Add header info */
+                       fifo_data_len += mem_cfg->rxfifo_size[i] +
+                                        sizeof(*dump_data) +
+                                        sizeof(struct iwl_fw_error_dump_fifo);
+               }
 
-       /* the register holds the value divided by 128 */
-       rxf_len = rxf_len << 7;
+               for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) {
+                       if (!mem_cfg->txfifo_size[i])
+                               continue;
+
+                       /* Add header info */
+                       fifo_data_len += mem_cfg->txfifo_size[i] +
+                                        sizeof(*dump_data) +
+                                        sizeof(struct iwl_fw_error_dump_fifo);
+               }
+       }
 
        file_len = sizeof(*dump_file) +
-                  sizeof(*dump_data) * 3 +
-                  sram_len +
-                  rxf_len +
+                  sizeof(*dump_data) * 2 +
+                  sram_len + sizeof(*dump_mem) +
+                  fifo_data_len +
                   sizeof(*dump_info);
 
+       if (mvm->fw_dump_desc)
+               file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
+                           mvm->fw_dump_desc->len;
+
+       /* Make room for the SMEM, if it exists */
+       if (smem_len)
+               file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
+
+       /* Make room for the secondary SRAM, if it exists */
+       if (sram2_len)
+               file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len;
+
        dump_file = vzalloc(file_len);
        if (!dump_file) {
                kfree(fw_error_dump);
+               iwl_mvm_free_fw_dump_desc(mvm);
                return;
        }
 
@@ -814,6 +1114,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
                        cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
+       dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev));
        memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
               sizeof(dump_info->fw_human_readable));
        strncpy(dump_info->dev_human_readable, mvm->cfg->name,
@@ -822,28 +1123,52 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                sizeof(dump_info->bus_human_readable));
 
        dump_data = iwl_fw_error_next_data(dump_data);
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
-       dump_data->len = cpu_to_le32(rxf_len);
-
-       if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
-               u32 *rxf = (void *)dump_data->data;
-               int i;
+       /* We only dump the FIFOs if the FW is in error state */
+       if (test_bit(STATUS_FW_ERROR, &mvm->trans->status))
+               iwl_mvm_dump_fifos(mvm, &dump_data);
+
+       if (mvm->fw_dump_desc) {
+               dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
+               dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
+                                            mvm->fw_dump_desc->len);
+               dump_trig = (void *)dump_data->data;
+               memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
+                      sizeof(*dump_trig) + mvm->fw_dump_desc->len);
+
+               /* now we can free this copy */
+               iwl_mvm_free_fw_dump_desc(mvm);
+               dump_data = iwl_fw_error_next_data(dump_data);
+       }
+
+       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+       dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
+       dump_mem = (void *)dump_data->data;
+       dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+       dump_mem->offset = cpu_to_le32(sram_ofs);
+       iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data,
+                                sram_len);
 
-               for (i = 0; i < (rxf_len / sizeof(u32)); i++) {
-                       iwl_trans_write_prph(mvm->trans,
-                                            RXF_LD_FENCE_OFFSET_ADDR,
-                                            i * sizeof(u32));
-                       rxf[i] = iwl_trans_read_prph(mvm->trans,
-                                                    RXF_FIFO_RD_FENCE_ADDR);
-               }
-               iwl_trans_release_nic_access(mvm->trans, &flags);
+       if (smem_len) {
+               dump_data = iwl_fw_error_next_data(dump_data);
+               dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+               dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem));
+               dump_mem = (void *)dump_data->data;
+               dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM);
+               dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset);
+               iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset,
+                                        dump_mem->data, smem_len);
        }
 
-       dump_data = iwl_fw_error_next_data(dump_data);
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
-       dump_data->len = cpu_to_le32(sram_len);
-       iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
-                                sram_len);
+       if (sram2_len) {
+               dump_data = iwl_fw_error_next_data(dump_data);
+               dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
+               dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem));
+               dump_mem = (void *)dump_data->data;
+               dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM);
+               dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset);
+               iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset,
+                                        dump_mem->data, sram2_len);
+       }
 
        fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans);
        fw_error_dump->op_mode_len = file_len;
@@ -853,16 +1178,31 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 
        dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
                      GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
+
+       clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
 }
 
+struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
+       .trig_desc = {
+               .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
+       },
+};
+
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
        /* clear the D3 reconfig, we only need it to avoid dumping a
         * firmware coredump on reconfiguration, we shouldn't do that
         * on D3->D0 transition
         */
-       if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
+       if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) {
+               mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert;
                iwl_mvm_fw_error_dump(mvm);
+       }
+
+       /* cleanup all stale references (scan, roc), but keep the
+        * ucode_down ref until reconfig is complete
+        */
+       iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
 
        iwl_trans_stop_device(mvm->trans);
 
@@ -893,15 +1233,15 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 
        ieee80211_wake_queues(mvm->hw);
 
-       /* cleanup all stale references (scan, roc), but keep the
-        * ucode_down ref until reconfig is complete */
-       iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
-
        /* clear any stale d0i3 state */
        clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
 
        mvm->vif_count = 0;
        mvm->rx_ba_sessions = 0;
+       mvm->fw_dbg_conf = FW_DBG_INVALID;
+
+       /* keep statistics ticking */
+       iwl_mvm_accu_radio_stats(mvm);
 }
 
 int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
@@ -933,6 +1273,19 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
 
+       /* Some hw restart cleanups must not hold the mutex */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+               /*
+                * Make sure we are out of d0i3. This is needed
+                * to make sure the reference accounting is correct
+                * (and there is no stale d0i3_exit_work).
+                */
+               wait_event_timeout(mvm->d0i3_exit_waitq,
+                                  !test_bit(IWL_MVM_STATUS_IN_D0I3,
+                                            &mvm->status),
+                                  HZ);
+       }
+
        mutex_lock(&mvm->mutex);
        ret = __iwl_mvm_mac_start(mvm);
        mutex_unlock(&mvm->mutex);
@@ -948,7 +1301,7 @@ static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
 
        clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
        iwl_mvm_d0i3_enable_tx(mvm, NULL);
-       ret = iwl_mvm_update_quotas(mvm, NULL);
+       ret = iwl_mvm_update_quotas(mvm, false, NULL);
        if (ret)
                IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
                        ret);
@@ -982,6 +1335,13 @@ static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
                IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
                _iwl_mvm_exit_d0i3(mvm);
        }
+
+       if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND)
+               if (!wait_event_timeout(mvm->d0i3_exit_waitq,
+                                       !test_bit(IWL_MVM_STATUS_IN_D0I3,
+                                                 &mvm->status),
+                                       HZ))
+                       WARN_ONCE(1, "D0i3 exit on resume timed out\n");
 }
 
 static void
@@ -1004,6 +1364,11 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
 {
        lockdep_assert_held(&mvm->mutex);
 
+       /* firmware counters are obviously reset now, but we shouldn't
+        * partially track so also clear the fw_reset_accu counters.
+        */
+       memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats));
+
        /*
         * Disallow low power states when the FW is down by taking
         * the UCODE_DOWN ref. in case of ongoing hw restart the
@@ -1043,7 +1408,8 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
 
        flush_work(&mvm->d0i3_exit_work);
        flush_work(&mvm->async_handlers_wk);
-       flush_work(&mvm->fw_error_dump_wk);
+       cancel_delayed_work_sync(&mvm->fw_dump_wk);
+       iwl_mvm_free_fw_dump_desc(mvm);
 
        mutex_lock(&mvm->mutex);
        __iwl_mvm_mac_stop(mvm);
@@ -1091,6 +1457,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
+       mvmvif->mvm = mvm;
+
        /*
         * make sure D0i3 exit is completed, otherwise a target access
         * during tx queue configuration could be done when still in
@@ -1108,6 +1476,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       /* make sure that beacon statistics don't go backwards with FW reset */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               mvmvif->beacon_stats.accu_num_beacons +=
+                       mvmvif->beacon_stats.num_beacons;
+
        /* Allocate resources for the MAC context, and add it to the fw  */
        ret = iwl_mvm_mac_ctxt_init(mvm, vif);
        if (ret)
@@ -1146,7 +1519,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
 
        ret = iwl_mvm_power_update_mac(mvm);
        if (ret)
-               goto out_release;
+               goto out_remove_mac;
 
        /* beacon filtering */
        ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
@@ -1601,8 +1974,13 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
 
        if (changes & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
+                       /* clear statistics to get clean beacon counter */
+                       iwl_mvm_request_statistics(mvm, true);
+                       memset(&mvmvif->beacon_stats, 0,
+                              sizeof(mvmvif->beacon_stats));
+
                        /* add quota for this interface */
-                       ret = iwl_mvm_update_quotas(mvm, NULL);
+                       ret = iwl_mvm_update_quotas(mvm, true, NULL);
                        if (ret) {
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
@@ -1654,7 +2032,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
                        mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT;
                        /* remove quota for this interface */
-                       ret = iwl_mvm_update_quotas(mvm, NULL);
+                       ret = iwl_mvm_update_quotas(mvm, false, NULL);
                        if (ret)
                                IWL_ERR(mvm, "failed to update quotas\n");
 
@@ -1773,7 +2151,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        /* power updated needs to be done before quotas */
        iwl_mvm_power_update_mac(mvm);
 
-       ret = iwl_mvm_update_quotas(mvm, NULL);
+       ret = iwl_mvm_update_quotas(mvm, false, NULL);
        if (ret)
                goto out_quota_failed;
 
@@ -1839,7 +2217,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
        if (vif->p2p && mvm->p2p_device_vif)
                iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL);
 
-       iwl_mvm_update_quotas(mvm, NULL);
+       iwl_mvm_update_quotas(mvm, false, NULL);
        iwl_mvm_send_rm_bcast_sta(mvm, vif);
        iwl_mvm_binding_remove_vif(mvm, vif);
 
@@ -1978,6 +2356,12 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
+       if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
+               IWL_ERR(mvm, "scan while LAR regdomain is not set\n");
+               ret = -EBUSY;
+               goto out;
+       }
+
        if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
                ret = -EBUSY;
                goto out;
@@ -1987,10 +2371,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
 
        if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
                ret = iwl_mvm_scan_umac(mvm, vif, hw_req);
-       else if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
-               ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
        else
-               ret = iwl_mvm_scan_request(mvm, vif, req);
+               ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
 
        if (ret)
                iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
@@ -2006,7 +2388,19 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
-       iwl_mvm_cancel_scan(mvm);
+       /* Due to a race condition, it's possible that mac80211 asks
+        * us to stop a hw_scan when it's already stopped.  This can
+        * happen, for instance, if we stopped the scan ourselves,
+        * called ieee80211_scan_completed() and the userspace called
+        * cancel scan scan before ieee80211_scan_work() could run.
+        * To handle that, simply return if the scan is not running.
+       */
+       /* FIXME: for now, we ignore this race for UMAC scans, since
+        * they don't set the scan_status.
+        */
+       if ((mvm->scan_status == IWL_MVM_SCAN_OS) ||
+           (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+               iwl_mvm_cancel_scan(mvm);
 
        mutex_unlock(&mvm->mutex);
 }
@@ -2048,25 +2442,35 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+       unsigned long txqs = 0, tids = 0;
        int tid;
 
+       spin_lock_bh(&mvmsta->lock);
+       for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+               struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+
+               if (tid_data->state != IWL_AGG_ON &&
+                   tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
+                       continue;
+
+               __set_bit(tid_data->txq_id, &txqs);
+
+               if (iwl_mvm_tid_queued(tid_data) == 0)
+                       continue;
+
+               __set_bit(tid, &tids);
+       }
+
        switch (cmd) {
        case STA_NOTIFY_SLEEP:
                if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0)
                        ieee80211_sta_block_awake(hw, sta, true);
-               spin_lock_bh(&mvmsta->lock);
-               for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
-                       struct iwl_mvm_tid_data *tid_data;
 
-                       tid_data = &mvmsta->tid_data[tid];
-                       if (tid_data->state != IWL_AGG_ON &&
-                           tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA)
-                               continue;
-                       if (iwl_mvm_tid_queued(tid_data) == 0)
-                               continue;
+               for_each_set_bit(tid, &tids, IWL_MAX_TID_COUNT)
                        ieee80211_sta_set_buffered(sta, tid, true);
-               }
-               spin_unlock_bh(&mvmsta->lock);
+
+               if (txqs)
+                       iwl_trans_freeze_txq_timer(mvm->trans, txqs, true);
                /*
                 * The fw updates the STA to be asleep. Tx packets on the Tx
                 * queues to this station will not be transmitted. The fw will
@@ -2076,11 +2480,15 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw,
        case STA_NOTIFY_AWAKE:
                if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT))
                        break;
+
+               if (txqs)
+                       iwl_trans_freeze_txq_timer(mvm->trans, txqs, false);
                iwl_mvm_sta_modify_ps_wake(mvm, sta);
                break;
        default:
                break;
        }
+       spin_unlock_bh(&mvmsta->lock);
 }
 
 static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
@@ -2088,7 +2496,7 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
                                       struct ieee80211_sta *sta)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 
        /*
         * This is called before mac80211 does RCU synchronisation,
@@ -2105,6 +2513,20 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
        mutex_unlock(&mvm->mutex);
 }
 
+static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               const u8 *bssid)
+{
+       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT))
+               return;
+
+       if (iwlwifi_mod_params.uapsd_disable) {
+               vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
+               return;
+       }
+
+       vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+}
+
 static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif,
                                 struct ieee80211_sta *sta,
@@ -2164,6 +2586,7 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
                 * Reset EBS status here assuming environment has been changed.
                 */
                mvm->last_ebs_successful = true;
+               iwl_mvm_check_uapsd(mvm, vif, sta->addr);
                ret = 0;
        } else if (old_state == IEEE80211_STA_AUTH &&
                   new_state == IEEE80211_STA_ASSOC) {
@@ -2303,13 +2726,13 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
 
        mutex_lock(&mvm->mutex);
 
-       /* Newest FW fixes sched scan while connected on another interface */
-       if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) {
-               if (!vif->bss_conf.idle) {
-                       ret = -EBUSY;
-                       goto out;
-               }
-       } else if (!iwl_mvm_is_idle(mvm)) {
+       if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
+               IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n");
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (!vif->bss_conf.idle) {
                ret = -EBUSY;
                goto out;
        }
@@ -2335,12 +2758,29 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
        int ret;
 
        mutex_lock(&mvm->mutex);
+
+       /* Due to a race condition, it's possible that mac80211 asks
+        * us to stop a sched_scan when it's already stopped.  This
+        * can happen, for instance, if we stopped the scan ourselves,
+        * called ieee80211_sched_scan_stopped() and the userspace called
+        * stop sched scan scan before ieee80211_sched_scan_stopped_work()
+        * could run.  To handle this, simply return if the scan is
+        * not running.
+       */
+       /* FIXME: for now, we ignore this race for UMAC scans, since
+        * they don't set the scan_status.
+        */
+       if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+           !(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+               mutex_unlock(&mvm->mutex);
+               return 0;
+       }
+
        ret = iwl_mvm_scan_offload_stop(mvm, false);
        mutex_unlock(&mvm->mutex);
        iwl_mvm_wait_for_async_handlers(mvm);
 
        return ret;
-
 }
 
 static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
@@ -2853,14 +3293,14 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
         */
        if (vif->type == NL80211_IFTYPE_MONITOR) {
                mvmvif->monitor_active = true;
-               ret = iwl_mvm_update_quotas(mvm, NULL);
+               ret = iwl_mvm_update_quotas(mvm, false, NULL);
                if (ret)
                        goto out_remove_binding;
        }
 
        /* Handle binding during CSA */
        if (vif->type == NL80211_IFTYPE_AP) {
-               iwl_mvm_update_quotas(mvm, NULL);
+               iwl_mvm_update_quotas(mvm, false, NULL);
                iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
        }
 
@@ -2884,7 +3324,7 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
 
                iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
 
-               iwl_mvm_update_quotas(mvm, NULL);
+               iwl_mvm_update_quotas(mvm, false, NULL);
        }
 
        goto out;
@@ -2957,7 +3397,7 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
                break;
        }
 
-       iwl_mvm_update_quotas(mvm, disabled_vif);
+       iwl_mvm_update_quotas(mvm, false, disabled_vif);
        iwl_mvm_binding_remove_vif(mvm, vif);
 
 out:
@@ -3103,7 +3543,7 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
                           bool set)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
+       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
 
        if (!mvm_sta || !mvm_sta->vif) {
                IWL_ERR(mvm, "Station is not associated to a vif\n");
@@ -3149,7 +3589,7 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm,
                mvm->noa_duration = noa_duration;
                mvm->noa_vif = vif;
 
-               return iwl_mvm_update_quotas(mvm, NULL);
+               return iwl_mvm_update_quotas(mvm, false, NULL);
        case IWL_MVM_TM_CMD_SET_BEACON_FILTER:
                /* must be associated client vif - ignore authorized */
                if (!vif || vif->type != NL80211_IFTYPE_STATION ||
@@ -3209,6 +3649,9 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
        IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
                           chsw->chandef.center_freq1);
 
+       iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH,
+                                      NULL, 0);
+
        switch (vif->type) {
        case NL80211_IFTYPE_AP:
                csa_vif =
@@ -3343,16 +3786,107 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
                msk |= mvmsta->tfd_queue_msk;
        }
 
-       msk &= ~BIT(vif->hw_queue[IEEE80211_AC_VO]);
+       if (drop) {
+               if (iwl_mvm_flush_tx_path(mvm, msk, true))
+                       IWL_ERR(mvm, "flush request fail\n");
+               mutex_unlock(&mvm->mutex);
+       } else {
+               mutex_unlock(&mvm->mutex);
+
+               /* this can take a while, and we may need/want other operations
+                * to succeed while doing this, so do it without the mutex held
+                */
+               iwl_trans_wait_tx_queue_empty(mvm->trans, msk);
+       }
+}
+
+static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
+                                 struct survey_info *survey)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       int ret;
+
+       memset(survey, 0, sizeof(*survey));
+
+       /* only support global statistics right now */
+       if (idx != 0)
+               return -ENOENT;
+
+       if (!(mvm->fw->ucode_capa.capa[0] &
+                       IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+               return -ENOENT;
+
+       mutex_lock(&mvm->mutex);
+
+       if (mvm->ucode_loaded) {
+               ret = iwl_mvm_request_statistics(mvm, false);
+               if (ret)
+                       goto out;
+       }
+
+       survey->filled = SURVEY_INFO_TIME |
+                        SURVEY_INFO_TIME_RX |
+                        SURVEY_INFO_TIME_TX |
+                        SURVEY_INFO_TIME_SCAN;
+       survey->time = mvm->accu_radio_stats.on_time_rf +
+                      mvm->radio_stats.on_time_rf;
+       do_div(survey->time, USEC_PER_MSEC);
+
+       survey->time_rx = mvm->accu_radio_stats.rx_time +
+                         mvm->radio_stats.rx_time;
+       do_div(survey->time_rx, USEC_PER_MSEC);
+
+       survey->time_tx = mvm->accu_radio_stats.tx_time +
+                         mvm->radio_stats.tx_time;
+       do_div(survey->time_tx, USEC_PER_MSEC);
 
-       if (iwl_mvm_flush_tx_path(mvm, msk, true))
-               IWL_ERR(mvm, "flush request fail\n");
+       survey->time_scan = mvm->accu_radio_stats.on_time_scan +
+                           mvm->radio_stats.on_time_scan;
+       do_div(survey->time_scan, USEC_PER_MSEC);
+
+ out:
        mutex_unlock(&mvm->mutex);
+       return ret;
+}
 
-       /* this can take a while, and we may need/want other operations
-        * to succeed while doing this, so do it without the mutex held
-        */
-       iwl_trans_wait_tx_queue_empty(mvm->trans, msk);
+static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_sta *sta,
+                                      struct station_info *sinfo)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+       if (!(mvm->fw->ucode_capa.capa[0] &
+                               IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+               return;
+
+       /* if beacon filtering isn't on mac80211 does it anyway */
+       if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER))
+               return;
+
+       if (!vif->bss_conf.assoc)
+               return;
+
+       mutex_lock(&mvm->mutex);
+
+       if (mvmvif->ap_sta_id != mvmsta->sta_id)
+               goto unlock;
+
+       if (iwl_mvm_request_statistics(mvm, false))
+               goto unlock;
+
+       sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons +
+                          mvmvif->beacon_stats.accu_num_beacons;
+       sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX);
+       if (mvmvif->beacon_stats.avg_signal) {
+               /* firmware only reports a value after RXing a few beacons */
+               sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal;
+               sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
+       }
+ unlock:
+       mutex_unlock(&mvm->mutex);
 }
 
 const struct ieee80211_ops iwl_mvm_hw_ops = {
@@ -3421,4 +3955,6 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
 #endif
        .set_default_unicast_key = iwl_mvm_set_default_unicast_key,
 #endif
+       .get_survey = iwl_mvm_mac_get_survey,
+       .sta_statistics = iwl_mvm_mac_sta_statistics,
 };