wifi: mt76: mt7996: add wed rro delete session garbage collector
authorLorenzo Bianconi <lorenzo@kernel.org>
Fri, 20 Oct 2023 10:31:00 +0000 (12:31 +0200)
committerFelix Fietkau <nbd@nbd.name>
Thu, 7 Dec 2023 17:50:20 +0000 (18:50 +0100)
Introduce the capability to clear WED rro session configured in the hw
according to the event reported by the MCU firmware

Co-developed-by: Sujuan Chen <sujuan.chen@mediatek.com>
Signed-off-by: Sujuan Chen <sujuan.chen@mediatek.com>
Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt76.h
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
drivers/net/wireless/mediatek/mt76/mt7996/init.c
drivers/net/wireless/mediatek/mt76/mt7996/mac.c
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
drivers/net/wireless/mediatek/mt76/mt7996/regs.h

index 36e54a3..b1d3f55 100644 (file)
@@ -404,6 +404,7 @@ struct mt76_rx_tid {
        spinlock_t lock;
        struct delayed_work reorder_work;
 
+       u16 id;
        u16 head;
        u16 size;
        u16 nframes;
index 0563b1b..97822f7 100644 (file)
@@ -1022,6 +1022,7 @@ enum {
        MCU_UNI_EVENT_ROC = 0x27,
        MCU_UNI_EVENT_TX_DONE = 0x2d,
        MCU_UNI_EVENT_NIC_CAPAB = 0x43,
+       MCU_UNI_EVENT_WED_RRO = 0x57,
        MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
        MCU_UNI_EVENT_ALL_STA_INFO = 0x6e,
 };
index a1adbc6..5af85dd 100644 (file)
@@ -641,6 +641,54 @@ static void mt7996_wed_rro_free(struct mt7996_dev *dev)
 #endif
 }
 
