Commit a5d028d6 authored by Lorenzo Bianconi's avatar Lorenzo Bianconi Committed by Felix Fietkau

wifi: mt76: mt7996: add wed rro delete session garbage collector

Introduce the capability to clear WED rro session configured in the hw
according to the event reported by the MCU firmware
Co-developed-by: default avatarSujuan Chen <sujuan.chen@mediatek.com>
Signed-off-by: default avatarSujuan Chen <sujuan.chen@mediatek.com>
Co-developed-by: default avatarBo Jiao <Bo.Jiao@mediatek.com>
Signed-off-by: default avatarBo Jiao <Bo.Jiao@mediatek.com>
Signed-off-by: default avatarLorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: default avatarFelix Fietkau <nbd@nbd.name>
parent 00d2ced0
......@@ -404,6 +404,7 @@ struct mt76_rx_tid {
spinlock_t lock;
struct delayed_work reorder_work;
u16 id;
u16 head;
u16 size;
u16 nframes;
......
......@@ -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,
};
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
}
......@@ -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,
......
......@@ -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)
{
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment