Add support for TP-LINK AX55 V1

What fixes? Ping @Ansuel

VLAN Question on RTL8367D (chip_id 0x6642)

Hi!

Current status: VLAN is disabled by skipping rtl8365mb_vlan_setup() for 0x6642.

The following errors were thrown during initialization:

[   23.910493] ipq5018-gmac-dwmac 39d00000.ethernet eth0: entered allmulticast mode
[   23.928190] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   23.978547] rtl8365mb-mdio 90000.mdio-1:1d lan4: failed to initialize vlan filtering on this port
[   23.983843] rtl8365mb-mdio 90000.mdio-1:1d lan4: left allmulticast mode
[   23.986414] ipq5018-gmac-dwmac 39d00000.ethernet eth0: left allmulticast mode
[   23.993321] br-lan: port 1(lan4) entered blocking state
[   24.000198] br-lan: port 1(lan4) entered disabled state
[   24.005204] rtl8365mb-mdio 90000.mdio-1:1d lan4: entered allmulticast mode
[   24.010439] ipq5018-gmac-dwmac 39d00000.ethernet eth0: entered allmulticast mode
[   24.087762] rtl8365mb-mdio 90000.mdio-1:1d wan: configuring for phy/gmii link mode
[   24.099566] rtl8365mb-mdio 90000.mdio-1:1d lan1: configuring for phy/gmii link mode
[   24.132037] br-lan: port 2(lan1) entered blocking state
[   24.132114] br-lan: port 2(lan1) entered disabled state
[   24.136179] rtl8365mb-mdio 90000.mdio-1:1d lan1: entered allmulticast mode
[   24.168313] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.218771] rtl8365mb-mdio 90000.mdio-1:1d lan1: failed to initialize vlan filtering on this port
[   24.221495] rtl8365mb-mdio 90000.mdio-1:1d lan1: left allmulticast mode
[   24.227090] br-lan: port 2(lan1) entered blocking state
[   24.233245] br-lan: port 2(lan1) entered disabled state
[   24.238336] rtl8365mb-mdio 90000.mdio-1:1d lan1: entered allmulticast mode
[   24.275297] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.308951] rtl8365mb-mdio 90000.mdio-1:1d lan1: failed to initialize vlan filtering on this port
[   24.312248] rtl8365mb-mdio 90000.mdio-1:1d lan1: left allmulticast mode
[   24.317283] br-lan: port 2(lan1) entered blocking state
[   24.323468] br-lan: port 2(lan1) entered disabled state
[   24.328683] rtl8365mb-mdio 90000.mdio-1:1d lan1: entered allmulticast mode
[   24.345702] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.388589] rtl8365mb-mdio 90000.mdio-1:1d lan1: failed to initialize vlan filtering on this port
[   24.391102] rtl8365mb-mdio 90000.mdio-1:1d lan1: left allmulticast mode
[   24.416190] rtl8365mb-mdio 90000.mdio-1:1d lan2: configuring for phy/gmii link mode
[   24.435762] br-lan: port 2(lan2) entered blocking state
[   24.435833] br-lan: port 2(lan2) entered disabled state
[   24.440013] rtl8365mb-mdio 90000.mdio-1:1d lan2: entered allmulticast mode
[   24.454125] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.498535] rtl8365mb-mdio 90000.mdio-1:1d lan2: failed to initialize vlan filtering on this port
[   24.500489] rtl8365mb-mdio 90000.mdio-1:1d lan2: left allmulticast mode
[   24.506715] br-lan: port 2(lan2) entered blocking state
[   24.512921] br-lan: port 2(lan2) entered disabled state
[   24.518069] rtl8365mb-mdio 90000.mdio-1:1d lan2: entered allmulticast mode
[   24.530408] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.568538] rtl8365mb-mdio 90000.mdio-1:1d lan2: failed to initialize vlan filtering on this port
[   24.570437] rtl8365mb-mdio 90000.mdio-1:1d lan2: left allmulticast mode
[   24.576718] br-lan: port 2(lan2) entered blocking state
[   24.582937] br-lan: port 2(lan2) entered disabled state
[   24.588082] rtl8365mb-mdio 90000.mdio-1:1d lan2: entered allmulticast mode
[   24.600377] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.638536] rtl8365mb-mdio 90000.mdio-1:1d lan2: failed to initialize vlan filtering on this port
[   24.640443] rtl8365mb-mdio 90000.mdio-1:1d lan2: left allmulticast mode
[   24.652280] rtl8365mb-mdio 90000.mdio-1:1d lan3: configuring for phy/gmii link mode
[   24.662297] br-lan: port 2(lan3) entered blocking state
[   24.662349] br-lan: port 2(lan3) entered disabled state
[   24.666393] rtl8365mb-mdio 90000.mdio-1:1d lan3: entered allmulticast mode
[   24.678899] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.718531] rtl8365mb-mdio 90000.mdio-1:1d lan3: failed to initialize vlan filtering on this port
[   24.720451] rtl8365mb-mdio 90000.mdio-1:1d lan3: left allmulticast mode
[   24.726702] br-lan: port 2(lan3) entered blocking state
[   24.732918] br-lan: port 2(lan3) entered disabled state
[   24.738068] rtl8365mb-mdio 90000.mdio-1:1d lan3: entered allmulticast mode
[   24.750451] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.788537] rtl8365mb-mdio 90000.mdio-1:1d lan3: failed to initialize vlan filtering on this port
[   24.790448] rtl8365mb-mdio 90000.mdio-1:1d lan3: left allmulticast mode
[   24.796700] br-lan: port 2(lan3) entered blocking state
[   24.802928] br-lan: port 2(lan3) entered disabled state
[   24.808077] rtl8365mb-mdio 90000.mdio-1:1d lan3: entered allmulticast mode
[   24.820451] rtl8365mb-mdio 90000.mdio-1:1d: VLAN should already exist in VLAN MC
[   24.858549] rtl8365mb-mdio 90000.mdio-1:1d lan3: failed to initialize vlan filtering on this port
[   24.860460] rtl8365mb-mdio 90000.mdio-1:1d lan3: left allmulticast mode
[   25.459019] br-lan: port 2(phy0-ap0) entered blocking state
[   25.459087] br-lan: port 2(phy0-ap0) entered disabled state
[   25.463481] ath11k c000000.wifi phy0-ap0: entered allmulticast mode
[   25.469663] ath11k c000000.wifi phy0-ap0: entered promiscuous mode
[   26.465184] br-lan: port 3(phy1-ap0) entered blocking state
[   26.465256] br-lan: port 3(phy1-ap0) entered disabled state
[   26.469779] ath11k b00a040.wifi phy1-ap0: entered allmulticast mode
[   26.475554] ath11k b00a040.wifi phy1-ap0: entered promiscuous mode
[   27.285128] br-lan: port 3(phy1-ap0) entered blocking state
[   27.285206] br-lan: port 3(phy1-ap0) entered forwarding state
[   30.750462] br-lan: port 2(phy0-ap0) entered blocking state
[   30.750538] br-lan: port 2(phy0-ap0) entered forwarding state

