Netdev List
 help / color / mirror / Atom feed
From: David Yang <mmyangfl@gmail.com>
To: netdev@vger.kernel.org
Cc: David Yang <mmyangfl@gmail.com>, Andrew Lunn <andrew@lunn.ch>,
	Vladimir Oltean <olteanv@gmail.com>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@kernel.org>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>,
	linux-kernel@vger.kernel.org
Subject: [PATCH net-next 6/6] net: dsa: motorcomm: Add BR_PORT_LOCKED and BR_PORT_MAB support
Date: Mon,  2 Nov 2026 23:56:38 +0800	[thread overview]
Message-ID: <20261102155641.3682626-7-mmyangfl@gmail.com> (raw)
In-Reply-To: <20261102155641.3682626-1-mmyangfl@gmail.com>

Program the per-port learn register to implement BR_PORT_LOCKED and
BR_PORT_MAB offload.

Learn-suspended FDB entries (status pending) created by locked ports
are never aged by the hardware. Add a delayed work to remove all pending
entries if any locked port exists.

Signed-off-by: David Yang <mmyangfl@gmail.com>
---
 drivers/net/dsa/motorcomm/chip.c | 161 ++++++++++++++++++++++++++++---
 drivers/net/dsa/motorcomm/chip.h |   9 ++
 2 files changed, 158 insertions(+), 12 deletions(-)

diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 447736854870..43e001da634e 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -822,6 +822,109 @@ yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
 	return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
 }
 
+/* Learn-suspended entries are never aged by the hardware, clean them up before
+ * the FDB fills up.
+ */
+static int yt921x_fdb_clean(struct yt921x_priv *priv)
+{
+	u8 status;
+	u16 index;
+	u32 ctrl;
+	u32 val;
+	int res;
+
+	ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX |
+	       YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
+	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+	if (res)
+		return res;
+
+	res = yt921x_fdb_wait(priv, &val);
+	if (res)
+		return res;
+
+	status = YT921X_FDB_ENTRY_STATUS_INVALID;
+	if (!(val & YT921X_FDB_RESULT_NOTFOUND)) {
+		res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val);
+		if (res)
+			return res;
+		status = FIELD_GET(YT921X_FDB_IO1_STATUS_M, val);
+	}
+
+	index = 0;
+	while (1) {
+		if (status == YT921X_FDB_ENTRY_STATUS_PENDING) {
+			ctrl = YT921X_FDB_OP_INDEX(index) |
+			       YT921X_FDB_OP_MODE_INDEX | YT921X_FDB_OP_OP_DEL |
+			       YT921X_FDB_OP_START;
+			res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+			if (res)
+				return res;
+
+			res = yt921x_fdb_wait(priv, &val);
+			if (res)
+				return res;
+		}
+
+		/* Never call GET_NEXT with 4095, otherwise it will hang
+		 * forever until a reset!
+		 */
+		if (index >= YT921X_FDB_NUM - 1)
+			break;
+
+		ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
+		       YT921X_FDB_OP_NEXT_TYPE_UCAST |
+		       YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START;
+		res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+		if (res)
+			return res;
+
+		res = yt921x_fdb_wait(priv, &val);
+		if (res)
+			return res;
+		if (val & YT921X_FDB_RESULT_NOTFOUND)
+			break;
+		index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
+
+		res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val);
+		if (res)
+			return res;
+		status = FIELD_GET(YT921X_FDB_IO1_STATUS_M, val);
+		if (status == YT921X_FDB_ENTRY_STATUS_INVALID)
+			break;
+	}
+
+	return 0;
+}
+
+static void yt921x_fdb_clean_work(struct work_struct *work)
+{
+	struct yt921x_priv *priv = container_of_const(work, struct yt921x_priv,
+						      fdb_clean.work);
+	struct device *dev = to_device(priv);
+	bool has_locked = false;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+
+	res = yt921x_fdb_clean(priv);
+
+	for (int port = 0; port < YT921X_PORT_NUM; port++)
+		if (priv->ports[port].locked) {
+			has_locked = true;
+			break;
+		}
+
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dev, "Failed to clean pending FDB entries: %i\n", res);
+
+	if (has_locked)
+		schedule_delayed_work(&priv->fdb_clean,
+				      priv->fdb_clean_interval);
+}
+
 static int
 yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port,
 			 dsa_fdb_dump_cb_t *cb, void *data)
@@ -1412,6 +1515,30 @@ static int yt921x_bridge_join(struct yt921x_priv *priv, int port)
 	return 0;
 }
 
+static int yt921x_port_learn_cfg(struct yt921x_priv *priv, int port)
+{
+	struct yt921x_port *pp = &priv->ports[port];
+	u32 ctrl;
+	u32 mask;
+
+	mask = YT921X_PORT_LEARN_MODE_M | YT921X_PORT_LEARN_DIS |
+	       YT921X_PORT_LEARN_SUSPEND_DROP_EN |
+	       YT921X_PORT_LEARN_SUSPEND_COPY_EN;
+	if (pp->locked) {
+		ctrl = YT921X_PORT_LEARN_MODE_CPU_CONTROL;
+		if (pp->mab)
+			ctrl |= YT921X_PORT_LEARN_SUSPEND_COPY_EN;
+		else
+			ctrl |= YT921X_PORT_LEARN_SUSPEND_DROP_EN;
+	} else if (pp->learning) {
+		ctrl = 0;
+	} else {
+		ctrl = YT921X_PORT_LEARN_DIS;
+	}
+	return yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port), mask,
+				      ctrl);
+}
+
 static int
 yt921x_bridge_flags(struct yt921x_priv *priv, int port,
 		    struct switchdev_brport_flags flags)
