wifi: mt76: add PPDU based TxS support for WED device
authorRyder Lee <ryder.lee@mediatek.com>
Wed, 10 Aug 2022 16:40:44 +0000 (00:40 +0800)
committerFelix Fietkau <nbd@nbd.name>
Thu, 15 Sep 2022 10:54:01 +0000 (12:54 +0200)
Given that there's no data coming from network stack for binding flows,
hence driver counts and reports station's statistics directly through
NL80211_STA_INFO_* based on active PPDU based TxS for offloading data.

Apart from that, WA firmware and its offloading engine (SDO) have hardcoded
"2" as PID, so we introduce MT_PACKET_ID_WED to differentiate WED reporting.

Note that PPDU format TxS is mutually exclusive with MT_TXD5_TX_STATUS_HOST.

Co-developed-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
Signed-off-by: Yi-Chia Hsieh <yi-chia.hsieh@mediatek.com>
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/mt76_connac.h
drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h
drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
drivers/net/wireless/mediatek/mt76/mt7915/mac.c
drivers/net/wireless/mediatek/mt76/mt7915/main.c
drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
drivers/net/wireless/mediatek/mt76/mt7915/pci.c
drivers/net/wireless/mediatek/mt76/mt7915/regs.h

index 5ece76b6215d17a33a8583815fd23dcb3f7636c0..87db9498dea44a517ad9cfd0c17f7c90303427fb 100644 (file)
@@ -270,6 +270,10 @@ struct mt76_sta_stats {
        u64 tx_bw[4];           /* 20, 40, 80, 160 */
        u64 tx_nss[4];          /* 1, 2, 3, 4 */
        u64 tx_mcs[16];         /* mcs idx */
+       u64 tx_bytes;
+       u32 tx_packets;
+       u32 tx_retries;
+       u32 tx_failed;
 };
 
 enum mt76_wcid_flags {
@@ -364,7 +368,8 @@ struct mt76_rx_tid {
 #define MT_PACKET_ID_MASK              GENMASK(6, 0)
 #define MT_PACKET_ID_NO_ACK            0
 #define MT_PACKET_ID_NO_SKB            1
-#define MT_PACKET_ID_FIRST             2
+#define MT_PACKET_ID_WED               2
+#define MT_PACKET_ID_FIRST             3
 #define MT_PACKET_ID_HAS_RATE          BIT(7)
 /* This is timer for when to give up when waiting for TXS callback,
  * with starting time being the time at which the DMA_DONE callback
index 851874f782c5870d96e82ac51ada9e30b60b10cc..635192c878cb4b9422d42af13145a21f16ce65b7 100644 (file)
@@ -354,6 +354,8 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
                                 struct sk_buff *skb, struct mt76_wcid *wcid,
                                 struct ieee80211_key_conf *key, int pid,
                                 enum mt76_txq_id qid, u32 changed);
+bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
+                              __le32 *txs_data);
 bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
                                  int pid, __le32 *txs_data);
 void mt76_connac2_mac_decode_he_radiotap(struct mt76_dev *dev,
index 67ce216fb564920a5ce7cfe15693c2be50d3057c..f33171bcd343222e31997d7f19e1a851c6196154 100644 (file)
@@ -158,6 +158,14 @@ enum {
 
 #define MT_TXS4_TIMESTAMP              GENMASK(31, 0)
 
+/* PPDU based TXS */
+#define MT_TXS5_MPDU_TX_BYTE           GENMASK(22, 0)
+#define MT_TXS5_MPDU_TX_CNT            GENMASK(31, 23)
+
+#define MT_TXS6_MPDU_FAIL_CNT          GENMASK(31, 23)
+
+#define MT_TXS7_MPDU_RETRY_CNT         GENMASK(31, 23)
+
 /* RXD DW1 */
 #define MT_RXD1_NORMAL_WLAN_IDX                GENMASK(9, 0)
 #define MT_RXD1_NORMAL_GROUP_1         BIT(11)
index d0a94cb6d08b157c9bef954d4a8382da4de0b68b..34ac3d81a510256f055cff164393afe32b808c3a 100644 (file)
@@ -490,6 +490,10 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
                p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT : MT_TX_TYPE_SF;
                q_idx = wmm_idx * MT76_CONNAC_MAX_WMM_SETS +
                        mt76_connac_lmac_mapping(skb_get_queue_mapping(skb));
+
+               /* counting non-offloading skbs */
+               wcid->stats.tx_bytes += skb->len;
+               wcid->stats.tx_packets++;
        }
 
        val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) |