Observations:
LAN works as an L2 switch β€” traffic passes between ports without VLAN filtering.


Questions:

  1. In your build (chip_id 0x6367), does VLAN work natively or is it disabled?

  2. On the RTL8367D, is the VLAN4K table write-only?
    In the GPL vendor driver, a software shadow table (Rtl8367dVirtualVlanTable[4096]) is used.
    To implement full VLAN support in mainline, the following changes are needed:

    • Add a shadow table to struct rtl8365mb.
    • Intercept VLAN MC reads β†’ read from the shadow table.
    • Intercept VLAN MC writes β†’ write to both the shadow table and the hardware.
    • During vlan_init() β€” populate the default VLAN (vid=1) in the shadow table.
  3. How critical is this for the AX55?
    Without VLAN, do LAN/WAN/WiFi still work properly?
    Is VLAN only required for isolation (guest network) and 802.1Q trunking?


Both chips use the same register 0x07A8 for VLAN control (bit 0). However:
RTL8367C β€” hardware VLAN4K table is readable/writable:

setAsicVlan4kEntry writes 3 words via the access table (0x0510-0x0512)

getAsicVlan4kEntry reads back the same 3 words

The driver can verify that the write has been saved
RTL8367D β€” hardware VLAN4K table is write-only:

setAsicVlan4kEntry writes 2 words via the access table

getAsicVlan4kEntry reads from the software shadow table (Rtl8367dVirtualVlanTable), not from hardware

The vendor driver knows that the hardware cannot be read β€” hence it uses a shadow table
In mainline rtl8365mb: there is no shadow table β†’ when reading VLAN entries, it returns 0 β†’ the switch sees an empty table β†’ drops frames.

Here is the translation of your message into English with the patch, code, and logs separated into distinct sections.


I added the patch as shown below. Maybe I did something wrong, I followed your recipe. But here are the logs.
The PC sends data to the router and it reaches it, but for some reason, the router doesn't respond.


PATCH CODE:

Index: linux-6.12.94/drivers/net/dsa/realtek/rtl8365mb_main.c
===================================================================
--- linux-6.12.94.orig/drivers/net/dsa/realtek/rtl8365mb_main.c
+++ linux-6.12.94/drivers/net/dsa/realtek/rtl8365mb_main.c
@@ -97,6 +97,7 @@
 #include <linux/mutex.h>
 #include <linux/of_irq.h>
 #include <linux/regmap.h>
+#include <linux/firmware.h>
 #include <linux/if_bridge.h>
 #include <linux/if_vlan.h>
 
@@ -213,23 +214,16 @@
 #define RTL8365MB_EXT_PORT_MODE_1000X_100FX	11
 #define RTL8365MB_EXT_PORT_MODE_1000X		12
 #define RTL8365MB_EXT_PORT_MODE_100FX		13
-/* Registers used to bring up the (H)SGMII extension interface. The
- * RTL8365MB_MAGIC_REG/RTL8365MB_MAGIC_VALUE, RTL8365MB_CHIP_VER_REG and
- * RTL8365MB_CHIP_RESET_REG defines already exist above and are reused here.
- */
+/* RTL8367D SerDes registers */
 #define RTL8365MB_OPTION_MAGIC_REG		0x13C0
 #define RTL8365MB_OPTION_REG			0x13C1
-
 #define RTL8365MB_MISC_CFG0_REG			0x130C
 #define   RTL8365MB_MISC_CFG0_DW8051_EN_MASK	0x0020
-
 #define   RTL8365MB_CHIP_RESET_DW8051_MASK	0x0010
-
 #define RTL8365MB_DW8051_RDY_REG		0x1336
 #define   RTL8365MB_DW8051_RDY_ACS_IROM_MASK	0x0002
 #define   RTL8365MB_DW8051_RDY_IROM_MSB_MASK	0x0004
 #define RTL8365MB_DW8051_IRAM_BASE		0xE000
-
 #define RTL8365MB_SDS_MISC_REG			0x1D11
 #define   RTL8365MB_SDS_MISC_MAC8_SGMII_MASK	0x0040
 #define   RTL8365MB_SDS_MISC_MAC8_HSGMII_MASK	0x0800
@@ -238,13 +232,23 @@
 #define   RTL8365MB_SDS_MISC_SGMII_FDUP_MASK	0x0400
 #define   RTL8365MB_SDS_MISC_SGMII_LINK_MASK	0x0200
 #define   RTL8365MB_SDS_MISC_SGMII_SPD_MASK	0x0180
-
 #define RTL8365MB_SDS_INDACS_CMD_REG		0x6600
 #define   RTL8365MB_SDS_INDACS_CMD_WRITE	0x00C0
-#define   RTL8365MB_SDS_INDACS_CMD_READ		0x0080
+#define   RTL8365MB_SDS_INDACS_CMD_READ	0x0080
 #define RTL8365MB_SDS_INDACS_ADR_REG		0x6601
 #define RTL8365MB_SDS_INDACS_DATA_REG		0x6602
-
+#define RTL8365MB_SDS_INDACS_CMD_WRITE_D	0x00CD
+#define RTL8365MB_SDS_INDACS_CMD_READ_D	0x008D
+#define RTL8365MB_D_FORCE_LINK_REG(id)		(0x12C5 + (id))
+#define RTL8365MB_D_FORCE_LINK_EN_REG(id)	(0x12CD + (id))
+#define RTL8365MB_D_SDS_MODE_MASK		0x001F
+#define RTL8365MB_D_SDS_MODE_DISABLE		0x1F
+#define RTL8365MB_D_SDS_MODE_SGMII		0x02
+#define RTL8365MB_D_SDS_MODE_HSGMII		0x12
+#define RTL8365MB_D_MAC6_SEL_SDS0		BIT(9)
+#define RTL8365MB_D_PA12PC_EN_S0		BIT(10)
+#define RTL8365MB_D_PA33PC_EN_S0		BIT(11)
+#define RTL8365MB_SGMII_FW_NAME "rtl_switch/rtl8367s-sgmii.bin"
 
 /* External interface mode configuration registers 0~1 */
 #define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0		0x1305 /* EXT0,EXT1 */
@@ -684,6 +688,18 @@ static const struct rtl8365mb_chip_info
 		.jam_table = rtl8365mb_init_jam_8365mb_vc,
 		.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
 	},
