]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge remote-tracking branch 'iwlwifi-fixes/master' into iwlwifi-next
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Tue, 6 May 2014 17:37:33 +0000 (20:37 +0300)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Tue, 6 May 2014 17:37:33 +0000 (20:37 +0300)
1  2 
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/mvm/coex.c
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sf.c

index 2c9b6fb456039e3fd93433a8e8f32e391c507879,4c2d4ef28b220c719ac49f9f9726b931c2d35442..f73de239cdc112d09f0f65097063a59bd9378bca
  #define IWL3160_UCODE_API_MAX 9
  
  /* Oldest version we won't warn about */
 -#define IWL7260_UCODE_API_OK  8
 -#define IWL3160_UCODE_API_OK  8
 +#define IWL7260_UCODE_API_OK  9
 +#define IWL3160_UCODE_API_OK  9
  
  /* Lowest firmware API version supported */
 -#define IWL7260_UCODE_API_MIN 7
 -#define IWL3160_UCODE_API_MIN 7
 +#define IWL7260_UCODE_API_MIN 8
 +#define IWL3160_UCODE_API_MIN 8
  
  /* NVM versions */
  #define IWL7260_NVM_VERSION           0x0a1d
@@@ -107,7 -107,6 +107,7 @@@ static const struct iwl_base_params iwl
        .max_event_log_size = 512,
        .shadow_reg_enable = true,
        .pcie_l1_allowed = true,
 +      .apmg_wake_up_wa = true,
  };
  
  static const struct iwl_ht_params iwl7000_ht_params = {
@@@ -245,3 -244,4 +245,4 @@@ const struct iwl_cfg iwl7265_n_cfg = 
  
  MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
  MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
+ MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
index 09186353784b21b70e0403bc1d3c7812ff5e30f8,0489314425cbdf4a4b782867644ee9a0a0ca4b63..4284672d03976143f5ea9c08816db169ec1f7357
@@@ -104,8 -104,11 +104,8 @@@ static const u8 iwl_bt_prio_tbl[BT_COEX
  #define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD  (-65)
  #define BT_ANTENNA_COUPLING_THRESHOLD         (30)
  
 -int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
 +static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
  {
 -      if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
 -              return 0;
 -
        return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC,
                                    sizeof(struct iwl_bt_coex_prio_tbl_cmd),
                                    &iwl_bt_prio_tbl);
@@@ -187,7 -190,7 +187,7 @@@ static const __le32 iwl_combined_lookup
                cpu_to_le32(0xcc00aaaa),
                cpu_to_le32(0x0000aaaa),
                cpu_to_le32(0xc0004000),
-               cpu_to_le32(0x00000000),
+               cpu_to_le32(0x00004000),
                cpu_to_le32(0xf0005000),
                cpu_to_le32(0xf0005000),
        },
                /* Tx Tx disabled */
                cpu_to_le32(0xaaaaaaaa),
                cpu_to_le32(0xaaaaaaaa),
-               cpu_to_le32(0xaaaaaaaa),
+               cpu_to_le32(0xeeaaaaaa),
                cpu_to_le32(0xaaaaaaaa),
                cpu_to_le32(0xcc00ff28),
                cpu_to_le32(0x0000aaaa),
                cpu_to_le32(0xcc00aaaa),
                cpu_to_le32(0x0000aaaa),
-               cpu_to_le32(0xC0004000),
-               cpu_to_le32(0xC0004000),
-               cpu_to_le32(0xF0005000),
-               cpu_to_le32(0xF0005000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xc0004000),
+               cpu_to_le32(0xf0005000),
+               cpu_to_le32(0xf0005000),
        },
  };
  
@@@ -570,9 -573,8 +570,9 @@@ int iwl_send_bt_init_conf(struct iwl_mv
        int ret;
        u32 flags;
  
 -      if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
 -              return 0;
 +      ret = iwl_send_bt_prio_tbl(mvm);
 +      if (ret)
 +              return ret;
  
        bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
        if (!bt_cmd)
        cmd.data[0] = bt_cmd;
  
        bt_cmd->max_kill = 5;
 -      bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD,
 -      bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling,
 -      bt_cmd->bt4_tx_tx_delta_freq_thr = 15,
 -      bt_cmd->bt4_tx_rx_max_freq0 = 15,
 +      bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
 +      bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
 +      bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
 +      bt_cmd->bt4_tx_rx_max_freq0 = 15;
 +      bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
 +      bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
  
        flags = iwlwifi_mod_params.bt_coex_active ?
                        BT_COEX_NW : BT_COEX_DISABLE;
                bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
  
        if (IWL_MVM_BT_COEX_CORUNNING) {
-               bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_CORUN_LUT_20 |
-                                                   BT_VALID_CORUN_LUT_40);
+               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
+                                                    BT_VALID_CORUN_LUT_40);
                bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
        }
  
        if (IWL_MVM_BT_COEX_MPLUT) {
                bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
-               bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+               bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
        }
  
        if (mvm->cfg->bt_shared_single_ant)
@@@ -1215,17 -1215,6 +1215,17 @@@ bool iwl_mvm_bt_coex_is_mimo_allowed(st
        return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
  }
  
 +bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
 +                                  enum ieee80211_band band)
 +{
 +      u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
 +
 +      if (band != IEEE80211_BAND_2GHZ)
 +              return false;
 +
 +      return bt_activity >= BT_LOW_TRAFFIC;
 +}
 +
  u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
                           struct ieee80211_tx_info *info, u8 ac)
  {
  
  void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
  {
 -      if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX))
 -              return;
 -
        iwl_mvm_bt_coex_notif_handle(mvm);
  }
  
@@@ -1270,6 -1262,7 +1270,7 @@@ int iwl_mvm_rx_ant_coupling_notif(struc
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
        u32 ant_isolation = le32_to_cpup((void *)pkt->data);
        u8 __maybe_unused lower_bound, upper_bound;
+       int ret;
        u8 lut;
  
        struct iwl_bt_coex_cmd *bt_cmd;
        memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
               sizeof(bt_cmd->bt4_corun_lut40));
  
-       return 0;
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       kfree(bt_cmd);
+       return ret;
  }