@@ -1420,17 +1547,23 @@ yt921x_bridge_flags(struct yt921x_priv *priv, int port,
 	bool change_isolated;
 	bool change_flood;
 	u32 ports_mask;
-	u32 mask;
 	int res;
 
-	if (flags.mask & BR_LEARNING) {
-		bool learning = flags.val & BR_LEARNING;
+	if (flags.mask & (BR_LEARNING | BR_PORT_LOCKED | BR_PORT_MAB)) {
+		if (flags.mask & BR_LEARNING)
+			pp->learning = flags.val & BR_LEARNING;
+		if (flags.mask & BR_PORT_LOCKED)
+			pp->locked = flags.val & BR_PORT_LOCKED;
+		if (flags.mask & BR_PORT_MAB)
+			pp->mab = flags.val & BR_PORT_MAB;
 
-		mask = YT921X_PORT_LEARN_DIS;
-		res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port),
-					     mask, !learning);
+		res = yt921x_port_learn_cfg(priv, port);
 		if (res)
 			return res;
+
+		if ((flags.mask & BR_PORT_LOCKED) && pp->locked)
+			schedule_delayed_work(&priv->fdb_clean,
+					      priv->fdb_clean_interval);
 	}
 
 	/* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
@@ -1482,7 +1615,8 @@ yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port,
 				 struct netlink_ext_ack *extack)
 {
 	if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD |
-			   BR_MCAST_FLOOD | BR_ISOLATED))
+			   BR_MCAST_FLOOD | BR_ISOLATED | BR_PORT_LOCKED |
+			   BR_PORT_MAB))
 		return -EINVAL;
 	return 0;
 }
@@ -1610,6 +1744,7 @@ yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
 	struct device *dev = yt921x_priv_to_device(priv);
 	struct dsa_port *dp = dsa_to_port(ds, port);
+	struct yt921x_port *pp = &priv->ports[port];
 	bool learning;
 	u32 mask;
 	u32 ctrl;
@@ -1644,10 +1779,8 @@ yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
 		if (res)
 			break;
 
-		mask = YT921X_PORT_LEARN_DIS;
-		ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0;
-		res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port),
-					     mask, ctrl);
+		pp->learning = learning;
+		res = yt921x_port_learn_cfg(priv, port);
 	} while (0);
 	mutex_unlock(&priv->reg_lock);
 
@@ -2242,9 +2375,11 @@ static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp)
 
 static void yt921x_dsa_teardown(struct dsa_switch *ds)
 {
-#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
 	struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
 
+	cancel_delayed_work_sync(&priv->fdb_clean);
+
+#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS)
 	yt921x_leds_remove(priv);
 #endif
 }
@@ -2723,6 +2858,8 @@ static int yt921x_mdio_probe(struct mdio_device *mdiodev)
 	mdio->switchid = 0;
 
 	mutex_init(&priv->reg_lock);
+	INIT_DELAYED_WORK(&priv->fdb_clean, yt921x_fdb_clean_work);
+	priv->fdb_clean_interval = 61 * HZ;
 
 	priv->reg_ops = &yt921x_reg_ops_mdio;
 	priv->reg_ctx = mdio;
diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h
index 842fd8f83903..81d2e75fe207 100644
--- a/drivers/net/dsa/motorcomm/chip.h
+++ b/drivers/net/dsa/motorcomm/chip.h
@@ -465,6 +465,12 @@ struct yt921x_port {
 	bool flood:1;
 	/* BR_MCAST_FLOOD */
 	bool mcast_flood:1;
+	/* BR_PORT_LOCKED */
+	bool locked:1;
+	/* BR_PORT_MAB */
+	bool mab:1;
+	/* BR_LEARNING */
+	bool learning:1;
 
 	unsigned short acl_cnt;
 
@@ -526,6 +532,9 @@ struct yt921x_priv {
 	struct mii_bus *mbus_int;
 	struct mii_bus *mbus_ext;
 
+	struct delayed_work fdb_clean;
+	unsigned int fdb_clean_interval;
+
 	struct yt921x_port ports[YT921X_PORT_NUM];
 
 	u16 eee_ports_mask;
-- 
2.53.0


      parent reply	other threads:[~2026-11-02 15:58 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02 15:56 [PATCH net-next 0/6] net: dsa: motorcomm: More BR_* flags support David Yang
2026-11-02 15:56 ` [PATCH net-next 1/6] net: dsa: motorcomm: Isolate all ports during chip setup David Yang
2026-11-02 15:56 ` [PATCH net-next 2/6] net: dsa: motorcomm: Rely on port isolation for CPU port David Yang
2026-11-02 15:56 ` [PATCH net-next 3/6] net: dsa: motorcomm: Add BR_FLOOD and BR_MCAST_FLOOD support David Yang
2026-11-02 15:56 ` [PATCH net-next 4/6] net: dsa: motorcomm: Translate FDB VID for VLAN-unaware bridges David Yang
2026-11-02 15:56 ` [PATCH net-next 5/6] net: dsa: tag_yt921x: handle FDB suspend copy tag code David Yang
2026-11-02 15:56 ` David Yang [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261102155641.3682626-7-mmyangfl@gmail.com \
    --to=mmyangfl@gmail.com \
    --cc=andrew@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=olteanv@gmail.com \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox