* [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support
@ 2026-02-28 18:52 Joris Vaisvila
2026-02-28 18:52 ` [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver Joris Vaisvila
` (2 more replies)
0 siblings, 3 replies; 12+ messages in thread
From: Joris Vaisvila @ 2026-02-28 18:52 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Joris Vaisvila
Hello,
This RFC adds initial support for the basic functionality needed to
operate the MT7628 embedded switch.
The driver configures the switch to isolate each port and does not
offload any bridge functions, so all of the forwarding is done in
software. Bridge and VLAN offloading are possible but omitted to
simplify this initial patch set.
The special tag currently overlaps some functionality with tag_8021q,
but it enables precise TX handling and (more) precise RX handling for
future VLAN offloading.
Tested on MT7628AN with 5 100mbps ports. All 5 ports were tested for
correct forwarding with two devices.
# Request for comments:
- What would be an acceptable way to present the undocumented phy init
in mt7628_vendor_phys_init and switch_init?
- Should I aim to set all register default values for general switch
config or is relying on the documented switch reset state acceptable?
- I am also looking for any and all feedback on the architecture and
coding style of this driver.
Thank you for your time and feedback.
Joris Vaisvila (2):
net: dsa: initial MT7628 special tag driver
net: dsa: initial support for MT7628 embedded switch
drivers/net/dsa/Kconfig | 6 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/mt7628.c | 567 +++++++++++++++++++++++++++++++++++++++
include/net/dsa.h | 2 +
net/dsa/Kconfig | 6 +
net/dsa/Makefile | 1 +
net/dsa/tag_mt7628.c | 85 ++++++
7 files changed, 668 insertions(+)
create mode 100644 drivers/net/dsa/mt7628.c
create mode 100644 net/dsa/tag_mt7628.c
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver
2026-02-28 18:52 [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support Joris Vaisvila
@ 2026-02-28 18:52 ` Joris Vaisvila
2026-02-28 20:12 ` Andrew Lunn
2026-03-04 22:16 ` Vladimir Oltean
2026-02-28 18:52 ` [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch Joris Vaisvila
2026-02-28 23:40 ` [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support Vladimir Oltean
2 siblings, 2 replies; 12+ messages in thread
From: Joris Vaisvila @ 2026-02-28 18:52 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Joris Vaisvila
Add support for the MT7628 embedded switch's ralink special tag.
The ralink "special tag" is merged with the VLAN TPID when a VLAN is
appended by the switch. It is not installed if the VLAN tag is already
there on ingress, so the tagger assumes VLAN tagging is set up using
tag_8021q and that the outer tag is only used for port identification
and can be dropped.
On egress the special tag allows precise TX, but the correct VLAN tag
still needs to be appended.
Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
---
include/net/dsa.h | 2 ++
net/dsa/Kconfig | 6 ++++
net/dsa/Makefile | 1 +
net/dsa/tag_mt7628.c | 85 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 94 insertions(+)
create mode 100644 net/dsa/tag_mt7628.c
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 6c17446f3dcc..e93f9356b5c3 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -58,6 +58,7 @@ struct tc_action;
#define DSA_TAG_PROTO_YT921X_VALUE 30
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
#define DSA_TAG_PROTO_MXL862_VALUE 32
+#define DSA_TAG_PROTO_MT7628_VALUE 33
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -93,6 +94,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_YT921X = DSA_TAG_PROTO_YT921X_VALUE,
DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
+ DSA_TAG_PROTO_MT7628 = DSA_TAG_PROTO_MT7628_VALUE,
};
struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 5ed8c704636d..4aa73bd1aa9b 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -211,4 +211,10 @@ config NET_DSA_TAG_YT921X
Say Y or M if you want to enable support for tagging frames for
Motorcomm YT921x switches.
+config NET_DSA_TAG_MT7628
+ tristate "Tag driver for the MT7628 embedded switch"
+ help
+ Say Y or M if you want to enable support for tagging frames for the
+ switch embedded in the MT7628 SoC.
+
endif
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index bf7247759a64..d25ec0ab7d67 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) += tag_vsc73xx_8021q.o
obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o
obj-$(CONFIG_NET_DSA_TAG_YT921X) += tag_yt921x.o
+obj-$(CONFIG_NET_DSA_TAG_MT7628) += tag_mt7628.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/net/dsa/tag_mt7628.c b/net/dsa/tag_mt7628.c
new file mode 100644
index 000000000000..a612df43682c
--- /dev/null
+++ b/net/dsa/tag_mt7628.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026, Joris Vaisvila <joey@tinyisr.com>
+ * MT7628 ralink special tag support
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/dsa/8021q.h>
+#include <net/dsa.h>
+
+#include "tag.h"
+
+/*
+ * MT7628 uses the ralink special tag. It merges the VLAN TPID with source
+ * port ID on RX and target port bitmap on TX.
+ *
+ * The switch forwarding is controlled with VLANs, so each port is put in a
+ * standalone VLAN using tag_8021q. Double tag is enabled to simulate VLAN
+ * unaware ports.
+ *
+ * A VLAN tag is constructed on egress to target the standalone VLAN and port.
+ * The outer VLAN tag is parsed and removed on ingress.
+ */
+
+#define MT7628_TAG_NAME "tag_mt7628"
+
+#define SPECIAL_HEADER_RECV_SOURCE_PORT_MASK GENMASK(2, 0)
+#define SPECIAL_TAG_LEN 4
+
+static struct sk_buff *dsa_user_special_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct dsa_port *dp = dsa_user_to_port(dev);
+ u16 xmit_vlan = dsa_tag_8021q_standalone_vid(dp);
+ u8 *special_tag;
+
+ skb_push(skb, SPECIAL_TAG_LEN);
+ dsa_alloc_etype_header(skb, SPECIAL_TAG_LEN);
+
+ special_tag = dsa_etype_header_pos_tx(skb);
+
+ special_tag[0] = ETH_P_8021Q >> 8;
+ special_tag[1] = BIT(dp->index);
+
+ special_tag[2] = xmit_vlan >> 8;
+ special_tag[3] = xmit_vlan & 0xff;
+ return skb;
+}
+
+static struct sk_buff *dsa_user_special_recv(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ u16 hdr;
+ int port;
+ __be16 *phdr;
+
+ if (unlikely(!pskb_may_pull(skb, SPECIAL_TAG_LEN)))
+ return NULL;
+
+ phdr = dsa_etype_header_pos_rx(skb);
+ hdr = ntohs(*phdr);
+ skb_pull_rcsum(skb, SPECIAL_TAG_LEN);
+ dsa_strip_etype_header(skb, SPECIAL_TAG_LEN);
+
+ port = hdr & SPECIAL_HEADER_RECV_SOURCE_PORT_MASK;
+
+ skb->dev = dsa_conduit_find_user(dev, 0, port);
+ if (!skb->dev)
+ return NULL;
+ dsa_default_offload_fwd_mark(skb);
+ return skb;
+}
+
+static const struct dsa_device_ops mt7628_tag_ops = {
+ .name = MT7628_TAG_NAME,
+ .proto = DSA_TAG_PROTO_MT7628,
+ .xmit = dsa_user_special_xmit,
+ .rcv = dsa_user_special_recv,
+ .needed_headroom = SPECIAL_TAG_LEN,
+};
+module_dsa_tag_driver(mt7628_tag_ops);
+
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MT7628, MT7628_TAG_NAME);
+MODULE_DESCRIPTION("DSA tag driver for MT7628 switch");
+MODULE_LICENSE("GPL v2");
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch
2026-02-28 18:52 [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support Joris Vaisvila
2026-02-28 18:52 ` [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver Joris Vaisvila
@ 2026-02-28 18:52 ` Joris Vaisvila
2026-02-28 20:16 ` Andrew Lunn
2026-02-28 20:31 ` Andrew Lunn
2026-02-28 23:40 ` [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support Vladimir Oltean
2 siblings, 2 replies; 12+ messages in thread
From: Joris Vaisvila @ 2026-02-28 18:52 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Joris Vaisvila
Add support for the MT7628 embedded switch.
The switch supports 16 VLANs, has 5x 100mbps built-in user ports and
support for a single external 1000mbps MAC as port 5.
It does not have any way to control inter-port forwarding outside of
VLANs, so the switch is configured with tag_8021q standalone VLANs.
Double tag is enabled to fake VLAN-unaware mode until support is added
for offloading VLAN filtering.
Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
---
drivers/net/dsa/Kconfig | 6 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/mt7628.c | 567 +++++++++++++++++++++++++++++++++++++++
3 files changed, 574 insertions(+)
create mode 100644 drivers/net/dsa/mt7628.c
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 39fb8ead16b5..7af20d0d22d7 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -70,6 +70,12 @@ config NET_DSA_MV88E6060
This enables support for the Marvell 88E6060 ethernet switch
chip.
+config NET_DSA_MT7628
+ tristate "MT7628 Embedded ethernet switch support"
+ select NET_DSA_TAG_MT7628
+ help
+ This enables support for the switch in the MT7628 SoC.
+
source "drivers/net/dsa/microchip/Kconfig"
source "drivers/net/dsa/mv88e6xxx/Kconfig"
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index f5a463b87ec2..22da6b680f29 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
+obj-$(CONFIG_NET_DSA_MT7628) += mt7628.o
obj-y += b53/
obj-y += hirschmann/
obj-y += lantiq/
diff --git a/drivers/net/dsa/mt7628.c b/drivers/net/dsa/mt7628.c
new file mode 100644
index 000000000000..25881f2acf12
--- /dev/null
+++ b/drivers/net/dsa/mt7628.c
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek MT7628 Embedded Switch (ESW) DSA driver
+ * Copyright (C) 2026 Joris Vaisvila <joey@tinyisr.com>
+ *
+ * Portions derived from OpenWRT esw_rt3050 driver:
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ * Copyright (C) 2016 Vittorio Gambaletta <openwrt@vittgam.net>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/dsa/8021q.h>
+#include <linux/if_bridge.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <net/dsa.h>
+
+#define MT7628_ESW_REG_IMR 0x04
+#define MT7628_ESW_REG_FCT0 0x08
+#define MT7628_ESW_REG_PFC1 0x14
+#define MT7628_ESW_REG_PVIDC(_n) (0x40 + 4 * (_n))
+#define MT7628_ESW_REG_VLANI(_n) (0x50 + 4 * (_n))
+#define MT7628_ESW_REG_VMSC(_n) (0x70 + 4 * (_n))
+#define MT7628_ESW_REG_VUB(_n) (0x100 + 4 * (_n))
+#define MT7628_ESW_REG_SOCPC 0x8c
+#define MT7628_ESW_REG_POC0 0x90
+#define MT7628_ESW_REG_POC2 0x98
+#define MT7628_ESW_REG_SGC 0x9c
+#define MT7628_ESW_REG_PCR0 0xc0
+#define MT7628_ESW_REG_PCR1 0xc4
+#define MT7628_ESW_REG_FPA2 0xc8
+#define MT7628_ESW_REG_FCT2 0xcc
+#define MT7628_ESW_REG_SGC2 0xe4
+
+#define MT7628_ESW_PFC1_EN_VLAN GENMASK(22, 16)
+
+#define MT7628_ESW_PVIDC_PVID_M 0xfff
+#define MT7628_ESW_PVIDC_PVID_S 12
+#define MT7628_ESW_VLANI_VID_M 0xfff
+#define MT7628_ESW_VLANI_VID_S 12
+#define MT7628_ESW_VMSC_MSC_M 0xff
+#define MT7628_ESW_VMSC_MSC_S 8
+#define MT7628_ESW_VUB_S 7
+#define MT7628_ESW_VUB_M 0x7f
+
+#define MT7628_ESW_SOCPC_CRC_PADDING BIT(25)
+#define MT7628_ESW_SOCPC_DISBC2CPU GENMASK(22, 16)
+#define MT7628_ESW_SOCPC_DISMC2CPU GENMASK(14, 8)
+#define MT7628_ESW_SOCPC_DISUN2CPU GENMASK(6, 0)
+
+#define MT7628_ESW_POC0_PORT_DISABLE GENMASK(29, 23)
+
+#define MT7628_ESW_POC2_PER_VLAN_UNTAG_EN BIT(15)
+
+#define MT7628_ESW_SGC_AGING_INTERVAL GENMASK(3, 0)
+#define MT7628_ESW_BC_STORM_PROT GENMASK(5, 4)
+#define MT7628_ESW_PKT_MAX_LEN GENMASK(7, 6)
+#define MT7628_ESW_DIS_PKT_ABORT BIT(8)
+#define MT7628_ESW_ADDRESS_HASH_ALG GENMASK(10, 9)
+#define MT7628_ESW_DISABLE_TX_BACKOFF BIT(11)
+#define MT7628_ESW_BP_JAM_CNT GENMASK(15, 12)
+#define MT7628_ESW_DISMIIPORT_WASTX GENMASK(17, 16)
+#define MT7628_ESW_BP_MODE GENMASK(19, 18)
+#define MT7628_ESW_BISH_DIS BIT(20)
+#define MT7628_ESW_BISH_TH GENMASK(22, 21)
+#define MT7628_ESW_LED_FLASH_TIME GENMASK(24, 23)
+#define MT7628_ESW_RMC_RULE GENMASK(26, 25)
+#define MT7628_ESW_IP_MULT_RULE GENMASK(28, 27)
+#define MT7628_ESW_LEN_ERR_CHK BIT(29)
+#define MT7628_ESW_BKOFF_ALG BIT(30)
+
+#define MT7628_ESW_PCR0_WT_NWAY_DATA GENMASK(31, 16)
+#define MT7628_ESW_PCR0_RD_PHY_CMD BIT(14)
+#define MT7628_ESW_PCR0_WT_PHY_CMD BIT(13)
+#define MT7628_ESW_PCR0_CPU_PHY_REG GENMASK(12, 8)
+#define MT7628_ESW_PCR0_CPU_PHY_ADDR GENMASK(4, 0)
+
+#define MT7628_ESW_PCR1_RD_DATA GENMASK(31, 16)
+#define MT7628_ESW_PCR1_RD_DONE BIT(1)
+#define MT7628_ESW_PCR1_WT_DONE BIT(0)
+
+#define MT7628_ESW_FPA2_AP_EN BIT(29)
+#define MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE GENMASK(28, 24)
+#define MT7628_ESW_FPA2_FORCE_RGMII_LINK1 BIT(13)
+#define MT7628_ESW_FPA2_FORCE_RGMII_EN1 BIT(11)
+
+#define MT7628_ESW_FCT2_MUST_DROP_RLS_TH GENMASK(17, 13)
+#define MT7628_ESW_FCT2_MUST_DROP_SET_TH GENMASK(12, 8)
+#define MT7628_ESW_FCT2_MC_PER_PORT_TH GENMASK(5, 0)
+
+#define MT7628_ESW_SGC2_SPECIAL_TAG_EN BIT(23)
+#define MT7628_ESW_SGC2_TX_CPU_TPID_BIT_MAP GENMASK(22, 16)
+#define MT7628_ESW_SGC2_DOUBLE_TAG_EN GENMASK(6, 0)
+
+#define MT7628_ESW_PORTS_NOCPU GENMASK(5, 0)
+#define MT7628_ESW_PORTS_CPU BIT(6)
+#define MT7628_ESW_PORTS_ALL GENMASK(6, 0)
+
+#define MT7628_ESW_NUM_PORTS 7
+#define MT7628_NUM_VLANS 16
+
+static const struct regmap_config mt7628_esw_regmap_cfg = {
+ .name = "mt7628-esw",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+struct mt7628_vlan {
+ bool active;
+ u8 members;
+ u8 untag;
+ u16 vid;
+};
+
+struct mt7628_esw {
+ void __iomem *base;
+ struct reset_control *rst_ephy;
+ struct reset_control *rst_esw;
+ struct regmap *regmap;
+ struct dsa_switch *ds;
+ u16 tag_8021q_pvid[MT7628_ESW_NUM_PORTS];
+ struct mt7628_vlan vlans[MT7628_NUM_VLANS];
+};
+
+static int mt7628_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+ int ret = 0;
+ u32 val = 0;
+ struct mt7628_esw *esw = ds->priv;
+
+ ret = regmap_read_poll_timeout(esw->regmap, MT7628_ESW_REG_PCR1, val,
+ !(val & MT7628_ESW_PCR1_RD_DONE), 10,
+ 5000);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(esw->regmap, MT7628_ESW_REG_PCR0,
+ FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_REG, regnum) |
+ FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_ADDR,
+ port) |
+ MT7628_ESW_PCR0_RD_PHY_CMD);
+ if (ret)
+ goto out;
+
+ ret = regmap_read_poll_timeout(esw->regmap, MT7628_ESW_REG_PCR1, val,
+ (val & MT7628_ESW_PCR1_RD_DONE), 10,
+ 5000);
+out:
+ if (ret) {
+ dev_err(ds->dev, "read failed. MDIO timeout?\n");
+ return -ETIMEDOUT;
+ }
+ return FIELD_GET(MT7628_ESW_PCR1_RD_DATA, val);
+}
+
+static int mt7628_phy_write(struct dsa_switch *ds, int port, int regnum,
+ u16 dat)
+{
+ u32 val;
+ int ret = 0;
+ struct mt7628_esw *esw = ds->priv;
+
+ ret = regmap_read_poll_timeout(esw->regmap, MT7628_ESW_REG_PCR1, val,
+ !(val & MT7628_ESW_PCR1_WT_DONE), 10,
+ 5000);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(
+ esw->regmap, MT7628_ESW_REG_PCR0,
+ FIELD_PREP(MT7628_ESW_PCR0_WT_NWAY_DATA, dat) |
+ FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_REG, regnum) |
+ FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_ADDR, port) |
+ MT7628_ESW_PCR0_WT_PHY_CMD);
+ if (ret)
+ goto out;
+
+ ret = regmap_read_poll_timeout(esw->regmap, MT7628_ESW_REG_PCR1, val,
+ (val & MT7628_ESW_PCR1_WT_DONE), 10,
+ 5000);
+out:
+ if (ret) {
+ dev_err(ds->dev, "write failed. MDIO timeout?\n");
+ return -ETIMEDOUT;
+ }
+ return ret;
+}
+
+static void mt7628_vendor_phys_init(struct dsa_switch *ds)
+{
+ /* vendor specific init sequence from openwrt/uboot */
+ mt7628_phy_write(ds, 0, 31, 0x2000); /* change G2 page */
+ mt7628_phy_write(ds, 0, 26, 0x0020);
+
+ for (int i = 0; i < 5; i++) {
+ mt7628_phy_write(ds, i, 31, 0x8000); /* change L0 page */
+ mt7628_phy_write(ds, i, 0, 0x3100);
+
+ /* EEE disable */
+ mt7628_phy_write(ds, i, 30, 0xa000);
+ mt7628_phy_write(ds, i, 31, 0xa000); /* change L2 page */
+ mt7628_phy_write(ds, i, 16, 0x0606);
+ mt7628_phy_write(ds, i, 23, 0x0f0e);
+ mt7628_phy_write(ds, i, 24, 0x1610);
+ mt7628_phy_write(ds, i, 30, 0x1f15);
+ mt7628_phy_write(ds, i, 28, 0x6111);
+ mt7628_phy_write(ds, i, 31, 0x2000);
+ mt7628_phy_write(ds, i, 26, 0x0000);
+ }
+
+ /* 100Base AOI setting */
+ mt7628_phy_write(ds, 0, 31, 0x5000); /* change G5 page */
+ mt7628_phy_write(ds, 0, 19, 0x004a);
+ mt7628_phy_write(ds, 0, 20, 0x015a);
+ mt7628_phy_write(ds, 0, 21, 0x00ee);
+ mt7628_phy_write(ds, 0, 22, 0x0033);
+ mt7628_phy_write(ds, 0, 23, 0x020a);
+ mt7628_phy_write(ds, 0, 24, 0x0000);
+ mt7628_phy_write(ds, 0, 25, 0x024a);
+ mt7628_phy_write(ds, 0, 26, 0x035a);
+ mt7628_phy_write(ds, 0, 27, 0x02ee);
+ mt7628_phy_write(ds, 0, 28, 0x0233);
+ mt7628_phy_write(ds, 0, 29, 0x000a);
+ mt7628_phy_write(ds, 0, 30, 0x0000);
+}
+
+static void mt7628_switch_init(struct dsa_switch *ds)
+{
+ struct mt7628_esw *esw = ds->priv;
+ /* undocumented init sequence from openwrt/uboot */
+ regmap_write(esw->regmap, MT7628_ESW_REG_FCT0, 0xC8A07850);
+ regmap_write(esw->regmap, MT7628_ESW_REG_SGC2, 0x00000000);
+
+ regmap_write(
+ esw->regmap, MT7628_ESW_REG_FCT2,
+ FIELD_PREP(MT7628_ESW_FCT2_MC_PER_PORT_TH, 0xc) |
+ FIELD_PREP(MT7628_ESW_FCT2_MUST_DROP_SET_TH, 0x10) |
+ FIELD_PREP(MT7628_ESW_FCT2_MUST_DROP_RLS_TH, 0x12));
+
+ /*
+ * general switch configuration:
+ * 300s aging interval
+ * broadcast storm prevention disabled
+ * max packet length 1536 bytes
+ * disable collision 16 packet abort and late collision abort
+ * use xor48 for address hashing
+ * disable tx backoff
+ * 10 packet back pressure jam
+ * disable was_transmit
+ * jam until BP condition released
+ * 30ms LED flash
+ * rmc tb fault to all ports
+ * unmatched IGMP as broadcast
+ */
+ regmap_write(esw->regmap, MT7628_ESW_REG_SGC,
+ FIELD_PREP(MT7628_ESW_SGC_AGING_INTERVAL, 1) |
+ FIELD_PREP(MT7628_ESW_BC_STORM_PROT, 0) |
+ FIELD_PREP(MT7628_ESW_PKT_MAX_LEN, 0) |
+ MT7628_ESW_DIS_PKT_ABORT |
+ FIELD_PREP(MT7628_ESW_ADDRESS_HASH_ALG, 1) |
+ MT7628_ESW_DISABLE_TX_BACKOFF |
+ FIELD_PREP(MT7628_ESW_BP_JAM_CNT, 10) |
+ FIELD_PREP(MT7628_ESW_DISMIIPORT_WASTX, 0) |
+ FIELD_PREP(MT7628_ESW_BP_MODE, 0b10) |
+ FIELD_PREP(MT7628_ESW_LED_FLASH_TIME, 0) |
+ FIELD_PREP(MT7628_ESW_RMC_RULE, 0) |
+ FIELD_PREP(MT7628_ESW_IP_MULT_RULE, 0));
+
+ regmap_write(esw->regmap, MT7628_ESW_REG_SOCPC,
+ MT7628_ESW_SOCPC_CRC_PADDING |
+ FIELD_PREP(MT7628_ESW_SOCPC_DISUN2CPU,
+ MT7628_ESW_PORTS_CPU) |
+ FIELD_PREP(MT7628_ESW_SOCPC_DISMC2CPU,
+ MT7628_ESW_PORTS_CPU) |
+ FIELD_PREP(MT7628_ESW_SOCPC_DISBC2CPU,
+ MT7628_ESW_PORTS_CPU));
+
+ regmap_set_bits(esw->regmap, MT7628_ESW_REG_FPA2,
+ MT7628_ESW_FPA2_FORCE_RGMII_EN1 |
+ MT7628_ESW_FPA2_FORCE_RGMII_LINK1 |
+ MT7628_ESW_FPA2_AP_EN);
+
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_FPA2,
+ MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE,
+ FIELD_PREP(MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE, 31));
+
+ /* disable all interrupts */
+ regmap_write(esw->regmap, MT7628_ESW_REG_IMR, 0);
+
+ /* enable special tag on CPU port */
+ regmap_write(esw->regmap, MT7628_ESW_REG_SGC2,
+ MT7628_ESW_SGC2_SPECIAL_TAG_EN |
+ FIELD_PREP(MT7628_ESW_SGC2_TX_CPU_TPID_BIT_MAP,
+ MT7628_ESW_PORTS_CPU));
+
+ /* set up switch for double tagging to simulate vlan unawareness */
+ regmap_set_bits(esw->regmap, MT7628_ESW_REG_POC2,
+ MT7628_ESW_POC2_PER_VLAN_UNTAG_EN);
+ regmap_update_bits(
+ esw->regmap, MT7628_ESW_REG_PFC1, MT7628_ESW_PFC1_EN_VLAN,
+ FIELD_PREP(MT7628_ESW_PFC1_EN_VLAN, MT7628_ESW_PORTS_ALL));
+}
+
+static void esw_set_vlan_id(struct mt7628_esw *esw, unsigned vlan, unsigned vid)
+{
+ unsigned s = MT7628_ESW_VLANI_VID_S * (vlan % 2);
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_VLANI(vlan / 2),
+ MT7628_ESW_VLANI_VID_M << s,
+ (vid & MT7628_ESW_VLANI_VID_M) << s);
+}
+
+static void esw_set_pvid(struct mt7628_esw *esw, unsigned port, unsigned pvid)
+{
+ unsigned s = MT7628_ESW_PVIDC_PVID_S * (port % 2);
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_PVIDC(port / 2),
+ MT7628_ESW_PVIDC_PVID_M << s,
+ (pvid & MT7628_ESW_PVIDC_PVID_M) << s);
+}
+
+static void esw_set_vmsc(struct mt7628_esw *esw, unsigned vlan, unsigned msc)
+{
+ unsigned s = MT7628_ESW_VMSC_MSC_S * (vlan % 4);
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_VMSC(vlan / 4),
+ MT7628_ESW_VMSC_MSC_M << s,
+ (msc & MT7628_ESW_VMSC_MSC_M) << s);
+}
+
+static void esw_set_vub(struct mt7628_esw *esw, unsigned vlan, unsigned msc)
+{
+ unsigned s = MT7628_ESW_VUB_S * (vlan % 4);
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_VUB(vlan / 4),
+ MT7628_ESW_VUB_M << s,
+ (msc & MT7628_ESW_VUB_M) << s);
+}
+
+static void mt7628_vlan_sync(struct dsa_switch *ds)
+{
+ struct mt7628_esw *esw = ds->priv;
+ int i;
+ for (i = 0; i < MT7628_NUM_VLANS; i++) {
+ struct mt7628_vlan *vlan = &esw->vlans[i];
+ esw_set_vmsc(esw, i, vlan->members);
+ esw_set_vlan_id(esw, i, vlan->vid);
+ esw_set_vub(esw, i, vlan->untag);
+ }
+
+ for (i = 0; i < ds->num_ports; i++)
+ esw_set_pvid(esw, i, esw->tag_8021q_pvid[i]);
+}
+
+static int mt7628_setup(struct dsa_switch *ds)
+{
+ struct mt7628_esw *esw = ds->priv;
+ reset_control_reset(esw->rst_esw);
+ usleep_range(1000, 2000);
+ reset_control_reset(esw->rst_ephy);
+ usleep_range(1000, 2000);
+ /*
+ * all MMIO reads hang if esw is not out of reset
+ * ephy needs extra time to get out of reset or it ends up misconfigured
+ */
+ mt7628_vendor_phys_init(ds);
+ mt7628_switch_init(ds);
+ rtnl_lock();
+ dsa_tag_8021q_register(ds, htons(ETH_P_8021Q));
+ rtnl_unlock();
+ return 0;
+}
+
+static int mt7628_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct mt7628_esw *esw = ds->priv;
+ regmap_clear_bits(esw->regmap, MT7628_ESW_REG_POC0,
+ FIELD_PREP(MT7628_ESW_POC0_PORT_DISABLE, BIT(port)));
+ return 0;
+}
+
+static void mt7628_port_disable(struct dsa_switch *ds, int port)
+{
+ struct mt7628_esw *esw = ds->priv;
+ regmap_set_bits(esw->regmap, MT7628_ESW_REG_POC0,
+ FIELD_PREP(MT7628_ESW_POC0_PORT_DISABLE, BIT(port)));
+}
+
+static enum dsa_tag_protocol
+mt7628_get_tag_proto(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp)
+{
+ return DSA_TAG_PROTO_MT7628;
+}
+
+static void mt7628_phylink_get_caps(struct dsa_switch *ds, int port,
+ struct phylink_config *config)
+{
+ config->mac_capabilities = MAC_100 | MAC_10;
+ __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces);
+}
+
+static int mt7628_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
+ u16 flags)
+{
+ struct mt7628_esw *esw = ds->priv;
+ struct mt7628_vlan *vlan = NULL;
+
+ for (int i = 0; i < MT7628_NUM_VLANS; i++) {
+ struct mt7628_vlan *check_vlan = &esw->vlans[i];
+ if (!check_vlan->active) {
+ vlan = check_vlan;
+ } else if (check_vlan->vid == vid) {
+ vlan = check_vlan;
+ break;
+ }
+ }
+
+ if (!vlan)
+ return -ENOSPC;
+
+ vlan->vid = vid;
+ vlan->active = true;
+ vlan->members |= BIT(port);
+
+ if (flags & BRIDGE_VLAN_INFO_PVID)
+ esw->tag_8021q_pvid[port] = vid;
+
+ if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
+ vlan->untag |= BIT(port);
+
+ mt7628_vlan_sync(ds);
+ return 0;
+}
+
+static int mt7628_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
+{
+ struct mt7628_esw *esw = ds->priv;
+ struct mt7628_vlan *vlan = NULL;
+
+ for (int i = 0; i < MT7628_NUM_VLANS; i++) {
+ struct mt7628_vlan *check_vlan = &esw->vlans[i];
+ if (check_vlan->active || check_vlan->vid != vid)
+ continue;
+ vlan = check_vlan;
+ break;
+ }
+ if (!vlan)
+ return -ENOENT;
+
+ vlan->members &= ~BIT(port);
+ vlan->untag &= ~BIT(port);
+
+ if (!vlan->members)
+ vlan->active = false;
+
+ mt7628_vlan_sync(ds);
+ return 0;
+}
+
+static struct dsa_switch_ops mt7628_switch_ops = {
+ .get_tag_protocol = mt7628_get_tag_proto,
+ .setup = mt7628_setup,
+ .port_enable = mt7628_port_enable,
+ .port_disable = mt7628_port_disable,
+ .phy_read = mt7628_phy_read,
+ .phy_write = mt7628_phy_write,
+ .phylink_get_caps = mt7628_phylink_get_caps,
+ .tag_8021q_vlan_add = mt7628_dsa_8021q_vlan_add,
+ .tag_8021q_vlan_del = mt7628_dsa_8021q_vlan_del,
+};
+
+static int mt7628_probe(struct platform_device *pdev)
+{
+ struct dsa_switch *ds = NULL;
+ struct mt7628_esw *esw = NULL;
+ struct device *dev = &pdev->dev;
+
+ ds = devm_kzalloc(&pdev->dev, sizeof(*ds), GFP_KERNEL);
+ if (!ds)
+ return -ENOMEM;
+
+ esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL);
+ if (!esw)
+ return -ENOMEM;
+
+ esw->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(esw->base))
+ return PTR_ERR(esw->base);
+
+ esw->regmap = devm_regmap_init_mmio(&pdev->dev, esw->base,
+ &mt7628_esw_regmap_cfg);
+ if (IS_ERR(esw->regmap))
+ return PTR_ERR(esw->regmap);
+
+ esw->rst_ephy = devm_reset_control_get_exclusive(&pdev->dev, "ephy");
+ if (IS_ERR(esw->rst_ephy)) {
+ dev_err(dev, "failed to get EPHY reset: %pe\n", esw->rst_ephy);
+ esw->rst_ephy = NULL;
+ }
+
+ esw->rst_esw = devm_reset_control_get_exclusive(&pdev->dev, "esw");
+ if (IS_ERR(esw->rst_esw)) {
+ dev_err(dev, "failed to get ESW reset: %pe\n", esw->rst_esw);
+ esw->rst_esw = NULL;
+ }
+
+ ds->dev = dev;
+ ds->num_ports = MT7628_ESW_NUM_PORTS;
+ ds->ops = &mt7628_switch_ops;
+ ds->priv = esw;
+ esw->ds = ds;
+ dev_set_drvdata(dev, esw);
+
+ return dsa_register_switch(ds);
+}
+
+static void mt7628_remove(struct platform_device *pdev)
+{
+ struct mt7628_esw *esw = platform_get_drvdata(pdev);
+ if (!esw)
+ return;
+
+ dsa_unregister_switch(esw->ds);
+}
+
+static void mt7628_shutdown(struct platform_device *pdev)
+{
+ struct mt7628_esw *esw = platform_get_drvdata(pdev);
+ if (!esw)
+ return;
+
+ dsa_switch_shutdown(esw->ds);
+ dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static const struct of_device_id mt7628_of_match[] = {
+ {
+ .compatible = "mediatek,mt7628-esw",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, mt7628_of_match);
+
+static struct platform_driver mt7628_driver = {
+ .driver = {
+ .name = "mt7628-esw",
+ .of_match_table = mt7628_of_match,
+ },
+ .probe = mt7628_probe,
+ .remove = mt7628_remove,
+ .shutdown = mt7628_shutdown,
+};
+
+module_platform_driver(mt7628_driver);
+
+MODULE_AUTHOR("Joris Vaisvila <joey@tinyisr.com>");
+MODULE_DESCRIPTION("Driver for Mediatek MT7628 embedded switch");
+MODULE_LICENSE("GPL v2");
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver
2026-02-28 18:52 ` [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver Joris Vaisvila
@ 2026-02-28 20:12 ` Andrew Lunn
2026-03-04 22:16 ` Vladimir Oltean
1 sibling, 0 replies; 12+ messages in thread
From: Andrew Lunn @ 2026-02-28 20:12 UTC (permalink / raw)
To: Joris Vaisvila
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
On Sat, Feb 28, 2026 at 08:52:42PM +0200, Joris Vaisvila wrote:
> Add support for the MT7628 embedded switch's ralink special tag.
>
> The ralink "special tag" is merged with the VLAN TPID when a VLAN is
> appended by the switch.
Does "special" actually add anything useful to the naming? If there a
"normal" tag as well? If you where to see a stack trace with
dsa_user_special_xmit() i would think it was some sort of generic
code. dsa_user_mt7628_xmit() is more meaningful. Especially when some
other vendor thinks their silicon is special and want to use that name
as well.
Andrew
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch
2026-02-28 18:52 ` [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch Joris Vaisvila
@ 2026-02-28 20:16 ` Andrew Lunn
2026-02-28 20:31 ` Andrew Lunn
1 sibling, 0 replies; 12+ messages in thread
From: Andrew Lunn @ 2026-02-28 20:16 UTC (permalink / raw)
To: Joris Vaisvila
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
> +static struct dsa_switch_ops mt7628_switch_ops = {
> + .get_tag_protocol = mt7628_get_tag_proto,
> + .setup = mt7628_setup,
> + .port_enable = mt7628_port_enable,
> + .port_disable = mt7628_port_disable,
> + .phy_read = mt7628_phy_read,
> + .phy_write = mt7628_phy_write,
Although phy_read/phy_write still works, all recent DSA drivers have
declared where own MDIO bus, and called of_mdiobus_register(). That
would be the preferred solution.
Andrew
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch
2026-02-28 18:52 ` [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch Joris Vaisvila
2026-02-28 20:16 ` Andrew Lunn
@ 2026-02-28 20:31 ` Andrew Lunn
2026-03-01 17:23 ` Joris Vaišvila
1 sibling, 1 reply; 12+ messages in thread
From: Andrew Lunn @ 2026-02-28 20:31 UTC (permalink / raw)
To: Joris Vaisvila
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
> +static void mt7628_vendor_phys_init(struct dsa_switch *ds)
> +{
> + /* vendor specific init sequence from openwrt/uboot */
> + mt7628_phy_write(ds, 0, 31, 0x2000); /* change G2 page */
> + mt7628_phy_write(ds, 0, 26, 0x0020);
> +
> + for (int i = 0; i < 5; i++) {
> + mt7628_phy_write(ds, i, 31, 0x8000); /* change L0 page */
> + mt7628_phy_write(ds, i, 0, 0x3100);
> +
> + /* EEE disable */
> + mt7628_phy_write(ds, i, 30, 0xa000);
> + mt7628_phy_write(ds, i, 31, 0xa000); /* change L2 page */
> + mt7628_phy_write(ds, i, 16, 0x0606);
> + mt7628_phy_write(ds, i, 23, 0x0f0e);
> + mt7628_phy_write(ds, i, 24, 0x1610);
> + mt7628_phy_write(ds, i, 30, 0x1f15);
> + mt7628_phy_write(ds, i, 28, 0x6111);
> + mt7628_phy_write(ds, i, 31, 0x2000);
> + mt7628_phy_write(ds, i, 26, 0x0000);
> + }
> +
> + /* 100Base AOI setting */
> + mt7628_phy_write(ds, 0, 31, 0x5000); /* change G5 page */
> + mt7628_phy_write(ds, 0, 19, 0x004a);
> + mt7628_phy_write(ds, 0, 20, 0x015a);
> + mt7628_phy_write(ds, 0, 21, 0x00ee);
> + mt7628_phy_write(ds, 0, 22, 0x0033);
> + mt7628_phy_write(ds, 0, 23, 0x020a);
> + mt7628_phy_write(ds, 0, 24, 0x0000);
> + mt7628_phy_write(ds, 0, 25, 0x024a);
> + mt7628_phy_write(ds, 0, 26, 0x035a);
> + mt7628_phy_write(ds, 0, 27, 0x02ee);
> + mt7628_phy_write(ds, 0, 28, 0x0233);
> + mt7628_phy_write(ds, 0, 29, 0x000a);
> + mt7628_phy_write(ds, 0, 30, 0x0000);
Do you have any idea what this is actually doing?
Normally, a MAC driver would not touch the PHY. It would be the PHY
driver which enables workarounds for erratas etc.
Does this PHY support C45 over C22? Can EEE be disabled in the normal
way, writing to the standard EEE registers?
I would try to put this code into the probe function of a PHY driver.
Andrew
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support
2026-02-28 18:52 [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support Joris Vaisvila
2026-02-28 18:52 ` [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver Joris Vaisvila
2026-02-28 18:52 ` [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch Joris Vaisvila
@ 2026-02-28 23:40 ` Vladimir Oltean
2026-03-01 12:00 ` Daniel Golle
2 siblings, 1 reply; 12+ messages in thread
From: Vladimir Oltean @ 2026-02-28 23:40 UTC (permalink / raw)
To: Joris Vaisvila
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Hi Joris,
On Sat, Feb 28, 2026 at 08:52:40PM +0200, Joris Vaisvila wrote:
> Hello,
>
> This RFC adds initial support for the basic functionality needed to
> operate the MT7628 embedded switch.
>
> The driver configures the switch to isolate each port and does not
> offload any bridge functions, so all of the forwarding is done in
> software. Bridge and VLAN offloading are possible but omitted to
> simplify this initial patch set.
>
> The special tag currently overlaps some functionality with tag_8021q,
> but it enables precise TX handling and (more) precise RX handling for
> future VLAN offloading.
>
> Tested on MT7628AN with 5 100mbps ports. All 5 ports were tested for
> correct forwarding with two devices.
>
> # Request for comments:
>
> - What would be an acceptable way to present the undocumented phy init
> in mt7628_vendor_phys_init and switch_init?
Like Andrew said. Run cat /sys/bus/mdio_bus/devices/.../phy_id for your
probed embedded PHYs, and write a specific PHY driver for that ID in
drivers/net/phy/. With some luck, you may already be using a specific
PHY driver, case in which you might be able to work out what the things
in mt7628_vendor_phys_init() do, based on what doesn't work without them.
What undocumented bits in switch_init? These?
/* undocumented init sequence from openwrt/uboot */
regmap_write(esw->regmap, MT7628_ESW_REG_FCT0, 0xC8A07850);
regmap_write(esw->regmap, MT7628_ESW_REG_SGC2, 0x00000000);
I ran a Google search for MT7628 and found:
https://vonger.cn/upload/MT7628_Full.pdf
It says:
FCT0 (Flow Control Threshold 0)
Bits 31:24 FC_RLS_TH Flow Control Release Threshold
Bits 23:16 FC_SET_TH Flow Control Set Threshold
Bits 15:8 DRO_RLS_TH Drop Release Threshold
Bits 7:0 DROP_SET_TH Drop Set Threshold
SGC2 (Switch Global Control 2)
Bits 31 P6_RXFC_QUE_EN Port 6 RX flow control on per egress queue
Bits 30 P6_TXFC_WL_EN Port 6 TX flow controll by Switch WAN/LAN port
Bits 29:24 LAN_PMAP Lan port bit map
Bits 23 SPECIAL_TAG_EN Special Tag enable
Bits 22:16 TX_CPU_TPID_BIT_MAP Transmit CPU TPID(810x) port bit map
Bits 12 P6_TXFC_QUE_EN Port 6 per queue TX flow control
Bits 11 ARBITER_LAN_EN Memory arbiter only for P0~P4 enable
Bits 10 CPU_TPID_EN CPU TPID(81xx) enable
Bits 9 ARBITER_GPT_EN Memory Arbiter only for P5 and P6
Bits 8 SLOT_4TO1 Memory Arbiter Ratio Selection
Bits 6:0 DOUBLE_TAG_EN Insert double tag field
I don't really understand why you left them "undocumented"?
The PHY registers indeed seem to not be described in that PDF. But maybe
it's an embedded version of a similar discrete PHY for which you can find
documentation (elsewhere).
> - Should I aim to set all register default values for general switch
> config or is relying on the documented switch reset state acceptable?
Too little information to answer the question. We need to know if the
documented out-of-reset state of the switch is reliable. You have more
time spent with the switch, so it's probably a question for you. You
assert two reset domains - rst_esw and rst_ephy. If that is enough to
restore the hardware to the documented states, then all is probably
fine. If not, you'd need to explain what is persistent across resets,
and who else may have touched it (bootloader, kexec etc).
If it was more of a general question - from network maintainers there is
no requirement that you redundantly write all registers with what is
supposed to be their out-of-reset value.
> - I am also looking for any and all feedback on the architecture and
> coding style of this driver.
Oh, this is the fun part :)
I'll start in order with what I think is the most important. I would
love to know more about the tag_8021q and VLAN unaware implementation,
but that may be made irrelevant by something else, so I'll skip it for
now.
I found some references in the documentation to a block named "Switch
DMA (SDM)" starting with base address 10100800. Can you comment on what
purpose this block serves? At a superficial glance, it appears to allow
packet injection into the switch from the embedded MIPS CPU. I wonder,
in that case, whether packet I/O can be done without DSA tags through
those packet rings (case in which a pure switchdev driver might have
been more appropriate than DSA).
I looked at arch/mips/boot/dts/ralink/mt7628a.dtsi and found nothing
Ethernet-related. So it's a bit hard to comment from me whether this is
an instance of mtk_eth_soc.c or something, that is being used as a DSA
conduit. Case in which your architectural choice is fine.
Perhaps a bit more wording in the cover letter and/or commit messages
regarding the general switch block diagram would be nice, in terms that
Linux people already understand.
In terms of coding style there's a few hiccups. I would call out
incorrect alignment of arguments of multi-line functions (they should be
aligned with the open parenthesis, accounting for 8-character wide tabs).
Also, we practice an empty line between local variable declarations and
the beginning of code in a function body.
And the struct platform_driver mt7628_driver definition is indented with
spaces, not tabs, unlike the rest of the code.
And you don't need to put a comma after sentinel entries ("{},"). The
only reason we do commas after terminating array entries is to minimize
the git diff when adding new elements to the array. But with sentinels,
we expect no follow-up by their very definition.
And we practice something called "reverse Christmas tree notation",
where local variable declarations in functions are sorted in the
decreasing order of their line length. No other subsystem except netdev
does that, but we're pretty crazy about it, so yeah, you're not going to
get us stop nagging you about it.
And we don't practice initializing variables with 0 (or NULL) as part of
their definition, when that value is going to be overwritten later in
the function with no reads in between. Examples: "ret", "val" in
mt7628_phy_read(), "ds", "esw" in mt7628_probe(), maybe more.
> Thank you for your time and feedback.
>
> Joris Vaisvila (2):
> net: dsa: initial MT7628 special tag driver
> net: dsa: initial support for MT7628 embedded switch
>
> drivers/net/dsa/Kconfig | 6 +
> drivers/net/dsa/Makefile | 1 +
> drivers/net/dsa/mt7628.c | 567 +++++++++++++++++++++++++++++++++++++++
> include/net/dsa.h | 2 +
> net/dsa/Kconfig | 6 +
> net/dsa/Makefile | 1 +
> net/dsa/tag_mt7628.c | 85 ++++++
> 7 files changed, 668 insertions(+)
> create mode 100644 drivers/net/dsa/mt7628.c
> create mode 100644 net/dsa/tag_mt7628.c
>
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support
2026-02-28 23:40 ` [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support Vladimir Oltean
@ 2026-03-01 12:00 ` Daniel Golle
2026-03-01 12:36 ` Vladimir Oltean
0 siblings, 1 reply; 12+ messages in thread
From: Daniel Golle @ 2026-03-01 12:00 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Joris Vaisvila, netdev, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman
On Sun, Mar 01, 2026 at 01:40:31AM +0200, Vladimir Oltean wrote:
> [...]
> I looked at arch/mips/boot/dts/ralink/mt7628a.dtsi and found nothing
> Ethernet-related. So it's a bit hard to comment from me whether this is
> an instance of mtk_eth_soc.c or something, that is being used as a DSA
> conduit. Case in which your architectural choice is fine.
Yes, the SoC Ethernet is supported by mtk_eth_soc.c, the reason why it
is still missing in mt7628a.dtsi is preceisely because there is no way to
actually use without a DSA/switch driver.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support
2026-03-01 12:00 ` Daniel Golle
@ 2026-03-01 12:36 ` Vladimir Oltean
2026-03-01 13:13 ` Joris Vaišvila
0 siblings, 1 reply; 12+ messages in thread
From: Vladimir Oltean @ 2026-03-01 12:36 UTC (permalink / raw)
To: Daniel Golle
Cc: Joris Vaisvila, netdev, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman
On Sun, Mar 01, 2026 at 12:00:55PM +0000, Daniel Golle wrote:
> On Sun, Mar 01, 2026 at 01:40:31AM +0200, Vladimir Oltean wrote:
> > [...]
> > I looked at arch/mips/boot/dts/ralink/mt7628a.dtsi and found nothing
> > Ethernet-related. So it's a bit hard to comment from me whether this is
> > an instance of mtk_eth_soc.c or something, that is being used as a DSA
> > conduit. Case in which your architectural choice is fine.
>
> Yes, the SoC Ethernet is supported by mtk_eth_soc.c, the reason why it
> is still missing in mt7628a.dtsi is preceisely because there is no way to
> actually use without a DSA/switch driver.
OK.
In that case I would like to know more about how is VLAN unawareness achieved,
with a concrete example.
In the code, this is attributed to:
/* set up switch for double tagging to simulate vlan unawareness */
regmap_set_bits(esw->regmap, MT7628_ESW_REG_POC2,
MT7628_ESW_POC2_PER_VLAN_UNTAG_EN);
which sets bit 15 (PER_VLAN_UNTAG_EN) of Port Control 2 (POC2),
documented as:
VLAN tag removal option.
0: Use per port UNTAG_EN
1: Use untag enable bitmap in VLAN table
I see a slight discrepancy between what the documentation claims the
hardware does, and what the driver claims it does. The documentation
seems to say that setting this bit is what allows the vlan->untag bits
to take effect.
There does exist a DOUBLE_TAG_EN field in SGC2, which this driver
documents as MT7628_ESW_SGC2_DOUBLE_TAG_EN, but doesn't use. Then it
SGC2 to 0, as part of that "undocumented" stuff.
Assuming that was the intended field to be written, here is how it is
documented:
6:0 DOUBLE_TAG_EN Insert double tag field
When this bit is set, the incoming packet is allowed to insert outer or
double tag.
1: enable double tag field
0: disable the double tag field.
(Note: Port5 funciton is only valid when port 5 Giga MAC is implemented)
The documentation is not specific enough for me to understand what it
does, so I'll give an example.
A VLAN-unaware port means that when an ingress packet comes with 802.1Q
TPID 0x8100 and VID X from the wire, this VLAN header will be ignored by
the switch, and the PVID of the port will be pushed as an outer tag, for
any value of X, regardless of whether it is in the VLAN table or not.
Does it do that?
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support
2026-03-01 12:36 ` Vladimir Oltean
@ 2026-03-01 13:13 ` Joris Vaišvila
0 siblings, 0 replies; 12+ messages in thread
From: Joris Vaišvila @ 2026-03-01 13:13 UTC (permalink / raw)
To: Vladimir Oltean
Cc: Daniel Golle, netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Hi Vladimir,
Thank you for the feedback.
On Sun, Mar 01, 2026 at 02:36:19PM +0200, Vladimir Oltean wrote:
> I see a slight discrepancy between what the documentation claims the
> hardware does, and what the driver claims it does. The documentation
> seems to say that setting this bit is what allows the vlan->untag bits
> to take effect.
The comment was referencing an accidentally deleted line that enables
double tagging:
/* set up switch for double tagging to simulate vlan unawareness */
regmap_set_bits(esw->regmap, MT7628_ESW_REG_SGC2,
FIELD_PREP(MT7628_ESW_SGC2_DOUBLE_TAG_EN,
MT7628_ESW_PORTS_NOCPU));
I only re-tested untagged traffic before sending this in. Apologies for
that. I will expand my testing procedures to prevent this from happening
again.
> A VLAN-unaware port means that when an ingress packet comes with 802.1Q
> TPID 0x8100 and VID X from the wire, this VLAN header will be ignored by
> the switch, and the PVID of the port will be pushed as an outer tag, for
> any value of X, regardless of whether it is in the VLAN table or not.
> Does it do that?
That is what is intended to happen and it does do that if the
DOUBLE_TAG_EN is properly set. Will be fixed in later revisions.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch
2026-02-28 20:31 ` Andrew Lunn
@ 2026-03-01 17:23 ` Joris Vaišvila
0 siblings, 0 replies; 12+ messages in thread
From: Joris Vaišvila @ 2026-03-01 17:23 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
Hi Andrew,
Thank you for the review.
> Does "special" actually add anything useful to the naming? If there a
> "normal" tag as well? If you where to see a stack trace with
> dsa_user_special_xmit() i would think it was some sort of generic
> code. dsa_user_mt7628_xmit() is more meaningful. Especially when some
> other vendor thinks their silicon is special and want to use that name
> as well.
The "special tag" is just vendor naming. Will update the patches to
consistently refer to it as the MT7628 tag in v2.
> Although phy_read/phy_write still works, all recent DSA drivers have
> declared where own MDIO bus, and called of_mdiobus_register(). That
> would be the preferred solution.
Will fix in v2.
> Do you have any idea what this is actually doing?
>
> Normally, a MAC driver would not touch the PHY. It would be the PHY
> driver which enables workarounds for erratas etc.
>
> Does this PHY support C45 over C22? Can EEE be disabled in the normal
> way, writing to the standard EEE registers?
>
> I would try to put this code into the probe function of a PHY driver.
The PHY init was inherited from OpenWRT, I am yet to figure out the
specifics of what this is doing. I will investigate further and rework
this into a proper PHY driver for v2.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver
2026-02-28 18:52 ` [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver Joris Vaisvila
2026-02-28 20:12 ` Andrew Lunn
@ 2026-03-04 22:16 ` Vladimir Oltean
1 sibling, 0 replies; 12+ messages in thread
From: Vladimir Oltean @ 2026-03-04 22:16 UTC (permalink / raw)
To: Joris Vaisvila
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman
On Sat, Feb 28, 2026 at 08:52:42PM +0200, Joris Vaisvila wrote:
> +#include <linux/etherdevice.h>
> +#include <linux/dsa/8021q.h>
> +#include <net/dsa.h>
> +
> +#include "tag.h"
> +
> +/*
You have a trailing whitespace here.
> + * MT7628 uses the ralink special tag. It merges the VLAN TPID with source
> + * port ID on RX and target port bitmap on TX.
> + *
> + * The switch forwarding is controlled with VLANs, so each port is put in a
> + * standalone VLAN using tag_8021q. Double tag is enabled to simulate VLAN
> + * unaware ports.
> + *
> + * A VLAN tag is constructed on egress to target the standalone VLAN and port.
> + * The outer VLAN tag is parsed and removed on ingress.
> + */
> +
> +#define MT7628_TAG_NAME "tag_mt7628"
No other protocol name starts with "tag_". This is exported to user
space (cat /sys/class/net/<conduit>/dsa/tagging), it's good to have some
uniformity.
> +
> +#define SPECIAL_HEADER_RECV_SOURCE_PORT_MASK GENMASK(2, 0)
> +#define SPECIAL_TAG_LEN 4
> +
> +static struct sk_buff *dsa_user_special_xmit(struct sk_buff *skb,
> + struct net_device *dev)
> +{
> + struct dsa_port *dp = dsa_user_to_port(dev);
> + u16 xmit_vlan = dsa_tag_8021q_standalone_vid(dp);
> + u8 *special_tag;
> +
> + skb_push(skb, SPECIAL_TAG_LEN);
> + dsa_alloc_etype_header(skb, SPECIAL_TAG_LEN);
> +
> + special_tag = dsa_etype_header_pos_tx(skb);
> +
> + special_tag[0] = ETH_P_8021Q >> 8;
> + special_tag[1] = BIT(dp->index);
We don't do BIT(dp->index) anymore. It's wrapped around a
dsa_xmit_port_mask() helper, which also gives you some HSR TX offloading
if you set the offload pointers to dsa_port_simple_hsr_join() and
dsa_port_simple_hsr_leave().
> +
> + special_tag[2] = xmit_vlan >> 8;
> + special_tag[3] = xmit_vlan & 0xff;
> + return skb;
> +}
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-03-04 22:16 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-28 18:52 [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support Joris Vaisvila
2026-02-28 18:52 ` [RFC PATCH 1/2 net-next] net: dsa: initial MT7628 special tag driver Joris Vaisvila
2026-02-28 20:12 ` Andrew Lunn
2026-03-04 22:16 ` Vladimir Oltean
2026-02-28 18:52 ` [RFC PATCH 2/2 net-next] net: dsa: initial support for MT7628 embedded switch Joris Vaisvila
2026-02-28 20:16 ` Andrew Lunn
2026-02-28 20:31 ` Andrew Lunn
2026-03-01 17:23 ` Joris Vaišvila
2026-02-28 23:40 ` [RFC 0/2 net-next] net: dsa: MT7628 embedded switch initial support Vladimir Oltean
2026-03-01 12:00 ` Daniel Golle
2026-03-01 12:36 ` Vladimir Oltean
2026-03-01 13:13 ` Joris Vaišvila
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox