[net-next PATCH v11 9/9] net: dsa: realtek: rtl8365mb: add bridge port flags

From: Luiz Angelo Daros de Luca

Date: Sun May 31 2026 - 22:58:04 EST


Implement support for bridge port flags to control learning and flooding
behavior. This patch maps hardware functionalities to the following
bridge flags:

- BR_LEARNING
- BR_FLOOD
- BR_MCAST_FLOOD
- BR_BCAST_FLOOD

By default, all flooding types are enabled during port setup to ensure
standard bridge behavior.

Reviewed-by: Linus Walleij <linusw@xxxxxxxxxx>
Reviewed-by: Mieczyslaw Nalewaj <namiltd@xxxxxxxxx>
Signed-off-by: Luiz Angelo Daros de Luca <luizluca@xxxxxxxxx>
---
drivers/net/dsa/realtek/realtek.h | 6 ++
drivers/net/dsa/realtek/rtl8365mb_main.c | 68 +++++++++++++++++++++
drivers/net/dsa/realtek/rtl83xx.c | 101 +++++++++++++++++++++++++++++++
drivers/net/dsa/realtek/rtl83xx.h | 4 ++
4 files changed, 179 insertions(+)

diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h
index 426c9d345e6e..452e4e6dd157 100644
--- a/drivers/net/dsa/realtek/realtek.h
+++ b/drivers/net/dsa/realtek/realtek.h
@@ -134,6 +134,12 @@ struct realtek_ops {
int (*port_set_efid)(struct realtek_priv *priv, int port, u32 efid);
int (*port_set_learning)(struct realtek_priv *priv, int port,
bool enable);
+ int (*port_set_ucast_flood)(struct realtek_priv *priv, int port,
+ bool enable);
+ int (*port_set_mcast_flood)(struct realtek_priv *priv, int port,
+ bool enable);
+ int (*port_set_bcast_flood)(struct realtek_priv *priv, int port,
+ bool enable);
int (*l2_add_uc)(struct realtek_priv *priv, int port,
const unsigned char addr[ETH_ALEN],
u16 efid, u16 vid);
diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c
index 75317e47e32d..71bc91aff7be 100644
--- a/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ b/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -310,6 +310,21 @@
#define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \
(0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport)))

+/* Unknown unicast DA flooding port mask */
+#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG 0x0890
+#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_MASK 0x07FF
+
+/* Unknown multicast DA flooding port mask */
+#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG 0x0891
+#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_MASK 0x07FF
+
+/* Broadcast flooding port mask */
+#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG 0x0892
+#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_MASK 0x07FF
+
+#define RTL8365MB_SUPPORTED_BRIDGE_FLAGS \
+ (BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD)
+
/* Miscellaneous port configuration register, incl. VLAN egress mode */
#define RTL8365MB_PORT_MISC_CFG_REG_BASE 0x000E
#define RTL8365MB_PORT_MISC_CFG_REG(_p) \
@@ -1554,6 +1569,49 @@ static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port,
enable ? RTL8365MB_LEARN_LIMIT_MAX : 0);
}