@@ -550,35 +554,29 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
 }
 EXPORT_SYMBOL_GPL(mt76_connac2_mac_write_txwi);
 
-bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
-                                 int pid, __le32 *txs_data)
+bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid,
+                              __le32 *txs_data)
 {
        struct mt76_sta_stats *stats = &wcid->stats;
        struct ieee80211_supported_band *sband;
        struct mt76_phy *mphy;
-       struct ieee80211_tx_info *info;
-       struct sk_buff_head list;
        struct rate_info rate = {};
-       struct sk_buff *skb;
        bool cck = false;
        u32 txrate, txs, mode;
 
-       mt76_tx_status_lock(dev, &list);
-       skb = mt76_tx_status_skb_get(dev, wcid, pid, &list);
-       if (!skb)
-               goto out;
-
        txs = le32_to_cpu(txs_data[0]);
 
-       info = IEEE80211_SKB_CB(skb);
-       if (!(txs & MT_TXS0_ACK_ERROR_MASK))
-               info->flags |= IEEE80211_TX_STAT_ACK;
-
-       info->status.ampdu_len = 1;
-       info->status.ampdu_ack_len = !!(info->flags &
-                                       IEEE80211_TX_STAT_ACK);
-
-       info->status.rates[0].idx = -1;
+       /* PPDU based reporting */
+       if (FIELD_GET(MT_TXS0_TXS_FORMAT, txs) > 1) {
+               stats->tx_bytes +=
+                       le32_get_bits(txs_data[5], MT_TXS5_MPDU_TX_BYTE);
+               stats->tx_packets +=
+                       le32_get_bits(txs_data[5], MT_TXS5_MPDU_TX_CNT);
+               stats->tx_failed +=
+                       le32_get_bits(txs_data[6], MT_TXS6_MPDU_FAIL_CNT);
+               stats->tx_retries +=
+                       le32_get_bits(txs_data[7], MT_TXS7_MPDU_RETRY_CNT);
+       }
 
        txrate = FIELD_GET(MT_TXS0_TX_RATE, txs);
 
@@ -613,7 +611,7 @@ bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
        case MT_PHY_TYPE_HT:
        case MT_PHY_TYPE_HT_GF:
                if (rate.mcs > 31)
-                       goto out;
+                       return false;
 
                rate.flags = RATE_INFO_FLAGS_MCS;
                if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI)
@@ -621,7 +619,7 @@ bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
                break;
        case MT_PHY_TYPE_VHT:
                if (rate.mcs > 9)
-                       goto out;
+                       return false;
 
                rate.flags = RATE_INFO_FLAGS_VHT_MCS;
                break;
@@ -630,14 +628,14 @@ bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
        case MT_PHY_TYPE_HE_TB:
        case MT_PHY_TYPE_HE_MU:
                if (rate.mcs > 11)
-                       goto out;
+                       return false;
 
                rate.he_gi = wcid->rate.he_gi;
                rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate);
                rate.flags = RATE_INFO_FLAGS_HE_MCS;
                break;
        default:
-               goto out;
+               return false;
        }
 
        stats->tx_mode[mode]++;
@@ -662,10 +660,34 @@ bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
        }
        wcid->rate = rate;
 
-out:
-       if (skb)
-               mt76_tx_status_skb_done(dev, skb, &list);
+       return true;
+}
+EXPORT_SYMBOL_GPL(mt76_connac2_mac_fill_txs);
+
+bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid,
+                                 int pid, __le32 *txs_data)
+{
+       struct sk_buff_head list;
+       struct sk_buff *skb;
 
+       mt76_tx_status_lock(dev, &list);
+       skb = mt76_tx_status_skb_get(dev, wcid, pid, &list);
+       if (skb) {
+               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+               bool noacked = !(info->flags & IEEE80211_TX_STAT_ACK);
+
+               if (!(le32_to_cpu(txs_data[0]) & MT_TXS0_ACK_ERROR_MASK))
+                       info->flags |= IEEE80211_TX_STAT_ACK;
+
+               info->status.ampdu_len = 1;
+               info->status.ampdu_ack_len = !noacked;
+               info->status.rates[0].idx = -1;
+
+               wcid->stats.tx_failed += noacked;
+
+               mt76_connac2_mac_fill_txs(dev, wcid, txs_data);
+               mt76_tx_status_skb_done(dev, skb, &list);
+       }
        mt76_tx_status_unlock(dev, &list);
 
        return !!skb;
index fd99117bcb6d5c4da272105646027fa36bfa5b98..be97dede2634dd0b6127b43dac1347549764db8e 100644 (file)
@@ -176,7 +176,7 @@ static void mt7915_mac_sta_poll(struct mt7915_dev *dev)
                /*
                 * We don't support reading GI info from txs packets.
                 * For accurate tx status reporting and AQL improvement,
-                 we need to make sure that flags match so polling GI
+                * we need to make sure that flags match so polling GI
                 * from per-sta counters directly.
                 */
                rate = &msta->wcid.rate;
@@ -1001,7 +1001,7 @@ static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data)
        wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
        pid = le32_get_bits(txs_data[3], MT_TXS3_PID);
 
-       if (pid < MT_PACKET_ID_FIRST)
+       if (pid < MT_PACKET_ID_WED)
                return;
 
        if (wcidx >= mt7915_wtbl_size(dev))
@@ -1015,7 +1015,11 @@ static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data)
 
        msta = container_of(wcid, struct mt7915_sta, wcid);
 
-       mt76_connac2_mac_add_txs_skb(&dev->mt76, wcid, pid, txs_data);
+       if (pid == MT_PACKET_ID_WED)
+               mt76_connac2_mac_fill_txs(&dev->mt76, wcid, txs_data);
+       else
+               mt76_connac2_mac_add_txs_skb(&dev->mt76, wcid, pid, txs_data);
+
        if (!wcid->sta)
                goto out;
 
@@ -1046,7 +1050,7 @@ bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len)
                return false;
        case PKT_TYPE_TXS:
                for (rxd += 2; rxd + 8 <= end; rxd += 8)
-                   mt7915_mac_add_txs(dev, rxd);
+                       mt7915_mac_add_txs(dev, rxd);
                return false;
        case PKT_TYPE_RX_FW_MONITOR:
                mt7915_debugfs_rx_fw_monitor(dev, data, len);
@@ -1083,7 +1087,7 @@ void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
                break;
        case PKT_TYPE_TXS:
                for (rxd += 2; rxd + 8 <= end; rxd += 8)
-                   mt7915_mac_add_txs(dev, rxd);
+                       mt7915_mac_add_txs(dev, rxd);
                dev_kfree_skb(skb);
                break;
        case PKT_TYPE_RX_FW_MONITOR:
index 090c52803052eea7c79e5747868b772a4c2bcb6c..89b519cfd14c3bb272c8f977ba00542a0975c2f3 100644 (file)
@@ -1010,6 +1010,23 @@ static void mt7915_sta_statistics(struct ieee80211_hw *hw,
        }
        sinfo->txrate.flags = txrate->flags;
        sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
+
+       /* offloading flows bypass networking stack, so driver counts and
+        * reports sta statistics via NL80211_STA_INFO when WED is active.
+        */
+       if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) {
+               sinfo->tx_bytes = msta->wcid.stats.tx_bytes;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64);
+
+               sinfo->tx_packets = msta->wcid.stats.tx_packets;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
+
+               sinfo->tx_failed = msta->wcid.stats.tx_failed;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+
+               sinfo->tx_retries = msta->wcid.stats.tx_retries;
+               sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES);
+       }
 }
 
 static void mt7915_sta_rc_work(void *data, struct ieee80211_sta *sta)