index 6174c027ff594e30c29c5c263c1efa33e6a5f5e8,d73a89ecd78aa0963c40a268fc7dd8e3d7fbe29d..6959fda3fe09d09e34d5fe19c7ded403fba79c37
@@@ -169,12 -169,8 +169,12 @@@ enum iwl_scan_type 
        SCAN_TYPE_DISCOVERY_FORCED      = 6,
  }; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
  
 -/* Maximal number of channels to scan */
 -#define MAX_NUM_SCAN_CHANNELS 0x24
 +/**
 + * Maximal number of channels to scan
 + * it should be equal to:
 + * max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000)
 + */
 +#define MAX_NUM_SCAN_CHANNELS 50
  
  /**
   * struct iwl_scan_cmd - scan request command
   *    this number of packets were received (typically 1)
   * @passive2active: is auto switching from passive to active during scan allowed
   * @rxchain_sel_flags: RXON_RX_CHAIN_*
-  * @max_out_time: in usecs, max out of serving channel time
+  * @max_out_time: in TUs, max out of serving channel time
   * @suspend_time: how long to pause scan when returning to service channel:
-  *    bits 0-19: beacon interal in usecs (suspend before executing)
+  *    bits 0-19: beacon interal in TUs (suspend before executing)
   *    bits 20-23: reserved
   *    bits 24-31: number of beacons (suspend between channels)
   * @rxon_flags: RXON_FLG_*
@@@ -387,8 -383,8 +387,8 @@@ enum scan_framework_client 
   * @quiet_plcp_th:    quiet channel num of packets threshold
   * @good_CRC_th:      passive to active promotion threshold
   * @rx_chain:         RXON rx chain.
-  * @max_out_time:     max uSec to be out of assoceated channel
-  * @suspend_time:     pause scan this long when returning to service channel
+  * @max_out_time:     max TUs to be out of assoceated channel
+  * @suspend_time:     pause scan this TUs when returning to service channel
   * @flags:            RXON flags
   * @filter_flags:     RXONfilter
   * @tx_cmd:           tx command for active scan; for 2GHz and for 5GHz.
@@@ -538,16 -534,13 +538,16 @@@ struct iwl_scan_offload_schedule 
   *
   * IWL_SCAN_OFFLOAD_FLAG_PASS_ALL: pass all results - no filtering.
   * IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan.
 - * IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan
 - *    on A band.
 + * IWL_SCAN_OFFLOAD_FLAG_EBS_QUICK_MODE: EBS duration is 100mSec - typical
 + *    beacon period. Finding channel activity in this mode is not guaranteed.
 + * IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE: EBS duration is 200mSec.
 + *    Assuming beacon period is 100ms finding channel activity is guaranteed.
   */
  enum iwl_scan_offload_flags {
        IWL_SCAN_OFFLOAD_FLAG_PASS_ALL          = BIT(0),
        IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL    = BIT(2),
 -      IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN       = BIT(3),
 +      IWL_SCAN_OFFLOAD_FLAG_EBS_QUICK_MODE    = BIT(5),
 +      IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE = BIT(6),
  };
  
  /**
@@@ -570,24 -563,17 +570,24 @@@ enum iwl_scan_offload_compleate_status 
        IWL_SCAN_OFFLOAD_ABORTED        = 2,
  };
  
 +enum iwl_scan_ebs_status {
 +      IWL_SCAN_EBS_SUCCESS,
 +      IWL_SCAN_EBS_FAILED,
 +      IWL_SCAN_EBS_CHAN_NOT_FOUND,
 +};
 +
  /**
   * iwl_scan_offload_complete - SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1
   * @last_schedule_line:               last schedule line executed (fast or regular)
   * @last_schedule_iteration:  last scan iteration executed before scan abort
   * @status:                   enum iwl_scan_offload_compleate_status
 + * @ebs_status: last EBS status, see IWL_SCAN_EBS_*
   */
  struct iwl_scan_offload_complete {
        u8 last_schedule_line;
        u8 last_schedule_iteration;
        u8 status;
 -      u8 reserved;
 +      u8 ebs_status;
  } __packed;
  
  /**
index 3e8437fadb4b8c002796bf6e945d00e0713b1501,f0cebf12c7b8415a3c787d0cc77a9b2b1c2a15ef..97c3deae655273ff3c5184cd86a4722f34714eae
@@@ -276,7 -276,6 +276,7 @@@ int iwl_mvm_mac_setup_register(struct i
                    IEEE80211_HW_AMPDU_AGGREGATION |
                    IEEE80211_HW_TIMING_BEACON_ONLY |
                    IEEE80211_HW_CONNECTION_MONITOR |
 +                  IEEE80211_HW_SUPPORTS_UAPSD |
                    IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
                    IEEE80211_HW_SUPPORTS_STATIC_SMPS;
  
                                    IEEE80211_RADIOTAP_MCS_HAVE_STBC;
        hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC;
        hw->rate_control_algorithm = "iwl-mvm-rs";
 +      hw->uapsd_queues = IWL_UAPSD_AC_INFO;
 +      hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
  
        /*
         * Enable 11w if advertised by firmware and software crypto
            !iwlwifi_mod_params.sw_crypto)
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
  
 -      if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) {
 -              hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
 -              hw->uapsd_queues = IWL_UAPSD_AC_INFO;
 -              hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
 -      }
 +      /* Disable uAPSD due to firmware issues */
 +      if (true)
 +              hw->flags &= ~IEEE80211_HW_SUPPORTS_UAPSD;
  
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
                BIT(NL80211_IFTYPE_P2P_CLIENT) |
                BIT(NL80211_IFTYPE_AP) |
                BIT(NL80211_IFTYPE_P2P_GO) |
 -              BIT(NL80211_IFTYPE_P2P_DEVICE);
 -
 -      /* IBSS has bugs in older versions */
 -      if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8)
 -              hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
 +              BIT(NL80211_IFTYPE_P2P_DEVICE) |
 +              BIT(NL80211_IFTYPE_ADHOC);
  
        hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
        hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
        else
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
  
 -      if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
 -              hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
 -              hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
 -              hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
 -              /* we create the 802.11 header and zero length SSID IE. */
 -              hw->wiphy->max_sched_scan_ie_len =
 -                                      SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
 -      }
 +      hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
 +      hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
 +      hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
 +      /* we create the 802.11 header and zero length SSID IE. */
 +      hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
  
        hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
                               NL80211_FEATURE_P2P_GO_OPPPS;
        }
  
  #ifdef CONFIG_PM_SLEEP
 -      if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
 +      if (iwl_mvm_is_d0i3_supported(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 &&
            mvm->trans->ops->d3_suspend &&
            mvm->trans->ops->d3_resume &&
            device_can_wakeup(mvm->trans->dev)) {
@@@ -826,7 -827,8 +826,7 @@@ static int iwl_mvm_mac_add_interface(st
                goto out_remove_mac;
  
        if (!mvm->bf_allowed_vif &&
 -          vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
 -          mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){
 +          vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
                mvm->bf_allowed_vif = mvmvif;
                vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
                                     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
@@@ -1221,10 -1223,6 +1221,10 @@@ static int iwl_mvm_configure_bcast_filt
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
                return 0;
  
 +      /* bcast filtering isn't supported for P2P client */
 +      if (vif->p2p)
 +              return 0;
 +
        if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
                return 0;
  
@@@ -1334,6 -1332,7 +1334,7 @@@ static void iwl_mvm_bss_info_changed_st
                 */
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
+               iwl_mvm_sf_update(mvm, vif, false);
                WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, CMD_SYNC));
        } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
                              BSS_CHANGED_QOS)) {
@@@ -1698,11 -1697,6 +1699,11 @@@ static int iwl_mvm_mac_sta_state(struc
                ret = iwl_mvm_add_sta(mvm, vif, sta);
        } else if (old_state == IEEE80211_STA_NONE &&
                   new_state == IEEE80211_STA_AUTH) {
 +              /*
 +               * EBS may be disabled due to previous failures reported by FW.
 +               * Reset EBS status here assuming environment has been changed.
 +               */
 +              mvm->last_ebs_successful = true;
                ret = 0;
        } else if (old_state == IEEE80211_STA_AUTH &&
                   new_state == IEEE80211_STA_ASSOC) {
index 7a332cb9c1ce16bffc00f4515120cfc0e26fa383,e1c838899363b373d176a71bc32d02282d56c018..857ddaf6f48c80149223178d3c184f5b4e3d2734
@@@ -59,7 -59,7 +59,7 @@@
  /* max allowed rate miss before sync LQ cmd */
  #define IWL_MISSED_RATE_MAX           15
  #define RS_STAY_IN_COLUMN_TIMEOUT       (5*HZ)
+ #define RS_IDLE_TIMEOUT                 (5*HZ)
  
  static u8 rs_ht_to_legacy[] = {
        [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX,
@@@ -142,7 -142,7 +142,7 @@@ enum rs_column_mode 
        RS_MIMO2,
  };
  
- #define MAX_NEXT_COLUMNS 5
+ #define MAX_NEXT_COLUMNS 7
  #define MAX_COLUMN_CHECKS 3
  
  typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm,
@@@ -212,8 -212,10 +212,10 @@@ static const struct rs_tx_column rs_tx_
                        RS_COLUMN_LEGACY_ANT_B,
                        RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_SISO_ANT_B,
-                       RS_COLUMN_MIMO2,
-                       RS_COLUMN_MIMO2_SGI,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
        },
        [RS_COLUMN_LEGACY_ANT_B] = {
                        RS_COLUMN_LEGACY_ANT_A,
                        RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_SISO_ANT_B,
-                       RS_COLUMN_MIMO2,
-                       RS_COLUMN_MIMO2_SGI,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
+                       RS_COLUMN_INVALID,
                },
        },
        [RS_COLUMN_SISO_ANT_A] = {
                        RS_COLUMN_MIMO2,
                        RS_COLUMN_SISO_ANT_A_SGI,
                        RS_COLUMN_SISO_ANT_B_SGI,
-                       RS_COLUMN_MIMO2_SGI,
+                       RS_COLUMN_LEGACY_ANT_A,
+                       RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
                        RS_COLUMN_MIMO2,
                        RS_COLUMN_SISO_ANT_B_SGI,
                        RS_COLUMN_SISO_ANT_A_SGI,
-                       RS_COLUMN_MIMO2_SGI,
+                       RS_COLUMN_LEGACY_ANT_A,
+                       RS_COLUMN_LEGACY_ANT_B,
+                       RS_COLUMN_INVALID,
                },
                .checks = {
                        rs_siso_allow,
                        RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_SISO_ANT_B,
                        RS_COLUMN_MIMO2,
+                       RS_COLUMN_LEGACY_ANT_A,
+                       RS_COLUMN_LEGACY_ANT_B,
                },
                .checks = {
                        rs_siso_allow,
                        RS_COLUMN_SISO_ANT_B,
                        RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_MIMO2,
+                       RS_COLUMN_LEGACY_ANT_A,
+                       RS_COLUMN_LEGACY_ANT_B,
                },
                .checks = {
                        rs_siso_allow,
                        RS_COLUMN_SISO_ANT_A_SGI,
                        RS_COLUMN_SISO_ANT_B_SGI,
                        RS_COLUMN_MIMO2_SGI,
+                       RS_COLUMN_LEGACY_ANT_A,
+                       RS_COLUMN_LEGACY_ANT_B,
                },
                .checks = {
                        rs_mimo_allow,
                        RS_COLUMN_SISO_ANT_A,
                        RS_COLUMN_SISO_ANT_B,
                        RS_COLUMN_MIMO2,
+                       RS_COLUMN_LEGACY_ANT_A,
+                       RS_COLUMN_LEGACY_ANT_B,
                },
                .checks = {
                        rs_mimo_allow,
@@@ -503,15 -519,14 +519,17 @@@ static void rs_rate_scale_clear_window(
        window->average_tpt = IWL_INVALID_VALUE;
  }
  
- static void rs_rate_scale_clear_tbl_windows(struct iwl_scale_tbl_info *tbl)
+ static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm,
+                                           struct iwl_scale_tbl_info *tbl)
  {
        int i;
  
+       IWL_DEBUG_RATE(mvm, "Clearing up window stats\n");
        for (i = 0; i < IWL_RATE_COUNT; i++)
                rs_rate_scale_clear_window(&tbl->win[i]);
 +
 +      for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++)
 +              rs_rate_scale_clear_window(&tbl->tpc_win[i]);
  }
  
  static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
