]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/net/wireless/mediatek/mt76/mt7615/mac.c
mt76: mt7615: fix PS buffering of action frames
[linux.git] / drivers / net / wireless / mediatek / mt76 / mt7615 / mac.c
index 1eb0e9c9970ce32a574836cbd9d4d383a5e052ee..fc98dabed594ff866256b629b73da4c0829e67b2 100644 (file)
@@ -248,12 +248,13 @@ void mt7615_tx_complete_skb(struct mt76_dev *mdev, enum mt76_txq_id qid,
                mt76_tx_complete_skb(mdev, e->skb);
 }
 
-u16 mt7615_mac_tx_rate_val(struct mt7615_dev *dev,
-                          const struct ieee80211_tx_rate *rate,
-                          bool stbc, u8 *bw)
+static u16
+mt7615_mac_tx_rate_val(struct mt7615_dev *dev,
+                      const struct ieee80211_tx_rate *rate,
+                      bool stbc, u8 *bw)
 {
        u8 phy, nss, rate_idx;
-       u16 rateval;
+       u16 rateval = 0;
 
        *bw = 0;
 
@@ -291,12 +292,14 @@ u16 mt7615_mac_tx_rate_val(struct mt7615_dev *dev,
                rate_idx = val & 0xff;
        }
 
-       rateval = (FIELD_PREP(MT_TX_RATE_IDX, rate_idx) |
-                  FIELD_PREP(MT_TX_RATE_MODE, phy) |
-                  FIELD_PREP(MT_TX_RATE_NSS, nss - 1));
-
-       if (stbc && nss == 1)
+       if (stbc && nss == 1) {
+               nss++;
                rateval |= MT_TX_RATE_STBC;
+       }
+
+       rateval |= (FIELD_PREP(MT_TX_RATE_IDX, rate_idx) |
+                   FIELD_PREP(MT_TX_RATE_MODE, phy) |
+                   FIELD_PREP(MT_TX_RATE_NSS, nss - 1));
 
        return rateval;
 }
@@ -331,7 +334,7 @@ int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi,
        fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2;
        fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4;
 
-       if (ieee80211_is_data(fc)) {
+       if (ieee80211_is_data(fc) || ieee80211_is_bufferable_mmpdu(fc)) {
                q_idx = skb_get_queue_mapping(skb);
                p_fmt = MT_TX_TYPE_CT;
        } else if (ieee80211_is_beacon(fc)) {
@@ -446,6 +449,140 @@ void mt7615_txp_skb_unmap(struct mt76_dev *dev,
                                 le16_to_cpu(txp->len[i]), DMA_TO_DEVICE);
 }
 
+void mt7615_mac_set_rates(struct mt7615_dev *dev, struct mt7615_sta *sta,
+                         struct ieee80211_tx_rate *probe_rate,
+                         struct ieee80211_tx_rate *rates)
+{
+       struct ieee80211_tx_rate *ref;
+       int wcid = sta->wcid.idx;
+       u32 addr = MT_WTBL_BASE + wcid * MT_WTBL_ENTRY_SIZE;
+       bool stbc = false;
+       int n_rates = sta->n_rates;
+       u8 bw, bw_prev, bw_idx = 0;
+       u16 val[4];
+       u16 probe_val;
+       u32 w5, w27;
+       bool rateset;
+       int i, k;
+
+       if (!mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000))
+               return;
+
+       for (i = n_rates; i < 4; i++)
+               rates[i] = rates[n_rates - 1];
+
+       rateset = !(sta->rate_set_tsf & BIT(0));
+       memcpy(sta->rateset[rateset].rates, rates,
+              sizeof(sta->rateset[rateset].rates));
+       if (probe_rate) {
+               sta->rateset[rateset].probe_rate = *probe_rate;
+               ref = &sta->rateset[rateset].probe_rate;
+       } else {
+               sta->rateset[rateset].probe_rate.idx = -1;
+               ref = &sta->rateset[rateset].rates[0];
+       }
+
+       rates = sta->rateset[rateset].rates;
+       for (i = 0; i < ARRAY_SIZE(sta->rateset[rateset].rates); i++) {
+               /*
+                * We don't support switching between short and long GI
+                * within the rate set. For accurate tx status reporting, we
+                * need to make sure that flags match.
+                * For improved performance, avoid duplicate entries by
+                * decrementing the MCS index if necessary
+                */
+               if ((ref->flags ^ rates[i].flags) & IEEE80211_TX_RC_SHORT_GI)
+                       rates[i].flags ^= IEEE80211_TX_RC_SHORT_GI;
+
+               for (k = 0; k < i; k++) {
+                       if (rates[i].idx != rates[k].idx)
+                               continue;
+                       if ((rates[i].flags ^ rates[k].flags) &
+                           (IEEE80211_TX_RC_40_MHZ_WIDTH |
+                            IEEE80211_TX_RC_80_MHZ_WIDTH |
+                            IEEE80211_TX_RC_160_MHZ_WIDTH))
+                               continue;
+
+                       rates[i].idx--;
+               }
+
+       }
+
+       val[0] = mt7615_mac_tx_rate_val(dev, &rates[0], stbc, &bw);
+       bw_prev = bw;
+
+       if (probe_rate) {
+               probe_val = mt7615_mac_tx_rate_val(dev, probe_rate, stbc, &bw);
+               if (bw)
+                       bw_idx = 1;
+               else
+                       bw_prev = 0;
+       } else {
+               probe_val = val[0];
+       }
+
+       val[1] = mt7615_mac_tx_rate_val(dev, &rates[1], stbc, &bw);
+       if (bw_prev) {
+               bw_idx = 3;
+               bw_prev = bw;
+       }
+
+       val[2] = mt7615_mac_tx_rate_val(dev, &rates[2], stbc, &bw);
+       if (bw_prev) {
+               bw_idx = 5;
+               bw_prev = bw;
+       }
+
+       val[3] = mt7615_mac_tx_rate_val(dev, &rates[3], stbc, &bw);
+       if (bw_prev)
+               bw_idx = 7;
+
+       w27 = mt76_rr(dev, addr + 27 * 4);
+       w27 &= ~MT_WTBL_W27_CC_BW_SEL;
+       w27 |= FIELD_PREP(MT_WTBL_W27_CC_BW_SEL, bw);
+
+       w5 = mt76_rr(dev, addr + 5 * 4);
+       w5 &= ~(MT_WTBL_W5_BW_CAP | MT_WTBL_W5_CHANGE_BW_RATE |
+               MT_WTBL_W5_MPDU_OK_COUNT |
+               MT_WTBL_W5_MPDU_FAIL_COUNT |
+               MT_WTBL_W5_RATE_IDX);
+       w5 |= FIELD_PREP(MT_WTBL_W5_BW_CAP, bw) |
+             FIELD_PREP(MT_WTBL_W5_CHANGE_BW_RATE, bw_idx ? bw_idx - 1 : 7);
+
+       mt76_wr(dev, MT_WTBL_RIUCR0, w5);
+
+       mt76_wr(dev, MT_WTBL_RIUCR1,
+               FIELD_PREP(MT_WTBL_RIUCR1_RATE0, probe_val) |
+               FIELD_PREP(MT_WTBL_RIUCR1_RATE1, val[0]) |
+               FIELD_PREP(MT_WTBL_RIUCR1_RATE2_LO, val[1]));
+
+       mt76_wr(dev, MT_WTBL_RIUCR2,
+               FIELD_PREP(MT_WTBL_RIUCR2_RATE2_HI, val[1] >> 8) |
+               FIELD_PREP(MT_WTBL_RIUCR2_RATE3, val[1]) |
+               FIELD_PREP(MT_WTBL_RIUCR2_RATE4, val[2]) |
+               FIELD_PREP(MT_WTBL_RIUCR2_RATE5_LO, val[2]));
+
+       mt76_wr(dev, MT_WTBL_RIUCR3,
+               FIELD_PREP(MT_WTBL_RIUCR3_RATE5_HI, val[2] >> 4) |
+               FIELD_PREP(MT_WTBL_RIUCR3_RATE6, val[3]) |
+               FIELD_PREP(MT_WTBL_RIUCR3_RATE7, val[3]));
+
+       mt76_wr(dev, MT_WTBL_UPDATE,
+               FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, wcid) |
+               MT_WTBL_UPDATE_RATE_UPDATE |
+               MT_WTBL_UPDATE_TX_COUNT_CLEAR);
+
+       mt76_wr(dev, addr + 27 * 4, w27);
+
+       mt76_set(dev, MT_LPON_T0CR, MT_LPON_T0CR_MODE); /* TSF read */
+       sta->rate_set_tsf = (mt76_rr(dev, MT_LPON_UTTR0) & ~BIT(0)) | rateset;
+
+       if (!(sta->wcid.tx_info & MT_WCID_TX_INFO_SET))
+               mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000);
+
+       sta->rate_count = 2 * MT7615_RATE_RETRY * n_rates;
+       sta->wcid.tx_info |= MT_WCID_TX_INFO_SET;
+}
 int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
                          enum mt76_txq_id qid, struct mt76_wcid *wcid,
                          struct ieee80211_sta *sta,
