wifi: mt76: mt7925: add MT7928 TXD/TXS/TX_DONE support

Add MT7928 TXD v2 fields, per-chip WTBL register addresses, UNI
TxDone event parsing, and TXS format acceptance for MPDU/PPDU.
Suppress HW AMSDU on management frames for MT7928.

Signed-off-by: Xiong <xiong.huang@mediatek.com>
Signed-off-by: Emery Hsin <emery.hsin@mediatek.com>
Link: https://patch.msgid.link/20260612075339.2578327-1-jb.tsai@mediatek.com
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Emery Hsin 2026-06-12 15:53:35 +08:00 committed by Felix Fietkau
parent a01a222ab4
commit 5e738c59e0
7 changed files with 292 additions and 12 deletions

View File

@ -301,6 +301,7 @@ static inline bool is_mt76_fw_txp(struct mt76_dev *dev)
case 0x7902:
case 0x7925:
case 0x7927:
case 0x7928:
case 0x7663:
case 0x7622:
return false;

View File

@ -18,6 +18,11 @@ enum {
MT_LMAC_PSMP0,
};
enum {
TXS_FM_MPDU = 0,
TXS_FM_PPDU = 2,
};
#define MT_CT_PARSE_LEN 72
#define MT_CT_DMA_BUF_NUM 2
@ -236,7 +241,9 @@ enum tx_frag_idx {
#define MT_TXD2_HDR_PAD GENMASK(11, 10)
#define MT_TXD2_RTS BIT(9)
#define MT_TXD2_OWN_MAC_MAP BIT(8)
#define MT_TXD2_OWN_MAC_MAP_V2 BIT(9)
#define MT_TXD2_BF_TYPE GENMASK(6, 7)
#define MT_TXD2_BF_TYPE_V2 GENMASK(6, 8)
#define MT_TXD2_FRAME_TYPE GENMASK(5, 4)
#define MT_TXD2_SUB_TYPE GENMASK(3, 0)
@ -261,6 +268,7 @@ enum tx_frag_idx {
#define MT_TXD5_BYPASS_TBB BIT(14)
#define MT_TXD5_BYPASS_RBB BIT(13)
#define MT_TXD5_BSS_COLOR_ZERO BIT(12)
#define MT_TXD5_OCUP_BY_OTHER_LNK BIT(11)
#define MT_TXD5_TX_STATUS_HOST BIT(10)
#define MT_TXD5_TX_STATUS_MCU BIT(9)
#define MT_TXD5_TX_STATUS_FMT BIT(8)
@ -270,15 +278,19 @@ enum tx_frag_idx {
#define MT_TXD6_VTA BIT(28)
#define MT_TXD6_FIXED_BW BIT(25)
#define MT_TXD6_BW GENMASK(24, 22)
#define MT_TXD6_BW_V2 GENMASK(25, 22)
#define MT_TXD6_TX_RATE GENMASK(21, 16)
#define MT_TXD6_TIMESTAMP_OFS_EN BIT(15)
#define MT_TXD6_TIMESTAMP_OFS_EN_V2 GENMASK(15, 13)
#define MT_TXD6_TIMESTAMP_OFS_IDX GENMASK(14, 10)
#define MT_TXD6_TIMESTAMP_OFS_IDX_V2 GENMASK(12, 8)
#define MT_TXD6_TID_ADDBA GENMASK(10, 8)
#define MT_TXD6_MSDU_CNT GENMASK(9, 4)
#define MT_TXD6_MSDU_CNT_V2 GENMASK(15, 10)
#define MT_TXD6_DIS_MAT BIT(3)
#define MT_TXD6_DAS BIT(2)
#define MT_TXD6_AMSDU_CAP BIT(1)
#define MT_TXD6_MLD BIT(0)
#define MT_TXD7_TXD_LEN GENMASK(31, 30)
#define MT_TXD7_IP_SUM BIT(29)
@ -287,6 +299,9 @@ enum tx_frag_idx {
#define MT_TXD7_CTXD BIT(26)
#define MT_TXD7_CTXD_CNT GENMASK(25, 22)
#define MT_TXD7_UDP_TCP_SUM BIT(15)
#define MT_TXD7_IMMEDIATE_TX BIT(14)
#define MT_TXD7_FORCE_RTS_CTS BIT(13)
#define MT_TXD7_ENABLE_ICI BIT(12)
#define MT_TXD7_TX_TIME GENMASK(9, 0)
#define MT_TXD9_WLAN_IDX GENMASK(23, 8)
@ -397,4 +412,31 @@ enum tx_frag_idx {
#define MT_TXS7_MPDU_RETRY_BYTE_SCALE BIT(15)
#define MT_TXS7_MPDU_RETRY_BYTE GENMASK(14, 0)
struct mt7928_uni_txdone_event {
__le16 tag;
__le16 len;
u8 pid; /* HW packet ID */
u8 status; /* TX_RESULT_xx */
__le16 seq; /* packet sequence number */
u8 wcid; /* WLAN index (WTBL) */
u8 tx_count; /* TX attempts including retries */
__le16 tx_rate;
u8 flag; /* TXS_WITH_ADVANCED_INFO or TXS_IS_EXIST */
u8 tid;
u8 rsp_rate;
u8 rate_tbl_idx; /* last TX rate index from WLAN table */
u8 bw; /* bandwidth used for this PPDU */
u8 tx_pwr; /* dBm */
u8 flush_reason;
u8 rsv[1];
__le32 tx_delay; /* unit: 32us, UMAC TX to TX status */
__le32 timestamp; /* local TSF at first bit of MAC header */
__le32 applied_flags;
} __packed;
#endif /* __MT76_CONNAC3_MAC_H */

View File

@ -12,10 +12,17 @@
bool mt7925_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask)
{
mt76_rmw(dev, MT7925_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX,
u32 wtbl_update;
if (is_mt7928(&dev->mt76))
wtbl_update = MT7928_WTBL_UPDATE;
else
wtbl_update = MT7925_WTBL_UPDATE;
mt76_rmw(dev, wtbl_update, MT_WTBL_UPDATE_WLAN_IDX,
FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask);
return mt76_poll(dev, MT7925_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY,
return mt76_poll(dev, wtbl_update, MT_WTBL_UPDATE_BUSY,
0, 5000);
}
@ -159,10 +166,17 @@ void mt7925_mac_set_fixed_rate_table(struct mt792x_dev *dev,
{
u32 ctrl = MT_WTBL_ITCR_WR | MT_WTBL_ITCR_EXEC | tbl_idx;
mt76_wr(dev, MT_WTBL_ITDR0, rate_idx);
/* use wtbl spe idx */
mt76_wr(dev, MT_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL);
mt76_wr(dev, MT_WTBL_ITCR, ctrl);
if (is_mt7928(&dev->mt76)) {
mt76_wr(dev, MT7928_WTBL_ITDR0, rate_idx);
/* use wtbl spe idx */
mt76_wr(dev, MT7928_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL);
mt76_wr(dev, MT7928_WTBL_ITCR, ctrl);
} else {
mt76_wr(dev, MT_WTBL_ITDR0, rate_idx);
/* use wtbl spe idx */
mt76_wr(dev, MT_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL);
mt76_wr(dev, MT_WTBL_ITCR, ctrl);
}
}
/* The HW does not translate the mac header to 802.3 for mesh point */
@ -705,6 +719,9 @@ mt7925_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi,
txwi[2] |= cpu_to_le32(val);
if (is_mt7928(dev) && ieee80211_is_mgmt(hdr->frame_control))
txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU);
txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast));
if (ieee80211_is_beacon(fc))
txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT);
@ -808,7 +825,11 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
txwi[5] = cpu_to_le32(val);
val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
if (is_mt7928(dev))
val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT_V2, 1);
else
val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT, 1);
if (vif && (!ieee80211_vif_is_mld(vif) ||
(q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0)))
val |= MT_TXD6_DIS_MAT;
@ -836,6 +857,10 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
}
txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_TX_RATE, idx));
if (is_mt7928(dev))
txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_BW_V2, 8));
txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE);
}
}
@ -916,7 +941,6 @@ mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid,
goto out_no_skb;
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;
@ -1033,17 +1057,192 @@ mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid,
return !!skb;
}
void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data)
static bool
mt7928_mac_add_txs_skb_msg(struct mt792x_dev *dev, struct mt76_wcid *wcid,
int pid, struct mt7928_uni_txdone_event *pevt)
{
struct mt76_sta_stats *stats = &wcid->stats;
struct ieee80211_supported_band *sband;
struct mt76_dev *mdev = &dev->mt76;
struct ieee80211_tx_info *info;
struct sk_buff_head list;
u32 txrate, mode, stbc;
struct rate_info rate;
struct mt76_phy *mphy;
struct sk_buff *skb;
bool cck = false;
mt76_tx_status_lock(mdev, &list);
skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list);
if (!skb)
goto out_no_skb;
info = IEEE80211_SKB_CB(skb);
if (!pevt->status)
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;
txrate = le16_to_cpu(pevt->tx_rate);
rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate);
rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1;
stbc = FIELD_GET(MT_TX_RATE_STBC, txrate);
if (stbc && rate.nss > 1)
rate.nss >>= 1;
if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss))
stats->tx_nss[rate.nss - 1]++;
if (rate.mcs < ARRAY_SIZE(stats->tx_mcs))
stats->tx_mcs[rate.mcs]++;
mode = FIELD_GET(MT_TX_RATE_MODE, txrate);
switch (mode) {
case MT_PHY_TYPE_CCK:
cck = true;
fallthrough;
case MT_PHY_TYPE_OFDM:
mphy = mt76_dev_phy(mdev, wcid->phy_idx);
if (mphy->chandef.chan->band == NL80211_BAND_5GHZ)
sband = &mphy->sband_5g.sband;
else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ)
sband = &mphy->sband_6g.sband;
else
sband = &mphy->sband_2g.sband;
rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck);
rate.legacy = sband->bitrates[rate.mcs].bitrate;
break;
case MT_PHY_TYPE_HT:
case MT_PHY_TYPE_HT_GF:
if (rate.mcs > 31)
goto out;
rate.flags = RATE_INFO_FLAGS_MCS;
if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI)
rate.flags |= RATE_INFO_FLAGS_SHORT_GI;
break;
case MT_PHY_TYPE_VHT:
if (rate.mcs > 9)
goto out;
rate.flags = RATE_INFO_FLAGS_VHT_MCS;
break;
case MT_PHY_TYPE_HE_SU:
case MT_PHY_TYPE_HE_EXT_SU:
case MT_PHY_TYPE_HE_TB:
case MT_PHY_TYPE_HE_MU:
if (rate.mcs > 11)
goto out;
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;
case MT_PHY_TYPE_EHT_SU:
case MT_PHY_TYPE_EHT_TRIG:
case MT_PHY_TYPE_EHT_MU:
if (rate.mcs > 13)
goto out;
rate.eht_gi = wcid->rate.eht_gi;
rate.flags = RATE_INFO_FLAGS_EHT_MCS;
break;
default:
goto out;
}
stats->tx_mode[mode]++;
switch (pevt->bw) {
case IEEE80211_STA_RX_BW_160:
rate.bw = RATE_INFO_BW_160;
stats->tx_bw[3]++;
break;
case IEEE80211_STA_RX_BW_80:
rate.bw = RATE_INFO_BW_80;
stats->tx_bw[2]++;
break;
case IEEE80211_STA_RX_BW_40:
rate.bw = RATE_INFO_BW_40;
stats->tx_bw[1]++;
break;
default:
rate.bw = RATE_INFO_BW_20;
stats->tx_bw[0]++;
break;
}
wcid->rate = rate;
out:
mt76_tx_status_skb_done(mdev, skb, &list);
out_no_skb:
mt76_tx_status_unlock(mdev, &list);
return !!skb;
}
void mt7928_mac_add_txs_msg(struct mt792x_dev *dev,
void *evt)
{
struct mt7928_uni_txdone_event *done_evt;
struct mt792x_link_sta *mlink = NULL;
struct mt76_wcid *wcid;
__le32 *txs_data = data;
u16 wcidx;
u8 pid;
if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1)
done_evt = (struct mt7928_uni_txdone_event *)evt;
wcidx = done_evt->wcid;
pid = done_evt->pid;
if (pid < MT_PACKET_ID_FIRST)
return;
if (wcidx >= MT792x_WTBL_SIZE)
return;
rcu_read_lock();
wcid = mt76_wcid_ptr(dev, wcidx);
if (!wcid)
goto out;
mlink = container_of(wcid, struct mt792x_link_sta, wcid);
mt7928_mac_add_txs_skb_msg(dev, wcid, pid, done_evt);
if (!wcid->sta)
goto out;
mt76_wcid_add_poll(&dev->mt76, &mlink->wcid);
out:
rcu_read_unlock();
}
void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data)
{
struct mt792x_link_sta *mlink = NULL;
__le32 *txs_data = data;
struct mt76_wcid *wcid;
u8 pid, txs_fm;
u16 wcidx;
txs_fm = le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT);
if (is_mt7928(&dev->mt76)) {
if (txs_fm != TXS_FM_MPDU && txs_fm != TXS_FM_PPDU)
return;
} else if (txs_fm > 1) {
return;
}
wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID);
pid = le32_get_bits(txs_data[3], MT_TXS3_PID);

