if (e->skb == DMA_DUMMY_DATA) {
                struct mt76_txwi_cache *t;
                struct mt7615_dev *dev;
-               struct mt7615_txp *txp;
+               struct mt7615_txp_common *txp;
+               u16 token;
 
                dev = container_of(mdev, struct mt7615_dev, mt76);
                txp = mt7615_txwi_to_txp(mdev, e->txwi);
 
+               if (is_mt7615(&dev->mt76))
+                       token = le16_to_cpu(txp->fw.token);
+               else
+                       token = le16_to_cpu(txp->hw.msdu_id[0]) &
+                               ~MT_MSDU_ID_VALID;
+
                spin_lock_bh(&dev->token_lock);
-               t = idr_remove(&dev->token, le16_to_cpu(txp->token));
+               t = idr_remove(&dev->token, token);
                spin_unlock_bh(&dev->token_lock);
                e->skb = t ? t->skb : NULL;
        }
        return 0;
 }
 
-void mt7615_txp_skb_unmap(struct mt76_dev *dev,
-                         struct mt76_txwi_cache *t)
+static void
+mt7615_txp_skb_unmap_fw(struct mt76_dev *dev, struct mt7615_fw_txp *txp)
 {
-       struct mt7615_txp *txp;
        int i;
 
-       txp = mt7615_txwi_to_txp(dev, t);
        for (i = 1; i < txp->nbuf; i++)
                dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]),
                                 le16_to_cpu(txp->len[i]), DMA_TO_DEVICE);
 }
 
+static void
+mt7615_txp_skb_unmap_hw(struct mt76_dev *dev, struct mt7615_hw_txp *txp)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(txp->ptr); i++) {
+               struct mt7615_txp_ptr *ptr = &txp->ptr[i];
+               bool last;
+               u16 len;
+
+               len = le16_to_cpu(ptr->len0);
+               last = len & MT_TXD_LEN_MSDU_LAST;
+               len &= ~MT_TXD_LEN_MSDU_LAST;
+               dma_unmap_single(dev->dev, le32_to_cpu(ptr->buf0), len,
+                                DMA_TO_DEVICE);
+               if (last)
+                       break;
+
+               len = le16_to_cpu(ptr->len1);
+               last = len & MT_TXD_LEN_MSDU_LAST;
+               len &= ~MT_TXD_LEN_MSDU_LAST;
+               dma_unmap_single(dev->dev, le32_to_cpu(ptr->buf1), len,
+                                DMA_TO_DEVICE);
+               if (last)
+                       break;
+       }
+}
+
+void mt7615_txp_skb_unmap(struct mt76_dev *dev,
+                         struct mt76_txwi_cache *t)
+{
+       struct mt7615_txp_common *txp;
+
+       txp = mt7615_txwi_to_txp(dev, t);
+       if (is_mt7615(dev))
+               mt7615_txp_skb_unmap_fw(dev, &txp->fw);
+       else
+               mt7615_txp_skb_unmap_hw(dev, &txp->hw);
+}
+
 static u32 mt7615_mac_wtbl_addr(int wcid)
 {
        return MT_WTBL_BASE + wcid * MT_WTBL_ENTRY_SIZE;
        return err;
 }
 
-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,
-                         struct mt76_tx_info *tx_info)
+static void
+mt7615_write_hw_txp(struct mt7615_dev *dev, struct mt76_tx_info *tx_info,
+                   void *txp_ptr, u32 id)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data;
-       struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
-       struct mt7615_sta *msta = container_of(wcid, struct mt7615_sta, wcid);
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
-       struct ieee80211_key_conf *key = info->control.hw_key;
-       struct ieee80211_vif *vif = info->control.vif;
-       int i, pid, id, nbuf = tx_info->nbuf - 1;
-       u8 *txwi = (u8 *)txwi_ptr;
-       struct mt76_txwi_cache *t;
-       struct mt7615_txp *txp;
+       struct mt7615_hw_txp *txp = txp_ptr;
+       struct mt7615_txp_ptr *ptr = &txp->ptr[0];
+       int nbuf = tx_info->nbuf - 1;
+       int i;
 
-       if (!wcid)
-               wcid = &dev->mt76.global_wcid;
+       tx_info->buf[0].len = MT_TXD_SIZE + sizeof(*txp);
+       tx_info->nbuf = 1;
 
-       pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
+       txp->msdu_id[0] = cpu_to_le16(id | MT_MSDU_ID_VALID);
 
-       if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) {
-               struct mt7615_phy *phy = &dev->phy;
+       for (i = 0; i < nbuf; i++) {
+               u32 addr = tx_info->buf[i + 1].addr;
+               u16 len = tx_info->buf[i + 1].len;
 
-               if ((info->hw_queue & MT_TX_HW_QUEUE_EXT_PHY) && mdev->phy2)
-                       phy = mdev->phy2->priv;
+               if (i == nbuf - 1)
+                       len |= MT_TXD_LEN_MSDU_LAST |
+                              MT_TXD_LEN_AMSDU_LAST;
 
-               spin_lock_bh(&dev->mt76.lock);
-               mt7615_mac_set_rates(phy, msta, &info->control.rates[0],
-                                    msta->rates);
-               msta->rate_probe = true;
-               spin_unlock_bh(&dev->mt76.lock);
+               if (i & 1) {
+                       ptr->buf1 = cpu_to_le32(addr);
+                       ptr->len1 = cpu_to_le16(len);
+                       ptr++;
+               } else {
+                       ptr->buf0 = cpu_to_le32(addr);
+                       ptr->len0 = cpu_to_le16(len);
+               }
        }
+}
 