index c1256defbea3e11fed88d6c11721799f81024acf..7bd5f6725d7b7b6de9874b1c6f53acd08d4e8592 100644 (file)
@@ -75,6 +75,7 @@ static const u32 mt7915_offs[] = {
        [AGG_AWSCR0]            = 0x05c,
        [AGG_PCR0]              = 0x06c,
        [AGG_ACR0]              = 0x084,
+       [AGG_ACR4]              = 0x08c,
        [AGG_MRCR]              = 0x098,
        [AGG_ATCR1]             = 0x0f0,
        [AGG_ATCR3]             = 0x0f4,
@@ -148,6 +149,7 @@ static const u32 mt7916_offs[] = {
        [AGG_AWSCR0]            = 0x030,
        [AGG_PCR0]              = 0x040,
        [AGG_ACR0]              = 0x054,
+       [AGG_ACR4]              = 0x05c,
        [AGG_MRCR]              = 0x068,
        [AGG_ATCR1]             = 0x1a8,
        [AGG_ATCR3]             = 0x080,
index d74f609775d3bbb23eecb8d04b294bf5eb1d323e..728a879c3b008fd6e56500e56efeb054d7b40052 100644 (file)
@@ -99,6 +99,7 @@ static int mt7915_pci_hif2_probe(struct pci_dev *pdev)
 static int mt7915_wed_offload_enable(struct mtk_wed_device *wed)
 {
        struct mt7915_dev *dev;
+       struct mt7915_phy *phy;
        int ret;
 
        dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
@@ -112,18 +113,38 @@ static int mt7915_wed_offload_enable(struct mtk_wed_device *wed)
        if (!ret)
                return -EAGAIN;
 
+       phy = &dev->phy;
+       mt76_set(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H);
+
+       phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL;
+       if (phy)
+               mt76_set(dev, MT_AGG_ACR4(phy->band_idx),
+                        MT_AGG_ACR_PPDU_TXS2H);
+
        return 0;
 }
 
 static void mt7915_wed_offload_disable(struct mtk_wed_device *wed)
 {
        struct mt7915_dev *dev;
+       struct mt7915_phy *phy;
 
        dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed);
 
        spin_lock_bh(&dev->mt76.token_lock);
        dev->mt76.token_size = MT7915_TOKEN_SIZE;
        spin_unlock_bh(&dev->mt76.token_lock);
+
+       /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than
+        * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set.
+        */
+       phy = &dev->phy;
+       mt76_clear(dev, MT_AGG_ACR4(phy->band_idx), MT_AGG_ACR_PPDU_TXS2H);
+
+       phy = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL;
+       if (phy)
+               mt76_clear(dev, MT_AGG_ACR4(phy->band_idx),
+                          MT_AGG_ACR_PPDU_TXS2H);
 }
 #endif
 
index 53061aa727e9353f89fb55cca499727e3ad80e0b..5920e705835a7b4ba0b9d399adf31518ffdec5e1 100644 (file)
@@ -46,6 +46,7 @@ enum offs_rev {
        AGG_AWSCR0,
        AGG_PCR0,
        AGG_ACR0,
+       AGG_ACR4,
        AGG_MRCR,
        AGG_ATCR1,
        AGG_ATCR3,
@@ -465,6 +466,9 @@ enum offs_rev {
 #define MT_AGG_ACR_CFEND_RATE          GENMASK(13, 0)
 #define MT_AGG_ACR_BAR_RATE            GENMASK(29, 16)
 
+#define MT_AGG_ACR4(_band)             MT_WF_AGG(_band, __OFFS(AGG_ACR4))
+#define MT_AGG_ACR_PPDU_TXS2H          BIT(1)
+
 #define MT_AGG_MRCR(_band)             MT_WF_AGG(_band, __OFFS(AGG_MRCR))
 #define MT_AGG_MRCR_BAR_CNT_LIMIT              GENMASK(15, 12)
 #define MT_AGG_MRCR_LAST_RTS_CTS_RN            BIT(6)