View File

@ -14,7 +14,14 @@
static inline u32 mt7925_mac_wtbl_lmac_addr(struct mt792x_dev *dev, u16 wcid, u8 dw)
{
mt76_wr(dev, MT7925_WTBLON_TOP_WDUCR,
u32 wdu_cr;
if (is_mt7928(&dev->mt76))
wdu_cr = MT7928_WTBLON_TOP_WDUCR;
else
wdu_cr = MT7925_WTBLON_TOP_WDUCR;
mt76_wr(dev, wdu_cr,
FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7)));
return MT_WTBL_LMAC_OFFS(wcid, dw);

View File

@ -437,6 +437,7 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb)
u8 rsv[3];
u8 data[];
} __packed * txs;
struct mt7928_uni_txdone_event *evt;
struct tlv *tlv;
u32 tlv_len;
@ -446,6 +447,29 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb)
while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) {
switch (le16_to_cpu(tlv->tag)) {
case UNI_EVENT_TX_DONE_MSG:
if (!is_mt7928(&dev->mt76))
break;
evt = (struct mt7928_uni_txdone_event *)tlv;
if (evt->status) {
dev_info(dev->mt76.dev,
"TxDone: pid=%u status=%#x sn=%#x wcid=%u "
"cnt=%u rate=%#x flag=%#x tid=%u pwr=%u "
"rsp_rate=%#x rate_idx=%u bw=%u flush=%#x "
"delay=%#x ts=%#x flags=%#x\n",
evt->pid, evt->status,
le16_to_cpu(evt->seq), evt->wcid,
evt->tx_count, le16_to_cpu(evt->tx_rate),
evt->flag, evt->tid, evt->tx_pwr,
evt->rsp_rate, evt->rate_tbl_idx,
evt->bw, evt->flush_reason,
le32_to_cpu(evt->tx_delay),
le32_to_cpu(evt->timestamp),
le32_to_cpu(evt->applied_flags));
}
mt7928_mac_add_txs_msg(dev, evt);
break;
case UNI_EVENT_TX_DONE_RAW:
txs = (struct mt7925_mcu_txs_event *)tlv->data;
mt7925_mac_add_txs(dev, txs->data);

View File

@ -354,6 +354,7 @@ int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd,
int mt7925e_mac_reset(struct mt792x_dev *dev);
int mt7925e_mcu_init(struct mt792x_dev *dev);
void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data);
void mt7928_mac_add_txs_msg(struct mt792x_dev *dev, void *evt);
void mt7925_set_runtime_pm(struct mt792x_dev *dev);
void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac,
struct ieee80211_vif *vif);

View File

@ -97,12 +97,18 @@
#define MT_WFSYS_SW_RST_B 0x7c000140
#define MT7925_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370)
#define MT7928_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x400)
#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0)
#define MT7925_WTBL_UPDATE MT_WTBLON_TOP(0x380)
#define MT7928_WTBL_UPDATE MT_WTBLON_TOP(0x410)
#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0)
#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14)
#define MT7928_WTBL_ITCR MT_WTBLON_TOP(0x440)
#define MT7928_WTBL_ITDR0 MT_WTBLON_TOP(0x448)
#define MT7928_WTBL_ITDR1 MT_WTBLON_TOP(0x44c)
#define MT7925_PCIE_MAC_BASE 0x10000
#define MT7925_PCIE_MAC(ofs) (MT7925_PCIE_MAC_BASE + (ofs))
#define MT7925_PCIE_MAC_INT_ENABLE MT7925_PCIE_MAC(0x188)