@@@ -641,34 -656,17 +659,34 @@@ static int _rs_collect_tx_data(struct i
        return 0;
  }
  
 -static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
 -                            int scale_index, int attempts, int successes)
 +static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta,
 +                            struct iwl_scale_tbl_info *tbl,
 +                            int scale_index, int attempts, int successes,
 +                            u8 reduced_txp)
  {
        struct iwl_rate_scale_data *window = NULL;
 +      int ret;
  
        if (scale_index < 0 || scale_index >= IWL_RATE_COUNT)
                return -EINVAL;
  
 +      if (tbl->column != RS_COLUMN_INVALID) {
 +              lq_sta->tx_stats[tbl->column][scale_index].total += attempts;
 +              lq_sta->tx_stats[tbl->column][scale_index].success += successes;
 +      }
 +
        /* Select window for current tx bit rate */
        window = &(tbl->win[scale_index]);
  
 +      ret = _rs_collect_tx_data(tbl, scale_index, attempts, successes,
 +                                window);
 +      if (ret)
 +              return ret;
 +
 +      if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION))
 +              return -EINVAL;
 +
 +      window = &tbl->tpc_win[reduced_txp];
        return _rs_collect_tx_data(tbl, scale_index, attempts, successes,
                                   window);
  }
@@@ -1002,7 -1000,6 +1020,7 @@@ static void rs_tx_status(void *mvm_r, s
        u32 ucode_rate;
        struct rs_rate rate;
        struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl;
 +      u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0];
  
        /* Treat uninitialized rate scaling data same as non-existing. */
        if (!lq_sta) {
                return;
        }
  
+ #ifdef CONFIG_MAC80211_DEBUGFS
+       /* Disable last tx check if we are debugging with fixed rate */
+       if (lq_sta->dbg_fixed_rate) {
+               IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n");
+               return;
+       }
+ #endif
        if (!ieee80211_is_data(hdr->frame_control) ||
            info->flags & IEEE80211_TX_CTL_NO_ACK)
                return;
                        mac_index++;
        }
  
+       if (time_after(jiffies,
+                      (unsigned long)(lq_sta->last_tx + RS_IDLE_TIMEOUT))) {
+               int tid;
+               IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
+               for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
+                       ieee80211_stop_tx_ba_session(sta, tid);
+               iwl_mvm_rs_rate_init(mvm, sta, sband->band, false);
+               return;
+       }
+       lq_sta->last_tx = jiffies;
        /* Here we actually compare this rate to the latest LQ command */
        if ((mac_index < 0) ||
            (rate.sgi != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) ||
        if (info->flags & IEEE80211_TX_STAT_AMPDU) {
                ucode_rate = le32_to_cpu(table->rs_table[0]);
                rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
 -              rs_collect_tx_data(curr_tbl, rate.index,
 +              rs_collect_tx_data(lq_sta, curr_tbl, rate.index,
                                   info->status.ampdu_len,
 -                                 info->status.ampdu_ack_len);
 +                                 info->status.ampdu_ack_len,
 +                                 reduced_txp);
  
                /* Update success/fail counts if not searching for new mode */
                if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) {
                        else
                                continue;
  
 -                      rs_collect_tx_data(tmp_tbl, rate.index, 1,
 -                                         i < retries ? 0 : legacy_success);
 +                      rs_collect_tx_data(lq_sta, tmp_tbl, rate.index, 1,
 +                                         i < retries ? 0 : legacy_success,
 +                                         reduced_txp);
                }
  
                /* Update success/fail counts if not searching for new mode */
        }
        /* The last TX rate is cached in lq_sta; it's set in if/else above */
        lq_sta->last_rate_n_flags = ucode_rate;
 +      IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
  done:
        /* See if there's a better rate or modulation mode to try. */
        if (sta && sta->supp_rates[sband->band])
@@@ -1210,9 -1223,26 +1247,26 @@@ static void rs_set_stay_in_table(struc
        lq_sta->visited_columns = 0;
  }
  
+ static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta,
+                                  const struct rs_tx_column *column)
+ {
+       switch (column->mode) {
+       case RS_LEGACY:
+               return lq_sta->max_legacy_rate_idx;
+       case RS_SISO:
+               return lq_sta->max_siso_rate_idx;
+       case RS_MIMO2:
+               return lq_sta->max_mimo2_rate_idx;
+       default:
+               WARN_ON_ONCE(1);
+       }
+       return lq_sta->max_legacy_rate_idx;
+ }
  static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta,
-                                     const struct rs_tx_column *column,
-                                     u32 bw)
+                                           const struct rs_tx_column *column,
+                                           u32 bw)
  {
        /* Used to choose among HT tables */
        const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT];
@@@ -1462,7 -1492,7 +1516,7 @@@ static void rs_stay_in_table(struct iwl
  
                                IWL_DEBUG_RATE(mvm,
                                               "LQ: stay in table clear win\n");
-                               rs_rate_scale_clear_tbl_windows(tbl);
+                               rs_rate_scale_clear_tbl_windows(mvm, tbl);
                        }
                }
  
                 * bitmaps and stats in active table (this will become the new
                 * "search" table). */
                if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) {
-                       IWL_DEBUG_RATE(mvm, "Clearing up window stats\n");
-                       rs_rate_scale_clear_tbl_windows(tbl);
+                       rs_rate_scale_clear_tbl_windows(mvm, tbl);
                }
        }
  }
@@@ -1509,14 -1538,14 +1562,14 @@@ static enum rs_column rs_get_next_colum
                                         struct ieee80211_sta *sta,
                                         struct iwl_scale_tbl_info *tbl)
  {
-       int i, j, n;
+       int i, j, max_rate;
        enum rs_column next_col_id;
        const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column];
        const struct rs_tx_column *next_col;
        allow_column_func_t allow_func;
        u8 valid_ants = mvm->fw->valid_tx_ant;
        const u16 *expected_tpt_tbl;
-       s32 tpt, max_expected_tpt;
+       u16 tpt, max_expected_tpt;
  
        for (i = 0; i < MAX_NEXT_COLUMNS; i++) {
                next_col_id = curr_col->next_columns[i];
                if (WARN_ON_ONCE(!expected_tpt_tbl))
                        continue;
  
-               max_expected_tpt = 0;
-               for (n = 0; n < IWL_RATE_COUNT; n++)
-                       if (expected_tpt_tbl[n] > max_expected_tpt)
-                               max_expected_tpt = expected_tpt_tbl[n];
+               max_rate = rs_get_max_allowed_rate(lq_sta, next_col);
+               if (WARN_ON_ONCE(max_rate == IWL_RATE_INVALID))
+                       continue;
  
+               max_expected_tpt = expected_tpt_tbl[max_rate];
                if (tpt >= max_expected_tpt) {
                        IWL_DEBUG_RATE(mvm,
                                       "Skip column %d: can't beat current TPT. Max expected %d current %d\n",
                        continue;
                }
  
+               IWL_DEBUG_RATE(mvm,
+                              "Found potential column %d. Max expected %d current %d\n",
+                              next_col_id, max_expected_tpt, tpt);
                break;
        }
  
        if (i == MAX_NEXT_COLUMNS)
                return RS_COLUMN_INVALID;
  
-       IWL_DEBUG_RATE(mvm, "Found potential column %d\n", next_col_id);
        return next_col_id;
  }
  
@@@ -1664,282 -1694,81 +1718,273 @@@ static enum rs_action rs_get_rate_actio
  {
        enum rs_action action = RS_ACTION_STAY;
  
-       /* Too many failures, decrease rate */
        if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
                IWL_DEBUG_RATE(mvm,
-                              "decrease rate because of low SR\n");
-               action = RS_ACTION_DOWNSCALE;
-       /* No throughput measured yet for adjacent rates; try increase. */
-       } else if ((low_tpt == IWL_INVALID_VALUE) &&
-                  (high_tpt == IWL_INVALID_VALUE)) {
-               if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) {
-                       IWL_DEBUG_RATE(mvm,
-                                      "Good SR and no high rate measurement. "
-                                      "Increase rate\n");
-                       action = RS_ACTION_UPSCALE;
-               } else if (low != IWL_RATE_INVALID) {
-                       IWL_DEBUG_RATE(mvm,
-                                      "Remain in current rate\n");
-                       action = RS_ACTION_STAY;
-               }
+                              "Decrease rate because of low SR\n");
+               return RS_ACTION_DOWNSCALE;
        }
  
-       /* Both adjacent throughputs are measured, but neither one has better
-        * throughput; we're using the best rate, don't change it!
-        */
-       else if ((low_tpt != IWL_INVALID_VALUE) &&
-                (high_tpt != IWL_INVALID_VALUE) &&
-                (low_tpt < current_tpt) &&
-                (high_tpt < current_tpt)) {
+       if ((low_tpt == IWL_INVALID_VALUE) &&
+           (high_tpt == IWL_INVALID_VALUE) &&
+           (high != IWL_RATE_INVALID)) {
                IWL_DEBUG_RATE(mvm,
-                              "Both high and low are worse. "
-                              "Maintain rate\n");
-               action = RS_ACTION_STAY;
+                              "No data about high/low rates. Increase rate\n");
+               return RS_ACTION_UPSCALE;
        }
  
-       /* At least one adjacent rate's throughput is measured,
-        * and may have better performance.
-        */
-       else {
-               /* Higher adjacent rate's throughput is measured */
-               if (high_tpt != IWL_INVALID_VALUE) {
-                       /* Higher rate has better throughput */
-                       if (high_tpt > current_tpt &&
-                           sr >= IWL_RATE_INCREASE_TH) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Higher rate is better and good "
-                                              "SR. Increate rate\n");
-                               action = RS_ACTION_UPSCALE;
-                       } else {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Higher rate isn't better OR "
-                                              "no good SR. Maintain rate\n");
-                               action = RS_ACTION_STAY;
-                       }
+       if ((high_tpt == IWL_INVALID_VALUE) &&
+           (high != IWL_RATE_INVALID) &&
+           (low_tpt != IWL_INVALID_VALUE) &&
+           (low_tpt < current_tpt)) {
+               IWL_DEBUG_RATE(mvm,
+                              "No data about high rate and low rate is worse. Increase rate\n");
+               return RS_ACTION_UPSCALE;
+       }
  
-               /* Lower adjacent rate's throughput is measured */
-               } else if (low_tpt != IWL_INVALID_VALUE) {
-                       /* Lower rate has better throughput */
-                       if (low_tpt > current_tpt) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Lower rate is better. "
-                                              "Decrease rate\n");
-                               action = RS_ACTION_DOWNSCALE;
-                       } else if (sr >= IWL_RATE_INCREASE_TH) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Lower rate isn't better and "
-                                              "good SR. Increase rate\n");
-                               action = RS_ACTION_UPSCALE;
-                       }
-               }
+       if ((high_tpt != IWL_INVALID_VALUE) &&
+           (high_tpt > current_tpt)) {
+               IWL_DEBUG_RATE(mvm,
+                              "Higher rate is better. Increate rate\n");
+               return RS_ACTION_UPSCALE;
        }
  
-       /* Sanity check; asked for decrease, but success rate or throughput
-        * has been good at old rate.  Don't change it.
-        */
-       if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID) &&
-           ((sr > IWL_RATE_HIGH_TH) ||
-            (current_tpt > (100 * tbl->expected_tpt[low])))) {
+       if ((low_tpt != IWL_INVALID_VALUE) &&
+           (high_tpt != IWL_INVALID_VALUE) &&
+           (low_tpt < current_tpt) &&
+           (high_tpt < current_tpt)) {
+               IWL_DEBUG_RATE(mvm,
+                              "Both high and low are worse. Maintain rate\n");
+               return RS_ACTION_STAY;
+       }
+       if ((low_tpt != IWL_INVALID_VALUE) &&
+           (low_tpt > current_tpt)) {
+               IWL_DEBUG_RATE(mvm,
+                              "Lower rate is better\n");
+               action = RS_ACTION_DOWNSCALE;
+               goto out;
+       }
+       if ((low_tpt == IWL_INVALID_VALUE) &&
+           (low != IWL_RATE_INVALID)) {
                IWL_DEBUG_RATE(mvm,
-                              "Sanity check failed. Maintain rate\n");
-               action = RS_ACTION_STAY;
+                              "No data about lower rate\n");
+               action = RS_ACTION_DOWNSCALE;
+               goto out;
+       }
+       IWL_DEBUG_RATE(mvm, "Maintain rate\n");
+ out:
+       if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) {
+               if (sr >= RS_SR_NO_DECREASE) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "SR is above NO DECREASE. Avoid downscale\n");
+                       action = RS_ACTION_STAY;
+               } else if (current_tpt > (100 * tbl->expected_tpt[low])) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "Current TPT is higher than max expected in low rate. Avoid downscale\n");
+                       action = RS_ACTION_STAY;
+               } else {
+                       IWL_DEBUG_RATE(mvm, "Decrease rate\n");
+               }
        }
  
        return action;
  }
  
 +static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
 +                              int *weaker, int *stronger)
 +{
 +      *weaker = index + TPC_TX_POWER_STEP;
 +      if (*weaker > TPC_MAX_REDUCTION)
 +              *weaker = TPC_INVALID;
 +
 +      *stronger = index - TPC_TX_POWER_STEP;
 +      if (*stronger < 0)
 +              *stronger = TPC_INVALID;
 +}
 +
 +static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct rs_rate *rate,
 +                         enum ieee80211_band band)
 +{
 +      int index = rate->index;
 +
 +      /*
 +       * allow tpc only if power management is enabled, or bt coex
 +       * activity grade allows it and we are on 2.4Ghz.
 +       */
 +      if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM &&
 +          !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band))
 +              return false;
 +
 +      IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type);
 +      if (is_legacy(rate))
 +              return index == IWL_RATE_54M_INDEX;
 +      if (is_ht(rate))
 +              return index == IWL_RATE_MCS_7_INDEX;
 +      if (is_vht(rate))
 +              return index == IWL_RATE_MCS_7_INDEX ||
 +                     index == IWL_RATE_MCS_8_INDEX ||
 +                     index == IWL_RATE_MCS_9_INDEX;
 +
 +      WARN_ON_ONCE(1);
 +      return false;
 +}
 +
 +enum tpc_action {
 +      TPC_ACTION_STAY,
 +      TPC_ACTION_DECREASE,
 +      TPC_ACTION_INCREASE,
 +      TPC_ACTION_NO_RESTIRCTION,
 +};
 +
 +static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm,
 +                                       s32 sr, int weak, int strong,
 +                                       int current_tpt,
 +                                       int weak_tpt, int strong_tpt)
 +{
 +      /* stay until we have valid tpt */
 +      if (current_tpt == IWL_INVALID_VALUE) {
 +              IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n");
 +              return TPC_ACTION_STAY;
 +      }
 +
 +      /* Too many failures, increase txp */
 +      if (sr <= TPC_SR_FORCE_INCREASE || current_tpt == 0) {
 +              IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n");
 +              return TPC_ACTION_NO_RESTIRCTION;
 +      }
 +
 +      /* try decreasing first if applicable */
 +      if (weak != TPC_INVALID) {
 +              if (weak_tpt == IWL_INVALID_VALUE &&
 +                  (strong_tpt == IWL_INVALID_VALUE ||
 +                   current_tpt >= strong_tpt)) {
 +                      IWL_DEBUG_RATE(mvm,
 +                                     "no weak txp measurement. decrease txp\n");
 +                      return TPC_ACTION_DECREASE;
 +              }
 +
 +              if (weak_tpt > current_tpt) {
 +                      IWL_DEBUG_RATE(mvm,
 +                                     "lower txp has better tpt. decrease txp\n");
 +                      return TPC_ACTION_DECREASE;
 +              }
 +      }
 +
 +      /* next, increase if needed */
 +      if (sr < TPC_SR_NO_INCREASE && strong != TPC_INVALID) {
 +              if (weak_tpt == IWL_INVALID_VALUE &&
 +                  strong_tpt != IWL_INVALID_VALUE &&
 +                  current_tpt < strong_tpt) {
 +                      IWL_DEBUG_RATE(mvm,
 +                                     "higher txp has better tpt. increase txp\n");
 +                      return TPC_ACTION_INCREASE;
 +              }
 +
 +              if (weak_tpt < current_tpt &&
 +                  (strong_tpt == IWL_INVALID_VALUE ||
 +                   strong_tpt > current_tpt)) {
 +                      IWL_DEBUG_RATE(mvm,
 +                                     "lower txp has worse tpt. increase txp\n");
 +                      return TPC_ACTION_INCREASE;
 +              }
 +      }
 +
 +      IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n");
 +      return TPC_ACTION_STAY;
 +}
 +
 +static bool rs_tpc_perform(struct iwl_mvm *mvm,
 +                         struct ieee80211_sta *sta,
 +                         struct iwl_lq_sta *lq_sta,
 +                         struct iwl_scale_tbl_info *tbl)
 +{
 +      struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv;
 +      struct ieee80211_vif *vif = mvm_sta->vif;
 +      struct ieee80211_chanctx_conf *chanctx_conf;
 +      enum ieee80211_band band;
 +      struct iwl_rate_scale_data *window;
 +      struct rs_rate *rate = &tbl->rate;
 +      enum tpc_action action;
 +      s32 sr;
 +      u8 cur = lq_sta->lq.reduced_tpc;
 +      int current_tpt;
 +      int weak, strong;
 +      int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
 +
 +#ifdef CONFIG_MAC80211_DEBUGFS
 +      if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
 +              IWL_DEBUG_RATE(mvm, "fixed tpc: %d",
 +                             lq_sta->dbg_fixed_txp_reduction);
 +              lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction;
 +              return cur != lq_sta->dbg_fixed_txp_reduction;
 +      }
 +#endif
 +
 +      rcu_read_lock();
 +      chanctx_conf = rcu_dereference(vif->chanctx_conf);
 +      if (WARN_ON(!chanctx_conf))
 +              band = IEEE80211_NUM_BANDS;
 +      else
 +              band = chanctx_conf->def.chan->band;
 +      rcu_read_unlock();
 +
 +      if (!rs_tpc_allowed(mvm, rate, band)) {
 +              IWL_DEBUG_RATE(mvm,
 +                             "tpc is not allowed. remove txp restrictions");
 +              lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
 +              return cur != TPC_NO_REDUCTION;
 +      }
 +
 +      rs_get_adjacent_txp(mvm, cur, &weak, &strong);
 +
 +      /* Collect measured throughputs for current and adjacent rates */
 +      window = tbl->tpc_win;
 +      sr = window[cur].success_ratio;
 +      current_tpt = window[cur].average_tpt;
 +      if (weak != TPC_INVALID)
 +              weak_tpt = window[weak].average_tpt;
 +      if (strong != TPC_INVALID)
 +              strong_tpt = window[strong].average_tpt;
 +
 +      IWL_DEBUG_RATE(mvm,
 +                     "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n",
 +                     cur, current_tpt, sr, weak, strong,
 +                     weak_tpt, strong_tpt);
 +
 +      action = rs_get_tpc_action(mvm, sr, weak, strong,
 +                                 current_tpt, weak_tpt, strong_tpt);
 +
 +      /* override actions if we are on the edge */
 +      if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) {
 +              IWL_DEBUG_RATE(mvm, "already in lowest txp, stay");
 +              action = TPC_ACTION_STAY;
 +      } else if (strong == TPC_INVALID &&
 +                 (action == TPC_ACTION_INCREASE ||
 +                  action == TPC_ACTION_NO_RESTIRCTION)) {
 +              IWL_DEBUG_RATE(mvm, "already in highest txp, stay");
 +              action = TPC_ACTION_STAY;
 +      }
 +
 +      switch (action) {
 +      case TPC_ACTION_DECREASE:
 +              lq_sta->lq.reduced_tpc = weak;
 +              return true;
 +      case TPC_ACTION_INCREASE:
 +              lq_sta->lq.reduced_tpc = strong;
 +              return true;
 +      case TPC_ACTION_NO_RESTIRCTION:
 +              lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION;
 +              return true;
 +      case TPC_ACTION_STAY:
 +              /* do nothing */
 +              break;
 +      }
 +      return false;
 +}
 +
  /*
   * Do rate scaling and search for new modulation mode.
   */
