mv88e6xxx_g1_atu_data_write() masked the port vector, which is correct
for a load operation (where portvec is a bitmap) but wrong for a move,
where the portvec encodes a ToPort/FromPort pair.
An all-ones ToPort value is the sentinel used to delete an entry. Chips
with fewer than eight ports (e.g. 88E6141) clipped this value, turning a
delete into a move (to port 3 on the 6141/6341). Other chips such as
the 7-port 88E6352 were unaffected by chance.
Fix this by moving the mv88e6xxx_port_mask() mask into
mv88e6xxx_g1_atu_loadpurge(), where the portvec always represents a
bitmap; the move path then writes its ToPort/FromPort pair unmasked.
Fixes: e606ca36bbf2 ("net: dsa: mv88e6xxx: rework ATU Remove")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Luke Howard <redacted>
---
drivers/net/dsa/mv88e6xxx/global1_atu.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c
index 522978f638e2f..6bee943c83575 100644
--- a/drivers/net/dsa/mv88e6xxx/global1_atu.c
+++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c
@@ -239,7 +239,7 @@ static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip,
if (entry->trunk)
data |= MV88E6XXX_G1_ATU_DATA_TRUNK;
- data |= (entry->portvec & mv88e6xxx_port_mask(chip)) << 4;
+ data |= entry->portvec << 4;
}
return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_DATA, data);
@@ -326,6 +326,11 @@ int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
if (err)
return err;
+ /* Mask portvec to a chip's real ports so they cannot spill into
+ * adjacent ATU Data register fields.
+ */
+ entry->portvec &= mv88e6xxx_port_mask(chip);
+
err = mv88e6xxx_g1_atu_data_write(chip, entry);
if (err)
return err;
--
2.43.0