@@ -469,9 +606,9 @@ int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
 
        if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) {
                spin_lock_bh(&dev->mt76.lock);
-               msta->rate_probe = true;
-               mt7615_mcu_set_rates(dev, msta, &info->control.rates[0],
+               mt7615_mac_set_rates(dev, msta, &info->control.rates[0],
                                     msta->rates);
+               msta->rate_probe = true;
                spin_unlock_bh(&dev->mt76.lock);
        }
 
@@ -523,9 +660,13 @@ static bool mt7615_fill_txs(struct mt7615_dev *dev, struct mt7615_sta *sta,
                            struct ieee80211_tx_info *info, __le32 *txs_data)
 {
        struct ieee80211_supported_band *sband;
-       int i, idx, count, final_idx = 0;
+       struct mt7615_rate_set *rs;
+       int first_idx = 0, last_idx;
+       int i, idx, count;
        bool fixed_rate, ack_timeout;
        bool probe, ampdu, cck = false;
+       bool rs_idx;
+       u32 rate_set_tsf;
        u32 final_rate, final_rate_flags, final_nss, txs;
 
        fixed_rate = info->status.rates[0].count;
@@ -536,6 +677,7 @@ static bool mt7615_fill_txs(struct mt7615_dev *dev, struct mt7615_sta *sta,
 
        txs = le32_to_cpu(txs_data[3]);
        count = FIELD_GET(MT_TXS3_TX_COUNT, txs);
+       last_idx = FIELD_GET(MT_TXS3_LAST_TX_RATE, txs);
 
        txs = le32_to_cpu(txs_data[0]);
        final_rate = FIELD_GET(MT_TXS0_TX_RATE, txs);
@@ -557,38 +699,56 @@ static bool mt7615_fill_txs(struct mt7615_dev *dev, struct mt7615_sta *sta,
        if (ampdu || (info->flags & IEEE80211_TX_CTL_AMPDU))
                info->flags |= IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_CTL_AMPDU;
 
+       first_idx = max_t(int, 0, last_idx - (count + 1) / MT7615_RATE_RETRY);
+
        if (fixed_rate && !probe) {
                info->status.rates[0].count = count;
+               i = 0;
                goto out;
        }
 
-       for (i = 0, idx = 0; i < ARRAY_SIZE(info->status.rates); i++) {
-               int cur_count = min_t(int, count, 2 * MT7615_RATE_RETRY);
+       rate_set_tsf = READ_ONCE(sta->rate_set_tsf);
+       rs_idx = !((u32)(FIELD_GET(MT_TXS4_F0_TIMESTAMP, le32_to_cpu(txs_data[4])) -
+                        rate_set_tsf) < 1000000);
+       rs_idx ^= rate_set_tsf & BIT(0);
+       rs = &sta->rateset[rs_idx];
 
-               if (!i && probe) {
-                       cur_count = 1;
-               } else {
-                       info->status.rates[i] = sta->rates[idx];
-                       idx++;
-               }
+       if (!first_idx && rs->probe_rate.idx >= 0) {
+               info->status.rates[0] = rs->probe_rate;
 
-               if (i && info->status.rates[i].idx < 0) {
-                       info->status.rates[i - 1].count += count;
-                       break;
+               spin_lock_bh(&dev->mt76.lock);
+               if (sta->rate_probe) {
+                       mt7615_mac_set_rates(dev, sta, NULL, sta->rates);
+                       sta->rate_probe = false;
                }
+               spin_unlock_bh(&dev->mt76.lock);
+       } else
+               info->status.rates[0] = rs->rates[first_idx / 2];
+       info->status.rates[0].count = 0;
 
-               if (!count) {
-                       info->status.rates[i].idx = -1;
-                       break;
-               }
+       for (i = 0, idx = first_idx; count && idx <= last_idx; idx++) {
+               struct ieee80211_tx_rate *cur_rate;
+               int cur_count;
 
-               info->status.rates[i].count = cur_count;
-               final_idx = i;
+               cur_rate = &rs->rates[idx / 2];
+               cur_count = min_t(int, MT7615_RATE_RETRY, count);
                count -= cur_count;
+
+               if (idx && (cur_rate->idx != info->status.rates[i].idx ||
+                           cur_rate->flags != info->status.rates[i].flags)) {
+                       i++;
+                       if (i == ARRAY_SIZE(info->status.rates))
+                               break;
+
+                       info->status.rates[i] = *cur_rate;
+                       info->status.rates[i].count = 0;
+               }
+
+               info->status.rates[i].count += cur_count;
        }
 
 out:
-       final_rate_flags = info->status.rates[final_idx].flags;
+       final_rate_flags = info->status.rates[i].flags;
 
        switch (FIELD_GET(MT_TX_RATE_MODE, final_rate)) {
        case MT_PHY_TYPE_CCK:
@@ -613,6 +773,10 @@ static bool mt7615_fill_txs(struct mt7615_dev *dev, struct mt7615_sta *sta,
                break;
        case MT_PHY_TYPE_VHT:
                final_nss = FIELD_GET(MT_TX_RATE_NSS, final_rate);
+
+               if ((final_rate & MT_TX_RATE_STBC) && final_nss)
+                       final_nss--;
+
                final_rate_flags |= IEEE80211_TX_RC_VHT_MCS;
                final_rate = (final_rate & MT_TX_RATE_IDX) | (final_nss << 4);
                break;
@@ -620,8 +784,8 @@ static bool mt7615_fill_txs(struct mt7615_dev *dev, struct mt7615_sta *sta,
                return false;
        }
 
-       info->status.rates[final_idx].idx = final_rate;
-       info->status.rates[final_idx].flags = final_rate_flags;
+       info->status.rates[i].idx = final_rate;
+       info->status.rates[i].flags = final_rate_flags;
 
        return true;
 }
@@ -642,16 +806,6 @@ static bool mt7615_mac_add_txs_skb(struct mt7615_dev *dev,
        if (skb) {
                struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
-               if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) {
-                       spin_lock_bh(&dev->mt76.lock);
-                       if (sta->rate_probe) {
-                               mt7615_mcu_set_rates(dev, sta, NULL,
-                                                    sta->rates);
-                               sta->rate_probe = false;
-                       }
-                       spin_unlock_bh(&dev->mt76.lock);
-               }
-
                if (!mt7615_fill_txs(dev, sta, info, txs_data)) {
                        ieee80211_tx_info_clear_status(info);
                        info->status.rates[0].idx = -1;
@@ -746,3 +900,91 @@ void mt7615_mac_work(struct work_struct *work)
        ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mt76.mac_work,
                                     MT7615_WATCHDOG_TIME);
 }
+
+int mt7615_dfs_stop_radar_detector(struct mt7615_dev *dev)
+{
+       struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
+       int err;
+
+       err = mt7615_mcu_rdd_cmd(dev, RDD_STOP, MT_HW_RDD0,
+                                MT_RX_SEL0, 0);
+       if (err < 0)
+               return err;
+
+       if (chandef->width == NL80211_CHAN_WIDTH_160 ||
+           chandef->width == NL80211_CHAN_WIDTH_80P80)
+               err = mt7615_mcu_rdd_cmd(dev, RDD_STOP, MT_HW_RDD1,
+                                        MT_RX_SEL0, 0);
+       return err;
+}
+
+static int mt7615_dfs_start_rdd(struct mt7615_dev *dev, int chain)
+{
+       int err;
+
+       err = mt7615_mcu_rdd_cmd(dev, RDD_START, chain, MT_RX_SEL0, 0);
+       if (err < 0)
+               return err;
+
+       return mt7615_mcu_rdd_cmd(dev, RDD_DET_MODE, chain,
+                                 MT_RX_SEL0, 1);
+}
+
+int mt7615_dfs_start_radar_detector(struct mt7615_dev *dev)
+{
+       struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
+       int err;
+
+       /* start CAC */
+       err = mt7615_mcu_rdd_cmd(dev, RDD_CAC_START, MT_HW_RDD0,
+                                MT_RX_SEL0, 0);
+       if (err < 0)
+               return err;
+
+       /* TODO: DBDC support */
+
+       err = mt7615_dfs_start_rdd(dev, MT_HW_RDD0);
+       if (err < 0)
+               return err;
+
+       if (chandef->width == NL80211_CHAN_WIDTH_160 ||
+           chandef->width == NL80211_CHAN_WIDTH_80P80) {
+               err = mt7615_dfs_start_rdd(dev, MT_HW_RDD1);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+int mt7615_dfs_init_radar_detector(struct mt7615_dev *dev)
+{
+       struct cfg80211_chan_def *chandef = &dev->mt76.chandef;
+       int err;
+
+       if (dev->mt76.region == NL80211_DFS_UNSET)
+               return 0;
+
+       if (test_bit(MT76_SCANNING, &dev->mt76.state))
+               return 0;
+
+       if (dev->dfs_state == chandef->chan->dfs_state)
+               return 0;
+
+       dev->dfs_state = chandef->chan->dfs_state;
+
+       if (chandef->chan->flags & IEEE80211_CHAN_RADAR) {
+               if (chandef->chan->dfs_state != NL80211_DFS_AVAILABLE)
+                       return mt7615_dfs_start_radar_detector(dev);
+               else
+                       return mt7615_mcu_rdd_cmd(dev, RDD_CAC_END, MT_HW_RDD0,
+                                                 MT_RX_SEL0, 0);
+       } else {
+               err = mt7615_mcu_rdd_cmd(dev, RDD_NORMAL_START,
+                                        MT_HW_RDD0, MT_RX_SEL0, 0);
+               if (err < 0)
+                       return err;
+
+               return mt7615_dfs_stop_radar_detector(dev);
+       }
+}