* [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload
@ 2026-11-12 8:37 Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 01/10] net: ieee8021q: print traffic type and queue count with %u Oleksij Rempel
` (9 more replies)
0 siblings, 10 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Add ingress QoS offload for the RTL8365MB switch through the dcbnl app
interface: a per-port default priority, PCP trust and DSCP trust. The
mapping helpers go to net/core/ieee8021q_helpers.c so other drivers
can share them.
Two changes are visible to users. The driver now rejects a devicetree
port that the chip does not have. With a default priority set, an
untagged frame that leaves a VLAN-tagged port carries that priority as
its PCP.
Tested on an RTL8365MB-VC with eight queues.
ETS scheduling and CPU-tag pre-classification will follow in separate
series.
v1:
https://lore.kernel.org/r/20261002115850.962676-1-o.rempel@pengutronix.de
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
Oleksij Rempel (10):
net: ieee8021q: print traffic type and queue count with %u
net: ieee8021q: add pcp_to_tt()
net: ieee8021q: clarify the tt_to_tc() traffic-class mapping
net: ieee8021q: add tt_to_pcp()
net: dsa: realtek: rtl8365mb: describe the ports of each chip
net: dsa: realtek: rtl8365mb: store the egress queue count per chip
net: dsa: realtek: rtl8365mb: add QoS baseline and DCB default prio
net: dsa: realtek: rtl8365mb: program the PCP of untagged frames
net: dsa: realtek: rtl8365mb: offload DCB apptrust
net: dsa: realtek: rtl8365mb: report the DCB DSCP-to-priority map
drivers/net/dsa/realtek/Kconfig | 1 +
drivers/net/dsa/realtek/Makefile | 5 +-
drivers/net/dsa/realtek/realtek.h | 32 ++
drivers/net/dsa/realtek/rtl8365mb_main.c | 70 ++++-
drivers/net/dsa/realtek/rtl8365mb_qos.c | 507 +++++++++++++++++++++++++++++++
drivers/net/dsa/realtek/rtl8365mb_qos.h | 33 ++
drivers/net/dsa/realtek/rtl83xx-qos.c | 209 +++++++++++++
drivers/net/dsa/realtek/rtl83xx-qos.h | 20 ++
include/net/ieee8021q.h | 21 +-
net/core/ieee8021q_helpers.c | 103 ++++++-
10 files changed, 979 insertions(+), 22 deletions(-)
---
base-commit: fa5196392691051963726787944db872420dfa75
change-id: 20261109-rtl8365mb-dcb-ingress-0dae31a02c0f
Best regards,
--
Oleksij Rempel <o.rempel@pengutronix.de>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 01/10] net: ieee8021q: print traffic type and queue count with %u
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 02/10] net: ieee8021q: add pcp_to_tt() Oleksij Rempel
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Switch the out-of-range diagnostic in ieee8021q_tt_to_tc() from %d to
%u. The traffic class count num_queues is an unsigned int and the
traffic type is an enumeration, so printing them as unsigned matches
their types. The print sits on the range-check error path, which still
rejects a negative traffic type; %u only changes how an out-of-range
value is rendered, not which values are rejected.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
---
Changes in v2:
- Drop the reference to a later patch from the commit message. The diff
is unchanged, so the Reviewed-by tags are kept.
---
net/core/ieee8021q_helpers.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c
index 669b357b73b2d7746e4856afdc8187cbaa4f9a6d..bbacdfff9b4c2ec14779baa79d341527fed1920c 100644
--- a/net/core/ieee8021q_helpers.c
+++ b/net/core/ieee8021q_helpers.c
@@ -99,7 +99,7 @@ static const u8 ieee8021q_1queue_tt_tc_map[] = {
int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues)
{
if (tt < 0 || tt >= IEEE8021Q_TT_MAX) {
- pr_err("Requested Traffic Type (%d) is out of range (%d)\n", tt,
+ pr_err("Requested Traffic Type (%u) is out of range (%u)\n", tt,
IEEE8021Q_TT_MAX);
return -EINVAL;
}
@@ -131,7 +131,7 @@ int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues)
return ieee8021q_1queue_tt_tc_map[tt];
}
- pr_err("Invalid number of queues %d\n", num_queues);
+ pr_err("Invalid number of queues %u\n", num_queues);
return -EINVAL;
}
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 02/10] net: ieee8021q: add pcp_to_tt()
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 01/10] net: ieee8021q: print traffic type and queue count with %u Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 03/10] net: ieee8021q: clarify the tt_to_tc() traffic-class mapping Oleksij Rempel
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Add a helper that maps an IEEE 802.1Q Priority Code Point (PCP) to the
corresponding 802.1Q traffic type, complementing the existing
ietf_dscp_to_ieee8021q_tt().
It is very nearly an identity map, except that PCP 0 (Best Effort) and
PCP 1 (Background) are swapped: per 802.1Q-2022 (Annex I, Table I-2)
Background is a lower priority than Best Effort even though it carries
the higher PCP value. PCP 2..7 map straight through to the matching
traffic type, and values above 7 are rejected with -EINVAL. This decodes
the 8P0D encoding (Table 6-3), where every PCP value is a priority and
none carries drop eligibility.
Drivers that classify ingress traffic by PCP need this to map into a
priority-ordered internal space without misordering Best Effort and
Background. The first consumer, the realtek rtl8365mb DSA driver, lands
later in this series.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Linus Walleij <linusw@kernel.org>
---
Changes in v2:
- use a lookup table instead of the switch (Luiz)
- check the lookup table size at build time
- kernel-doc: cite Table 6-3 instead of Table I-7, fix the sentence on
7P1D/6P2D/5P3D, document the stub's return value
- commit message: cite Table 6-3 instead of Table I-7
- reword the @IEEE8021Q_TT_BE kernel-doc and the enum title
- keep Linus's Reviewed-by: the mapping is unchanged
---
include/net/ieee8021q.h | 15 ++++++++++++---
net/core/ieee8021q_helpers.c | 42 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/include/net/ieee8021q.h b/include/net/ieee8021q.h
index 8bfe903dd3d00fb432b32e3150d77f3a43d00f0c..c6c8c94ec7fb39603f631d50928f2354bfcd8f81 100644
--- a/include/net/ieee8021q.h
+++ b/include/net/ieee8021q.h
@@ -7,11 +7,14 @@
#include <linux/errno.h>
/**
- * enum ieee8021q_traffic_type - 802.1Q traffic type priority values (802.1Q-2022)
+ * enum ieee8021q_traffic_type - 802.1Q traffic types (802.1Q-2022)
*
* @IEEE8021Q_TT_BK: Background
- * @IEEE8021Q_TT_BE: Best Effort (default). According to 802.1Q-2022, BE is 0
- * but has higher priority than BK which is 1.
+ * @IEEE8021Q_TT_BE: Best Effort (default). The enum value is not the 802.1Q
+ * priority. IEEE 802.1Q-2022 (Annex I.4, Table I-2) gives Best Effort priority
+ * 0 and Background priority 1, and says that priority 1 is lower than priority
+ * 0. The enum orders the traffic types lowest first, so Background is 0 and
+ * Best Effort is 1.
* @IEEE8021Q_TT_EE: Excellent Effort
* @IEEE8021Q_TT_CA: Critical Applications
* @IEEE8021Q_TT_VI: Video, < 100 ms latency and jitter
@@ -38,6 +41,7 @@ enum ieee8021q_traffic_type {
#if IS_ENABLED(CONFIG_NET_IEEE8021Q_HELPERS)
int ietf_dscp_to_ieee8021q_tt(u8 dscp);
+int ieee8021q_pcp_to_tt(u8 pcp);
int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues);
#else
@@ -47,6 +51,11 @@ static inline int ietf_dscp_to_ieee8021q_tt(u8 dscp)
return -EOPNOTSUPP;
}
+static inline int ieee8021q_pcp_to_tt(u8 pcp)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt,
unsigned int num_queues)
{
diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c
index bbacdfff9b4c2ec14779baa79d341527fed1920c..b052120e67c525ac5a45b26de4cb879a0d758d17 100644
--- a/net/core/ieee8021q_helpers.c
+++ b/net/core/ieee8021q_helpers.c
@@ -2,6 +2,7 @@
// Copyright (c) 2024 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
#include <linux/array_size.h>
+#include <linux/if_vlan.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <net/dscp.h>
@@ -222,3 +223,44 @@ int ietf_dscp_to_ieee8021q_tt(u8 dscp)
return SIMPLE_IETF_DSCP_TO_IEEE8021Q_TT(dscp);
}
EXPORT_SYMBOL_GPL(ietf_dscp_to_ieee8021q_tt);
+
+/* Index: the PCP, which in 8P0D is the 802.1Q priority. Priority 0 is Best
+ * Effort and priority 1 Background, the reverse of their enum order.
+ */
+static const u8 ieee8021q_pcp_to_tt_map[] = {
+ [0] = IEEE8021Q_TT_BE,
+ [1] = IEEE8021Q_TT_BK,
+ [2] = IEEE8021Q_TT_EE,
+ [3] = IEEE8021Q_TT_CA,
+ [4] = IEEE8021Q_TT_VI,
+ [5] = IEEE8021Q_TT_VO,
+ [6] = IEEE8021Q_TT_IC,
+ [7] = IEEE8021Q_TT_NC,
+};
+
+static_assert(ARRAY_SIZE(ieee8021q_pcp_to_tt_map) ==
+ (VLAN_PRIO_MASK >> VLAN_PRIO_SHIFT) + 1);
+
+/**
+ * ieee8021q_pcp_to_tt - Map an IEEE 802.1Q PCP to a traffic type
+ * @pcp: IEEE 802.1Q Priority Code Point value
+ *
+ * Decode @pcp with the 8P0D row of Table 6-3 (IEEE 802.1Q-2022, 6.9.3). In that
+ * row the priority equals the PCP, and no PCP carries drop eligibility. Then
+ * look up the traffic type for that priority in Table I-2. The enum values of
+ * Best Effort and Background are not their priorities, see
+ * &enum ieee8021q_traffic_type.
+ *
+ * The other rows of Table 6-3 (7P1D, 6P2D, 5P3D) are not decoded here.
+ *
+ * Return: the traffic type, or -EINVAL if @pcp is above 7. Without
+ * CONFIG_NET_IEEE8021Q_HELPERS, the stub returns -EOPNOTSUPP.
+ */
+int ieee8021q_pcp_to_tt(u8 pcp)
+{
+ if (pcp >= ARRAY_SIZE(ieee8021q_pcp_to_tt_map))
+ return -EINVAL;
+
+ return ieee8021q_pcp_to_tt_map[pcp];
+}
+EXPORT_SYMBOL_GPL(ieee8021q_pcp_to_tt);
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 03/10] net: ieee8021q: clarify the tt_to_tc() traffic-class mapping
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 01/10] net: ieee8021q: print traffic type and queue count with %u Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 02/10] net: ieee8021q: add pcp_to_tt() Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 04/10] net: ieee8021q: add tt_to_pcp() Oleksij Rempel
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Document that the tt_to_tc() traffic-class numbers are the pre-computed
composition of Table I-2 (traffic type -> priority) with Table 8-5
(priority -> traffic class). Note that Table 8-5 is the mapping for
implementations without the credit-based shaper, so the helper's scope
is clear; credit-based-shaper implementations use Table 34-1/34-2
instead.
The existing kdoc cited Annex I Table I-1 as the mapping basis, but
Table I-1 only partitions traffic types into classes; it does not assign
the class numbers.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
---
Q: Luiz: does make htmldocs build cleanly and render the diagram?
A: No .rst under Documentation/ includes net/core/ieee8021q_helpers.c
today, so make htmldocs does not render it. scripts/kernel-doc -rst
-Wall on the file is clean, and an htmldocs build with the file added
to Documentation/networking/kapi.rst produced no new warning. Adding it
to kapi.rst will follow as a separate patch.
Changes in v2:
- make the mapping diagram an RST literal block (Luiz)
- call Tables 8-5 and 34-1/34-2 the recommended mappings, as 802.1Q does
- keep the Reviewed-by tags: only the kdoc wording and the diagram
format changed
---
net/core/ieee8021q_helpers.c | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c
index b052120e67c525ac5a45b26de4cb879a0d758d17..8fd3ea87c7bcb6d7f34a4cf5969530633edd6abb 100644
--- a/net/core/ieee8021q_helpers.c
+++ b/net/core/ieee8021q_helpers.c
@@ -13,10 +13,10 @@
compiletime_assert(ARRAY_SIZE(tbl) == IEEE8021Q_TT_MAX, \
#tbl " size mismatch")
-/* The following arrays map Traffic Types (TT) to traffic classes (TC) for
- * different number of queues as shown in the example provided by
- * IEEE 802.1Q-2022 in Annex I "I.3 Traffic type to traffic class mapping" and
- * Table I-1 "Traffic type to traffic class mapping".
+/* The following arrays map a traffic type (TT) to a traffic class (TC) for
+ * each supported number of queues. The mapping is the traffic-type -> priority
+ * -> traffic-class composition documented in the ieee8021q_tt_to_tc() kdoc
+ * below, reduced for the given number of queues.
*/
static const u8 ieee8021q_8queue_tt_tc_map[] = {
[IEEE8021Q_TT_BK] = 0,
@@ -85,17 +85,22 @@ static const u8 ieee8021q_1queue_tt_tc_map[] = {
};
/**
- * ieee8021q_tt_to_tc - Map IEEE 802.1Q Traffic Type to Traffic Class
- * @tt: IEEE 802.1Q Traffic Type
- * @num_queues: Number of queues
+ * ieee8021q_tt_to_tc - Map an IEEE 802.1Q traffic type to a traffic class
+ * @tt: IEEE 802.1Q traffic type
+ * @num_queues: number of traffic classes (queues) available, 1..8
*
- * This function maps an IEEE 802.1Q Traffic Type to a Traffic Class (TC) based
- * on the number of queues configured on the NIC. The mapping is based on the
- * example provided by IEEE 802.1Q-2022 in Annex I "I.3 Traffic type to traffic
- * class mapping" and Table I-1 "Traffic type to traffic class mapping".
+ * Return the traffic-class number for @tt. It is the pre-computed composition
+ * of two IEEE 802.1Q-2022 mappings::
*
- * Return: Traffic Class corresponding to the given Traffic Type or negative
- * value in case of error.
+ * traffic type --[Table I-2]--> priority --[Table 8-5]--> traffic class
+ *
+ * Table 8-5 is the recommended priority-to-traffic-class mapping for
+ * implementations that do NOT support the credit-based shaper (8.6.8.2).
+ * Credit-based-shaper implementations have a different recommended mapping
+ * (Table 34-1 for SR classes A and B, or Table 34-2 for SR class B only),
+ * which this helper does not implement.
+ *
+ * Return: the traffic class, or a negative value on error.
*/
int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues)
{
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 04/10] net: ieee8021q: add tt_to_pcp()
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
` (2 preceding siblings ...)
2026-11-12 8:37 ` [PATCH net-next v2 03/10] net: ieee8021q: clarify the tt_to_tc() traffic-class mapping Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 05/10] net: dsa: realtek: rtl8365mb: describe the ports of each chip Oleksij Rempel
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Add ieee8021q_tt_to_pcp(), the inverse of ieee8021q_pcp_to_tt(): it
encodes an 802.1Q traffic type back to its Priority Code Point, and
completes the pcp/tt helper family.
A driver that reseeds its PCP classifier through ieee8021q_pcp_to_tt()
stores traffic types in the hardware tables, while the dcbnl priority
ops exchange 802.1p priorities across the DSA boundary. Reading such a
table back therefore needs the reverse mapping to restore the 802.1p
value, which no existing helper provides. Because pcp_to_tt only
transposes Best Effort and Background and is the identity elsewhere the
two directions are numerically equal; provide tt_to_pcp as its own named
function so the read path does not decode a traffic type as if it were a
PCP.
The first user is the Realtek rtl8365mb DSA driver, whose dcbnl
get_default_prio and get_dscp_prio paths read back traffic types and
must return 802.1p priorities.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Linus Walleij <linusw@kernel.org>
---
Changes in v2:
- look the PCP up in the pcp_to_tt() table instead of a switch, so the
two helpers cannot disagree (Luiz)
- check at build time that the table has one entry per traffic type
- kernel-doc: cite the encoding table, Table 6-2 (6.9.3), not the
decoding Table I-7, and name the IEEE 802.1Q-2022 edition
- kernel-doc: refer to the enum doc for the Best Effort/Background
order, and document each return value, including the stub's
- keep Linus's Reviewed-by: the mapping is unchanged
---
include/net/ieee8021q.h | 6 ++++++
net/core/ieee8021q_helpers.c | 26 ++++++++++++++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/include/net/ieee8021q.h b/include/net/ieee8021q.h
index c6c8c94ec7fb39603f631d50928f2354bfcd8f81..c0cb6a8004da451fe2be835b9ec1f6fbef1779d5 100644
--- a/include/net/ieee8021q.h
+++ b/include/net/ieee8021q.h
@@ -42,6 +42,7 @@ enum ieee8021q_traffic_type {
int ietf_dscp_to_ieee8021q_tt(u8 dscp);
int ieee8021q_pcp_to_tt(u8 pcp);
+int ieee8021q_tt_to_pcp(enum ieee8021q_traffic_type tt);
int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt, unsigned int num_queues);
#else
@@ -56,6 +57,11 @@ static inline int ieee8021q_pcp_to_tt(u8 pcp)
return -EOPNOTSUPP;
}
+static inline int ieee8021q_tt_to_pcp(enum ieee8021q_traffic_type tt)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int ieee8021q_tt_to_tc(enum ieee8021q_traffic_type tt,
unsigned int num_queues)
{
diff --git a/net/core/ieee8021q_helpers.c b/net/core/ieee8021q_helpers.c
index 8fd3ea87c7bcb6d7f34a4cf5969530633edd6abb..d9033398b0ec6320c4e0e2fa3f0913d96937d72e 100644
--- a/net/core/ieee8021q_helpers.c
+++ b/net/core/ieee8021q_helpers.c
@@ -245,6 +245,8 @@ static const u8 ieee8021q_pcp_to_tt_map[] = {
static_assert(ARRAY_SIZE(ieee8021q_pcp_to_tt_map) ==
(VLAN_PRIO_MASK >> VLAN_PRIO_SHIFT) + 1);
+/* ieee8021q_tt_to_pcp() needs one entry per traffic type. */
+static_assert(ARRAY_SIZE(ieee8021q_pcp_to_tt_map) == IEEE8021Q_TT_MAX);
/**
* ieee8021q_pcp_to_tt - Map an IEEE 802.1Q PCP to a traffic type
@@ -269,3 +271,27 @@ int ieee8021q_pcp_to_tt(u8 pcp)
return ieee8021q_pcp_to_tt_map[pcp];
}
EXPORT_SYMBOL_GPL(ieee8021q_pcp_to_tt);
+
+/**
+ * ieee8021q_tt_to_pcp - Map an IEEE 802.1Q traffic type to a PCP
+ * @tt: IEEE 802.1Q traffic type
+ *
+ * Encode @tt to its priority per Table I-2 (IEEE 802.1Q-2022, I.4). Then
+ * encode the priority to its PCP per the 8P0D row of Table 6-2 (6.9.3). This
+ * is the inverse of ieee8021q_pcp_to_tt(). The enum values of Best Effort and
+ * Background are not their priorities, see &enum ieee8021q_traffic_type.
+ *
+ * Return: the PCP (0..7), or -EINVAL if @tt is not a traffic type. Without
+ * CONFIG_NET_IEEE8021Q_HELPERS, the stub returns -EOPNOTSUPP.
+ */
+int ieee8021q_tt_to_pcp(enum ieee8021q_traffic_type tt)
+{
+ int pcp;
+
+ for (pcp = 0; pcp < ARRAY_SIZE(ieee8021q_pcp_to_tt_map); pcp++)
+ if (ieee8021q_pcp_to_tt_map[pcp] == tt)
+ return pcp;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ieee8021q_tt_to_pcp);
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 05/10] net: dsa: realtek: rtl8365mb: describe the ports of each chip
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
` (3 preceding siblings ...)
2026-11-12 8:37 ` [PATCH net-next v2 04/10] net: ieee8021q: add tt_to_pcp() Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 15:08 ` Alvin Šipraga
2026-11-12 8:37 ` [PATCH net-next v2 06/10] net: dsa: realtek: rtl8365mb: store the egress queue count per chip Oleksij Rempel
` (4 subsequent siblings)
9 siblings, 1 reply; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Record which ports each chip has, report one more than its highest
port to DSA as the port count, and reject a devicetree port the chip
does not have. Lower the family maximum to 8 to match.
Commit ca5ecd4246d4 ("net: dsa: realtek: rtl8365mb: correct the max
number of ports") raised the port count of all chips to 11, but no
supported chip has a port above 7.
Since commit 51a4a22301b0 ("net: dsa: realtek: rtl8365mb: use dsa
helpers for port iteration"), setup also programs unused ports, so
every probe writes per-port registers of ports 8..10. Their learn-limit
writes go to 0x0A28..0x0A2A, above the learn-limit registers of ports
0..7. We have not seen a visible effect from these writes.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
Changes in v2:
- New in v2. The QoS patches after it handle ports 0..7 only.
---
drivers/net/dsa/realtek/rtl8365mb_main.c | 28 ++++++++++++++++++++++++++--
1 file changed, 26 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index da06f952a053d1497647a2c6f15a861fc21860f9..1e6cdc61f6bd3b3f74e422dbbcfdcb67cc0532a7 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -115,7 +115,7 @@
#define RTL8365MB_PHYADDRMAX 7
#define RTL8365MB_NUM_PHYREGS 32
#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1)
-#define RTL8365MB_MAX_NUM_PORTS 11
+#define RTL8365MB_MAX_NUM_PORTS 8
/* Valid for the whole family except RTL8370B, which has 4160 entries.
* RTL8370B is mentioned in vendor code but it might not even belong
* to the same RTL8367C family.
@@ -707,6 +707,7 @@ struct rtl8365mb_extint {
* @name: human-readable chip name
* @chip_id: chip identifier
* @chip_ver: chip silicon revision
+ * @port_mask: ports the chip has
* @extints: available external interfaces
* @jam_table: chip-specific initialization jam table
* @jam_size: size of the chip's jam table
@@ -719,6 +720,7 @@ struct rtl8365mb_chip_info {
const char *name;
u32 chip_id;
u32 chip_ver;
+ u32 port_mask;
const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS];
const struct rtl8365mb_jam_tbl_entry *jam_table;
size_t jam_size;
@@ -726,11 +728,20 @@ struct rtl8365mb_chip_info {
/* Chip info for each supported switch in the family */
#define PHY_INTF(_mode) (RTL8365MB_PHY_INTERFACE_MODE_ ## _mode)
+#define PORT_MASK(_m) \
+ ((_m) + \
+ BUILD_BUG_ON_ZERO((_m) & ~GENMASK(RTL8365MB_MAX_NUM_PORTS - 1, 0)))
+
+/* Copper ports are 0..3 on the RTL8365MB-VC and 0..4 on the others. Extension
+ * interfaces are port 6 on the VC and ports 6 and 7 on the others, as the
+ * extints rows show.
+ */
static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
{
.name = "RTL8365MB-VC",
.chip_id = 0x6367,
.chip_ver = 0x0040,
+ .port_mask = PORT_MASK(GENMASK(3, 0) | BIT(6)),
.extints = {
{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
PHY_INTF(RMII) | PHY_INTF(RGMII) },
@@ -742,6 +753,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
.name = "RTL8367S",
.chip_id = 0x6367,
.chip_ver = 0x00A0,
+ .port_mask = PORT_MASK(GENMASK(4, 0) | BIT(6) | BIT(7)),
.extints = {
{ 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) },
{ 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) |
@@ -754,6 +766,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
.name = "RTL8367SB",
.chip_id = 0x6367,
.chip_ver = 0x0010,
+ .port_mask = PORT_MASK(GENMASK(4, 0) | BIT(6) | BIT(7)),
.extints = {
{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
PHY_INTF(RMII) | PHY_INTF(RGMII) |
@@ -768,6 +781,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
.name = "RTL8367RB-VB",
.chip_id = 0x6367,
.chip_ver = 0x0020,
+ .port_mask = PORT_MASK(GENMASK(4, 0) | BIT(6) | BIT(7)),
.extints = {
{ 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
PHY_INTF(RMII) | PHY_INTF(RGMII) },
@@ -3140,6 +3154,16 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
dev_info(priv->dev, "no interrupt support\n");
dsa_switch_for_each_port(dp, ds) {
+ if (dsa_port_is_unused(dp))
+ continue;
+
+ if (!(mb->chip_info->port_mask & BIT(dp->index))) {
+ dev_err(priv->dev, "port %d does not exist on %s\n",
+ dp->index, mb->chip_info->name);
+ ret = -EINVAL;
+ goto out_teardown_irq;
+ }
+
/* Cascading (DSA links) is not supported yet.
* Historically, the driver has always been broken
* without a dedicated CPU port because CPU tagging
@@ -3349,7 +3373,7 @@ static int rtl8365mb_detect(struct realtek_priv *priv)
dev_info(priv->dev, "found an %s switch\n", mb->chip_info->name);
- priv->num_ports = RTL8365MB_MAX_NUM_PORTS;
+ priv->num_ports = fls(mb->chip_info->port_mask);
mb->priv = priv;
mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS;
mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL;
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 06/10] net: dsa: realtek: rtl8365mb: store the egress queue count per chip
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
` (4 preceding siblings ...)
2026-11-12 8:37 ` [PATCH net-next v2 05/10] net: dsa: realtek: rtl8365mb: describe the ports of each chip Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 07/10] net: dsa: realtek: rtl8365mb: add QoS baseline and DCB default prio Oleksij Rempel
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Store the number of egress queues in the per-chip info structure and
advertise it through ds->num_tx_queues, following the ksz driver
(struct ksz_chip_data.num_tx_queues).
The queue count is a per-chip hardware property, and this driver supports
several chips of the rtl8367c family. All currently supported chips expose
eight queues, but keeping the count in chip_info lets the QoS code that
follows program the priority-to-queue map via the ieee8021q helpers for
whatever queue count a chip carries, so the 802.1Q collapse is correct
rather than hard-coded to eight.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Reviewed-by: Luiz Angelo Daros de Luca <luizluca@gmail.com>
---
Q: Linus: why is the queue count not in struct realtek_variant?
A: The count is per chip, while realtek_variant is per subdriver, and
rtl8365mb serves several chips. The QoS code added later in this series
reads ds->num_tx_queues, as Linus suggested in his v1 review, so
RTL8366RB can advertise its own count the same way.
Changes in v2:
- drop the num_tx_queues comment, which restated the assignment
- keep the Reviewed-by tags: v2 only drops a comment
---
drivers/net/dsa/realtek/rtl8365mb_main.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index 1e6cdc61f6bd3b3f74e422dbbcfdcb67cc0532a7..f7960d94f1cd9f6a1982849a9e5a1b5d98880b4b 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -711,6 +711,7 @@ struct rtl8365mb_extint {
* @extints: available external interfaces
* @jam_table: chip-specific initialization jam table
* @jam_size: size of the chip's jam table
+ * @num_tx_queues: number of egress queues exposed by the chip
*
* These data are specific to a given chip in the family of switches supported
* by this driver. When adding support for another chip in the family, a new
@@ -724,6 +725,7 @@ struct rtl8365mb_chip_info {
const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS];
const struct rtl8365mb_jam_tbl_entry *jam_table;
size_t jam_size;
+ unsigned int num_tx_queues;
};
/* Chip info for each supported switch in the family */
@@ -748,6 +750,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
},
.jam_table = rtl8365mb_init_jam_8365mb_vc,
.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+ .num_tx_queues = 8,
},
{
.name = "RTL8367S",
@@ -761,6 +764,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
},
.jam_table = rtl8365mb_init_jam_8365mb_vc,
.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+ .num_tx_queues = 8,
},
{
.name = "RTL8367SB",
@@ -776,6 +780,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
},
.jam_table = rtl8365mb_init_jam_8365mb_vc,
.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+ .num_tx_queues = 8,
},
{
.name = "RTL8367RB-VB",
@@ -790,6 +795,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
},
.jam_table = rtl8365mb_init_jam_8365mb_vc,
.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+ .num_tx_queues = 8,
},
};
@@ -3288,6 +3294,8 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
goto out_teardown_irq;
}
+ ds->num_tx_queues = mb->chip_info->num_tx_queues;
+
/* Start statistics counter polling */
ret = rtl8365mb_stats_setup(priv);
if (ret) {
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 07/10] net: dsa: realtek: rtl8365mb: add QoS baseline and DCB default prio
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
` (5 preceding siblings ...)
2026-11-12 8:37 ` [PATCH net-next v2 06/10] net: dsa: realtek: rtl8365mb: store the egress queue count per chip Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 08/10] net: dsa: realtek: rtl8365mb: program the PCP of untagged frames Oleksij Rempel
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Add a shared rtl83xx-qos.c layer and the rtl8365mb backend behind it.
The backend programs the QoS classification tables and one
priority-to-queue table at setup instead of leaving them at their reset
state, and offloads the DCB default priority.
The switch classifies traffic by the port default, by PCP and by DSCP.
Each source numbers its values on its own scale. Use the IEEE 802.1Q
traffic types as the meaning of the switch's 3-bit internal priority,
and map every source onto them. Best Effort then outranks Background,
instead of following the raw PCP number.
Keep the driver-independent dcbnl handling and the ieee8021q priority
conversions in rtl83xx-qos.c. That layer programs the chip only through the
realtek_ops qos_* callbacks, so other realtek DSA backends can share one
dcbnl implementation and only program their own chip registers.
The baseline trusts only the port default. PCP and DSCP stay untrusted;
classification by PCP or DSCP starts only once the port trusts it
through dcb apptrust, offloaded by "net: dsa: realtek: rtl8365mb:
offload DCB apptrust".
Every port in use defaults to Best Effort, so all classified traffic
takes one egress queue.
Select NET_IEEE8021Q_HELPERS from NET_DSA_REALTEK, but not DCB. The new
code calls no DCB-core function, and DSA installs the dcbnl ops only
when CONFIG_DCB is set. The QoS baseline is programmed either way.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
Q: Luiz, Linus: should the driver select DCB, as the Microchip KSZ
driver does, or have it enabled by default?
A: No. The QoS baseline is built and programmed without DCB on purpose.
The code calls no DCB-core function, and only the dcbnl hookup in DSA
depends on CONFIG_DCB. Whether DCB is enabled stays a choice of the
kernel configuration.
Q: Linus: can the port queue-number field and the priority-to-queue
table use one index, as in the RTL8370 code?
A: No. Measured on RTL8365MB-VC for every field value, the port field
picks one of eight priority-to-queue tables: value v picks table v - 1,
and 0 picks table 7. One shared index would make the port read another
table than the one programmed. A field value of 1 did not limit the port
to one queue, so the field is not a queue count. The driver programs
table 7 only and points every port in use at it.
Q: Luiz: the rtl8365mb_set_field()/get_field() names hide the regmap
access; use field_prep()/field_get(), or drop the wrappers?
A: They now use field_prep()/field_get() and are renamed
rtl8365mb_qos_regmap_update_field()/_read_field(), so the name shows the
regmap access. They stay as helpers because every QoS field access would
otherwise repeat the regmap call and the field conversion.
Changes in v2:
- move the dcbnl handling, priority conversions and PCP/DSCP baselines
into a shared rtl83xx-qos.c behind realtek_ops callbacks (Linus)
- seed the DSCP-to-priority table here, not in the DSCP patch (sashiko)
- select NET_IEEE8021Q_HELPERS from NET_DSA_REALTEK, which now builds
the shared QoS code
- do not select DCB (Luiz)
- handle only ports 0..7, the ports of the supported chips (new patch
"describe the ports of each chip")
- rename the field wrappers to rtl8365mb_qos_regmap_update/read_field()
and build them on field_prep()/field_get() (Luiz)
- pick the decision-table register with RTL8365MB_QOS_PRIDEC_REG()
instead of a helper (Luiz)
- program the switch-wide QoS baseline before the per-port defaults
(Luiz)
- name the decision sources in an enum and the field widths in defines
- use DSCP_MAX from <net/dscp.h>
- place the top traffic class on queue 7 when the switch uses fewer than
eight queues
- rename RTL8365MB_QOS_WEIGHT_UNTRUSTED to _DISABLED, as both decision
tables use it
- shorten the subject
- program one priority-to-queue table and point every port in use at
it, not a table chosen by the queue count (Linus)
- rename rtl8365mb_dcb_init{,_port}() to rtl83xx_qos_init{,_port}() and
move them to rtl83xx-qos.c, as they also run without CONFIG_DCB
- check at build time that the per-port QoS registers cover all ports
- program the priority-to-queue table in the backend, once at setup
like the decision tables, instead of the suggested
qos_set_port_num_queues()/qos_set_prio_tc() callbacks (Linus): one
table serves every port, so a shared per-port map only added state
- program the per-port priority and queue-map select only on ports in
use
---
drivers/net/dsa/realtek/Kconfig | 1 +
drivers/net/dsa/realtek/Makefile | 5 +-
drivers/net/dsa/realtek/realtek.h | 11 ++
drivers/net/dsa/realtek/rtl8365mb_main.c | 32 +++-
drivers/net/dsa/realtek/rtl8365mb_qos.c | 313 +++++++++++++++++++++++++++++++
drivers/net/dsa/realtek/rtl8365mb_qos.h | 27 +++
drivers/net/dsa/realtek/rtl83xx-qos.c | 109 +++++++++++
drivers/net/dsa/realtek/rtl83xx-qos.h | 15 ++
8 files changed, 509 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/realtek/Kconfig b/drivers/net/dsa/realtek/Kconfig
index d6eb6713e5f6baf0e01f8992fed12951b2db8de6..f1530ba0db61272a4427bbbd611a559e7f0918da 100644
--- a/drivers/net/dsa/realtek/Kconfig
+++ b/drivers/net/dsa/realtek/Kconfig
@@ -4,6 +4,7 @@ menuconfig NET_DSA_REALTEK
depends on NET_DSA
select FIXED_PHY
select IRQ_DOMAIN
+ select NET_IEEE8021Q_HELPERS
select REALTEK_PHY
select REGMAP
help
diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile
index 0a94a619b2c1e405996f1c00ab6bce64b6f9a5eb..600a20911c1d09316d033495d704ba0501c92c18 100644
--- a/drivers/net/dsa/realtek/Makefile
+++ b/drivers/net/dsa/realtek/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o
-realtek_dsa-y := rtl83xx.o
+realtek_dsa-y := rtl83xx.o rtl83xx-qos.o
realtek_dsa-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o
realtek_dsa-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o
@@ -12,4 +12,5 @@ obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o
rtl8365mb-y := rtl8365mb_main.o \
rtl8365mb_table.o \
rtl8365mb_vlan.o \
- rtl8365mb_l2.o
+ rtl8365mb_l2.o \
+ rtl8365mb_qos.o
diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h
index 33f3b4c249fca3e29c8d0862e6cd4a3bc0dee159..9a7906331cbdeef03c59cb4859c32c6fef2020d9 100644
--- a/drivers/net/dsa/realtek/realtek.h
+++ b/drivers/net/dsa/realtek/realtek.h
@@ -159,6 +159,17 @@ struct realtek_ops {
int regnum);
int (*phy_write_c45)(struct realtek_priv *priv, int phy, int devad,
int regnum, u16 val);
+
+ /* QoS/DCB backend callbacks for rtl83xx-qos.c. An iprio is an internal
+ * priority holding an enum ieee8021q_traffic_type value, not an 802.1p
+ * priority. A *_prio getter returns an iprio or a negative errno.
+ */
+ int (*qos_get_port_prio)(struct realtek_priv *priv, int port);
+ int (*qos_set_port_prio)(struct realtek_priv *priv, int port,
+ u8 iprio);
+ int (*qos_set_pcp_prio)(struct realtek_priv *priv, u8 pcp, u8 iprio);
+ int (*qos_set_dscp_prio)(struct realtek_priv *priv, u8 dscp,
+ u8 iprio);
};
struct realtek_variant {
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index f7960d94f1cd9f6a1982849a9e5a1b5d98880b4b..512463be9212bb072380bc26c44b1bef23428917 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -108,6 +108,8 @@
#include "realtek-smi.h"
#include "realtek-mdio.h"
#include "rtl83xx.h"
+#include "rtl83xx-qos.h"
+#include "rtl8365mb_qos.h"
#include "rtl8365mb_l2.h"
#include "rtl8365mb_vlan.h"
@@ -123,6 +125,8 @@
#define RTL8365MB_LEARN_LIMIT_MAX 2112
#define RTL8365MB_MAX_NUM_EXTINTS 3
+static_assert(RTL8365MB_MAX_NUM_PORTS <= RTL8365MB_QOS_MAX_PORTS);
+
/* Chip identification registers */
#define RTL8365MB_CHIP_ID_REG 0x1300
@@ -3183,6 +3187,22 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
}
}
+ /* The QoS baseline below assigns queues from this count */
+ ds->num_tx_queues = mb->chip_info->num_tx_queues;
+
+ /* Set up the QoS baseline before the per-port defaults */
+ ret = rtl83xx_qos_init(ds);
+ if (ret)
+ goto out_teardown_irq;
+
+ ret = rtl8365mb_qos_pridec_init(priv);
+ if (ret)
+ goto out_teardown_irq;
+
+ ret = rtl8365mb_qos_qmap_init(priv);
+ if (ret)
+ goto out_teardown_irq;
+
/* Start with all ports blocked, including unused ports */
dsa_switch_for_each_port(dp, ds) {
struct rtl8365mb_port *p = &mb->ports[dp->index];
@@ -3213,6 +3233,10 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
if (ret)
goto out_teardown_irq;
+ ret = rtl83xx_qos_init_port(ds, dp->index);
+ if (ret)
+ goto out_teardown_irq;
+
/* Set up per-port private data */
p->priv = priv;
p->index = dp->index;
@@ -3294,8 +3318,6 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
goto out_teardown_irq;
}
- ds->num_tx_queues = mb->chip_info->num_tx_queues;
-
/* Start statistics counter polling */
ret = rtl8365mb_stats_setup(priv);
if (ret) {
@@ -3457,6 +3479,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = {
.port_bridge_leave = rtl83xx_port_bridge_leave,
.port_pre_bridge_flags = rtl8365mb_port_pre_bridge_flags,
.port_bridge_flags = rtl83xx_port_bridge_flags,
+ .port_get_default_prio = rtl83xx_port_get_default_prio,
+ .port_set_default_prio = rtl83xx_port_set_default_prio,
.port_stp_state_set = rtl8365mb_port_stp_state_set,
.port_fast_age = rtl83xx_port_fast_age,
.port_fdb_add = rtl83xx_port_fdb_add,
@@ -3499,6 +3523,10 @@ static const struct realtek_ops rtl8365mb_ops = {
.phy_write = rtl8365mb_phy_write,
.phy_read_c45 = rtl8365mb_phy_read_c45,
.phy_write_c45 = rtl8365mb_phy_write_c45,
+ .qos_get_port_prio = rtl8365mb_qos_get_port_prio,
+ .qos_set_port_prio = rtl8365mb_qos_set_port_prio,
+ .qos_set_pcp_prio = rtl8365mb_qos_set_pcp_prio,
+ .qos_set_dscp_prio = rtl8365mb_qos_set_dscp_prio,
};
static const char *const rtl8365mb_supplies[] = {
diff --git a/drivers/net/dsa/realtek/rtl8365mb_qos.c b/drivers/net/dsa/realtek/rtl8365mb_qos.c
new file mode 100644
index 0000000000000000000000000000000000000000..378cd6ed7fe6447cc7508fef90dec4c5332cf3e1
--- /dev/null
+++ b/drivers/net/dsa/realtek/rtl8365mb_qos.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0
+/* QoS and DCB register programming for the rtl8365mb switch family
+ *
+ * The rtl8365mb backend of the shared rtl83xx-qos.c layer. rtl8365mb_setup()
+ * calls its init functions directly; the shared layer reaches the rest through
+ * the realtek_ops qos_* callbacks.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/build_bug.h>
+#include <linux/dcbnl.h>
+#include <linux/regmap.h>
+#include <net/dsa.h>
+#include <net/dscp.h>
+#include <net/ieee8021q.h>
+
+#include "realtek.h"
+#include "rtl8365mb_qos.h"
+
+/* The chip's eight internal priorities carry 802.1Q traffic types, one
+ * internal priority per traffic type.
+ */
+static_assert(RTL8365MB_NUM_INTERNAL_PRIOS == IEEE8021Q_TT_MAX);
+
+#define RTL8365MB_QOS_REG_BITS 16
+
+/* QoS priority and queue selectors are 3-bit fields on 4-bit boundaries,
+ * four per register.
+ */
+#define RTL8365MB_QOS_SEL_BITS 3
+#define RTL8365MB_QOS_SEL_STRIDE 4
+#define RTL8365MB_QOS_SELS_PER_REG 4
+#define RTL8365MB_QOS_SEL_OFFSET(_i) \
+ (((_i) % RTL8365MB_QOS_SELS_PER_REG) * RTL8365MB_QOS_SEL_STRIDE)
+
+static_assert(RTL8365MB_QOS_SEL_BITS <= RTL8365MB_QOS_SEL_STRIDE);
+static_assert(RTL8365MB_QOS_SEL_STRIDE * RTL8365MB_QOS_SELS_PER_REG <=
+ RTL8365MB_QOS_REG_BITS);
+
+/* qmaps: the queue id of each internal priority, one table per qmap */
+#define RTL8365MB_QOS_QMAP_TBL_REGS \
+ (RTL8365MB_NUM_INTERNAL_PRIOS / RTL8365MB_QOS_SELS_PER_REG)
+#define RTL8365MB_QOS_QMAP_TBL_REG(_m, _iprio) \
+ (0x0904 + (_m) * RTL8365MB_QOS_QMAP_TBL_REGS + \
+ (_iprio) / RTL8365MB_QOS_SELS_PER_REG)
+
+/* The qmap each port reads, one select per port */
+#define RTL8365MB_QOS_PORT_QMAP_SEL_REG(_p) \
+ (0x0900 + (_p) / RTL8365MB_QOS_SELS_PER_REG)
+
+static_assert(BIT(RTL8365MB_QOS_SEL_BITS) == RTL8365MB_QOS_NUM_QMAPS);
+/* Ports 0..7 fit in 0x0900..0x0901; 0x0902 does not read back */
+static_assert(RTL8365MB_QOS_PORT_QMAP_SEL_REG(RTL8365MB_QOS_MAX_PORTS - 1) <=
+ 0x0901);
+
+/* qmap 7 is the table that select value 0 picks */
+#define RTL8365MB_QOS_QMAP_DEFAULT 7
+
+static_assert(RTL8365MB_QOS_QMAP_DEFAULT < RTL8365MB_QOS_NUM_QMAPS);
+
+/* 802.1p (PCP) -> internal priority remap */
+#define RTL8365MB_QOS_1Q_REMAP_REG(_pcp) \
+ (0x0865 + (_pcp) / RTL8365MB_QOS_SELS_PER_REG)
+
+/* Port-based (default) priority, one select per port */
+#define RTL8365MB_QOS_PORT_PRI_REG(_p) \
+ (0x0877 + (_p) / RTL8365MB_QOS_SELS_PER_REG)
+
+/* Ports 0..7 fit in 0x0877..0x0878; the next one is at 0x087a */
+static_assert(RTL8365MB_QOS_PORT_PRI_REG(RTL8365MB_QOS_MAX_PORTS - 1) <=
+ 0x0878);
+
+/* DSCP -> internal priority, one table for the whole chip */
+#define RTL8365MB_QOS_DSCP_PRI_REG(_d) \
+ (0x0867 + (_d) / RTL8365MB_QOS_SELS_PER_REG)
+
+static_assert(RTL8365MB_QOS_1Q_REMAP_REG(IEEE_8021Q_MAX_PRIORITIES - 1) <
+ RTL8365MB_QOS_DSCP_PRI_REG(0));
+static_assert(RTL8365MB_QOS_DSCP_PRI_REG(DSCP_MAX - 1) <
+ RTL8365MB_QOS_PORT_PRI_REG(0));
+
+/* Priority-decision weight tables. In the driver's model of the decision
+ * hardware, a source ranks by its highest set weight bit (a one-hot rank), not
+ * by numeric magnitude. A weight of zero disables the source.
+ */
+#define RTL8365MB_QOS_WEIGHT_BITS 8
+#define RTL8365MB_QOS_WEIGHTS_PER_REG 2
+#define RTL8365MB_QOS_PRIDEC_TBL0_BASE 0x087b
+#define RTL8365MB_QOS_PRIDEC_TBL1_BASE 0x0885
+#define RTL8365MB_QOS_PRIDEC_REG(_t, _s) \
+ (((_t) == RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED ? \
+ RTL8365MB_QOS_PRIDEC_TBL1_BASE : RTL8365MB_QOS_PRIDEC_TBL0_BASE) + \
+ (_s) / RTL8365MB_QOS_WEIGHTS_PER_REG)
+#define RTL8365MB_QOS_PRIDEC_OFFSET(_s) \
+ (((_s) % RTL8365MB_QOS_WEIGHTS_PER_REG) * RTL8365MB_QOS_WEIGHT_BITS)
+
+static_assert(RTL8365MB_QOS_WEIGHT_BITS * RTL8365MB_QOS_WEIGHTS_PER_REG <=
+ RTL8365MB_QOS_REG_BITS);
+
+/* In the driver's model, each port selects one of the two decision tables;
+ * one bit per port.
+ */
+#define RTL8365MB_QOS_PRIDEC_TBL_SEL_REG 0x0889
+#define RTL8365MB_QOS_PRIDEC_TABLE_UNTRUSTED 0
+#define RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED 1
+
+/* Priority-decision sources, in the hardware's numbering */
+enum rtl8365mb_qos_pridec_src {
+ RTL8365MB_QOS_PRIDEC_PORT = 0,
+ RTL8365MB_QOS_PRIDEC_ACL = 1,
+ RTL8365MB_QOS_PRIDEC_DSCP = 2,
+ RTL8365MB_QOS_PRIDEC_1Q = 3,
+ RTL8365MB_QOS_PRIDEC_SVLAN = 4,
+ RTL8365MB_QOS_PRIDEC_CVLAN = 5,
+ RTL8365MB_QOS_PRIDEC_DA = 6,
+ RTL8365MB_QOS_PRIDEC_SA = 7,
+ RTL8365MB_QOS_PRIDEC_NUM_SRC,
+};
+
+static_assert(RTL8365MB_QOS_PRIDEC_REG(RTL8365MB_QOS_PRIDEC_TABLE_UNTRUSTED,
+ RTL8365MB_QOS_PRIDEC_NUM_SRC - 1) <
+ RTL8365MB_QOS_PRIDEC_TBL1_BASE);
+static_assert(RTL8365MB_QOS_PRIDEC_REG(RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED,
+ RTL8365MB_QOS_PRIDEC_NUM_SRC - 1) <
+ RTL8365MB_QOS_PRIDEC_TBL_SEL_REG);
+
+/* Priority-decision weights. The baseline trusts only the port-based priority;
+ * every other source is disabled (weight 0). The port default holds the lowest
+ * one-hot rank, BIT(0).
+ */
+#define RTL8365MB_QOS_WEIGHT_DISABLED 0
+#define RTL8365MB_QOS_WEIGHT_PORT BIT(0)
+
+static u32 rtl8365mb_qos_sel_mask(unsigned int idx)
+{
+ unsigned int off = RTL8365MB_QOS_SEL_OFFSET(idx);
+
+ return GENMASK(off + RTL8365MB_QOS_SEL_BITS - 1, off);
+}
+
+/* Mask of the weight of priority-decision source @src in its register */
+static u32 rtl8365mb_qos_weight_mask(unsigned int src)
+{
+ unsigned int off = RTL8365MB_QOS_PRIDEC_OFFSET(src);
+
+ return GENMASK(off + RTL8365MB_QOS_WEIGHT_BITS - 1, off);
+}
+
+static int rtl8365mb_qos_regmap_update_field(struct realtek_priv *priv,
+ u32 reg, u32 mask, u32 val)
+{
+ return regmap_update_bits(priv->map, reg, mask, field_prep(mask, val));
+}
+
+/* Return the field value, or a negative errno if the register read fails */
+static int rtl8365mb_qos_regmap_read_field(struct realtek_priv *priv,
+ u32 reg, u32 mask)
+{
+ u32 data;
+ int ret;
+
+ ret = regmap_read(priv->map, reg, &data);
+ if (ret)
+ return ret;
+
+ return field_get(mask, data);
+}
+
+/* Unused ports carry no traffic; no per-port QoS request is made for them */
+static int rtl8365mb_qos_check_port(struct realtek_priv *priv, int port)
+{
+ if (WARN_ON_ONCE(dsa_is_unused_port(&priv->ds, port)))
+ return -EINVAL;
+
+ return 0;
+}
+
+int rtl8365mb_qos_get_port_prio(struct realtek_priv *priv, int port)
+{
+ return rtl8365mb_qos_regmap_read_field(priv,
+ RTL8365MB_QOS_PORT_PRI_REG(port),
+ rtl8365mb_qos_sel_mask(port));
+}
+
+int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio)
+{
+ int ret;
+
+ ret = rtl8365mb_qos_check_port(priv, port);
+ if (ret)
+ return ret;
+
+ return rtl8365mb_qos_regmap_update_field(priv,
+ RTL8365MB_QOS_PORT_PRI_REG(port),
+ rtl8365mb_qos_sel_mask(port),
+ iprio);
+}
+
+int rtl8365mb_qos_set_pcp_prio(struct realtek_priv *priv, u8 pcp, u8 iprio)
+{
+ return rtl8365mb_qos_regmap_update_field(priv,
+ RTL8365MB_QOS_1Q_REMAP_REG(pcp),
+ rtl8365mb_qos_sel_mask(pcp),
+ iprio);
+}
+
+int rtl8365mb_qos_set_dscp_prio(struct realtek_priv *priv, u8 dscp, u8 iprio)
+{
+ return rtl8365mb_qos_regmap_update_field(priv,
+ RTL8365MB_QOS_DSCP_PRI_REG(dscp),
+ rtl8365mb_qos_sel_mask(dscp),
+ iprio);
+}
+
+static int rtl8365mb_qos_set_port_qmap(struct realtek_priv *priv, int port,
+ int qmap)
+{
+ /* Field value v selects qmap v - 1; 0 selects qmap 7 */
+ u32 val = (qmap + 1) % RTL8365MB_QOS_NUM_QMAPS;
+
+ return rtl8365mb_qos_regmap_update_field(priv,
+ RTL8365MB_QOS_PORT_QMAP_SEL_REG(port),
+ rtl8365mb_qos_sel_mask(port),
+ val);
+}
+
+static int rtl8365mb_qos_set_qmap_qid(struct realtek_priv *priv, int qmap,
+ u8 iprio, u8 qid)
+{
+ return rtl8365mb_qos_regmap_update_field(priv,
+ RTL8365MB_QOS_QMAP_TBL_REG(qmap, iprio),
+ rtl8365mb_qos_sel_mask(iprio),
+ qid);
+}
+
+/* At reset, qmap 0 uses only queue 0, and qmap k (1..6) uses queues 0..k-1 and
+ * queue 7. Follow that layout: with more than one queue in use, map the top
+ * traffic class to queue 7.
+ */
+#define RTL8365MB_QOS_QID_TOP 7
+
+static u8 rtl8365mb_qos_tc_to_qid(unsigned int num_queues, u8 tc)
+{
+ if (num_queues > 1 && tc == num_queues - 1)
+ return RTL8365MB_QOS_QID_TOP;
+
+ return tc;
+}
+
+int rtl8365mb_qos_qmap_init(struct realtek_priv *priv)
+{
+ unsigned int nq = priv->ds.num_tx_queues;
+ struct dsa_port *dp;
+ int iprio, ret;
+
+ for (iprio = 0; iprio < RTL8365MB_NUM_INTERNAL_PRIOS; iprio++) {
+ int tc = ieee8021q_tt_to_tc(iprio, nq);
+
+ if (tc < 0)
+ return tc;
+
+ ret = rtl8365mb_qos_set_qmap_qid(priv, RTL8365MB_QOS_QMAP_DEFAULT,
+ iprio,
+ rtl8365mb_qos_tc_to_qid(nq, tc));
+ if (ret)
+ return ret;
+ }
+
+ /* Unused ports carry no traffic; their select is left alone */
+ dsa_switch_for_each_available_port(dp, &priv->ds) {
+ ret = rtl8365mb_qos_set_port_qmap(priv, dp->index,
+ RTL8365MB_QOS_QMAP_DEFAULT);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtl8365mb_qos_set_pridec(struct realtek_priv *priv, int table,
+ int src, u8 weight)
+{
+ return rtl8365mb_qos_regmap_update_field(priv,
+ RTL8365MB_QOS_PRIDEC_REG(table, src),
+ rtl8365mb_qos_weight_mask(src),
+ weight);
+}
+
+/* Program every source in both tables rather than relying on the reset state */
+int rtl8365mb_qos_pridec_init(struct realtek_priv *priv)
+{
+ int table, ret;
+
+ for (table = RTL8365MB_QOS_PRIDEC_TABLE_UNTRUSTED;
+ table <= RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED; table++) {
+ int src;
+
+ for (src = 0; src < RTL8365MB_QOS_PRIDEC_NUM_SRC; src++) {
+ u8 weight = src == RTL8365MB_QOS_PRIDEC_PORT ?
+ RTL8365MB_QOS_WEIGHT_PORT :
+ RTL8365MB_QOS_WEIGHT_DISABLED;
+
+ ret = rtl8365mb_qos_set_pridec(priv, table, src, weight);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* In the driver's model, a clear bit selects the untrusted table */
+ return regmap_write(priv->map, RTL8365MB_QOS_PRIDEC_TBL_SEL_REG, 0);
+}
diff --git a/drivers/net/dsa/realtek/rtl8365mb_qos.h b/drivers/net/dsa/realtek/rtl8365mb_qos.h
new file mode 100644
index 0000000000000000000000000000000000000000..a3568d47f6c6ee9566074af0ec493ff4bbdbf44b
--- /dev/null
+++ b/drivers/net/dsa/realtek/rtl8365mb_qos.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* QoS and DCB register programming for the rtl8365mb switch family */
+
+#ifndef _REALTEK_RTL8365MB_QOS_H
+#define _REALTEK_RTL8365MB_QOS_H
+
+#include <linux/types.h>
+
+struct realtek_priv;
+
+/* The switch has eight internal priorities (0..7) */
+#define RTL8365MB_NUM_INTERNAL_PRIOS 8
+
+/* Ports the QoS code handles */
+#define RTL8365MB_QOS_MAX_PORTS 8
+
+/* Internal priority -> queue id tables (qmaps) */
+#define RTL8365MB_QOS_NUM_QMAPS 8
+
+int rtl8365mb_qos_pridec_init(struct realtek_priv *priv);
+int rtl8365mb_qos_qmap_init(struct realtek_priv *priv);
+int rtl8365mb_qos_get_port_prio(struct realtek_priv *priv, int port);
+int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio);
+int rtl8365mb_qos_set_pcp_prio(struct realtek_priv *priv, u8 pcp, u8 iprio);
+int rtl8365mb_qos_set_dscp_prio(struct realtek_priv *priv, u8 dscp, u8 iprio);
+
+#endif /* _REALTEK_RTL8365MB_QOS_H */
diff --git a/drivers/net/dsa/realtek/rtl83xx-qos.c b/drivers/net/dsa/realtek/rtl83xx-qos.c
new file mode 100644
index 0000000000000000000000000000000000000000..0a02e6c4538a919e30e3c35594c7e7e7cdc3cf81
--- /dev/null
+++ b/drivers/net/dsa/realtek/rtl83xx-qos.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Shared QoS and DCB logic for the Realtek DSA switch family
+ *
+ * Every classifier (port default, PCP, DSCP) yields an internal priority that
+ * is an 802.1Q traffic type. dcbnl uses 802.1p priorities. An 802.1p priority
+ * equals the PCP value in the default PCP decoding (IEEE 802.1Q-2022 6.9.3).
+ * This layer converts at its edges: every priority it hands a backend is an
+ * internal priority.
+ *
+ * The backend maps internal priorities to its egress queues itself.
+ */
+
+#include <linux/dcbnl.h>
+#include <linux/errno.h>
+#include <net/dscp.h>
+#include <net/dsa.h>
+#include <net/ieee8021q.h>
+
+#include "realtek.h"
+#include "rtl83xx-qos.h"
+
+/* Seed the PCP and DSCP tables, although neither source is trusted yet */
+int rtl83xx_qos_init(struct dsa_switch *ds)
+{
+ struct realtek_priv *priv = ds->priv;
+ int i, ret;
+
+ if (!priv->ops->qos_set_pcp_prio || !priv->ops->qos_set_dscp_prio)
+ return -EOPNOTSUPP;
+
+ /* Map ingress PCP to the internal priority via its 802.1Q traffic type,
+ * so Best Effort (PCP 0) outranks Background (PCP 1).
+ */
+ for (i = 0; i < IEEE_8021Q_MAX_PRIORITIES; i++) {
+ int tt = ieee8021q_pcp_to_tt(i);
+
+ if (tt < 0)
+ return tt;
+
+ ret = priv->ops->qos_set_pcp_prio(priv, i, tt);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < DSCP_MAX; i++) {
+ int tt = ietf_dscp_to_ieee8021q_tt(i);
+
+ if (tt < 0)
+ return tt;
+
+ ret = priv->ops->qos_set_dscp_prio(priv, i, tt);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_qos_init, "REALTEK_DSA");
+
+int rtl83xx_qos_init_port(struct dsa_switch *ds, int port)
+{
+ struct realtek_priv *priv = ds->priv;
+
+ if (!priv->ops->qos_set_port_prio)
+ return -EOPNOTSUPP;
+
+ /* An unused port's priority is left alone */
+ if (dsa_is_unused_port(ds, port))
+ return 0;
+
+ /* Best Effort, not internal priority 0, which is Background */
+ return priv->ops->qos_set_port_prio(priv, port, IEEE8021Q_TT_BE);
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_qos_init_port, "REALTEK_DSA");
+
+int rtl83xx_port_get_default_prio(struct dsa_switch *ds, int port)
+{
+ struct realtek_priv *priv = ds->priv;
+ int tt;
+
+ if (!priv->ops->qos_get_port_prio)
+ return -EOPNOTSUPP;
+
+ tt = priv->ops->qos_get_port_prio(priv, port);
+ if (tt < 0)
+ return tt;
+
+ return ieee8021q_tt_to_pcp(tt);
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_port_get_default_prio, "REALTEK_DSA");
+
+int rtl83xx_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
+{
+ struct realtek_priv *priv = ds->priv;
+ int tt;
+
+ if (!priv->ops->qos_set_port_prio)
+ return -EOPNOTSUPP;
+
+ if (prio >= IEEE_8021Q_MAX_PRIORITIES)
+ return -ERANGE;
+
+ tt = ieee8021q_pcp_to_tt(prio);
+ if (tt < 0)
+ return tt;
+
+ return priv->ops->qos_set_port_prio(priv, port, tt);
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_port_set_default_prio, "REALTEK_DSA");
diff --git a/drivers/net/dsa/realtek/rtl83xx-qos.h b/drivers/net/dsa/realtek/rtl83xx-qos.h
new file mode 100644
index 0000000000000000000000000000000000000000..82ad9338d78db9062d8ceed06378b31e467ee04d
--- /dev/null
+++ b/drivers/net/dsa/realtek/rtl83xx-qos.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Shared QoS and DCB logic for the Realtek DSA switch family */
+
+#ifndef _RTL83XX_QOS_H
+#define _RTL83XX_QOS_H
+
+#include <linux/types.h>
+#include <net/dsa.h>
+
+int rtl83xx_qos_init(struct dsa_switch *ds);
+int rtl83xx_qos_init_port(struct dsa_switch *ds, int port);
+int rtl83xx_port_get_default_prio(struct dsa_switch *ds, int port);
+int rtl83xx_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio);
+
+#endif /* _RTL83XX_QOS_H */
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 08/10] net: dsa: realtek: rtl8365mb: program the PCP of untagged frames
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
` (6 preceding siblings ...)
2026-11-12 8:37 ` [PATCH net-next v2 07/10] net: dsa: realtek: rtl8365mb: add QoS baseline and DCB default prio Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 09/10] net: dsa: realtek: rtl8365mb: offload DCB apptrust Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 10/10] net: dsa: realtek: rtl8365mb: report the DCB DSCP-to-priority map Oleksij Rempel
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Program the PCP that the switch gives to untagged frames whenever the
port default priority is set.
With PCP trust, the switch classifies an untagged frame by this value.
The value is 0 after reset, so untagged frames would be Best Effort
whatever the port default priority is. PCP trust is added by "net: dsa:
realtek: rtl8365mb: offload DCB apptrust".
The switch also puts this value into the tag it adds on egress. So far,
an untagged frame that left a VLAN-tagged port carried PCP 0. With a
default priority set, it now carries that priority as its PCP. The
standards require this:
IEEE 802.1AC-2016, 13.1: "The priority parameter of the
M_UNITDATA.indication shall take the value of the Default Priority
parameter for the SAP on which the MA_DATA.indication was received."
IEEE 802.1Q-2022, 6.9.4: "For untagged frames, the priority parameter
signaled in the corresponding M_UNITDATA.indication is taken to be the
received priority."
IEEE 802.1Q-2022, 6.9.3: "The priority and drop_eligible parameters
are encoded in the PCP field of the VLAN tag using the Priority Code
Point Encoding Table for the Port". With the default priority
regeneration (Table 6-4) and the 8P0D encoding (Table 6-2), the PCP
equals the Default Priority.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
Changes in v2:
- New in v2. PCP trust needs it, or untagged frames would be Best
Effort.
---
drivers/net/dsa/realtek/realtek.h | 6 +++++-
drivers/net/dsa/realtek/rtl8365mb_qos.c | 18 +++++++++++++++++-
drivers/net/dsa/realtek/rtl8365mb_qos.h | 3 ++-
drivers/net/dsa/realtek/rtl83xx-qos.c | 14 ++++++++++----
4 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h
index 9a7906331cbdeef03c59cb4859c32c6fef2020d9..a9bdadbea3cbe19eeef0f33762318f022881e75f 100644
--- a/drivers/net/dsa/realtek/realtek.h
+++ b/drivers/net/dsa/realtek/realtek.h
@@ -165,8 +165,12 @@ struct realtek_ops {
* priority. A *_prio getter returns an iprio or a negative errno.
*/
int (*qos_get_port_prio)(struct realtek_priv *priv, int port);
+ /* Set the priority of @port to @iprio, and make @untag_pcp the PCP
+ * given to untagged frames on @port. @untag_pcp is an 802.1p
+ * priority, not an iprio.
+ */
int (*qos_set_port_prio)(struct realtek_priv *priv, int port,
- u8 iprio);
+ u8 iprio, u8 untag_pcp);
int (*qos_set_pcp_prio)(struct realtek_priv *priv, u8 pcp, u8 iprio);
int (*qos_set_dscp_prio)(struct realtek_priv *priv, u8 dscp,
u8 iprio);
diff --git a/drivers/net/dsa/realtek/rtl8365mb_qos.c b/drivers/net/dsa/realtek/rtl8365mb_qos.c
index 378cd6ed7fe6447cc7508fef90dec4c5332cf3e1..2a849d0e4898a712d8f4babd5cdd03fe8e207814 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_qos.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_qos.c
@@ -64,6 +64,14 @@ static_assert(RTL8365MB_QOS_QMAP_DEFAULT < RTL8365MB_QOS_NUM_QMAPS);
#define RTL8365MB_QOS_1Q_REMAP_REG(_pcp) \
(0x0865 + (_pcp) / RTL8365MB_QOS_SELS_PER_REG)
+/* PCP given to untagged frames, one 3-bit field per port */
+#define RTL8365MB_QOS_PORT_UNTAG_PCP_REG(_p) \
+ (0x0851 + (_p) / RTL8365MB_QOS_SELS_PER_REG)
+
+/* Ports 0..7 fit in 0x0851..0x0852 */
+static_assert(RTL8365MB_QOS_PORT_UNTAG_PCP_REG(RTL8365MB_QOS_MAX_PORTS - 1) <=
+ 0x0852);
+
/* Port-based (default) priority, one select per port */
#define RTL8365MB_QOS_PORT_PRI_REG(_p) \
(0x0877 + (_p) / RTL8365MB_QOS_SELS_PER_REG)
@@ -184,7 +192,8 @@ int rtl8365mb_qos_get_port_prio(struct realtek_priv *priv, int port)
rtl8365mb_qos_sel_mask(port));
}
-int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio)
+int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio,
+ u8 untag_pcp)
{
int ret;
@@ -192,6 +201,13 @@ int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio)
if (ret)
return ret;
+ ret = rtl8365mb_qos_regmap_update_field(priv,
+ RTL8365MB_QOS_PORT_UNTAG_PCP_REG(port),
+ rtl8365mb_qos_sel_mask(port),
+ untag_pcp);
+ if (ret)
+ return ret;
+
return rtl8365mb_qos_regmap_update_field(priv,
RTL8365MB_QOS_PORT_PRI_REG(port),
rtl8365mb_qos_sel_mask(port),
diff --git a/drivers/net/dsa/realtek/rtl8365mb_qos.h b/drivers/net/dsa/realtek/rtl8365mb_qos.h
index a3568d47f6c6ee9566074af0ec493ff4bbdbf44b..71949ece4af8fae5c49cf105ddaa3db9bc622cf2 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_qos.h
+++ b/drivers/net/dsa/realtek/rtl8365mb_qos.h
@@ -20,7 +20,8 @@ struct realtek_priv;
int rtl8365mb_qos_pridec_init(struct realtek_priv *priv);
int rtl8365mb_qos_qmap_init(struct realtek_priv *priv);
int rtl8365mb_qos_get_port_prio(struct realtek_priv *priv, int port);
-int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio);
+int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio,
+ u8 untag_pcp);
int rtl8365mb_qos_set_pcp_prio(struct realtek_priv *priv, u8 pcp, u8 iprio);
int rtl8365mb_qos_set_dscp_prio(struct realtek_priv *priv, u8 dscp, u8 iprio);
diff --git a/drivers/net/dsa/realtek/rtl83xx-qos.c b/drivers/net/dsa/realtek/rtl83xx-qos.c
index 0a02e6c4538a919e30e3c35594c7e7e7cdc3cf81..08091586901291e45ad1a2c64730cdcc30298b7a 100644
--- a/drivers/net/dsa/realtek/rtl83xx-qos.c
+++ b/drivers/net/dsa/realtek/rtl83xx-qos.c
@@ -5,7 +5,7 @@
* is an 802.1Q traffic type. dcbnl uses 802.1p priorities. An 802.1p priority
* equals the PCP value in the default PCP decoding (IEEE 802.1Q-2022 6.9.3).
* This layer converts at its edges: every priority it hands a backend is an
- * internal priority.
+ * internal priority, except the 802.1p PCP given to untagged frames.
*
* The backend maps internal priorities to its egress queues itself.
*/
@@ -60,6 +60,7 @@ EXPORT_SYMBOL_NS_GPL(rtl83xx_qos_init, "REALTEK_DSA");
int rtl83xx_qos_init_port(struct dsa_switch *ds, int port)
{
struct realtek_priv *priv = ds->priv;
+ int untag_pcp;
if (!priv->ops->qos_set_port_prio)
return -EOPNOTSUPP;
@@ -68,8 +69,13 @@ int rtl83xx_qos_init_port(struct dsa_switch *ds, int port)
if (dsa_is_unused_port(ds, port))
return 0;
- /* Best Effort, not internal priority 0, which is Background */
- return priv->ops->qos_set_port_prio(priv, port, IEEE8021Q_TT_BE);
+ untag_pcp = ieee8021q_tt_to_pcp(IEEE8021Q_TT_BE);
+ if (untag_pcp < 0)
+ return untag_pcp;
+
+ /* iprio is Best Effort, not 0, which is Background */
+ return priv->ops->qos_set_port_prio(priv, port, IEEE8021Q_TT_BE,
+ untag_pcp);
}
EXPORT_SYMBOL_NS_GPL(rtl83xx_qos_init_port, "REALTEK_DSA");
@@ -104,6 +110,6 @@ int rtl83xx_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
if (tt < 0)
return tt;
- return priv->ops->qos_set_port_prio(priv, port, tt);
+ return priv->ops->qos_set_port_prio(priv, port, tt, prio);
}
EXPORT_SYMBOL_NS_GPL(rtl83xx_port_set_default_prio, "REALTEK_DSA");
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 09/10] net: dsa: realtek: rtl8365mb: offload DCB apptrust
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
` (7 preceding siblings ...)
2026-11-12 8:37 ` [PATCH net-next v2 08/10] net: dsa: realtek: rtl8365mb: program the PCP of untagged frames Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 10/10] net: dsa: realtek: rtl8365mb: report the DCB DSCP-to-priority map Oleksij Rempel
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Wire up port_get_apptrust and port_set_apptrust. The admin can then
choose through dcbnl apptrust which ingress classification a port trusts
for its priority: 802.1p (PCP), DSCP or both. A port that trusts nothing
keeps the port default priority.
In the driver's model of the hardware, the trusted priority-decision
table is switch-wide, so all ports that trust something must trust the
same selectors. While another port trusts a set of selectors, a request
to trust a different set is refused with -EBUSY.
When both are trusted, DSCP must come first. When PCP is trusted, the
switch classifies an untagged frame by the PCP given to untagged
frames. A PCP source ranked above DSCP would then hide DSCP from
untagged IP frames. Refuse the order pcp dscp and any unknown selector
with -EOPNOTSUPP.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
Changes in v2:
- move selector and precedence validation into rtl83xx-qos.c behind new
qos_get/set_apptrust callbacks; weights and -EBUSY stay here (Linus)
- use one-hot decision weights; in the driver's model, the v1 linear
weights could tie PCP with DSCP (Linus)
- define the apptrust slot order once (enum rtl83xx_apptrust_slot) for
the shared selector table and the backend source table
- write the PCP and DSCP weights with one register update, so a failed
write cannot leave the trusted table half programmed (sashiko)
- check that all apptrust sources share the one register that is updated
- rank DSCP above PCP and refuse pcp dscp with -EOPNOTSUPP, because
with PCP first, untagged IP frames never reach DSCP
---
drivers/net/dsa/realtek/realtek.h | 16 +++
drivers/net/dsa/realtek/rtl8365mb_main.c | 4 +
drivers/net/dsa/realtek/rtl8365mb_qos.c | 171 +++++++++++++++++++++++++++++++
drivers/net/dsa/realtek/rtl8365mb_qos.h | 4 +
drivers/net/dsa/realtek/rtl83xx-qos.c | 75 ++++++++++++++
drivers/net/dsa/realtek/rtl83xx-qos.h | 4 +
6 files changed, 274 insertions(+)
diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h
index a9bdadbea3cbe19eeef0f33762318f022881e75f..0008ba9681cccb521d85d9bfc97425ad1b777aa6 100644
--- a/drivers/net/dsa/realtek/realtek.h
+++ b/drivers/net/dsa/realtek/realtek.h
@@ -100,6 +100,15 @@ struct realtek_priv {
void *chip_data; /* Per-chip extra variant data */
};
+/* apptrust selector slots: the index into the trust[] array of the
+ * qos_get/set_apptrust callbacks, in precedence order (highest first)
+ */
+enum rtl83xx_apptrust_slot {
+ RTL83XX_APPTRUST_DSCP,
+ RTL83XX_APPTRUST_PCP,
+ RTL83XX_APPTRUST_COUNT,
+};
+
/*
* struct realtek_ops - vtable for the per-SMI-chiptype operations
* @detect: detects the chiptype
@@ -174,6 +183,13 @@ struct realtek_ops {
int (*qos_set_pcp_prio)(struct realtek_priv *priv, u8 pcp, u8 iprio);
int (*qos_set_dscp_prio)(struct realtek_priv *priv, u8 dscp,
u8 iprio);
+ /* @trust is indexed by enum rtl83xx_apptrust_slot and has @n entries.
+ * The trust scope (per port or switch-wide) differs between chips.
+ */
+ int (*qos_set_apptrust)(struct realtek_priv *priv, int port,
+ const bool *trust, unsigned int n);
+ int (*qos_get_apptrust)(struct realtek_priv *priv, int port,
+ bool *trust, unsigned int n);
};
struct realtek_variant {
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index 512463be9212bb072380bc26c44b1bef23428917..d16e7c99217bdc01d1bdf7dbbcb8e7ed1441e6a8 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -3481,6 +3481,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = {
.port_bridge_flags = rtl83xx_port_bridge_flags,
.port_get_default_prio = rtl83xx_port_get_default_prio,
.port_set_default_prio = rtl83xx_port_set_default_prio,
+ .port_get_apptrust = rtl83xx_port_get_apptrust,
+ .port_set_apptrust = rtl83xx_port_set_apptrust,
.port_stp_state_set = rtl8365mb_port_stp_state_set,
.port_fast_age = rtl83xx_port_fast_age,
.port_fdb_add = rtl83xx_port_fdb_add,
@@ -3527,6 +3529,8 @@ static const struct realtek_ops rtl8365mb_ops = {
.qos_set_port_prio = rtl8365mb_qos_set_port_prio,
.qos_set_pcp_prio = rtl8365mb_qos_set_pcp_prio,
.qos_set_dscp_prio = rtl8365mb_qos_set_dscp_prio,
+ .qos_set_apptrust = rtl8365mb_qos_set_apptrust,
+ .qos_get_apptrust = rtl8365mb_qos_get_apptrust,
};
static const char *const rtl8365mb_supplies[] = {
diff --git a/drivers/net/dsa/realtek/rtl8365mb_qos.c b/drivers/net/dsa/realtek/rtl8365mb_qos.c
index 2a849d0e4898a712d8f4babd5cdd03fe8e207814..c465d639ee5e527a604498fb064d6757aac1fda4 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_qos.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_qos.c
@@ -12,6 +12,7 @@
#include <linux/build_bug.h>
#include <linux/dcbnl.h>
#include <linux/regmap.h>
+#include <linux/string.h>
#include <net/dsa.h>
#include <net/dscp.h>
#include <net/ieee8021q.h>
@@ -114,6 +115,8 @@ static_assert(RTL8365MB_QOS_WEIGHT_BITS * RTL8365MB_QOS_WEIGHTS_PER_REG <=
#define RTL8365MB_QOS_PRIDEC_TABLE_UNTRUSTED 0
#define RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED 1
+static_assert(RTL8365MB_QOS_MAX_PORTS <= RTL8365MB_QOS_REG_BITS);
+
/* Priority-decision sources, in the hardware's numbering */
enum rtl8365mb_qos_pridec_src {
RTL8365MB_QOS_PRIDEC_PORT = 0,
@@ -141,6 +144,36 @@ static_assert(RTL8365MB_QOS_PRIDEC_REG(RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED,
#define RTL8365MB_QOS_WEIGHT_DISABLED 0
#define RTL8365MB_QOS_WEIGHT_PORT BIT(0)
+/* Priority-decision source of each apptrust slot */
+static const u8 rtl8365mb_qos_apptrust_src[] = {
+ [RTL83XX_APPTRUST_DSCP] = RTL8365MB_QOS_PRIDEC_DSCP,
+ [RTL83XX_APPTRUST_PCP] = RTL8365MB_QOS_PRIDEC_1Q,
+};
+
+/* The 1Q source also classifies untagged frames, by the PCP given to untagged
+ * frames. Ranked above DSCP, it would hide DSCP from untagged IP frames.
+ */
+static_assert(RTL83XX_APPTRUST_DSCP < RTL83XX_APPTRUST_PCP);
+
+static_assert(ARRAY_SIZE(rtl8365mb_qos_apptrust_src) ==
+ RTL83XX_APPTRUST_COUNT);
+
+/* Register of source @_s in the trusted decision table */
+#define RTL8365MB_QOS_APPTRUST_REG(_s) \
+ RTL8365MB_QOS_PRIDEC_REG(RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED, _s)
+
+/* The top trusted rank must fit in the weight field */
+static_assert((RTL8365MB_QOS_WEIGHT_PORT <<
+ ARRAY_SIZE(rtl8365mb_qos_apptrust_src)) <
+ BIT(RTL8365MB_QOS_WEIGHT_BITS));
+
+/* Each trusted slot outranks the port default and every later slot */
+static u8 rtl8365mb_qos_apptrust_weight(unsigned int slot)
+{
+ return RTL8365MB_QOS_WEIGHT_PORT <<
+ (ARRAY_SIZE(rtl8365mb_qos_apptrust_src) - slot);
+}
+
static u32 rtl8365mb_qos_sel_mask(unsigned int idx)
{
unsigned int off = RTL8365MB_QOS_SEL_OFFSET(idx);
@@ -304,6 +337,14 @@ static int rtl8365mb_qos_set_pridec(struct realtek_priv *priv, int table,
weight);
}
+static int rtl8365mb_qos_get_pridec(struct realtek_priv *priv, int table,
+ int src)
+{
+ return rtl8365mb_qos_regmap_read_field(priv,
+ RTL8365MB_QOS_PRIDEC_REG(table, src),
+ rtl8365mb_qos_weight_mask(src));
+}
+
/* Program every source in both tables rather than relying on the reset state */
int rtl8365mb_qos_pridec_init(struct realtek_priv *priv)
{
@@ -327,3 +368,133 @@ int rtl8365mb_qos_pridec_init(struct realtek_priv *priv)
/* In the driver's model, a clear bit selects the untrusted table */
return regmap_write(priv->map, RTL8365MB_QOS_PRIDEC_TBL_SEL_REG, 0);
}
+
+/* Read the trust state of each apptrust slot from the trusted table */
+static int rtl8365mb_qos_apptrust_read(struct realtek_priv *priv, bool *trust)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rtl8365mb_qos_apptrust_src); i++) {
+ int weight;
+
+ weight = rtl8365mb_qos_get_pridec(priv,
+ RTL8365MB_QOS_PRIDEC_TABLE_TRUSTED,
+ rtl8365mb_qos_apptrust_src[i]);
+ if (weight < 0)
+ return weight;
+
+ trust[i] = weight != RTL8365MB_QOS_WEIGHT_DISABLED;
+ }
+
+ return 0;
+}
+
+int rtl8365mb_qos_get_apptrust(struct realtek_priv *priv, int port, bool *trust,
+ unsigned int n)
+{
+ u32 trusted_ports;
+ int ret;
+
+ if (n != ARRAY_SIZE(rtl8365mb_qos_apptrust_src))
+ return -EINVAL;
+
+ ret = regmap_read(priv->map, RTL8365MB_QOS_PRIDEC_TBL_SEL_REG,
+ &trusted_ports);
+ if (ret)
+ return ret;
+
+ /* On the untrusted table nothing but the port default is trusted. */
+ if (!(trusted_ports & BIT(port))) {
+ memset(trust, 0, n * sizeof(*trust));
+ return 0;
+ }
+
+ return rtl8365mb_qos_apptrust_read(priv, trust);
+}
+
+/* The trusted table is shared by all ports. A port may switch to it only if it
+ * trusts the same sources as the other ports already on it.
+ */
+static int rtl8365mb_qos_apptrust_check_shared(struct realtek_priv *priv,
+ int port, const bool *trust)
+{
+ bool other[ARRAY_SIZE(rtl8365mb_qos_apptrust_src)];
+ u32 trusted_ports;
+ int ret;
+
+ ret = regmap_read(priv->map, RTL8365MB_QOS_PRIDEC_TBL_SEL_REG,
+ &trusted_ports);
+ if (ret)
+ return ret;
+
+ if (!(trusted_ports & ~BIT(port)))
+ return 0;
+
+ ret = rtl8365mb_qos_apptrust_read(priv, other);
+ if (ret)
+ return ret;
+
+ if (memcmp(trust, other, sizeof(other))) {
+ dev_err(priv->dev,
+ "trust profile is switch-wide; another port already trusts different sources\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+int rtl8365mb_qos_set_apptrust(struct realtek_priv *priv, int port,
+ const bool *trust, unsigned int n)
+{
+ u32 reg = RTL8365MB_QOS_APPTRUST_REG(rtl8365mb_qos_apptrust_src[0]);
+ u32 mask = 0, val = 0;
+ bool any = false;
+ int ret, i;
+
+ if (n != ARRAY_SIZE(rtl8365mb_qos_apptrust_src))
+ return -EINVAL;
+
+ ret = rtl8365mb_qos_check_port(priv, port);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < n; i++)
+ any |= trust[i];
+
+ if (!any)
+ return regmap_clear_bits(priv->map,
+ RTL8365MB_QOS_PRIDEC_TBL_SEL_REG,
+ BIT(port));
+
+ ret = rtl8365mb_qos_apptrust_check_shared(priv, port, trust);
+ if (ret)
+ return ret;
+
+ /* Collect all slot weights into one mask and value and write them once,
+ * so a failed write cannot leave the trusted table half programmed.
+ */
+ for (i = 0; i < n; i++) {
+ u8 weight = trust[i] ? rtl8365mb_qos_apptrust_weight(i) :
+ RTL8365MB_QOS_WEIGHT_DISABLED;
+ u8 src = rtl8365mb_qos_apptrust_src[i];
+ u32 src_mask = rtl8365mb_qos_weight_mask(src);
+
+ /* A slot missing from rtl8365mb_qos_apptrust_src[] is 0, which
+ * is the port source. Reject it. Also reject a source whose
+ * weight is in a different register than reg.
+ */
+ if (WARN_ON_ONCE(src == RTL8365MB_QOS_PRIDEC_PORT ||
+ RTL8365MB_QOS_APPTRUST_REG(src) != reg))
+ return -EINVAL;
+
+ mask |= src_mask;
+ val |= field_prep(src_mask, weight);
+ }
+
+ ret = regmap_update_bits(priv->map, reg, mask, val);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(priv->map, RTL8365MB_QOS_PRIDEC_TBL_SEL_REG,
+ BIT(port));
+}
diff --git a/drivers/net/dsa/realtek/rtl8365mb_qos.h b/drivers/net/dsa/realtek/rtl8365mb_qos.h
index 71949ece4af8fae5c49cf105ddaa3db9bc622cf2..eed5ea5fa7f345ce6989e3e890d7cd755da9d0fc 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_qos.h
+++ b/drivers/net/dsa/realtek/rtl8365mb_qos.h
@@ -24,5 +24,9 @@ int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio,
u8 untag_pcp);
int rtl8365mb_qos_set_pcp_prio(struct realtek_priv *priv, u8 pcp, u8 iprio);
int rtl8365mb_qos_set_dscp_prio(struct realtek_priv *priv, u8 dscp, u8 iprio);
+int rtl8365mb_qos_set_apptrust(struct realtek_priv *priv, int port,
+ const bool *trust, unsigned int n);
+int rtl8365mb_qos_get_apptrust(struct realtek_priv *priv, int port, bool *trust,
+ unsigned int n);
#endif /* _REALTEK_RTL8365MB_QOS_H */
diff --git a/drivers/net/dsa/realtek/rtl83xx-qos.c b/drivers/net/dsa/realtek/rtl83xx-qos.c
index 08091586901291e45ad1a2c64730cdcc30298b7a..b25a9dfb9138b5c2c176dbe68974cdd65e1643bb 100644
--- a/drivers/net/dsa/realtek/rtl83xx-qos.c
+++ b/drivers/net/dsa/realtek/rtl83xx-qos.c
@@ -10,6 +10,7 @@
* The backend maps internal priorities to its egress queues itself.
*/
+#include <linux/build_bug.h>
#include <linux/dcbnl.h>
#include <linux/errno.h>
#include <net/dscp.h>
@@ -19,6 +20,25 @@
#include "realtek.h"
#include "rtl83xx-qos.h"
+/* dcbnl selector of each apptrust slot */
+static const u8 rtl83xx_apptrust_sel[] = {
+ [RTL83XX_APPTRUST_DSCP] = IEEE_8021QAZ_APP_SEL_DSCP,
+ [RTL83XX_APPTRUST_PCP] = DCB_APP_SEL_PCP,
+};
+
+static_assert(ARRAY_SIZE(rtl83xx_apptrust_sel) == RTL83XX_APPTRUST_COUNT);
+
+static int rtl83xx_apptrust_sel_to_slot(u8 sel)
+{
+ int slot;
+
+ for (slot = 0; slot < ARRAY_SIZE(rtl83xx_apptrust_sel); slot++)
+ if (rtl83xx_apptrust_sel[slot] == sel)
+ return slot;
+
+ return -EINVAL;
+}
+
/* Seed the PCP and DSCP tables, although neither source is trusted yet */
int rtl83xx_qos_init(struct dsa_switch *ds)
{
@@ -113,3 +133,58 @@ int rtl83xx_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
return priv->ops->qos_set_port_prio(priv, port, tt, prio);
}
EXPORT_SYMBOL_NS_GPL(rtl83xx_port_set_default_prio, "REALTEK_DSA");
+
+int rtl83xx_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel,
+ int *nsel)
+{
+ bool trust[ARRAY_SIZE(rtl83xx_apptrust_sel)];
+ struct realtek_priv *priv = ds->priv;
+ int i, ret;
+
+ if (!priv->ops->qos_get_apptrust)
+ return -EOPNOTSUPP;
+
+ ret = priv->ops->qos_get_apptrust(priv, port, trust,
+ ARRAY_SIZE(trust));
+ if (ret)
+ return ret;
+
+ *nsel = 0;
+
+ for (i = 0; i < ARRAY_SIZE(rtl83xx_apptrust_sel); i++)
+ if (trust[i])
+ sel[(*nsel)++] = rtl83xx_apptrust_sel[i];
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_port_get_apptrust, "REALTEK_DSA");
+
+int rtl83xx_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel,
+ int nsel)
+{
+ bool trust[ARRAY_SIZE(rtl83xx_apptrust_sel)] = { };
+ struct realtek_priv *priv = ds->priv;
+ int i, prev = -1;
+
+ if (!priv->ops->qos_set_apptrust)
+ return -EOPNOTSUPP;
+
+ /* The backends use a fixed precedence, so the selector list
+ * must follow the slot order.
+ */
+ for (i = 0; i < nsel; i++) {
+ int slot = rtl83xx_apptrust_sel_to_slot(sel[i]);
+
+ if (slot < 0 || slot <= prev) {
+ dev_err(priv->dev,
+ "unsupported apptrust order; supported: dscp, pcp, dscp pcp\n");
+ return -EOPNOTSUPP;
+ }
+
+ prev = slot;
+ trust[slot] = true;
+ }
+
+ return priv->ops->qos_set_apptrust(priv, port, trust, ARRAY_SIZE(trust));
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_port_set_apptrust, "REALTEK_DSA");
diff --git a/drivers/net/dsa/realtek/rtl83xx-qos.h b/drivers/net/dsa/realtek/rtl83xx-qos.h
index 82ad9338d78db9062d8ceed06378b31e467ee04d..fbddc7b7d9dd37918423afc5373b684596637a05 100644
--- a/drivers/net/dsa/realtek/rtl83xx-qos.h
+++ b/drivers/net/dsa/realtek/rtl83xx-qos.h
@@ -11,5 +11,9 @@ int rtl83xx_qos_init(struct dsa_switch *ds);
int rtl83xx_qos_init_port(struct dsa_switch *ds, int port);
int rtl83xx_port_get_default_prio(struct dsa_switch *ds, int port);
int rtl83xx_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio);
+int rtl83xx_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel,
+ int *nsel);
+int rtl83xx_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel,
+ int nsel);
#endif /* _RTL83XX_QOS_H */
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v2 10/10] net: dsa: realtek: rtl8365mb: report the DCB DSCP-to-priority map
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
` (8 preceding siblings ...)
2026-11-12 8:37 ` [PATCH net-next v2 09/10] net: dsa: realtek: rtl8365mb: offload DCB apptrust Oleksij Rempel
@ 2026-11-12 8:37 ` Oleksij Rempel
9 siblings, 0 replies; 12+ messages in thread
From: Oleksij Rempel @ 2026-11-12 8:37 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Alvin Šipraga, Andrew Lunn, Vladimir Oltean
Cc: netdev, linux-kernel, Oleksij Rempel, kernel
Report the DSCP-to-priority map through dcbnl, so that "dcb app show"
lists, on every port, the priority a DSCP value gets once the port
trusts DSCP.
The switch has one DSCP-to-priority table for all its ports, with
exactly one priority per DSCP value. dcbnl, in contrast, configures DSCP
priorities per port and allows several priorities for one DSCP value.
DSA could mirror a change to every port (dscp_prio_mapping_is_global),
but a request on one port would still change the classification of all
ports. Keep the map read-only. The QoS baseline seeds it with the
default mapping from ietf_dscp_to_ieee8021q_tt(). Requests to change a
DSCP entry fail with -EOPNOTSUPP.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
Q: The v1 review (Linus) suggested DSCP get, add and delete callbacks
in the common code, and setting ds->dscp_prio_mapping_is_global when
registering DSCP offload. Why are add, delete and the flag gone in v2?
A: The DSCP get moved into the shared code as asked. Add and del were
dropped because the table is chip-wide, as the commit message explains.
Without add and del, nothing would use the flag, so it is not set.
Changes in v2:
- make the DSCP map read-only, as the table is shared by all ports of
the chip and per-port remapping would change every port
- drop the DSCP add/del ops, whose delete path could leave the hardware
out of step with the remaining APP entries (sashiko)
- do not set ds->dscp_prio_mapping_is_global; without DSCP add/del
nothing needs its global set/del path (sashiko)
- retitled (was "offload DCB DSCP-to-priority")
- move the DSCP get handling into rtl83xx-qos.c behind a new
qos_get_dscp_prio callback (Linus)
- DSCP table seeding moved to the baseline patch
- read the table entry through rtl8365mb_qos_regmap_read_field(), which
uses field_get() (Luiz)
---
drivers/net/dsa/realtek/realtek.h | 1 +
drivers/net/dsa/realtek/rtl8365mb_main.c | 2 ++
drivers/net/dsa/realtek/rtl8365mb_qos.c | 7 +++++++
drivers/net/dsa/realtek/rtl8365mb_qos.h | 1 +
drivers/net/dsa/realtek/rtl83xx-qos.c | 19 +++++++++++++++++++
drivers/net/dsa/realtek/rtl83xx-qos.h | 1 +
6 files changed, 31 insertions(+)
diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h
index 0008ba9681cccb521d85d9bfc97425ad1b777aa6..6f2bdeab703a7758e01f983b8907c9421c89fa52 100644
--- a/drivers/net/dsa/realtek/realtek.h
+++ b/drivers/net/dsa/realtek/realtek.h
@@ -181,6 +181,7 @@ struct realtek_ops {
int (*qos_set_port_prio)(struct realtek_priv *priv, int port,
u8 iprio, u8 untag_pcp);
int (*qos_set_pcp_prio)(struct realtek_priv *priv, u8 pcp, u8 iprio);
+ int (*qos_get_dscp_prio)(struct realtek_priv *priv, u8 dscp);
int (*qos_set_dscp_prio)(struct realtek_priv *priv, u8 dscp,
u8 iprio);
/* @trust is indexed by enum rtl83xx_apptrust_slot and has @n entries.
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index d16e7c99217bdc01d1bdf7dbbcb8e7ed1441e6a8..9377bdbae36c487cf11dc48e77f23ca9f78565dc 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -3483,6 +3483,7 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = {
.port_set_default_prio = rtl83xx_port_set_default_prio,
.port_get_apptrust = rtl83xx_port_get_apptrust,
.port_set_apptrust = rtl83xx_port_set_apptrust,
+ .port_get_dscp_prio = rtl83xx_port_get_dscp_prio,
.port_stp_state_set = rtl8365mb_port_stp_state_set,
.port_fast_age = rtl83xx_port_fast_age,
.port_fdb_add = rtl83xx_port_fdb_add,
@@ -3528,6 +3529,7 @@ static const struct realtek_ops rtl8365mb_ops = {
.qos_get_port_prio = rtl8365mb_qos_get_port_prio,
.qos_set_port_prio = rtl8365mb_qos_set_port_prio,
.qos_set_pcp_prio = rtl8365mb_qos_set_pcp_prio,
+ .qos_get_dscp_prio = rtl8365mb_qos_get_dscp_prio,
.qos_set_dscp_prio = rtl8365mb_qos_set_dscp_prio,
.qos_set_apptrust = rtl8365mb_qos_set_apptrust,
.qos_get_apptrust = rtl8365mb_qos_get_apptrust,
diff --git a/drivers/net/dsa/realtek/rtl8365mb_qos.c b/drivers/net/dsa/realtek/rtl8365mb_qos.c
index c465d639ee5e527a604498fb064d6757aac1fda4..095f4a77bd1c666528b5fa9f57aa427114ebefb3 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_qos.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_qos.c
@@ -255,6 +255,13 @@ int rtl8365mb_qos_set_pcp_prio(struct realtek_priv *priv, u8 pcp, u8 iprio)
iprio);
}
+int rtl8365mb_qos_get_dscp_prio(struct realtek_priv *priv, u8 dscp)
+{
+ return rtl8365mb_qos_regmap_read_field(priv,
+ RTL8365MB_QOS_DSCP_PRI_REG(dscp),
+ rtl8365mb_qos_sel_mask(dscp));
+}
+
int rtl8365mb_qos_set_dscp_prio(struct realtek_priv *priv, u8 dscp, u8 iprio)
{
return rtl8365mb_qos_regmap_update_field(priv,
diff --git a/drivers/net/dsa/realtek/rtl8365mb_qos.h b/drivers/net/dsa/realtek/rtl8365mb_qos.h
index eed5ea5fa7f345ce6989e3e890d7cd755da9d0fc..9602807f7db5b9c5bf0202612e4cd77b85079cb7 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_qos.h
+++ b/drivers/net/dsa/realtek/rtl8365mb_qos.h
@@ -23,6 +23,7 @@ int rtl8365mb_qos_get_port_prio(struct realtek_priv *priv, int port);
int rtl8365mb_qos_set_port_prio(struct realtek_priv *priv, int port, u8 iprio,
u8 untag_pcp);
int rtl8365mb_qos_set_pcp_prio(struct realtek_priv *priv, u8 pcp, u8 iprio);
+int rtl8365mb_qos_get_dscp_prio(struct realtek_priv *priv, u8 dscp);
int rtl8365mb_qos_set_dscp_prio(struct realtek_priv *priv, u8 dscp, u8 iprio);
int rtl8365mb_qos_set_apptrust(struct realtek_priv *priv, int port,
const bool *trust, unsigned int n);
diff --git a/drivers/net/dsa/realtek/rtl83xx-qos.c b/drivers/net/dsa/realtek/rtl83xx-qos.c
index b25a9dfb9138b5c2c176dbe68974cdd65e1643bb..f4b7866c7f70ba5ea74f5d8631a9b66afcf50728 100644
--- a/drivers/net/dsa/realtek/rtl83xx-qos.c
+++ b/drivers/net/dsa/realtek/rtl83xx-qos.c
@@ -134,6 +134,25 @@ int rtl83xx_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
}
EXPORT_SYMBOL_NS_GPL(rtl83xx_port_set_default_prio, "REALTEK_DSA");
+int rtl83xx_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp)
+{
+ struct realtek_priv *priv = ds->priv;
+ int tt;
+
+ if (!priv->ops->qos_get_dscp_prio)
+ return -EOPNOTSUPP;
+
+ if (dscp >= DSCP_MAX)
+ return -EINVAL;
+
+ tt = priv->ops->qos_get_dscp_prio(priv, dscp);
+ if (tt < 0)
+ return tt;
+
+ return ieee8021q_tt_to_pcp(tt);
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_port_get_dscp_prio, "REALTEK_DSA");
+
int rtl83xx_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel,
int *nsel)
{
diff --git a/drivers/net/dsa/realtek/rtl83xx-qos.h b/drivers/net/dsa/realtek/rtl83xx-qos.h
index fbddc7b7d9dd37918423afc5373b684596637a05..06dbfd24b1af5cced870f463f15b1b39eceb56ee 100644
--- a/drivers/net/dsa/realtek/rtl83xx-qos.h
+++ b/drivers/net/dsa/realtek/rtl83xx-qos.h
@@ -15,5 +15,6 @@ int rtl83xx_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel,
int *nsel);
int rtl83xx_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel,
int nsel);
+int rtl83xx_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp);
#endif /* _RTL83XX_QOS_H */
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next v2 05/10] net: dsa: realtek: rtl8365mb: describe the ports of each chip
2026-11-12 8:37 ` [PATCH net-next v2 05/10] net: dsa: realtek: rtl8365mb: describe the ports of each chip Oleksij Rempel
@ 2026-11-12 15:08 ` Alvin Šipraga
0 siblings, 0 replies; 12+ messages in thread
From: Alvin Šipraga @ 2026-11-12 15:08 UTC (permalink / raw)
To: Oleksij Rempel
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Linus Walleij, Luiz Angelo Daros de Luca,
Andrew Lunn, Vladimir Oltean, netdev, linux-kernel, kernel
On Thu, Nov 12, 2026 at 09:37:48AM +0100, Oleksij Rempel wrote:
> Record which ports each chip has, report one more than its highest
> port to DSA as the port count, and reject a devicetree port the chip
> does not have. Lower the family maximum to 8 to match.
>
> Commit ca5ecd4246d4 ("net: dsa: realtek: rtl8365mb: correct the max
> number of ports") raised the port count of all chips to 11, but no
> supported chip has a port above 7.
>
> Since commit 51a4a22301b0 ("net: dsa: realtek: rtl8365mb: use dsa
> helpers for port iteration"), setup also programs unused ports, so
> every probe writes per-port registers of ports 8..10. Their learn-limit
> writes go to 0x0A28..0x0A2A, above the learn-limit registers of ports
> 0..7. We have not seen a visible effect from these writes.
I'm not quite sure what the point of the patch is (sorry, maybe it's
clear later in the series, but I didn't get that far yet). Was the setup
of nonexistent ports clobbering some other registers?
Of course in practice none of the supported chips are >8 so there's
nothing technically wrong with this patch, but just wondering.
Also in the blamed commit there was:
-/* RTL8370MB and RTL8310SR, possibly suportable by this driver, have 10 ports */
-#define RTL8365MB_MAX_NUM_PORTS 10
+#define RTL8365MB_MAX_NUM_PORTS 11
So maybe it needs bumping in the future if support is added for those
chips. That's why I'm wondering if you saw something off?
>
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
> Changes in v2:
> - New in v2. The QoS patches after it handle ports 0..7 only.
> ---
> drivers/net/dsa/realtek/rtl8365mb_main.c | 28 ++++++++++++++++++++++++++--
> 1 file changed, 26 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
> index da06f952a053d1497647a2c6f15a861fc21860f9..1e6cdc61f6bd3b3f74e422dbbcfdcb67cc0532a7 100644
> --- a/drivers/net/dsa/realtek/rtl8365mb_main.c
> +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
> @@ -115,7 +115,7 @@
> #define RTL8365MB_PHYADDRMAX 7
> #define RTL8365MB_NUM_PHYREGS 32
> #define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1)
> -#define RTL8365MB_MAX_NUM_PORTS 11
> +#define RTL8365MB_MAX_NUM_PORTS 8
> /* Valid for the whole family except RTL8370B, which has 4160 entries.
> * RTL8370B is mentioned in vendor code but it might not even belong
> * to the same RTL8367C family.
> @@ -707,6 +707,7 @@ struct rtl8365mb_extint {
> * @name: human-readable chip name
> * @chip_id: chip identifier
> * @chip_ver: chip silicon revision
> + * @port_mask: ports the chip has
> * @extints: available external interfaces
> * @jam_table: chip-specific initialization jam table
> * @jam_size: size of the chip's jam table
> @@ -719,6 +720,7 @@ struct rtl8365mb_chip_info {
> const char *name;
> u32 chip_id;
> u32 chip_ver;
> + u32 port_mask;
> const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS];
> const struct rtl8365mb_jam_tbl_entry *jam_table;
> size_t jam_size;
> @@ -726,11 +728,20 @@ struct rtl8365mb_chip_info {
>
> /* Chip info for each supported switch in the family */
> #define PHY_INTF(_mode) (RTL8365MB_PHY_INTERFACE_MODE_ ## _mode)
> +#define PORT_MASK(_m) \
> + ((_m) + \
> + BUILD_BUG_ON_ZERO((_m) & ~GENMASK(RTL8365MB_MAX_NUM_PORTS - 1, 0)))
> +
> +/* Copper ports are 0..3 on the RTL8365MB-VC and 0..4 on the others. Extension
> + * interfaces are port 6 on the VC and ports 6 and 7 on the others, as the
> + * extints rows show.
> + */
> static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
> {
> .name = "RTL8365MB-VC",
> .chip_id = 0x6367,
> .chip_ver = 0x0040,
> + .port_mask = PORT_MASK(GENMASK(3, 0) | BIT(6)),
> .extints = {
> { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
> PHY_INTF(RMII) | PHY_INTF(RGMII) },
> @@ -742,6 +753,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
> .name = "RTL8367S",
> .chip_id = 0x6367,
> .chip_ver = 0x00A0,
> + .port_mask = PORT_MASK(GENMASK(4, 0) | BIT(6) | BIT(7)),
> .extints = {
> { 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) },
> { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) |
> @@ -754,6 +766,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
> .name = "RTL8367SB",
> .chip_id = 0x6367,
> .chip_ver = 0x0010,
> + .port_mask = PORT_MASK(GENMASK(4, 0) | BIT(6) | BIT(7)),
> .extints = {
> { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
> PHY_INTF(RMII) | PHY_INTF(RGMII) |
> @@ -768,6 +781,7 @@ static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = {
> .name = "RTL8367RB-VB",
> .chip_id = 0x6367,
> .chip_ver = 0x0020,
> + .port_mask = PORT_MASK(GENMASK(4, 0) | BIT(6) | BIT(7)),
> .extints = {
> { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) |
> PHY_INTF(RMII) | PHY_INTF(RGMII) },
> @@ -3140,6 +3154,16 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
> dev_info(priv->dev, "no interrupt support\n");
>
> dsa_switch_for_each_port(dp, ds) {
> + if (dsa_port_is_unused(dp))
> + continue;
You can use dsa_switch_for_each_available_port() here.
> +
> + if (!(mb->chip_info->port_mask & BIT(dp->index))) {
> + dev_err(priv->dev, "port %d does not exist on %s\n",
> + dp->index, mb->chip_info->name);
> + ret = -EINVAL;
> + goto out_teardown_irq;
> + }
> +
> /* Cascading (DSA links) is not supported yet.
> * Historically, the driver has always been broken
> * without a dedicated CPU port because CPU tagging
> @@ -3349,7 +3373,7 @@ static int rtl8365mb_detect(struct realtek_priv *priv)
>
> dev_info(priv->dev, "found an %s switch\n", mb->chip_info->name);
>
> - priv->num_ports = RTL8365MB_MAX_NUM_PORTS;
> + priv->num_ports = fls(mb->chip_info->port_mask);
> mb->priv = priv;
> mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS;
> mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL;
>
> --
> 2.47.3
>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-11-12 15:09 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-12 8:37 [PATCH net-next v2 00/10] net: dsa: realtek: rtl8365mb: DCB ingress QoS offload Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 01/10] net: ieee8021q: print traffic type and queue count with %u Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 02/10] net: ieee8021q: add pcp_to_tt() Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 03/10] net: ieee8021q: clarify the tt_to_tc() traffic-class mapping Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 04/10] net: ieee8021q: add tt_to_pcp() Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 05/10] net: dsa: realtek: rtl8365mb: describe the ports of each chip Oleksij Rempel
2026-11-12 15:08 ` Alvin Šipraga
2026-11-12 8:37 ` [PATCH net-next v2 06/10] net: dsa: realtek: rtl8365mb: store the egress queue count per chip Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 07/10] net: dsa: realtek: rtl8365mb: add QoS baseline and DCB default prio Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 08/10] net: dsa: realtek: rtl8365mb: program the PCP of untagged frames Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 09/10] net: dsa: realtek: rtl8365mb: offload DCB apptrust Oleksij Rempel
2026-11-12 8:37 ` [PATCH net-next v2 10/10] net: dsa: realtek: rtl8365mb: report the DCB DSCP-to-priority map Oleksij Rempel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®