+static int rtl8365mb_port_set_ucast_flood(struct realtek_priv *priv, int port,
+ bool enable)
+{
+ /* Frames with unknown unicast DA will be flooded to a programmable
+ * port mask that by default includes all ports. Add or remove
+ * the specified port from this port mask accordingly.
+ */
+ return regmap_update_bits(priv->map,
+ RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG,
+ BIT(port), enable ? BIT(port) : 0);
+}
+
+static int rtl8365mb_port_set_mcast_flood(struct realtek_priv *priv, int port,
+ bool enable)
+{
+ return regmap_update_bits(priv->map,
+ RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG,
+ BIT(port), enable ? BIT(port) : 0);
+}
+
+static int rtl8365mb_port_set_bcast_flood(struct realtek_priv *priv, int port,
+ bool enable)
+{
+ return regmap_update_bits(priv->map,
+ RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG,
+ BIT(port), enable ? BIT(port) : 0);
+}
+
+static int rtl8365mb_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags flags,
+ struct netlink_ext_ack *extack)
+{
+ struct realtek_priv *priv = ds->priv;
+
+ dev_dbg(priv->dev, "pre_bridge_flags port:%d flags:%lx supported:%lx\n",
+ port, flags.mask, RTL8365MB_SUPPORTED_BRIDGE_FLAGS);
+
+ if (flags.mask & ~RTL8365MB_SUPPORTED_BRIDGE_FLAGS)
+ return -EINVAL;
+
+ return 0;
+}
+
static int rtl8365mb_port_set_efid(struct realtek_priv *priv, int port,
u32 efid)
{
@@ -2404,6 +2462,11 @@ static int rtl8365mb_setup(struct dsa_switch *ds)
if (ret)
goto out_teardown_irq;

+ /* Enable all types of flooding */
+ ret = rtl83xx_setup_port_flood_control(priv, dp->index);
+ if (ret)
+ goto out_teardown_irq;
+
/* Set up per-port private data */
p->priv = priv;
p->index = dp->index;
@@ -2589,6 +2652,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = {
.phylink_get_caps = rtl8365mb_phylink_get_caps,
.port_bridge_join = rtl83xx_port_bridge_join,
.port_bridge_leave = rtl83xx_port_bridge_leave,
+ .port_pre_bridge_flags = rtl8365mb_port_pre_bridge_flags,
+ .port_bridge_flags = rtl83xx_port_bridge_flags,
.port_stp_state_set = rtl8365mb_port_stp_state_set,
.port_fast_age = rtl83xx_port_fast_age,
.port_fdb_add = rtl83xx_port_fdb_add,
@@ -2618,6 +2683,9 @@ static const struct realtek_ops rtl8365mb_ops = {
.port_remove_isolation = rtl8365mb_port_remove_isolation,
.port_set_efid = rtl8365mb_port_set_efid,
.port_set_learning = rtl8365mb_port_set_learning,
+ .port_set_ucast_flood = rtl8365mb_port_set_ucast_flood,
+ .port_set_mcast_flood = rtl8365mb_port_set_mcast_flood,
+ .port_set_bcast_flood = rtl8365mb_port_set_bcast_flood,
.l2_add_uc = rtl8365mb_l2_add_uc,
.l2_del_uc = rtl8365mb_l2_del_uc,
.l2_get_next_uc = rtl8365mb_l2_get_next_uc,
diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c
index 8b7154db3887..218de350918f 100644
--- a/drivers/net/dsa/realtek/rtl83xx.c
+++ b/drivers/net/dsa/realtek/rtl83xx.c
@@ -3,6 +3,7 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/of_mdio.h>
+#include <linux/if_bridge.h>
#include <linux/etherdevice.h>

#include "realtek.h"
@@ -793,6 +794,106 @@ int rtl83xx_port_mdb_del(struct dsa_switch *ds, int port,
}
EXPORT_SYMBOL_NS_GPL(rtl83xx_port_mdb_del, "REALTEK_DSA");

+/**
+ * rtl83xx_port_bridge_flags() - set port bridge flags
+ * @ds: DSA switch instance
+ * @port: port index
+ * @flags: bridge port flags
+ * @extack: netlink extended ack for reporting errors
+ *
+ * This function handles setting bridge port flags like learning and flooding.
+ *
+ * Context: Can sleep.
+ * Return: 0 on success, negative value for failure.
+ */
+int rtl83xx_port_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags flags,
+ struct netlink_ext_ack *extack)
+{
+ struct realtek_priv *priv = ds->priv;
+ bool enable;
+ int ret;
+
+ if (flags.mask & BR_LEARNING) {
+ if (!priv->ops->port_set_learning)
+ return -EOPNOTSUPP;
+
+ enable = !!(flags.val & BR_LEARNING);
+ ret = priv->ops->port_set_learning(priv, port, enable);
+ if (ret)
+ return ret;
+ }
+
+ if (flags.mask & BR_FLOOD) {
+ if (!priv->ops->port_set_ucast_flood)
+ return -EOPNOTSUPP;
+
+ enable = !!(flags.val & BR_FLOOD);
+ ret = priv->ops->port_set_ucast_flood(priv, port, enable);
+ if (ret)
+ return ret;
+ }
+
+ if (flags.mask & BR_MCAST_FLOOD) {
+ if (!priv->ops->port_set_mcast_flood)
+ return -EOPNOTSUPP;
+
+ enable = !!(flags.val & BR_MCAST_FLOOD);
+ ret = priv->ops->port_set_mcast_flood(priv, port, enable);
+ if (ret)
+ return ret;
+ }
+
+ if (flags.mask & BR_BCAST_FLOOD) {
+ if (!priv->ops->port_set_bcast_flood)
+ return -EOPNOTSUPP;
+
+ enable = !!(flags.val & BR_BCAST_FLOOD);
+ ret = priv->ops->port_set_bcast_flood(priv, port, enable);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_port_bridge_flags, "REALTEK_DSA");
+
+/**
+ * rtl83xx_setup_port_flood_control() - setup default flood control for a port
+ * @priv: realtek_priv pointer
+ * @port: port index
+ *
+ * This function enables flooding for a given port.
+ *
+ * Context: Can sleep.
+ * Return: 0 on success, negative value for failure.
+ */
+int rtl83xx_setup_port_flood_control(struct realtek_priv *priv, int port)
+{
+ int ret;
+
+ if (priv->ops->port_set_ucast_flood) {
+ ret = priv->ops->port_set_ucast_flood(priv, port, true);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->ops->port_set_mcast_flood) {
+ ret = priv->ops->port_set_mcast_flood(priv, port, true);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->ops->port_set_bcast_flood) {
+ ret = priv->ops->port_set_bcast_flood(priv, port, true);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(rtl83xx_setup_port_flood_control, "REALTEK_DSA");
+
MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@xxxxxxxxx>");
MODULE_AUTHOR("Linus Walleij <linus.walleij@xxxxxxxxxx>");
MODULE_DESCRIPTION("Realtek DSA switches common module");
diff --git a/drivers/net/dsa/realtek/rtl83xx.h b/drivers/net/dsa/realtek/rtl83xx.h
index dcb819fe567f..d86447121276 100644
--- a/drivers/net/dsa/realtek/rtl83xx.h
+++ b/drivers/net/dsa/realtek/rtl83xx.h
@@ -27,6 +27,10 @@ int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack);
void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port,
struct dsa_bridge bridge);
+int rtl83xx_port_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags flags,
+ struct netlink_ext_ack *extack);
+int rtl83xx_setup_port_flood_control(struct realtek_priv *priv, int port);

void rtl83xx_port_fast_age(struct dsa_switch *ds, int port);
int rtl83xx_port_fdb_add(struct dsa_switch *ds, int port,

--
2.54.0