-       mt7615_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, sta,
-                             pid, key);
+static void
+mt7615_write_fw_txp(struct mt7615_dev *dev, struct mt76_tx_info *tx_info,
+                   void *txp_ptr, u32 id)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
+       struct ieee80211_key_conf *key = info->control.hw_key;
+       struct ieee80211_vif *vif = info->control.vif;
+       struct mt7615_fw_txp *txp = txp_ptr;
+       int nbuf = tx_info->nbuf - 1;
+       int i;
 
-       txp = (struct mt7615_txp *)(txwi + MT_TXD_SIZE);
        for (i = 0; i < nbuf; i++) {
                txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr);
                txp->len[i] = cpu_to_le16(tx_info->buf[i + 1].len);
        txp->nbuf = nbuf;
 
        /* pass partial skb header to fw */
+       tx_info->buf[0].len = MT_TXD_SIZE + sizeof(*txp);
        tx_info->buf[1].len = MT_CT_PARSE_LEN;
        tx_info->nbuf = MT_CT_DMA_BUF_NUM;
 
                txp->bss_idx = mvif->idx;
        }
 
+       txp->token = cpu_to_le16(id);
+       txp->rept_wds_wcid = 0xff;
+}
+
+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,
+                         struct mt76_tx_info *tx_info)
+{
+       struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
+       struct mt7615_sta *msta = container_of(wcid, struct mt7615_sta, wcid);
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
+       struct ieee80211_key_conf *key = info->control.hw_key;
+       int pid, id;
+       u8 *txwi = (u8 *)txwi_ptr;
+       struct mt76_txwi_cache *t;
+       void *txp;
+
+       if (!wcid)
+               wcid = &dev->mt76.global_wcid;
+
+       pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb);
+
+       if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) {
+               struct mt7615_phy *phy = &dev->phy;
+
+               if ((info->hw_queue & MT_TX_HW_QUEUE_EXT_PHY) && mdev->phy2)
+                       phy = mdev->phy2->priv;
+
+               spin_lock_bh(&dev->mt76.lock);
+               mt7615_mac_set_rates(phy, msta, &info->control.rates[0],
+                                    msta->rates);
+               msta->rate_probe = true;
+               spin_unlock_bh(&dev->mt76.lock);
+       }
+
        t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
        t->skb = tx_info->skb;
 
        if (id < 0)
                return id;
 
-       txp->token = cpu_to_le16(id);
-       txp->rept_wds_wcid = 0xff;
+       mt7615_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, sta,
+                             pid, key);
+
+       txp = txwi + MT_TXD_SIZE;
+       memset(txp, 0, sizeof(struct mt7615_txp_common));
+       if (is_mt7615(&dev->mt76))
+               mt7615_write_fw_txp(dev, tx_info, txp, id);
+       else
+               mt7615_write_hw_txp(dev, tx_info, txp, id);
+
        tx_info->skb = DMA_DUMMY_DATA;
 
        return 0;
        rcu_read_unlock();
 }
 
-void mt7615_mac_tx_free(struct mt7615_dev *dev, struct sk_buff *skb)
+static void
+mt7615_mac_tx_free_token(struct mt7615_dev *dev, u16 token)
 {
-       struct mt7615_tx_free *free = (struct mt7615_tx_free *)skb->data;
        struct mt76_dev *mdev = &dev->mt76;
        struct mt76_txwi_cache *txwi;
-       u8 i, count;
 
-       count = FIELD_GET(MT_TX_FREE_MSDU_ID_CNT, le16_to_cpu(free->ctrl));
-       for (i = 0; i < count; i++) {
-               u16 token = le16_to_cpu(free->token[i]);
+       trace_mac_tx_free(dev, token);
 
-               spin_lock_bh(&dev->token_lock);
-               txwi = idr_remove(&dev->token, token);
-               spin_unlock_bh(&dev->token_lock);
+       spin_lock_bh(&dev->token_lock);
+       txwi = idr_remove(&dev->token, token);
+       spin_unlock_bh(&dev->token_lock);
 
-               if (!txwi)
-                       continue;
+       if (!txwi)
+               return;
+
+       mt7615_txp_skb_unmap(mdev, txwi);
+       if (txwi->skb) {
+               mt76_tx_complete_skb(mdev, txwi->skb);
+               txwi->skb = NULL;
+       }
 
-               trace_mac_tx_free(dev, token);
+       mt76_put_txwi(mdev, txwi);
+}
 
-               mt7615_txp_skb_unmap(mdev, txwi);
-               if (txwi->skb) {
-                       mt76_tx_complete_skb(mdev, txwi->skb);
-                       txwi->skb = NULL;
-               }
+void mt7615_mac_tx_free(struct mt7615_dev *dev, struct sk_buff *skb)
+{
+       struct mt7615_tx_free *free = (struct mt7615_tx_free *)skb->data;
+       u8 i, count;
+
+       count = FIELD_GET(MT_TX_FREE_MSDU_ID_CNT, le16_to_cpu(free->ctrl));
+       if (is_mt7615(&dev->mt76)) {
+               __le16 *token = &free->token[0];
+
+               for (i = 0; i < count; i++)
+                       mt7615_mac_tx_free_token(dev, le16_to_cpu(token[i]));
+       } else {
+               __le32 *token = (__le32 *)&free->token[0];
 
-               mt76_put_txwi(mdev, txwi);
+               for (i = 0; i < count; i++)
+                       mt7615_mac_tx_free_token(dev, le32_to_cpu(token[i]));
        }
+
        dev_kfree_skb(skb);
 }