* [PATCH v6] net: airoha: npu: use coherent DMA for mailbox messages
@ 2026-08-09 15:28 Daniel Pawlik
2026-08-14 1:05 ` Jakub Kicinski
0 siblings, 1 reply; 2+ messages in thread
From: Daniel Pawlik @ 2026-08-09 15:28 UTC (permalink / raw)
To: netdev; +Cc: lorenzo, win847, linux-arm-kernel, linux-mediatek, Daniel Pawlik
Commit 6f884eb87a79 ("net: airoha: Fix DMA direction for NPU mailbox
buffer") switched airoha_npu_send_msg() to DMA_BIDIRECTIONAL so
non-coherent CPUs invalidate caches before reading NPU GET responses.
On EN7581 + MT7996 that change regresses probe: the mailbox completes
successfully, but WLAN_FUNC_GET_WAIT_NPU_VERSION still reads as 0.0 and
mt76 never binds NPU offload. Healthy boards report 0.1111.
airoha_npu_send_msg() is also used from PPE foe_commit under
spin_lock_bh(), so per-message dma_alloc_coherent(GFP_ATOMIC) is a poor
fit. Allocate one device-managed coherent bounce buffer per NPU core at
probe and reuse it under the existing per-core mailbox lock. That also
keeps the buffer valid if the NPU completes a write after a mailbox
timeout.
Introduce __airoha_npu_send_msg() with an optional reply pointer so GET
callers can copy the trailing response payload once. Keep
airoha_npu_send_msg() as a wrapper that copies the full response back
into the caller buffer, matching the old bidirectional mapping.
Verified on Quantum Fiber / Gemtek W1700K (EN7581 + MT7996).
Fixes: 6f884eb87a79 ("net: airoha: Fix DMA direction for NPU mailbox buffer")
Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260805070851.2885888-1-pawlik.dan@gmail.com/
Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260806072601.1815487-1-pawlik.dan@gmail.com/
Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260807064058.1117747-1-pawlik.dan@gmail.com/
Assisted-by: Cursor:composer-2
Signed-off-by: Daniel Pawlik <pawlik.dan@gmail.com>
---
v6:
- use airoha_npu_send_msg() in ppe_stats_setup() again
- make airoha_npu_send_msg() copy the full response back into data
(same as the old bidirectional DMA mapping)
- rename local core pointer from c to core
v5:
- use u16 for reply_len and split the reply length check
- drop inline from airoha_npu_send_msg() in the .c file
v4:
- drop rsp_off; copy trailing reply_len bytes into reply pointer
- split __airoha_npu_send_msg() + thin airoha_npu_send_msg() wrapper
so SET callers stay unchanged
- fix reverse-christmas-tree local variable ordering
- constify data argument; clarify reply copy comment
v3:
- move coherent mailbox buffer into struct airoha_npu_core (buf/addr)
- allocate one buffer per core at probe
- add optional reply buffer to avoid the extra copy in
airoha_npu_wlan_msg_get()
- document AIROHA_NPU_MBOX_SIZE vs ppe_mbox_data / WLAN payloads
v2:
- allocate one coherent mailbox buffer at probe and reuse it under the
per-core lock instead of per-message dma_alloc_coherent(GFP_ATOMIC)
drivers/net/ethernet/airoha/airoha_npu.c | 67 +++++++++++++++++------
include/linux/soc/airoha/airoha_offload.h | 3 +
2 files changed, 53 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c
index b679bed952de..c839e0a419aa 100644
--- a/drivers/net/ethernet/airoha/airoha_npu.c
+++ b/drivers/net/ethernet/airoha/airoha_npu.c
@@ -23,6 +23,12 @@
#define NPU_EN7581_FIRMWARE_RV32_MAX_SIZE 0x200000
#define NPU_EN7581_FIRMWARE_DATA_MAX_SIZE 0x10000
#define NPU_DUMP_SIZE 512
+/*
+ * Mailbox DMA payload size. Covers sizeof(struct ppe_mbox_data) (28) and
+ * WLAN TLV messages (header + payload); largest in-tree WLAN payload today
+ * is 16 bytes (INODE_TXRX_REG_ADDR). Keep headroom for future commands.
+ */
+#define AIROHA_NPU_MBOX_SIZE 256
#define REG_NPU_LOCAL_SRAM 0x0
@@ -160,23 +166,34 @@ struct wlan_mbox_data {
DECLARE_FLEX_ARRAY(u8, d);
};
-static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
- void *p, int size)
+static int __airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
+ const void *data, int len, void *reply,
+ u16 reply_len)
{
- u16 core = 0; /* FIXME */
- u32 val, offset = core << 4;
- dma_addr_t dma_addr;
+ struct airoha_npu_core *core = &npu->cores[0]; /* FIXME: core */
+ u32 val, offset = 0;
int ret;
- dma_addr = dma_map_single(npu->dev, p, size, DMA_BIDIRECTIONAL);
- ret = dma_mapping_error(npu->dev, dma_addr);
- if (ret)
- return ret;
+ if (len > AIROHA_NPU_MBOX_SIZE)
+ return -EINVAL;
+
+ if (reply && reply_len > len)
+ return -EINVAL;
+
+ /*
+ * Mailbox payloads are bidirectional (CPU request, NPU response).
+ * On EN7581+MT7996, streaming DMA_BIDIRECTIONAL against the
+ * caller kzalloc() buffer can leave WLAN_FUNC_GET_WAIT_NPU_VERSION
+ * reading as 0.0 despite MBOX success. Reuse a probe-time coherent
+ * bounce buffer under the per-core lock (also used from PPE
+ * foe_commit under atomic context).
+ */
+ spin_lock_bh(&core->lock);
- spin_lock_bh(&npu->cores[core].lock);
+ memcpy(core->buf, data, len);
- regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(0) + offset, dma_addr);
- regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(1) + offset, size);
+ regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(0) + offset, core->addr);
+ regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(1) + offset, len);
regmap_read(npu->regmap, REG_CR_MBQ0_CTRL(2) + offset, &val);
regmap_write(npu->regmap, REG_CR_MBQ0_CTRL(2) + offset, val + 1);
val = FIELD_PREP(MBOX_MSG_FUNC_ID, func_id) | MBOX_MSG_WAIT_RSP;
@@ -189,13 +206,21 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
if (!ret && FIELD_GET(MBOX_MSG_STATUS, val) != NPU_MBOX_SUCCESS)
ret = -EINVAL;
- spin_unlock_bh(&npu->cores[core].lock);
+ /* Copy the trailing reply_len bytes of the response. */
+ if (!ret && reply)
+ memcpy(reply, core->buf + len - reply_len, reply_len);
- dma_unmap_single(npu->dev, dma_addr, size, DMA_BIDIRECTIONAL);
+ spin_unlock_bh(&core->lock);
return ret;
}
+static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
+ void *data, int len)
+{
+ return __airoha_npu_send_msg(npu, func_id, data, len, data, len);
+}
+
static int airoha_npu_load_firmware(struct device *dev, void __iomem *addr,
const char *fw_name, int fw_max_size)
{
@@ -497,9 +522,8 @@ static int airoha_npu_wlan_msg_get(struct airoha_npu *npu, int ifindex,
wlan_data->func_type = NPU_OP_GET;
wlan_data->func_id = func_id;
- err = airoha_npu_send_msg(npu, NPU_FUNC_WIFI, wlan_data, len);
- if (!err)
- memcpy(data, wlan_data->d, data_len);
+ err = __airoha_npu_send_msg(npu, NPU_FUNC_WIFI, wlan_data, len,
+ data, data_len);
kfree(wlan_data);
return err;
@@ -770,6 +794,15 @@ static int airoha_npu_probe(struct platform_device *pdev)
if (err)
return err;
+ for (i = 0; i < ARRAY_SIZE(npu->cores); i++) {
+ struct airoha_npu_core *core = &npu->cores[i];
+
+ core->buf = dmam_alloc_coherent(dev, AIROHA_NPU_MBOX_SIZE,
+ &core->addr, GFP_KERNEL);
+ if (!core->buf)
+ return -ENOMEM;
+ }
+
err = airoha_npu_run_firmware(dev, base, &res);
if (err)
return dev_err_probe(dev, err, "failed to run npu firmware\n");
diff --git a/include/linux/soc/airoha/airoha_offload.h b/include/linux/soc/airoha/airoha_offload.h
index 7589fccfeef6..16a988ad7467 100644
--- a/include/linux/soc/airoha/airoha_offload.h
+++ b/include/linux/soc/airoha/airoha_offload.h
@@ -173,6 +173,9 @@ struct airoha_npu {
/* protect concurrent npu memory accesses */
spinlock_t lock;
struct work_struct wdt_work;
+ /* Coherent bounce buffer for mailbox cmd/rsp */
+ void *buf;
+ dma_addr_t addr;
} cores[NPU_NUM_CORES];
int irqs[NPU_NUM_IRQ];
--
2.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v6] net: airoha: npu: use coherent DMA for mailbox messages
2026-08-09 15:28 [PATCH v6] net: airoha: npu: use coherent DMA for mailbox messages Daniel Pawlik
@ 2026-08-14 1:05 ` Jakub Kicinski
0 siblings, 0 replies; 2+ messages in thread
From: Jakub Kicinski @ 2026-08-14 1:05 UTC (permalink / raw)
To: Daniel Pawlik; +Cc: netdev, lorenzo, win847, linux-arm-kernel, linux-mediatek
On Sun, 9 Aug 2026 17:28:13 +0200 Daniel Pawlik wrote:
> Commit 6f884eb87a79 ("net: airoha: Fix DMA direction for NPU mailbox
> buffer") switched airoha_npu_send_msg() to DMA_BIDIRECTIONAL so
> non-coherent CPUs invalidate caches before reading NPU GET responses.
>
> On EN7581 + MT7996 that change regresses probe: the mailbox completes
> successfully, but WLAN_FUNC_GET_WAIT_NPU_VERSION still reads as 0.0 and
> mt76 never binds NPU offload. Healthy boards report 0.1111.
This sounds very odd. Is the DMA API broken on the platforms you are
testing? The code looks correct as is.
FWIW here's what GPT suggests:
Indeed, HEAD^ uses the DMA API correctly:
DMA_BIDIRECTIONAL map/unmap must make the NPU response visible to the CPU.
The important wrinkle is that, on arm64, these small kzalloc() buffers are
forced through SWIOTLB when mapped bidirectionally. DMA_TO_DEVICE did not
bounce them. HEAD switches to coherent memory and therefore bypasses SWIOTLB
entirely. That suggests the actual problem is one of:
- SWIOTLB memory is outside the NPU’s real DMA aperture.
- The platform’s dma-ranges/DMA mask does not describe that aperture.
- SWIOTLB copyback or cache handling is broken.
Using coherent memory for a mailbox is reasonable, but this patch masks that
underlying problem rather than explaining it. Before accepting it, I would
test a cacheline-aligned, rounded-size streaming buffer with
DMA_BIDIRECTIONAL, while programming the original payload length into the
mailbox. If that works, it strongly implicates the SWIOTLB/platform setup.
We can take a revert of the patch under Fixes if it's urgent.
The real fix requires a deeper investigation.
--
pw-bot: cr
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-14 1:05 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-09 15:28 [PATCH v6] net: airoha: npu: use coherent DMA for mailbox messages Daniel Pawlik
2026-08-14 1:05 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox