[PATCH v10 2/2] net: airoha: npu: use cacheline-sized buffers for mailbox DMA

Daniel Pawlik pawlik.dan at gmail.com
Wed Sep 30 09:19:31 PDT 2026


Hi Kuba, Lorenzo, Airoha team,

I found a concrete lead for the unresolved cacheline patch in the vendor
firmware. The installed en7581_MT7996_npu_rv32.bin matches the blob in
linux-firmware commit 1043c0303910 by SHA-256.

At file offset 0x3a9c, the Wi-Fi mailbox handler converts the supplied
buffer address before reading the command:

  lui   a5,0x40000
  addi  a4,a5,-1
  and   a4,a4,a0
  or    a4,a4,a5
  lw    a1,0(a4)

This implements (address & 0x3fffffff) | 0x40000000. Both 0xbf6cf000
and 0xff6cf000 produce 0x7f6cf000. W1700K has 2 GiB of RAM starting
at 0x80000000; its current SWIOTLB pool starts at 0xff6cf000.

The handler also returns 1 (success) for an unsupported operation type
at file offset 0x3b4a. My earlier claim that mailbox success proved a
correct command read was therefore unjustified. The traces do not
establish a read-succeeds/write-fails limitation.

This suggests an address-window problem, but the NPU-side bus mapping
still needs confirmation. The cacheline patch avoids the observed bounce
path without constraining the address of future direct allocations.

Can Airoha confirm which host DRAM range this alias covers, and whether
mailbox buffers above 0xbfffffff are supported?

I have prepared, but not yet run, a test retaining all 2 GiB of RAM and
the 12-byte DMA_BIDIRECTIONAL mapping, with the SWIOTLB boot pool below
0xc0000000. A successful version response through a verified bounced
mapping there would help distinguish the address limitation from a
SWIOTLB copy/cache issue.

Thanks,
Dan

czw., 20 sie 2026 o 11:00 Daniel Pawlik <pawlik.dan at gmail.com> napisał(a):
>
> On non-coherent ARM64 (EN7581), the kernel's dma_direct_map_page()
> forces swiotlb bounce buffering when:
>
>   !IS_ALIGNED(phys | size, dma_get_cache_alignment())
>
> The NPU mailbox buffers are 12-24 bytes, well below the 64-byte cache
> line size, so this check always fails regardless of physical address
> alignment. The swiotlb sync path itself works correctly, but the
> EN7581 NPU cannot DMA-write to the swiotlb bounce buffer address
> range — it reads commands fine (mbox_status=0x7 success) but never
> writes responses back, leaving response fields as zeros.
>
> This was confirmed through five boot traces on a Gemtek W1700K:
>
>   - Boots 1-4: all streaming DMA through swiotlb (is_swiotlb=1),
>     bounce buffer byte-identical to pre-map after NPU completion,
>     even after explicit CPU cache invalidation (dcache_inval_poc).
>     NPU version reads 0.0.
>
>   - Boot 5: kmalloc'd cache-line-aligned buffer bypasses swiotlb
>     (is_swiotlb=0 on all 169 mappings), NPU writes response data
>     directly, version reads 0.1111, WiFi fully functional.
>
> Fix by rounding mailbox buffer allocations up to
> dma_get_cache_alignment() and mapping the rounded length. The original
> payload size is still programmed into the mailbox length register.
> kmalloc returns cache-line-aligned pointers on ARM64 for allocations
> >= ARCH_KMALLOC_MINALIGN, so the IS_ALIGNED check passes and swiotlb
> is not triggered.
>
> 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: Claude:claude-opus-4-6
> Signed-off-by: Daniel Pawlik <pawlik.dan at 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 4045d1eb93ea..3416f921f961 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(len, dma_get_cache_alignment());
> +}
> +
>  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
>


-- 
Z poważaniem,
Daniel Pawlik



More information about the Linux-mediatek mailing list