+static void mt7996_wed_rro_work(struct work_struct *work)
+{
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+       struct mt7996_dev *dev;
+       LIST_HEAD(list);
+
+       dev = (struct mt7996_dev *)container_of(work, struct mt7996_dev,
+                                               wed_rro.work);
+
+       spin_lock_bh(&dev->wed_rro.lock);
+       list_splice_init(&dev->wed_rro.poll_list, &list);
+       spin_unlock_bh(&dev->wed_rro.lock);
+
+       while (!list_empty(&list)) {
+               struct mt7996_wed_rro_session_id *e;
+               int i;
+
+               e = list_first_entry(&list, struct mt7996_wed_rro_session_id,
+                                    list);
+               list_del_init(&e->list);
+
+               for (i = 0; i < MT7996_RRO_WINDOW_MAX_LEN; i++) {
+                       void *ptr = dev->wed_rro.session.ptr;
+                       struct mt7996_wed_rro_addr *elem;
+                       u32 idx, elem_id = i;
+
+                       if (e->id == MT7996_RRO_MAX_SESSION)
+                               goto reset;
+
+                       idx = e->id / MT7996_RRO_BA_BITMAP_SESSION_SIZE;
+                       if (idx >= ARRAY_SIZE(dev->wed_rro.addr_elem))
+                               goto out;
+
+                       ptr = dev->wed_rro.addr_elem[idx].ptr;
+                       elem_id +=
+                               (e->id % MT7996_RRO_BA_BITMAP_SESSION_SIZE) *
+                               MT7996_RRO_WINDOW_MAX_LEN;
+reset:
+                       elem = ptr + elem_id * sizeof(*elem);
+                       elem->signature = 0xff;
+               }
+               mt7996_mcu_wed_rro_reset_sessions(dev, e->id);
+out:
+               kfree(e);
+       }
+#endif
+}
+
 static int mt7996_init_hardware(struct mt7996_dev *dev)
 {
        int ret, idx;
@@ -648,6 +696,9 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
        mt76_wr(dev, MT_INT_SOURCE_CSR, ~0);
 
        INIT_WORK(&dev->init_work, mt7996_init_work);
+       INIT_WORK(&dev->wed_rro.work, mt7996_wed_rro_work);
+       INIT_LIST_HEAD(&dev->wed_rro.poll_list);
+       spin_lock_init(&dev->wed_rro.lock);
 
        dev->dbdc_support = true;
        dev->tbtc_support = true;
@@ -1100,6 +1151,7 @@ int mt7996_register_device(struct mt7996_dev *dev)
 
 void mt7996_unregister_device(struct mt7996_dev *dev)
 {
+       cancel_work_sync(&dev->wed_rro.work);
        mt7996_unregister_phy(mt7996_phy3(dev), MT_BAND2);
        mt7996_unregister_phy(mt7996_phy2(dev), MT_BAND1);
        mt7996_coredump_unregister(dev);
index b790d41..f653e93 100644 (file)
@@ -1822,6 +1822,7 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
        if (phy3)
                ieee80211_stop_queues(phy3->mt76->hw);
 
+       cancel_work_sync(&dev->wed_rro.work);
        cancel_delayed_work_sync(&dev->mphy.mac_work);
        if (phy2)
                cancel_delayed_work_sync(&phy2->mt76->mac_work);
@@ -1920,6 +1921,8 @@ void mt7996_mac_reset_work(struct work_struct *work)
        set_bit(MT76_RESET, &dev->mphy.state);
        set_bit(MT76_MCU_RESET, &dev->mphy.state);
        wake_up(&dev->mt76.mcu.wait);
+
+       cancel_work_sync(&dev->wed_rro.work);
        cancel_delayed_work_sync(&dev->mphy.mac_work);
        if (phy2) {
                set_bit(MT76_RESET, &phy2->mt76->state);
index 5369f0a..03a9474 100644 (file)
@@ -526,6 +526,73 @@ mt7996_mcu_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
        dev_kfree_skb(skb);
 }
 
+static void
+mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb)
+{
+       struct mt7996_mcu_wed_rro_event *event = (void *)skb->data;
+
+       if (!dev->has_rro)
+               return;
+
+       skb_pull(skb, sizeof(struct mt7996_mcu_rxd) + 4);
+
+       switch (le16_to_cpu(event->tag)) {
+       case UNI_WED_RRO_BA_SESSION_STATUS: {
+               struct mt7996_mcu_wed_rro_ba_event *e;
+
+               while (skb->len >= sizeof(*e)) {
+                       struct mt76_rx_tid *tid;
+                       struct mt76_wcid *wcid;
+                       u16 idx;
+
+                       e = (void *)skb->data;
+                       idx = le16_to_cpu(e->wlan_id);
+                       if (idx >= ARRAY_SIZE(dev->mt76.wcid))
+                               break;
+
+                       wcid = rcu_dereference(dev->mt76.wcid[idx]);
+                       if (!wcid || !wcid->sta)
+                               break;
+
+                       if (e->tid >= ARRAY_SIZE(wcid->aggr))
+                               break;
+
+                       tid = rcu_dereference(wcid->aggr[e->tid]);
+                       if (!tid)
+                               break;
+
+                       tid->id = le16_to_cpu(e->id);
+                       skb_pull(skb, sizeof(*e));
+               }
+               break;
+       }
+       case UNI_WED_RRO_BA_SESSION_DELETE: {
+               struct mt7996_mcu_wed_rro_ba_delete_event *e;
+
+               while (skb->len >= sizeof(*e)) {
+                       struct mt7996_wed_rro_session_id *session;
+
+                       e = (void *)skb->data;
+                       session = kzalloc(sizeof(*session), GFP_ATOMIC);
+                       if (!session)
+                               break;
+
+                       session->id = le16_to_cpu(e->session_id);
+
+                       spin_lock_bh(&dev->wed_rro.lock);
+                       list_add_tail(&session->list, &dev->wed_rro.poll_list);
+                       spin_unlock_bh(&dev->wed_rro.lock);
+
+                       ieee80211_queue_work(mt76_hw(dev), &dev->wed_rro.work);
+                       skb_pull(skb, sizeof(*e));
+               }
+               break;
+       }
+       default:
+               break;
+       }
+}
+
 static void
 mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
 {
@@ -544,6 +611,9 @@ mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb)
        case MCU_UNI_EVENT_ALL_STA_INFO:
                mt7996_mcu_rx_all_sta_info_event(dev, skb);
                break;
+       case MCU_UNI_EVENT_WED_RRO:
+               mt7996_mcu_wed_rro_event(dev, skb);
+               break;
        default:
                break;
        }
@@ -4087,3 +4157,22 @@ int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag)
        return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO),
                                 &req, sizeof(req), false);
 }
+
+int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id)
+{
+       struct {
+               u8 __rsv[4];
+
+               __le16 tag;
+               __le16 len;
+               __le16 session_id;
+               u8 pad[4];
+       } __packed req = {
+               .tag = cpu_to_le16(UNI_RRO_DEL_BA_SESSION),
+               .len = cpu_to_le16(sizeof(req) - 4),
+               .session_id = cpu_to_le16(id),
+       };
+
+       return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req,
+                                sizeof(req), true);
+}
index a4715b8..e32a78d 100644 (file)
@@ -179,6 +179,41 @@ struct mt7996_mcu_all_sta_info_event {
        };
 } __packed;
 
+struct mt7996_mcu_wed_rro_event {
+       struct mt7996_mcu_rxd rxd;
+
+       u8 __rsv1[4];
+
+       __le16 tag;
+       __le16 len;
+} __packed;
+
+struct mt7996_mcu_wed_rro_ba_event {
+       __le16 tag;
+       __le16 len;
+
+       __le16 wlan_id;
+       u8 tid;
+       u8 __rsv1;
+       __le32 status;
+       __le16 id;
+       u8 __rsv2[2];
+} __packed;
+
+struct mt7996_mcu_wed_rro_ba_delete_event {
+       __le16 tag;
+       __le16 len;
+
+       __le16 session_id;
+       u8 __rsv2[2];
+} __packed;
+
+enum  {
+       UNI_WED_RRO_BA_SESSION_STATUS,
+       UNI_WED_RRO_BA_SESSION_TBL,
+       UNI_WED_RRO_BA_SESSION_DELETE,
+};
+
 enum mt7996_chan_mib_offs {
        UNI_MIB_OBSS_AIRTIME = 26,
        UNI_MIB_NON_WIFI_TIME = 27,
index f7b6945..e7818b2 100644 (file)
@@ -182,6 +182,11 @@ struct mt7996_wed_rro_addr {
        u32 signature : 8;
 };
 
+struct mt7996_wed_rro_session_id {
+       struct list_head list;
+       u16 id;
+};
+
 struct mt7996_phy {
        struct mt76_phy *mt76;
        struct mt7996_dev *dev;
@@ -276,6 +281,10 @@ struct mt7996_dev {
                        void *ptr;
                        dma_addr_t phy_addr;
                } session;
+
+               struct work_struct work;
+               struct list_head poll_list;
+               spinlock_t lock;
        } wed_rro;
 
        bool ibf;
@@ -456,6 +465,7 @@ int mt7996_mcu_trigger_assert(struct mt7996_dev *dev);
 void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
 void mt7996_mcu_exit(struct mt7996_dev *dev);
 int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag);
+int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id);
 
 static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev)
 {
index 49eb583..e9edba8 100644 (file)
@@ -71,6 +71,12 @@ enum base_rev {
 #define MT_RRO_ACK_SN_CTRL_SN_MASK             GENMASK(27, 16)
 #define MT_RRO_ACK_SN_CTRL_SESSION_MASK                GENMASK(11, 0)
 
+#define MT_RRO_DBG_RD_CTRL                     MT_RRO_TOP(0xe0)
+#define MT_RRO_DBG_RD_ADDR                     GENMASK(15, 0)
+#define MT_RRO_DBG_RD_EXEC                     BIT(31)
+
+#define MT_RRO_DBG_RDAT_DW(_n)                 MT_RRO_TOP(0xf0 + (_n) * 0x4)
+
 #define MT_MCU_INT_EVENT                       0x2108
 #define MT_MCU_INT_EVENT_DMA_STOPPED           BIT(0)
 #define MT_MCU_INT_EVENT_DMA_INIT              BIT(1)