Files
mt76/mt792x_dma.c
T
Sean WangandFelix Fietkau b81028de36 wifi: mt76: mt792x: factor out common DMA queue allocation
The mt792x PCIe DMA setup uses the same standard queue allocation
sequence for data, MCU, firmware download and RX rings.

Factor this part out into a small common helper so later chip support
can reuse the existing flow without duplicating the queue setup logic.

This keeps the common DMA skeleton in one place and makes follow-up
chip-specific DMA changes smaller and easier to review.

Signed-off-by: Sean Wang <[email protected]>
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Felix Fietkau <[email protected]>
2026-05-28 13:32:33 +02:00

500 lines
14 KiB
C

// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/module.h>
#include <linux/firmware.h>
#include "mt792x.h"
#include "dma.h"
#include "trace.h"
irqreturn_t mt792x_irq_handler(int irq, void *dev_instance)
{
struct mt792x_dev *dev = dev_instance;
if (test_bit(MT76_REMOVED, &dev->mt76.phy.state))
return IRQ_NONE;
mt76_wr(dev, dev->irq_map->host_irq_enable, 0);
if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state))
return IRQ_NONE;
tasklet_schedule(&dev->mt76.irq_tasklet);
return IRQ_HANDLED;
}
EXPORT_SYMBOL_GPL(mt792x_irq_handler);
void mt792x_irq_tasklet(unsigned long data)
{
struct mt792x_dev *dev = (struct mt792x_dev *)data;
const struct mt792x_irq_map *irq_map = dev->irq_map;
u32 intr, mask = 0;
mt76_wr(dev, irq_map->host_irq_enable, 0);
intr = mt76_rr(dev, MT_WFDMA0_HOST_INT_STA);
intr &= dev->mt76.mmio.irqmask;
mt76_wr(dev, MT_WFDMA0_HOST_INT_STA, intr);
trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask);
mask |= intr & (irq_map->rx.data_complete_mask |
irq_map->rx.wm_complete_mask |
irq_map->rx.wm2_complete_mask);
if (intr & dev->irq_map->tx.mcu_complete_mask)
mask |= dev->irq_map->tx.mcu_complete_mask;
if (intr & MT_INT_MCU_CMD) {
u32 intr_sw;
intr_sw = mt76_rr(dev, MT_MCU_CMD);
/* ack MCU2HOST_SW_INT_STA */
mt76_wr(dev, MT_MCU_CMD, intr_sw);
if (intr_sw & MT_MCU_CMD_WAKE_RX_PCIE) {
mask |= irq_map->rx.data_complete_mask;
intr |= irq_map->rx.data_complete_mask;
}
}
mt76_set_irq_mask(&dev->mt76, irq_map->host_irq_enable, mask, 0);
if (intr & dev->irq_map->tx.all_complete_mask)
napi_schedule(&dev->mt76.tx_napi);
if (intr & irq_map->rx.wm_complete_mask)
napi_schedule(&dev->mt76.napi[MT_RXQ_MCU]);
if (intr & irq_map->rx.wm2_complete_mask)
napi_schedule(&dev->mt76.napi[MT_RXQ_MCU_WA]);
if (intr & irq_map->rx.data_complete_mask)
napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN]);
}
EXPORT_SYMBOL_GPL(mt792x_irq_tasklet);
void mt792x_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
const struct mt792x_irq_map *irq_map = dev->irq_map;
if (q == MT_RXQ_MAIN)
mt76_connac_irq_enable(mdev, irq_map->rx.data_complete_mask);
else if (q == MT_RXQ_MCU_WA)
mt76_connac_irq_enable(mdev, irq_map->rx.wm2_complete_mask);
else
mt76_connac_irq_enable(mdev, irq_map->rx.wm_complete_mask);
}
EXPORT_SYMBOL_GPL(mt792x_rx_poll_complete);
int mt792x_dma_alloc_queues(struct mt792x_dev *dev,
const struct mt792x_dma_layout *layout)
{
int ret;
mt76_dma_attach(&dev->mt76);
ret = mt792x_dma_disable(dev, true);
if (ret)
return ret;
/* init tx queue */
ret = mt76_connac_init_tx_queues(dev->phy.mt76, layout->tx_data0.qid,
layout->tx_data0.n_desc,
layout->tx_data0.ring_base,
NULL, 0);
if (ret)
return ret;
mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4);
/* command to WM */
ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM,
layout->tx_mcu.qid,
layout->tx_mcu.n_desc,
layout->tx_mcu.ring_base);
if (ret)
return ret;
/* firmware download */
ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL,
layout->tx_fwdl.qid,
layout->tx_fwdl.n_desc,
layout->tx_fwdl.ring_base);
if (ret)
return ret;
/* rx event */
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU],
layout->rx_mcu.qid,
layout->rx_mcu.n_desc,
MT_RX_BUF_SIZE,
layout->rx_mcu.ring_base);
if (ret)
return ret;
/* rx data */
ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN],
layout->rx_data.qid,
layout->rx_data.n_desc,
MT_RX_BUF_SIZE,
layout->rx_data.ring_base);
return ret;
}
EXPORT_SYMBOL_GPL(mt792x_dma_alloc_queues);
#define PREFETCH(base, depth) ((base) << 16 | (depth))
static void mt792x_dma_prefetch(struct mt792x_dev *dev)
{
if (is_mt7925(&dev->mt76)) {
/* rx ring */
mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4));
/* tx ring */
mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x10));
mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0200, 0x10));
mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0300, 0x10));
mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10));
mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4));
} else if (is_mt7902(&dev->mt76)) {
/* rx ring */
mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4));
/* tx ring */
mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0140, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0180, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x01c0, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x0200, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x0240, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x0280, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x02c0, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0300, 0x4));
} else {
/* rx ring */
mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x40, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x80, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0xc0, 0x4));
mt76_wr(dev, MT_WFDMA0_RX_RING5_EXT_CTRL, PREFETCH(0x100, 0x4));
/* tx ring */
mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x140, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x180, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x1c0, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x200, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x240, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x280, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x2c0, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x340, 0x4));
mt76_wr(dev, MT_WFDMA0_TX_RING17_EXT_CTRL, PREFETCH(0x380, 0x4));
}
}
int mt792x_dma_enable(struct mt792x_dev *dev)
{
/* configure perfetch settings */
mt792x_dma_prefetch(dev);
/* reset dma idx */
mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0);
if (is_mt7925(&dev->mt76))
mt76_wr(dev, MT_WFDMA0_RST_DRX_PTR, ~0);
/* configure delay interrupt */
mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0);
mt76_set(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_WB_DDONE |
MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN |
MT_WFDMA0_GLO_CFG_CLK_GAT_DIS |
MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
FIELD_PREP(MT_WFDMA0_GLO_CFG_DMA_SIZE, 3) |
MT_WFDMA0_GLO_CFG_FIFO_DIS_CHECK |
MT_WFDMA0_GLO_CFG_RX_WB_DDONE |
MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN |
MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
mt76_set(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN);
if (is_mt7925(&dev->mt76)) {
mt76_rmw(dev, MT_UWFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28));
mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00);
mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00);
}
mt76_set(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT);
/* enable interrupts for TX/RX rings */
mt76_connac_irq_enable(&dev->mt76,
dev->irq_map->tx.all_complete_mask |
dev->irq_map->rx.data_complete_mask |
dev->irq_map->rx.wm2_complete_mask |
dev->irq_map->rx.wm_complete_mask |
MT_INT_MCU_CMD);
mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE);
return 0;
}
EXPORT_SYMBOL_GPL(mt792x_dma_enable);
static int
mt792x_dma_reset(struct mt792x_dev *dev, bool force)
{
int i, err;
err = mt792x_dma_disable(dev, force);
if (err)
return err;
/* reset hw queues */
for (i = 0; i < __MT_TXQ_MAX; i++)
mt76_queue_reset(dev, dev->mphy.q_tx[i], true);
for (i = 0; i < __MT_MCUQ_MAX; i++)
mt76_queue_reset(dev, dev->mt76.q_mcu[i], true);
mt76_for_each_q_rx(&dev->mt76, i)
mt76_queue_reset(dev, &dev->mt76.q_rx[i], true);
mt76_tx_status_check(&dev->mt76, true);
return mt792x_dma_enable(dev);
}
int mt792x_wpdma_reset(struct mt792x_dev *dev, bool force)
{
int i, err;
/* clean up hw queues */
for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++)
mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true);
for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++)
mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true);
mt76_for_each_q_rx(&dev->mt76, i)
mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]);
if (force) {
err = mt792x_wfsys_reset(dev);
if (err)
return err;
}
err = mt792x_dma_reset(dev, force);
if (err)
return err;
mt76_for_each_q_rx(&dev->mt76, i)
mt76_queue_rx_reset(dev, i);
return 0;
}
EXPORT_SYMBOL_GPL(mt792x_wpdma_reset);
int mt792x_wpdma_reinit_cond(struct mt792x_dev *dev)
{
struct mt76_connac_pm *pm = &dev->pm;
int err;
/* check if the wpdma must be reinitialized */
if (mt792x_dma_need_reinit(dev)) {
/* disable interrutpts */
mt76_wr(dev, dev->irq_map->host_irq_enable, 0);
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0);
err = mt792x_wpdma_reset(dev, false);
if (err) {
dev_err(dev->mt76.dev, "wpdma reset failed\n");
return err;
}
/* enable interrutpts */
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
pm->stats.lp_wake++;
}
return 0;
}
EXPORT_SYMBOL_GPL(mt792x_wpdma_reinit_cond);
int mt792x_dma_disable(struct mt792x_dev *dev, bool force)
{
/* disable WFDMA0 */
mt76_clear(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN |
MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN |
MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
MT_WFDMA0_GLO_CFG_OMIT_RX_INFO |
MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
if (!mt76_poll_msec_tick(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_BUSY |
MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 100, 1))
return -ETIMEDOUT;
/* disable dmashdl */
mt76_clear(dev, MT_WFDMA0_GLO_CFG_EXT0,
MT_WFDMA0_CSR_TX_DMASHDL_ENABLE);
mt76_set(dev, MT_DMASHDL_SW_CONTROL, MT_DMASHDL_DMASHDL_BYPASS);
if (force) {
/* reset */
mt76_clear(dev, MT_WFDMA0_RST,
MT_WFDMA0_RST_DMASHDL_ALL_RST |
MT_WFDMA0_RST_LOGIC_RST);
mt76_set(dev, MT_WFDMA0_RST,
MT_WFDMA0_RST_DMASHDL_ALL_RST |
MT_WFDMA0_RST_LOGIC_RST);
}
return 0;
}
EXPORT_SYMBOL_GPL(mt792x_dma_disable);
void mt792x_dma_cleanup(struct mt792x_dev *dev)
{
/* disable */
mt76_clear(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_EN |
MT_WFDMA0_GLO_CFG_RX_DMA_EN |
MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN |
MT_WFDMA0_GLO_CFG_OMIT_TX_INFO |
MT_WFDMA0_GLO_CFG_OMIT_RX_INFO |
MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2);
mt76_poll_msec_tick(dev, MT_WFDMA0_GLO_CFG,
MT_WFDMA0_GLO_CFG_TX_DMA_BUSY |
MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 100, 1);
/* reset */
mt76_clear(dev, MT_WFDMA0_RST,
MT_WFDMA0_RST_DMASHDL_ALL_RST |
MT_WFDMA0_RST_LOGIC_RST);
mt76_set(dev, MT_WFDMA0_RST,
MT_WFDMA0_RST_DMASHDL_ALL_RST |
MT_WFDMA0_RST_LOGIC_RST);
mt76_dma_cleanup(&dev->mt76);
}
EXPORT_SYMBOL_GPL(mt792x_dma_cleanup);
int mt792x_poll_tx(struct napi_struct *napi, int budget)
{
struct mt792x_dev *dev;
dev = container_of(napi, struct mt792x_dev, mt76.tx_napi);
if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) {
napi_complete(napi);
queue_work(dev->mt76.wq, &dev->pm.wake_work);
return 0;
}
mt76_connac_tx_cleanup(&dev->mt76);
if (napi_complete(napi))
mt76_connac_irq_enable(&dev->mt76,
dev->irq_map->tx.all_complete_mask);
mt76_connac_pm_unref(&dev->mphy, &dev->pm);
return 0;
}
EXPORT_SYMBOL_GPL(mt792x_poll_tx);
int mt792x_poll_rx(struct napi_struct *napi, int budget)
{
struct mt792x_dev *dev;
int done;
dev = mt76_priv(napi->dev);
if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) {
napi_complete(napi);
queue_work(dev->mt76.wq, &dev->pm.wake_work);
return 0;
}
done = mt76_dma_rx_poll(napi, budget);
mt76_connac_pm_unref(&dev->mphy, &dev->pm);
return done;
}
EXPORT_SYMBOL_GPL(mt792x_poll_rx);
static void mt7927_sema_status_read(struct mt792x_dev *dev, u32 addr)
{
u32 remap;
remap = mt76_rr(dev, MT7927_PCIE2AP_REMAP_WF_0_54);
mt76_wr(dev, MT7927_PCIE2AP_REMAP_WF_0_54,
(remap & ~MT7927_PCIE2AP_REMAP_WF_0_54_MASK) |
MT7927_PCIE2AP_REMAP_WF_0_54_VAL);
usleep_range(10, 20);
mt76_rr(dev, addr);
mt76_wr(dev, MT7927_PCIE2AP_REMAP_WF_0_54, remap);
usleep_range(10, 20);
}
static int mt7927_wfsys_reset(struct mt792x_dev *dev)
{
struct mt76_dev *mdev = &dev->mt76;
u32 val;
mt7927_sema_status_read(dev, MT7927_SEMA_OWN_STA);
mt76_set(dev, MT7927_CBINFRA_RGU_WF_RST,
MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS);
usleep_range(1000, 2000);
mt76_clear(dev, MT7927_CBINFRA_RGU_WF_RST,
MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS);
usleep_range(5000, 10000);
mt76_wr(dev, MT7927_CBINFRA_MCU_OWN_SET, BIT(0));
if (!__mt76_poll_msec(mdev, MT7927_ROMCODE_INDEX, 0xffff,
MT7927_MCU_IDLE_VALUE, 200)) {
val = mt76_rr(dev, MT7927_ROMCODE_INDEX);
dev_err(mdev->dev,
"MT7927 WFSYS reset timeout (ROMCODE_INDEX=0x%04x)\n",
val & 0xffff);
return -ETIMEDOUT;
}
mt7927_sema_status_read(dev, MT7927_SEMA_OWN_STA_REP);
return 0;
}
static int mt792x_wfsys_reset_default(struct mt792x_dev *dev)
{
u32 addr = is_connac2(&dev->mt76) ? 0x18000140 : 0x7c000140;
mt76_clear(dev, addr, WFSYS_SW_RST_B);
msleep(50);
mt76_set(dev, addr, WFSYS_SW_RST_B);
if (!__mt76_poll_msec(&dev->mt76, addr, WFSYS_SW_INIT_DONE,
WFSYS_SW_INIT_DONE, 500))
return -ETIMEDOUT;
return 0;
}
int mt792x_wfsys_reset(struct mt792x_dev *dev)
{
if (is_mt7927(&dev->mt76))
return mt7927_wfsys_reset(dev);
return mt792x_wfsys_reset_default(dev);
}
EXPORT_SYMBOL_GPL(mt792x_wfsys_reset);