accel: ethosu: Handle U85 internal chaining buffer

The Ethos-U85 supports an internal chaining buffer as temporary storage
between some operations. When chaining is activated, the IFM/OFM region
setting selects a chaining buffer rather than a region, and the IFM/OFM
base addresses don't matter. In this case, the feature matrix size
calculations should be skipped. Otherwise, the command stream will be
intermittently rejected depending on prior feature matrix base
addresses.

Fixes: 5a5e9c0228 ("accel: Add Arm Ethos-U NPU driver")
Acked-by: Tomeu Vizoso <tomeu@tomeuvizoso.net>
Link: https://patch.msgid.link/20260720231450.485221-2-robh@kernel.org
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
This commit is contained in:
Rob Herring (Arm) 2026-07-20 18:14:48 -05:00
parent 18a551482a
commit 6b7e006629

View File

@ -192,7 +192,15 @@ static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
return len;
}
static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
static bool feat_matrix_chained(struct ethosu_device *edev, struct feat_matrix *fm)
{
u32 storage = fm->precision >> 14;
return !ethosu_is_u65(edev) && storage == 2;
}
static u64 feat_matrix_length(struct ethosu_device *edev,
struct ethosu_validated_cmdstream_info *info,
struct feat_matrix *fm,
u32 x, u32 y, u32 c, bool ofm)
{
@ -203,6 +211,9 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
if (fm->region < 0)
return U64_MAX;
if (feat_matrix_chained(edev, fm))
return 0;
switch (storage) {
case 0:
if (x >= fm->width0 + 1) {
@ -223,6 +234,8 @@ static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
tile = 1;
}
break;
default:
return U64_MAX;
}
if (fm->base[tile] == U64_MAX)
return U64_MAX;
@ -251,6 +264,7 @@ static int calc_sizes(struct drm_device *ddev,
u16 op, struct cmd_state *st,
bool ifm, bool ifm2, bool weight, bool scale)
{
struct ethosu_device *edev = to_ethosu_device(ddev);
u64 len;
if (ifm) {
@ -268,7 +282,7 @@ static int calc_sizes(struct drm_device *ddev,
if (ifm_height < 0 || ifm_width < 0)
return -EINVAL;
len = feat_matrix_length(info, &st->ifm, ifm_width,
len = feat_matrix_length(edev, info, &st->ifm, ifm_width,
ifm_height, st->ifm.depth, false);
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
op, st->ifm.region, st->ifm.base[0], len);
@ -277,7 +291,7 @@ static int calc_sizes(struct drm_device *ddev,
}
if (ifm2) {
len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
len = feat_matrix_length(edev, info, &st->ifm2, st->ifm.depth,
0, st->ofm.depth, false);
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
op, st->ifm2.region, st->ifm2.base[0], len);
@ -309,13 +323,14 @@ static int calc_sizes(struct drm_device *ddev,
st->scale[0].base + st->scale[0].length);
}
len = feat_matrix_length(info, &st->ofm, st->ofm.width,
len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
st->ofm.height[2], st->ofm.depth, true);
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
op, st->ofm.region, st->ofm.base[0], len);
if (len == U64_MAX)
return -EINVAL;
info->output_region[st->ofm.region] = true;
if (!feat_matrix_chained(edev, &st->ofm))
info->output_region[st->ofm.region] = true;
return 0;
}
@ -325,6 +340,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
u16 op, struct cmd_state *st,
bool ifm, bool ifm2)
{
struct ethosu_device *edev = to_ethosu_device(ddev);
u32 height, width, depth;
u64 len;
@ -333,7 +349,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
len = feat_matrix_length(info, &st->ifm, width,
len = feat_matrix_length(edev, info, &st->ifm, width,
height, depth, false);
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
op, st->ifm.region, st->ifm.base[0], len);
@ -346,7 +362,7 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width;
depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
len = feat_matrix_length(info, &st->ifm2, width,
len = feat_matrix_length(edev, info, &st->ifm2, width,
height, depth, false);
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
op, st->ifm2.region, st->ifm2.base[0], len);
@ -354,13 +370,14 @@ static int calc_sizes_elemwise(struct drm_device *ddev,
return -EINVAL;
}
len = feat_matrix_length(info, &st->ofm, st->ofm.width,
len = feat_matrix_length(edev, info, &st->ofm, st->ofm.width,
st->ofm.height[2], st->ofm.depth, true);
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
op, st->ofm.region, st->ofm.base[0], len);
if (len == U64_MAX)
return -EINVAL;
info->output_region[st->ofm.region] = true;
if (!feat_matrix_chained(edev, &st->ofm))
info->output_region[st->ofm.region] = true;
return 0;
}