+	{
+		.name = "RTL8367D",
+		.chip_id = 0x6642,
+		.chip_ver = 0x0010,
+		.extints = {
+			{ 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) },
+			{ 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) |
+				PHY_INTF(RMII) | PHY_INTF(RGMII) },
+		},
+		.jam_table = rtl8365mb_init_jam_8365mb_vc,
+		.jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc),
+	},
 };
 
 enum rtl8365mb_stp_state {
@@ -1424,6 +1440,119 @@ static int rtl8365mb_ext_config_sgmii(st
 	return 0;
 }
 
+/* RTL8367D SerDes calibration tables from GPL vendor driver */
+static const u16 rtl8365mb_sds_d_sgmii[][2] = {
+	{ 0x1020, 0x0423 }, { 0x5242, 0x0425 }, { 0x4E0C, 0x0427 },
+	{ 0xAA00, 0x0428 }, { 0x031B, 0x0484 },
+	{ 0x1400, 0x0000 }, { 0x1403, 0x0000 },
+};
+static const u16 rtl8365mb_sds_d_hsgmii[][2] = {
+	{ 0x8E11, 0x0421 }, { 0x1020, 0x0423 }, { 0x5242, 0x0425 },
+	{ 0x4E0C, 0x0427 }, { 0xAA00, 0x0428 }, { 0x051B, 0x0504 },
+	{ 0x1400, 0x0000 }, { 0x1403, 0x0000 },
+};
+static int rtl8365mb_sds_indacs_write_d(struct realtek_priv *priv, u16 data,
+					 u16 addr)
+{
+	int ret;
+	ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_DATA_REG, data);
+	if (ret) return ret;
+	ret = regmap_write(priv->map, RTL8365MB_SDS_INDACS_ADR_REG, addr);
+	if (ret) return ret;
+	return regmap_write(priv->map, RTL8365MB_SDS_INDACS_CMD_REG,
+			    RTL8365MB_SDS_INDACS_CMD_WRITE_D);
+}
+static int rtl8365mb_ext_config_sgmii_d(struct realtek_priv *priv, int port,
+					 phy_interface_t interface)
+{
+	const struct rtl8365mb_extint *extint =
+		rtl8365mb_get_port_extint(priv, port);
+	bool hsgmii = interface == PHY_INTERFACE_MODE_2500BASEX;
+	const u16 (*red)[2];
+	size_t red_cnt;
+	int ret;
+	size_t i;
+	u32 val;
+	if (!extint) return -ENODEV;
+	if (extint->id != 1) return -EOPNOTSUPP;
+	ret = regmap_update_bits(priv->map, 0x03F7, BIT(extint->id), 0);
+	if (ret) return ret;
+	ret = regmap_update_bits(priv->map,
+		RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(extint->id), 0xF0, 0);
+	if (ret) return ret;
+	ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
+				 RTL8365MB_D_SDS_MODE_MASK,
+				 RTL8365MB_D_SDS_MODE_DISABLE);
+	if (ret) return ret;
+	ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
+				 RTL8365MB_D_MAC6_SEL_SDS0 |
+				 RTL8365MB_D_PA12PC_EN_S0 |
+				 RTL8365MB_D_PA33PC_EN_S0,
+				 RTL8365MB_D_MAC6_SEL_SDS0 |
+				 RTL8365MB_D_PA12PC_EN_S0 |
+				 RTL8365MB_D_PA33PC_EN_S0);
+	if (ret) return ret;
+	ret = regmap_update_bits(priv->map, 0x13E8, 0xC0, 0x40);
+	if (ret) return ret;
+	ret = regmap_update_bits(priv->map, 0x13EB, 0xF000, 0xE000);
+	if (ret) return ret;
+	if (hsgmii) {
+		red = rtl8365mb_sds_d_hsgmii;
+		red_cnt = ARRAY_SIZE(rtl8365mb_sds_d_hsgmii);
+	} else {
+		red = rtl8365mb_sds_d_sgmii;
+		red_cnt = ARRAY_SIZE(rtl8365mb_sds_d_sgmii);
+	}
+	for (i = 0; i < red_cnt; i++) {
+		ret = rtl8365mb_sds_indacs_write_d(priv, red[i][0], red[i][1]);
+		if (ret) return ret;
+	}
+	ret = regmap_update_bits(priv->map, RTL8365MB_SDS_MISC_REG,
+				 RTL8365MB_D_SDS_MODE_MASK,
+				 hsgmii ? RTL8365MB_D_SDS_MODE_HSGMII :
+					  RTL8365MB_D_SDS_MODE_SGMII);
+	if (ret) return ret;
+	ret = rtl8365mb_sds_indacs_read(priv, 0x0002, &val);
+	if (ret) return ret;
+	val &= ~BIT(9);
+	val |= BIT(8);
+	ret = rtl8365mb_sds_indacs_write_d(priv, val, 0x0002);
+	if (ret) return ret;
+	return 0;
+}
+static int rtl8365mb_ext_config_forcemode_d(struct realtek_priv *priv, int port,
+					    phy_interface_t interface,
+					    bool link, int speed, int duplex,
+					    bool tx_pause, bool rx_pause)
+{
+	const struct rtl8365mb_extint *extint =
+		rtl8365mb_get_port_extint(priv, port);
+	u32 val;
+	int ret;
+	if (!extint) return -ENODEV;
+	val = (link ? (1 << 4) : 0) |
+	      (tx_pause ? (1 << 6) : 0) |
+	      (rx_pause ? (1 << 5) : 0) |
+	      (duplex == DUPLEX_FULL ? (1 << 2) : 0);
+	if (speed == SPEED_2500) {
+		val |= (1 << 12) | (1 << 0);
+		val |= (1 << 2);
+	} else if (speed == SPEED_1000) {
+		val |= (1 << 12) | (1 << 0);
+		val |= (1 << 2);
+	} else if (speed == SPEED_100) {
+		val |= (1 << 12) | (1 << 0);
+	}
+	ret = regmap_write(priv->map,
+			   RTL8365MB_D_FORCE_LINK_REG(extint->id), val);
+	if (ret) return ret;
+	ret = regmap_write(priv->map,
+			   RTL8365MB_D_FORCE_LINK_EN_REG(extint->id),
+			   link ? 0xFFFF : 0);
+	if (ret) return ret;
+	return 0;
+}
+
 static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port,
 				       struct phylink_config *config)
 {
@@ -1490,7 +1619,11 @@ static void rtl8365mb_phylink_mac_config
 
 	if (state->interface == PHY_INTERFACE_MODE_SGMII ||
 	    state->interface == PHY_INTERFACE_MODE_2500BASEX) {
-		ret = rtl8365mb_ext_config_sgmii(priv, port, state->interface);
+		struct rtl8365mb *mb = priv->chip_data;
+		if (mb->chip_info->chip_id == 0x6642)
+			ret = rtl8365mb_ext_config_sgmii_d(priv, port, state->interface);
+		else
+			ret = rtl8365mb_ext_config_sgmii(priv, port, state->interface);
 		if (ret)
 			dev_err(priv->dev,
 				"failed to configure SGMII mode on port %d: %d\n",
@@ -1520,9 +1653,13 @@ static void rtl8365mb_phylink_mac_link_d
 	if (phy_interface_mode_is_rgmii(interface) ||
 	    interface == PHY_INTERFACE_MODE_SGMII ||
 	    interface == PHY_INTERFACE_MODE_2500BASEX) {
-		ret = rtl8365mb_ext_config_forcemode(priv, port, interface,
-						     false, 0, 0,
-						     false, false);
+		struct rtl8365mb *mb = priv->chip_data;
+		if (mb->chip_info->chip_id == 0x6642)
+			ret = rtl8365mb_ext_config_forcemode_d(priv, port, interface,
+							       false, 0, 0, false, false);
+		else
+			ret = rtl8365mb_ext_config_forcemode(priv, port, interface,
+							     false, 0, 0, false, false);
 		if (ret)
 			dev_err(priv->dev,
 				"failed to reset forced mode on port %d: %pe\n",
@@ -1553,10 +1690,15 @@ static void rtl8365mb_phylink_mac_link_u
 	if (phy_interface_mode_is_rgmii(interface) ||
 	    interface == PHY_INTERFACE_MODE_SGMII ||
 	    interface == PHY_INTERFACE_MODE_2500BASEX) {
-		ret = rtl8365mb_ext_config_forcemode(priv, port, interface,
-						     true, speed,
-						     duplex, tx_pause,
-						     rx_pause);
+		struct rtl8365mb *mb = priv->chip_data;
+		if (mb->chip_info->chip_id == 0x6642)
+			ret = rtl8365mb_ext_config_forcemode_d(priv, port, interface,
+							       true, speed, duplex,
+							       tx_pause, rx_pause);
+		else
+			ret = rtl8365mb_ext_config_forcemode(priv, port, interface,
+							     true, speed, duplex,
+							     tx_pause, rx_pause);
 		if (ret)
 			dev_err(priv->dev,
 				"failed to force mode on port %d: %pe\n", port,
@@ -1678,11 +1820,16 @@ static int rtl8365mb_port_vlan_filtering
 	enum rtl8365mb_frame_ingress accepted_frame, prev_accepted_frame;
 	enum rtl8365mb_vlan_egress_mode mode;
 	struct realtek_priv *priv = ds->priv;
+	struct rtl8365mb *mb = priv->chip_data;
 	u32 configured_ports = 0;
 	struct dsa_port *dp;
 	u16 pvid_vid;
 	int ret;
 
+	/* RTL8367D: write-only VLAN4K registers, skip VLAN filtering */
+	if (mb->chip_info->chip_id == 0x6642)
+		return 0;
+
 	dev_dbg(priv->dev, "port %d: %s VLAN filtering\n", port,
 		vlan_filtering ? "enable" : "disable");
 
@@ -1789,8 +1936,13 @@ static int rtl8365mb_port_vlan_add(struc
 	bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID);
 	u16 pvid_vid;
 	struct realtek_priv *priv = ds->priv;
+	struct rtl8365mb *mb = priv->chip_data;
 	int ret;
 
+	/* RTL8367D: skip VLAN add - write-only registers */
+	if (mb->chip_info->chip_id == 0x6642)
+		return 0;
+
 	dev_dbg(priv->dev, "add VLAN %d on port %d, %s, %s\n",
 		vlan->vid, port, untagged ? "untagged" : "tagged",
 		pvid ? "PVID" : "no PVID");
@@ -1850,8 +2002,13 @@ static int rtl8365mb_port_vlan_del(struc
 	bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
 	bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID);
 	struct realtek_priv *priv = ds->priv;
+	struct rtl8365mb *mb = priv->chip_data;
 	int ret;
 
+	/* RTL8367D: skip VLAN del - write-only registers */
+	if (mb->chip_info->chip_id == 0x6642)
+		return 0;
+
 	dev_dbg(priv->dev, "del VLAN %d on port %d, %s, %s\n",
 		vlan->vid, port, untagged ? "untagged" : "tagged",
 		pvid ? "PVID" : "no PVID");
@@ -1899,7 +2056,15 @@ static int rtl8365mb_vlan_setup(struct d
 		}
 	}
 
-	/* VLAN is always enabled. */
+	/* On RTL8367D (0x6642) VLAN4K registers are write-only. Enabling
+	 * global VLAN causes the switch to check membership against an empty
+	 * table and drop all frames. Skip VLAN enable on this chip.
+	 */
+	{
+		struct rtl8365mb *mb = priv->chip_data;
+		if (mb->chip_info->chip_id == 0x6642)
+			return 0;
+	}
 	ret = regmap_update_bits(priv->map, RTL8365MB_VLAN_CTRL_REG,
 				 RTL8365MB_VLAN_CTRL_EN_MASK,
 				 FIELD_PREP(RTL8365MB_VLAN_CTRL_EN_MASK, 1));

LOGS:

root@OpenWrt:~# ethtool -S eth0 | grep -E "p06_if"
     p06_ifInOctets: 0
     p06_ifInUcastPkts: 0
     p06_ifInMulticastPkts: 0
     p06_ifInBroadcastPkts: 0
     p06_ifOutOctets: 2930
     p06_ifOutDiscards: 0
     p06_ifOutUcastPkts: 0
     p06_ifOutMulticastPkts: 7
     p06_ifOutBroadcastPkts: 32

root@OpenWrt:~# ethtool -S eth0 | grep -E "p06_if"
     p06_ifInOctets: 0
     p06_ifInUcastPkts: 0
     p06_ifInMulticastPkts: 0
     p06_ifInBroadcastPkts: 0
     p06_ifOutOctets: 2930
     p06_ifOutDiscards: 0
     p06_ifOutUcastPkts: 0
     p06_ifOutMulticastPkts: 7
     p06_ifOutBroadcastPkts: 32

root@OpenWrt:~# ip neigh show
root@OpenWrt:~# 
[   70.571922] rtl8365mb-mdio 90000.mdio-1:1d lan1: Link is Down
[   70.572121] br-lan: port 1(lan1) entered disabled state
[   73.693291] rtl8365mb-mdio 90000.mdio-1:1d lan1: Link is Up - 1Gbps/Full - flow control rx/tx
[   73.693393] br-lan: port 1(lan1) entered blocking state
[   73.700870] br-lan: port 1(lan1) entered forwarding state

root@OpenWrt:~# ip neigh show
root@OpenWrt:~# ip neigh show
root@OpenWrt:~# brctl show
bridge name     bridge id               STP enabled     interfaces
br-lan          7fff.ac15a2b57580       no              phy1-ap0
                                                        lan4
                                                        lan2
                                                        lan3
                                                        lan1
                                                        phy0-ap0

root@OpenWrt:~# ip link show | grep -E "lan|wan"
3: wan@eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN qlen 1000
4: lan1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-lan state UP qlen 1000
5: lan2@eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue master br-lan state LOWERLAYERDOWN qlen 1000
6: lan3@eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue master br-lan state LOWERLAYERDOWN qlen 1000
7: lan4@eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue master br-lan state LOWERLAYERDOWN qlen 1000
8: br-lan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
9: phy0-ap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-lan state UP qlen 1000
10: phy1-ap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-lan state UP qlen 1000

root@OpenWrt:~# dmesg | grep -i "vlan\|failed"
[    0.000000] psci: [Firmware Bug]: failed to set PC mode: -1
[    1.448615] 8021q: 802.1Q VLAN Support v1.8

root@OpenWrt:~# ping 192.168.1.100
PING 192.168.1.100 (192.168.1.100): 56 data bytes
^C
--- 192.168.1.100 ping statistics ---
6 packets transmitted, 0 packets received, 100% packet loss

root@OpenWrt:~# ethtool -S eth0 | grep -E "p06_if"
     p06_ifInOctets: 0
     p06_ifInUcastPkts: 0
     p06_ifInMulticastPkts: 0
     p06_ifInBroadcastPkts: 0
     p06_ifOutOctets: 5312
     p06_ifOutDiscards: 0
     p06_ifOutUcastPkts: 0
     p06_ifOutMulticastPkts: 10
     p06_ifOutBroadcastPkts: 62

root@OpenWrt:~# ethtool -S eth0 | grep -E "p06_if"
     p06_ifInOctets: 0
     p06_ifInUcastPkts: 0
     p06_ifInMulticastPkts: 0
     p06_ifInBroadcastPkts: 0
     p06_ifOutOctets: 7511
     p06_ifOutDiscards: 0
     p06_ifOutUcastPkts: 0
     p06_ifOutMulticastPkts: 17
     p06_ifOutBroadcastPkts: 72

root@OpenWrt:~# ethtool -S eth0 | grep -E "p06_if"
     p06_ifInOctets: 0
     p06_ifInUcastPkts: 0
     p06_ifInMulticastPkts: 0
     p06_ifInBroadcastPkts: 0
     p06_ifOutOctets: 7511
     p06_ifOutDiscards: 0
     p06_ifOutUcastPkts: 0
     p06_ifOutMulticastPkts: 17
     p06_ifOutBroadcastPkts: 72


root@OpenWrt:~# cat /sys/kernel/debug/regmap/90000.mdio-1\:1d/registers | grep -
E "^(03f7|07a8|12c6|12ce|1305|13e8|13eb|1d11):"
03f7: 0000
07a8: 0000
12c6: 1075
12ce: ffff
1305: c000
13e8: 3040
13eb: e5bb
1d11: 0e12



root@OpenWrt:~# ip neigh show
192.168.1.100 dev br-lan  used 0/0/0 probes 6 FAILED

The write-only VLAN4K table is a great find β€” that explains why the always-on VLAN design of the new driver can't work as-is on the D. Skipping the VLAN ops for 0x6642 is the right interim call; when we fold your chip into the SGMII/HSGMII patch, we can start exactly like that and add a software shadow table later (the vendor DAL proves it's workable). Please share your patch when it's stable.

On the unidirectional traffic β€” that's the exact symptom class I've now debugged twice on the S, so here's the short checklist:

  1. Force-link value: for the 2.5G HSGMII test it must be 0x12c6 = 0x1075 (2500M encoding: speed=0x5 packed as bits[13:12]+[1:0]), not the 0x0076 from my earlier SGMII recipe β€” plus 0x12ce = 0xFFFF. With 1G forced on the MAC against a 2.5G SerDes you get exactly "frames counted out, nothing valid in".

  2. Use the CPU-port FCS counter as your compass β€” it distinguishes the two failure modes. Flood from the PC and watch deltas of dot3StatsFCSErrors vs ifInUcastPkts on the CPU port:

    • FCS rising, ifIn flat β†’ frames DO cross the SerDes but arrive corrupted (misalignment). Pulse the SerDes reset again after everything else is configured (INDACS write SDS reg 0x0000 = 0x1400, then 0x1403) and re-check. I hit a state on the S this week where the SDS status register even reported lock while 100% of frames arrived as FCS garbage β€” the status bit lies, the MIB counters don't.
    • both flat β†’ frames never leave the port toward the CPU: force-link (see 1), port isolation mask, or the MAC6_SEL_SDS0/pad-enable bits from the full bring-up list I posted earlier.
  3. Order matters: SDS mode (0x1d11 bits[4:0] = 0x12) as the last config step, after the jam table; force-link after that.

FWIW I ended up giving the S a runtime "SerDes babysitter" in the driver (poll lock + MIB deltas from the periodic stats work, pulse the SerDes reset on garbage, escalate to a full re-config) because a cold-booted SerDes occasionally comes up broken even with a correct static config. If your D shows the same cold-boot lottery once traffic works, the same medicine should apply.

Maybe even it won't help, because I found out that a full s-like s family has been added to the mb driver, and the d family family has not been added, and now they are doing it, so apparently I'm temporarily in flight, since I'm not very erudite in this(

I asked the developers who are engaged in the mb driver of our real library, so far the answer is unambiguous, what will they do later(

And you're good that you were able to debug the work, s series

Thanks for the update β€” that explains it. The mainline rtl8365mb driver only covers the RTL8367C register family (RTL8365MB-VC, RTL8367S, RTL8367RB-VB). The D-family chips (RTL8367D/S-VB etc.) have a different register map β€” in Realtek's vendor SDK they are handled by a separate rtl8367d sub-driver, and nobody has ported that to mainline DSA yet.

So unfortunately your board variant needs real driver work, not just a DTS change. If you post the exact marking printed on your switch chip, it will help whoever picks this up later.

On our S-series variant everything works now (trunk, WAN/NAT, WiFi), including a fix for cold-boot SerDes lock issues β€” so once a D-family driver exists, the rest of the port should carry over.

It's good news that everything works in the s family, you're good, I'm not strong in this, and I'll wait for the addition of the d series to the main driver's they, photos for reference how the d series looks like

its
RTL8367D chip-id 0x6642 rev 0x0010

@kuncy7 Hello. Thank you for all the hard work. I managed to enable Telnet, but I am unable to log into the root account. It keeps asking me for a password, and I am unable to find it anywhere. I probably missed something, but could you lead me in the right direction? Thank you.

My repack doesn't authenticate at all: I added one line to /etc/rc.local, before exit 0:

telnetd -l /bin/sh &

-l /bin/sh tells busybox telnetd to spawn a shell directly instead of /bin/login, so it drops you straight into a root shell β€” no password, /etc/passwd and /etc/shadow are never consulted.

What's happening on your side is the stock /etc/init.d/telnet, which starts telnetd -l /bin/login. That path does authenticate, and stock ships root with an invalid hash (root:x:... in /etc/shadow), so no password will ever work β€” that's why it keeps asking.

Two ways to fix it, both edited into the rootfs before you repack:

  1. Bypass login (what I do): add telnetd -l /bin/sh & to /etc/rc.local. Simplest for a throwaway install shell.
  2. Give root a real password: in /etc/shadow change the root line's second field from x to an empty string (root::0:0:99999:7:::) β€” empty password β€” or to a known hash. Then stock -l /bin/login telnet will let you in.

Hi,

To enable telnet login without any password, stop using TP link's login binary from sbin. Instead use login.sh (OpenWrt shell wrapper) which should directly drop you into root shell. Unecessary to mess with passwd and other stuffs.

You can see this patch here for enabling telnet. Can confirm this works perfectly -- at least on ax53. Should work for ax55 too.

Also I notice in recent firmwares from tplink, they've started RSA encrypting the firmware to avoid tampering. I found a way to decrypt that -- see here if you're interested.

Cheers.

The first method worked perfectly. The router seems functional. It was basically a paperweight until now. Now, I can use it as a test device to learn stuff. Thanks for your assistance.

Status update on the AX55 v1 port β€” and images to try

Quick summary for everyone following this thread: the TP-Link Archer AX55 v1 port is now a fully functional router on mainline OpenWrt β€” no QSDK, no NSS, no SerDes firmware blob. WAN + NAT + IPv6, 4Γ—LAN over DSA, both Wi-Fi radios (2.4 + 5 GHz) with the correct factory calibration, factory MACs, USB2, and the RTL8367S ↔ SoC link running as a 2.5G HSGMII trunk (faster than stock, which caps it at 1G). The whole thing was brought up without ever soldering a serial console.

Sources, ready-to-flash images and full install/recovery notes:
https://github.com/kuncy7/openwrt-ax55-v1 β€” Release v1.0 (factory.ubi for a first install via mtd write, sysupgrade.bin for updates).

Upstream submission (draft while the switch-driver patches go through netdev):
https://github.com/openwrt/openwrt/pull/24197

Two things worth knowing:

  • Check your switch chip first. This targets v1 with the RTL8367S. Some v1 units shipped an RTL8367D instead, which isn't supported yet β€” on such a board Ethernet would come up dead and Wi-Fi off by default. (v2 is a different SoC entirely.)
  • One open issue we're actively chasing: after the router sits powered off for several hours, the 2.5G trunk can come up degraded on the first boot (a reboot recovers it). It's being worked on the netdev list and the "reduce number of drivers for rtl8367s" thread; the current candidate fix is already in the v1.0 build. Warm reboots and short power-cycles are unaffected.

If you flash it, feedback is very welcome β€” especially anyone who can reproduce (or not reproduce) the cold-boot trunk behaviour on their unit. More units in the wild is exactly what this needs right now. Thanks to everyone who helped get here. :slightly_smiling_face:

Good afternoon. My first experience with OpenWRT was with this router. I managed to install your build. Everything seems to be working fine. One bug I encountered is that when I change the Wi-Fi settings, the Wi-Fi stops working until I reboot the router. Also, I can’t install the WireGuard client, but that might be due to the specifics of your build (I’m still new to OpenWRT). Other than that, I haven’t encountered any critical errors during the 12 hours I’ve been using it. If you need any further feedback, I can provide it to the best of my ability and knowledge. (Again, this is limited by my current understanding of OpenWRT, since I’ve only been using it for literally one day =))