* [PATCH net-next 0/6] net: dsa: mv88e6xxx: add MQPRIO/CBS offload
@ 2026-05-27 0:42 Luke Howard
2026-05-27 0:42 ` [PATCH net-next 1/6] net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag Luke Howard
` (5 more replies)
0 siblings, 6 replies; 14+ messages in thread
From: Luke Howard @ 2026-05-27 0:42 UTC (permalink / raw)
To: netdev; +Cc: Luke Howard, Kieran Tyrrell, Max Hunter, Cedric Jehasse
This series adds hardware offload of 802.1Qav (Credit Based Shaper) for
conforming Marvell switches. It also introduces a new bridge MDB flag,
MDB_FLAGS_STREAM_RESERVED, that userspace can set on RTM_NEWMDB to mark a
multicast destination as belonging to a reserved stream; the flag is
propagated through switchdev to hardware drivers that can enforce
admission of AVB / SR priority traffic only to flagged destinations.
The patches were originally proposed as an RFC in September 2025 [1], but have
been updated to absorb most of Cedric Jehasse's recent CBS changes [2] (with
the exception of support for more than two shaped queues). The principal
application is hardware-offloaded switching of AVB/TSN traffic rather than
host-initiated traffic.
In addition to programming the per-queue shaper, the following related
registers are configured:
* global isochronous queue pointer threshold
* strict-priority queue scheduling while CBS is active on a port
* in Enhanced/Secure AVB mode, hardware drops frames with AVB priorities
whose destination MAC has not been added to the MDB with the new
MDB_FLAGS_STREAM_RESERVED flag
A more detailed overview is given in drivers/net/dsa/mv88e6xxx/avb.h.
Support for MDB_FLAGS_STREAM_RESERVED in the software bridge will be submitted
as a separate patch series.
[1] https://lore.kernel.org/netdev/20250927070724.734933-1-lukeh@padl.com/
[2] https://lore.kernel.org/netdev/20260522-net-next-mv88e6xxx-cbs-v1-2-c87a8e6bcc0c@luminex.be/
Luke Howard (6):
net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag
net: dsa: mv88e6xxx: add num_tx_queues to chip info structure
net: dsa: mv88e6xxx: add MV88E6XXX_G1_ATU_CTL_MAC_AVB setter
net: dsa: mv88e6xxx: MQPRIO support
net: dsa: mv88e6xxx: CBS support
dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property
.../bindings/net/dsa/marvell,mv88e6xxx.yaml | 26 +
drivers/net/dsa/mv88e6xxx/Makefile | 3 +-
drivers/net/dsa/mv88e6xxx/avb.c | 633 ++++++++++++++++++
drivers/net/dsa/mv88e6xxx/avb.h | 228 +++++++
drivers/net/dsa/mv88e6xxx/chip.c | 372 +++++++++-
drivers/net/dsa/mv88e6xxx/chip.h | 101 +++
drivers/net/dsa/mv88e6xxx/global1.c | 9 +-
drivers/net/dsa/mv88e6xxx/global1.h | 47 +-
drivers/net/dsa/mv88e6xxx/global1_atu.c | 17 +
drivers/net/dsa/mv88e6xxx/global2.h | 14 +-
drivers/net/dsa/mv88e6xxx/global2_avb.c | 205 +++++-
drivers/net/dsa/mv88e6xxx/port.c | 54 ++
drivers/net/dsa/mv88e6xxx/port.h | 22 +
include/linux/platform_data/mv88e6xxx.h | 1 +
include/net/switchdev.h | 4 +
include/uapi/linux/if_bridge.h | 2 +
net/bridge/br_mdb.c | 12 +
net/bridge/br_private.h | 2 +
net/bridge/br_switchdev.c | 17 +-
19 files changed, 1744 insertions(+), 25 deletions(-)
create mode 100644 drivers/net/dsa/mv88e6xxx/avb.c
create mode 100644 drivers/net/dsa/mv88e6xxx/avb.h
--
2.43.0
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH net-next 1/6] net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag
2026-05-27 0:42 [PATCH net-next 0/6] net: dsa: mv88e6xxx: add MQPRIO/CBS offload Luke Howard
@ 2026-05-27 0:42 ` Luke Howard
2026-05-27 7:05 ` Nikolay Aleksandrov
2026-05-27 0:42 ` [PATCH net-next 2/6] net: dsa: mv88e6xxx: add num_tx_queues to chip info structure Luke Howard
` (4 subsequent siblings)
5 siblings, 1 reply; 14+ messages in thread
From: Luke Howard @ 2026-05-27 0:42 UTC (permalink / raw)
To: netdev
Cc: Luke Howard, Kieran Tyrrell, Max Hunter, Cedric Jehasse,
Jiri Pirko, Ivan Vecera, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Andrew Lunn,
Nikolay Aleksandrov, Ido Schimmel, linux-kernel, bridge
Add a new MDB entry flag, MDB_FLAGS_STREAM_RESERVED, that userspace
can set on RTM_NEWMDB to mark a multicast destination as belonging
to a reserved stream (e.g. IEEE 802.1Q Stream Reservation Protocol /
IEEE 1722 / TSN). The bridge core does no admission control on the
basis of the flag; it is metadata propagated through switchdev to
hardware drivers that can themselves enforce admission of AVB / SR
priority traffic only to flagged destinations.
The flag is settable via the new nested attribute MDBE_ATTR_FLAGS
(NLA_U32 bitmask, validated against MDB_FLAGS_SETTABLE_MASK), and is
reflected in dump output via br_mdb_entry.flags as is done today for
the OFFLOAD/BLOCKED/STAR_EXCL flags.
Assisted-by: Claude:claude-4.7-opus
Signed-off-by: Luke Howard <lukeh@padl.com>
---
include/net/switchdev.h | 4 ++++
include/uapi/linux/if_bridge.h | 2 ++
net/bridge/br_mdb.c | 12 ++++++++++++
net/bridge/br_private.h | 2 ++
net/bridge/br_switchdev.c | 17 +++++++++++------
5 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index ee500706496b0..03d176708b768 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -111,10 +111,14 @@ struct switchdev_obj_port_vlan {
container_of((OBJ), struct switchdev_obj_port_vlan, obj)
/* SWITCHDEV_OBJ_ID_PORT_MDB */
+
+#define SWITCHDEV_MDB_F_STREAM_RESERVED BIT(0)
+
struct switchdev_obj_port_mdb {
struct switchdev_obj obj;
unsigned char addr[ETH_ALEN];
u16 vid;
+ u32 flags;
};
#define SWITCHDEV_OBJ_PORT_MDB(OBJ) \
diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
index 21a700c02ef76..51ec314994bec 100644
--- a/include/uapi/linux/if_bridge.h
+++ b/include/uapi/linux/if_bridge.h
@@ -705,6 +705,7 @@ struct br_mdb_entry {
#define MDB_FLAGS_STAR_EXCL (1 << 2)
#define MDB_FLAGS_BLOCKED (1 << 3)
#define MDB_FLAGS_OFFLOAD_FAILED (1 << 4)
+#define MDB_FLAGS_STREAM_RESERVED (1 << 5)
__u8 flags;
__u16 vid;
struct {
@@ -760,6 +761,7 @@ enum {
MDBE_ATTR_IFINDEX,
MDBE_ATTR_SRC_VNI,
MDBE_ATTR_STATE_MASK,
+ MDBE_ATTR_FLAGS,
__MDBE_ATTR_MAX,
};
#define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1)
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
index e0c7020b12f5f..1320ccd81b0a1 100644
--- a/net/bridge/br_mdb.c
+++ b/net/bridge/br_mdb.c
@@ -146,6 +146,8 @@ static void __mdb_entry_fill_flags(struct br_mdb_entry *e, unsigned char flags)
e->flags |= MDB_FLAGS_BLOCKED;
if (flags & MDB_PG_FLAGS_OFFLOAD_FAILED)
e->flags |= MDB_FLAGS_OFFLOAD_FAILED;
+ if (flags & MDB_PG_FLAGS_STREAM_RESERVED)
+ e->flags |= MDB_FLAGS_STREAM_RESERVED;
}
static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip,
@@ -664,6 +666,7 @@ static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = {
MCAST_INCLUDE),
[MDBE_ATTR_SRC_LIST] = NLA_POLICY_NESTED(br_mdbe_src_list_pol),
[MDBE_ATTR_RTPROT] = NLA_POLICY_MIN(NLA_U8, RTPROT_STATIC),
+ [MDBE_ATTR_FLAGS] = NLA_POLICY_MASK(NLA_U32, MDB_FLAGS_STREAM_RESERVED),
};
static bool is_valid_mdb_source(struct nlattr *attr, __be16 proto,
@@ -1072,6 +1075,8 @@ static int br_mdb_add_group(const struct br_mdb_config *cfg,
if (entry->state == MDB_PERMANENT)
flags |= MDB_PG_FLAGS_PERMANENT;
+ flags |= cfg->pg_flags;
+
if (br_multicast_is_star_g(&group))
return br_mdb_add_group_star_g(cfg, mp, brmctx, flags, extack);
else
@@ -1225,6 +1230,13 @@ static int br_mdb_config_attrs_init(struct nlattr *set_attrs,
cfg->rt_protocol = nla_get_u8(mdb_attrs[MDBE_ATTR_RTPROT]);
}
+ if (mdb_attrs[MDBE_ATTR_FLAGS]) {
+ u32 user_flags = nla_get_u32(mdb_attrs[MDBE_ATTR_FLAGS]);
+
+ if (user_flags & MDB_FLAGS_STREAM_RESERVED)
+ cfg->pg_flags |= MDB_PG_FLAGS_STREAM_RESERVED;
+ }
+
return 0;
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 02671e648dac7..b9ee19448e38b 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -111,6 +111,7 @@ struct br_mdb_config {
struct br_mdb_src_entry *src_entries;
int num_src_entries;
u8 rt_protocol;
+ unsigned char pg_flags;
};
#endif
@@ -317,6 +318,7 @@ struct net_bridge_fdb_flush_desc {
#define MDB_PG_FLAGS_STAR_EXCL BIT(3)
#define MDB_PG_FLAGS_BLOCKED BIT(4)
#define MDB_PG_FLAGS_OFFLOAD_FAILED BIT(5)
+#define MDB_PG_FLAGS_STREAM_RESERVED BIT(6)
#define PG_SRC_ENT_LIMIT 32
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index 18b558a931ad9..bc05cda2f5350 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -548,7 +548,8 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
}
static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb,
- const struct net_bridge_mdb_entry *mp)
+ const struct net_bridge_mdb_entry *mp,
+ const struct net_bridge_port_group *pg)
{
if (mp->addr.proto == htons(ETH_P_IP))
ip_eth_mc_map(mp->addr.dst.ip4, mdb->addr);
@@ -560,6 +561,9 @@ static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb,
ether_addr_copy(mdb->addr, mp->addr.dst.mac_addr);
mdb->vid = mp->addr.vid;
+ mdb->flags = 0;
+ if (pg && (pg->flags & MDB_PG_FLAGS_STREAM_RESERVED))
+ mdb->flags |= SWITCHDEV_MDB_F_STREAM_RESERVED;
}
static void br_switchdev_host_mdb_one(struct net_device *dev,
@@ -575,7 +579,7 @@ static void br_switchdev_host_mdb_one(struct net_device *dev,
},
};
- br_switchdev_mdb_populate(&mdb, mp);
+ br_switchdev_mdb_populate(&mdb, mp, NULL);
switch (type) {
case RTM_NEWMDB:
@@ -622,6 +626,7 @@ static int br_switchdev_mdb_queue_one(struct list_head *mdb_list,
unsigned long action,
enum switchdev_obj_id id,
const struct net_bridge_mdb_entry *mp,
+ const struct net_bridge_port_group *pg,
struct net_device *orig_dev)
{
struct switchdev_obj_port_mdb mdb = {
@@ -632,7 +637,7 @@ static int br_switchdev_mdb_queue_one(struct list_head *mdb_list,
};
struct switchdev_obj_port_mdb *pmdb;
- br_switchdev_mdb_populate(&mdb, mp);
+ br_switchdev_mdb_populate(&mdb, mp, pg);
if (action == SWITCHDEV_PORT_OBJ_ADD &&
switchdev_port_obj_act_is_deferred(dev, action, &mdb.obj)) {
@@ -671,7 +676,7 @@ void br_switchdev_mdb_notify(struct net_device *dev,
if (!pg)
return br_switchdev_host_mdb(dev, mp, type);
- br_switchdev_mdb_populate(&mdb, mp);
+ br_switchdev_mdb_populate(&mdb, mp, pg);
mdb.obj.orig_dev = pg->key.port->dev;
switch (type) {
@@ -740,7 +745,7 @@ br_switchdev_mdb_replay(struct net_device *br_dev, struct net_device *dev,
if (mp->host_joined) {
err = br_switchdev_mdb_queue_one(&mdb_list, dev, action,
SWITCHDEV_OBJ_ID_HOST_MDB,
- mp, br_dev);
+ mp, NULL, br_dev);
if (err) {
spin_unlock_bh(&br->multicast_lock);
goto out_free_mdb;
@@ -754,7 +759,7 @@ br_switchdev_mdb_replay(struct net_device *br_dev, struct net_device *dev,
err = br_switchdev_mdb_queue_one(&mdb_list, dev, action,
SWITCHDEV_OBJ_ID_PORT_MDB,
- mp, dev);
+ mp, p, dev);
if (err) {
spin_unlock_bh(&br->multicast_lock);
goto out_free_mdb;
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next 2/6] net: dsa: mv88e6xxx: add num_tx_queues to chip info structure
2026-05-27 0:42 [PATCH net-next 0/6] net: dsa: mv88e6xxx: add MQPRIO/CBS offload Luke Howard
2026-05-27 0:42 ` [PATCH net-next 1/6] net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag Luke Howard
@ 2026-05-27 0:42 ` Luke Howard
2026-05-27 0:42 ` [PATCH net-next 3/6] net: dsa: mv88e6xxx: add MV88E6XXX_G1_ATU_CTL_MAC_AVB setter Luke Howard
` (3 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Luke Howard @ 2026-05-27 0:42 UTC (permalink / raw)
To: netdev
Cc: Luke Howard, Kieran Tyrrell, Max Hunter, Cedric Jehasse,
Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
In preparation for adding 802.1Qav support (FQTSS), add an element to
struct mv88e6xxx_info indicating the number of transmit queues supported
by each chip.
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 23 +++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/chip.h | 3 +++
2 files changed, 26 insertions(+)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index c30e4c13565a5..5d45363c67bbd 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -4106,6 +4106,8 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
goto unlock;
}
+ ds->num_tx_queues = chip->info->num_tx_queues;
+
err = mv88e6xxx_stats_setup(chip);
if (err)
goto unlock;
@@ -5873,6 +5875,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
+ .num_tx_queues = 4,
.ops = &mv88e6141_ops,
},
@@ -5899,6 +5902,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
+ .num_tx_queues = 4,
.ops = &mv88e6161_ops,
},
@@ -5924,6 +5928,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.ptp_support = true,
+ .num_tx_queues = 4,
.ops = &mv88e6165_ops,
},
@@ -5975,6 +5980,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
+ .num_tx_queues = 4,
.ops = &mv88e6172_ops,
},
@@ -6026,6 +6032,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
+ .num_tx_queues = 4,
.ops = &mv88e6176_ops,
},
@@ -6073,6 +6080,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.atu_move_port_mask = 0x1f,
+ .num_tx_queues = 8,
.ops = &mv88e6190_ops,
},
@@ -6098,6 +6106,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0x1f,
.pvt = true,
.multi_chip = true,
+ .num_tx_queues = 8,
.ops = &mv88e6190x_ops,
},
@@ -6123,6 +6132,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.ptp_support = true,
+ .num_tx_queues = 4,
.ops = &mv88e6191_ops,
},
@@ -6148,6 +6158,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.ptp_support = true,
+ .num_tx_queues = 8,
.ops = &mv88e6393x_ops,
},
@@ -6175,6 +6186,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.ptp_support = true,
+ .num_tx_queues = 8,
.ops = &mv88e6393x_ops,
},
@@ -6229,6 +6241,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
+ .num_tx_queues = 4,
.ops = &mv88e6240_ops,
},
@@ -6278,6 +6291,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.ptp_support = true,
+ .num_tx_queues = 8,
.ops = &mv88e6290_ops,
},
@@ -6306,6 +6320,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
+ .num_tx_queues = 4,
.ops = &mv88e6320_ops,
},
@@ -6334,6 +6349,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
+ .num_tx_queues = 4,
.ops = &mv88e6321_ops,
},
@@ -6361,6 +6377,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
+ .num_tx_queues = 4,
.ops = &mv88e6341_ops,
},
@@ -6411,6 +6428,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
+ .num_tx_queues = 4,
.ops = &mv88e6351_ops,
},
@@ -6438,6 +6456,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
+ .num_tx_queues = 4,
.ops = &mv88e6352_ops,
},
[MV88E6361] = {
@@ -6465,6 +6484,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.ptp_support = true,
+ .num_tx_queues = 8,
.ops = &mv88e6393x_ops,
},
[MV88E6390] = {
@@ -6493,6 +6513,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_UNDOCUMENTED,
.ptp_support = true,
+ .num_tx_queues = 8,
.ops = &mv88e6390_ops,
},
[MV88E6390X] = {
@@ -6519,6 +6540,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_UNDOCUMENTED,
.ptp_support = true,
+ .num_tx_queues = 8,
.ops = &mv88e6390x_ops,
},
@@ -6546,6 +6568,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true,
.multi_chip = true,
.ptp_support = true,
+ .num_tx_queues = 8,
.ops = &mv88e6393x_ops,
},
};
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index e966e7c4cc5de..146723fbca658 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -176,6 +176,9 @@ struct mv88e6xxx_info {
/* Supports PTP */
bool ptp_support;
+ /* Number of TX queues */
+ u8 num_tx_queues;
+
/* Internal PHY start index. 0 means that internal PHYs range starts at
* port 0, 1 means internal PHYs range starts at port 1, etc
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next 3/6] net: dsa: mv88e6xxx: add MV88E6XXX_G1_ATU_CTL_MAC_AVB setter
2026-05-27 0:42 [PATCH net-next 0/6] net: dsa: mv88e6xxx: add MQPRIO/CBS offload Luke Howard
2026-05-27 0:42 ` [PATCH net-next 1/6] net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag Luke Howard
2026-05-27 0:42 ` [PATCH net-next 2/6] net: dsa: mv88e6xxx: add num_tx_queues to chip info structure Luke Howard
@ 2026-05-27 0:42 ` Luke Howard
2026-05-27 0:42 ` [PATCH net-next 4/6] net: dsa: mv88e6xxx: MQPRIO support Luke Howard
` (2 subsequent siblings)
5 siblings, 0 replies; 14+ messages in thread
From: Luke Howard @ 2026-05-27 0:42 UTC (permalink / raw)
To: netdev
Cc: Luke Howard, Kieran Tyrrell, Max Hunter, Cedric Jehasse,
Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
Add accessors for the MACAVB bit, which controls whether certain ATU bits
cause the entry to be interpreted as AVB or NRL (non-rate-limiting)
entries. This is necessary on switches such as the 88E6352 and 88E6240
that support both AVB and NRL ATU entries.
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/global1.h | 2 ++
drivers/net/dsa/mv88e6xxx/global1_atu.c | 17 +++++++++++++++++
2 files changed, 19 insertions(+)
diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
index 3dbb7a1b8fe11..8204321502c1f 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.h
+++ b/drivers/net/dsa/mv88e6xxx/global1.h
@@ -111,6 +111,7 @@
/* Offset 0x0A: ATU Control Register */
#define MV88E6XXX_G1_ATU_CTL 0x0a
+#define MV88E6XXX_G1_ATU_CTL_MAC_AVB 0x8000
#define MV88E6XXX_G1_ATU_CTL_LEARN2ALL 0x0008
#define MV88E6161_G1_ATU_CTL_HASH_MASK 0x0003
@@ -322,6 +323,7 @@ int mv88e6390_g1_rmu_disable(struct mv88e6xxx_chip *chip);
int mv88e6xxx_g1_set_device_number(struct mv88e6xxx_chip *chip, int index);
int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all);
+int mv88e6xxx_g1_atu_set_mac_avb(struct mv88e6xxx_chip *chip, bool mac_avb);
int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip,
unsigned int msecs);
int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c
index c47f068f56b32..429a1ee44e47d 100644
--- a/drivers/net/dsa/mv88e6xxx/global1_atu.c
+++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c
@@ -41,6 +41,23 @@ int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all)
return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, val);
}
+int mv88e6xxx_g1_atu_set_mac_avb(struct mv88e6xxx_chip *chip, bool mac_avb)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_CTL, &val);
+ if (err)
+ return err;
+
+ if (mac_avb)
+ val |= MV88E6XXX_G1_ATU_CTL_MAC_AVB;
+ else
+ val &= ~MV88E6XXX_G1_ATU_CTL_MAC_AVB;
+
+ return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_ATU_CTL, val);
+}
+
int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip,
unsigned int msecs)
{
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next 4/6] net: dsa: mv88e6xxx: MQPRIO support
2026-05-27 0:42 [PATCH net-next 0/6] net: dsa: mv88e6xxx: add MQPRIO/CBS offload Luke Howard
` (2 preceding siblings ...)
2026-05-27 0:42 ` [PATCH net-next 3/6] net: dsa: mv88e6xxx: add MV88E6XXX_G1_ATU_CTL_MAC_AVB setter Luke Howard
@ 2026-05-27 0:42 ` Luke Howard
2026-05-27 0:42 ` [PATCH net-next 5/6] net: dsa: mv88e6xxx: CBS support Luke Howard
2026-05-27 0:42 ` [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property Luke Howard
5 siblings, 0 replies; 14+ messages in thread
From: Luke Howard @ 2026-05-27 0:42 UTC (permalink / raw)
To: netdev
Cc: Luke Howard, Kieran Tyrrell, Max Hunter, Cedric Jehasse,
Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Russell King, linux-kernel
Add support for MQPRIO TC for the Marvell 6352 and 6390 families of
switches. Three traffic classes are supported: legacy (TC0), low (TC1)
and high (TC2), corresponding to non-AVB, AVB Class B and Class A.
A single Ethernet frame priority can be mapped to either AVB class.
"Legacy" (non-AVB) Ethernet frame priorities are distributed amongst the
remaining queues, per the MQPRIO policy.
Owing to hardware limitations, queue and frame priority policy is
per-switch, not per-port; HW offload can only be enabled across multiple
ports if the policy on each enabled port is the same.
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/Makefile | 3 +-
drivers/net/dsa/mv88e6xxx/avb.c | 518 ++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/avb.h | 197 +++++++++
drivers/net/dsa/mv88e6xxx/chip.c | 276 ++++++++++++-
drivers/net/dsa/mv88e6xxx/chip.h | 76 ++++
drivers/net/dsa/mv88e6xxx/global1.c | 9 +-
drivers/net/dsa/mv88e6xxx/global1.h | 45 +-
drivers/net/dsa/mv88e6xxx/global2.h | 14 +-
drivers/net/dsa/mv88e6xxx/global2_avb.c | 205 +++++++++-
drivers/net/dsa/mv88e6xxx/port.c | 9 +
drivers/net/dsa/mv88e6xxx/port.h | 2 +
include/linux/platform_data/mv88e6xxx.h | 1 +
12 files changed, 1336 insertions(+), 19 deletions(-)
create mode 100644 drivers/net/dsa/mv88e6xxx/avb.c
create mode 100644 drivers/net/dsa/mv88e6xxx/avb.h
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
index b0b08c6f159c6..6123b431e255e 100644
--- a/drivers/net/dsa/mv88e6xxx/Makefile
+++ b/drivers/net/dsa/mv88e6xxx/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
-mv88e6xxx-objs := chip.o
+mv88e6xxx-objs := avb.o
+mv88e6xxx-objs += chip.o
mv88e6xxx-objs += devlink.o
mv88e6xxx-objs += global1.o
mv88e6xxx-objs += global1_atu.o
diff --git a/drivers/net/dsa/mv88e6xxx/avb.c b/drivers/net/dsa/mv88e6xxx/avb.c
new file mode 100644
index 0000000000000..c535bf4fe3623
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/avb.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Marvell 88E6xxx Switch AVB support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2024-2026 PADL Software Pty Ltd
+ */
+
+#include <linux/dcbnl.h> /* for IEEE_8021Q_MAX_PRIORITIES */
+
+#include "avb.h"
+#include "chip.h"
+#include "global1.h"
+#include "global2.h"
+#include "port.h"
+
+/* AVB operation wrappers */
+
+static int mv88e6xxx_port_avb_read(struct mv88e6xxx_chip *chip, int port,
+ int addr, u16 *data, int len)
+{
+ if (!chip->info->ops->avb_ops->port_avb_read)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->port_avb_read(chip, port, addr,
+ data, len);
+}
+
+static int mv88e6xxx_port_avb_write(struct mv88e6xxx_chip *chip, int port,
+ int addr, u16 data)
+{
+ if (!chip->info->ops->avb_ops->port_avb_write)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->port_avb_write(chip, port, addr, data);
+}
+
+static int mv88e6xxx_avb_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ if (!chip->info->ops->avb_ops->avb_read)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->avb_read(chip, addr, data, len);
+}
+
+static int mv88e6xxx_avb_write(struct mv88e6xxx_chip *chip, int addr, u16 data)
+{
+ if (!chip->info->ops->avb_ops->avb_write)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->avb_write(chip, addr, data);
+}
+
+/* 802.1Qav operation wrappers */
+
+static int mv88e6xxx_qav_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ if (!chip->info->ops->avb_ops->qav_read)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->qav_read(chip, addr, data, len);
+}
+
+static int mv88e6xxx_qav_write(struct mv88e6xxx_chip *chip, int addr, u16 data)
+{
+ if (!chip->info->ops->avb_ops->qav_write)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->qav_write(chip, addr, data);
+}
+
+static int mv88e6xxx_port_qav_read(struct mv88e6xxx_chip *chip, int port,
+ int addr, u16 *data, int len)
+{
+ if (!chip->info->ops->avb_ops->port_qav_read)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->port_qav_read(chip, port, addr, data, len);
+}
+
+static int mv88e6xxx_port_qav_write(struct mv88e6xxx_chip *chip, int port,
+ int addr, u16 data)
+{
+ if (!chip->info->ops->avb_ops->port_qav_write)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->avb_ops->port_qav_write(chip, port, addr, data);
+}
+
+static int mv88e6xxx_tc_enable(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_avb_tc_policy *policy)
+{
+ if (!chip->info->ops->tc_ops->tc_enable)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->tc_ops->tc_enable(chip, policy);
+}
+
+static int mv88e6xxx_tc_disable(struct mv88e6xxx_chip *chip)
+{
+ if (!chip->info->ops->tc_ops->tc_disable)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->tc_ops->tc_disable(chip);
+}
+
+/* MQPRIO helpers */
+
+/* Set the AVB global policy limit registers
+ *
+ * @param chip Marvell switch chip instance
+ * @param hilimit Maximum frame size allowed for AVB Class A frames
+ *
+ * @return 0 on success, or a negative error value otherwise
+ */
+static int mv88e6xxx_avb_set_hilimit(struct mv88e6xxx_chip *chip, u16 hilimit)
+{
+ u16 data;
+ int err;
+
+ if (hilimit > MV88E6XXX_AVB_CFG_HI_LIMIT_MASK)
+ return -EINVAL;
+
+ err = mv88e6xxx_avb_read(chip, MV88E6XXX_AVB_CFG_HI_LIMIT, &data, 1);
+ if (err)
+ return err;
+
+ data &= ~(MV88E6XXX_AVB_CFG_HI_LIMIT_MASK);
+ data |= MV88E6XXX_AVB_CFG_HI_LIMIT_SET(hilimit);
+
+ err = mv88e6xxx_avb_write(chip, MV88E6XXX_AVB_CFG_HI_LIMIT, data);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/* Set the global isochronous queue pointer threshold
+ *
+ * @param chip Marvell switch chip instance
+ * @param threshold Total number of pointers reserved for isochronous streams
+ *
+ * @return 0 on success, or a negative error value otherwise
+ */
+static int mv88e6xxx_qav_set_iso_ptr(struct mv88e6xxx_chip *chip, u16 threshold)
+{
+ u16 data;
+ int err;
+
+ err = mv88e6xxx_qav_read(chip, MV88E6XXX_QAV_CFG, &data, 1);
+ if (err)
+ return err;
+
+ data &= ~(MV88E6XXX_QAV_CFG_GLOBAL_ISO_PTR_MASK);
+ data |= MV88E6XXX_QAV_CFG_GLOBAL_ISO_PTR_SET(threshold);
+
+ err = mv88e6xxx_qav_write(chip, MV88E6XXX_QAV_CFG, data);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/* Map the global AVB mode to a port AVB mode.
+ *
+ * @param chip Marvell switch chip instance
+ *
+ * @return A MV88E6XXX_PORT_AVB_CFG_AVB_MODE_XXX bitmask
+ */
+static u16 mv88e6xxx_avb_get_cfg_avb_mode(struct mv88e6xxx_chip *chip)
+{
+ u16 cfg;
+
+ switch (chip->avb_tc_policy.mode) {
+ case MV88E6XXX_AVB_MODE_STANDARD:
+ cfg = MV88E6XXX_PORT_AVB_CFG_AVB_MODE_STANDARD;
+ break;
+ case MV88E6XXX_AVB_MODE_ENHANCED:
+ cfg = MV88E6XXX_PORT_AVB_CFG_AVB_MODE_ENHANCED;
+ break;
+ case MV88E6XXX_AVB_MODE_SECURE:
+ cfg = MV88E6XXX_PORT_AVB_CFG_AVB_MODE_SECURE;
+ break;
+ default:
+ cfg = MV88E6XXX_PORT_AVB_CFG_AVB_MODE_LEGACY;
+ break;
+ }
+
+ if (chip->avb_tc_policy.mode >= MV88E6XXX_AVB_MODE_ENHANCED) {
+ cfg |= MV88E6XXX_PORT_AVB_CFG_AVB_FILTER_BAD_AVB |
+ MV88E6XXX_PORT_AVB_CFG_AVB_DISCARD_BAD;
+ }
+
+ return cfg;
+}
+
+/* Enable or disable a port for AVB
+ *
+ * @param chip Marvell switch chip instance
+ * @param port Switch port
+ * @param enable If true, will enable AVB queues on this port.
+ *
+ * @return 0 on success, or a negative error value otherwise
+ */
+static int mv88e6xxx_avb_set_port_avb_mode(struct mv88e6xxx_chip *chip,
+ int port, bool enable)
+{
+ u16 cfg;
+
+ if (enable)
+ cfg = mv88e6xxx_avb_get_cfg_avb_mode(chip);
+ else
+ cfg = MV88E6XXX_PORT_AVB_CFG_AVB_MODE_LEGACY;
+
+ return mv88e6xxx_port_avb_write(chip, port, MV88E6XXX_PORT_AVB_CFG, cfg);
+}
+
+static int mv88e6xxx_avb_set_avb_mode(struct mv88e6xxx_chip *chip, bool enable)
+{
+ int port, err;
+
+ for (port = 0, err = 0; port < mv88e6xxx_num_ports(chip); ++port) {
+ if (!dsa_is_user_port(chip->ds, port))
+ continue;
+
+ err = mv88e6xxx_avb_set_port_avb_mode(chip, port, enable);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+int mv88e6xxx_avb_tc_enable(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_avb_tc_policy *policy)
+{
+ int err;
+
+ if (chip->avb_tc_policy.mode >= MV88E6XXX_AVB_MODE_ENHANCED) {
+ /* interpret AVB_NRL bits in ATU as AVB entries */
+ err = mv88e6xxx_g1_atu_set_mac_avb(chip, true);
+ if (err)
+ return err;
+ }
+
+ err = mv88e6xxx_qav_set_iso_ptr(chip, mv88e6xxx_num_ports(chip) << 6);
+ if (err)
+ goto err_mac_avb;
+
+ err = mv88e6xxx_tc_enable(chip, policy);
+ if (err)
+ goto err_iso_ptr;
+
+ err = mv88e6xxx_avb_set_avb_mode(chip, true);
+ if (err)
+ goto err_tc;
+
+ return 0;
+
+err_tc:
+ mv88e6xxx_tc_disable(chip);
+err_iso_ptr:
+ mv88e6xxx_qav_set_iso_ptr(chip, 0);
+err_mac_avb:
+ if (chip->avb_tc_policy.mode >= MV88E6XXX_AVB_MODE_ENHANCED)
+ mv88e6xxx_g1_atu_set_mac_avb(chip, false);
+
+ return err;
+}
+
+int mv88e6xxx_avb_tc_disable(struct mv88e6xxx_chip *chip)
+{
+ int err;
+
+ err = mv88e6xxx_avb_set_avb_mode(chip, false);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_tc_disable(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_qav_set_iso_ptr(chip, 0);
+ if (err)
+ return err;
+
+ if (chip->avb_tc_policy.mode >= MV88E6XXX_AVB_MODE_ENHANCED) {
+ /* don't interpret AVB NRL bits in ATU as AVB entries */
+ err = mv88e6xxx_g1_atu_set_mac_avb(chip, false);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Assign FPri to QPri mappings for each traffic class
+ *
+ * @param chip Marvell switch chip instance
+ * @param policy AVB policy settings
+ * @param map Callback for setting individual FPri:QPri mapping
+ * @param context Opaque context passed to callback function
+ *
+ * @return 0 on success, or error returned by callback
+ */
+static int mv88e6xxx_qav_assign_qpri(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_avb_tc_policy *policy,
+ int (*map)(u8 fpri, u8 qpri, void *context),
+ void *context)
+{
+ int tc0_qcount, tc0_base_qpri;
+ size_t tc0_fpri_per_qpri;
+ int err, fpri;
+
+ tc0_base_qpri = policy->map[MV88E6XXX_AVB_TC_LEGACY].qpri;
+ tc0_fpri_per_qpri =
+ DIV_ROUND_UP(IEEE_8021Q_MAX_PRIORITIES - 2,
+ policy->map[MV88E6XXX_AVB_TC_LEGACY].count);
+
+ /* Match TC1/TC2 (AVB) FPri to QPri mappings to avoid needing to
+ * configure legacy AVB registers, which map non-AVB frame FPri/QPris
+ * to non-conflicting values.
+ *
+ * Distribute TC0 (non-AVB) queues amongst remaining FPris.
+ */
+ for (fpri = 0, tc0_qcount = 0; fpri < IEEE_8021Q_MAX_PRIORITIES; fpri++) {
+ if (policy->map[MV88E6XXX_AVB_TC_LO].fpri == fpri) {
+ err = map(fpri, policy->map[MV88E6XXX_AVB_TC_LO].qpri, context);
+ } else if (policy->map[MV88E6XXX_AVB_TC_HI].fpri == fpri) {
+ err = map(fpri, policy->map[MV88E6XXX_AVB_TC_HI].qpri, context);
+ } else {
+ int qpri = tc0_base_qpri + (tc0_qcount++ / tc0_fpri_per_qpri);
+
+ err = map(fpri, qpri, context);
+ }
+
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+/* Family-specific 802.1Qav support */
+
+static inline u16 mv88e6352_avb_pri_map_to_reg(const struct mv88e6xxx_avb_priority_map map[])
+{
+ return MV88E6352_AVB_CFG_AVB_HI_FPRI_SET(map[MV88E6XXX_AVB_TC_HI].fpri) |
+ MV88E6352_AVB_CFG_AVB_HI_QPRI_SET(map[MV88E6XXX_AVB_TC_HI].qpri) |
+ MV88E6352_AVB_CFG_AVB_LO_FPRI_SET(map[MV88E6XXX_AVB_TC_LO].fpri) |
+ MV88E6352_AVB_CFG_AVB_LO_QPRI_SET(map[MV88E6XXX_AVB_TC_LO].qpri);
+}
+
+static int mv88e6352_qav_map_fpri_qpri(u8 fpri, u8 qpri, void *reg)
+{
+ mv88e6352_g1_ieee_pri_set(fpri, qpri, (u16 *)reg);
+
+ return 0;
+}
+
+static int mv88e6352_tc_enable(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_avb_tc_policy *policy)
+{
+ u16 reg = 0;
+ int err;
+ int tc;
+
+ /* Validate TC to QPri mapping */
+ for (tc = MV88E6XXX_AVB_TC_LO; tc <= MV88E6XXX_AVB_TC_HI; tc++) {
+ if (policy->map[tc].qpri < MV88E6352_AVB_QUEUE_MIN(tc) ||
+ policy->map[tc].qpri > MV88E6352_AVB_QUEUE_MAX(tc)) {
+ dev_err(chip->dev, "%s: bad QPri %d for TC %d\n",
+ __func__, policy->map[tc].qpri, tc);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ err = mv88e6xxx_avb_write(chip, MV88E6XXX_AVB_CFG_AVB,
+ mv88e6352_avb_pri_map_to_reg(policy->map));
+ if (err)
+ return err;
+
+ err = mv88e6xxx_qav_assign_qpri(chip, policy, mv88e6352_qav_map_fpri_qpri, ®);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_g1_set_ieee_pri_map(chip, reg);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static struct mv88e6xxx_avb_priority_map
+mv88e6352_init_avb_pri_map[MV88E6XXX_AVB_TC_MAX + 1] = {
+ [MV88E6XXX_AVB_TC_LO] = {
+ /* VI, queue 2 */
+ .fpri = 0x4,
+ .qpri = 0x2
+ },
+ [MV88E6XXX_AVB_TC_HI] = {
+ /* VO, queue 3 */
+ .fpri = 0x5,
+ .qpri = 0x3
+ },
+};
+
+static int mv88e6352_tc_disable(struct mv88e6xxx_chip *chip)
+{
+ int err;
+
+ err = mv88e6250_g1_ieee_pri_map(chip);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_avb_write(chip, MV88E6XXX_AVB_CFG_AVB,
+ mv88e6352_avb_pri_map_to_reg(mv88e6352_init_avb_pri_map));
+ if (err)
+ return err;
+
+ return 0;
+}
+
+const struct mv88e6xxx_tc_ops mv88e6341_tc_ops = {
+ .tc_enable = mv88e6352_tc_enable,
+ .tc_disable = mv88e6352_tc_disable,
+};
+
+const struct mv88e6xxx_tc_ops mv88e6352_tc_ops = {
+ .tc_enable = mv88e6352_tc_enable,
+ .tc_disable = mv88e6352_tc_disable,
+};
+
+static inline u16 mv88e6390_avb_pri_map_to_reg(const struct mv88e6xxx_avb_priority_map map[])
+{
+ return MV88E6390_AVB_CFG_AVB_HI_FPRI_SET(map[MV88E6XXX_AVB_TC_HI].fpri) |
+ MV88E6390_AVB_CFG_AVB_HI_QPRI_SET(map[MV88E6XXX_AVB_TC_HI].qpri) |
+ MV88E6390_AVB_CFG_AVB_LO_FPRI_SET(map[MV88E6XXX_AVB_TC_LO].fpri) |
+ MV88E6390_AVB_CFG_AVB_LO_QPRI_SET(map[MV88E6XXX_AVB_TC_LO].qpri);
+}
+
+static int mv88e6390_qav_map_fpri_qpri(u8 fpri, u8 qpri, void *context)
+{
+ int err, port;
+ struct mv88e6xxx_chip *chip = (struct mv88e6xxx_chip *)context;
+
+ for (port = 0, err = 0; port < mv88e6xxx_num_ports(chip); port++) {
+ if (!dsa_is_user_port(chip->ds, port))
+ continue;
+
+ err = mv88e6390_port_set_ieeepmt_ingress_pcp(chip, port, fpri,
+ fpri, qpri);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+static int mv88e6390_tc_enable(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_avb_tc_policy *policy)
+{
+ int err;
+
+ err = mv88e6xxx_avb_write(chip, MV88E6XXX_AVB_CFG_AVB,
+ mv88e6390_avb_pri_map_to_reg(policy->map));
+ if (err)
+ return err;
+
+ err = mv88e6xxx_qav_assign_qpri(chip, policy, mv88e6390_qav_map_fpri_qpri, chip);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static struct mv88e6xxx_avb_priority_map
+mv88e6390_init_avb_pri_map[MV88E6XXX_AVB_TC_MAX + 1] = {
+ [MV88E6XXX_AVB_TC_LO] = {
+ /* EE, queue 6 */
+ .fpri = 0x2,
+ .qpri = 0x6
+ },
+ [MV88E6XXX_AVB_TC_HI] = {
+ /* CA, queue 7 */
+ .fpri = 0x3,
+ .qpri = 0x7
+ },
+};
+
+static int mv88e6390_tc_disable(struct mv88e6xxx_chip *chip)
+{
+ int err, port;
+
+ for (port = 0, err = 0; port < mv88e6xxx_num_ports(chip); port++) {
+ if (!dsa_is_user_port(chip->ds, port))
+ continue;
+
+ err = mv88e6390_port_tag_remap(chip, port);
+ if (err)
+ break;
+ }
+
+ err = mv88e6xxx_avb_write(chip, MV88E6XXX_AVB_CFG_AVB,
+ mv88e6390_avb_pri_map_to_reg(mv88e6390_init_avb_pri_map));
+ if (err)
+ return err;
+
+ return err;
+}
+
+const struct mv88e6xxx_tc_ops mv88e6390_tc_ops = {
+ .tc_enable = mv88e6390_tc_enable,
+ .tc_disable = mv88e6390_tc_disable,
+};
diff --git a/drivers/net/dsa/mv88e6xxx/avb.h b/drivers/net/dsa/mv88e6xxx/avb.h
new file mode 100644
index 0000000000000..72d1c26a84731
--- /dev/null
+++ b/drivers/net/dsa/mv88e6xxx/avb.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Marvell 88E6xxx Switch PTP support
+ *
+ * Copyright (c) 2008 Marvell Semiconductor
+ *
+ * Copyright (c) 2024 PADL Software Pty Ltd
+ */
+
+#ifndef _MV88E6XXX_AVB_H
+#define _MV88E6XXX_AVB_H
+
+#include "chip.h"
+
+/* The Marvell 6352 and 6390 families support the Credit Based Shaper defined
+ * in 802.1Qav. (The 6390 family also supports 802.1Qbv but that is presently
+ * unimplemented.)
+ *
+ * On ingress, frame priority tags (PCP for L2) are mapped to an internal frame
+ * priority, or FPri. This mapping is per-port on all switches that support
+ * AVB. The 6352 family has a per-switch mapping between FPri and QPri (the TX
+ * queue), whereas this mapping on the 6390 family is per-port. Both families
+ * support per-port CBS policies.
+ *
+ * In addition to traffic shaping, the Marvell switches also support a form of
+ * admission control, where true AVB frames are distinguished from other frames
+ * that share the same frame priority. This is done by flagging ATU entries
+ * with the ATU_DATA_STATE_{UC,MC}_STATIC_AVB_NRL flags. When the port is
+ * configured in Enhanced, rather than Standard, AVB mode, AVB frames will only
+ * be forwarded when the DA ATU entry has this bit set. Admission control would
+ * typically be managed by a user-space 802.1Q Stream Reservation Protocol
+ * (SRP) service. This, in combination with the global IsoPtrs register,
+ * ensures that AVB streams always have priority over other traffic. These
+ * features are necessary for Avnu certification.
+ *
+ * A final point is whether Linux TCs should be mapped to AVB classes or
+ * directly to queues. Enhanced AVB support above requires dedicated, global
+ * queues for Class A and B traffic, implying a mapping between TCs and AVB
+ * classes. Unfortunately this means that Marvell switches that support a
+ * larger number of TX queues (such as the 6390 family) must still funnel their
+ * MQPRIO policy through these three TCs. Further, this limits the 6390 family
+ * to per-switch MQPRIO policies whereas otherwise port-port policies could be
+ * supported.
+ *
+ * With that in mind, the current implementation has the following properties:
+ *
+ * - there are only three traffic classes, hi (2), lo (1) and legacy (0),
+ * which correspond to AVB Class A, B, and non-AVB traffic
+ *
+ * - only a single Ethernet frame priority can be mapped to either of the
+ * AVB traffic classes
+ *
+ * - legacy Ethernet frame priorities are distributed amongst the
+ * remaining queues per the MQPRIO policy
+ *
+ * - queue and frame priority policy is per-switch, not per-port, so
+ * HW offload can only be enabled across multiple ports if the policy
+ * on each port is the same
+ *
+ * - on the 6352 family of switches, TC2 can only be in queue 2/3 and
+ * TC1 only in queue 1/2; this does not apply to the 6390 family
+ *
+ * - because the Netlink API has no way to distinguish between FDB/MDB
+ * entries managed by SRP from those that are not, the
+ * "marvell,mv88e6xxx-avb-mode" device tree property controls whether
+ * a FDB or MDB entry is required in order for AVB frames to egress.
+ * To avoid breaking static IP MDB entries, only multicast addresses
+ * with OUI prefix of 91:e0:ff (IEEE 1722 Annex D) will have the AVB
+ * flag set on their ATU entry.
+ */
+
+/* Global AVB registers */
+
+/* Offset 0x00: AVB Global Config */
+
+#define MV88E6XXX_AVB_CFG_AVB 0x00
+#define MV88E6XXX_AVB_CFG_LEGACY 0x04
+
+/* Common AVB Global Config */
+
+#define MV88E6XXX_AVB_CFG_AVB_HI_FPRI_MASK GENMASK(14, 12)
+#define MV88E6XXX_AVB_CFG_AVB_HI_FPRI_GET(p) FIELD_GET(MV88E6XXX_AVB_CFG_AVB_HI_FPRI_MASK, p)
+#define MV88E6XXX_AVB_CFG_AVB_HI_FPRI_SET(p) FIELD_PREP(MV88E6XXX_AVB_CFG_AVB_HI_FPRI_MASK, p)
+
+#define MV88E6XXX_AVB_CFG_AVB_LO_FPRI_MASK GENMASK(6, 4)
+#define MV88E6XXX_AVB_CFG_AVB_LO_FPRI_GET(p) FIELD_GET(MV88E6XXX_AVB_CFG_AVB_LO_FPRI_MASK, p)
+#define MV88E6XXX_AVB_CFG_AVB_LO_FPRI_SET(p) FIELD_PREP(MV88E6XXX_AVB_CFG_AVB_LO_FPRI_MASK, p)
+
+#define MV88E6XXX_AVB_CFG_HI_LIMIT 0x08 /* max frame size for Class A */
+#define MV88E6XXX_AVB_CFG_HI_LIMIT_MASK GENMASK(10, 0)
+#define MV88E6XXX_AVB_CFG_HI_LIMIT_GET(p) FIELD_GET(MV88E6XXX_AVB_CFG_HI_LIMIT_MASK, p)
+#define MV88E6XXX_AVB_CFG_HI_LIMIT_SET(p) FIELD_PREP(MV88E6XXX_AVB_CFG_HI_LIMIT_MASK, p)
+
+#define MV88E6XXX_AVB_CFG_OUI_HI 0x0C
+#define MV88E6XXX_AVB_CFG_OUI_LO 0x0D
+
+/* 6352 Family AVB Global Config (4 TX queues) */
+
+#define MV88E6352_AVB_CFG_AVB_HI_FPRI_GET(p) MV88E6XXX_AVB_CFG_AVB_HI_FPRI_GET(p)
+#define MV88E6352_AVB_CFG_AVB_HI_FPRI_SET(p) MV88E6XXX_AVB_CFG_AVB_HI_FPRI_SET(p)
+
+#define MV88E6352_AVB_CFG_AVB_HI_QPRI_MASK GENMASK(9, 8)
+#define MV88E6352_AVB_CFG_AVB_HI_QPRI_GET(p) FIELD_GET(MV88E6352_AVB_CFG_AVB_HI_QPRI_MASK, p)
+#define MV88E6352_AVB_CFG_AVB_HI_QPRI_SET(p) FIELD_PREP(MV88E6352_AVB_CFG_AVB_HI_QPRI_MASK, p)
+
+#define MV88E6352_AVB_CFG_AVB_LO_FPRI_GET(p) MV88E6XXX_AVB_CFG_AVB_LO_FPRI_GET(p)
+#define MV88E6352_AVB_CFG_AVB_LO_FPRI_SET(p) MV88E6XXX_AVB_CFG_AVB_LO_FPRI_SET(p)
+
+#define MV88E6352_AVB_CFG_AVB_LO_QPRI_MASK GENMASK(1, 0)
+#define MV88E6352_AVB_CFG_AVB_LO_QPRI_GET(p) FIELD_GET(MV88E6352_AVB_CFG_AVB_LO_QPRI_MASK, p)
+#define MV88E6352_AVB_CFG_AVB_LO_QPRI_SET(p) FIELD_PREP(MV88E6352_AVB_CFG_AVB_LO_QPRI_MASK, p)
+
+/* 6390 Family AVB Global Config (8 TX queues) */
+
+#define MV88E6390_AVB_CFG_AVB_HI_FPRI_GET(p) MV88E6XXX_AVB_CFG_AVB_HI_FPRI_GET(p)
+#define MV88E6390_AVB_CFG_AVB_HI_FPRI_SET(p) MV88E6XXX_AVB_CFG_AVB_HI_FPRI_SET(p)
+
+#define MV88E6390_AVB_CFG_AVB_HI_QPRI_MASK GENMASK(10, 8)
+#define MV88E6390_AVB_CFG_AVB_HI_QPRI_GET(p) FIELD_GET(MV88E6390_AVB_CFG_AVB_HI_QPRI_MASK, p)
+#define MV88E6390_AVB_CFG_AVB_HI_QPRI_SET(p) FIELD_PREP(MV88E6390_AVB_CFG_AVB_HI_QPRI_MASK, p)
+
+#define MV88E6390_AVB_CFG_AVB_LO_FPRI_GET(p) MV88E6XXX_AVB_CFG_AVB_LO_FPRI_GET(p)
+#define MV88E6390_AVB_CFG_AVB_LO_FPRI_SET(p) MV88E6XXX_AVB_CFG_AVB_LO_FPRI_SET(p)
+
+#define MV88E6390_AVB_CFG_AVB_LO_QPRI_MASK GENMASK(2, 0)
+#define MV88E6390_AVB_CFG_AVB_LO_QPRI_GET(p) FIELD_GET(MV88E6390_AVB_CFG_AVB_LO_QPRI_MASK, p)
+#define MV88E6390_AVB_CFG_AVB_LO_QPRI_SET(p) FIELD_PREP(MV88E6390_AVB_CFG_AVB_LO_QPRI_MASK, p)
+
+#define MV88E6352_AVB_QUEUE_MIN(tc) (tc)
+#define MV88E6352_AVB_QUEUE_MAX(tc) ((tc) + 1)
+
+/* Global Qav registers */
+#define MV88E6XXX_QAV_CFG 0x00
+
+#define MV88E6XXX_QAV_CFG_GLOBAL_ISO_PTR_MASK GENMASK(9, 0)
+#define MV88E6XXX_QAV_CFG_GLOBAL_ISO_PTR_GET(x) FIELD_GET(MV88E6XXX_QAV_CFG_GLOBAL_ISO_PTR_MASK, x)
+#define MV88E6XXX_QAV_CFG_GLOBAL_ISO_PTR_SET(x) FIELD_PREP(MV88E6XXX_QAV_CFG_GLOBAL_ISO_PTR_MASK, x)
+
+/* allow mgmt frames in isochronous pointer pool */
+#define MV88E6XXX_QAV_CFG_ADMIT_MGMT 0x8000
+
+/* Per-port AVB registers */
+
+/* Offset 0x00: AVB Port Config */
+#define MV88E6XXX_PORT_AVB_CFG 0x00
+#define MV88E6XXX_PORT_AVB_CFG_AVB_MODE GENMASK(15, 14)
+/* all frames legacy (non-AVB) unless overridden */
+#define MV88E6XXX_PORT_AVB_CFG_AVB_MODE_LEGACY 0x0000
+/* AVB frames indicated by priority */
+#define MV88E6XXX_PORT_AVB_CFG_AVB_MODE_STANDARD 0x4000
+/* STANDARD && ATU has STATIC_AVB_NRL bit set */
+#define MV88E6XXX_PORT_AVB_CFG_AVB_MODE_ENHANCED 0x8000
+/* ENHANCED && source port in destination port vector */
+#define MV88E6XXX_PORT_AVB_CFG_AVB_MODE_SECURE 0xc000
+
+#define MV88E6XXX_PORT_AVB_CFG_AVB_OVERRIDE 0x2000
+#define MV88E6XXX_PORT_AVB_CFG_AVB_FILTER_BAD_AVB 0x1000
+#define MV88E6XXX_PORT_AVB_CFG_AVB_TUNNEL 0x0800
+#define MV88E6XXX_PORT_AVB_CFG_AVB_DISCARD_BAD 0x0400
+
+/* action is mv88e6xxx_policy_action */
+#define MV88E6XXX_PORT_AVB_CFG_AVB_HI_POLICY_MASK GENMASK(3, 2)
+#define MV88E6XXX_PORT_AVB_CFG_AVB_HI_POLICY_GET(p) \
+ FIELD_GET(MV88E6XXX_PORT_AVB_CFG_AVB_HI_POLICY_MASK, p)
+#define MV88E6XXX_PORT_AVB_CFG_AVB_HI_POLICY_SET(p) \
+ FIELD_PREP(MV88E6XXX_PORT_AVB_CFG_AVB_HI_POLICY_MASK, p)
+
+#define MV88E6XXX_PORT_AVB_CFG_AVB_LO_POLICY_MASK GENMASK(1, 0)
+#define MV88E6XXX_PORT_AVB_CFG_AVB_LO_POLICY_GET(p) \
+ FIELD_GET(MV88E6XXX_PORT_AVB_CFG_AVB_LO_POLICY_MASK, p)
+#define MV88E6XXX_PORT_AVB_CFG_AVB_LO_POLICY_SET(p) \
+ FIELD_PREP(MV88E6XXX_PORT_AVB_CFG_AVB_LO_POLICY_MASK, p)
+
+/* Per-family 802.1Qav operation tables */
+extern const struct mv88e6xxx_tc_ops mv88e6341_tc_ops;
+extern const struct mv88e6xxx_tc_ops mv88e6352_tc_ops;
+extern const struct mv88e6xxx_tc_ops mv88e6390_tc_ops;
+
+/* Set AVB queue priority policy. Caller must acquire register lock.
+ *
+ * @param chip Marvell switch chip instance
+ * @param policy policy settings to apply
+ *
+ * @return 0 on success, or a negative error value otherwise
+ */
+int mv88e6xxx_avb_tc_enable(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_avb_tc_policy *policy);
+
+/* Clear AVB queue priority policy. Caller must acquire register lock.
+ *
+ * @param chip Marvell switch chip instance
+ *
+ * @return 0 on success, or a negative error value otherwise
+ */
+int mv88e6xxx_avb_tc_disable(struct mv88e6xxx_chip *chip);
+
+#endif /* _MV88E6XXX_AVB_H */
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 5d45363c67bbd..cb7dd75ab6ef2 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -11,6 +11,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/dcbnl.h>
#include <linux/delay.h>
#include <linux/dsa/mv88e6xxx.h>
#include <linux/etherdevice.h>
@@ -33,6 +34,7 @@
#include <linux/phylink.h>
#include <net/dsa.h>
+#include "avb.h"
#include "chip.h"
#include "devlink.h"
#include "global1.h"
@@ -2291,7 +2293,8 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
if (!entry.portvec)
entry.state = 0;
} else {
- if (state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC)
+ if (state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC ||
+ state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL)
entry.portvec = BIT(port);
else
entry.portvec |= BIT(port);
@@ -2931,7 +2934,9 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
continue;
is_static = (addr.state ==
- MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC);
+ MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC ||
+ addr.state ==
+ MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL);
err = cb(addr.mac, vid, is_static, data);
if (err)
return err;
@@ -4539,6 +4544,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = {
.ptp_ops = &mv88e6165_ptp_ops,
.phylink_get_caps = mv88e6185_phylink_get_caps,
.set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6165_ops = {
@@ -4576,6 +4582,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = {
.avb_ops = &mv88e6165_avb_ops,
.ptp_ops = &mv88e6165_ptp_ops,
.phylink_get_caps = mv88e6185_phylink_get_caps,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6171_ops = {
@@ -4993,10 +5000,11 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
.serdes_get_stats = mv88e6390_serdes_get_stats,
.serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
.serdes_get_regs = mv88e6390_serdes_get_regs,
- .avb_ops = &mv88e6390_avb_ops,
+ .avb_ops = &mv88e6352_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
.phylink_get_caps = mv88e6390_phylink_get_caps,
.pcs_ops = &mv88e6390_pcs_ops,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6240_ops = {
@@ -5057,6 +5065,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.ptp_ops = &mv88e6352_ptp_ops,
.phylink_get_caps = mv88e6352_phylink_get_caps,
.pcs_ops = &mv88e6352_pcs_ops,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6250_ops = {
@@ -5102,6 +5111,7 @@ static const struct mv88e6xxx_ops mv88e6250_ops = {
.ptp_ops = &mv88e6352_ptp_ops,
.phylink_get_caps = mv88e6250_phylink_get_caps,
.set_max_frame_size = mv88e6185_g1_set_max_frame_size,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6290_ops = {
@@ -5163,6 +5173,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
.phylink_get_caps = mv88e6390_phylink_get_caps,
.pcs_ops = &mv88e6390_pcs_ops,
.tcam_ops = &mv88e6390_tcam_ops,
+ .tc_ops = &mv88e6390_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -5219,6 +5230,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
.avb_ops = &mv88e6352_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
.phylink_get_caps = mv88e632x_phylink_get_caps,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -5275,6 +5287,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
.avb_ops = &mv88e6352_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
.phylink_get_caps = mv88e632x_phylink_get_caps,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6341_ops = {
@@ -5339,6 +5352,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
.serdes_get_regs = mv88e6390_serdes_get_regs,
.phylink_get_caps = mv88e6341_phylink_get_caps,
.pcs_ops = &mv88e6390_pcs_ops,
+ .tc_ops = &mv88e6341_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6350_ops = {
@@ -5433,6 +5447,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
.avb_ops = &mv88e6352_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
.phylink_get_caps = mv88e6351_phylink_get_caps,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -5496,6 +5511,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.serdes_get_regs = mv88e6352_serdes_get_regs,
.phylink_get_caps = mv88e6352_phylink_get_caps,
.pcs_ops = &mv88e6352_pcs_ops,
+ .tc_ops = &mv88e6352_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6390_ops = {
@@ -5560,6 +5576,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
.phylink_get_caps = mv88e6390_phylink_get_caps,
.pcs_ops = &mv88e6390_pcs_ops,
.tcam_ops = &mv88e6390_tcam_ops,
+ .tc_ops = &mv88e6390_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6390x_ops = {
@@ -5623,6 +5640,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
.ptp_ops = &mv88e6390_ptp_ops,
.phylink_get_caps = mv88e6390x_phylink_get_caps,
.pcs_ops = &mv88e6390_pcs_ops,
+ .tc_ops = &mv88e6390_tc_ops,
};
static const struct mv88e6xxx_ops mv88e6393x_ops = {
@@ -5687,6 +5705,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
.phylink_get_caps = mv88e6393x_phylink_get_caps,
.pcs_ops = &mv88e6393x_pcs_ops,
.tcam_ops = &mv88e6393_tcam_ops,
+ .tc_ops = &mv88e6390_tc_ops,
};
static const struct mv88e6xxx_info mv88e6xxx_table[] = {
@@ -6724,11 +6743,48 @@ static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
struct dsa_db db)
{
struct mv88e6xxx_chip *chip = ds->priv;
+ struct mv88e6xxx_atu_entry existing;
+ u16 fid;
+ u8 state;
int err;
+ /* In Enhanced and Secure AVB modes, the switch drops AVB-priority
+ * frames whose destination ATU entry lacks the AVB bit. Userspace
+ * marks an MDB entry as belonging to a reserved stream via the
+ * MDB_FLAGS_STREAM_RESERVED bridge flag, which arrives here as
+ * SWITCHDEV_MDB_F_STREAM_RESERVED.
+ */
+ if (mdb->flags & SWITCHDEV_MDB_F_STREAM_RESERVED)
+ state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL;
+ else
+ state = MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC;
+
mv88e6xxx_reg_lock(chip);
- err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
- MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC);
+
+ /* The ATU AVB bit is per-entry (per (FID, MAC)) and applies to every
+ * port in the entry's portvec. The bridge's MDB_PG_FLAGS_STREAM_RESERVED
+ * is per port group. Detect and reject the mismatch case where another
+ * port has already established a different state for this MAC, so that
+ * userspace gets MDB_FLAGS_OFFLOAD_FAILED rather than silently having
+ * one port's intent overwrite another's.
+ */
+ err = mv88e6xxx_port_db_get(chip, mdb->addr, mdb->vid, &fid, &existing);
+ if (err)
+ goto out;
+
+ if (ether_addr_equal(existing.mac, mdb->addr) &&
+ (existing.state == MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC ||
+ existing.state == MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL) &&
+ existing.state != state &&
+ (existing.portvec & ~BIT(port))) {
+ dev_info_ratelimited(chip->dev,
+ "p%d: MDB %pM stream-reserved flag mismatches existing ATU entry\n",
+ port, mdb->addr);
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid, state);
if (err)
goto out;
@@ -6824,6 +6880,189 @@ static void mv88e6xxx_port_mirror_del(struct dsa_switch *ds, int port,
mutex_unlock(&chip->reg_lock);
}
+static int mv88e6xxx_qos_query_caps(struct tc_query_caps_base *base)
+{
+ switch (base->type) {
+ case TC_SETUP_QDISC_MQPRIO: {
+ struct tc_mqprio_caps *caps = base->caps;
+
+ caps->validate_queue_counts = true;
+
+ return 0;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int mv88e6xxx_qos_validate_mqprio(const struct device *dev,
+ const struct mv88e6xxx_chip *chip,
+ int port,
+ const struct tc_mqprio_qopt_offload *mqprio,
+ struct mv88e6xxx_avb_tc_policy *tcpol)
+{
+ const struct tc_mqprio_qopt *qopt = &mqprio->qopt;
+ struct netlink_ext_ack *extack = mqprio->extack;
+ u8 avb_tcpol_set = 0;
+ int tc, pri;
+
+ memset(tcpol, 0, sizeof(*tcpol));
+
+ if (qopt->hw != TC_MQPRIO_HW_OFFLOAD_TCS) {
+ NL_SET_ERR_MSG(extack, "only full TC hardware offload is supported");
+ return -EOPNOTSUPP;
+ } else if (mqprio->shaper != TC_MQPRIO_SHAPER_DCB) {
+ NL_SET_ERR_MSG(extack, "only DCB shaper is supported");
+ return -EOPNOTSUPP;
+ } else if (qopt->num_tc > MV88E6XXX_AVB_TC_MAX + 1) {
+ NL_SET_ERR_MSG_FMT(extack, "too many traffic classes: %d", qopt->num_tc);
+ return -EOPNOTSUPP;
+ }
+
+ if (qopt->num_tc == 0)
+ return 0;
+
+ /* Validate and map TC to TX queue */
+ for (tc = MV88E6XXX_AVB_TC_LEGACY; tc < qopt->num_tc; tc++) {
+ if (qopt->offset[tc] + qopt->count[tc] > chip->info->num_tx_queues) {
+ NL_SET_ERR_MSG_FMT(extack, "queue %d out of range",
+ qopt->offset[tc] + qopt->count[tc] - 1);
+ return -EOPNOTSUPP;
+ }
+
+ if (tc == MV88E6XXX_AVB_TC_LEGACY) {
+ if (qopt->count[tc] == 0) {
+ NL_SET_ERR_MSG(extack, "legacy TC must have at least one queue");
+ return -ERANGE;
+ }
+ tcpol->map[tc].count = qopt->count[tc];
+ } else if (qopt->count[tc] != 1) {
+ NL_SET_ERR_MSG_FMT(extack, "only one queue supported for TC %d", tc);
+ return -EOPNOTSUPP;
+ }
+
+ tcpol->map[tc].qpri = qopt->offset[tc];
+ }
+
+ /* Validate and map priority to TC */
+ for (pri = 0; pri < IEEE_8021Q_MAX_PRIORITIES; pri++) {
+ tc = qopt->prio_tc_map[pri];
+
+ if (tc == MV88E6XXX_AVB_TC_LEGACY)
+ continue;
+
+ if (avb_tcpol_set & BIT(tc)) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "only one frame priority can be mapped to TC %d", tc);
+ return -EOPNOTSUPP;
+ }
+
+ avb_tcpol_set |= BIT(tc);
+ tcpol->map[tc].fpri = pri;
+ }
+
+ if (avb_tcpol_set != GENMASK(MV88E6XXX_AVB_TC_HI, MV88E6XXX_AVB_TC_LO)) {
+ NL_SET_ERR_MSG(extack,
+ "both hi and lo priority TCs must have 802.1p priorities");
+ return -EOPNOTSUPP;
+ }
+
+ return qopt->num_tc;
+}
+
+static int mv88e6xxx_qos_port_mqprio(struct dsa_switch *ds, int port,
+ struct tc_mqprio_qopt_offload *mqprio)
+{
+ struct netlink_ext_ack *extack = mqprio->extack;
+ struct mv88e6xxx_avb_tc_policy tcpol;
+ struct mv88e6xxx_chip *chip = ds->priv;
+ struct net_device *user;
+ int err, num_tc, tc;
+ bool can_update_pol;
+
+ if (!dsa_is_user_port(ds, port))
+ return -EINVAL;
+
+ num_tc = mv88e6xxx_qos_validate_mqprio(ds->dev, chip, port, mqprio, &tcpol);
+ if (num_tc < 0)
+ return num_tc;
+
+ user = dsa_to_port(ds, port)->user;
+
+ mutex_lock(&chip->reg_lock);
+
+ can_update_pol = chip->avb_tc_policy.port_mask == 0 ||
+ (hweight16(chip->avb_tc_policy.port_mask) == 1 &&
+ ffs(chip->avb_tc_policy.port_mask) == port + 1);
+
+ if (!can_update_pol &&
+ num_tc > 0 &&
+ memcmp(&tcpol.map, &chip->avb_tc_policy.map, sizeof(tcpol.map)) != 0) {
+ NL_SET_ERR_MSG(extack, "only a single AVB queue policy is supported per switch");
+ err = -EOPNOTSUPP;
+ goto err_unlock;
+ }
+
+ err = netdev_set_num_tc(user, num_tc);
+ if (err)
+ goto err_reset_tc;
+
+ for (tc = 0; tc < num_tc; tc++) {
+ const struct tc_mqprio_qopt *qopt = &mqprio->qopt;
+
+ err = netdev_set_tc_queue(user, tc, 1, qopt->offset[tc]);
+ if (err)
+ goto err_reset_tc;
+ }
+
+ if (can_update_pol) {
+ err = num_tc > 0 ? mv88e6xxx_avb_tc_enable(chip, &tcpol)
+ : mv88e6xxx_avb_tc_disable(chip);
+ if (err) {
+ NL_SET_ERR_MSG_FMT(extack, "failed to %s AVB queue policy: %d",
+ num_tc > 0 ? "enable" : "disable", err);
+ goto err_reset_tc;
+ }
+
+ memcpy(&chip->avb_tc_policy.map, &tcpol.map, sizeof(tcpol.map));
+ }
+
+ if (num_tc)
+ chip->avb_tc_policy.port_mask |= BIT(port);
+ else
+ chip->avb_tc_policy.port_mask &= ~BIT(port);
+
+ mutex_unlock(&chip->reg_lock);
+
+ return 0;
+
+err_reset_tc:
+ netdev_reset_tc(user);
+err_unlock:
+ mutex_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+static int mv88e6xxx_port_setup_tc(struct dsa_switch *ds, int port,
+ enum tc_setup_type type,
+ void *type_data)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+
+ if (!chip->info->ops->tc_ops)
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case TC_QUERY_CAPS:
+ return mv88e6xxx_qos_query_caps(type_data);
+ case TC_SETUP_QDISC_MQPRIO:
+ return mv88e6xxx_qos_port_mqprio(ds, port, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static int mv88e6xxx_port_pre_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack)
@@ -7243,6 +7482,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.port_mdb_del = mv88e6xxx_port_mdb_del,
.port_mirror_add = mv88e6xxx_port_mirror_add,
.port_mirror_del = mv88e6xxx_port_mirror_del,
+ .port_setup_tc = mv88e6xxx_port_setup_tc,
.crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join,
.crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave,
.port_hwtstamp_set = mv88e6xxx_port_hwtstamp_set,
@@ -7310,6 +7550,28 @@ static const void *pdata_device_get_match_data(struct device *dev)
return NULL;
}
+static int mv88e6xxx_get_avb_mode(struct mv88e6xxx_chip *chip,
+ enum mv88e6xxx_avb_mode *modep)
+{
+ struct dsa_mv88e6xxx_pdata *pdata = chip->dev->platform_data;
+ struct device_node *np = chip->dev->of_node;
+ int mode = MV88E6XXX_AVB_MODE_STANDARD;
+
+ if (np)
+ of_property_read_u32(np, "marvell,mv88e6xxx-avb-mode", &mode);
+ else if (pdata)
+ mode = pdata->avb_mode;
+
+ if (mode < MV88E6XXX_AVB_MODE_STANDARD ||
+ mode > MV88E6XXX_AVB_MODE_SECURE) {
+ dev_err(chip->dev, "Invalid AVB mode %d\n", mode);
+ return -EINVAL;
+ }
+
+ *modep = mode;
+ return 0;
+}
+
/* There is no suspend to RAM support at DSA level yet, the switch configuration
* would be lost after a power cycle so prevent it to be suspended.
*/
@@ -7405,6 +7667,10 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
chip->eeprom_len = pdata->eeprom_len;
}
+ err = mv88e6xxx_get_avb_mode(chip, &chip->avb_tc_policy.mode);
+ if (err)
+ goto out;
+
mv88e6xxx_reg_lock(chip);
err = mv88e6xxx_switch_reset(chip);
mv88e6xxx_reg_unlock(chip);
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 146723fbca658..b6f4331affdc6 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -19,6 +19,7 @@
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
#include <net/dsa.h>
+#include <net/pkt_sched.h>
#define EDSA_HLEN 8
#define MV88E6XXX_N_FID 4096
@@ -216,6 +217,7 @@ struct mv88e6xxx_ptp_ops;
struct mv88e6xxx_pcs_ops;
struct mv88e6xxx_cc_coeffs;
struct mv88e6xxx_tcam_ops;
+struct mv88e6xxx_tc_ops;
struct mv88e6xxx_irq {
u16 masked;
@@ -250,6 +252,46 @@ struct mv88e6xxx_port_hwtstamp {
struct kernel_hwtstamp_config tstamp_config;
};
+enum mv88e6xxx_avb_mode {
+ MV88E6XXX_AVB_MODE_STANDARD = 0,
+ MV88E6XXX_AVB_MODE_ENHANCED,
+ MV88E6XXX_AVB_MODE_SECURE,
+};
+
+enum mv88e6xxx_avb_tc {
+ /* Non-AVB traffic */
+ MV88E6XXX_AVB_TC_LEGACY = 0,
+ /* Higher latency, low priority AVB flows (class B) */
+ MV88E6XXX_AVB_TC_LO = 1,
+ /* Low latency, high priority AVB flows (class A) */
+ MV88E6XXX_AVB_TC_HI = 2,
+ MV88E6XXX_AVB_TC_MAX = MV88E6XXX_AVB_TC_HI
+};
+
+struct mv88e6xxx_avb_priority_map {
+ union {
+ /* Number of queues, for MV88E6XXX_AVB_TC_LEGACY */
+ u8 count;
+
+ /* Frame priority, for MV88E6XXX_AVB_TC_LO/HI */
+ u8 fpri;
+ };
+
+ /* Queue priority*/
+ u8 qpri;
+};
+
+struct mv88e6xxx_avb_tc_policy {
+ /* AVB mode */
+ enum mv88e6xxx_avb_mode mode;
+
+ /* Ports participating in HW offload priority mapping */
+ u16 port_mask;
+
+ /* Map from 802.1p frame priority to queue */
+ struct mv88e6xxx_avb_priority_map map[MV88E6XXX_AVB_TC_MAX + 1];
+};
+
enum mv88e6xxx_policy_mapping {
MV88E6XXX_POLICY_MAPPING_DA,
MV88E6XXX_POLICY_MAPPING_SA,
@@ -440,6 +482,9 @@ struct mv88e6xxx_chip {
int egress_dest_port;
int ingress_dest_port;
+ /* Global AVB queue policy resources */
+ struct mv88e6xxx_avb_tc_policy avb_tc_policy;
+
/* Per-port timestamping resources. */
struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS];
@@ -721,6 +766,9 @@ struct mv88e6xxx_ops {
/* Ternary Content Addressable Memory operations */
const struct mv88e6xxx_tcam_ops *tcam_ops;
+
+ /* Traffic control / Qdisc operations */
+ const struct mv88e6xxx_tc_ops *tc_ops;
};
struct mv88e6xxx_irq_ops {
@@ -766,6 +814,28 @@ struct mv88e6xxx_avb_ops {
int (*tai_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data,
int len);
int (*tai_write)(struct mv88e6xxx_chip *chip, int addr, u16 data);
+
+ /* Access port-scoped Audio Video Bridging registers */
+ int (*port_avb_read)(struct mv88e6xxx_chip *chip, int port, int addr,
+ u16 *data, int len);
+ int (*port_avb_write)(struct mv88e6xxx_chip *chip, int port, int addr,
+ u16 data);
+
+ /* Access global Audio Video Bridging registers */
+ int (*avb_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data,
+ int len);
+ int (*avb_write)(struct mv88e6xxx_chip *chip, int addr, u16 data);
+
+ /* Access global Class Shaping and Pacing registers */
+ int (*qav_read)(struct mv88e6xxx_chip *chip, int addr, u16 *data,
+ int len);
+ int (*qav_write)(struct mv88e6xxx_chip *chip, int addr, u16 data);
+
+ /* Access port-scoped Class Shaping and Pacing registers */
+ int (*port_qav_read)(struct mv88e6xxx_chip *chip, int port, int addr,
+ u16 *data, int len);
+ int (*port_qav_write)(struct mv88e6xxx_chip *chip, int port, int addr,
+ u16 data);
};
struct mv88e6xxx_ptp_ops {
@@ -801,6 +871,12 @@ struct mv88e6xxx_tcam_ops {
int (*flush_tcam)(struct mv88e6xxx_chip *chip);
};
+struct mv88e6xxx_tc_ops {
+ int (*tc_enable)(struct mv88e6xxx_chip *chip,
+ const struct mv88e6xxx_avb_tc_policy *policy);
+ int (*tc_disable)(struct mv88e6xxx_chip *chip);
+};
+
static inline bool mv88e6xxx_has_stu(struct mv88e6xxx_chip *chip)
{
return chip->info->max_sid > 0 &&
diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c
index 9820cd5967574..ce2c5cc3ea585 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.c
+++ b/drivers/net/dsa/mv88e6xxx/global1.c
@@ -356,16 +356,21 @@ int mv88e6085_g1_ip_pri_map(struct mv88e6xxx_chip *chip)
/* Offset 0x18: IEEE-PRI Register */
+int mv88e6xxx_g1_set_ieee_pri_map(struct mv88e6xxx_chip *chip, u16 map)
+{
+ return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, map);
+}
+
int mv88e6085_g1_ieee_pri_map(struct mv88e6xxx_chip *chip)
{
/* Reset the IEEE Tag priorities to defaults */
- return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, 0xfa41);
+ return mv88e6xxx_g1_set_ieee_pri_map(chip, 0xfa41);
}
int mv88e6250_g1_ieee_pri_map(struct mv88e6xxx_chip *chip)
{
/* Reset the IEEE Tag priorities to defaults */
- return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_IEEE_PRI, 0xfa50);
+ return mv88e6xxx_g1_set_ieee_pri_map(chip, 0xfa50);
}
/* Offset 0x1a: Monitor Control */
diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
index 8204321502c1f..1a0f9c5de8132 100644
--- a/drivers/net/dsa/mv88e6xxx/global1.h
+++ b/drivers/net/dsa/mv88e6xxx/global1.h
@@ -2,7 +2,7 @@
/*
* Marvell 88E6xxx Switch Global (1) Registers support
*
- * Copyright (c) 2008 Marvell Semiconductor
+ * Copyright (c) 2008-2021 Marvell Semiconductor
*
* Copyright (c) 2016-2017 Savoir-faire Linux Inc.
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
@@ -62,6 +62,7 @@
#define MV88E6185_G1_CTL1_MAX_FRAME_1632 0x0400
#define MV88E6185_G1_CTL1_RELOAD_EEPROM 0x0200
#define MV88E6393X_G1_CTL1_DEVICE2_EN 0x0200
+#define MV88E6XXX_G1_CTL1_AVB_EN 0x0100
#define MV88E6XXX_G1_CTL1_DEVICE_EN 0x0080
#define MV88E6XXX_G1_CTL1_STATS_DONE_EN 0x0040
#define MV88E6XXX_G1_CTL1_VTU_PROBLEM_EN 0x0020
@@ -193,6 +194,47 @@
/* Offset 0x18: IEEE-PRI Register */
#define MV88E6XXX_G1_IEEE_PRI 0x18
+/* Switches supporting 4 TX queues pack FPri to QPri mappings into a single
+ * register value.
+ *
+ * Extract QPri for a given FPri from the provided register value.
+ *
+ * @param val register value
+ * @param fpri frame priority
+ *
+ * @return queue priority
+ */
+static inline u16 mv88e6352_g1_ieee_pri_get(u16 val, u8 fpri)
+{
+ u16 mask;
+
+ fpri &= 0x7;
+ fpri <<= 1;
+ mask = GENMASK(fpri + 1, fpri);
+
+ return (val & mask) >> fpri;
+}
+
+/* Add a FPri to QPri mapping to the register value argument.
+ *
+ * @param fpri frame priority
+ * @param qpri queue priority
+ * @param reg register value, should be 0 on first call
+ */
+static inline void mv88e6352_g1_ieee_pri_set(u8 fpri, u8 qpri, u16 *reg)
+{
+ u16 mask;
+
+ fpri &= 0x7;
+ fpri <<= 1;
+ mask = GENMASK(fpri + 1, fpri);
+
+ *reg &= ~(mask);
+ *reg |= (qpri & 0x3) << fpri;
+}
+
+#define MV88E6390_G1_IEEE_PRI_UPDATE 0x80
+
/* Offset 0x19: Core Tag Type */
#define MV88E6185_G1_CORE_TAG_TYPE 0x19
@@ -311,6 +353,7 @@ int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
int mv88e6085_g1_ip_pri_map(struct mv88e6xxx_chip *chip);
+int mv88e6xxx_g1_set_ieee_pri_map(struct mv88e6xxx_chip *chip, u16 map);
int mv88e6085_g1_ieee_pri_map(struct mv88e6xxx_chip *chip);
int mv88e6250_g1_ieee_pri_map(struct mv88e6xxx_chip *chip);
diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h
index ff5adf7c9bc3a..8645add6f7da0 100644
--- a/drivers/net/dsa/mv88e6xxx/global2.h
+++ b/drivers/net/dsa/mv88e6xxx/global2.h
@@ -176,15 +176,17 @@
#define MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL 0xe
#define MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL 0xf
#define MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL 0xf
+#define MV88E6352_G2_AVB_CMD_PORT_AVBGLOBAL 0xf
#define MV88E6390_G2_AVB_CMD_PORT_MASK 0x1f00
#define MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL 0x1e
#define MV88E6390_G2_AVB_CMD_PORT_PTPGLOBAL 0x1f
-#define MV88E6352_G2_AVB_CMD_BLOCK_PTP 0
-#define MV88E6352_G2_AVB_CMD_BLOCK_AVB 1
-#define MV88E6352_G2_AVB_CMD_BLOCK_QAV 2
-#define MV88E6352_G2_AVB_CMD_BLOCK_QVB 3
-#define MV88E6352_G2_AVB_CMD_BLOCK_MASK 0x00e0
-#define MV88E6352_G2_AVB_CMD_ADDR_MASK 0x001f
+#define MV88E6390_G2_AVB_CMD_PORT_AVBGLOBAL 0x1f
+#define MV88E6XXX_G2_AVB_CMD_BLOCK_PTP 0
+#define MV88E6XXX_G2_AVB_CMD_BLOCK_AVB 1
+#define MV88E6XXX_G2_AVB_CMD_BLOCK_QAV 2
+#define MV88E6XXX_G2_AVB_CMD_BLOCK_QVB 3
+#define MV88E6XXX_G2_AVB_CMD_BLOCK_MASK 0x00e0
+#define MV88E6XXX_G2_AVB_CMD_ADDR_MASK 0x001f
/* Offset 0x17: AVB Data Register */
#define MV88E6352_G2_AVB_DATA 0x17
diff --git a/drivers/net/dsa/mv88e6xxx/global2_avb.c b/drivers/net/dsa/mv88e6xxx/global2_avb.c
index 657783e043ff1..e9f75f7d3b3a2 100644
--- a/drivers/net/dsa/mv88e6xxx/global2_avb.c
+++ b/drivers/net/dsa/mv88e6xxx/global2_avb.c
@@ -14,6 +14,7 @@
#include <linux/bitfield.h>
#include "global2.h"
+#include "avb.h"
/* Offset 0x16: AVB Command Register
* Offset 0x17: AVB Data Register
@@ -95,7 +96,7 @@ static int mv88e6352_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip,
{
u16 readop = (len == 1 ? MV88E6352_G2_AVB_CMD_OP_READ :
MV88E6352_G2_AVB_CMD_OP_READ_INCR) |
- (port << 8) | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) |
+ (port << 8) | (MV88E6XXX_G2_AVB_CMD_BLOCK_PTP << 5) |
addr;
return mv88e6xxx_g2_avb_read(chip, readop, data, len);
@@ -105,7 +106,7 @@ static int mv88e6352_g2_avb_port_ptp_write(struct mv88e6xxx_chip *chip,
int port, int addr, u16 data)
{
u16 writeop = MV88E6352_G2_AVB_CMD_OP_WRITE | (port << 8) |
- (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | addr;
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_PTP << 5) | addr;
return mv88e6xxx_g2_avb_write(chip, writeop, data);
}
@@ -142,6 +143,92 @@ static int mv88e6352_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr,
addr, data);
}
+static int mv88e6352_g2_avb_port_avb_read(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 *data,
+ int len)
+{
+ u16 readop = (len == 1 ? MV88E6352_G2_AVB_CMD_OP_READ :
+ MV88E6352_G2_AVB_CMD_OP_READ_INCR) |
+ (port << 8) | (MV88E6XXX_G2_AVB_CMD_BLOCK_AVB << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6352_g2_avb_port_avb_write(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 data)
+{
+ u16 writeop = MV88E6352_G2_AVB_CMD_OP_WRITE | (port << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_AVB << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
+static int mv88e6352_g2_avb_avb_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ u16 readop = (len == 1 ? MV88E6352_G2_AVB_CMD_OP_READ :
+ MV88E6352_G2_AVB_CMD_OP_READ_INCR) |
+ (MV88E6352_G2_AVB_CMD_PORT_AVBGLOBAL << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_AVB << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6352_g2_avb_avb_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ u16 writeop = MV88E6352_G2_AVB_CMD_OP_WRITE |
+ (MV88E6352_G2_AVB_CMD_PORT_AVBGLOBAL << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_AVB << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
+static int mv88e6352_g2_avb_qav_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ u16 readop = (len == 1 ? MV88E6352_G2_AVB_CMD_OP_READ :
+ MV88E6352_G2_AVB_CMD_OP_READ_INCR) |
+ (MV88E6352_G2_AVB_CMD_PORT_AVBGLOBAL << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_QAV << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6352_g2_avb_qav_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ u16 writeop = MV88E6352_G2_AVB_CMD_OP_WRITE |
+ (MV88E6352_G2_AVB_CMD_PORT_AVBGLOBAL << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_QAV << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
+static int mv88e6352_g2_avb_port_qav_read(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 *data,
+ int len)
+{
+ u16 readop = (len == 1 ? MV88E6352_G2_AVB_CMD_OP_READ :
+ MV88E6352_G2_AVB_CMD_OP_READ_INCR) |
+ (port << 8) | (MV88E6XXX_G2_AVB_CMD_BLOCK_QAV << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6352_g2_avb_port_qav_write(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 data)
+{
+ u16 writeop = MV88E6352_G2_AVB_CMD_OP_WRITE | (port << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_QAV << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {
.port_ptp_read = mv88e6352_g2_avb_port_ptp_read,
.port_ptp_write = mv88e6352_g2_avb_port_ptp_write,
@@ -149,6 +236,14 @@ const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {
.ptp_write = mv88e6352_g2_avb_ptp_write,
.tai_read = mv88e6352_g2_avb_tai_read,
.tai_write = mv88e6352_g2_avb_tai_write,
+ .port_avb_read = mv88e6352_g2_avb_port_avb_read,
+ .port_avb_write = mv88e6352_g2_avb_port_avb_write,
+ .avb_read = mv88e6352_g2_avb_avb_read,
+ .avb_write = mv88e6352_g2_avb_avb_write,
+ .qav_read = mv88e6352_g2_avb_qav_read,
+ .qav_write = mv88e6352_g2_avb_qav_write,
+ .port_qav_read = mv88e6352_g2_avb_port_qav_read,
+ .port_qav_write = mv88e6352_g2_avb_port_qav_write,
};
static int mv88e6165_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr,
@@ -174,6 +269,14 @@ const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = {
.ptp_write = mv88e6352_g2_avb_ptp_write,
.tai_read = mv88e6165_g2_avb_tai_read,
.tai_write = mv88e6165_g2_avb_tai_write,
+ .port_avb_read = mv88e6352_g2_avb_port_avb_read,
+ .port_avb_write = mv88e6352_g2_avb_port_avb_write,
+ .avb_read = mv88e6352_g2_avb_avb_read,
+ .avb_write = mv88e6352_g2_avb_avb_write,
+ .qav_read = mv88e6352_g2_avb_qav_read,
+ .qav_write = mv88e6352_g2_avb_qav_write,
+ .port_qav_read = mv88e6352_g2_avb_port_qav_read,
+ .port_qav_write = mv88e6352_g2_avb_port_qav_write,
};
static int mv88e6390_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip,
@@ -182,7 +285,7 @@ static int mv88e6390_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip,
{
u16 readop = (len == 1 ? MV88E6390_G2_AVB_CMD_OP_READ :
MV88E6390_G2_AVB_CMD_OP_READ_INCR) |
- (port << 8) | (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) |
+ (port << 8) | (MV88E6XXX_G2_AVB_CMD_BLOCK_PTP << 5) |
addr;
return mv88e6xxx_g2_avb_read(chip, readop, data, len);
@@ -192,7 +295,7 @@ static int mv88e6390_g2_avb_port_ptp_write(struct mv88e6xxx_chip *chip,
int port, int addr, u16 data)
{
u16 writeop = MV88E6390_G2_AVB_CMD_OP_WRITE | (port << 8) |
- (MV88E6352_G2_AVB_CMD_BLOCK_PTP << 5) | addr;
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_PTP << 5) | addr;
return mv88e6xxx_g2_avb_write(chip, writeop, data);
}
@@ -229,6 +332,92 @@ static int mv88e6390_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr,
addr, data);
}
+static int mv88e6390_g2_avb_port_avb_read(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 *data,
+ int len)
+{
+ u16 readop = (len == 1 ? MV88E6390_G2_AVB_CMD_OP_READ :
+ MV88E6390_G2_AVB_CMD_OP_READ_INCR) |
+ (port << 8) | (MV88E6XXX_G2_AVB_CMD_BLOCK_AVB << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6390_g2_avb_port_avb_write(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 data)
+{
+ u16 writeop = MV88E6390_G2_AVB_CMD_OP_WRITE | (port << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_AVB << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
+static int mv88e6390_g2_avb_avb_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ u16 readop = (len == 1 ? MV88E6390_G2_AVB_CMD_OP_READ :
+ MV88E6390_G2_AVB_CMD_OP_READ_INCR) |
+ (MV88E6390_G2_AVB_CMD_PORT_AVBGLOBAL << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_AVB << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6390_g2_avb_avb_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ u16 writeop = MV88E6390_G2_AVB_CMD_OP_WRITE |
+ (MV88E6390_G2_AVB_CMD_PORT_AVBGLOBAL << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_AVB << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
+static int mv88e6390_g2_avb_qav_read(struct mv88e6xxx_chip *chip, int addr,
+ u16 *data, int len)
+{
+ u16 readop = (len == 1 ? MV88E6390_G2_AVB_CMD_OP_READ :
+ MV88E6390_G2_AVB_CMD_OP_READ_INCR) |
+ (MV88E6390_G2_AVB_CMD_PORT_AVBGLOBAL << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_QAV << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6390_g2_avb_qav_write(struct mv88e6xxx_chip *chip, int addr,
+ u16 data)
+{
+ u16 writeop = MV88E6390_G2_AVB_CMD_OP_WRITE |
+ (MV88E6390_G2_AVB_CMD_PORT_AVBGLOBAL << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_QAV << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
+static int mv88e6390_g2_avb_port_qav_read(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 *data,
+ int len)
+{
+ u16 readop = (len == 1 ? MV88E6390_G2_AVB_CMD_OP_READ :
+ MV88E6390_G2_AVB_CMD_OP_READ_INCR) |
+ (port << 8) | (MV88E6XXX_G2_AVB_CMD_BLOCK_QAV << 5) |
+ addr;
+
+ return mv88e6xxx_g2_avb_read(chip, readop, data, len);
+}
+
+static int mv88e6390_g2_avb_port_qav_write(struct mv88e6xxx_chip *chip,
+ int port, int addr, u16 data)
+{
+ u16 writeop = MV88E6390_G2_AVB_CMD_OP_WRITE | (port << 8) |
+ (MV88E6XXX_G2_AVB_CMD_BLOCK_QAV << 5) | addr;
+
+ return mv88e6xxx_g2_avb_write(chip, writeop, data);
+}
+
const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {
.port_ptp_read = mv88e6390_g2_avb_port_ptp_read,
.port_ptp_write = mv88e6390_g2_avb_port_ptp_write,
@@ -236,4 +425,12 @@ const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {
.ptp_write = mv88e6390_g2_avb_ptp_write,
.tai_read = mv88e6390_g2_avb_tai_read,
.tai_write = mv88e6390_g2_avb_tai_write,
+ .port_avb_read = mv88e6390_g2_avb_port_avb_read,
+ .port_avb_write = mv88e6390_g2_avb_port_avb_write,
+ .avb_read = mv88e6390_g2_avb_avb_read,
+ .avb_write = mv88e6390_g2_avb_avb_write,
+ .qav_read = mv88e6390_g2_avb_qav_read,
+ .qav_write = mv88e6390_g2_avb_qav_write,
+ .port_qav_read = mv88e6390_g2_avb_port_qav_read,
+ .port_qav_write = mv88e6390_g2_avb_port_qav_write,
};
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index ea1fab71968a0..3a842c4d18105 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -1606,6 +1606,15 @@ int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port)
return 0;
}
+int mv88e6390_port_set_ieeepmt_ingress_pcp(struct mv88e6xxx_chip *chip, int port,
+ u8 pcp, u8 fpri, u8 qpri)
+{
+ return mv88e6xxx_port_ieeepmt_write(chip, port,
+ MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_INGRESS_PCP,
+ pcp,
+ (fpri | qpri << 4));
+}
+
/* Offset 0x0E: Policy Control Register */
static int
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 5b6cde9f7406f..3ebd2ebafbf0f 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -523,6 +523,8 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
u16 mode);
int mv88e6095_port_tag_remap(struct mv88e6xxx_chip *chip, int port);
int mv88e6390_port_tag_remap(struct mv88e6xxx_chip *chip, int port);
+int mv88e6390_port_set_ieeepmt_ingress_pcp(struct mv88e6xxx_chip *chip, int port,
+ u8 pcp, u8 fpri, u8 qpri);
int mv88e6xxx_port_set_egress_mode(struct mv88e6xxx_chip *chip, int port,
enum mv88e6xxx_egress_mode mode);
int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port,
diff --git a/include/linux/platform_data/mv88e6xxx.h b/include/linux/platform_data/mv88e6xxx.h
index 21452a9365e1e..6366360fc15e1 100644
--- a/include/linux/platform_data/mv88e6xxx.h
+++ b/include/linux/platform_data/mv88e6xxx.h
@@ -14,6 +14,7 @@ struct dsa_mv88e6xxx_pdata {
struct net_device *netdev;
u32 eeprom_len;
int irq;
+ int avb_mode;
};
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next 5/6] net: dsa: mv88e6xxx: CBS support
2026-05-27 0:42 [PATCH net-next 0/6] net: dsa: mv88e6xxx: add MQPRIO/CBS offload Luke Howard
` (3 preceding siblings ...)
2026-05-27 0:42 ` [PATCH net-next 4/6] net: dsa: mv88e6xxx: MQPRIO support Luke Howard
@ 2026-05-27 0:42 ` Luke Howard
2026-05-27 9:28 ` Cedric Jehasse
2026-05-27 0:42 ` [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property Luke Howard
5 siblings, 1 reply; 14+ messages in thread
From: Luke Howard @ 2026-05-27 0:42 UTC (permalink / raw)
To: netdev
Cc: Luke Howard, Kieran Tyrrell, Max Hunter, Cedric Jehasse,
Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-kernel
Add support for the 802.1Qav Credit Based Shaper (CBS) to Marvell switches
that support AVB. CBS policies can be configured per-port, but are subject
to the global policy limitations imposed by the Marvell MQPRIO
implementation.
Co-developed-by: Cedric Jehasse <cedric.jehasse@luminex.be>
Assisted-by: Claude:claude-4.7-opus
Signed-off-by: Luke Howard <lukeh@padl.com>
---
drivers/net/dsa/mv88e6xxx/avb.c | 189 +++++++++++++++++++++++++------
drivers/net/dsa/mv88e6xxx/avb.h | 31 +++++
drivers/net/dsa/mv88e6xxx/chip.c | 73 ++++++++++++
drivers/net/dsa/mv88e6xxx/chip.h | 22 ++++
drivers/net/dsa/mv88e6xxx/port.c | 45 ++++++++
drivers/net/dsa/mv88e6xxx/port.h | 20 ++++
6 files changed, 343 insertions(+), 37 deletions(-)
diff --git a/drivers/net/dsa/mv88e6xxx/avb.c b/drivers/net/dsa/mv88e6xxx/avb.c
index c535bf4fe3623..1a6365ff66378 100644
--- a/drivers/net/dsa/mv88e6xxx/avb.c
+++ b/drivers/net/dsa/mv88e6xxx/avb.c
@@ -107,7 +107,67 @@ static int mv88e6xxx_tc_disable(struct mv88e6xxx_chip *chip)
return chip->info->ops->tc_ops->tc_disable(chip);
}
-/* MQPRIO helpers */
+/* MQPRIO and CBS helpers */
+
+/* Ensure strict priority scheduling is enabled for CBS ports */
+static int mv88e6xxx_cbs_update_scheduler(struct mv88e6xxx_chip *chip, int port,
+ bool has_cbs)
+{
+ const struct mv88e6xxx_ops *ops = chip->info->ops;
+ u8 mode;
+
+ if (!ops->port_set_scheduling_mode)
+ return 0;
+
+ mode = has_cbs ? chip->info->num_tx_queues - 1 : 0;
+
+ return ops->port_set_scheduling_mode(chip, port, mode);
+}
+
+/* Return true if any CBS queue has a non-zero rate configured on a port. */
+static bool mv88e6xxx_avb_port_has_cbs(struct mv88e6xxx_chip *chip, int port)
+{
+ int queue, err;
+ u16 rate;
+
+ for (queue = 0; queue < chip->info->num_tx_queues; queue++) {
+ err = mv88e6xxx_port_qav_read(chip, port,
+ MV88E6XXX_PORT_QAV_CFG_RATE(queue),
+ &rate, 1);
+ if (err)
+ continue;
+ else if (rate)
+ return true;
+ }
+
+ return false;
+}
+
+/* Enable/disable CBS and, if supported, strict priority, on a port */
+int mv88e6xxx_qav_set_port_cbs_qopt(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ const struct mv88e6xxx_ops *ops = chip->info->ops;
+ const struct mv88e6xxx_tc_ops *tc_ops = ops->tc_ops;
+ bool old_active, new_active;
+ int err;
+
+ if (!tc_ops->set_port_cbs_qopt)
+ return -EOPNOTSUPP;
+
+ old_active = mv88e6xxx_avb_port_has_cbs(chip, port);
+
+ err = tc_ops->set_port_cbs_qopt(chip, port, cbs_qopt);
+ if (err)
+ return err;
+
+ new_active = cbs_qopt->enable ? true : mv88e6xxx_avb_port_has_cbs(chip, port);
+
+ if (old_active != new_active)
+ return mv88e6xxx_cbs_update_scheduler(chip, port, new_active);
+
+ return 0;
+}
/* Set the AVB global policy limit registers
*
@@ -197,19 +257,19 @@ static u16 mv88e6xxx_avb_get_cfg_avb_mode(struct mv88e6xxx_chip *chip)
return cfg;
}
-/* Enable or disable a port for AVB
- *
- * @param chip Marvell switch chip instance
- * @param port Switch port
- * @param enable If true, will enable AVB queues on this port.
- *
- * @return 0 on success, or a negative error value otherwise
- */
-static int mv88e6xxx_avb_set_port_avb_mode(struct mv88e6xxx_chip *chip,
- int port, bool enable)
+int mv88e6xxx_avb_set_port_avb_mode(struct mv88e6xxx_chip *chip,
+ int port, bool enable)
{
u16 cfg;
+ /* When disabling, only revert to legacy mode if no CBS queue
+ * is still active on this port.
+ */
+ if (!enable && mv88e6xxx_avb_port_has_cbs(chip, port)) {
+ dev_info(chip->dev, "p%d: CBS active, not disabling AVB\n", port);
+ return 0;
+ }
+
if (enable)
cfg = mv88e6xxx_avb_get_cfg_avb_mode(chip);
else
@@ -218,22 +278,6 @@ static int mv88e6xxx_avb_set_port_avb_mode(struct mv88e6xxx_chip *chip,
return mv88e6xxx_port_avb_write(chip, port, MV88E6XXX_PORT_AVB_CFG, cfg);
}
-static int mv88e6xxx_avb_set_avb_mode(struct mv88e6xxx_chip *chip, bool enable)
-{
- int port, err;
-
- for (port = 0, err = 0; port < mv88e6xxx_num_ports(chip); ++port) {
- if (!dsa_is_user_port(chip->ds, port))
- continue;
-
- err = mv88e6xxx_avb_set_port_avb_mode(chip, port, enable);
- if (err)
- break;
- }
-
- return err;
-}
-
int mv88e6xxx_avb_tc_enable(struct mv88e6xxx_chip *chip,
const struct mv88e6xxx_avb_tc_policy *policy)
{
@@ -254,14 +298,8 @@ int mv88e6xxx_avb_tc_enable(struct mv88e6xxx_chip *chip,
if (err)
goto err_iso_ptr;
- err = mv88e6xxx_avb_set_avb_mode(chip, true);
- if (err)
- goto err_tc;
-
return 0;
-err_tc:
- mv88e6xxx_tc_disable(chip);
err_iso_ptr:
mv88e6xxx_qav_set_iso_ptr(chip, 0);
err_mac_avb:
@@ -273,11 +311,18 @@ int mv88e6xxx_avb_tc_enable(struct mv88e6xxx_chip *chip,
int mv88e6xxx_avb_tc_disable(struct mv88e6xxx_chip *chip)
{
- int err;
+ int port, err;
- err = mv88e6xxx_avb_set_avb_mode(chip, false);
- if (err)
- return err;
+ /* Revert all user ports to legacy mode irrespective of CBS setting */
+ for (port = 0; port < mv88e6xxx_num_ports(chip); port++) {
+ if (!dsa_is_user_port(chip->ds, port))
+ continue;
+ err = mv88e6xxx_port_avb_write(chip, port,
+ MV88E6XXX_PORT_AVB_CFG,
+ MV88E6XXX_PORT_AVB_CFG_AVB_MODE_LEGACY);
+ if (err)
+ return err;
+ }
err = mv88e6xxx_tc_disable(chip);
if (err)
@@ -297,6 +342,26 @@ int mv88e6xxx_avb_tc_disable(struct mv88e6xxx_chip *chip)
return 0;
}
+static int mv88e6xxx_qav_set_port_config(struct mv88e6xxx_chip *chip, int port,
+ int queue, u16 rate, u16 hilimit)
+{
+ int err;
+
+ err = mv88e6xxx_port_qav_write(chip, port,
+ MV88E6XXX_PORT_QAV_CFG_RATE(queue),
+ rate);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_port_qav_write(chip, port,
+ MV88E6XXX_PORT_QAV_CFG_HI_LIMIT(queue),
+ hilimit);
+ if (err)
+ return err;
+
+ return 0;
+}
+
/* Assign FPri to QPri mappings for each traffic class
*
* @param chip Marvell switch chip instance
@@ -424,14 +489,63 @@ static int mv88e6352_tc_disable(struct mv88e6xxx_chip *chip)
return 0;
}
+static int mv88e6xxx_set_port_cbs_qopt(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ const struct mv88e6xxx_qav_info *qav = chip->info->qav;
+ u16 rate, hilimit;
+
+ if (!qav)
+ return -EOPNOTSUPP;
+
+ if (cbs_qopt->enable) {
+ rate = DIV_ROUND_UP(cbs_qopt->idleslope, qav->rate_unit);
+ rate = clamp_t(u16, rate, 1, qav->rate_mask);
+
+ hilimit = cbs_qopt->hicredit;
+ hilimit = clamp_t(u16, hilimit, 1, qav->hi_limit_mask);
+ } else {
+ rate = 0;
+ hilimit = qav->hi_limit_mask;
+ }
+
+ return mv88e6xxx_qav_set_port_config(chip, port, cbs_qopt->queue,
+ rate, hilimit);
+}
+
const struct mv88e6xxx_tc_ops mv88e6341_tc_ops = {
.tc_enable = mv88e6352_tc_enable,
.tc_disable = mv88e6352_tc_disable,
+ .set_port_cbs_qopt = mv88e6xxx_set_port_cbs_qopt,
};
+static int mv88e6352_set_port_cbs_qopt(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ u16 cfg;
+ int err;
+
+ err = mv88e6xxx_set_port_cbs_qopt(chip, port, cbs_qopt);
+ if (err)
+ return err;
+
+ /* Set undocumented enable register */
+ err = mv88e6xxx_port_qav_read(chip, port, MV88E6352_PORT_QAV_CFG, &cfg, 1);
+ if (err)
+ return err;
+
+ if (cbs_qopt->enable)
+ cfg |= MV88E6352_PORT_QAV_CFG_ENABLE;
+ else
+ cfg &= ~MV88E6352_PORT_QAV_CFG_ENABLE;
+
+ return mv88e6xxx_port_qav_write(chip, port, MV88E6352_PORT_QAV_CFG, cfg);
+}
+
const struct mv88e6xxx_tc_ops mv88e6352_tc_ops = {
.tc_enable = mv88e6352_tc_enable,
.tc_disable = mv88e6352_tc_disable,
+ .set_port_cbs_qopt = mv88e6352_set_port_cbs_qopt,
};
static inline u16 mv88e6390_avb_pri_map_to_reg(const struct mv88e6xxx_avb_priority_map map[])
@@ -515,4 +629,5 @@ static int mv88e6390_tc_disable(struct mv88e6xxx_chip *chip)
const struct mv88e6xxx_tc_ops mv88e6390_tc_ops = {
.tc_enable = mv88e6390_tc_enable,
.tc_disable = mv88e6390_tc_disable,
+ .set_port_cbs_qopt = mv88e6xxx_set_port_cbs_qopt,
};
diff --git a/drivers/net/dsa/mv88e6xxx/avb.h b/drivers/net/dsa/mv88e6xxx/avb.h
index 72d1c26a84731..5c21842bb68c4 100644
--- a/drivers/net/dsa/mv88e6xxx/avb.h
+++ b/drivers/net/dsa/mv88e6xxx/avb.h
@@ -94,6 +94,9 @@
#define MV88E6XXX_AVB_CFG_OUI_HI 0x0C
#define MV88E6XXX_AVB_CFG_OUI_LO 0x0D
+#define MV88E6XXX_PORT_QAV_CFG_RATE(queue) ((((queue) & 0x7) << 1))
+#define MV88E6XXX_PORT_QAV_CFG_HI_LIMIT(queue) ((((queue) & 0x7) << 1) + 1)
+
/* 6352 Family AVB Global Config (4 TX queues) */
#define MV88E6352_AVB_CFG_AVB_HI_FPRI_GET(p) MV88E6XXX_AVB_CFG_AVB_HI_FPRI_GET(p)
@@ -110,6 +113,9 @@
#define MV88E6352_AVB_CFG_AVB_LO_QPRI_GET(p) FIELD_GET(MV88E6352_AVB_CFG_AVB_LO_QPRI_MASK, p)
#define MV88E6352_AVB_CFG_AVB_LO_QPRI_SET(p) FIELD_PREP(MV88E6352_AVB_CFG_AVB_LO_QPRI_MASK, p)
+#define MV88E6352_PORT_QAV_CFG 0x08
+#define MV88E6352_PORT_QAV_CFG_ENABLE 0x8000
+
/* 6390 Family AVB Global Config (8 TX queues) */
#define MV88E6390_AVB_CFG_AVB_HI_FPRI_GET(p) MV88E6XXX_AVB_CFG_AVB_HI_FPRI_GET(p)
@@ -176,6 +182,17 @@ extern const struct mv88e6xxx_tc_ops mv88e6341_tc_ops;
extern const struct mv88e6xxx_tc_ops mv88e6352_tc_ops;
extern const struct mv88e6xxx_tc_ops mv88e6390_tc_ops;
+/* Enable or disable a port for AVB. Caller must acquire register lock.
+ *
+ * @param chip Marvell switch chip instance
+ * @param port Switch port
+ * @param enable If true, will enable AVB queues on this port.
+ *
+ * @return 0 on success, or a negative error value otherwise
+ */
+int mv88e6xxx_avb_set_port_avb_mode(struct mv88e6xxx_chip *chip,
+ int port, bool enable);
+
/* Set AVB queue priority policy. Caller must acquire register lock.
*
* @param chip Marvell switch chip instance
@@ -194,4 +211,18 @@ int mv88e6xxx_avb_tc_enable(struct mv88e6xxx_chip *chip,
*/
int mv88e6xxx_avb_tc_disable(struct mv88e6xxx_chip *chip);
+struct tc_cbs_qopt_offload;
+
+/* Set AVB credit based shaper policy. Caller must acquire register lock.
+ *
+ * @param chip Marvell switch chip instance
+ * @param port Switch port
+ * @param cbs_qopt CBS policy to apply
+ *
+ * @return 0 on success, or a negative error value otherwise
+ */
+int mv88e6xxx_qav_set_port_cbs_qopt(struct mv88e6xxx_chip *chip,
+ int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt);
+
#endif /* _MV88E6XXX_AVB_H */
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index cb7dd75ab6ef2..bea9356b3e244 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -5031,6 +5031,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.port_set_ether_type = mv88e6351_port_set_ether_type,
.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+ .port_set_scheduling_mode = mv88e6352_port_set_scheduling_mode,
.port_pause_limit = mv88e6097_port_pause_limit,
.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
@@ -5474,6 +5475,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.port_set_ether_type = mv88e6351_port_set_ether_type,
.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+ .port_set_scheduling_mode = mv88e6352_port_set_scheduling_mode,
.port_pause_limit = mv88e6097_port_pause_limit,
.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
@@ -5537,6 +5539,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
.port_set_ether_type = mv88e6351_port_set_ether_type,
.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+ .port_set_scheduling_mode = mv88e6390_port_set_scheduling_mode,
.port_pause_limit = mv88e6390_port_pause_limit,
.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
@@ -5602,6 +5605,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
.port_set_ether_type = mv88e6351_port_set_ether_type,
.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+ .port_set_scheduling_mode = mv88e6390_port_set_scheduling_mode,
.port_pause_limit = mv88e6390_port_pause_limit,
.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
@@ -5665,6 +5669,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
.port_set_ether_type = mv88e6393x_port_set_ether_type,
.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
+ .port_set_scheduling_mode = mv88e6390_port_set_scheduling_mode,
.port_pause_limit = mv88e6390_port_pause_limit,
.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
@@ -5708,6 +5713,19 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
.tc_ops = &mv88e6390_tc_ops,
};
+static const struct mv88e6xxx_qav_info mv88e6352_qav_info = {
+ .rate_unit = 32,
+ .rate_mask = GENMASK(14, 0),
+ .hi_limit_mask = GENMASK(14, 0),
+};
+
+/* The 6341 family shares the 6390-family rate-shaper encoding. */
+static const struct mv88e6xxx_qav_info mv88e6390_qav_info = {
+ .rate_unit = 64,
+ .rate_mask = GENMASK(15, 0),
+ .hi_limit_mask = GENMASK(13, 0),
+};
+
static const struct mv88e6xxx_info mv88e6xxx_table[] = {
[MV88E6020] = {
.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6020,
@@ -5922,6 +5940,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.num_tx_queues = 4,
+ .qav = &mv88e6352_qav_info,
.ops = &mv88e6161_ops,
},
@@ -5948,6 +5967,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.ptp_support = true,
.num_tx_queues = 4,
+ .qav = &mv88e6352_qav_info,
.ops = &mv88e6165_ops,
},
@@ -6152,6 +6172,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.ptp_support = true,
.num_tx_queues = 4,
+ .qav = &mv88e6352_qav_info,
.ops = &mv88e6191_ops,
},
@@ -6178,6 +6199,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.ptp_support = true,
.num_tx_queues = 8,
+ .qav = &mv88e6390_qav_info,
.ops = &mv88e6393x_ops,
},
@@ -6206,6 +6228,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.ptp_support = true,
.num_tx_queues = 8,
+ .qav = &mv88e6390_qav_info,
.ops = &mv88e6393x_ops,
},
@@ -6261,6 +6284,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.num_tx_queues = 4,
+ .qav = &mv88e6352_qav_info,
.ops = &mv88e6240_ops,
},
@@ -6311,6 +6335,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.ptp_support = true,
.num_tx_queues = 8,
+ .qav = &mv88e6390_qav_info,
.ops = &mv88e6290_ops,
},
@@ -6340,6 +6365,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.num_tx_queues = 4,
+ .qav = &mv88e6352_qav_info,
.ops = &mv88e6320_ops,
},
@@ -6369,6 +6395,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.num_tx_queues = 4,
+ .qav = &mv88e6352_qav_info,
.ops = &mv88e6321_ops,
},
@@ -6397,6 +6424,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.num_tx_queues = 4,
+ .qav = &mv88e6390_qav_info,
.ops = &mv88e6341_ops,
},
@@ -6448,6 +6476,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.num_tx_queues = 4,
+ .qav = &mv88e6352_qav_info,
.ops = &mv88e6351_ops,
},
@@ -6476,6 +6505,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.edsa_support = MV88E6XXX_EDSA_SUPPORTED,
.ptp_support = true,
.num_tx_queues = 4,
+ .qav = &mv88e6352_qav_info,
.ops = &mv88e6352_ops,
},
[MV88E6361] = {
@@ -6504,6 +6534,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.ptp_support = true,
.num_tx_queues = 8,
+ .qav = &mv88e6390_qav_info,
.ops = &mv88e6393x_ops,
},
[MV88E6390] = {
@@ -6533,6 +6564,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.edsa_support = MV88E6XXX_EDSA_UNDOCUMENTED,
.ptp_support = true,
.num_tx_queues = 8,
+ .qav = &mv88e6390_qav_info,
.ops = &mv88e6390_ops,
},
[MV88E6390X] = {
@@ -6560,6 +6592,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.edsa_support = MV88E6XXX_EDSA_UNDOCUMENTED,
.ptp_support = true,
.num_tx_queues = 8,
+ .qav = &mv88e6390_qav_info,
.ops = &mv88e6390x_ops,
},
@@ -6588,6 +6621,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.multi_chip = true,
.ptp_support = true,
.num_tx_queues = 8,
+ .qav = &mv88e6390_qav_info,
.ops = &mv88e6393x_ops,
},
};
@@ -7044,6 +7078,43 @@ static int mv88e6xxx_qos_port_mqprio(struct dsa_switch *ds, int port,
return err;
}
+static int mv88e6xxx_qos_port_cbs_set(struct dsa_switch *ds, int port,
+ struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
+ if (!dsa_is_user_port(ds, port))
+ return -EINVAL;
+
+ if (cbs_qopt->queue >= chip->info->num_tx_queues) {
+ dev_err(ds->dev, "p%d: invalid AVB queue %d\n", port, cbs_qopt->queue);
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->reg_lock);
+
+ if (cbs_qopt->enable &&
+ !(chip->avb_tc_policy.port_mask & BIT(port))) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = mv88e6xxx_qav_set_port_cbs_qopt(chip, port, cbs_qopt);
+ if (!err)
+ err = mv88e6xxx_avb_set_port_avb_mode(chip, port, cbs_qopt->enable);
+
+out:
+ mutex_unlock(&chip->reg_lock);
+
+ if (err) {
+ dev_info(ds->dev, "p%d: failed to %s AVB CBS policy: %d\n",
+ port, cbs_qopt->enable ? "enable" : "disable", err);
+ }
+
+ return err;
+}
+
static int mv88e6xxx_port_setup_tc(struct dsa_switch *ds, int port,
enum tc_setup_type type,
void *type_data)
@@ -7058,6 +7129,8 @@ static int mv88e6xxx_port_setup_tc(struct dsa_switch *ds, int port,
return mv88e6xxx_qos_query_caps(type_data);
case TC_SETUP_QDISC_MQPRIO:
return mv88e6xxx_qos_port_mqprio(ds, port, type_data);
+ case TC_SETUP_QDISC_CBS:
+ return mv88e6xxx_qos_port_cbs_set(ds, port, type_data);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index b6f4331affdc6..6c2f67e3378ef 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -126,6 +126,7 @@ enum mv88e6xxx_edsa_support {
};
struct mv88e6xxx_ops;
+struct mv88e6xxx_qav_info;
struct mv88e6xxx_info {
enum mv88e6xxx_family family;
@@ -180,6 +181,11 @@ struct mv88e6xxx_info {
/* Number of TX queues */
u8 num_tx_queues;
+ /* 802.1Qav credit-based shaping capability data, or NULL if the
+ * chip does not support CBS offload.
+ */
+ const struct mv88e6xxx_qav_info *qav;
+
/* Internal PHY start index. 0 means that internal PHYs range starts at
* port 0, 1 means internal PHYs range starts at port 1, etc
*/
@@ -654,6 +660,13 @@ struct mv88e6xxx_ops {
size_t size);
int (*port_egress_rate_limiting)(struct mv88e6xxx_chip *chip, int port);
+
+ /* Select egress scheduling mode: number of highest-numbered queues that
+ * use strict priority instead of WRR/WFQ. Used to ensure CBS-shaped
+ * queues take precedence over best-effort traffic.
+ */
+ int (*port_set_scheduling_mode)(struct mv88e6xxx_chip *chip, int port,
+ u8 mode);
int (*port_pause_limit)(struct mv88e6xxx_chip *chip, int port, u8 in,
u8 out);
int (*port_disable_learn_limit)(struct mv88e6xxx_chip *chip, int port);
@@ -875,6 +888,15 @@ struct mv88e6xxx_tc_ops {
int (*tc_enable)(struct mv88e6xxx_chip *chip,
const struct mv88e6xxx_avb_tc_policy *policy);
int (*tc_disable)(struct mv88e6xxx_chip *chip);
+ int (*set_port_cbs_qopt)(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt);
+};
+
+/* Per-family 802.1Qav credit-based shaping capability data. */
+struct mv88e6xxx_qav_info {
+ u16 rate_unit; /* in kbps */
+ u16 rate_mask; /* QPri Rate valid bits mask */
+ u16 hi_limit_mask; /* QPri Hi Limit valid bits mask */
};
static inline bool mv88e6xxx_has_stu(struct mv88e6xxx_chip *chip)
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 3a842c4d18105..936b07d8e6b0a 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -1323,6 +1323,51 @@ int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port)
0x0001);
}
+/* Set the egress scheduling mode (WRR vs strict priority for top queues).
+ * @mode is the number of high-numbered queues to put into strict priority
+ * (0 = WRR for all queues, 3 = all queues strict).
+ */
+int mv88e6352_port_set_scheduling_mode(struct mv88e6xxx_chip *chip, int port,
+ u8 mode)
+{
+ u16 reg;
+ int err;
+
+ if (mode > FIELD_MAX(MV88E6352_PORT_EGRESS_RATE_CTL2_SCHEDULE_MASK))
+ return -EINVAL;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL2,
+ ®);
+ if (err)
+ return err;
+
+ reg &= ~MV88E6352_PORT_EGRESS_RATE_CTL2_SCHEDULE_MASK;
+ reg |= FIELD_PREP(MV88E6352_PORT_EGRESS_RATE_CTL2_SCHEDULE_MASK, mode);
+
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_EGRESS_RATE_CTL2,
+ reg);
+}
+
+/* Set the egress scheduling mode (WFQ vs strict priority for top queues).
+ * @mode is the number of high-numbered queues to put into strict priority
+ * (0 = WFQ for all queues, 7 = all queues strict).
+ */
+int mv88e6390_port_set_scheduling_mode(struct mv88e6xxx_chip *chip, int port,
+ u8 mode)
+{
+ u16 reg;
+
+ if (mode > FIELD_MAX(MV88E6390_PORT_QUEUE_CTL_SCHEDULE_MASK))
+ return -EINVAL;
+
+ reg = MV88E6390_PORT_QUEUE_CTL_UPDATE |
+ FIELD_PREP(MV88E6390_PORT_QUEUE_CTL_PTR_MASK,
+ MV88E6390_PORT_QUEUE_CTL_PTR_SCHEDULE) |
+ FIELD_PREP(MV88E6390_PORT_QUEUE_CTL_SCHEDULE_MASK, mode);
+
+ return mv88e6xxx_port_write(chip, port, MV88E6390_PORT_QUEUE_CTL, reg);
+}
+
/* Offset 0x0B: Port Association Vector */
int mv88e6xxx_port_set_assoc_vector(struct mv88e6xxx_chip *chip, int port,
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 3ebd2ebafbf0f..8aaf3adb5e4d9 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -241,6 +241,22 @@
/* Offset 0x0A: Egress Rate Control 2 */
#define MV88E6XXX_PORT_EGRESS_RATE_CTL2 0x0a
+/* Number of highest-numbered queues using strict priority instead of WRR.
+ * 0 = WRR for all queues, 3 = all queues strict priority.
+ */
+#define MV88E6352_PORT_EGRESS_RATE_CTL2_SCHEDULE_MASK GENMASK(13, 12)
+
+/* Offset 0x1C: Port Queue Control (6390 family) */
+#define MV88E6390_PORT_QUEUE_CTL 0x1c
+#define MV88E6390_PORT_QUEUE_CTL_UPDATE BIT(15)
+#define MV88E6390_PORT_QUEUE_CTL_PTR_MASK GENMASK(14, 8)
+#define MV88E6390_PORT_QUEUE_CTL_DATA_MASK GENMASK(7, 0)
+/* Pointers */
+#define MV88E6390_PORT_QUEUE_CTL_PTR_SCHEDULE 0x00
+/* Number of highest-numbered queues using strict priority instead of WFQ.
+ * 0 = WFQ for all queues, 7 = all queues strict priority.
+ */
+#define MV88E6390_PORT_QUEUE_CTL_SCHEDULE_MASK GENMASK(2, 0)
/* Offset 0x0B: Port Association Vector */
#define MV88E6XXX_PORT_ASSOC_VECTOR 0x0b
@@ -563,6 +579,10 @@ int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
size_t size);
int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port);
int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port);
+int mv88e6352_port_set_scheduling_mode(struct mv88e6xxx_chip *chip, int port,
+ u8 mode);
+int mv88e6390_port_set_scheduling_mode(struct mv88e6xxx_chip *chip, int port,
+ u8 mode);
int mv88e6xxx_port_set_assoc_vector(struct mv88e6xxx_chip *chip, int port,
u16 pav);
int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property
2026-05-27 0:42 [PATCH net-next 0/6] net: dsa: mv88e6xxx: add MQPRIO/CBS offload Luke Howard
` (4 preceding siblings ...)
2026-05-27 0:42 ` [PATCH net-next 5/6] net: dsa: mv88e6xxx: CBS support Luke Howard
@ 2026-05-27 0:42 ` Luke Howard
2026-05-27 15:04 ` Conor Dooley
2026-05-30 0:49 ` sashiko-bot
5 siblings, 2 replies; 14+ messages in thread
From: Luke Howard @ 2026-05-27 0:42 UTC (permalink / raw)
To: netdev
Cc: Luke Howard, Kieran Tyrrell, Max Hunter, Cedric Jehasse,
Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, devicetree, linux-kernel
Add the vendor-specific marvell,mv88e6xxx-avb-mode property for adding
stricter handling of frames with non-AVB frame priorities and destination
addresses.
Signed-off-by: Luke Howard <lukeh@padl.com>
---
.../bindings/net/dsa/marvell,mv88e6xxx.yaml | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml b/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
index 19ae600e93394..1a01810e7682c 100644
--- a/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
+++ b/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
@@ -97,6 +97,32 @@ properties:
required:
- compatible
+ marvell,mv88e6xxx-avb-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Marvell MV88E6xxx switches that support Audio Video Bridging /
+ Time-Sensitive Networking (AVB/TSN) traffic prioritization can have
+ ports configured in one of several modes. These modes control the
+ handling of frames with non-AVB frame priorities and destination
+ addresses.
+ oneOf:
+ - description:
+ Standard Mode. Frames whose priority is mapped to an AVB
+ traffic class (TC) are considered AVB frames; other frames are
+ considered legacy (non-AVB).
+ const: 0
+ - description:
+ Enhanced Mode. Frames whose priority is mapped to an AVB TC
+ and for which a static FDB or MDB entry exists are considered
+ AVB frames. Frames with an AVB TC but no matching FDB or MDB
+ entry are dropped. AVB MDB entries can be added using the
+ MDB_FLAGS_STREAM_RESERVED netlink flag.
+ const: 1
+ - description:
+ Secure Mode. As Enhanced Mode, but the matching FDB or MDB
+ entry must also have the source port's bit set.
+ const: 2
+
allOf:
- $ref: dsa.yaml#/$defs/ethernet-ports
--
2.43.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 1/6] net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag
2026-05-27 0:42 ` [PATCH net-next 1/6] net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag Luke Howard
@ 2026-05-27 7:05 ` Nikolay Aleksandrov
2026-05-27 7:15 ` Luke Howard
0 siblings, 1 reply; 14+ messages in thread
From: Nikolay Aleksandrov @ 2026-05-27 7:05 UTC (permalink / raw)
To: Luke Howard, netdev
Cc: Kieran Tyrrell, Max Hunter, Cedric Jehasse, Jiri Pirko,
Ivan Vecera, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Andrew Lunn, Ido Schimmel,
linux-kernel, bridge
On 27/05/2026 03:42, Luke Howard wrote:
> Add a new MDB entry flag, MDB_FLAGS_STREAM_RESERVED, that userspace
> can set on RTM_NEWMDB to mark a multicast destination as belonging
> to a reserved stream (e.g. IEEE 802.1Q Stream Reservation Protocol /
> IEEE 1722 / TSN). The bridge core does no admission control on the
> basis of the flag; it is metadata propagated through switchdev to
> hardware drivers that can themselves enforce admission of AVB / SR
> priority traffic only to flagged destinations.
>
> The flag is settable via the new nested attribute MDBE_ATTR_FLAGS
> (NLA_U32 bitmask, validated against MDB_FLAGS_SETTABLE_MASK), and is
> reflected in dump output via br_mdb_entry.flags as is done today for
> the OFFLOAD/BLOCKED/STAR_EXCL flags.
>
> Assisted-by: Claude:claude-4.7-opus
> Signed-off-by: Luke Howard <lukeh@padl.com>
> ---
> include/net/switchdev.h | 4 ++++
> include/uapi/linux/if_bridge.h | 2 ++
> net/bridge/br_mdb.c | 12 ++++++++++++
> net/bridge/br_private.h | 2 ++
> net/bridge/br_switchdev.c | 17 +++++++++++------
> 5 files changed, 31 insertions(+), 6 deletions(-)
>
We don't add hw-only functionality, if you'd like to have this flag
you'll have to do a software implementation of this feature first.
Also please check the slop(code) that Claude generates before sending
it, there are obvious issues in this patch.
Cheers,
Nik
> diff --git a/include/net/switchdev.h b/include/net/switchdev.h
> index ee500706496b0..03d176708b768 100644
> --- a/include/net/switchdev.h
> +++ b/include/net/switchdev.h
> @@ -111,10 +111,14 @@ struct switchdev_obj_port_vlan {
> container_of((OBJ), struct switchdev_obj_port_vlan, obj)
>
> /* SWITCHDEV_OBJ_ID_PORT_MDB */
> +
> +#define SWITCHDEV_MDB_F_STREAM_RESERVED BIT(0)
> +
> struct switchdev_obj_port_mdb {
> struct switchdev_obj obj;
> unsigned char addr[ETH_ALEN];
> u16 vid;
> + u32 flags;
> };
>
> #define SWITCHDEV_OBJ_PORT_MDB(OBJ) \
> diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h
> index 21a700c02ef76..51ec314994bec 100644
> --- a/include/uapi/linux/if_bridge.h
> +++ b/include/uapi/linux/if_bridge.h
> @@ -705,6 +705,7 @@ struct br_mdb_entry {
> #define MDB_FLAGS_STAR_EXCL (1 << 2)
> #define MDB_FLAGS_BLOCKED (1 << 3)
> #define MDB_FLAGS_OFFLOAD_FAILED (1 << 4)
> +#define MDB_FLAGS_STREAM_RESERVED (1 << 5)
> __u8 flags;
> __u16 vid;
> struct {
> @@ -760,6 +761,7 @@ enum {
> MDBE_ATTR_IFINDEX,
> MDBE_ATTR_SRC_VNI,
> MDBE_ATTR_STATE_MASK,
> + MDBE_ATTR_FLAGS,
> __MDBE_ATTR_MAX,
> };
> #define MDBE_ATTR_MAX (__MDBE_ATTR_MAX - 1)
> diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
> index e0c7020b12f5f..1320ccd81b0a1 100644
> --- a/net/bridge/br_mdb.c
> +++ b/net/bridge/br_mdb.c
> @@ -146,6 +146,8 @@ static void __mdb_entry_fill_flags(struct br_mdb_entry *e, unsigned char flags)
> e->flags |= MDB_FLAGS_BLOCKED;
> if (flags & MDB_PG_FLAGS_OFFLOAD_FAILED)
> e->flags |= MDB_FLAGS_OFFLOAD_FAILED;
> + if (flags & MDB_PG_FLAGS_STREAM_RESERVED)
> + e->flags |= MDB_FLAGS_STREAM_RESERVED;
> }
>
> static void __mdb_entry_to_br_ip(struct br_mdb_entry *entry, struct br_ip *ip,
> @@ -664,6 +666,7 @@ static const struct nla_policy br_mdbe_attrs_pol[MDBE_ATTR_MAX + 1] = {
> MCAST_INCLUDE),
> [MDBE_ATTR_SRC_LIST] = NLA_POLICY_NESTED(br_mdbe_src_list_pol),
> [MDBE_ATTR_RTPROT] = NLA_POLICY_MIN(NLA_U8, RTPROT_STATIC),
> + [MDBE_ATTR_FLAGS] = NLA_POLICY_MASK(NLA_U32, MDB_FLAGS_STREAM_RESERVED),
> };
>
> static bool is_valid_mdb_source(struct nlattr *attr, __be16 proto,
> @@ -1072,6 +1075,8 @@ static int br_mdb_add_group(const struct br_mdb_config *cfg,
> if (entry->state == MDB_PERMANENT)
> flags |= MDB_PG_FLAGS_PERMANENT;
>
> + flags |= cfg->pg_flags;
> +
> if (br_multicast_is_star_g(&group))
> return br_mdb_add_group_star_g(cfg, mp, brmctx, flags, extack);
> else
> @@ -1225,6 +1230,13 @@ static int br_mdb_config_attrs_init(struct nlattr *set_attrs,
> cfg->rt_protocol = nla_get_u8(mdb_attrs[MDBE_ATTR_RTPROT]);
> }
>
> + if (mdb_attrs[MDBE_ATTR_FLAGS]) {
> + u32 user_flags = nla_get_u32(mdb_attrs[MDBE_ATTR_FLAGS]);
> +
> + if (user_flags & MDB_FLAGS_STREAM_RESERVED)
> + cfg->pg_flags |= MDB_PG_FLAGS_STREAM_RESERVED;
> + }
> +
> return 0;
> }
>
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 02671e648dac7..b9ee19448e38b 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -111,6 +111,7 @@ struct br_mdb_config {
> struct br_mdb_src_entry *src_entries;
> int num_src_entries;
> u8 rt_protocol;
> + unsigned char pg_flags;
> };
> #endif
>
> @@ -317,6 +318,7 @@ struct net_bridge_fdb_flush_desc {
> #define MDB_PG_FLAGS_STAR_EXCL BIT(3)
> #define MDB_PG_FLAGS_BLOCKED BIT(4)
> #define MDB_PG_FLAGS_OFFLOAD_FAILED BIT(5)
> +#define MDB_PG_FLAGS_STREAM_RESERVED BIT(6)
>
> #define PG_SRC_ENT_LIMIT 32
>
> diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
> index 18b558a931ad9..bc05cda2f5350 100644
> --- a/net/bridge/br_switchdev.c
> +++ b/net/bridge/br_switchdev.c
> @@ -548,7 +548,8 @@ static void br_switchdev_mdb_complete(struct net_device *dev, int err, void *pri
> }
>
> static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb,
> - const struct net_bridge_mdb_entry *mp)
> + const struct net_bridge_mdb_entry *mp,
> + const struct net_bridge_port_group *pg)
> {
> if (mp->addr.proto == htons(ETH_P_IP))
> ip_eth_mc_map(mp->addr.dst.ip4, mdb->addr);
> @@ -560,6 +561,9 @@ static void br_switchdev_mdb_populate(struct switchdev_obj_port_mdb *mdb,
> ether_addr_copy(mdb->addr, mp->addr.dst.mac_addr);
>
> mdb->vid = mp->addr.vid;
> + mdb->flags = 0;
> + if (pg && (pg->flags & MDB_PG_FLAGS_STREAM_RESERVED))
> + mdb->flags |= SWITCHDEV_MDB_F_STREAM_RESERVED;
> }
>
> static void br_switchdev_host_mdb_one(struct net_device *dev,
> @@ -575,7 +579,7 @@ static void br_switchdev_host_mdb_one(struct net_device *dev,
> },
> };
>
> - br_switchdev_mdb_populate(&mdb, mp);
> + br_switchdev_mdb_populate(&mdb, mp, NULL);
>
> switch (type) {
> case RTM_NEWMDB:
> @@ -622,6 +626,7 @@ static int br_switchdev_mdb_queue_one(struct list_head *mdb_list,
> unsigned long action,
> enum switchdev_obj_id id,
> const struct net_bridge_mdb_entry *mp,
> + const struct net_bridge_port_group *pg,
> struct net_device *orig_dev)
> {
> struct switchdev_obj_port_mdb mdb = {
> @@ -632,7 +637,7 @@ static int br_switchdev_mdb_queue_one(struct list_head *mdb_list,
> };
> struct switchdev_obj_port_mdb *pmdb;
>
> - br_switchdev_mdb_populate(&mdb, mp);
> + br_switchdev_mdb_populate(&mdb, mp, pg);
>
> if (action == SWITCHDEV_PORT_OBJ_ADD &&
> switchdev_port_obj_act_is_deferred(dev, action, &mdb.obj)) {
> @@ -671,7 +676,7 @@ void br_switchdev_mdb_notify(struct net_device *dev,
> if (!pg)
> return br_switchdev_host_mdb(dev, mp, type);
>
> - br_switchdev_mdb_populate(&mdb, mp);
> + br_switchdev_mdb_populate(&mdb, mp, pg);
>
> mdb.obj.orig_dev = pg->key.port->dev;
> switch (type) {
> @@ -740,7 +745,7 @@ br_switchdev_mdb_replay(struct net_device *br_dev, struct net_device *dev,
> if (mp->host_joined) {
> err = br_switchdev_mdb_queue_one(&mdb_list, dev, action,
> SWITCHDEV_OBJ_ID_HOST_MDB,
> - mp, br_dev);
> + mp, NULL, br_dev);
> if (err) {
> spin_unlock_bh(&br->multicast_lock);
> goto out_free_mdb;
> @@ -754,7 +759,7 @@ br_switchdev_mdb_replay(struct net_device *br_dev, struct net_device *dev,
>
> err = br_switchdev_mdb_queue_one(&mdb_list, dev, action,
> SWITCHDEV_OBJ_ID_PORT_MDB,
> - mp, dev);
> + mp, p, dev);
> if (err) {
> spin_unlock_bh(&br->multicast_lock);
> goto out_free_mdb;
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 1/6] net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag
2026-05-27 7:05 ` Nikolay Aleksandrov
@ 2026-05-27 7:15 ` Luke Howard
0 siblings, 0 replies; 14+ messages in thread
From: Luke Howard @ 2026-05-27 7:15 UTC (permalink / raw)
To: Nikolay Aleksandrov
Cc: netdev, Kieran Tyrrell, Max Hunter, Cedric Jehasse, Jiri Pirko,
Ivan Vecera, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Andrew Lunn, Ido Schimmel,
linux-kernel, bridge
Hi Nik,
> We don't add hw-only functionality, if you'd like to have this flag
> you'll have to do a software implementation of this feature first.
> Also please check the slop(code) that Claude generates before sending
> it, there are obvious issues in this patch.
Noted, will review. I do have a software bridge implementation which I shall include in the next revision if it progresses. I didn’t include it as it still needs configuration knobs to associate one or more traffic classes (or priorities) with this flag.
Cheers,
Luke
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 5/6] net: dsa: mv88e6xxx: CBS support
2026-05-27 0:42 ` [PATCH net-next 5/6] net: dsa: mv88e6xxx: CBS support Luke Howard
@ 2026-05-27 9:28 ` Cedric Jehasse
2026-05-27 11:04 ` Luke Howard
0 siblings, 1 reply; 14+ messages in thread
From: Cedric Jehasse @ 2026-05-27 9:28 UTC (permalink / raw)
To: Luke Howard
Cc: netdev, Kieran Tyrrell, Max Hunter, Cedric Jehasse, Andrew Lunn,
Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-kernel
> +static int mv88e6xxx_qos_port_cbs_set(struct dsa_switch *ds, int port,
> + struct tc_cbs_qopt_offload *cbs_qopt)
> +{
> + struct mv88e6xxx_chip *chip = ds->priv;
> + int err;
> +
> + if (!dsa_is_user_port(ds, port))
> + return -EINVAL;
> +
> + if (cbs_qopt->queue >= chip->info->num_tx_queues) {
> + dev_err(ds->dev, "p%d: invalid AVB queue %d\n", port, cbs_qopt->queue);
> + return -EINVAL;
> + }
> +
> + mutex_lock(&chip->reg_lock);
> +
> + if (cbs_qopt->enable &&
> + !(chip->avb_tc_policy.port_mask & BIT(port))) {
> + err = -EOPNOTSUPP;
> + goto out;
> + }
> +
> + err = mv88e6xxx_qav_set_port_cbs_qopt(chip, port, cbs_qopt);
> + if (!err)
> + err = mv88e6xxx_avb_set_port_avb_mode(chip, port, cbs_qopt->enable);
> +
Why is the avb_mode set from the cbs setter?
avb_mode is an ingress setting that controls if frames are classified as
AVB frames. Cbs configures shaping on egress.
Eg. if an AVB stream is received on port 1 and is forwarded by the switch to
port 2. AvbMode needs to be set on port 1 and cbs is configured on port 2.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 5/6] net: dsa: mv88e6xxx: CBS support
2026-05-27 9:28 ` Cedric Jehasse
@ 2026-05-27 11:04 ` Luke Howard
0 siblings, 0 replies; 14+ messages in thread
From: Luke Howard @ 2026-05-27 11:04 UTC (permalink / raw)
To: Cedric Jehasse
Cc: netdev, Kieran Tyrrell, Max Hunter, Cedric Jehasse, Andrew Lunn,
Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-kernel
> Why is the avb_mode set from the cbs setter?
> avb_mode is an ingress setting that controls if frames are classified as
> AVB frames. Cbs configures shaping on egress.
> Eg. if an AVB stream is received on port 1 and is forwarded by the switch to
> port 2. AvbMode needs to be set on port 1 and cbs is configured on port 2.
Sounds like a bug which might explain why enhanced/secure mode didn’t work when I last tested it :)
I think it might make sense to have CBS just configure the shaper/strict priority (i.e. as your patch does), and then only enable AVB mode if the shaper TC has a MQPRIO parent (retaining the mapping of TCs to AVB classes).
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property
2026-05-27 0:42 ` [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property Luke Howard
@ 2026-05-27 15:04 ` Conor Dooley
2026-05-30 0:49 ` sashiko-bot
1 sibling, 0 replies; 14+ messages in thread
From: Conor Dooley @ 2026-05-27 15:04 UTC (permalink / raw)
To: Luke Howard
Cc: netdev, Kieran Tyrrell, Max Hunter, Cedric Jehasse, Andrew Lunn,
Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 2570 bytes --]
On Wed, May 27, 2026 at 10:42:52AM +1000, Luke Howard wrote:
> Add the vendor-specific marvell,mv88e6xxx-avb-mode property for adding
> stricter handling of frames with non-AVB frame priorities and destination
> addresses.
>
> Signed-off-by: Luke Howard <lukeh@padl.com>
> ---
> .../bindings/net/dsa/marvell,mv88e6xxx.yaml | 26 +++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml b/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
> index 19ae600e93394..1a01810e7682c 100644
> --- a/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
> +++ b/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
> @@ -97,6 +97,32 @@ properties:
> required:
> - compatible
>
> + marvell,mv88e6xxx-avb-mode:
"marvell,avb-mode" should be sufficient here.
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description:
> + Marvell MV88E6xxx switches that support Audio Video Bridging /
> + Time-Sensitive Networking (AVB/TSN) traffic prioritization can have
> + ports configured in one of several modes. These modes control the
> + handling of frames with non-AVB frame priorities and destination
> + addresses.
I've got no idea if this is a valid thing to make a property for, but I
really don't like this things being integers when there's some pretty
clear strings you could use instead: standard, enhanced and secure.
Also, you're missing a default, which is required for optional
properties.
Cheers,
Conor.
pw-bot: changes-requested
> + oneOf:
> + - description:
> + Standard Mode. Frames whose priority is mapped to an AVB
> + traffic class (TC) are considered AVB frames; other frames are
> + considered legacy (non-AVB).
> + const: 0
> + - description:
> + Enhanced Mode. Frames whose priority is mapped to an AVB TC
> + and for which a static FDB or MDB entry exists are considered
> + AVB frames. Frames with an AVB TC but no matching FDB or MDB
> + entry are dropped. AVB MDB entries can be added using the
> + MDB_FLAGS_STREAM_RESERVED netlink flag.
> + const: 1
> + - description:
> + Secure Mode. As Enhanced Mode, but the matching FDB or MDB
> + entry must also have the source port's bit set.
> + const: 2
> +
> allOf:
> - $ref: dsa.yaml#/$defs/ethernet-ports
>
> --
> 2.43.0
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property
2026-05-27 0:42 ` [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property Luke Howard
2026-05-27 15:04 ` Conor Dooley
@ 2026-05-30 0:49 ` sashiko-bot
2026-05-30 2:01 ` Luke Howard
1 sibling, 1 reply; 14+ messages in thread
From: sashiko-bot @ 2026-05-30 0:49 UTC (permalink / raw)
To: Luke Howard; +Cc: robh, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] Encoding runtime network configuration (traffic policy) in the Device Tree.
- [Low] Schema validation fails for all valid inputs due to an array/integer type mismatch.
- [Low] The Device Tree binding description violates OS-agnostic rules by referencing Linux-specific netlink flags.
--
commit aae733d9f490b168e212410379f1d8278d34ee3c
Author: Luke Howard <lukeh@padl.com>
dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property
This commit adds a vendor-specific marvell,mv88e6xxx-avb-mode property to the
Marvell MV88E6xxx DSA switch binding. The property configures the handling of
frames with non-AVB priorities and destination addresses into Standard,
Enhanced, or Secure modes.
> diff --git a/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml b/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
> index 19ae600e93394..1a01810e7682c 100644
> --- a/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
> +++ b/Documentation/devicetree/bindings/net/dsa/marvell,mv88e6xxx.yaml
> @@ -97,6 +97,32 @@ properties:
> required:
> - compatible
>
> + marvell,mv88e6xxx-avb-mode:
> + $ref: /schemas/types.yaml#/definitions/uint32
[Severity: Medium]
Does adding this property encode dynamic network configuration policy into the
Device Tree?
Device trees are typically meant for static hardware description. Setting how
AVB frames are handled and whether frames are dropped appears to be a runtime
traffic policy.
Shouldn't this be managed via standard Linux networking UAPIs like devlink,
bridge attributes, or tc, rather than being hardcoded in the DT?
> + description:
> + Marvell MV88E6xxx switches that support Audio Video Bridging /
> + Time-Sensitive Networking (AVB/TSN) traffic prioritization can have
> + ports configured in one of several modes. These modes control the
> + handling of frames with non-AVB frame priorities and destination
> + addresses.
> + oneOf:
> + - description:
> + Standard Mode. Frames whose priority is mapped to an AVB
> + traffic class (TC) are considered AVB frames; other frames are
> + considered legacy (non-AVB).
> + const: 0
> + - description:
> + Enhanced Mode. Frames whose priority is mapped to an AVB TC
> + and for which a static FDB or MDB entry exists are considered
> + AVB frames. Frames with an AVB TC but no matching FDB or MDB
> + entry are dropped. AVB MDB entries can be added using the
> + MDB_FLAGS_STREAM_RESERVED netlink flag.
[Severity: Low]
Does referencing MDB_FLAGS_STREAM_RESERVED violate the OS-agnostic rules for
Device Tree bindings?
This leaks Linux-specific netlink implementation details directly into the
hardware specification.
> + const: 1
[Severity: Low]
Will this schema fail validation for all valid inputs due to an array and
integer type mismatch?
The uint32 reference above expects the property to be parsed as a
single-element array, but this applies a const constraint for an integer.
In JSON schema, an array does not match an integer.
Should this use enum: [0, 1, 2] instead, or place the const inside an items
array so it passes the binding checks?
> + - description:
> + Secure Mode. As Enhanced Mode, but the matching FDB or MDB
> + entry must also have the source port's bit set.
> + const: 2
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1779841530.git.lukeh@padl.com?part=6
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property
2026-05-30 0:49 ` sashiko-bot
@ 2026-05-30 2:01 ` Luke Howard
0 siblings, 0 replies; 14+ messages in thread
From: Luke Howard @ 2026-05-30 2:01 UTC (permalink / raw)
To: sashiko-reviews; +Cc: robh, devicetree, conor+dt
> On 30 May 2026, at 10:49 am, sashiko-bot@kernel.org wrote:
>
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [Medium] Encoding runtime network configuration (traffic policy) in the Device Tree.
> - [Low] Schema validation fails for all valid inputs due to an array/integer type mismatch.
> - [Low] The Device Tree binding description violates OS-agnostic rules by referencing Linux-specific netlink flags.
Thank you for the comments. This commit will be removed in the next revision, in favour of configuring the device using devlink.
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-05-30 2:01 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-27 0:42 [PATCH net-next 0/6] net: dsa: mv88e6xxx: add MQPRIO/CBS offload Luke Howard
2026-05-27 0:42 ` [PATCH net-next 1/6] net: bridge: mdb: add MDB_FLAGS_STREAM_RESERVED flag Luke Howard
2026-05-27 7:05 ` Nikolay Aleksandrov
2026-05-27 7:15 ` Luke Howard
2026-05-27 0:42 ` [PATCH net-next 2/6] net: dsa: mv88e6xxx: add num_tx_queues to chip info structure Luke Howard
2026-05-27 0:42 ` [PATCH net-next 3/6] net: dsa: mv88e6xxx: add MV88E6XXX_G1_ATU_CTL_MAC_AVB setter Luke Howard
2026-05-27 0:42 ` [PATCH net-next 4/6] net: dsa: mv88e6xxx: MQPRIO support Luke Howard
2026-05-27 0:42 ` [PATCH net-next 5/6] net: dsa: mv88e6xxx: CBS support Luke Howard
2026-05-27 9:28 ` Cedric Jehasse
2026-05-27 11:04 ` Luke Howard
2026-05-27 0:42 ` [PATCH net-next 6/6] dt-bindings: net: dsa: mv88e6xxx: add mv88e6xxx-avb-mode property Luke Howard
2026-05-27 15:04 ` Conor Dooley
2026-05-30 0:49 ` sashiko-bot
2026-05-30 2:01 ` Luke Howard
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.