iwlwifi: mvm: add ATPC implementation
[pandora-kernel.git] / drivers / net / wireless / iwlwifi / mvm / rs.c
index 568abd6..fd090bc 100644 (file)
@@ -509,6 +509,9 @@ static void rs_rate_scale_clear_tbl_windows(struct iwl_scale_tbl_info *tbl)
 
        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)
@@ -639,9 +642,11 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
 }
 
 static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
-                             int scale_index, int attempts, int successes)
+                             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;
@@ -649,6 +654,15 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
        /* 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);
 }
@@ -982,6 +996,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        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) {
@@ -1106,7 +1121,8 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
                rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
                rs_collect_tx_data(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) {
@@ -1140,7 +1156,8 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
                                continue;
 
                        rs_collect_tx_data(tmp_tbl, rate.index, 1,
-                                          i < retries ? 0 : legacy_success);
+                                          i < retries ? 0 : legacy_success,
+                                          reduced_txp);
                }
 
                /* Update success/fail counts if not searching for new mode */
@@ -1151,6 +1168,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
        }
        /* 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])
@@ -1724,6 +1742,189 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
        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;
+
+       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.
  */
@@ -1973,6 +2174,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                break;
        case RS_ACTION_STAY:
                /* No change */
+               update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl);
+               break;
        default:
                break;
        }
@@ -2584,6 +2787,7 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
                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
@@ -2787,6 +2991,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
                        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],