]> Git Repo - linux.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwif...
authorJohn W. Linville <[email protected]>
Tue, 22 Apr 2014 19:01:24 +0000 (15:01 -0400)
committerJohn W. Linville <[email protected]>
Tue, 22 Apr 2014 19:01:24 +0000 (15:01 -0400)
1  2 
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/mvm/coex.c
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/sf.c

index 4c2d4ef28b220c719ac49f9f9726b931c2d35442,2c9b6fb456039e3fd93433a8e8f32e391c507879..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,6 -107,7 +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 = {
@@@ -244,4 -245,3 +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 fa858d548d13c0bd794b98dc4da2053893b460dc,09186353784b21b70e0403bc1d3c7812ff5e30f8..8f4b03dbaf3f4ecae21927a0c976d2b1c3852157
@@@ -104,11 -104,8 +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);
@@@ -190,7 -187,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),
        },
  };
  
@@@ -573,8 -570,9 +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;
@@@ -1215,6 -1215,17 +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);
  }
  
@@@ -1262,7 -1270,6 +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 f0cebf12c7b8415a3c787d0cc77a9b2b1c2a15ef,3e8437fadb4b8c002796bf6e945d00e0713b1501..97c3deae655273ff3c5184cd86a4722f34714eae
@@@ -276,6 -276,7 +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)) {
@@@ -827,8 -826,7 +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;
@@@ -1223,6 -1221,10 +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;
  
@@@ -1332,7 -1334,6 +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)) {
@@@ -1697,6 -1698,11 +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 9f52c5b3f0ec0e9b2da5949f2af88bbcd13d89ce,7a332cb9c1ce16bffc00f4515120cfc0e26fa383..d44b2b33b5ccca71b1f1e6ad0ec6ab2573fdd083
@@@ -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,10 -212,8 +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,
@@@ -519,14 -503,15 +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)
@@@ -656,17 -641,34 +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);
  }
@@@ -1000,6 -1002,7 +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 CPTCFG_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])
@@@ -1223,26 -1210,9 +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];
@@@ -1492,7 -1462,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);
                }
        }
  }
@@@ -1538,14 -1509,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;
  }
  
@@@ -1694,81 -1664,282 +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.
   */
@@@ -1837,7 -2008,6 +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;
        }
@@@ -2067,7 -2239,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
@@@ -2398,10 -2560,9 +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);
@@@ -2615,7 -2766,6 +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;
@@@ -2653,6 -2803,7 +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
@@@ -2783,7 -2934,6 +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);
@@@ -2818,7 -2968,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],
@@@ -2928,6 -3079,94 +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 0acfac96a56c6dca2d2799812231404921abd15b,958c99d7e36eaf7b908bcc97bfa896c55fb10ce3..374a83d7db25a98dd76da34d3fdff9557e48664f
@@@ -156,8 -156,14 +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)
@@@ -266,9 -272,16 +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
   *
@@@ -280,6 -293,8 +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 {
@@@ -311,20 -326,15 +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 88809b2d165445fcf9188c8f91bcf755a9e6704f,aa819e613650e2f2833714a2e0d2143c9f88ec2b..7edfd15efc9d001f227ea2c35b046c0f47cb55af
@@@ -237,9 -237,6 +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;
This page took 0.142087 seconds and 4 git commands to generate.