@@@ -2008,6 -1837,7 +2053,7 @@@ static void rs_rate_scale_perform(struc
                               "Aggregation changed: prev %d current %d. Update expected TPT table\n",
                               prev_agg, lq_sta->is_agg);
                rs_set_expected_tpt_table(lq_sta, tbl);
+               rs_rate_scale_clear_tbl_windows(mvm, tbl);
        }
  
        /* current tx rate */
                break;
        case RS_ACTION_STAY:
                /* No change */
 +              update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
 +              break;
        default:
                break;
        }
@@@ -2239,7 -2067,7 +2285,7 @@@ lq_update
                if (lq_sta->search_better_tbl) {
                        /* Access the "search" table, clear its history. */
                        tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]);
-                       rs_rate_scale_clear_tbl_windows(tbl);
+                       rs_rate_scale_clear_tbl_windows(mvm, tbl);
  
                        /* Use new "search" start rate */
                        index = tbl->rate.index;
                 * stay with best antenna legacy modulation for a while
                 * before next round of mode comparisons. */
                tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]);
-               if (is_legacy(&tbl1->rate) && !sta->ht_cap.ht_supported) {
+               if (is_legacy(&tbl1->rate)) {
                        IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n");
+                       if (tid != IWL_MAX_TID_COUNT) {
+                               tid_data = &sta_priv->tid_data[tid];
+                               if (tid_data->state != IWL_AGG_OFF) {
+                                       IWL_DEBUG_RATE(mvm,
+                                                      "Stop aggregation on tid %d\n",
+                                                      tid);
+                                       ieee80211_stop_tx_ba_session(sta, tid);
+                               }
+                       }
                        rs_set_stay_in_table(mvm, 1, lq_sta);
                } else {
                /* If we're in an HT mode, and all 3 mode switch actions
@@@ -2560,9 -2398,10 +2616,10 @@@ void iwl_mvm_rs_rate_init(struct iwl_mv
        lq_sta->lq.sta_id = sta_priv->sta_id;
  
        for (j = 0; j < LQ_SIZE; j++)
-               rs_rate_scale_clear_tbl_windows(&lq_sta->lq_info[j]);
+               rs_rate_scale_clear_tbl_windows(mvm, &lq_sta->lq_info[j]);
  
        lq_sta->flush_timer = 0;
+       lq_sta->last_tx = jiffies;
  
        IWL_DEBUG_RATE(mvm,
                       "LQ: *** rate scale station global init for station %d ***\n",
                lq_sta->is_vht = true;
        }
  
-       IWL_DEBUG_RATE(mvm,
-                      "SISO-RATE=%X MIMO2-RATE=%X VHT=%d\n",
+       lq_sta->max_legacy_rate_idx = find_last_bit(&lq_sta->active_legacy_rate,
+                                                   BITS_PER_LONG);
+       lq_sta->max_siso_rate_idx = find_last_bit(&lq_sta->active_siso_rate,
+                                                 BITS_PER_LONG);
+       lq_sta->max_mimo2_rate_idx = find_last_bit(&lq_sta->active_mimo2_rate,
+                                                  BITS_PER_LONG);
+       IWL_DEBUG_RATE(mvm, "RATE MASK: LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d\n",
+                      lq_sta->active_legacy_rate,
                       lq_sta->active_siso_rate,
                       lq_sta->active_mimo2_rate,
                       lq_sta->is_vht);
+       IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n",
+                      lq_sta->max_legacy_rate_idx,
+                      lq_sta->max_siso_rate_idx,
+                      lq_sta->max_mimo2_rate_idx);
  
        /* These values will be overridden later */
        lq_sta->lq.single_stream_ant_msk =
        lq_sta->is_agg = 0;
  #ifdef CONFIG_MAC80211_DEBUGFS
        lq_sta->dbg_fixed_rate = 0;
 +      lq_sta->dbg_fixed_txp_reduction = TPC_INVALID;
  #endif
  #ifdef CONFIG_IWLWIFI_DEBUGFS
        iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats);
@@@ -2766,6 -2615,7 +2834,7 @@@ static void rs_build_rates_table(struc
        if (is_siso(&rate)) {
                num_rates = RS_SECONDARY_SISO_NUM_RATES;
                num_retries = RS_SECONDARY_SISO_RETRIES;
+               lq_cmd->mimo_delim = index;
        } else if (is_legacy(&rate)) {
                num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
                num_retries = RS_LEGACY_RETRIES_PER_RATE;
@@@ -2803,7 -2653,6 +2872,7 @@@ static void rs_fill_lq_cmd(struct iwl_m
                rs_build_rates_table_from_fixed(mvm, lq_cmd,
                                                lq_sta->band,
                                                lq_sta->dbg_fixed_rate);
 +              lq_cmd->reduced_tpc = 0;
                ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
                        RATE_MCS_ANT_POS;
        } else
@@@ -2934,6 -2783,7 +3003,6 @@@ static ssize_t rs_sta_dbgfs_scale_table
        size_t buf_size;
        u32 parsed_rate;
  
 -
        mvm = lq_sta->drv;
        memset(buf, 0, sizeof(buf));
        buf_size = min(count, sizeof(buf) -  1);
@@@ -2968,7 -2818,7 +3037,7 @@@ static ssize_t rs_sta_dbgfs_scale_table
                return -ENOMEM;
  
        desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id);
-       desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n",
+       desc += sprintf(buff+desc, "failed=%d success=%d rate=0%lX\n",
                        lq_sta->total_failed, lq_sta->total_success,
                        lq_sta->active_legacy_rate);
        desc += sprintf(buff+desc, "fixed rate 0x%X\n",
                        lq_sta->lq.agg_disable_start_th,
                        lq_sta->lq.agg_frame_cnt_limit);
  
 +      desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc);
        desc += sprintf(buff+desc,
                        "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n",
                        lq_sta->lq.initial_rate_index[0],
@@@ -3079,94 -2928,6 +3148,94 @@@ static const struct file_operations rs_
        .llseek = default_llseek,
  };
  
 +static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file,
 +                                            char __user *user_buf,
 +                                            size_t count, loff_t *ppos)
 +{
 +      static const char * const column_name[] = {
 +              [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A",
 +              [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B",
 +              [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A",
 +              [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B",
 +              [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI",
 +              [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI",
 +              [RS_COLUMN_MIMO2] = "MIMO2",
 +              [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI",
 +      };
 +
 +      static const char * const rate_name[] = {
 +              [IWL_RATE_1M_INDEX] = "1M",
 +              [IWL_RATE_2M_INDEX] = "2M",
 +              [IWL_RATE_5M_INDEX] = "5.5M",
 +              [IWL_RATE_11M_INDEX] = "11M",
 +              [IWL_RATE_6M_INDEX] = "6M|MCS0",
 +              [IWL_RATE_9M_INDEX] = "9M",
 +              [IWL_RATE_12M_INDEX] = "12M|MCS1",
 +              [IWL_RATE_18M_INDEX] = "18M|MCS2",
 +              [IWL_RATE_24M_INDEX] = "24M|MCS3",
 +              [IWL_RATE_36M_INDEX] = "36M|MCS4",
 +              [IWL_RATE_48M_INDEX] = "48M|MCS5",
 +              [IWL_RATE_54M_INDEX] = "54M|MCS6",
 +              [IWL_RATE_MCS_7_INDEX] = "MCS7",
 +              [IWL_RATE_MCS_8_INDEX] = "MCS8",
 +              [IWL_RATE_MCS_9_INDEX] = "MCS9",
 +      };
 +
 +      char *buff, *pos, *endpos;
 +      int col, rate;
 +      ssize_t ret;
 +      struct iwl_lq_sta *lq_sta = file->private_data;
 +      struct rs_rate_stats *stats;
 +      static const size_t bufsz = 1024;
 +
 +      buff = kmalloc(bufsz, GFP_KERNEL);
 +      if (!buff)
 +              return -ENOMEM;
 +
 +      pos = buff;
 +      endpos = pos + bufsz;
 +
 +      pos += scnprintf(pos, endpos - pos, "COLUMN,");
 +      for (rate = 0; rate < IWL_RATE_COUNT; rate++)
 +              pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]);
 +      pos += scnprintf(pos, endpos - pos, "\n");
 +
 +      for (col = 0; col < RS_COLUMN_COUNT; col++) {
 +              pos += scnprintf(pos, endpos - pos,
 +                               "%s,", column_name[col]);
 +
 +              for (rate = 0; rate < IWL_RATE_COUNT; rate++) {
 +                      stats = &(lq_sta->tx_stats[col][rate]);
 +                      pos += scnprintf(pos, endpos - pos,
 +                                       "%llu/%llu,",
 +                                       stats->success,
 +                                       stats->total);
 +              }
 +              pos += scnprintf(pos, endpos - pos, "\n");
 +      }
 +
 +      ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff);
 +      kfree(buff);
 +      return ret;
 +}
 +
 +static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file,
 +                                             const char __user *user_buf,
 +                                             size_t count, loff_t *ppos)
 +{
 +      struct iwl_lq_sta *lq_sta = file->private_data;
 +      memset(lq_sta->tx_stats, 0, sizeof(lq_sta->tx_stats));
 +
 +      return count;
 +}
 +
 +static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = {
 +      .read = rs_sta_dbgfs_drv_tx_stats_read,
 +      .write = rs_sta_dbgfs_drv_tx_stats_write,
 +      .open = simple_open,
 +      .llseek = default_llseek,
 +};
 +
  static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
  {
        struct iwl_lq_sta *lq_sta = mvm_sta;
        lq_sta->rs_sta_dbgfs_stats_table_file =
                debugfs_create_file("rate_stats_table", S_IRUSR, dir,
                                    lq_sta, &rs_sta_dbgfs_stats_table_ops);
 +      lq_sta->rs_sta_dbgfs_drv_tx_stats_file =
 +              debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
 +                                  lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
        lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
                debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
                                  &lq_sta->tx_agg_tid_en);
 +      lq_sta->rs_sta_dbgfs_reduced_txp_file =
 +              debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
 +                                &lq_sta->dbg_fixed_txp_reduction);
  }
  
  static void rs_remove_debugfs(void *mvm, void *mvm_sta)
        struct iwl_lq_sta *lq_sta = mvm_sta;
        debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
        debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
 +      debugfs_remove(lq_sta->rs_sta_dbgfs_drv_tx_stats_file);
        debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
 +      debugfs_remove(lq_sta->rs_sta_dbgfs_reduced_txp_file);
  }
  #endif
  
index 958c99d7e36eaf7b908bcc97bfa896c55fb10ce3,0acfac96a56c6dca2d2799812231404921abd15b..374a83d7db25a98dd76da34d3fdff9557e48664f
@@@ -156,14 -156,8 +156,15 @@@ enum 
  #define IWL_RATE_HIGH_TH              10880   /*  85% */
  #define IWL_RATE_INCREASE_TH          6400    /*  50% */
  #define RS_SR_FORCE_DECREASE          1920    /*  15% */
+ #define RS_SR_NO_DECREASE             10880   /*  85% */
  
 +#define TPC_SR_FORCE_INCREASE         9600    /* 75% */
 +#define TPC_SR_NO_INCREASE            10880   /* 85% */
 +#define TPC_TX_POWER_STEP             3
 +#define TPC_MAX_REDUCTION             15
 +#define TPC_NO_REDUCTION              0
 +#define TPC_INVALID                   0xff
 +
  #define LINK_QUAL_AGG_TIME_LIMIT_DEF  (4000) /* 4 milliseconds */
  #define LINK_QUAL_AGG_TIME_LIMIT_MAX  (8000)
  #define LINK_QUAL_AGG_TIME_LIMIT_MIN  (100)
@@@ -272,16 -266,9 +273,16 @@@ enum rs_column 
        RS_COLUMN_MIMO2_SGI,
  
        RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI,
 +      RS_COLUMN_COUNT = RS_COLUMN_LAST + 1,
        RS_COLUMN_INVALID,
  };
  
 +/* Packet stats per rate */
 +struct rs_rate_stats {
 +      u64 success;
 +      u64 total;
 +};
 +
  /**
   * struct iwl_scale_tbl_info -- tx params and success history for all rates
   *
@@@ -293,8 -280,6 +294,8 @@@ struct iwl_scale_tbl_info 
        enum rs_column column;
        const u16 *expected_tpt;        /* throughput metrics; expected_tpt_G, etc. */
        struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
 +      /* per txpower-reduction history */
 +      struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1];
  };
  
  enum {
@@@ -326,15 -311,20 +327,22 @@@ struct iwl_lq_sta 
        u32 visited_columns;    /* Bitmask marking which Tx columns were
                                 * explored during a search cycle
                                 */
+       u64 last_tx;
        bool is_vht;
        enum ieee80211_band band;
  
 +      struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT];
 +
        /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
