* [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
@ 2026-08-17 9:06 Daniel Pawlik
2026-08-17 9:52 ` Qingfang Deng
2026-08-17 23:17 ` Jakub Kicinski
0 siblings, 2 replies; 9+ messages in thread
From: Daniel Pawlik @ 2026-08-17 9:06 UTC (permalink / raw)
To: netdev; +Cc: lorenzo, win847, linux-arm-kernel, linux-mediatek, Daniel Pawlik
On EN7581 + MT7996 (Gemtek W1700K), mapping small caller buffers with
DMA_BIDIRECTIONAL regresses NPU version probe: the mailbox completes
successfully but WLAN_FUNC_GET_WAIT_NPU_VERSION reads as 0.0 instead of
0.1111.
Allocate at least SMP_CACHE_BYTES for each mailbox payload and map
ALIGN(len, SMP_CACHE_BYTES) with DMA_BIDIRECTIONAL while programming
the original payload length into the mailbox length register.
Tested on Quantum Fiber / Gemtek W1700K (EN7581 + MT7996), kernel
6.18.44, including two cold reboots and sustained WiFi use.
Fixes: 6f884eb87a79 ("net: airoha: Fix DMA direction for NPU mailbox buffer")
Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260814110017.2795022-1-pawlik.dan@gmail.com/
Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260809152813.585797-1-pawlik.dan@gmail.com/
Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260805070851.2885888-1-pawlik.dan@gmail.com/
Assisted-by: Cursor:composer-2
Signed-off-by: Daniel Pawlik <pawlik.dan@gmail.com>
---
drivers/net/ethernet/airoha/airoha_npu.c | 25 +++++++++++++++---------
1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c
index b679bed952de..d560b8763753 100644
--- a/drivers/net/ethernet/airoha/airoha_npu.c
+++ b/drivers/net/ethernet/airoha/airoha_npu.c
@@ -5,6 +5,7 @@
*/
#include <linux/devcoredump.h>
+#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include <linux/of_net.h>
@@ -160,15 +161,21 @@ struct wlan_mbox_data {
DECLARE_FLEX_ARRAY(u8, d);
};
+static size_t airoha_npu_mbox_size(size_t len)
+{
+ return ALIGN(max(len, SMP_CACHE_BYTES), SMP_CACHE_BYTES);
+}
+
static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
void *p, int size)
{
u16 core = 0; /* FIXME */
u32 val, offset = core << 4;
dma_addr_t dma_addr;
+ size_t map_len = airoha_npu_mbox_size(size);
int ret;
- dma_addr = dma_map_single(npu->dev, p, size, DMA_BIDIRECTIONAL);
+ dma_addr = dma_map_single(npu->dev, p, map_len, DMA_BIDIRECTIONAL);
ret = dma_mapping_error(npu->dev, dma_addr);
if (ret)
return ret;
@@ -191,7 +198,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
spin_unlock_bh(&npu->cores[core].lock);
- dma_unmap_single(npu->dev, dma_addr, size, DMA_BIDIRECTIONAL);
+ dma_unmap_single(npu->dev, dma_addr, map_len, DMA_BIDIRECTIONAL);
return ret;
}
@@ -333,7 +340,7 @@ static int airoha_npu_ppe_init(struct airoha_npu *npu)
struct ppe_mbox_data *ppe_data;
int err;
- ppe_data = kzalloc_obj(*ppe_data);
+ ppe_data = kzalloc(airoha_npu_mbox_size(sizeof(*ppe_data)), GFP_KERNEL);
if (!ppe_data)
return -ENOMEM;
@@ -354,7 +361,7 @@ static int airoha_npu_ppe_deinit(struct airoha_npu *npu)
struct ppe_mbox_data *ppe_data;
int err;
- ppe_data = kzalloc_obj(*ppe_data);
+ ppe_data = kzalloc(airoha_npu_mbox_size(sizeof(*ppe_data)), GFP_KERNEL);
if (!ppe_data)
return -ENOMEM;
@@ -375,7 +382,7 @@ static int airoha_npu_ppe_flush_sram_entries(struct airoha_npu *npu,
struct ppe_mbox_data *ppe_data;
int err;
- ppe_data = kzalloc_obj(*ppe_data);
+ ppe_data = kzalloc(airoha_npu_mbox_size(sizeof(*ppe_data)), GFP_KERNEL);
if (!ppe_data)
return -ENOMEM;
@@ -399,7 +406,7 @@ static int airoha_npu_foe_commit_entry(struct airoha_npu *npu,
struct ppe_mbox_data *ppe_data;
int err;
- ppe_data = kzalloc_obj(*ppe_data, GFP_ATOMIC);
+ ppe_data = kzalloc(airoha_npu_mbox_size(sizeof(*ppe_data)), GFP_ATOMIC);
if (!ppe_data)
return -ENOMEM;
@@ -434,7 +441,7 @@ static int airoha_npu_ppe_stats_setup(struct airoha_npu *npu,
int err, size = num_stats_entries * sizeof(*npu->stats);
struct ppe_mbox_data *ppe_data;
- ppe_data = kzalloc_obj(*ppe_data, GFP_ATOMIC);
+ ppe_data = kzalloc(airoha_npu_mbox_size(sizeof(*ppe_data)), GFP_ATOMIC);
if (!ppe_data)
return -ENOMEM;
@@ -466,7 +473,7 @@ static int airoha_npu_wlan_msg_send(struct airoha_npu *npu, int ifindex,
int err, len;
len = sizeof(*wlan_data) + data_len;
- wlan_data = kzalloc(len, gfp);
+ wlan_data = kzalloc(airoha_npu_mbox_size(len), gfp);
if (!wlan_data)
return -ENOMEM;
@@ -489,7 +496,7 @@ static int airoha_npu_wlan_msg_get(struct airoha_npu *npu, int ifindex,
int err, len;
len = sizeof(*wlan_data) + data_len;
- wlan_data = kzalloc(len, gfp);
+ wlan_data = kzalloc(airoha_npu_mbox_size(len), gfp);
if (!wlan_data)
return -ENOMEM;
--
2.55.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
2026-08-17 9:06 [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA Daniel Pawlik
@ 2026-08-17 9:52 ` Qingfang Deng
2026-08-17 23:17 ` Jakub Kicinski
1 sibling, 0 replies; 9+ messages in thread
From: Qingfang Deng @ 2026-08-17 9:52 UTC (permalink / raw)
To: Daniel Pawlik, netdev; +Cc: lorenzo, win847, linux-arm-kernel, linux-mediatek
Hi,
On 2026/8/17 17:06, Daniel Pawlik wrote:
> On EN7581 + MT7996 (Gemtek W1700K), mapping small caller buffers with
> DMA_BIDIRECTIONAL regresses NPU version probe: the mailbox completes
> successfully but WLAN_FUNC_GET_WAIT_NPU_VERSION reads as 0.0 instead of
> 0.1111.
>
> Allocate at least SMP_CACHE_BYTES for each mailbox payload and map
> ALIGN(len, SMP_CACHE_BYTES) with DMA_BIDIRECTIONAL while programming
> the original payload length into the mailbox length register.
>
> Tested on Quantum Fiber / Gemtek W1700K (EN7581 + MT7996), kernel
> 6.18.44, including two cold reboots and sustained WiFi use.
>
> Fixes: 6f884eb87a79 ("net: airoha: Fix DMA direction for NPU mailbox buffer")
> Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260814110017.2795022-1-pawlik.dan@gmail.com/
> Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260809152813.585797-1-pawlik.dan@gmail.com/
> Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260805070851.2885888-1-pawlik.dan@gmail.com/
> Assisted-by: Cursor:composer-2
> Signed-off-by: Daniel Pawlik <pawlik.dan@gmail.com>
> ---
> drivers/net/ethernet/airoha/airoha_npu.c | 25 +++++++++++++++---------
> 1 file changed, 16 insertions(+), 9 deletions(-)
>
> --
> 2.55.0
>
> diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c
> index b679bed952de..d560b8763753 100644
> --- a/drivers/net/ethernet/airoha/airoha_npu.c
> +++ b/drivers/net/ethernet/airoha/airoha_npu.c
> @@ -5,6 +5,7 @@
> */
>
> #include <linux/devcoredump.h>
> +#include <linux/dma-mapping.h>
> #include <linux/firmware.h>
> #include <linux/platform_device.h>
> #include <linux/of_net.h>
> @@ -160,15 +161,21 @@ struct wlan_mbox_data {
> DECLARE_FLEX_ARRAY(u8, d);
> };
>
> +static size_t airoha_npu_mbox_size(size_t len)
> +{
> + return ALIGN(max(len, SMP_CACHE_BYTES), SMP_CACHE_BYTES);
SMP_CACHE_BYTES defaults to L1_CACHE_BYTES on Aarch64. On Cortex-A53
this is fine, but some CPU's L2 cache line size might differ. For
portability, please use dma_get_cache_alignment() instead.
Also "max" is redundant.
> +}
> +
> static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
> void *p, int size)
> {
> u16 core = 0; /* FIXME */
> u32 val, offset = core << 4;
> dma_addr_t dma_addr;
> + size_t map_len = airoha_npu_mbox_size(size);
> int ret;
>
> - dma_addr = dma_map_single(npu->dev, p, size, DMA_BIDIRECTIONAL);
> + dma_addr = dma_map_single(npu->dev, p, map_len, DMA_BIDIRECTIONAL);
You don't have to align the size passed to DMA API. The underlying
implementation already aligns it.
> ret = dma_mapping_error(npu->dev, dma_addr);
> if (ret)
> return ret;
> @@ -191,7 +198,7 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
>
> spin_unlock_bh(&npu->cores[core].lock);
>
> - dma_unmap_single(npu->dev, dma_addr, size, DMA_BIDIRECTIONAL);
> + dma_unmap_single(npu->dev, dma_addr, map_len, DMA_BIDIRECTIONAL);
>
> return ret;
> }
Best regards,
Qingfang
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
2026-08-17 9:06 [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA Daniel Pawlik
2026-08-17 9:52 ` Qingfang Deng
@ 2026-08-17 23:17 ` Jakub Kicinski
2026-08-18 6:23 ` Daniel Pawlik
1 sibling, 1 reply; 9+ messages in thread
From: Jakub Kicinski @ 2026-08-17 23:17 UTC (permalink / raw)
To: Daniel Pawlik; +Cc: netdev, lorenzo, win847, linux-arm-kernel, linux-mediatek
On Mon, 17 Aug 2026 11:06:01 +0200 Daniel Pawlik wrote:
> On EN7581 + MT7996 (Gemtek W1700K), mapping small caller buffers with
> DMA_BIDIRECTIONAL regresses NPU version probe: the mailbox completes
> successfully but WLAN_FUNC_GET_WAIT_NPU_VERSION reads as 0.0 instead of
> 0.1111.
>
> Allocate at least SMP_CACHE_BYTES for each mailbox payload and map
> ALIGN(len, SMP_CACHE_BYTES) with DMA_BIDIRECTIONAL while programming
> the original payload length into the mailbox length register.
>
> Tested on Quantum Fiber / Gemtek W1700K (EN7581 + MT7996), kernel
> 6.18.44, including two cold reboots and sustained WiFi use.
To me the fact that this patch helps only proves that the swiotlb
implementation on this platform is wonky, no?
IOW AFAIU we are avoiding the bounce buffer so things work, but
the bounce buffer is still broken?
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
2026-08-17 23:17 ` Jakub Kicinski
@ 2026-08-18 6:23 ` Daniel Pawlik
2026-08-18 15:29 ` Jakub Kicinski
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Pawlik @ 2026-08-18 6:23 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: netdev, lorenzo, win847, linux-arm-kernel, linux-mediatek
Hi Kuba,
You may be right that the underlying issue is the SWIOTLB/bounce path on
EN7581 rather than DMA_BIDIRECTIONAL being wrong in general. W1700K does
show SWIOTLB enabled at boot.
v8 does not switch to coherent DMA; it only rounds up mailbox payload
allocations (dma_get_cache_alignment() in v9) and maps the same rounded
length with DMA_BIDIRECTIONAL, while the mailbox length register still
uses the original payload size. On this board, mapping only the payload
size regresses probe back to 0.0 even with the larger allocation, so the
rounded map length seems required here.
I have not bisected whether that avoids SWIOTLB entirely or just makes
the bounce/sync path behave. I agree a platform fix would be better if
the bounce implementation is broken. For now this restores NPU probe on
W1700K after 6f884eb.
I'm happy to gather more data (dma mapping debug, swiotlb usage, etc.) if
that helps track the root cause.
Thanks,
Daniel
wt., 18 sie 2026 o 01:17 Jakub Kicinski <kuba@kernel.org> napisał(a):
>
> On Mon, 17 Aug 2026 11:06:01 +0200 Daniel Pawlik wrote:
> > On EN7581 + MT7996 (Gemtek W1700K), mapping small caller buffers with
> > DMA_BIDIRECTIONAL regresses NPU version probe: the mailbox completes
> > successfully but WLAN_FUNC_GET_WAIT_NPU_VERSION reads as 0.0 instead of
> > 0.1111.
> >
> > Allocate at least SMP_CACHE_BYTES for each mailbox payload and map
> > ALIGN(len, SMP_CACHE_BYTES) with DMA_BIDIRECTIONAL while programming
> > the original payload length into the mailbox length register.
> >
> > Tested on Quantum Fiber / Gemtek W1700K (EN7581 + MT7996), kernel
> > 6.18.44, including two cold reboots and sustained WiFi use.
>
> To me the fact that this patch helps only proves that the swiotlb
> implementation on this platform is wonky, no?
> IOW AFAIU we are avoiding the bounce buffer so things work, but
> the bounce buffer is still broken?
--
Z poważaniem,
Daniel Pawlik
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
2026-08-18 6:23 ` Daniel Pawlik
@ 2026-08-18 15:29 ` Jakub Kicinski
2026-08-19 12:27 ` Daniel Pawlik
0 siblings, 1 reply; 9+ messages in thread
From: Jakub Kicinski @ 2026-08-18 15:29 UTC (permalink / raw)
To: Daniel Pawlik; +Cc: netdev, lorenzo, win847, linux-arm-kernel, linux-mediatek
On Tue, 18 Aug 2026 08:23:33 +0200 Daniel Pawlik wrote:
> v8 does not switch to coherent DMA; it only rounds up mailbox payload
> allocations (dma_get_cache_alignment() in v9) and maps the same rounded
> length with DMA_BIDIRECTIONAL, while the mailbox length register still
> uses the original payload size. On this board, mapping only the payload
> size regresses probe back to 0.0 even with the larger allocation, so the
> rounded map length seems required here.
Please trace into the dma API implementation on this platform where the
alignment makes a difference. The "unaligned length" path must be buggy.
We should fix it there, not in all the drivers (my concern being that
this is not the only driver that hits the issue).
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
2026-08-18 15:29 ` Jakub Kicinski
@ 2026-08-19 12:27 ` Daniel Pawlik
2026-08-20 8:20 ` Daniel Pawlik
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Pawlik @ 2026-08-19 12:27 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: netdev, lorenzo, win847, linux-arm-kernel, linux-mediatek
Hi,
I instrumented airoha_npu_send_msg() on EN7581 (Gemtek W1700K) with
DMA_BIDIRECTIONAL and the original unaligned 24-byte payload. Here is
what I found.
1) Every dma_map_single() call lands in swiotlb bounce buffers.
The driver only calls dma_set_coherent_mask(DMA_BIT_MASK(32)) at
probe but never calls dma_set_mask(). On this non-coherent ARM64
platform that appears to force all streaming DMA through swiotlb.
2) The NPU permanently hangs after ~41 mailbox calls.
The first 41 PPE SRAM-init calls succeed (mbox_status=0x7, ~10ms
each). Starting at the 42nd, the NPU never sets the DONE bit —
every subsequent call times out at 100ms (ret=-110,
mbox_status=0x1), including the WLAN version query that triggers
the "failed getting NPU fw version" probe failure.
3) Response data is never synced back through the bounce buffer.
post-sync and post-unmap hex dumps are byte-identical to pre-map
for both successful and timed-out calls.
4) Buffer pointer is 64-byte aligned (p_aligned=1), size is 24
(sub-cacheline, cache_align=64). The swiotlb slot size is 2048,
so the bounce buffer itself is always page-aligned.
5) dma_alloc_coherent mappings (shown by DMA-API debug dump) map
P==D at addresses around 0x91xxxxxx, bypassing swiotlb entirely.
The mailbox works reliably through that path.
Representative trace (last success / first failure):
[19.541] func_id=0 p=...dfc0 size=24 cache_align=64 p_aligned=1
[19.545] mapped: dma=0xff765000 dma_aligned=1 is_swiotlb=1
[19.541] mbox done: ret=0 mbox_status=0x7 <-- last success
[19.588] func_id=0 p=...dfc0 size=24 cache_align=64 p_aligned=1
[19.618] mapped: dma=0xff765800 dma_aligned=1 is_swiotlb=1
[19.728] mbox done: ret=-110 mbox_status=0x1 <-- first timeout
(all subsequent calls also timeout, NPU never recovers)
My working theory is that the missing dma_set_mask() is the root cause.
Without it the kernel bounces every streaming mapping through swiotlb,
and either the bounce-buffer sync path on this non-coherent platform
has a bug with sub-cacheline transfers, or the NPU firmware cannot
handle the latency/address-range change that bouncing introduces.
I plan to test next with dma_set_mask_and_coherent(DMA_BIT_MASK(32))
to see whether that eliminates the swiotlb bounce and restores the
mailbox. If it does, that narrows the bug to either the swiotlb sync
implementation or the platform DMA ops for non-coherent devices.
Debug instrumentation patch (applied on top of OpenWrt's 6.18.44):
https://github.com/openwrt/openwrt/commit/13ea79d9a411dc497882e820e4c881443457bd97
Full dmesg from the instrumented boot:
https://gist.github.com/danpawlik/b351e8a06218a3aaf68a2cb31a32ea8f
Thanks for help,
Dan
wt., 18 sie 2026 o 17:29 Jakub Kicinski <kuba@kernel.org> napisał(a):
>
> On Tue, 18 Aug 2026 08:23:33 +0200 Daniel Pawlik wrote:
> > v8 does not switch to coherent DMA; it only rounds up mailbox payload
> > allocations (dma_get_cache_alignment() in v9) and maps the same rounded
> > length with DMA_BIDIRECTIONAL, while the mailbox length register still
> > uses the original payload size. On this board, mapping only the payload
> > size regresses probe back to 0.0 even with the larger allocation, so the
> > rounded map length seems required here.
>
> Please trace into the dma API implementation on this platform where the
> alignment makes a difference. The "unaligned length" path must be buggy.
> We should fix it there, not in all the drivers (my concern being that
> this is not the only driver that hits the issue).
--
Z poważaniem,
Daniel Pawlik
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
2026-08-19 12:27 ` Daniel Pawlik
@ 2026-08-20 8:20 ` Daniel Pawlik
2026-08-20 17:18 ` Jakub Kicinski
0 siblings, 1 reply; 9+ messages in thread
From: Daniel Pawlik @ 2026-08-20 8:20 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: netdev, lorenzo, win847, linux-arm-kernel, linux-mediatek
Hi,
TL;DR: The kernel forces swiotlb bounce for sub-cacheline-sized DMA on
non-coherent ARM64. The EN7581 NPU cannot write to the swiotlb bounce
buffer region. Cache-line-aligned buffers bypass swiotlb entirely and
streaming DMA works — NPU version reads 0.1111, WiFi fully functional.
Test progression (EN7581 / Gemtek W1700K, kernel 6.18, cache_align=64):
Boot 1 - stock (dma_set_coherent_mask only):
91/91 swiotlb bounce, NPU hangs after ~41 calls, version 0.0
Boot 2 - + dma_set_mask_and_coherent(DMA_BIT_MASK(32)):
Still 91/91 swiotlb. Hang gone (all 116 succeed), version still 0.0.
Bounce-direct read via phys_to_virt matches pre-map — NPU never wrote.
Boot 3 - + dcache_inval_poc before bounce read:
Bounce buffer still matches pre-map after cache invalidation.
Rules out CPU cache — NPU genuinely never writes to bounce region.
Boot 4 - + phys/mask logging:
dma_mask=0xffffffff, phys fits. size=24 fails IS_ALIGNED(size, 64)
in dma_direct_map_page() — this is why swiotlb is forced.
Boot 5 - kmalloc(ALIGN(size, 64), GFP_KERNEL):
is_swiotlb=0 on all 169 mappings. Version 0.1111. 0 timeouts.
mt7996e firmware loaded, WiFi works.
Version query (size=12):
pre-map: 30 00 00 00 0a 00 00 00 00 00 00 00
post-unmap: 30 00 00 00 0a 00 00 00 57 04 00 00
Bytes 8-11 now contain response data (always zeros in boots 1-4).
Root cause:
dma_direct_map_page() forces swiotlb when:
!dev_is_dma_coherent(dev) &&
!IS_ALIGNED(phys | size, dma_get_cache_alignment())
size=24 < cache_line=64 fails this regardless of address alignment.
The swiotlb sync code itself is fine, but the NPU cannot DMA-write to
the bounce buffer address range (it reads commands fine, never writes
responses back). This is probably an EN7581 platform limitation.
Testing a fix:
Align the mailbox DMA buffer to cache line size:
aligned_size = ALIGN(size, dma_get_cache_alignment());
dma_buf = kmalloc(aligned_size, GFP_KERNEL);
dma_map_single(dev, dma_buf, aligned_size, DMA_BIDIRECTIONAL);
This keeps streaming DMA, eliminates swiotlb, and works. The separate
dma_set_mask_and_coherent() fix is still needed (fixes hang after ~41
calls) but alone does not prevent bounce — the sub-cacheline size
triggers it regardless of mask.
Debug commit:
https://github.com/openwrt/openwrt/commit/c9363b2597528eb3badef3bb04608c0d21c81b6f
Boot 5 dmesg available if needed.
Regards,
Dan
śr., 19 sie 2026 o 14:27 Daniel Pawlik <pawlik.dan@gmail.com> napisał(a):
>
> Hi,
>
> I instrumented airoha_npu_send_msg() on EN7581 (Gemtek W1700K) with
> DMA_BIDIRECTIONAL and the original unaligned 24-byte payload. Here is
> what I found.
>
> 1) Every dma_map_single() call lands in swiotlb bounce buffers.
> The driver only calls dma_set_coherent_mask(DMA_BIT_MASK(32)) at
> probe but never calls dma_set_mask(). On this non-coherent ARM64
> platform that appears to force all streaming DMA through swiotlb.
>
> 2) The NPU permanently hangs after ~41 mailbox calls.
> The first 41 PPE SRAM-init calls succeed (mbox_status=0x7, ~10ms
> each). Starting at the 42nd, the NPU never sets the DONE bit —
> every subsequent call times out at 100ms (ret=-110,
> mbox_status=0x1), including the WLAN version query that triggers
> the "failed getting NPU fw version" probe failure.
>
> 3) Response data is never synced back through the bounce buffer.
> post-sync and post-unmap hex dumps are byte-identical to pre-map
> for both successful and timed-out calls.
>
> 4) Buffer pointer is 64-byte aligned (p_aligned=1), size is 24
> (sub-cacheline, cache_align=64). The swiotlb slot size is 2048,
> so the bounce buffer itself is always page-aligned.
>
> 5) dma_alloc_coherent mappings (shown by DMA-API debug dump) map
> P==D at addresses around 0x91xxxxxx, bypassing swiotlb entirely.
> The mailbox works reliably through that path.
>
> Representative trace (last success / first failure):
>
> [19.541] func_id=0 p=...dfc0 size=24 cache_align=64 p_aligned=1
> [19.545] mapped: dma=0xff765000 dma_aligned=1 is_swiotlb=1
> [19.541] mbox done: ret=0 mbox_status=0x7 <-- last success
>
> [19.588] func_id=0 p=...dfc0 size=24 cache_align=64 p_aligned=1
> [19.618] mapped: dma=0xff765800 dma_aligned=1 is_swiotlb=1
> [19.728] mbox done: ret=-110 mbox_status=0x1 <-- first timeout
> (all subsequent calls also timeout, NPU never recovers)
>
> My working theory is that the missing dma_set_mask() is the root cause.
> Without it the kernel bounces every streaming mapping through swiotlb,
> and either the bounce-buffer sync path on this non-coherent platform
> has a bug with sub-cacheline transfers, or the NPU firmware cannot
> handle the latency/address-range change that bouncing introduces.
>
> I plan to test next with dma_set_mask_and_coherent(DMA_BIT_MASK(32))
> to see whether that eliminates the swiotlb bounce and restores the
> mailbox. If it does, that narrows the bug to either the swiotlb sync
> implementation or the platform DMA ops for non-coherent devices.
>
> Debug instrumentation patch (applied on top of OpenWrt's 6.18.44):
> https://github.com/openwrt/openwrt/commit/13ea79d9a411dc497882e820e4c881443457bd97
>
> Full dmesg from the instrumented boot:
> https://gist.github.com/danpawlik/b351e8a06218a3aaf68a2cb31a32ea8f
>
> Thanks for help,
> Dan
>
>
> wt., 18 sie 2026 o 17:29 Jakub Kicinski <kuba@kernel.org> napisał(a):
> >
> > On Tue, 18 Aug 2026 08:23:33 +0200 Daniel Pawlik wrote:
> > > v8 does not switch to coherent DMA; it only rounds up mailbox payload
> > > allocations (dma_get_cache_alignment() in v9) and maps the same rounded
> > > length with DMA_BIDIRECTIONAL, while the mailbox length register still
> > > uses the original payload size. On this board, mapping only the payload
> > > size regresses probe back to 0.0 even with the larger allocation, so the
> > > rounded map length seems required here.
> >
> > Please trace into the dma API implementation on this platform where the
> > alignment makes a difference. The "unaligned length" path must be buggy.
> > We should fix it there, not in all the drivers (my concern being that
> > this is not the only driver that hits the issue).
>
>
>
> --
> Z poważaniem,
> Daniel Pawlik
--
Z poważaniem,
Daniel Pawlik
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
2026-08-20 8:20 ` Daniel Pawlik
@ 2026-08-20 17:18 ` Jakub Kicinski
2026-08-20 20:28 ` Daniel Pawlik
0 siblings, 1 reply; 9+ messages in thread
From: Jakub Kicinski @ 2026-08-20 17:18 UTC (permalink / raw)
To: Daniel Pawlik; +Cc: netdev, lorenzo, win847, linux-arm-kernel, linux-mediatek
On Thu, 20 Aug 2026 10:20:08 +0200 Daniel Pawlik wrote:
> size=24 < cache_line=64 fails this regardless of address alignment.
> The swiotlb sync code itself is fine, but the NPU cannot DMA-write to
> the bounce buffer address range (it reads commands fine, never writes
> responses back). This is probably an EN7581 platform limitation.
Sure, that's pretty much what GPT told us some revisions ago.
The question is why.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA
2026-08-20 17:18 ` Jakub Kicinski
@ 2026-08-20 20:28 ` Daniel Pawlik
0 siblings, 0 replies; 9+ messages in thread
From: Daniel Pawlik @ 2026-08-20 20:28 UTC (permalink / raw)
To: Jakub Kicinski; +Cc: netdev, lorenzo, win847, linux-arm-kernel, linux-mediatek
Hi Kuba,
Fair point - the interesting part is the mechanism. I don't have the
EN7581 TRM, so I can't name the exact register. From ATF, DT, NPU
firmware, and the instrumented kernel path, the picture looks like this:
TL;DR: Sub-cacheline map length forces SWIOTLB on this non-coherent
arm64 setup. SWIOTLB sync/invalidation doesn't explain the failure -
the response never appears in the bounce buffer. Cache-line-aligned
streaming buffers avoid SWIOTLB entirely and the probe reads 0.1111. The
remaining question is why the NPU doesn't produce visible writes in
the bounce region (high phys ~0xFF6x vs vendor DMA pools ~0x84-0x91).
What's ruled out:
- No IOMMU/SMMU on this SoC; DMA addresses equal physical addresses.
- No EMI MPU - unlike MT7622/MT7986, the Airoha ATF [1] configures
only TZPC (secure/non-secure for NPU SRAM and EFUSE), not DMA
address ranges.
- No PMP in NPU firmware — disassembly of the RISC-V binary shows
zero accesses to pmpcfg*/pmpaddr* CSRs.
- No dma-ranges in DT - the soc node uses identity-mapped ranges;
the kernel sees no address translation for NPU DMA.
- set_memory_decrypted() on swiotlb pages is a no-op on standard
arm64 (no CCA/memory encryption on EN7581).
What the bus fabric looks like:
The ATF bus test code [2] reveals the internal topology. Each DMA
engine has a dedicated port through the R2C (RBUS-to-DRAM converter)
bridge:
port 0: GDMA/HSDMA
port 1: PPE
port 2: QDMA LAN (TX)
port 3: QDMA WAN (TX)
port 4: TDMA
port 5: NPU
The RBUS also has a "DMA block" mechanism (0x1FA000EC) that
serializes CPU writes against DMA reads within 64B/128B-masked
regions, with per-engine block counters at 0x1FA000F4-0x1FA00108.
All vendor DMA buffers sit in one contiguous region:
npu-binary: 0x84000000 (10MB)
qdma0-buf: 0x87000000 (32MB)
qdma1-buf: 0x89000000 (16MB)
npu-pkt: 0x8A000000 (44MB)
npu-txpkt: 0x8CC00000 (64MB)
npu-txbufid: 0x90C00000
npu-ba: 0x90C06800
The ATF bus test uses DRAM_TEST_BASE = 0x84000000 exclusively -
no test exercises DMA at high physical addresses.
The NPU reads the mailbox command from the swiotlb bounce address
(~0xFF6D0000) successfully - mbox_status returns success. But it
never writes the response back; the buffer stays zero, causing a
timeout.
Read-succeeds/write-fails at a high physical address, while all
production DMA stays in 0x84-0x91, points to the R2C bridge's
write path having a narrower address reach than the read path.
Whether that's an address decode window, a write-buffer range
check, or a port-specific routing constraint isn't visible in
the open-source ATF or kernel code - likely hardwired or set by
boot ROM before ATF runs.
So I suspect this is less "swiotlb unaligned-length sync is broken"
and more "this DMA master shouldn't be bounced to that DRAM region" -
but I can't prove the bridge/decode story without vendor docs.
Regards,
Dan
[1] https://github.com/Ansuel/atf-airoha
[2] atf-airoha/plat/ecnt/en7523/cpu_bus_bl2_test.c
czw., 20 sie 2026 o 19:18 Jakub Kicinski <kuba@kernel.org> napisał(a):
>
> On Thu, 20 Aug 2026 10:20:08 +0200 Daniel Pawlik wrote:
> > size=24 < cache_line=64 fails this regardless of address alignment.
> > The swiotlb sync code itself is fine, but the NPU cannot DMA-write to
> > the bounce buffer address range (it reads commands fine, never writes
> > responses back). This is probably an EN7581 platform limitation.
>
> Sure, that's pretty much what GPT told us some revisions ago.
> The question is why.
--
Z poważaniem,
Daniel Pawlik
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-08-20 20:28 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-17 9:06 [PATCH v8] net: airoha: npu: use cacheline-sized buffers for mailbox DMA Daniel Pawlik
2026-08-17 9:52 ` Qingfang Deng
2026-08-17 23:17 ` Jakub Kicinski
2026-08-18 6:23 ` Daniel Pawlik
2026-08-18 15:29 ` Jakub Kicinski
2026-08-19 12:27 ` Daniel Pawlik
2026-08-20 8:20 ` Daniel Pawlik
2026-08-20 17:18 ` Jakub Kicinski
2026-08-20 20:28 ` Daniel Pawlik
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.