mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 13:26:48 +00:00
MT7996 driver can use both wed and wed_hif2 devices to offload traffic from/to the wireless NIC. In the current codebase we assume to always use the primary wed device in wed callbacks resulting in the following crash if the hw runs wed_hif2 (e.g. 6GHz link). [ 297.455876] Unable to handle kernel read from unreadable memory at virtual address 000000000000080a [ 297.464928] Mem abort info: [ 297.467722] ESR = 0x0000000096000005 [ 297.471461] EC = 0x25: DABT (current EL), IL = 32 bits [ 297.476766] SET = 0, FnV = 0 [ 297.479809] EA = 0, S1PTW = 0 [ 297.482940] FSC = 0x05: level 1 translation fault [ 297.487809] Data abort info: [ 297.490679] ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000 [ 297.496156] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 [ 297.501196] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [ 297.506500] user pgtable: 4k pages, 39-bit VAs, pgdp=0000000107480000 [ 297.512927] [000000000000080a] pgd=08000001097fb003, p4d=08000001097fb003, pud=08000001097fb003, pmd=0000000000000000 [ 297.523532] Internal error: Oops: 0000000096000005 [#1] SMP [ 297.715393] CPU: 2 UID: 0 PID: 45 Comm: kworker/u16:2 Tainted: G O 6.12.50 #0 [ 297.723908] Tainted: [O]=OOT_MODULE [ 297.727384] Hardware name: Banana Pi BPI-R4 (2x SFP+) (DT) [ 297.732857] Workqueue: nf_ft_offload_del nf_flow_rule_route_ipv6 [nf_flow_table] [ 297.740254] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 297.747205] pc : mt76_wed_offload_disable+0x64/0xa0 [mt76] [ 297.752688] lr : mtk_wed_flow_remove+0x58/0x80 [ 297.757126] sp : ffffffc080fe3ae0 [ 297.760430] x29: ffffffc080fe3ae0 x28: ffffffc080fe3be0 x27: 00000000deadbef7 [ 297.767557] x26: ffffff80c5ebca00 x25: 0000000000000001 x24: ffffff80c85f4c00 [ 297.774683] x23: ffffff80c1875b78 x22: ffffffc080d42cd0 x21: ffffffc080660018 [ 297.781809] x20: ffffff80c6a076d0 x19: ffffff80c6a043c8 x18: 0000000000000000 [ 297.788935] x17: 0000000000000000 x16: 0000000000000001 x15: 0000000000000000 [ 297.796060] x14: 0000000000000019 x13: ffffff80c0ad8ec0 x12: 00000000fa83b2da [ 297.803185] x11: ffffff80c02700c0 x10: ffffff80c0ad8ec0 x9 : ffffff81fef96200 [ 297.810311] x8 : ffffff80c02700c0 x7 : ffffff80c02700d0 x6 : 0000000000000002 [ 297.817435] x5 : 0000000000000400 x4 : 0000000000000000 x3 : 0000000000000000 [ 297.824561] x2 : 0000000000000001 x1 : 0000000000000800 x0 : ffffff80c6a063c8 [ 297.831686] Call trace: [ 297.834123] mt76_wed_offload_disable+0x64/0xa0 [mt76] [ 297.839254] mtk_wed_flow_remove+0x58/0x80 [ 297.843342] mtk_flow_offload_cmd+0x434/0x574 [ 297.847689] mtk_wed_setup_tc_block_cb+0x30/0x40 [ 297.852295] nf_flow_offload_ipv6_hook+0x7f4/0x964 [nf_flow_table] [ 297.858466] nf_flow_rule_route_ipv6+0x438/0x4a4 [nf_flow_table] [ 297.864463] process_one_work+0x174/0x300 [ 297.868465] worker_thread+0x278/0x430 [ 297.872204] kthread+0xd8/0xdc [ 297.875251] ret_from_fork+0x10/0x20 [ 297.878820] Code: 928b5ae0 8b000273 91400a60 f943fa61 (79401421) [ 297.884901] ---[ end trace 0000000000000000 ]--- Fix the issue detecting the proper wed reference to use running wed callabacks. Fixes: 83eafc9251d6 ("wifi: mt76: mt7996: add wed tx support") Tested-by: Daniel Pawlik <[email protected]> Tested-by: Matteo Croce <[email protected]> Signed-off-by: Lorenzo Bianconi <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Felix Fietkau <[email protected]>
210 lines
4.9 KiB
C
210 lines
4.9 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
/*
|
|
* Copyright (C) 2023 Lorenzo Bianconi <[email protected]>
|
|
*/
|
|
|
|
#include "mt76.h"
|
|
#include "dma.h"
|
|
|
|
void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
|
|
{
|
|
struct mt76_dev *dev = mt76_wed_to_dev(wed);
|
|
int i;
|
|
|
|
for (i = 0; i < dev->rx_token_size; i++) {
|
|
struct mt76_txwi_cache *t;
|
|
|
|
t = mt76_rx_token_release(dev, i);
|
|
if (!t || !t->ptr)
|
|
continue;
|
|
|
|
mt76_put_page_pool_buf(t->ptr, false);
|
|
t->ptr = NULL;
|
|
|
|
mt76_put_rxwi(dev, t);
|
|
}
|
|
|
|
mt76_free_pending_rxwi(dev);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf);
|
|
|
|
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
|
|
u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
|
|
{
|
|
struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc;
|
|
struct mt76_dev *dev = mt76_wed_to_dev(wed);
|
|
struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
|
|
struct mt76_txwi_cache *t = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
dma_addr_t addr;
|
|
u32 offset;
|
|
int token;
|
|
void *buf;
|
|
|
|
t = mt76_get_rxwi(dev);
|
|
if (!t)
|
|
goto unmap;
|
|
|
|
buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
|
|
if (!buf)
|
|
goto unmap;
|
|
|
|
addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset;
|
|
desc->buf0 = cpu_to_le32(addr);
|
|
token = mt76_rx_token_consume(dev, buf, t, addr);
|
|
if (token < 0) {
|
|
mt76_put_page_pool_buf(buf, false);
|
|
goto unmap;
|
|
}
|
|
|
|
token = FIELD_PREP(MT_DMA_CTL_TOKEN, token);
|
|
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
|
|
token |= FIELD_PREP(MT_DMA_CTL_SDP0_H, addr >> 32);
|
|
#endif
|
|
desc->token |= cpu_to_le32(token);
|
|
desc++;
|
|
}
|
|
|
|
return 0;
|
|
|
|
unmap:
|
|
if (t)
|
|
mt76_put_rxwi(dev, t);
|
|
mt76_wed_release_rx_buf(wed);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76_wed_init_rx_buf);
|
|
|
|
int mt76_wed_offload_enable(struct mtk_wed_device *wed)
|
|
{
|
|
struct mt76_dev *dev = mt76_wed_to_dev(wed);
|
|
|
|
spin_lock_bh(&dev->token_lock);
|
|
dev->token_size = wed->wlan.token_start;
|
|
spin_unlock_bh(&dev->token_lock);
|
|
|
|
return !wait_event_timeout(dev->tx_wait, !dev->wed_token_count, HZ);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76_wed_offload_enable);
|
|
|
|
int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset)
|
|
{
|
|
int ret = 0, type, ring;
|
|
u16 flags;
|
|
|
|
if (!q || !q->ndesc)
|
|
return -EINVAL;
|
|
|
|
flags = q->flags;
|
|
if (!q->wed || !mtk_wed_device_active(q->wed))
|
|
q->flags &= ~MT_QFLAG_WED;
|
|
|
|
if (!(q->flags & MT_QFLAG_WED))
|
|
return 0;
|
|
|
|
type = FIELD_GET(MT_QFLAG_WED_TYPE, q->flags);
|
|
ring = FIELD_GET(MT_QFLAG_WED_RING, q->flags);
|
|
|
|
switch (type) {
|
|
case MT76_WED_Q_TX:
|
|
ret = mtk_wed_device_tx_ring_setup(q->wed, ring, q->regs,
|
|
reset);
|
|
if (!ret)
|
|
q->wed_regs = q->wed->tx_ring[ring].reg_base;
|
|
break;
|
|
case MT76_WED_Q_TXFREE:
|
|
/* WED txfree queue needs ring to be initialized before setup */
|
|
q->flags = 0;
|
|
mt76_dma_queue_reset(dev, q, true);
|
|
mt76_dma_rx_fill(dev, q, false);
|
|
|
|
ret = mtk_wed_device_txfree_ring_setup(q->wed, q->regs);
|
|
if (!ret)
|
|
q->wed_regs = q->wed->txfree_ring.reg_base;
|
|
break;
|
|
case MT76_WED_Q_RX:
|
|
ret = mtk_wed_device_rx_ring_setup(q->wed, ring, q->regs,
|
|
reset);
|
|
if (!ret)
|
|
q->wed_regs = q->wed->rx_ring[ring].reg_base;
|
|
break;
|
|
case MT76_WED_RRO_Q_DATA:
|
|
q->flags &= ~MT_QFLAG_WED;
|
|
mt76_dma_queue_reset(dev, q, false);
|
|
mtk_wed_device_rro_rx_ring_setup(q->wed, ring, q->regs);
|
|
q->head = q->ndesc - 1;
|
|
q->queued = q->head;
|
|
break;
|
|
case MT76_WED_RRO_Q_MSDU_PG:
|
|
q->flags &= ~MT_QFLAG_WED;
|
|
mt76_dma_queue_reset(dev, q, false);
|
|
mtk_wed_device_msdu_pg_rx_ring_setup(q->wed, ring, q->regs);
|
|
q->head = q->ndesc - 1;
|
|
q->queued = q->head;
|
|
break;
|
|
case MT76_WED_RRO_Q_IND:
|
|
q->flags &= ~MT_QFLAG_WED;
|
|
mt76_dma_queue_reset(dev, q, true);
|
|
mt76_dma_rx_fill(dev, q, false);
|
|
mtk_wed_device_ind_rx_ring_setup(q->wed, q->regs);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
q->flags = flags;
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76_wed_dma_setup);
|
|
#endif /*CONFIG_NET_MEDIATEK_SOC_WED */
|
|
|
|
void mt76_wed_offload_disable(struct mtk_wed_device *wed)
|
|
{
|
|
struct mt76_dev *dev = mt76_wed_to_dev(wed);
|
|
|
|
spin_lock_bh(&dev->token_lock);
|
|
dev->token_size = dev->drv->token_size;
|
|
spin_unlock_bh(&dev->token_lock);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76_wed_offload_disable);
|
|
|
|
void mt76_wed_reset_complete(struct mtk_wed_device *wed)
|
|
{
|
|
struct mt76_dev *dev = mt76_wed_to_dev(wed);
|
|
|
|
complete(&dev->mmio.wed_reset_complete);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76_wed_reset_complete);
|
|
|
|
int mt76_wed_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|
struct net_device *netdev, enum tc_setup_type type,
|
|
void *type_data)
|
|
{
|
|
struct mt76_phy *phy = hw->priv;
|
|
struct mtk_wed_device *wed = &phy->dev->mmio.wed;
|
|
|
|
if (!mtk_wed_device_active(wed))
|
|
return -EOPNOTSUPP;
|
|
|
|
return mtk_wed_device_setup_tc(wed, netdev, type, type_data);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76_wed_net_setup_tc);
|
|
|
|
void mt76_wed_dma_reset(struct mt76_dev *dev)
|
|
{
|
|
struct mt76_mmio *mmio = &dev->mmio;
|
|
|
|
if (!test_bit(MT76_STATE_WED_RESET, &dev->phy.state))
|
|
return;
|
|
|
|
complete(&mmio->wed_reset);
|
|
|
|
if (!wait_for_completion_timeout(&mmio->wed_reset_complete, 3 * HZ))
|
|
dev_err(dev->dev, "wed reset complete timeout\n");
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt76_wed_dma_reset);
|