-       u16 active_legacy_rate;
-       u16 active_siso_rate;
-       u16 active_mimo2_rate;
+       unsigned long active_legacy_rate;
+       unsigned long active_siso_rate;
+       unsigned long active_mimo2_rate;
+       /* Highest rate per Tx mode */
+       u8 max_legacy_rate_idx;
+       u8 max_siso_rate_idx;
+       u8 max_mimo2_rate_idx;
        s8 max_rate_idx;     /* Max rate set by user */
        u8 missed_rate_counter;
  
  #ifdef CONFIG_MAC80211_DEBUGFS
        struct dentry *rs_sta_dbgfs_scale_table_file;
        struct dentry *rs_sta_dbgfs_stats_table_file;
 +      struct dentry *rs_sta_dbgfs_drv_tx_stats_file;
        struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
 +      struct dentry *rs_sta_dbgfs_reduced_txp_file;
        u32 dbg_fixed_rate;
 +      u8 dbg_fixed_txp_reduction;
  #endif
        struct iwl_mvm *drv;
  
        u32 last_rate_n_flags;
        /* packets destined for this STA are aggregated */
        u8 is_agg;
 +
 +      /* tx power reduce for this sta */
 +      int tpc_reduce;
  };
  
  /* Initialize station's rate scaling information after adding station */
index 63e7b16edb552be4b626fca9d0b64b064adc6fd2,cba88a379fc8b6111e4edbd516f0080b7c6f0acb..3e25af44afd70a5492aef5031a02bc2fb70c5cb9
@@@ -277,51 -277,22 +277,22 @@@ static void iwl_mvm_scan_calc_params(st
                                            IEEE80211_IFACE_ITER_NORMAL,
                                            iwl_mvm_scan_condition_iterator,
                                            &global_bound);
-       /*
-        * Under low latency traffic passive scan is fragmented meaning
-        * that dwell on a particular channel will be fragmented. Each fragment
-        * dwell time is 20ms and fragments period is 105ms. Skipping to next
-        * channel will be delayed by the same period - 105ms. So suspend_time
-        * parameter describing both fragments and channels skipping periods is
-        * set to 105ms. This value is chosen so that overall passive scan
-        * duration will not be too long. Max_out_time in this case is set to
-        * 70ms, so for active scanning operating channel will be left for 70ms
-        * while for passive still for 20ms (fragment dwell).
-        */
-       if (global_bound) {
-               if (!iwl_mvm_low_latency(mvm)) {
-                       params->suspend_time = ieee80211_tu_to_usec(100);
-                       params->max_out_time = ieee80211_tu_to_usec(600);
-               } else {
-                       params->suspend_time = ieee80211_tu_to_usec(105);
-                       /* P2P doesn't support fragmented passive scan, so
-                        * configure max_out_time to be at least longest dwell
-                        * time for passive scan.
-                        */
-                       if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p) {
-                               params->max_out_time = ieee80211_tu_to_usec(70);
-                               params->passive_fragmented = true;
-                       } else {
-                               u32 passive_dwell;
-                               /*
-                                * Use band G so that passive channel dwell time
-                                * will be assigned with maximum value.
-                                */
-                               band = IEEE80211_BAND_2GHZ;
-                               passive_dwell = iwl_mvm_get_passive_dwell(band);
-                               params->max_out_time =
-                                       ieee80211_tu_to_usec(passive_dwell);
-                       }
-               }
+       if (!global_bound)
+               goto not_bound;
+       params->suspend_time = 100;
+       params->max_out_time = 600;
+       if (iwl_mvm_low_latency(mvm)) {
+               params->suspend_time = 250;
+               params->max_out_time = 250;
        }
  
+ not_bound:
        for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
-               if (params->passive_fragmented)
-                       params->dwell[band].passive = 20;
-               else
-                       params->dwell[band].passive =
-                               iwl_mvm_get_passive_dwell(band);
+               params->dwell[band].passive = iwl_mvm_get_passive_dwell(band);
                params->dwell[band].active = iwl_mvm_get_active_dwell(band,
                                                                      n_ssids);
        }
@@@ -348,10 -319,7 +319,10 @@@ int iwl_mvm_scan_request(struct iwl_mv
        struct iwl_mvm_scan_params params = {};
  
        lockdep_assert_held(&mvm->mutex);
 -      BUG_ON(mvm->scan_cmd == NULL);
 +
 +      /* we should have failed registration if scan_cmd was NULL */
 +      if (WARN_ON(mvm->scan_cmd == NULL))
 +              return -ENOMEM;
  
        IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n");
        mvm->scan_status = IWL_MVM_SCAN_OS;
@@@ -570,13 -538,9 +541,13 @@@ int iwl_mvm_rx_scan_offload_complete_no
        /* scan status must be locked for proper checking */
        lockdep_assert_held(&mvm->mutex);
  
 -      IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n",
 +      IWL_DEBUG_SCAN(mvm,
 +                     "Scheduled scan completed, status %s EBS status %s:%d\n",
                       scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
 -                     "completed" : "aborted");
 +                     "completed" : "aborted", scan_notif->ebs_status ==
 +                     IWL_SCAN_EBS_SUCCESS ? "success" : "failed",
 +                     scan_notif->ebs_status);
 +
  
        /* only call mac80211 completion if the stop was initiated by FW */
        if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
                ieee80211_sched_scan_stopped(mvm->hw);
        }
  
 +      mvm->last_ebs_successful = !scan_notif->ebs_status;
 +
        return 0;
  }
  
@@@ -922,11 -884,6 +893,11 @@@ int iwl_mvm_sched_scan_start(struct iwl
                scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL);
        }
  
 +      if (mvm->last_ebs_successful &&
 +          mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT)
 +              scan_req.flags |=
 +                      cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE);
 +
        return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC,
                                    sizeof(scan_req), &scan_req);
  }
index aa819e613650e2f2833714a2e0d2143c9f88ec2b,88809b2d165445fcf9188c8f91bcf755a9e6704f..7edfd15efc9d001f227ea2c35b046c0f47cb55af
@@@ -237,6 -237,9 +237,6 @@@ int iwl_mvm_sf_update(struct iwl_mvm *m
                .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
        };
  
 -      if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
 -              return 0;
 -
        /*
         * Ignore the call if we are in HW Restart flow, or if the handled
         * vif is a p2p device.
                                return -EINVAL;
                        if (changed_vif->type != NL80211_IFTYPE_STATION) {
                                new_state = SF_UNINIT;
-                       } else if (changed_vif->bss_conf.assoc) {
+                       } else if (changed_vif->bss_conf.assoc &&
+                                  changed_vif->bss_conf.dtim_period) {
                                mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
                                sta_id = mvmvif->ap_sta_id;
                                new_state = SF_FULL_ON;