Netdev List
 help / color / mirror / Atom feed
* [RESEND PATCH v1] net: dsa: motorcomm: add yt92xx dsa driver
@ 2026-06-15 11:12 Kyle Switch
  2026-06-15 11:34 ` Andrew Lunn
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Kyle Switch @ 2026-06-15 11:12 UTC (permalink / raw)
  To: mmyangfl, andrew, olteanv, davem, edumazet, kuba, pabeni, horms,
	netdev, linux-kernel
  Cc: ming.xu, xiaolin.xu, jianmin.wang, de.ge

Add yt92xx dsa driver supports yt921x and yt922x switch series.
To support more switch series in the future (e.g., yt923x), the most common configurations are abstracted into switch operation interfaces, due to yt921x and yt922x share similar register layouts and operational logic.

- Merge drivers/net/dsa/yt921x.c and the new yt922x support into a
  unified yt92xx.c driver.

- Add support for yt922x, which can operate with either 4 bytes or
  8 bytes DSA tag.

- Not change yt921x behaviour but use common switch apis

Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
---
 drivers/net/dsa/Kconfig                       |   10 +-
 drivers/net/dsa/Makefile                      |    2 +-
 drivers/net/dsa/motorcomm/Kconfig             |   30 +
 drivers/net/dsa/motorcomm/Makefile            |    6 +
 drivers/net/dsa/motorcomm/switch/Makefile     |   47 +
 drivers/net/dsa/motorcomm/switch/i2c.c        |  121 +
 drivers/net/dsa/motorcomm/switch/i2c.h        |   21 +
 drivers/net/dsa/motorcomm/switch/smi.c        |  100 +
 drivers/net/dsa/motorcomm/switch/smi.h        |   13 +
 drivers/net/dsa/motorcomm/switch/yt_cmm.h     |  103 +
 drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.c | 1146 +++
 drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.h |  345 +
 drivers/net/dsa/motorcomm/switch/yt_drv.c     |   42 +
 drivers/net/dsa/motorcomm/switch/yt_drv.h     |   25 +
 drivers/net/dsa/motorcomm/switch/yt_init.c    |  348 +
 drivers/net/dsa/motorcomm/switch/yt_init.h    |   30 +
 .../net/dsa/motorcomm/switch/yt_interrupt.c   |  369 +
 .../net/dsa/motorcomm/switch/yt_interrupt.h   |  102 +
 .../net/dsa/motorcomm/switch/yt_isolation.c   |  294 +
 .../net/dsa/motorcomm/switch/yt_isolation.h   |  151 +
 drivers/net/dsa/motorcomm/switch/yt_l2.c      | 1075 +++
 drivers/net/dsa/motorcomm/switch/yt_l2.h      |  329 +
 drivers/net/dsa/motorcomm/switch/yt_lag.c     |  216 +
 drivers/net/dsa/motorcomm/switch/yt_lag.h     |  119 +
 drivers/net/dsa/motorcomm/switch/yt_led.c     | 1799 +++++
 drivers/net/dsa/motorcomm/switch/yt_led.h     |  479 ++
 .../net/dsa/motorcomm/switch/yt_loopdetect.c  |  156 +
 .../net/dsa/motorcomm/switch/yt_loopdetect.h  |   60 +
 drivers/net/dsa/motorcomm/switch/yt_mirror.c  |  205 +
 drivers/net/dsa/motorcomm/switch/yt_mirror.h  |   67 +
 .../net/dsa/motorcomm/switch/yt_multicast.c   |  540 ++
 .../net/dsa/motorcomm/switch/yt_multicast.h   |   84 +
 drivers/net/dsa/motorcomm/switch/yt_nic.c     |  270 +
 drivers/net/dsa/motorcomm/switch/yt_nic.h     |  168 +
 drivers/net/dsa/motorcomm/switch/yt_port.c    | 6375 +++++++++++++++++
 drivers/net/dsa/motorcomm/switch/yt_port.h    |  824 +++
 drivers/net/dsa/motorcomm/switch/yt_qos.c     |  425 ++
 drivers/net/dsa/motorcomm/switch/yt_qos.h     |  149 +
 .../net/dsa/motorcomm/switch/yt_reg_921x.h    |  422 ++
 .../net/dsa/motorcomm/switch/yt_reg_922x.h    |  571 ++
 .../net/dsa/motorcomm/switch/yt_reg_923x.h    |  218 +
 drivers/net/dsa/motorcomm/switch/yt_stat.c    |  167 +
 drivers/net/dsa/motorcomm/switch/yt_stat.h    |  242 +
 .../net/dsa/motorcomm/switch/yt_storm_ctrl.c  |  477 ++
 .../net/dsa/motorcomm/switch/yt_storm_ctrl.h  |  183 +
 drivers/net/dsa/motorcomm/switch/yt_stp.c     |  155 +
 drivers/net/dsa/motorcomm/switch/yt_stp.h     |   66 +
 drivers/net/dsa/motorcomm/switch/yt_sys.c     |   37 +
 drivers/net/dsa/motorcomm/switch/yt_sys.h     |   56 +
 drivers/net/dsa/motorcomm/switch/yt_types.h   |  172 +
 drivers/net/dsa/motorcomm/switch/yt_vlan.c    |  604 ++
 drivers/net/dsa/motorcomm/switch/yt_vlan.h    |  361 +
 drivers/net/dsa/motorcomm/yt92xx.c            | 5929 +++++++++++++++
 drivers/net/dsa/motorcomm/yt92xx.h            | 1218 ++++
 drivers/net/dsa/yt921x.c                      | 4982 -------------
 drivers/net/dsa/yt921x.h                      |  977 ---
 include/net/dsa.h                             |    4 +
 include/uapi/linux/if_ether.h                 |    2 +-
 net/dsa/Kconfig                               |    6 +-
 net/dsa/Makefile                              |    2 +-
 net/dsa/tag_yt921x.c                          |  168 -
 net/dsa/tag_yt92xx.c                          |  376 +
 62 files changed, 27899 insertions(+), 6141 deletions(-)
 create mode 100644 drivers/net/dsa/motorcomm/Kconfig
 create mode 100644 drivers/net/dsa/motorcomm/Makefile
 create mode 100644 drivers/net/dsa/motorcomm/switch/Makefile
 create mode 100644 drivers/net/dsa/motorcomm/switch/i2c.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/i2c.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/smi.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/smi.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_cmm.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_drv.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_drv.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_init.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_init.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_interrupt.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_interrupt.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_isolation.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_isolation.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_l2.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_l2.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_lag.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_lag.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_led.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_led.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_loopdetect.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_loopdetect.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_mirror.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_mirror.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_multicast.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_multicast.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_nic.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_nic.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_port.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_port.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_qos.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_qos.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_reg_921x.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_reg_922x.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_reg_923x.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_stat.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_stat.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_storm_ctrl.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_storm_ctrl.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_stp.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_stp.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_sys.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_sys.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_types.h
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_vlan.c
 create mode 100644 drivers/net/dsa/motorcomm/switch/yt_vlan.h
 create mode 100644 drivers/net/dsa/motorcomm/yt92xx.c
 create mode 100644 drivers/net/dsa/motorcomm/yt92xx.h
 delete mode 100644 drivers/net/dsa/yt921x.c
 delete mode 100644 drivers/net/dsa/yt921x.h
 delete mode 100644 net/dsa/tag_yt921x.c
 create mode 100644 net/dsa/tag_yt92xx.c

diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4ab567c5bbaf..7ba5244eeac2 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -88,6 +88,8 @@ source "drivers/net/dsa/xrs700x/Kconfig"
 
 source "drivers/net/dsa/realtek/Kconfig"
 
+source "drivers/net/dsa/motorcomm/Kconfig"
+
 config NET_DSA_RZN1_A5PSW
 	tristate "Renesas RZ/N1 A5PSW Ethernet switch support"
 	depends on OF && (ARCH_RZN1 || COMPILE_TEST)
@@ -158,12 +160,4 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM
 	  This enables support for the Vitesse VSC7385, VSC7388, VSC7395
 	  and VSC7398 SparX integrated ethernet switches, connected over
 	  a CPU-attached address bus and work in memory-mapped I/O mode.
-
-config NET_DSA_YT921X
-	tristate "Motorcomm YT9215 ethernet switch chip support"
-	select NET_DSA_TAG_YT921X
-	select NET_IEEE8021Q_HELPERS if DCB
-	help
-	  This enables support for the Motorcomm YT9215 ethernet switch
-	  chip.
 endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index d2975badffc0..c6b7cd7b606d 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -14,7 +14,6 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
 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-y				+= b53/
 obj-y				+= hirschmann/
 obj-y				+= lantiq/
@@ -27,3 +26,4 @@ obj-y				+= qca/
 obj-y				+= realtek/
 obj-y				+= sja1105/
 obj-y				+= xrs700x/
+obj-y				+= motorcomm/
diff --git a/drivers/net/dsa/motorcomm/Kconfig b/drivers/net/dsa/motorcomm/Kconfig
new file mode 100644
index 000000000000..f40d75e2a3f2
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/Kconfig
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config MOTORCOMM
+	tristate "Motorcomm YT92XX switch family support"
+	depends on NET_DSA
+	select FIXED_PHY
+	help
+	  This driver adds support for Motorcomm switch chips. It supports
+	  YT921X and YT922X switch.
+
+choice
+	prompt "Motorcomm switch series selection"
+	depends on MOTORCOMM
+
+config MOTORCOMM_YT921X
+	bool "Motorcomm YT921X series."
+
+config MOTORCOMM_YT922X
+	bool "Motorcomm YT922X series."
+endchoice
+
+config NET_DSA_MOTORCOMM
+	tristate "Motorcomm YT92XX switch DSA driver"
+	depends on MOTORCOMM
+	depends on (MOTORCOMM_YT921X || MOTORCOMM_YT922X)
+	select NET_DSA_TAG_MOTORCOMM
+	select NET_IEEE8021Q_HELPERS if DCB
+	help
+	  Select to enable support for Motorcomm driver.
+
diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
new file mode 100644
index 000000000000..6323a257fea0
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_NET_DSA_MOTORCOMM)	+= dsa_motorcomm.o
+dsa_motorcomm-y += \
+	yt92xx.o
+obj-y				+= switch/
diff --git a/drivers/net/dsa/motorcomm/switch/Makefile b/drivers/net/dsa/motorcomm/switch/Makefile
new file mode 100644
index 000000000000..72f26c520589
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/Makefile
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_MOTORCOMM_YT921X)	+= chip_yt92xx.o
+chip_yt92xx-y += \
+	yt_port.o \
+	i2c.o \
+	smi.o \
+	yt_ctrlpkt.o \
+	yt_drv.o \
+	yt_init.o \
+	yt_interrupt.o \
+	yt_isolation.o \
+	yt_l2.o \
+	yt_led.o \
+	yt_lag.o \
+	yt_loopdetect.o \
+	yt_mirror.o \
+	yt_multicast.o \
+	yt_nic.o \
+	yt_stat.o \
+	yt_storm_ctrl.o \
+	yt_stp.o \
+	yt_sys.o \
+	yt_vlan.o \
+	yt_qos.o
+obj-$(CONFIG_MOTORCOMM_YT922X)	+= chip_yt92xx.o
+chip_yt92xx-y += \
+	yt_port.o \
+	i2c.o \
+	smi.o \
+	yt_ctrlpkt.o \
+	yt_drv.o \
+	yt_init.o \
+	yt_interrupt.o \
+	yt_isolation.o \
+	yt_l2.o \
+	yt_led.o \
+	yt_lag.o \
+	yt_loopdetect.o \
+	yt_mirror.o \
+	yt_multicast.o \
+	yt_nic.o \
+	yt_stat.o \
+	yt_storm_ctrl.o \
+	yt_stp.o \
+	yt_sys.o \
+	yt_vlan.o \
+	yt_qos.o
diff --git a/drivers/net/dsa/motorcomm/switch/i2c.c b/drivers/net/dsa/motorcomm/switch/i2c.c
new file mode 100644
index 000000000000..89c67e33d3cb
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/i2c.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include <linux/types.h>
+#include "i2c.h"
+
+/* i2c_cb_write and i2c_cb_read code need user to complete */
+static u32 i2c_cb_write(u8 *addr, u32 len, u8 *val, u32 val_len)
+{
+	return 0;
+}
+
+static u32 i2c_cb_read(u8 *addr, u32 len, u8 *val, u32 val_len)
+{
+	return 0;
+}
+
+static bool calc_parity(u32 data)
+{
+	u32 len = sizeof(data) * 8;
+	bool parity = 0;
+	u32 i;
+
+	for (i = 0; i < len; i++) {
+		if (data & 0x1)
+			parity = !parity;
+		data >>= 1;
+	}
+
+	return parity;
+}
+
+static int word_to_bytes(u32 data, u8 *out, u8 len, bool is_addr,
+			 bool bigendian)
+{
+	u32 i;
+	bool parity;
+
+	CMM_PARAM_CHK((!out || len < sizeof(data)), CMM_ERR_INPUT);
+
+	for (i = 0; i < UINT32_BYTES_NUM; i++) {
+		if (bigendian)
+			out[i] = (data >> ((UINT32_BYTES_NUM - i - 1) * 8)) &
+				 0xff;
+		else
+			out[i] = (data >> (i * 8)) & 0xff;
+	}
+
+	if (is_addr) {
+		parity = calc_parity(data & 0xfffffffc);
+		if (bigendian)
+			out[UINT32_BYTES_NUM - 1] =
+				(out[UINT32_BYTES_NUM - 1] & 0xfc) + parity;
+		else
+			out[0] = (out[0] & 0xfc) + parity;
+	}
+
+	return CMM_ERR_OK;
+}
+
+static int bytes_to_word(u8 *data, u32 len, u32 *val, bool bigendian)
+{
+	u32 i;
+
+	CMM_PARAM_CHK((!data || !val || len > sizeof(*val)), CMM_ERR_INPUT);
+
+	*val = 0;
+	for (i = 0; i < len; i++) {
+		if (bigendian)
+			*val |= data[i] << ((len - i - 1) * 8);
+		else
+			*val |= data[i] << (i * 8);
+	}
+
+	return CMM_ERR_OK;
+}
+
+u32 i2c_switch_write(u8 unit, u32 addr, u32 val)
+{
+	enum yt_i2c_mode i2c = YT_I2C_SIMPLE;
+	bool isbigendian = (i2c == YT_I2C_SIMPLE) ? 0 : 1;
+	u8 address[UINT32_BYTES_NUM];
+	u8 value[UINT32_BYTES_NUM];
+	int ret;
+
+	word_to_bytes(addr, address, sizeof(address), 1, isbigendian);
+	isbigendian = (i2c == YT_I2C_STD_MSB) ? 1 : 0;
+	word_to_bytes(val, value, sizeof(value), 0, isbigendian);
+	CMM_ERR_CHK(i2c_cb_write(address, sizeof(address), value,
+				 sizeof(value)),
+		    ret);
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(i2c_switch_write);
+
+u32 i2c_switch_read(u8 unit, u32 addr, u32 *val)
+{
+	enum yt_i2c_mode i2c = YT_I2C_SIMPLE;
+	bool isbigendian = (i2c == YT_I2C_SIMPLE) ? 0 : 1;
+	u8 address[UINT32_BYTES_NUM] = { 0 };
+	u8 value[UINT32_BYTES_NUM] = { 0 };
+	int ret;
+
+	CMM_PARAM_CHK(!val, CMM_ERR_NULL_POINT);
+
+	word_to_bytes(addr, address, sizeof(address), 1, isbigendian);
+	CMM_ERR_CHK(i2c_cb_read(address, sizeof(address), value, sizeof(value)),
+		    ret);
+
+	isbigendian = (i2c == YT_I2C_STD_MSB) ? 1 : 0;
+	CMM_ERR_CHK(bytes_to_word(value, sizeof(value), val, isbigendian), ret);
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(i2c_switch_read);
+
diff --git a/drivers/net/dsa/motorcomm/switch/i2c.h b/drivers/net/dsa/motorcomm/switch/i2c.h
new file mode 100644
index 000000000000..ba20a7d19a7f
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/i2c.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __CTRLIF_I2C_H__
+#define __CTRLIF_I2C_H__
+#include "yt_cmm.h"
+
+#define UINT32_BYTES_NUM (sizeof(u32))
+
+enum yt_i2c_mode {
+	YT_I2C_STD_MSB = 1,
+	YT_I2C_STD_LSB,
+	YT_I2C_SIMPLE,
+};
+
+u32 i2c_switch_write(u8 unit, u32 addr, u32 val);
+u32 i2c_switch_read(u8 unit, u32 addr, u32 *val);
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/smi.c b/drivers/net/dsa/motorcomm/switch/smi.c
new file mode 100644
index 000000000000..6bd1b25c1eb2
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/smi.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "smi.h"
+
+/* smi_cl22_write and smi_cl22_read code need user to complete */
+static int smi_cl22_write(u8 phy, u8 reg, u16 val)
+{
+	return 0;
+}
+
+static int smi_cl22_read(u8 phy, u8 reg, u16 *val)
+{
+	return 0;
+}
+
+/* SMI format */
+#define REG_ADDR_BIT1_ADDR 0
+#define REG_ADDR
+#define REG_ADDR_BIT1_DATA 1
+#define REG_ADDR_BIT0_WRITE 0
+#define REG_ADDR_BIT0_READ 1
+#define PHYADDR 0x1D /*base on Hardware Switch Phyaddr*/
+
+u32 smi_switch_write(u8 unit, u32 reg_addr, u32 reg_value)
+{
+	u8 switchid;
+	u16 val;
+	u8 phy;
+	u8 reg;
+
+	phy = PHYADDR;
+	switchid = unit;
+	reg = (switchid << 2) | (REG_ADDR_BIT1_ADDR << 1) |
+	      (REG_ADDR_BIT0_WRITE);
+	/* Set reg_addr[31:16] */
+	val = (reg_addr >> 16) & 0xffff;
+	smi_cl22_write(phy, reg, val);
+
+	/* Set reg_addr[15:0] */
+	val = reg_addr & 0xffff;
+	smi_cl22_write(phy, reg, val);
+
+	/* Write Data [31:16] out */
+	reg = (switchid << 2) | (REG_ADDR_BIT1_DATA << 1) |
+	      (REG_ADDR_BIT0_WRITE);
+	val = (reg_value >> 16) & 0xffff;
+	smi_cl22_write(phy, reg, val);
+
+	/* Write Data [15:0] out */
+	val = reg_value & 0xffff;
+	smi_cl22_write(phy, reg, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(smi_switch_write);
+
+u32 smi_switch_read(u8 unit, u32 reg_addr, u32 *reg_value)
+{
+	u8 switchid;
+	u32 rdata;
+	u16 val;
+	u8 phy;
+	u8 reg;
+
+	phy = PHYADDR;
+	switchid = unit;
+	reg = (switchid << 2) | (REG_ADDR_BIT1_ADDR << 1) |
+	      (REG_ADDR_BIT0_READ);
+	/* Set reg_addr[31:16] */
+	val = (reg_addr >> 16) & 0xffff;
+	smi_cl22_write(phy, reg, val); /*change to platform smi write*/
+
+	/* Set reg_addr[15:0] */
+	val = reg_addr & 0xffff;
+	smi_cl22_write(phy, reg, val);
+
+	reg = (switchid << 2) | (REG_ADDR_BIT1_DATA << 1) |
+	      (REG_ADDR_BIT0_READ);
+	/* Read Data [31:16] */
+	val = 0x0;
+	smi_cl22_read(phy, reg, &val); /*change to platform smi read*/
+	rdata = (u32)(val << 16);
+
+	/* Read Data [15:0] */
+	val = 0x0;
+	smi_cl22_read(phy, reg, &val);
+
+	rdata |= val;
+
+	*reg_value = rdata;
+
+	return 0;
+}
+EXPORT_SYMBOL(smi_switch_read);
+
diff --git a/drivers/net/dsa/motorcomm/switch/smi.h b/drivers/net/dsa/motorcomm/switch/smi.h
new file mode 100644
index 000000000000..cd9b44e36c0b
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/smi.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __CTRLIF_SMI_H__
+#define __CTRLIF_SMI_H__
+#include "yt_types.h"
+
+u32 smi_switch_write(u8 unit, u32 reg_addr, u32 reg_value);
+u32 smi_switch_read(u8 unit, u32 reg_addr, u32 *reg_value);
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_cmm.h b/drivers/net/dsa/motorcomm/switch/yt_cmm.h
new file mode 100644
index 000000000000..4f0ed39b8c3a
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_cmm.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_CMM_H
+#define __YT_CMM_H
+
+#include "yt_types.h"
+#include "yt_drv.h"
+#include "yt_reg_921x.h"
+#include "yt_reg_922x.h"
+#include "yt_reg_923x.h"
+
+#define CMM_PARAM_CHK(expr, err_code)    \
+	do {                             \
+		if ((u32)(expr)) {       \
+			return err_code; \
+		}                        \
+	} while (0)
+
+#define CMM_ERR_CHK(op, ret)           \
+	do {                           \
+		ret = (op);            \
+		if (ret != CMM_ERR_OK) \
+			return ret;    \
+	} while (0)
+
+#define HAL_MEM_DIRECT_READ(unit, memaddr, pvalue) \
+	yt_drv_switch_reg_read(unit, memaddr, pvalue)
+#define HAL_MEM_DIRECT_WRITE(unit, memaddr, value) \
+	yt_drv_switch_reg_write(unit, memaddr, value)
+
+#define GET_FIELD(value, low_bit, width) \
+	(((value) >> (low_bit)) & ((1U << (width)) - 1))
+#define CLR_FIELD(value, low_bit, width) \
+	((value) & (~(((1U << (width)) - 1) << (low_bit))))
+
+#define HAL_FIELD_SET(low_bit, width, entry, data)                    \
+	do {                                                          \
+		*(entry) &= (~(((1UL << (width)) - 1) << (low_bit))); \
+		*(entry) |= ((data) << (low_bit));                    \
+	} while (0)
+
+#define HAL_FIELD_GET(low_bit, width, entry, pdata) \
+	(*(pdata) = (((*(entry)) >> (low_bit)) & ((1UL << (width)) - 1)))
+
+#define CAL_YTP_TO_MAC(unit, port) ((void)(unit), (port))
+#define CAL_MAC_TO_YTP(unit, macid, ytport) ((void)(unit), ytport = macid)
+
+#define CMM_CLEAR_MEMBER_PORT(portmask)                                  \
+	do {                                                             \
+		u8 u1_pos;                                               \
+		for (u1_pos = 0; u1_pos < YT_PORTS_WORD_NUM; u1_pos++) { \
+			portmask.portbits[u1_pos] = 0;                   \
+		}                                                        \
+	} while (0)
+
+#define CMM_SET_MEMBER_PORT(portmask, port)                               \
+	do {                                                              \
+		u8 u1Int32Pos;                                            \
+		u8 u1BitPos;                                              \
+		u1Int32Pos = (u8)(port / YT_PORTS_WORD_SIZE);             \
+		u1BitPos = (u8)(port % YT_PORTS_WORD_SIZE);               \
+		if (u1Int32Pos < YT_PORTS_WORD_NUM)                       \
+			portmask.portbits[u1Int32Pos] |= (1 << u1BitPos); \
+	} while (0)
+
+#define CMM_IS_MEMBER_PORT(portmask, port, result)                          \
+	do {                                                                \
+		u8 u1Int32Pos;                                              \
+		u8 u1BitPos;                                                \
+		u1Int32Pos = (u8)(port / YT_PORTS_WORD_SIZE);               \
+		u1BitPos = (u8)(port % YT_PORTS_WORD_SIZE);                 \
+		if (u1Int32Pos < YT_PORTS_WORD_NUM &&                     \
+		    (portmask.portbits[u1Int32Pos] & (1 << u1BitPos)) != 0) \
+			result = TRUE;                                      \
+		else                                                        \
+			result = FALSE;                                     \
+	} while (0)
+
+/* convert yt portmask to mac portmask */
+#define CAL_YTPLIST_TO_MLIST(unit, portlist, maclist) \
+	((void)(unit), (maclist.portbits[0] = portlist.portbits[0]))
+
+/* convert mac portmask to yt portmask */
+#define CAL_MLIST_TO_YTPLIST(unit, maclist, portlist) \
+	((void)(unit), (portlist.portbits[0] = maclist.portbits[0]))
+
+#define IS_BIT_SET(value, bit) (((value) >> (bit)) & 1l)
+#define SET_BIT(value, bit) ((value) |= (1l << (bit)))
+#define GET_BIT(value, bit) (((value) >> (bit)) & 1l)
+#define CLEAR_BIT(value, bit) ((value) &= (~(1l << (bit))))
+
+#ifndef BIT
+#define BIT(nr) (1UL << (nr))
+#endif
+
+#define CMM_UNUSED_PARAM(x) ((void)(x))
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.c b/drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.c
new file mode 100644
index 000000000000..8b6293432e4e
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.c
@@ -0,0 +1,1146 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_ctrlpkt.h"
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+static u32 yt_ctrlpkt_act_mask_get(u32 macid, enum yt_act_type act_type,
+				   u32 *copy_mask_ptr, u32 *drop_mask_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!copy_mask_ptr || !drop_mask_ptr,
+		      CMM_ERR_NULL_POINT);
+	if (act_type == ACT_TYPE_FWD) {
+		*copy_mask_ptr = (u32)CLR_FIELD((*copy_mask_ptr), macid, 1);
+		*drop_mask_ptr = (u32)CLR_FIELD((*drop_mask_ptr), macid, 1);
+	} else if (act_type == ACT_TYPE_TRAP) {
+		*copy_mask_ptr |= (u32)(1U << macid);
+		*drop_mask_ptr |= (u32)(1U << macid);
+	} else if (act_type == ACT_TYPE_DROP) {
+		*copy_mask_ptr = (u32)CLR_FIELD((*copy_mask_ptr), macid, 1);
+		*drop_mask_ptr |= (u32)(1U << macid);
+	} else if (act_type == ACT_TYPE_COPY) {
+		*copy_mask_ptr |= (u32)(1U << macid);
+		*drop_mask_ptr = (u32)CLR_FIELD((*drop_mask_ptr), macid, 1);
+	} else {
+		return CMM_ERR_INPUT;
+	}
+
+	return ret;
+}
+
+u32 yt_ctrlpkt_act_set(u8 unit, u32 port, enum yt_ctrlpkt_type pkt_type,
+		       enum yt_act_type act_type)
+{
+	u32 copy_mask = 0;
+	u32 drop_mask = 0;
+	u32 entry = 0;
+	u32 macid = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+					(unit, L2_ARP_PER_PORT_CTRL,
+					&entry), ret);
+		HAL_FIELD_GET(0, 11, &entry, &copy_mask);
+		HAL_FIELD_GET(11, 11, &entry, &drop_mask);
+		CMM_ERR_CHK(yt_ctrlpkt_act_mask_get(macid, act_type, &copy_mask,
+						    &drop_mask),
+			    ret);
+		HAL_FIELD_SET(0, 11, &entry, copy_mask);
+		HAL_FIELD_SET(11, 11, &entry, drop_mask);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ARP_PER_PORT_CTRL, entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_ND_PER_PORT_CTRL,
+						&entry),
+			    ret);
+		HAL_FIELD_GET(0, 11, &entry, &copy_mask);
+		HAL_FIELD_GET(11, 11, &entry, &drop_mask);
+		CMM_ERR_CHK(yt_ctrlpkt_act_mask_get(macid, act_type, &copy_mask,
+						    &drop_mask),
+			    ret);
+		HAL_FIELD_SET(0, 11, &entry, copy_mask);
+		HAL_FIELD_SET(11, 11, &entry, drop_mask);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_ND_PER_PORT_CTRL,
+						 entry),
+			    ret);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_LLDP_PER_PORT_CTRL,
+						&entry),
+			    ret);
+		HAL_FIELD_GET(0, 11, &entry, &copy_mask);
+		HAL_FIELD_GET(11, 11, &entry, &drop_mask);
+		CMM_ERR_CHK(yt_ctrlpkt_act_mask_get(macid, act_type, &copy_mask,
+						    &drop_mask),
+			    ret);
+		HAL_FIELD_SET(0, 11, &entry, copy_mask);
+		HAL_FIELD_SET(11, 11, &entry, drop_mask);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_LLDP_PER_PORT_CTRL,
+						 entry),
+			    ret);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_EEE_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_LLDP_EEE_PER_PORT_CTRL,
+						&entry),
+			    ret);
+		HAL_FIELD_GET(0, 11, &entry, &copy_mask);
+		HAL_FIELD_GET(11, 11, &entry, &drop_mask);
+		CMM_ERR_CHK(yt_ctrlpkt_act_mask_get(macid, act_type, &copy_mask,
+						    &drop_mask),
+			    ret);
+		HAL_FIELD_SET(0, 11, &entry, copy_mask);
+		HAL_FIELD_SET(11, 11, &entry, drop_mask);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_LLDP_EEE_PER_PORT_CTRL, entry), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_act_set);
+
+u32 yt_ctrlpkt_act_get(u8 unit, u32 port, enum yt_ctrlpkt_type pkt_type,
+		       enum yt_act_type *act_type_ptr)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	u32 copy_mask = 0;
+	u32 drop_mask = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ARP_PER_PORT_CTRL, &entry), ret);
+		HAL_FIELD_GET(0, 11, &entry, &copy_mask);
+		HAL_FIELD_GET(11, 11, &entry, &drop_mask);
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_ND_PER_PORT_CTRL,
+						&entry),
+			    ret);
+		HAL_FIELD_GET(0, 11, &entry, &copy_mask);
+		HAL_FIELD_GET(11, 11, &entry, &drop_mask);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_LLDP_PER_PORT_CTRL,
+						&entry), ret);
+		HAL_FIELD_GET(0, 11, &entry, &copy_mask);
+		HAL_FIELD_GET(11, 11, &entry, &drop_mask);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_EEE_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_LLDP_EEE_PER_PORT_CTRL,
+						&entry),
+			    ret);
+		HAL_FIELD_GET(0, 11, &entry, &copy_mask);
+		HAL_FIELD_GET(11, 11, &entry, &drop_mask);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	if (copy_mask & (1UL << macid)) {
+		if (drop_mask & (1UL << macid))
+			*act_type_ptr = ACT_TYPE_TRAP;
+		else
+			*act_type_ptr = ACT_TYPE_COPY;
+	} else {
+		if (drop_mask & (1UL << macid))
+			*act_type_ptr = ACT_TYPE_DROP;
+		else
+			*act_type_ptr = ACT_TYPE_FWD;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_act_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+u32 yt_ctrlpkt_act_set(u8 unit, u32 port, enum yt_ctrlpkt_type pkt_type,
+		       enum yt_act_type act_type)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_ARP_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_ARP_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ND_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_SET(L2_ND_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_ND_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ND_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_LLDP_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_LLDP_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ERPS_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ERPS_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_ERPS_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_ERPS_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ERPS_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_RRPP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_RRPP_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_SET(L2_RRPP_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_RRPP_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_RRPP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_IGMP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_SET(L2_IGMP_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_IGMP_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_MLD_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_SET(L2_MLD_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_MLD_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_PIM_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_PIM_PER_PORT_CTRLN(macid), &entry),
+			 ret);
+		HAL_FIELD_SET(L2_PIM_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_PIM_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_PIM_PER_PORT_CTRLN(macid), entry), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_act_set);
+
+u32 yt_ctrlpkt_act_get(u8 unit, u32 port, enum yt_ctrlpkt_type pkt_type,
+		       enum yt_act_type *act_type_ptr)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	enum yt_act_type get_act_type = ACT_TYPE_FWD;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!act_type_ptr, CMM_ERR_NULL_POINT);
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_GET(L2_ARP_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_ARP_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      &get_act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ND_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_GET(L2_ND_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_ND_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      &get_act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ND_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_GET(L2_LLDP_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_LLDP_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      &get_act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ERPS_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+						L2_ERPS_PER_PORT_CTRLN(macid),
+						&entry), ret);
+		HAL_FIELD_GET(L2_ERPS_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_ERPS_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      &get_act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ERPS_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_RRPP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+						L2_RRPP_PER_PORT_CTRLN(macid),
+						&entry), ret);
+		HAL_FIELD_GET(L2_RRPP_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_RRPP_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      &get_act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_RRPP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_IGMP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_GET(L2_IGMP_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_IGMP_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      &get_act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_MLD_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_GET(L2_MLD_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_MLD_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      &get_act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_PIM_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_PIM_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_GET(L2_PIM_PORT_CTRLN_ACTION_LOWBIT,
+			      L2_PIM_PORT_CTRLN_ACTION_WIDTH, &entry,
+			      &get_act_type);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_PIM_PER_PORT_CTRLN(macid), entry), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	*act_type_ptr = get_act_type;
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_act_get);
+
+u32 yt_ctrlpkt_mymac_act_set(u8 unit,
+			     struct yt_ctrlpkt_mymac_act *ctrlpkt_mymac_act_ptr)
+{
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!ctrlpkt_mymac_act_ptr, CMM_ERR_NULL_POINT);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MAC_TO_ME_CTRL, &entry), ret);
+	HAL_FIELD_SET(MAC_TO_ME_CTRL_MGMT_VID_LOWBIT,
+		      MAC_TO_ME_CTRL_MGMT_VID_WIDTH, &entry,
+		      ctrlpkt_mymac_act_ptr->manage_vid);
+	HAL_FIELD_SET(MAC_TO_ME_CTRL_ACTION_LOWBIT, MAC_TO_ME_CTRL_ACTION_WIDTH,
+		      &entry, ctrlpkt_mymac_act_ptr->action);
+	HAL_FIELD_SET(MAC_TO_ME_CTRL_VALID_LOWBIT, MAC_TO_ME_CTRL_VALID_WIDTH,
+		      &entry, ctrlpkt_mymac_act_ptr->valid);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, MAC_TO_ME_CTRL, entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_mymac_act_set);
+
+u32 yt_ctrlpkt_mymac_act_get(u8 unit,
+			     struct yt_ctrlpkt_mymac_act *ctrlpkt_mymac_act_ptr)
+{
+	u16 get_manage_vid = 0;
+	enum yt_act_type get_action = ACT_TYPE_FWD;
+	u32 enable = 0;
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!ctrlpkt_mymac_act_ptr, CMM_ERR_NULL_POINT);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MAC_TO_ME_CTRL, &entry), ret);
+	HAL_FIELD_GET(MAC_TO_ME_CTRL_MGMT_VID_LOWBIT,
+		      MAC_TO_ME_CTRL_MGMT_VID_WIDTH, &entry, &get_manage_vid);
+	HAL_FIELD_GET(MAC_TO_ME_CTRL_ACTION_LOWBIT, MAC_TO_ME_CTRL_ACTION_WIDTH,
+		      &entry, &get_action);
+	HAL_FIELD_GET(MAC_TO_ME_CTRL_VALID_LOWBIT, MAC_TO_ME_CTRL_VALID_WIDTH,
+		      &entry, &enable);
+	ctrlpkt_mymac_act_ptr->action = get_action;
+	ctrlpkt_mymac_act_ptr->manage_vid = get_manage_vid;
+	if (enable == 1U)
+		ctrlpkt_mymac_act_ptr->valid = (bool)TRUE;
+	else
+		ctrlpkt_mymac_act_ptr->valid = (bool)FALSE;
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_mymac_act_get);
+
+u32 yt_ctrlpkt_bypass_set(u8 unit, u32 port, enum yt_ctrlpkt_type pkt_type,
+			  struct yt_ctrlpkt_bypass *bypass_conf_ptr)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!bypass_conf_ptr, CMM_ERR_NULL_POINT);
+	if (bypass_conf_ptr->flag == 0)
+		return CMM_ERR_OK;
+
+	if (((!(bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_VLAN_BIT)) ||
+	     (!(bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_PORT_ISO_BIT))) &&
+	    pkt_type != YT_CTRL_PKT_MLD_TYPE &&
+	    pkt_type != YT_CTRL_PKT_IGMP_TYPE) {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_ARP_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_ARP_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_ARP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_ARP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_ARP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_ARP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_flow_meter);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ARP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ND_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_SET(L2_ND_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				      L2_ND_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				      &entry, bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_ND_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_ND_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_ND_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_ND_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_flow_meter);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+				(unit, L2_ND_PER_PORT_CTRLN(macid),
+				 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_LLDP_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_LLDP_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_LLDP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_LLDP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_LLDP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_LLDP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_flow_meter);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ERPS_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ERPS_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_ERPS_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_ERPS_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_ERPS_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_ERPS_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_ERPS_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_ERPS_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_flow_meter);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ERPS_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_RRPP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_RRPP_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_RRPP_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_RRPP_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_RRPP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_RRPP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_RRPP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_RRPP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_flow_meter);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_RRPP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_IGMP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_IGMP_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_IGMP_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_IGMP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_IGMP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_IGMP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_IGMP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_flow_meter);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_VLAN_BIT) != 0)
+			HAL_FIELD_SET(L2_IGMP_PORT_CTRLN_VLAN_LEAKY_LOWBIT,
+				      L2_IGMP_PORT_CTRLN_VLAN_LEAKY_WIDTH,
+				      &entry, bypass_conf_ptr->bypass_vlan);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_PORT_ISO_BIT) !=
+		    0)
+			HAL_FIELD_SET
+				(L2_IGMP_PORT_CTRLN_PORT_ISO_LEAKY_LOWBIT,
+				 L2_IGMP_PORT_CTRLN_PORT_ISO_LEAKY_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_iso);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_MLD_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_SET(L2_MLD_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				      L2_MLD_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				      &entry, bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_MLD_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_MLD_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_MLD_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_MLD_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_flow_meter);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_VLAN_BIT) != 0)
+			HAL_FIELD_SET(L2_MLD_PORT_CTRLN_VLAN_LEAKY_LOWBIT,
+				      L2_MLD_PORT_CTRLN_VLAN_LEAKY_WIDTH,
+				      &entry, bypass_conf_ptr->bypass_vlan);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_PORT_ISO_BIT) !=
+		    0)
+			HAL_FIELD_SET
+				(L2_MLD_PORT_CTRLN_PORT_ISO_LEAKY_LOWBIT,
+				 L2_MLD_PORT_CTRLN_PORT_ISO_LEAKY_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_iso);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_PIM_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_PIM_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_SET(L2_PIM_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				      L2_PIM_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				      &entry, bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_PIM_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_PIM_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_SET
+				(L2_PIM_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_PIM_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, bypass_conf_ptr->bypass_flow_meter);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_PIM_PER_PORT_CTRLN(macid), entry), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_bypass_set);
+
+u32 yt_ctrlpkt_bypass_get(u8 unit, u32 port, enum yt_ctrlpkt_type pkt_type,
+			  struct yt_ctrlpkt_bypass *bypass_conf_ptr)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!bypass_conf_ptr, CMM_ERR_NULL_POINT);
+	if (bypass_conf_ptr->flag == 0)
+		return CMM_ERR_OK;
+
+	if (bypass_conf_ptr->flag == YT_CTRL_PKT_BYPASS_ALL &&
+	    pkt_type != YT_CTRL_PKT_MLD_TYPE &&
+	    pkt_type != YT_CTRL_PKT_IGMP_TYPE) {
+		bypass_conf_ptr->flag &= (~YT_CTRL_PKT_BYPASS_VLAN_BIT);
+		bypass_conf_ptr->flag &= (~YT_CTRL_PKT_BYPASS_PORT_ISO_BIT);
+	}
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_ARP_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_ARP_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_ARP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_ARP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_ARP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_ARP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_flow_meter);
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ND_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_GET(L2_ND_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				      L2_ND_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				      &entry, &bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_ND_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_ND_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_ND_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_ND_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_flow_meter);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+						L2_LLDP_PER_PORT_CTRLN(macid),
+						&entry),
+			    ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_LLDP_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_LLDP_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_LLDP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_LLDP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_LLDP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_LLDP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_flow_meter);
+	} else if (pkt_type == YT_CTRL_PKT_ERPS_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+						L2_ERPS_PER_PORT_CTRLN(macid),
+						&entry),
+			    ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_ERPS_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_ERPS_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_ERPS_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_ERPS_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_ERPS_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_ERPS_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_flow_meter);
+	} else if (pkt_type == YT_CTRL_PKT_RRPP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+						L2_RRPP_PER_PORT_CTRLN(macid),
+						&entry),
+			    ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_RRPP_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_RRPP_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_RRPP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_RRPP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_RRPP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_RRPP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_flow_meter);
+	} else if (pkt_type == YT_CTRL_PKT_IGMP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+						L2_IGMP_PER_PORT_CTRLN(macid),
+						&entry),
+			    ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_IGMP_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				 L2_IGMP_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_IGMP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_IGMP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_IGMP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_IGMP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_flow_meter);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_VLAN_BIT) != 0)
+			HAL_FIELD_GET(L2_IGMP_PORT_CTRLN_VLAN_LEAKY_LOWBIT,
+				      L2_IGMP_PORT_CTRLN_VLAN_LEAKY_WIDTH,
+				      &entry, &bypass_conf_ptr->bypass_vlan);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_PORT_ISO_BIT) !=
+		    0)
+			HAL_FIELD_GET
+				(L2_IGMP_PORT_CTRLN_PORT_ISO_LEAKY_LOWBIT,
+				 L2_IGMP_PORT_CTRLN_PORT_ISO_LEAKY_WIDTH,
+				 &entry,
+				 &bypass_conf_ptr->bypass_port_iso);
+	} else if (pkt_type == YT_CTRL_PKT_MLD_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_GET(L2_MLD_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				      L2_MLD_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				      &entry, &bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_MLD_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_MLD_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_MLD_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_MLD_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_flow_meter);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_VLAN_BIT) != 0)
+			HAL_FIELD_GET(L2_MLD_PORT_CTRLN_VLAN_LEAKY_LOWBIT,
+				      L2_MLD_PORT_CTRLN_VLAN_LEAKY_WIDTH,
+				      &entry, &bypass_conf_ptr->bypass_vlan);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_PORT_ISO_BIT) !=
+		    0)
+			HAL_FIELD_GET
+				(L2_MLD_PORT_CTRLN_PORT_ISO_LEAKY_LOWBIT,
+				 L2_MLD_PORT_CTRLN_PORT_ISO_LEAKY_WIDTH,
+				 &entry,
+				 &bypass_conf_ptr->bypass_port_iso);
+	} else if (pkt_type == YT_CTRL_PKT_PIM_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_PIM_PER_PORT_CTRLN(macid), &entry), ret);
+		if ((bypass_conf_ptr->flag & YT_CTRL_PKT_BYPASS_STORM_BIT) != 0)
+			HAL_FIELD_GET(L2_PIM_PORT_CTRLN_BYPASS_STORM_LOWBIT,
+				      L2_PIM_PORT_CTRLN_BYPASS_STORM_WIDTH,
+				      &entry, &bypass_conf_ptr->bypass_storm);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_PORT_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_PIM_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT,
+				 L2_PIM_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_port_meter);
+		if ((bypass_conf_ptr->flag &
+		     YT_CTRL_PKT_BYPASS_FLOW_METER_BIT) != 0)
+			HAL_FIELD_GET
+				(L2_PIM_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT,
+				 L2_PIM_PORT_CTRLN_BYPASS_PORTMETER_WIDTH,
+				 &entry, &bypass_conf_ptr->bypass_flow_meter);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_bypass_get);
+
+u32 yt_ctrlpkt_bypass_stp_set(u8 unit, enum yt_ctrlpkt_type pkt_type,
+			      enum yt_enable enable)
+{
+	u32 lowbit = 0;
+	u32 width = 0;
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		lowbit = L2_CTRL_PACKET_ARP_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_ARP_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		lowbit = L2_CTRL_PACKET_ND_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_ND_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		lowbit = L2_CTRL_PACKET_LLDP_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_LLDP_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_ERPS_TYPE) {
+		lowbit = L2_CTRL_PACKET_ERPS_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_ERPS_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_RRPP_TYPE) {
+		lowbit = L2_CTRL_PACKET_RRPP_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_RRPP_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_IGMP_TYPE) {
+		lowbit = L2_CTRL_PACKET_IGMP_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_IGMP_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_MLD_TYPE) {
+		lowbit = L2_CTRL_PACKET_MLD_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_MLD_STP_BYPASS_WIDTH;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_CTRL_PACKET_STP_BYPASS,
+					&entry), ret);
+	HAL_FIELD_SET(lowbit, width, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_CTRL_PACKET_STP_BYPASS,
+					 entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_bypass_stp_set);
+
+u32 yt_ctrlpkt_bypass_stp_get(u8 unit, enum yt_ctrlpkt_type pkt_type,
+			      enum yt_enable *enable_ptr)
+{
+	u32 lowbit = 0;
+	u32 width = 0;
+	u32 entry = 0;
+	u32 enable = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!enable_ptr, CMM_ERR_NULL_POINT);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		lowbit = L2_CTRL_PACKET_ARP_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_ARP_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		lowbit = L2_CTRL_PACKET_ND_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_ND_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		lowbit = L2_CTRL_PACKET_LLDP_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_LLDP_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_ERPS_TYPE) {
+		lowbit = L2_CTRL_PACKET_ERPS_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_ERPS_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_RRPP_TYPE) {
+		lowbit = L2_CTRL_PACKET_RRPP_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_RRPP_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_IGMP_TYPE) {
+		lowbit = L2_CTRL_PACKET_IGMP_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_IGMP_STP_BYPASS_WIDTH;
+	} else if (pkt_type == YT_CTRL_PKT_MLD_TYPE) {
+		lowbit = L2_CTRL_PACKET_MLD_STP_BYPASS_LOWBIT;
+		width = L2_CTRL_PACKET_MLD_STP_BYPASS_WIDTH;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_CTRL_PACKET_STP_BYPASS,
+					&entry),  ret);
+	HAL_FIELD_GET(lowbit, width, &entry, &enable);
+	if (enable == 0)
+		*enable_ptr = YT_DISABLE;
+	else
+		*enable_ptr = YT_ENABLE;
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_bypass_stp_get);
+
+u32 yt_ctrlpkt_sa_learn_set(u8 unit, u32 port,
+			    enum yt_ctrlpkt_type pkt_type,
+			    enum yt_enable enable)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET
+			(L2_ARP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			 L2_ARP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			 &entry, !enable);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ND_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_ND_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_ND_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, !enable);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ND_PER_PORT_CTRLN(macid),
+			 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_LLDP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_LLDP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, !enable);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid),
+			 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_ERPS_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ERPS_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_ERPS_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_ERPS_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, !enable);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_ERPS_PER_PORT_CTRLN(macid),
+			 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_RRPP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_RRPP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_RRPP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_RRPP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, !enable);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_RRPP_PER_PORT_CTRLN(macid),
+			 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_IGMP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_IGMP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_IGMP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, !enable);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid),
+			 entry), ret);
+	} else if (pkt_type == YT_CTRL_PKT_MLD_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_MLD_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_SET(L2_MLD_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_MLD_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, !enable);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), entry), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_sa_learn_set);
+
+u32 yt_ctrlpkt_sa_learn_get(u8 unit, u32 port,
+			    enum yt_ctrlpkt_type pkt_type,
+			    enum yt_enable *enable_ptr)
+{
+	enum yt_enable enable = YT_DISABLE;
+	u32 entry = 0;
+	u32 macid = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!enable_ptr, CMM_ERR_NULL_POINT);
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pkt_type == YT_CTRL_PKT_ARP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ARP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_GET
+			(L2_ARP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			 L2_ARP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			 &entry, &enable);
+	} else if (pkt_type == YT_CTRL_PKT_ND_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ND_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_GET(L2_ND_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_ND_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, &enable);
+	} else if (pkt_type == YT_CTRL_PKT_LLDP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_LLDP_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_GET(L2_LLDP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_LLDP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, &enable);
+	} else if (pkt_type == YT_CTRL_PKT_ERPS_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_ERPS_PER_PORT_CTRLN(macid),
+			 &entry), ret);
+		HAL_FIELD_GET(L2_ERPS_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_ERPS_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, &enable);
+	} else if (pkt_type == YT_CTRL_PKT_RRPP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit,
+			 L2_RRPP_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_GET(L2_RRPP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_RRPP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, &enable);
+	} else if (pkt_type == YT_CTRL_PKT_IGMP_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_IGMP_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_GET(L2_IGMP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_IGMP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, &enable);
+	} else if (pkt_type == YT_CTRL_PKT_MLD_TYPE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_MLD_PER_PORT_CTRLN(macid), &entry), ret);
+		HAL_FIELD_GET(L2_MLD_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT,
+			      L2_MLD_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH,
+			      &entry, &enable);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	*enable_ptr = (!enable);
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_sa_learn_get);
+
+u32 yt_ctrlpkt_parse_en_set(u8 unit, enum yt_ctrlpkt_parse_type parse_type,
+			    enum yt_enable enable)
+{
+	u32 lowbit = 0;
+	u32 width = 0;
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (parse_type == YT_CTRL_PKT_PARSE_ARP_TYPE) {
+		lowbit = PARSER_CTRL_ARP_LOWBIT;
+		width = PARSER_CTRL_ARP_WIDTH;
+	} else if (parse_type == YT_CTRL_PKT_PARSE_PPPOE_TYPE) {
+		lowbit = PARSER_CTRL_PPPOE_LOWBIT;
+		width = PARSER_CTRL_PPPOE_WIDTH;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_CTRL, &entry), ret);
+	HAL_FIELD_SET(lowbit, width, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PARSER_CTRL, entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_parse_en_set);
+
+u32 yt_ctrlpkt_parse_en_get(u8 unit, enum yt_ctrlpkt_parse_type parse_type,
+			    enum yt_enable *enable_ptr)
+{
+	u32 lowbit = 0;
+	u32 width = 0;
+	u32 entry = 0;
+	enum yt_enable enable = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!enable_ptr, CMM_ERR_NULL_POINT);
+	if (parse_type == YT_CTRL_PKT_PARSE_ARP_TYPE) {
+		lowbit = PARSER_CTRL_ARP_LOWBIT;
+		width = PARSER_CTRL_ARP_WIDTH;
+	} else if (parse_type == YT_CTRL_PKT_PARSE_PPPOE_TYPE) {
+		lowbit = PARSER_CTRL_PPPOE_LOWBIT;
+		width = PARSER_CTRL_PPPOE_WIDTH;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_CTRL, &entry), ret);
+	HAL_FIELD_GET(lowbit, width, &entry, &enable);
+	*enable_ptr = enable;
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_parse_en_get);
+
+u32 yt_ctrlpkt_rrpp_parse_set(u8 unit,
+			      struct yt_ctrlpkt_rrpp_parse *parse_conf_ptr)
+{
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!parse_conf_ptr, CMM_ERR_NULL_POINT);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_RRPP_ETHTYPE, &entry),
+		    ret);
+	HAL_FIELD_SET(PARSER_RRPP_ETHTYPE_LOWBIT, PARSER_RRPP_ETHTYPE_WIDTH,
+		      &entry, parse_conf_ptr->ethertype);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PARSER_RRPP_ETHTYPE, entry),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_RRPP_OUI, &entry), ret);
+	HAL_FIELD_SET(PARSER_RRPP_OUI_LOWBIT, PARSER_RRPP_OUI_WIDTH, &entry,
+		      parse_conf_ptr->oui);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PARSER_RRPP_OUI, entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_rrpp_parse_set);
+
+u32 yt_ctrlpkt_rrpp_parse_get(u8 unit,
+			      struct yt_ctrlpkt_rrpp_parse *parse_conf_ptr)
+{
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!parse_conf_ptr, CMM_ERR_NULL_POINT);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_RRPP_ETHTYPE, &entry),
+		    ret);
+	HAL_FIELD_GET(PARSER_RRPP_ETHTYPE_LOWBIT, PARSER_RRPP_ETHTYPE_WIDTH,
+		      &entry, &parse_conf_ptr->ethertype);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_RRPP_OUI, &entry), ret);
+	HAL_FIELD_GET(PARSER_RRPP_OUI_LOWBIT, PARSER_RRPP_OUI_WIDTH, &entry,
+		      &parse_conf_ptr->oui);
+	return ret;
+}
+EXPORT_SYMBOL(yt_ctrlpkt_rrpp_parse_get);
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.h b/drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.h
new file mode 100644
index 000000000000..f7bc0054d950
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_ctrlpkt.h
@@ -0,0 +1,345 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_CTRLPKT_H
+#define __YT_CTRLPKT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+#define YT_CTRL_PKT_BYPASS_VLAN_BIT BIT(0)
+#define YT_CTRL_PKT_BYPASS_PORT_ISO_BIT BIT(1)
+#define YT_CTRL_PKT_BYPASS_STORM_BIT BIT(2)
+#define YT_CTRL_PKT_BYPASS_PORT_METER_BIT BIT(3)
+#define YT_CTRL_PKT_BYPASS_FLOW_METER_BIT BIT(4)
+#define YT_CTRL_PKT_BYPASS_ALL                                              \
+	(YT_CTRL_PKT_BYPASS_VLAN_BIT | YT_CTRL_PKT_BYPASS_PORT_ISO_BIT |    \
+	 YT_CTRL_PKT_BYPASS_STORM_BIT | YT_CTRL_PKT_BYPASS_PORT_METER_BIT | \
+	 YT_CTRL_PKT_BYPASS_FLOW_METER_BIT)
+
+enum yt_ctrlpkt_l2_action {
+	L2_ACTION_FWD = 0,
+	L2_ACTION_TRAP,
+	L2_ACTION_DROP,
+	L2_ACTION_COPY,
+	L2_ACTION_END
+};
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+enum yt_ctrlpkt_type {
+	YT_CTRL_PKT_ARP_TYPE = 0,
+	YT_CTRL_PKT_ND_TYPE,
+	YT_CTRL_PKT_LLDP_TYPE,
+	YT_CTRL_PKT_LLDP_EEE_TYPE,
+	YT_CTRL_PKT_END_TYPE,
+};
+
+#elif defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+
+enum yt_ctrlpkt_parse_type {
+	YT_CTRL_PKT_PARSE_ARP_TYPE,
+	YT_CTRL_PKT_PARSE_PPPOE_TYPE,
+	YT_CTRL_PKT_PARSE_END_TYPE
+};
+
+enum yt_ctrlpkt_type {
+	YT_CTRL_PKT_ARP_TYPE = 0,
+	YT_CTRL_PKT_ND_TYPE,
+	YT_CTRL_PKT_LLDP_TYPE,
+	YT_CTRL_PKT_ERPS_TYPE,
+	YT_CTRL_PKT_RRPP_TYPE,
+	YT_CTRL_PKT_PIM_TYPE,
+	YT_CTRL_PKT_IGMP_TYPE,
+	YT_CTRL_PKT_MLD_TYPE,
+	YT_CTRL_PKT_END_TYPE,
+};
+
+struct yt_ctrlpkt_bypass {
+	enum yt_enable bypass_vlan;
+	enum yt_enable bypass_port_iso;
+	enum yt_enable bypass_storm;
+	enum yt_enable bypass_port_meter;
+	enum yt_enable bypass_flow_meter;
+	u32 flag;
+};
+
+struct yt_ctrlpkt_mymac_act {
+	enum yt_act_type action;
+	u16 manage_vid;
+	bool valid;
+};
+
+struct yt_ctrlpkt_rrpp_parse {
+	u16 ethertype;
+	u32 oui;
+};
+#endif
+
+/**
+ * @internal      yt_ctrlpkt_act_set
+ * @endinternal
+ *
+ * @brief         Set action of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Tiger,Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     pkt_type            -packet type
+ * @param[in]     act_type            -packet action: FWD/TRAP/DROP/COPY
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -invalid input
+ * @retval        CMM_ERR_PORT        -invalid port
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_act_set(u8 unit, u32 port, enum yt_ctrlpkt_type pkt_type,
+		       enum yt_act_type act_type);
+
+/**
+ * @internal      yt_ctrlpkt_act_get
+ * @endinternal
+ *
+ * @brief         Get action of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark,Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     pktType             -packet type
+ * @param[out]    act_type            -packet action: FWD/TRAP/DROP/COPY
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -invalid input
+ * @retval        CMM_ERR_PORT        -invalid port
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_act_get(u8 unit, u32 port, enum yt_ctrlpkt_type pkt_type,
+		       enum yt_act_type *act_type);
+
+#if defined(CONFIG_MOTORCOMM_YT922X)
+
+/**
+ * @internal      yt_ctrlpkt_myMac_act_set
+ * @endinternal
+ *
+ * @brief         Set action of my mac packet
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     ctrlpkt_mymac_act_ptr -action config data struct
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ */
+u32 yt_ctrlpkt_mymac_act_set
+	(u8 unit, struct yt_ctrlpkt_mymac_act *ctrlpkt_mymac_act_ptr);
+
+/**
+ * @internal      yt_ctrlpkt_myMac_act_get
+ * @endinternal
+ *
+ * @brief         Get action of my mac packet
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[out]    ctrlpkt_mymac_act_ptr -action config data struct
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ */
+u32 yt_ctrlpkt_mymac_act_get
+	(u8 unit, struct yt_ctrlpkt_mymac_act *ctrlpkt_mymac_act_ptr);
+
+/**
+ * @internal      yt_ctrlpkt_bypass_set
+ * @endinternal
+ *
+ * @brief         Set bypass state of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     ctrl_pkt_type       -ARP/ND/LLDP/ERPS/RRPP/IGMP/MLD/PIM
+ * @param[in]     bypass_conf_ptr     -config data
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -invalid input
+ * @retval        CMM_ERR_PORT        -invalid port
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_bypass_set(u8 unit, u32 port, enum yt_ctrlpkt_type ctrl_pkt_type,
+			  struct yt_ctrlpkt_bypass *bypass_conf_ptr);
+
+/**
+ * @internal      yt_ctrlpkt_bypass_get
+ * @endinternal
+ *
+ * @brief         Get bypass state of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     ctrl_pkt_type       -ARP/ND/LLDP/ERPS/RRPP/IGMP/MLD/PIM
+ * @param[out]    bypass_conf_ptr     -config data
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -invalid input
+ * @retval        CMM_ERR_PORT        -invalid port
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_bypass_get(u8 unit, u32 port, enum yt_ctrlpkt_type ctrl_pkt_type,
+			  struct yt_ctrlpkt_bypass *bypass_conf_ptr);
+
+/**
+ * @internal      yt_ctrlpkt_bypass_stp_set
+ * @endinternal
+ *
+ * @brief         Set stp bypass state of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     ctrl_pkt_type       -ARP/ND/LLDP/ERPS/RRPP/IGMP/MLD/PIM
+ * @param[in]     enable              -YT_ENABLE/YT_DISABLE
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -invalid input
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_bypass_stp_set(u8 unit, enum yt_ctrlpkt_type ctrl_pkt_type,
+			      enum yt_enable enable);
+
+/**
+ * @internal      yt_ctrlpkt_bypass_stp_get
+ * @endinternal
+ *
+ * @brief         Get stp bypass state of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     ctrl_pkt_type       -ARP/ND/LLDP/ERPS/RRPP/IGMP/MLD/PIM
+ * @param[out]    enable_ptr          -YT_ENABLE/YT_DISABLE
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_bypass_stp_get(u8 unit, enum yt_ctrlpkt_type ctrl_pkt_type,
+			      enum yt_enable *enable_ptr);
+
+/**
+ * @internal      yt_ctrlpkt_sa_learn_set
+ * @endinternal
+ *
+ * @brief         Set sa-learning state of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     yt_ctrlpkt_type     -ARP/ND/LLDP/ERPS/RRPP/IGMP/MLD/PIM
+ * @param[in]     enable              -YT_ENABLE/YT_DISABLE
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -invalid input
+ * @retval        CMM_ERR_PORT        -invalid port
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_sa_learn_set(u8 unit, u32 port,
+			    enum yt_ctrlpkt_type ctrl_pkt_type,
+			    enum yt_enable enable);
+
+/**
+ * @internal      yt_ctrlpkt_sa_learn_get
+ * @endinternal
+ *
+ * @brief         Get sa-learning state of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     ctrl_pkt_type       -ARP/ND/LLDP/ERPS/RRPP/IGMP/MLD/PIM
+ * @param[out]    enable_ptr          -YT_ENABLE/YT_DISABLE
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -invalid input
+ * @retval        CMM_ERR_PORT        -invalid port
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_sa_learn_get(u8 unit, u32 port,
+			    enum yt_ctrlpkt_type ctrl_pkt_type,
+			    enum yt_enable *enable_ptr);
+
+/**
+ * @internal      yt_ctrlpkt_parse_en_set
+ * @endinternal
+ *
+ * @brief         Set parse state of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     parse_type          -ARP/PPPOE
+ * @param[in]     enable              -YT_ENABLE/YT_DISABLE
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -invalid input
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_parse_en_set(u8 unit, enum yt_ctrlpkt_parse_type parse_type,
+			    enum yt_enable enable);
+
+/**
+ * @internal      yt_ctrlpkt_parse_en_get
+ * @endinternal
+ *
+ * @brief         Get parse state of ctrl-pkt
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     parse_type          -ARP/PPPOE
+ * @param[out]    enable_ptr          -YT_ENABLE/YT_DISABLE
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_parse_en_get(u8 unit, enum yt_ctrlpkt_parse_type parse_type,
+			    enum yt_enable *enable_ptr);
+
+/**
+ * @internal      yt_ctrlpkt_rrpp_parse_set
+ * @endinternal
+ *
+ * @brief         Set parse state of rrpp packets
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     parse_conf_ptr      -x
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_rrpp_parse_set(u8 unit,
+			      struct yt_ctrlpkt_rrpp_parse *parse_conf_ptr);
+
+/**
+ * @internal      yt_ctrlpkt_rrpp_parse_get
+ * @endinternal
+ *
+ * @brief         Get parse state of rrpp packets
+ * @note          APPLICABLE DEVICES  -Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[out]    parse_conf_ptr       -x
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ * @retval        CMM_ERR_NOT_INIT    -module not init
+ */
+u32 yt_ctrlpkt_rrpp_parse_get(u8 unit,
+			      struct yt_ctrlpkt_rrpp_parse *parse_conf_ptr);
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_drv.c b/drivers/net/dsa/motorcomm/switch/yt_drv.c
new file mode 100644
index 000000000000..add435367525
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_drv.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_drv.h"
+
+struct yt_drv_func yt_drv_func = {
+	.switch_reg_read = NULL,
+	.switch_reg_write = NULL,
+};
+
+u32 yt_drv_register(struct yt_drv_func drv_func)
+{
+	yt_drv_func.switch_reg_read = drv_func.switch_reg_read;
+	yt_drv_func.switch_reg_write = drv_func.switch_reg_write;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_drv_register);
+
+u32 yt_drv_switch_reg_read(u8 unit, u32 reg_addr, u32 *reg_value_ptr)
+{
+	if (!yt_drv_func.switch_reg_read)
+		return yt_drv_func.switch_reg_read(unit, reg_addr,
+						   reg_value_ptr);
+
+	return CMM_ERR_FAIL;
+}
+EXPORT_SYMBOL(yt_drv_switch_reg_read);
+
+u32 yt_drv_switch_reg_write(u8 unit, u32 reg_addr, u32 reg_value)
+{
+	if (!yt_drv_func.switch_reg_write)
+		return yt_drv_func.switch_reg_write(unit, reg_addr, reg_value);
+
+	return CMM_ERR_FAIL;
+}
+EXPORT_SYMBOL(yt_drv_switch_reg_write);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_drv.h b/drivers/net/dsa/motorcomm/switch/yt_drv.h
new file mode 100644
index 000000000000..81b4711ccf6c
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_drv.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_DRV_H
+#define __YT_DRV_H
+
+/*
+ * Include Files
+ */
+#include "yt_cmm.h"
+
+struct yt_drv_func {
+	u32 (*switch_reg_read)(u8 unit, u32 reg_addr, u32 *reg_value_ptr);
+	u32 (*switch_reg_write)(u8 unit, u32 reg_addr, u32 reg_value);
+};
+
+u32 yt_drv_register(struct yt_drv_func drv_func);
+u32 yt_drv_switch_reg_read(u8 unit, u32 reg_addr, u32 *reg_value_ptr);
+u32 yt_drv_switch_reg_write(u8 unit, u32 reg_addr, u32 reg_value);
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_init.c b/drivers/net/dsa/motorcomm/switch/yt_init.c
new file mode 100644
index 000000000000..98b4ce622d6c
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_init.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#include "yt_drv.h"
+#include "yt_init.h"
+#ifdef INTERRUPT_INCLUDED
+#include "yt_interrupt.h"
+#endif
+#ifdef NIC_INCLUDED
+#include "yt_nic.h"
+#endif
+#ifdef PORT_INCLUDED
+#include "yt_port.h"
+#endif
+#ifdef STORM_CTRL_INCLUDED
+#include "yt_storm_ctrl.h"
+#endif
+#ifdef VLAN_INCLUDED
+#include "yt_vlan.h"
+#endif
+#include <linux/export.h>
+
+static struct yt_switch_port_info sw_port_info[YT_MAX_UNIT];
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+static enum yt_switch_chip_id sw_chip_id = YT_SW_ID_9215;
+#elif defined(CONFIG_MOTORCOMM_YT923X)
+static enum yt_switch_chip_id sw_chip_id = YT_SW_ID_923X;
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+static enum yt_switch_chip_id sw_chip_id = YT_SW_ID_922X;
+#endif
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+static u32 yt_patch_qos_init(u8 unit)
+{
+	u32 data2;
+	u32 addr;
+	u32 data;
+	u32 port;
+	u32 qid;
+	int ret;
+
+	for (port = 0; port < YT_MAX_PORT_PER_UNIT; port++) {
+		for (qid = 0; qid < CAL_MAX_UCAST_QUEUE_NUM; qid++) {
+			addr = QOS_FORCEAC_UCASTQUE_REG(unit, port, qid);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr, &data),
+				    ret);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr + 4, &data2),
+				    ret);
+			data &= ~(0xff);
+			data |= 0x20;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, data),
+				    ret);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr + 4, data2),
+				    ret);
+		}
+
+		for (qid = 0; qid < CAL_MAX_MCAST_QUEUE_NUM; qid++) {
+			addr = QOS_FORCEAC_MCASTQUE_REG(unit, port, qid);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr, &data),
+				    ret);
+			data &= ~(0xff);
+			data |= 0xc;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, data),
+				    ret);
+		}
+	}
+
+	return ret;
+}
+
+static u32 yt_patch_init(u8 unit)
+{
+	u8 tune_val[4] = { 0xE1, 0xE2, 0xE3, 0xE4 };
+	u32 ceiling = 0xdc;
+	u32 entry = 0;
+	u32 data2 = 0;
+	u32 data = 0;
+	u32 addr2;
+	u32 addr;
+	u8 i = 0;
+	u32 ret;
+
+	if (sw_chip_id != YT_SW_ID_920X) {
+		/* flow control */
+		for (i = 0; i < YT_MAX_PORT_PER_UNIT; i++) {
+			addr = 0x281000 + i * 8;
+			addr2 = 0x281004 + i * 8;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr, &data),
+				    ret);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr2, &data2),
+				    ret);
+			data &= ~(0x1ff | (0x7 << 29));
+			data2 &= ~(0xff);
+			data |= ((0x4b & 0x1ff) | ((ceiling & 0x7) << 29));
+			data2 |= ((ceiling >> 3) & 0xff);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, data),
+				    ret);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr2, data2),
+				    ret);
+		}
+		for (i = 0; i < 4; i++) {
+			if (sw_chip_id == YT_SW_ID_9215) {
+				addr = 0x2801D0 + i * 4;
+				CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr,
+								&data),
+					    ret);
+				data &= ~(0x3ff);
+				data |= (0x14a & 0x3ff);
+				CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr,
+								 data),
+					    ret);
+			}
+			addr = 0x180904 + i * 4;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr,
+							 tune_val[i]),
+				    ret);
+		}
+
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_CHIP_ID_REG, &data),
+			    ret);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &entry),
+			    ret);
+		HAL_FIELD_SET(2, 1, &entry, YT_ENABLE);
+		if (0x1 == (data & 0x0ffff))
+			HAL_FIELD_SET(2, 1, &entry, YT_ENABLE);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, GLOBAL_CTRL1, entry),
+			    ret);
+
+		/*
+		 * disable sds intf,will be enabled when config sds mode
+		 * set to mdio access mode
+		 */
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_INTERFACE_CTRL_REG,
+						&data),
+			    ret);
+		data &= ~(0x43 << 0);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CHIP_INTERFACE_CTRL_REG,
+						 data),
+			    ret);
+
+		/*
+		 * adjust dying gasp for YT_SW_ID_9218
+		 */
+		if (sw_chip_id == YT_SW_ID_9218) {
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_TOPBIAS_REG,
+							&data),
+				    ret);
+			data &= ~(0x3 << 22);
+			data |= (0x3 << 22);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CHIP_TOPBIAS_REG,
+							 data),
+				    ret);
+		}
+
+		CMM_ERR_CHK(yt_patch_qos_init(unit), ret);
+	} else {
+		addr = 0xf0004;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr, &data), ret);
+		data &= ~(0xfU << 8U);
+		data |= (0x4U << 8U);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, data), ret);
+
+		addr = 0x8000c0dc;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr, &data), ret);
+		if (!IS_BIT_SET(data, 5)) {
+			SET_BIT(data, 5);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, data),
+				    ret);
+		}
+	}
+
+	return ret;
+}
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+static u32 whale_all_port_mac_en_set(u8 unit, enum yt_enable enable)
+{
+	u32 entry = 0;
+	u32 addr;
+	u32 macid;
+	u32 port;
+	u32 ret;
+
+	for (port = 0; port < YT_MAX_PORT_PER_UNIT; port++) {
+		macid = CAL_YTP_TO_MAC(unit, port);
+		addr = PORT_CTRL + macid * 4;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr, &entry), ret);
+		HAL_FIELD_SET(PORT_CTRL_CFG_RXMAC_EN_LOWBIT,
+			      PORT_CTRL_CFG_RXMAC_EN_WIDTH, &entry, enable);
+		HAL_FIELD_SET(PORT_CTRL_CFG_TXMAC_EN_LOWBIT,
+			      PORT_CTRL_CFG_TXMAC_EN_WIDTH, &entry, enable);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, entry), ret);
+	}
+
+	return ret;
+}
+
+static u32 whale_tick_set(u8 unit)
+{
+	u32 addr;
+	u32 ret;
+	u8 i;
+
+	CMM_ERR_CHK(whale_all_port_mac_en_set(unit, YT_DISABLE), ret);
+
+	/* Currently only support 9224 */
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0x908A0, 0xc), ret);
+	for (i = 0; i < 4; i++) {
+		addr = 0x92E00 + (0xC * i);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, 0x10), ret);
+		addr += 4;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, 0x18), ret);
+	}
+	for (i = 0; i < 4; i++) {
+		addr = 0x92E08 + (0xC * i);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, (0x14 + i)), ret);
+	}
+
+	CMM_ERR_CHK(whale_all_port_mac_en_set(unit, YT_ENABLE), ret);
+
+	return ret;
+}
+
+static u32 yt_patch_init(u8 unit)
+{
+	u8 tune_val[4] = { 0xE1, 0xE2, 0xE3, 0xE4 };
+	u32 data;
+	u32 addr;
+	u32 ret;
+	u8 i;
+
+	CMM_ERR_CHK(whale_tick_set(unit), ret);
+
+	/* select mdio grp1 pin */
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, 0x80400, &data), ret);
+	data |= (1 << 4);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0x80400, data), ret);
+
+	for (i = 0; i < 4; i++)
+		HAL_MEM_DIRECT_WRITE(unit, 0x181310 + i * 4, tune_val[i]);
+
+	addr = EXT_IF_ADDR_CTRL;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr, &data), ret);
+	data &= ~(0xfU << 8U);
+	data |= (EXT_IF_FREQUENCY_SEL << 8U);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, data), ret);
+
+	return ret;
+}
+#endif
+
+static u32 yt_modules_init(u8 unit)
+{
+	u32 ret = CMM_ERR_OK;
+
+#ifdef PORT_INCLUDED
+	CMM_ERR_CHK(yt_port_init(unit), ret);
+#endif
+
+#ifdef STORM_CTRL_INCLUDED
+	CMM_ERR_CHK(yt_storm_ctrl_init(unit), ret);
+#endif
+
+#ifdef VLAN_INCLUDED
+	CMM_ERR_CHK(yt_vlan_init(unit), ret);
+#endif
+
+#ifdef NIC_INCLUDED
+	CMM_ERR_CHK(yt_nic_init(unit), ret);
+#endif
+
+#ifdef INTERRUPT_INCLUDED
+	CMM_ERR_CHK(yt_int_init(unit), ret);
+#endif
+
+	return ret;
+}
+
+u32 yt_init(void)
+{
+	u8 unit;
+	u32 ret;
+
+	for (unit = 0; unit < YT_MAX_UNIT; unit++) {
+		CMM_ERR_CHK(yt_patch_init(unit), ret);
+		CMM_ERR_CHK(yt_modules_init(unit), ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_init);
+
+u32 yt_cal_board_chip_set(enum yt_switch_chip_id chip_id)
+{
+	CMM_PARAM_CHK((chip_id > YT_SW_ID_920X), CMM_ERR_INPUT);
+
+	sw_chip_id = chip_id;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_cal_board_chip_set);
+
+u32 yt_cal_board_chip_get(enum yt_switch_chip_id *chip_id)
+{
+	CMM_PARAM_CHK((!chip_id), CMM_ERR_NULL_POINT);
+
+	*chip_id = sw_chip_id;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_cal_board_chip_get);
+
+u32 yt_board_port_num_get(u8 unit, u32 *max_port)
+{
+	CMM_PARAM_CHK((!max_port), CMM_ERR_INPUT);
+	CMM_PARAM_CHK((unit >= YT_MAX_UNIT), CMM_ERR_INPUT);
+
+	*max_port = YT_MAX_PORT_PER_UNIT;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_board_port_num_get);
+
+u32 yt_board_port_info_set(u8 unit, struct yt_switch_port_info port_info)
+{
+	CMM_PARAM_CHK((unit >= YT_MAX_UNIT), CMM_ERR_INPUT);
+
+	sw_port_info[unit] = port_info;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_board_port_info_set);
+
+u32 yt_board_port_info_get(u8 unit, struct yt_switch_port_info *port_info)
+{
+	CMM_PARAM_CHK((unit >= YT_MAX_UNIT), CMM_ERR_INPUT);
+
+	*port_info = sw_port_info[unit];
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_board_port_info_get);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_init.h b/drivers/net/dsa/motorcomm/switch/yt_init.h
new file mode 100644
index 000000000000..3ff0c1aeadcf
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_init.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_INIT_H__
+#define __YT_INIT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Include Files
+ */
+#include "yt_cmm.h"
+
+u32 yt_init(void);
+u32 yt_cal_board_chip_set(enum yt_switch_chip_id chip_id);
+u32 yt_cal_board_chip_get(enum yt_switch_chip_id *chip_id);
+u32 yt_board_port_num_get(u8 unit, u32 *max_port);
+u32 yt_board_port_info_set(u8 unit, struct yt_switch_port_info port_info);
+u32 yt_board_port_info_get(u8 unit, struct yt_switch_port_info *port_info);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_interrupt.c b/drivers/net/dsa/motorcomm/switch/yt_interrupt.c
new file mode 100644
index 000000000000..8893f0137384
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_interrupt.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_interrupt.h"
+#include "yt_init.h"
+#include "yt_port.h"
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+u32 yt_int_init(u8 unit)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+	u32 i;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_addr = 0x12;
+	reg_attr.reg_space = YT_PHY_REG_SPACE_TX;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id == YT_SW_ID_920X) {
+		for (i = 0; i < 4; i++) {
+			reg_attr.reg_space = YT_PHY_REG_SPACE_T1;
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, i, reg_attr,
+							0xC00),
+				    ret);
+			reg_attr.reg_space = YT_PHY_REG_SPACE_TX;
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, i, reg_attr,
+							0xC00),
+				    ret);
+		}
+	} else {
+		for (i = 0; i < YT_MAX_PORT_PER_UNIT - 3; i++)
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, i, reg_attr,
+							0xC00),
+				    ret);
+
+		reg_attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, 8, reg_attr, 0x3), ret);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, 9, reg_attr, 0x3), ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_init);
+
+u32 yt_int_polarity_set(u8 unit, enum yt_int_polarity type)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 active_low = 0;
+	u32 disneg = 0;
+	u32 ponstrap;
+	u32 entry;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id == YT_SW_ID_920X) {
+		ret |= HAL_MEM_DIRECT_READ(unit, PON_STRAP, &ponstrap);
+		active_low = (ponstrap >> 13) & 0x1;
+
+		if ((active_low == 1 && type == INT_POLAR_LOW) ||
+		    (active_low == 0 && type == INT_POLAR_HIGH))
+			disneg = 1;
+		else
+			disneg = 0;
+
+		ret |= HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &entry);
+		HAL_FIELD_SET(INT_POL_L, INT_POL_W, &entry, disneg);
+		ret |= HAL_MEM_DIRECT_WRITE(unit, GLOBAL_CTRL1, entry);
+	} else {
+		ret |= HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &entry);
+		HAL_FIELD_SET(INT_POL_L, INT_POL_W, &entry, type);
+		ret |= HAL_MEM_DIRECT_WRITE(unit, GLOBAL_CTRL1, entry);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_polarity_set);
+
+u32 yt_int_polarity_get(u8 unit, enum yt_int_polarity *ptype)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 active_low = 0;
+	u32 disneg = 0;
+	u32 ponstrap;
+	u32 entry;
+	u32 pol;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id == YT_SW_ID_920X) {
+		ret |= HAL_MEM_DIRECT_READ(unit, PON_STRAP, &ponstrap);
+		active_low = (ponstrap >> 13) & 0x1;
+
+		ret |= HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &entry);
+		HAL_FIELD_GET(INT_POL_L, INT_POL_W, &entry, &disneg);
+
+		if ((active_low == 1 && disneg == 1) ||
+		    (active_low == 0 && disneg == 0))
+			*ptype = INT_POLAR_LOW;
+		else
+			*ptype = INT_POLAR_HIGH;
+	} else {
+		ret |= HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &entry);
+		HAL_FIELD_GET(INT_POL_L, INT_POL_W, &entry, &pol);
+		*ptype = pol ? INT_POLAR_HIGH : INT_POLAR_LOW;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_polarity_get);
+
+u32 yt_int_port_int_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 intr_mask;
+	u32 mac_id;
+	u32 mask;
+
+	mac_id = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INTR_MASK, &intr_mask), ret);
+	mask = (enable == YT_ENABLE) ? 1 : 0;
+	HAL_FIELD_SET((mac_id + 1), 1, &intr_mask, mask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, INTR_MASK, intr_mask), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_port_int_set);
+
+u32 yt_int_port_int_get(u8 unit, u32 port, enum yt_enable *penable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 intr_mask;
+	u32 mask;
+	u32 mac_id;
+
+	mac_id = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INTR_MASK, &intr_mask), ret);
+	HAL_FIELD_GET((mac_id + 1), 1, &intr_mask, &mask);
+	*penable = mask ? YT_ENABLE : YT_DISABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_port_int_get);
+
+u32 yt_int_allport_intstatus_get(u8 unit, u32 *pintstatus)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	struct yt_port_link_status_all status_all;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+	u16 int_final = 0;
+	u16 regdata = 0;
+	u32 intr_st = 0;
+	u16 int_st = 0;
+	u16 int_t1 = 0;
+	u32 i;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_addr = 0x13;
+	reg_attr.reg_space = YT_PHY_REG_SPACE_TX;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	*pintstatus = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INTR_STATUS, &intr_st), ret);
+
+	for (i = 0; i < YT_MAX_PORT_PER_UNIT - 1; i++) {
+		if (IS_BIT_SET(intr_st, (i + 1)) == 0)
+			continue;
+
+		if (chip_id != YT_SW_ID_920X) {
+			yt_port_phy_reg_get(unit, i, reg_attr, &int_st);
+			int_final = int_st;
+		} else {
+			/*get tx and t1 intr, return one valid of them*/
+			if (i < 4) {
+				reg_attr.reg_space = YT_PHY_REG_SPACE_T1;
+				yt_port_phy_reg_get(unit, i, reg_attr, &int_t1);
+				reg_attr.reg_space = YT_PHY_REG_SPACE_TX;
+				yt_port_phy_reg_get(unit, i, reg_attr, &int_st);
+				reg_attr.reg_addr = 0xb;
+				reg_attr.reg_space = YT_PHY_REG_SPACE_LDS;
+				yt_port_phy_reg_get(unit, i, reg_attr,
+						    &regdata);
+				yt_port_link_status_all_get(unit, i,
+							    &status_all);
+				int_final = int_st;
+				if (int_st == 0 && int_t1 == 0) {
+					int_final = 0;
+				} else if (int_st != 0 && int_t1 == 0) {
+					int_final = int_st;
+				} else if (int_st == 0 && int_t1 != 0) {
+					int_final = int_t1;
+				} else if ((int_st != 0 && int_t1 != 0) &&
+					   (status_all.link_status ==
+						    PORT_LINK_UP &&
+					    IS_BIT_SET(regdata, 2) == 0 &&
+					    IS_BIT_SET(regdata, 3) == 0)) {
+					int_final = int_st;
+				} else {
+					int_final = int_t1;
+				}
+			} else if (i == 9) {
+				reg_attr.reg_addr = 0x13;
+				reg_attr.reg_space = YT_PHY_REG_SPACE_TX;
+				reg_attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+				yt_port_phy_reg_get(unit, i, reg_attr, &int_st);
+			} else {
+				continue;
+			}
+		}
+
+		if (i == 8 || i == 9) {
+			if (IS_BIT_SET(int_final, 0) == 1 ||
+			    IS_BIT_SET(int_final, 1) == 1)
+				SET_BIT(*pintstatus, i);
+		} else {
+			if (IS_BIT_SET(int_final, 10) == 1 ||
+			    IS_BIT_SET(int_final, 11) == 1)
+				SET_BIT(*pintstatus, i);
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_allport_intstatus_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+/* enable internal phy interrupt mask for link up and link down */
+u32 yt_int_init(u8 unit)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+	u32 i;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_addr = 0x12;
+	reg_attr.reg_space = YT_PHY_REG_SPACE_TX;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id == YT_SW_ID_922X) {
+		/* sds has only up and down intr,enabled by default */
+		for (i = 4; i <= 7; i++)
+			ret |= yt_port_phy_reg_set(unit, i, reg_attr, 0xC00);
+	} else {
+		for (i = 0; i < YT_MAX_PORT_PER_UNIT - 1; i++) {
+			if (i >= 8 || i <= 15) {
+				ret |= yt_port_phy_reg_set(unit, i, reg_attr,
+							   0xC00);
+			} else {
+				reg_attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+				ret |= yt_port_phy_reg_set(unit, i, reg_attr,
+							   0x3);
+			}
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_init);
+
+u32 yt_int_polarity_set(u8 unit, enum yt_int_polarity type)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &entry), ret);
+	HAL_FIELD_SET(INT_POL_L, INT_POL_W, &entry, type);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, GLOBAL_CTRL1, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_polarity_set);
+
+u32 yt_int_polarity_get(u8 unit, enum yt_int_polarity *ptype)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry;
+	u32 pol;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &entry), ret);
+	HAL_FIELD_GET(INT_POL_L, INT_POL_W, &entry, &pol);
+	*ptype = pol ? INT_POLAR_HIGH : INT_POLAR_LOW;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_polarity_get);
+
+u32 yt_int_port_int_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 intr_mask;
+	u32 mac_id;
+	u32 mask;
+
+	mac_id = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INTR1_MASK, &intr_mask), ret);
+	mask = (enable == YT_ENABLE) ? 1 : 0;
+	HAL_FIELD_SET(mac_id, 1, &intr_mask, mask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, INTR1_MASK, intr_mask), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_port_int_set);
+
+u32 yt_int_port_int_get(u8 unit, u32 port, enum yt_enable *penable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 intr_mask;
+	u32 mac_id;
+	u32 mask;
+
+	mac_id = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INTR1_MASK, &intr_mask), ret);
+	HAL_FIELD_GET(mac_id, 1, &intr_mask, &mask);
+	*penable = mask ? YT_ENABLE : YT_DISABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_port_int_get);
+
+u32 yt_int_allport_intstatus_get(u8 unit, u32 *pintstatus)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+	u32 intr_st = 0;
+	u16 phy_int = 0;
+	u32 i;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_addr = 0x13;
+	reg_attr.reg_space = YT_PHY_REG_SPACE_TX;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INTR1_STATUS, &intr_st), ret);
+	*pintstatus = intr_st;
+
+	for (i = 0; i < YT_MAX_PORT_PER_UNIT - 1; i++) {
+		if (IS_BIT_SET(intr_st, i) == 0)
+			continue;
+
+		if (i == 0 || i == 8)
+			reg_attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+		else
+			reg_attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+		/* get and clear phy intr status */
+		yt_port_phy_reg_get(unit, i, reg_attr, &phy_int);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_int_allport_intstatus_get);
+#endif /* end of shark and whale series */
diff --git a/drivers/net/dsa/motorcomm/switch/yt_interrupt.h b/drivers/net/dsa/motorcomm/switch/yt_interrupt.h
new file mode 100644
index 000000000000..4a95cd28d542
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_interrupt.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_INTERRUPT_H__
+#define __YT_INTERRUPT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+enum yt_int_polarity { INT_POLAR_LOW = 0, INT_POLAR_HIGH, INT_POLAR_END };
+
+/**
+ * @internal      yt_int_init
+ * @endinternal
+ *
+ * @brief         do init configuration if necessary
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_int_init(u8 unit);
+
+/**
+ * @internal      yt_int_polarity_set
+ * @endinternal
+ *
+ * @brief         Set interrupt polarity type
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -high level low level
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_int_polarity_set(u8 unit, enum yt_int_polarity type);
+
+/**
+ * @internal      yt_int_polarity_get
+ * @endinternal
+ *
+ * @brief         Get interrupt polarity type
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[out]    ptype               -high level low level
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_int_polarity_get(u8 unit, enum yt_int_polarity *ptype);
+
+/**
+ * @internal      yt_int_port_int_set
+ * @endinternal
+ *
+ * @brief         Set port interrupt mask
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_int_port_int_set(u8 unit, u32 port, enum yt_enable enable);
+
+/**
+ * @internal      yt_int_port_int_get
+ * @endinternal
+ *
+ * @brief         Get port interrupt mask state
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    penable             -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_int_port_int_get(u8 unit, u32 port, enum yt_enable *penable);
+
+/**
+ * @internal      yt_int_port_intstatus_get
+ * @endinternal
+ *
+ * @brief         Get all ports link change interrupt state
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[out]    pintstatus          -interrupt state,
+				refer register description to get intr bit
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_int_allport_intstatus_get(u8 unit, u32 *pintstatus);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_isolation.c b/drivers/net/dsa/motorcomm/switch/yt_isolation.c
new file mode 100644
index 000000000000..4badbbdeed1e
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_isolation.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_isolation.h"
+
+u32 yt_isolation_port_set(u8 unit, u32 port, struct yt_port_mask iso_portmask)
+{
+	struct yt_port_mask macmask;
+	u32 macid;
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CAL_YTPLIST_TO_MLIST(unit, iso_portmask, macmask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_PORT_ISOLATION_CTRLN(macid),
+					&entry), ret);
+	HAL_FIELD_SET(L2_PORT_ISOLATION_CTRLN_PORTMASK_LOWBIT,
+		      L2_PORT_ISOLATION_CTRLN_PORTMASK_WIDTH, &entry,
+		      macmask.portbits[0]);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_PORT_ISOLATION_CTRLN(macid),
+					 entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_isolation_port_set);
+
+u32 yt_isolation_port_get(u8 unit, u32 port,
+			  struct yt_port_mask *iso_portmask_ptr)
+{
+	u32 portmask = 0;
+	struct yt_port_mask macmask;
+	u32 entry = 0;
+	u32 macid = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!iso_portmask_ptr, CMM_ERR_NULL_POINT);
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CMM_CLEAR_MEMBER_PORT(macmask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_PORT_ISOLATION_CTRLN(macid),
+					&entry), ret);
+	HAL_FIELD_GET(L2_PORT_ISOLATION_CTRLN_PORTMASK_LOWBIT,
+		      L2_PORT_ISOLATION_CTRLN_PORTMASK_WIDTH, &entry,
+		      &portmask);
+	macmask.portbits[0] = portmask;
+	CAL_MLIST_TO_YTPLIST(unit, macmask, (*iso_portmask_ptr));
+	return ret;
+}
+EXPORT_SYMBOL(yt_isolation_port_get);
+
+#if defined(CONFIG_MOTORCOMM_YT922X)
+u32 yt_isolation_vlan_add(u8 unit, struct yt_iso_vlan_info iso_info,
+			  u16 *idx_ptr)
+{
+	u16 vlan_iso_index = 0;
+	struct yt_port_mask tmp_iso_msk;
+	u16 valid_num = 0;
+	u32 max_vid = 0;
+	u32 min_vid = 0;
+	struct yt_port_mask macmask;
+	u32 entry = 0;
+	int index = -1;
+	u32 valid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!idx_ptr, CMM_ERR_NULL_POINT);
+	for (vlan_iso_index = 0; vlan_iso_index < 16; vlan_iso_index++) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit,
+			L2_VLAN_BASED_ISO_VID_RANGEN(vlan_iso_index),
+			&entry), ret);
+		HAL_FIELD_GET(L2_VLAN_BASED_ISO_VID_RANGEN_VALID_LOWBIT,
+			      L2_VLAN_BASED_ISO_VID_RANGEN_VALID_WIDTH, &entry,
+			      &valid);
+		HAL_FIELD_GET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_LOWBIT,
+			      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_WIDTH,
+			      &entry, &min_vid);
+		HAL_FIELD_GET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_LOWBIT,
+			      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_WIDTH,
+			      &entry, &max_vid);
+		if (valid) {
+			if (iso_info.max_vid == max_vid &&
+			    iso_info.min_vid == min_vid)
+				return CMM_ERR_SAMEENTRY_EXIST;
+			if ((iso_info.max_vid <= max_vid &&
+			     iso_info.max_vid >= min_vid) ||
+			    (iso_info.min_vid <= max_vid &&
+			     iso_info.max_vid >= min_vid))
+				return CMM_ERR_ENTRY_OVERLAPPING;
+			valid_num++;
+		} else {
+			if (index == -1)
+				index = vlan_iso_index;
+		}
+	}
+
+	if (valid_num == 16 || index == -1)
+		return CMM_ERR_ENTRY_FULL;
+
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VALID_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VALID_WIDTH, &entry, TRUE);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_WIDTH, &entry,
+		      iso_info.min_vid);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_WIDTH, &entry,
+		      iso_info.max_vid);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, L2_VLAN_BASED_ISO_VID_RANGEN(index),
+		 entry), ret);
+	tmp_iso_msk.portbits[0] = (~iso_info.iso_msk.portbits[0]) & 0x3FFU;
+	CAL_YTPLIST_TO_MLIST(unit, tmp_iso_msk, macmask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+		(unit, L2_VLAN_BASED_ISO_PORT_MASKN(index), &entry),
+		 ret);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_LOWBIT,
+		      L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_WIDTH, &entry,
+		      macmask.portbits[0]);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, L2_VLAN_BASED_ISO_PORT_MASKN(index),
+		 entry), ret);
+	*idx_ptr = index;
+	return ret;
+}
+EXPORT_SYMBOL(yt_isolation_vlan_add);
+
+u32 yt_isolation_vlan_del_by_index(u8 unit, u16 idx)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(idx >= 16U, CMM_ERR_EXCEED_RANGE);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_VID_RANGEN(idx),
+					&entry),
+		    ret);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VALID_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VALID_WIDTH, &entry, FALSE);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_WIDTH, &entry, 0);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_WIDTH, &entry, 0);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, L2_VLAN_BASED_ISO_VID_RANGEN(idx), entry),
+		 ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_PORT_MASKN(idx),
+					&entry), ret);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_LOWBIT,
+		      L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_WIDTH, &entry,
+		      0x3FF);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, L2_VLAN_BASED_ISO_PORT_MASKN(idx),
+		 entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_isolation_vlan_del_by_index);
+
+u32 yt_isolation_vlan_set_by_index(u8 unit, u16 idx,
+				   struct yt_iso_vlan_info iso_info)
+{
+	u16 vlan_iso_index = 0;
+	u32 max_vid = 0;
+	u32 min_vid = 0;
+	struct yt_port_mask macmask;
+	u32 entry = 0;
+	u32 valid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(idx >= 16U, CMM_ERR_EXCEED_RANGE);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_VID_RANGEN(idx),
+					&entry), ret);
+	HAL_FIELD_GET(L2_VLAN_BASED_ISO_VID_RANGEN_VALID_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VALID_WIDTH, &entry, &valid);
+	CMM_PARAM_CHK((valid == 0), CMM_ERR_ENTRY_INVALID);
+	for (vlan_iso_index = 0; vlan_iso_index < 16U; vlan_iso_index++) {
+		if (vlan_iso_index != idx) {
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+				(unit,
+				 L2_VLAN_BASED_ISO_VID_RANGEN(vlan_iso_index),
+				 &entry), ret);
+			HAL_FIELD_GET(L2_VLAN_BASED_ISO_VID_RANGEN_VALID_LOWBIT,
+				      L2_VLAN_BASED_ISO_VID_RANGEN_VALID_WIDTH,
+				      &entry, &valid);
+			HAL_FIELD_GET
+				(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_LOWBIT,
+				 L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_WIDTH,
+				 &entry, &min_vid);
+			HAL_FIELD_GET
+				(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_LOWBIT,
+				 L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_WIDTH,
+				 &entry, &max_vid);
+			if (valid) {
+				if (iso_info.max_vid == max_vid &&
+				    iso_info.min_vid == min_vid)
+					return CMM_ERR_SAMEENTRY_EXIST;
+				if ((iso_info.max_vid <= max_vid &&
+				     iso_info.max_vid >= min_vid) ||
+				    (iso_info.min_vid <= max_vid &&
+				     iso_info.max_vid >= min_vid))
+					return CMM_ERR_ENTRY_OVERLAPPING;
+			} else {
+				continue;
+			}
+		}
+	}
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_VID_RANGEN(idx),
+					&entry),
+		    ret);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VALID_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VALID_WIDTH, &entry, TRUE);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_WIDTH, &entry,
+		      iso_info.min_vid);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_WIDTH, &entry,
+		      iso_info.max_vid);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, L2_VLAN_BASED_ISO_VID_RANGEN(idx), entry), ret);
+	iso_info.iso_msk.portbits[0] = (~iso_info.iso_msk.portbits[0]) & 0x3FFU;
+	CAL_YTPLIST_TO_MLIST(unit, iso_info.iso_msk, macmask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_PORT_MASKN(idx),
+					&entry), ret);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_LOWBIT,
+		      L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_WIDTH, &entry,
+		      macmask.portbits[0]);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, L2_VLAN_BASED_ISO_PORT_MASKN(idx), entry), ret);
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_isolation_vlan_set_by_index);
+
+u32 yt_isolation_vlan_get_by_index(u8 unit, u16 idx,
+				   struct yt_iso_vlan_info *iso_info_ptr)
+{
+	struct yt_port_mask macmask;
+	u32 entry = 0;
+	u32 valid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(idx >= 16U, CMM_ERR_EXCEED_RANGE);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_VID_RANGEN(idx),
+					&entry),
+		    ret);
+	HAL_FIELD_GET(L2_VLAN_BASED_ISO_VID_RANGEN_VALID_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VALID_WIDTH, &entry, &valid);
+	CMM_PARAM_CHK((valid == 0), CMM_ERR_ENTRY_INVALID);
+	HAL_FIELD_GET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_WIDTH, &entry,
+		      &iso_info_ptr->min_vid);
+	HAL_FIELD_GET(L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_LOWBIT,
+		      L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_WIDTH, &entry,
+		      &iso_info_ptr->max_vid);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_PORT_MASKN(idx),
+					&entry),
+		    ret);
+	HAL_FIELD_GET(L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_LOWBIT,
+		      L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_WIDTH, &entry,
+		      &macmask.portbits[0]);
+	macmask.portbits[0] = (~macmask.portbits[0]) & 0x3FFU;
+	CAL_MLIST_TO_YTPLIST(unit, macmask, iso_info_ptr->iso_msk);
+	return ret;
+}
+EXPORT_SYMBOL(yt_isolation_vlan_get_by_index);
+
+u32 yt_isolation_vlan_type_set(u8 unit, enum yt_iso_vlan_type type)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_CTRL, &entry),
+		    ret);
+	HAL_FIELD_SET(L2_VLAN_BASED_ISO_CTRL_TYPE_LOWBIT,
+		      L2_VLAN_BASED_ISO_CTRL_TYPE_WIDTH, &entry, type);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_VLAN_BASED_ISO_CTRL, entry),
+		    ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_isolation_vlan_type_set);
+
+u32 yt_isolation_vlan_type_get(u8 unit, enum yt_iso_vlan_type *type_ptr)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(!type_ptr, CMM_ERR_NULL_POINT);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_BASED_ISO_CTRL, &entry),
+		    ret);
+	HAL_FIELD_GET(L2_VLAN_BASED_ISO_CTRL_TYPE_LOWBIT,
+		      L2_VLAN_BASED_ISO_CTRL_TYPE_WIDTH, &entry, type_ptr);
+	return ret;
+}
+EXPORT_SYMBOL(yt_isolation_vlan_type_get);
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_isolation.h b/drivers/net/dsa/motorcomm/switch/yt_isolation.h
new file mode 100644
index 000000000000..4b377f681485
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_isolation.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_ISOLATION_H
+#define __YT_ISOLATION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+#if defined(CONFIG_MOTORCOMM_YT922X)
+struct yt_iso_vlan_info {
+	u16 min_vid;
+	u16 max_vid;
+	struct yt_port_mask iso_msk;
+};
+
+enum yt_iso_vlan_type {
+	YT_ISO_VLAN_TYPE_FVID = 0,
+	YT_ISO_VLAN_TYPE_CVID,
+	YT_ISO_VLAN_TYPE_SVID
+};
+#endif
+
+/**
+ * @internal      yt_isolation_port_set
+ * @endinternal
+ *
+ * @brief         set allowed dest port mask of the port
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     isoPortMask        -the allowed dest portmask of specific port
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_isolation_port_set(u8 unit, u32 port,
+			  struct yt_port_mask iso_portmask);
+
+/**
+ * @internal      yt_isolation_port_get
+ * @endinternal
+ *
+ * @brief         get allowed dest port mask of the port
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    pIsoPortMask       -the allowed dest portmask of specific port
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_isolation_port_get(u8 unit, u32 port,
+			  struct yt_port_mask *iso_portmask_ptr);
+
+#if defined(CONFIG_MOTORCOMM_YT922X)
+/**
+ * @internal      yt_isolation_vlan_add
+ * @endinternal
+ *
+ * @brief         add isolation vlan info
+ * @note          APPLICABLE DEVICES  -Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     isoInfo             -vlan isolation info
+ * @param[out]    idx                 -the index of the vlan isolation entry
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_isolation_vlan_add(u8 unit, struct yt_iso_vlan_info iso_info,
+			  u16 *idx_ptr);
+
+/**
+ * @internal      yt_isolation_vlan_del_by_index
+ * @endinternal
+ *
+ * @brief         delete isolation vlan by index
+ * @note          APPLICABLE DEVICES  -Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     idx                 -the index of the vlan isolation entry
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_isolation_vlan_del_by_index(u8 unit, u16 idx);
+
+/**
+ * @internal      yt_isolation_vlan_set_by_index
+ * @endinternal
+ *
+ * @brief         set isolation vlan by index
+ * @note          APPLICABLE DEVICES  -Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     idx                 -the index of the vlan isolation entry
+ * @param[in]     isoInfo             -vlan isolation info
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_isolation_vlan_set_by_index(u8 unit, u16 idx,
+				   struct yt_iso_vlan_info iso_info);
+
+/**
+ * @internal      yt_isolation_vlan_get_by_index
+ * @endinternal
+ *
+ * @brief         get isolation vlan by index
+ * @note          APPLICABLE DEVICES  -Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     idx                 -the index of the vlan isolation entry
+ * @param[out]    isoInfo             -vlan isolation info
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32
+yt_isolation_vlan_get_by_index(u8 unit, u16 idx,
+			       struct yt_iso_vlan_info *iso_info_ptr);
+
+/**
+ * @internal      yt_isolation_vlan_type_set
+ * @endinternal
+ *
+ * @brief         set isolation vlan type
+ * @note          APPLICABLE DEVICES  -Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -the type of VLAN isolation
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_isolation_vlan_type_set(u8 unit, enum yt_iso_vlan_type type);
+
+/**
+ * @internal      yt_isolation_vlan_type_get
+ * @endinternal
+ *
+ * @brief         get isolation vlan type
+ * @note          APPLICABLE DEVICES  -Whale
+ * @param[in]     unit                -unit id
+ * @param[out]    pType               -the type of VLAN isolation
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_isolation_vlan_type_get(u8 unit, enum yt_iso_vlan_type *type_ptr);
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_l2.c b/drivers/net/dsa/motorcomm/switch/yt_l2.c
new file mode 100644
index 000000000000..5e3e6a964e67
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_l2.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include <linux/string.h>
+#include "yt_l2.h"
+
+static u32 yt_l2_op_result_get(u8 unit, struct yt_l2_fdb_op_result *op_result);
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+static u32
+yt_l2_fdb_ucast_addr_add_op(u8 unit, struct yt_l2_fdb_op op_mode,
+			    const struct yt_ucast_macaddr_info *ucast_info,
+			    struct yt_l2_fdb_op_result *op_result)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_port_mask mac_mask;
+	struct yt_port_mask port_mask;
+	u32 lag = 0;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 data2 = 0;
+	u32 op = 0;
+
+	memset(&mac_mask, 0, sizeof(struct yt_port_mask));
+	memset(&port_mask, 0, sizeof(struct yt_port_mask));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      ((ucast_info->macaddr.addr[0] << 24) |
+		       (ucast_info->macaddr.addr[1] << 16) |
+		       (ucast_info->macaddr.addr[2] << 8) |
+		       ucast_info->macaddr.addr[3]));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      ((ucast_info->macaddr.addr[4] << 8) |
+		       ucast_info->macaddr.addr[5]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1,
+		      ucast_info->vid);
+
+	if (ucast_info->type == YT_L2_FDB_TYPE_STATIC)
+		HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+			      FDB_STATUS_STATIC);
+	else if (ucast_info->type == YT_L2_FDB_TYPE_DYNAMIC)
+		HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+			      FDB_STATUS_MAX_TIME);
+	else
+		return CMM_ERR_NOT_SUPPORT;
+
+	if (ucast_info->islag) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+				(unit, LINK_AGG_GROUPN(ucast_info->port),
+				 &lag), ret);
+		HAL_FIELD_GET(LINK_AGG_GROUPN_PORT_MASK_LOWBIT,
+			      LINK_AGG_GROUPN_PORT_MASK_WIDTH, &lag,
+			      mac_mask.portbits);
+	} else {
+		CMM_SET_MEMBER_PORT(port_mask, ucast_info->port);
+		CAL_YTPLIST_TO_MLIST(unit, port_mask, mac_mask);
+	}
+	HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_WIDTH, &data2,
+		      mac_mask.portbits[0]);
+
+	if (ucast_info->action == YT_L2_FDB_ACTION_TYPE_DA_DROP) {
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_DMAC_DROP_WIDTH, &data2,
+			      TRUE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_SMAC_DROP_WIDTH, &data2,
+			      FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_WIDTH, &data2,
+			      FALSE);
+	} else if (ucast_info->action == YT_L2_FDB_ACTION_TYPE_SA_DROP) {
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_DMAC_DROP_WIDTH, &data2,
+			      FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_SMAC_DROP_WIDTH, &data2,
+			      TRUE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_WIDTH, &data2,
+			      FALSE);
+	} else if (ucast_info->action == YT_L2_FDB_ACTION_TYPE_SA_DA_DROP) {
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_DMAC_DROP_WIDTH, &data2,
+			      TRUE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_SMAC_DROP_WIDTH, &data2,
+			      TRUE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_WIDTH, &data2,
+			      FALSE);
+	} else if (ucast_info->action == YT_L2_FDB_ACTION_TYPE_COPY_TO_CPU) {
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_DMAC_DROP_WIDTH, &data2,
+			      FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_SMAC_DROP_WIDTH, &data2,
+			      FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_WIDTH, &data2,
+			      TRUE);
+	} else {
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_DMAC_DROP_WIDTH, &data2,
+			      FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_SMAC_DROP_WIDTH, &data2,
+			      FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_LOWBIT,
+			      L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_WIDTH, &data2,
+			      FALSE);
+	}
+
+	HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_WIDTH, &data2, 0);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_2, data2),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_ADD);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, op_mode.op_mode);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_ENTRY_INDEX_LOWBIT,
+		      L2_FDB_TBL_OP_ENTRY_INDEX_WIDTH, &op, op_mode.entry_idx);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_l2_op_result_get(unit, op_result), ret);
+
+	return ret;
+}
+
+static u32 yt_l2_op_info_get(u8 unit, struct yt_l2_fdb_info *fdb_info)
+{
+	u32 data0;
+	u32 data1;
+	u32 data2;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 mac_addr0 = 0;
+	u16 mac_addr1 = 0;
+
+	CMM_PARAM_CHK((!fdb_info), CMM_ERR_NULL_POINT);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_0_DUMMY,
+					&data0), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_1_DUMMY,
+					&data1), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_2_DUMMY,
+					&data2), ret);
+
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      &mac_addr0);
+	fdb_info->mac_addr.addr[0] = ((mac_addr0 & 0xFF000000) >> 24);
+	fdb_info->mac_addr.addr[1] = ((mac_addr0 & 0xFF0000) >> 16);
+	fdb_info->mac_addr.addr[2] = ((mac_addr0 & 0xFF00) >> 8);
+	fdb_info->mac_addr.addr[3] = (mac_addr0 & 0xFF);
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      &mac_addr1);
+	fdb_info->mac_addr.addr[4] = ((mac_addr1 & 0xFF00) >> 8);
+	fdb_info->mac_addr.addr[5] = (mac_addr1 & 0xFF);
+
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1,
+		      &fdb_info->fid);
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+		      &fdb_info->status);
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_DMAC_INT_PRI_EN_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_DMAC_INT_PRI_EN_WIDTH, &data1,
+		      &fdb_info->dmac_int_pri_en);
+
+	HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_WIDTH, &data2,
+		      &fdb_info->new_vid);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_INT_PRI_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_INT_PRI_WIDTH, &data2,
+		      &fdb_info->int_pri);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_SMAC_INT_PRI_EN_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_SMAC_INT_PRI_EN_WIDTH, &data2,
+		      &fdb_info->smac_int_pri_en);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_WIDTH, &data2,
+		      &fdb_info->copy_to_cpu);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_DMAC_DROP_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_DMAC_DROP_WIDTH, &data2,
+		      &fdb_info->dmac_drop);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_WIDTH, &data2,
+		      fdb_info->dst_port_mask.portbits);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_SMAC_DROP_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_SMAC_DROP_WIDTH, &data2,
+		      &fdb_info->smac_drop);
+
+	return ret;
+}
+
+static u32 yt_l2_lag_group_get(u8 unit, u32 port, u8 *group_id)
+{
+	u8 index = 0;
+	u32 lag_entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_port_mask mac_mask;
+	struct yt_port_mask port_mask;
+
+	memset(&mac_mask, 0, sizeof(struct yt_port_mask));
+	memset(&port_mask, 0, sizeof(struct yt_port_mask));
+
+	for (index = 0; index < LAG_GROUP_NUM_MAX; index++) {
+		lag_entry = 0;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+						LINK_AGG_GROUPN(index * 4),
+						&lag_entry), ret);
+		HAL_FIELD_GET(LINK_AGG_GROUPN_PORT_MASK_LOWBIT,
+			      LINK_AGG_GROUPN_PORT_MASK_WIDTH, &lag_entry,
+			      mac_mask.portbits);
+		CAL_MLIST_TO_YTPLIST(unit, mac_mask, port_mask);
+		if (IS_BIT_SET(port_mask.portbits[0], port) != 0) {
+			*group_id = index;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static u32 yt_l2_uc_info_set(u8 unit, struct yt_l2_fdb_info fdb_info,
+			     struct yt_ucast_macaddr_info *ucast_info)
+{
+	struct yt_mac_addr mac_addr;
+	u32 port = 0;
+	u8 mac_id = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u8 lag_grp_id = LAG_GROUP_NUM_MAX;
+
+	memcpy(mac_addr.addr, fdb_info.mac_addr.addr, MAC_ADDR_LEN);
+
+	if (fdb_info.status == FDB_STATUS_INVALID ||
+	    IS_MCAST_ADDR(mac_addr.addr))
+		return CMM_ERR_ENTRY_NOT_FOUND;
+
+	for (port = 0; port < YT_MAX_PORT_PER_UNIT; port++) {
+		mac_id = CAL_YTP_TO_MAC(unit, port);
+		if (IS_BIT_SET(fdb_info.dst_port_mask.portbits[0], mac_id))
+			break;
+	}
+	ucast_info->port = port;
+
+	CMM_ERR_CHK(yt_l2_lag_group_get(unit, ucast_info->port, &lag_grp_id),
+		    ret);
+	if (lag_grp_id != LAG_GROUP_NUM_MAX) {
+		ucast_info->port = lag_grp_id;
+		ucast_info->islag = TRUE;
+	} else {
+		ucast_info->islag = FALSE;
+	}
+
+	ucast_info->vid = fdb_info.fid;
+	memcpy(ucast_info->macaddr.addr, mac_addr.addr, MAC_ADDR_LEN);
+
+	if (fdb_info.status == FDB_STATUS_PENDING)
+		ucast_info->type = YT_L2_FDB_TYPE_PENDING;
+	else if (fdb_info.status == FDB_STATUS_STATIC)
+		ucast_info->type = YT_L2_FDB_TYPE_STATIC;
+	else
+		ucast_info->type = YT_L2_FDB_TYPE_DYNAMIC;
+
+	if (fdb_info.dmac_drop != 0 && fdb_info.smac_drop == 0)
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_DA_DROP;
+	else if (fdb_info.dmac_drop == 0 && fdb_info.smac_drop != 0)
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_SA_DROP;
+	else if (fdb_info.dmac_drop != 0 && fdb_info.smac_drop != 0)
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_SA_DA_DROP;
+	else if (fdb_info.copy_to_cpu)
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_COPY_TO_CPU;
+	else
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_FORWARD;
+
+	return ret;
+}
+
+static u32 yt_l2_fdb_op_flush(u8 unit, struct yt_l2_tbl_flush_ctrl op_flush)
+{
+	struct yt_l2_fdb_op_result op_result;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 data2 = 0;
+	u32 op = 0;
+
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1, op_flush.fid);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_WIDTH, &data2,
+		      op_flush.port_mask.portbits[0]);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_2, data2),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_FLUSH);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_FLUSH_MODE_LOWBIT,
+		      L2_FDB_TBL_OP_FLUSH_MODE_WIDTH, &op, op_flush.mode);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_FLUSH_STATIC_EN_LOWBIT,
+		      L2_FDB_TBL_OP_FLUSH_STATIC_EN_WIDTH, &op,
+		      op_flush.flush_static_en);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_l2_op_result_get(unit, &op_result), ret);
+
+	return ret;
+}
+
+u32 yt_l2_fdb_ucast_flush(u8 unit, struct yt_l2_uc_flush_info flush_info)
+{
+	struct yt_l2_tbl_flush_ctrl flush_op;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 lag = 0;
+
+	memset(&flush_op, 0, sizeof(struct yt_l2_tbl_flush_ctrl));
+
+	if (flush_info.flush_mode == YT_L2_FDB_FLUSH_PORT)
+		flush_op.mode = L2_FDB_FLUSH_MODE_UFDB_PORT;
+	else if (flush_info.flush_mode == YT_L2_FDB_FLUSH_VLAN)
+		flush_op.mode = L2_FDB_FLUSH_MODE_UFDB_FID;
+	else if (flush_info.flush_mode == YT_L2_FDB_FLUSH_PORT_VLAN)
+		flush_op.mode = L2_FDB_FLUSH_MODE_UFDB_PORT_FID;
+	else
+		flush_op.mode = L2_FDB_FLUSH_MODE_UFDB_ALL;
+
+	flush_op.fid = flush_info.vid;
+	flush_op.flush_static_en = flush_info.flush_static_en;
+
+	if (flush_info.is_lag) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+				(unit, LINK_AGG_GROUPN(flush_info.port),
+				 &lag), ret);
+		HAL_FIELD_GET(LINK_AGG_GROUPN_PORT_MASK_LOWBIT,
+			      LINK_AGG_GROUPN_PORT_MASK_WIDTH, &lag,
+			      flush_op.port_mask.portbits);
+	} else {
+		CMM_SET_MEMBER_PORT(flush_op.port_mask,
+				    CAL_YTP_TO_MAC(unit, flush_info.port));
+	}
+
+	ret = yt_l2_fdb_op_flush(unit, flush_op);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_fdb_ucast_flush);
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+static u32
+yt_l2_fdb_ucast_addr_add_op(u8 unit, struct yt_l2_fdb_op op_mode,
+			    const struct yt_ucast_macaddr_info *ucast_info,
+			    struct yt_l2_fdb_op_result *op_result)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 data2 = 0;
+	u32 op = 0;
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      ((ucast_info->macaddr.addr[0] << 24) |
+		       (ucast_info->macaddr.addr[1] << 16) |
+		       (ucast_info->macaddr.addr[2] << 8) |
+		       ucast_info->macaddr.addr[3]));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      ((ucast_info->macaddr.addr[4] << 8) |
+		       ucast_info->macaddr.addr[5]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1,
+		      ucast_info->vid);
+
+	if (ucast_info->type == YT_L2_FDB_TYPE_STATIC)
+		HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+			      FDB_STATUS_STATIC);
+	else if (ucast_info->type == YT_L2_FDB_TYPE_DYNAMIC)
+		HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+			      FDB_STATUS_MAX_TIME);
+	else
+		return CMM_ERR_NOT_SUPPORT;
+
+	if (ucast_info->islag) {
+		HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_WIDTH, &data2,
+			      TRUE);
+		HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_WIDTH,
+			      &data2, ucast_info->port);
+	} else {
+		HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_WIDTH, &data2,
+			      FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_WIDTH,
+			      &data2, CAL_YTP_TO_MAC(unit, ucast_info->port));
+	}
+
+	if (ucast_info->action == YT_L2_FDB_ACTION_TYPE_DA_DROP) {
+		HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_WIDTH,
+			      &data2, TRUE);
+		HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_WIDTH, &data1,
+			      FALSE);
+	} else if (ucast_info->action == YT_L2_FDB_ACTION_TYPE_SA_DROP) {
+		HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_WIDTH,
+			      &data2, FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_WIDTH, &data1,
+			      TRUE);
+	} else if (ucast_info->action == YT_L2_FDB_ACTION_TYPE_SA_DA_DROP) {
+		HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_WIDTH,
+			      &data2, TRUE);
+		HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_WIDTH, &data1,
+			      TRUE);
+	} else {
+		HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_WIDTH,
+			      &data2, FALSE);
+		HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_WIDTH, &data1,
+			      FALSE);
+	}
+
+	HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_WIDTH, &data2, 0);
+	HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_MOVE_AGING_STATUS_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_MOVE_AGING_STATUS_WIDTH,
+		      &data2, 0);
+	HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_PENDING_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_PENDING_WIDTH, &data2, 0);
+	HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_IP_MC_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_IP_MC_WIDTH, &data2, 0);
+	HAL_FIELD_SET(L2_FDB_TBL_UC_MAC_OP_DATA_2_VALID_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_VALID_WIDTH, &data2, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_2, data2),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_ADD);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, op_mode.op_mode);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_ENTRY_INDEX_LOWBIT,
+		      L2_FDB_TBL_OP_ENTRY_INDEX_WIDTH, &op, op_mode.entry_idx);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_l2_op_result_get(unit, op_result), ret);
+
+	return ret;
+}
+
+static u32 yt_l2_op_info_get(u8 unit, struct yt_l2_fdb_info *fdb_info)
+{
+	u32 data0;
+	u32 data1;
+	u32 data2;
+	u32 data3;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 mac_addr0 = 0;
+	u16 mac_addr1 = 0;
+	u32 ip_mc = 0;
+	u32 lag_en = 0;
+	u32 dst_port = 0;
+	u32 mc_pmsk0 = 0;
+	u32 mc_pmsk1 = 0;
+	u8 is_mc = 0;
+	struct yt_port_mask macmask;
+
+	CMM_CLEAR_MEMBER_PORT(macmask);
+
+	CMM_PARAM_CHK((!fdb_info), CMM_ERR_NULL_POINT);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_0_DUMMY,
+					&data0), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_1_DUMMY,
+					&data1), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_2_DUMMY,
+					&data2), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_3_DUMMY,
+					&data3), ret);
+
+	HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_IP_MC_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_IP_MC_WIDTH, &data2, &ip_mc);
+
+	if (ip_mc == 0) {
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+			      &mac_addr0);
+		fdb_info->mac_addr.addr[0] = ((mac_addr0 & 0xFF000000) >> 24);
+		fdb_info->mac_addr.addr[1] = ((mac_addr0 & 0xFF0000) >> 16);
+		fdb_info->mac_addr.addr[2] = ((mac_addr0 & 0xFF00) >> 8);
+		fdb_info->mac_addr.addr[3] = (mac_addr0 & 0xFF);
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+			      &mac_addr1);
+		fdb_info->mac_addr.addr[4] = ((mac_addr1 & 0xFF00) >> 8);
+		fdb_info->mac_addr.addr[5] = (mac_addr1 & 0xFF);
+		if (!IS_BIT_SET(mac_addr0, L2_MCAST_BIT)) {
+			HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_LOWBIT,
+				      L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_WIDTH,
+				      &data2, &lag_en);
+			HAL_FIELD_GET
+				(L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_LOWBIT,
+				L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_WIDTH,
+				&data2, &dst_port);
+
+			if (lag_en) {
+				macmask.portbits[0] = 1 << dst_port;
+				fdb_info->lag_en = TRUE;
+			} else {
+				macmask.portbits[0] = 1 << dst_port;
+			}
+
+			HAL_FIELD_GET
+				(L2_FDB_TBL_UC_MAC_OP_DATA_2_PENDING_LOWBIT,
+				L2_FDB_TBL_UC_MAC_OP_DATA_2_PENDING_WIDTH,
+				&data2, &fdb_info->pending);
+		} else {
+			is_mc = 1;
+		}
+		HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_WIDTH, &data2,
+			      &fdb_info->new_vid);
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1,
+			      &fdb_info->fid);
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+			      &fdb_info->status);
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_WIDTH, &data1,
+			      &fdb_info->sa_drop);
+		HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_WIDTH,
+			      &data2, &fdb_info->da_drop);
+	}
+
+	if (is_mc || ip_mc) {
+		HAL_FIELD_GET(L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_PMSK_0_LOWBIT,
+			      L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_PMSK_0_WIDTH,
+			      &data2, &mc_pmsk0);
+		HAL_FIELD_GET(L2_FDB_TBL_MC_MAC_OP_DATA_3_MC_PMSK_1_LOWBIT,
+			      L2_FDB_TBL_MC_MAC_OP_DATA_3_MC_PMSK_1_WIDTH,
+			      &data3, &mc_pmsk1);
+		macmask.portbits[0] = (mc_pmsk1 << 8) | mc_pmsk0;
+		HAL_FIELD_GET(L2_FDB_TBL_MC_MAC_OP_DATA_3_IS_MC_HW_LOWBIT,
+			      L2_FDB_TBL_MC_MAC_OP_DATA_3_IS_MC_HW_WIDTH,
+			      &data3, &fdb_info->hw_mc);
+	}
+
+	CAL_MLIST_TO_YTPLIST(unit, macmask, fdb_info->dst_port_mask);
+
+	fdb_info->ip_mc = ip_mc;
+
+	return ret;
+}
+
+static u32 yt_l2_uc_info_set(u8 unit, struct yt_l2_fdb_info fdb_info,
+			     struct yt_ucast_macaddr_info *ucast_info)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_mac_addr mac_addr;
+	u8 mac_id = 0;
+
+	memcpy(mac_addr.addr, fdb_info.mac_addr.addr, MAC_ADDR_LEN);
+
+	if (fdb_info.status == FDB_STATUS_INVALID ||
+	    IS_MCAST_ADDR(mac_addr.addr)) {
+		return CMM_ERR_ENTRY_NOT_FOUND;
+	}
+
+	if (fdb_info.lag_en) {
+		ucast_info->islag = TRUE;
+		for (mac_id = 0; mac_id < YT_MAX_PORT_PER_UNIT; mac_id++) {
+			if (IS_BIT_SET(fdb_info.dst_port_mask.portbits[0],
+				       mac_id)) {
+				break;
+			}
+		}
+		ucast_info->port = mac_id;
+	} else {
+		ucast_info->islag = FALSE;
+		for (mac_id = 0; mac_id < YT_MAX_PORT_PER_UNIT; mac_id++) {
+			if (IS_BIT_SET(fdb_info.dst_port_mask.portbits[0],
+				       mac_id)) {
+				break;
+			}
+		}
+		CAL_MAC_TO_YTP(unit, mac_id, ucast_info->port);
+	}
+
+	ucast_info->vid = fdb_info.fid;
+	memcpy(ucast_info->macaddr.addr, mac_addr.addr, MAC_ADDR_LEN);
+
+	if (fdb_info.pending) {
+		ucast_info->type = YT_L2_FDB_TYPE_PENDING;
+	} else {
+		if (fdb_info.status == FDB_STATUS_STATIC)
+			ucast_info->type = YT_L2_FDB_TYPE_STATIC;
+		else
+			ucast_info->type = YT_L2_FDB_TYPE_DYNAMIC;
+	}
+
+	if (fdb_info.sa_drop != 0 && fdb_info.da_drop == 0)
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_SA_DROP;
+	else if (fdb_info.sa_drop == 0 && fdb_info.da_drop != 0)
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_DA_DROP;
+	else if (fdb_info.sa_drop != 0 && fdb_info.da_drop != 0)
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_SA_DA_DROP;
+	else
+		ucast_info->action = YT_L2_FDB_ACTION_TYPE_FORWARD;
+
+	return ret;
+}
+
+static u32 yt_l2_fdb_op_flush(u8 unit, struct yt_l2_tbl_flush_ctrl op_flush)
+{
+	struct yt_l2_fdb_op_result op_result;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 data2 = 0;
+	u32 data3 = 0;
+	u32 op = 0;
+
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+
+	HAL_FIELD_SET(L2_FDB_TBL_FLUSH_OP_DATA_0_FID_LOWBIT,
+		      L2_FDB_TBL_FLUSH_OP_DATA_0_FID_WIDTH, &data0,
+		      op_flush.fid);
+	HAL_FIELD_SET(L2_FDB_TBL_FLUSH_OP_DATA_0_DST_PORT_LOWBIT,
+		      L2_FDB_TBL_FLUSH_OP_DATA_0_DST_PORT_WIDTH, &data0,
+		      op_flush.port);
+	HAL_FIELD_SET(L2_FDB_TBL_FLUSH_OP_DATA_0_LAG_EN_LOWBIT,
+		      L2_FDB_TBL_FLUSH_OP_DATA_0_LAG_EN_WIDTH, &data0,
+		      op_flush.is_lag);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_2, data2),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_3, data3),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_FLUSH);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_FLUSH_MODE_LOWBIT,
+		      L2_FDB_TBL_OP_FLUSH_MODE_WIDTH, &op, op_flush.mode);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_FLUSH_STATIC_EN_LOWBIT,
+		      L2_FDB_TBL_OP_FLUSH_STATIC_EN_WIDTH, &op,
+		      op_flush.flush_static_en);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_l2_op_result_get(unit, &op_result), ret);
+
+	return ret;
+}
+
+u32 yt_l2_fdb_ucast_flush(u8 unit, struct yt_l2_uc_flush_info flush_info)
+{
+	struct yt_l2_tbl_flush_ctrl flush_op;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	memset(&flush_op, 0, sizeof(struct yt_l2_tbl_flush_ctrl));
+
+	if (flush_info.flush_mode == YT_L2_FDB_FLUSH_PORT)
+		flush_op.mode = L2_FDB_FLUSH_MODE_UFDB_PORT;
+	else if (flush_info.flush_mode == YT_L2_FDB_FLUSH_VLAN)
+		flush_op.mode = L2_FDB_FLUSH_MODE_UFDB_FID;
+	else if (flush_info.flush_mode == YT_L2_FDB_FLUSH_PORT_VLAN)
+		flush_op.mode = L2_FDB_FLUSH_MODE_UFDB_PORT_FID;
+	else
+		flush_op.mode = L2_FDB_FLUSH_MODE_UFDB_ALL;
+
+	flush_op.fid = flush_info.vid;
+	flush_op.flush_static_en = flush_info.flush_static_en;
+	if (flush_info.is_lag) {
+		flush_op.is_lag = YT_ENABLE;
+		flush_op.port = flush_info.port;
+	} else {
+		flush_op.port = CAL_YTP_TO_MAC(unit, flush_info.port);
+	}
+
+	ret = yt_l2_fdb_op_flush(unit, flush_op);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_fdb_ucast_flush);
+#endif
+
+static u32 yt_l2_op_result_get(u8 unit, struct yt_l2_fdb_op_result *op_result)
+{
+	u32 result_tmp = 0;
+	u32 result = 0;
+	u16 busy_cnt = FDB_BUSY_CHECK_NUMBER;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 op_done = 0;
+	u32 index = 0;
+	u32 overwrite = 0;
+	u32 lookup_fail = 0;
+
+	CMM_PARAM_CHK((!op_result), CMM_ERR_NULL_POINT);
+
+	while (busy_cnt) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_RESULT,
+						&result), ret);
+
+		HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_OP_DONE_LOWBIT,
+			      L2_FDB_TBL_OP_RESULT_OP_DONE_WIDTH, &result,
+			      &op_done);
+		if (op_done) {
+			HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_ENTRY_INDEX_LOWBIT,
+				      L2_FDB_TBL_OP_RESULT_ENTRY_INDEX_WIDTH,
+				      &result, &index);
+			HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_OP_RESULT_LOWBIT,
+				      L2_FDB_TBL_OP_RESULT_OP_RESULT_WIDTH,
+				      &result, &result_tmp);
+			HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_OVERWRITE_LOWBIT,
+				      L2_FDB_TBL_OP_RESULT_OVERWRITE_WIDTH,
+				      &result, &overwrite);
+			HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_LOOKUP_FAIL_LOWBIT,
+				      L2_FDB_TBL_OP_RESULT_LOOKUP_FAIL_WIDTH,
+				      &result, &lookup_fail);
+
+			op_result->entry_idx = index;
+			op_result->op_result = result_tmp;
+			op_result->overwrite = overwrite;
+			op_result->lookup_fail = lookup_fail;
+			op_result->op_done = TRUE;
+			break;
+		}
+
+		busy_cnt--;
+		if (busy_cnt == 0) {
+			op_result->lookup_fail = TRUE;
+			op_result->op_done = FALSE;
+
+			return CMM_ERR_FDB_OP_BUSY;
+		}
+	}
+
+	return ret;
+}
+
+u32 yt_l2_fdb_ucast_addr_add(u8 unit,
+			     const struct yt_ucast_macaddr_info *ucast_info)
+{
+	struct yt_l2_fdb_op op_mode;
+	struct yt_l2_fdb_op_result op_result;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	memset(&op_mode, 0, sizeof(struct yt_l2_fdb_op));
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+
+	op_mode.op_mode = L2_FDB_OP_MODE_HASH;
+	op_mode.entry_idx = 0;
+
+	CMM_ERR_CHK(yt_l2_fdb_ucast_addr_add_op(unit, op_mode, ucast_info,
+						&op_result), ret);
+
+	if (op_result.op_result == 1U && op_result.lookup_fail == 1U) {
+		if (ucast_info->force_flag && op_result.overwrite == 1U) {
+			op_mode.op_mode = L2_FDB_OP_MODE_INDEX;
+			op_mode.entry_idx = op_result.entry_idx;
+
+			CMM_ERR_CHK(yt_l2_fdb_ucast_addr_add_op
+					(unit, op_mode,
+					 ucast_info,
+					 &op_result), ret);
+
+			if (op_result.op_result == 1U)
+				return CMM_ERR_FAIL;
+		} else {
+			return CMM_ERR_ENTRY_FULL;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_fdb_ucast_addr_add);
+
+u32 yt_l2_fdb_ucast_addr_del(u8 unit, u16 vid, struct yt_mac_addr mac_addr)
+{
+	struct yt_l2_fdb_op_result op_result;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 data2 = 0;
+	u32 op = 0;
+
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+
+	if (IS_MCAST_ADDR(mac_addr.addr))
+		return CMM_ERR_INPUT;
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      ((mac_addr.addr[0] << 24) | (mac_addr.addr[1] << 16) |
+		       (mac_addr.addr[2] << 8) | mac_addr.addr[3]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      ((mac_addr.addr[4] << 8) | mac_addr.addr[5]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1, vid);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_2, data2),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_DEL);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, L2_FDB_OP_MODE_HASH);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_l2_op_result_get(unit, &op_result), ret);
+
+	if (op_result.lookup_fail)
+		return CMM_ERR_ENTRY_NOT_FOUND;
+	else if (op_result.op_result)
+		return CMM_ERR_FAIL;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_fdb_ucast_addr_del);
+
+u32 yt_l2_fdb_uc_withindex_get(u8 unit, u16 index,
+			       struct yt_ucast_macaddr_info *ucast_info)
+{
+	struct yt_l2_fdb_op_result op_result;
+	struct yt_l2_fdb_info fdb_info;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 op = 0;
+
+	CMM_PARAM_CHK((!ucast_info), CMM_ERR_NULL_POINT);
+
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+	memset(&fdb_info, 0, sizeof(struct yt_l2_fdb_info));
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_GET_ONE);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, L2_FDB_OP_MODE_INDEX);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_ENTRY_INDEX_LOWBIT,
+		      L2_FDB_TBL_OP_ENTRY_INDEX_WIDTH, &op, index);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_l2_op_result_get(unit, &op_result), ret);
+
+	CMM_ERR_CHK(yt_l2_op_info_get(unit, &fdb_info), ret);
+
+	CMM_ERR_CHK(yt_l2_uc_info_set(unit, fdb_info, ucast_info), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_fdb_uc_withindex_get);
+
+u32 yt_l2_fdb_uc_withindex_getnext(u8 unit, u16 index, u16 *next_index_ptr,
+				   struct yt_ucast_macaddr_info *ucast_info)
+{
+	struct yt_l2_fdb_op_result op_result;
+	struct yt_l2_fdb_info fdb_info;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 op = 0;
+
+	CMM_PARAM_CHK((!ucast_info), CMM_ERR_NULL_POINT);
+
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+	memset(&fdb_info, 0, sizeof(struct yt_l2_fdb_info));
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_GET_NEXT);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_GET_NEXT_TYPE_LOWBIT,
+		      L2_FDB_TBL_OP_GET_NEXT_TYPE_WIDTH, &op,
+		      L2_OP_GETNEXT_TYPE_UCAST);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, L2_FDB_OP_MODE_INDEX);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_ENTRY_INDEX_LOWBIT,
+		      L2_FDB_TBL_OP_ENTRY_INDEX_WIDTH, &op, index);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_l2_op_result_get(unit, &op_result), ret);
+	if (op_result.lookup_fail)
+		return CMM_ERR_ENTRY_NOT_FOUND;
+
+	*next_index_ptr = op_result.entry_idx;
+
+	CMM_ERR_CHK(yt_l2_op_info_get(unit, &fdb_info), ret);
+
+	CMM_ERR_CHK(yt_l2_uc_info_set(unit, fdb_info, ucast_info), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_fdb_uc_withindex_getnext);
+
+u32 yt_l2_fdb_uc_get(u8 unit, u16 vid, struct yt_mac_addr mac_addr,
+		     struct yt_ucast_macaddr_info *ucast_info)
+{
+	struct yt_l2_fdb_op_result op_result;
+	struct yt_l2_fdb_info fdb_info;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 op = 0;
+
+	CMM_PARAM_CHK((!ucast_info), CMM_ERR_NULL_POINT);
+
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+	memset(&fdb_info, 0, sizeof(struct yt_l2_fdb_info));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      ((mac_addr.addr[0] << 24) | (mac_addr.addr[1] << 16) |
+		       (mac_addr.addr[2] << 8) | mac_addr.addr[3]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      ((mac_addr.addr[4] << 8) | mac_addr.addr[5]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1, vid);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_GET_ONE);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, L2_FDB_OP_MODE_HASH);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_l2_op_result_get(unit, &op_result), ret);
+	if (op_result.lookup_fail)
+		return CMM_ERR_ENTRY_NOT_FOUND;
+
+	CMM_ERR_CHK(yt_l2_op_info_get(unit, &fdb_info), ret);
+
+	CMM_ERR_CHK(yt_l2_uc_info_set(unit, fdb_info, ucast_info), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_fdb_uc_get);
+
+u32 yt_l2_fdb_aging_time_set(u8 unit, u32 sec)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 aging_ctrl = 0;
+	u32 age_time = 0;
+
+	if (sec > FDB_STATUS_MAX_TIME * 65535)
+		return CMM_ERR_INPUT;
+
+	if (sec < FDB_STATUS_MAX_TIME)
+		age_time = 1;
+	else
+		age_time = (sec / FDB_STATUS_MAX_TIME);
+
+	HAL_FIELD_SET(L2_AGING_CTRL_AGING_INTERVAL_LOWBIT,
+		      L2_AGING_CTRL_AGING_INTERVAL_WIDTH, &aging_ctrl,
+		      age_time);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_AGING_CTRL, aging_ctrl), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_fdb_aging_time_set);
+
+u32 yt_l2_port_learn_en_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_LEARN_PER_PORT_CTRL + macid * 4,
+			 &entry), ret);
+
+	HAL_FIELD_SET(L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT,
+		      L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH, &entry,
+		      enable ? YT_DISABLE : YT_ENABLE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, L2_LEARN_PER_PORT_CTRL + macid * 4,
+			 entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_port_learn_en_set);
+
+u32 yt_l2_port_learn_en_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	u32 entry = 0;
+	enum yt_enable tmp = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+			(unit, L2_LEARN_PER_PORT_CTRL + macid * 4, &entry),
+			ret);
+
+	HAL_FIELD_GET(L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT,
+		      L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH, &entry,
+		      &tmp);
+	*enable = tmp ? YT_DISABLE : YT_ENABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_l2_port_learn_en_get);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_l2.h b/drivers/net/dsa/motorcomm/switch/yt_l2.h
new file mode 100644
index 000000000000..04bf03ff0cf8
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_l2.h
@@ -0,0 +1,329 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_L2_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+#include "yt_types.h"
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+#define FDB_STATUS_MAX_TIME (5)
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+#define FDB_STATUS_MAX_TIME (6)
+#endif
+#define FDB_STATUS_PENDING (6)
+#define FDB_STATUS_STATIC (7)
+#define FDB_STATUS_INVALID (0)
+#define FDB_BUSY_CHECK_NUMBER (1000)
+#define L2_MCAST_BIT (24)
+
+#define LAG_GROUP_NUM_MAX 2
+
+enum yt_l2_fdb_type {
+	YT_L2_FDB_TYPE_INVALID,
+	YT_L2_FDB_TYPE_DYNAMIC,
+	YT_L2_FDB_TYPE_STATIC,
+	YT_L2_FDB_TYPE_PENDING,
+};
+
+enum yt_l2_ucast_macaddr_action {
+	YT_L2_FDB_ACTION_TYPE_FORWARD,
+	YT_L2_FDB_ACTION_TYPE_DA_DROP,
+	YT_L2_FDB_ACTION_TYPE_SA_DROP,
+#if defined(CONFIG_MOTORCOMM_YT921X)
+	YT_L2_FDB_ACTION_TYPE_COPY_TO_CPU,
+#endif
+	YT_L2_FDB_ACTION_TYPE_SA_DA_DROP,
+	YT_L2_FDB_ACTION_TYPE_MAX,
+};
+
+struct yt_ucast_macaddr_info {
+	u32 port;
+	bool islag;
+	u16 vid;
+	struct yt_mac_addr macaddr;
+	enum yt_l2_fdb_type type;
+	enum yt_l2_ucast_macaddr_action action;
+	bool force_flag;
+};
+
+struct yt_l2_fdb_op_result {
+	u16 entry_idx;
+	u8 op_result;
+	u8 overwrite;
+	u8 lookup_fail;
+	u8 op_done;
+};
+
+enum yt_l2_fdb_op_mode {
+	L2_FDB_OP_MODE_HASH,
+	L2_FDB_OP_MODE_INDEX,
+};
+
+struct yt_l2_fdb_op {
+	enum yt_l2_fdb_op_mode op_mode;
+	u16 entry_idx;
+	u8 key_type;
+};
+
+enum yt_l2_fdb_op_cmd {
+	L2_FDB_OP_CMD_ADD,
+	L2_FDB_OP_CMD_DEL,
+	L2_FDB_OP_CMD_GET_ONE,
+	L2_FDB_OP_CMD_GET_NEXT,
+	L2_FDB_OP_CMD_FLUSH,
+#if defined(CONFIG_MOTORCOMM_YT922X)
+	L2_FDB_OP_CMD_HWMC_FLUSH,
+	L2_FDB_OP_CMD_HWMC_DEL,
+#endif
+};
+
+enum yt_l2_fdb_op_get_next_type {
+	L2_OP_GETNEXT_TYPE_UCAST_PORT,
+	L2_OP_GETNEXT_TYPE_UCAST_FID,
+	L2_OP_GETNEXT_TYPE_UCAST,
+	L2_OP_GETNEXT_TYPE_MCAST,
+	L2_OP_GETNEXT_TYPE_IPMC,
+};
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+struct yt_l2_fdb_info {
+	struct yt_mac_addr mac_addr;
+	u16 fid;
+	u8 status;
+	u8 dmac_int_pri_en;
+	u16 new_vid;
+	u8 int_pri;
+	u8 smac_int_pri_en;
+	u8 copy_to_cpu;
+	u8 dmac_drop;
+	struct yt_port_mask dst_port_mask;
+	u8 smac_drop;
+};
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+struct yt_l2_fdb_info {
+	struct yt_port_mask dst_port_mask;
+	struct yt_mac_addr mac_addr;
+	u16 fid;
+	u32 mac_id;
+	u8 ip_mc;
+	u8 hw_mc;
+	u8 lag_en;
+	u8 lag_id;
+	u8 status;
+	u8 pending;
+	u16 new_vid;
+	u16 entry_id;
+	u8 sa_drop;
+	u8 da_drop;
+};
+#endif
+
+enum yt_l2_uc_flush_mode {
+	YT_L2_FDB_FLUSH_PORT,
+	YT_L2_FDB_FLUSH_VLAN,
+	YT_L2_FDB_FLUSH_PORT_VLAN,
+	YT_L2_FDB_FLUSH_ALL
+};
+
+struct yt_l2_uc_flush_info {
+	u32 port;
+	bool is_lag;
+	u16 vid;
+	enum yt_l2_uc_flush_mode flush_mode;
+	bool flush_static_en;
+};
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+enum yt_l2_tbl_op_flush_mode {
+	L2_FDB_FLUSH_MODE_UFDB_ALL,
+	L2_FDB_FLUSH_MODE_UFDB_PORT,
+	L2_FDB_FLUSH_MODE_UFDB_PORT_FID,
+	L2_FDB_FLUSH_MODE_UFDB_FID,
+	L2_FDB_FLUSH_MODE_MFDB_ALL,
+	L2_FDB_FLUSH_MODE_MFDB_PORT,
+	L2_FDB_FLUSH_MODE_MFDB_PORT_FID,
+	L2_FDB_FLUSH_MODE_MFDB_FID,
+};
+
+struct yt_l2_tbl_flush_ctrl {
+	enum yt_l2_tbl_op_flush_mode mode;
+	u8 flush_static_en;
+	u16 fid;
+	struct yt_port_mask port_mask;
+};
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+enum yt_l2_tbl_op_flush_mode {
+	L2_FDB_FLUSH_MODE_UFDB_ALL,
+	L2_FDB_FLUSH_MODE_UFDB_PORT,
+	L2_FDB_FLUSH_MODE_UFDB_PORT_FID,
+	L2_FDB_FLUSH_MODE_UFDB_FID,
+	L2_FDB_FLUSH_MODE_MFDB_ALL,
+	L2_FDB_FLUSH_MODE_MFDB_FID,
+	L2_FDB_FLUSH_MODE_IPMC_ALL,
+	L2_FDB_FLUSH_MODE_IPMC_FID,
+};
+
+struct yt_l2_tbl_flush_ctrl {
+	enum yt_l2_tbl_op_flush_mode mode;
+	u8 flush_static_en;
+	u16 fid;
+	u32 port;
+	enum yt_enable is_lag;
+};
+#endif
+
+#define IS_MCAST_ADDR(paddr) (((paddr[0] & 0x01) != 0) ? TRUE : FALSE)
+
+/**
+ * @internal      yt_l2_fdb_ucast_addr_add
+ * @endinternal
+ *
+ * @brief         Add unicast fdb entry
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     ucast_info      -unicast info pointer
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY -fdb op busy
+ * @retval        CMM_ERR_ENTRY_FULL  -fdb table full
+ */
+u32 yt_l2_fdb_ucast_addr_add(u8 unit,
+			     const struct yt_ucast_macaddr_info *ucast_info);
+
+/**
+ * @internal      yt_l2_fdb_ucast_addr_del
+ * @endinternal
+ *
+ * @brief         Delete unicast fdb entry
+ * @note          APPLICABLE DEVICES      -Tiger, Whale
+ * @param[in]     unit                    -unit id
+ * @param[in]     vid                     -vlan id
+ * @param[in]     mac_addr                -mac addr
+ * @retval        CMM_ERR_OK              -on success
+ * @retval        CMM_ERR_FAIL            -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY     -fdb op busy
+ * @retval        CMM_ERR_ENTRY_NOT_FOUND -fdb entry not found
+ */
+u32 yt_l2_fdb_ucast_addr_del(u8 unit, u16 vid, struct yt_mac_addr mac_addr);
+
+/**
+ * @internal      yt_l2_fdb_uc_withindex_get
+ * @endinternal
+ *
+ * @brief         Get unicast fdb entry
+ * @note          APPLICABLE DEVICES      -Tiger, Whale
+ * @param[in]     unit                    -unit id
+ * @param[in]     index                   -fdb entry index
+ * @param[out]     ucast_info         -unicast info pointer
+ * @retval        CMM_ERR_OK              -on success
+ * @retval        CMM_ERR_FAIL            -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY     -fdb op busy
+ * @retval        CMM_ERR_ENTRY_NOT_FOUND -fdb entry not found
+ */
+u32 yt_l2_fdb_uc_withindex_get(u8 unit, u16 index,
+			       struct yt_ucast_macaddr_info *ucast_info);
+
+/**
+ * @internal      yt_l2_fdb_uc_withindex_getnext
+ * @endinternal
+ *
+ * @brief         Get next unicast fdb entry
+ * @note          APPLICABLE DEVICES      -Tiger, Whale
+ * @param[in]     unit                    -unit id
+ * @param[in]     index                   -fdb entry index
+ * @param[out]     next_index_ptr         -next unicast entry index pointer
+ * @param[out]     ucast_info         -next unicast entry info pointer
+ * @retval        CMM_ERR_OK              -on success
+ * @retval        CMM_ERR_FAIL            -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY     -fdb op busy
+ * @retval        CMM_ERR_ENTRY_NOT_FOUND -fdb entry not found
+ */
+u32 yt_l2_fdb_uc_withindex_getnext(u8 unit, u16 index, u16 *next_index_ptr,
+				   struct yt_ucast_macaddr_info *ucast_info);
+
+/**
+ * @internal      yt_l2_fdb_uc_get
+ * @endinternal
+ *
+ * @brief         Get unicast fdb entry with mac and vid
+ * @note          APPLICABLE DEVICES      -Tiger, Whale
+ * @param[in]     unit                    -unit id
+ * @param[in]     vid                     -vlan id
+ * @param[in]     mac_addr                -mac addr
+ * @param[out]    ucast_info              -unicast info pointer
+ * @retval        CMM_ERR_OK              -on success
+ * @retval        CMM_ERR_FAIL            -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY     -fdb op busy
+ * @retval        CMM_ERR_ENTRY_NOT_FOUND -fdb entry not found
+ */
+u32 yt_l2_fdb_uc_get(u8 unit, u16 vid, struct yt_mac_addr mac_addr,
+		     struct yt_ucast_macaddr_info *ucast_info);
+
+/**
+ * @internal      yt_l2_fdb_ucast_flush
+ * @endinternal
+ *
+ * @brief         Flush unicast fdb entry
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     flush_info          -flush info
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY -fdb op busy
+ */
+u32 yt_l2_fdb_ucast_flush(u8 unit, struct yt_l2_uc_flush_info flush_info);
+
+/**
+ * @internal      yt_l2_fdb_aging_time_set
+ * @endinternal
+ *
+ * @brief         Set unicast fdb aging time
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     sec                 -agint time
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_INPUT       -sec range error
+ * @retval        CMM_ERR_FDB_OP_BUSY -fdb op busy
+ */
+u32 yt_l2_fdb_aging_time_set(u8 unit, u32 sec);
+
+/**
+ * @internal      yt_l2_port_learn_en_set
+ * @endinternal
+ *
+ * @brief         Set fdb learn by port
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port
+ * @param[in]     enable              -enable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on success
+ */
+u32 yt_l2_port_learn_en_set(u8 unit, u32 port, enum yt_enable enable);
+
+/**
+ * @internal      yt_l2_port_learn_en_get
+ * @endinternal
+ *
+ * @brief         Get fdb learn status by port
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port
+ * @param[in]     enable              -enable status pointer
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on success
+ */
+u32 yt_l2_port_learn_en_get(u8 unit, u32 port, enum yt_enable *enable);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_lag.c b/drivers/net/dsa/motorcomm/switch/yt_lag.c
new file mode 100644
index 000000000000..536c75577e83
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_lag.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_lag.h"
+
+u32 yt_lag_hash_sel_set(u8 unit, u8 hashmask)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LINK_AGG_HASH_CTRL, &entry), ret);
+	HAL_FIELD_SET(LINK_AGG_HASH_CTRL_HASH_LOWBIT,
+		      LINK_AGG_HASH_CTRL_HASH_WIDTH, &entry, hashmask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LINK_AGG_HASH_CTRL, entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_lag_hash_sel_set);
+
+u32 yt_lag_hash_sel_get(u8 unit, u8 *hashmask_ptr)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LINK_AGG_HASH_CTRL, &entry), ret);
+	HAL_FIELD_GET(LINK_AGG_HASH_CTRL_HASH_LOWBIT,
+		      LINK_AGG_HASH_CTRL_HASH_WIDTH, &entry, hashmask_ptr);
+	return ret;
+}
+EXPORT_SYMBOL(yt_lag_hash_sel_get);
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+u32 yt_lag_group_port_set(u8 unit, u8 group_id,
+			  struct yt_port_mask member_portmask)
+{
+	u32 member_entry[YT_LAG_MAX_NUM] = { 0 };
+	u32 group_entry = 0;
+	u8 idx_offset = 0;
+	struct yt_port_mask macmask;
+	u8 index = 0;
+	u8 macid = 0;
+	u32 mask = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CAL_YTPLIST_TO_MLIST(unit, member_portmask, macmask);
+	mask = macmask.portbits[0];
+	while (mask) {
+		if ((mask & 0x1) != 0) {
+			if (index >= YT_LAG_MAX_NUM)
+				return CMM_ERR_TABLE_FULL;
+			HAL_FIELD_SET(LINK_AGG_MEMBERN_PORT_LOWBIT,
+				      LINK_AGG_MEMBERN_PORT_WIDTH,
+				      &member_entry[index],
+				      macid); index++;
+		}
+		macid++;
+		mask >>= 1U;
+	}
+
+	HAL_FIELD_SET(LINK_AGG_GROUPN_MEMBER_NUM_LOWBIT,
+		      LINK_AGG_GROUPN_MEMBER_NUM_WIDTH, &group_entry, index);
+	idx_offset = (group_id == 1) ? YT_LAG_MAX_NUM : 0;
+	for (index = 0; index < YT_LAG_MAX_NUM; index++) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+				(unit, LINK_AGG_MEMBERN(index + idx_offset),
+				 member_entry[index]), ret);
+	}
+	HAL_FIELD_SET(LINK_AGG_GROUPN_PORT_MASK_LOWBIT,
+		      LINK_AGG_GROUPN_PORT_MASK_WIDTH, &group_entry,
+		      macmask.portbits[0]);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LINK_AGG_GROUPN(group_id),
+					 group_entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_lag_group_port_set);
+
+u32 yt_lag_group_port_get(u8 unit, u8 group_id,
+			  struct yt_port_mask *member_portmask_ptr)
+{
+	u32 group_entry = 0;
+	struct yt_port_mask macmask;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LINK_AGG_GROUPN(group_id),
+					&group_entry), ret);
+	HAL_FIELD_GET(LINK_AGG_GROUPN_PORT_MASK_LOWBIT,
+		      LINK_AGG_GROUPN_PORT_MASK_WIDTH, &group_entry,
+		      macmask.portbits);
+	CAL_MLIST_TO_YTPLIST(unit, macmask, (*member_portmask_ptr));
+	return ret;
+}
+EXPORT_SYMBOL(yt_lag_group_port_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+static u32 yt_lag_id_set(u8 unit, u32 port, enum yt_enable lag_state, u8 lagid)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_PORT_CTRLN(macid), &entry),
+		    ret);
+	HAL_FIELD_SET(PARSER_PORT_CTRLN_LAG_EN_LOWBIT,
+		      PARSER_PORT_CTRLN_LAG_EN_WIDTH, &entry, lag_state);
+	HAL_FIELD_SET(PARSER_PORT_CTRLN_LAG_ID_LOWBIT,
+		      PARSER_PORT_CTRLN_LAG_ID_WIDTH, &entry, lagid);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PARSER_PORT_CTRLN(macid), entry),
+		    ret);
+	return ret;
+}
+
+u32 yt_lag_init(u8 unit)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LINK_AGG_HASH_CTRL, &entry), ret);
+	HAL_FIELD_SET(LINK_AGG_HASH_CTRL_MAC_SA_SHIFT_LOWBIT,
+		      LINK_AGG_HASH_CTRL_MAC_SA_SHIFT_WIDTH, &entry, 0x5U);
+	HAL_FIELD_SET(LINK_AGG_HASH_CTRL_MAC_DA_SHIFT_LOWBIT,
+		      LINK_AGG_HASH_CTRL_MAC_DA_SHIFT_WIDTH, &entry, 0x4U);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LINK_AGG_HASH_CTRL, entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_lag_init);
+
+u32 yt_lag_en_get(u8 unit, u32 port, enum yt_enable *lag_state_ptr,
+		  u8 *lagid_ptr)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+	u32 macid = 0;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_PORT_CTRLN(macid), &entry),
+		    ret);
+	HAL_FIELD_GET(PARSER_PORT_CTRLN_LAG_EN_LOWBIT,
+		      PARSER_PORT_CTRLN_LAG_EN_WIDTH, &entry, lag_state_ptr);
+	HAL_FIELD_GET(PARSER_PORT_CTRLN_LAG_ID_LOWBIT,
+		      PARSER_PORT_CTRLN_LAG_ID_WIDTH, &entry, lagid_ptr);
+	return ret;
+}
+EXPORT_SYMBOL(yt_lag_en_get);
+
+u32 yt_lag_group_port_set(u8 unit, u8 group_id,
+			  struct yt_port_mask member_portmask)
+{
+	struct yt_port_mask temp_member_portmask;
+	bool current_state = FALSE;
+	bool origin_state = FALSE;
+	u32 group_entry = 0;
+	struct yt_port_mask macmask;
+	u8 index = 0;
+	u32 port = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_CLEAR_MEMBER_PORT(temp_member_portmask);
+	CMM_PARAM_CHK(((YT_LAG_MAX_NUM - 1) < group_id), CMM_ERR_INPUT);
+	/* check whether the port is in other lag group */
+	for (index = 0; index < YT_LAG_MAX_NUM; index++) {
+		if (index != group_id) {
+			CMM_ERR_CHK(yt_lag_group_port_get
+					(unit, index,
+					 &temp_member_portmask), ret);
+			if (member_portmask.portbits[0] &
+			    temp_member_portmask.portbits[0])
+				return CMM_ERR_SAMEENTRY_EXIST;
+		}
+	}
+
+	/* sync member ports to parser_port_ctrln */
+	CMM_ERR_CHK(yt_lag_group_port_get
+			(unit, group_id,
+			 &temp_member_portmask), ret);
+	for (port = 0; port < YT_MAX_PORT_PER_UNIT; port++) {
+		CMM_IS_MEMBER_PORT(temp_member_portmask, port, origin_state);
+		CMM_IS_MEMBER_PORT(member_portmask, port, current_state);
+		if (origin_state && !current_state)
+			CMM_ERR_CHK(yt_lag_id_set(unit, port, FALSE, 0), ret);
+		if (!origin_state && current_state)
+			CMM_ERR_CHK(yt_lag_id_set(unit, port, TRUE, group_id),
+				    ret);
+	}
+	CAL_YTPLIST_TO_MLIST(unit, member_portmask, macmask);
+	HAL_FIELD_SET(LINK_AGG_GROUPN_PORT_MASK_LOWBIT,
+		      LINK_AGG_GROUPN_PORT_MASK_WIDTH, &group_entry,
+		      macmask.portbits[0]);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LINK_AGG_GROUPN(group_id),
+					 group_entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_lag_group_port_set);
+
+u32 yt_lag_group_port_get(u8 unit, u8 group_id,
+			  struct yt_port_mask *member_portmask_ptr)
+{
+	u32 group_entry = 0;
+	struct yt_port_mask macmask;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_PARAM_CHK(((YT_LAG_MAX_NUM - 1) < group_id), CMM_ERR_INPUT);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LINK_AGG_GROUPN(group_id),
+					&group_entry), ret);
+	HAL_FIELD_GET(LINK_AGG_GROUPN_PORT_MASK_LOWBIT,
+		      LINK_AGG_GROUPN_PORT_MASK_WIDTH, &group_entry,
+		      macmask.portbits);
+	CAL_MLIST_TO_YTPLIST(unit, macmask, (*member_portmask_ptr));
+	return ret;
+}
+EXPORT_SYMBOL(yt_lag_group_port_get);
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_lag.h b/drivers/net/dsa/motorcomm/switch/yt_lag.h
new file mode 100644
index 000000000000..419a070ccd0f
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_lag.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_LAG_H
+#define __YT_LAG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+#define YT_AGG_HASH_SEL_SRC_PORT BIT(0U)
+#define YT_AGG_HASH_SEL_MAC_DA BIT(1U)
+#define YT_AGG_HASH_SEL_MAC_SA BIT(2U)
+#define YT_AGG_HASH_SEL_IP_DEST BIT(3U)
+#define YT_AGG_HASH_SEL_IP_SRC BIT(4U)
+#define YT_AGG_HASH_SEL_IP_PROTO BIT(5U)
+#define YT_AGG_HASH_SEL_L4_DPORT BIT(6U)
+#define YT_AGG_HASH_SEL_L4_SPORT BIT(7U)
+#define YT_LAG_AGG_HASH_SEL_MAX (8U)
+
+/**
+ * @internal      yt_lag_hash_sel_set
+ * @endinternal
+ *
+ * @brief         set link aggregation hash algorithm bitmask
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     hashmask            -link aggregation hash algorithm bitmask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_lag_hash_sel_set(u8 unit, u8 hashmask);
+
+/**
+ * @internal      yt_lag_hash_sel_get
+ * @endinternal
+ *
+ * @brief         get link aggregation hash algorithm bitmask
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[out]    hashmask_ptr        -link aggregation hash algorithm bitmask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_lag_hash_sel_get(u8 unit, u8 *hashmask_ptr);
+
+/**
+ * @internal      yt_lag_group_port_set
+ * @endinternal
+ *
+ * @brief         set lag group member ports
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     group_id            -lag group index
+ * @param[in]     member_portmask     -port bit mask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_SAMEENTRY_EXIST   - member port conflict with other
+ * lag
+ */
+u32 yt_lag_group_port_set(u8 unit, u8 group_id,
+			  struct yt_port_mask member_portmask);
+
+/**
+ * @internal      yt_lag_group_port_get
+ * @endinternal
+ *
+ * @brief         get lag group member portmask
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     group_id            -lag group index
+ * @param[out]    member_portmask_ptr -link aggregation group portmask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_lag_group_port_get(u8 unit, u8 group_id,
+			  struct yt_port_mask *member_portmask_ptr);
+
+#if defined(CONFIG_MOTORCOMM_YT922X)
+/**
+ * @internal      yt_lag_init
+ * @endinternal
+ *
+ * @brief         init lag hash sel
+ * @note          APPLICABLE DEVICES  -Whale
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_lag_init(u8 unit);
+
+/**
+ * @internal      yt_lag_en_get
+ * @endinternal
+ *
+ * @brief         get lag en state and lag id  based on port
+ * @note          APPLICABLE DEVICES  -Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port id
+ * @param[out]    lag_state_ptr       -lag en
+ * @param[out]    lagid_ptr           -lag id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_lag_en_get(u8 unit, u32 port, enum yt_enable *lag_state_ptr,
+		  u8 *lagid_ptr);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_led.c b/drivers/net/dsa/motorcomm/switch/yt_led.c
new file mode 100644
index 000000000000..3c299f019cdd
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_led.c
@@ -0,0 +1,1799 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include <linux/string.h>
+#include "yt_led.h"
+#include "yt_port.h"
+#include "yt_init.h"
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+/* remapping array for LED_DATA */
+const struct yt_led_slot remap_data[SLED_DATANUM_MAX][30] = {
+	/* SLED_DATANUM_YT9215_P5L1 */
+	{ { 9, LED_ID_0 },
+	  { 8, LED_ID_0 },
+	  { 7, LED_ID_0 },
+	  { 6, LED_ID_0 },
+	  { 5, LED_ID_0 } },
+
+	/* SLED_DATANUM_YT9215_P7L1 */
+	{ { 9, LED_ID_0 },
+	  { 8, LED_ID_0 },
+	  { 4, LED_ID_0 },
+	  { 3, LED_ID_0 },
+	  { 2, LED_ID_0 },
+	  { 1, LED_ID_0 },
+	  { 0, LED_ID_0 } },
+
+	/* SLED_DATANUM_YT9215_P5L2 */
+	{ { 9, LED_ID_0 },
+	  { 8, LED_ID_0 },
+	  { 7, LED_ID_0 },
+	  { 6, LED_ID_0 },
+	  { 5, LED_ID_0 },
+	  { 4, LED_ID_0 },
+	  { 3, LED_ID_0 },
+	  { 2, LED_ID_0 },
+	  { 1, LED_ID_0 },
+	  { 0, LED_ID_0 } },
+
+	/* SLED_DATANUM_YT9215_P7L2 */
+	{ { 9, LED_ID_0 },
+	  { 8, LED_ID_0 },
+	  { 4, LED_ID_0 },
+	  { 3, LED_ID_0 },
+	  { 2, LED_ID_0 },
+	  { 1, LED_ID_0 },
+	  { 0, LED_ID_0 },
+	  { 9, LED_ID_1 },
+	  { 8, LED_ID_1 },
+	  { 4, LED_ID_1 },
+	  { 3, LED_ID_1 },
+	  { 2, LED_ID_1 },
+	  { 1, LED_ID_1 },
+	  { 0, LED_ID_1 } },
+
+	/* SLED_DATANUM_YT9215_P5L3 */
+	{ { 9, LED_ID_0 },
+	  { 8, LED_ID_0 },
+	  { 7, LED_ID_0 },
+	  { 6, LED_ID_0 },
+	  { 5, LED_ID_0 },
+	  { 4, LED_ID_0 },
+	  { 3, LED_ID_0 },
+	  { 2, LED_ID_0 },
+	  { 1, LED_ID_0 },
+	  { 0, LED_ID_0 },
+	  { 9, LED_ID_1 },
+	  { 8, LED_ID_1 },
+	  { 7, LED_ID_1 },
+	  { 6, LED_ID_1 },
+	  { 5, LED_ID_1 } },
+
+	/* SLED_DATANUM_YT9215_P7L3 */
+	{ { 9, LED_ID_0 }, { 8, LED_ID_0 }, { 4, LED_ID_0 }, { 3, LED_ID_0 },
+	  { 2, LED_ID_0 }, { 1, LED_ID_0 }, { 0, LED_ID_0 }, { 9, LED_ID_1 },
+	  { 8, LED_ID_1 }, { 4, LED_ID_1 }, { 3, LED_ID_1 }, { 2, LED_ID_1 },
+	  { 1, LED_ID_1 }, { 0, LED_ID_1 }, { 9, LED_ID_2 }, { 8, LED_ID_2 },
+	  { 4, LED_ID_2 }, { 3, LED_ID_2 }, { 2, LED_ID_2 }, { 1, LED_ID_2 },
+	  { 0, LED_ID_2 } },
+
+	/* SLED_DATANUM_YT9218_P10L1 */
+	{ { 9, LED_ID_2 },
+	  { 8, LED_ID_2 },
+	  { 7, LED_ID_2 },
+	  { 6, LED_ID_2 },
+	  { 5, LED_ID_2 },
+	  { 4, LED_ID_2 },
+	  { 3, LED_ID_2 },
+	  { 2, LED_ID_2 },
+	  { 1, LED_ID_2 },
+	  { 0, LED_ID_2 } },
+
+	/* SLED_DATANUM_YT9218_P10L2 */
+	{ { 9, LED_ID_2 }, { 8, LED_ID_2 }, { 7, LED_ID_2 }, { 6, LED_ID_2 },
+	  { 5, LED_ID_2 }, { 4, LED_ID_2 }, { 3, LED_ID_2 }, { 2, LED_ID_2 },
+	  { 1, LED_ID_2 }, { 0, LED_ID_2 }, { 9, LED_ID_1 }, { 8, LED_ID_1 },
+	  { 7, LED_ID_1 }, { 6, LED_ID_1 }, { 5, LED_ID_1 }, { 4, LED_ID_1 },
+	  { 3, LED_ID_1 }, { 2, LED_ID_1 }, { 1, LED_ID_1 }, { 0, LED_ID_1 } },
+
+	/* SLED_DATANUM_YT9218_P10L3 */
+	{ { 9, LED_ID_0 }, { 8, LED_ID_0 }, { 7, LED_ID_0 }, { 6, LED_ID_0 },
+	  { 5, LED_ID_0 }, { 4, LED_ID_0 }, { 3, LED_ID_0 }, { 2, LED_ID_0 },
+	  { 1, LED_ID_0 }, { 0, LED_ID_0 }, { 9, LED_ID_1 }, { 8, LED_ID_1 },
+	  { 7, LED_ID_1 }, { 6, LED_ID_1 }, { 5, LED_ID_1 }, { 4, LED_ID_1 },
+	  { 3, LED_ID_1 }, { 2, LED_ID_1 }, { 1, LED_ID_1 }, { 0, LED_ID_1 },
+	  { 9, LED_ID_2 }, { 8, LED_ID_2 }, { 7, LED_ID_2 }, { 6, LED_ID_2 },
+	  { 5, LED_ID_2 }, { 4, LED_ID_2 }, { 3, LED_ID_2 }, { 2, LED_ID_2 },
+	  { 1, LED_ID_2 }, { 0, LED_ID_2 } },
+
+	/* SLED_DATANUM_YT9204_P6L1 */
+	{ { 9, LED_ID_0 },
+	  { 8, LED_ID_0 },
+	  { 3, LED_ID_0 },
+	  { 2, LED_ID_0 },
+	  { 1, LED_ID_0 },
+	  { 0, LED_ID_0 } },
+
+	/* SLED_DATANUM_YT9204_P6L2 */
+	{ { 9, LED_ID_0 },
+	  { 8, LED_ID_0 },
+	  { 3, LED_ID_0 },
+	  { 2, LED_ID_0 },
+	  { 1, LED_ID_0 },
+	  { 0, LED_ID_0 },
+	  { 9, LED_ID_1 },
+	  { 8, LED_ID_1 },
+	  { 3, LED_ID_1 },
+	  { 2, LED_ID_1 },
+	  { 1, LED_ID_1 },
+	  { 0, LED_ID_1 } },
+
+	/* SLED_DATANUM_YT9204_P6L3 */
+	{ { 9, LED_ID_0 },
+	  { 8, LED_ID_0 },
+	  { 3, LED_ID_0 },
+	  { 2, LED_ID_0 },
+	  { 1, LED_ID_0 },
+	  { 0, LED_ID_0 },
+	  { 9, LED_ID_1 },
+	  { 8, LED_ID_1 },
+	  { 3, LED_ID_1 },
+	  { 2, LED_ID_1 },
+	  { 1, LED_ID_1 },
+	  { 0, LED_ID_1 },
+	  { 9, LED_ID_2 },
+	  { 8, LED_ID_2 },
+	  { 3, LED_ID_2 },
+	  { 2, LED_ID_2 },
+	  { 1, LED_ID_2 },
+	  { 0, LED_ID_2 } }
+};
+
+const u8 led_max_num[SLED_DATANUM_MAX] = { 5,  7,  10, 14, 15, 21,
+					   10, 20, 30, 6,  12, 18 };
+
+u32 sled_output_mode = SLED_DATANUM_YT9215_P7L3;
+
+u32 yt_led_enable(u8 unit)
+{
+	u32 entry1;
+	u32 entry2;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 port;
+
+	/* preset disable_link_try bit of LED0 action(exclude CPU_PORT) */
+	for (port = 0; port < YT_MAX_PORT_PER_UNIT - 1; port++) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+				(unit, LED_CTRL_0_BASE + port * 4,
+				 &entry1), ret);
+		HAL_FIELD_SET(LED_CTRL_0_BASE_DISABLE_LED_LINK_TRY_LOWBIT,
+			      LED_CTRL_0_BASE_DISABLE_LED_LINK_TRY_WIDTH,
+			      &entry1, 1);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+				(unit, LED_CTRL_0_BASE + port * 4,
+				 entry1), ret);
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_GLB_CTRL, &entry2), ret);
+	HAL_FIELD_SET(LED_GLB_CTRL_LED_CONFIG_DONE_LOWBIT,
+		      LED_GLB_CTRL_LED_CONFIG_DONE_WIDTH, &entry2, 1);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_GLB_CTRL, entry2), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_enable);
+
+u32 yt_led_mode_set(u8 unit, enum yt_led_mode mode)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_GLB_CTRL, &entry), ret);
+	HAL_FIELD_SET(LED_GLB_CTRL_LED_MODE_LOWBIT, LED_GLB_CTRL_LED_MODE_WIDTH,
+		      &entry, mode);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_GLB_CTRL, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_mode_set);
+
+u32 yt_led_mode_get(u8 unit, enum yt_led_mode *mode_ptr)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_GLB_CTRL, &entry), ret);
+	HAL_FIELD_GET(LED_GLB_CTRL_LED_MODE_LOWBIT, LED_GLB_CTRL_LED_MODE_WIDTH,
+		      &entry, mode_ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_mode_get);
+
+u32 yt_led_action_set(u8 unit, u32 port, enum yt_led_id led_id,
+		      struct yt_led_act_cfg led_act_cfg)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 led_ctrl_addr[LED_ID_NUM] = { LED_CTRL_0_BASE, LED_CTRL_1_BASE,
+					  LED_CTRL_2_BASE };
+	u32 led_action_low_bit[LED_ID_NUM] = {
+		LED_CTRL_0_BASE_LED_ACTION_LOWBIT,
+		LED_CTRL_1_BASE_LED_ACTION_LOWBIT,
+		LED_CTRL_2_BASE_LED_ACTION_LOWBIT
+	};
+	u32 led_action_width[LED_ID_NUM] = { LED_CTRL_0_BASE_LED_ACTION_WIDTH,
+					     LED_CTRL_1_BASE_LED_ACTION_WIDTH,
+					     LED_CTRL_2_BASE_LED_ACTION_WIDTH };
+	u32 action_set = 0;
+	u32 macid;
+	u32 reg_addr;
+
+	if (led_id < LED_ID_0 || led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	if (led_act_cfg.spd10m_blink_en)
+		action_set |= LED_ACTION_10M_BLINK;
+
+	if (led_act_cfg.spd100m_blink_en)
+		action_set |= LED_ACTION_100M_BLINK;
+
+	if (led_act_cfg.spd1000m_blink_en)
+		action_set |= LED_ACTION_1000M_BLINK;
+
+	if (led_act_cfg.collision_blink_en)
+		action_set |= LED_ACTION_COLLISION_BLINK_ENABLE;
+
+	if (led_act_cfg.spd10m_on_en)
+		action_set |= LED_ACTION_10M_ON;
+
+	if (led_act_cfg.spd100m_on_en)
+		action_set |= LED_ACTION_100M_ON;
+
+	if (led_act_cfg.spd1000m_on_en)
+		action_set |= LED_ACTION_1000M_ON;
+
+	if (led_act_cfg.rxact_on_en)
+		action_set |= LED_ACTION_RXACT_ON;
+
+	if (led_act_cfg.txact_on_en)
+		action_set |= LED_ACTION_TXACT_ON;
+
+	if (led_act_cfg.rxact_blink_en)
+		action_set |= LED_ACTION_RXACT_BLINK;
+
+	if (led_act_cfg.txact_blink_en)
+		action_set |= LED_ACTION_TXACT_BLINK;
+
+	if (led_act_cfg.half_duplex_en)
+		action_set |= LED_ACTION_HALFDUPLEX_ON;
+
+	if (led_act_cfg.full_duplex_en)
+		action_set |= LED_ACTION_FULLDUPLEX_ON;
+
+	if (led_act_cfg.active_blink_indicate_en)
+		action_set |= LED_ACTION_ACTIVE_BLINK_INDICATE;
+
+	if (led_act_cfg.loopdetect_indicate_en)
+		action_set |= LED_ACTION_LOOPDETECT_INDICATE;
+
+	if (led_act_cfg.eee_indicate_en)
+		action_set |= LED_ACTION_EEE_INDICATE;
+
+	if (led_act_cfg.collision_blink_indicate_en)
+		action_set |= LED_ACTION_COLLISION_BLINK;
+
+	/* set disable_link_try bit to 1 */
+	if (led_id == LED_ID_0)
+		action_set |= LED_ACTION_DISABLE_LINK_TRY;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	reg_addr = led_ctrl_addr[led_id] + macid * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &entry), ret);
+	HAL_FIELD_SET(led_action_low_bit[led_id], led_action_width[led_id],
+		      &entry, action_set);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg_addr, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_action_set);
+
+u32 yt_led_action_get(u8 unit, u32 port, enum yt_led_id led_id,
+		      struct yt_led_act_cfg *led_act_cfg_ptr)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 led_ctrl_addr[LED_ID_NUM] = { LED_CTRL_0_BASE, LED_CTRL_1_BASE,
+					  LED_CTRL_2_BASE };
+	u32 led_action_low_bit[LED_ID_NUM] = {
+		LED_CTRL_0_BASE_LED_ACTION_LOWBIT,
+		LED_CTRL_1_BASE_LED_ACTION_LOWBIT,
+		LED_CTRL_2_BASE_LED_ACTION_LOWBIT
+	};
+	u32 led_action_width[LED_ID_NUM] = { LED_CTRL_0_BASE_LED_ACTION_WIDTH,
+					     LED_CTRL_1_BASE_LED_ACTION_WIDTH,
+					     LED_CTRL_2_BASE_LED_ACTION_WIDTH };
+	u32 action_set = 0;
+	u32 macid;
+	u32 reg_addr;
+
+	if (led_id < LED_ID_0 || led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	memset(led_act_cfg_ptr, 0, sizeof(struct yt_led_act_cfg));
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	reg_addr = led_ctrl_addr[led_id] + macid * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &entry), ret);
+	HAL_FIELD_GET(led_action_low_bit[led_id], led_action_width[led_id],
+		      &entry, &action_set);
+
+	if (action_set & LED_ACTION_10M_BLINK)
+		led_act_cfg_ptr->spd10m_blink_en = 1;
+
+	if (action_set & LED_ACTION_100M_BLINK)
+		led_act_cfg_ptr->spd100m_blink_en = 1;
+
+	if (action_set & LED_ACTION_1000M_BLINK)
+		led_act_cfg_ptr->spd1000m_blink_en = 1;
+
+	if (action_set & LED_ACTION_COLLISION_BLINK_ENABLE)
+		led_act_cfg_ptr->collision_blink_en = 1;
+
+	if (action_set & LED_ACTION_10M_ON)
+		led_act_cfg_ptr->spd10m_on_en = 1;
+
+	if (action_set & LED_ACTION_100M_ON)
+		led_act_cfg_ptr->spd100m_on_en = 1;
+
+	if (action_set & LED_ACTION_1000M_ON)
+		led_act_cfg_ptr->spd1000m_on_en = 1;
+
+	if (action_set & LED_ACTION_RXACT_ON)
+		led_act_cfg_ptr->rxact_on_en = 1;
+
+	if (action_set & LED_ACTION_TXACT_ON)
+		led_act_cfg_ptr->txact_on_en = 1;
+
+	if (action_set & LED_ACTION_RXACT_BLINK)
+		led_act_cfg_ptr->rxact_blink_en = 1;
+
+	if (action_set & LED_ACTION_TXACT_BLINK)
+		led_act_cfg_ptr->txact_blink_en = 1;
+
+	if (action_set & LED_ACTION_HALFDUPLEX_ON)
+		led_act_cfg_ptr->half_duplex_en = 1;
+
+	if (action_set & LED_ACTION_FULLDUPLEX_ON)
+		led_act_cfg_ptr->full_duplex_en = 1;
+
+	if (action_set & LED_ACTION_ACTIVE_BLINK_INDICATE)
+		led_act_cfg_ptr->active_blink_indicate_en = 1;
+
+	if (action_set & LED_ACTION_LOOPDETECT_INDICATE)
+		led_act_cfg_ptr->loopdetect_indicate_en = 1;
+
+	if (action_set & LED_ACTION_EEE_INDICATE)
+		led_act_cfg_ptr->eee_indicate_en = 1;
+
+	if (action_set & LED_ACTION_COLLISION_BLINK)
+		led_act_cfg_ptr->collision_blink_indicate_en = 1;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_action_get);
+
+u32 yt_led_serial_output_mode_set(u8 unit, enum yt_sled_data_num mode)
+{
+	u32 entry1;
+	u32 entry2;
+	u32 entry3;
+	enum cmm_err ret = CMM_ERR_OK;
+	u8 port_num;
+	u8 led_num;
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+
+	yt_cal_board_chip_get(&chip_id);
+	sled_output_mode = mode;
+	switch (sled_output_mode) {
+	case SLED_DATANUM_YT9215_P5L1:
+		port_num = 5;
+		led_num = 1;
+		break;
+
+	case SLED_DATANUM_YT9215_P7L1:
+		port_num = 7;
+		led_num = 1;
+		break;
+
+	case SLED_DATANUM_YT9215_P5L2:
+		port_num = 5;
+		led_num = 2;
+		break;
+
+	case SLED_DATANUM_YT9215_P7L2:
+		port_num = 7;
+		led_num = 2;
+		break;
+
+	case SLED_DATANUM_YT9215_P5L3:
+		port_num = 5;
+		led_num = 3;
+		break;
+
+	case SLED_DATANUM_YT9215_P7L3:
+		port_num = 7;
+		led_num = 3;
+		break;
+
+	case SLED_DATANUM_YT9218_P10L1:
+		port_num = 10;
+		led_num = 1;
+		break;
+
+	case SLED_DATANUM_YT9218_P10L2:
+		port_num = 10;
+		led_num = 2;
+		break;
+
+	case SLED_DATANUM_YT9218_P10L3:
+		port_num = 10;
+		led_num = 3;
+		break;
+
+	case SLED_DATANUM_YT9204_P6L1:
+		port_num = 6;
+		led_num = 1;
+		break;
+
+	case SLED_DATANUM_YT9204_P6L2:
+		port_num = 6;
+		led_num = 2;
+		break;
+
+	case SLED_DATANUM_YT9204_P6L3:
+		port_num = 6;
+		led_num = 3;
+		break;
+
+	default:
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	if (chip_id != YT_SW_ID_920X) {
+		/* serial port num-- start */
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_GLB_CTRL, &entry1),
+			    ret);
+		HAL_FIELD_SET(LED_GLB_CTRL_PORT_NUM_LOWBIT,
+			      LED_GLB_CTRL_PORT_NUM_WIDTH, &entry1, port_num);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_GLB_CTRL, entry1),
+			    ret);
+		/* serial port num-- end */
+	}
+
+	/* serial pin num-- start */
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_SERIAL_CTRL, &entry2), ret);
+	HAL_FIELD_SET(LED_SERIAL_CTRL_LED_NUM_LOWBIT,
+		      LED_SERIAL_CTRL_LED_NUM_WIDTH, &entry2, led_num - 1);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_SERIAL_CTRL, entry2), ret);
+	/* serial pin num-- end */
+
+	/* enable serial led */
+	if (chip_id == YT_SW_ID_920X) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_CTRL7, &entry3), ret);
+		HAL_FIELD_SET(LED_CTRL7_LED_NUM_LOWBIT, LED_CTRL7_LED_NUM_WIDTH,
+			      &entry3, 1);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_CTRL7, entry3), ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_output_mode_set);
+
+u32 yt_led_serial_output_mode_get(u8 unit, enum yt_sled_data_num *mode_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+
+	*mode_ptr = sled_output_mode;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_output_mode_get);
+
+u32 yt_led_serial_active_mode_set(u8 unit, enum yt_sled_active_mode mode)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (mode < LED_SERIAL_ACTIVE_MODE_HIGH ||
+	    mode > LED_SERIAL_ACTIVE_MODE_LOW)
+		return CMM_ERR_INPUT;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_SERIAL_CTRL, &entry), ret);
+	HAL_FIELD_SET(LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT,
+		      LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH, &entry, mode);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_SERIAL_CTRL, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_active_mode_set);
+
+u32 yt_led_serial_active_mode_get(u8 unit, enum yt_sled_active_mode *mode_ptr)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_SERIAL_CTRL, &entry), ret);
+	HAL_FIELD_GET(LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT,
+		      LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH, &entry, mode_ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_active_mode_get);
+
+u32 yt_led_serial_remapping_set(u8 unit, u8 index,
+				struct yt_led_remapping dst_info)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 src_macid;
+	u32 dst_macid;
+	u32 reg_addr;
+	u32 reg_val;
+	u32 val_mask;
+	enum yt_sled_data_num data_num = sled_output_mode;
+
+	if (dst_info.led_id < LED_ID_0 || dst_info.led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	if (led_max_num[data_num] <= index)
+		return CMM_ERR_EXCEED_RANGE;
+
+	src_macid = remap_data[data_num][index].serial_id;
+	reg_addr = LED_SERIAL_REMAPPING_BASE +
+		   (LED_ID_2 - remap_data[data_num][index].led_id) * 8 +
+		   (src_macid / 5) * 4;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &reg_val), ret);
+
+	/* port num */
+	dst_macid = CAL_YTP_TO_MAC(unit, dst_info.port);
+	val_mask = 0xfUL << ((src_macid % 5) * 6 + 2);
+	reg_val &= (~val_mask);
+	reg_val |= ((dst_macid & 0xf) << ((src_macid % 5) * 6 + 2));
+
+	/* LED ID */
+	val_mask = 0x3UL << ((src_macid % 5) * 6);
+	reg_val &= (~val_mask);
+	reg_val |= ((dst_info.led_id & 0x3) << ((src_macid % 5) * 6));
+
+	HAL_MEM_DIRECT_WRITE(unit, reg_addr, reg_val);
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_remapping_set);
+
+u32 yt_led_serial_remapping_get(u8 unit, u8 index,
+				struct yt_led_remapping *dst_info_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 src_macid;
+	u32 dst_macid;
+	u32 reg_addr;
+	u32 reg_val;
+	enum yt_sled_data_num data_num;
+
+	data_num = sled_output_mode;
+
+	if (led_max_num[data_num] <= index)
+		return CMM_ERR_EXCEED_RANGE;
+
+	src_macid = remap_data[data_num][index].serial_id;
+
+	reg_addr = LED_SERIAL_REMAPPING_BASE +
+		   (LED_ID_2 - remap_data[data_num][index].led_id) * 8 +
+		   (src_macid / 5) * 4;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &reg_val), ret);
+
+	/* port num */
+	dst_macid = (reg_val >> ((src_macid % 5) * 6 + 2)) & 0xf;
+	CAL_MAC_TO_YTP(unit, dst_macid, dst_info_ptr->port);
+
+	/* LED ID */
+	dst_info_ptr->led_id = (reg_val >> ((src_macid % 5) * 6)) & 0x3;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_remapping_get);
+
+u32 yt_led_parallel_output_set(u8 unit, struct yt_port_mask port_mask)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry;
+	u32 val_mask = 0x3ff;
+	struct yt_port_mask macmask;
+	u8 i = 0;
+	u32 pin_mux_val[6] = { 0x3, 0xc, 0x30, 0xc0, 0x300, 0xc00 };
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+
+	CAL_YTPLIST_TO_MLIST(unit, port_mask, macmask);
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		val_mask = 0xfff;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_PIN_MUX_CTRL, &entry),
+			    ret);
+		entry &= (~val_mask);
+		for (i = 0; i < 6; i++) {
+			if (GET_BIT(macmask.portbits[0], i))
+				entry |= (pin_mux_val[i] & val_mask);
+		}
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_PIN_MUX_CTRL, entry),
+			    ret);
+	} else {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_PARALLEL_OUTPUT_CTRL,
+						&entry), ret);
+		HAL_FIELD_SET(LED_PARALLEL_OUTPUT_CTRL_MODE_LOWBIT,
+			      LED_PARALLEL_OUTPUT_CTRL_MODE_WIDTH, &entry,
+			      macmask.portbits[0] & val_mask);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_PARALLEL_OUTPUT_CTRL,
+						 entry), ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_output_set);
+
+u32 yt_led_parallel_output_get(u8 unit, struct yt_port_mask *port_mask_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry;
+	struct yt_port_mask macmask;
+	u8 i = 0;
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		(*port_mask_ptr).portbits[0] = 0;
+
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_PIN_MUX_CTRL, &entry),
+			    ret);
+		for (i = 0; i < 6; i++) {
+			if (IS_BIT_SET(entry, (i * 2)))
+				SET_BIT((*port_mask_ptr).portbits[0], i);
+		}
+	} else {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_PARALLEL_OUTPUT_CTRL,
+						&entry), ret);
+		HAL_FIELD_GET(LED_PARALLEL_OUTPUT_CTRL_MODE_LOWBIT,
+			      LED_PARALLEL_OUTPUT_CTRL_MODE_WIDTH, &entry,
+			      &macmask.portbits[0]);
+		CAL_MLIST_TO_YTPLIST(unit, macmask, (*port_mask_ptr));
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_output_get);
+
+u32 yt_led_parallel_remapping_set(u8 unit, struct yt_led_remapping src_info,
+				  struct yt_led_remapping dst_info)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 src_macid;
+	u32 dst_macid;
+	u32 reg_addr;
+	u32 reg_val;
+	u32 val_mask;
+
+	if (src_info.led_id < LED_ID_0 || src_info.led_id > LED_ID_2 ||
+	    dst_info.led_id < LED_ID_0 || dst_info.led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	src_macid = CAL_YTP_TO_MAC(unit, src_info.port);
+	reg_addr = LED_PARALLEL_REMAPPING_BASE + src_macid * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &reg_val), ret);
+
+	/* port num */
+	dst_macid = CAL_YTP_TO_MAC(unit, dst_info.port);
+	val_mask = 0xfUL << (src_info.led_id * 6 + 2);
+	reg_val &= (~val_mask);
+	reg_val |= ((dst_macid & 0xf) << (src_info.led_id * 6 + 2));
+
+	/* LED ID */
+	val_mask = 0x3UL << (src_info.led_id * 6);
+	reg_val &= (~val_mask);
+	reg_val |= ((dst_info.led_id & 0x3) << (src_info.led_id * 6));
+
+	HAL_MEM_DIRECT_WRITE(unit, reg_addr, reg_val);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_remapping_set);
+
+u32 yt_led_parallel_remapping_get(u8 unit, struct yt_led_remapping src_info,
+				  struct yt_led_remapping *dst_info_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 src_macid;
+	u32 dst_macid;
+	u32 reg_addr;
+	u32 reg_val;
+
+	if (src_info.led_id < LED_ID_0 || src_info.led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	src_macid = CAL_YTP_TO_MAC(unit, src_info.port);
+	reg_addr = LED_PARALLEL_REMAPPING_BASE + src_macid * 4;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &reg_val), ret);
+
+	/* port num */
+	dst_macid = (reg_val >> (src_info.led_id * 6 + 2)) & 0xf;
+	CAL_MAC_TO_YTP(unit, dst_macid, dst_info_ptr->port);
+
+	/* LED ID */
+	dst_info_ptr->led_id = (reg_val >> (src_info.led_id * 6)) & 0x3;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_remapping_get);
+
+static u8 led_pos_record_flag;
+static u32 led_pos_val;
+u32 yt_led_parallel_pos_set(u8 unit, u32 port, enum yt_led_id led_id,
+			    enum yt_led_pos pos)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid;
+	u32 reg_addr;
+	u32 reg_val;
+	u32 val_mask;
+	u32 reg_addr1;
+	u32 led_pos;
+	u32 pos_invert_val;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	val_mask = (0x1UL << (led_id * 10 + macid));
+
+	reg_addr = LED_PARALLEL_POS_INVERT_CTRL;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &reg_val), ret);
+	pos_invert_val = GET_BIT(reg_val, led_id * 10 + macid);
+	reg_val &= (~val_mask);
+
+	if (!led_pos_record_flag) {
+		reg_addr1 = LED_PARALLEL_POS;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr1, &led_pos_val),
+			    ret);
+		led_pos_record_flag = 1;
+	}
+
+	led_pos = GET_BIT(led_pos_val, macid * 3 + led_id);
+
+	if (pos == LED_POS_HIGH && led_pos == 1) {
+		CLEAR_BIT(led_pos_val, macid * 3 + led_id);
+		reg_val |= (pos_invert_val ^ 1) << (led_id * 10 + macid);
+		HAL_MEM_DIRECT_WRITE(unit, reg_addr, reg_val);
+	}
+
+	if (pos == LED_POS_LOW && led_pos == 0) {
+		SET_BIT(led_pos_val, macid * 3 + led_id);
+		reg_val |= (pos_invert_val ^ 1) << (led_id * 10 + macid);
+		HAL_MEM_DIRECT_WRITE(unit, reg_addr, reg_val);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_pos_set);
+
+u32 yt_led_parallel_pos_get(u8 unit, u32 port, enum yt_led_id led_id,
+			    enum yt_led_pos *pos_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid;
+	u32 reg_addr;
+	u32 led_pos;
+
+	if (led_id < LED_ID_0 || LED_ID_2 < led_id)
+		return CMM_ERR_INPUT;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+
+	if (!led_pos_record_flag) {
+		reg_addr = LED_PARALLEL_POS;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &led_pos_val),
+			    ret);
+		led_pos_record_flag = 1;
+	}
+	led_pos = GET_BIT(led_pos_val, macid * 3 + led_id);
+	*pos_ptr = (led_pos == 0) ? LED_POS_HIGH : LED_POS_LOW;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_pos_get);
+#endif
+
+#if defined(CONFIG_MOTORCOMM_YT923X)
+u32 yt_led_enable(u8 unit)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_GLB_CTRL, &entry), ret);
+	HAL_FIELD_SET(LED_GLB_CTRL_LED_CONFIG_DONE_LOWBIT,
+		      LED_GLB_CTRL_LED_CONFIG_DONE_WIDTH, &entry, 1);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_GLB_CTRL, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_enable);
+
+static u32 yt_shark_port_to_led_group(u8 unit, u32 port, u8 *port_led_group_ptr)
+{
+	u32 macid;
+
+	if (!port_led_group_ptr)
+		return CMM_ERR_INPUT;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (macid < 4)
+		*port_led_group_ptr = 0;
+	else if ((macid < 8) && (macid >= 4))
+		*port_led_group_ptr = 1;
+	else if ((macid < 16) && (macid >= 8))
+		*port_led_group_ptr = 2;
+	else if ((macid < 20) && (macid >= 16))
+		*port_led_group_ptr = 3;
+	else if ((macid < 24) && (macid >= 20))
+		*port_led_group_ptr = 4;
+	else
+		*port_led_group_ptr = 5;
+
+	return CMM_ERR_OK;
+}
+
+u32 yt_led_action_set(u8 unit, u32 port, enum yt_led_id led_id,
+		      struct yt_led_act_cfg led_act_cfg)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 led_ctrl_addr[LED_ID_NUM] = { LED_CTRL_0_BASE, LED_CTRL_1_BASE,
+					  LED_CTRL_2_BASE };
+	u32 led_action_low_bit[LED_ID_NUM] = {
+		LED_CTRL_0_BASE_LED_ACTION_LOWBIT,
+		LED_CTRL_1_BASE_LED_ACTION_LOWBIT,
+		LED_CTRL_2_BASE_LED_ACTION_LOWBIT
+	};
+	u32 led_action_width[LED_ID_NUM] = { LED_CTRL_0_BASE_LED_ACTION_WIDTH,
+					     LED_CTRL_1_BASE_LED_ACTION_WIDTH,
+					     LED_CTRL_2_BASE_LED_ACTION_WIDTH };
+	u32 action_set = 0;
+	u32 macid;
+	u32 reg_addr;
+	u8 led_group_id;
+
+	if (led_act_cfg.spd10m_blink_en)
+		action_set |= LED_ACTION_10M_BLINK;
+
+	if (led_act_cfg.spd100m_blink_en)
+		action_set |= LED_ACTION_100M_BLINK;
+
+	if (led_act_cfg.spd1000m_blink_en)
+		action_set |= LED_ACTION_1000M_BLINK;
+
+	if (led_act_cfg.spd2500m_blink_en)
+		action_set |= LED_ACTION_2500M_BLINK;
+
+	if (led_act_cfg.collision_blink_en)
+		action_set |= LED_ACTION_COLLISION_BLINK_ENABLE;
+
+	if (led_act_cfg.spd10m_on_en)
+		action_set |= LED_ACTION_10M_ON;
+
+	if (led_act_cfg.spd100m_on_en)
+		action_set |= LED_ACTION_100M_ON;
+
+	if (led_act_cfg.spd1000m_on_en)
+		action_set |= LED_ACTION_1000M_ON;
+
+	if (led_act_cfg.spd2500m_on_en)
+		action_set |= LED_ACTION_2500M_ON;
+
+	if (led_act_cfg.rxact_on_en)
+		action_set |= LED_ACTION_RXACT_ON;
+
+	if (led_act_cfg.txact_on_en)
+		action_set |= LED_ACTION_TXACT_ON;
+
+	if (led_act_cfg.rxact_blink_en)
+		action_set |= LED_ACTION_RXACT_BLINK;
+
+	if (led_act_cfg.txact_blink_en)
+		action_set |= LED_ACTION_TXACT_BLINK;
+
+	if (led_act_cfg.half_duplex_en)
+		action_set |= LED_ACTION_HALFDUPLEX_ON;
+
+	if (led_act_cfg.full_duplex_en)
+		action_set |= LED_ACTION_FULLDUPLEX_ON;
+
+	if (led_act_cfg.active_blink_indicate_en)
+		action_set |= LED_ACTION_ACTIVE_BLINK_INDICATE;
+
+	if (led_act_cfg.loopdetect_indicate_en)
+		action_set |= LED_ACTION_LOOPDETECT_INDICATE;
+
+	if (led_act_cfg.eee_indicate_en)
+		action_set |= LED_ACTION_EEE_INDICATE;
+
+	/* set disable_link_try bit to 1 */
+	if (led_id == LED_ID_0)
+		action_set |= LED_ACTION_DISABLE_LINK_TRY;
+
+	yt_shark_port_to_led_group(unit, port, &led_group_id);
+	reg_addr = led_ctrl_addr[led_id] + led_group_id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &entry), ret);
+	HAL_FIELD_SET(led_action_low_bit[led_id], led_action_width[led_id],
+		      &entry, action_set);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg_addr, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_action_set);
+
+u32 yt_led_action_get(u8 unit, u32 port, enum yt_led_id led_id,
+		      struct yt_led_act_cfg *led_act_cfg_ptr)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 led_ctrl_addr[LED_ID_NUM] = { LED_CTRL_0_BASE, LED_CTRL_1_BASE,
+					  LED_CTRL_2_BASE };
+	u32 led_action_low_bit[LED_ID_NUM] = {
+		LED_CTRL_0_BASE_LED_ACTION_LOWBIT,
+		LED_CTRL_1_BASE_LED_ACTION_LOWBIT,
+		LED_CTRL_2_BASE_LED_ACTION_LOWBIT
+	};
+	u32 led_action_width[LED_ID_NUM] = { LED_CTRL_0_BASE_LED_ACTION_WIDTH,
+					     LED_CTRL_1_BASE_LED_ACTION_WIDTH,
+					     LED_CTRL_2_BASE_LED_ACTION_WIDTH };
+	u32 action_set = 0;
+	u32 macid;
+	u32 reg_addr;
+	u8 led_group_id;
+
+	yt_shark_port_to_led_group(unit, port, &led_group_id);
+	reg_addr = led_ctrl_addr[led_id] + led_group_id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_addr, &entry), ret);
+	HAL_FIELD_GET(led_action_low_bit[led_id], led_action_width[led_id],
+		      &entry, &action_set);
+
+	if (action_set & LED_ACTION_10M_BLINK)
+		led_act_cfg_ptr->spd10m_blink_en = 1;
+
+	if (action_set & LED_ACTION_100M_BLINK)
+		led_act_cfg_ptr->spd100m_blink_en = 1;
+
+	if (action_set & LED_ACTION_1000M_BLINK)
+		led_act_cfg_ptr->spd1000m_blink_en = 1;
+
+	if (action_set & LED_ACTION_2500M_BLINK)
+		led_act_cfg_ptr->spd2500m_blink_en = 1;
+
+	if (action_set & LED_ACTION_COLLISION_BLINK_ENABLE)
+		led_act_cfg_ptr->collision_blink_en = 1;
+
+	if (action_set & LED_ACTION_10M_ON)
+		led_act_cfg_ptr->spd10m_on_en = 1;
+
+	if (action_set & LED_ACTION_100M_ON)
+		led_act_cfg_ptr->spd100m_on_en = 1;
+
+	if (action_set & LED_ACTION_1000M_ON)
+		led_act_cfg_ptr->spd1000m_on_en = 1;
+
+	if (action_set & LED_ACTION_2500M_ON)
+		led_act_cfg_ptr->spd2500m_on_en = 1;
+
+	if (action_set & LED_ACTION_RXACT_ON)
+		led_act_cfg_ptr->rxact_on_en = 1;
+
+	if (action_set & LED_ACTION_TXACT_ON)
+		led_act_cfg_ptr->txact_on_en = 1;
+
+	if (action_set & LED_ACTION_RXACT_BLINK)
+		led_act_cfg_ptr->rxact_blink_en = 1;
+
+	if (action_set & LED_ACTION_TXACT_BLINK)
+		led_act_cfg_ptr->txact_blink_en = 1;
+
+	if (action_set & LED_ACTION_HALFDUPLEX_ON)
+		led_act_cfg_ptr->half_duplex_en = 1;
+
+	if (action_set & LED_ACTION_FULLDUPLEX_ON)
+		led_act_cfg_ptr->full_duplex_en = 1;
+
+	if (action_set & LED_ACTION_ACTIVE_BLINK_INDICATE)
+		led_act_cfg_ptr->active_blink_indicate_en = 1;
+
+	if (action_set & LED_ACTION_LOOPDETECT_INDICATE)
+		led_act_cfg_ptr->loopdetect_indicate_en = 1;
+
+	if (action_set & LED_ACTION_EEE_INDICATE)
+		led_act_cfg_ptr->eee_indicate_en = 1;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_action_get);
+
+u32 yt_led_serial_active_mode_set(u8 unit, enum yt_sled_active_mode mode)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (mode < LED_SERIAL_ACTIVE_MODE_HIGH ||
+	    mode > LED_SERIAL_ACTIVE_MODE_LOW)
+		return CMM_ERR_INPUT;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_SERIAL_CTRL, &entry), ret);
+	HAL_FIELD_SET(LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT,
+		      LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH, &entry, mode);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LED_SERIAL_CTRL, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_active_mode_set);
+
+u32 yt_led_serial_active_mode_get(u8 unit, enum yt_sled_active_mode *mode_ptr)
+{
+	u32 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LED_SERIAL_CTRL, &entry), ret);
+	HAL_FIELD_GET(LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT,
+		      LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH, &entry, mode_ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_active_mode_get);
+#endif
+
+#if defined(CONFIG_MOTORCOMM_YT922X)
+u32 sled_output_mode = SLED_DATANUM_YT9228_P9L3;
+
+u32 yt_led_enable(u8 unit)
+{
+	u16 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_GLB_CTRL2;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &entry), ret);
+	HAL_FIELD_SET(LED_GLB_CTRL2_LED_CONFIG_DONE_LOWBIT,
+		      LED_GLB_CTRL2_LED_CONFIG_DONE_WIDTH, &entry, 1);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_enable);
+
+u32 yt_led_mode_set(u8 unit, enum yt_led_mode mode)
+{
+	u16 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+	u32 mode_val;
+
+	if (mode == LED_MODE_PARALLEL)
+		mode_val = 1;
+	else
+		mode_val = 0;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_GLB_CTRL1;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &entry), ret);
+	HAL_FIELD_SET(LED_GLB_CTRL1_LED_MODE_LOWBIT,
+		      LED_GLB_CTRL1_LED_MODE_WIDTH, &entry, mode_val);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_mode_set);
+
+u32 yt_led_mode_get(u8 unit, enum yt_led_mode *mode_ptr)
+{
+	u16 entry;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+	u32 mode_val;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_GLB_CTRL1;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &entry), ret);
+	HAL_FIELD_GET(LED_GLB_CTRL1_LED_MODE_LOWBIT,
+		      LED_GLB_CTRL1_LED_MODE_WIDTH, &entry, &mode_val);
+	if (mode_val == 0)
+		*mode_ptr = LED_MODE_SERIAL;
+	else
+		*mode_ptr = LED_MODE_PARALLEL;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_mode_get);
+
+u32 yt_led_action_set(u8 unit, u32 port, enum yt_led_id led_id,
+		      struct yt_led_act_cfg led_act_cfg)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 led_ctrl_addr_h[LED_ID_NUM] = { LED_CTRL_0_BASE_H,
+					    LED_CTRL_1_BASE_H,
+					    LED_CTRL_2_BASE_H };
+	u32 led_ctrl_addr_l[LED_ID_NUM] = { LED_CTRL_0_BASE_L,
+					    LED_CTRL_1_BASE_L,
+					    LED_CTRL_2_BASE_L };
+	u32 act_mask[LED_ID_NUM] = { 0x1fffff, 0x1fffff, 0x1fffff };
+	u32 action_set = 0;
+	u32 macid = 0;
+	u32 reg_addr_h = 0;
+	u32 reg_addr_l = 0;
+	u32 reg_val_tmp = 0;
+	u16 reg_val = 0;
+	struct yt_phy_reg_attr reg_attr;
+
+	if (led_id < LED_ID_0 || led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	if (led_act_cfg.spd10m_blink_en)
+		action_set |= LED_ACTION_10M_BLINK;
+
+	if (led_act_cfg.spd100m_blink_en)
+		action_set |= LED_ACTION_100M_BLINK;
+
+	if (led_act_cfg.spd1000m_blink_en)
+		action_set |= LED_ACTION_1000M_BLINK;
+
+	if (led_act_cfg.spd2500m_blink_en)
+		action_set |= LED_ACTION_2500M_BLINK;
+
+	if (led_act_cfg.spd5000m_blink_en)
+		action_set |= LED_ACTION_5000M_BLINK;
+
+	if (led_act_cfg.spd10000m_blink_en)
+		action_set |= LED_ACTION_10000M_BLINK;
+
+	if (led_act_cfg.collision_blink_en)
+		action_set |= LED_ACTION_COLLISION_BLINK_ENABLE;
+
+	if (led_act_cfg.spd10m_on_en)
+		action_set |= LED_ACTION_10M_ON;
+
+	if (led_act_cfg.spd100m_on_en)
+		action_set |= LED_ACTION_100M_ON;
+
+	if (led_act_cfg.spd1000m_on_en)
+		action_set |= LED_ACTION_1000M_ON;
+
+	if (led_act_cfg.spd2500m_on_en)
+		action_set |= LED_ACTION_2500M_ON;
+
+	if (led_act_cfg.spd5000m_on_en)
+		action_set |= LED_ACTION_5000M_ON;
+
+	if (led_act_cfg.spd10000m_on_en)
+		action_set |= LED_ACTION_10000M_ON;
+
+	if (led_act_cfg.rxact_on_en)
+		action_set |= LED_ACTION_RXACT_ON;
+
+	if (led_act_cfg.txact_on_en)
+		action_set |= LED_ACTION_TXACT_ON;
+
+	if (led_act_cfg.rxact_blink_en)
+		action_set |= LED_ACTION_RXACT_BLINK;
+
+	if (led_act_cfg.txact_blink_en)
+		action_set |= LED_ACTION_TXACT_BLINK;
+
+	if (led_act_cfg.half_duplex_en)
+		action_set |= LED_ACTION_HALFDUPLEX_ON;
+
+	if (led_act_cfg.full_duplex_en)
+		action_set |= LED_ACTION_FULLDUPLEX_ON;
+
+	if (led_act_cfg.loopdetect_indicate_en)
+		action_set |= LED_ACTION_LOOPDETECT_INDICATE;
+
+	if (led_act_cfg.eee_indicate_en)
+		action_set |= LED_ACTION_EEE_INDICATE;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+
+	reg_addr_h = led_ctrl_addr_h[led_id] + macid;
+	reg_addr_l = led_ctrl_addr_l[led_id] + macid;
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = reg_addr_h;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp = reg_val << 16;
+	reg_attr.reg_addr = reg_addr_l;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp |= (u32)reg_val;
+	reg_val_tmp &= (~act_mask[led_id]);
+	reg_val_tmp |= (action_set & act_mask[led_id]);
+
+	reg_val = (reg_val_tmp >> 16) & 0xFFFF;
+	reg_attr.reg_addr = reg_addr_h;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val), ret);
+	reg_val = reg_val_tmp & 0xFFFF;
+	reg_attr.reg_addr = reg_addr_l;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val), ret);
+
+	/* set disable LED when link try bit to 1*/
+	reg_attr.reg_addr = LED_BLINK_CTRL + macid;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val &= 0xff7f;
+	reg_val |= 0x80;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_action_set);
+
+u32 yt_led_action_get(u8 unit, u32 port, enum yt_led_id led_id,
+		      struct yt_led_act_cfg *led_act_cfg_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 led_ctrl_addr_h[LED_ID_NUM] = { LED_CTRL_0_BASE_H,
+					    LED_CTRL_1_BASE_H,
+					    LED_CTRL_2_BASE_H };
+	u32 led_ctrl_addr_l[LED_ID_NUM] = { LED_CTRL_0_BASE_L,
+					    LED_CTRL_1_BASE_L,
+					    LED_CTRL_2_BASE_L };
+	u32 act_mask[LED_ID_NUM] = { 0x1fffff, 0x1fffff, 0x1fffff };
+	u32 action_set = 0;
+	u32 macid = 0;
+	u32 reg_addr_h = 0;
+	u32 reg_addr_l = 0;
+	u32 reg_val_tmp = 0;
+	u16 reg_val = 0;
+	struct yt_phy_reg_attr reg_attr;
+
+	if (led_id < LED_ID_0 || led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	memset(led_act_cfg_ptr, 0, sizeof(struct yt_led_act_cfg));
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+
+	reg_addr_h = led_ctrl_addr_h[led_id] + macid;
+	reg_addr_l = led_ctrl_addr_l[led_id] + macid;
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = reg_addr_h;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp = reg_val << 16;
+	reg_attr.reg_addr = reg_addr_l;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp |= (u32)reg_val;
+
+	action_set = reg_val_tmp & act_mask[led_id];
+	if (action_set & LED_ACTION_10M_BLINK)
+		led_act_cfg_ptr->spd10m_blink_en = 1;
+
+	if (action_set & LED_ACTION_100M_BLINK)
+		led_act_cfg_ptr->spd100m_blink_en = 1;
+
+	if (action_set & LED_ACTION_1000M_BLINK)
+		led_act_cfg_ptr->spd1000m_blink_en = 1;
+
+	if (action_set & LED_ACTION_2500M_BLINK)
+		led_act_cfg_ptr->spd2500m_blink_en = 1;
+
+	if (action_set & LED_ACTION_5000M_BLINK)
+		led_act_cfg_ptr->spd5000m_blink_en = 1;
+
+	if (action_set & LED_ACTION_10000M_BLINK)
+		led_act_cfg_ptr->spd10000m_blink_en = 1;
+
+	if (action_set & LED_ACTION_COLLISION_BLINK_ENABLE)
+		led_act_cfg_ptr->collision_blink_en = 1;
+
+	if (action_set & LED_ACTION_10M_ON)
+		led_act_cfg_ptr->spd10m_on_en = 1;
+
+	if (action_set & LED_ACTION_100M_ON)
+		led_act_cfg_ptr->spd100m_on_en = 1;
+
+	if (action_set & LED_ACTION_1000M_ON)
+		led_act_cfg_ptr->spd1000m_on_en = 1;
+
+	if (action_set & LED_ACTION_2500M_ON)
+		led_act_cfg_ptr->spd2500m_on_en = 1;
+
+	if (action_set & LED_ACTION_5000M_ON)
+		led_act_cfg_ptr->spd5000m_on_en = 1;
+
+	if (action_set & LED_ACTION_10000M_ON)
+		led_act_cfg_ptr->spd10000m_on_en = 1;
+
+	if (action_set & LED_ACTION_RXACT_ON)
+		led_act_cfg_ptr->rxact_on_en = 1;
+
+	if (action_set & LED_ACTION_TXACT_ON)
+		led_act_cfg_ptr->txact_on_en = 1;
+
+	if (action_set & LED_ACTION_RXACT_BLINK)
+		led_act_cfg_ptr->rxact_blink_en = 1;
+
+	if (action_set & LED_ACTION_TXACT_BLINK)
+		led_act_cfg_ptr->txact_blink_en = 1;
+
+	if (action_set & LED_ACTION_HALFDUPLEX_ON)
+		led_act_cfg_ptr->half_duplex_en = 1;
+
+	if (action_set & LED_ACTION_FULLDUPLEX_ON)
+		led_act_cfg_ptr->full_duplex_en = 1;
+
+	if (action_set & LED_ACTION_LOOPDETECT_INDICATE)
+		led_act_cfg_ptr->loopdetect_indicate_en = 1;
+
+	if (action_set & LED_ACTION_EEE_INDICATE)
+		led_act_cfg_ptr->eee_indicate_en = 1;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_action_get);
+
+u32 yt_led_serial_output_mode_set(u8 unit, enum yt_sled_data_num mode)
+{
+	u16 reg_val = 0;
+	u16 port_num = 0;
+	u16 led_num = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_led_mode led_mode;
+	u16 port_mask = 0x1ff;
+	struct yt_phy_reg_attr reg_attr;
+
+	sled_output_mode = mode;
+
+	CMM_ERR_CHK(yt_led_mode_get(unit, &led_mode), ret);
+	if (led_mode == LED_MODE_SERIAL) {
+		if (sled_output_mode == SLED_DATANUM_YT9224_P6L1) {
+			port_num = 6;
+			led_num = 1;
+		} else if (sled_output_mode == SLED_DATANUM_YT9224_P6L2) {
+			port_num = 6;
+			led_num = 2;
+		} else if (sled_output_mode == SLED_DATANUM_YT9224_P6L3) {
+			port_num = 6;
+			led_num = 3;
+		} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L1) {
+			port_num = 9;
+			led_num = 1;
+		} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L2) {
+			port_num = 9;
+			led_num = 2;
+		} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L3) {
+			port_num = 9;
+			led_num = 3;
+		} else if (sled_output_mode == SLED_DATANUM_YT922X_P8L1) {
+			port_num = 8;
+			led_num = 1;
+			port_mask = 0x1fd;
+		} else {
+			return CMM_ERR_NOT_SUPPORT;
+		}
+
+		/* set port_num and led_num */
+		reg_attr.phy_type = PHY_INTERNAL;
+		reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+		reg_attr.reg_addr = LED_GLB_CTRL1;
+		reg_attr.reg_space = 0;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val),
+			    ret);
+		reg_val &= 0xe003;
+		reg_val |= ((led_num - 1) << 2);
+		reg_val |= (port_mask << 4);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val),
+			    ret);
+
+		reg_attr.reg_addr = LED_SERIAL_CTRL;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val),
+			    ret);
+		reg_val &= 0xff;
+		reg_val |= ((led_num * port_num) << 8);
+		if (sled_output_mode == SLED_DATANUM_YT922X_P8L1)
+			reg_val |= (0x3 << 3);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val),
+			    ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_output_mode_set);
+
+u32 yt_led_serial_output_mode_get(u8 unit, enum yt_sled_data_num *mode_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+
+	*mode_ptr = sled_output_mode;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_output_mode_get);
+
+u32 yt_led_serial_active_mode_set(u8 unit, enum yt_sled_active_mode mode)
+{
+	u16 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+
+	if (mode < LED_SERIAL_ACTIVE_MODE_HIGH ||
+	    mode > LED_SERIAL_ACTIVE_MODE_LOW)
+		return CMM_ERR_INPUT;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_SERIAL_CTRL;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &entry), ret);
+	HAL_FIELD_SET(LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT,
+		      LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH, &entry, mode);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_active_mode_set);
+
+u32 yt_led_serial_active_mode_get(u8 unit, enum yt_sled_active_mode *mode_ptr)
+{
+	u16 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr reg_attr;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_SERIAL_CTRL;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &entry), ret);
+	HAL_FIELD_GET(LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT,
+		      LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH, &entry, mode_ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_active_mode_get);
+
+u32 yt_led_serial_remapping_set(u8 unit, u8 index,
+				struct yt_led_remapping dst_info)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 dst_macid = 0;
+	u32 reg_val_tmp = 0;
+	u16 reg_val = 0;
+	u8 per_port_led_num = 0;
+	u8 index_tmp = 0;
+	enum yt_led_mode mode;
+	struct yt_phy_reg_attr reg_attr;
+
+	/* get remapping index */
+	if (sled_output_mode == SLED_DATANUM_YT9224_P6L1) {
+		per_port_led_num = 1;
+		if (index > 0)
+			index_tmp = ((index + 3) / per_port_led_num) * 3 +
+				    (index + 3) % per_port_led_num;
+		else
+			index_tmp = index;
+	} else if (sled_output_mode == SLED_DATANUM_YT9224_P6L2) {
+		per_port_led_num = 2;
+		if (index > 1)
+			index_tmp = ((index + 6) / per_port_led_num) * 3 +
+				    (index + 6) % per_port_led_num;
+		else
+			index_tmp = index;
+	} else if (sled_output_mode == SLED_DATANUM_YT9224_P6L3) {
+		per_port_led_num = 3;
+		if (index > 2)
+			index_tmp = ((index + 9) / per_port_led_num) * 3 +
+				    (index + 9) % per_port_led_num;
+		else
+			index_tmp = index;
+	} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L1) {
+		per_port_led_num = 1;
+		index_tmp = (index / per_port_led_num) * 3 +
+			    index % per_port_led_num;
+	} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L2) {
+		per_port_led_num = 2;
+		index_tmp = (index / per_port_led_num) * 3 +
+			    index % per_port_led_num;
+	} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L3) {
+		per_port_led_num = 3;
+		index_tmp = (index / per_port_led_num) * 3 +
+			    index % per_port_led_num;
+	} else if (sled_output_mode == SLED_DATANUM_YT922X_P8L1) {
+		per_port_led_num = 1;
+		index_tmp = (index / per_port_led_num) * 3 +
+			    index % per_port_led_num;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	CMM_ERR_CHK(yt_led_mode_get(unit, &mode), ret);
+	if (mode != LED_MODE_SERIAL)
+		return CMM_ERR_NOT_SUPPORT;
+
+	if (dst_info.led_id < LED_ID_0 || LED_ID_2 < dst_info.led_id ||
+	    index_tmp > 26)
+		return CMM_ERR_INPUT;
+
+	dst_macid = CAL_YTP_TO_MAC(unit, dst_info.port);
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_MAPPING_BASE + (index_tmp / 3) * 2;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp = reg_val << 16;
+	reg_attr.reg_addr = LED_MAPPING_BASE + (index_tmp / 3) * 2 + 1;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp |= (u32)reg_val;
+
+	reg_val_tmp &= ~(0x3fU << ((index_tmp % 3) * 6));
+	reg_val_tmp |= dst_macid << (((index_tmp % 3) * 6) + 2);
+	reg_val_tmp |= dst_info.led_id << ((index_tmp % 3) * 6);
+
+	reg_val = (reg_val_tmp >> 16) & 0xFFFF;
+	reg_attr.reg_addr = LED_MAPPING_BASE + (index_tmp / 3) * 2;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val), ret);
+	reg_val = reg_val_tmp & 0xFFFF;
+	reg_attr.reg_addr = LED_MAPPING_BASE + (index_tmp / 3) * 2 + 1;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_remapping_set);
+
+u32 yt_led_serial_remapping_get(u8 unit, u8 index,
+				struct yt_led_remapping *dst_info_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 dst_macid = 0;
+	u32 reg_val_tmp = 0;
+	u16 reg_val = 0;
+	u8 per_port_led_num = 0;
+	u8 index_tmp = 0;
+	enum yt_led_mode mode;
+	struct yt_phy_reg_attr reg_attr;
+
+	/* get remapping index */
+	if (sled_output_mode == SLED_DATANUM_YT9224_P6L1) {
+		per_port_led_num = 1;
+
+		if (index > 0)
+			index_tmp = ((index + 3) / per_port_led_num) * 3 +
+				    (index + 3) % per_port_led_num;
+		else
+			index_tmp = index;
+	} else if (sled_output_mode == SLED_DATANUM_YT9224_P6L2) {
+		per_port_led_num = 2;
+
+		if (index > 1)
+			index_tmp = ((index + 6) / per_port_led_num) * 3 +
+				    (index + 6) % per_port_led_num;
+		else
+			index_tmp = index;
+	} else if (sled_output_mode == SLED_DATANUM_YT9224_P6L3) {
+		per_port_led_num = 3;
+
+		if (index > 2)
+			index_tmp = ((index + 9) / per_port_led_num) * 3 +
+				    (index + 9) % per_port_led_num;
+		else
+			index_tmp = index;
+	} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L1) {
+		per_port_led_num = 1;
+
+		index_tmp = (index / per_port_led_num) * 3 +
+			    index % per_port_led_num;
+	} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L2) {
+		per_port_led_num = 2;
+
+		index_tmp = (index / per_port_led_num) * 3 +
+			    index % per_port_led_num;
+	} else if (sled_output_mode == SLED_DATANUM_YT9228_P9L3) {
+		per_port_led_num = 3;
+
+		index_tmp = (index / per_port_led_num) * 3 +
+			    index % per_port_led_num;
+	} else if (sled_output_mode == SLED_DATANUM_YT922X_P8L1) {
+		per_port_led_num = 1;
+
+		index_tmp = (index / per_port_led_num) * 3 +
+			    index % per_port_led_num;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	CMM_ERR_CHK(yt_led_mode_get(unit, &mode), ret);
+	if (mode != LED_MODE_SERIAL)
+		return CMM_ERR_NOT_SUPPORT;
+
+	if (index_tmp > 26)
+		return CMM_ERR_INPUT;
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_MAPPING_BASE + (index_tmp / 3) * 2;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp = reg_val << 16;
+	reg_attr.reg_addr = LED_MAPPING_BASE + (index_tmp / 3) * 2 + 1;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp |= (u32)reg_val;
+
+	dst_macid = (reg_val_tmp >> (((index_tmp % 3) * 6) + 2)) & 0xf;
+	CAL_MAC_TO_YTP(unit, dst_macid, dst_info_ptr->port);
+	dst_info_ptr->led_id = (reg_val_tmp >> ((index_tmp % 3) * 6)) & 0x3;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_serial_remapping_get);
+
+u32 yt_led_parallel_remapping_set(u8 unit, struct yt_led_remapping src_info,
+				  struct yt_led_remapping dst_info)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 src_macid = 0;
+	u32 dst_macid = 0;
+	u16 reg_val = 0;
+	u32 reg_val_tmp = 0;
+	enum yt_led_mode mode;
+	struct yt_phy_reg_attr reg_attr;
+
+	CMM_ERR_CHK(yt_led_mode_get(unit, &mode), ret);
+	if (mode != LED_MODE_PARALLEL)
+		return CMM_ERR_NOT_SUPPORT;
+
+	if (src_info.led_id < LED_ID_0 || src_info.led_id > LED_ID_2 ||
+	    dst_info.led_id < LED_ID_0 || src_info.led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	src_macid = CAL_YTP_TO_MAC(unit, src_info.port);
+	dst_macid = CAL_YTP_TO_MAC(unit, dst_info.port);
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_MAPPING_BASE + src_macid * 2;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp = reg_val << 16;
+	reg_attr.reg_addr = LED_MAPPING_BASE + src_macid * 2 + 1;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp |= (u32)reg_val;
+
+	reg_val_tmp &= ~(0x3fU << (src_info.led_id * 6));
+	reg_val_tmp |= dst_macid << ((src_info.led_id * 6) + 2);
+	reg_val_tmp |= dst_info.led_id << (src_info.led_id * 6);
+
+	reg_val = (reg_val_tmp >> 16) & 0xFFFF;
+	reg_attr.reg_addr = LED_MAPPING_BASE + src_macid * 2;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val), ret);
+	reg_val = reg_val_tmp & 0xFFFF;
+	reg_attr.reg_addr = LED_MAPPING_BASE + src_macid * 2 + 1;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_remapping_set);
+
+u32 yt_led_parallel_remapping_get(u8 unit, struct yt_led_remapping src_info,
+				  struct yt_led_remapping *dst_info_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 src_macid = 0;
+	u32 dst_macid = 0;
+	u16 reg_val = 0;
+	u32 reg_val_tmp = 0;
+	enum yt_led_mode mode;
+	struct yt_phy_reg_attr reg_attr;
+
+	CMM_ERR_CHK(yt_led_mode_get(unit, &mode), ret);
+	if (mode != LED_MODE_PARALLEL)
+		return CMM_ERR_NOT_SUPPORT;
+
+	if (src_info.led_id < LED_ID_0 || src_info.led_id > LED_ID_2)
+		return CMM_ERR_INPUT;
+
+	src_macid = CAL_YTP_TO_MAC(unit, src_info.port);
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_addr = LED_MAPPING_BASE + src_macid * 2;
+	reg_attr.reg_space = 0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp = reg_val << 16;
+	reg_attr.reg_addr = LED_MAPPING_BASE + src_macid * 2 + 1;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val), ret);
+	reg_val_tmp |= (u32)reg_val;
+
+	dst_macid = (reg_val_tmp >> (((src_info.led_id * 6) + 2))) & 0xf;
+	CAL_MAC_TO_YTP(unit, dst_macid, dst_info_ptr->port);
+	dst_info_ptr->led_id = (reg_val_tmp >> ((src_info.led_id * 6))) & 0x3;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_remapping_get);
+
+u32 yt_led_parallel_pos_set(u8 unit, u32 port, enum yt_led_id led_id,
+			    enum yt_led_pos pos)
+{
+	u16 reg_val = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = 0;
+	struct yt_phy_reg_attr reg_attr;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_space = 0;
+	if (macid < 5U) {
+		reg_attr.reg_addr = LED_POS_CTRL1;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val),
+			    ret);
+		reg_val &= ~(1U << ((macid * 3) + led_id));
+		reg_val |= ((pos & 0x1) << ((macid * 3) + led_id));
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val),
+			    ret);
+	} else {
+		reg_attr.reg_addr = LED_POS_CTRL2;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val),
+			    ret);
+		reg_val &= ~(1U << (((macid - 5) * 3) + led_id));
+		reg_val |= ((pos & 0x1) << (((macid - 5) * 3) + led_id));
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, reg_attr, reg_val),
+			    ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_pos_set);
+
+u32 yt_led_parallel_pos_get(u8 unit, u32 port, enum yt_led_id led_id,
+			    enum yt_led_pos *pos_ptr)
+{
+	u16 reg_val = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = 0;
+	struct yt_phy_reg_attr reg_attr;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+
+	reg_attr.phy_type = PHY_INTERNAL;
+	reg_attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+	reg_attr.reg_space = 0;
+	if (macid < 5U) {
+		reg_attr.reg_addr = LED_POS_CTRL1;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val),
+			    ret);
+		*pos_ptr = (reg_val >> ((macid * 3) + led_id)) & 0x1;
+	} else {
+		reg_attr.reg_addr = LED_POS_CTRL2;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, reg_attr, &reg_val),
+			    ret);
+		*pos_ptr = (reg_val >> (((macid - 5) * 3) + led_id)) & 0x1;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_led_parallel_pos_get);
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_led.h b/drivers/net/dsa/motorcomm/switch/yt_led.h
new file mode 100644
index 000000000000..672139968927
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_led.h
@@ -0,0 +1,479 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_LED_H
+#define __YT_LED_H
+
+#ifdef __cplusplus
+"C"
+{
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+	enum yt_led_id { LED_ID_0 = 0, LED_ID_1, LED_ID_2, LED_ID_NUM };
+
+	struct yt_led_act_cfg {
+		u8 spd10m_blink_en;
+		u8 spd100m_blink_en;
+		u8 spd1000m_blink_en;
+#if defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+		u8 spd2500m_blink_en;
+#endif
+#if defined(CONFIG_MOTORCOMM_YT922X)
+		u8 spd5000m_blink_en;
+		u8 spd10000m_blink_en;
+#endif
+		u8 collision_blink_en;
+		u8 spd10m_on_en;
+		u8 spd100m_on_en;
+		u8 spd1000m_on_en;
+#if defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+		u8 spd2500m_on_en;
+#endif
+#if defined(CONFIG_MOTORCOMM_YT922X)
+		u8 spd5000m_on_en;
+		u8 spd10000m_on_en;
+#endif
+		u8 rxact_on_en;
+		u8 txact_on_en;
+		u8 rxact_blink_en;
+		u8 txact_blink_en;
+		u8 half_duplex_en; /* half duplex on enable */
+		u8 full_duplex_en; /* full duplex on enable */
+#if defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT921X)
+		u8 active_blink_indicate_en;
+#endif
+		u8 loopdetect_indicate_en;
+		u8 eee_indicate_en;
+#if defined(CONFIG_MOTORCOMM_YT921X)
+		u8 collision_blink_indicate_en; /* only for LED0 */
+#endif
+	};
+
+	enum yt_led_blink_event {
+		LED_BLINK_EVENT_EEE = 0,
+		LED_BLINK_EVENT_OTHERS,
+		LED_BLINK_EVENT_NUM
+	};
+
+	enum yt_led_blink_freq {
+		LED_BLINK_FREQ_2HZ = 0,
+		LED_BLINK_FREQ_4HZ,
+		LED_BLINK_FREQ_8HZ,
+		LED_BLINK_FREQ_16HZ,
+		LED_BLINK_FREQ_NUM
+	};
+
+	enum yt_led_blink_duty {
+		LED_BLINK_DUTY_50 = 0,
+		LED_BLINK_DUTY_67 = 1,
+		LED_BLINK_DUTY_75 = 2,
+		LED_BLINK_DUTY_83 = 3,
+		LED_BLINK_DUTY_33 = 5,
+		LED_BLINK_DUTY_25 = 6,
+		LED_BLINK_DUTY_17 = 7
+	};
+
+	enum yt_led_loopdetect_blink_rate {
+		LED_LOOPDETECT_RATE_512MS = 0,
+		LED_LOOPDETECT_RATE_1024MS,
+		LED_LOOPDETECT_RATE_1536MS,
+		LED_LOOPDETECT_RATE_2048MS,
+		LED_LOOPDETECT_RATE_NUM
+	};
+
+	enum yt_led_force_mode {
+		LED_FORCE_MODE_NORMAL = 0, /* no force */
+		LED_FORCE_MODE_BLINK,
+		LED_FORCE_MODE_LOW,
+		LED_FORCE_MODE_HIGH,
+		LED_FORCE_MODE_NUM
+	};
+
+	enum yt_led_force_rate {
+		LED_FORCE_RATE_512MS = 0,
+		LED_FORCE_RATE_1024MS,
+		LED_FORCE_RATE_2048MS,
+		LED_FORCE_RATE_NORMAL,
+		LED_FORCE_RATE_NUM
+	};
+
+	/* bit num on LED_DATA */
+	enum yt_sled_data_num {
+#if defined(CONFIG_MOTORCOMM_YT921X)
+		SLED_DATANUM_YT9215_P5L1 = 0,
+		SLED_DATANUM_YT9215_P7L1,
+		SLED_DATANUM_YT9215_P5L2,
+		SLED_DATANUM_YT9215_P7L2,
+		SLED_DATANUM_YT9215_P5L3,
+		SLED_DATANUM_YT9215_P7L3,
+		SLED_DATANUM_YT9218_P10L1,
+		SLED_DATANUM_YT9218_P10L2,
+		SLED_DATANUM_YT9218_P10L3,
+		SLED_DATANUM_YT9204_P6L1,
+		SLED_DATANUM_YT9204_P6L2,
+		SLED_DATANUM_YT9204_P6L3,
+#endif
+#if defined(CONFIG_MOTORCOMM_YT922X)
+		SLED_DATANUM_YT9224_P6L1,
+		SLED_DATANUM_YT9224_P6L2,
+		SLED_DATANUM_YT9224_P6L3,
+		SLED_DATANUM_YT9228_P9L1,
+		SLED_DATANUM_YT9228_P9L2,
+		SLED_DATANUM_YT9228_P9L3,
+		SLED_DATANUM_YT922X_P8L1,
+#endif
+		SLED_DATANUM_MAX
+	};
+
+	/* LED description */
+	enum yt_led_mode {
+		LED_MODE_PARALLEL = 0,
+		LED_MODE_SCAN, /* no support */
+		LED_MODE_SERIAL,
+		LED_MODE_NUM
+	};
+
+	enum yt_sled_active_mode {
+		LED_SERIAL_ACTIVE_MODE_HIGH = 0,
+		LED_SERIAL_ACTIVE_MODE_LOW
+	};
+
+	/* valid only for serial mode */
+	struct yt_sled_remap_info {
+		u8 port : 6;
+		u8 led_id : 2;
+	};
+
+	struct yt_led_remapping {
+		u32 port;
+		enum yt_led_id led_id;
+	};
+
+	enum yt_led_seled_num {
+		YT_LED_SELED_NUM_ONE = 0,
+		YT_LED_SELED_NUM_TWO,
+		YT_LED_SELED_NUM_THREE,
+		YT_LED_SELED_NUM_MAX
+	};
+
+	struct yt_led_seled_info {
+		struct yt_port_mask ledportmask;
+		struct yt_port_mask comboportmask;
+		enum yt_led_seled_num led_mode;
+	};
+
+	enum yt_led_phy_type {
+		YT_LED_PHY_TYPE_COPPER,
+		YT_LED__PHY_TYPE_FIBER,
+	};
+
+	struct yt_led_slot {
+		u8 serial_id;
+		u8 led_id;
+	};
+
+	enum yt_led_pos { LED_POS_HIGH = 0, LED_POS_LOW };
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+	enum led_action {
+		LED_ACTION_10M_BLINK = 0x1,
+		LED_ACTION_100M_BLINK = 0x2,
+		LED_ACTION_1000M_BLINK = 0x4,
+		LED_ACTION_COLLISION_BLINK_ENABLE = 0x8,
+		LED_ACTION_10M_ON = 0x10,
+		LED_ACTION_100M_ON = 0x20,
+		LED_ACTION_1000M_ON = 0x40,
+		LED_ACTION_RXACT_ON = 0x80,
+		LED_ACTION_TXACT_ON = 0x100,
+		LED_ACTION_RXACT_BLINK = 0x200,
+		LED_ACTION_TXACT_BLINK = 0x400,
+		LED_ACTION_HALFDUPLEX_ON = 0x800,
+		LED_ACTION_FULLDUPLEX_ON = 0x1000,
+		LED_ACTION_ACTIVE_BLINK_INDICATE = 0x2000,
+		LED_ACTION_LOOPDETECT_INDICATE = 0x4000,
+		LED_ACTION_EEE_INDICATE = 0x8000,
+		LED_ACTION_COLLISION_BLINK = 0x10000, /* only tiger for LED0 */
+		LED_ACTION_DISABLE_LINK_TRY = 0x20000, /* only tiger for LED0 */
+		LED_ACTION_NUM
+	};
+#endif
+
+#if defined(CONFIG_MOTORCOMM_YT923X)
+	enum led_action {
+		LED_ACTION_10M_BLINK = 0x1,
+		LED_ACTION_100M_BLINK = 0x2,
+		LED_ACTION_1000M_BLINK = 0x4,
+		LED_ACTION_2500M_BLINK = 0x8,
+		LED_ACTION_COLLISION_BLINK_ENABLE = 0x10,
+		LED_ACTION_10M_ON = 0x20,
+		LED_ACTION_100M_ON = 0x40,
+		LED_ACTION_1000M_ON = 0x80,
+		LED_ACTION_2500M_ON = 0x100,
+		LED_ACTION_RXACT_ON = 0x200,
+		LED_ACTION_TXACT_ON = 0x400,
+		LED_ACTION_RXACT_BLINK = 0x800,
+		LED_ACTION_TXACT_BLINK = 0x1000,
+		LED_ACTION_HALFDUPLEX_ON = 0x2000,
+		LED_ACTION_FULLDUPLEX_ON = 0x4000,
+		LED_ACTION_ACTIVE_BLINK_INDICATE = 0x8000,
+		LED_ACTION_LOOPDETECT_INDICATE = 0x10000,
+		LED_ACTION_EEE_INDICATE = 0x20000,
+		LED_ACTION_DISABLE_LINK_TRY = 0x40000, /* only for LED0 */
+		LED_ACTION_NUM
+	};
+#endif
+
+#if defined(CONFIG_MOTORCOMM_YT922X)
+	enum led_action {
+		LED_ACTION_10M_BLINK = 0x1,
+		LED_ACTION_100M_BLINK = 0x2,
+		LED_ACTION_1000M_BLINK = 0x4,
+		LED_ACTION_2500M_BLINK = 0x8,
+		LED_ACTION_5000M_BLINK = 0x10,
+		LED_ACTION_10000M_BLINK = 0x20,
+		LED_ACTION_COLLISION_BLINK_ENABLE = 0x40,
+		LED_ACTION_10M_ON = 0x80,
+		LED_ACTION_100M_ON = 0x100,
+		LED_ACTION_1000M_ON = 0x200,
+		LED_ACTION_2500M_ON = 0x400,
+		LED_ACTION_5000M_ON = 0x800,
+		LED_ACTION_10000M_ON = 0x1000,
+		LED_ACTION_RXACT_ON = 0x2000,
+		LED_ACTION_TXACT_ON = 0x4000,
+		LED_ACTION_RXACT_BLINK = 0x8000,
+		LED_ACTION_TXACT_BLINK = 0x10000,
+		LED_ACTION_HALFDUPLEX_ON = 0x20000,
+		LED_ACTION_FULLDUPLEX_ON = 0x40000,
+		LED_ACTION_LOOPDETECT_INDICATE = 0x80000,
+		LED_ACTION_EEE_INDICATE = 0x100000,
+		LED_ACTION_NUM
+	};
+#endif
+
+/**
+ * @internal      yt_led_enable
+ * @endinternal
+ *
+ * @brief         enabel LED
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_enable(u8 unit);
+
+/**
+ * @internal      yt_led_mode_set
+ * @endinternal
+ *
+ * @brief         select the mode of led
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     mode                -serial or parallel mode
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_mode_set(u8 unit, enum yt_led_mode mode);
+
+/**
+ * @internal      yt_led_mode_get
+ * @endinternal
+ *
+ * @brief         get the mode of led
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[out]    mode_ptr            -serial or parallel mode
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_mode_get(u8 unit, enum yt_led_mode *mode_ptr);
+
+/**
+ * @internal      yt_led_action_set
+ * @endinternal
+ *
+ * @brief         select the action of LED0~2 per port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     led_id              -led id
+ * @param[in]     led_act_cfg         -config of led action
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_action_set(u8 unit, u32 port, enum yt_led_id led_id,
+		      struct yt_led_act_cfg led_act_cfg);
+
+/**
+ * @internal      yt_led_action_get
+ * @endinternal
+ *
+ * @brief         get the action of LED0~2 per port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     led_id              -led id
+ * @param[out]    led_act_cfg_ptr     -config of led action
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_action_get(u8 unit, u32 port, enum yt_led_id led_id,
+		      struct yt_led_act_cfg *led_act_cfg_ptr);
+
+u32 yt_led_serial_output_mode_set(u8 unit, enum yt_sled_data_num mode);
+u32 yt_led_serial_output_mode_get(u8 unit, enum yt_sled_data_num *mode_ptr);
+
+/**
+ * @internal      yt_led_serial_active_mode_set
+ * @endinternal
+ *
+ * @brief         select the active mode of serial LED
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     mode                -active mode
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_serial_active_mode_set(u8 unit, enum yt_sled_active_mode mode);
+
+/**
+ * @internal      yt_led_serial_active_mode_get
+ * @endinternal
+ *
+ * @brief         get the active mode of serial LED
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]    mode_ptr            -active mode
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_serial_active_mode_get(u8 unit, enum yt_sled_active_mode *mode_ptr);
+
+/**
+ * @internal      yt_led_serial_remapping_set
+ * @endinternal
+ *
+ * @brief         select the remapping state of serial LED(dst-->src)
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     index               -index of led_data
+ * @param[in]     dst_info            -the destination information of remapping
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_serial_remapping_set(u8 unit, u8 index,
+				struct yt_led_remapping dst_info);
+/**
+ * @internal      yt_led_serial_remapping_get
+ * @endinternal
+ *
+ * @brief         get the remapping state of serial LED
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     index               -index of led_data
+ * @param[out]    dst_info_ptr        -the destination information of remapping
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_serial_remapping_get(u8 unit, u8 index,
+				struct yt_led_remapping *dst_info_ptr);
+
+/**
+ * @internal      yt_led_parallel_output_set
+ * @endinternal
+ *
+ * @brief         select output port mask of parallel LED
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port_mask           -port bit mask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_parallel_output_set(u8 unit, struct yt_port_mask port_mask);
+
+/**
+ * @internal      yt_led_parallel_output_get
+ * @endinternal
+ *
+ * @brief         get output port mask of parallel LED
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[out]    port_mask_ptr       -port bit mask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_parallel_output_get(u8 unit, struct yt_port_mask *port_mask_ptr);
+
+/**
+ * @internal      yt_led_parallel_remapping_set
+ * @endinternal
+ *
+ * @brief         select the remapping state of parallel LED(dst-->src)
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     src_info            -the source information of remapping
+ * @param[in]     dst_info            -the destination information of remapping
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_parallel_remapping_set(u8 unit, struct yt_led_remapping src_info,
+				  struct yt_led_remapping dst_info);
+
+/**
+ * @internal      yt_led_parallel_remapping_get
+ * @endinternal
+ *
+ * @brief         get the remapping state of parallel LED
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     src_info            -the source information of remapping
+ * @param[out]    dst_info_ptr        -the destination information of remapping
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_parallel_remapping_get(u8 unit, struct yt_led_remapping src_info,
+				  struct yt_led_remapping *dst_info_ptr);
+
+/**
+ * @internal      yt_led_parallel_pos_set
+ * @endinternal
+ *
+ * @brief         set led pos signal for parallel mode
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     led_id              -led id
+ * @param[in]     pos                 -led pos
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_parallel_pos_set(u8 unit, u32 port, enum yt_led_id led_id,
+			    enum yt_led_pos pos);
+
+/**
+ * @internal      yt_led_parallel_pos_get
+ * @endinternal
+ *
+ * @brief         get inverted status of led_pos signal
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     led_id              -led id
+ * @param[in]     pos_ptr             -led pos
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_led_parallel_pos_get(u8 unit, u32 port, enum yt_led_id led_id,
+			    enum yt_led_pos *pos_ptr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_loopdetect.c b/drivers/net/dsa/motorcomm/switch/yt_loopdetect.c
new file mode 100644
index 000000000000..6dc152301a3f
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_loopdetect.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_init.h"
+#include "yt_loopdetect.h"
+#include "yt_port.h"
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+u32 yt_loop_detect_enable_set(u8 unit, enum yt_enable enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	u32 reg_data;
+	u32 ret;
+	u32 entry;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (enable == YT_ENABLE) {
+		HAL_MEM_DIRECT_READ(unit, PON_STRAP, &reg_data);
+		if (IS_BIT_SET(reg_data, 7)) {
+			HAL_MEM_DIRECT_READ(unit, PON_STRAP_EN, &reg_data);
+			SET_BIT(reg_data, 7);
+			HAL_MEM_DIRECT_WRITE(unit, PON_STRAP_EN, reg_data);
+			HAL_MEM_DIRECT_READ(unit, PON_STRAP_VAL, &reg_data);
+			CLEAR_BIT(reg_data, 7);
+			HAL_MEM_DIRECT_WRITE(unit, PON_STRAP_VAL, reg_data);
+		}
+
+		if (chip_id == YT_SW_ID_920X) {
+			HAL_MEM_DIRECT_READ(unit, LOOP_DETECT_CTRL1, &reg_data);
+			if (IS_BIT_SET(reg_data, 10)) {
+				CLEAR_BIT(reg_data, 10);
+				HAL_MEM_DIRECT_WRITE(unit, LOOP_DETECT_CTRL1,
+						     reg_data);
+			}
+		}
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LOOP_DETECT_TOP_CTRL, &entry),
+		    ret);
+	HAL_FIELD_SET(LOOP_DETECT_TOP_CTRL_EN_LOWBIT,
+		      LOOP_DETECT_TOP_CTRL_EN_WIDTH, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LOOP_DETECT_TOP_CTRL, entry),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_loop_detect_enable_set);
+
+#elif defined(CONFIG_MOTORCOMM_YT923X)
+u32 yt_loop_detect_enable_set(u8 unit, enum yt_enable enable)
+{
+	u32 reg_data;
+	u32 entry;
+	u32 ret;
+
+	if (enable == YT_ENABLE) {
+		HAL_MEM_DIRECT_READ(unit, PON_STRAP, &reg_data);
+		if (IS_BIT_SET(reg_data, 7)) {
+			HAL_MEM_DIRECT_READ(unit, PON_STRAP_EN, &reg_data);
+			SET_BIT(reg_data, 7);
+			HAL_MEM_DIRECT_WRITE(unit, PON_STRAP_EN, reg_data);
+			HAL_MEM_DIRECT_READ(unit, PON_STRAP_VAL, &reg_data);
+			CLEAR_BIT(reg_data, 7);
+			HAL_MEM_DIRECT_WRITE(unit, PON_STRAP_VAL, reg_data);
+		}
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LOOP_DETECT_TOP_CTRL, &entry),
+		    ret);
+	HAL_FIELD_SET(LOOP_DETECT_TOP_CTRL_EN_LOWBIT,
+		      LOOP_DETECT_TOP_CTRL_EN_WIDTH, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LOOP_DETECT_TOP_CTRL, entry),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_loop_detect_enable_set);
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+u32 yt_loop_detect_enable_set(u8 unit, enum yt_enable enable)
+{
+	struct yt_phy_reg_attr attr;
+	u16 data;
+	u32 entry;
+	u32 ret;
+
+	if (enable == YT_ENABLE) {
+		HAL_MEM_DIRECT_READ(unit, PON_STRAP, &entry);
+		if (IS_BIT_SET(entry, 7)) {
+			attr.phy_type = PHY_INTERNAL;
+			attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+			attr.reg_addr = PON_STRAP_EN;
+			attr.reg_space = 0;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, attr, &data),
+				    ret);
+			SET_BIT(data, 7);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, attr, data),
+				    ret);
+
+			attr.reg_addr = PON_STRAP_VAL;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, 0, attr, &data),
+				    ret);
+			CLEAR_BIT(data, 7);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, 0, attr, data),
+				    ret);
+		}
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LOOP_DETECT_TOP_CTRL, &entry),
+		    ret);
+	HAL_FIELD_SET(LOOP_DETECT_TOP_CTRL_EN_LOWBIT,
+		      LOOP_DETECT_TOP_CTRL_EN_WIDTH, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, LOOP_DETECT_TOP_CTRL, entry),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_loop_detect_enable_set);
+#endif
+
+u32 yt_loop_detect_enable_get(u8 unit, enum yt_enable *enable)
+{
+	u32 entry;
+	u32 ret;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, LOOP_DETECT_TOP_CTRL, &entry),
+		    ret);
+	HAL_FIELD_GET(LOOP_DETECT_TOP_CTRL_EN_LOWBIT,
+		      LOOP_DETECT_TOP_CTRL_EN_WIDTH, &entry, enable);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_loop_detect_enable_get);
+
+u32 yt_loop_detect_loopedports_get(u8 unit, struct yt_port_mask *port_mask)
+{
+	struct yt_port_mask macmask;
+	u32 reg_data = 0;
+	u32 ret;
+
+	CMM_PARAM_CHK((!port_mask), CMM_ERR_INPUT);
+	CMM_CLEAR_MEMBER_PORT(macmask);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_LOOP_DETECT_FLAG_DUMMY,
+					&reg_data), ret);
+	macmask.portbits[0] = reg_data;
+	CAL_MLIST_TO_YTPLIST(unit, macmask, (*port_mask));
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_loop_detect_loopedports_get);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_loopdetect.h b/drivers/net/dsa/motorcomm/switch/yt_loopdetect.h
new file mode 100644
index 000000000000..e5e465c3e39e
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_loopdetect.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_LOOPDETECT_H
+#define __YT_LOOPDETECT_H
+
+#include "yt_cmm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @internal      yt_loop_detect_enable_set
+ * @endinternal
+ *
+ * @brief         Set loopdetect state
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_loop_detect_enable_set(u8 unit, enum yt_enable enable);
+
+/**
+ * @internal      yt_loop_detect_enable_get
+ * @endinternal
+ *
+ * @brief         Get loopdetect state
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]    enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_loop_detect_enable_get(u8 unit, enum yt_enable *enable);
+
+/**
+ * @internal      yt_loop_detect_loopedports_get
+ * @endinternal
+ *
+ * @brief         Get looped ports
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]    port_mask           -pointer to port mask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_loop_detect_loopedports_get(u8 unit, struct yt_port_mask *port_mask);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_mirror.c b/drivers/net/dsa/motorcomm/switch/yt_mirror.c
new file mode 100644
index 000000000000..b64e817b0f7e
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_mirror.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_mirror.h"
+#if defined(CONFIG_MOTORCOMM_YT921X)
+u32 yt_mirror_group_set(u8 unit, u8 group, struct yt_mirror_entry *mirror_entry)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_port_mask macmsk;
+	u32 macid;
+	u32 cfg;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MIRROR_CTRL, &cfg), ret);
+	if (mirror_entry->flags & YT_MIRROR_FLAG_MIRROR_PORT) {
+		/* disable port mirror */
+		if (mirror_entry->mirrorport == YT_MIRROR_INVALID_PORT) {
+			HAL_FIELD_SET(MIRROR_CTRL_MIRROR_PORT_LOWBIT,
+				      MIRROR_CTRL_MIRROR_PORT_WIDTH, &cfg,
+				      YT_MIRROR_INVALID_PORT);
+		} else {
+			macid = CAL_YTP_TO_MAC(unit, mirror_entry->mirrorport);
+			HAL_FIELD_SET(MIRROR_CTRL_MIRROR_PORT_LOWBIT,
+				      MIRROR_CTRL_MIRROR_PORT_WIDTH, &cfg,
+				      macid);
+		}
+	}
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_PORT_INGRESS) {
+		CAL_YTPLIST_TO_MLIST(unit, mirror_entry->rxportmask, macmsk);
+		HAL_FIELD_SET(MIRROR_CTRL_INGR_MIRROR_EN_LOWBIT,
+			      MIRROR_CTRL_INGR_MIRROR_EN_WIDTH, &cfg,
+			      macmsk.portbits[0]);
+	}
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_PORT_EGRESS) {
+		CAL_YTPLIST_TO_MLIST(unit, mirror_entry->txportmask, macmsk);
+		HAL_FIELD_SET(MIRROR_CTRL_EGR_MIRROR_EN_LOWBIT,
+			      MIRROR_CTRL_EGR_MIRROR_EN_WIDTH, &cfg,
+			      macmsk.portbits[0]);
+	}
+	SET_BIT(cfg, 27);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, MIRROR_CTRL, cfg), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_mirror_group_set);
+
+u32 yt_mirror_group_get(u8 unit, u8 group, struct yt_mirror_entry *mirror_entry)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_port_mask macmsk;
+	u32 ingress;
+	u32 egress;
+	u32 macid;
+	u32 cfg;
+
+	CMM_UNUSED_PARAM(group);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MIRROR_CTRL, &cfg), ret);
+	if (mirror_entry->flags & YT_MIRROR_FLAG_MIRROR_PORT) {
+		HAL_FIELD_GET(MIRROR_CTRL_MIRROR_PORT_LOWBIT,
+			      MIRROR_CTRL_MIRROR_PORT_WIDTH, &cfg, &macid);
+		if (macid == YT_MIRROR_INVALID_PORT)
+			mirror_entry->mirrorport = YT_MIRROR_INVALID_PORT;
+		else
+			CAL_MAC_TO_YTP(unit, macid, mirror_entry->mirrorport);
+
+		mirror_entry->islag = FALSE;
+	}
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_PORT_INGRESS) {
+		CMM_CLEAR_MEMBER_PORT(macmsk);
+		HAL_FIELD_GET(MIRROR_CTRL_INGR_MIRROR_EN_LOWBIT,
+			      MIRROR_CTRL_INGR_MIRROR_EN_WIDTH, &cfg, &ingress);
+		macmsk.portbits[0] = ingress;
+		CAL_MLIST_TO_YTPLIST(unit, macmsk, mirror_entry->rxportmask);
+	}
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_PORT_EGRESS) {
+		CMM_CLEAR_MEMBER_PORT(macmsk);
+		HAL_FIELD_GET(MIRROR_CTRL_EGR_MIRROR_EN_LOWBIT,
+			      MIRROR_CTRL_EGR_MIRROR_EN_WIDTH, &cfg, &egress);
+		macmsk.portbits[0] = egress;
+		CAL_MLIST_TO_YTPLIST(unit, macmsk, mirror_entry->txportmask);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(yt_mirror_group_get);
+#elif defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+u32 yt_mirror_group_set(u8 unit, u8 group, struct yt_mirror_entry *mirror_entry)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_port_mask macmsk;
+	u32 macid;
+	u32 cfg;
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_MIRROR_PORT) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+				(unit, MIRROR_PORT_CTRL + 4 * group,
+				 &cfg), ret);
+		if (mirror_entry->islag) {
+			HAL_FIELD_SET(MIRROR_CTRL_IS_LAG_LOWBIT,
+				      MIRROR_CTRL_IS_LAG_WIDTH, &cfg, 1);
+			HAL_FIELD_SET(MIRROR_CTRL_MIRROR_PORT_LOWBIT,
+				      MIRROR_CTRL_MIRROR_PORT_WIDTH, &cfg,
+				      mirror_entry->mirrorport);
+		} else {
+			HAL_FIELD_SET(MIRROR_CTRL_IS_LAG_LOWBIT,
+				      MIRROR_CTRL_IS_LAG_WIDTH, &cfg, 0);
+			if (mirror_entry->mirrorport ==
+			    YT_MIRROR_INVALID_PORT) {
+				HAL_FIELD_SET(MIRROR_CTRL_MIRROR_PORT_LOWBIT,
+					      MIRROR_CTRL_MIRROR_PORT_WIDTH,
+					      &cfg, YT_MIRROR_INVALID_PORT);
+			} else {
+				macid = CAL_YTP_TO_MAC
+					(unit, mirror_entry->mirrorport);
+				HAL_FIELD_SET(MIRROR_CTRL_MIRROR_PORT_LOWBIT,
+					      MIRROR_CTRL_MIRROR_PORT_WIDTH,
+					      &cfg, macid);
+			}
+		}
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+				(unit, MIRROR_PORT_CTRL + 4 * group,
+				 cfg), ret);
+	}
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_PORT_INGRESS) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MIRROR_CTRL0 + 4 * group,
+						&cfg), ret);
+		CAL_YTPLIST_TO_MLIST(unit, mirror_entry->rxportmask, macmsk);
+		HAL_FIELD_SET(MIRROR_CTRL_INGR_MIRROR_EN_LOWBIT,
+			      MIRROR_CTRL_INGR_MIRROR_EN_WIDTH, &cfg,
+			      macmsk.portbits[0]);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, MIRROR_CTRL0 + 4 * group,
+						 cfg), ret);
+	}
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_PORT_EGRESS) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MIRROR_CTRL1 + 4 * group,
+						&cfg), ret);
+		CAL_YTPLIST_TO_MLIST(unit, mirror_entry->txportmask, macmsk);
+		HAL_FIELD_SET(MIRROR_CTRL_EGR_MIRROR_EN_LOWBIT,
+			      MIRROR_CTRL_EGR_MIRROR_EN_WIDTH, &cfg,
+			      macmsk.portbits[0]);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, MIRROR_CTRL1 + 4 * group,
+						 cfg), ret);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(yt_mirror_group_set);
+
+u32 yt_mirror_group_get(u8 unit, u8 group, struct yt_mirror_entry *mirror_entry)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_port_mask macmsk;
+	u32 ingress;
+	u32 egress;
+	u32 macid;
+	u32 cfg;
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_MIRROR_PORT) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+				(unit, MIRROR_PORT_CTRL + 4 * group,
+				 &cfg), ret);
+		HAL_FIELD_GET(MIRROR_CTRL_MIRROR_PORT_LOWBIT,
+			      MIRROR_CTRL_MIRROR_PORT_WIDTH, &cfg, &macid);
+		if (macid == YT_MIRROR_INVALID_PORT) {
+			mirror_entry->mirrorport = YT_MIRROR_INVALID_PORT;
+		} else {
+			if (mirror_entry->islag)
+				mirror_entry->mirrorport = macid;
+			else
+				CAL_MAC_TO_YTP(unit, macid,
+					       mirror_entry->mirrorport);
+		}
+	}
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_PORT_INGRESS) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MIRROR_CTRL0 + 4 * group,
+						&cfg), ret);
+		CMM_CLEAR_MEMBER_PORT(macmsk);
+		HAL_FIELD_GET(MIRROR_CTRL_INGR_MIRROR_EN_LOWBIT,
+			      MIRROR_CTRL_INGR_MIRROR_EN_WIDTH, &cfg, &ingress);
+		macmsk.portbits[0] = ingress;
+		CAL_MLIST_TO_YTPLIST(unit, macmsk, mirror_entry->rxportmask);
+	}
+
+	if (mirror_entry->flags & YT_MIRROR_FLAG_PORT_EGRESS) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MIRROR_CTRL1 + 4 * group,
+						&cfg), ret);
+		CMM_CLEAR_MEMBER_PORT(macmsk);
+		HAL_FIELD_GET(MIRROR_CTRL_EGR_MIRROR_EN_LOWBIT,
+			      MIRROR_CTRL_EGR_MIRROR_EN_WIDTH, &cfg, &egress);
+		macmsk.portbits[0] = egress;
+		CAL_MLIST_TO_YTPLIST(unit, macmsk, mirror_entry->txportmask);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(yt_mirror_group_get);
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_mirror.h b/drivers/net/dsa/motorcomm/switch/yt_mirror.h
new file mode 100644
index 000000000000..006fa194a6eb
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_mirror.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_MIRROR_H__
+#define __YT_MIRROR_H__
+
+#include "yt_cmm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+struct yt_mirror_entry {
+	u32 flags;
+	u32 mirrorport;
+	bool islag;
+	struct yt_port_mask rxportmask;
+	struct yt_port_mask txportmask;
+};
+
+/* flags for mirror_group_set/get */
+#define YT_MIRROR_FLAG_PORT_INGRESS BIT(0)
+#define YT_MIRROR_FLAG_PORT_EGRESS BIT(1)
+#define YT_MIRROR_FLAG_MIRROR_PORT BIT(2)
+#define YT_MIRROR_FLAG_ALL                                          \
+	(YT_MIRROR_FLAG_PORT_INGRESS | YT_MIRROR_FLAG_PORT_EGRESS | \
+	 YT_MIRROR_FLAG_MIRROR_PORT)
+
+/**
+ * @internal      yt_mirror_group_set
+ * @endinternal
+ *
+ * @brief         Set mirror group entry, set mirrorPort =
+ *                YT_MIRROR_INVALID_PORT to disable mirror function.
+ *                Tiger not support mirror port is lag port.
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     group               -group id
+ * @param[in]     mirror_entry        -pointer to mirror entry
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_mirror_group_set(u8 unit, u8 group,
+			struct yt_mirror_entry *mirror_entry);
+
+/**
+ * @internal      yt_mirror_group_get
+ * @endinternal
+ *
+ * @brief         Get mirror group entry.
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     group               -group id
+ * @param[out]    mirror_entry        -pointer to mirror entry
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_mirror_group_get(u8 unit, u8 group,
+			struct yt_mirror_entry *mirror_entry);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_multicast.c b/drivers/net/dsa/motorcomm/switch/yt_multicast.c
new file mode 100644
index 000000000000..d4be0337046a
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_multicast.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include <linux/string.h>
+#include "yt_multicast.h"
+#include "yt_l2.h"
+
+static u32 yt_multicast_op_result_get(u8 unit,
+				      struct yt_l2_fdb_op_result *op_result);
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+static u32 yt_multicast_addr_add_op(u8 unit, struct yt_l2_fdb_op op_mode,
+				    struct yt_l2_fdb_info fdb_info,
+				    struct yt_l2_fdb_op_result *op_result)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 data2 = 0;
+	u32 op = 0;
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      ((fdb_info.mac_addr.addr[0] << 24) |
+		       (fdb_info.mac_addr.addr[1] << 16) |
+		       (fdb_info.mac_addr.addr[2] << 8) |
+		       fdb_info.mac_addr.addr[3]));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      ((fdb_info.mac_addr.addr[4] << 8) |
+		       fdb_info.mac_addr.addr[5]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1, fdb_info.fid);
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+		      fdb_info.status);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_WIDTH, &data2,
+		      fdb_info.dst_port_mask.portbits[0] & 0x7FF);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_2, data2),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_ADD);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, op_mode.op_mode);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_ENTRY_INDEX_LOWBIT,
+		      L2_FDB_TBL_OP_ENTRY_INDEX_WIDTH, &op, op_mode.entry_idx);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_multicast_op_result_get(unit, op_result), ret);
+
+	return ret;
+}
+
+static u32 yt_multicast_op_info_get(u8 unit, struct yt_l2_fdb_info *fdb_info)
+{
+	u32 data0;
+	u32 data1;
+	u32 data2;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 mac_addr0 = 0;
+	u16 mac_addr1 = 0;
+
+	CMM_PARAM_CHK((!fdb_info), CMM_ERR_NULL_POINT);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_0_DUMMY,
+					&data0), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_1_DUMMY,
+					&data1), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_2_DUMMY,
+					&data2), ret);
+
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      &mac_addr0);
+	fdb_info->mac_addr.addr[0] = ((mac_addr0 & 0xFF000000) >> 24);
+	fdb_info->mac_addr.addr[1] = ((mac_addr0 & 0xFF0000) >> 16);
+	fdb_info->mac_addr.addr[2] = ((mac_addr0 & 0xFF00) >> 8);
+	fdb_info->mac_addr.addr[3] = (mac_addr0 & 0xFF);
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      &mac_addr1);
+	fdb_info->mac_addr.addr[4] = ((mac_addr1 & 0xFF00) >> 8);
+	fdb_info->mac_addr.addr[5] = (mac_addr1 & 0xFF);
+
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1,
+		      &fdb_info->fid);
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+		      &fdb_info->status);
+	HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_DMAC_INT_PRI_EN_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_DMAC_INT_PRI_EN_WIDTH, &data1,
+		      &fdb_info->dmac_int_pri_en);
+
+	HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_WIDTH, &data2,
+		      &fdb_info->new_vid);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_INT_PRI_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_INT_PRI_WIDTH, &data2,
+		      &fdb_info->int_pri);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_SMAC_INT_PRI_EN_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_SMAC_INT_PRI_EN_WIDTH, &data2,
+		      &fdb_info->smac_int_pri_en);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_WIDTH, &data2,
+		      &fdb_info->copy_to_cpu);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_DMAC_DROP_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_DMAC_DROP_WIDTH, &data2,
+		      &fdb_info->dmac_drop);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_WIDTH, &data2,
+		      fdb_info->dst_port_mask.portbits);
+	HAL_FIELD_GET(L2_FDB_TBL_OP_DATA_2_SMAC_DROP_LOWBIT,
+		      L2_FDB_TBL_OP_DATA_2_SMAC_DROP_WIDTH, &data2,
+		      &fdb_info->smac_drop);
+
+	return ret;
+}
+
+static u32 yt_multicast_info_set(u8 unit, struct yt_l2_fdb_info fdb_info,
+				 struct yt_mcast_mac_data *mcast_info)
+{
+	u32 port = 0;
+	u8 mac_id = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (!IS_MCAST_ADDR(fdb_info.mac_addr.addr))
+		return CMM_ERR_ENTRY_NOT_FOUND;
+
+	mcast_info->vid = fdb_info.fid;
+	memcpy(mcast_info->mac_addr.addr, fdb_info.mac_addr.addr, MAC_ADDR_LEN);
+
+	for (port = 0; port < YT_MAX_PORT_PER_UNIT; port++) {
+		mac_id = CAL_YTP_TO_MAC(unit, port);
+		if (IS_BIT_SET(fdb_info.dst_port_mask.portbits[0], mac_id))
+			SET_BIT(mcast_info->port_mask.portbits[0], port);
+	}
+
+	if (fdb_info.status == FDB_STATUS_STATIC)
+		mcast_info->type = YT_L2_FDB_TYPE_STATIC;
+	else
+		mcast_info->type = YT_L2_FDB_TYPE_DYNAMIC;
+
+	return ret;
+}
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+static u32 yt_multicast_addr_add_op(u8 unit, struct yt_l2_fdb_op op_mode,
+				    struct yt_l2_fdb_info fdb_info,
+				    struct yt_l2_fdb_op_result *op_result)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 data2 = 0;
+	u32 data3 = 0;
+	u32 op = 0;
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      ((fdb_info.mac_addr.addr[0] << 24) |
+		       (fdb_info.mac_addr.addr[1] << 16) |
+		       (fdb_info.mac_addr.addr[2] << 8) |
+		       fdb_info.mac_addr.addr[3]));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      ((fdb_info.mac_addr.addr[4] << 8) |
+		       fdb_info.mac_addr.addr[5]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1, fdb_info.fid);
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+		      fdb_info.status);
+
+	HAL_FIELD_SET(L2_FDB_TBL_MC_MAC_OP_DATA_2_IP_MC_LOWBIT,
+		      L2_FDB_TBL_MC_MAC_OP_DATA_2_IP_MC_WIDTH, &data2, FALSE);
+	HAL_FIELD_SET(L2_FDB_TBL_MC_MAC_OP_DATA_2_VALID_LOWBIT,
+		      L2_FDB_TBL_MC_MAC_OP_DATA_2_VALID_WIDTH, &data2, TRUE);
+	HAL_FIELD_SET(L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_PMSK_0_LOWBIT,
+		      L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_PMSK_0_WIDTH, &data2,
+		      fdb_info.dst_port_mask.portbits[0] & 0xFF);
+
+	HAL_FIELD_SET(L2_FDB_TBL_MC_MAC_OP_DATA_3_MC_PMSK_1_LOWBIT,
+		      L2_FDB_TBL_MC_MAC_OP_DATA_3_MC_PMSK_1_WIDTH, &data3,
+		      fdb_info.dst_port_mask.portbits[0] >> 8);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_2, data2),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_3, data3),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_ADD);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, op_mode.op_mode);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_ENTRY_INDEX_LOWBIT,
+		      L2_FDB_TBL_OP_ENTRY_INDEX_WIDTH, &op, op_mode.entry_idx);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_multicast_op_result_get(unit, op_result), ret);
+
+	return ret;
+}
+
+static u32 yt_multicast_op_info_get(u8 unit, struct yt_l2_fdb_info *fdb_info)
+{
+	u32 data0;
+	u32 data1;
+	u32 data2;
+	u32 data3;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 mac_addr0 = 0;
+	u16 mac_addr1 = 0;
+	u32 ip_mc = 0;
+	u32 lag_en = 0;
+	u32 dst_port = 0;
+	u32 mc_pmsk0 = 0;
+	u32 mc_pmsk1 = 0;
+	u8 is_mc = 0;
+	struct yt_port_mask macmask;
+
+	CMM_CLEAR_MEMBER_PORT(macmask);
+
+	CMM_PARAM_CHK((!fdb_info), CMM_ERR_NULL_POINT);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_0_DUMMY,
+					&data0), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_1_DUMMY,
+					&data1), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_2_DUMMY,
+					&data2), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_DATA_3_DUMMY,
+					&data3), ret);
+
+	HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_IP_MC_LOWBIT,
+		      L2_FDB_TBL_UC_MAC_OP_DATA_2_IP_MC_WIDTH, &data2, &ip_mc);
+
+	if (ip_mc == 0) {
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+			      &mac_addr0);
+		fdb_info->mac_addr.addr[0] = ((mac_addr0 & 0xFF000000) >> 24);
+		fdb_info->mac_addr.addr[1] = ((mac_addr0 & 0xFF0000) >> 16);
+		fdb_info->mac_addr.addr[2] = ((mac_addr0 & 0xFF00) >> 8);
+		fdb_info->mac_addr.addr[3] = (mac_addr0 & 0xFF);
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+			      &mac_addr1);
+		fdb_info->mac_addr.addr[4] = ((mac_addr1 & 0xFF00) >> 8);
+		fdb_info->mac_addr.addr[5] = (mac_addr1 & 0xFF);
+		if (!IS_BIT_SET(mac_addr0, L2_MCAST_BIT)) {
+			HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_LOWBIT,
+				      L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_WIDTH,
+				      &data2, &lag_en);
+			HAL_FIELD_GET
+				(L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_LOWBIT,
+				L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_WIDTH,
+				&data2, &dst_port);
+
+			if (lag_en) {
+				macmask.portbits[0] = 1 << dst_port;
+				fdb_info->lag_en = TRUE;
+			} else {
+				macmask.portbits[0] = 1 << dst_port;
+			}
+
+			HAL_FIELD_GET
+				(L2_FDB_TBL_UC_MAC_OP_DATA_2_PENDING_LOWBIT,
+				L2_FDB_TBL_UC_MAC_OP_DATA_2_PENDING_WIDTH,
+				&data2, &fdb_info->pending);
+		} else {
+			is_mc = 1;
+		}
+		HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_WIDTH, &data2,
+			      &fdb_info->new_vid);
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1,
+			      &fdb_info->fid);
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH, &data1,
+			      &fdb_info->status);
+		HAL_FIELD_GET(L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_WIDTH, &data1,
+			      &fdb_info->sa_drop);
+		HAL_FIELD_GET(L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_LOWBIT,
+			      L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_WIDTH,
+			      &data2, &fdb_info->da_drop);
+	}
+
+	if (is_mc || ip_mc) {
+		HAL_FIELD_GET(L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_PMSK_0_LOWBIT,
+			      L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_PMSK_0_WIDTH,
+			      &data2, &mc_pmsk0);
+		HAL_FIELD_GET(L2_FDB_TBL_MC_MAC_OP_DATA_3_MC_PMSK_1_LOWBIT,
+			      L2_FDB_TBL_MC_MAC_OP_DATA_3_MC_PMSK_1_WIDTH,
+			      &data3, &mc_pmsk1);
+		macmask.portbits[0] = (mc_pmsk1 << 8) | mc_pmsk0;
+		HAL_FIELD_GET(L2_FDB_TBL_MC_MAC_OP_DATA_3_IS_MC_HW_LOWBIT,
+			      L2_FDB_TBL_MC_MAC_OP_DATA_3_IS_MC_HW_WIDTH,
+			      &data3, &fdb_info->hw_mc);
+	}
+
+	CAL_MLIST_TO_YTPLIST(unit, macmask, fdb_info->dst_port_mask);
+
+	fdb_info->ip_mc = ip_mc;
+
+	return ret;
+}
+
+static u32 yt_multicast_info_set(u8 unit, struct yt_l2_fdb_info fdb_info,
+				 struct yt_mcast_mac_data *mcast_info)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (!IS_MCAST_ADDR(fdb_info.mac_addr.addr))
+		return CMM_ERR_ENTRY_NOT_FOUND;
+
+	if (fdb_info.hw_mc)
+		mcast_info->type = YT_MULTI_TYPE_DYNAMIC;
+	else
+		mcast_info->type = YT_MULTI_TYPE_STATIC;
+
+	mcast_info->vid = fdb_info.fid;
+	memcpy(mcast_info->mac_addr.addr, fdb_info.mac_addr.addr, MAC_ADDR_LEN);
+	memcpy(mcast_info->port_mask.portbits, fdb_info.dst_port_mask.portbits,
+	       sizeof(fdb_info.dst_port_mask.portbits[0]));
+
+	return ret;
+}
+#endif
+static u32 yt_multicast_op_result_get(u8 unit,
+				      struct yt_l2_fdb_op_result *op_result)
+{
+	u16 busy_cnt = FDB_BUSY_CHECK_NUMBER;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 result_tmp = 0;
+	u32 result = 0;
+	u32 op_done = 0;
+	u32 index = 0;
+	u32 overwrite = 0;
+	u32 lookup_fail = 0;
+
+	CMM_PARAM_CHK((!op_result), CMM_ERR_NULL_POINT);
+
+	while (busy_cnt) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_FDB_TBL_OP_RESULT,
+						&result), ret);
+
+		HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_OP_DONE_LOWBIT,
+			      L2_FDB_TBL_OP_RESULT_OP_DONE_WIDTH, &result,
+			      &op_done);
+		if (op_done) {
+			HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_ENTRY_INDEX_LOWBIT,
+				      L2_FDB_TBL_OP_RESULT_ENTRY_INDEX_WIDTH,
+				      &result, &index);
+			HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_OP_RESULT_LOWBIT,
+				      L2_FDB_TBL_OP_RESULT_OP_RESULT_WIDTH,
+				      &result, &result_tmp);
+			HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_OVERWRITE_LOWBIT,
+				      L2_FDB_TBL_OP_RESULT_OVERWRITE_WIDTH,
+				      &result, &overwrite);
+			HAL_FIELD_GET(L2_FDB_TBL_OP_RESULT_LOOKUP_FAIL_LOWBIT,
+				      L2_FDB_TBL_OP_RESULT_LOOKUP_FAIL_WIDTH,
+				      &result, &lookup_fail);
+
+			op_result->entry_idx = index;
+			op_result->op_result = result_tmp;
+			op_result->overwrite = overwrite;
+			op_result->lookup_fail = lookup_fail;
+			op_result->op_done = TRUE;
+			break;
+		}
+
+		busy_cnt--;
+		if (busy_cnt == 0) {
+			op_result->lookup_fail = TRUE;
+			op_result->op_done = FALSE;
+
+			return CMM_ERR_FDB_OP_BUSY;
+		}
+	}
+
+	return ret;
+}
+
+u32 yt_multicast_addr_add(u8 unit, u16 vid, struct yt_mac_addr mac_addr,
+			  struct yt_port_mask port_mask)
+{
+	struct yt_l2_fdb_info fdb_info;
+	struct yt_l2_fdb_op op_mode;
+	struct yt_l2_fdb_op_result op_result;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	memset(&fdb_info, 0, sizeof(struct yt_l2_fdb_info));
+	memset(&op_mode, 0, sizeof(struct yt_l2_fdb_op));
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+
+	fdb_info.fid = vid;
+	fdb_info.status = FDB_STATUS_STATIC;
+	CAL_YTPLIST_TO_MLIST(unit, port_mask, fdb_info.dst_port_mask);
+	memcpy(fdb_info.mac_addr.addr, mac_addr.addr, MAC_ADDR_LEN);
+
+	op_mode.op_mode = L2_FDB_OP_MODE_HASH;
+	op_mode.entry_idx = 0;
+
+	CMM_ERR_CHK(yt_multicast_addr_add_op(unit, op_mode, fdb_info,
+					     &op_result), ret);
+
+	if (!op_result.lookup_fail && op_result.overwrite)
+		return CMM_ERR_SAMEENTRY_EXIST;
+	else if (op_result.lookup_fail)
+		return CMM_ERR_ENTRY_FULL;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_multicast_addr_add);
+
+u32 yt_multicast_addr_del(u8 unit, u16 vid, struct yt_mac_addr mac_addr)
+{
+	struct yt_l2_fdb_op_result op_result;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 data2 = 0;
+	u32 op = 0;
+
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      ((mac_addr.addr[0] << 24) | (mac_addr.addr[1] << 16) |
+		       (mac_addr.addr[2] << 8) | mac_addr.addr[3]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      ((mac_addr.addr[4] << 8) | mac_addr.addr[5]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1, vid);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_2, data2),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_DEL);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, L2_FDB_OP_MODE_HASH);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_multicast_op_result_get(unit, &op_result), ret);
+
+	if (op_result.lookup_fail)
+		return CMM_ERR_ENTRY_NOT_FOUND;
+	else if (op_result.op_result)
+		return CMM_ERR_FAIL;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_multicast_addr_del);
+
+u32 yt_multicast_get(u8 unit, u16 vid, struct yt_mac_addr mac_addr,
+		     struct yt_mcast_mac_data *mcast_info)
+{
+	struct yt_l2_fdb_op_result op_result;
+	struct yt_l2_fdb_info fdb_info;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data0 = 0;
+	u32 data1 = 0;
+	u32 op = 0;
+
+	CMM_PARAM_CHK((!mcast_info), CMM_ERR_NULL_POINT);
+
+	memset(&op_result, 0, sizeof(struct yt_l2_fdb_op_result));
+	memset(&fdb_info, 0, sizeof(struct yt_l2_fdb_info));
+
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH, &data0,
+		      ((mac_addr.addr[0] << 24) | (mac_addr.addr[1] << 16) |
+		       (mac_addr.addr[2] << 8) | mac_addr.addr[3]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH, &data1,
+		      ((mac_addr.addr[4] << 8) | mac_addr.addr[5]));
+	HAL_FIELD_SET(L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT,
+		      L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH, &data1, vid);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_0, data0),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP_DATA_1, data1),
+		    ret);
+
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_CMD_LOWBIT, L2_FDB_TBL_OP_OP_CMD_WIDTH,
+		      &op, L2_FDB_OP_CMD_GET_ONE);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_MODE_LOWBIT, L2_FDB_TBL_OP_OP_MODE_WIDTH,
+		      &op, L2_FDB_OP_MODE_HASH);
+	HAL_FIELD_SET(L2_FDB_TBL_OP_OP_START_LOWBIT,
+		      L2_FDB_TBL_OP_OP_START_WIDTH, &op, TRUE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_FDB_TBL_OP, op), ret);
+
+	CMM_ERR_CHK(yt_multicast_op_result_get(unit, &op_result), ret);
+	if (op_result.lookup_fail)
+		return CMM_ERR_ENTRY_NOT_FOUND;
+
+	CMM_ERR_CHK(yt_multicast_op_info_get(unit, &fdb_info), ret);
+
+	CMM_ERR_CHK(yt_multicast_info_set(unit, fdb_info, mcast_info), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_multicast_get);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_multicast.h b/drivers/net/dsa/motorcomm/switch/yt_multicast.h
new file mode 100644
index 000000000000..4828dcab1b1c
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_multicast.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_MULTICAST_H
+#define __YT_MULTICAST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+enum yt_mcast_fdb_type {
+	YT_MULTI_TYPE_STATIC,
+	YT_MULTI_TYPE_DYNAMIC,
+};
+
+struct yt_mcast_mac_data {
+	enum yt_mcast_fdb_type type; /* static, dynamic */
+	struct yt_port_mask port_mask;
+	struct yt_mac_addr mac_addr;
+	u16 vid;
+};
+
+/**
+ * @internal      yt_multicast_addr_add
+ * @endinternal
+ *
+ * @brief         Add multicast entry
+ * @note          APPLICABLE DEVICES      -Tiger, Whale
+ * @param[in]     unit                    -unit id
+ * @param[in]     vid                     -vlan id
+ * @param[in]     mac_addr                -mac addr
+ * @param[in]     port_mask               -port mask
+ * @retval        CMM_ERR_OK              -on success
+ * @retval        CMM_ERR_FAIL            -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY     -fdb op busy
+ * @retval        CMM_ERR_ENTRY_FULL      -fdb table full
+ * @retval        CMM_ERR_SAMEENTRY_EXIST -entry exist
+ */
+u32 yt_multicast_addr_add(u8 unit, u16 vid, struct yt_mac_addr mac_addr,
+			  struct yt_port_mask port_mask);
+
+/**
+ * @internal      yt_multicast_addr_del
+ * @endinternal
+ *
+ * @brief         Delete multicast entry
+ * @note          APPLICABLE DEVICES      -Tiger, Whale
+ * @param[in]     unit                    -unit id
+ * @param[in]     vid                     -vlan id
+ * @param[in]     mac_addr                -mac addr
+ * @retval        CMM_ERR_OK              -on success
+ * @retval        CMM_ERR_FAIL            -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY     -fdb op busy
+ * @retval        CMM_ERR_ENTRY_NOT_FOUND -fdb entry not found
+ */
+u32 yt_multicast_addr_del(u8 unit, u16 vid, struct yt_mac_addr mac_addr);
+
+/**
+ * @internal      yt_multicast_get
+ * @endinternal
+ *
+ * @brief         Get multicast entry with mac and vid
+ * @note          APPLICABLE DEVICES      -Tiger, Whale
+ * @param[in]     unit                    -unit id
+ * @param[in]     vid                     -vlan id
+ * @param[in]     mac_addr                -mac addr
+ * @param[out]    mcast_info              -mcast info pointer
+ * @retval        CMM_ERR_OK              -on success
+ * @retval        CMM_ERR_FAIL            -on fail
+ * @retval        CMM_ERR_FDB_OP_BUSY     -fdb op busy
+ * @retval        CMM_ERR_ENTRY_NOT_FOUND -fdb entry not found
+ */
+u32 yt_multicast_get(u8 unit, u16 vid, struct yt_mac_addr mac_addr,
+		     struct yt_mcast_mac_data *mcast_info);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_nic.c b/drivers/net/dsa/motorcomm/switch/yt_nic.c
new file mode 100644
index 000000000000..181f743088bb
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_nic.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_nic.h"
+
+u32 yt_nic_init(u8 unit)
+{
+	u32 l2_learn_per_port_ctrl = 0;
+	u32 tmp_entry;
+	u32 ret;
+
+	/*disable internal cpu port learn fdb*/
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+					L2_LEARN_PER_PORT_CTRLN +
+						YT_INTERNAL_CPU_MACID * 4,
+					&l2_learn_per_port_ctrl), ret);
+	HAL_FIELD_SET(L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT,
+		      L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH,
+		      &l2_learn_per_port_ctrl, YT_ENABLE);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit,
+					 L2_LEARN_PER_PORT_CTRLN +
+						 YT_INTERNAL_CPU_MACID * 4,
+					 l2_learn_per_port_ctrl), ret);
+
+	/* set external cpu mode */
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CPU_COPY_DST_CTRL, &tmp_entry),
+		    ret);
+	HAL_FIELD_SET(CPU_COPY_DST_CTRL_TO_INT_CPU_LOWBIT,
+		      CPU_COPY_DST_CTRL_TO_INT_CPU_WIDTH, &tmp_entry,
+		      YT_DISABLE);
+	HAL_FIELD_SET(CPU_COPY_DST_CTRL_TO_EXT_CPU_LOWBIT,
+		      CPU_COPY_DST_CTRL_TO_EXT_CPU_WIDTH, &tmp_entry,
+		      YT_ENABLE);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CPU_COPY_DST_CTRL, tmp_entry),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_init);
+
+u32 yt_nic_ext_cpuport_en_set(u8 unit, enum yt_enable enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry = 0;
+	u32 macid = 0;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, EXT_CPU_PORT_CTRL, &entry), ret);
+	HAL_FIELD_SET(EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_LOWBIT,
+		      EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_WIDTH, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, EXT_CPU_PORT_CTRL, entry), ret);
+
+	HAL_FIELD_GET(EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_LOWBIT,
+		      EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_WIDTH, &entry, &macid);
+
+	/* enable the backpress on cpu port */
+	entry = 0;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PORT_CTRL + macid * 4, &entry),
+		    ret);
+	if (chip_id == YT_SW_ID_920X)
+		HAL_FIELD_SET(PORT_CTRL_HALF_FC_EN_LOWBIT,
+			      PORT_CTRL_HALF_FC_EN_WIDTH, &entry,
+			      (enable ? YT_ENABLE : YT_DISABLE));
+	else
+		HAL_FIELD_SET(PORT_CTRL_HALF_FC_EN_LOWBIT,
+			      PORT_CTRL_HALF_FC_EN_WIDTH, &entry,
+			      (enable ? YT_DISABLE : YT_ENABLE));
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PORT_CTRL + macid * 4, entry),
+		    ret);
+
+	/* l2 learn on port */
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+					(L2_LEARN_PER_PORT_CTRLN + macid * 4),
+					&entry), ret);
+	HAL_FIELD_SET(L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT,
+		      L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH, &entry,
+		      (enable ? YT_DISABLE : YT_ENABLE));
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+			(unit, (L2_LEARN_PER_PORT_CTRLN + macid * 4),
+			 entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_ext_cpuport_en_set);
+
+u32 yt_nic_ext_cpuport_en_get(u8 unit, enum yt_enable *enable_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 enable = 0;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, EXT_CPU_PORT_CTRL, &entry), ret);
+	HAL_FIELD_GET(EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_LOWBIT,
+		      EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_WIDTH, &entry, &enable);
+	*enable_ptr = enable ? YT_ENABLE : YT_DISABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_ext_cpuport_en_get);
+
+u32 yt_nic_ext_cpuport_port_set(u8 unit, u32 port)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = 0;
+	u32 enable = 0;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	macid = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, EXT_CPU_PORT_CTRL, &entry), ret);
+	HAL_FIELD_SET(EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_LOWBIT,
+		      EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_WIDTH, &entry, macid);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, EXT_CPU_PORT_CTRL, entry), ret);
+
+	HAL_FIELD_GET(EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_LOWBIT,
+		      EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_WIDTH, &entry, &enable);
+	if (enable == YT_ENABLE) {
+		/* disable the backpress on cpu port */
+		entry = 0;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PORT_CTRL + macid * 4,
+						&entry), ret);
+		if (chip_id == YT_SW_ID_920X)
+			HAL_FIELD_SET(PORT_CTRL_HALF_FC_EN_LOWBIT,
+				      PORT_CTRL_HALF_FC_EN_WIDTH, &entry,
+				      YT_ENABLE);
+		else
+			HAL_FIELD_SET(PORT_CTRL_HALF_FC_EN_LOWBIT,
+				      PORT_CTRL_HALF_FC_EN_WIDTH, &entry,
+				      YT_DISABLE);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PORT_CTRL + macid * 4,
+						 entry), ret);
+
+		/* l2 learn on port */
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+				(unit, (L2_LEARN_PER_PORT_CTRLN + macid * 4),
+				 &entry), ret);
+		HAL_FIELD_SET(L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT,
+			      L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH,
+			      &entry, YT_DISABLE);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+				(unit, (L2_LEARN_PER_PORT_CTRLN + macid * 4),
+				 entry), ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_ext_cpuport_port_set);
+
+u32 yt_nic_ext_cpuport_port_get(u8 unit, u32 *port_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry = 0;
+	u32 macid = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, EXT_CPU_PORT_CTRL, &entry), ret);
+	HAL_FIELD_GET(EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_LOWBIT,
+		      EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_WIDTH, &entry, &macid);
+	CAL_MAC_TO_YTP(unit, macid, (*port_ptr));
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_ext_cpuport_port_get);
+
+u32 yt_nic_cpuport_tagtpid_set(u8 unit, u16 tpid)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 cpu_tag_tpid = 0;
+
+	HAL_FIELD_SET(CPU_TAG_TPID_TPID_LOWBIT, CPU_TAG_TPID_TPID_WIDTH,
+		      &cpu_tag_tpid, tpid);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CPU_TAG_TPID, cpu_tag_tpid),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_cpuport_tagtpid_set);
+
+u32 yt_nic_cpuport_tagtpid_get(u8 unit, u16 *tpid_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 cpu_tag_tpid = 0;
+	u32 tpid = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CPU_TAG_TPID, &cpu_tag_tpid),
+		    ret);
+	HAL_FIELD_GET(CPU_TAG_TPID_TPID_LOWBIT, CPU_TAG_TPID_TPID_WIDTH,
+		      &cpu_tag_tpid, &tpid);
+	*tpid_ptr = tpid;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_cpuport_tagtpid_get);
+
+u32 yt_nic_ext_cputag_en_set(u8 unit, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, EXT_CPU_PORT_CTRL, &entry), ret);
+	HAL_FIELD_SET(EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_LOWBIT,
+		      EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_WIDTH, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, EXT_CPU_PORT_CTRL, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_ext_cputag_en_set);
+
+u32 yt_nic_ext_cputag_en_get(u8 unit, enum yt_enable *enable_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 enable = 0;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, EXT_CPU_PORT_CTRL, &entry), ret);
+	HAL_FIELD_GET(EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_LOWBIT,
+		      EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_WIDTH, &entry, &enable);
+	*enable_ptr = enable ? YT_ENABLE : YT_DISABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_ext_cputag_en_get);
+
+u32 yt_nic_cpu_pkt_keepall_en_set(u8 unit, enum yt_enable ext_en,
+				  enum yt_enable int_en)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 cpu_bypass = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CPU_PKT_BYPASSEDIT_CTRL,
+					&cpu_bypass), ret);
+	HAL_FIELD_SET(CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_LOWBIT,
+		      CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_WIDTH,
+		      &cpu_bypass, ext_en);
+	HAL_FIELD_SET(CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_LOWBIT,
+		      CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_WIDTH,
+		      &cpu_bypass, int_en);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CPU_PKT_BYPASSEDIT_CTRL,
+					 cpu_bypass), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_cpu_pkt_keepall_en_set);
+
+u32 yt_nic_cpu_pkt_keepall_en_get(u8 unit, enum yt_enable *ext_en_ptr,
+				  enum yt_enable *int_en_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 cpu_bypass = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CPU_PKT_BYPASSEDIT_CTRL,
+					&cpu_bypass), ret);
+	HAL_FIELD_GET(CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_LOWBIT,
+		      CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_WIDTH,
+		      &cpu_bypass, ext_en_ptr);
+	HAL_FIELD_GET(CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_LOWBIT,
+		      CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_WIDTH,
+		      &cpu_bypass, int_en_ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_nic_cpu_pkt_keepall_en_get);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_nic.h b/drivers/net/dsa/motorcomm/switch/yt_nic.h
new file mode 100644
index 000000000000..c61061232b1e
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_nic.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_NIC_H
+#define __YT_NIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+#include "yt_init.h"
+
+/**
+ * @internal      yt_nic_init
+ * @endinternal
+ *
+ * @brief         init nic module
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_init(u8 unit);
+
+/**
+ * @internal      yt_nic_ext_cpuport_en_set
+ * @endinternal
+ *
+ * @brief         enable or disable extend cpu port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_ext_cpuport_en_set(u8 unit, enum yt_enable enable);
+
+/**
+ * @internal      yt_nic_ext_cpuport_en_get
+ * @endinternal
+ *
+ * @brief         get the state of extend cpu port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]    enable_ptr             -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_ext_cpuport_en_get(u8 unit, enum yt_enable *enable_ptr);
+
+/**
+ * @internal      yt_nic_ext_cpuport_port_set
+ * @endinternal
+ *
+ * @brief         set the extend cpu port num
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_ext_cpuport_port_set(u8 unit, u32 port);
+
+/**
+ * @internal      yt_nic_ext_cpuport_port_get
+ * @endinternal
+ *
+ * @brief         get the extend cpu port num
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]    port_ptr               -port num
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_ext_cpuport_port_get(u8 unit, u32 *port_ptr);
+
+/**
+ * @internal      yt_nic_cpuport_tagtpid_set
+ * @endinternal
+ *
+ * @brief         set vlan tag tpid for cpu port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     tpid                -tag tpid value
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_cpuport_tagtpid_set(u8 unit, u16 tpid);
+
+/**
+ * @internal      yt_nic_cpuport_tagtpid_get
+ * @endinternal
+ *
+ * @brief         get vlan tag tpid for cpu port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]    tpid_ptr               -tag tpid value
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_cpuport_tagtpid_get(u8 unit, u16 *tpid_ptr);
+
+/**
+ * @internal      yt_nic_ext_cputag_en_set
+ * @endinternal
+ *
+ * @brief         enable or disable extend cpu tag
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_ext_cputag_en_set(u8 unit, enum yt_enable enable);
+
+/**
+ * @internal      yt_nic_ext_cputag_en_get
+ * @endinternal
+ *
+ * @brief         get the state of extend cpu tag
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]    enable_ptr             -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_ext_cputag_en_get(u8 unit, enum yt_enable *enable_ptr);
+
+/**
+ * @internal      yt_nic_cpu_pkt_keepall_en_set
+ * @endinternal
+ *
+ * @brief         enable/disable external/internal cpu port tx packets keep all
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     ext_en               -enable/disable
+ * @param[in]     int_en               -enable/disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_cpu_pkt_keepall_en_set(u8 unit, enum yt_enable ext_en,
+				  enum yt_enable int_en);
+
+/**
+ * @internal      yt_nic_cpu_pkt_keepall_en_get
+ * @endinternal
+ *
+ * @brief         get state of external/internal cpu port tx packets keep all
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]     ext_en_ptr             -enable/disable
+ * @param[out]     int_en_ptr             -enable/disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_nic_cpu_pkt_keepall_en_get(u8 unit, enum yt_enable *ext_en_ptr,
+				  enum yt_enable *int_en_ptr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_port.c b/drivers/net/dsa/motorcomm/switch/yt_port.c
new file mode 100644
index 000000000000..a63ba0f9ce8d
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_port.c
@@ -0,0 +1,6375 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include <linux/delay.h>
+#include "yt_port.h"
+#include "yt_init.h"
+
+static u32 yt_switch_intif_read(u8 unit, u8 intif, u32 reg, u16 *data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 wait = 0;
+	u32 op = 0;
+	u32 base;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INT_IF_ADDR_CTRL, &base), ret);
+	base &= 0xFC00FFF1;
+	base |= ((intif & 0x1f) << 21 | (reg & 0x1f) << 16 | 2 << 2);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, INT_IF_ADDR_CTRL, base), ret);
+	op = 1;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, INT_IF_FRAME_CTRL, op), ret);
+
+	while (wait <= MAX_BUSYING_WAIT_TIME) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INT_IF_FRAME_CTRL, &op),
+			    ret);
+		if (!op) {
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+						(unit, INT_IF_DATA_1_ADDR,
+						&base), ret);
+			*data = base;
+			return CMM_ERR_OK;
+		}
+		wait++;
+	}
+
+	return CMM_ERR_BUSYING_TIME;
+}
+
+static u32 yt_switch_intif_write(u8 unit, u8 intif, u32 reg, u16 data)
+{
+	u32 frame = INT_IF_FRAME_CTRL;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 wait = 0;
+	u32 op = 0;
+	u32 base;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, INT_IF_ADDR_CTRL, &base), ret);
+	base &= 0xFC00FFF1;
+	base |= ((intif & 0x1f) << 21 | (reg & 0x1f) << 16 | 1 << 2);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, INT_IF_ADDR_CTRL, base), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, INT_IF_DATA_0_ADDR, data), ret);
+	op = 1;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, frame, op), ret);
+
+	while (wait <= MAX_BUSYING_WAIT_TIME) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, frame, &op), ret);
+		if (!op)
+			return CMM_ERR_OK;
+		wait++;
+	}
+
+	return CMM_ERR_BUSYING_TIME;
+}
+
+static u32 yt_switch_extif_read(u8 unit, u8 extif, u32 reg, u16 *data)
+{
+	u32 addr = EXT_IF_ADDR_CTRL;
+	u32 frame = EXT_IF_FRAME_CTRL;
+	u32 data1 = EXT_IF_DATA_1_ADDR;
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 wait = 0;
+	u32 op = 0;
+	u32 base;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		addr += EXT_IF_INDEX_OFFSET;
+		frame += EXT_IF_INDEX_OFFSET;
+		data1 += EXT_IF_INDEX_OFFSET;
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr, &base), ret);
+	base &= 0xFC00FFF1; /*clause 22*/
+	base |= ((extif & 0x1f) << 21 | (reg & 0x1f) << 16 | 2 << 2);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr, base), ret);
+	op = 1;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, frame, op), ret);
+
+	while (wait <= MAX_BUSYING_WAIT_TIME) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, frame, &op), ret);
+		if (!op) {
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, data1, &base),
+				    ret);
+			*data = base;
+			return CMM_ERR_OK;
+		}
+		wait++;
+	}
+
+	return CMM_ERR_BUSYING_TIME;
+}
+
+static u32 yt_switch_extif_write(u8 unit, u8 extif, u32 reg, u16 data)
+{
+	u32 data_ctrl = EXT_IF_DATA_0_ADDR;
+	u32 frame_ctrl = EXT_IF_FRAME_CTRL;
+	u32 addr_ctrl = EXT_IF_ADDR_CTRL;
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 wait = 0;
+	u32 op = 0;
+	u32 base;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		addr_ctrl += EXT_IF_INDEX_OFFSET;
+		frame_ctrl += EXT_IF_INDEX_OFFSET;
+		data_ctrl += EXT_IF_INDEX_OFFSET;
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, addr_ctrl, &base), ret);
+	base &= 0xFC00FFF1; /*clause 22*/
+	base |= ((extif & 0x1f) << 21 | (reg & 0x1f) << 16 | 1 << 2);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, addr_ctrl, base), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, data_ctrl, data), ret);
+
+	op = 1;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, frame_ctrl, op), ret);
+
+	while (wait <= MAX_BUSYING_WAIT_TIME) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, frame_ctrl, &op), ret);
+		if (!op)
+			return CMM_ERR_OK;
+		wait++;
+	}
+
+	return CMM_ERR_BUSYING_TIME;
+}
+
+static u32 yt_port_speed_dup_split(enum yt_port_speed_duplex speed_dup,
+				   enum yt_port_speed *speed,
+				   enum yt_port_duplex *duplex)
+{
+	switch (speed_dup) {
+	case PORT_SPEED_DUP_10HALF:
+		*speed = PORT_SPEED_10M;
+		*duplex = PORT_DUPLEX_HALF;
+		break;
+	case PORT_SPEED_DUP_10FULL:
+		*speed = PORT_SPEED_10M;
+		*duplex = PORT_DUPLEX_FULL;
+		break;
+	case PORT_SPEED_DUP_100HALF:
+		*speed = PORT_SPEED_100M;
+		*duplex = PORT_DUPLEX_HALF;
+		break;
+	case PORT_SPEED_DUP_100FULL:
+		*speed = PORT_SPEED_100M;
+		*duplex = PORT_DUPLEX_FULL;
+		break;
+	case PORT_SPEED_DUP_1000FULL:
+		*speed = PORT_SPEED_1000M;
+		*duplex = PORT_DUPLEX_FULL;
+		break;
+	case PORT_SPEED_DUP_2500FULL:
+		*speed = PORT_SPEED_2500M;
+		*duplex = PORT_DUPLEX_FULL;
+		break;
+	default:
+		return CMM_ERR_INPUT;
+	}
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_port_speed_dup_combine(enum yt_port_speed speed,
+				     enum yt_port_duplex duplex,
+				     enum yt_port_speed_duplex *speed_dup)
+{
+	if (duplex == PORT_DUPLEX_FULL) {
+		if (speed == PORT_SPEED_10M)
+			*speed_dup = PORT_SPEED_DUP_10FULL;
+		else if (speed == PORT_SPEED_100M)
+			*speed_dup = PORT_SPEED_DUP_100FULL;
+		else if (speed == PORT_SPEED_1000M)
+			*speed_dup = PORT_SPEED_DUP_1000FULL;
+		else if (speed == PORT_SPEED_2500M)
+			*speed_dup = PORT_SPEED_DUP_2500FULL;
+		else
+			return CMM_ERR_INPUT;
+	} else if (duplex == PORT_DUPLEX_HALF) {
+		if (speed == PORT_SPEED_10M)
+			*speed_dup = PORT_SPEED_DUP_10HALF;
+		else if (speed == PORT_SPEED_100M)
+			*speed_dup = PORT_SPEED_DUP_100HALF;
+		else
+			return CMM_ERR_INPUT;
+	} else {
+		return CMM_ERR_INPUT;
+	}
+
+	return CMM_ERR_OK;
+}
+
+#ifdef CONFIG_MOTORCOMM_YT921X
+static u32 intphy_reg_offset_get(enum yt_phy_reg_space space,
+				 enum yt_phy_reg_type type, u32 *reg)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 offset = 0;
+
+	if (!reg)
+		return CMM_ERR_INPUT;
+
+	switch (type) {
+	case YT_PHY_REG_TYPE_PHY_MII:
+		offset = 0;
+		break;
+	case YT_PHY_REG_TYPE_PHY_EXT:
+		if (space == YT_PHY_REG_SPACE_T1)
+			offset = 0xc00;
+		else
+			offset = 0x800;
+		break;
+	case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		if (space == YT_PHY_REG_SPACE_T1)
+			offset = 0x800;
+		else if (space == YT_PHY_REG_SPACE_TX)
+			offset = 0xc00;
+		else
+			ret = CMM_ERR_NOT_SUPPORT;
+		break;
+	case YT_PHY_REG_TYPE_PHY_MMD1:
+		if (space == YT_PHY_REG_SPACE_T1 ||
+		    space == YT_PHY_REG_SPACE_TX)
+			offset = 0x100;
+		else
+			ret = CMM_ERR_NOT_SUPPORT;
+		break;
+	case YT_PHY_REG_TYPE_PHY_MMD3:
+		if (space == YT_PHY_REG_SPACE_T1 ||
+		    space == YT_PHY_REG_SPACE_TX)
+			offset = 0x200;
+		else
+			ret = CMM_ERR_NOT_SUPPORT;
+		break;
+	case YT_PHY_REG_TYPE_PHY_MMD7:
+		if (space == YT_PHY_REG_SPACE_TX)
+			offset = 0x300;
+		else
+			ret = CMM_ERR_NOT_SUPPORT;
+		break;
+	default:
+		ret = CMM_ERR_NOT_SUPPORT;
+		break;
+	}
+	*reg = offset;
+
+	return ret;
+}
+
+static u32 intsds_reg_addr_convert(u16 phy, u16 reg, u32 *addr)
+{
+	u8 type = PHY_GET_REGTYPE(phy);
+	u32 address = 0;
+
+	if (!addr)
+		return CMM_ERR_NULL_POINT;
+
+	if (type == YT_PHY_REG_TYPE_SG_MII)
+		address += PHY_BASEADDR_SGMII;
+	else if (type == YT_PHY_REG_TYPE_SG_EXT)
+		address += PHY_BASEADDR_SGMII + PHY_BASEADDR_OFFSET;
+	else
+		return CMM_ERR_NOT_SUPPORT;
+	*addr = address + (reg << 2);
+
+	return CMM_ERR_OK;
+}
+
+static u32 intphy_reg_addr_convert(u16 phy, u16 reg, u32 *addr)
+{
+	u8 space = PHY_GET_REGSPACE(phy);
+	u8 type = PHY_GET_REGTYPE(phy);
+	u8 tmp = PHY_GET_PHYADDR(phy);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 address = 0;
+	u32 offset = 0;
+
+	if (!addr)
+		return CMM_ERR_NULL_POINT;
+
+	if (type == YT_PHY_REG_TYPE_TOP_EXT) {
+		address += PHY_BASEADDR_FE_TOP;
+	} else {
+		CMM_ERR_CHK(intphy_reg_offset_get(space, type, &offset), ret);
+		if (space == YT_PHY_REG_SPACE_T1)
+			address += PHY_BASEADDR_T1(tmp);
+		else if (space == YT_PHY_REG_SPACE_TX)
+			address += PHY_BASEADDR_TX(tmp);
+		else
+			address += PHY_BASEADDR_LDS(tmp);
+		address += offset;
+	}
+	*addr = address + (reg << 2);
+
+	return CMM_ERR_OK;
+}
+
+static u32 intxmii_reg_addr_convert(u16 phy, u16 reg, u32 *addr)
+{
+	u8 type = PHY_GET_REGTYPE(phy);
+	u32 address = 0;
+
+	if (!addr)
+		return CMM_ERR_NULL_POINT;
+
+	/* xmii just support ext reg */
+	if (type == YT_PHY_REG_TYPE_PHY_EXT)
+		address += PHY_BASEADDR_XMII_EXT;
+	else
+		return CMM_ERR_NOT_SUPPORT;
+
+	*addr = address + (reg << 2);
+
+	return CMM_ERR_OK;
+}
+
+static u32 reg_addr_convert(u16 phy, u16 reg, u32 *addr)
+{
+	u8 tmp = PHY_GET_PHYADDR(phy);
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (tmp == 9)
+		CMM_ERR_CHK(intsds_reg_addr_convert(phy, reg, addr), ret);
+	else if (tmp == 8)
+		CMM_ERR_CHK(intxmii_reg_addr_convert(phy, reg, addr), ret);
+	else if (tmp < 4)
+		CMM_ERR_CHK(intphy_reg_addr_convert(phy, reg, addr), ret);
+	else
+		return CMM_ERR_NOT_SUPPORT;
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_920x_intif_write(u8 unit, u16 phy, u32 reg, u16 data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 reg_data = data;
+	u32 address = reg;
+
+	CMM_ERR_CHK(reg_addr_convert(phy, reg, &address), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, address, reg_data), ret);
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_920x_intif_read(u8 unit, u16 phy, u32 reg, u16 *data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 address = reg;
+	u32 reg_data = 0;
+
+	CMM_ERR_CHK(reg_addr_convert(phy, reg, &address), ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, address, &reg_data), ret);
+	*data = (u16)(reg_data & 0xffffU);
+
+	return CMM_ERR_OK;
+}
+
+static u32 intif_ext_reg_read(u8 unit, u8 phy, u16 reg, u16 *data)
+{
+	yt_switch_intif_write(unit, phy, 0x1e, reg);
+	yt_switch_intif_read(unit, phy, 0x1f, data);
+
+	return CMM_ERR_OK;
+}
+
+static u32 intif_ext_reg_write(u8 unit, u8 phy, u16 reg, u16 data)
+{
+	yt_switch_intif_write(unit, phy, 0x1e, reg);
+	yt_switch_intif_write(unit, phy, 0x1f, data);
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_861x_restart(u8 unit, u8 phy)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+	u16 org = 0;
+
+	CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x0, &org), ret);
+	data = (org | 1 << 15);
+	CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, data), ret);
+
+	if (IS_BIT_SET(org, 11))
+		yt_switch_intif_write(unit, phy, 0x0, org);
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_intphy_template_set(u8 unit, u8 phy,
+				  enum yt_utp_template_testmode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (mode < YT_UTP_TEMPLATE_TMODE_10M_10MSINE ||
+	    mode > YT_UTP_TEMPLATE_TMODE_1000M_T4)
+		return CMM_ERR_INPUT;
+
+	/*for common setting*/
+	CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x27, 0x2026), ret);
+	CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x10, 0x2), ret);
+
+	if (mode < YT_UTP_TEMPLATE_TMODE_100M_MDI)
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, 0x8100), ret);
+	else if (mode >= YT_UTP_TEMPLATE_TMODE_1000M_T1 &&
+		 mode <= YT_UTP_TEMPLATE_TMODE_1000M_T4)
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, 0x8140), ret);
+
+	switch (mode) {
+	case YT_UTP_TEMPLATE_TMODE_10M_10MSINE:
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, 0x209), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_10M_PRANDOM:
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, 0x20a), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_10M_LINKPULSE:
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, 0x20b), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_10M_5MSINE:
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, 0x20c), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_10M_NORMAL:
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, 0x20d), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_100M_MDI:
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, 0xa100), ret);
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x51, 0x4a9), ret);
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x57, 0x274c), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_100M_MDIX:
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x10, 0x22), ret);
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, 0xa100), ret);
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x51, 0x4a9), ret);
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x57, 0x274c), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_1000M_T1:
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x9, 0x2200), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_1000M_T2:
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x9, 0x5a00), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_1000M_T3:
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x9, 0x7200), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_1000M_T4:
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x9, 0x8200), ret);
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x51, 0x14a2), ret);
+		break;
+	default:
+		return CMM_ERR_INPUT;
+	}
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_rgmii_strength_set(u8 unit, enum yt_dvddio_power_pad pad,
+				 enum yt_dvddio_power_level level)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data = 0;
+	u32 index = 0;
+
+	if (pad == POWER_PAD_NORMAL)
+		return CMM_ERR_OK;
+
+	index = (pad == POWER_PAD_RGMII1) ? 0 : 1;
+	switch (level) {
+	case POWER18V:
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_MODE_SEL_REG, &data),
+			    ret);
+		SET_BIT(data, 9);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CHIP_MODE_SEL_REG, data),
+			    ret);
+
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_PAD_PART_REG(index),
+						&data), ret);
+		data &= ~(0x7 << 15);
+		data |= (0x4 << 15);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CHIP_PAD_PART_REG(index),
+						 data), ret);
+		break;
+	case POWER25V:
+	case POWER33V:
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_MODE_SEL_REG, &data),
+			    ret);
+		CLEAR_BIT(data, 9);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CHIP_MODE_SEL_REG, data),
+			    ret);
+
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_PAD_PART_REG(index),
+						&data), ret);
+		data &= ~(0x7 << 15);
+		data |= (0x3 << 15);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, CHIP_PAD_PART_REG(index),
+						 data), ret);
+		break;
+	default:
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	return ret;
+}
+
+static u32 yt_rgmii_vbias_set(u8 unit, enum yt_dvddio_power_pad pad,
+			      enum yt_dvddio_power_level level)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	u32 clk = CHIP_DCC_CALIB_CLK_SEL_REG;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data = 0;
+	u32 index = 0;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (pad == POWER_PAD_NORMAL)
+		return ret;
+
+	if ((chip_id == YT_SW_ID_9218 && pad == POWER_PAD_RGMII1) ||
+	    chip_id == YT_SW_ID_9215) {
+		index = 1;
+	} else if (chip_id == YT_SW_ID_9218 && pad == POWER_PAD_RGMII2) {
+		index = 3;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	switch (level) {
+	case POWER18V:
+	case POWER25V:
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, clk, &data), ret);
+		data &= ~(0x3 << index);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, clk, data), ret);
+		break;
+	case POWER33V:
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, clk, &data), ret);
+		data &= ~(0x3 << index);
+		data |= (0x2 << index);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, clk, data), ret);
+		break;
+	default:
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+static u32 yt_sds_mode_check(u8 unit, enum yt_extif_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 count = 10000;
+	u16 phy = 0x9;
+	u16 data = 0;
+
+	while (count) {
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_SG_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x10, &data), ret);
+		data &= 0x7;
+		if (data == (mode - YT_EXTIF_MODE_SG_MAC))
+			break;
+		count--;
+	}
+	if (count == 0)
+		return CMM_ERR_FAIL;
+	else
+		return CMM_ERR_OK;
+}
+
+static u32 yt_intsds_patch_configuration(u8 unit, enum yt_extif_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 phy = 0x9;
+	u16 data = 0;
+
+	/* auto sense */
+	if (mode == YT_EXTIF_MODE_SGFIB_AS) {
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_SG_EXT);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x1d0, 0x80), ret);
+	}
+
+	/* 17378 */
+	if (mode == YT_EXTIF_MODE_FIB_1000 || mode == YT_EXTIF_MODE_SG_MAC ||
+	    mode == YT_EXTIF_MODE_SG_PHY) {
+		PHY_CLEAR_REGTYPE(phy);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_SG_MII);
+		ret = yt_sds_mode_check(unit, mode);
+		if (ret != 0)
+			return CMM_ERR_FAIL;
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x14, 0x7020), ret);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		SET_BIT(data, 15);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+	}
+
+	return ret;
+}
+
+static u32 yt_intxmii_patch_configuration(u8 unit, enum yt_extif_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 phy = 0x8;
+
+	if (mode == YT_EXTIF_MODE_RMII_MAC || mode == YT_EXTIF_MODE_RMII_PHY) {
+		/* xmii clk */
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_EXT);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x22, 0x6), ret);
+	}
+	/* xmii ldo */
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_EXT);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x36, 0x1b0), ret);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x39, 0x10), ret);
+
+	return ret;
+}
+
+static u32 yt_phy_mode_set(u8 unit, u32 port, enum yt_extif_mode mode)
+{
+	u16 phy = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+
+	if (mode < YT_EXTIF_MODE_LRE || mode > YT_EXTIF_MODE_AUTO_LRE_FE) {
+		ret = CMM_ERR_NOT_SUPPORT;
+	} else {
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_LDS);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		switch (mode) {
+		case YT_EXTIF_MODE_LRE:
+			/*just support T1 mode */
+			CLEAR_BIT(data, 12);
+			CLEAR_BIT(data, 2);
+			/* select pair */
+			CLEAR_BIT(data, 4);
+			CLEAR_BIT(data, 5);
+			break;
+		case YT_EXTIF_MODE_FE:
+			CLEAR_BIT(data, 12);
+			SET_BIT(data, 2);
+			/* select pair */
+			SET_BIT(data, 4);
+			CLEAR_BIT(data, 5);
+			/* always set speed to 100M */
+			data &= ~(0xF << 6);
+			data |= (0x8 << 6);
+			break;
+		case YT_EXTIF_MODE_AUTO_LRE_FE:
+			SET_BIT(data, 12);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+			break;
+		}
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+	}
+
+	return ret;
+}
+
+static u32 yt_phy_interface_mode_get(u8 unit, u16 phy, enum yt_extif_mode *mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_LDS);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+
+	if (IS_BIT_SET(data, 12)) {
+		*mode = YT_EXTIF_MODE_AUTO_LRE_FE;
+	} else {
+		if (IS_BIT_SET(data, 2))
+			*mode = YT_EXTIF_MODE_FE;
+		else
+			*mode = YT_EXTIF_MODE_LRE;
+	}
+
+	return ret;
+}
+
+static u32 yt_851x_power_down(u8 unit, u32 phy, u16 data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (IS_BIT_SET(data, 11))
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+
+	return ret;
+}
+
+static u32 yt_851x_restart(u8 unit, u32 phy)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode = 0;
+	u16 data = 0;
+	u16 org = 0;
+
+	CMM_ERR_CHK(yt_phy_interface_mode_get(unit, phy, &mode), ret);
+	if (mode == YT_EXTIF_MODE_AUTO_LRE_FE) {
+		/* down both LRE and FE */
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		org = data;
+		SET_BIT(data, 15);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+		CMM_ERR_CHK(yt_851x_power_down(unit, phy, org), ret);
+
+		PHY_CLEAR_REGSPACE(phy);
+		PHY_CLEAR_REGTYPE(phy);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		org = data;
+		SET_BIT(data, 15);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+		CMM_ERR_CHK(yt_851x_power_down(unit, phy, org), ret);
+
+		PHY_CLEAR_REGSPACE(phy);
+		PHY_CLEAR_REGTYPE(phy);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_LDS);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		org = data;
+		SET_BIT(data, 15);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+		CMM_ERR_CHK(yt_851x_power_down(unit, phy, org), ret);
+	} else if (mode == YT_EXTIF_MODE_LRE) {
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		org = data;
+		SET_BIT(data, 15);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+		CMM_ERR_CHK(yt_851x_power_down(unit, phy, org), ret);
+	} else {
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		org = data;
+		SET_BIT(data, 15);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+		CMM_ERR_CHK(yt_851x_power_down(unit, phy, org), ret);
+	}
+
+	return ret;
+}
+
+static u32 yt_phy_enable_set(u8 unit, u16 phy, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode = 0;
+	u16 data = 0;
+
+	CMM_ERR_CHK(yt_phy_interface_mode_get(unit, phy, &mode), ret);
+	if (mode == YT_EXTIF_MODE_AUTO_LRE_FE) {
+		/* down both LRE and FE */
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_LDS);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		if (enable == YT_ENABLE)
+			CLEAR_BIT(data, 11);
+		else
+			SET_BIT(data, 11);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+
+		PHY_CLEAR_REGSPACE(phy);
+		PHY_CLEAR_REGTYPE(phy);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		if (enable == YT_ENABLE)
+			CLEAR_BIT(data, 11);
+		else
+			SET_BIT(data, 11);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+
+		PHY_CLEAR_REGSPACE(phy);
+		PHY_CLEAR_REGTYPE(phy);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		if (enable == YT_ENABLE)
+			CLEAR_BIT(data, 11);
+		else
+			SET_BIT(data, 11);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+	} else if (mode == YT_EXTIF_MODE_LRE) {
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		if (enable == YT_ENABLE)
+			CLEAR_BIT(data, 11);
+		else
+			SET_BIT(data, 11);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+	} else {
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		if (enable == YT_ENABLE)
+			CLEAR_BIT(data, 11);
+		else
+			SET_BIT(data, 11);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+	}
+
+	return ret;
+}
+
+static u32 yt_phy_enable_get(u8 unit, u16 phy, enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode = 0;
+	u16 data = 0;
+
+	CMM_ERR_CHK(yt_phy_interface_mode_get(unit, phy, &mode), ret);
+	if (mode == YT_EXTIF_MODE_AUTO_LRE_FE) {
+		/* down both LRE and FE */
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_LDS);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		if (IS_BIT_SET(data, 11))
+			*enable = YT_DISABLE;
+		else
+			*enable = YT_ENABLE;
+	} else if (mode == YT_EXTIF_MODE_LRE) {
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		if (IS_BIT_SET(data, 11))
+			*enable = YT_DISABLE;
+		else
+			*enable = YT_ENABLE;
+	} else {
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+		if (IS_BIT_SET(data, 11))
+			*enable = YT_DISABLE;
+		else
+			*enable = YT_ENABLE;
+	}
+
+	return ret;
+}
+
+static u32 yt_sds_enable_set(u8 unit, u16 phy, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_SG_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+	if (enable == YT_ENABLE)
+		CLEAR_BIT(data, 11);
+	else
+		SET_BIT(data, 11);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+
+	return ret;
+}
+
+static u32 yt_sds_enable_get(u8 unit, u16 phy, enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_SG_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+	if (IS_BIT_SET(data, 11))
+		*enable = YT_DISABLE;
+	else
+		*enable = YT_ENABLE;
+
+	return ret;
+}
+
+static u32 lds_auto_neg_ability_set(u8 unit, u16 phy,
+				    struct yt_port_an_ability ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 tx_data = 0;
+	u16 data = 0;
+
+	/* both support,  set tx, and t1 ability */
+	PHY_CLEAR_REGSPACE(phy);
+	PHY_CLEAR_REGTYPE(phy);
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_LDS);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &data), ret);
+	if (ability.lds_mode == YT_LDS_MODE_LRE) {
+		if (ability.full_10_en)
+			SET_BIT(data, 1);
+		else
+			CLEAR_BIT(data, 1);
+		if (ability.full_100_en)
+			SET_BIT(data, 5);
+		else
+			CLEAR_BIT(data, 5);
+		if (ability.full_10_en == 0 && ability.full_100_en == 0)
+			return CMM_ERR_NOT_SUPPORT;
+		CLEAR_BIT(data, 0);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x4, data), ret);
+	} else if (ability.lds_mode == YT_LDS_MODE_TX) {
+		CLEAR_BIT(data, 1);
+		CLEAR_BIT(data, 5);
+		SET_BIT(data, 0);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x4, data), ret);
+		/* tx ability */
+		PHY_CLEAR_REGSPACE(phy);
+		PHY_CLEAR_REGTYPE(phy);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &tx_data), ret);
+		if (ability.half_10_en)
+			SET_BIT(tx_data, 5);
+		else
+			CLEAR_BIT(tx_data, 5);
+
+		if (ability.full_10_en)
+			SET_BIT(tx_data, 6);
+		else
+			CLEAR_BIT(tx_data, 6);
+
+		if (ability.half_100_en)
+			SET_BIT(tx_data, 7);
+		else
+			CLEAR_BIT(tx_data, 7);
+
+		if (ability.full_100_en)
+			SET_BIT(tx_data, 8);
+		else
+			CLEAR_BIT(tx_data, 8);
+
+		if (ability.asy_fc_en)
+			SET_BIT(tx_data, 11);
+		else
+			CLEAR_BIT(tx_data, 11);
+
+		if (ability.fc_en)
+			SET_BIT(tx_data, 10);
+		else
+			CLEAR_BIT(tx_data, 10);
+
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x4, tx_data), ret);
+	} else if (ability.lds_mode == YT_LDS_MODE_BOTH_LRE_AND_TX) {
+		if (ability.full_10_en)
+			SET_BIT(data, 1);
+		else
+			CLEAR_BIT(data, 1);
+
+		if (ability.full_100_en)
+			SET_BIT(data, 5);
+		else
+			CLEAR_BIT(data, 5);
+
+		if (ability.full_10_en == 0 && ability.full_100_en == 0)
+			return CMM_ERR_NOT_SUPPORT;
+		SET_BIT(data, 0);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x4, data), ret);
+		/* tx ability */
+		PHY_CLEAR_REGSPACE(phy);
+		PHY_CLEAR_REGTYPE(phy);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &tx_data), ret);
+		if (ability.half_10_en)
+			SET_BIT(tx_data, 5);
+		else
+			CLEAR_BIT(tx_data, 5);
+
+		if (ability.full_10_en)
+			SET_BIT(tx_data, 6);
+		else
+			CLEAR_BIT(tx_data, 6);
+
+		if (ability.half_100_en)
+			SET_BIT(tx_data, 7);
+		else
+			CLEAR_BIT(tx_data, 7);
+
+		if (ability.full_100_en)
+			SET_BIT(tx_data, 8);
+		else
+			CLEAR_BIT(tx_data, 8);
+
+		if (ability.asy_fc_en)
+			SET_BIT(tx_data, 11);
+		else
+			CLEAR_BIT(tx_data, 11);
+
+		if (ability.fc_en)
+			SET_BIT(tx_data, 10);
+		else
+			CLEAR_BIT(tx_data, 10);
+
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x4, tx_data), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+static u32 yt_phy_auto_neg_ability_set(u8 unit, u16 phy,
+				       struct yt_port_an_ability ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode = 0;
+	u16 tx_data = 0;
+
+	if (ability.full_2500_en || ability.full_1000_en)
+		return CMM_ERR_NOT_SUPPORT;
+	CMM_ERR_CHK(yt_port_extif_mode_get(unit, phy, &mode), ret);
+
+	if (mode == YT_EXTIF_MODE_AUTO_LRE_FE) {
+		CMM_ERR_CHK(lds_auto_neg_ability_set(unit, phy, ability), ret);
+	} else if (mode == YT_EXTIF_MODE_FE) {
+		PHY_CLEAR_REGSPACE(phy);
+		PHY_CLEAR_REGTYPE(phy);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &tx_data), ret);
+		if (ability.half_10_en)
+			SET_BIT(tx_data, 5);
+		else
+			CLEAR_BIT(tx_data, 5);
+
+		if (ability.full_10_en)
+			SET_BIT(tx_data, 6);
+		else
+			CLEAR_BIT(tx_data, 6);
+
+		if (ability.half_100_en)
+			SET_BIT(tx_data, 7);
+		else
+			CLEAR_BIT(tx_data, 7);
+
+		if (ability.full_100_en)
+			SET_BIT(tx_data, 8);
+		else
+			CLEAR_BIT(tx_data, 8);
+
+		if (ability.asy_fc_en)
+			SET_BIT(tx_data, 11);
+		else
+			CLEAR_BIT(tx_data, 11);
+
+		if (ability.fc_en)
+			SET_BIT(tx_data, 10);
+		else
+			CLEAR_BIT(tx_data, 10);
+
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x4, tx_data), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	PHY_CLEAR_REGSPACE(phy);
+	PHY_CLEAR_REGTYPE(phy);
+	CMM_ERR_CHK(yt_851x_restart(unit, phy), ret);
+
+	return ret;
+}
+
+static u32 lds_auto_neg_ability_get(u8 unit, u16 phy,
+				    struct yt_port_an_ability *ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 tx_data = 0;
+	u16 data = 0;
+
+	/* lds mii 0x4 */
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_LDS);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &data), ret);
+	/* tx mii 0x4 */
+	PHY_CLEAR_REGSPACE(phy);
+	PHY_CLEAR_REGTYPE(phy);
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &tx_data), ret);
+	if (IS_BIT_SET(data, 0)) {
+		if (IS_BIT_SET(data, 1) || IS_BIT_SET(data, 5)) {
+			ability->lds_mode = YT_LDS_MODE_BOTH_LRE_AND_TX;
+			/* both mode, need combine tx and t1 ability */
+			if (IS_BIT_SET(data, 5) || IS_BIT_SET(tx_data, 8))
+				ability->full_100_en = TRUE;
+			else
+				ability->full_100_en = FALSE;
+
+			if (IS_BIT_SET(data, 1) || IS_BIT_SET(tx_data, 6))
+				ability->full_10_en = TRUE;
+			else
+				ability->full_10_en = FALSE;
+
+			if (IS_BIT_SET(tx_data, 7))
+				ability->half_100_en = TRUE;
+			else
+				ability->half_100_en = FALSE;
+
+			if (IS_BIT_SET(tx_data, 5))
+				ability->half_10_en = TRUE;
+			else
+				ability->half_10_en = FALSE;
+
+			if (IS_BIT_SET(data, 11))
+				ability->asy_fc_en = TRUE;
+			else
+				ability->asy_fc_en = FALSE;
+
+			if (IS_BIT_SET(data, 10))
+				ability->fc_en = TRUE;
+			else
+				ability->fc_en = FALSE;
+		} else {
+			ability->lds_mode = YT_LDS_MODE_TX;
+			/* TX mode, tx ability */
+			if (IS_BIT_SET(tx_data, 8))
+				ability->full_100_en = TRUE;
+			else
+				ability->full_100_en = FALSE;
+
+			if (IS_BIT_SET(tx_data, 6))
+				ability->full_10_en = TRUE;
+			else
+				ability->full_10_en = FALSE;
+
+			if (IS_BIT_SET(tx_data, 7))
+				ability->half_100_en = TRUE;
+			else
+				ability->half_100_en = FALSE;
+
+			if (IS_BIT_SET(tx_data, 5))
+				ability->half_10_en = TRUE;
+			else
+				ability->half_10_en = FALSE;
+
+			if (IS_BIT_SET(data, 11))
+				ability->asy_fc_en = TRUE;
+			else
+				ability->asy_fc_en = FALSE;
+
+			if (IS_BIT_SET(data, 10))
+				ability->fc_en = TRUE;
+			else
+				ability->fc_en = FALSE;
+		}
+	} else {
+		ability->lds_mode = YT_LDS_MODE_LRE;
+		if (IS_BIT_SET(data, 5))
+			ability->full_100_en = TRUE;
+		else
+			ability->full_100_en = FALSE;
+
+		if (IS_BIT_SET(data, 1))
+			ability->full_10_en = TRUE;
+		else
+			ability->full_10_en = FALSE;
+
+		ability->half_10_en = FALSE;
+		ability->half_100_en = FALSE;
+		ability->asy_fc_en = TRUE;
+		ability->fc_en = TRUE;
+	}
+	ability->full_2500_en = FALSE;
+	ability->full_1000_en = FALSE;
+
+	return ret;
+}
+
+static u32 yt_phy_auto_neg_ability_get(u8 unit, u16 phy,
+				       struct yt_port_an_ability *ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode = 0;
+	u16 tx_data = 0;
+	u16 data = 0;
+
+	CMM_ERR_CHK(yt_port_extif_mode_get(unit, phy, &mode), ret);
+	/* lds mii 0x4 */
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_LDS);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &data), ret);
+	/* tx mii 0x4 */
+	PHY_CLEAR_REGSPACE(phy);
+	PHY_CLEAR_REGTYPE(phy);
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &tx_data), ret);
+	if (mode == YT_EXTIF_MODE_AUTO_LRE_FE) {
+		CMM_ERR_CHK(lds_auto_neg_ability_get(unit, phy, ability), ret);
+	} else if (mode == YT_EXTIF_MODE_FE) {
+		ability->lds_mode = YT_LDS_MODE_TX;
+		/* TX mode, tx ability */
+		if (IS_BIT_SET(tx_data, 8))
+			ability->full_100_en = TRUE;
+		else
+			ability->full_100_en = FALSE;
+
+		if (IS_BIT_SET(tx_data, 6))
+			ability->full_10_en = TRUE;
+		else
+			ability->full_10_en = FALSE;
+
+		if (IS_BIT_SET(tx_data, 7))
+			ability->half_100_en = TRUE;
+		else
+			ability->half_100_en = FALSE;
+
+		if (IS_BIT_SET(tx_data, 5))
+			ability->half_10_en = TRUE;
+		else
+			ability->half_10_en = FALSE;
+
+		if (IS_BIT_SET(data, 11))
+			ability->asy_fc_en = TRUE;
+		else
+			ability->asy_fc_en = FALSE;
+
+		if (IS_BIT_SET(data, 10))
+			ability->fc_en = TRUE;
+		else
+			ability->fc_en = FALSE;
+	} else {
+		ret = CMM_ERR_NOT_SUPPORT;
+	}
+	ability->full_2500_en = FALSE;
+	ability->full_1000_en = FALSE;
+
+	return ret;
+}
+
+static u32 yt_sds_auto_neg_ability_set(u8 unit, u16 phy,
+				       struct yt_port_an_ability ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+	u16 org = 0;
+
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_SG_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &data), ret);
+	CLEAR_BIT(data, 8);
+	CLEAR_BIT(data, 7);
+
+	if (ability.asy_fc_en == 1)
+		SET_BIT(data, 8);
+
+	if (ability.fc_en == 1)
+		SET_BIT(data, 7);
+
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x4, data), ret);
+
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &org), ret);
+	data = org;
+	SET_BIT(data, 15);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+
+	if (IS_BIT_SET(data, 11))
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, org), ret);
+
+	return ret;
+}
+
+static u32 yt_sds_auto_neg_ability_get(u8 unit, u16 phy,
+				       struct yt_port_an_ability *ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_SG_MII);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x4, &data), ret);
+	if (IS_BIT_SET(data, 8))
+		ability->asy_fc_en = 1;
+	if (IS_BIT_SET(data, 7))
+		ability->fc_en = 1;
+
+	ability->full_1000_en = 1;
+	ability->full_100_en = 1;
+	ability->full_10_en = 1;
+
+	return ret;
+}
+
+static u32 yt_921x_intif_init(u8 unit, u8 phy)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+	u16 org = 0;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	/*vga init*/
+	if (chip_id != YT_SW_ID_9218) {
+		CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x50, &data), ret);
+		data &= 0xF3FF;
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x50, data), ret);
+	}
+
+	/*CSD ca*/
+	CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x408, &data), ret);
+	data &= 0x0FFFE;
+	CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x408, data), ret);
+
+	/*UTP init*/
+	CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x29, &data), ret);
+	data &= (~(0x3F));
+	data |= 0x8;
+	CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x29, data), ret);
+
+	/*Green Init(CLD)*/
+	CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x3a9, &data), ret);
+	data &= (~(0x3F));
+	data |= 0x17;
+	CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x3a9, data), ret);
+	CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x3a0, &data), ret);
+	data &= 0x3FFF;
+	CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x3a0, data), ret);
+	CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x3cc, &data), ret);
+	data &= 0x8FFF;
+	CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x3cc, data), ret);
+
+	CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x0, &org), ret);
+	data = (org);
+	SET_BIT(data, 15);
+	CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, data), ret);
+	if (IS_BIT_SET(org, 11))
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, org), ret);
+
+	return ret;
+}
+
+static u32 yt_921x_extif_init(u8 unit, u8 phy)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+	u32 bit = 0;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0xa0, &data), ret);
+	if (chip_id == YT_SW_ID_9218)
+		bit = 13;
+	else
+		bit = 14;
+
+	if (IS_BIT_SET(data, bit)) {
+		CLEAR_BIT(data, bit);
+		CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa0, data), ret);
+	}
+
+	/*vco band init*/
+	CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x2e, &data), ret);
+	SET_BIT(data, 9);
+	CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x2e, data), ret);
+
+	/*serdes as mode */
+	CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x1d0, &data), ret);
+	data &= ~(0xff << 0);
+	data |= (0x80 << 0);
+	CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x1d0, data), ret);
+
+	return ret;
+}
+
+static u32 yt_920x_intif_init(u8 unit, u8 phy)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_EXT);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x23, 0x456), ret);
+
+	/* 17303 */
+	PHY_CLEAR_REGSPACE(phy);
+	PHY_CLEAR_REGTYPE(phy);
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_TOP_EXT);
+	CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &data), ret);
+	SET_BIT(data, 7);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, data), ret);
+
+	/* csd */
+	PHY_CLEAR_REGSPACE(phy);
+	PHY_CLEAR_REGTYPE(phy);
+	PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_TX);
+	PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_TOP_EXT);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x58, 0x1228), ret);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x52, 0xe446), ret);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x56, 0x2805), ret);
+	CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x57, 0x514), ret);
+
+	return ret;
+}
+
+u32 yt_port_init(u8 unit)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u8 phy;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id != YT_SW_ID_920X) {
+		for (phy = 0; phy <= 7; phy++)
+			CMM_ERR_CHK(yt_921x_intif_init(unit, phy), ret);
+
+		for (phy = 8; phy <= 9; phy++)
+			CMM_ERR_CHK(yt_921x_extif_init(unit, phy), ret);
+	} else {
+		for (phy = 0; phy < 4; phy++)
+			CMM_ERR_CHK(yt_920x_intif_init(unit, phy), ret);
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_init);
+
+u32 yt_port_mac_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_enable org;
+	u32 reg = 0;
+	u32 id;
+	u32 ctrl;
+
+	yt_cal_board_chip_get(&chip_id);
+	id = CAL_YTP_TO_MAC(unit, port);
+	/* if no change, do nothing */
+	yt_port_mac_enable_get(unit, port, &org);
+	if (org == enable)
+		return CMM_ERR_OK;
+
+	reg = CHIP_PORT_CTRL + id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &ctrl), ret);
+	HAL_FIELD_SET(9, 1, &ctrl, enable);
+	HAL_FIELD_SET(3, 1, &ctrl, enable);
+	HAL_FIELD_SET(4, 1, &ctrl, enable);
+	if (chip_id == YT_SW_ID_920X) {
+		HAL_FIELD_SET(10, 1, &ctrl, enable);
+		if (enable == YT_DISABLE)
+			HAL_FIELD_SET(14, 1, &ctrl, enable);
+	}
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, ctrl), ret);
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_mac_enable_set);
+
+u32 yt_port_mac_enable_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 reg = 0;
+	u32 id;
+	u32 an_en;
+	u32 tx_en;
+	u32 rx_en;
+	u32 ctrl;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	reg = CHIP_PORT_CTRL + id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &ctrl), ret);
+	if (chip_id != YT_SW_ID_920X)
+		HAL_FIELD_GET(9, 1, &ctrl, &an_en);
+	else
+		HAL_FIELD_GET(10, 1, &ctrl, &an_en);
+	HAL_FIELD_GET(3, 1, &ctrl, &tx_en);
+	HAL_FIELD_GET(4, 1, &ctrl, &rx_en);
+
+	if ((an_en || tx_en || rx_en))
+		*enable = YT_ENABLE;
+	else
+		*enable = YT_DISABLE;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_mac_enable_get);
+
+u32 yt_port_mac_force_set(u8 unit, u32 port, struct yt_port_force_ctrl ctrl)
+{
+	enum yt_port_speed_mode speed_mode = PORT_SPEED_MODE_1000M;
+	enum yt_port_duplex duplex = PORT_DUPLEX_FULL;
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum yt_port_speed speed = PORT_SPEED_1000M;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 reg = 0;
+	u32 entry;
+	u32 id;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	id = CAL_YTP_TO_MAC(unit, port);
+	ret = yt_port_speed_dup_split(ctrl.speed_dup, &speed, &duplex);
+	if (ret != CMM_ERR_OK)
+		return ret;
+
+	if (speed == PORT_SPEED_2500M)
+		speed_mode = PORT_SPEED_MODE_2500M;
+	else
+		speed_mode = (enum yt_port_speed_mode)speed;
+
+	reg = CHIP_PORT_CTRL + id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &entry), ret);
+	if (chip_id != YT_SW_ID_920X) {
+		HAL_FIELD_SET(10, 1, &entry, YT_DISABLE);
+		HAL_FIELD_SET(9, 1, &entry, YT_DISABLE);
+		HAL_FIELD_SET(0, 3, &entry, speed_mode);
+		HAL_FIELD_SET(7, 1, &entry, duplex);
+		HAL_FIELD_SET(6, 1, &entry, ctrl.rx_fc_en);
+		HAL_FIELD_SET(5, 1, &entry, ctrl.tx_fc_en);
+	} else {
+		if (speed_mode >= PORT_SPEED_MODE_1000M && id < 4U)
+			return CMM_ERR_NOT_SUPPORT;
+		HAL_FIELD_SET(10, 1, &entry, YT_DISABLE);
+		HAL_FIELD_SET(11, 1, &entry, YT_DISABLE);
+		HAL_FIELD_SET(6, 1, &entry, ctrl.rx_fc_en);
+		HAL_FIELD_SET(5, 1, &entry, ctrl.tx_fc_en);
+		HAL_FIELD_SET(0, 3, &entry, speed_mode);
+		HAL_FIELD_SET(7, 1, &entry, duplex);
+		HAL_FIELD_SET(14, 1, &entry, YT_ENABLE);
+	}
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, entry), ret);
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_mac_force_set);
+
+u32 yt_port_mac_force_get(u8 unit, u32 port, struct yt_port_force_ctrl *ctrl)
+{
+	enum yt_port_speed_mode speed_mode = PORT_SPEED_MODE_1000M;
+	enum yt_port_duplex duplex = PORT_DUPLEX_FULL;
+	enum yt_port_speed speed = PORT_SPEED_1000M;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 reg = 0;
+	u32 entry;
+	u32 id;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	reg = CHIP_PORT_CTRL + id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &entry), ret);
+	HAL_FIELD_GET(0, 3, &entry, &speed_mode);
+	HAL_FIELD_GET(7, 1, &entry, &duplex);
+	HAL_FIELD_GET(6, 1, &entry, &ctrl->rx_fc_en);
+	HAL_FIELD_GET(5, 1, &entry, &ctrl->tx_fc_en);
+
+	/*b100 for mac and sds 2.5g*/
+	if (speed_mode == PORT_SPEED_MODE_2500M)
+		speed = PORT_SPEED_2500M;
+	else
+		speed = (enum yt_port_speed)speed_mode;
+
+	ret = yt_port_speed_dup_combine(speed, duplex, &ctrl->speed_dup);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_mac_force_get);
+
+u32 yt_port_mac_eee_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	u32 ret = CMM_ERR_OK;
+	static u8 eee;
+	u32 eee_enable;
+	u32 mac_id;
+	u32 eee_data;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	/*enable global eee*/
+	if (eee == 0 && enable == YT_ENABLE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, 0x80320, &eee_enable),
+			    ret);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, 0x80324, &eee_data), ret);
+		eee_enable |= (1 << 16);
+		eee_data |= (1 << 16);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0x80320, eee_enable),
+			    ret);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0x80324, eee_data), ret);
+		eee = 1;
+	}
+
+	mac_id = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, 0xb0000, &eee_enable), ret);
+	if (enable == YT_ENABLE)
+		eee_enable |= (1 << mac_id);
+	else
+		eee_enable &= ~(1 << mac_id);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0xb0000, eee_enable), ret);
+
+	if (chip_id != YT_SW_ID_920X) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, 0xa0000 + 0x40 * mac_id,
+						&eee_data), ret);
+		if (enable == YT_ENABLE)
+			SET_BIT(eee_data, 0);
+		else
+			CLEAR_BIT(eee_data, 0);
+
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0xa0000 + 0x40 * mac_id,
+						 eee_data), ret);
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_mac_eee_enable_set);
+
+static u32 int_xmii_force_set(u8 unit, u32 port, struct yt_port_force_ctrl ctrl)
+{
+	enum yt_port_speed_mode speed_mode = PORT_SPEED_MODE_1000M;
+	enum yt_port_duplex duplex = PORT_DUPLEX_FULL;
+	enum yt_port_speed speed = PORT_SPEED_1000M;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data;
+
+	ret = yt_port_speed_dup_split(ctrl.speed_dup, &speed, &duplex);
+	if (ret != CMM_ERR_OK)
+		return ret;
+
+	if (speed == PORT_SPEED_2500M) /*b100 for mac and sds 2.5g*/
+		speed_mode = PORT_SPEED_MODE_2500M;
+	else
+		speed_mode = (enum yt_port_speed_mode)speed;
+
+	attr.reg_addr = 0x15;
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_space = 0x0;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	SET_BIT(data, 4);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	attr.reg_addr = 0x32;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	SET_BIT(data, 2);
+	CLEAR_BIT(data, 13);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	attr.reg_addr = 0x32;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	if (ctrl.rx_fc_en)
+		SET_BIT(data, 10);
+	else
+		CLEAR_BIT(data, 10);
+
+	if (ctrl.tx_fc_en)
+		SET_BIT(data, 11);
+	else
+		CLEAR_BIT(data, 11);
+
+	if (duplex)
+		SET_BIT(data, 12);
+	else
+		CLEAR_BIT(data, 12);
+
+	data &= ~(0x3 << 14);
+	data |= (speed_mode << 14);
+	SET_BIT(data, 0);
+	SET_BIT(data, 1);
+	SET_BIT(data, 2);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	attr.reg_addr = 0x32;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	SET_BIT(data, 13);
+	SET_BIT(data, 2);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+u32 yt_port_xmii_force_set(u8 unit, u32 port, struct yt_port_force_ctrl ctrl)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 id;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	id = CAL_YTP_TO_MAC(unit, port);
+	if (chip_id == YT_SW_ID_920X) {
+		if (id == 0x8)
+			ret = int_xmii_force_set(unit, port, ctrl);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_xmii_force_set);
+
+static u32 int_xmii_force_get(u8 unit, u32 port,
+			      struct yt_port_force_ctrl *ctrl)
+{
+	enum yt_port_speed_mode speed_mode = PORT_SPEED_MODE_1000M;
+	enum yt_port_duplex duplex = PORT_DUPLEX_FULL;
+	enum yt_port_speed speed = PORT_SPEED_1000M;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data;
+
+	attr.reg_addr = 0x32;
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_space = 0x0;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+
+	speed_mode = (data >> 14) & 0x3;
+	duplex = IS_BIT_SET(data, 12) ? PORT_DUPLEX_FULL : PORT_DUPLEX_HALF;
+	ctrl->rx_fc_en = IS_BIT_SET(data, 10) ? YT_ENABLE : YT_DISABLE;
+	ctrl->tx_fc_en = IS_BIT_SET(data, 11) ? YT_ENABLE : YT_DISABLE;
+	/*b100 for mac and sds 2.5g*/
+	if (speed_mode == PORT_SPEED_MODE_2500M)
+		speed = PORT_SPEED_2500M;
+	else
+		speed = (enum yt_port_speed)speed_mode;
+	CMM_ERR_CHK(yt_port_speed_dup_combine(speed, duplex, &ctrl->speed_dup),
+		    ret);
+
+	return ret;
+}
+
+u32 yt_port_xmii_force_get(u8 unit, u32 port, struct yt_port_force_ctrl *ctrl)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 id;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	id = CAL_YTP_TO_MAC(unit, port);
+	if (chip_id == YT_SW_ID_920X) {
+		if (id == 0x8)
+			ret = int_xmii_force_get(unit, port, ctrl);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_xmii_force_get);
+
+u32 yt_port_extif_mode_set(u8 unit, u32 port, enum yt_extif_mode mode)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 phy = 0;
+	u16 tmp = 0;
+	u32 extif_reg;
+	u32 sg_data;
+	u8 index = 0;
+	u32 data = 0;
+	u32 extif;
+	u32 bit;
+	u32 id;
+	u32 reg;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	phy = CAL_YTP_TO_MAC(unit, port);
+	if (chip_id != YT_SW_ID_920X) {
+		if (mode == YT_EXTIF_MODE_SG_MAC) {
+			CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x1, 0x2700),
+				    ret);
+		}
+
+		if (CAL_YTP_TO_MAC(unit, port) == 4) {
+			if (mode >= YT_EXTIF_MODE_SG_MAC)
+				return CMM_ERR_NOT_SUPPORT;
+
+			id = 1;
+			bit = 2;
+		} else if (CAL_YTP_TO_MAC(unit, port) == 8) {
+			id = 0;
+			bit = (id == 0) ? 1 : 0;
+		} else if (CAL_YTP_TO_MAC(unit, port) == 9) {
+			id = 1;
+			bit = (id == 0) ? 1 : 0;
+		} else {
+			return CMM_ERR_INPUT;
+		}
+	}
+	switch (mode) {
+	case YT_EXTIF_MODE_MII:
+	case YT_EXTIF_MODE_REMII:
+	case YT_EXTIF_MODE_RMII_MAC:
+	case YT_EXTIF_MODE_RMII_PHY:
+	case YT_EXTIF_MODE_RGMII:
+	case YT_EXTIF_MODE_MII_MAC:
+	case YT_EXTIF_MODE_XMII_DISABLE:
+		if (chip_id != YT_SW_ID_920X) {
+			extif_reg = (id == 0) ? EXTIF0_MODE : EXTIF1_MODE;
+			/*select xmii mode*/
+			reg = CHIP_INTERFACE_SELECT_REG;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+			if (mode == YT_EXTIF_MODE_XMII_DISABLE)
+				CLEAR_BIT(data, bit);
+			else
+				SET_BIT(data, bit);
+
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+			/*config rgmii or xmii mode*/
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, extif_reg,
+							&extif), ret);
+			HAL_FIELD_SET(29, 3, &data, mode);
+			if (mode == YT_EXTIF_MODE_XMII_DISABLE)
+				HAL_FIELD_SET(18, 1, &data, FALSE);
+			else
+				HAL_FIELD_SET(18, 1, &data, TRUE);
+
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, extif_reg, data),
+				    ret);
+			HAL_MEM_DIRECT_WRITE(unit, 0x8035C + 4 * id, TRUE);
+		} else {
+			reg = CHIP_INTERFACE_XMII_PINMUX;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+			SET_BIT(data, 9);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+
+			reg = CHIP_INTERFACE_XMII_MODE;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+			data &= ~(0x7 << 2);
+			if (mode == YT_EXTIF_MODE_MII)
+				index = 4;
+			else if (mode == YT_EXTIF_MODE_MII_MAC)
+				index = 3;
+			else if (mode == YT_EXTIF_MODE_RMII_MAC)
+				index = 1;
+			else if (mode == YT_EXTIF_MODE_RMII_PHY)
+				index = 2;
+			else if (mode == YT_EXTIF_MODE_RGMII)
+				index = 0;
+			else
+				return CMM_ERR_NOT_SUPPORT;
+
+			data |= index << 2;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+			/* xmii patch */
+			CMM_ERR_CHK(yt_intxmii_patch_configuration(unit, mode),
+				    ret);
+		}
+		break;
+	case YT_EXTIF_MODE_SG_MAC:
+	case YT_EXTIF_MODE_SG_PHY:
+	case YT_EXTIF_MODE_FIB_1000:
+	case YT_EXTIF_MODE_FIB_100:
+	case YT_EXTIF_MODE_BX2500:
+	case YT_EXTIF_MODE_SGFIB_AS:
+		if (chip_id != YT_SW_ID_920X) {
+			/*enable serder interface*/
+			reg = CHIP_INTERFACE_CTRL_REG;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+			data |= 1 << id;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+			/*select sg mode*/
+			reg = CHIP_INTERFACE_SELECT_REG;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+			data &= ~(1 << bit);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+			/*config fiber or sg mode*/
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, SG_PHY + id * 4,
+							&sg_data), ret);
+			HAL_FIELD_SET(7, 3, &sg_data,
+				      mode - YT_EXTIF_MODE_SG_MAC);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, SG_PHY + id * 4,
+							 sg_data), ret);
+		} else {
+			if (mode == YT_EXTIF_MODE_SG_MAC ||
+			    mode == YT_EXTIF_MODE_SG_PHY ||
+			    mode == YT_EXTIF_MODE_FIB_1000) {
+				/* 17378 */
+				tmp = 0x9;
+				PHY_SET_REGTYPE(tmp, YT_PHY_REG_TYPE_SG_MII);
+				CMM_ERR_CHK(yt_920x_intif_write(unit, tmp, 0x14,
+								0xf020), ret);
+			}
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+							CHIP_INTERFACE_SDS_MODE,
+							&data), ret);
+			index = mode - YT_EXTIF_MODE_SG_MAC;
+			data &= ~(0x7 << 7);
+			data |= index << 7;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+						(unit, CHIP_INTERFACE_SDS_MODE,
+					    data), ret);
+			/* sds patch */
+			CMM_ERR_CHK(yt_intsds_patch_configuration(unit, mode),
+				    ret);
+		}
+		break;
+	case YT_EXTIF_MODE_LRE:
+	case YT_EXTIF_MODE_FE:
+	case YT_EXTIF_MODE_AUTO_LRE_FE:
+		if (chip_id == YT_SW_ID_920X)
+			CMM_ERR_CHK(yt_phy_mode_set(unit, port, mode), ret);
+		break;
+	default:
+		return CMM_ERR_INPUT;
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_extif_mode_set);
+
+u32 yt_port_extif_mode_get(u8 unit, u32 port, enum yt_extif_mode *mode)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 extif_reg;
+	u32 data = 0;
+	u32 sg_data;
+	u32 extif;
+	u32 bit;
+	u32 id;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id != YT_SW_ID_920X) {
+		if (CAL_YTP_TO_MAC(unit, port) == 4) {
+			id = 1;
+			bit = 2;
+		} else if (CAL_YTP_TO_MAC(unit, port) == 8) {
+			id = 0;
+			bit = (id == 0) ? 1 : 0;
+		} else if (CAL_YTP_TO_MAC(unit, port) == 9) {
+			id = 1;
+			bit = (id == 0) ? 1 : 0;
+		} else {
+			return CMM_ERR_INPUT;
+		}
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_INTERFACE_SELECT_REG,
+						&data), ret);
+		if (data & (1 << bit)) {
+			extif_reg = (id == 0) ? EXTIF0_MODE : EXTIF1_MODE;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, extif_reg, &data),
+				    ret);
+			HAL_FIELD_GET(29, 3, &extif, mode);
+			if (*mode > YT_EXTIF_MODE_XMII_DISABLE) {
+				*mode = YT_EXTIF_MODE_XMII_DISABLE;
+				return CMM_ERR_FAIL;
+			}
+		} else {
+			if (CAL_YTP_TO_MAC(unit, port) == 4) {
+				*mode = YT_EXTIF_MODE_XMII_DISABLE;
+				return CMM_ERR_OK;
+			}
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, SG_PHY + id * 4,
+							&sg_data), ret);
+			HAL_FIELD_GET(7, 3, &sg_data, mode);
+			if (*mode >
+			    YT_EXTIF_MODE_SGFIB_AS - YT_EXTIF_MODE_SG_MAC) {
+				*mode = YT_EXTIF_MODE_SGFIB_AS;
+				return CMM_ERR_FAIL;
+			}
+			*mode = *mode + YT_EXTIF_MODE_SG_MAC;
+		}
+	} else {
+		if (port == 0x9) {
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+							CHIP_INTERFACE_SDS_MODE,
+							&data), ret);
+			data = (data >> 7) & 0x7;
+			*mode = data + YT_EXTIF_MODE_SG_MAC;
+		} else if (port < 0x4) {
+			CMM_ERR_CHK(yt_phy_interface_mode_get(unit, port, mode),
+				    ret);
+		} else if (port == 0x8) {
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+						(unit, CHIP_INTERFACE_XMII_MODE,
+					    &data), ret);
+			data = (data >> 2) & 0x7;
+			if (data == 4) /* mii_phy */
+				*mode = YT_EXTIF_MODE_MII;
+			else if (data == 3)
+				/*mii_mac */
+				*mode = YT_EXTIF_MODE_MII_MAC;
+			else if (data == 1)
+				/* rmii1 */
+				*mode = YT_EXTIF_MODE_RMII_MAC;
+			else if (data == 2)
+				/* rmii2 */
+				*mode = YT_EXTIF_MODE_RMII_PHY;
+			else if (data == 0)
+				*mode = YT_EXTIF_MODE_RGMII;
+			else
+				return CMM_ERR_NOT_SUPPORT;
+
+		} else {
+			return CMM_ERR_NOT_SUPPORT;
+		}
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_extif_mode_get);
+
+u32 yt_port_extif_rgmii_delay_set(u8 unit, u32 port, u8 rxc_delay, u8 txc_delay,
+				  enum yt_enable txc_2ns_en)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode;
+	u16 delay;
+	u32 data;
+	u32 phy;
+	u32 reg;
+	u32 id;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (rxc_delay > 0xF || txc_delay > 0xF)
+		return CMM_ERR_INPUT;
+
+	if (chip_id != YT_SW_ID_920X) {
+		if (CAL_YTP_TO_MAC(unit, port) == 4) {
+			ret = yt_port_extif_mode_get(unit, port, &mode);
+			if (ret != CMM_ERR_OK)
+				return ret;
+			if (mode == YT_EXTIF_MODE_RGMII)
+				id = 1;
+			else
+				return CMM_ERR_NOT_SUPPORT;
+		} else if (CAL_YTP_TO_MAC(unit, port) == 8) {
+			id = 0;
+		} else if (CAL_YTP_TO_MAC(unit, port) == 9) {
+			id = 1;
+		} else {
+			return CMM_ERR_INPUT;
+		}
+
+		reg = (id == 0) ? EXTIF0_MODE : EXTIF1_MODE;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		HAL_FIELD_SET(8, 1, &data, txc_2ns_en);
+		HAL_FIELD_SET(13, 4, &data, txc_delay);
+		HAL_FIELD_SET(3, 4, &data, rxc_delay);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+	} else {
+		phy = CAL_YTP_TO_MAC(unit, port);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_EXT);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &delay), ret);
+		if (txc_2ns_en == YT_ENABLE)
+			SET_BIT(delay, 8);
+		else
+			CLEAR_BIT(delay, 8);
+
+		delay &= ~(0xFF);
+		delay |= (txc_delay);
+		delay |= (rxc_delay << 4);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x0, delay), ret);
+
+		/* delay calibration */
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x2, &delay), ret);
+		SET_BIT(delay, 6);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x2, delay), ret);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x2, &delay), ret);
+		CLEAR_BIT(delay, 6);
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x2, delay), ret);
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_extif_rgmii_delay_set);
+
+u32 yt_port_extif_rgmii_delay_get(u8 unit, u32 port, u8 *rxc_delay,
+				  u8 *txc_delay, enum yt_enable *txc_2ns_en)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode;
+	u32 data = 0;
+	u16 delay;
+	u32 reg;
+	u32 phy;
+	u32 id;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id != YT_SW_ID_920X) {
+		if (CAL_YTP_TO_MAC(unit, port) == 4) {
+			ret = yt_port_extif_mode_get(unit, port, &mode);
+			if (ret != CMM_ERR_OK)
+				return ret;
+
+			if (mode == YT_EXTIF_MODE_RGMII)
+				id = 1;
+			else
+				return CMM_ERR_NOT_SUPPORT;
+		} else if (CAL_YTP_TO_MAC(unit, port) == 8) {
+			id = 0;
+		} else if (CAL_YTP_TO_MAC(unit, port) == 9) {
+			id = 1;
+		} else {
+			return CMM_ERR_INPUT;
+		}
+
+		reg = (id == 0) ? EXTIF0_MODE : EXTIF1_MODE;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		HAL_FIELD_GET(8, 1, &data, txc_2ns_en);
+		HAL_FIELD_GET(13, 4, &data, txc_delay);
+		HAL_FIELD_GET(3, 4, &data, rxc_delay);
+	} else {
+		phy = CAL_YTP_TO_MAC(unit, port);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_EXT);
+		PHY_SET_REGSPACE(phy, YT_PHY_REG_SPACE_T1);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x0, &delay), ret);
+		if (IS_BIT_SET(delay, 8))
+			*txc_2ns_en = YT_ENABLE;
+		else
+			*txc_2ns_en = YT_DISABLE;
+
+		*txc_delay = (delay & 0xF);
+		*rxc_delay = (delay >> 4) & 0xF;
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_extif_rgmii_delay_get);
+
+u32 yt_port_dvddio_power_level_set(u8 unit, enum yt_dvddio_power_pad pad,
+				   enum yt_dvddio_power_level level)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	u32 reg = CHIP_IO_LEVEL_MODE_REG;
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 io_level = 0;
+	u32 data = 0;
+	u32 index = 0;
+	u32 value = 0;
+	u32 phy;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id == YT_SW_ID_9218) {
+		if (pad == POWER_PAD_NORMAL)
+			index = 0;
+		else if (pad == POWER_PAD_RGMII1)
+			index = 2;
+		else
+			index = 4;
+
+	} else if (chip_id == YT_SW_ID_9215) {
+		if (pad == POWER_PAD_NORMAL)
+			index = 4;
+		else if (pad == POWER_PAD_RGMII1)
+			index = 0;
+		else
+			index = 2;
+
+	} else if (chip_id == YT_SW_ID_920X) {
+		phy = CAL_YTP_TO_MAC(unit, 0x8);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_EXT);
+		/* need refactor */
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x18, &io_level),
+			    ret);
+		io_level &= ~(0x3);
+		if (io_level == POWER18V)
+			io_level |= 0x3;
+		else if (io_level == POWER25V)
+			io_level |= 0x2;
+		else if (io_level == POWER33V)
+			io_level &= ~(0x3);
+		else
+			return CMM_ERR_NOT_SUPPORT;
+
+		CMM_ERR_CHK(yt_920x_intif_write(unit, phy, 0x18, (u16)io_level),
+			    ret);
+
+		return ret;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	if (pad == POWER_PAD_NORMAL) {
+		if (level == POWER25V || level == POWER33V)
+			value = 0x0;
+		else
+			value = 0x3;
+
+	} else {
+		if (level == POWER18V)
+			value = 0x2;
+		else if (level == POWER25V)
+			value = 0x1;
+		else
+			value = 0x0;
+	}
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+	data &= ~(0x3 << index);
+	data |= (value << index);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+
+	ret = yt_rgmii_strength_set(unit, pad, level);
+	if (ret != CMM_ERR_OK)
+		return ret;
+
+	ret = yt_rgmii_vbias_set(unit, pad, level);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_dvddio_power_level_set);
+
+u32 yt_port_dvddio_power_level_get(u8 unit, enum yt_dvddio_power_pad pad,
+				   enum yt_dvddio_power_level *level)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 io_level = 0;
+	u32 data = 0;
+	u32 phy = 0;
+	u8 index = 0;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id == YT_SW_ID_9218) {
+		if (pad == POWER_PAD_NORMAL)
+			index = 0;
+		else if (pad == POWER_PAD_RGMII1)
+			index = 2;
+		else
+			index = 4;
+	} else if (chip_id == YT_SW_ID_9215) {
+		if (pad == POWER_PAD_NORMAL)
+			index = 4;
+		else if (pad == POWER_PAD_RGMII1)
+			index = 0;
+		else
+			index = 2;
+	} else if (chip_id == YT_SW_ID_920X) {
+		phy = CAL_YTP_TO_MAC(unit, 0x8);
+		PHY_SET_REGTYPE(phy, YT_PHY_REG_TYPE_PHY_EXT);
+		CMM_ERR_CHK(yt_920x_intif_read(unit, phy, 0x18, &io_level),
+			    ret);
+		io_level &= 0x3;
+		if (io_level == 0x0)
+			*level = POWER33V;
+		else if (io_level == 0x2)
+			*level = POWER25V;
+		else if (io_level == 0x3)
+			*level = POWER18V;
+		else
+			return CMM_ERR_NOT_SUPPORT;
+
+		return ret;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_IO_LEVEL_MODE_REG, &data),
+		    ret);
+	data = (0x3) & (data >> index);
+	if (pad == POWER_PAD_NORMAL) {
+		if (data == 0)
+			*level = POWER33V_OR_25V;
+		else
+			*level = POWER18V;
+	} else {
+		if (data == 0)
+			*level = POWER33V;
+		else if (data == 1)
+			*level = POWER25V;
+		else
+			*level = POWER18V;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_dvddio_power_level_get);
+
+u32 yt_port_extif_xmiiclk_invert_set(u8 unit, u32 port, enum yt_enable state)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 index;
+	u32 data;
+	u32 reg;
+
+	if (port == 4 || port == 9)
+		index = 1;
+	else
+		index = 0;
+
+	reg = 0x80400 + index * 0x8;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+	HAL_FIELD_SET(11, 1, &data, state);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_extif_xmiiclk_invert_set);
+
+u32 yt_port_extif_xmiiclk_invert_get(u8 unit, u32 port, enum yt_enable *state)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 index;
+	u32 data;
+	u32 extif;
+	u32 reg;
+
+	if (port == 4 || port == 9)
+		index = 1;
+	else
+		index = 0;
+
+	reg = 0x80400 + index * 0x8;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+	HAL_FIELD_GET(11, 1, &extif, &data);
+	if (data)
+		*state = YT_ENABLE;
+	else
+		*state = YT_DISABLE;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_extif_xmiiclk_invert_get);
+
+u32 yt_port_jumbo_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 id;
+	u32 data;
+	u32 reg;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	reg = 0x81008 + 0x200 * id;
+	if (chip_id == YT_SW_ID_920X)
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+	else
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+
+	if (enable == YT_ENABLE) {
+		if (((data & 0x3FFF00) >> 8) <= YT_JUMBO_SIZE_MIN) {
+			data &= 0xFFC000FF;
+			data |= (YT_JUMBO_SIZE_MAX << 8);
+		}
+	} else {
+		data &= 0xFFC000FF;
+		data |= (YT_JUMBO_SIZE_MIN << 8);
+	}
+	if (chip_id == YT_SW_ID_920X)
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+	else
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_jumbo_enable_set);
+
+u32 yt_port_jumbo_enable_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 id;
+	u32 data;
+	u32 reg;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	reg = 0x81008 + 0x1000 * id;
+	if (chip_id == YT_SW_ID_920X)
+		ret = HAL_MEM_DIRECT_READ(unit, 0x81008 + 0x200 * id, &data);
+
+	else
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+	*enable = ((data & 0x3FFF00) <= (YT_JUMBO_SIZE_MAX << 8) &&
+		   (data & 0x3FFF00) > (YT_JUMBO_SIZE_MIN << 8)) ?
+			  YT_ENABLE :
+			  YT_DISABLE;
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_jumbo_enable_get);
+
+u32 yt_port_jumbo_size_set(u8 unit, u32 port, u32 size)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_enable enable;
+	u32 id;
+	u32 data;
+	u32 reg;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	id = CAL_YTP_TO_MAC(unit, port);
+
+	if (yt_port_jumbo_enable_get(unit, port, &enable) == CMM_ERR_OK) {
+		if (enable == YT_ENABLE) {
+			if (chip_id == YT_SW_ID_920X) {
+				reg = 0x81008 + 0x200 * id;
+				CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg,
+								&data), ret);
+			} else {
+				reg = 0x81008 + 0x1000 * id;
+				CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg,
+								&data), ret);
+			}
+			data &= 0xFFC000FF;
+			data |= (size << 8);
+			if (chip_id == YT_SW_ID_920X) {
+				reg = 0x81008 + 0x200 * id;
+				CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg,
+								 data), ret);
+			} else {
+				reg = 0x81008 + 0x1000 * id;
+				CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg,
+								 data), ret);
+			}
+		} else {
+			return CMM_ERR_FORBIDDEN;
+		}
+	} else {
+		return CMM_ERR_FAIL;
+	}
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_jumbo_size_set);
+
+u32 yt_port_jumbo_size_get(u8 unit, u32 port, u32 *size)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 id;
+	u32 data;
+	u32 index;
+	u32 reg;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id == YT_SW_ID_920X)
+		index = 0x200;
+	else
+		index = 0x1000;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	reg = 0x81008 + index * id;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+	*size = ((data & 0x3FFF00) >> 8);
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_jumbo_size_get);
+
+u32 yt_port_link_status_all_get(u8 unit, u32 port,
+				struct yt_port_link_status_all *all_status)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 status;
+	u32 id;
+	u32 reg;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+
+	reg = PORT_STATUS + id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &status), ret);
+	HAL_FIELD_GET(8, 1, &status, &all_status->link_status);
+	HAL_FIELD_GET(0, 3, &status, &all_status->link_speed);
+	HAL_FIELD_GET(7, 1, &status, &all_status->link_duplex);
+	HAL_FIELD_GET(6, 1, &status, &all_status->rx_fc_en);
+	HAL_FIELD_GET(5, 1, &status, &all_status->tx_fc_en);
+
+	/*b100 for mac and sds 2.5g*/
+	if ((enum yt_port_speed_mode)all_status->link_speed ==
+	    PORT_SPEED_MODE_2500M)
+		all_status->link_speed = PORT_SPEED_2500M;
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_link_status_all_get);
+
+u32 yt_port_phy_enable_set(u8 unit, u32 port, enum yt_phy_chip_mode mode,
+			   enum yt_enable enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 phy_data = 0;
+	u32 phy;
+	u32 id;
+	u32 reg_data1;
+	u32 reg_data;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	phy = CAL_YTP_TO_MAC(unit, port);
+	CMM_UNUSED_PARAM(mode);
+	if (chip_id == YT_SW_ID_920X) {
+		HAL_MEM_DIRECT_READ(unit, 0x80000040, &reg_data);
+		HAL_MEM_DIRECT_READ(unit, 0x80000044, &reg_data1);
+		if (enable == YT_ENABLE) {
+			/* enable phy */
+			SET_BIT(reg_data, id);
+			SET_BIT(reg_data1, id);
+		} else {
+			/* disable phy */
+			SET_BIT(reg_data, id);
+			CLEAR_BIT(reg_data1, id);
+		}
+		HAL_MEM_DIRECT_WRITE(unit, 0x80000044, reg_data1);
+		HAL_MEM_DIRECT_WRITE(unit, 0x80000040, reg_data);
+
+		if (id < 4)
+			CMM_ERR_CHK(yt_phy_enable_set(unit, phy, enable), ret);
+		else if (id == 0x9)
+			CMM_ERR_CHK(yt_sds_enable_set(unit, phy, enable), ret);
+		else
+			return CMM_ERR_OK;
+	} else {
+		if (id > 7)
+			return CMM_ERR_NOT_SUPPORT;
+		HAL_MEM_DIRECT_READ(unit, 0x8002c, &reg_data);
+		if (enable == YT_ENABLE) {
+			/* enable phy */
+			SET_BIT(reg_data, id);
+			SET_BIT(reg_data, id + 8);
+		} else {
+			/* disable phy */
+			CLEAR_BIT(reg_data, id);
+			SET_BIT(reg_data, id + 8);
+		}
+		HAL_MEM_DIRECT_WRITE(unit, 0x8002c, reg_data);
+
+		CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x0, &phy_data),
+			    ret);
+		if (enable == YT_ENABLE) {
+			phy_data &= 0xf7ff;
+			phy_data |= 0x200;
+		} else {
+			phy_data |= 0x800;
+		}
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, phy_data),
+			    ret);
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_phy_enable_set);
+
+u32 yt_port_phy_enable_get(u8 unit, u32 port, enum yt_phy_chip_mode mode,
+			   enum yt_enable *enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data = 0;
+	u32 phy;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+	CMM_UNUSED_PARAM(mode);
+
+	if (chip_id != YT_SW_ID_920X) {
+		CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x0, &data), ret);
+		*enable = ((data >> 11) & 0x1) ? YT_DISABLE : YT_ENABLE;
+	} else {
+		if (phy < 4)
+			CMM_ERR_CHK(yt_phy_enable_get(unit, phy, enable), ret);
+		else if (phy == 0x9)
+			CMM_ERR_CHK(yt_sds_enable_get(unit, phy, enable), ret);
+		else
+			return CMM_ERR_OK;
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_phy_enable_get);
+
+u32 yt_port_phy_auto_neg_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode = 0;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+	u32 phy;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		phy = CAL_YTP_TO_MAC(unit, port);
+		CMM_ERR_CHK(yt_port_extif_mode_get(unit, port, &mode), ret);
+		if (mode == YT_EXTIF_MODE_AUTO_LRE_FE ||
+		    mode == YT_EXTIF_MODE_LRE) {
+			/* AUTO_LRE_FE FEmust autoNeg enable, T1 not support */
+			ret = CMM_ERR_NOT_SUPPORT;
+		} else {
+			attr.phy_type = PHY_INTERNAL;
+			attr.reg_addr = 0x0;
+			attr.reg_space = YT_PHY_REG_SPACE_TX;
+			attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr,
+							&data), ret);
+			if (enable == YT_DISABLE)
+				CLEAR_BIT(data, 12);
+			else
+				SET_BIT(data, 12);
+
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data),
+				    ret);
+			yt_851x_restart(unit, phy);
+		}
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_auto_neg_set);
+
+u32 yt_port_phy_auto_neg_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode = 0;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		CMM_ERR_CHK(yt_port_extif_mode_get(unit, port, &mode), ret);
+		if (mode == YT_EXTIF_MODE_AUTO_LRE_FE ||
+		    mode == YT_EXTIF_MODE_LRE) {
+			/* AUTO_LRE_FE FEmust autoNeg enable, T1 not support */
+			ret = CMM_ERR_NOT_SUPPORT;
+		} else {
+			attr.phy_type = PHY_INTERNAL;
+			attr.reg_addr = 0x0;
+			attr.reg_space = YT_PHY_REG_SPACE_TX;
+			attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr,
+							&data), ret);
+			if (IS_BIT_SET(data, 12))
+				*enable = YT_ENABLE;
+			else
+				*enable = YT_DISABLE;
+		}
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_auto_neg_get);
+
+u32 yt_port_phy_force_speed_set(u8 unit, u32 port,
+				enum yt_port_speed_duplex speed_dup)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	enum yt_extif_mode mode = 0;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		if (speed_dup == PORT_SPEED_DUP_1000FULL ||
+		    speed_dup == PORT_SPEED_DUP_2500FULL)
+			return CMM_ERR_NOT_SUPPORT;
+
+		CMM_ERR_CHK(yt_port_extif_mode_get(unit, port, &mode), ret);
+		if (mode == YT_EXTIF_MODE_AUTO_LRE_FE) {
+			/* mode auto, not support force speed */
+			return CMM_ERR_NOT_SUPPORT;
+		} else if (mode == YT_EXTIF_MODE_FE) {
+			/* both support t1, tx force speed */
+			attr.phy_type = PHY_INTERNAL;
+			attr.reg_addr = 0x0;
+			attr.reg_space = YT_PHY_REG_SPACE_LDS;
+			attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr,
+							&data), ret);
+			data &= ~(0xF << 6);
+			if (speed_dup == PORT_SPEED_DUP_100FULL ||
+			    speed_dup == PORT_SPEED_DUP_100HALF)
+				data |= (0x8 << 6);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data),
+				    ret);
+
+			/* tx force speed */
+			attr.phy_type = PHY_INTERNAL;
+			attr.reg_addr = 0x0;
+			attr.reg_space = YT_PHY_REG_SPACE_TX;
+			attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr,
+							&data), ret);
+			if (speed_dup == PORT_SPEED_DUP_10HALF ||
+			    speed_dup == PORT_SPEED_DUP_10HALF)
+				CLEAR_BIT(data, 8);
+			else
+				SET_BIT(data, 8);
+			if (speed_dup == PORT_SPEED_DUP_10HALF ||
+			    speed_dup == PORT_SPEED_DUP_10FULL) {
+				CLEAR_BIT(data, 6);
+				CLEAR_BIT(data, 13);
+				SET_BIT(data, 15);
+			} else {
+				CLEAR_BIT(data, 6);
+				SET_BIT(data, 13);
+				SET_BIT(data, 15);
+			}
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data),
+				    ret);
+		} else {
+			/* support t1 force speed */
+			attr.phy_type = PHY_INTERNAL;
+			attr.reg_addr = 0x0;
+			attr.reg_space = YT_PHY_REG_SPACE_LDS;
+			attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr,
+							&data), ret);
+			data &= ~(0xF << 6);
+			if (speed_dup == PORT_SPEED_DUP_100FULL)
+				data |= (0x8 << 6);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data),
+				    ret);
+		}
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_force_speed_set);
+
+u32 yt_port_phy_force_speed_get(u8 unit, u32 port,
+				enum yt_port_speed_duplex *speed)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	enum yt_extif_mode mode = 0;
+	u16 tx_reg = 0;
+	u16 data = 0;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		CMM_ERR_CHK(yt_port_extif_mode_get(unit, port, &mode), ret);
+		if (mode == YT_EXTIF_MODE_AUTO_LRE_FE ||
+		    mode == YT_EXTIF_MODE_FE) {
+			/* both support t1, tx force speed */
+			attr.phy_type = PHY_INTERNAL;
+			attr.reg_addr = 0x0;
+			attr.reg_space = YT_PHY_REG_SPACE_TX;
+			attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr,
+							&data), ret);
+			data = (data >> 6) & 0xF;
+			if (data == 0x0) {
+				if (IS_BIT_SET(tx_reg, 8))
+					*speed = PORT_SPEED_DUP_10FULL;
+				else
+					*speed = PORT_SPEED_DUP_10HALF;
+			} else if ((data == 0x8)) {
+				if (IS_BIT_SET(tx_reg, 8))
+					*speed = PORT_SPEED_DUP_100FULL;
+				else
+					*speed = PORT_SPEED_DUP_100HALF;
+			} else {
+				return CMM_ERR_NOT_SUPPORT;
+			}
+		} else {
+			/* support t1 force speed */
+			attr.phy_type = PHY_INTERNAL;
+			attr.reg_addr = 0x0;
+			attr.reg_space = YT_PHY_REG_SPACE_LDS;
+			attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr,
+							&data), ret);
+			data = data >> 6;
+			if (data == 0x0)
+				*speed = PORT_SPEED_DUP_10FULL;
+			else if (data == 0x8)
+				*speed = PORT_SPEED_DUP_100FULL;
+			else
+				return CMM_ERR_NOT_SUPPORT;
+		}
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_force_speed_get);
+
+static u32 yt_port_phy920x_reg_set(u8 unit, u32 port,
+				   struct yt_phy_reg_attr attr, u16 data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 index = 0;
+	u32 phy;
+
+	if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD1)
+		index = 1;
+	else if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD3)
+		index = 3;
+	else
+		index = 7;
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+
+	if (attr.phy_type == PHY_INTERNAL) {
+		if (phy < 4 || phy == 8 || phy == 9) {
+			/* internal phy */
+			if (phy < 4)
+				PHY_SET_REGSPACE(phy, attr.reg_space);
+			PHY_SET_REGTYPE(phy, attr.reg_type);
+			CMM_ERR_CHK(yt_920x_intif_write(unit, phy,
+							attr.reg_addr,
+							data), ret);
+		} else {
+			return CMM_ERR_NOT_SUPPORT;
+		}
+	} else if (attr.phy_type == PHY_EXTERNAL) {
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1f,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy,
+							  attr.reg_addr,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else {
+		ret = CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+static u32 yt_port_phy920x_reg_get(u8 unit, u32 port,
+				   struct yt_phy_reg_attr attr, u16 *data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 index = 0;
+	u32 phy;
+
+	if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD1)
+		index = 1;
+	else if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD3)
+		index = 3;
+	else
+		index = 7;
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+
+	if (attr.phy_type == PHY_INTERNAL) {
+		if (phy < 4 || phy == 8 || phy == 9) {
+			/* internal phy */
+			if (phy < 4)
+				PHY_SET_REGSPACE(phy, attr.reg_space);
+			PHY_SET_REGTYPE(phy, attr.reg_type);
+			CMM_ERR_CHK(yt_920x_intif_read(unit, phy, attr.reg_addr,
+						       data), ret);
+		} else {
+			return CMM_ERR_NOT_SUPPORT;
+		}
+	} else if (attr.phy_type == PHY_EXTERNAL) {
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy, 0x1f, data),
+				    ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy,
+							 attr.reg_addr,
+							 data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else {
+		ret = CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+static u32 yt_port_phy921x_reg_set(u8 unit, u32 port,
+				   struct yt_phy_reg_attr attr, u16 data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 index = 0;
+	u32 phy;
+
+	if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD1)
+		index = 1;
+	else if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD3)
+		index = 3;
+	else
+		index = 7;
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+	if (attr.phy_type == PHY_INTERNAL) {
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x1f,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy,
+							  attr.reg_addr,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else if (attr.phy_type == PHY_EXTERNAL) {
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1f,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy,
+							  attr.reg_addr,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else {
+		ret = CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+static u32 yt_port_phy921x_reg_get(u8 unit, u32 port,
+				   struct yt_phy_reg_attr attr, u16 *data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 index = 0;
+	u32 phy;
+
+	if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD1)
+		index = 1;
+	else if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD3)
+		index = 3;
+	else
+		index = 7;
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+	if (attr.phy_type == PHY_INTERNAL) {
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x1f, data),
+				    ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_intif_read(unit, phy,
+							 attr.reg_addr,
+							 data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else if (attr.phy_type == PHY_EXTERNAL) {
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy, 0x1f, data),
+				    ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy,
+							 attr.reg_addr,
+							 data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else {
+		ret = CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+u32 yt_port_phy_reg_set(u8 unit, u32 port, struct yt_phy_reg_attr attr,
+			u16 data)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	if (chip_id == YT_SW_ID_920X)
+		CMM_ERR_CHK(yt_port_phy920x_reg_set(unit, port, attr, data),
+			    ret);
+	else if (chip_id == YT_SW_ID_9215 || chip_id == YT_SW_ID_9218)
+		CMM_ERR_CHK(yt_port_phy921x_reg_set(unit, port, attr, data),
+			    ret);
+	else
+		ret = CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_reg_set);
+
+u32 yt_port_phy_reg_get(u8 unit, u32 port, struct yt_phy_reg_attr attr,
+			u16 *data)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	if (chip_id == YT_SW_ID_920X)
+		CMM_ERR_CHK(yt_port_phy920x_reg_get(unit, port, attr, data),
+			    ret);
+	else if (chip_id == YT_SW_ID_9215 || chip_id == YT_SW_ID_9218)
+		CMM_ERR_CHK(yt_port_phy921x_reg_get(unit, port, attr, data),
+			    ret);
+	else
+		ret = CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_reg_get);
+
+u32 yt_port_phy_auto_neg_ability_set(u8 unit, u32 port,
+				     enum yt_phy_chip_mode mode,
+				     struct yt_port_an_ability ability)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data4 = 0;
+	u16 data9 = 0;
+	u16 data = 0;
+	u16 org = 0;
+	u32 phy;
+
+	yt_cal_board_chip_get(&chip_id);
+	phy = CAL_YTP_TO_MAC(unit, port);
+	CMM_UNUSED_PARAM(mode);
+
+	if (chip_id != YT_SW_ID_920X) {
+		if (ability.asy_fc_en == 1)
+			data4 |= 1 << 11;
+		else
+			data4 &= ~(1 << 11);
+
+		if (ability.fc_en == 1)
+			data4 |= 1 << 10;
+		else
+			data4 &= ~(1 << 10);
+
+		if (ability.half_10_en == 1)
+			data4 |= 1 << 5;
+		else
+			data4 &= ~(1 << 5);
+
+		if (ability.full_10_en == 1)
+			data4 |= 1 << 6;
+		else
+			data4 &= ~(1 << 6);
+
+		if (ability.half_100_en == 1)
+			data4 |= 1 << 7;
+		else
+			data4 &= ~(1 << 7);
+
+		if (ability.full_100_en == 1)
+			data4 |= 1 << 8;
+		else
+			data4 &= ~(1 << 8);
+
+		if (ability.full_1000_en == 1)
+			data9 |= 1 << 9;
+		else
+			data9 &= ~(1 << 9);
+
+		CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x4, &data), ret);
+		data = (data & (~(0xde0))) | data4;
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x4, data), ret);
+
+		CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x9, &data), ret);
+		data = (data & (~(0x200))) | data9;
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x9, data), ret);
+
+		CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x0, &org), ret);
+		data = org | (1 << 15); /*reset*/
+		CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, data), ret);
+		if (IS_BIT_SET(org, 11)) {
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, org),
+				    ret);
+		}
+	} else {
+		if (phy < 4)
+			CMM_ERR_CHK(yt_phy_auto_neg_ability_set(unit, phy,
+								ability), ret);
+		else if (phy == 0x9)
+			CMM_ERR_CHK(yt_sds_auto_neg_ability_set(unit, phy,
+								ability), ret);
+		else
+			return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_phy_auto_neg_ability_set);
+
+u32 yt_port_phy_auto_neg_ability_get(u8 unit, u32 port,
+				     enum yt_phy_chip_mode mode,
+				     struct yt_port_an_ability *ability)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 data4 = 0;
+	u16 data9 = 0;
+	u32 phy;
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+	CMM_UNUSED_PARAM(mode);
+	yt_cal_board_chip_get(&chip_id);
+
+	if (chip_id != YT_SW_ID_920X) {
+		CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x4, &data4), ret);
+		CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x9, &data9), ret);
+
+		if ((data9 >> 9) & 0x1)
+			ability->full_1000_en = 1;
+		else
+			ability->full_1000_en = 0;
+
+		if ((data4 >> 10) & 0x1)
+			ability->fc_en = 1;
+		else
+			ability->fc_en = 0;
+
+		if ((data4 >> 11) & 0x1)
+			ability->asy_fc_en = 1;
+		else
+			ability->asy_fc_en = 0;
+
+		if ((data4 >> 8) & 0x1)
+			ability->full_100_en = 1;
+		else
+			ability->full_100_en = 0;
+
+		if ((data4 >> 7) & 0x1)
+			ability->half_100_en = 1;
+		else
+			ability->half_100_en = 0;
+
+		if ((data4 >> 6) & 0x1)
+			ability->full_10_en = 1;
+		else
+			ability->full_10_en = 0;
+
+		if ((data4 >> 5) & 0x1)
+			ability->half_10_en = 1;
+		else
+			ability->half_10_en = 0;
+	} else {
+		if (phy < 4)
+			CMM_ERR_CHK(yt_phy_auto_neg_ability_get(unit, phy,
+								ability), ret);
+		else if (phy == 0x9)
+			CMM_ERR_CHK(yt_sds_auto_neg_ability_get(unit, phy,
+								ability), ret);
+		else
+			return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_phy_auto_neg_ability_get);
+
+u32 yt_port_phy_cross_mode_set(u8 unit, u32 port,
+			       enum yt_utp_crossover_mode mode)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x10;
+		attr.reg_space = YT_PHY_REG_SPACE_TX;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		data &= ~(0x3 << 5);
+		switch (mode) {
+		case YT_UTP_CROSSOVER_MODE_MDI:
+			break;
+		case YT_UTP_CROSSOVER_MODE_MDIX:
+			data |= 0x1 << 5;
+			break;
+		case YT_UTP_CROSSOVER_MODE_RESV:
+			data |= 0x2 << 5;
+			break;
+		case YT_UTP_CROSSOVER_MODE_AUTO:
+			data |= 0x3 << 5;
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+			break;
+		}
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_cross_mode_set);
+
+u32 yt_port_phy_cross_mode_get(u8 unit, u32 port,
+			       enum yt_utp_crossover_mode *mode)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X) {
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x10;
+		attr.reg_space = YT_PHY_REG_SPACE_TX;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+		yt_port_phy_reg_get(unit, port, attr, &data);
+		*mode = ((data >> 5) & 0x3);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_phy_cross_mode_get);
+
+u32 yt_port_phy_template_set(u8 unit, u32 port,
+			     enum yt_utp_template_testmode mode)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u32 val = 0;
+	u32 phy;
+	u16 data1;
+	u32 data;
+
+	yt_cal_board_chip_get(&chip_id);
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+	if (chip_id != YT_SW_ID_920X) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, 0x80028, &data), ret);
+
+		if (CAL_YTP_TO_MAC(unit, port) == 8 ||
+		    CAL_YTP_TO_MAC(unit, port) == 9) {
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0x80028,
+							 (1 << (2 + port - 5)) |
+								 0x3), ret);
+			/*disable serdes interface*/
+			data &= 0x7c;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0x80028, data),
+				    ret);
+			if (mode == YT_UTP_TEMPLATE_TMODE_SDS2500M)
+				yt_port_extif_mode_set(unit, port,
+						       YT_EXTIF_MODE_BX2500);
+			else if (mode == YT_UTP_TEMPLATE_TMODE_SDS1000M)
+				yt_port_extif_mode_set(unit, port,
+						       YT_EXTIF_MODE_FIB_1000);
+		} else {
+			/*disable serdes interface*/
+			data &= 0x7c;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0x80028, data),
+				    ret);
+			val = (1 << (16 + port) | 0xff);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0x8002c, val),
+				    ret);
+		}
+
+		ret = yt_intphy_template_set(unit, phy, mode);
+	} else {
+		if (mode < YT_UTP_TEMPLATE_TMODE_1000M_T1) {
+			if (port < 4) {
+				attr.phy_type = PHY_INTERNAL;
+				attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+				attr.reg_addr = 0x0;
+				attr.reg_space = YT_PHY_REG_SPACE_LDS;
+				/* lds off */
+				CMM_ERR_CHK(yt_port_phy_reg_get(unit, port,
+								attr,
+								&data1), ret);
+				CLEAR_BIT(data1, 12);
+				CMM_ERR_CHK(yt_port_phy_reg_set(unit, port,
+								attr,
+								data1), ret);
+				attr.reg_addr = 0x10;
+				attr.reg_space = YT_PHY_REG_SPACE_TX;
+				CMM_ERR_CHK(yt_port_phy_reg_set(unit, port,
+								attr,
+								0x2), ret);
+				/* tx auto neg off */
+				attr.reg_addr = 0x0;
+				CMM_ERR_CHK(yt_port_phy_reg_set(unit, port,
+								attr,
+								0xa100), ret);
+			} else {
+				return CMM_ERR_INPUT;
+			}
+		} else {
+			return CMM_ERR_NOT_SUPPORT;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_template_set);
+
+static u32 yt_port_tx_phy_loopback_set(u8 unit, u32 port,
+				       enum yt_phy_loopback_mode mode)
+{
+	struct yt_phy_reg_attr attr;
+	enum cmm_err ret;
+	u16 data;
+
+	switch (mode) {
+	case YT_PHY_LOOPBACK_MODE_INTERNAL:
+		/* enable internal loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x0;
+		attr.reg_space = YT_PHY_REG_SPACE_TX;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		SET_BIT(data, 14);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		/* disable external and remote loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x2;
+		attr.reg_space = YT_PHY_REG_SPACE_TX;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_TOP_EXT;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		CLEAR_BIT(data, 11);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		break;
+	case YT_PHY_LOOPBACK_MODE_EXTERNAL:
+		/* enable external loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x0;
+		attr.reg_space = YT_PHY_REG_SPACE_LDS;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		CLEAR_BIT(data, 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		/* disable internal and remote loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x2;
+		attr.reg_space = YT_PHY_REG_SPACE_TX;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_TOP_EXT;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		CLEAR_BIT(data, 11);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		CMM_ERR_CHK(yt_851x_restart(unit, port), ret);
+		break;
+	case YT_PHY_LOOPBACK_MODE_REMOTE:
+		/* enable remote loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x2;
+		attr.reg_space = YT_PHY_REG_SPACE_TX;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_TOP_EXT;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		SET_BIT(data, 11);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		/* disable internal and remote loopback */
+		CMM_ERR_CHK(yt_851x_restart(unit, port), ret);
+		break;
+	case YT_PHY_LOOPBACK_MODE_DISABLE:
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x2;
+		attr.reg_space = YT_PHY_REG_SPACE_TX;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_TOP_EXT;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		CLEAR_BIT(data, 11);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		CMM_ERR_CHK(yt_851x_restart(unit, port), ret);
+		break;
+	default:
+		ret = CMM_ERR_NOT_SUPPORT;
+		break;
+	}
+
+	return ret;
+}
+
+static u32 yt_port_t1_phy_loopback_set(u8 unit, u32 port,
+				       enum yt_phy_loopback_mode mode)
+{
+	struct yt_phy_reg_attr attr;
+	enum cmm_err ret;
+	u16 data;
+
+	switch (mode) {
+	case YT_PHY_LOOPBACK_MODE_T1_INTERNAL:
+		/* enable internal loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x0;
+		attr.reg_space = YT_PHY_REG_SPACE_T1;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		SET_BIT(data, 14);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		/* disable external and remote loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x50;
+		attr.reg_space = YT_PHY_REG_SPACE_T1;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		CLEAR_BIT(data, 11);
+		CLEAR_BIT(data, 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		break;
+	case YT_PHY_LOOPBACK_MODE_T1_EXTERNAL:
+		/* enable external loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x0;
+		attr.reg_space = YT_PHY_REG_SPACE_LDS;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		CLEAR_BIT(data, 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x50;
+		attr.reg_space = YT_PHY_REG_SPACE_T1;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		SET_BIT(data, 12);
+		/* disable internal and remote loopback */
+		CLEAR_BIT(data, 11);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		CMM_ERR_CHK(yt_851x_restart(unit, port), ret);
+		break;
+	case YT_PHY_LOOPBACK_MODE_T1_REMOTE:
+		/* enable remote loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x50;
+		attr.reg_space = YT_PHY_REG_SPACE_T1;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		SET_BIT(data, 11);
+		/* disable internal and external loopback */
+		CLEAR_BIT(data, 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		CMM_ERR_CHK(yt_851x_restart(unit, port), ret);
+		break;
+	case YT_PHY_LOOPBACK_MODE_DISABLE:
+		/* disable all loopback */
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x50;
+		attr.reg_space = YT_PHY_REG_SPACE_T1;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		CLEAR_BIT(data, 11);
+		CLEAR_BIT(data, 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		CMM_ERR_CHK(yt_851x_restart(unit, port), ret);
+		break;
+	default:
+		ret = CMM_ERR_NOT_SUPPORT;
+		break;
+	}
+
+	return ret;
+}
+
+static u32 intphy_loopback_set(u8 unit, u32 port,
+			       enum yt_phy_loopback_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 phy;
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+	if (port >= 4)
+		return CMM_ERR_INPUT;
+	switch (mode) {
+	case YT_PHY_LOOPBACK_MODE_INTERNAL:
+	case YT_PHY_LOOPBACK_MODE_EXTERNAL:
+	case YT_PHY_LOOPBACK_MODE_REMOTE:
+		CMM_ERR_CHK(yt_port_t1_phy_loopback_set
+					(unit, port,
+					YT_PHY_LOOPBACK_MODE_DISABLE), ret);
+		CMM_ERR_CHK(yt_port_tx_phy_loopback_set(unit, port, mode), ret);
+		break;
+	case YT_PHY_LOOPBACK_MODE_T1_INTERNAL:
+	case YT_PHY_LOOPBACK_MODE_T1_EXTERNAL:
+	case YT_PHY_LOOPBACK_MODE_T1_REMOTE:
+		CMM_ERR_CHK(yt_port_tx_phy_loopback_set
+					(unit, port,
+					YT_PHY_LOOPBACK_MODE_DISABLE), ret);
+		CMM_ERR_CHK(yt_port_t1_phy_loopback_set(unit, port, mode), ret);
+		break;
+	case YT_PHY_LOOPBACK_MODE_DISABLE:
+		CMM_ERR_CHK(yt_port_tx_phy_loopback_set
+					(unit, port,
+					YT_PHY_LOOPBACK_MODE_DISABLE), ret);
+		CMM_ERR_CHK(yt_port_t1_phy_loopback_set
+					(unit, port,
+					YT_PHY_LOOPBACK_MODE_DISABLE), ret);
+		/* reset */
+		CMM_ERR_CHK(yt_861x_restart(unit, phy), ret);
+		break;
+	default:
+		return CMM_ERR_INPUT;
+	}
+
+	return ret;
+}
+
+u32 yt_port_phy_loopback_set(u8 unit, u32 port, enum yt_phy_chip_mode phy_mode,
+			     enum yt_phy_loopback_mode mode)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 phy;
+	u16 data;
+
+	yt_cal_board_chip_get(&chip_id);
+	phy = CAL_YTP_TO_MAC(unit, port);
+	CMM_UNUSED_PARAM(phy_mode);
+
+	if (chip_id != YT_SW_ID_920X) {
+		switch (mode) {
+		case YT_PHY_LOOPBACK_MODE_INTERNAL:
+			CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x0, &data),
+				    ret);
+			SET_BIT(data, 14);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, data),
+				    ret);
+			break;
+		case YT_PHY_LOOPBACK_MODE_EXTERNAL:
+			/* disable sleep */
+			CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x27, &data),
+				    ret);
+			CLEAR_BIT(data, 15);
+			CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x27, data),
+				    ret);
+
+			/* enable external loopback */
+			CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0xa, &data),
+				    ret);
+			SET_BIT(data, 4);
+			CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, data),
+				    ret);
+
+			/* reset */
+			CMM_ERR_CHK(yt_861x_restart(unit, phy), ret);
+			break;
+		case YT_PHY_LOOPBACK_MODE_REMOTE:
+			CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0xa, &data),
+				    ret);
+			SET_BIT(data, 5);
+			CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, data),
+				    ret);
+			break;
+		case YT_PHY_LOOPBACK_MODE_DISABLE:
+			/* disable internal loopback */
+			CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x0, &data),
+				    ret);
+			CLEAR_BIT(data, 14);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x0, data),
+				    ret);
+
+			/* disable external loopback */
+			CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0x27, &data),
+				    ret);
+			SET_BIT(data, 15);
+			CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0x27, data),
+				    ret);
+
+			CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0xa, &data),
+				    ret);
+			CLEAR_BIT(data, 4);
+			CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, data),
+				    ret);
+
+			/* disable remote loopback */
+			CMM_ERR_CHK(intif_ext_reg_read(unit, phy, 0xa, &data),
+				    ret);
+			CLEAR_BIT(data, 5);
+			CMM_ERR_CHK(intif_ext_reg_write(unit, phy, 0xa, data),
+				    ret);
+
+			/* reset */
+			CMM_ERR_CHK(yt_861x_restart(unit, phy), ret);
+			break;
+		default:
+			return CMM_ERR_INPUT;
+		}
+	} else {
+		CMM_ERR_CHK(intphy_loopback_set(unit, port, mode), ret);
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_phy_loopback_set);
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+
+static u32 yt_extphy_addr_get(u8 unit, int port, enum yt_phy_reg_type reg_type,
+			      u32 *phy_addr)
+{
+	u32 base_addr = 0;
+	u32 tmp = 0;
+	u32 relative_addr;
+	enum cmm_err ret = 0;
+
+	tmp = CAL_YTP_TO_MAC(unit, port);
+	if (tmp < base_addr)
+		*phy_addr = tmp;
+
+	if (reg_type == YT_PHY_REG_TYPE_TOP_EXT)
+		*phy_addr = base_addr + 4;
+
+	relative_addr = tmp - base_addr;
+	switch (reg_type) {
+	case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		if (relative_addr <= 1)
+			*phy_addr = base_addr;
+		else if (relative_addr <= 3)
+			*phy_addr = base_addr + 2;
+		break;
+	case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+	case YT_PHY_REG_TYPE_USXG_EXT:
+		*phy_addr = base_addr;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+static u32 yt_intphy_addr_get(u8 unit, int port, enum yt_phy_reg_type reg_type,
+			      u32 *phy_addr)
+{
+	int tmp = 0;
+
+	if (port >= 4 && port <= 7) {
+		switch (reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+			tmp = 9;
+			break;
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+			if (port <= 5 && port >= 4)
+				tmp = 4;
+			else if (port <= 7 && port >= 6)
+				tmp = 6;
+			break;
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_SG_EXT:
+			break;
+		default:
+			tmp = port;
+			break;
+		}
+	} else {
+		if (port == 8) {
+			switch (reg_type) {
+			case YT_PHY_REG_TYPE_TOP_EXT:
+				tmp = 9;
+				break;
+			case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+			case YT_PHY_REG_TYPE_USXG_EXT:
+			case YT_PHY_REG_TYPE_USXG_MII:
+			case YT_PHY_REG_TYPE_SG_MII:
+			case YT_PHY_REG_TYPE_SG_EXT:
+				tmp = 8;
+				break;
+			default:
+				tmp = port;
+				break;
+			}
+			return tmp;
+		}
+		switch (reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+			tmp = 9;
+			break;
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+		case YT_PHY_REG_TYPE_USXG_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_SG_EXT:
+			tmp = 0;
+			break;
+		default:
+			tmp = port;
+			break;
+		}
+	}
+	*phy_addr = tmp;
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_edata_read(u8 unit, u16 addr, u8 *data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 op_data = 0;
+	u32 wait_count = 0;
+
+	op_data = 0x05 | ((addr << 12) & 0x1FF000);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, E_OP_CTRL_0, op_data), ret);
+	while (wait_count <= MAX_BUSYING_WAIT_TIME) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, E_OP_CTRL_1, &op_data),
+			    ret);
+
+		if ((op_data & 0x0F) == 0x03) {
+			*data = (op_data >> 24) & 0xFF;
+			return CMM_ERR_OK;
+		}
+		wait_count++;
+	}
+
+	return CMM_ERR_BUSYING_TIME;
+}
+
+u32 yt_port_init(u8 unit)
+{
+	struct yt_phy_reg_attr attr;
+	u8 efuse_data1 = 0;
+	u8 efuse_data2 = 0;
+	u16 data = 0;
+	u8 tmp = 0;
+	u8 phy;
+	u32 ret;
+
+	/* int phy */
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+	for (phy = 4; phy < 8; phy++) {
+		/* phy write protect */
+		attr.reg_addr = 0x1;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x3), ret);
+		/* PLL adjust */
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+		attr.reg_addr = 0x0;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x1900), ret);
+		attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+		attr.reg_addr = 0xa20e;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xcba), ret);
+		attr.reg_addr = 0xa20a;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xc3f1), ret);
+		attr.reg_addr = 0xa20c;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x1620), ret);
+		attr.reg_addr = 0xa2b6;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xa00), ret);
+		attr.reg_addr = 0xa2b6;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xe00), ret);
+		/* Nibble */
+		attr.reg_addr = 0xa003;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x3), ret);
+		/* idle error detect enable */
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+		attr.reg_addr = 0x3d0;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x5210), ret);
+		/* 2.5G utp */
+		attr.reg_addr = 0x372;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x5038), ret);
+		attr.reg_addr = 0x37c;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x6068), ret);
+		attr.reg_addr = 0x388;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xa0), ret);
+		/* fast retrain */
+		attr.reg_addr = 0x359;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x2140), ret);
+		/* 2.5G template */
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_TOP_EXT;
+		attr.reg_addr = 0xa2fa;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x83), ret);
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+		attr.reg_addr = 0x4e2;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x149), ret);
+		/* 2.5G template */
+		attr.reg_addr = 0x47e;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x3939), ret);
+		attr.reg_addr = 0x47f;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x3939), ret);
+		attr.reg_addr = 0x480;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x3939), ret);
+		attr.reg_addr = 0x481;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x3939), ret);
+		/* 1000 cable length threshold */
+		attr.reg_addr = 0x336;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xab0a), ret);
+		attr.reg_addr = 0x340;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x301d), ret);
+		/* 1000 performance */
+		attr.reg_addr = 0x34a;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xff03), ret);
+		attr.reg_addr = 0xf8;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xb3ff), ret);
+		attr.reg_addr = 0x32c;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x5094), ret);
+		attr.reg_addr = 0x32d;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xd094), ret);
+		attr.reg_addr = 0x32e;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x5308), ret);
+		attr.reg_addr = 0x322;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x6440), ret);
+		attr.reg_addr = 0x4d3;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x5220), ret);
+		attr.reg_addr = 0x4d2;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x5220), ret);
+		/* 100 template */
+		attr.reg_addr = 0x46e;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x4545), ret);
+		attr.reg_addr = 0x46f;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x4545), ret);
+		attr.reg_addr = 0x470;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x4545), ret);
+		attr.reg_addr = 0x471;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x4545), ret);
+		/* 100 cable length threshold */
+		attr.reg_addr = 0x30b;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xaa1d), ret);
+		attr.reg_addr = 0x71f;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x36), ret);
+		/* 10 template */
+		attr.reg_addr = 0x46b;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x1818), ret);
+		attr.reg_addr = 0x46c;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x1818), ret);
+		/* 10 template MAU*/
+		attr.reg_addr = 0x466;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x6c6c), ret);
+		attr.reg_addr = 0x467;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x6c6c), ret);
+		attr.reg_addr = 0x468;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x6c6c), ret);
+		attr.reg_addr = 0x469;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x6c6c), ret);
+		/* EMC CS */
+		attr.reg_addr = 0xc8;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xfff), ret);
+		attr.reg_addr = 0xbe;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x6406), ret);
+		attr.reg_addr = 0x37a;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x40ff), ret);
+		/* EMC RE*/
+		attr.reg_addr = 0x482;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xffff), ret);
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_TOP_EXT;
+		attr.reg_addr = 0x482;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xffff), ret);
+		attr.reg_addr = 0xa2d5;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x1f1f), ret);
+		attr.reg_addr = 0xa2d6;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x1f1f), ret);
+		attr.reg_addr = 0xa2d7;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x1f1f), ret);
+		attr.reg_addr = 0xa2d8;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x1f1f), ret);
+		attr.reg_addr = 0xa218;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x6e), ret);
+		attr.reg_addr = 0xa01d;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xfff0), ret);
+		attr.reg_addr = 0xa01e;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xfff0), ret);
+		attr.reg_addr = 0xa01d;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xffff), ret);
+		attr.reg_addr = 0xa01e;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0xffff), ret);
+		/* mac clk */
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+		attr.reg_addr = 0xc;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x41a1), ret);
+		/* rst */
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+		attr.reg_addr = 0x84;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x2000), ret);
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+		attr.reg_addr = 0x0;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x9140), ret);
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+		attr.reg_addr = 0x84;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, 0x0), ret);
+	}
+
+	/* int sds 0 and sds1 */
+	phy = 0;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_edata_read(unit, 7, &efuse_data1), ret);
+	CMM_ERR_CHK(yt_edata_read(unit, 93, &efuse_data2), ret);
+	if (IS_BIT_SET(efuse_data2, 3)) {
+		CMM_ERR_CHK(yt_edata_read(unit, 31, &efuse_data1), ret);
+		tmp = (efuse_data1 >> 5) & 0x7;
+		attr.reg_addr = 0x4b4;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x7 << 8))) | (tmp << 8);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+		tmp = (efuse_data1 >> 2) & 0x7;
+		attr.reg_addr = 0x4b5;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x7 << 4))) | (tmp << 4);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+		CMM_ERR_CHK(yt_edata_read(unit, 33, &efuse_data1), ret);
+		tmp = efuse_data1 & 0x3;
+		attr.reg_addr = 0x42b;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x3 << 12))) | (tmp << 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+		CMM_ERR_CHK(yt_edata_read(unit, 93, &efuse_data1), ret);
+		tmp = (efuse_data1 >> 6) & 0x3;
+		attr.reg_addr = 0x429;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x3 << 12))) | (tmp << 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+		tmp = (efuse_data1 >> 4) & 0x3;
+		attr.reg_addr = 0x441;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x3))) | (tmp);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+	} else {
+		if (IS_BIT_SET(efuse_data1, 7)) {
+			CMM_ERR_CHK(yt_edata_read(unit, 17, &efuse_data1), ret);
+			tmp = (efuse_data1) & 0x1;
+			attr.reg_addr = 0x4b4;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x1 << 10))) | (tmp << 10);
+			CMM_ERR_CHK(yt_edata_read(unit, 16, &efuse_data1), ret);
+			tmp = (efuse_data1 >> 6) & 0x3;
+			data = (data & (~(0x3 << 8))) | (tmp << 8);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			tmp = (efuse_data1 >> 3) & 0x7;
+			attr.reg_addr = 0x4b5;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x7 << 4))) | (tmp << 4);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			tmp = (efuse_data1 >> 1) & 0x3;
+			attr.reg_addr = 0x42b;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x3 << 12))) | (tmp << 12);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			tmp = (efuse_data1) & 0x1;
+			attr.reg_addr = 0x429;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x1 << 13))) | (tmp << 13);
+			CMM_ERR_CHK(yt_edata_read(unit, 29, &efuse_data1), ret);
+			tmp = (efuse_data1 >> 6) & 0x1;
+			data = (data & (~(0x1 << 12))) | (tmp << 12);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			tmp = (efuse_data1 >> 7) & 0x1;
+			attr.reg_addr = 0x441;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x1))) | (tmp);
+			CMM_ERR_CHK(yt_edata_read(unit, 30, &efuse_data1), ret);
+			tmp = (efuse_data1) & 0x1;
+			data = (data & (~(0x1 << 1))) | (tmp << 1);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+		} else {
+			attr.reg_addr = 0x4b4;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data >> 8) & 0x7) + 0x1)) << 8) |
+			       (data & (~(0x7 << 8)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			attr.reg_addr = 0x4b5;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data >> 4) & 0x7) + 0x1)) << 4) |
+			       (data & (~(0x7 << 4)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			attr.reg_addr = 0x42b;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data >> 12) & 0x3) + 0x1)) << 12) |
+			       (data & (~(0x3 << 12)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			attr.reg_addr = 0x429;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data >> 12) & 0x3) + 0x1)) << 12) |
+			       (data & (~(0x3 << 12)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			attr.reg_addr = 0x441;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data) & 0x3) + 0x1))) | (data & (~(0x3)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+		}
+	}
+
+	phy = 8;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_edata_read(unit, 7, &efuse_data1), ret);
+	CMM_ERR_CHK(yt_edata_read(unit, 93, &efuse_data2), ret);
+	if (IS_BIT_SET(efuse_data2, 3)) {
+		CMM_ERR_CHK(yt_edata_read(unit, 425, &efuse_data1), ret);
+		tmp = (efuse_data1 >> 5) & 0x7;
+		attr.reg_addr = 0x4b4;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x7 << 8))) | (tmp << 8);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+		tmp = (efuse_data1 >> 2) & 0x7;
+		attr.reg_addr = 0x4b5;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x7 << 4))) | (tmp << 4);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+		CMM_ERR_CHK(yt_edata_read(unit, 424, &efuse_data1), ret);
+		tmp = (efuse_data1 >> 0x6) & 0x3;
+		attr.reg_addr = 0x42b;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x3 << 12))) | (tmp << 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+
+		tmp = (efuse_data1 >> 2) & 0x3;
+		attr.reg_addr = 0x429;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x3 << 12))) | (tmp << 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+		tmp = (efuse_data1 >> 4) & 0x3;
+		attr.reg_addr = 0x441;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data), ret);
+		data = (data & (~(0x3))) | (tmp);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data), ret);
+	} else {
+		if (IS_BIT_SET(efuse_data1, 7)) {
+			CMM_ERR_CHK(yt_edata_read(unit, 129, &efuse_data1),
+				    ret);
+			tmp = (efuse_data1 >> 0x5) & 0x7;
+			attr.reg_addr = 0x4b4;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x7 << 8))) | (tmp << 8);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			tmp = (efuse_data1 >> 0x2) & 0x7;
+			attr.reg_addr = 0x4b5;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x7 << 4))) | (tmp << 4);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			CMM_ERR_CHK(yt_edata_read(unit, 128, &efuse_data1),
+				    ret);
+			tmp = (efuse_data1 >> 0x6) & 0x3;
+			attr.reg_addr = 0x42b;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x3 << 12))) | (tmp << 12);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			tmp = (efuse_data1 >> 0x2) & 0x3;
+			attr.reg_addr = 0x429;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x3 << 12))) | (tmp << 12);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			tmp = (efuse_data1 >> 0x4) & 0x3;
+			attr.reg_addr = 0x441;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (data & (~(0x3))) | (tmp);
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+		} else {
+			attr.reg_addr = 0x4b4;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data >> 8) & 0x7) + 0x1)) << 8) |
+			       (data & (~(0x7 << 8)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			attr.reg_addr = 0x4b5;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data >> 4) & 0x7) + 0x1)) << 4) |
+			       (data & (~(0x7 << 4)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			attr.reg_addr = 0x42b;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data >> 12) & 0x3) + 0x1)) << 12) |
+			       (data & (~(0x3 << 12)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			attr.reg_addr = 0x429;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data >> 12) & 0x3) + 0x1)) << 12) |
+			       (data & (~(0x3 << 12)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+
+			attr.reg_addr = 0x441;
+			CMM_ERR_CHK(yt_port_phy_reg_get(unit, phy, attr, &data),
+				    ret);
+			data = (((((data) & 0x3) + 0x1))) | (data & (~(0x3)));
+			CMM_ERR_CHK(yt_port_phy_reg_set(unit, phy, attr, data),
+				    ret);
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_init);
+
+u32 yt_port_mac_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	enum yt_enable ori;
+	u32 id;
+	u32 ctrl;
+	u16 data;
+	u32 reg;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	/* if no change, do nothing */
+	CMM_ERR_CHK(yt_port_mac_enable_get(unit, port, &ori), ret);
+	if (ori == enable)
+		return CMM_ERR_OK;
+
+	reg = CHIP_PORT_CTRL + id * 0x4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &ctrl), ret);
+	HAL_FIELD_SET(9, 1, &ctrl, enable);
+	HAL_FIELD_SET(3, 1, &ctrl, enable);
+	HAL_FIELD_SET(4, 1, &ctrl, enable);
+	HAL_FIELD_SET(10, 1, &ctrl, enable);
+	if (enable == YT_DISABLE)
+		HAL_FIELD_SET(14, 1, &ctrl, YT_ENABLE);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, ctrl), ret);
+
+	/* softreset */
+	if (ori == YT_DISABLE && enable == YT_ENABLE) {
+		attr.phy_type = PHY_INTERNAL;
+		attr.reg_addr = 0x0;
+		attr.reg_space = YT_PHY_REG_SPACE_T1;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		SET_BIT(data, 15);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_mac_enable_set);
+
+u32 yt_port_mac_enable_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 id;
+	u32 ctrl;
+	u32 reg;
+	u32 an;
+	u32 tx;
+	u32 rx;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	reg = CHIP_PORT_CTRL + id * 0x4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &ctrl), ret);
+	HAL_FIELD_GET(3, 1, &ctrl, &tx);
+	HAL_FIELD_GET(4, 1, &ctrl, &rx);
+	HAL_FIELD_GET(10, 1, &ctrl, &an);
+
+	if ((an || tx || rx))
+		*enable = YT_ENABLE;
+	else
+		*enable = YT_DISABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_mac_enable_get);
+
+u32 yt_port_mac_force_set(u8 unit, u32 port, struct yt_port_force_ctrl ctrl)
+{
+	enum yt_port_speed_mode speed_mode = PORT_SPEED_MODE_1000M;
+	enum yt_port_duplex duplex = PORT_DUPLEX_FULL;
+	enum yt_port_speed speed = PORT_SPEED_1000M;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry;
+	u32 id;
+	u32 reg;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	ret = yt_port_speed_dup_split(ctrl.speed_dup, &speed, &duplex);
+	if (ret != CMM_ERR_OK)
+		return ret;
+
+	if (speed == PORT_SPEED_2500M) /*b100 for mac and sds 2.5g*/
+		speed_mode = PORT_SPEED_MODE_2500M;
+	else
+		speed_mode = (enum yt_port_speed_mode)speed;
+
+	reg = CHIP_PORT_CTRL + id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &entry), ret);
+	HAL_FIELD_SET(10, 1, &entry, YT_DISABLE);
+	HAL_FIELD_SET(11, 1, &entry, YT_DISABLE);
+	HAL_FIELD_SET(6, 1, &entry, ctrl.rx_fc_en);
+	HAL_FIELD_SET(5, 1, &entry, ctrl.tx_fc_en);
+	HAL_FIELD_SET(0, 3, &entry, speed_mode);
+	HAL_FIELD_SET(7, 1, &entry, duplex);
+	HAL_FIELD_SET(9, 1, &entry, YT_ENABLE);
+	HAL_FIELD_SET(14, 1, &entry, YT_ENABLE);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_mac_force_set);
+
+u32 yt_port_mac_force_get(u8 unit, u32 port, struct yt_port_force_ctrl *ctrl)
+{
+	enum yt_port_speed_mode speed_mode = PORT_SPEED_MODE_1000M;
+	enum yt_port_duplex duplex = PORT_DUPLEX_FULL;
+	enum yt_port_speed speed = PORT_SPEED_1000M;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry;
+	u32 id;
+	u32 reg;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	reg = CHIP_PORT_CTRL + id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &entry), ret);
+	HAL_FIELD_GET(0, 3, &entry, &speed_mode);
+	HAL_FIELD_GET(7, 1, &entry, &duplex);
+	HAL_FIELD_GET(6, 1, &entry, &ctrl->rx_fc_en);
+	HAL_FIELD_GET(5, 1, &entry, &ctrl->tx_fc_en);
+
+	/*b100 for mac and sds 2.5g*/
+	if (speed_mode == PORT_SPEED_MODE_2500M)
+		speed = PORT_SPEED_2500M;
+	else
+		speed = (enum yt_port_speed)speed_mode;
+
+	return yt_port_speed_dup_combine(speed, duplex, &ctrl->speed_dup);
+}
+EXPORT_SYMBOL(yt_port_mac_force_get);
+
+u32 yt_port_mac_eee_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 eee_enable;
+	u32 id;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, 0xb0000, &eee_enable), ret);
+	if (enable == YT_ENABLE)
+		SET_BIT(eee_enable, id);
+	else
+		CLEAR_BIT(eee_enable, id);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, 0xb0000, eee_enable), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_mac_eee_enable_set);
+
+static u32 yt_intsds_patch_configuration(u8 unit, u32 port,
+					 enum yt_extif_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_TOP_EXT;
+
+	attr.reg_addr = 0x4be;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xd), ret);
+	/* CDR */
+	if (mode == YT_EXTIF_MODE_FIB_100) {
+		attr.reg_addr = 0x406;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x0), ret);
+		attr.reg_addr = 0x416;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x3458), ret);
+	} else {
+		attr.reg_addr = 0x406;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x800), ret);
+		attr.reg_addr = 0x416;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x4558), ret);
+	}
+	/* PLL */
+	if (mode == YT_EXTIF_MODE_10GQXGMII ||
+	    mode == YT_EXTIF_MODE_USXGMII_10G) {
+		attr.reg_addr = 0x43a;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x1006), ret);
+		attr.reg_addr = 0x43f;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x3029), ret);
+		attr.reg_addr = 0x42a;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xf070), ret);
+	} else {
+		attr.reg_addr = 0x43d;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x207d), ret);
+		attr.reg_addr = 0x43c;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x207d), ret);
+		attr.reg_addr = 0x43f;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x3032), ret);
+		attr.reg_addr = 0x43a;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x6), ret);
+		attr.reg_addr = 0x42a;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xf070), ret);
+	}
+	/*VCO */
+	attr.reg_addr = 0x439;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xC0), ret);
+	/* Vdac */
+	attr.reg_addr = 0x492;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7f7f), ret);
+	attr.reg_addr = 0x491;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7f), ret);
+	/* Eye */
+	attr.reg_addr = 0x454;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xf14), ret);
+	attr.reg_addr = 0x497;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xa44), ret);
+	attr.reg_addr = 0x4cd;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x0), ret);
+
+	attr.reg_addr = 0x4af;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x45e3), ret);
+	attr.reg_addr = 0x48a;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xfff), ret);
+	attr.reg_addr = 0x408;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7c00), ret);
+	attr.reg_addr = 0x4d6;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7f), ret);
+	attr.reg_addr = 0x44f;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xff08), ret);
+	/* FFE */
+	attr.reg_addr = 0x48e;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7d00), ret);
+	attr.reg_addr = 0xd;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x60f), ret);
+	/* CTLE */
+	attr.reg_addr = 0x4b0;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x804), ret);
+	attr.reg_addr = 0x4b1;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7774), ret);
+	attr.reg_addr = 0x4af;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x45e7), ret);
+	/* RST double check delay*/
+	attr.reg_addr = 0x3;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x5603), ret);
+	msleep(20);
+	attr.reg_addr = 0x492;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7fff), ret);
+	attr.reg_addr = 0x492;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7f7f), ret);
+	attr.reg_addr = 0x2000;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x40), ret);
+	attr.reg_addr = 0x2000;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x0), ret);
+	attr.reg_addr = 0x1000;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x1721), ret);
+	attr.reg_addr = 0x1000;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x1720), ret);
+	if (mode == YT_EXTIF_MODE_SG_MAC || mode == YT_EXTIF_MODE_SG_PHY) {
+		attr.reg_addr = 0x1042;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x48c), ret);
+	}
+
+	/* soft reset */
+	attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+	attr.reg_addr = 0x0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	SET_BIT(data, 15);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+	attr.reg_addr = 0x0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	SET_BIT(data, 15);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+static u32 yt_intsds_mode_configuration(u8 unit, u32 port,
+					enum yt_extif_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data &= (~(0x7 << 4));
+	switch (mode) {
+	case YT_EXTIF_MODE_SG_MAC:
+	case YT_EXTIF_MODE_SG_PHY:
+	case YT_EXTIF_MODE_FIB_1000:
+	case YT_EXTIF_MODE_FIB_100:
+	case YT_EXTIF_MODE_BX2500:
+		data |= (mode - YT_EXTIF_MODE_SG_MAC) << 0x4;
+		break;
+	case YT_EXTIF_MODE_USXGMII_10G:
+		/* sds0 sds1 support US_SP_10G */
+		data |= (0x6 << 4);
+		break;
+	case YT_EXTIF_MODE_10GQXGMII:
+		/* sds1 support US_MP_10G */
+		data |= (0x7 << 4);
+		break;
+	default:
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+u32 yt_port_extif_mode_set(u8 unit, u32 port, enum yt_extif_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+
+	if (port == 0 || port == 8) {
+		/* sds patch before mode configuration */
+		CMM_ERR_CHK(yt_intsds_patch_configuration(unit, port, mode),
+			    ret);
+		CMM_ERR_CHK(yt_intsds_mode_configuration(unit, port, mode),
+			    ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_extif_mode_set);
+
+u32 yt_port_extif_mode_get(u8 unit, u32 port, enum yt_extif_mode *mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 0x4) & 0xF;
+	if (data <= 0x4)
+		*mode = data + YT_EXTIF_MODE_SG_MAC;
+	else if (data == 0x6)
+		*mode = YT_EXTIF_MODE_USXGMII_10G;
+	else if (data == 0x7)
+		*mode = YT_EXTIF_MODE_10GQXGMII;
+	else
+		return CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_extif_mode_get);
+
+u32 yt_port_jumbo_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data1 = 0;
+	u32 data = 0;
+	u32 id = 0;
+	u32 reg;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_JUMBO_CTRL, &data1), ret);
+	if (id == 8 || id == 0) {
+		/* xgmac */
+		if (id == 0) {
+			reg = CHIP_XGMAC_WATCHDOG_CTRL(0);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		} else {
+			reg = CHIP_XGMAC_WATCHDOG_CTRL(1);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		}
+	} else {
+		/* gmac */
+		reg = CHIP_GMAC_GLOBAL1;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+	}
+	if (enable == YT_ENABLE) {
+		data1 = CHIP_JUMBO_DEFAULT_SIZE;
+		if (id == 8 || id == 0) {
+			data &= ~(0x1F);
+			data |= 0x17; /* bit4 1,bit0:3 */
+		} else {
+			data &= ~(0x3FFF << 18);
+			data |= (CHIP_JUMBO_DEFAULT_SIZE << 18);
+		}
+	} else {
+		data1 = CHIP_JUMBO_SIZE_MIN;
+		if (id == 8 || id == 0) {
+			data &= ~(0x1F);
+			data |= 0x10; /* bit4 1,bit0:3 */
+		} else {
+			data &= ~(0x3FFF << 18);
+			data |= (CHIP_JUMBO_SIZE_MIN << 18);
+		}
+	}
+	if (id == 8 || id == 0) {
+		/* xgmac */
+		if (id == 0) {
+			reg = CHIP_XGMAC_WATCHDOG_CTRL(0);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+		} else {
+			reg = CHIP_XGMAC_WATCHDOG_CTRL(1);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+		}
+	} else {
+		/* gmac */
+		reg = CHIP_GMAC_GLOBAL1;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+	}
+	reg = CHIP_JUMBO_CTRL;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data1), ret);
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_jumbo_enable_set);
+
+u32 yt_port_jumbo_enable_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 id = 0;
+	u32 data = 0;
+	u32 reg;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	if (id == 0) {
+		reg = CHIP_XGMAC_WATCHDOG_CTRL(0);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		if ((data & 0x1F) == 0x10)
+			*enable = YT_DISABLE;
+		else
+			*enable = YT_ENABLE;
+
+	} else if (id == 8) {
+		reg = CHIP_XGMAC_WATCHDOG_CTRL(1);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		if ((data & 0x1F) == 0x10)
+			*enable = YT_DISABLE;
+		else
+			*enable = YT_ENABLE;
+	} else {
+		reg = CHIP_GMAC_GLOBAL1;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		data = (data >> 18) & 0x3FFF;
+		*enable = (data != CHIP_JUMBO_SIZE_MIN) ? YT_ENABLE :
+							  YT_DISABLE;
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_jumbo_enable_get);
+
+u32 yt_port_jumbo_size_set(u8 unit, u32 port, u32 size)
+{
+	enum yt_enable enable = YT_ENABLE;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data1 = 0;
+	u32 data = 0;
+	u32 id = 0;
+	u32 reg;
+
+	if (size < CHIP_JUMBO_SIZE_MIN || (size % 1024) != 0) {
+		/* size less than 2000, not a multiple of 1024 */
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(yt_port_jumbo_enable_get(unit, port, &enable), ret);
+	if (enable == YT_ENABLE) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, CHIP_JUMBO_CTRL, &data1),
+			    ret);
+		if (id == 0) {
+			reg = CHIP_XGMAC_WATCHDOG_CTRL(0);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		} else if (id == 0x8) {
+			reg = CHIP_XGMAC_WATCHDOG_CTRL(1);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		} else {
+			reg = CHIP_GMAC_GLOBAL1;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		}
+
+		if (id == 0 || id == 8) {
+			data &= ~(0x1F);
+			SET_BIT(data, 4);
+			data |= ((size / 1024) - 2);
+		} else {
+			data &= ~(0x3FFF << 18);
+			data |= (size << 18);
+		}
+
+		if (id == 0) {
+			reg = CHIP_XGMAC_WATCHDOG_CTRL(0);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+		} else if (id == 8) {
+			reg = CHIP_XGMAC_WATCHDOG_CTRL(1);
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+		} else {
+			reg = CHIP_GMAC_GLOBAL1;
+			CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data), ret);
+		}
+
+		data1 = size;
+		reg = CHIP_JUMBO_CTRL;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, reg, data1), ret);
+	} else {
+		return CMM_ERR_FORBIDDEN;
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_jumbo_size_set);
+
+u32 yt_port_jumbo_size_get(u8 unit, u32 port, u32 *size)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data = 0;
+	u32 id = 0;
+	u32 reg;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	if (id == 0) {
+		reg = CHIP_XGMAC_WATCHDOG_CTRL(0);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		*size = ((data & 0xF) + 2) * 1024;
+	} else if (id == 8) {
+		reg = CHIP_XGMAC_WATCHDOG_CTRL(1);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		*size = ((data & 0xF) + 2) * 1024;
+	} else {
+		reg = CHIP_GMAC_GLOBAL1;
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &data), ret);
+		*size = ((data & 0xFFFC0000) >> 18);
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_jumbo_size_get);
+
+u32 yt_port_link_status_all_get(u8 unit, u32 port,
+				struct yt_port_link_status_all *all_link_status)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 id = 0;
+	u32 status;
+	u32 reg;
+
+	id = CAL_YTP_TO_MAC(unit, port);
+	reg = PORT_STATUS + id * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg, &status), ret);
+	HAL_FIELD_GET(8, 1, &status, &all_link_status->link_status);
+	HAL_FIELD_GET(0, 3, &status, &all_link_status->link_speed);
+	HAL_FIELD_GET(7, 1, &status, &all_link_status->link_duplex);
+	HAL_FIELD_GET(6, 1, &status, &all_link_status->rx_fc_en);
+	HAL_FIELD_GET(5, 1, &status, &all_link_status->tx_fc_en);
+
+	if ((enum yt_port_speed_mode)all_link_status->link_speed ==
+	    PORT_SPEED_MODE_2500M) {
+		all_link_status->link_speed = PORT_SPEED_2500M;
+	} else if ((enum yt_port_speed_mode)all_link_status->link_speed ==
+		   PORT_SPEED_MODE_5G) {
+		all_link_status->link_speed = PORT_SPEED_5G;
+	} else if ((enum yt_port_speed_mode)all_link_status->link_speed ==
+		   PORT_SPEED_MODE_10G) {
+		all_link_status->link_speed = PORT_SPEED_10G;
+	} else {
+		/* do nothing */
+	}
+
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_port_link_status_all_get);
+
+static u32 yt_port_intphy_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 mode = 0;
+	u16 data = 0;
+
+	/* WA for softreset, power down, power up */
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x84;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &mode), ret);
+	data = mode;
+	data &= (~(0x3F << 10));
+	data |= (1 << 13);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	attr.reg_addr = 0x0;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	SET_BIT(data, 10);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	if (enable == YT_ENABLE) {
+		data &= 0xf7ff;
+		data |= 0x8000;
+	} else {
+		data |= 0x800;
+	}
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	attr.reg_addr = 0x84;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, mode), ret);
+
+	attr.reg_addr = 0x0;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	CLEAR_BIT(data, 10);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+static u32 yt_port_intsds_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data > 0x4)
+		/* usxgmii space */
+		attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+	else
+		attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+
+	attr.reg_addr = 0x0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	if (enable == YT_ENABLE) {
+		CLEAR_BIT(data, 11);
+		SET_BIT(data, 15);
+	} else {
+		SET_BIT(data, 11);
+	}
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+u32 yt_port_phy_enable_set(u8 unit, u32 port, enum yt_phy_chip_mode phy_mode,
+			   enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_enable_set(unit, port, enable), ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_enable_set(unit, port, enable), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_enable_set);
+
+static u32 yt_port_intphy_enable_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	/* WA for softreset, power down, power up */
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	*enable = ((data >> 11) & 0x1) ? YT_DISABLE : YT_ENABLE;
+
+	return ret;
+}
+
+static u32 yt_port_intsds_enable_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data > 0x4)
+		/* usxgmii space */
+		attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+	else
+		attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+
+	attr.reg_addr = 0x0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	*enable = ((data >> 11) & 0x1) ? YT_DISABLE : YT_ENABLE;
+
+	return ret;
+}
+
+u32 yt_port_phy_enable_get(u8 unit, u32 port, enum yt_phy_chip_mode phy_mode,
+			   enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_enable_get(unit, port, enable), ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_enable_get(unit, port, enable), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_enable_get);
+
+static u32 yt_port_intphy_restart(u8 unit, u32 port)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 mode = 0;
+	u16 data = 0;
+	u16 ori = 0;
+
+	/* WA for softreset, power down, power up */
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x84;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &mode), ret);
+	data = mode;
+	data &= (~(0x3F << 10));
+	data |= (1 << 13);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	attr.reg_addr = 0x0;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	SET_BIT(data, 10);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &ori), ret);
+	data = ori;
+	SET_BIT(data, 15);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	if (((ori >> 11U) & 1U) == 1U)
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, ori), ret);
+
+	attr.reg_addr = 0x84;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, mode), ret);
+
+	attr.reg_addr = 0x0;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	CLEAR_BIT(data, 10);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+static u32 yt_port_intsds_restart(u8 unit, u32 port)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data;
+	u16 ori;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data > 0x4)
+		/* usxgmii space */
+		attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+	else
+		attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+
+	attr.reg_addr = 0x0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &ori), ret);
+	data = ori;
+	SET_BIT(data, 15);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	if (((ori >> 11U) & 1U) == 1U)
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, ori), ret);
+
+	return ret;
+}
+
+static u32 yt_port_intphy_auto_neg_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	if (enable == YT_ENABLE)
+		data |= 1 << 12;
+	else
+		data &= ~(1 << 12);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	if (enable == YT_ENABLE)
+		CMM_ERR_CHK(yt_port_intphy_restart(unit, port), ret);
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_port_intsds_auto_neg_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data13 = 0;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data > 0x4) {
+		/* usxgmii space */
+		attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+		attr.reg_addr = 0x0;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		attr.reg_addr = 0x13;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data13),
+			    ret);
+		if (enable == YT_ENABLE) {
+			SET_BIT(data, 12);
+			CLEAR_BIT(data13, 11);
+			CLEAR_BIT(data13, 15);
+		} else {
+			CLEAR_BIT(data, 12);
+			SET_BIT(data13, 11);
+			SET_BIT(data13, 15);
+		}
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data13), ret);
+		attr.reg_addr = 0x0;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	} else {
+		attr.reg_addr = 0x0;
+		attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		if (enable == YT_ENABLE)
+			SET_BIT(data, 12);
+		else
+			CLEAR_BIT(data, 12);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	}
+
+	if (enable == YT_ENABLE)
+		CMM_ERR_CHK(yt_port_intsds_restart(unit, port), ret);
+
+	return ret;
+}
+
+u32 yt_port_phy_auto_neg_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_auto_neg_set(unit, port, enable),
+			    ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_auto_neg_set(unit, port, enable),
+			    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_auto_neg_set);
+
+static u32 yt_port_intphy_auto_neg_get(u8 unit, u32 port,
+				       enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	*enable = (data >> 12 & 0x01) ? YT_ENABLE : YT_DISABLE;
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_port_intsds_auto_neg_get(u8 unit, u32 port,
+				       enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data > 0x4)
+		attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+	else
+		attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+
+	attr.reg_addr = 0x0;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 12) & 0x1U;
+	*enable = (data == 1U) ? YT_ENABLE : YT_DISABLE;
+
+	return ret;
+}
+
+u32 yt_port_phy_auto_neg_get(u8 unit, u32 port, enum yt_enable *enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_auto_neg_get(unit, port, enable),
+			    ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_auto_neg_get(unit, port, enable),
+			    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_auto_neg_get);
+
+static u32 yt_port_intphy_force_speed_set(u8 unit, u32 port,
+					  enum yt_port_speed_duplex speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	switch (speed_dup) {
+	case PORT_SPEED_DUP_10HALF:
+		CLEAR_BIT(data, 6);
+		CLEAR_BIT(data, 13);
+		CLEAR_BIT(data, 8);
+		break;
+	case PORT_SPEED_DUP_10FULL:
+		CLEAR_BIT(data, 6);
+		CLEAR_BIT(data, 13);
+		SET_BIT(data, 8);
+		break;
+	case PORT_SPEED_DUP_100HALF:
+		CLEAR_BIT(data, 6);
+		SET_BIT(data, 13);
+		CLEAR_BIT(data, 8);
+		break;
+	case PORT_SPEED_DUP_100FULL:
+		CLEAR_BIT(data, 6);
+		SET_BIT(data, 13);
+		SET_BIT(data, 8);
+		break;
+	default:
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	CMM_ERR_CHK(yt_port_intphy_restart(unit, port), ret);
+
+	return ret;
+}
+
+static u32
+yt_port_sgmii_mac_force_speed_set(u8 unit, u32 port,
+				  enum yt_port_speed_duplex speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	if (speed_dup == PORT_SPEED_DUP_10FULL) {
+		SET_BIT(data, 8);
+		CLEAR_BIT(data, 6);
+		CLEAR_BIT(data, 13);
+	} else if (speed_dup == PORT_SPEED_DUP_10HALF) {
+		CLEAR_BIT(data, 8);
+		CLEAR_BIT(data, 6);
+		CLEAR_BIT(data, 13);
+	} else if (speed_dup == PORT_SPEED_DUP_100FULL) {
+		SET_BIT(data, 8);
+		CLEAR_BIT(data, 6);
+		SET_BIT(data, 13);
+	} else if (speed_dup == PORT_SPEED_DUP_100HALF) {
+		CLEAR_BIT(data, 8);
+		CLEAR_BIT(data, 6);
+		SET_BIT(data, 13);
+	} else if (speed_dup == PORT_SPEED_DUP_1000FULL) {
+		SET_BIT(data, 8);
+		SET_BIT(data, 6);
+		CLEAR_BIT(data, 13);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+static u32
+yt_port_sgmii_mac_force_speed_get(u8 unit, u32 port,
+				  enum yt_port_speed_duplex *speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	if (((data >> 0x8U) & 0x1U) == 0x0U) {
+		if ((((data >> 0x6U) & 0x1U) == 0x0U) &&
+		    (((data >> 13U) & 0x1U) == 0x0U)) {
+			*speed_dup = PORT_SPEED_DUP_10HALF;
+		} else if ((((data >> 0x6U) & 0x1U) == 0x1U) &&
+			   (((data >> 13U) & 0x1U) == 0x0U)) {
+			*speed_dup = PORT_SPEED_DUP_1000FULL;
+		} else if ((((data >> 0x6U) & 0x1U) == 0x0U) &&
+			   (((data >> 13U) & 0x1U) == 0x1U)) {
+			*speed_dup = PORT_SPEED_DUP_100HALF;
+		} else {
+			return CMM_ERR_NOT_SUPPORT;
+		}
+	} else {
+		if ((((data >> 0x6U) & 0x1U) == 0x0U) &&
+		    (((data >> 13U) & 0x1U) == 0x0U)) {
+			*speed_dup = PORT_SPEED_DUP_10FULL;
+		} else if ((((data >> 0x6U) & 0x1U) == 0x1U) &&
+			   (((data >> 13U) & 0x1U) == 0x0U)) {
+			*speed_dup = PORT_SPEED_DUP_1000FULL;
+		} else if ((((data >> 0x6U) & 0x1U) == 0x0U) &&
+			   (((data >> 13U) & 0x1U) == 0x1U)) {
+			*speed_dup = PORT_SPEED_DUP_100FULL;
+		} else {
+			return CMM_ERR_NOT_SUPPORT;
+		}
+	}
+
+	return ret;
+}
+
+static u32
+yt_port_sgmii_phy_force_speed_set(u8 unit, u32 port,
+				  enum yt_port_speed_duplex speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 ext_data = 0;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+
+	attr.reg_addr = 0x1;
+	attr.reg_type = YT_PHY_REG_TYPE_SG_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &ext_data), ret);
+	if (speed_dup == PORT_SPEED_DUP_10FULL) {
+		SET_BIT(data, 8);
+		CLEAR_BIT(ext_data, 2);
+		CLEAR_BIT(ext_data, 3);
+		CLEAR_BIT(ext_data, 4);
+	} else if (speed_dup == PORT_SPEED_DUP_10HALF) {
+		CLEAR_BIT(data, 8);
+		CLEAR_BIT(ext_data, 2);
+		CLEAR_BIT(ext_data, 3);
+		CLEAR_BIT(ext_data, 4);
+	} else if (speed_dup == PORT_SPEED_DUP_100FULL) {
+		SET_BIT(data, 8);
+		SET_BIT(ext_data, 2);
+		CLEAR_BIT(ext_data, 3);
+		CLEAR_BIT(ext_data, 4);
+	} else if (speed_dup == PORT_SPEED_DUP_100HALF) {
+		CLEAR_BIT(data, 8);
+		SET_BIT(ext_data, 2);
+		CLEAR_BIT(ext_data, 3);
+		CLEAR_BIT(ext_data, 4);
+	} else if (speed_dup == PORT_SPEED_DUP_1000FULL) {
+		SET_BIT(data, 8);
+		CLEAR_BIT(ext_data, 2);
+		SET_BIT(ext_data, 3);
+		CLEAR_BIT(ext_data, 4);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, ext_data), ret);
+
+	attr.reg_addr = 0x0;
+	attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+static u32
+yt_port_sgmii_phy_force_speed_get(u8 unit, u32 port,
+				  enum yt_port_speed_duplex *speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 ext_data = 0;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+
+	attr.reg_addr = 0x1;
+	attr.reg_type = YT_PHY_REG_TYPE_SG_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &ext_data), ret);
+	if (((data >> 0x8U) & 0x1U) == 0x0U) {
+		if (((ext_data >> 0x2U) & 0x7U) == 0x0U)
+			*speed_dup = PORT_SPEED_DUP_10HALF;
+		else if (((ext_data >> 0x2U) & 0x7U) == 0x1U)
+			*speed_dup = PORT_SPEED_DUP_100HALF;
+		else if (((ext_data >> 0x2U) & 0x7U) == 0x2U)
+			*speed_dup = PORT_SPEED_DUP_1000FULL;
+		else
+			return CMM_ERR_NOT_SUPPORT;
+	} else {
+		if (((ext_data >> 0x2U) & 0x7U) == 0x0U)
+			*speed_dup = PORT_SPEED_DUP_10FULL;
+		else if (((ext_data >> 0x2U) & 0x7U) == 0x1U)
+			*speed_dup = PORT_SPEED_DUP_100FULL;
+		else if (((ext_data >> 0x2U) & 0x7U) == 0x2U)
+			*speed_dup = PORT_SPEED_DUP_1000FULL;
+		else
+			return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+static u32 yt_port_usxgmii_force_speed_set(u8 unit, u32 port,
+					   enum yt_port_speed_duplex speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u32 speed = 0;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x13;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	if (speed_dup == PORT_SPEED_DUP_100FULL ||
+	    speed_dup == PORT_SPEED_DUP_100HALF)
+		speed = 0x1;
+	else if (speed_dup == PORT_SPEED_DUP_1000FULL)
+		speed = 0x2;
+	else if (speed_dup == PORT_SPEED_DUP_2500FULL)
+		speed = 0x4;
+	else if (speed_dup == PORT_SPEED_DUP_5GFULL)
+		speed = 0x5;
+	else if (speed_dup == PORT_SPEED_DUP_10GFULL)
+		speed = 0x3;
+	else
+		return CMM_ERR_NOT_SUPPORT;
+
+	data = data & (~(0x7U << 8U));
+	data = data & (~(0x7U << 12U));
+	data = data | (speed << 8U) | (speed << 12U);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+	return ret;
+}
+
+static u32 yt_port_usxgmii_force_speed_get(u8 unit, u32 port,
+					   enum yt_port_speed_duplex *speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u32 speed = 0;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x13;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_USXG_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	speed = (data >> 0x8U) & 0x7U;
+	if (speed == 0x1U)
+		*speed_dup = PORT_SPEED_DUP_100FULL;
+	else if (speed == 0x2U)
+		*speed_dup = PORT_SPEED_DUP_1000FULL;
+	else if (speed == 0x3U)
+		*speed_dup = PORT_SPEED_DUP_10GFULL;
+	else if (speed == 0x4U)
+		*speed_dup = PORT_SPEED_DUP_2500FULL;
+	else if (speed == 0x5U)
+		*speed_dup = PORT_SPEED_DUP_5GFULL;
+	else
+		return CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+
+static u32 yt_port_intsds_force_speed_set(u8 unit, u32 port,
+					  enum yt_port_speed_duplex speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data == 0x2U || data == 0x4U || data == 0x3U) {
+		// fiber 1000, bx2500, bx100
+		attr.reg_addr = 0x0;
+		attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		SET_BIT(data, 8);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	} else if (data == 0x0U) {
+		CMM_ERR_CHK(yt_port_sgmii_mac_force_speed_set(unit, port,
+							      speed_dup), ret);
+	} else if (data == 0x1U) {
+		CMM_ERR_CHK(yt_port_sgmii_phy_force_speed_set(unit, port,
+							      speed_dup), ret);
+	} else {
+		CMM_ERR_CHK(yt_port_usxgmii_force_speed_set(unit, port,
+							    speed_dup), ret);
+	}
+
+	CMM_ERR_CHK(yt_port_intsds_restart(unit, port), ret);
+
+	return ret;
+}
+
+u32 yt_port_phy_force_speed_set(u8 unit, u32 port,
+				enum yt_port_speed_duplex speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_force_speed_set(unit, port,
+							   speed_dup), ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_force_speed_set(unit, port,
+							   speed_dup), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_force_speed_set);
+
+static u32 yt_port_intphy_force_speed_get(u8 unit, u32 port,
+					  enum yt_port_speed_duplex *speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x0;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	if ((data >> 8) & 0x1) {
+		if ((data >> 13) & 0x1 && !((data >> 6) & 0x1))
+			*speed_dup = PORT_SPEED_DUP_100FULL;
+		else if ((data >> 6) & 0x1 && !((data >> 13) & 0x1))
+			*speed_dup = PORT_SPEED_DUP_1000FULL;
+		else if (!((data >> 6) & 0x1) && !((data >> 13) & 0x1))
+			*speed_dup = PORT_SPEED_DUP_10FULL;
+	} else {
+		if ((data >> 13) & 0x1 && !((data >> 6) & 0x1))
+			*speed_dup = PORT_SPEED_DUP_100HALF;
+		else if (!((data >> 6) & 0x1) && !((data >> 13) & 0x1))
+			*speed_dup = PORT_SPEED_DUP_10HALF;
+	}
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_port_intsds_force_speed_get(u8 unit, u32 port,
+					  enum yt_port_speed_duplex *speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data == 0x2U || data == 0x4U || data == 0x3U)
+		*speed_dup = PORT_SPEED_DUP_1000FULL;
+	else if (data == 0x4U)
+		*speed_dup = PORT_SPEED_DUP_2500FULL;
+	else if (data == 0x3)
+		*speed_dup = PORT_SPEED_DUP_100FULL;
+	else if (data == 0x0U)
+		CMM_ERR_CHK(yt_port_sgmii_mac_force_speed_get(unit, port,
+							      speed_dup), ret);
+	else if (data == 0x1U)
+		CMM_ERR_CHK(yt_port_sgmii_phy_force_speed_get(unit, port,
+							      speed_dup), ret);
+	else
+		CMM_ERR_CHK(yt_port_usxgmii_force_speed_get(unit, port,
+							    speed_dup), ret);
+
+	return ret;
+}
+
+u32 yt_port_phy_force_speed_get(u8 unit, u32 port,
+				enum yt_port_speed_duplex *speed_dup)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_force_speed_get(unit, port,
+							   speed_dup), ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_force_speed_get(unit, port,
+							   speed_dup), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_force_speed_get);
+
+static u32 yt_port_phy922x_reg_set(u8 unit, u32 port,
+				   struct yt_phy_reg_attr attr, u16 data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 index = 0;
+	u32 phy;
+
+	if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD1)
+		index = 1;
+	else if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD3)
+		index = 3;
+	else
+		index = 7;
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+	if (attr.phy_type == PHY_INTERNAL) {
+		ret = yt_intphy_addr_get(unit, port, attr.reg_type, &phy);
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x1f,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy,
+							  attr.reg_addr,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else if (attr.phy_type == PHY_EXTERNAL) {
+		ret = yt_extphy_addr_get(unit, port, attr.reg_type, &phy);
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1f,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy,
+							  attr.reg_addr,
+							  data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else {
+		ret = CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+static u32 yt_port_phy922x_reg_get(u8 unit, u32 port,
+				   struct yt_phy_reg_attr attr, u16 *data)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 index = 0;
+	u32 phy;
+
+	if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD1)
+		index = 1;
+	else if (attr.reg_type == YT_PHY_REG_TYPE_PHY_MMD3)
+		index = 3;
+	else
+		index = 7;
+
+	phy = CAL_YTP_TO_MAC(unit, port);
+	if (attr.phy_type == PHY_INTERNAL) {
+		ret = yt_intphy_addr_get(unit, port, attr.reg_type, &phy);
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0x1f, data),
+				    ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_intif_read(unit, phy,
+							 attr.reg_addr,
+							 data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_intif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_intif_read(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+		}
+	} else if (attr.phy_type == PHY_EXTERNAL) {
+		ret = yt_extphy_addr_get(unit, port, attr.reg_type, &phy);
+		switch (attr.reg_type) {
+		case YT_PHY_REG_TYPE_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_TOP_EXT:
+		case YT_PHY_REG_TYPE_PHY_EXT:
+		case YT_PHY_REG_TYPE_SG_EXT:
+		case YT_PHY_REG_TYPE_SDS_TOP_EXT:
+		case YT_PHY_REG_TYPE_USXG_EXT:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0x1e,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy, 0x1f, data),
+				    ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MII:
+		case YT_PHY_REG_TYPE_SG_MII:
+		case YT_PHY_REG_TYPE_USXG_MII:
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy,
+							 attr.reg_addr,
+							 data), ret);
+			break;
+		case YT_PHY_REG_TYPE_PHY_MMD1:
+		case YT_PHY_REG_TYPE_PHY_MMD3:
+		case YT_PHY_REG_TYPE_PHY_MMD7:
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  index), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xe,
+							  attr.reg_addr), ret);
+			CMM_ERR_CHK(yt_switch_extif_write(unit, phy, 0xd,
+							  0x4000 + index), ret);
+			CMM_ERR_CHK(yt_switch_extif_read(unit, phy, 0xe, data),
+				    ret);
+			break;
+		default:
+			ret = CMM_ERR_NOT_SUPPORT;
+			break;
+		}
+	} else {
+		ret = CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+u32 yt_port_phy_reg_set(u8 unit, u32 port, struct yt_phy_reg_attr attr,
+			u16 data)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	if (chip_id == YT_SW_ID_922X)
+		CMM_ERR_CHK(yt_port_phy922x_reg_set(unit, port, attr, data),
+			    ret);
+	else
+		ret = CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_reg_set);
+
+u32 yt_port_phy_reg_get(u8 unit, u32 port, struct yt_phy_reg_attr attr,
+			u16 *data)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	enum cmm_err ret;
+
+	CMM_ERR_CHK(yt_cal_board_chip_get(&chip_id), ret);
+	if (chip_id == YT_SW_ID_922X)
+		CMM_ERR_CHK(yt_port_phy922x_reg_get(unit, port, attr, data),
+			    ret);
+	else
+		ret = CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_reg_get);
+
+static u32
+yt_port_intphy_auto_neg_ability_set(u8 unit, u32 port,
+				    struct yt_port_an_ability ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data_mmd = 0;
+	u16 data4 = 0;
+	u16 data9 = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x4;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data4), ret);
+	attr.reg_addr = 0x9;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data9), ret);
+	if (ability.asy_fc_en == 1)
+		SET_BIT(data4, 11);
+	else
+		CLEAR_BIT(data4, 11);
+
+	if (ability.fc_en == 1)
+		SET_BIT(data4, 10);
+	else
+		CLEAR_BIT(data4, 10);
+
+	if (ability.half_10_en == 1)
+		SET_BIT(data4, 5);
+	else
+		CLEAR_BIT(data4, 5);
+
+	if (ability.full_10_en == 1)
+		SET_BIT(data4, 6);
+	else
+		CLEAR_BIT(data4, 6);
+
+	if (ability.half_100_en == 1)
+		SET_BIT(data4, 7);
+	else
+		CLEAR_BIT(data4, 7);
+
+	if (ability.full_100_en == 1)
+		SET_BIT(data4, 8);
+	else
+		CLEAR_BIT(data4, 8);
+
+	if (ability.full_1000_en == 1)
+		SET_BIT(data9, 9);
+	else
+		CLEAR_BIT(data9, 9);
+
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data9), ret);
+	attr.reg_addr = 0x4;
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data4), ret);
+
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD7;
+	attr.reg_addr = 0x20;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data_mmd), ret);
+	if (ability.full_2500_en == 1)
+		SET_BIT(data_mmd, 7);
+	else
+		CLEAR_BIT(data_mmd, 7);
+
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data_mmd), ret);
+
+	CMM_ERR_CHK(yt_port_intphy_restart(unit, port), ret);
+
+	return CMM_ERR_OK;
+}
+
+static u32
+yt_port_intphy_auto_neg_ability_get(u8 unit, u32 port,
+				    struct yt_port_an_ability *ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data_mmd = 0;
+	u16 data4 = 0;
+	u16 data9 = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x4;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data4), ret);
+	attr.reg_addr = 0x9;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data9), ret);
+	if ((data9 >> 9) & 0x1)
+		ability->full_1000_en = 1;
+	else
+		ability->full_1000_en = 0;
+
+	if ((data4 >> 10) & 0x1)
+		ability->fc_en = 1;
+	else
+		ability->fc_en = 0;
+
+	if ((data4 >> 11) & 0x1)
+		ability->asy_fc_en = 1;
+	else
+		ability->asy_fc_en = 0;
+
+	if ((data4 >> 8) & 0x1)
+		ability->full_100_en = 1;
+	else
+		ability->full_100_en = 0;
+
+	if ((data4 >> 7) & 0x1)
+		ability->half_100_en = 1;
+	else
+		ability->half_100_en = 0;
+
+	if ((data4 >> 6) & 0x1)
+		ability->full_10_en = 1;
+	else
+		ability->full_10_en = 0;
+
+	if ((data4 >> 5) & 0x1)
+		ability->half_10_en = 1;
+	else
+		ability->half_10_en = 0;
+
+	attr.reg_addr = 0x20;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD7;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data_mmd), ret);
+	if ((data_mmd >> 7) & 0x1)
+		ability->full_2500_en = 1;
+	else
+		ability->full_2500_en = 0;
+
+	return ret;
+}
+
+static u32
+yt_port_intsds_auto_neg_ability_set(u8 unit, u32 port,
+				    struct yt_port_an_ability ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data == 2 || data == 4) {
+		/* BX1000 and BX2500 support fc auto negotiation */
+		attr.reg_addr = 0x4;
+		attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		if (ability.asy_fc_en == YT_ENABLE)
+			SET_BIT(data, 8);
+		else
+			CLEAR_BIT(data, 8);
+
+		if (ability.fc_en == YT_ENABLE)
+			SET_BIT(data, 7);
+		else
+			CLEAR_BIT(data, 7);
+
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		CMM_ERR_CHK(yt_port_intsds_restart(unit, port), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+static u32
+yt_port_intsds_auto_neg_ability_get(u8 unit, u32 port,
+				    struct yt_port_an_ability *ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x400;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_SDS_TOP_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	data = (data >> 4) & 0x7;
+	if (data == 2 || data == 4) {
+		/* BX1000 and BX2500 support fc auto negotiation */
+		attr.reg_addr = 0x4;
+		attr.reg_type = YT_PHY_REG_TYPE_SG_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		ability->asy_fc_en = IS_BIT_SET(data, 8) ? YT_ENABLE :
+							   YT_DISABLE;
+		ability->fc_en = IS_BIT_SET(data, 7) ? YT_ENABLE : YT_DISABLE;
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return ret;
+}
+
+u32 yt_port_phy_auto_neg_ability_set(u8 unit, u32 port,
+				     enum yt_phy_chip_mode phy_mode,
+				     struct yt_port_an_ability ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_auto_neg_ability_set(unit, port,
+								ability), ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_auto_neg_ability_set(unit, port,
+								ability), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_auto_neg_ability_set);
+
+u32 yt_port_phy_auto_neg_ability_get(u8 unit, u32 port,
+				     enum yt_phy_chip_mode phy_mode,
+				     struct yt_port_an_ability *ability)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_auto_neg_ability_get(unit, port,
+								ability), ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_auto_neg_ability_get(unit, port,
+								ability), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_auto_neg_ability_get);
+
+static u32 yt_port_intphy_crossmode_set(u8 unit, u32 port,
+					enum yt_utp_crossover_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x16;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	switch (mode) {
+	case YT_UTP_CROSSOVER_MODE_MDI:
+		data &= ~(1 << 6);
+		data &= ~(1 << 5);
+		break;
+	case YT_UTP_CROSSOVER_MODE_MDIX:
+		data &= ~(1 << 6);
+		data |= 1 << 5;
+		break;
+	case YT_UTP_CROSSOVER_MODE_AUTO:
+		data |= 1 << 6;
+		data |= 1 << 5;
+		break;
+	default:
+		return CMM_ERR_INPUT;
+	}
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	CMM_ERR_CHK(yt_port_intphy_restart(unit, port), ret);
+
+	return CMM_ERR_OK;
+}
+
+static u32 yt_port_intphy_crossmode_get(u8 unit, u32 port,
+					enum yt_utp_crossover_mode *mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x16;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	*mode = (data >> 5) & 0x3;
+
+	return CMM_ERR_OK;
+}
+
+u32 yt_port_phy_cross_mode_set(u8 unit, u32 port,
+			       enum yt_utp_crossover_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_crossmode_set(unit, port, mode),
+			    ret);
+	else
+		ret = CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_cross_mode_set);
+
+u32 yt_port_phy_cross_mode_get(u8 unit, u32 port,
+			       enum yt_utp_crossover_mode *mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_crossmode_get(unit, port, mode),
+			    ret);
+	else
+		ret = CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_cross_mode_get);
+
+static u32 yt_port_intphy_template_set(u8 unit, u32 port,
+				       enum yt_utp_template_testmode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	struct yt_phy_reg_attr attr;
+	u16 data = 0;
+
+	attr.phy_type = PHY_INTERNAL;
+	attr.reg_addr = 0x27;
+	attr.reg_space = YT_PHY_REG_SPACE_T1;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+	CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+	CLEAR_BIT(data, 15);
+	CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+	/* mdi */
+	CMM_ERR_CHK(yt_port_phy_cross_mode_set(unit, port,
+					       YT_UTP_CROSSOVER_MODE_MDI), ret);
+	attr.reg_addr = 0x0;
+	attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+	/* speed */
+	if (mode >= YT_UTP_TEMPLATE_TMODE_10M_10MSINE &&
+	    mode <= YT_UTP_TEMPLATE_TMODE_10M_NORMAL) {
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x8100), ret);
+	} else if (mode >= YT_UTP_TEMPLATE_TMODE_100M_T1 &&
+		   mode <= YT_UTP_TEMPLATE_TMODE_100M_T4) {
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xa100), ret);
+	} else if (mode >= YT_UTP_TEMPLATE_TMODE_1000M_T1 &&
+		   mode <= YT_UTP_TEMPLATE_TMODE_1000M_T4) {
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x8140), ret);
+		attr.reg_addr = 0x9;
+	} else if (mode >= YT_UTP_TEMPLATE_TMODE_2500M_T1 &&
+		   mode <= YT_UTP_TEMPLATE_TMODE_2500M_T7) {
+		attr.reg_addr = 0x0;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x2058), ret);
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xa140), ret);
+		attr.reg_addr = 0x84;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+	} else if (mode == YT_UTP_TEMPLATE_TMODE_NONE) {
+		CMM_ERR_CHK(yt_port_phy_cross_mode_set(unit, port,
+						       0x3), ret);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x9140), ret);
+	} else {
+		return CMM_ERR_NOT_SUPPORT;
+	}
+	switch (mode) {
+	case YT_UTP_TEMPLATE_TMODE_2500M_T1:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x2000), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T2:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x4000), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T3:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x6000), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_1:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x8400), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_2:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x8800), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_3:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x9000), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_4:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x9400), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_5:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x9800), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T5:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xa000), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T6:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xc000), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_2500M_T7:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0xe000), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_1000M_T1:
+	case YT_UTP_TEMPLATE_TMODE_100M_T1:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x2200), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_1000M_T2:
+	case YT_UTP_TEMPLATE_TMODE_100M_T2:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x5a00), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_1000M_T3:
+	case YT_UTP_TEMPLATE_TMODE_100M_T3:
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, 0x7200), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_1000M_T4:
+	case YT_UTP_TEMPLATE_TMODE_100M_T4:
+		ret |= yt_port_phy_reg_set(unit, port, attr, 0x8200);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_10M_10MSINE:
+	case YT_UTP_TEMPLATE_TMODE_10M_PRANDOM:
+	case YT_UTP_TEMPLATE_TMODE_10M_LINKPULSE:
+	case YT_UTP_TEMPLATE_TMODE_10M_5MSINE:
+	case YT_UTP_TEMPLATE_TMODE_10M_NORMAL:
+		attr.reg_addr = 0xa;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		data &= ~(0x7);
+		data |= (mode + 1);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		break;
+	case YT_UTP_TEMPLATE_TMODE_NONE:
+		attr.reg_addr = 0xa;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_EXT;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		data &= ~(0x7);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+		attr.reg_addr = 0x9;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MII;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		data &= ~(0x7 << 13);
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+
+		attr.reg_addr = 0x84;
+		attr.reg_type = YT_PHY_REG_TYPE_PHY_MMD1;
+		CMM_ERR_CHK(yt_port_phy_reg_get(unit, port, attr, &data), ret);
+		data &= (~(0x3F << 10));
+		CMM_ERR_CHK(yt_port_phy_reg_set(unit, port, attr, data), ret);
+		break;
+	default:
+		ret = CMM_ERR_NOT_SUPPORT;
+		break;
+	}
+
+	return ret;
+}
+
+u32 yt_port_phy_template_set(u8 unit, u32 port,
+			     enum yt_utp_template_testmode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_template_set(unit, port, mode), ret);
+	else
+		ret = CMM_ERR_NOT_SUPPORT;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_template_set);
+
+static u32 yt_port_intphy_loopback_set(u8 unit, u32 port,
+				       enum yt_phy_loopback_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	return ret;
+}
+
+static u32 yt_port_intsds_loopback_set(u8 unit, u32 port,
+				       enum yt_phy_loopback_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	return ret;
+}
+
+u32 yt_port_phy_loopback_set(u8 unit, u32 port, enum yt_phy_chip_mode phy_mode,
+			     enum yt_phy_loopback_mode mode)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 addr = 0;
+
+	addr = CAL_YTP_TO_MAC(unit, port);
+	if (addr >= 4 && addr <= 7)
+		CMM_ERR_CHK(yt_port_intphy_loopback_set(unit, port, mode), ret);
+	else
+		CMM_ERR_CHK(yt_port_intsds_loopback_set(unit, port, mode), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_port_phy_loopback_set);
+
+#elif defined(CONFIG_MOTORCOMM_YT923X)
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_port.h b/drivers/net/dsa/motorcomm/switch/yt_port.h
new file mode 100644
index 000000000000..313f95841749
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_port.h
@@ -0,0 +1,824 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_PORT_H__
+#define __YT_PORT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#include "yt_types.h"
+#include "yt_cmm.h"
+
+enum yt_port_speed_mode {
+	PORT_SPEED_MODE_10M = 0,
+	PORT_SPEED_MODE_100M,
+	PORT_SPEED_MODE_1000M,
+	PORT_SPEED_MODE_10G,
+	PORT_SPEED_MODE_2500M,
+	PORT_SPEED_MODE_5G,
+	PORT_SPEED_MODE_END
+};
+
+enum yt_phy_reg_space {
+	YT_PHY_REG_SPACE_T1,
+	YT_PHY_REG_SPACE_TX,
+	YT_PHY_REG_SPACE_LDS,
+};
+
+enum yt_phy_type {
+	PHY_INTERNAL,
+	PHY_EXTERNAL,
+	PHY_TYPE_MAX,
+};
+
+enum yt_phy_reg_type {
+	YT_PHY_REG_TYPE_TOP_EXT,
+	YT_PHY_REG_TYPE_PHY_TOP_EXT,
+	YT_PHY_REG_TYPE_PHY_MII,
+	YT_PHY_REG_TYPE_PHY_EXT,
+	YT_PHY_REG_TYPE_PHY_MMD1,
+	YT_PHY_REG_TYPE_PHY_MMD3,
+	YT_PHY_REG_TYPE_PHY_MMD7,
+	YT_PHY_REG_TYPE_SDS_TOP_EXT,
+	YT_PHY_REG_TYPE_SG_MII,
+	YT_PHY_REG_TYPE_SG_EXT,
+	YT_PHY_REG_TYPE_USXG_MII,
+	YT_PHY_REG_TYPE_USXG_EXT,
+};
+
+struct yt_phy_reg_attr {
+	u16 reg_addr;
+	enum yt_phy_type phy_type;
+	enum yt_phy_reg_type reg_type;
+	enum yt_phy_reg_space reg_space;
+};
+
+enum yt_utp_crossover_mode {
+	YT_UTP_CROSSOVER_MODE_MDI = 0,
+	YT_UTP_CROSSOVER_MODE_MDIX,
+	YT_UTP_CROSSOVER_MODE_RESV,
+	YT_UTP_CROSSOVER_MODE_AUTO
+};
+
+#define PHY_GET_PHYADDR(addr) ((addr) & 0xFF)
+#define PHY_GET_REGSPACE(addr) (((addr) >> 14) & 0x3)
+#define PHY_GET_REGTYPE(addr) (((addr) >> 10) & 0xF)
+#define PHY_SET_REGSPACE(addr, reg_space) \
+	((addr) = ((addr) | (((reg_space) & 0x3) << 14)))
+#define PHY_CLEAR_REGSPACE(addr) ((addr) = ((addr) & ((~((0x3) << 14)))))
+#define PHY_SET_REGTYPE(addr, reg_type) \
+	((addr) = ((addr) | (((reg_type) & 0xF) << 10)))
+#define PHY_CLEAR_REGTYPE(addr) ((addr) = ((addr) & (~(0xF << 10))))
+
+enum yt_utp_template_testmode {
+	YT_UTP_TEMPLATE_TMODE_10M_10MSINE = 0,
+	YT_UTP_TEMPLATE_TMODE_10M_PRANDOM,
+	YT_UTP_TEMPLATE_TMODE_10M_LINKPULSE,
+	YT_UTP_TEMPLATE_TMODE_10M_5MSINE,
+	YT_UTP_TEMPLATE_TMODE_10M_NORMAL,
+	YT_UTP_TEMPLATE_TMODE_100M_MDI = 5,
+	YT_UTP_TEMPLATE_TMODE_100M_MDIX,
+	YT_UTP_TEMPLATE_TMODE_100M_T1,
+	YT_UTP_TEMPLATE_TMODE_100M_T2,
+	YT_UTP_TEMPLATE_TMODE_100M_T3,
+	YT_UTP_TEMPLATE_TMODE_100M_T4 = 10,
+	YT_UTP_TEMPLATE_TMODE_1000M_T1,
+	YT_UTP_TEMPLATE_TMODE_1000M_T2,
+	YT_UTP_TEMPLATE_TMODE_1000M_T3,
+	YT_UTP_TEMPLATE_TMODE_1000M_T4 = 14,
+	YT_UTP_TEMPLATE_TMODE_SDS2500M,
+	YT_UTP_TEMPLATE_TMODE_SDS1000M,
+	YT_UTP_TEMPLATE_TMODE_2500M_T1,
+	YT_UTP_TEMPLATE_TMODE_2500M_T2,
+	YT_UTP_TEMPLATE_TMODE_2500M_T3,
+	YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_1,
+	YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_2,
+	YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_3,
+	YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_4,
+	YT_UTP_TEMPLATE_TMODE_2500M_T4_DUALTONE_5,
+	YT_UTP_TEMPLATE_TMODE_2500M_T5,
+	YT_UTP_TEMPLATE_TMODE_2500M_T6,
+	YT_UTP_TEMPLATE_TMODE_2500M_T7,
+	YT_UTP_TEMPLATE_TMODE_NONE
+};
+
+enum yt_phy_loopback_mode {
+	YT_PHY_LOOPBACK_MODE_INTERNAL,
+	YT_PHY_LOOPBACK_MODE_EXTERNAL,
+	YT_PHY_LOOPBACK_MODE_REMOTE,
+	YT_PHY_LOOPBACK_MODE_T1_INTERNAL,
+	YT_PHY_LOOPBACK_MODE_T1_EXTERNAL,
+	YT_PHY_LOOPBACK_MODE_T1_REMOTE,
+	YT_PHY_LOOPBACK_MODE_DISABLE
+};
+
+enum yt_dvddio_power_pad {
+	POWER_PAD_NORMAL,
+	POWER_PAD_RGMII1,
+	POWER_PAD_RGMII2
+};
+
+enum yt_dvddio_power_level {
+	POWER18V = 0,
+	POWER25V,
+	POWER33V,
+	POWER33V_OR_25V
+};
+
+/* port medium type */
+enum yt_port_medium {
+	PORT_MEDI_COPPER = 0,
+	PORT_MEDI_FIBER,
+	PORT_MEDI_COMBO_FIBER,
+	PORT_MEDI_COMBO_COPPER,
+	PORT_MEDI_SERDES,
+	PORT_MEDI_END
+};
+
+enum yt_port_speed {
+	PORT_SPEED_10M = 0,
+	PORT_SPEED_100M,
+	PORT_SPEED_1000M,
+	PORT_SPEED_2500M,
+	PORT_SPEED_5G,
+	PORT_SPEED_10G,
+	PORT_SPEED_END
+};
+
+enum yt_port_duplex { PORT_DUPLEX_HALF = 0, PORT_DUPLEX_FULL };
+
+enum yt_port_speed_duplex {
+	PORT_SPEED_DUP_10HALF = 0,
+	PORT_SPEED_DUP_10FULL,
+	PORT_SPEED_DUP_100HALF,
+	PORT_SPEED_DUP_100FULL,
+	PORT_SPEED_DUP_1000FULL,
+	PORT_SPEED_DUP_2500FULL,
+	PORT_SPEED_DUP_5GFULL,
+	PORT_SPEED_DUP_10GFULL,
+	PORT_SPEED_DUP_END
+};
+
+enum yt_port_link_status { PORT_LINK_DOWN = 0, PORT_LINK_UP };
+
+enum yt_extif_mode {
+	YT_EXTIF_MODE_MII = 0,
+	YT_EXTIF_MODE_REMII,
+	YT_EXTIF_MODE_RMII_MAC,
+	YT_EXTIF_MODE_RMII_PHY,
+	YT_EXTIF_MODE_RGMII,
+	YT_EXTIF_MODE_XMII_DISABLE,
+	YT_EXTIF_MODE_SG_MAC,
+	YT_EXTIF_MODE_SG_PHY,
+	YT_EXTIF_MODE_FIB_1000,
+	YT_EXTIF_MODE_FIB_100,
+	YT_EXTIF_MODE_BX2500,
+	YT_EXTIF_MODE_SGFIB_AS,
+	YT_EXTIF_MODE_QSGMII,
+	YT_EXTIF_MODE_10GQXGMII,
+	YT_EXTIF_MODE_USXGMII_10G,
+	YT_EXTIF_MODE_LRE = 15,
+	YT_EXTIF_MODE_FE,
+	YT_EXTIF_MODE_AUTO_LRE_FE,
+	YT_EXTIF_MODE_MII_MAC,
+};
+
+enum yt_phy_chip_mode {
+	YT_PHY_CHIP_MODE_COPPER = 0,
+	YT_PHY_CHIP_MODE_FIBER,
+	YT_PHY_CHIP_MODE_COMBO,
+	YT_PHY_CHIP_MODE_AUTO_BX2500_SGMII,
+	YT_PHY_CHIP_MODE_FORCE_BX2500,
+	YT_PHY_CHIP_MODE_FIBER_FORCE_BX2500,
+	YT_PHY_CHIP_MODE_RESERVED
+};
+
+enum yt_port_lds_mode {
+	YT_LDS_MODE_LRE = 0,
+	YT_LDS_MODE_TX,
+	YT_LDS_MODE_BOTH_LRE_AND_TX,
+	YT_LDS_MODE_MAX,
+};
+
+struct yt_port_an_ability {
+	bool half_10_en;
+	bool full_10_en;
+	bool half_100_en;
+	bool full_100_en;
+	bool full_1000_en;
+	bool full_2500_en;
+	bool fc_en;
+	bool asy_fc_en;
+	enum yt_port_lds_mode lds_mode;
+};
+
+struct yt_port_link_status_all {
+	enum yt_port_link_status link_status;
+	enum yt_port_speed link_speed;
+	enum yt_port_duplex link_duplex;
+	bool rx_fc_en;
+	bool tx_fc_en;
+};
+
+struct yt_port_force_ctrl {
+	enum yt_port_speed_duplex speed_dup;
+	bool rx_fc_en;
+	bool tx_fc_en;
+};
+
+/**
+ * @internal      yt_port_init
+ * @endinternal
+ *
+ * @brief         Description
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_init(u8 unit);
+
+/**
+ * @internal      yt_port_mac_enable_set
+ * @endinternal
+ *
+ * @brief         disable or enable port,includes mac and phy(if phy exists)
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_mac_enable_set(u8 unit, u32 port, enum yt_enable enable);
+
+/**
+ * @internal      yt_port_mac_enable_get
+ * @endinternal
+ *
+ * @brief         get port enable status
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_mac_enable_get(u8 unit, u32 port, enum yt_enable *enable);
+
+/**
+ * @internal      yt_port_mac_force_set
+ * @endinternal
+ *
+ * @brief         set mac force control configuration.
+ *                should disable mac AN first.
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     port_ctrl           -force control configuration
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_mac_force_set(u8 unit, u32 port,
+			  struct yt_port_force_ctrl port_ctrl);
+
+/**
+ * @internal      yt_port_mac_force_get
+ * @endinternal
+ *
+ * @brief         get mac force control configuration
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    port_ctrl           -force control configuration
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_mac_force_get(u8 unit, u32 port,
+			  struct yt_port_force_ctrl *port_ctrl);
+
+/**
+ * @internal      yt_port_xmii_force_set
+ * @endinternal
+ *
+ * @brief         set xmii force control configuration.
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     force_ctrl          -force control configuration
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_xmii_force_set(u8 unit, u32 port,
+			   struct yt_port_force_ctrl force_ctrl);
+
+/**
+ * @internal      yt_port_xmii_force_get
+ * @endinternal
+ *
+ * @brief         get xmii force control configuration
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    force_ctrl          -force control configuration
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_xmii_force_get(u8 unit, u32 port,
+			   struct yt_port_force_ctrl *force_ctrl);
+
+/**
+ * @internal      yt_port_mac_eee_enable_set
+ * @endinternal
+ *
+ * @brief         enable EEE on port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_mac_eee_enable_set(u8 unit, u32 port, enum yt_enable enable);
+
+/**
+ * @internal      yt_port_extif_mode_set
+ * @endinternal
+ *
+ * @brief         set ext interface mode,XMII or SGMII etc.
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     mode                -MII,RGMII,SGMII etc.
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_extif_mode_set(u8 unit, u32 port, enum yt_extif_mode mode);
+
+/**
+ * @internal      yt_port_extif_mode_get
+ * @endinternal
+ *
+ * @brief         get ext interface mode
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    mode                -MII,RGMII,SGMII etc.
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_extif_mode_get(u8 unit, u32 port, enum yt_extif_mode *mode);
+
+/**
+ * @internal      yt_port_extif_rgmii_delay_set
+ * @endinternal
+ *
+ * @brief         set tx and rx delay value for rgmii.txc delay =
+ *                txc_2ns_en * 2ns + txc_delay * 0.15ns;
+ *                rxc delay = rxc_delay * 0.15ns
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     rxc_delay           -tx delay,range(0~15)
+ * @param[in]     txc_delay           -rx delay,range(0~15)
+ * @param[in]     txc_2ns_en          -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_extif_rgmii_delay_set(u8 unit, u32 port, u8 rxc_delay, u8 txc_delay,
+				  enum yt_enable txc_2ns_en);
+
+/**
+ * @internal      yt_port_extif_rgmii_delay_get
+ * @endinternal
+ *
+ * @brief         get tx and rx delay value of  rgmii
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    rxc_delay           -x
+ * @param[out]    txc_delay           -x
+ * @param[out]    txc_2ns_en          -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_extif_rgmii_delay_get(u8 unit, u32 port, u8 *rxc_delay,
+				  u8 *txc_delay, enum yt_enable *txc_2ns_en);
+
+/**
+ * @internal      yt_port_dvddio_power_level_set
+ * @endinternal
+ *
+ * @brief         set dvddio power level
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     power_pad           -powerpad
+ * @param[out]    power_level         -1.8v,2.5v,3.3v
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_dvddio_power_level_set(u8 unit, enum yt_dvddio_power_pad power_pad,
+				   enum yt_dvddio_power_level power_level);
+
+/**
+ * @internal      yt_port_dvddio_power_level_get
+ * @endinternal
+ *
+ * @brief         get dvddio power level
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     power_pad           -power_pad
+ * @param[out]    power_level         -1.8v,2.5v,3.3v
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_dvddio_power_level_get(u8 unit, enum yt_dvddio_power_pad power_pad,
+				   enum yt_dvddio_power_level *power_level);
+
+/**
+ * @internal      yt_port_extif_xmiiclk_invert_set
+ * @endinternal
+ *
+ * @brief         set extif xmii clk revert state
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    state               -invert state enable/disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_extif_xmiiclk_invert_set(u8 unit, u32 port, enum yt_enable state);
+
+/**
+ * @internal      yt_port_extif_xmiiclk_invert_get
+ * @endinternal
+ *
+ * @brief         get extif xmii clk revert state
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    state               -invert state enable/disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_extif_xmiiclk_invert_get(u8 unit, u32 port, enum yt_enable *state);
+
+#if defined(CONFIG_MOTORCOMM_YT921X) || defined(CONFIG_MOTORCOMM_YT922X)
+/**
+ * @internal      yt_port_jumbo_size_set
+ * @endinternal
+ *
+ * @brief          set jumbo frame size on port,9kB max length by default
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     size                -jumbo frame size
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_jumbo_size_set(u8 unit, u32 port, u32 size);
+
+/**
+ * @internal      yt_port_jumbo_size_get
+ * @endinternal
+ *
+ * @brief         get jumbo frame size on port
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    size                -jumbo frame size
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_jumbo_size_get(u8 unit, u32 port, u32 *size);
+
+/**
+ * @internal      yt_port_jumbo_enable_set
+ * @endinternal
+ *
+ * @brief         enable jumbo frame on port,9kB max length by default
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_jumbo_enable_set(u8 unit, u32 port, enum yt_enable enable);
+
+/**
+ * @internal      yt_port_jumbo_enable_get
+ * @endinternal
+ *
+ * @brief         get enable status of jumbo frame on port
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_jumbo_enable_get(u8 unit, u32 port, enum yt_enable *enable);
+
+#else
+
+/**
+ * @internal      yt_port_jumbo_size_set
+ * @endinternal
+ *
+ * @brief          set jumbo frame size on port,9kB max length by default
+ * @note          APPLICABLE DEVICES  -Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     size                -jumbo frame size
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_jumbo_size_set(u8 unit, u32 size);
+
+/**
+ * @internal      yt_port_jumbo_size_get
+ * @endinternal
+ *
+ * @brief         get jumbo frame size on port
+ * @note          APPLICABLE DEVICES  -Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    size                -jumbo frame size
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_jumbo_size_get(u8 unit, u32 *size);
+
+/**
+ * @internal      yt_port_jumbo_enable_set
+ * @endinternal
+ *
+ * @brief         enable jumbo frame on port,9kB max length by default
+ * @note          APPLICABLE DEVICES  -Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_jumbo_enable_set(u8 unit, enum yt_enable enable);
+
+/**
+ * @internal      yt_port_jumbo_enable_get
+ * @endinternal
+ *
+ * @brief         get enable status of jumbo frame on port
+ * @note          APPLICABLE DEVICES  -Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_jumbo_enable_get(u8 unit, enum yt_enable *enable);
+#endif
+
+/**
+ * @internal      yt_port_link_status_all_get
+ * @endinternal
+ *
+ * @brief         get full status of port,includes link,speed,duplex etc.
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    link_status         -all link status
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_link_status_all_get(u8 unit, u32 port,
+				struct yt_port_link_status_all *link_status);
+
+/**
+ * @internal      yt_port_phy_enable_set
+ * @endinternal
+ *
+ * @brief         disable or enable phy
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     phy_mode            -copper, phy, combo
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_enable_set(u8 unit, u32 port, enum yt_phy_chip_mode phy_mode,
+			   enum yt_enable enable);
+
+/**
+ * @internal      yt_port_phy_enable_get
+ * @endinternal
+ *
+ * @brief         get port enable status phy
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     phy_mode            -copper, phy
+ * @param[out]    enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_enable_get(u8 unit, u32 port, enum yt_phy_chip_mode phy_mode,
+			   enum yt_enable *enable);
+
+/**
+ * @internal      yt_port_phy_reg_set
+ * @endinternal
+ *
+ * @brief         set phy register data include lds,t1,tx regspace
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     reg_attr            -reg attribute
+ * @param[in]     data                -x
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_reg_set(u8 unit, u32 port, struct yt_phy_reg_attr reg_attr,
+			u16 data);
+
+/**
+ * @internal      yt_port_phy_reg_get
+ * @endinternal
+ *
+ * @brief         get phy register data include lds, t1, tx reg space
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     reg_attr            -reg attribute
+ * @param[out]    data                -x
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_reg_get(u8 unit, u32 port, struct yt_phy_reg_attr reg_attr,
+			u16 *data);
+
+/**
+ * @internal      yt_port_phy_auto_neg_ability_set
+ * @endinternal
+ *
+ * @brief         set phy AN ability configuration
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     phy_mode            -copper, phy, combo
+ * @param[in]     ability             -AN ability configuration
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_auto_neg_ability_set(u8 unit, u32 port,
+				     enum yt_phy_chip_mode phy_mode,
+				     struct yt_port_an_ability ability);
+
+/**
+ * @internal      yt_port_phy_auto_neg_ability_get
+ * @endinternal
+ *
+ * @brief         get phy AN ability configuration
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     phy_mode            -copper, phy
+ * @param[out]    ability             -AN ability configuration
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_auto_neg_ability_get(u8 unit, u32 port,
+				     enum yt_phy_chip_mode phy_mode,
+				     struct yt_port_an_ability *ability);
+
+/**
+ * @internal      yt_port_phy_auto_neg_set
+ * @endinternal
+ *
+ * @brief         set phy AN enable status
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_auto_neg_set(u8 unit, u32 port, enum yt_enable enable);
+
+/**
+ * @internal      yt_port_phy_auto_neg_get
+ * @endinternal
+ *
+ * @brief         get phy AN enable status
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_auto_neg_get(u8 unit, u32 port, enum yt_enable *enable);
+
+/**
+ * @internal      yt_port_phy_force_speed_set
+ * @endinternal
+ *
+ * @brief         set phy force speed and duplex, phy include phy and sds.
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     phy_mode            -copper, phy, combo
+ * @param[in]     speed_dup           -speed and duplex
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_force_speed_set(u8 unit, u32 port,
+				enum yt_port_speed_duplex speed_dup);
+
+/**
+ * @internal      yt_port_phy_force_speed_get
+ * @endinternal
+ *
+ * @brief         get phy force speed and duplex configuration,
+ *                phy include phy and sds.
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    speed_dup           -speed and duplex
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_force_speed_get(u8 unit, u32 port,
+				enum yt_port_speed_duplex *speed_dup);
+
+/**
+ * @internal      yt_port_phy_cross_mode_set
+ * @endinternal
+ *
+ * @brief         set utp crossover mode
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     mode                -utp crossover mode--mdi,mdix,auto
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_cross_mode_set(u8 unit, u32 port,
+			       enum yt_utp_crossover_mode mode);
+
+/**
+ * @internal      yt_port_phy_cross_mode_get
+ * @endinternal
+ *
+ * @brief         get utp crossover mode
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    mode                -utp crossover mode--mdi,mdix,auto
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_cross_mode_get(u8 unit, u32 port,
+			       enum yt_utp_crossover_mode *mode);
+
+/**
+ * @internal      yt_port_phy_template_set
+ * @endinternal
+ *
+ * @brief         set utp template test mode.
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     mode                -different test mode,refer
+ *                                     yt_utp_template_testmode_t for details.
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_template_set(u8 unit, u32 port,
+			     enum yt_utp_template_testmode mode);
+
+/**
+ * @internal      yt_port_phy_loopback_set
+ * @endinternal
+ *
+ * @brief         set phy loopback mode.Note:use yt_port_phy_force_set() to
+ *                set port speed if needed.
+ * @note          APPLICABLE DEVICES  -Tiger
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     phy_mode            -copper, phy, combo
+ * @param[in]     mode                -loopback mode
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_port_phy_loopback_set(u8 unit, u32 port, enum yt_phy_chip_mode phy_mode,
+			     enum yt_phy_loopback_mode mode);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_qos.c b/drivers/net/dsa/motorcomm/switch/yt_qos.c
new file mode 100644
index 000000000000..270d36f99b70
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_qos.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_init.h"
+#include "yt_qos.h"
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+static u32 yt_qos_920x_intpri_prece_set(u8 unit, u32 port,
+					struct yt_qos_intpri_prece *pri_sel_ptr)
+{
+	u32 offset = 0;
+	u32 entry = 0;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid),
+					&entry), ret);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SMAC) {
+		CMM_PARAM_CHK(pri_sel_ptr->smac_pri > 7, CMM_ERR_INPUT);
+		offset = 0;
+		entry = CLR_FIELD(entry, offset, 4);
+		entry |= (pri_sel_ptr->smac_pri << offset);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DMAC) {
+		CMM_PARAM_CHK(pri_sel_ptr->dmac_pri > 7, CMM_ERR_INPUT);
+		offset = 4;
+		entry = CLR_FIELD(entry, offset, 4);
+		entry |= (pri_sel_ptr->dmac_pri << offset);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_VLAN) {
+		CMM_PARAM_CHK(pri_sel_ptr->vlan_pri > 7, CMM_ERR_INPUT);
+		offset = 8;
+		entry = CLR_FIELD(entry, offset, 4);
+		entry |= (pri_sel_ptr->vlan_pri << offset);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_ACL) {
+		CMM_PARAM_CHK(pri_sel_ptr->acl_pri > 7, CMM_ERR_INPUT);
+		offset = 12;
+		entry = CLR_FIELD(entry, offset, 4);
+		entry |= (pri_sel_ptr->acl_pri << offset);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DSCP) {
+		CMM_PARAM_CHK(pri_sel_ptr->dscp_pri > 7, CMM_ERR_INPUT);
+		offset = 16;
+		entry = CLR_FIELD(entry, offset, 4);
+		entry |= (pri_sel_ptr->dscp_pri << offset);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_CPRI) {
+		CMM_PARAM_CHK(pri_sel_ptr->cpri > 7, CMM_ERR_INPUT);
+		offset = 20U;
+		entry = CLR_FIELD(entry, offset, 4);
+		entry |= (pri_sel_ptr->cpri << offset);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SPRI) {
+		CMM_PARAM_CHK(pri_sel_ptr->spri > 7, CMM_ERR_INPUT);
+		offset = 24;
+		entry = CLR_FIELD(entry, offset, 4);
+		entry |= (pri_sel_ptr->spri << offset);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_PORT) {
+		CMM_PARAM_CHK(pri_sel_ptr->port_pri > 7, CMM_ERR_INPUT);
+		offset = 28;
+		entry = CLR_FIELD(entry, offset, 4);
+		entry |= (pri_sel_ptr->port_pri << offset);
+	}
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid), entry), ret);
+	return ret;
+}
+
+static u32 yt_qos_920x_intpri_prece_get(u8 unit, u32 port,
+					struct yt_qos_intpri_prece *pri_sel_ptr)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid),
+					&entry), ret);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SMAC)
+		pri_sel_ptr->smac_pri = GET_FIELD(entry, 0, 4);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DMAC)
+		pri_sel_ptr->dmac_pri = GET_FIELD(entry, 4, 4);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_VLAN)
+		pri_sel_ptr->vlan_pri = GET_FIELD(entry, 8, 4);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_ACL)
+		pri_sel_ptr->acl_pri = GET_FIELD(entry, 12, 4);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DSCP)
+		pri_sel_ptr->dscp_pri = GET_FIELD(entry, 16, 4);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_CPRI)
+		pri_sel_ptr->cpri = GET_FIELD(entry, 20, 4);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SPRI)
+		pri_sel_ptr->spri = GET_FIELD(entry, 24, 4);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_PORT)
+		pri_sel_ptr->port_pri = GET_FIELD(entry, 28, 4);
+	return ret;
+}
+
+u32 yt_qos_intpri_sel_precedence_set(u8 unit, u32 port,
+				     struct yt_qos_intpri_prece *pri_sel_ptr)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	u32 entry = 0;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	yt_cal_board_chip_get(&chip_id);
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pri_sel_ptr->flags & (YT_QOS_SEL_PRECEDENCE_FLAG_INTCPRI |
+				  YT_QOS_SEL_PRECEDENCE_FLAG_INTSPRI))
+		return CMM_ERR_NOT_SUPPORT;
+
+	if (chip_id == YT_SW_ID_920X) {
+		CMM_ERR_CHK(yt_qos_920x_intpri_prece_set(unit, port,
+							 pri_sel_ptr), ret);
+		return ret;
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid),
+					&entry),
+		    ret);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SMAC) {
+		CMM_PARAM_CHK(pri_sel_ptr->smac_pri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_SMAC2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_SMAC2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->smac_pri);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DMAC) {
+		CMM_PARAM_CHK(pri_sel_ptr->dmac_pri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_DMAC2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_DMAC2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->dmac_pri);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_VLAN) {
+		CMM_PARAM_CHK(pri_sel_ptr->vlan_pri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_VLAN2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_VLAN2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->vlan_pri);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_ACL) {
+		CMM_PARAM_CHK(pri_sel_ptr->acl_pri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->acl_pri);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DSCP) {
+		CMM_PARAM_CHK(pri_sel_ptr->dscp_pri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->dscp_pri);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_CPRI) {
+		CMM_PARAM_CHK(pri_sel_ptr->cpri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->cpri);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SPRI) {
+		CMM_PARAM_CHK(pri_sel_ptr->spri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->spri);
+	}
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_PORT) {
+		CMM_PARAM_CHK(pri_sel_ptr->port_pri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->port_pri);
+	}
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid), entry), ret);
+	return CMM_ERR_OK;
+}
+EXPORT_SYMBOL(yt_qos_intpri_sel_precedence_set);
+
+u32 yt_qos_intpri_sel_precedence_get(u8 unit, u32 port,
+				     struct yt_qos_intpri_prece *pri_sel_ptr)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	u32 entry;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (pri_sel_ptr->flags & (YT_QOS_SEL_PRECEDENCE_FLAG_INTCPRI |
+				  YT_QOS_SEL_PRECEDENCE_FLAG_INTSPRI))
+		return CMM_ERR_NOT_SUPPORT;
+
+	if (chip_id == YT_SW_ID_920X) {
+		CMM_ERR_CHK(yt_qos_920x_intpri_prece_get(unit, port,
+							 pri_sel_ptr), ret);
+		return ret;
+	}
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid),
+					&entry), ret);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SMAC)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_SMAC2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_SMAC2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->smac_pri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DMAC)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_DMAC2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_DMAC2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->dmac_pri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_VLAN)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_VLAN2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_VLAN2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->vlan_pri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_ACL)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->acl_pri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DSCP)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->dscp_pri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_CPRI)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->cpri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SPRI)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->spri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_PORT)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->port_pri);
+	return ret;
+}
+EXPORT_SYMBOL(yt_qos_intpri_sel_precedence_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+u32 yt_qos_intpri_sel_precedence_set(u8 unit, u32 port,
+				     struct yt_qos_intpri_prece *pri_sel_ptr)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pri_sel_ptr->flags &
+	    (YT_QOS_SEL_PRECEDENCE_FLAG_SMAC | YT_QOS_SEL_PRECEDENCE_FLAG_DMAC |
+	     YT_QOS_SEL_PRECEDENCE_FLAG_VLAN))
+		return CMM_ERR_NOT_SUPPORT;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid),
+					&entry), ret);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_INTCPRI) {
+		CMM_PARAM_CHK(pri_sel_ptr->int_cpri > 7U, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_INTCPRI2INTPRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_INTCPRI2INTPRI_WIDTH,
+			      &entry, pri_sel_ptr->int_cpri);
+	}
+
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_INTSPRI) {
+		CMM_PARAM_CHK(pri_sel_ptr->int_spri > 7U, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_INTSPRI2INTPRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_INTSPRI2INTPRI_WIDTH,
+			      &entry, pri_sel_ptr->int_spri);
+	}
+
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_ACL) {
+		CMM_PARAM_CHK(pri_sel_ptr->acl_pri > 7U, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->acl_pri);
+	}
+
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DSCP) {
+		CMM_PARAM_CHK(pri_sel_ptr->dscp_pri > 7U, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->dscp_pri);
+	}
+
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_CPRI) {
+		CMM_PARAM_CHK(pri_sel_ptr->cpri > 7U, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->cpri);
+	}
+
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SPRI) {
+		CMM_PARAM_CHK(pri_sel_ptr->spri > 7, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->spri);
+	}
+
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_PORT) {
+		CMM_PARAM_CHK(pri_sel_ptr->port_pri > 7U, CMM_ERR_INPUT);
+		HAL_FIELD_SET(QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_WIDTH,
+			      &entry, pri_sel_ptr->port_pri);
+	}
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+		(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid), entry), ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_qos_intpri_sel_precedence_set);
+
+u32 yt_qos_intpri_sel_precedence_get(u8 unit, u32 port,
+				     struct yt_qos_intpri_prece *pri_sel_ptr)
+{
+	u32 entry;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	if (pri_sel_ptr->flags &
+	    (YT_QOS_SEL_PRECEDENCE_FLAG_SMAC | YT_QOS_SEL_PRECEDENCE_FLAG_DMAC |
+	     YT_QOS_SEL_PRECEDENCE_FLAG_VLAN))
+		return CMM_ERR_NOT_SUPPORT;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, QOS_MERGE_PRECEDENCE_CTRLN(macid),
+					&entry), ret);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_INTCPRI)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_INTCPRI2INTPRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_INTCPRI2INTPRI_WIDTH,
+			      &entry, &pri_sel_ptr->int_cpri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_INTSPRI)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_INTSPRI2INTPRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_INTSPRI2INTPRI_WIDTH,
+			      &entry, &pri_sel_ptr->int_spri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_ACL)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->acl_pri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_DSCP)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->dscp_pri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_CPRI)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->cpri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_SPRI)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->spri);
+	if (pri_sel_ptr->flags & YT_QOS_SEL_PRECEDENCE_FLAG_PORT)
+		HAL_FIELD_GET(QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_LOWBIT,
+			      QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_WIDTH,
+			      &entry, &pri_sel_ptr->port_pri);
+	return ret;
+}
+EXPORT_SYMBOL(yt_qos_intpri_sel_precedence_get);
+
+#endif
+
+u32 yt_qos_intpri_dscp_map_set(u8 unit, u8 dscp, u8 pri)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, DSCP2INT_PRI_MAPN(dscp), &entry),
+		    ret);
+	HAL_FIELD_SET(DSCP2INT_PRI_MAPN_INT_PRIO_LOWBIT,
+		      DSCP2INT_PRI_MAPN_INT_PRIO_WIDTH, &entry, pri);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, DSCP2INT_PRI_MAPN(dscp), entry),
+		    ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_qos_intpri_dscp_map_set);
+
+u32 yt_qos_intpri_dscp_map_get(u8 unit, u8 dscp, u8 *pri_ptr)
+{
+	u32 entry = 0;
+	u32 ret = CMM_ERR_OK;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, DSCP2INT_PRI_MAPN(dscp), &entry),
+		    ret);
+	HAL_FIELD_GET(DSCP2INT_PRI_MAPN_INT_PRIO_LOWBIT,
+		      DSCP2INT_PRI_MAPN_INT_PRIO_WIDTH, &entry, pri_ptr);
+	return ret;
+}
+EXPORT_SYMBOL(yt_qos_intpri_dscp_map_get);
+
+u32 yt_qos_intpri_portdefpri_set(u8 unit, u32 port, enum yt_enable enable,
+				 u8 pri)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, QOS_PORT_CTRLN(macid), &entry),
+		    ret);
+	HAL_FIELD_SET(QOS_PORT_CTRLN_PORT_INT_PRIO_LOWBIT,
+		      QOS_PORT_CTRLN_PORT_INT_PRIO_WIDTH, &entry, pri);
+	HAL_FIELD_SET(QOS_PORT_CTRLN_PORT_INT_PRIO_EN_LOWBIT,
+		      QOS_PORT_CTRLN_PORT_INT_PRIO_EN_WIDTH, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, QOS_PORT_CTRLN(macid), entry),
+		    ret);
+	return ret;
+}
+EXPORT_SYMBOL(yt_qos_intpri_portdefpri_set);
+
+u32 yt_qos_intpri_portdefpri_get(u8 unit, u32 port, enum yt_enable *enable_ptr,
+				 u8 *pri_ptr)
+{
+	u32 entry = 0;
+	u32 macid = 0;
+	u32 ret = CMM_ERR_OK;
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, QOS_PORT_CTRLN(macid), &entry),
+		    ret);
+	HAL_FIELD_GET(QOS_PORT_CTRLN_PORT_INT_PRIO_LOWBIT,
+		      QOS_PORT_CTRLN_PORT_INT_PRIO_WIDTH, &entry, pri_ptr);
+	HAL_FIELD_GET(QOS_PORT_CTRLN_PORT_INT_PRIO_EN_LOWBIT,
+		      QOS_PORT_CTRLN_PORT_INT_PRIO_EN_WIDTH, &entry,
+		      enable_ptr);
+	return ret;
+}
+EXPORT_SYMBOL(yt_qos_intpri_portdefpri_get);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_qos.h b/drivers/net/dsa/motorcomm/switch/yt_qos.h
new file mode 100644
index 000000000000..a7f071b24827
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_qos.h
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_QOS_H__
+#define __YT_QOS_H__
+
+#include "yt_cmm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct yt_qos_intpri_prece {
+	u32 flags;
+	u8 int_cpri;
+	u8 int_spri;
+	u8 smac_pri;
+	u8 dmac_pri;
+	u8 vlan_pri;
+	u8 acl_pri;
+	u8 dscp_pri;
+	u8 cpri;
+	u8 spri;
+	u8 port_pri;
+};
+
+#define YT_QOS_SEL_PRECEDENCE_FLAG_INTCPRI BIT(0)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_INTSPRI BIT(1)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_SMAC BIT(2)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_DMAC BIT(3)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_VLAN BIT(4)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_ACL BIT(5)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_DSCP BIT(6)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_CPRI BIT(7)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_SPRI BIT(8)
+#define YT_QOS_SEL_PRECEDENCE_FLAG_PORT BIT(9)
+#ifdef CONFIG_MOTORCOMM_YT921X
+#define YT_QOS_SEL_PRECEDENCE_FLAG_ALL                                       \
+	(YT_QOS_SEL_PRECEDENCE_FLAG_SMAC | YT_QOS_SEL_PRECEDENCE_FLAG_DMAC | \
+	 YT_QOS_SEL_PRECEDENCE_FLAG_VLAN | YT_QOS_SEL_PRECEDENCE_FLAG_ACL |  \
+	 YT_QOS_SEL_PRECEDENCE_FLAG_DSCP | YT_QOS_SEL_PRECEDENCE_FLAG_CPRI | \
+	 YT_QOS_SEL_PRECEDENCE_FLAG_SPRI | YT_QOS_SEL_PRECEDENCE_FLAG_PORT)
+#else
+#define YT_QOS_SEL_PRECEDENCE_FLAG_ALL                                       \
+	(YT_QOS_SEL_PRECEDENCE_FLAG_INTCPRI |                                \
+	 YT_QOS_SEL_PRECEDENCE_FLAG_INTSPRI |                                \
+	 YT_QOS_SEL_PRECEDENCE_FLAG_DSCP | YT_QOS_SEL_PRECEDENCE_FLAG_ACL |  \
+	 YT_QOS_SEL_PRECEDENCE_FLAG_SPRI | YT_QOS_SEL_PRECEDENCE_FLAG_CPRI | \
+	 YT_QOS_SEL_PRECEDENCE_FLAG_PORT)
+#endif
+
+/**
+ * @internal      yt_qos_intpri_sel_precedence_set
+ * @endinternal
+ *
+ * @brief         Config the priority between different priority mechanism.
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     pPriTbl             -priority select
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_qos_intpri_sel_precedence_set(u8 unit, u32 port,
+				     struct yt_qos_intpri_prece *pri_sel_ptr);
+
+/**
+ * @internal      yt_qos_intpri_sel_precedence_get
+ * @endinternal
+ *
+ * @brief         Get the priority configuration.
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    pPriTbl             -priority select
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_qos_intpri_sel_precedence_get(u8 unit, u32 port,
+				     struct yt_qos_intpri_prece *pri_sel_ptr);
+
+/**
+ * @internal      yt_qos_intpri_dscp_map_set
+ * @endinternal
+ *
+ * @brief         Map dscp value to internal priority.
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     dscp                -the dscp value (0 - MAX_DSCP_VALUE)
+ * @param[in]     pri                 -internal priority (0 - MAX_PRIORITY)
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_qos_intpri_dscp_map_set(u8 unit, u8 dscp, u8 pri);
+
+/**
+ * @internal      yt_qos_intpri_dscp_map_get
+ * @endinternal
+ *
+ * @brief         Get dscp value to internal priority.
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     dscp                -the dscp value (0 - MAX_DSCP_VALUE)
+ * @param[out]    pPri                -internal priority (0 - MAX_PRIORITY)
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_qos_intpri_dscp_map_get(u8 unit, u8 dscp, u8 *pri_ptr);
+
+/**
+ * @internal      yt_qos_intpri_portdefpri_set
+ * @endinternal
+ *
+ * @brief         Map port default value to internal priority.
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port id
+ * @param[in]     enable              -YT_ENABLE/YT_DISABLE
+ * @param[in]     pri                 -internal priority(0 - MAX_PRIORITY)
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_qos_intpri_portdefpri_set(u8 unit, u32 port, enum yt_enable enable,
+				 u8 pri);
+
+/**
+ * @internal      yt_qos_intpri_portdefpri_get
+ * @endinternal
+ *
+ * @brief         Get port default value to internal priority.
+ * @note          APPLICABLE DEVICES  -Tiger, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port id
+ * @param[out]    enable_ptr           -YT_ENABLE/YT_DISABLE
+ * @param[out]    pri_ptr             -internal priority (0 - MAX_PRIORITY)
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_qos_intpri_portdefpri_get(u8 unit, u32 port, enum yt_enable *enable_ptr,
+				 u8 *pri_ptr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_reg_921x.h b/drivers/net/dsa/motorcomm/switch/yt_reg_921x.h
new file mode 100644
index 000000000000..357ca0b65dd8
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_reg_921x.h
@@ -0,0 +1,422 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_REG_921X_H
+#define __YT_REG_921X_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Register Configuration */
+#if defined(CONFIG_MOTORCOMM_YT921X)
+/* Global module define start */
+#define GLOBAL_CTRL0 (0x80000)
+#define GLOBAL_CTRL0_CHIP_SW_RST_LOWBIT 1
+#define GLOBAL_CTRL0_CHIP_SW_RST_WIDTH 1
+
+#define GLOBAL_CTRL1 (0x80004)
+#define GLOBAL_CTRL1_MIB_EN_LOWBIT 1
+#define GLOBAL_CTRL1_MIB_EN_WIDTH 1
+#define GLOBAL_CTRL1_METER_EN_LOWBIT 4
+#define GLOBAL_CTRL1_METER_EN_WIDTH 1
+
+#define CHIP_INTERFACE_CTRL_REG (0x80028)
+#define CHIP_CHIP_ID_REG (0x80008)
+#define CHIP_TOPBIAS_REG (0x803A0)
+
+/* Interrupt polarity */
+#define INT_POL_L 0
+#define INT_POL_W 1
+
+#define PON_STRAP_EN (0x80320)
+#define PON_STRAP_VAL (0x80324)
+#define PON_STRAP (0x80328)
+
+#define L2_LEARN_PER_PORT_CTRLN (0x1803d0)
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT 17
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH 1
+/* Global module define end */
+
+/* L2 module define start */
+/*data 0*/
+#define L2_FDB_TBL_OP_DATA_0 (0x180454)
+#define L2_FDB_TBL_OP_DATA_0_DUMMY (0x1804b0)
+/*uc & mc*/
+#define L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT 0
+#define L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH 32
+
+/*data 1*/
+#define L2_FDB_TBL_OP_DATA_1 (0x180458)
+#define L2_FDB_TBL_OP_DATA_1_DUMMY (0x1804b4)
+/*uc */
+#define L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT 0
+#define L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH 16
+#define L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT 16
+#define L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH 12
+#define L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT 28
+#define L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH 3
+#define L2_FDB_TBL_MAC_OP_DATA_1_DMAC_INT_PRI_EN_LOWBIT 31
+#define L2_FDB_TBL_MAC_OP_DATA_1_DMAC_INT_PRI_EN_WIDTH 1
+
+/*data 2*/
+#define L2_FDB_TBL_OP_DATA_2 (0x18045c)
+#define L2_FDB_TBL_OP_DATA_2_DUMMY (0x1804b8)
+/*uc*/
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_LOWBIT 0
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_WIDTH 12
+#define L2_FDB_TBL_OP_DATA_2_INT_PRI_LOWBIT 12
+#define L2_FDB_TBL_OP_DATA_2_INT_PRI_WIDTH 3
+#define L2_FDB_TBL_OP_DATA_2_SMAC_INT_PRI_EN_LOWBIT 15
+#define L2_FDB_TBL_OP_DATA_2_SMAC_INT_PRI_EN_WIDTH 1
+#define L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_LOWBIT 16
+#define L2_FDB_TBL_OP_DATA_2_COPY_TO_CPU_WIDTH 1
+#define L2_FDB_TBL_OP_DATA_2_DMAC_DROP_LOWBIT 17
+#define L2_FDB_TBL_OP_DATA_2_DMAC_DROP_WIDTH 1
+#define L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_LOWBIT 18
+#define L2_FDB_TBL_OP_DATA_2_DST_PORT_MASK_WIDTH 11
+#define L2_FDB_TBL_OP_DATA_2_SMAC_DROP_LOWBIT 29
+#define L2_FDB_TBL_OP_DATA_2_SMAC_DROP_WIDTH 1
+#define L2_FDB_TBL_OP_DATA_2_MOVE_AGING_STATUS_LOWBIT 30
+#define L2_FDB_TBL_OP_DATA_2_MOVE_AGING_STATUS_WIDTH 2
+
+#define L2_FDB_TBL_OP (0x180460)
+#define L2_FDB_TBL_OP_OP_START_LOWBIT 0
+#define L2_FDB_TBL_OP_OP_START_WIDTH 1
+#define L2_FDB_TBL_OP_OP_CMD_LOWBIT 1
+#define L2_FDB_TBL_OP_OP_CMD_WIDTH 3
+#define L2_FDB_TBL_OP_GET_NEXT_TYPE_LOWBIT 4
+#define L2_FDB_TBL_OP_GET_NEXT_TYPE_WIDTH 2
+#define L2_FDB_TBL_OP_FLUSH_STATIC_EN_LOWBIT 6
+#define L2_FDB_TBL_OP_FLUSH_STATIC_EN_WIDTH 1
+#define L2_FDB_TBL_OP_FLUSH_MODE_LOWBIT 7
+#define L2_FDB_TBL_OP_FLUSH_MODE_WIDTH 3
+#define L2_FDB_TBL_OP_OP_MODE_LOWBIT 10
+#define L2_FDB_TBL_OP_OP_MODE_WIDTH 1
+#define L2_FDB_TBL_OP_ENTRY_INDEX_LOWBIT 11
+#define L2_FDB_TBL_OP_ENTRY_INDEX_WIDTH 12
+
+#define L2_FDB_TBL_OP_RESULT (0x180464)
+#define L2_FDB_TBL_OP_RESULT_ENTRY_INDEX_LOWBIT 0
+#define L2_FDB_TBL_OP_RESULT_ENTRY_INDEX_WIDTH 12
+#define L2_FDB_TBL_OP_RESULT_OP_RESULT_LOWBIT 12
+#define L2_FDB_TBL_OP_RESULT_OP_RESULT_WIDTH 1
+#define L2_FDB_TBL_OP_RESULT_OVERWRITE_LOWBIT 13
+#define L2_FDB_TBL_OP_RESULT_OVERWRITE_WIDTH 1
+#define L2_FDB_TBL_OP_RESULT_LOOKUP_FAIL_LOWBIT 14
+#define L2_FDB_TBL_OP_RESULT_LOOKUP_FAIL_WIDTH 1
+#define L2_FDB_TBL_OP_RESULT_OP_DONE_LOWBIT 15
+#define L2_FDB_TBL_OP_RESULT_OP_DONE_WIDTH 1
+
+#define L2_AGING_CTRL (0x180440)
+#define L2_AGING_CTRL_AGING_INTERVAL_LOWBIT 0
+#define L2_AGING_CTRL_AGING_INTERVAL_WIDTH 16
+
+#define L2_LEARN_PER_PORT_CTRL (0x1803d0)
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABL_LOWBIT 17
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABL_WIDTH 1
+/* L2 module define end */
+
+/* VLAN module define start */
+#define L2_VLAN_TBL (0x188000)
+#define VLAN_UNTAG_TBL (0x0)
+#define L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT 7
+#define L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH 11
+#define L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT 8
+#define L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH 11
+
+#define PORT_VLAN_CTRLN (0x230010)
+#define PORT_VLAN_CTRLN_DEFAULT_SVID_LOWBIT 18
+#define PORT_VLAN_CTRLN_DEFAULT_SVID_WIDTH 12
+#define PORT_VLAN_CTRLN_DEFAULT_CVID_LOWBIT 6
+#define PORT_VLAN_CTRLN_DEFAULT_CVID_WIDTH 12
+
+#define L2_VLAN_INGRESS_FILTER_EN (0x180280)
+#define L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_LOWBIT 0
+#define L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_WIDTH 11
+
+#define L2_EGR_VLAN_FILTER_EN (0x180598)
+#define L2_EGR_VLAN_FILTER_EN_FILTER_EN_LOWBIT 0
+#define L2_EGR_VLAN_FILTER_EN_FILTER_EN_WIDTH 11
+
+#define EGR_PORT_VLAN_CTRLN (0x100080)
+#define EGR_PORT_VLAN_CTRLN_CTAG_MODE_LOWBIT 12
+#define EGR_PORT_VLAN_CTRLN_CTAG_MODE_WIDTH 3
+#define EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_LOWBIT 30
+#define EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_WIDTH 1
+#define EGR_PORT_VLAN_CTRLN_STAG_MODE_LOWBIT 27
+#define EGR_PORT_VLAN_CTRLN_STAG_MODE_WIDTH 3
+#define EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_LOWBIT 31
+#define EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_WIDTH 1
+
+#define EGR_TPID_PROFILE (0x100300)
+
+#define TPID_PROFILE0 (0x210000)
+#define TPID_PROFILE0_TPID_LOWBIT 0
+#define TPID_PROFILE0_TPID_WIDTH 16
+
+#define TPID_PROFILE1 (0x210008)
+#define TPID_PROFILE1_TPID_LOWBIT 0
+#define TPID_PROFILE1_TPID_WIDTH 16
+
+#define PARSER_PORT_CTRLN(idx) (0x210010U + (0x4U) * idx)
+#define PARSER_PORT_CTRLN_CTAG_TPID_MASK_LOWBIT 0
+#define PARSER_PORT_CTRLN_CTAG_TPID_MASK_WIDTH 4
+#define PARSER_PORT_CTRLN_STAG_TPID_MASK_LOWBIT 4
+#define PARSER_PORT_CTRLN_STAG_TPID_MASK_WIDTH 4
+
+#define PORT_VLAN_CTRL1N (0x230080)
+#define PORT_VLAN_CTRL1N_CTAG_AFT_LOWBIT 0
+#define PORT_VLAN_CTRL1N_CTAG_AFT_WIDTH 2
+#define PORT_VLAN_CTRL1N_STAG_AFT_LOWBIT 2
+#define PORT_VLAN_CTRL1N_STAG_AFT_WIDTH 2
+/* VLAN module define end */
+
+/* STAT module define start */
+#define MIB_CTRL_ADDR (0xc0000)
+#define MIB_OP_ADDR (0xc0004)
+#define MIB_TBL_BASE_ADDR0 (0xc0100)
+/* STAT module define end */
+
+/* STP module define start */
+#define L2_STP_STATEN (0x18038c)
+#define L2_STP_STATEN_STATE_LOWBIT 0
+#define L2_STP_STATEN_STATE_WIDTH 22
+/* STP module define end */
+
+/* Loopdetect module define start */
+#define L2_LOOP_DETECT_FLAG_DUMMY (0x180814)
+#define LOOP_DETECT_TOP_CTRL (0x80230)
+#define LOOP_DETECT_TOP_CTRL_EN_LOWBIT (18)
+#define LOOP_DETECT_TOP_CTRL_EN_WIDTH (1)
+
+#define LOOP_DETECT_CTRL1 (0x8023c)
+/* Loopdetect module define end */
+
+/* storm ctrl module define start */
+#define STORM_CTRL_TIMESLOT (0x220100)
+#define STORM_CTRL_TIMESLOT_TIMESLOT_LOWBIT (0)
+#define STORM_CTRL_TIMESLOT_TIMESLOT_WIDTH (12)
+
+#define STORM_CTRL_CONFIG_TBL (0x220200)
+#define STORM_CTRL_CONFIG_TBL_STORM_EN_LOWBIT (0)
+#define STORM_CTRL_CONFIG_TBL_STORM_EN_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_RATE_MODE_LOWBIT (1)
+#define STORM_CTRL_CONFIG_TBL_RATE_MODE_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_GAP_INC_LOWBIT (2)
+#define STORM_CTRL_CONFIG_TBL_GAP_INC_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_CIR_LOWBIT (13)
+#define STORM_CTRL_CONFIG_TBL_CIR_WIDTH (19)
+#define STORM_CTRL_CONFIG_TBL_CBS_LOWBIT (3)
+#define STORM_CTRL_CONFIG_TBL_CBS_WIDTH (10)
+
+#define STORM_CTRL_MC_TYPE_CTRL (0x220140)
+#define STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT (0)
+#define STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH (11)
+/* storm ctrl module define end */
+
+/* QoS module define start */
+#define CAL_MAX_UCAST_QUEUE_NUM (8)
+#define CAL_MAX_MCAST_QUEUE_NUM (4)
+#define QOS_FORCEAC_UCASTQUE_REG(unit, macid, qid) \
+	(0x301000 + ((macid) * CAL_MAX_UCAST_QUEUE_NUM * 8) + ((qid) * 8))
+#define QOS_FORCEAC_MCASTQUE_REG(unit, macid, qid) \
+	(0x302000 + ((macid) * CAL_MAX_MCAST_QUEUE_NUM * 4) + ((qid) * 4))
+/* QoS module define end */
+
+/* Mirror module define start */
+#define MIRROR_CTRL (0x300300)
+#define MIRROR_CTRL_MIRROR_PORT_LOWBIT 0
+#define MIRROR_CTRL_MIRROR_PORT_WIDTH 4
+#define MIRROR_CTRL_EGR_MIRROR_EN_LOWBIT 4
+#define MIRROR_CTRL_EGR_MIRROR_EN_WIDTH 11
+#define MIRROR_CTRL_INGR_MIRROR_EN_LOWBIT 16
+#define MIRROR_CTRL_INGR_MIRROR_EN_WIDTH 11
+/* Mirror module define end */
+
+/* NIC module define start */
+#define CPU_COPY_DST_CTRL (0x180690)
+#define CPU_COPY_DST_CTRL_TO_INT_CPU_LOWBIT 1
+#define CPU_COPY_DST_CTRL_TO_INT_CPU_WIDTH 1
+#define CPU_COPY_DST_CTRL_TO_EXT_CPU_LOWBIT 0
+#define CPU_COPY_DST_CTRL_TO_EXT_CPU_WIDTH 1
+
+#define EXT_CPU_PORT_CTRL (0x8000c)
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_LOWBIT 14
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_WIDTH 1
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_LOWBIT 0
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_WIDTH 4
+#define EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_LOWBIT 15
+#define EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_WIDTH 1
+
+#define PORT_CTRL (0x80100)
+#define PORT_CTRL_HALF_FC_EN_LOWBIT 8
+#define PORT_CTRL_HALF_FC_EN_WIDTH 1
+
+#define CPU_TAG_TPID (0x80010)
+#define CPU_TAG_TPID_TPID_LOWBIT 0
+#define CPU_TAG_TPID_TPID_WIDTH 16
+
+#define CPU_PKT_BYPASSEDIT_CTRL (0x1004d4)
+#define CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_LOWBIT 0
+#define CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_WIDTH 1
+#define CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_LOWBIT 1
+#define CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_WIDTH 1
+/* NIC module define end */
+
+/* LED module define start */
+#define LED_CTRL_0_BASE (0xd0004)
+#define LED_CTRL_0_BASE_DISABLE_LED_LINK_TRY_LOWBIT 17
+#define LED_CTRL_0_BASE_DISABLE_LED_LINK_TRY_WIDTH 1
+#define LED_CTRL_0_BASE_LED_ACTION_LOWBIT 0
+#define LED_CTRL_0_BASE_LED_ACTION_WIDTH 18
+
+#define LED_CTRL_1_BASE (0xd0040)
+#define LED_CTRL_1_BASE_LED_ACTION_LOWBIT 0
+#define LED_CTRL_1_BASE_LED_ACTION_WIDTH 16
+
+#define LED_CTRL_2_BASE (0xd0080)
+#define LED_CTRL_2_BASE_LED_ACTION_LOWBIT 0
+#define LED_CTRL_2_BASE_LED_ACTION_WIDTH 16
+
+#define LED_GLB_CTRL (0xd0000)
+#define LED_GLB_CTRL_LED_CONFIG_DONE_LOWBIT 21
+#define LED_GLB_CTRL_LED_CONFIG_DONE_WIDTH 1
+#define LED_GLB_CTRL_LED_MODE_LOWBIT 0
+#define LED_GLB_CTRL_LED_MODE_WIDTH 2
+#define LED_GLB_CTRL_PORT_NUM_LOWBIT 13
+#define LED_GLB_CTRL_PORT_NUM_WIDTH 4
+
+#define LED_SERIAL_CTRL (0xd0100)
+#define LED_SERIAL_CTRL_LED_NUM_LOWBIT 0
+#define LED_SERIAL_CTRL_LED_NUM_WIDTH 2
+#define LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT 4
+#define LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH 1
+#define LED_SERIAL_REMAPPING_BASE (0xd0104)
+#define LED_PARALLEL_REMAPPING_BASE (0xd01d0)
+
+#define LED_PARALLEL_OUTPUT_CTRL (0xd01c4)
+#define LED_PARALLEL_OUTPUT_CTRL_MODE_LOWBIT 0
+#define LED_PARALLEL_OUTPUT_CTRL_MODE_WIDTH 10
+
+#define LED_PARALLEL_POS_INVERT_CTRL (0xd01c8)
+#define LED_PARALLEL_POS (0x803c0)
+#define LED_PIN_MUX_CTRL (0x80002018)
+#define LED_CTRL7 (0x8000201c)
+#define LED_CTRL7_LED_NUM_LOWBIT 14
+#define LED_CTRL7_LED_NUM_WIDTH 1
+/* LED module define end */
+
+/* Port module define start */
+#define MAX_BUSYING_WAIT_TIME (200)
+#define EXT_IF_INDEX_OFFSET (0x1000)
+#define INT_IF_FRAME_CTRL (0xf0000)
+#define INT_IF_ADDR_CTRL (0xf0004)
+#define INT_IF_DATA_0_ADDR (0xf0008)
+#define INT_IF_DATA_1_ADDR (0xf000c)
+#define EXT_IF_FRAME_CTRL (0x6a000)
+#define EXT_IF_ADDR_CTRL (0x6a004)
+#define EXT_IF_DATA_0_ADDR (0x6a008)
+#define EXT_IF_DATA_1_ADDR (0x6a00c)
+#define PHY_BASEADDR_OFFSET (0x1000)
+#define PHY_BASEADDR_T1(idx) (0x80020000 + ((idx) * PHY_BASEADDR_OFFSET))
+#define PHY_BASEADDR_TX(idx) (0x80024000 + ((idx) * PHY_BASEADDR_OFFSET))
+#define PHY_BASEADDR_LDS(idx) (0x80028000 + ((idx) * PHY_BASEADDR_OFFSET))
+#define PHY_BASEADDR_SGMII (0x8000E000)
+#define PHY_BASEADDR_FE_TOP (0x8002F000)
+#define PHY_BASEADDR_XMII_EXT (0x8000C000)
+#define CHIP_INTERFACE_XMII_MODE (0x80000000)
+#define CHIP_INTERFACE_XMII_PINMUX (0x8000201c)
+#define CHIP_INTERFACE_SDS_MODE (0x8008c)
+#define YT_JUMBO_SIZE_MAX 0x2400
+#define YT_JUMBO_SIZE_MIN 0x5EE
+#define YT_PKTGAP_SIZE_MAX 0x1F
+#define YT_PKTGAP_SIZE_MIN 0x0
+#define EXTIF0_MODE 0x80400
+#define EXTIF1_MODE 0x80408
+#define SG_PHY 0x8008c
+#define PORT_STATUS 0x80200
+
+#define CHIP_INTERFACE_SELECT_REG (0x80394)
+#define CHIP_INTERFACE_CTRL_REG (0x80028)
+#define CHIP_PORT_CTRL (0x80100)
+#define CHIP_IO_LEVEL_MODE_REG (0x80030)
+#define CHIP_MODE_SEL_REG (0x80358)
+#define CHIP_PAD_PART_REG(N) (0x803b0 + (0x4) * (N))
+#define CHIP_DCC_CALIB_CLK_SEL_REG (0x8d01c)
+/* Port module define end */
+
+/* Interrupt module define end */
+#define INTR_MASK (0x80040)
+#define INTR_STATUS (0x80044)
+/* Interrupt module define end */
+
+/* ctrl pkt define start */
+#define L2_ARP_PER_PORT_CTRL (0x180284)
+#define L2_ND_PER_PORT_CTRL (0x180288)
+#define L2_LLDP_PER_PORT_CTRL (0x180290)
+#define L2_LLDP_EEE_PER_PORT_CTRL (0x18028c)
+/* ctrl pkt define end */
+
+/* port isolation define start */
+#define L2_PORT_ISOLATION_CTRLN(idx) (0x180294 + (0x4) * (idx))
+#define L2_PORT_ISOLATION_CTRLN_PORTMASK_LOWBIT 0
+#define L2_PORT_ISOLATION_CTRLN_PORTMASK_WIDTH 11
+/* port isolation define end */
+
+/* qos define start */
+#define QOS_MERGE_PRECEDENCE_CTRLN(idx) (0x180200U + (0x4U) * (idx))
+#define QOS_MERGE_PRECEDENCE_CTRLN_SMAC2INT_PRI_LOWBIT 0
+#define QOS_MERGE_PRECEDENCE_CTRLN_SMAC2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_DMAC2INT_PRI_LOWBIT 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_DMAC2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_VLAN2INT_PRI_LOWBIT 6
+#define QOS_MERGE_PRECEDENCE_CTRLN_VLAN2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_LOWBIT 9
+#define QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_LOWBIT 12
+#define QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_LOWBIT 15
+#define QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_LOWBIT 18
+#define QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_LOWBIT 21
+#define QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_WIDTH 3
+
+#define DSCP2INT_PRI_MAPN(idx) (0x180000U + (0x4U) * idx)
+#define DSCP2INT_PRI_MAPN_INT_PRIO_LOWBIT 2
+#define DSCP2INT_PRI_MAPN_INT_PRIO_WIDTH 3
+#define QOS_PORT_CTRLN(idx) (0x180180U + (0x4U) * idx)
+#define QOS_PORT_CTRLN_PORT_INT_PRIO_EN_LOWBIT 0
+#define QOS_PORT_CTRLN_PORT_INT_PRIO_EN_WIDTH 1
+#define QOS_PORT_CTRLN_PORT_INT_PRIO_LOWBIT 1
+#define QOS_PORT_CTRLN_PORT_INT_PRIO_WIDTH 3
+/* qos define end */
+
+/* lag define start*/
+#define LINK_AGG_MEMBERN(idx) (0x1805b0U + (0x4U) * (idx))
+#define LINK_AGG_MEMBERN_PORT_LOWBIT 0
+#define LINK_AGG_MEMBERN_PORT_WIDTH 4
+#define LINK_AGG_GROUPN(idx) (0x1805a8U + (0x4U) * (idx))
+#define LINK_AGG_GROUPN_MEMBER_NUM_LOWBIT 0
+#define LINK_AGG_GROUPN_MEMBER_NUM_WIDTH 3
+#define LINK_AGG_GROUPN_PORT_MASK_LOWBIT 3
+#define LINK_AGG_GROUPN_PORT_MASK_WIDTH 11
+
+#define LINK_AGG_HASH_CTRL (0x210090U)
+#define LINK_AGG_HASH_CTRL_HASH_LOWBIT 0
+#define LINK_AGG_HASH_CTRL_HASH_WIDTH 8
+
+/* lag define end*/
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_reg_922x.h b/drivers/net/dsa/motorcomm/switch/yt_reg_922x.h
new file mode 100644
index 000000000000..a026a99eb151
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_reg_922x.h
@@ -0,0 +1,571 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_REG_922X_H
+#define __YT_REG_922X_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Register Configuration */
+#if defined(CONFIG_MOTORCOMM_YT922X)
+
+/* Global module define start */
+#define GLOBAL_CTRL0 (0x80000)
+#define GLOBAL_CTRL0_CHIP_SW_RST_LOWBIT 1
+#define GLOBAL_CTRL0_CHIP_SW_RST_WIDTH 1
+
+#define GLOBAL_CTRL1 (0x80004)
+#define GLOBAL_CTRL1_MIB_EN_LOWBIT 4
+#define GLOBAL_CTRL1_MIB_EN_WIDTH 1
+#define GLOBAL_CTRL1_METER_EN_LOWBIT 7
+#define GLOBAL_CTRL1_METER_EN_WIDTH 1
+
+/* Interrupt polarity */
+#define INT_POL_L 0
+#define INT_POL_W 1
+
+#define PON_STRAP_EN (0xa003)
+#define PON_STRAP_VAL (0xa004)
+#define PON_STRAP (0x80328)
+
+#define L2_LEARN_PER_PORT_CTRLN (0x180300)
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT 18
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH 1
+/* Global module define end */
+
+/* L2 module define start */
+/*data 0*/
+#define L2_FDB_TBL_OP_DATA_0 (0x180000)
+#define L2_FDB_TBL_OP_DATA_0_DUMMY (0x180010)
+/*uc & mc*/
+#define L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_LOWBIT 0
+#define L2_FDB_TBL_MAC_OP_DATA_0_MAC_DA_0_WIDTH 32
+
+/*data 1*/
+#define L2_FDB_TBL_OP_DATA_1 (0x180004)
+#define L2_FDB_TBL_OP_DATA_1_DUMMY (0x180014)
+/*uc & mc*/
+#define L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_LOWBIT 0
+#define L2_FDB_TBL_MAC_OP_DATA_1_MAC_DA_1_WIDTH 16
+#define L2_FDB_TBL_MAC_OP_DATA_1_FID_LOWBIT 16
+#define L2_FDB_TBL_MAC_OP_DATA_1_FID_WIDTH 12
+#define L2_FDB_TBL_MAC_OP_DATA_1_STATUS_LOWBIT 28
+#define L2_FDB_TBL_MAC_OP_DATA_1_STATUS_WIDTH 3
+#define L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_LOWBIT 31
+#define L2_FDB_TBL_MAC_OP_DATA_1_SMAC_DROP_WIDTH 1
+/*flush op*/
+#define L2_FDB_TBL_FLUSH_OP_DATA_0_DST_PORT_LOWBIT 0
+#define L2_FDB_TBL_FLUSH_OP_DATA_0_DST_PORT_WIDTH 4
+#define L2_FDB_TBL_FLUSH_OP_DATA_0_LAG_EN_LOWBIT 5
+#define L2_FDB_TBL_FLUSH_OP_DATA_0_LAG_EN_WIDTH 1
+#define L2_FDB_TBL_FLUSH_OP_DATA_0_FID_LOWBIT 6
+#define L2_FDB_TBL_FLUSH_OP_DATA_0_FID_WIDTH 12
+
+/*data 2*/
+#define L2_FDB_TBL_OP_DATA_2 (0x180008)
+#define L2_FDB_TBL_OP_DATA_2_DUMMY (0x180018)
+/*uc*/
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_LOWBIT 0
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_NEW_VID_WIDTH 12
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_LOWBIT 12
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_DST_PORT_WIDTH 4
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_MOVE_AGING_STATUS_LOWBIT 17
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_MOVE_AGING_STATUS_WIDTH 2
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_PENDING_LOWBIT 19
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_PENDING_WIDTH 1
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_LOWBIT 20
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_LAG_EN_WIDTH 1
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_LOWBIT 21
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_DMAC_DROP_WIDTH 1
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_IP_MC_LOWBIT 22
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_IP_MC_WIDTH 1
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_VALID_LOWBIT 23
+#define L2_FDB_TBL_UC_MAC_OP_DATA_2_VALID_WIDTH 1
+/*mc*/
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_NEW_VID_LOWBIT 0
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_NEW_VID_WIDTH 12
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_CNT_IDX_LOWBIT 12
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_CNT_IDX_WIDTH 8
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_RSVD_LOWBIT 20
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_RSVD_WIDTH 1
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_DMAC_DROP_LOWBIT 21
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_DMAC_DROP_WIDTH 1
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_IP_MC_LOWBIT 22
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_IP_MC_WIDTH 1
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_VALID_LOWBIT 23
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_VALID_WIDTH 1
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_PMSK_0_LOWBIT 24
+#define L2_FDB_TBL_MC_MAC_OP_DATA_2_MC_PMSK_0_WIDTH 8
+
+/*data 3*/
+#define L2_FDB_TBL_OP_DATA_3 (0x18000c)
+#define L2_FDB_TBL_OP_DATA_3_DUMMY (0x18001c)
+/*mc*/
+#define L2_FDB_TBL_MC_MAC_OP_DATA_3_MC_PMSK_1_LOWBIT 0
+#define L2_FDB_TBL_MC_MAC_OP_DATA_3_MC_PMSK_1_WIDTH 2
+#define L2_FDB_TBL_MC_MAC_OP_DATA_3_IS_MC_HW_LOWBIT 2
+#define L2_FDB_TBL_MC_MAC_OP_DATA_3_IS_MC_HW_WIDTH 1
+
+#define L2_FDB_TBL_OP (0x180020)
+#define L2_FDB_TBL_OP_OP_START_LOWBIT 0
+#define L2_FDB_TBL_OP_OP_START_WIDTH 1
+#define L2_FDB_TBL_OP_OP_CMD_LOWBIT 1
+#define L2_FDB_TBL_OP_OP_CMD_WIDTH 3
+#define L2_FDB_TBL_OP_GET_NEXT_TYPE_LOWBIT 4
+#define L2_FDB_TBL_OP_GET_NEXT_TYPE_WIDTH 3
+#define L2_FDB_TBL_OP_FLUSH_STATIC_EN_LOWBIT 7
+#define L2_FDB_TBL_OP_FLUSH_STATIC_EN_WIDTH 1
+#define L2_FDB_TBL_OP_FLUSH_MODE_LOWBIT 8
+#define L2_FDB_TBL_OP_FLUSH_MODE_WIDTH 4
+#define L2_FDB_TBL_OP_OP_MODE_LOWBIT 12
+#define L2_FDB_TBL_OP_OP_MODE_WIDTH 1
+#define L2_FDB_TBL_OP_ENTRY_INDEX_LOWBIT 13
+#define L2_FDB_TBL_OP_ENTRY_INDEX_WIDTH 12
+
+#define L2_FDB_TBL_OP_RESULT (0x180024)
+#define L2_FDB_TBL_OP_RESULT_ENTRY_INDEX_LOWBIT 0
+#define L2_FDB_TBL_OP_RESULT_ENTRY_INDEX_WIDTH 12
+#define L2_FDB_TBL_OP_RESULT_OP_RESULT_LOWBIT 12
+#define L2_FDB_TBL_OP_RESULT_OP_RESULT_WIDTH 1
+#define L2_FDB_TBL_OP_RESULT_OVERWRITE_LOWBIT 13
+#define L2_FDB_TBL_OP_RESULT_OVERWRITE_WIDTH 1
+#define L2_FDB_TBL_OP_RESULT_LOOKUP_FAIL_LOWBIT 14
+#define L2_FDB_TBL_OP_RESULT_LOOKUP_FAIL_WIDTH 1
+#define L2_FDB_TBL_OP_RESULT_OP_DONE_LOWBIT 15
+#define L2_FDB_TBL_OP_RESULT_OP_DONE_WIDTH 1
+
+#define L2_AGING_CTRL (0x18043c)
+#define L2_AGING_CTRL_AGING_INTERVAL_LOWBIT 0
+#define L2_AGING_CTRL_AGING_INTERVAL_WIDTH 16
+#define L2_AGING_CTRL_HWMC_AGING_EN_LOWBIT 16
+#define L2_AGING_CTRL_HWMC_AGING_EN_WIDTH 1
+
+#define L2_LEARN_PER_PORT_CTRL (0x180300)
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT 18
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH 1
+/* L2 module define end */
+
+/* VLAN module define start */
+#define L2_VLAN_TBL (0x188000)
+#define VLAN_UNTAG_TBL (0x104000)
+#define L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT 0
+#define L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH 10
+#define L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT 0
+#define L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH 10
+
+#define PORT_VLAN_CTRLN (0x230000)
+#define PORT_VLAN_CTRLN_DEFAULT_SVID_LOWBIT 18
+#define PORT_VLAN_CTRLN_DEFAULT_SVID_WIDTH 12
+#define PORT_VLAN_CTRLN_DEFAULT_CVID_LOWBIT 6
+#define PORT_VLAN_CTRLN_DEFAULT_CVID_WIDTH 12
+
+#define L2_VLAN_INGRESS_FILTER_EN (0x180180)
+#define L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_LOWBIT 0
+#define L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_WIDTH 10
+
+#define L2_EGR_VLAN_FILTER_EN (0x180ed0)
+#define L2_EGR_VLAN_FILTER_EN_FILTER_EN_LOWBIT 0
+#define L2_EGR_VLAN_FILTER_EN_FILTER_EN_WIDTH 10
+
+#define EGR_PORT_VLAN_CTRLN (0x100080)
+#define EGR_PORT_VLAN_CTRLN_CTAG_MODE_LOWBIT 12
+#define EGR_PORT_VLAN_CTRLN_CTAG_MODE_WIDTH 3
+#define EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_LOWBIT 30
+#define EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_WIDTH 1
+#define EGR_PORT_VLAN_CTRLN_STAG_MODE_LOWBIT 27
+#define EGR_PORT_VLAN_CTRLN_STAG_MODE_WIDTH 3
+#define EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_LOWBIT 31
+#define EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_WIDTH 1
+
+#define EGR_TPID_PROFILE (0x100280)
+
+#define TPID_PROFILE0 (0x210000)
+#define TPID_PROFILE0_TPID_LOWBIT 0
+#define TPID_PROFILE0_TPID_WIDTH 16
+
+#define TPID_PROFILE1 (0x210008)
+#define TPID_PROFILE1_TPID_LOWBIT 0
+#define TPID_PROFILE1_TPID_WIDTH 16
+
+#define PARSER_PORT_CTRLN(idx) (0x210014U + (0x4U) * idx)
+#define PARSER_PORT_CTRLN_CTAG_TPID_MASK_LOWBIT 0
+#define PARSER_PORT_CTRLN_CTAG_TPID_MASK_WIDTH 4
+#define PARSER_PORT_CTRLN_STAG_TPID_MASK_LOWBIT 4
+#define PARSER_PORT_CTRLN_STAG_TPID_MASK_WIDTH 4
+#define PARSER_PORT_CTRLN_LAG_EN_LOWBIT 11
+#define PARSER_PORT_CTRLN_LAG_EN_WIDTH 1
+#define PARSER_PORT_CTRLN_LAG_ID_LOWBIT 12
+#define PARSER_PORT_CTRLN_LAG_ID_WIDTH 3
+
+#define PORT_VLAN_CTRL1N (0x230080)
+#define PORT_VLAN_CTRL1N_CTAG_AFT_LOWBIT 0
+#define PORT_VLAN_CTRL1N_CTAG_AFT_WIDTH 2
+#define PORT_VLAN_CTRL1N_STAG_AFT_LOWBIT 2
+#define PORT_VLAN_CTRL1N_STAG_AFT_WIDTH 2
+/* VLAN module define end */
+
+/* STAT module define start */
+#define MIB_CTRL_ADDR (0xc0000)
+#define MIB_OP_ADDR (0xc0004)
+#define MIB_TBL_BASE_ADDR0 (0xc0200)
+/* STAT module define end */
+
+/* STP module define start */
+#define L2_STP_STATEN (0x1b8000)
+#define L2_STP_STATEN_STATE_LOWBIT 0
+#define L2_STP_STATEN_STATE_WIDTH 20
+/* STP module define end */
+
+/* Loopdetect module define start */
+#define L2_LOOP_DETECT_FLAG_DUMMY (0x180ef8)
+#define LOOP_DETECT_TOP_CTRL (0x80300)
+#define LOOP_DETECT_TOP_CTRL_EN_LOWBIT (18)
+#define LOOP_DETECT_TOP_CTRL_EN_WIDTH (1)
+/* Loopdetect module define end */
+
+/* storm ctrl module define start */
+#define STORM_CTRL_TIMESLOT (0x220100)
+#define STORM_CTRL_TIMESLOT_TIMESLOT_LOWBIT (0)
+#define STORM_CTRL_TIMESLOT_TIMESLOT_WIDTH (12)
+
+#define STORM_CTRL_CONFIG_TBL (0x220400)
+#define STORM_CTRL_CONFIG_TBL_STORM_EN_LOWBIT (0)
+#define STORM_CTRL_CONFIG_TBL_STORM_EN_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_RATE_MODE_LOWBIT (1)
+#define STORM_CTRL_CONFIG_TBL_RATE_MODE_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_GAP_INC_LOWBIT (2)
+#define STORM_CTRL_CONFIG_TBL_GAP_INC_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_CIR_LOWBIT (13)
+#define STORM_CTRL_CONFIG_TBL_CIR_WIDTH (19)
+#define STORM_CTRL_CONFIG_TBL_CBS_LOWBIT (3)
+#define STORM_CTRL_CONFIG_TBL_CBS_WIDTH (10)
+
+#define STORM_CTRL_MC_TYPE_CTRL (0x220200)
+#define STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT (0)
+#define STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH (11)
+/* storm ctrl module define end */
+
+/* Mirror module define start */
+#define MIRROR_PORT_CTRL (0x80560)
+#define MIRROR_CTRL_MIRROR_PORT_LOWBIT 0
+#define MIRROR_CTRL_MIRROR_PORT_WIDTH 4
+#define MIRROR_CTRL_IS_LAG_LOWBIT 4
+#define MIRROR_CTRL_IS_LAG_WIDTH 1
+#define MIRROR_CTRL0 (0x480400)
+#define MIRROR_CTRL_INGR_MIRROR_EN_LOWBIT 0
+#define MIRROR_CTRL_INGR_MIRROR_EN_WIDTH 10
+#define MIRROR_CTRL1 (0x480500)
+#define MIRROR_CTRL_EGR_MIRROR_EN_LOWBIT 0
+#define MIRROR_CTRL_EGR_MIRROR_EN_WIDTH 10
+/* Mirror module define end */
+
+/* NIC module define start */
+#define CPU_COPY_DST_CTRL (0x181100)
+#define CPU_COPY_DST_CTRL_TO_INT_CPU_LOWBIT 1
+#define CPU_COPY_DST_CTRL_TO_INT_CPU_WIDTH 1
+#define CPU_COPY_DST_CTRL_TO_EXT_CPU_LOWBIT 0
+#define CPU_COPY_DST_CTRL_TO_EXT_CPU_WIDTH 1
+
+#define EXT_CPU_PORT_CTRL (0x8000c)
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_LOWBIT 14
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_WIDTH 1
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_LOWBIT 0
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_WIDTH 4
+#define EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_LOWBIT 15
+#define EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_WIDTH 1
+
+#define PORT_CTRL (0x80080)
+#define PORT_CTRL_HALF_FC_EN_LOWBIT 8
+#define PORT_CTRL_HALF_FC_EN_WIDTH 1
+#define PORT_CTRL_CFG_RXMAC_EN_LOWBIT 12
+#define PORT_CTRL_CFG_RXMAC_EN_WIDTH 1
+#define PORT_CTRL_CFG_TXMAC_EN_LOWBIT 13
+#define PORT_CTRL_CFG_TXMAC_EN_WIDTH 1
+
+#define CPU_TAG_TPID (0x80010)
+#define CPU_TAG_TPID_TPID_LOWBIT 0
+#define CPU_TAG_TPID_TPID_WIDTH 16
+
+#define CPU_PKT_BYPASSEDIT_CTRL (0x100400)
+#define CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_LOWBIT 0
+#define CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_WIDTH 1
+#define CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_LOWBIT 1
+#define CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_WIDTH 1
+/* NIC module define end */
+
+/* LED module define start */
+#define LED_GLB_CTRL1 (0xa028)
+#define LED_GLB_CTRL1_LED_MODE_LOWBIT 13
+#define LED_GLB_CTRL1_LED_MODE_WIDTH 1
+
+#define LED_GLB_CTRL2 (0xa029)
+#define LED_GLB_CTRL2_LED_CONFIG_DONE_LOWBIT 15
+#define LED_GLB_CTRL2_LED_CONFIG_DONE_WIDTH 1
+
+#define LED_CTRL_0_BASE_H (0xa02d)
+#define LED_CTRL_0_BASE_L (0xa036)
+#define LED_CTRL_1_BASE_H (0xa03f)
+#define LED_CTRL_1_BASE_L (0xa048)
+#define LED_CTRL_2_BASE_H (0xa051)
+#define LED_CTRL_2_BASE_L (0xa05a)
+
+#define LED_SERIAL_CTRL (0xa02a)
+#define LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT 2
+#define LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH 1
+
+#define LED_BLINK_CTRL (0xa063)
+#define LED_POS_CTRL1 (0xa026)
+#define LED_POS_CTRL2 (0xa027)
+#define LED_MAPPING_BASE (0xa06c)
+/* LED module define end */
+
+/* Port module define start */
+#define MAX_BUSYING_WAIT_TIME (200)
+#define EXT_IF_INDEX_OFFSET (0x1000)
+#define EXT_IF_FREQUENCY_SEL (4U)
+#define INT_IF_FRAME_CTRL (0xf0000)
+#define INT_IF_ADDR_CTRL (0xf0004)
+#define INT_IF_DATA_0_ADDR (0xf0008)
+#define INT_IF_DATA_1_ADDR (0xf000c)
+
+#define EXT_IF_FRAME_CTRL (0xf1000)
+#define EXT_IF_ADDR_CTRL (0xf1004)
+#define EXT_IF_DATA_0_ADDR (0xf1008)
+#define EXT_IF_DATA_1_ADDR (0xf100c)
+
+#define CHIP_PORT_CTRL (0x80080)
+#define CHIP_GMAC_GLOBAL1 (0x80580)
+#define CHIP_JUMBO_CTRL (0x210228)
+#define CHIP_XGMAC_WATCHDOG_CTRL(N) (0xD0008 + (0x800) * (N))
+#define CHIP_JUMBO_SIZE_MAX (0x2800)
+#define CHIP_JUMBO_SIZE_MIN (0x800)
+#define CHIP_JUMBO_DEFAULT_SIZE (0x2400)
+#define PORT_STATUS 0x80200
+/* Port module define end */
+
+/* Interrupt module define end */
+#define INTR1_MASK (0x80058)
+#define INTR1_STATUS (0x8005C)
+/* Interrupt module define end */
+
+#define E_OP_CTRL_0 (0xE0000)
+#define E_OP_CTRL_1 (0xE0004)
+
+/* ctrl pkt define start */
+#define L2_ARP_PER_PORT_CTRLN(idx) (0x180800 + (0x4) * idx)
+#define L2_ARP_PORT_CTRLN_ACTION_LOWBIT 0
+#define L2_ARP_PORT_CTRLN_ACTION_WIDTH 2
+#define L2_ARP_PORT_CTRLN_BYPASS_STORM_LOWBIT 2
+#define L2_ARP_PORT_CTRLN_BYPASS_STORM_WIDTH 1
+#define L2_ARP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT 3
+#define L2_ARP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH 1
+#define L2_ARP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT 4
+#define L2_ARP_PORT_CTRLN_BYPASS_FLOWMETER_WIDTH 1
+#define L2_ARP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT 5
+#define L2_ARP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH 1
+
+#define L2_ND_PER_PORT_CTRLN(idx) (0x180880 + (0x4) * idx)
+#define L2_ND_PORT_CTRLN_ACTION_LOWBIT 0
+#define L2_ND_PORT_CTRLN_ACTION_WIDTH 2
+#define L2_ND_PORT_CTRLN_BYPASS_STORM_LOWBIT 2
+#define L2_ND_PORT_CTRLN_BYPASS_STORM_WIDTH 1
+#define L2_ND_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT 3
+#define L2_ND_PORT_CTRLN_BYPASS_PORTMETER_WIDTH 1
+#define L2_ND_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT 4
+#define L2_ND_PORT_CTRLN_BYPASS_FLOWMETER_WIDTH 1
+#define L2_ND_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT 5
+#define L2_ND_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH 1
+
+#define L2_LLDP_PER_PORT_CTRLN(idx) (0x180980 + (0x4) * idx)
+#define L2_LLDP_PORT_CTRLN_ACTION_LOWBIT 0
+#define L2_LLDP_PORT_CTRLN_ACTION_WIDTH 2
+#define L2_LLDP_PORT_CTRLN_BYPASS_STORM_LOWBIT 2
+#define L2_LLDP_PORT_CTRLN_BYPASS_STORM_WIDTH 1
+#define L2_LLDP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT 3
+#define L2_LLDP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH 1
+#define L2_LLDP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT 4
+#define L2_LLDP_PORT_CTRLN_BYPASS_FLOWMETER_WIDTH 1
+#define L2_LLDP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT 5
+#define L2_LLDP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH 1
+
+#define L2_RRPP_PER_PORT_CTRLN(idx) (0x180a00 + (0x4) * idx)
+#define L2_RRPP_PORT_CTRLN_ACTION_LOWBIT 0
+#define L2_RRPP_PORT_CTRLN_ACTION_WIDTH 2
+#define L2_RRPP_PORT_CTRLN_BYPASS_STORM_LOWBIT 2
+#define L2_RRPP_PORT_CTRLN_BYPASS_STORM_WIDTH 1
+#define L2_RRPP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT 3
+#define L2_RRPP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH 1
+#define L2_RRPP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT 4
+#define L2_RRPP_PORT_CTRLN_BYPASS_FLOWMETER_WIDTH 1
+#define L2_RRPP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT 5
+#define L2_RRPP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH 1
+
+#define L2_ERPS_PER_PORT_CTRLN(idx) (0x180a80 + (0x4) * idx)
+#define L2_ERPS_PORT_CTRLN_ACTION_LOWBIT 0
+#define L2_ERPS_PORT_CTRLN_ACTION_WIDTH 2
+#define L2_ERPS_PORT_CTRLN_BYPASS_STORM_LOWBIT 2
+#define L2_ERPS_PORT_CTRLN_BYPASS_STORM_WIDTH 1
+#define L2_ERPS_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT 3
+#define L2_ERPS_PORT_CTRLN_BYPASS_PORTMETER_WIDTH 1
+#define L2_ERPS_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT 4
+#define L2_ERPS_PORT_CTRLN_BYPASS_FLOWMETER_WIDTH 1
+#define L2_ERPS_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT 5
+#define L2_ERPS_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH 1
+
+#define L2_IGMP_PER_PORT_CTRLN(idx) (0x180b00 + (0x4) * idx)
+#define L2_IGMP_PORT_CTRLN_ACTION_LOWBIT 0
+#define L2_IGMP_PORT_CTRLN_ACTION_WIDTH 2
+#define L2_IGMP_PORT_CTRLN_BYPASS_STORM_LOWBIT 2
+#define L2_IGMP_PORT_CTRLN_BYPASS_STORM_WIDTH 1
+#define L2_IGMP_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT 3
+#define L2_IGMP_PORT_CTRLN_BYPASS_PORTMETER_WIDTH 1
+#define L2_IGMP_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT 4
+#define L2_IGMP_PORT_CTRLN_BYPASS_FLOWMETER_WIDTH 1
+#define L2_IGMP_PORT_CTRLN_PORT_ISO_LEAKY_LOWBIT 5
+#define L2_IGMP_PORT_CTRLN_PORT_ISO_LEAKY_WIDTH 1
+#define L2_IGMP_PORT_CTRLN_VLAN_LEAKY_LOWBIT 6
+#define L2_IGMP_PORT_CTRLN_VLAN_LEAKY_WIDTH 1
+#define L2_IGMP_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT 7
+#define L2_IGMP_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH 1
+
+#define L2_MLD_PER_PORT_CTRLN(idx) (0x180b80 + (0x4) * idx)
+#define L2_MLD_PORT_CTRLN_ACTION_LOWBIT 0
+#define L2_MLD_PORT_CTRLN_ACTION_WIDTH 2
+#define L2_MLD_PORT_CTRLN_BYPASS_STORM_LOWBIT 2
+#define L2_MLD_PORT_CTRLN_BYPASS_STORM_WIDTH 1
+#define L2_MLD_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT 3
+#define L2_MLD_PORT_CTRLN_BYPASS_PORTMETER_WIDTH 1
+#define L2_MLD_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT 4
+#define L2_MLD_PORT_CTRLN_BYPASS_FLOWMETER_WIDTH 1
+#define L2_MLD_PORT_CTRLN_PORT_ISO_LEAKY_LOWBIT 5
+#define L2_MLD_PORT_CTRLN_PORT_ISO_LEAKY_WIDTH 1
+#define L2_MLD_PORT_CTRLN_VLAN_LEAKY_LOWBIT 6
+#define L2_MLD_PORT_CTRLN_VLAN_LEAKY_WIDTH 1
+#define L2_MLD_PORT_CTRLN_SA_LEARN_DISABLE_LOWBIT 7
+#define L2_MLD_PORT_CTRLN_SA_LEARN_DISABLE_WIDTH 1
+
+#define L2_PIM_PER_PORT_CTRLN(idx) (0x181180 + (0x4) * idx)
+#define L2_PIM_PORT_CTRLN_ACTION_LOWBIT 0
+#define L2_PIM_PORT_CTRLN_ACTION_WIDTH 2
+#define L2_PIM_PORT_CTRLN_BYPASS_STORM_LOWBIT 2
+#define L2_PIM_PORT_CTRLN_BYPASS_STORM_WIDTH 1
+#define L2_PIM_PORT_CTRLN_BYPASS_PORTMETER_LOWBIT 3
+#define L2_PIM_PORT_CTRLN_BYPASS_PORTMETER_WIDTH 1
+#define L2_PIM_PORT_CTRLN_BYPASS_FLOWMETER_LOWBIT 4
+#define L2_PIM_PORT_CTRLN_BYPASS_FLOWMETER_WIDTH 1
+
+#define MAC_TO_ME_CTRL 0x80520
+#define MAC_TO_ME_CTRL_MGMT_VID_LOWBIT 0
+#define MAC_TO_ME_CTRL_MGMT_VID_WIDTH 12
+#define MAC_TO_ME_CTRL_ACTION_LOWBIT 12
+#define MAC_TO_ME_CTRL_ACTION_WIDTH 2
+#define MAC_TO_ME_CTRL_VALID_LOWBIT 14
+#define MAC_TO_ME_CTRL_VALID_WIDTH 1
+
+#define L2_CTRL_PACKET_STP_BYPASS (0x181134)
+#define L2_CTRL_PACKET_ERPS_STP_BYPASS_LOWBIT 0
+#define L2_CTRL_PACKET_ERPS_STP_BYPASS_WIDTH 1
+#define L2_CTRL_PACKET_RRPP_STP_BYPASS_LOWBIT 1
+#define L2_CTRL_PACKET_RRPP_STP_BYPASS_WIDTH 1
+#define L2_CTRL_PACKET_LLDP_STP_BYPASS_LOWBIT 2
+#define L2_CTRL_PACKET_LLDP_STP_BYPASS_WIDTH 1
+#define L2_CTRL_PACKET_MLD_STP_BYPASS_LOWBIT 3
+#define L2_CTRL_PACKET_MLD_STP_BYPASS_WIDTH 1
+#define L2_CTRL_PACKET_IGMP_STP_BYPASS_LOWBIT 4
+#define L2_CTRL_PACKET_IGMP_STP_BYPASS_WIDTH 1
+#define L2_CTRL_PACKET_ND_STP_BYPASS_LOWBIT 5
+#define L2_CTRL_PACKET_ND_STP_BYPASS_WIDTH 1
+#define L2_CTRL_PACKET_ARP_STP_BYPASS_LOWBIT 6
+#define L2_CTRL_PACKET_ARP_STP_BYPASS_WIDTH 1
+
+#define PARSER_CTRL 0x210010
+#define PARSER_CTRL_ARP_LOWBIT 1
+#define PARSER_CTRL_ARP_WIDTH 1
+#define PARSER_CTRL_PPPOE_LOWBIT 0
+#define PARSER_CTRL_PPPOE_WIDTH 1
+
+#define PARSER_RRPP_ETHTYPE 0x210118
+#define PARSER_RRPP_ETHTYPE_LOWBIT 0
+#define PARSER_RRPP_ETHTYPE_WIDTH 16
+#define PARSER_RRPP_OUI 0x210114
+#define PARSER_RRPP_OUI_LOWBIT 0
+#define PARSER_RRPP_OUI_WIDTH 24
+
+/* ctrl pkt define end */
+
+/* port isolation define start */
+#define L2_PORT_ISOLATION_CTRLN(idx) (0x180d80 + (0x4) * idx)
+#define L2_PORT_ISOLATION_CTRLN_PORTMASK_LOWBIT 0
+#define L2_PORT_ISOLATION_CTRLN_PORTMASK_WIDTH 10
+
+#define L2_VLAN_BASED_ISO_VID_RANGEN(idx) (0x180e00 + (0x4) * idx)
+#define L2_VLAN_BASED_ISO_VID_RANGEN_VALID_LOWBIT 0
+#define L2_VLAN_BASED_ISO_VID_RANGEN_VALID_WIDTH 1
+#define L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_LOWBIT 1
+#define L2_VLAN_BASED_ISO_VID_RANGEN_VID_MAX_WIDTH 12
+#define L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_LOWBIT 13
+#define L2_VLAN_BASED_ISO_VID_RANGEN_VID_MIN_WIDTH 12
+#define L2_VLAN_BASED_ISO_PORT_MASKN(idx) (0x180e40 + (0x4) * idx)
+#define L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_LOWBIT 0
+#define L2_VLAN_BASED_ISO_PORT_MASKN_PORTMASK_WIDTH 10
+
+#define L2_VLAN_BASED_ISO_CTRL 0x180eC0
+#define L2_VLAN_BASED_ISO_CTRL_TYPE_LOWBIT 0
+#define L2_VLAN_BASED_ISO_CTRL_TYPE_WIDTH 2
+/* port isolation define end */
+
+/* qos define start */
+#define QOS_MERGE_PRECEDENCE_CTRLN(idx) (0x180700U + (0x4U) * idx)
+#define QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_LOWBIT 0
+#define QOS_MERGE_PRECEDENCE_CTRLN_PORT2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_LOWBIT 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_SPRI2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_LOWBIT 6
+#define QOS_MERGE_PRECEDENCE_CTRLN_CPRI2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_LOWBIT 9
+#define QOS_MERGE_PRECEDENCE_CTRLN_DSCP2INT_PRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_INTSPRI2INTPRI_LOWBIT 12
+#define QOS_MERGE_PRECEDENCE_CTRLN_INTSPRI2INTPRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_INTCPRI2INTPRI_LOWBIT 15
+#define QOS_MERGE_PRECEDENCE_CTRLN_INTCPRI2INTPRI_WIDTH 3
+#define QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_LOWBIT 18
+#define QOS_MERGE_PRECEDENCE_CTRLN_ACL2INT_PRI_WIDTH 3
+
+#define DSCP2INT_PRI_MAPN(idx) (0x180500U + (0x4U) * idx)
+#define DSCP2INT_PRI_MAPN_INT_PRIO_LOWBIT 2
+#define DSCP2INT_PRI_MAPN_INT_PRIO_WIDTH 3
+
+#define QOS_PORT_CTRLN(idx) (0x180600U + (0x4U) * idx)
+#define QOS_PORT_CTRLN_PORT_INT_PRIO_EN_LOWBIT 0
+#define QOS_PORT_CTRLN_PORT_INT_PRIO_EN_WIDTH 1
+#define QOS_PORT_CTRLN_PORT_INT_PRIO_LOWBIT 1
+#define QOS_PORT_CTRLN_PORT_INT_PRIO_WIDTH 3
+/* qos define end */
+
+/* lag define start */
+#define LINK_AGG_HASH_CTRL (0x210090U)
+#define LINK_AGG_HASH_CTRL_HASH_LOWBIT 0
+#define LINK_AGG_HASH_CTRL_HASH_WIDTH 8
+#define LINK_AGG_HASH_CTRL_MAC_SA_SHIFT_LOWBIT 23
+#define LINK_AGG_HASH_CTRL_MAC_SA_SHIFT_WIDTH 3
+#define LINK_AGG_HASH_CTRL_MAC_DA_SHIFT_LOWBIT 26
+#define LINK_AGG_HASH_CTRL_MAC_DA_SHIFT_WIDTH 3
+
+#define LINK_AGG_GROUPN(idx) (0x80530U + (0x4U) * idx)
+#define LINK_AGG_GROUPN_PORT_MASK_LOWBIT 0
+#define LINK_AGG_GROUPN_PORT_MASK_WIDTH 10
+/* lag define end */
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_reg_923x.h b/drivers/net/dsa/motorcomm/switch/yt_reg_923x.h
new file mode 100644
index 000000000000..c72fddf0ef1c
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_reg_923x.h
@@ -0,0 +1,218 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_REG_923X_H
+#define __YT_REG_923X_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Register Configuration */
+#if defined(CONFIG_MOTORCOMM_YT923X)
+
+/* Global module define start */
+#define GLOBAL_CTRL0 (0x80000)
+#define GLOBAL_CTRL0_CHIP_SW_RST_LOWBIT 1
+#define GLOBAL_CTRL0_CHIP_SW_RST_WIDTH 1
+
+#define GLOBAL_CTRL1 (0x80004)
+#define GLOBAL_CTRL1_MIB_EN_LOWBIT 1
+#define GLOBAL_CTRL1_MIB_EN_WIDTH 1
+#define GLOBAL_CTRL1_METER_EN_LOWBIT 4
+#define GLOBAL_CTRL1_METER_EN_WIDTH 1
+
+/* Interrupt polarity */
+#define INT_POL_L 0
+#define INT_POL_W 1
+
+#define PON_STRAP_EN (0x80320)
+#define PON_STRAP_VAL (0x80324)
+#define PON_STRAP (0x80328)
+
+#define L2_LEARN_PER_PORT_CTRLN (0x180300)
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_LOWBIT 19
+#define L2_LEARN_PER_PORT_CTRLN_LEARN_DISABLE_WIDTH 1
+/* Global module define end */
+
+/* VLAN module define start */
+#define L2_VLAN_TBL (0x188000)
+#define VLAN_UNTAG_TBL (0x104000)
+#define L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT 0
+#define L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH 29
+#define L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT 0
+#define L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH 29
+
+#define PORT_VLAN_CTRLN (0x230000)
+#define PORT_VLAN_CTRLN_DEFAULT_SVID_LOWBIT 18
+#define PORT_VLAN_CTRLN_DEFAULT_SVID_WIDTH 12
+#define PORT_VLAN_CTRLN_DEFAULT_CVID_LOWBIT 6
+#define PORT_VLAN_CTRLN_DEFAULT_CVID_WIDTH 12
+
+#define L2_VLAN_INGRESS_FILTER_EN (0x180180)
+#define L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_LOWBIT 0
+#define L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_WIDTH 29
+
+#define L2_EGR_VLAN_FILTER_EN (0x180ed0)
+#define L2_EGR_VLAN_FILTER_EN_FILTER_EN_LOWBIT 0
+#define L2_EGR_VLAN_FILTER_EN_FILTER_EN_WIDTH 29
+
+#define EGR_PORT_VLAN_CTRLN (0x100080)
+#define EGR_PORT_VLAN_CTRLN_CTAG_MODE_LOWBIT 12
+#define EGR_PORT_VLAN_CTRLN_CTAG_MODE_WIDTH 3
+#define EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_LOWBIT 30
+#define EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_WIDTH 1
+#define EGR_PORT_VLAN_CTRLN_STAG_MODE_LOWBIT 27
+#define EGR_PORT_VLAN_CTRLN_STAG_MODE_WIDTH 3
+#define EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_LOWBIT 31
+#define EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_WIDTH 1
+
+#define EGR_TPID_PROFILE (0x100280)
+
+#define TPID_PROFILE0 (0x210000)
+#define TPID_PROFILE0_TPID_LOWBIT 0
+#define TPID_PROFILE0_TPID_WIDTH 16
+
+#define TPID_PROFILE1 (0x210008)
+#define TPID_PROFILE1_TPID_LOWBIT 0
+#define TPID_PROFILE1_TPID_WIDTH 16
+
+#define PARSER_PORT_CTRLN(idx) (0x210014U + (0x4U) * idx)
+#define PARSER_PORT_CTRLN_CTAG_TPID_MASK_LOWBIT 0
+#define PARSER_PORT_CTRLN_CTAG_TPID_MASK_WIDTH 4
+#define PARSER_PORT_CTRLN_STAG_TPID_MASK_LOWBIT 4
+#define PARSER_PORT_CTRLN_STAG_TPID_MASK_WIDTH 4
+
+#define PORT_VLAN_CTRL1N (0x230080)
+#define PORT_VLAN_CTRL1N_CTAG_AFT_LOWBIT 0
+#define PORT_VLAN_CTRL1N_CTAG_AFT_WIDTH 2
+#define PORT_VLAN_CTRL1N_STAG_AFT_LOWBIT 2
+#define PORT_VLAN_CTRL1N_STAG_AFT_WIDTH 2
+/* VLAN module define end */
+
+/* STAT module define start */
+#define MIB_CTRL_ADDR (0xc0000)
+#define MIB_OP_ADDR (0xc0004)
+#define MIB_TBL_BASE_ADDR0 (0xc0100)
+/* STAT module define end */
+
+/* STP module define start */
+#define L2_STP_STATEN (0x1b8000)
+#define L2_STP_STATEN_STATE_LOWBIT 0
+#define L2_STP_STATEN_STATE_WIDTH 32
+/* STP module define end */
+
+/* Loopdetect module define start */
+#define L2_LOOP_DETECT_FLAG_DUMMY (0x180ef8)
+#define LOOP_DETECT_TOP_CTRL (0x80300)
+#define LOOP_DETECT_TOP_CTRL_EN_LOWBIT (18)
+#define LOOP_DETECT_TOP_CTRL_EN_WIDTH (1)
+/* Loopdetect module define end */
+
+/* storm ctrl module define start */
+#define STORM_CTRL_TIMESLOT (0x220100)
+#define STORM_CTRL_TIMESLOT_TIMESLOT_LOWBIT (0)
+#define STORM_CTRL_TIMESLOT_TIMESLOT_WIDTH (12)
+
+#define STORM_CTRL_CONFIG_TBL (0x220400)
+#define STORM_CTRL_CONFIG_TBL_STORM_EN_LOWBIT (0)
+#define STORM_CTRL_CONFIG_TBL_STORM_EN_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_RATE_MODE_LOWBIT (1)
+#define STORM_CTRL_CONFIG_TBL_RATE_MODE_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_GAP_INC_LOWBIT (2)
+#define STORM_CTRL_CONFIG_TBL_GAP_INC_WIDTH (1)
+#define STORM_CTRL_CONFIG_TBL_CIR_LOWBIT (13)
+#define STORM_CTRL_CONFIG_TBL_CIR_WIDTH (19)
+#define STORM_CTRL_CONFIG_TBL_CBS_LOWBIT (3)
+#define STORM_CTRL_CONFIG_TBL_CBS_WIDTH (10)
+
+#define STORM_CTRL_MC_TYPE_CTRL (0x220200)
+#define STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT (0)
+#define STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH (11)
+/* storm ctrl module define end */
+
+/* Mirror module define start */
+#define MIRROR_PORT_CTRL (0x80560)
+#define MIRROR_CTRL_MIRROR_PORT_LOWBIT 0
+#define MIRROR_CTRL_MIRROR_PORT_WIDTH 5
+#define MIRROR_CTRL_IS_LAG_LOWBIT 5
+#define MIRROR_CTRL_IS_LAG_WIDTH 1
+#define MIRROR_CTRL0 (0x480400)
+#define MIRROR_CTRL_INGR_MIRROR_EN_LOWBIT 0
+#define MIRROR_CTRL_INGR_MIRROR_EN_WIDTH 29
+#define MIRROR_CTRL1 (0x480500)
+#define MIRROR_CTRL_EGR_MIRROR_EN_LOWBIT 0
+#define MIRROR_CTRL_EGR_MIRROR_EN_WIDTH 29
+/* Mirror module define end */
+
+/* NIC module define start */
+#define CPU_COPY_DST_CTRL (0x181100)
+#define CPU_COPY_DST_CTRL_TO_INT_CPU_LOWBIT 1
+#define CPU_COPY_DST_CTRL_TO_INT_CPU_WIDTH 1
+#define CPU_COPY_DST_CTRL_TO_EXT_CPU_LOWBIT 0
+#define CPU_COPY_DST_CTRL_TO_EXT_CPU_WIDTH 1
+
+#define EXT_CPU_PORT_CTRL (0x8000c)
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_LOWBIT 14
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_EN_WIDTH 1
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_LOWBIT 0
+#define EXT_CPU_PORT_CTRL_EXT_CPU_PORT_NUM_WIDTH 5
+#define EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_LOWBIT 15
+#define EXT_CPU_PORT_CTRL_EXT_CPU_TAG_EN_WIDTH 1
+
+#define PORT_CTRL (0x80080)
+#define PORT_CTRL_HALF_FC_EN_LOWBIT 8
+#define PORT_CTRL_HALF_FC_EN_WIDTH 1
+
+#define CPU_TAG_TPID (0x80010)
+#define CPU_TAG_TPID_TPID_LOWBIT 0
+#define CPU_TAG_TPID_TPID_WIDTH 16
+
+#define CPU_PKT_BYPASSEDIT_CTRL (0x1004d4)
+#define CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_LOWBIT 0
+#define CPU_PKT_BYPASSEDIT_CTRL_EXT_BYPASSEDIT_EN_WIDTH 1
+#define CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_LOWBIT 1
+#define CPU_PKT_BYPASSEDIT_CTRL_INT_BYPASSEDIT_EN_WIDTH 1
+/* NIC module define end */
+
+/* LED module define start */
+#define LED_GLB_CTRL (0xd0000)
+#define LED_GLB_CTRL_LED_CONFIG_DONE_LOWBIT 8
+#define LED_GLB_CTRL_LED_CONFIG_DONE_WIDTH 1
+
+#define LED_CTRL_0_BASE (0xd0004)
+#define LED_CTRL_0_BASE_LED_ACTION_LOWBIT 0
+#define LED_CTRL_0_BASE_LED_ACTION_WIDTH 18
+
+#define LED_CTRL_1_BASE (0xd0080)
+#define LED_CTRL_1_BASE_LED_ACTION_LOWBIT 0
+#define LED_CTRL_1_BASE_LED_ACTION_WIDTH 18
+
+#define LED_CTRL_2_BASE (0xd0100)
+#define LED_CTRL_2_BASE_LED_ACTION_LOWBIT 0
+#define LED_CTRL_2_BASE_LED_ACTION_WIDTH 18
+
+#define LED_SERIAL_CTRL (0xd0200)
+#define LED_SERIAL_CTRL_ACTIVE_MODE_LOWBIT 4
+#define LED_SERIAL_CTRL_ACTIVE_MODE_WIDTH 1
+#define LED_PERPORT_EN_BASE (0xd0204)
+#define LED_IS_COMBO_LED_BASE (0xd0208)
+#define LED_SOFT_PHY_TYPE_BASE (0xd020c)
+/* LED module define end */
+
+/* Interrupt module define end */
+#define INTR1_MASK (0x80058)
+#define INTR1_STATUS (0x8005C)
+/* Interrupt module define end */
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_stat.c b/drivers/net/dsa/motorcomm/switch/yt_stat.c
new file mode 100644
index 000000000000..77aff8c902de
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_stat.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_stat.h"
+
+u32 yt_stat_mib_enable_set(u8 unit, enum yt_enable enable)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data_tmp = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &data_tmp), ret);
+	HAL_FIELD_SET(GLOBAL_CTRL1_MIB_EN_LOWBIT, GLOBAL_CTRL1_MIB_EN_WIDTH,
+		      &data_tmp, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, GLOBAL_CTRL1, data_tmp), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stat_mib_enable_set);
+
+u32 yt_stat_mib_enable_get(u8 unit, enum yt_enable *enable_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 data_tmp = 0;
+	u32 enable = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &data_tmp), ret);
+	HAL_FIELD_GET(GLOBAL_CTRL1_MIB_EN_LOWBIT, GLOBAL_CTRL1_MIB_EN_WIDTH,
+		      &data_tmp, &enable);
+	*enable_ptr = enable ? YT_ENABLE : YT_DISABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stat_mib_enable_get);
+
+u32 yt_stat_mib_clear(u8 unit, u32 port)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 port_mac_id = 0;
+	u32 ctrl_data = 0;
+
+	port_mac_id = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MIB_OP_ADDR, &ctrl_data), ret);
+	ctrl_data = port_mac_id << 3 | 0x2 << 0;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, MIB_OP_ADDR, ctrl_data), ret);
+	ctrl_data |= 1 << 30;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, MIB_OP_ADDR, ctrl_data), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stat_mib_clear);
+
+u32 yt_stat_mib_clear_all(u8 unit)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 ctrl_data = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, MIB_OP_ADDR, &ctrl_data), ret);
+	ctrl_data = 0;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, MIB_OP_ADDR, ctrl_data), ret);
+	ctrl_data |= 1 << 30;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, MIB_OP_ADDR, ctrl_data), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stat_mib_clear_all);
+
+#if defined(CONFIG_MOTORCOMM_YT921X) || defined(CONFIG_MOTORCOMM_YT923X)
+u32 yt_stat_mib_port_singletype_cnt_get(u8 unit, u32 port,
+					enum yt_stat_mib mib_type, u64 *cnt_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 port_mac_id = 0;
+	u64 *port_cnt = NULL;
+	u32 data_high1 = 0;
+	u32 data_high2 = 0;
+	u32 mib_index = 0;
+	u64 result_data = 0;
+	u32 data_low = 0;
+	u32 mib_data = 0;
+	u32 reg_tmp = 0;
+	u32 reg_tmp_hi = 0;
+
+	port_mac_id = CAL_YTP_TO_MAC(unit, port);
+
+	port_cnt = (u64 *)cnt_ptr;
+	mib_index = mib_type;
+
+	reg_tmp = MIB_TBL_BASE_ADDR0 + 0x100 * port_mac_id + 0x4 * mib_index;
+	reg_tmp_hi = MIB_TBL_BASE_ADDR0 + 0x100 * port_mac_id +
+		     0x4 * (mib_index + 1);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_tmp, &mib_data), ret);
+	if (mib_index == RX_OKBYTE || mib_index == RX_NOT_OKBYTE ||
+	    mib_index == TX_OKBYTE) {
+		result_data = 0;
+		data_high1 = 0;
+		data_high2 = 0;
+		data_low = 0;
+
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_tmp_hi, &data_high1),
+			    ret);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_tmp, &data_low), ret);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_tmp_hi, &data_high2),
+			    ret);
+
+		if (data_high1 == data_high2) {
+			result_data = data_high1 & 0xffffU;
+			result_data = result_data << 32;
+			*port_cnt = result_data | data_low;
+		} else if (data_low > 0xffffU) {
+			result_data = data_high1 & 0xffffU;
+			result_data = result_data << 32;
+			*port_cnt = result_data | data_low;
+		} else {
+			result_data = data_high2 & 0xffffU;
+			result_data = result_data << 32;
+			*port_cnt = result_data | data_low;
+		}
+	} else if ((mib_index == RX_UNICAST) || (mib_index == TX_UNICAST)) {
+		ret = CMM_ERR_NOT_SUPPORT;
+	} else {
+		*port_cnt = mib_data;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stat_mib_port_singletype_cnt_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+
+u32 yt_stat_mib_port_singletype_cnt_get(u8 unit, u32 port,
+					enum yt_stat_mib mib_type, u64 *cnt_ptr)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 port_mac_id = 0;
+	u32 data_high1 = 0;
+	u32 data_high2 = 0;
+	u64 result_data = 0;
+	u32 data_low = 0;
+	u32 reg_tmp = 0;
+	u32 reg_tmp_hi = 0;
+
+	reg_tmp = MIB_TBL_BASE_ADDR0 + 0x200 * port_mac_id + 0x8 * mib_type;
+	reg_tmp_hi =
+		MIB_TBL_BASE_ADDR0 + 0x200 * port_mac_id + 0x8 * mib_type + 4;
+	port_mac_id = CAL_YTP_TO_MAC(unit, port);
+
+	do {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_tmp_hi, &data_high1),
+			    ret);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_tmp, &data_low), ret);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, reg_tmp_hi, &data_high2),
+			    ret);
+	} while (data_high1 != data_high2);
+
+	result_data = data_high1;
+	result_data = result_data << 32;
+	*cnt_ptr = result_data | data_low;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stat_mib_port_singletype_cnt_get);
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_stat.h b/drivers/net/dsa/motorcomm/switch/yt_stat.h
new file mode 100644
index 000000000000..eb780eafdd73
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_stat.h
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_STAT_H
+#define __YT_STAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+enum yt_stat_mib {
+	RX_BROADCAST = 0,
+	RX_PAUSE,
+	RX_MULTICAST,
+	RX_FCS_ERR,
+	RX_ALIGNMENT_ERR,
+	RX_UNDERSIZE,
+	RX_FRAGMENT,
+	RX_64B,
+	RX_65_127B,
+	RX_128_255B,
+	RX_256_511B,
+	RX_512_1023B,
+	RX_1024_1518B,
+	RX_JUMBO,
+	RX_OKBYTE = 0xf,
+	RX_NOT_OKBYTE = 0X11,
+	RX_OVERSIZE = 0X13,
+	RX_DISCARD,
+	TX_BROADCAST,
+	TX_PAUSE,
+	TX_MULTICAST,
+	TX_UNDERSIZE,
+	TX_64B,
+	TX_65_127B,
+	TX_128_255B,
+	TX_256_511B,
+	TX_512_1023B,
+	TX_1024_1518B,
+	TX_JUMBO,
+	TX_OKBYTE = 0X21,
+	TX_COLLISION = 0X23,
+	TX_EXCESSIVE_COLLISION,
+	TX_MULTI_COLLISION,
+	TX_SINGLE_COLLISION,
+	TX_OK_PKT,
+	TX_DEFER,
+	TX_LATE_COLLISION,
+	RX_OAM_COUNTER,
+	TX_OAM_COUNTER,
+	RX_UNICAST,
+	TX_UNICAST,
+	YT_STAT_MAX
+};
+
+#elif defined(CONFIG_MOTORCOMM_YT923X)
+
+enum yt_stat_mib {
+	RX_BROADCAST = 0,
+	RX_PAUSE,
+	RX_UNSUPPORTFRAME,
+	RX_OAM_COUNTER,
+	RX_MULTICAST,
+	RX_ALIGN_ERR_LESS_64B,
+	RX_ALIGN_ERR_64B_1518B,
+	RX_ALIGN_ERR_JUMBO,
+	RX_FCS_ERR_64B_1518B,
+	RX_FCS_ERR_JUMBO,
+	RX_UNDERSIZE,
+	RX_FRAGMENT,
+	RX_64B,
+	RX_65_127B,
+	RX_128_255B,
+	RX_256_511B,
+	RX_512_1023B,
+	RX_1024_1518B,
+	RX_JUMBO,
+	RX_OVERSIZE,
+	RX_OKBYTE,
+	RX_NOT_OKBYTE = 0X16,
+	TX_BROADCAST = 0X18,
+	TX_PAUSE,
+	TX_OAM_COUNTER,
+	TX_MULTICAST,
+	RX_FC_DROP,
+	TX_64B,
+	TX_65_127B,
+	TX_128_255B,
+	TX_256_511B,
+	TX_512_1023B,
+	TX_1024_1518B,
+	TX_JUMBO,
+	TX_OVERSIZE,
+	TX_LATE_COLLISION,
+	TX_EXCESSIVE_COLLISION,
+	TX_MULTI_COLLISION,
+	TX_SINGLE_COLLISION,
+	TX_OK_PKT,
+	TX_DEFER,
+	TX_COLLISION,
+	TX_OKBYTE,
+	RX_DISCARD = 0X2e,
+	TX_QUEUE_DROP,
+	RX_UNICAST,
+	TX_UNICAST,
+	YT_STAT_MAX
+};
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+
+enum yt_stat_mib {
+	RX_BROADCAST = 0,
+	RX_PAUSE,
+	RX_UNICAST,
+	RX_OAM_COUNTER,
+	RX_MULTICAST,
+	RX_ALIGN_ERR_LESS_64B,
+	RX_ALIGN_ERR_64B_1518B,
+	RX_ALIGN_ERR_JUMBO,
+	RX_FCS_ERR_64B_1518B,
+	RX_FCS_ERR_JUMBO,
+	RX_UNDERSIZE,
+	RX_FRAGMENT,
+	RX_64B,
+	RX_65_127B,
+	RX_128_255B,
+	RX_256_511B,
+	RX_512_1023B,
+	RX_1024_1518B,
+	RX_JUMBO,
+	RX_OVERSIZE,
+	TX_BROADCAST,
+	TX_PAUSE,
+	TX_OAM_COUNTER,
+	TX_MULTICAST,
+	RX_FC_DROP,
+	TX_64B,
+	TX_65_127B,
+	TX_128_255B,
+	TX_256_511B,
+	TX_512_1023B,
+	TX_1024_1518B,
+	TX_JUMBO,
+	TX_OVERSIZE,
+	TX_LATE_COLLISION,
+	TX_EXCESSIVE_COLLISION,
+	TX_MULTI_COLLISION,
+	TX_SINGLE_COLLISION,
+	TX_UNICAST,
+	TX_DEFER,
+	TX_COLLISION,
+	RX_DISCARD,
+	TX_QUEUE_DROP,
+	TX_MAC_FILTER,
+	RX_OKBYTE,
+	RX_NOT_OKBYTE,
+	TX_OKBYTE,
+	YT_STAT_MAX
+};
+
+#endif
+
+/**
+ * @internal      yt_stat_mib_enable_set
+ * @endinternal
+ *
+ * @brief         enable mib function
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_stat_mib_enable_set(u8 unit, enum yt_enable enable);
+
+/**
+ * @internal      yt_stat_mib_enable_get
+ * @endinternal
+ *
+ * @brief         get mib enable value
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[out]    enable_ptr             -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_stat_mib_enable_get(u8 unit, enum yt_enable *enable_ptr);
+
+/**
+ * @internal      yt_stat_mib_clear
+ * @endinternal
+ *
+ * @brief         clear one port mib data
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_stat_mib_clear(u8 unit, u32 port);
+
+/**
+ * @internal      yt_stat_mib_clear_all
+ * @endinternal
+ *
+ * @brief         clear all ports mib data
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_stat_mib_clear_all(u8 unit);
+
+/**
+ * @internal      yt_stat_mib_port_get
+ * @endinternal
+ *
+ * @brief         get one port mib counter by mib type
+ * @note          APPLICABLE DEVICES  -Tiger, Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     mib_type                -mib type
+ * @param[out]    cnt_ptr                -mib info
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_stat_mib_port_singletype_cnt_get(u8 unit, u32 port,
+					enum yt_stat_mib mib_type,
+					u64 *cnt_ptr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_storm_ctrl.c b/drivers/net/dsa/motorcomm/switch/yt_storm_ctrl.c
new file mode 100644
index 000000000000..04109a496223
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_storm_ctrl.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_init.h"
+#include "yt_storm_ctrl.h"
+
+static u32 rate_convert_to_cir(u8 rate_mode, u32 time_slot, u64 rate,
+			       u32 *res_cir)
+{
+	u32 cir = 0;
+	u32 divisor;
+	u64 dividend;
+
+	divisor = 1000000000;
+	if (rate_mode == YT_RATE_MODE_BPS) {
+		dividend = ((u64)rate * time_slot) >> 3;
+		do_div64(dividend, divisor);
+		cir = (u32)dividend;
+		cir = (cir == 0) ? 1 : cir;
+	} else {
+		dividend = (u64)rate * 1024 * time_slot;
+		do_div64(dividend, divisor);
+		cir = (u32)dividend;
+		cir = (cir <= 1) ? 2 : cir;
+	}
+
+	if (cir > (0x80000 - 1))
+		return CMM_ERR_NOT_SUPPORT;
+
+	*res_cir = cir;
+
+	return CMM_ERR_OK;
+}
+
+static u32 cir_convert_to_rate(u8 rate_mode, u32 time_slot, u32 cir, u64 *rate)
+{
+	u32 divisor;
+	u64 dividend;
+
+	divisor = time_slot;
+	if (rate_mode == YT_RATE_MODE_BPS)
+		dividend = (u64)cir * 8 * 1000000000;
+	else
+		dividend = ((u64)cir * 1000000000) >> 10;
+	do_div64(dividend, divisor);
+	*rate = (u64)dividend;
+
+	return CMM_ERR_OK;
+}
+
+static u32 storm_ctrl_cbs_get(u8 rate_mode, u32 *res_cbs)
+{
+	u32 cbs = 0;
+
+	if (rate_mode == YT_RATE_MODE_BPS)
+		cbs = STORM_DEFAULT_CBS_BYTE;
+	else
+		cbs = STORM_DEFAULT_CBS_PACKET;
+
+	cbs = (cbs == 0) ? 1 : cbs;
+	if (cbs > (0x400 - 1))
+		return CMM_ERR_NOT_SUPPORT;
+
+	*res_cbs = cbs;
+
+	return CMM_ERR_OK;
+}
+
+static u32 storm_ctrl_timeslot_get(u8 unit, u32 *timeslot)
+{
+	u32 ret = CMM_ERR_OK;
+	u32 time;
+	u32 data;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, STORM_CTRL_TIMESLOT, &data), ret);
+	HAL_FIELD_GET(STORM_CTRL_TIMESLOT_TIMESLOT_LOWBIT,
+		      STORM_CTRL_TIMESLOT_TIMESLOT_WIDTH, &data, &time);
+	time = time * 10000;
+	if (time == 0)
+		return CMM_ERR_NOT_INIT;
+
+	*timeslot = time;
+
+	return ret;
+}
+
+static u32 fal_tiger_get_regidx(u8 unit, u32 port,
+				enum yt_storm_type storm_type, u8 *idx)
+{
+	u32 mac_id;
+
+	mac_id = CAL_YTP_TO_MAC(unit, port);
+	switch (storm_type) {
+	case YT_STORM_TYPE_BCAST:
+		*idx = mac_id;
+		break;
+	case YT_STORM_TYPE_L2_MCAST:
+	case YT_STORM_TYPE_L2_UNKNOWN_MCAST:
+		*idx = YT_MAX_PORT_PER_UNIT + mac_id;
+		break;
+	case YT_STORM_TYPE_UNKNOWN_UCAST:
+		*idx = YT_MAX_PORT_PER_UNIT * 2 + mac_id;
+		break;
+	default:
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return CMM_ERR_OK;
+}
+
+static u32 storm_ctrl_get_regidx(u8 unit, u32 port,
+				 enum yt_storm_type storm_type, u8 *idx)
+{
+	enum yt_switch_chip_id chip_id = YT_SW_ID_9215;
+	u32 macid;
+
+	yt_cal_board_chip_get(&chip_id);
+	if (chip_id == YT_SW_ID_920X || chip_id == YT_SW_ID_9215 ||
+	    chip_id == YT_SW_ID_9218)
+		return fal_tiger_get_regidx(unit, port, storm_type, idx);
+
+	macid = CAL_YTP_TO_MAC(unit, port);
+	switch (storm_type) {
+	case YT_STORM_TYPE_BCAST:
+		*idx = macid;
+		break;
+
+	case YT_STORM_TYPE_UNKNOWN_UCAST:
+		*idx = YT_MAX_PORT_PER_UNIT + macid;
+		break;
+
+	case YT_STORM_TYPE_L2_MCAST:
+	case YT_STORM_TYPE_L2_UNKNOWN_MCAST:
+		*idx = YT_MAX_PORT_PER_UNIT * 2 + macid;
+		break;
+#if defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+	case YT_STORM_TYPE_IPV4_MCAST:
+	case YT_STORM_TYPE_IPV4_UNKNOWN_MCAST:
+		*idx = YT_MAX_PORT_PER_UNIT * 3 + macid;
+		break;
+
+	case YT_STORM_TYPE_IPV6_MCAST:
+	case YT_STORM_TYPE_IPV6_UNKNOWN_MCAST:
+		*idx = YT_MAX_PORT_PER_UNIT * 4 + macid;
+		break;
+#endif
+	default:
+		return CMM_ERR_NOT_SUPPORT;
+	}
+
+	return CMM_ERR_OK;
+}
+
+u32 yt_storm_ctrl_init(u8 unit)
+{
+	u32 global_ctrl = 0;
+	u32 time_slot = 0;
+	u32 ret;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL1, &global_ctrl), ret);
+	HAL_FIELD_SET(GLOBAL_CTRL1_METER_EN_LOWBIT, GLOBAL_CTRL1_METER_EN_WIDTH,
+		      &global_ctrl, YT_ENABLE);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, GLOBAL_CTRL1, global_ctrl), ret);
+
+	HAL_FIELD_SET(STORM_CTRL_TIMESLOT_TIMESLOT_LOWBIT,
+		      STORM_CTRL_TIMESLOT_TIMESLOT_WIDTH, &time_slot,
+		      STORM_DEFAULT_TIMESLOT);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, STORM_CTRL_TIMESLOT, time_slot),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_init);
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+u32 yt_storm_ctrl_enable_set(u8 unit, u32 port, enum yt_storm_type storm_type,
+			     enum yt_enable enable)
+{
+	u32 mac_id = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 strom_ctrl_addr = 0;
+	u32 mc_entry;
+	u32 entry;
+	u16 mc_type = 0;
+	u8 idx = 0;
+
+	CMM_ERR_CHK(storm_ctrl_get_regidx(unit, port, storm_type, &idx), ret);
+	strom_ctrl_addr = STORM_CTRL_CONFIG_TBL + idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, strom_ctrl_addr, &entry), ret);
+	HAL_FIELD_SET(STORM_CTRL_CONFIG_TBL_STORM_EN_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_STORM_EN_WIDTH, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, strom_ctrl_addr, entry), ret);
+
+	if (storm_type != YT_STORM_TYPE_L2_UNKNOWN_MCAST &&
+	    storm_type != YT_STORM_TYPE_L2_MCAST)
+		return ret;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, STORM_CTRL_MC_TYPE_CTRL,
+					&mc_entry), ret);
+	HAL_FIELD_GET(STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT,
+		      STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH,
+		      &mc_entry, &mc_type);
+
+	if (storm_type == YT_STORM_TYPE_L2_UNKNOWN_MCAST)
+		SET_BIT(mc_type, mac_id);
+	else
+		CLEAR_BIT(mc_type, mac_id);
+
+	HAL_FIELD_SET(STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT,
+		      STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH,
+		      &mc_entry, mc_type);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, STORM_CTRL_MC_TYPE_CTRL,
+					 mc_entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_enable_set);
+
+u32 yt_storm_ctrl_enable_get(u8 unit, u32 port, enum yt_storm_type storm_type,
+			     enum yt_enable *enable)
+{
+	u32 mac_id = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 strom_ctrl_addr = 0;
+	u16 mc_type = 0;
+	u32 mc_entry;
+	u32 entry;
+	u8 idx = 0;
+
+	CMM_ERR_CHK(storm_ctrl_get_regidx(unit, port, storm_type, &idx), ret);
+	strom_ctrl_addr = STORM_CTRL_CONFIG_TBL + idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, strom_ctrl_addr, &entry), ret);
+	HAL_FIELD_GET(STORM_CTRL_CONFIG_TBL_STORM_EN_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_STORM_EN_WIDTH, &entry, enable);
+
+	if (storm_type != YT_STORM_TYPE_L2_UNKNOWN_MCAST &&
+	    storm_type != YT_STORM_TYPE_L2_MCAST)
+		return ret;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, STORM_CTRL_MC_TYPE_CTRL,
+					&mc_entry), ret);
+	HAL_FIELD_GET(STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT,
+		      STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH,
+		      &mc_entry, &mc_type);
+	if (storm_type == YT_STORM_TYPE_L2_MCAST) {
+		if ((mc_type & (0x1 << mac_id))) {
+			*enable = YT_DISABLE;
+			return ret;
+		}
+	} else {
+		if ((mc_type & (0x1 << mac_id)) == 0x0) {
+			*enable = YT_DISABLE;
+			return ret;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_enable_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+static u8 storm_ctrl_mc_type_idx_get(enum yt_storm_type storm_type)
+{
+	u8 mc_idx;
+
+	if (storm_type == YT_STORM_TYPE_L2_MCAST ||
+	    storm_type == YT_STORM_TYPE_L2_UNKNOWN_MCAST)
+		mc_idx = 0;
+	else if (storm_type == YT_STORM_TYPE_IPV4_MCAST ||
+		 storm_type == YT_STORM_TYPE_IPV4_UNKNOWN_MCAST)
+		mc_idx = 1;
+	else if (storm_type == YT_STORM_TYPE_IPV6_MCAST ||
+		 storm_type == YT_STORM_TYPE_IPV6_UNKNOWN_MCAST)
+		mc_idx = 2;
+	else
+		mc_idx = 0xff;
+
+	return mc_idx;
+}
+
+u32 yt_storm_ctrl_enable_set(u8 unit, u32 port, enum yt_storm_type storm_type,
+			     enum yt_enable enable)
+{
+	u32 mac_id = CAL_YTP_TO_MAC(unit, port);
+	u32 strom_ctrl_addr = 0;
+	u32 ret = CMM_ERR_OK;
+	u32 mc_type_addr = 0;
+	u32 mc_entry;
+	u32 mc_type = 0;
+	u32 entry;
+	u8 mc_idx;
+	u8 idx = 0;
+
+	CMM_ERR_CHK(storm_ctrl_get_regidx(unit, port, storm_type, &idx), ret);
+	strom_ctrl_addr = STORM_CTRL_CONFIG_TBL + idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, strom_ctrl_addr, &entry), ret);
+	HAL_FIELD_SET(STORM_CTRL_CONFIG_TBL_STORM_EN_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_STORM_EN_WIDTH, &entry, enable);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, strom_ctrl_addr, entry), ret);
+
+	mc_idx = storm_ctrl_mc_type_idx_get(storm_type);
+	if (mc_idx > STORM_CTRL_MC_TYPE_MAX_ID)
+		return ret;
+
+	mc_type_addr = STORM_CTRL_MC_TYPE_CTRL + mc_idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, mc_type_addr, &mc_entry), ret);
+	HAL_FIELD_GET(STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT,
+		      STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH,
+		      &mc_entry, &mc_type);
+
+	if (storm_type >= YT_STORM_TYPE_L2_MCAST &&
+	    storm_type <= YT_STORM_TYPE_IPV6_MCAST)
+		CLEAR_BIT(mc_type, mac_id);
+	else
+		SET_BIT(mc_type, mac_id);
+
+	HAL_FIELD_SET(STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT,
+		      STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH,
+		      &mc_entry, mc_type);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, mc_type_addr, mc_entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_enable_set);
+
+u32 yt_storm_ctrl_enable_get(u8 unit, u32 port, enum yt_storm_type storm_type,
+			     enum yt_enable *enable)
+{
+	u32 mac_id = CAL_YTP_TO_MAC(unit, port);
+	u32 ret = CMM_ERR_OK;
+	u32 strom_ctrl_addr = 0;
+	u32 mc_type_addr = 0;
+	u32 mc_entry;
+	u32 entry;
+	u16 mc_type = 0;
+	u8 mc_idx;
+	u8 idx = 0;
+
+	CMM_ERR_CHK(storm_ctrl_get_regidx(unit, port, storm_type, &idx), ret);
+	strom_ctrl_addr = STORM_CTRL_CONFIG_TBL + idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, strom_ctrl_addr, &entry), ret);
+	HAL_FIELD_GET(STORM_CTRL_CONFIG_TBL_STORM_EN_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_STORM_EN_WIDTH, &entry, enable);
+
+	mc_idx = storm_ctrl_mc_type_idx_get(storm_type);
+	if (mc_idx > STORM_CTRL_MC_TYPE_MAX_ID || (*enable) == YT_DISABLE)
+		return ret;
+
+	mc_type_addr = STORM_CTRL_MC_TYPE_CTRL + mc_idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, mc_type_addr, &mc_entry), ret);
+	HAL_FIELD_GET(STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_LOWBIT,
+		      STORM_CTRL_MC_TYPE_CTRL_STORM_CTRL_MC_TYPE_WIDTH,
+		      &mc_entry, &mc_type);
+
+	if (storm_type >= YT_STORM_TYPE_L2_MCAST &&
+	    storm_type <= YT_STORM_TYPE_IPV6_MCAST) {
+		if (mc_type & (0x1 << mac_id))
+			*enable = YT_DISABLE;
+	} else {
+		if ((mc_type & (0x1 << mac_id)) == 0x0)
+			*enable = YT_DISABLE;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_enable_get);
+#endif
+
+u32 yt_storm_ctrl_rate_mode_set(u8 unit, u32 port,
+				enum yt_storm_type storm_type,
+				struct yt_port_rate_mode rate_mode)
+{
+	u32 strom_ctrl_addr = 0;
+	u32 ret = CMM_ERR_OK;
+	u32 entry;
+	u8 idx = 0;
+
+	CMM_ERR_CHK(storm_ctrl_get_regidx(unit, port, storm_type, &idx), ret);
+	strom_ctrl_addr = STORM_CTRL_CONFIG_TBL + idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, strom_ctrl_addr, &entry), ret);
+	HAL_FIELD_SET(STORM_CTRL_CONFIG_TBL_RATE_MODE_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_RATE_MODE_WIDTH, &entry,
+		      rate_mode.rate_mode);
+	HAL_FIELD_SET(STORM_CTRL_CONFIG_TBL_GAP_INC_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_GAP_INC_WIDTH, &entry,
+		      rate_mode.gap_mode);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, strom_ctrl_addr, entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_rate_mode_set);
+
+u32 yt_storm_ctrl_rate_mode_get(u8 unit, u32 port,
+				enum yt_storm_type storm_type,
+				struct yt_port_rate_mode *rate_mode)
+{
+	u32 strom_ctrl_addr = 0;
+	u32 ret = CMM_ERR_OK;
+	u8 gap_inc = 0;
+	u32 entry;
+	u8 idx = 0;
+	u8 mode = 0;
+
+	CMM_ERR_CHK(storm_ctrl_get_regidx(unit, port, storm_type, &idx), ret);
+	strom_ctrl_addr = STORM_CTRL_CONFIG_TBL + idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, strom_ctrl_addr, &entry), ret);
+	HAL_FIELD_GET(STORM_CTRL_CONFIG_TBL_RATE_MODE_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_RATE_MODE_WIDTH, &entry, &mode);
+	HAL_FIELD_GET(STORM_CTRL_CONFIG_TBL_GAP_INC_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_GAP_INC_WIDTH, &entry, &gap_inc);
+	rate_mode->rate_mode = mode ? YT_RATE_MODE_PPS : YT_RATE_MODE_BPS;
+	rate_mode->gap_mode = gap_inc ? YT_RATE_BPS_GAP_INCLUDE :
+					YT_RATE_BPS_GAP_EXCLUDE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_rate_mode_get);
+
+u32 yt_storm_ctrl_rate_set(u8 unit, u32 port, enum yt_storm_type storm_type,
+			   u64 rate)
+{
+	u32 strom_ctrl_addr = 0;
+	u32 ret = CMM_ERR_OK;
+	u8 rate_mode = 0;
+	u32 time_slot;
+	u32 tbl;
+	u32 cir = 0;
+	u32 cbs = 0;
+	u8 idx = 0;
+
+	CMM_ERR_CHK(storm_ctrl_get_regidx(unit, port, storm_type, &idx), ret);
+	CMM_ERR_CHK(storm_ctrl_timeslot_get(unit, &time_slot), ret);
+	strom_ctrl_addr = STORM_CTRL_CONFIG_TBL + idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, strom_ctrl_addr, &tbl), ret);
+	HAL_FIELD_GET(STORM_CTRL_CONFIG_TBL_RATE_MODE_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_RATE_MODE_WIDTH, &tbl, &rate_mode);
+	CMM_ERR_CHK(rate_convert_to_cir(rate_mode, time_slot, rate, &cir), ret);
+	CMM_ERR_CHK(storm_ctrl_cbs_get(rate_mode, &cbs), ret);
+	HAL_FIELD_SET(STORM_CTRL_CONFIG_TBL_CIR_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_CIR_WIDTH, &tbl, cir);
+	HAL_FIELD_SET(STORM_CTRL_CONFIG_TBL_CBS_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_CBS_WIDTH, &tbl, cbs);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, strom_ctrl_addr, tbl), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_rate_set);
+
+u32 yt_storm_ctrl_rate_get(u8 unit, u32 port, enum yt_storm_type storm_type,
+			   u64 *rate)
+{
+	u32 strom_ctrl_addr = 0;
+	u32 ret = CMM_ERR_OK;
+	u8 rate_mode = 0;
+	u32 time_slot;
+	u32 tbl;
+	u32 cir = 0;
+	u8 idx = 0;
+
+	CMM_ERR_CHK(storm_ctrl_get_regidx(unit, port, storm_type, &idx), ret);
+	CMM_ERR_CHK(storm_ctrl_timeslot_get(unit, &time_slot), ret);
+	strom_ctrl_addr = STORM_CTRL_CONFIG_TBL + idx * 4;
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, strom_ctrl_addr, &tbl), ret);
+	HAL_FIELD_GET(STORM_CTRL_CONFIG_TBL_RATE_MODE_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_RATE_MODE_WIDTH, &tbl, &rate_mode);
+	HAL_FIELD_GET(STORM_CTRL_CONFIG_TBL_CIR_LOWBIT,
+		      STORM_CTRL_CONFIG_TBL_CIR_WIDTH, &tbl, &cir);
+	CMM_ERR_CHK(cir_convert_to_rate(rate_mode, time_slot, cir, rate), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_storm_ctrl_rate_get);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_storm_ctrl.h b/drivers/net/dsa/motorcomm/switch/yt_storm_ctrl.h
new file mode 100644
index 000000000000..91f0a9f5cd06
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_storm_ctrl.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_STORM_CTRL_H
+#define __YT_STORM_CTRL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+#define STORM_DEFAULT_TIMESLOT 41
+#define STORM_DEFAULT_CBS_BYTE 1000
+#define STORM_DEFAULT_CBS_PACKET 0x1E8
+#define STORM_CTRL_MC_TYPE_MAX_ID 2
+
+#if defined(__KERNEL__)
+#include <asm/div64.h>
+#endif
+
+#if defined(__KERNEL__)
+/* b must be u32 */
+#define do_div64(a, b) do_div((a), (b))
+#else
+#define do_div64(a, b)                 \
+	do {                           \
+		typeof(a) __tmp = (a); \
+		(a) = ((__tmp) / (b)); \
+	} while (0)
+#endif
+
+enum yt_storm_type {
+	YT_STORM_TYPE_BCAST,
+	YT_STORM_TYPE_UNKNOWN_UCAST,
+	YT_STORM_TYPE_L2_MCAST,
+#if defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+	YT_STORM_TYPE_IPV4_MCAST,
+	YT_STORM_TYPE_IPV6_MCAST,
+#endif
+	YT_STORM_TYPE_L2_UNKNOWN_MCAST,
+#if defined(CONFIG_MOTORCOMM_YT923X) || defined(CONFIG_MOTORCOMM_YT922X)
+	YT_STORM_TYPE_IPV4_UNKNOWN_MCAST,
+	YT_STORM_TYPE_IPV6_UNKNOWN_MCAST,
+#endif
+};
+
+enum yt_rate_bps_gap_e {
+	YT_RATE_BPS_GAP_EXCLUDE,
+	YT_RATE_BPS_GAP_INCLUDE,
+};
+
+enum yt_rate_mode {
+	YT_RATE_MODE_BPS,
+	YT_RATE_MODE_PPS,
+};
+
+struct yt_port_rate_mode {
+	enum yt_rate_mode rate_mode;
+	enum yt_rate_bps_gap_e gap_mode;
+};
+
+/**
+ * @internal      yt_storm_ctrl_init
+ * @endinternal
+ *
+ * @brief         init storm control module
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_storm_ctrl_init(u8 unit);
+
+/**
+ * @internal      yt_storm_ctrl_enable_set
+ * @endinternal
+ *
+ * @brief         enable specific storm control on port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     storm_type          -storm type,refer to enum yt_storm_type
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_storm_ctrl_enable_set(u8 unit, u32 port, enum yt_storm_type storm_type,
+			     enum yt_enable enable);
+
+/**
+ * @internal      yt_storm_ctrl_enable_get
+ * @endinternal
+ *
+ * @brief         get the storm control enable state on port
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     storm_type          -storm type,refer to enum yt_storm_type
+ * @param[out]    enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_storm_ctrl_enable_get(u8 unit, u32 port, enum yt_storm_type storm_type,
+			     enum yt_enable *enable);
+
+/**
+ * @internal      yt_storm_ctrl_rate_mode_set
+ * @endinternal
+ *
+ * @brief         set the storm control rate mode
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     storm_type          -storm type,refer to enum yt_storm_type
+ * @param[in]     rate_mode           -storm rate mode,bps or pps
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_storm_ctrl_rate_mode_set(u8 unit, u32 port,
+				enum yt_storm_type storm_type,
+				struct yt_port_rate_mode rate_mode);
+
+/**
+ * @internal      yt_storm_ctrl_rate_mode_get
+ * @endinternal
+ *
+ * @brief         get the storm control rate mode
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     storm_type          -storm type,refer to enum yt_storm_type
+ * @param[out]    rate_mode           -storm rate mode,bps or pps
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_storm_ctrl_rate_mode_get(u8 unit, u32 port,
+				enum yt_storm_type storm_type,
+				struct yt_port_rate_mode *rate_mode);
+
+/**
+ * @internal      yt_storm_ctrl_rate_set
+ * @endinternal
+ *
+ * @brief         set storm control rate value.
+ *		  The min unit is 20kbps(1k=1000) or 4pps.
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     storm_type          -storm type,refer to enum yt_storm_type
+ * @param[in]     rate                -storm rate.
+ *				       20kbps~4*10^6kbps or 1~2^9kpps.
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_storm_ctrl_rate_set(u8 unit, u32 port, enum yt_storm_type storm_type,
+			   u64 rate);
+
+/**
+ * @internal      yt_storm_ctrl_rate_get
+ * @endinternal
+ *
+ * @brief         get storm control rate value
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     storm_type          -storm type,refer to enum yt_storm_type
+ * @param[out]    rate               -storm rate
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_storm_ctrl_rate_get(u8 unit, u32 port, enum yt_storm_type storm_type,
+			   u64 *rate);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_stp.c b/drivers/net/dsa/motorcomm/switch/yt_stp.c
new file mode 100644
index 000000000000..027bbd56a61f
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_stp.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_stp.h"
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+
+u32 yt_stp_state_set(u8 unit, u8 stp_id, u32 port, enum yt_stp_state state)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 l2_stp_staten = 0;
+	u32 stp_state = 0;
+
+	//Compatible with shark stp state structures
+	if (state == STP_STATE_DISABLE)
+		state = 0;
+	else if (state == STP_STATE_FORWARD)
+		state = 3;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_STP_STATEN + stp_id * 4,
+					&l2_stp_staten), ret);
+	HAL_FIELD_GET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, &stp_state);
+	stp_state &= ~(0x3 << (macid << 1));
+	stp_state |= (state << (macid << 1));
+	HAL_FIELD_SET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, stp_state);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_STP_STATEN + stp_id * 4,
+					 l2_stp_staten), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stp_state_set);
+
+u32 yt_stp_state_get(u8 unit, u8 stp_id, u32 port, enum yt_stp_state *state_ptr)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 l2_stp_staten = 0;
+	u32 stp_state = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_STP_STATEN + stp_id * 4,
+					&l2_stp_staten), ret);
+	HAL_FIELD_GET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, &stp_state);
+	*state_ptr = 0x3 & (stp_state >> (macid << 1));
+	//Compatible with shark stp state structures
+	if (*state_ptr == STP_STATE_DISABLE)
+		*state_ptr = 0;
+	else if (*state_ptr == STP_STATE_FORWARD)
+		*state_ptr = 3;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stp_state_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT923X)
+
+u32 yt_stp_state_set(u8 unit, u8 stp_id, u32 port, enum yt_stp_state state)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 l2_stp_staten = 0;
+	u32 macid_tmp = 0;
+	u32 stp_state = 0;
+
+	macid_tmp = (macid < 16) ? macid : (macid - 16);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+					L2_STP_STATEN + stp_id * 8 +
+						((macid < 16) ? 0 : 4),
+					&l2_stp_staten), ret);
+	HAL_FIELD_GET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, &stp_state);
+	stp_state &= ~(0x3 << (macid_tmp << 1));
+	stp_state |= (state << (macid_tmp << 1));
+	HAL_FIELD_SET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, stp_state);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit,
+					 L2_STP_STATEN + stp_id * 8 +
+						 ((macid < 16) ? 0 : 4),
+					 l2_stp_staten), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stp_state_set);
+
+u32 yt_stp_state_get(u8 unit, u8 stp_id, u32 port, enum yt_stp_state *state_ptr)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 l2_stp_staten = 0;
+	u32 stp_state = 0;
+
+	u32 macid_tmp = (macid < 16) ? macid : (macid - 16);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit,
+					L2_STP_STATEN + stp_id * 8 +
+						((macid < 16) ? 0 : 4),
+					&l2_stp_staten), ret);
+	HAL_FIELD_GET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, &stp_state);
+	*state_ptr = 0x3 & (stp_state >> (macid_tmp << 1));
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stp_state_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+
+u32 yt_stp_state_set(u8 unit, u8 stp_id, u32 port, enum yt_stp_state state)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 l2_stp_staten = 0;
+	u32 stp_state = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_STP_STATEN + stp_id * 4,
+					&l2_stp_staten), ret);
+	HAL_FIELD_GET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, &stp_state);
+	stp_state &= ~(0x3 << (macid << 1));
+	stp_state |= (state << (macid << 1));
+	HAL_FIELD_SET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, stp_state);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_STP_STATEN + stp_id * 4,
+					 l2_stp_staten), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stp_state_set);
+
+u32 yt_stp_state_get(u8 unit, u8 stp_id, u32 port, enum yt_stp_state *state_ptr)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 l2_stp_staten = 0;
+	u32 stp_state = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_STP_STATEN + stp_id * 4,
+					&l2_stp_staten), ret);
+	HAL_FIELD_GET(L2_STP_STATEN_STATE_LOWBIT, L2_STP_STATEN_STATE_WIDTH,
+		      &l2_stp_staten, &stp_state);
+	*state_ptr = 0x3 & (stp_state >> (macid << 1));
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_stp_state_get);
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_stp.h b/drivers/net/dsa/motorcomm/switch/yt_stp.h
new file mode 100644
index 000000000000..e89e9fc06f0f
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_stp.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_STP_H
+#define __YT_STP_H
+
+#include "yt_cmm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+enum yt_stp_state {
+	STP_STATE_FORWARD = 0,
+	STP_STATE_LEARN,
+	STP_STATE_DISCARD,
+	STP_STATE_DISABLE,
+};
+
+/**
+ * @internal      yt_stp_state_set
+ * @endinternal
+ *
+ * @brief         Set stp state based on port index
+ * @note          APPLICABLE DEVICES  -Tiger,Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     stp_id               -stp instance id
+ * @param[in]     port                -port num
+ * @param[in]     state               -stp state
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port is not valid
+ * @retval        CMM_ERR_EXCEED_RANGE  -input value out of range
+ */
+u32 yt_stp_state_set(u8 unit, u8 stp_id, u32 port, enum yt_stp_state state);
+
+/**
+ * @internal      yt_stp_state_get
+ * @endinternal
+ *
+ * @brief         Get stp state based on port index
+ * @note          APPLICABLE DEVICES  -Tiger,Shark
+ * @param[in]     unit                -unit id
+ * @param[in]     stp_id               -stp instance id
+ * @param[in]     port                -port num
+ * @param[out]    state_ptr              -stp state
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -on port is not valid
+ * @retval        CMM_ERR_NULL_POINT  -null pointer
+ */
+u32 yt_stp_state_get(u8 unit, u8 stp_id, u32 port,
+		     enum yt_stp_state *state_ptr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_sys.c b/drivers/net/dsa/motorcomm/switch/yt_sys.c
new file mode 100644
index 000000000000..d486b5bc9c49
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_sys.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_sys.h"
+
+u32 yt_sys_chip_reset(u8 unit)
+{
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 global_entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, GLOBAL_CTRL0, &global_entry),
+		    ret);
+	HAL_FIELD_SET(GLOBAL_CTRL0_CHIP_SW_RST_LOWBIT,
+		      GLOBAL_CTRL0_CHIP_SW_RST_WIDTH, &global_entry, YT_ENABLE);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, GLOBAL_CTRL0, global_entry),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_sys_chip_reset);
+
+u32 yt_sys_register_value_get(u8 unit, u32 reg_addr, u32 *value_ptr)
+{
+	return HAL_MEM_DIRECT_READ(unit, reg_addr, value_ptr);
+}
+EXPORT_SYMBOL(yt_sys_register_value_get);
+
+u32 yt_sys_register_value_set(u8 unit, u32 reg_addr, u32 value)
+{
+	return HAL_MEM_DIRECT_WRITE(unit, reg_addr, value);
+}
+EXPORT_SYMBOL(yt_sys_register_value_set);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_sys.h b/drivers/net/dsa/motorcomm/switch/yt_sys.h
new file mode 100644
index 000000000000..0fbd945d7b17
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_sys.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT_SYS_H
+#define __YT_SYS_H
+
+/*
+ * Include Files
+ */
+#include "yt_cmm.h"
+
+/**
+ * @internal      yt_sys_chip_reset
+ * @endinternal
+ *
+ * @brief         reset chip reg value to default
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_sys_chip_reset(u8 unit);
+
+/**
+ * @internal      yt_sys_register_value_get
+ * @endinternal
+ *
+ * @brief         get specific register value
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     reg_addr          -register address
+ * @param[out]   value_ptr                -register value
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_sys_register_value_get(u8 unit, u32 reg_addr, u32 *value_ptr);
+
+/**
+ * @internal      yt_sys_register_value_set
+ * @endinternal
+ *
+ * @brief         set specific register value
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     reg_addr          -register address
+ * @param[in]     value              -register value
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_sys_register_value_set(u8 unit, u32 reg_addr, u32 value);
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/switch/yt_types.h b/drivers/net/dsa/motorcomm/switch/yt_types.h
new file mode 100644
index 000000000000..08c231a7d5f8
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_types.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef _YT_TYPES_H
+#define _YT_TYPES_H
+#include <linux/types.h>
+/*
+ * Include Files
+ */
+#if defined(__LINUX__)
+#include <stdint.h>
+#include <unistd.h>
+#endif
+
+#if !defined(__KERNEL__)
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#endif
+
+/*
+ * Symbol Definition
+ */
+
+/*
+ * Macro Definition
+ */
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define LINE_LEN 128
+#define MAC_ADDR_LEN 6
+#define IP4_ADDR_LEN 4
+#define IP6_ADDR_LEN 16
+
+#define YT_MAX_PORT 32 /* max user port num */
+#define YT_MAX_UNIT 1
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+#define YT_MAX_PORT_PER_UNIT 11
+#define YT_INTERNAL_CPU_MACID 10
+#define YT_MIRROR_MAX_GROUP_NUM 1
+#define YT_MIRROR_INVALID_PORT 0XF
+#define YT_LAG_MAX_NUM 4
+#elif defined(CONFIG_MOTORCOMM_YT923X)
+#define YT_MAX_PORT_PER_UNIT 29
+#define YT_INTERNAL_CPU_MACID 28
+#define YT_MIRROR_MAX_GROUP_NUM 4
+#define YT_MIRROR_INVALID_PORT 0X1F
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+#define YT_MAX_PORT_PER_UNIT 10
+#define YT_INTERNAL_CPU_MACID 9
+#define YT_MIRROR_MAX_GROUP_NUM 4
+#define YT_MIRROR_INVALID_PORT 0X0F
+#define YT_LAG_MAX_NUM 4
+#endif
+
+#define YT_PORTS_WORD_SIZE 32
+#define YT_PORTS_WORD_NUM ((YT_MAX_PORT + 31) / YT_PORTS_WORD_SIZE)
+
+#define YT_PHY_NONE (0)
+#define YT_PHY_INT BIT(PHY_INTERNAL)
+#define YT_PHY_EXT BIT(PHY_EXTERNAL)
+#define YT_PHY_INTEXT (YT_PHY_INT | YT_PHY_EXT)
+
+enum yt_fwd_type {
+	FWD_TYPE_FWD,
+	FWD_TYPE_DROP,
+	FWD_TYPE_COPY,
+	FWD_TYPE_TRAP,
+};
+
+enum yt_l2_type {
+	L2_NONE,
+	L2_ETHV2,
+	L2_ETHSAP,
+	L2_ETHSNAP,
+#ifdef CONFIG_MOTORCOMM_YT923X
+	L2_ETH_LOOP,
+	L2_ETH_WOL,
+#endif
+	L2_ETHMAX
+};
+
+enum cmm_err {
+	CMM_ERR_OK = 0,
+	CMM_ERR_FAIL = 1,
+	CMM_ERR_NULL_POINT = 2,
+	CMM_ERR_NOT_SUPPORT,
+	CMM_ERR_NOT_INIT,
+	CMM_ERR_INPUT = 5,
+	CMM_ERR_REG_TABLE_NUM,
+	CMM_ERR_REG_TABLE_OP,
+	CMM_ERR_TABLE_FULL,
+	CMM_ERR_ENTRY_NOT_FOUND,
+	CMM_ERR_REG_TABLE_IDX = 10,
+	CMM_ERR_SAMEENTRY_EXIST,
+	CMM_ERR_ENTRY_FULL,
+	CMM_ERR_FDB_OP_BUSY,
+	CMM_ERR_PORT,
+	CMM_ERR_PORTLIST = 15,
+	CMM_ERR_BUSYING_TIME,
+	CMM_ERR_EXCEED_RANGE,
+	CMM_ERR_PORT_BEEN_IN_LAG,
+	CMM_ERR_TOO_LESS_INFO,
+	CMM_ERR_ACL_NOT_INIT,
+	CMM_ERR_ACL_INSTANCE_NOT_FOUND,
+	CMM_ERR_ACL_INSTANCE_FULL,
+	CMM_ERR_ACL_KEY_SELECT_FAIL,
+	CMM_ERR_ACL_ENTRY_NOT_FOUND,
+	CMM_ERR_ACL_PATTERN_NOT_FOUND,
+	CMM_ERR_ACL_BIN_NOT_ENOUGH,
+	CMM_ERR_ACL_INVALID_KEY_ON_INSTANCE,
+	CMM_ERR_ACL_OP_BUSY,
+	CMM_ERR_ACL_OP_PROHIBIT,
+	CMM_ERR_FORBIDDEN,
+	CMM_ERR_ENTRY_OVERLAPPING,
+	CMM_ERR_ENTRY_INVALID,
+	CMM_ERR_MAX,
+};
+
+enum yt_act_type {
+	ACT_TYPE_FWD,
+	ACT_TYPE_DROP,
+	ACT_TYPE_COPY,
+	ACT_TYPE_TRAP,
+};
+
+enum yt_enable {
+	YT_DISABLE = 0,
+	YT_ENABLE = 1,
+};
+
+enum yt_switch_chip_id {
+	YT_SW_ID_9215 = 0x9002,
+	YT_SW_ID_9218 = 0x9001,
+	YT_SW_ID_923X = 0x9003,
+	YT_SW_ID_922X = 0x9004,
+	YT_SW_ID_920X = 0x9005,
+};
+
+struct yt_mac_addr {
+	u8 addr[MAC_ADDR_LEN];
+};
+
+struct yt_ipv6_addr {
+	u8 addr[IP6_ADDR_LEN];
+};
+
+struct yt_port_mask {
+	u32 portbits[YT_PORTS_WORD_NUM];
+};
+
+struct yt_switch_port_info {
+	struct yt_port_mask int_port_mask;
+	struct yt_port_mask ext_port_mask;
+};
+
+#endif //YT_TYPES_H
diff --git a/drivers/net/dsa/motorcomm/switch/yt_vlan.c b/drivers/net/dsa/motorcomm/switch/yt_vlan.c
new file mode 100644
index 000000000000..32bef3616ef4
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_vlan.c
@@ -0,0 +1,604 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#include <linux/export.h>
+#include "yt_vlan.h"
+
+u32 yt_vlan_init(u8 unit)
+{
+	struct yt_port_mask mbr_mask;
+	struct yt_port_mask untag_mask;
+	u32 port_index = 0;
+	u32 ret = CMM_ERR_OK;
+	u16 vid = 1;
+
+	/* set egress CTPID 0x8100, set egress STPID 0x88a8 */
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, EGR_TPID_PROFILE, 0x8100), ret);
+	for (port_index = 0; port_index < YT_MAX_PORT_PER_UNIT; port_index++) {
+		CMM_ERR_CHK(yt_vlan_port_igrfilter_enable_set(unit, port_index,
+							      YT_ENABLE), ret);
+		CMM_ERR_CHK(yt_vlan_port_egrfilter_enable_set(unit, port_index,
+							      YT_ENABLE), ret);
+		CMM_ERR_CHK(yt_vlan_port_egrtagmode_set
+				(unit, YT_VLAN_TYPE_CVLAN, port_index,
+				 YT_VLAN_TAG_MODE_ENTRY_BASED), ret);
+	}
+
+	mbr_mask.portbits[0] = (1 << YT_MAX_PORT_PER_UNIT) - 1;
+	untag_mask = mbr_mask;
+	CMM_ERR_CHK(yt_vlan_port_set(unit, vid, mbr_mask, untag_mask), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_init);
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+u32 yt_vlan_port_set(u8 unit, u16 vid, struct yt_port_mask member_portmask,
+		     struct yt_port_mask untag_portmask)
+{
+	struct yt_port_mask utag_macmask;
+	struct yt_port_mask mac_mask;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry1 = 0;
+	u32 entry2 = 0;
+
+	CAL_YTPLIST_TO_MLIST(unit, member_portmask, mac_mask);
+	CAL_YTPLIST_TO_MLIST(unit, untag_portmask, utag_macmask);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_TBL + vid * 8, &entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_TBL + vid * 8 + 4,
+					&entry2), ret);
+
+	HAL_FIELD_SET(L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH, &entry1,
+		      mac_mask.portbits[0]);
+	HAL_FIELD_SET(L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH, &entry2,
+		      utag_macmask.portbits[0]);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_VLAN_TBL + vid * 8, entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_VLAN_TBL + vid * 8 + 4,
+					 entry2), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_set);
+
+u32 yt_vlan_port_get(u8 unit, u16 vid, struct yt_port_mask *member_portmask_ptr,
+		     struct yt_port_mask *untag_portmask_ptr)
+{
+	struct yt_port_mask utag_macmask;
+	struct yt_port_mask mac_mask;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry1 = 0;
+	u32 entry2 = 0;
+
+	CMM_CLEAR_MEMBER_PORT(mac_mask);
+	CMM_CLEAR_MEMBER_PORT(utag_macmask);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_TBL + vid * 8, &entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_TBL + vid * 8 + 4,
+					&entry2), ret);
+
+	HAL_FIELD_GET(L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH, &entry1,
+		      &mac_mask.portbits[0]);
+	HAL_FIELD_GET(L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH, &entry2,
+		      &utag_macmask.portbits[0]);
+
+	CAL_MLIST_TO_YTPLIST(unit, mac_mask, (*member_portmask_ptr));
+	CAL_MLIST_TO_YTPLIST(unit, utag_macmask, (*untag_portmask_ptr));
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_get);
+#elif defined(CONFIG_MOTORCOMM_YT923X)
+u32 yt_vlan_port_set(u8 unit, u16 vid, struct yt_port_mask member_portmask,
+		     struct yt_port_mask untag_portmask)
+{
+	struct yt_port_mask utag_macmask;
+	struct yt_port_mask mac_mask;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry1 = 0;
+	u32 entry2 = 0;
+
+	CAL_YTPLIST_TO_MLIST(unit, member_portmask, mac_mask);
+	CAL_YTPLIST_TO_MLIST(unit, untag_portmask, utag_macmask);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_TBL + vid * 8, &entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, VLAN_UNTAG_TBL + vid * 4,
+					&entry2), ret);
+
+	HAL_FIELD_SET(L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH, &entry1,
+		      mac_mask.portbits[0]);
+	HAL_FIELD_SET(L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH, &entry2,
+		      utag_macmask.portbits[0]);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_VLAN_TBL + vid * 8, entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, VLAN_UNTAG_TBL + vid * 4,
+					 entry2), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_set);
+
+u32 yt_vlan_port_get(u8 unit, u16 vid, struct yt_port_mask *member_portmask_ptr,
+		     struct yt_port_mask *untag_portmask_ptr)
+{
+	struct yt_port_mask utag_macmask;
+	struct yt_port_mask mac_mask;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry1 = 0;
+	u32 entry2 = 0;
+
+	CMM_CLEAR_MEMBER_PORT(mac_mask);
+	CMM_CLEAR_MEMBER_PORT(utag_macmask);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_TBL + vid * 8, &entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, VLAN_UNTAG_TBL + vid * 4,
+					&entry2), ret);
+
+	HAL_FIELD_GET(L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH, &entry1,
+		      &mac_mask.portbits[0]);
+	HAL_FIELD_GET(L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH, &entry2,
+		      &utag_macmask.portbits[0]);
+
+	CAL_MLIST_TO_YTPLIST(unit, mac_mask, (*member_portmask_ptr));
+	CAL_MLIST_TO_YTPLIST(unit, utag_macmask, (*untag_portmask_ptr));
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_get);
+
+#elif defined(CONFIG_MOTORCOMM_YT922X)
+u32 yt_vlan_port_set(u8 unit, u16 vid, struct yt_port_mask member_portmask,
+		     struct yt_port_mask untag_portmask)
+{
+	struct yt_port_mask utag_macmask;
+	struct yt_port_mask mac_mask;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry1 = 0;
+	u32 entry2 = 0;
+
+	CAL_YTPLIST_TO_MLIST(unit, member_portmask, mac_mask);
+	CAL_YTPLIST_TO_MLIST(unit, untag_portmask, utag_macmask);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_TBL + vid * 4, &entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, VLAN_UNTAG_TBL + vid * 4,
+					&entry2), ret);
+
+	HAL_FIELD_SET(L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH, &entry1,
+		      mac_mask.portbits[0]);
+	HAL_FIELD_SET(L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH, &entry2,
+		      utag_macmask.portbits[0]);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_VLAN_TBL + vid * 4, entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, VLAN_UNTAG_TBL + vid * 4,
+					 entry2), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_set);
+
+u32 yt_vlan_port_get(u8 unit, u16 vid, struct yt_port_mask *member_portmask_ptr,
+		     struct yt_port_mask *untag_portmask_ptr)
+{
+	struct yt_port_mask utag_macmask;
+	struct yt_port_mask mac_mask;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry1 = 0;
+	u32 entry2 = 0;
+
+	CMM_CLEAR_MEMBER_PORT(mac_mask);
+	CMM_CLEAR_MEMBER_PORT(utag_macmask);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_TBL + vid * 4, &entry1),
+		    ret);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, VLAN_UNTAG_TBL + vid * 4,
+					&entry2), ret);
+
+	HAL_FIELD_GET(L2_VLAN_TBL_PORT_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_PORT_MEMBER_BITMAP_WIDTH, &entry1,
+		      &mac_mask.portbits[0]);
+	HAL_FIELD_GET(L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_LOWBIT,
+		      L2_VLAN_TBL_UNTAG_MEMBER_BITMAP_WIDTH, &entry2,
+		      &utag_macmask.portbits[0]);
+
+	CAL_MLIST_TO_YTPLIST(unit, mac_mask, (*member_portmask_ptr));
+	CAL_MLIST_TO_YTPLIST(unit, utag_macmask, (*untag_portmask_ptr));
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_get);
+
+#endif
+
+u32 yt_vlan_port_igrpvid_set(u8 unit, enum yt_vlan_type type, u32 port, u16 vid)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PORT_VLAN_CTRLN + macid * 4,
+					&entry), ret);
+
+	if (type == YT_VLAN_TYPE_SVLAN)
+		HAL_FIELD_SET(PORT_VLAN_CTRLN_DEFAULT_SVID_LOWBIT,
+			      PORT_VLAN_CTRLN_DEFAULT_SVID_WIDTH, &entry, vid);
+	else if (type == YT_VLAN_TYPE_CVLAN)
+		HAL_FIELD_SET(PORT_VLAN_CTRLN_DEFAULT_CVID_LOWBIT,
+			      PORT_VLAN_CTRLN_DEFAULT_CVID_WIDTH, &entry, vid);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PORT_VLAN_CTRLN + macid * 4,
+					 entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_igrpvid_set);
+
+u32 yt_vlan_port_igrpvid_get(u8 unit, enum yt_vlan_type type, u32 port,
+			     u16 *vid_ptr)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PORT_VLAN_CTRLN + macid * 4,
+					&entry),
+		    ret);
+	if (type == YT_VLAN_TYPE_SVLAN)
+		HAL_FIELD_GET(PORT_VLAN_CTRLN_DEFAULT_SVID_LOWBIT,
+			      PORT_VLAN_CTRLN_DEFAULT_SVID_WIDTH, &entry,
+			      vid_ptr);
+	else if (type == YT_VLAN_TYPE_CVLAN)
+		HAL_FIELD_GET(PORT_VLAN_CTRLN_DEFAULT_CVID_LOWBIT,
+			      PORT_VLAN_CTRLN_DEFAULT_CVID_WIDTH, &entry,
+			      vid_ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_igrpvid_get);
+
+u32 yt_vlan_port_igrfilter_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 mac_mask = 0;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_INGRESS_FILTER_EN,
+					&entry), ret);
+	HAL_FIELD_GET(L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_LOWBIT,
+		      L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_WIDTH, &entry,
+		      &mac_mask);
+	if (enable == YT_ENABLE)
+		SET_BIT(mac_mask, macid);
+	else
+		CLEAR_BIT(mac_mask, macid);
+
+	HAL_FIELD_SET(L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_LOWBIT,
+		      L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_WIDTH, &entry,
+		      mac_mask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_VLAN_INGRESS_FILTER_EN,
+					 entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_igrfilter_enable_set);
+
+u32 yt_vlan_port_igrfilter_enable_get(u8 unit, u32 port,
+				      enum yt_enable *enable_ptr)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 mac_mask = 0;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_VLAN_INGRESS_FILTER_EN,
+					&entry), ret);
+	HAL_FIELD_GET(L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_LOWBIT,
+		      L2_VLAN_INGRESS_FILTER_EN_FILTER_EN_WIDTH, &entry,
+		      &mac_mask);
+
+	*enable_ptr = IS_BIT_SET(mac_mask, macid) ? YT_ENABLE : YT_DISABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_igrfilter_enable_get);
+
+u32 yt_vlan_port_egrfilter_enable_set(u8 unit, u32 port, enum yt_enable enable)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 mac_mask = 0;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_EGR_VLAN_FILTER_EN, &entry),
+		    ret);
+	HAL_FIELD_GET(L2_EGR_VLAN_FILTER_EN_FILTER_EN_LOWBIT,
+		      L2_EGR_VLAN_FILTER_EN_FILTER_EN_WIDTH, &entry, &mac_mask);
+	if (enable == YT_ENABLE)
+		SET_BIT(mac_mask, macid);
+	else
+		CLEAR_BIT(mac_mask, macid);
+
+	HAL_FIELD_SET(L2_EGR_VLAN_FILTER_EN_FILTER_EN_LOWBIT,
+		      L2_EGR_VLAN_FILTER_EN_FILTER_EN_WIDTH, &entry, mac_mask);
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, L2_EGR_VLAN_FILTER_EN, entry),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_egrfilter_enable_set);
+
+u32 yt_vlan_port_egrfilter_enable_get(u8 unit, u32 port,
+				      enum yt_enable *enable_ptr)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum cmm_err ret = CMM_ERR_OK;
+	u16 mac_mask = 0;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, L2_EGR_VLAN_FILTER_EN, &entry),
+		    ret);
+	HAL_FIELD_GET(L2_EGR_VLAN_FILTER_EN_FILTER_EN_LOWBIT,
+		      L2_EGR_VLAN_FILTER_EN_FILTER_EN_WIDTH, &entry, &mac_mask);
+	if (IS_BIT_SET(mac_mask, macid))
+		*enable_ptr = YT_ENABLE;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_egrfilter_enable_get);
+
+u32 yt_vlan_port_egrtagmode_set(u8 unit, enum yt_vlan_type type, u32 port,
+				enum yt_egr_tag_mode tag_mode)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum yt_enable tag_keeptag_only = YT_DISABLE;
+	enum cmm_err ret = CMM_ERR_OK;
+	u8 tag_val = tag_mode;
+	u32 entry = 0;
+
+	if (tag_mode == YT_VLAN_TAG_MODE_KEEP_TAGGED_MODE ||
+	    tag_mode == YT_VLAN_TAG_MODE_ENTRY_BASED)
+		tag_val = tag_mode - 1;
+
+	/* keep all or keep tag only*/
+	if (tag_mode == YT_VLAN_TAG_MODE_KEEP_TAGGED_MODE ||
+	    tag_mode == YT_VLAN_TAG_MODE_KEEP_ALL)
+		tag_keeptag_only = (tag_mode == YT_VLAN_TAG_MODE_KEEP_ALL) ?
+					   YT_DISABLE :
+					   YT_ENABLE;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, EGR_PORT_VLAN_CTRLN + macid * 4,
+					&entry), ret);
+	if (type == YT_VLAN_TYPE_CVLAN) {
+		HAL_FIELD_SET(EGR_PORT_VLAN_CTRLN_CTAG_MODE_LOWBIT,
+			      EGR_PORT_VLAN_CTRLN_CTAG_MODE_WIDTH, &entry,
+			      tag_val);
+		HAL_FIELD_SET(EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_LOWBIT,
+			      EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_WIDTH, &entry,
+			      tag_keeptag_only);
+	} else if (type == YT_VLAN_TYPE_SVLAN) {
+		HAL_FIELD_SET(EGR_PORT_VLAN_CTRLN_STAG_MODE_LOWBIT,
+			      EGR_PORT_VLAN_CTRLN_STAG_MODE_WIDTH, &entry,
+			      tag_val);
+		HAL_FIELD_SET(EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_LOWBIT,
+			      EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_WIDTH, &entry,
+			      tag_keeptag_only);
+	}
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, EGR_PORT_VLAN_CTRLN + macid * 4,
+					 entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_egrtagmode_set);
+
+u32 yt_vlan_port_egrtagmode_get(u8 unit, enum yt_vlan_type type, u32 port,
+				enum yt_egr_tag_mode *tag_mode_ptr)
+{
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+	enum yt_enable tag_keeptag_only = YT_DISABLE;
+	enum cmm_err ret = CMM_ERR_OK;
+	u8 tag_mode = 0;
+	u32 entry = 0;
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, EGR_PORT_VLAN_CTRLN + macid * 4,
+					&entry), ret);
+	if (type == YT_VLAN_TYPE_CVLAN) {
+		HAL_FIELD_GET(EGR_PORT_VLAN_CTRLN_CTAG_MODE_LOWBIT,
+			      EGR_PORT_VLAN_CTRLN_CTAG_MODE_WIDTH, &entry,
+			      &tag_mode);
+		HAL_FIELD_GET(EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_LOWBIT,
+			      EGR_PORT_VLAN_CTRLN_CTAG_KEEP_MODE_WIDTH, &entry,
+			      &tag_keeptag_only);
+	} else if (type == YT_VLAN_TYPE_SVLAN) {
+		HAL_FIELD_GET(EGR_PORT_VLAN_CTRLN_STAG_MODE_LOWBIT,
+			      EGR_PORT_VLAN_CTRLN_STAG_MODE_WIDTH, &entry,
+			      &tag_mode);
+		HAL_FIELD_GET(EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_LOWBIT,
+			      EGR_PORT_VLAN_CTRLN_STAG_KEEP_MODE_WIDTH, &entry,
+			      &tag_keeptag_only);
+	}
+
+	*tag_mode_ptr = tag_mode;
+
+	if (tag_mode == YT_VLAN_TAG_MODE_ENTRY_BASED - 1) {
+		*tag_mode_ptr = YT_VLAN_TAG_MODE_ENTRY_BASED;
+		return ret;
+	}
+
+	/* keep all or keep tag only*/
+	if (tag_mode == YT_VLAN_TAG_MODE_KEEP_ALL)
+		*tag_mode_ptr = tag_keeptag_only == YT_ENABLE ?
+					YT_VLAN_TAG_MODE_KEEP_TAGGED_MODE :
+					YT_VLAN_TAG_MODE_KEEP_ALL;
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_egrtagmode_get);
+
+u32 yt_vlan_igrtpid_set(u8 unit, struct yt_tpid_profiles tpid)
+{
+	u32 entry = 0;
+	u8 i = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	for (i = 0; i < YT_VLAN_TPID_PROFILE_NUM / 2; i++) {
+		HAL_FIELD_SET(TPID_PROFILE0_TPID_LOWBIT,
+			      TPID_PROFILE0_TPID_WIDTH, &entry, tpid.tpid[i]);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, TPID_PROFILE0 + i * 4,
+						 entry), ret);
+	}
+
+	for (i = 2; i < YT_VLAN_TPID_PROFILE_NUM; i++) {
+		HAL_FIELD_SET(TPID_PROFILE1_TPID_LOWBIT,
+			      TPID_PROFILE1_TPID_WIDTH, &entry, tpid.tpid[i]);
+		CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE
+				(unit, TPID_PROFILE1 + (i - 2) * 4,
+				 entry), ret);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_igrtpid_set);
+
+u32 yt_vlan_igrtpid_get(u8 unit, struct yt_tpid_profiles *tpid_ptr)
+{
+	u32 entry = 0;
+	u8 i = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+
+	for (i = 0; i < YT_VLAN_TPID_PROFILE_NUM / 2; i++) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, TPID_PROFILE0 + i * 4,
+						&entry), ret);
+		HAL_FIELD_GET(TPID_PROFILE0_TPID_LOWBIT,
+			      TPID_PROFILE0_TPID_WIDTH, &entry,
+			      &tpid_ptr->tpid[i]);
+	}
+
+	for (i = 2; i < YT_VLAN_TPID_PROFILE_NUM; i++) {
+		CMM_ERR_CHK(HAL_MEM_DIRECT_READ
+				(unit, TPID_PROFILE1 + (i - 2) * 4,
+				 &entry), ret);
+		HAL_FIELD_GET(TPID_PROFILE1_TPID_LOWBIT,
+			      TPID_PROFILE1_TPID_WIDTH, &entry,
+			      &tpid_ptr->tpid[i]);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_igrtpid_get);
+
+u32 yt_vlan_port_igrtpidsel_set(u8 unit, enum yt_vlan_type type, u32 port,
+				u8 tpid_idx_mask)
+{
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_PORT_CTRLN(macid), &entry),
+		    ret);
+
+	if (type == YT_VLAN_TYPE_SVLAN)
+		HAL_FIELD_SET(PARSER_PORT_CTRLN_STAG_TPID_MASK_LOWBIT,
+			      PARSER_PORT_CTRLN_STAG_TPID_MASK_WIDTH, &entry,
+			      tpid_idx_mask & 0xf);
+	else if (type == YT_VLAN_TYPE_CVLAN)
+		HAL_FIELD_SET(PARSER_PORT_CTRLN_CTAG_TPID_MASK_LOWBIT,
+			      PARSER_PORT_CTRLN_CTAG_TPID_MASK_WIDTH, &entry,
+			      tpid_idx_mask & 0xf);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PARSER_PORT_CTRLN(macid), entry),
+		    ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_igrtpidsel_set);
+
+u32 yt_vlan_port_igrtpidsel_get(u8 unit, enum yt_vlan_type type, u32 port,
+				u8 *tpid_idx_mask_ptr)
+{
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PARSER_PORT_CTRLN(macid), &entry),
+		    ret);
+
+	if (type == YT_VLAN_TYPE_SVLAN)
+		HAL_FIELD_GET(PARSER_PORT_CTRLN_STAG_TPID_MASK_LOWBIT,
+			      PARSER_PORT_CTRLN_STAG_TPID_MASK_WIDTH, &entry,
+			      tpid_idx_mask_ptr);
+	else if (type == YT_VLAN_TYPE_CVLAN)
+		HAL_FIELD_GET(PARSER_PORT_CTRLN_CTAG_TPID_MASK_LOWBIT,
+			      PARSER_PORT_CTRLN_CTAG_TPID_MASK_WIDTH, &entry,
+			      tpid_idx_mask_ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_igrtpidsel_get);
+
+u32 yt_vlan_port_aft_set(u8 unit, enum yt_vlan_type type, u32 port,
+			 enum yt_vlan_aft aft)
+{
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PORT_VLAN_CTRL1N + macid * 4,
+					&entry), ret);
+
+	if (type == YT_VLAN_TYPE_SVLAN)
+		HAL_FIELD_SET(PORT_VLAN_CTRL1N_STAG_AFT_LOWBIT,
+			      PORT_VLAN_CTRL1N_STAG_AFT_WIDTH, &entry, aft);
+	else if (type == YT_VLAN_TYPE_CVLAN)
+		HAL_FIELD_SET(PORT_VLAN_CTRL1N_CTAG_AFT_LOWBIT,
+			      PORT_VLAN_CTRL1N_CTAG_AFT_WIDTH, &entry, aft);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_WRITE(unit, PORT_VLAN_CTRL1N + macid * 4,
+					 entry), ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_aft_set);
+
+u32 yt_vlan_port_aft_get(u8 unit, enum yt_vlan_type type, u32 port,
+			 enum yt_vlan_aft *aft_ptr)
+{
+	u32 entry = 0;
+	enum cmm_err ret = CMM_ERR_OK;
+	u32 macid = CAL_YTP_TO_MAC(unit, port);
+
+	CMM_ERR_CHK(HAL_MEM_DIRECT_READ(unit, PORT_VLAN_CTRL1N + macid * 4,
+					&entry), ret);
+
+	if (type == YT_VLAN_TYPE_SVLAN)
+		HAL_FIELD_GET(PORT_VLAN_CTRL1N_STAG_AFT_LOWBIT,
+			      PORT_VLAN_CTRL1N_STAG_AFT_WIDTH, &entry, aft_ptr);
+	else if (type == YT_VLAN_TYPE_CVLAN)
+		HAL_FIELD_GET(PORT_VLAN_CTRL1N_CTAG_AFT_LOWBIT,
+			      PORT_VLAN_CTRL1N_CTAG_AFT_WIDTH, &entry, aft_ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(yt_vlan_port_aft_get);
+
diff --git a/drivers/net/dsa/motorcomm/switch/yt_vlan.h b/drivers/net/dsa/motorcomm/switch/yt_vlan.h
new file mode 100644
index 000000000000..a3d2e5aa9d05
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/switch/yt_vlan.h
@@ -0,0 +1,361 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Motorcomm YT921x/YT922x/YT923x/YT920x Switch Configuration
+ *
+ * Copyright (c) 2026 Kyle Switch
+ */
+#ifndef __YT_VLAN_H
+#define __YT_VLAN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "yt_cmm.h"
+
+#define YT_TPID_PROFILE_MAX_CNT 4
+#define YT_VLAN_TPID_PROFILE_NUM 4
+
+struct yt_tpid_profiles {
+	u16 tpid[YT_TPID_PROFILE_MAX_CNT];
+};
+
+enum yt_vlan_aft {
+	YT_VLAN_AFT_ALL,
+	YT_VLAN_AFT_TAGGED,
+	YT_VLAN_AFT_UNTAGGED,
+	YT_VLAN_AFT_NONE,
+};
+
+enum yt_vlan_type {
+	YT_VLAN_TYPE_CVLAN,
+	YT_VLAN_TYPE_SVLAN,
+};
+
+enum yt_egr_tag_mode {
+	YT_VLAN_TAG_MODE_UNTAGGED,
+	YT_VLAN_TAG_MODE_TAGGED,
+	YT_VLAN_TAG_MODE_TAGGED_EXCEPT_DEF_VID,
+	YT_VLAN_TAG_MODE_PRIO_TAGGED,
+	YT_VLAN_TAG_MODE_KEEP_ALL,
+	YT_VLAN_TAG_MODE_KEEP_TAGGED_MODE,
+	YT_VLAN_TAG_MODE_ENTRY_BASED
+};
+
+/**
+ * @internal      yt_vlan_init
+ * @endinternal
+ *
+ * @brief         init vlan module
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ */
+u32 yt_vlan_init(u8 unit);
+
+/**
+ * @internal      yt_vlan_port_set
+ * @endinternal
+ *
+ * @brief         Set vlan untag port list and tag port list
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     vid                 -vlan id
+ * @param[in]     member_portmask      -member port bit mask
+ * @param[in]     untag_portmask       -untag member port bit mask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORTLIST    -portlist err
+ * @retval        CMM_ERR_EXCEED_RANGE  -input value out of range
+ */
+u32 yt_vlan_port_set(u8 unit, u16 vid, struct yt_port_mask member_portmask,
+		     struct yt_port_mask untag_portmask);
+
+/**
+ * @internal      yt_vlan_port_get
+ * @endinternal
+ *
+ * @brief         Get vlan untag port list and tag port list
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     vid                 -vlan id
+ * @param[out]    member_portmask_ptr     -member port bit mask
+ * @param[in]     untag_portmask_ptr      -untag member port bit mask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_NULL_POINT  -point is NULL
+ * @retval        CMM_ERR_EXCEED_RANGE  -input value out of range
+ */
+u32 yt_vlan_port_get(u8 unit, u16 vid, struct yt_port_mask *member_portmask_ptr,
+		     struct yt_port_mask *untag_portmask_ptr);
+
+/**
+ * @internal      yt_vlan_port_igrpvid_set
+ * @endinternal
+ *
+ * @brief         Set per port ingress PVID of SVLAN or CVLAN
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -YT_VLAN_TYPE_CVLAN or YT_VLAN_TYPE_SVLAN
+ * @param[in]     port                -port num
+ * @param[in]     vid                 -vlan id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ * @retval        CMM_ERR_EXCEED_RANGE  -input value out of range
+ */
+u32 yt_vlan_port_igrpvid_set(u8 unit, enum yt_vlan_type type, u32 port,
+			     u16 vid);
+
+/**
+ * @internal      yt_vlan_port_igrpvid_get
+ * @endinternal
+ *
+ * @brief         Get per port ingress PVID of SVLAN or CVLAN
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -YT_VLAN_TYPE_CVLAN or YT_VLAN_TYPE_SVLAN
+ * @param[in]     port                -port num
+ * @param[out]    vid_ptr                -vlan id
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_NULL_POINT  -point is NULL
+ * @retval        CMM_ERR_PORT        -port err
+ */
+u32 yt_vlan_port_igrpvid_get(u8 unit, enum yt_vlan_type type, u32 port,
+			     u16 *vid_ptr);
+
+/**
+ * @internal      yt_vlan_port_igrfilter_enable_set
+ * @endinternal
+ *
+ * @brief         Set per port ingress filter to enable or disable
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     enable              -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ */
+u32 yt_vlan_port_igrfilter_enable_set(u8 unit, u32 port, enum yt_enable enable);
+
+/**
+ * @internal      yt_vlan_port_igrfilter_enable_get
+ * @endinternal
+ *
+ * @brief         Get per port ingress filter whether is enable or disable
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    enable_ptr             -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_NULL_POINT  -point is NULL
+ * @retval        CMM_ERR_PORT        -port err
+ */
+u32 yt_vlan_port_igrfilter_enable_get(u8 unit, u32 port,
+				      enum yt_enable *enable_ptr);
+
+/**
+ * @internal      yt_vlan_port_egrfilter_enable_set
+ * @endinternal
+ *
+ * @brief         Set per port egress filter to enable or disable
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[in]     enable             -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ */
+u32 yt_vlan_port_egrfilter_enable_set(u8 unit, u32 port, enum yt_enable enable);
+
+/**
+ * @internal      yt_vlan_port_egrfilter_enable_get
+ * @endinternal
+ *
+ * @brief         Get per port egress filter to enable or disable
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     port                -port num
+ * @param[out]    enable_ptr            -enable or disable
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ * @retval        CMM_ERR_NULL_POINT  -point is NULL
+ */
+u32 yt_vlan_port_egrfilter_enable_get(u8 unit, u32 port,
+				      enum yt_enable *enable_ptr);
+
+/**
+ * @internal      yt_vlan_port_egrtagmode_set
+ * @endinternal
+ *
+ * @brief         Set port egress tag mode
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -YT_VLAN_TYPE_CVLAN or YT_VLAN_TYPE_SVLAN
+ * @param[in]     port                -port num
+ * @param[in]     tag_mode             -tag mode
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ */
+u32 yt_vlan_port_egrtagmode_set(u8 unit, enum yt_vlan_type type, u32 port,
+				enum yt_egr_tag_mode tag_mode);
+
+/**
+ * @internal      yt_vlan_port_egrtagmode_get
+ * @endinternal
+ *
+ * @brief         Get port egress tag mode
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -YT_VLAN_TYPE_CVLAN or YT_VLAN_TYPE_SVLAN
+ * @param[in]     port                -port num
+ * @param[out]    tag_mode_ptr            -tag mode
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ * @retval        CMM_ERR_NULL_POINT  -point is NULL
+ */
+u32 yt_vlan_port_egrtagmode_get(u8 unit, enum yt_vlan_type type, u32 port,
+				enum yt_egr_tag_mode *tag_mode_ptr);
+
+/**
+ * @internal      yt_vlan_igrtpid_set
+ * @endinternal
+ *
+ * @brief         Set ingress tpid value
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     tpid                -tpid profile
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ */
+u32 yt_vlan_igrtpid_set(u8 unit, struct yt_tpid_profiles tpid);
+
+/**
+ * @internal      yt_vlan_igrtpid_get
+ * @endinternal
+ *
+ * @brief         Get ingress tpid value
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[out]    pTpid               -tpid profile
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_NULL_POINT  -point is NULL
+ */
+u32 yt_vlan_igrtpid_get(u8 unit, struct yt_tpid_profiles *tpid_ptr);
+
+/**
+ * @internal      yt_vlan_port_igrtpidsel_set
+ * @endinternal
+ *
+ * @brief         Set per port ingress tpid profile mask of SVLAN or CVLAN
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -YT_VLAN_TYPE_CVLAN or YT_VLAN_TYPE_SVLAN
+ * @param[in]     port                -port num
+ * @param[in]     tpid_idx_mask       -tpid profile mask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ */
+u32 yt_vlan_port_igrtpidsel_set(u8 unit, enum yt_vlan_type type, u32 port,
+				u8 tpid_idx_mask);
+
+/**
+ * @internal      yt_vlan_port_igrtpidsel_get
+ * @endinternal
+ *
+ * @brief         Get per port ingress tpid profile mask of SVLAN or CVLAN
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -YT_VLAN_TYPE_CVLAN or YT_VLAN_TYPE_SVLAN
+ * @param[in]     port                -port num
+ * @param[out]    tpid_idx_mask_ptr   -tpid profile mask
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_NULL_POINT  -point is NULL
+ * @retval        CMM_ERR_PORT        -port err
+ */
+u32 yt_vlan_port_igrtpidsel_get(u8 unit, enum yt_vlan_type type, u32 port,
+				u8 *tpid_idx_mask_ptr);
+
+/**
+ * @internal      yt_vlan_port_aft_set
+ * @endinternal
+ *
+ * @brief         Set port accept frame type
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -YT_VLAN_TYPE_CVLAN or YT_VLAN_TYPE_SVLAN
+ * @param[in]     port                -port num
+ * @param[in]     aft                 -accept frame type
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ */
+u32 yt_vlan_port_aft_set(u8 unit, enum yt_vlan_type type, u32 port,
+			 enum yt_vlan_aft aft);
+
+/**
+ * @internal      yt_vlan_port_aft_get
+ * @endinternal
+ *
+ * @brief         Get port accept frame type
+ * @note          APPLICABLE DEVICES  -Tiger, Shark, Whale
+ * @param[in]     unit                -unit id
+ * @param[in]     type                -YT_VLAN_TYPE_CVLAN or YT_VLAN_TYPE_SVLAN
+ * @param[in]     port                -port num
+ * @param[out]    aft_ptr             -accept frame type
+ * @retval        CMM_ERR_OK          -on success
+ * @retval        CMM_ERR_FAIL        -on fail
+ * @retval        CMM_ERR_INPUT       -input value err
+ * @retval        CMM_ERR_NOT_INIT    -not init
+ * @retval        CMM_ERR_PORT        -port err
+ * @retval        CMM_ERR_NULL_POINT  -point is NULL
+ */
+u32 yt_vlan_port_aft_get(u8 unit, enum yt_vlan_type type, u32 port,
+			 enum yt_vlan_aft *aft_ptr);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/drivers/net/dsa/motorcomm/yt92xx.c b/drivers/net/dsa/motorcomm/yt92xx.c
new file mode 100644
index 000000000000..d4b303c48abb
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/yt92xx.c
@@ -0,0 +1,5929 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Motorcomm YT921x and YT922x Switch
+ *
+ * Should work on YT9213/YT9214/YT9215/YT9218/YT9224/YT9228
+ * be sure to do your own checks before porting to another chip.
+ *
+ * Copyright (c) 2025 David Yang
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#include <linux/dcbnl.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/if_hsr.h>
+#include <linux/if_vlan.h>
+#include <linux/iopoll.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/sort.h>
+
+#include <net/dsa.h>
+#include <net/dscp.h>
+#include <net/ieee8021q.h>
+#include <net/pkt_cls.h>
+
+#include "yt92xx.h"
+#include "switch/yt_isolation.h"
+#include "switch/yt_l2.h"
+#include "switch/yt_lag.h"
+#include "switch/yt_nic.h"
+#include "switch/yt_mirror.h"
+#include "switch/yt_multicast.h"
+#include "switch/yt_port.h"
+#include "switch/yt_qos.h"
+#include "switch/yt_stat.h"
+#include "switch/yt_vlan.h"
+#include "switch/yt_stp.h"
+#include "switch/yt_sys.h"
+
+struct yt92xx_mib_desc {
+	unsigned int type;
+	const char *name;
+};
+
+#define MIB_DESC(_type, _name) \
+	{                      \
+		_type, _name   \
+	}
+
+#define to_yt92xx_priv(_ds) container_of_const(_ds, struct yt92xx_priv, ds)
+#define to_device(priv) ((priv)->ds.dev)
+
+static int result_convert(u32 result)
+{
+	int res;
+
+	switch (result) {
+	case CMM_ERR_OK:
+		res = 0;
+		break;
+	case CMM_ERR_FAIL:
+	case CMM_ERR_NOT_SUPPORT:
+		res = -EOPNOTSUPP;
+		break;
+	case CMM_ERR_NULL_POINT:
+		res = -ENOMEM;
+		break;
+	case CMM_ERR_NOT_INIT:
+		res = -ENOMEM;
+		break;
+	case CMM_ERR_INPUT:
+		res = -EINVAL;
+		break;
+	case CMM_ERR_SAMEENTRY_EXIST:
+	case CMM_ERR_ENTRY_OVERLAPPING:
+		res = -EEXIST;
+		break;
+	case CMM_ERR_FDB_OP_BUSY:
+	case CMM_ERR_ACL_OP_BUSY:
+	case CMM_ERR_BUSYING_TIME:
+		res = -EBUSY;
+		break;
+	default:
+		res = -EINVAL;
+		break;
+	}
+
+	return res;
+}
+
+#define YT_NAME "motorcomm"
+
+struct yt92xx_info {
+	const char *name;
+	u16 major;
+	/* Unknown, seems to be plain enumeration */
+	u8 mode;
+	u8 extmode;
+	u16 internal_mask;
+	u16 external_mask;
+};
+
+static const struct ethtool_rmon_hist_range yt92xx_rmon_ranges[] = {
+	{ 0, 64 },
+	{ 65, 127 },
+	{ 128, 255 },
+	{ 256, 511 },
+	{ 512, 1023 },
+	{ 1024, 1518 },
+	{ 1519, YT92XX_FRAME_SIZE_MAX },
+	{}
+};
+
+#define YT921X_PORT_MASK_INTn(port) BIT(port)
+#define YT921X_PORT_MASK_INT0_n(n) GENMASK((n) - 1, 0)
+#define YT921X_PORT_MASK_EXT0 BIT(8)
+#define YT921X_PORT_MASK_EXT1 BIT(9)
+
+static const struct yt92xx_info yt92xx_infos[] = {
+	{
+		"YT9215SC",
+		YT9215_MAJOR,
+		1,
+		0,
+		YT921X_PORT_MASK_INT0_n(5),
+		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+	},
+	{
+		"YT9215S",
+		YT9215_MAJOR,
+		2,
+		0,
+		YT921X_PORT_MASK_INT0_n(5),
+		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+	},
+	{
+		"YT9215RB",
+		YT9215_MAJOR,
+		3,
+		0,
+		YT921X_PORT_MASK_INT0_n(5),
+		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+	},
+	{
+		"YT9214NB",
+		YT9215_MAJOR,
+		3,
+		2,
+		YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
+		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+	},
+	{
+		"YT9213NB",
+		YT9215_MAJOR,
+		3,
+		3,
+		YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
+		YT921X_PORT_MASK_EXT1,
+	},
+	{
+		"YT9218N",
+		YT9218_MAJOR,
+		0,
+		0,
+		YT921X_PORT_MASK_INT0_n(8),
+		0,
+	},
+	{
+		"YT9218MB",
+		YT9218_MAJOR,
+		1,
+		0,
+		YT921X_PORT_MASK_INT0_n(8),
+		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+	},
+	{
+		"YT9224",
+		 YT9224_MAJOR,
+		 0,
+		 0,
+		 0x1F1,
+		 0x0,
+	},
+	{}
+};
+
+struct yt92xx_reg_mdio {
+	struct mii_bus *bus;
+	int addr;
+	/* SWITCH_ID_1 / SWITCH_ID_0 of the device
+	 *
+	 * This is a way to multiplex multiple devices on the same MII phyaddr
+	 * and should be configurable in DT. However, MDIO core simply doesn't
+	 * allow multiple devices over one reg addr, so this is a fixed value
+	 * for now until a solution is found.
+	 *
+	 * Keep this because we need switchid to form MII regaddrs anyway.
+	 */
+	unsigned char switchid;
+};
+
+static struct yt92xx_reg_mdio *yt92xx_reg_mdios[YT92XX_MAX_SWITCHES];
+
+/* check internal phy and external phy */
+static bool
+yt92xx_port_is_external(struct yt92xx_priv *priv, int port)
+{
+	if (BIT(port) & priv->info->external_mask)
+		return true;
+	else
+		return false;
+}
+
+static int
+yt92xx_port_is_internal(struct yt92xx_priv *priv, int port)
+{
+	if (BIT(port) & priv->info->internal_mask)
+		return true;
+	else
+		return false;
+}
+
+/* The interval should be small enough to avoid overflow of 32bit MIBs.
+ *
+ * Until we can read MIBs from stats64 call directly (i.e. sleep
+ * there), we have to poll stats more frequently then it is actually needed.
+ * For overflow protection, normally, 100 sec interval should have been OK.
+ */
+#define YT92XX_STATS_INTERVAL_JIFFIES (3 * HZ)
+
+static void yt92xx_poll_mib(struct work_struct *work)
+{
+	struct yt92xx_port *pp =
+		container_of_const(work, struct yt92xx_port, mib_read.work);
+	struct yt92xx_priv *priv =
+		(void *)(pp - pp->index) - offsetof(struct yt92xx_priv, ports);
+	struct dsa_switch *ds = &priv->ds;
+	unsigned long delay = YT92XX_STATS_INTERVAL_JIFFIES;
+	int port = pp->index;
+	int res;
+
+	if (priv->chip_info->ops->stats_read)
+		res = priv->chip_info->ops->stats_read(ds, port);
+
+	if (res)
+		delay *= 4;
+
+	schedule_delayed_work(&pp->mib_read, delay);
+}
+
+#define YT92XX_POLL_SLEEP_US 10000
+#define YT92XX_POLL_TIMEOUT_US 100000
+
+static int yt92xx_reg_read(struct yt92xx_priv *priv, u32 reg, u32 *valp)
+{
+	int res;
+
+	WARN_ON(!mutex_is_locked(&priv->reg_lock));
+	res = result_convert(yt_sys_register_value_get(priv->chip_info->unit,
+						       reg, valp));
+
+	return res;
+}
+
+static int yt92xx_reg_write(struct yt92xx_priv *priv, u32 reg, u32 val)
+{
+	int res;
+
+	WARN_ON(!mutex_is_locked(&priv->reg_lock));
+	res = result_convert(yt_sys_register_value_set(priv->chip_info->unit,
+						       reg, val));
+
+	return res;
+}
+
+static int yt92xx_reg_wait(struct yt92xx_priv *priv, u32 reg, u32 mask,
+			   u32 *valp)
+{
+	u32 val;
+	int res;
+	int ret;
+
+	ret = read_poll_timeout(yt92xx_reg_read, res,
+				res || (val & mask) == *valp,
+				YT92XX_POLL_SLEEP_US, YT92XX_POLL_TIMEOUT_US,
+				false, priv, reg, &val);
+	if (ret)
+		return ret;
+	if (res)
+		return res;
+
+	*valp = val;
+	return 0;
+}
+
+static int yt92xx_reg_update_bits(struct yt92xx_priv *priv, u32 reg, u32 mask,
+				  u32 val)
+{
+	int res;
+	u32 v;
+	u32 u;
+
+	res = yt92xx_reg_read(priv, reg, &v);
+	if (res)
+		return res;
+
+	u = v;
+	u &= ~mask;
+	u |= val;
+	if (u == v)
+		return 0;
+
+	return yt92xx_reg_write(priv, reg, u);
+}
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+static int yt92xx_reg_set_bits(struct yt92xx_priv *priv, u32 reg, u32 mask)
+{
+	return yt92xx_reg_update_bits(priv, reg, 0, mask);
+}
+
+static int yt92xx_reg_clear_bits(struct yt92xx_priv *priv, u32 reg, u32 mask)
+{
+	return yt92xx_reg_update_bits(priv, reg, mask, 0);
+}
+
+/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
+ * long register. More specifically, writes to parts of its words won't become
+ * visible, until the last word is written.
+ *
+ * Here we require full read and write operations over these registers to
+ * eliminate potential issues, although partial reads/writes are also possible.
+ */
+
+static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)
+{
+	*lo &= ~lower_32_bits(mask);
+	*hi &= ~upper_32_bits(mask);
+	*lo |= lower_32_bits(val);
+	*hi |= upper_32_bits(val);
+}
+#endif
+
+static int yt92xx_regs_read(struct yt92xx_priv *priv, u32 reg, u32 *vals,
+			    unsigned int num_regs)
+{
+	int res;
+
+	for (unsigned int i = 0; i < num_regs; i++) {
+		res = yt92xx_reg_read(priv, reg + 4 * i, &vals[i]);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static int yt92xx_regs_write(struct yt92xx_priv *priv, u32 reg, const u32 *vals,
+			     unsigned int num_regs)
+{
+	int res;
+
+	for (unsigned int i = 0; i < num_regs; i++) {
+		res = yt92xx_reg_write(priv, reg + 4 * i, vals[i]);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static int yt92xx_regs_update_bits(struct yt92xx_priv *priv, u32 reg,
+				   const u32 *masks, const u32 *vals,
+				   unsigned int num_regs)
+{
+	bool changed = false;
+	u32 vs[4];
+	int res;
+
+	BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
+
+	res = yt92xx_regs_read(priv, reg, vs, num_regs);
+	if (res)
+		return res;
+
+	for (unsigned int i = 0; i < num_regs; i++) {
+		u32 u = vs[i];
+
+		u &= ~masks[i];
+		u |= vals[i];
+		if (u != vs[i])
+			changed = true;
+
+		vs[i] = u;
+	}
+
+	if (!changed)
+		return 0;
+
+	return yt92xx_regs_write(priv, reg, vs, num_regs);
+}
+
+#if defined(CONFIG_MOTORCOMM_YT921X)
+static int yt92xx_reg96_write(struct yt92xx_priv *priv, u32 reg,
+			      const u32 *vals)
+{
+	return yt92xx_regs_write(priv, reg, vals, 3);
+}
+#endif
+
+static u32 yt92xx_reg_mdio_read_register(u8 unit, u32 reg, u32 *valp)
+{
+	struct yt92xx_reg_mdio *mdio = yt92xx_reg_mdios[unit];
+	struct mii_bus *bus = mdio->bus;
+	int addr = mdio->addr;
+	u32 reg_addr;
+	u32 reg_data;
+	u32 val;
+	int res;
+
+	/* Hold the mdio bus lock to avoid (un)locking for 4 times */
+	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+	reg_addr = YT92XX_SMI_SWITCHID(mdio->switchid) | YT92XX_SMI_ADDR |
+		   YT92XX_SMI_READ;
+	res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
+	if (res)
+		goto end;
+	res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
+	if (res)
+		goto end;
+
+	reg_data = YT92XX_SMI_SWITCHID(mdio->switchid) | YT92XX_SMI_DATA |
+		   YT92XX_SMI_READ;
+	res = __mdiobus_read(bus, addr, reg_data);
+	if (res < 0)
+		goto end;
+	val = (u16)res;
+	res = __mdiobus_read(bus, addr, reg_data);
+	if (res < 0)
+		goto end;
+	val = (val << 16) | (u16)res;
+
+	*valp = val;
+	res = 0;
+
+end:
+	mutex_unlock(&bus->mdio_lock);
+	return (u32)-res;
+}
+
+static u32 yt92xx_reg_mdio_write_register(u8 unit, u32 reg, u32 val)
+{
+	struct yt92xx_reg_mdio *mdio = yt92xx_reg_mdios[unit];
+	struct mii_bus *bus = mdio->bus;
+	int addr = mdio->addr;
+	u32 reg_addr;
+	u32 reg_data;
+	int res;
+
+	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+	reg_addr = YT92XX_SMI_SWITCHID(mdio->switchid) | YT92XX_SMI_ADDR |
+		   YT92XX_SMI_WRITE;
+	res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
+	if (res)
+		goto end;
+	res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
+	if (res)
+		goto end;
+
+	reg_data = YT92XX_SMI_SWITCHID(mdio->switchid) | YT92XX_SMI_DATA |
+		   YT92XX_SMI_WRITE;
+	res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16));
+	if (res)
+		goto end;
+	res = __mdiobus_write(bus, addr, reg_data, (u16)val);
+	if (res)
+		goto end;
+
+	res = 0;
+
+end:
+	mutex_unlock(&bus->mdio_lock);
+	return (u32)-res;
+}
+
+/* TODO: SPI/I2C */
+
+static int yt92xx_intif_wait(struct yt92xx_priv *priv)
+{
+	u32 val = 0;
+
+	return yt92xx_reg_wait(priv, YT92XX_INT_MBUS_OP, YT92XX_MBUS_OP_START,
+			       &val);
+}
+
+static int yt92xx_intif_read(struct yt92xx_priv *priv, int port, int reg,
+			     u16 *valp)
+{
+	struct device *dev = to_device(priv);
+	u32 mask;
+	u32 ctrl;
+	u32 val;
+	int res;
+
+	res = yt92xx_intif_wait(priv);
+	if (res)
+		return res;
+
+	mask = YT92XX_MBUS_CTRL_PORT_M | YT92XX_MBUS_CTRL_REG_M |
+	       YT92XX_MBUS_CTRL_OP_M;
+	ctrl = YT92XX_MBUS_CTRL_PORT(port) | YT92XX_MBUS_CTRL_REG(reg) |
+	       YT92XX_MBUS_CTRL_READ;
+	res = yt92xx_reg_update_bits(priv, YT92XX_INT_MBUS_CTRL, mask, ctrl);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT92XX_INT_MBUS_OP, YT92XX_MBUS_OP_START);
+	if (res)
+		return res;
+
+	res = yt92xx_intif_wait(priv);
+	if (res)
+		return res;
+	res = yt92xx_reg_read(priv, YT92XX_INT_MBUS_DIN, &val);
+	if (res)
+		return res;
+
+	if ((u16)val != val)
+		dev_info(dev,
+			 "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
+			 __func__, port, reg, val);
+	*valp = (u16)val;
+	return 0;
+}
+
+static int yt92xx_intif_write(struct yt92xx_priv *priv, int port, int reg,
+			      u16 val)
+{
+	u32 mask;
+	u32 ctrl;
+	int res;
+
+	res = yt92xx_intif_wait(priv);
+	if (res)
+		return res;
+
+	mask = YT92XX_MBUS_CTRL_PORT_M | YT92XX_MBUS_CTRL_REG_M |
+	       YT92XX_MBUS_CTRL_OP_M;
+	ctrl = YT92XX_MBUS_CTRL_PORT(port) | YT92XX_MBUS_CTRL_REG(reg) |
+	       YT92XX_MBUS_CTRL_WRITE;
+	res = yt92xx_reg_update_bits(priv, YT92XX_INT_MBUS_CTRL, mask, ctrl);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT92XX_INT_MBUS_DOUT, val);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT92XX_INT_MBUS_OP, YT92XX_MBUS_OP_START);
+	if (res)
+		return res;
+
+	return yt92xx_intif_wait(priv);
+}
+
+static int yt92xx_mbus_int_read(struct mii_bus *mbus, int port, int reg)
+{
+	struct yt92xx_priv *priv = mbus->priv;
+	u16 val;
+	int res;
+
+	if (port >= YT92XX_PORT_NUM)
+		return U16_MAX;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_intif_read(priv, port, reg, &val);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		return res;
+	return val;
+}
+
+static int yt92xx_mbus_int_write(struct mii_bus *mbus, int port, int reg,
+				 u16 data)
+{
+	struct yt92xx_priv *priv = mbus->priv;
+	int res;
+
+	if (port >= YT92XX_PORT_NUM)
+		return -ENODEV;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_intif_write(priv, port, reg, data);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_mbus_int_init(struct yt92xx_priv *priv,
+				struct device_node *mnp)
+{
+	struct device *dev = to_device(priv);
+	struct mii_bus *mbus;
+	int res;
+
+	mbus = devm_mdiobus_alloc(dev);
+	if (!mbus)
+		return -ENOMEM;
+
+	mbus->name = "YT92xx internal MDIO bus";
+	snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+	mbus->priv = priv;
+	mbus->read = yt92xx_mbus_int_read;
+	mbus->write = yt92xx_mbus_int_write;
+	mbus->parent = dev;
+	mbus->phy_mask = (u32)~GENMASK(YT92XX_PORT_NUM - 1, 0);
+
+	res = devm_of_mdiobus_register(dev, mbus, mnp);
+	if (res)
+		return res;
+
+	priv->mbus_int = mbus;
+
+	return 0;
+}
+
+static int yt92xx_extif_wait(struct yt92xx_priv *priv)
+{
+	u32 val = 0;
+
+	return yt92xx_reg_wait(priv, YT92XX_EXT_MBUS_OP, YT92XX_MBUS_OP_START,
+			       &val);
+}
+
+static int yt92xx_extif_read(struct yt92xx_priv *priv, int port, int reg,
+			     u16 *valp)
+{
+	struct device *dev = to_device(priv);
+	u32 mask;
+	u32 ctrl;
+	u32 val;
+	int res;
+
+	res = yt92xx_extif_wait(priv);
+	if (res)
+		return res;
+
+	mask = YT92XX_MBUS_CTRL_PORT_M | YT92XX_MBUS_CTRL_REG_M |
+	       YT92XX_MBUS_CTRL_TYPE_M | YT92XX_MBUS_CTRL_OP_M;
+	ctrl = YT92XX_MBUS_CTRL_PORT(port) | YT92XX_MBUS_CTRL_REG(reg) |
+	       YT92XX_MBUS_CTRL_TYPE_C22 | YT92XX_MBUS_CTRL_READ;
+	res = yt92xx_reg_update_bits(priv, YT92XX_EXT_MBUS_CTRL, mask, ctrl);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT92XX_EXT_MBUS_OP, YT92XX_MBUS_OP_START);
+	if (res)
+		return res;
+
+	res = yt92xx_extif_wait(priv);
+	if (res)
+		return res;
+	res = yt92xx_reg_read(priv, YT92XX_EXT_MBUS_DIN, &val);
+	if (res)
+		return res;
+
+	if ((u16)val != val)
+		dev_info(dev,
+			 "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
+			 __func__, port, reg, val);
+	*valp = (u16)val;
+	return 0;
+}
+
+static int yt92xx_extif_write(struct yt92xx_priv *priv, int port, int reg,
+			      u16 val)
+{
+	u32 mask;
+	u32 ctrl;
+	int res;
+
+	res = yt92xx_extif_wait(priv);
+	if (res)
+		return res;
+
+	mask = YT92XX_MBUS_CTRL_PORT_M | YT92XX_MBUS_CTRL_REG_M |
+	       YT92XX_MBUS_CTRL_TYPE_M | YT92XX_MBUS_CTRL_OP_M;
+	ctrl = YT92XX_MBUS_CTRL_PORT(port) | YT92XX_MBUS_CTRL_REG(reg) |
+	       YT92XX_MBUS_CTRL_TYPE_C22 | YT92XX_MBUS_CTRL_WRITE;
+	res = yt92xx_reg_update_bits(priv, YT92XX_EXT_MBUS_CTRL, mask, ctrl);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT92XX_EXT_MBUS_DOUT, val);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT92XX_EXT_MBUS_OP, YT92XX_MBUS_OP_START);
+	if (res)
+		return res;
+
+	return yt92xx_extif_wait(priv);
+}
+
+static int yt92xx_mbus_ext_read(struct mii_bus *mbus, int port, int reg)
+{
+	struct yt92xx_priv *priv = mbus->priv;
+	u16 val;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_extif_read(priv, port, reg, &val);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		return res;
+	return val;
+}
+
+static int yt92xx_mbus_ext_write(struct mii_bus *mbus, int port, int reg,
+				 u16 data)
+{
+	struct yt92xx_priv *priv = mbus->priv;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_extif_write(priv, port, reg, data);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_mbus_ext_init(struct yt92xx_priv *priv,
+				struct device_node *mnp)
+{
+	struct device *dev = to_device(priv);
+	struct mii_bus *mbus;
+	int res;
+
+	mbus = devm_mdiobus_alloc(dev);
+	if (!mbus)
+		return -ENOMEM;
+
+	mbus->name = "YT92xx external MDIO bus";
+	snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev));
+	mbus->priv = priv;
+	/* TODO: c45? */
+	mbus->read = yt92xx_mbus_ext_read;
+	mbus->write = yt92xx_mbus_ext_write;
+	mbus->parent = dev;
+
+	res = devm_of_mdiobus_register(dev, mbus, mnp);
+	if (res)
+		return res;
+
+	priv->mbus_ext = mbus;
+
+	return 0;
+}
+
+/* Must agree with yt92xx_mib
+ *
+ * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
+ * structured go to their *_stats() methods, but we need their sizes and offsets
+ * to perform 32bit MIB overflow wraparound.
+ */
+#if IS_ENABLED(CONFIG_MOTORCOMM_YT921X)
+static const struct yt92xx_mib_desc yt92xx_mib_descs[] = {
+	MIB_DESC(RX_BROADCAST, NULL),
+	MIB_DESC(RX_PAUSE, NULL),
+	MIB_DESC(RX_MULTICAST, NULL),
+	MIB_DESC(RX_FCS_ERR, NULL),
+
+	MIB_DESC(RX_ALIGNMENT_ERR, NULL),
+	MIB_DESC(RX_UNDERSIZE, NULL),
+	MIB_DESC(RX_FRAGMENT, NULL),
+	MIB_DESC(RX_64B, NULL),
+
+	MIB_DESC(RX_65_127B, NULL),
+	MIB_DESC(RX_128_255B, NULL),
+	MIB_DESC(RX_256_511B, NULL),
+	MIB_DESC(RX_512_1023B, NULL),
+
+	MIB_DESC(RX_1024_1518B, NULL),
+	MIB_DESC(RX_JUMBO, NULL),
+	MIB_DESC(RX_OKBYTE, NULL),
+
+	MIB_DESC(RX_NOT_OKBYTE, "RxBadBytes"),
+	MIB_DESC(RX_OVERSIZE, NULL),
+
+	MIB_DESC(RX_DISCARD, NULL),
+	MIB_DESC(TX_BROADCAST, NULL),
+	MIB_DESC(TX_PAUSE, NULL),
+	MIB_DESC(TX_MULTICAST, NULL),
+
+	MIB_DESC(TX_UNDERSIZE, NULL),
+	MIB_DESC(TX_64B, NULL),
+	MIB_DESC(TX_65_127B, NULL),
+	MIB_DESC(TX_128_255B, NULL),
+
+	MIB_DESC(TX_256_511B, NULL),
+	MIB_DESC(TX_512_1023B, NULL),
+	MIB_DESC(TX_1024_1518B, NULL),
+	MIB_DESC(TX_JUMBO, NULL),
+
+	MIB_DESC(TX_OKBYTE, NULL),
+	MIB_DESC(TX_COLLISION, NULL),
+
+	MIB_DESC(TX_EXCESSIVE_COLLISION, NULL),
+	MIB_DESC(TX_MULTI_COLLISION, NULL),
+	MIB_DESC(TX_SINGLE_COLLISION, NULL),
+	MIB_DESC(TX_OK_PKT, NULL),
+
+	MIB_DESC(TX_DEFER, NULL),
+	MIB_DESC(TX_LATE_COLLISION, NULL),
+	MIB_DESC(RX_OAM_COUNTER, "RxOAM"),
+	MIB_DESC(TX_OAM_COUNTER, "TxOAM"),
+};
+
+static u32 ethaddr_hi4_to_u32(const unsigned char *addr)
+{
+	return (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
+}
+
+static u32 ethaddr_lo2_to_u32(const unsigned char *addr)
+{
+	return (addr[4] << 8) | addr[5];
+}
+
+static int
+yt92xx_reg64_write(struct yt92xx_priv *priv, u32 reg, const u32 *vals)
+{
+	return yt92xx_regs_write(priv, reg, vals, 2);
+}
+
+static int yt921x_stats_read(struct dsa_switch *ds, int port)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct device *dev = to_device(priv);
+	struct yt92xx_mib *mib = &pp->mib;
+	int res = 0;
+
+	/* Reading of yt92xx_port::mib is not protected by a lock and it's vain
+	 * to keep its consistency, since we have to read registers one by one
+	 * and there is no way to make a snapshot of MIB stats.
+	 *
+	 * Writing (by this function only) is and should be protected by
+	 * reg_lock.
+	 */
+	mutex_lock(&priv->reg_lock);
+	for (size_t i = 0; i < ARRAY_SIZE(yt92xx_mib_descs); i++) {
+		const struct yt92xx_mib_desc *desc = &yt92xx_mib_descs[i];
+
+		res = result_convert(yt_stat_mib_port_singletype_cnt_get
+			(priv->chip_info->unit, port, desc->type,
+			&((u64 *)mib)[i]));
+	}
+	mutex_unlock(&priv->reg_lock);
+
+	pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
+			mib->rx_128_255byte + mib->rx_256_511byte +
+			mib->rx_512_1023byte + mib->rx_1024_1518byte +
+			mib->rx_jumbo;
+	pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
+			mib->tx_128_255byte + mib->tx_256_511byte +
+			mib->tx_512_1023byte + mib->tx_1024_1518byte +
+			mib->tx_jumbo;
+
+	if (res)
+		dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
+			port, res);
+
+	return res;
+}
+
+static int yt921x_stats_eth_mac_stats(struct dsa_switch *ds, int port,
+				      struct ethtool_eth_mac_stats *mac_stats)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+	int res = 0;
+
+	res = yt921x_stats_read(ds, port);
+	if (res)
+		return res;
+
+	mac_stats->FramesTransmittedOK = pp->tx_frames;
+	mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
+	mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
+	mac_stats->FramesReceivedOK = pp->rx_frames;
+	mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
+	mac_stats->AlignmentErrors = mib->rx_alignment_errors;
+	mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
+	mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
+	mac_stats->LateCollisions = mib->tx_late_collisions;
+	mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
+	/* mac_stats->FramesLostDueToIntMACXmitError */
+	/* mac_stats->CarrierSenseErrors */
+	mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
+	/* mac_stats->FramesLostDueToIntMACRcvError */
+	mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
+	mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
+	/* mac_stats->FramesWithExcessiveDeferral */
+	mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
+	mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
+	/* mac_stats->InRangeLengthErrors */
+	/* mac_stats->OutOfRangeLengthField */
+	mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
+
+	return res;
+}
+
+static int
+yt921x_stats_rmon_stats(struct dsa_switch *ds, int port,
+			struct ethtool_rmon_stats *rmon_stats,
+			const struct ethtool_rmon_hist_range **ranges)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+	int res;
+
+	res = yt921x_stats_read(ds, port);
+	if (res)
+		return res;
+
+	*ranges = yt92xx_rmon_ranges;
+
+	rmon_stats->undersize_pkts = mib->rx_undersize_errors;
+	rmon_stats->oversize_pkts = mib->rx_oversize_errors;
+	rmon_stats->fragments = mib->rx_fragment_errors;
+	/* rmon_stats->jabbers */
+
+	rmon_stats->hist[0] = mib->rx_64byte;
+	rmon_stats->hist[1] = mib->rx_65_127byte;
+	rmon_stats->hist[2] = mib->rx_128_255byte;
+	rmon_stats->hist[3] = mib->rx_256_511byte;
+	rmon_stats->hist[4] = mib->rx_512_1023byte;
+	rmon_stats->hist[5] = mib->rx_1024_1518byte;
+	rmon_stats->hist[6] = mib->rx_jumbo;
+
+	rmon_stats->hist_tx[0] = mib->tx_64byte;
+	rmon_stats->hist_tx[1] = mib->tx_65_127byte;
+	rmon_stats->hist_tx[2] = mib->tx_128_255byte;
+	rmon_stats->hist_tx[3] = mib->tx_256_511byte;
+	rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
+	rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
+	rmon_stats->hist_tx[6] = mib->tx_jumbo;
+
+	return res;
+}
+
+static int yt921x_stats_stats64(struct dsa_switch *ds, int port,
+				struct rtnl_link_stats64 *stats)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+
+	stats->rx_length_errors =
+		mib->rx_undersize_errors + mib->rx_fragment_errors;
+	stats->rx_over_errors = mib->rx_oversize_errors;
+	stats->rx_crc_errors = mib->rx_crc_errors;
+	stats->rx_frame_errors = mib->rx_alignment_errors;
+	/* stats->rx_fifo_errors */
+	/* stats->rx_missed_errors */
+
+	stats->tx_aborted_errors = mib->tx_aborted_errors;
+	/* stats->tx_carrier_errors */
+	stats->tx_fifo_errors = mib->tx_undersize_errors;
+	/* stats->tx_heartbeat_errors */
+	stats->tx_window_errors = mib->tx_late_collisions;
+
+	stats->rx_packets = pp->rx_frames;
+	stats->tx_packets = pp->tx_frames;
+	stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
+	stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
+	stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
+			   stats->rx_crc_errors + stats->rx_frame_errors;
+	stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
+			   stats->tx_window_errors;
+	stats->rx_dropped = mib->rx_dropped;
+	/* stats->tx_dropped */
+	stats->multicast = mib->rx_multicast;
+	stats->collisions = mib->tx_collisions;
+
+	return 0;
+}
+
+static void yt921x_rate_policer_del(struct dsa_switch *ds, int port)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct device *dev = to_device(priv);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert(yt_sys_register_value_set
+		(priv->chip_info->unit, YT921X_PORTn_METER(port), 0));
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dev, "Failed to %s port %d: %i\n", "delete policer on",
+			port, res);
+}
+
+static int yt921x_police_validate(const struct flow_action_police *police,
+				  const struct flow_action *action,
+				  const struct flow_action_entry *act,
+				  struct netlink_ext_ack *extack)
+{
+	if (police->exceed.act_id != FLOW_ACTION_DROP) {
+		NL_SET_ERR_MSG_MOD
+			(extack,
+			"Offload not supported when exceed action is not drop");
+		return -EOPNOTSUPP;
+	}
+
+	if (police->notexceed.act_id != FLOW_ACTION_PIPE &&
+	    police->notexceed.act_id != FLOW_ACTION_ACCEPT) {
+		NL_SET_ERR_MSG_MOD
+			(extack,
+			"Offload not supported when conform action is not pipe or ok");
+		return -EOPNOTSUPP;
+	}
+
+	if (police->notexceed.act_id == FLOW_ACTION_ACCEPT && action && act &&
+	    !flow_action_is_last_entry(action, act)) {
+		NL_SET_ERR_MSG_MOD
+			(extack,
+			"Offload not supported when conform action is ok, but action is not last");
+		return -EOPNOTSUPP;
+	}
+
+	/* mtu defaults to unlimited but we got 2040 here, don't know why */
+	if (police->peakrate_bytes_ps || police->avrate || police->overhead) {
+		NL_SET_ERR_MSG_MOD
+			(extack,
+			"Offload not supported when peakrate/avrate/overhead is configured");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+/* v * 2^e */
+static u64 ldexpu64(u64 v, int e)
+{
+	return e >= 0 ? v << e : v >> -e;
+}
+
+/* slot (ns) * rate (/s) / 10^9 (ns/s) = 2^C * token * 4^unit */
+static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C)
+{
+	int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
+
+	return div_u64(ldexpu64(slot_ns * rate, -e), 1000000000);
+}
+
+static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C)
+{
+	int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
+
+	return div_u64(ldexpu64(mul_u32_u32(1000000000, token), e), slot_ns);
+}
+
+/* burst = 2^C * token * 4^unit */
+static u32 burst2token(u64 burst, int unit, int C)
+{
+	return ldexpu64(burst, -(2 * unit + C));
+}
+
+static u64 token2burst(u32 token, int unit, int C)
+{
+	return ldexpu64(token, 2 * unit + C);
+}
+
+struct yt921x_marker {
+	u32 cir;
+	u32 cbs;
+	u32 ebs;
+	int unit;
+	bool pkt_mode;
+};
+
+#define YT921X_MARKER_PKT_MODE BIT(0)
+#define YT921X_MARKER_SINGLE_BUCKET BIT(1)
+
+static int yt921x_mtu_fetch(struct yt92xx_priv *priv, int port)
+{
+	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+
+	return dp->user ? READ_ONCE(dp->user->mtu) : ETH_DATA_LEN;
+}
+
+static int yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst,
+			     unsigned int flags, unsigned int slot_ns,
+			     u32 cir_max, u32 cbs_max, int unit_max,
+			     struct yt92xx_priv *priv, int port,
+			     struct netlink_ext_ack *extack)
+{
+	const int C = flags & YT921X_MARKER_PKT_MODE ? YT921X_TOKEN_PKT_C :
+						       YT921X_TOKEN_BYTE_C;
+	struct device *dev = to_device(priv);
+	struct yt921x_marker m;
+	u64 burst_est;
+	u64 burst_sug;
+	u64 burst_max;
+	u64 rate_max;
+
+	m.unit = unit_max;
+	rate_max = token2rate(cir_max, slot_ns, m.unit, C);
+	burst_max = token2burst(cbs_max, m.unit, C);
+
+	/* Check for unusual values */
+	if (rate > rate_max || burst > burst_max) {
+		NL_SET_ERR_MSG_MOD(extack, "Unexpected tremendous rate");
+		return -ERANGE;
+	}
+
+	/* Check for matching burst */
+	burst_est = div_u64(slot_ns * rate, 1000000000);
+	burst_sug = burst_est;
+	if (flags & YT921X_MARKER_PKT_MODE)
+		burst_sug++;
+	else
+		burst_sug +=
+			ETH_HLEN + yt921x_mtu_fetch(priv, port) + ETH_FCS_LEN;
+	if (burst_sug > burst)
+		NL_SET_ERR_MSG_FMT_MOD
+			(extack,
+			"Consider match rate %llu with burst at least %llu",
+			rate, burst_sug);
+
+	/* Select unit */
+	for (; m.unit > 0; m.unit--) {
+		if (rate > (rate_max >> 2) || burst > (burst_max >> 2))
+			break;
+		rate_max >>= 2;
+		burst_max >>= 2;
+	}
+
+	/* Calculate information rate and bucket size */
+	m.cir = rate2token(rate, slot_ns, m.unit, C);
+	if (!m.cir)
+		m.cir = 1;
+	else if (WARN_ON(m.cir > cir_max))
+		m.cir = cir_max;
+	m.cbs = burst2token(burst, m.unit, C);
+	if (!m.cbs)
+		m.cbs = 1;
+	else if (WARN_ON(m.cbs > cbs_max))
+		m.cbs = cbs_max;
+
+	/* Cut EBS */
+	m.ebs = 0;
+	if (!(flags & YT921X_MARKER_SINGLE_BUCKET)) {
+		/* We don't have a chance to adjust rate when MTU is changed */
+		if (flags & YT921X_MARKER_PKT_MODE)
+			burst_est++;
+		else
+			burst_est += YT92XX_FRAME_SIZE_MAX;
+
+		if (burst_est < burst) {
+			u32 pbs = m.cbs;
+
+			m.cbs = burst2token(burst_est, m.unit, C);
+			if (!m.cbs)
+				m.cbs = 1;
+			else if (WARN_ON(m.cbs > cbs_max))
+				m.cbs = cbs_max;
+
+			if (pbs > m.cbs)
+				m.ebs = pbs - m.cbs;
+		}
+	}
+
+	dev_dbg(dev,
+		"slot %u ns, rate %llu, burst %llu -> unit %d, cir %u, cbs %u, ebs %u\n",
+		slot_ns, rate, burst, m.unit, m.cir, m.cbs, m.ebs);
+
+	m.pkt_mode = flags & YT921X_MARKER_PKT_MODE;
+	*marker = m;
+	return 0;
+}
+
+static int
+yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst,
+			unsigned int flags, struct yt92xx_priv *priv, int port,
+			struct netlink_ext_ack *extack)
+{
+	return yt921x_marker_tfm(marker, rate, burst, flags,
+				 priv->port_shape_slot_ns, YT921X_SHAPE_CIR_MAX,
+				 YT921X_SHAPE_CBS_MAX, YT921X_SHAPE_UNIT_MAX,
+				 priv, port, extack);
+}
+
+static int yt921x_marker_tfm_police(struct yt921x_marker *marker,
+				    const struct flow_action_police *police,
+				    unsigned int flags,
+				    struct yt92xx_priv *priv, int port,
+				    struct netlink_ext_ack *extack)
+{
+	bool pkt_mode = !!police->rate_pkt_ps;
+	u64 burst;
+	u64 rate;
+
+	rate = pkt_mode ? police->rate_pkt_ps : police->rate_bytes_ps;
+	burst = pkt_mode ? police->burst_pkt : police->burst;
+	if (pkt_mode)
+		flags |= YT921X_MARKER_PKT_MODE;
+
+	return yt921x_marker_tfm(marker, rate, burst, flags,
+				 priv->meter_slot_ns, YT921X_METER_CIR_MAX,
+				 YT921X_METER_CBS_MAX, YT921X_METER_UNIT_MAX,
+				 priv, port, extack);
+}
+
+static int yt921x_meter_config(struct yt92xx_priv *priv, unsigned int id,
+			       const struct yt921x_marker *marker)
+{
+	u32 ctrls[3];
+	int res;
+
+	ctrls[0] = 0;
+	ctrls[1] = YT921X_METER_CTRLb_CIR(marker->cir);
+	ctrls[2] = YT921X_METER_CTRLc_UNIT(marker->unit) |
+		   YT921X_METER_CTRLc_DROP_R |
+		   YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN |
+		   YT921X_METER_CTRLc_METER_EN;
+	if (marker->pkt_mode)
+		ctrls[2] |= YT921X_METER_CTRLc_PKT_MODE;
+	update_ctrls_unaligned(&ctrls[0], &ctrls[1], YT921X_METER_CTRLab_EBS_M,
+			       YT921X_METER_CTRLab_EBS(marker->ebs));
+	update_ctrls_unaligned(&ctrls[1], &ctrls[2], YT921X_METER_CTRLbc_CBS_M,
+			       YT921X_METER_CTRLbc_CBS(marker->cbs));
+
+	res = yt92xx_reg96_write(priv, YT921X_METERn_CTRL(id), ctrls);
+
+	return res;
+}
+
+static int yt921x_rate_policer_add(struct dsa_switch *ds, int port,
+				   const struct flow_action_police *police,
+				   struct netlink_ext_ack *extack)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt921x_marker marker;
+	u32 ctrl;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_police_validate(police, NULL, NULL, extack);
+	if (res)
+		goto unlock;
+
+	res = yt921x_marker_tfm_police(&marker, police, 0, priv, port, extack);
+	if (res)
+		goto unlock;
+
+	res = yt921x_meter_config(priv, port + YT921X_METER_NUM, &marker);
+	if (res)
+		goto unlock;
+
+	ctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN;
+	res = result_convert(yt_sys_register_value_set
+		(priv->chip_info->unit, YT921X_PORTn_METER(port), ctrl));
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt921x_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
+				const struct switchdev_vlan_msti *msti)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u32 masks[2];
+	u32 ctrls[2];
+	int res;
+
+	if (!msti->vid)
+		return -EINVAL;
+	if (!msti->msti || msti->msti >= YT92XX_MSTI_NUM)
+		return -EINVAL;
+
+	masks[0] = 0;
+	ctrls[0] = 0;
+	masks[1] = YT92XX_VLAN_CTRLb_STP_ID_M;
+	ctrls[1] = YT92XX_VLAN_CTRLb_STP_ID(msti->msti);
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_regs_update_bits(priv, YT92XX_VLANn_CTRL(msti->vid),
+				      masks, ctrls, 2);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static void yt921x_mac_caps(struct dsa_switch *ds, int port,
+			    struct phylink_config *config)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	const struct yt92xx_info *info = priv->info;
+
+	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
+				   MAC_100 | MAC_1000;
+
+	if (info->internal_mask & BIT(port)) {
+		/* Port 10 for MCU should probably go here too. But since that
+		 * is untested yet, turn it down for the moment by letting it
+		 * fall to the default branch.
+		 */
+		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
+			  config->supported_interfaces);
+	} else if (info->external_mask & BIT(port)) {
+		/* TODO: external ports may support SERDES only, XMII only, or
+		 * SERDES + XMII depending on the chip. However, we can't get
+		 * the accurate config table due to lack of document, thus
+		 * we simply declare SERDES + XMII and rely on the correctness
+		 * of devicetree for now.
+		 */
+
+		/* SERDES */
+		__set_bit(PHY_INTERFACE_MODE_SGMII,
+			  config->supported_interfaces);
+		/* REVSGMII (SGMII in PHY role) should go here, once
+		 * PHY_INTERFACE_MODE_REVSGMII is introduced.
+		 */
+		__set_bit(PHY_INTERFACE_MODE_100BASEX,
+			  config->supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
+			  config->supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
+			  config->supported_interfaces);
+		config->mac_capabilities |= MAC_2500FD;
+
+		/* XMII */
+
+		/* Not tested. To add support for XMII:
+		 *   - Add proper interface modes below
+		 *   - Handle them in yt921x_port_config()
+		 */
+	}
+	/* no such port: empty supported_interfaces causes phylink to turn it
+	 * down
+	 */
+}
+
+static int yt921x_edata_wait(struct yt92xx_priv *priv, u32 *valp)
+{
+	u32 val = YT921X_EDATA_DATA_IDLE;
+	int res;
+
+	res = yt92xx_reg_wait(priv, YT921X_EDATA_DATA,
+			      YT921X_EDATA_DATA_STATUS_M, &val);
+	if (res)
+		return res;
+
+	*valp = val;
+	return 0;
+}
+
+static int yt921x_edata_read_cont(struct yt92xx_priv *priv, u8 addr, u8 *valp)
+{
+	u32 ctrl;
+	u32 val;
+	int res;
+
+	ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ;
+	res = yt92xx_reg_write(priv, YT921X_EDATA_CTRL, ctrl);
+	if (res)
+		return res;
+	res = yt921x_edata_wait(priv, &val);
+	if (res)
+		return res;
+
+	*valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val);
+	return 0;
+}
+
+static int yt921x_edata_read(struct yt92xx_priv *priv, u8 addr, u8 *valp)
+{
+	u32 val;
+	int res;
+
+	res = yt921x_edata_wait(priv, &val);
+	if (res)
+		return res;
+	return yt921x_edata_read_cont(priv, addr, valp);
+}
+
+static int yt921x_chip_detect(struct yt92xx_priv *priv)
+{
+	struct device *dev = to_device(priv);
+	const struct yt92xx_info *info;
+	u8 extmode;
+	u32 chipid;
+	u32 major;
+	u32 mode;
+	u32 val;
+	int res;
+
+	res = yt92xx_reg_read(priv, YT921X_CHIP_ID, &chipid);
+	if (res)
+		return res;
+
+	major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);
+
+	for (info = yt92xx_infos; info->name; info++)
+		if (info->major == major)
+			break;
+	if (!info->name) {
+		dev_err(dev, "Unexpected chipid 0x%x\n", chipid);
+		return -ENODEV;
+	}
+
+	res = yt92xx_reg_read(priv, YT921X_CHIP_MODE, &mode);
+	if (res)
+		return res;
+	res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode);
+	if (res)
+		return res;
+
+	for (; info->name; info++)
+		if (info->major == major && info->mode == mode &&
+		    info->extmode == extmode)
+			break;
+	if (!info->name) {
+		dev_err(dev,
+			"Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n",
+			chipid, mode, extmode);
+		return -ENODEV;
+	}
+
+	res = yt92xx_reg_read(priv, YT921X_SYS_CLK, &val);
+	if (res)
+		return res;
+	switch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) {
+	case 0:
+		priv->cycle_ns = info->major == YT9215_MAJOR ? 8 : 6;
+		break;
+	case YT921X_SYS_CLK_143M:
+		priv->cycle_ns = 7;
+		break;
+	default:
+		priv->cycle_ns = 8;
+	}
+
+	/* Print chipid here since we are interested in lower 16 bits */
+	dev_info
+		(dev,
+		"Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n",
+		info->name, chipid, mode, extmode);
+
+	priv->info = info;
+
+	return 0;
+}
+
+static int yt921x_chip_reset(struct yt92xx_priv *priv)
+{
+	struct device *dev = to_device(priv);
+	u16 eth_p_tag;
+	u32 val;
+	int res;
+
+	res = yt921x_chip_detect(priv);
+	if (res)
+		return res;
+
+	/* Reset */
+	res = yt92xx_reg_write(priv, YT921X_RST, YT921X_RST_HW);
+	if (res)
+		return res;
+
+	/* RST_HW is almost same as GPIO hard reset, so we need this delay. */
+	fsleep(YT92XX_RST_DELAY_US);
+
+	val = 0;
+	res = yt92xx_reg_wait(priv, YT921X_RST, ~0, &val);
+	if (res)
+		return res;
+
+	/* Check for tag EtherType; do it after reset in case you messed it up
+	 * before.
+	 */
+	res = yt92xx_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
+	if (res)
+		return res;
+	eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
+	if (eth_p_tag != ETH_P_YT92XX) {
+		dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
+			ETH_P_YT92XX);
+		/* Despite being possible, we choose not to set CPU_TAG_TPID,
+		 * since there is no way it can be different unless you have the
+		 * wrong chip.
+		 */
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int yt921x_chip_setup_dsa(struct yt92xx_priv *priv)
+{
+	struct dsa_switch *ds = &priv->ds;
+	unsigned long cpu_ports_mask;
+	struct yt_port_mask member;
+	struct yt_port_mask untag;
+	u32 ctrl;
+	int port;
+	int res;
+
+	/* Enable DSA */
+	priv->cpu_ports_mask = dsa_cpu_ports(ds);
+
+	ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN |
+	       YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask));
+	res = yt92xx_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl);
+	if (res)
+		return res;
+
+	/* Setup software switch */
+	ctrl = YT921X_CPU_COPY_TO_EXT_CPU;
+	res = yt92xx_reg_write(priv, YT921X_CPU_COPY, ctrl);
+	if (res)
+		return res;
+
+	ctrl = GENMASK(10, 0);
+	res = yt92xx_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl);
+	if (res)
+		return res;
+
+	/* YT921x does not support native DSA port bridging, so we use port
+	 * isolation to emulate it. However, be especially careful that port
+	 * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from
+	 * another bridge) is matched and the destination port (in another
+	 * bridge) is blocked, the packet will be dropped instead of flooding to
+	 * the "bridged" ports, thus we need to trap and handle those packets by
+	 * software.
+	 *
+	 * If there is no more than one bridge, we might be able to drop them
+	 * directly given some conditions are met, but we trap them in all cases
+	 * for now.
+	 */
+	ctrl = 0;
+	for (int i = 0; i < YT92XX_PORT_NUM; i++)
+		ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i);
+	/* Except for CPU ports, if any packets are sent via CPU ports without
+	 * tag, they should be dropped.
+	 */
+	cpu_ports_mask = priv->cpu_ports_mask;
+	for_each_set_bit(port, &cpu_ports_mask, YT92XX_PORT_NUM) {
+		ctrl &= ~YT921X_ACT_UNK_ACTn_M(port);
+		ctrl |= YT921X_ACT_UNK_ACTn_DROP(port);
+	}
+	res = yt92xx_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl);
+	if (res)
+		return res;
+
+	/* Tagged VID 0 should be treated as untagged, which confuses the
+	 * hardware a lot
+	 */
+	res = result_convert(yt_l2_port_learn_en_set
+	(priv->chip_info->unit, 0, YT_DISABLE));
+	if (res)
+		return res;
+	res = result_convert(yt_vlan_port_get(priv->chip_info->unit,
+					      0, &member, &untag));
+	if (res)
+		return res;
+	member.portbits[0] = 0;
+	res = result_convert(yt_vlan_port_set(priv->chip_info->unit,
+					      0, member, untag));
+
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt921x_chip_setup_tc(struct yt92xx_priv *priv)
+{
+	unsigned int op_ns;
+	u32 ctrl;
+	int res;
+
+	op_ns = 8 * priv->cycle_ns;
+
+	ctrl = max(priv->meter_slot_ns / op_ns, YT921X_METER_SLOT_MIN);
+	res = yt92xx_reg_write(priv, YT921X_METER_SLOT, ctrl);
+	if (res)
+		return res;
+	priv->meter_slot_ns = ctrl * op_ns;
+
+	ctrl = max(priv->port_shape_slot_ns / op_ns,
+		   YT921X_PORT_SHAPE_SLOT_MIN);
+	res = yt92xx_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl);
+	if (res)
+		return res;
+	priv->port_shape_slot_ns = ctrl * op_ns;
+
+	return 0;
+}
+
+static int yt921x_chip_setup_acl(struct yt92xx_priv *priv)
+{
+	u32 ctrl;
+	int res;
+
+	ctrl = YT921X_ACL_PERMIT_UNMATCH_PORTS_M;
+	res = yt92xx_reg_write(priv, YT921X_ACL_PERMIT_UNMATCH, ctrl);
+	if (res)
+		return res;
+
+	ctrl = YT921X_ACL_PORT_PORTS_M;
+	res = yt92xx_reg_write(priv, YT921X_ACL_PORT, ctrl);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int __maybe_unused yt921x_chip_setup_qos(struct yt92xx_priv *priv)
+{
+	u32 ctrl;
+	int res;
+
+	/* DSCP to internal priorities */
+	for (u8 dscp = 0; dscp < DSCP_MAX; dscp++) {
+		int prio = ietf_dscp_to_ieee8021q_tt(dscp);
+
+		if (prio < 0)
+			return prio;
+
+		res = yt92xx_reg_write(priv, YT92XX_IPM_DSCPn(dscp),
+				       YT92XX_IPM_PRIO(prio));
+		if (res)
+			return res;
+	}
+
+	/* 802.1Q QoS to internal priorities */
+	for (u8 pcp = 0; pcp < 8; pcp++)
+		for (u8 dei = 0; dei < 2; dei++) {
+			ctrl = YT92XX_IPM_PRIO(pcp);
+			if (dei)
+				/* "Red" almost means drop, so it's not that
+				 * useful. Note that tc police does not support
+				 * Three-Color very well
+				 */
+				ctrl |= YT92XX_IPM_COLOR_YELLOW;
+
+			for (u8 svlan = 0; svlan < 2; svlan++) {
+				u32 reg = YT92XX_IPM_PCPn(svlan, dei, pcp);
+
+				res = yt92xx_reg_write(priv, reg, ctrl);
+				if (res)
+					return res;
+			}
+		}
+
+	return 0;
+}
+
+static int yt921x_chip_setup(struct yt92xx_priv *priv)
+{
+	u32 ctrl;
+	int res;
+
+	ctrl = YT921X_FUNC_MIB | YT921X_FUNC_METER | YT921X_FUNC_ACL;
+	res = yt92xx_reg_set_bits(priv, YT921X_FUNC, ctrl);
+	if (res)
+		return res;
+
+	res = yt921x_chip_setup_dsa(priv);
+	if (res)
+		return res;
+
+	res = yt921x_chip_setup_tc(priv);
+	if (res)
+		return res;
+
+	res = yt921x_chip_setup_acl(priv);
+	if (res)
+		return res;
+
+#if IS_ENABLED(CONFIG_DCB)
+	res = yt921x_chip_setup_qos(priv);
+	if (res)
+		return res;
+#endif
+
+	/* Clear MIB */
+	ctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT;
+	res = yt92xx_reg_write(priv, YT921X_MIB_CTRL, ctrl);
+	if (res)
+		return res;
+
+	/* Miscellaneous */
+	res = yt92xx_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt921x_dsa_setup(struct dsa_switch *ds)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct device *dev = to_device(priv);
+	struct device_node *np = dev->of_node;
+	struct device_node *child;
+	struct yt_drv_func drv;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	drv.switch_reg_read = &yt92xx_reg_mdio_read_register;
+	drv.switch_reg_write = &yt92xx_reg_mdio_write_register;
+	res = result_convert(yt_drv_register(drv));
+	mutex_unlock(&priv->reg_lock);
+	if (res)
+		return res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_chip_reset(priv);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		return res;
+
+	/* Register the internal mdio bus. Nodes for internal ports should have
+	 * proper phy-handle pointing to their PHYs. Not enabling the internal
+	 * bus is possible, though pretty wired, if internal ports are not used.
+	 */
+	child = of_get_child_by_name(np, "mdio");
+	if (child) {
+		res = yt92xx_mbus_int_init(priv, child);
+		of_node_put(child);
+		if (res)
+			return res;
+	}
+
+	/* External mdio bus is optional */
+	child = of_get_child_by_name(np, "mdio-external");
+	if (child) {
+		res = yt92xx_mbus_ext_init(priv, child);
+		of_node_put(child);
+		if (res)
+			return res;
+
+		dev_err(dev, "Untested external mdio bus\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_chip_setup(priv);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int
+yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
+				  const struct tc_tbf_qopt_offload *qopt)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct netlink_ext_ack *extack = qopt->extack;
+	u32 ctrls[2];
+	int res;
+
+	if (qopt->parent != TC_H_ROOT)
+		return -EOPNOTSUPP;
+
+	switch (qopt->command) {
+	case TC_TBF_STATS:
+		/* Unfortunately the convention for TC_*_STATS is a mess,
+		 * neither 0 nor -EOPNOTSUPP is perfect here.
+		 */
+		return -EOPNOTSUPP;
+	case TC_TBF_DESTROY:
+		ctrls[0] = 0;
+		ctrls[1] = 0;
+		break;
+	case TC_TBF_REPLACE: {
+		const struct tc_tbf_qopt_offload_replace_params *p;
+		struct yt921x_marker marker;
+
+		p = &qopt->replace_params;
+
+		res = yt921x_marker_tfm_shape(&marker, p->rate.rate_bytes_ps,
+					      p->max_size,
+					      YT921X_MARKER_SINGLE_BUCKET,
+					      priv, port, extack);
+		if (res)
+			return res;
+
+		ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) |
+			   YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs);
+		ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) |
+			   YT921X_PORT_SHAPE_CTRLb_EN;
+		break;
+	}
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+/* ACL: 48 blocks * 8 entries
+ *
+ * One rule can span multiple entries, but within a block.
+ */
+
+static void
+yt921x_acl_entry_set(struct yt921x_acl_entry *entry, unsigned int offset,
+		     u32 flags, bool set)
+{
+	if (set)
+		entry->key[offset] |= flags;
+	entry->mask[offset] |= flags;
+}
+
+static unsigned int
+yt921x_acl_entries_set_is_fragment(struct yt921x_acl_entry *entries,
+				   unsigned int size, bool set)
+{
+	for (unsigned int i = 0; i < size; i++)
+		switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
+		case YT921X_ACL_TYPE_IPV4_DA:
+		case YT921X_ACL_TYPE_IPV4_SA:
+			yt921x_acl_entry_set(&entries[i], 1,
+					     YT921X_ACL_BINb_IPV4_FRAG, set);
+			return size;
+		case YT921X_ACL_TYPE_IPV6_DA3:
+		case YT921X_ACL_TYPE_IPV6_SA3:
+			yt921x_acl_entry_set(&entries[i], 1,
+					     YT921X_ACL_BINb_IPV6_xA3_FRAG,
+					     set);
+			return size;
+		case YT921X_ACL_TYPE_MISC:
+			yt921x_acl_entry_set(&entries[i], 1,
+					     YT921X_ACL_BINb_MISC_FRAG, set);
+			return size;
+		case YT921X_ACL_TYPE_L4:
+			yt921x_acl_entry_set(&entries[i], 1,
+					     YT921X_ACL_BINb_L4_FRAG, set);
+			return size;
+		}
+
+	if (size >= YT921X_ACL_ENT_PER_BLK)
+		return 0;
+
+	entries[size] = (typeof(*entries)){};
+	entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
+	yt921x_acl_entry_set(&entries[size], 1, YT921X_ACL_BINb_MISC_FRAG, set);
+
+	return size + 1;
+}
+
+static unsigned int
+yt921x_acl_entries_set_first_frag(struct yt921x_acl_entry *entries,
+				  unsigned int size, bool set)
+{
+	for (unsigned int i = 0; i < size; i++)
+		switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
+		case YT921X_ACL_TYPE_IPV6_DA2:
+		case YT921X_ACL_TYPE_IPV6_SA2:
+			yt921x_acl_entry_set(&entries[i], 1,
+					     YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG,
+					     set);
+			return size;
+		case YT921X_ACL_TYPE_MISC:
+			yt921x_acl_entry_set(&entries[i], 0,
+					     YT921X_ACL_BINa_MISC_FIRST_FRAG,
+					     set);
+			return size;
+		}
+
+	if (size >= YT921X_ACL_ENT_PER_BLK)
+		return 0;
+
+	entries[size] = (typeof(*entries)){};
+	entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
+	yt921x_acl_entry_set(&entries[size], 0,
+			     YT921X_ACL_BINa_MISC_FIRST_FRAG, set);
+
+	return size + 1;
+}
+
+static unsigned int
+yt921x_acl_entries_set_l3_type(struct yt921x_acl_entry *entries,
+			       unsigned int size, enum yt921x_l3_type type)
+{
+	for (unsigned int i = 0; i < size; i++)
+		switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
+		case YT921X_ACL_TYPE_MAC_DA0:
+		case YT921X_ACL_TYPE_MAC_SA0:
+			entries[i].key[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE(type);
+			entries[i].mask[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M;
+			return size;
+		case YT921X_ACL_TYPE_MISC:
+			entries[i].key[0] |= YT921X_ACL_BINa_MISC_L3_TYPE(type);
+			entries[i].mask[0] |= YT921X_ACL_BINa_MISC_L3_TYPE_M;
+			return size;
+		}
+
+	if (size >= YT921X_ACL_ENT_PER_BLK)
+		return 0;
+
+	entries[size] = (typeof(*entries)){};
+	entries[size].key[0] = YT921X_ACL_BINa_MISC_L3_TYPE(type);
+	entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
+	entries[size].mask[0] = YT921X_ACL_BINa_MISC_L3_TYPE_M;
+
+	return size + 1;
+}
+
+static unsigned int
+yt921x_acl_entries_set_l4_type(struct yt921x_acl_entry *entries,
+			       unsigned int size, enum yt921x_l4_type type)
+{
+	for (unsigned int i = 0; i < size; i++)
+		switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
+		case YT921X_ACL_TYPE_IPV4_DA:
+		case YT921X_ACL_TYPE_IPV4_SA:
+			entries[i].key[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE(type);
+			entries[i].mask[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE_M;
+			return size;
+		case YT921X_ACL_TYPE_IPV6_DA0:
+		case YT921X_ACL_TYPE_IPV6_DA1:
+		case YT921X_ACL_TYPE_IPV6_DA2:
+		case YT921X_ACL_TYPE_IPV6_DA3:
+		case YT921X_ACL_TYPE_IPV6_SA0:
+		case YT921X_ACL_TYPE_IPV6_SA1:
+		case YT921X_ACL_TYPE_IPV6_SA2:
+		case YT921X_ACL_TYPE_IPV6_SA3:
+			entries[i].key[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE(type);
+			entries[i].mask[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE_M;
+			return size;
+		case YT921X_ACL_TYPE_L4:
+			entries[i].key[1] |= YT921X_ACL_BINb_L4_TYPE(type);
+			entries[i].mask[1] |= YT921X_ACL_BINb_L4_TYPE_M;
+			return size;
+		case YT921X_ACL_TYPE_MISC:
+			entries[i].key[1] |= YT921X_ACL_BINb_MISC_L4_TYPE(type);
+			entries[i].mask[1] |= YT921X_ACL_BINb_MISC_L4_TYPE_M;
+			return size;
+		}
+
+	if (size >= YT921X_ACL_ENT_PER_BLK)
+		return 0;
+
+	entries[size] = (typeof(*entries)){};
+	entries[size].key[1] = YT921X_ACL_BINb_MISC_L4_TYPE(type) |
+			       YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
+	entries[size].mask[1] = YT921X_ACL_BINb_MISC_L4_TYPE_M;
+
+	return size + 1;
+}
+
+static struct yt921x_acl_entry *
+yt921x_acl_entries_new(struct yt921x_acl_entry *entries, unsigned int *sizep,
+		       u32 type)
+{
+	unsigned int size = *sizep;
+
+	if (size >= YT921X_ACL_ENT_PER_BLK)
+		return NULL;
+
+	entries[size] = (typeof(*entries)){};
+	entries[size].key[1] = YT921X_ACL_KEYb_TYPE(type);
+
+	(*sizep)++;
+	return &entries[size];
+}
+
+static struct yt921x_acl_entry *
+yt921x_acl_entries_find(struct yt921x_acl_entry *entries, unsigned int *sizep,
+			u32 type)
+{
+	for (unsigned int i = 0; i < *sizep; i++)
+		if (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1]) ==
+		    type)
+			return &entries[i];
+	return yt921x_acl_entries_new(entries, sizep, type);
+}
+
+static void
+yt921x_acl_rule_set_ports(struct yt921x_acl_rule *aclrule, u16 ord,
+			  u16 ports_mask)
+{
+	struct yt921x_acl_entry *entries = aclrule->entries;
+
+	for (unsigned int i = 0; i < hweight8(aclrule->mask); i++) {
+		entries[i].key[1] |= YT921X_ACL_KEYb_SPORTS(ports_mask) |
+				     YT921X_ACL_KEYb_ORD(ord);
+	}
+}
+
+struct yt921x_acl_rule_ext {
+	struct yt921x_acl_rule r;
+
+	struct yt921x_marker marker;
+};
+
+static int
+yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
+				       const struct flow_cls_offload *cls)
+{
+	const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
+	struct yt921x_acl_entry *entries = ruleext->r.entries;
+	struct netlink_ext_ack *extack = cls->common.extack;
+	const struct flow_dissector *dissector;
+	struct yt921x_acl_entry *entry;
+	unsigned int size = 0;
+	bool use_dport;
+	bool use_sport;
+
+	/* Incomplete and probably won't, since it supports custom u32 filters.
+	 * New adapters are welcome.
+	 */
+	dissector = rule->match.dissector;
+	if (dissector->used_keys &
+	    ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
+	      BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
+	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
+	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) |
+	      BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+	      BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
+	      BIT_ULL(FLOW_DISSECTOR_KEY_TCP))) {
+		NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used");
+		return -EOPNOTSUPP;
+	}
+
+	/* Entries */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
+		struct flow_match_ipv4_addrs match;
+
+		flow_rule_match_ipv4_addrs(rule, &match);
+
+		if (match.mask->dst) {
+			entry = yt921x_acl_entries_new(entries, &size,
+						       YT921X_ACL_TYPE_IPV4_DA);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= ntohl(match.key->dst);
+			entry->mask[0] |= ntohl(match.mask->dst);
+		}
+
+		if (match.mask->src) {
+			entry = yt921x_acl_entries_new(entries, &size,
+						       YT921X_ACL_TYPE_IPV4_SA);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= ntohl(match.key->src);
+			entry->mask[0] |= ntohl(match.mask->src);
+		}
+	}
+
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
+		struct flow_match_ipv6_addrs match;
+
+		flow_rule_match_ipv6_addrs(rule, &match);
+
+		for (unsigned int i = 0; i < 4; i++) {
+			if (!match.mask->dst.s6_addr32[i])
+				continue;
+
+			entry = yt921x_acl_entries_new(entries, &size,
+						       YT921X_ACL_TYPE_IPV6_DA0 + i);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= ntohl(match.key->dst.s6_addr32[i]);
+			entry->mask[0] |= ntohl(match.mask->dst.s6_addr32[i]);
+		}
+
+		for (unsigned int i = 0; i < 4; i++) {
+			if (!match.mask->src.s6_addr32[i])
+				continue;
+
+			entry = yt921x_acl_entries_new(entries, &size,
+						       YT921X_ACL_TYPE_IPV6_SA0 + i);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= ntohl(match.key->src.s6_addr32[i]);
+			entry->mask[0] |= ntohl(match.mask->src.s6_addr32[i]);
+		}
+	}
+
+	use_dport = false;
+	use_sport = false;
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+		struct flow_match_ports match;
+
+		entry = yt921x_acl_entries_new(entries, &size,
+					       YT921X_ACL_TYPE_L4);
+		if (!entry)
+			goto err;
+
+		flow_rule_match_ports(rule, &match);
+
+		use_dport = !!match.mask->dst;
+		use_sport = !!match.mask->src;
+
+		entry->key[0] |= (ntohs(match.key->dst) << 16) |
+				 ntohs(match.key->src);
+		entry->mask[0] |= (ntohs(match.mask->dst) << 16) |
+				  ntohs(match.mask->src);
+	}
+
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) {
+		struct flow_match_ports_range match;
+
+		entry = yt921x_acl_entries_find(entries, &size,
+						YT921X_ACL_TYPE_L4);
+		if (!entry)
+			goto err;
+
+		flow_rule_match_ports_range(rule, &match);
+
+		if ((use_dport && match.mask->tp.dst) ||
+		    (use_sport && match.mask->tp.src)) {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "Port mask and range are mutually exclusive");
+			return -EINVAL;
+		}
+
+		if (match.mask->tp.dst) {
+			entry->key[0] |= ntohs(match.key->tp_min.dst) << 16;
+			entry->key[1] |= YT921X_ACL_KEYb_L4_DPORT_RANGE_EN;
+			entry->mask[0] |= ntohs(match.key->tp_max.dst) << 16;
+		}
+
+		if (match.mask->tp.src) {
+			entry->key[0] |= ntohs(match.key->tp_min.src);
+			entry->key[1] |= YT921X_ACL_KEYb_L4_SPORT_RANGE_EN;
+			entry->mask[0] |= ntohs(match.key->tp_max.src);
+		}
+	}
+
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+		struct flow_match_eth_addrs match;
+		u32 mask;
+
+		flow_rule_match_eth_addrs(rule, &match);
+
+		mask = ethaddr_hi4_to_u32(match.mask->dst);
+		if (mask) {
+			entry = yt921x_acl_entries_new(entries, &size,
+						       YT921X_ACL_TYPE_MAC_DA0);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= ethaddr_hi4_to_u32(match.key->dst);
+			entry->mask[0] |= mask;
+		}
+
+		mask = ethaddr_hi4_to_u32(match.mask->src);
+		if (mask) {
+			entry = yt921x_acl_entries_new(entries, &size,
+						       YT921X_ACL_TYPE_MAC_SA0);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= ethaddr_hi4_to_u32(match.key->src);
+			entry->mask[0] |= mask;
+		}
+
+		mask = (ethaddr_lo2_to_u32(match.mask->dst) << 16) |
+		       ethaddr_lo2_to_u32(match.mask->src);
+		if (mask) {
+			entry = yt921x_acl_entries_new(entries, &size,
+						       YT921X_ACL_TYPE_MAC_DA1_SA1);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= (ethaddr_lo2_to_u32(match.key->dst) << 16) |
+					 ethaddr_lo2_to_u32(match.key->src);
+			entry->mask[0] |= mask;
+		}
+	}
+
+	/* Entries + Misc */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+		struct flow_match_basic match;
+
+		flow_rule_match_basic(rule, &match);
+
+		if (match.mask->n_proto) {
+			enum yt921x_l3_type l3type = YT921X_L3_TYPE_OTHER;
+
+			if (match.mask->n_proto == htons(~0))
+				switch (match.key->n_proto) {
+				case htons(ETH_P_IP):
+					l3type = YT921X_L3_TYPE_IPV4;
+					break;
+				case htons(ETH_P_IPV6):
+					l3type = YT921X_L3_TYPE_IPV6;
+					break;
+				case htons(ETH_P_ARP):
+					l3type = YT921X_L3_TYPE_ARP;
+					break;
+				case htons(ETH_P_LLDP):
+					l3type = YT921X_L3_TYPE_LLDP;
+					break;
+				case htons(ETH_P_PAE):
+					l3type = YT921X_L3_TYPE_PAE;
+					break;
+				case htons(ETH_P_CFM):
+					l3type = YT921X_L3_TYPE_ERP;
+					break;
+				}
+
+			if (l3type != YT921X_L3_TYPE_OTHER) {
+				size = yt921x_acl_entries_set_l3_type(entries,
+								      size,
+								      l3type);
+				if (!size)
+					goto err;
+			} else {
+				entry = yt921x_acl_entries_new(entries, &size,
+							       YT921X_ACL_TYPE_ETHERTYPE);
+				if (!entry)
+					goto err;
+
+				entry->key[0] |= ntohs(match.key->n_proto);
+				entry->mask[0] |= ntohs(match.mask->n_proto);
+			}
+		}
+
+		if (match.mask->ip_proto) {
+			enum yt921x_l4_type l4type = YT921X_L4_TYPE_OTHER;
+
+			if (match.mask->ip_proto == (u8)~0)
+				switch (match.key->ip_proto) {
+				case IPPROTO_TCP:
+					l4type = YT921X_L4_TYPE_TCP;
+					break;
+				case IPPROTO_UDP:
+					l4type = YT921X_L4_TYPE_UDP;
+					break;
+				case IPPROTO_UDPLITE:
+					l4type = YT921X_L4_TYPE_UDPLITE;
+					break;
+				case IPPROTO_ICMP:
+					l4type = YT921X_L4_TYPE_ICMP;
+					break;
+				case IPPROTO_IGMP:
+					l4type = YT921X_L4_TYPE_IGMP;
+					break;
+				}
+
+			if (l4type != YT921X_L4_TYPE_OTHER) {
+				size = yt921x_acl_entries_set_l4_type(entries,
+								      size,
+								      l4type);
+				if (!size)
+					goto err;
+			} else {
+				entry = yt921x_acl_entries_find(entries, &size,
+								YT921X_ACL_TYPE_MISC);
+				if (!entry)
+					goto err;
+
+				entry->key[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.key->ip_proto);
+				entry->mask[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.mask->ip_proto);
+			}
+		}
+	}
+
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+		u32 supp_flags = FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG;
+		struct flow_match_control match;
+
+		flow_rule_match_control(rule, &match);
+		if (!flow_rule_is_supp_control_flags(supp_flags,
+						     match.mask->flags, extack))
+			return -EOPNOTSUPP;
+
+		if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
+			bool set = match.key->flags & FLOW_DIS_IS_FRAGMENT;
+
+			size = yt921x_acl_entries_set_is_fragment(entries, size,
+								  set);
+			if (!size)
+				goto err;
+		}
+		if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {
+			bool set = match.key->flags & FLOW_DIS_FIRST_FRAG;
+
+			size = yt921x_acl_entries_set_first_frag(entries, size,
+								 set);
+			if (!size)
+				goto err;
+		}
+	}
+
+	/* Misc only */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
+		struct flow_match_ip match;
+
+		flow_rule_match_ip(rule, &match);
+		if (match.mask->ttl) {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "Matching on TTL not supported");
+			return -EOPNOTSUPP;
+		}
+
+		if (match.mask->tos) {
+			entry = yt921x_acl_entries_find(entries, &size,
+							YT921X_ACL_TYPE_MISC);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= YT921X_ACL_BINa_MISC_TOS(match.key->tos);
+			entry->mask[0] |= YT921X_ACL_BINa_MISC_TOS(match.mask->tos);
+		}
+	}
+
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) {
+		struct flow_match_tcp match;
+
+		flow_rule_match_tcp(rule, &match);
+		if (match.mask->flags & htons(~0xff)) {
+			NL_SET_ERR_MSG_MOD(extack, "Unsupported TCP flags");
+			return -EOPNOTSUPP;
+		}
+
+		if (match.mask->flags) {
+			entry = yt921x_acl_entries_find(entries, &size,
+							YT921X_ACL_TYPE_MISC);
+			if (!entry)
+				goto err;
+
+			entry->key[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.key->flags));
+			entry->mask[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.mask->flags));
+		}
+	}
+
+	if (!size) {
+		NL_SET_ERR_MSG_MOD(extack, "Empty rule generated, this should not happen");
+		return -EOPNOTSUPP;
+	}
+
+	ruleext->r.mask = (1 << size) - 1;
+	return 0;
+
+err:
+	NL_SET_ERR_MSG_MOD(extack, "Rule too complex");
+	return -EOPNOTSUPP;
+}
+
+static int
+yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext,
+				      const struct flow_cls_offload *cls,
+				      struct yt92xx_priv *priv, int port)
+{
+	const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
+	const struct flow_action *flow_action = &rule->action;
+	struct netlink_ext_ack *extack = cls->common.extack;
+	enum flow_action_id redir_act = NUM_FLOW_ACTIONS;
+	const struct flow_action_entry *act;
+	u32 *action = ruleext->r.action;
+	bool seen_priority = false;
+	const char *reason = NULL;
+	bool seen_police = false;
+	unsigned int i;
+	int res;
+
+	memset(action, 0, 3 * sizeof(*action));
+	flow_action_for_each(i, act, flow_action)
+		switch (act->id) {
+		case FLOW_ACTION_ACCEPT:
+		case FLOW_ACTION_DROP:
+		case FLOW_ACTION_REDIRECT:
+			if (redir_act != NUM_FLOW_ACTIONS &&
+			    redir_act != act->id) {
+				reason = "Different redirect actions";
+				goto fallback;
+			}
+			redir_act = act->id;
+
+			switch (act->id) {
+			case FLOW_ACTION_ACCEPT:
+				action[2] |= YT921X_ACL_ACTc_FWD_EN |
+					     YT921X_ACL_ACTc_FWD_FWD;
+				break;
+			case FLOW_ACTION_DROP:
+				action[2] |= YT921X_ACL_ACTc_FWD_EN |
+					     YT921X_ACL_ACTc_FWD_REDIR;
+				break;
+			case FLOW_ACTION_REDIRECT: {
+				struct dsa_port *to_dp;
+
+				to_dp = dsa_port_from_netdev(act->dev);
+				if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) {
+					reason = "Redirect to non-local port";
+					goto fallback;
+				}
+
+				action[2] |= YT921X_ACL_ACTc_FWD_EN |
+					     YT921X_ACL_ACTc_FWD_REDIR |
+					     YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index);
+				break;
+			}
+			default:
+				break;
+			}
+			break;
+		case FLOW_ACTION_PRIORITY:
+			if (seen_priority) {
+				action[0] &= ~YT921X_ACL_ACTa_PRIO_EN;
+				action[1] &= ~YT921X_ACL_ACTb_PRIO_M;
+
+				reason = "Multiple priority actions";
+				goto fallback;
+			}
+			seen_priority = true;
+
+			if (act->priority >= YT92XX_PRIO_NUM) {
+				NL_SET_ERR_MSG_MOD(extack,
+						   "Priority value is too high");
+				return -EOPNOTSUPP;
+			}
+			action[0] |= YT921X_ACL_ACTa_PRIO_EN;
+			action[1] |= YT921X_ACL_ACTb_PRIO(act->priority);
+			break;
+		case FLOW_ACTION_POLICE: {
+			const struct flow_action_police *police = &act->police;
+
+			if (seen_police) {
+				action[0] &= ~YT921X_ACL_ACTa_METER_EN;
+
+				reason = "Multiple police actions";
+				goto fallback;
+			}
+			seen_police = true;
+
+			res = yt921x_police_validate(police, flow_action, act,
+						     extack);
+			if (res)
+				return res;
+
+			res = yt921x_marker_tfm_police(&ruleext->marker, police,
+						       0, priv, port, extack);
+			if (res)
+				return res;
+
+			action[0] |= YT921X_ACL_ACTa_METER_EN;
+			break;
+		}
+		default:
+fallback:
+			if (cls->common.skip_sw) {
+				NL_SET_ERR_MSG_FMT_MOD(extack,
+						       "Action not supported when skip_sw: %s",
+						       reason);
+				return -EOPNOTSUPP;
+			}
+			fallthrough;
+		case FLOW_ACTION_TRAP:
+			redir_act = FLOW_ACTION_TRAP;
+
+			action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
+				     ~YT921X_ACL_ACTc_FWD_M;
+			action[2] |= YT921X_ACL_ACTc_FWD_EN |
+				     YT921X_ACL_ACTc_FWD_TRAP;
+			break;
+		}
+
+	ruleext->r.sw_assisted = !cls->common.skip_sw;
+	return 0;
+}
+
+static int
+yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
+			       const struct flow_cls_offload *cls, bool ingress,
+			       struct yt92xx_priv *priv)
+{
+	struct netlink_ext_ack *extack = cls->common.extack;
+	int res;
+
+	if (!ingress) {
+		NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
+		return -EOPNOTSUPP;
+	}
+
+	if (cls->common.chain_index) {
+		NL_SET_ERR_MSG(extack, "Only chain 0 is supported");
+		return -EOPNOTSUPP;
+	}
+
+	res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port);
+	if (res)
+		return res;
+	res = yt921x_acl_rule_ext_parse_flow_entries(ruleext, cls);
+	if (res)
+		return res;
+
+	yt921x_acl_rule_set_ports(&ruleext->r, 0, BIT(port));
+	ruleext->r.tag = cls->cookie;
+	ruleext->r.type = TC_SETUP_CLSFLOWER;
+	return 0;
+}
+
+static unsigned int
+yt921x_acl_find(const struct yt92xx_priv *priv, enum tc_setup_type type,
+		unsigned long tag)
+{
+	for (unsigned int blkid = 0; blkid < YT921X_ACL_BLK_NUM; blkid++) {
+		const struct yt921x_acl_blk *aclblk = priv->acl_blks[blkid];
+
+		if (!aclblk)
+			continue;
+
+		for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++)
+			if (aclblk->rules[i] && aclblk->rules[i]->tag == tag &&
+			    aclblk->rules[i]->type == type)
+				return YT921X_ACL_ENT_PER_BLK * blkid + i;
+	}
+
+	return UINT_MAX;
+}
+
+static unsigned int
+yt921x_acl_reserve(struct yt92xx_priv *priv, unsigned int entscnt,
+		   struct netlink_ext_ack *extack)
+{
+	int candidates[YT921X_ACL_ENT_PER_BLK + 1];
+	unsigned int acl_used_cnt = 0;
+
+	if (WARN_ON(entscnt > YT921X_ACL_ENT_PER_BLK))
+		return UINT_MAX;
+
+	for (unsigned int i = 0; i < ARRAY_SIZE(candidates); i++)
+		candidates[i] = -1;
+	for (unsigned int i = YT921X_ACL_BLK_NUM; i-- > 0;) {
+		unsigned int blk_used_cnt = hweight8(priv->acl_masks[i]);
+
+		candidates[blk_used_cnt] = i;
+		acl_used_cnt += blk_used_cnt;
+	}
+
+	if (acl_used_cnt >= YT921X_ACL_NUM) {
+		NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
+		return UINT_MAX;
+	}
+	if (acl_used_cnt + entscnt <= YT921X_ACL_NUM)
+		for (unsigned int i = YT921X_ACL_ENT_PER_BLK - entscnt + 1;
+		     i-- > 0;)
+			if (candidates[i] >= 0)
+				return YT921X_ACL_ENT_PER_BLK * candidates[i] +
+				       ffz(priv->acl_masks[candidates[i]]);
+
+	NL_SET_ERR_MSG_MOD(extack,
+			   "ACL entry allocation failed, simplify your rules or remove existing rules");
+	return UINT_MAX;
+}
+
+static int
+yt921x_acl_commit(struct yt92xx_priv *priv, unsigned int entid, u8 entsmask)
+{
+	const struct yt921x_acl_rule *aclrule;
+	const struct yt921x_acl_blk *aclblk;
+	unsigned int blkid;
+	unsigned int binid;
+	unsigned long mask;
+	u32 zeros[3] = {};
+	unsigned int i;
+	unsigned int o;
+	u32 ctrl;
+	int res;
+
+	blkid = entid / YT921X_ACL_ENT_PER_BLK;
+	binid = entid % YT921X_ACL_ENT_PER_BLK;
+	aclblk = priv->acl_blks[blkid];
+	aclrule = aclblk->rules[binid];
+
+	/* Write actions */
+	res = yt92xx_reg96_write(priv, YT921X_ACLn_ACT(entid),
+				 aclrule ? aclrule->action : zeros);
+	if (res)
+		return res;
+
+	/* Select the block */
+	ctrl = YT921X_ACL_BLK_CMD_MODIFY | YT921X_ACL_BLK_CMD_BLKID(blkid);
+	res = yt92xx_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl);
+	if (res)
+		return res;
+
+	/* Write keys and masks */
+	ctrl = 0;
+	for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++)
+		ctrl |= YT921X_ACL_BLK_KEEP_KEEPn(i);
+
+	mask = entsmask;
+	i = 0;
+	for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
+		res = yt92xx_reg64_write(priv, YT921X_ACLn_KEYm(blkid, o),
+					 aclrule ? aclrule->entries[i].key :
+					 zeros);
+		if (res)
+			return res;
+
+		res = yt92xx_reg64_write(priv, YT921X_ACLn_MASKm(blkid, o),
+					 aclrule ? aclrule->entries[i].mask :
+					 zeros);
+		if (res)
+			return res;
+
+		ctrl &= ~YT921X_ACL_BLK_KEEP_KEEPn(o);
+		i++;
+	}
+
+	res = yt92xx_reg_write(priv, YT921X_ACL_BLK_KEEP, ctrl);
+	if (res)
+		return res;
+
+	ctrl = 0;
+	for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) {
+		const struct yt921x_acl_rule *other = aclblk->rules[i];
+
+		if (!other)
+			continue;
+
+		mask = other->mask;
+		for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK)
+			ctrl |= YT921X_ACL_ENTRY_ENm(o) |
+				YT921X_ACL_ENTRY_GRPIDm(o, i);
+	}
+	res = yt92xx_reg_write(priv, YT921X_ACLn_ENTRY(blkid), ctrl);
+	if (res)
+		return res;
+
+	/* Commit the block */
+	ctrl = YT921X_ACL_BLK_CMD_BLKID(blkid);
+	res = yt92xx_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int
+yt921x_acl_del(struct yt92xx_priv *priv, enum tc_setup_type type,
+	       unsigned long tag)
+{
+	struct yt921x_acl_rule *aclrule;
+	struct yt921x_acl_blk *aclblk;
+	unsigned int binid;
+	unsigned int blkid;
+	unsigned int entid;
+	int res;
+
+	entid = yt921x_acl_find(priv, type, tag);
+	if (entid == UINT_MAX)
+		return -ENOENT;
+
+	blkid = entid / YT921X_ACL_ENT_PER_BLK;
+	binid = entid % YT921X_ACL_ENT_PER_BLK;
+	aclblk = priv->acl_blks[blkid];
+	aclrule = aclblk->rules[binid];
+
+	aclblk->rules[binid] = NULL;
+	res = yt921x_acl_commit(priv, entid, aclrule->mask);
+	/* the kernel never rolls back on failure */
+
+	if (aclrule->action[0] & YT921X_ACL_ACTa_METER_EN)
+		clear_bit(FIELD_GET(YT921X_ACL_ACTa_METER_ID_M,
+				    aclrule->action[0]),
+			  priv->meters_map);
+	priv->acl_masks[blkid] &= ~aclrule->mask;
+	kvfree(aclrule);
+	if (!priv->acl_masks[blkid]) {
+		kvfree(aclblk);
+		priv->acl_blks[blkid] = NULL;
+	}
+	return res;
+}
+
+static int
+yt921x_acl_add(struct dsa_switch *ds, int port,
+			  struct flow_cls_offload *cls, bool ingress)
+{
+	struct netlink_ext_ack *extack = cls->common.extack;
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt921x_acl_rule_ext ruleext;
+	struct yt921x_acl_rule *aclrule;
+	struct yt921x_acl_blk *aclblk;
+	bool use_trap = false;
+	unsigned int meterid;
+	unsigned int entscnt;
+	unsigned long mask;
+	unsigned int binid;
+	unsigned int blkid;
+	unsigned int entid;
+	unsigned int o;
+	int res;
+
+	res = yt921x_acl_rule_ext_parse_flow(&ruleext, port, cls, ingress,
+					     priv);
+	if (res)
+		return res;
+	entscnt = hweight8(ruleext.r.mask);
+	/* Allocate resources */
+	entid = yt921x_acl_reserve(priv, entscnt, extack);
+	if (entid == UINT_MAX)
+		return -EOPNOTSUPP;
+
+	if (!(ruleext.r.action[0] & YT921X_ACL_ACTa_METER_EN)) {
+		meterid = YT921X_METER_NUM;
+	} else {
+		meterid = find_first_zero_bit(priv->meters_map,
+					      YT921X_METER_NUM);
+		if (meterid < YT921X_METER_NUM) {
+			res = yt921x_meter_config(priv, meterid,
+						  &ruleext.marker);
+			if (res)
+				return res;
+		} else if (ruleext.r.sw_assisted) {
+			use_trap = true;
+		} else {
+			NL_SET_ERR_MSG_MOD(extack,
+					   "No more meters available");
+			return -EOPNOTSUPP;
+		}
+	}
+
+	/* Prepare acl block ctrlblk */
+	blkid = entid / YT921X_ACL_ENT_PER_BLK;
+	binid = entid % YT921X_ACL_ENT_PER_BLK;
+	aclblk = priv->acl_blks[blkid];
+	if (!aclblk) {
+		aclblk = kvzalloc_obj(*aclblk);
+		if (!aclblk)
+			return -ENOMEM;
+		priv->acl_blks[blkid] = aclblk;
+	}
+
+	/* Prepare acl rule ctrlblk */
+	aclrule = kvmemdup(&ruleext.r,
+			   offsetof(struct yt921x_acl_rule, entries[entscnt]),
+			   GFP_KERNEL);
+	if (!aclrule) {
+		res = -ENOMEM;
+		goto err;
+	}
+
+	/* Replace the placeholder resource IDs */
+	aclrule->mask = 0;
+	mask = priv->acl_masks[blkid];
+	for_each_clear_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
+		aclrule->mask |= BIT(o);
+		entscnt--;
+		if (!entscnt)
+			break;
+	}
+
+	if (use_trap) {
+		aclrule->action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
+				      ~YT921X_ACL_ACTc_FWD_M;
+		aclrule->action[2] |= YT921X_ACL_ACTc_FWD_EN |
+				      YT921X_ACL_ACTc_FWD_TRAP;
+	}
+	if (meterid < YT921X_METER_NUM)
+		aclrule->action[0] |= YT921X_ACL_ACTa_METER_ID(meterid);
+	else
+		aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN;
+
+	/* Write rules */
+	aclblk->rules[binid] = aclrule;
+	res = yt921x_acl_commit(priv, entid, aclrule->mask);
+	if (res) {
+		aclblk->rules[binid] = NULL;
+		kvfree(aclrule);
+		goto err;
+	}
+
+	if (meterid < YT921X_METER_NUM)
+		set_bit(meterid, priv->meters_map);
+	priv->acl_masks[blkid] |= aclrule->mask;
+	return 0;
+
+err:
+	if (!priv->acl_masks[blkid]) {
+		kvfree(aclblk);
+		priv->acl_blks[blkid] = NULL;
+	}
+	return res;
+}
+
+static const struct yt92xx_switch_ops yt921x_switch_ops = {
+	.stats_read = yt921x_stats_read,
+	.stats_eth_mac_stats = yt921x_stats_eth_mac_stats,
+	.stats_rmon_stats = yt921x_stats_rmon_stats,
+	.stats_stats64 = yt921x_stats_stats64,
+	.rate_policer_del = yt921x_rate_policer_del,
+	.rate_policer_add = yt921x_rate_policer_add,
+	.port_setup_tc_tbf_port = yt921x_dsa_port_setup_tc_tbf_port,
+	.acl_delete = yt921x_acl_del,
+	.acl_add = yt921x_acl_add,
+	.vlan_msti_set = yt921x_vlan_msti_set,
+	.mac_caps = yt921x_mac_caps,
+	.dsa_setup = yt921x_dsa_setup,
+};
+
+static int yt921x_port_down(struct yt92xx_priv *priv, int port)
+{
+	u32 mask;
+	int res;
+
+	mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
+	res = yt_port_mac_enable_set(priv->chip_info->unit, port,
+				     YT_DISABLE);
+	if (res)
+		return res;
+
+	if (yt92xx_port_is_internal(priv, port)) {
+		mask = YT921X_SERDES_LINK;
+		res = yt92xx_reg_clear_bits(priv, YT921X_SERDESn(port), mask);
+		if (res)
+			return res;
+
+		mask = YT921X_XMII_LINK;
+		res = yt92xx_reg_clear_bits(priv, YT921X_XMIIn(port), mask);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static void yt921x_phylink_mac_link_down(struct phylink_config *config,
+					 unsigned int mode,
+					 phy_interface_t interface)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct yt92xx_priv *priv = to_yt92xx_priv(dp->ds);
+	int port = dp->index;
+	int res;
+
+	/* No need to sync; port control block is hold until device remove */
+	cancel_delayed_work(&priv->ports[port].mib_read);
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_port_down(priv, port);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down",
+			port, res);
+}
+
+static int yt921x_port_up(struct yt92xx_priv *priv, int port, unsigned int mode,
+			  phy_interface_t interface, int speed, int duplex,
+			  bool tx_pause, bool rx_pause)
+{
+	struct yt_port_force_ctrl ctrl;
+	u32 data;
+	u32 mask;
+	int res;
+
+	if (duplex) {
+		switch (speed) {
+		case SPEED_10:
+			ctrl.speed_dup = PORT_SPEED_DUP_10FULL;
+			break;
+		case SPEED_100:
+			ctrl.speed_dup = PORT_SPEED_DUP_100FULL;
+			break;
+		case SPEED_1000:
+			ctrl.speed_dup = PORT_SPEED_DUP_1000FULL;
+			break;
+		case SPEED_2500:
+			ctrl.speed_dup = PORT_SPEED_DUP_2500FULL;
+			break;
+		case SPEED_5000:
+			ctrl.speed_dup = PORT_SPEED_DUP_5GFULL;
+			break;
+		case SPEED_10000:
+			ctrl.speed_dup = PORT_SPEED_DUP_10GFULL;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (speed) {
+		case SPEED_10:
+			ctrl.speed_dup = PORT_SPEED_DUP_10HALF;
+			break;
+		case SPEED_100:
+			ctrl.speed_dup = PORT_SPEED_DUP_100HALF;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	if (rx_pause)
+		ctrl.rx_fc_en = 1;
+	else
+		ctrl.rx_fc_en = 0;
+
+	if (rx_pause)
+		ctrl.tx_fc_en = 1;
+	else
+		ctrl.tx_fc_en = 0;
+
+	res = result_convert
+		(yt_port_mac_force_set(priv->chip_info->unit, port, ctrl));
+	if (res)
+		return res;
+
+	if (yt92xx_port_is_external(priv, port)) {
+		mask = YT921X_SERDES_SPEED_M;
+		switch (speed) {
+		case SPEED_10:
+			data = YT921X_SERDES_SPEED_10;
+			break;
+		case SPEED_100:
+			data = YT921X_SERDES_SPEED_100;
+			break;
+		case SPEED_1000:
+			data = YT921X_SERDES_SPEED_1000;
+			break;
+		case SPEED_2500:
+			data = YT921X_SERDES_SPEED_2500;
+			break;
+		case SPEED_10000:
+			data = YT921X_SERDES_SPEED_10000;
+			break;
+		default:
+			return -EINVAL;
+		}
+		mask |= YT921X_SERDES_DUPLEX_FULL;
+		if (duplex == DUPLEX_FULL)
+			data |= YT921X_SERDES_DUPLEX_FULL;
+		mask |= YT921X_SERDES_TX_PAUSE;
+		if (tx_pause)
+			data |= YT921X_SERDES_TX_PAUSE;
+		mask |= YT921X_SERDES_RX_PAUSE;
+		if (rx_pause)
+			data |= YT921X_SERDES_RX_PAUSE;
+		mask |= YT921X_SERDES_LINK;
+		data |= YT921X_SERDES_LINK;
+		res = yt92xx_reg_update_bits(priv, YT921X_SERDESn(port), mask,
+					     data);
+		if (res)
+			return res;
+
+		mask = YT921X_XMII_LINK;
+		res = yt92xx_reg_set_bits(priv, YT921X_XMIIn(port), mask);
+		if (res)
+			return res;
+
+		switch (speed) {
+		case SPEED_10:
+			data = YT921X_MDIO_POLLING_SPEED_10;
+			break;
+		case SPEED_100:
+			data = YT921X_MDIO_POLLING_SPEED_100;
+			break;
+		case SPEED_1000:
+			data = YT921X_MDIO_POLLING_SPEED_1000;
+			break;
+		case SPEED_2500:
+			data = YT921X_MDIO_POLLING_SPEED_2500;
+			break;
+		case SPEED_10000:
+			data = YT921X_MDIO_POLLING_SPEED_10000;
+			break;
+		default:
+			return -EINVAL;
+		}
+		if (duplex == DUPLEX_FULL)
+			data |= YT921X_MDIO_POLLING_DUPLEX_FULL;
+		data |= YT921X_MDIO_POLLING_LINK;
+		res = yt92xx_reg_write(priv, YT921X_MDIO_POLLINGn(port), data);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static void yt921x_phylink_mac_link_up(struct phylink_config *config,
+				       struct phy_device *phydev,
+				       unsigned int mode,
+				       phy_interface_t interface, int speed,
+				       int duplex, bool tx_pause, bool rx_pause)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct yt92xx_priv *priv = to_yt92xx_priv(dp->ds);
+	int port = dp->index;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_port_up(priv, port, mode, interface, speed, duplex,
+			     tx_pause, rx_pause);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
+			port, res);
+
+	schedule_delayed_work(&priv->ports[port].mib_read, 0);
+}
+
+static int yt921x_port_config(struct yt92xx_priv *priv, int port,
+			      unsigned int mode, phy_interface_t interface)
+{
+	struct device *dev = to_device(priv);
+	enum yt_extif_mode extif_mode;
+	int res;
+
+	if (!yt92xx_port_is_external(priv, port)) {
+		if (interface != PHY_INTERFACE_MODE_INTERNAL) {
+			dev_err(dev, "Wrong mode %d on port %d\n", interface,
+				port);
+			return -EINVAL;
+		}
+		return 0;
+	}
+	switch (interface) {
+	case PHY_INTERFACE_MODE_MII:
+		extif_mode = YT_EXTIF_MODE_MII;
+		break;
+	case PHY_INTERFACE_MODE_RGMII:
+		extif_mode = YT_EXTIF_MODE_RGMII;
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+		extif_mode = YT_EXTIF_MODE_SG_MAC;
+		break;
+	case PHY_INTERFACE_MODE_100BASEX:
+		extif_mode = YT_EXTIF_MODE_FIB_100;
+		break;
+	case PHY_INTERFACE_MODE_1000BASEX:
+		extif_mode = YT_EXTIF_MODE_FIB_100;
+		break;
+	case PHY_INTERFACE_MODE_2500BASEX:
+		extif_mode = YT_EXTIF_MODE_BX2500;
+		break;
+	case PHY_INTERFACE_MODE_USXGMII:
+		extif_mode = YT_EXTIF_MODE_USXGMII_10G;
+		break;
+	case PHY_INTERFACE_MODE_QUSGMII:
+		extif_mode = YT_EXTIF_MODE_10GQXGMII;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	res = result_convert(yt_port_extif_mode_set(priv->chip_info->unit,
+						    port, extif_mode));
+
+	return 0;
+}
+
+static void yt921x_phylink_mac_config(struct phylink_config *config,
+				      unsigned int mode,
+				      const struct phylink_link_state *state)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct yt92xx_priv *priv = to_yt92xx_priv(dp->ds);
+	int port = dp->index;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt921x_port_config(priv, port, mode, state->interface);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config",
+			port, res);
+}
+
+static const struct phylink_mac_ops yt921x_phylink_mac_ops = {
+	.mac_link_down = yt921x_phylink_mac_link_down,
+	.mac_link_up = yt921x_phylink_mac_link_up,
+	.mac_config = yt921x_phylink_mac_config,
+};
+
+#elif IS_ENABLED(CONFIG_MOTORCOMM_YT922X)
+
+static const struct yt92xx_mib_desc yt92xx_mib_descs[] = {
+	MIB_DESC(RX_BROADCAST, NULL),
+	MIB_DESC(RX_PAUSE, NULL),
+	MIB_DESC(RX_UNICAST, NULL),
+	MIB_DESC(RX_OAM_COUNTER, "RxOAM"),
+
+	MIB_DESC(RX_MULTICAST, NULL),
+	MIB_DESC(RX_ALIGN_ERR_LESS_64B, NULL),
+	MIB_DESC(RX_ALIGN_ERR_64B_1518B, NULL),
+	MIB_DESC(RX_ALIGN_ERR_JUMBO, NULL),
+
+	MIB_DESC(RX_FCS_ERR_64B_1518B, NULL),
+	MIB_DESC(RX_FCS_ERR_JUMBO, NULL),
+	MIB_DESC(RX_UNDERSIZE, NULL),
+	MIB_DESC(RX_FRAGMENT, NULL),
+
+	MIB_DESC(RX_64B, NULL),
+	MIB_DESC(RX_65_127B, NULL),
+	MIB_DESC(RX_128_255B, NULL),
+
+	MIB_DESC(RX_256_511B, NULL),
+	MIB_DESC(RX_512_1023B, NULL),
+
+	MIB_DESC(RX_1024_1518B, NULL),
+	MIB_DESC(RX_JUMBO, NULL),
+	MIB_DESC(RX_OVERSIZE, NULL),
+	MIB_DESC(TX_BROADCAST, NULL),
+
+	MIB_DESC(TX_PAUSE, NULL),
+	MIB_DESC(TX_OAM_COUNTER, "TxOAM"),
+	MIB_DESC(TX_MULTICAST, NULL),
+	MIB_DESC(RX_FC_DROP, NULL),
+
+	MIB_DESC(TX_64B, NULL),
+	MIB_DESC(TX_65_127B, NULL),
+	MIB_DESC(TX_128_255B, NULL),
+	MIB_DESC(TX_256_511B, NULL),
+
+	MIB_DESC(TX_512_1023B, NULL),
+	MIB_DESC(TX_1024_1518B, NULL),
+
+	MIB_DESC(TX_JUMBO, NULL),
+	MIB_DESC(TX_OVERSIZE, NULL),
+	MIB_DESC(TX_LATE_COLLISION, NULL),
+	MIB_DESC(TX_EXCESSIVE_COLLISION, NULL),
+
+	MIB_DESC(TX_MULTI_COLLISION, NULL),
+	MIB_DESC(TX_SINGLE_COLLISION, NULL),
+	MIB_DESC(TX_UNICAST, NULL),
+	MIB_DESC(TX_DEFER, NULL),
+
+	MIB_DESC(TX_COLLISION, NULL),
+	MIB_DESC(RX_DISCARD, NULL),
+	MIB_DESC(TX_QUEUE_DROP, NULL),
+	MIB_DESC(TX_MAC_FILTER, NULL),
+
+	MIB_DESC(RX_OKBYTE, NULL),
+	MIB_DESC(RX_NOT_OKBYTE, "RxBadBytes"),
+	MIB_DESC(TX_OKBYTE, NULL),
+};
+
+static const struct ethtool_rmon_hist_range yt922x_rmon_ranges[] = {
+	{ 0, 64 },
+	{ 65, 127 },
+	{ 128, 255 },
+	{ 256, 511 },
+	{ 512, 1023 },
+	{ 1024, 1518 },
+	{ 1519, YT92XX_FRAME_SIZE_MAX },
+	{}
+};
+
+static int yt922x_stats_read(struct dsa_switch *ds, int port)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct device *dev = to_device(priv);
+	struct yt92xx_mib *mib = &pp->mib;
+	int res = 0;
+
+	/* Reading of yt92xx_port::mib is not protected by a lock and it's vain
+	 * to keep its consistency, since we have to read registers one by one
+	 * and there is no way to make a snapshot of MIB stats.
+	 *
+	 * Writing (by this function only) is and should be protected by
+	 * reg_lock.
+	 */
+	mutex_lock(&priv->reg_lock);
+	for (size_t i = 0; i < ARRAY_SIZE(yt92xx_mib_descs); i++) {
+		const struct yt92xx_mib_desc *desc = &yt92xx_mib_descs[i];
+
+		res = result_convert(yt_stat_mib_port_singletype_cnt_get
+			(priv->chip_info->unit, port, desc->type,
+			&((u64 *)mib)[i]));
+	}
+	mutex_unlock(&priv->reg_lock);
+
+	pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
+			mib->rx_128_255byte + mib->rx_256_511byte +
+			mib->rx_512_1023byte + mib->rx_1024_1518byte +
+			mib->rx_jumbo;
+	pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
+			mib->tx_128_255byte + mib->tx_256_511byte +
+			mib->tx_512_1023byte + mib->tx_1024_1518byte +
+			mib->tx_jumbo;
+
+	if (res)
+		dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
+			port, res);
+
+	return res;
+}
+
+static int yt922x_stats_eth_mac_stats(struct dsa_switch *ds, int port,
+				      struct ethtool_eth_mac_stats *mac_stats)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+	int res = 0;
+
+	res = yt922x_stats_read(ds, port);
+	if (res)
+		return res;
+
+	mac_stats->FramesTransmittedOK = pp->tx_frames;
+	mac_stats->SingleCollisionFrames = mib->tx_single_collision;
+	mac_stats->MultipleCollisionFrames = mib->tx_multi_collision;
+	mac_stats->FramesReceivedOK = pp->rx_frames;
+	mac_stats->FrameCheckSequenceErrors = mib->rx_fcs_err_64b_1518b +
+					      mib->rx_fcs_err_jumbo;
+	mac_stats->AlignmentErrors = mib->rx_align_err_less_64byte +
+	mib->rx_align_err_64b_1518byte + mib->rx_align_err_jumbo;
+	mac_stats->OctetsTransmittedOK = mib->tx_okbyte;
+	mac_stats->FramesWithDeferredXmissions = mib->tx_defer;
+	mac_stats->LateCollisions = mib->tx_late_collision;
+	mac_stats->FramesAbortedDueToXSColls = mib->tx_multi_collision;
+	/* mac_stats->FramesLostDueToIntMACXmitError */
+	/* mac_stats->CarrierSenseErrors */
+	mac_stats->OctetsReceivedOK = mib->rx_okbyte;
+	/* mac_stats->FramesLostDueToIntMACRcvError */
+	mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
+	mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
+	/* mac_stats->FramesWithExcessiveDeferral */
+	mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
+	mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
+	/* mac_stats->InRangeLengthErrors */
+	/* mac_stats->OutOfRangeLengthField */
+	mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
+
+	return res;
+}
+
+static int
+yt922x_stats_rmon_stats(struct dsa_switch *ds, int port,
+			struct ethtool_rmon_stats *rmon_stats,
+			const struct ethtool_rmon_hist_range **ranges)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+	int res;
+
+	res = yt922x_stats_read(ds, port);
+	if (res)
+		return res;
+
+	*ranges = yt92xx_rmon_ranges;
+
+	rmon_stats->undersize_pkts = mib->rx_undersize_errors;
+	rmon_stats->oversize_pkts = mib->rx_oversize_errors;
+	rmon_stats->fragments = mib->rx_fragment_errors;
+	/* rmon_stats->jabbers */
+
+	rmon_stats->hist[0] = mib->rx_64byte;
+	rmon_stats->hist[1] = mib->rx_65_127byte;
+	rmon_stats->hist[2] = mib->rx_128_255byte;
+	rmon_stats->hist[3] = mib->rx_256_511byte;
+	rmon_stats->hist[4] = mib->rx_512_1023byte;
+	rmon_stats->hist[5] = mib->rx_1024_1518byte;
+	rmon_stats->hist[6] = mib->rx_jumbo;
+
+	rmon_stats->hist_tx[0] = mib->tx_64byte;
+	rmon_stats->hist_tx[1] = mib->tx_65_127byte;
+	rmon_stats->hist_tx[2] = mib->tx_128_255byte;
+	rmon_stats->hist_tx[3] = mib->tx_256_511byte;
+	rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
+	rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
+	rmon_stats->hist_tx[6] = mib->tx_jumbo;
+
+	return res;
+}
+
+static int yt922x_stats_stats64(struct dsa_switch *ds, int port,
+				struct rtnl_link_stats64 *stats)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+
+	stats->rx_length_errors =
+		mib->rx_undersize_errors + mib->rx_fragment_errors;
+	stats->rx_over_errors = mib->rx_oversize_errors;
+	stats->rx_crc_errors = mib->rx_fcs_err_64b_1518b +
+	mib->rx_fcs_err_jumbo;
+	stats->rx_frame_errors = mib->rx_align_err_less_64byte +
+	mib->rx_align_err_64b_1518byte + mib->rx_align_err_jumbo;
+	/* stats->rx_fifo_errors */
+	/* stats->rx_missed_errors */
+
+	stats->tx_aborted_errors = mib->tx_excessive_collision;
+	/* stats->tx_carrier_errors */
+	/* YT922X switch no undersize pkt to transmit */
+	stats->tx_fifo_errors = 0;
+	/* stats->tx_heartbeat_errors */
+	stats->tx_window_errors = mib->tx_late_collision;
+
+	stats->rx_packets = pp->rx_frames;
+	stats->tx_packets = pp->tx_frames;
+	stats->rx_bytes = mib->rx_okbyte - ETH_FCS_LEN * stats->rx_packets;
+	stats->tx_bytes = mib->tx_okbyte - ETH_FCS_LEN * stats->tx_packets;
+	stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
+			   stats->rx_crc_errors + stats->rx_frame_errors;
+	stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
+			   stats->tx_window_errors;
+	stats->rx_dropped = mib->rx_discard;
+	/* stats->tx_dropped */
+	stats->multicast = mib->rx_multicast;
+	stats->collisions = mib->tx_collision;
+
+	return 0;
+}
+
+static void yt922x_rate_policer_del(struct dsa_switch *ds, int port)
+{
+}
+
+static int yt922x_rate_policer_add(struct dsa_switch *ds, int port,
+				   const struct flow_action_police *police,
+				   struct netlink_ext_ack *extack)
+{
+	return -EOPNOTSUPP;
+}
+
+static int yt922x_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
+				const struct switchdev_vlan_msti *msti)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u32 masks;
+	u32 ctrls;
+	int res;
+
+	if (!msti->vid)
+		return -EINVAL;
+	if (!msti->msti || msti->msti >= YT92XX_MSTI_NUM)
+		return -EINVAL;
+
+	masks = YT92XX_VLAN_CTRLb_STP_ID_M;
+	ctrls = YT92XX_VLAN_CTRLb_STP_ID(msti->msti);
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_regs_update_bits(priv, YT92XX_VLANn_CTRL(msti->vid),
+				      &masks, &ctrls, 1);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static void yt922x_mac_caps(struct dsa_switch *ds, int port,
+			    struct phylink_config *config)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	const struct yt92xx_info *info = priv->info;
+
+	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
+				   MAC_100 | MAC_1000;
+
+	if (info->internal_mask & BIT(port)) {
+		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
+			  config->supported_interfaces);
+	} else if (info->external_mask & BIT(port)) {
+		/* SERDES */
+		__set_bit(PHY_INTERFACE_MODE_SGMII,
+			  config->supported_interfaces);
+		/* REVSGMII (SGMII in PHY role) should go here, once
+		 * PHY_INTERFACE_MODE_REVSGMII is introduced.
+		 */
+		__set_bit(PHY_INTERFACE_MODE_100BASEX,
+			  config->supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
+			  config->supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
+			  config->supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_USXGMII,
+			  config->supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_10G_QXGMII,
+			  config->supported_interfaces);
+		config->mac_capabilities |= MAC_2500FD |
+					    MAC_5000FD |
+					    MAC_10000FD;
+	}
+}
+
+static int yt922x_chip_detect(struct yt92xx_priv *priv)
+{
+	struct device *dev = to_device(priv);
+	const struct yt92xx_info *info;
+	const char *name;
+	u8 extmode;
+	u32 chipid;
+	u32 major;
+	u32 mode;
+	int res;
+
+	res = yt92xx_reg_read(priv, YT922X_CHIP_ID, &chipid);
+	if (res)
+		return res;
+
+	major = FIELD_GET(YT922X_CHIP_ID_MAJOR, chipid);
+
+	for (info = yt92xx_infos; info->name; info++)
+		if (info->major == major)
+			break;
+	if (!info->name) {
+		dev_err(dev, "Unexpected chipid 0x%x\n", chipid);
+		return -ENODEV;
+	}
+
+	if (priv->chip_info->mode == YT9224)
+		name = "YT9224";
+	else
+		name = "YT9228";
+	for (; info->name; info++)
+		if (info->major == major && info->name == name)
+			break;
+	if (!info->name) {
+		dev_err(dev,
+			"Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n",
+			chipid, mode, extmode);
+		return -ENODEV;
+	}
+
+	/* Print chipid here since we are interested in lower 16 bits */
+	dev_info(dev,
+		 "Motorcomm %s ethernet switch.\n",
+		 info->name);
+
+	priv->info = info;
+
+	return 0;
+}
+
+static int yt922x_cpu_tag_mode_set(struct yt92xx_priv *priv)
+{
+	u32 val;
+	u32 val1;
+	int res;
+
+	/* cpu tag mode set */
+	res = yt92xx_reg_read(priv, YT922X_CPU_TAG_RX_CTRL, &val);
+	if (res)
+		return res;
+	res = yt92xx_reg_read(priv, YT922X_CPU_TAG_TX_CTRL, &val1);
+	if (res)
+		return res;
+	if (priv->chip_info->cpu_tag == CPU_TAG_8B) {
+		val &= ~YT922X_CPU_TAG_RX_MODE;
+		val1 &= ~YT922X_CPU_TAG_TX_MODE;
+		val1 &= ~YT922X_CPU_TAG_TX_TYPE;
+	} else {
+		val |= YT922X_CPU_TAG_RX_MODE;
+		val1 |= YT922X_CPU_TAG_TX_MODE;
+		val1 &= ~YT922X_CPU_TAG_TX_TYPE;
+	}
+	res = yt92xx_reg_write(priv, YT922X_CPU_TAG_RX_CTRL, val);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT922X_CPU_TAG_TX_CTRL, val1);
+	if (res)
+		return res;
+
+	return res;
+}
+
+static int yt92xx_cpu_port_set(struct yt92xx_priv *priv)
+{
+	struct device *dev = to_device(priv);
+	struct dsa_switch *ds = &priv->ds;
+	u32 ctrl;
+	int port;
+	u32 val;
+	int res;
+
+	/* cpu tag mode */
+	res = yt922x_cpu_tag_mode_set(priv);
+	if (res)
+		return res;
+
+	/* Enable DSA */
+	priv->cpu_ports_mask = dsa_cpu_ports(ds);
+	res = result_convert(yt_nic_ext_cpuport_en_set
+			(priv->chip_info->unit, YT_ENABLE));
+	if (res)
+		return res;
+
+	port = __ffs(priv->cpu_ports_mask);
+	res = result_convert(yt_nic_ext_cpuport_port_set
+			(priv->chip_info->unit, port));
+	if (res)
+		return res;
+	res = result_convert(yt_nic_ext_cputag_en_set
+			(priv->chip_info->unit, YT_ENABLE));
+	if (res)
+		return res;
+
+	/* Setup software switch */
+	ctrl = YT922X_CPU_COPY_TO_EXT_CPU;
+	res = yt92xx_reg_write(priv, YT922X_CPU_COPY, ctrl);
+	if (res)
+		return res;
+
+	/* Check for tag EtherType; do it after reset in case you messed it up
+	 * before.
+	 */
+	res = result_convert(yt_nic_cpuport_tagtpid_get
+			(priv->chip_info->unit, (u16 *)&val));
+	if (res)
+		return res;
+	if (val != ETH_P_YT92XX) {
+		dev_err(dev, "Tag type 0x%x != 0x%x\n", val,
+			ETH_P_YT92XX);
+		/* Despite being possible, we choose not to set CPU_TAG_TPID,
+		 * since there is no way it can be different unless you have the
+		 * wrong chip.
+		 */
+		return -EINVAL;
+	}
+
+	return res;
+}
+
+static int yt922x_chip_reset(struct yt92xx_priv *priv)
+{
+	int res;
+
+	res = yt922x_chip_detect(priv);
+	if (res)
+		return res;
+
+	/* Reset */
+	res = result_convert(yt_sys_chip_reset(priv->chip_info->unit));
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt922x_chip_setup_dsa(struct yt92xx_priv *priv)
+{
+	unsigned long cpu_ports_mask;
+	struct yt_port_mask member;
+	struct yt_port_mask untag;
+	u32 ctrl;
+	int port;
+	int res;
+
+	/* chip init */
+	res = result_convert(yt_init());
+	if (res)
+		return res;
+
+	ctrl = GENMASK(9, 0);
+	res = yt92xx_reg_write(priv, YT922X_FILTER_UNK_UCAST, ctrl);
+	if (res)
+		return res;
+
+	/* YT92xx does not support native DSA port bridging, so we use port
+	 * isolation to emulate it. However, be especially careful that port
+	 * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from
+	 * another bridge) is matched and the destination port (in another
+	 * bridge) is blocked, the packet will be dropped instead of flooding to
+	 * the "bridged" ports, thus we need to trap and handle those packets by
+	 * software.
+	 *
+	 * If there is no more than one bridge, we might be able to drop them
+	 * directly given some conditions are met, but we trap them in all cases
+	 * for now.
+	 */
+	ctrl = 0;
+	for (int i = 0; i < YT92XX_PORT_NUM; i++)
+		ctrl |= YT922X_ACT_UNK_ACTn_TRAP(i);
+	/* Except for CPU ports, if any packets are sent via CPU ports without
+	 * tag, they should be dropped.
+	 */
+	cpu_ports_mask = priv->cpu_ports_mask;
+	for_each_set_bit(port, &cpu_ports_mask, YT92XX_PORT_NUM) {
+		ctrl &= ~YT922X_ACT_UNK_ACTn_M(port);
+		ctrl |= YT922X_ACT_UNK_ACTn_DROP(port);
+	}
+	res = yt92xx_reg_write(priv, YT922X_ACT_UNK_UCAST, ctrl);
+	if (res)
+		return res;
+	res = yt92xx_reg_write(priv, YT922X_ACT_UNK_MCAST, ctrl);
+	if (res)
+		return res;
+
+	/* Tagged VID 0 should be treated as untagged, which confuses the
+	 * hardware a lot
+	 */
+	if (res)
+		return res;
+	res = result_convert(yt_vlan_port_get
+			(priv->chip_info->unit, 0, &member, &untag));
+	if (res)
+		return res;
+	member.portbits[0] = 0;
+	res = result_convert(yt_vlan_port_set
+			(priv->chip_info->unit, 0, member, untag));
+
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int __maybe_unused yt922x_chip_setup_qos(struct yt92xx_priv *priv)
+{
+	u32 ctrl;
+	int res;
+
+	/* DSCP to internal priorities */
+	for (u8 dscp = 0; dscp < DSCP_MAX; dscp++) {
+		int prio = ietf_dscp_to_ieee8021q_tt(dscp);
+
+		if (prio < 0)
+			return prio;
+
+		res = yt92xx_reg_write(priv, YT92XX_IPM_DSCPn(dscp),
+				       YT92XX_IPM_PRIO(prio));
+		if (res)
+			return res;
+	}
+
+	/* 802.1Q QoS to internal priorities */
+	for (u8 pcp = 0; pcp < 8; pcp++)
+		for (u8 dei = 0; dei < 2; dei++) {
+			ctrl = YT92XX_IPM_PRIO(pcp);
+			if (dei)
+				/* "Red" almost means drop, so it's not that
+				 * useful. Note that tc police does not support
+				 * Three-Color very well
+				 */
+				ctrl |= YT92XX_IPM_COLOR_YELLOW;
+
+			u32 reg = YT92XX_IPM_PCPn(dei, pcp);
+
+			res = yt92xx_reg_write(priv, reg, ctrl);
+			if (res)
+				return res;
+		}
+
+	return 0;
+}
+
+static int yt922x_chip_setup(struct yt92xx_priv *priv)
+{
+	u32 ctrl;
+	int res;
+
+	/* MIB enable */
+	res = result_convert(yt_stat_mib_enable_set
+			(priv->chip_info->unit, YT_ENABLE));
+	if (res)
+		return res;
+
+	/* meter enable */
+	res = yt92xx_reg_read(priv, YT922X_GLOBAL_CTRL1, &ctrl);
+	if (res)
+		return res;
+	ctrl |= YT922X_METER_EN;
+	res = yt92xx_reg_write(priv, YT922X_GLOBAL_CTRL1, ctrl);
+	if (res)
+		return res;
+
+	res = yt922x_chip_setup_dsa(priv);
+	if (res)
+		return res;
+
+#if IS_ENABLED(CONFIG_DCB)
+	res = yt922x_chip_setup_qos(priv);
+	if (res)
+		return res;
+#endif
+
+	/* Clear MIB */
+	res = result_convert(yt_stat_mib_clear_all(priv->chip_info->unit));
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt922x_dsa_setup(struct dsa_switch *ds)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct device *dev = to_device(priv);
+	struct device_node *np = dev->of_node;
+	struct device_node *child;
+	struct yt_drv_func drv;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	drv.switch_reg_read = &yt92xx_reg_mdio_read_register;
+	drv.switch_reg_write = &yt92xx_reg_mdio_write_register;
+	res = result_convert(yt_drv_register(drv));
+	mutex_unlock(&priv->reg_lock);
+	if (res)
+		return res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt922x_chip_reset(priv);
+	mutex_unlock(&priv->reg_lock);
+	if (res)
+		return res;
+
+	/* sleep after reset */
+	fsleep(YT92XX_RST_DELAY_US);
+
+	/* cpu port set */
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_cpu_port_set(priv);
+	mutex_unlock(&priv->reg_lock);
+	if (res)
+		return res;
+
+	/* Register the internal mdio bus. Nodes for internal ports should have
+	 * proper phy-handle pointing to their PHYs. Not enabling the internal
+	 * bus is possible, though pretty wired, if internal ports are not used.
+	 */
+	child = of_get_child_by_name(np, "mdio");
+	if (child) {
+		res = yt92xx_mbus_int_init(priv, child);
+		of_node_put(child);
+		if (res)
+			return res;
+	}
+
+	/* External mdio bus is optional */
+	child = of_get_child_by_name(np, "mdio-external");
+	if (child) {
+		res = yt92xx_mbus_ext_init(priv, child);
+		of_node_put(child);
+		if (res)
+			return res;
+
+		dev_err(dev, "Untested external mdio bus\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&priv->reg_lock);
+	res = yt922x_chip_setup(priv);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int
+yt922x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
+				  const struct tc_tbf_qopt_offload *qopt)
+{
+	return -EOPNOTSUPP;
+}
+
+static int
+yt922x_acl_add(struct dsa_switch *ds, int port,
+	       struct flow_cls_offload *cls, bool ingress)
+{
+	return -EOPNOTSUPP;
+}
+
+static int
+yt922x_acl_del(struct yt92xx_priv *priv, enum tc_setup_type type,
+	       unsigned long tag)
+{
+	return -EOPNOTSUPP;
+}
+
+static const struct yt92xx_switch_ops yt922x_switch_ops = {
+	.stats_read = yt922x_stats_read,
+	.stats_eth_mac_stats = yt922x_stats_eth_mac_stats,
+	.stats_rmon_stats = yt922x_stats_rmon_stats,
+	.stats_stats64 = yt922x_stats_stats64,
+	.rate_policer_del = yt922x_rate_policer_del,
+	.rate_policer_add = yt922x_rate_policer_add,
+	.port_setup_tc_tbf_port = yt922x_dsa_port_setup_tc_tbf_port,
+	.acl_delete = yt922x_acl_del,
+	.acl_add = yt922x_acl_add,
+	.vlan_msti_set = yt922x_vlan_msti_set,
+	.mac_caps = yt922x_mac_caps,
+	.dsa_setup = yt922x_dsa_setup,
+};
+
+static int yt922x_port_down(struct yt92xx_priv *priv, int port)
+{
+	int res;
+
+	res = yt_port_mac_enable_set(priv->chip_info->unit, port,
+				     YT_DISABLE);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static void yt922x_phylink_mac_link_down(struct phylink_config *config,
+					 unsigned int mode,
+					 phy_interface_t interface)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct yt92xx_priv *priv = to_yt92xx_priv(dp->ds);
+	int port = dp->index;
+	int res;
+
+	/* No need to sync; port control block is hold until device remove */
+	cancel_delayed_work(&priv->ports[port].mib_read);
+
+	mutex_lock(&priv->reg_lock);
+	res = yt922x_port_down(priv, port);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down",
+			port, res);
+}
+
+static int yt922x_port_up(struct yt92xx_priv *priv, int port, unsigned int mode,
+			  phy_interface_t interface, int speed, int duplex,
+			  bool tx_pause, bool rx_pause)
+{
+	struct yt_port_force_ctrl ctrl;
+	int res;
+
+	if (duplex) {
+		switch (speed) {
+		case SPEED_10:
+			ctrl.speed_dup = PORT_SPEED_DUP_10FULL;
+			break;
+		case SPEED_100:
+			ctrl.speed_dup = PORT_SPEED_DUP_100FULL;
+			break;
+		case SPEED_1000:
+			ctrl.speed_dup = PORT_SPEED_DUP_1000FULL;
+			break;
+		case SPEED_2500:
+			ctrl.speed_dup = PORT_SPEED_DUP_2500FULL;
+			break;
+		case SPEED_5000:
+			ctrl.speed_dup = PORT_SPEED_DUP_5GFULL;
+			break;
+		case SPEED_10000:
+			ctrl.speed_dup = PORT_SPEED_DUP_10GFULL;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (speed) {
+		case SPEED_10:
+			ctrl.speed_dup = PORT_SPEED_DUP_10HALF;
+			break;
+		case SPEED_100:
+			ctrl.speed_dup = PORT_SPEED_DUP_100HALF;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	if (rx_pause)
+		ctrl.rx_fc_en = 1;
+	else
+		ctrl.rx_fc_en = 0;
+
+	if (rx_pause)
+		ctrl.tx_fc_en = 1;
+	else
+		ctrl.tx_fc_en = 0;
+
+	res = result_convert
+		(yt_port_mac_force_set(priv->chip_info->unit, port, ctrl));
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static void yt922x_phylink_mac_link_up(struct phylink_config *config,
+				       struct phy_device *phydev,
+				       unsigned int mode,
+				       phy_interface_t interface, int speed,
+				       int duplex, bool tx_pause, bool rx_pause)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct yt92xx_priv *priv = to_yt92xx_priv(dp->ds);
+	int port = dp->index;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt922x_port_up(priv, port, mode, interface, speed, duplex,
+			     tx_pause, rx_pause);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
+			port, res);
+
+	schedule_delayed_work(&priv->ports[port].mib_read, 0);
+}
+
+static int yt922x_port_config(struct yt92xx_priv *priv, int port,
+			      unsigned int mode, phy_interface_t interface)
+{
+	struct device *dev = to_device(priv);
+	enum yt_extif_mode extif_mode;
+	int res;
+
+	if (!yt92xx_port_is_external(priv, port)) {
+		if (interface != PHY_INTERFACE_MODE_INTERNAL) {
+			dev_err(dev, "Wrong mode %d on port %d\n", interface,
+				port);
+			return -EINVAL;
+		}
+		return 0;
+	}
+	switch (interface) {
+	case PHY_INTERFACE_MODE_MII:
+		extif_mode = YT_EXTIF_MODE_MII;
+		break;
+	case PHY_INTERFACE_MODE_RGMII:
+		extif_mode = YT_EXTIF_MODE_RGMII;
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+		extif_mode = YT_EXTIF_MODE_SG_MAC;
+		break;
+	case PHY_INTERFACE_MODE_100BASEX:
+		extif_mode = YT_EXTIF_MODE_FIB_100;
+		break;
+	case PHY_INTERFACE_MODE_1000BASEX:
+		extif_mode = YT_EXTIF_MODE_FIB_100;
+		break;
+	case PHY_INTERFACE_MODE_2500BASEX:
+		extif_mode = YT_EXTIF_MODE_BX2500;
+		break;
+	case PHY_INTERFACE_MODE_USXGMII:
+		extif_mode = YT_EXTIF_MODE_USXGMII_10G;
+		break;
+	case PHY_INTERFACE_MODE_QUSGMII:
+		extif_mode = YT_EXTIF_MODE_10GQXGMII;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	res = result_convert(yt_port_extif_mode_set(priv->chip_info->unit,
+						    port, extif_mode));
+
+	return 0;
+}
+
+static void yt922x_phylink_mac_config(struct phylink_config *config,
+				      unsigned int mode,
+				      const struct phylink_link_state *state)
+{
+	struct dsa_port *dp = dsa_phylink_to_port(config);
+	struct yt92xx_priv *priv = to_yt92xx_priv(dp->ds);
+	int port = dp->index;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt922x_port_config(priv, port, mode, state->interface);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config",
+			port, res);
+}
+
+static const struct phylink_mac_ops yt922x_phylink_mac_ops = {
+	.mac_link_down = yt922x_phylink_mac_link_down,
+	.mac_link_up = yt922x_phylink_mac_link_up,
+	.mac_config = yt922x_phylink_mac_config,
+};
+
+#endif
+
+static const struct yt92xx_chip_info yt92xx_chip_info[] = {
+#if IS_ENABLED(CONFIG_MOTORCOMM_YT921X)
+	[YT9215] = {
+		.switchid = 0,
+		.unit = 0,
+		.mode = YT9215,
+		.cpu_tag = CPU_TAG_8B,
+		.ageing_time_min = 1 * 5000,
+		.ageing_time_max = U16_MAX * 5000,
+		.max_ports = YT92XX_PORT_NUM,
+		.max_lag_groups = YT92XX_LAG_NUM,
+		.ops = &yt921x_switch_ops,
+		.mac_ops = &yt921x_phylink_mac_ops,
+	},
+#elif IS_ENABLED(CONFIG_MOTORCOMM_YT922X)
+	[YT9224_4B]{
+		.switchid = 0,
+		.unit = 0,
+		.mode = YT9224,
+		.cpu_tag = CPU_TAG_4B,
+		.ageing_time_min = 1 * 6000,
+		.ageing_time_max = U16_MAX * 6000,
+		.max_ports = YT92XX_PORT_NUM,
+		.max_lag_groups = YT92XX_LAG_NUM,
+		.ops = &yt922x_switch_ops,
+		.mac_ops = &yt922x_phylink_mac_ops,
+	},
+	[YT9224_8B] = {
+		.switchid = 0,
+		.unit = 0,
+		.mode = YT9224,
+		.cpu_tag = CPU_TAG_8B,
+		.ageing_time_min = 1 * 6000,
+		.ageing_time_max = U16_MAX * 6000,
+		.max_ports = YT92XX_PORT_NUM,
+		.max_lag_groups = YT92XX_LAG_NUM,
+		.ops = &yt922x_switch_ops,
+		.mac_ops = &yt922x_phylink_mac_ops,
+	},
+#endif
+};
+
+static void yt92xx_dsa_get_strings(struct dsa_switch *ds, int port,
+				   u32 stringset, u8 *data)
+{
+	if (stringset != ETH_SS_STATS)
+		return;
+
+	for (size_t i = 0; i < ARRAY_SIZE(yt92xx_mib_descs); i++) {
+		const struct yt92xx_mib_desc *desc = &yt92xx_mib_descs[i];
+
+		if (desc->name)
+			ethtool_puts(&data, desc->name);
+	}
+}
+
+static void yt92xx_dsa_get_ethtool_stats(struct dsa_switch *ds, int port,
+					 u64 *data)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+	size_t j;
+
+	if (priv->chip_info->ops->stats_read)
+		priv->chip_info->ops->stats_read(ds, port);
+
+	j = 0;
+	for (size_t i = 0; i < ARRAY_SIZE(yt92xx_mib_descs); i++) {
+		const struct yt92xx_mib_desc *desc = &yt92xx_mib_descs[i];
+
+		if (!desc->name)
+			continue;
+
+		data[j] = ((u64 *)mib)[i];
+		j++;
+	}
+}
+
+static int yt92xx_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+	int cnt = 0;
+
+	if (sset != ETH_SS_STATS)
+		return 0;
+
+	for (size_t i = 0; i < ARRAY_SIZE(yt92xx_mib_descs); i++) {
+		const struct yt92xx_mib_desc *desc = &yt92xx_mib_descs[i];
+
+		if (desc->name)
+			cnt++;
+	}
+
+	return cnt;
+}
+
+static void
+yt92xx_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
+			     struct ethtool_eth_mac_stats *mac_stats)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+
+	if (priv->chip_info->ops->stats_eth_mac_stats)
+		priv->chip_info->ops->stats_eth_mac_stats(ds, port, mac_stats);
+}
+
+static void
+yt92xx_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+			      struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+	int res;
+
+	if (priv->chip_info->ops->stats_read) {
+		res = priv->chip_info->ops->stats_read(ds, port);
+		if (!res) {
+			ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
+			ctrl_stats->MACControlFramesReceived = mib->rx_pause;
+		}
+	}
+}
+
+static void
+yt92xx_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
+			  struct ethtool_rmon_stats *rmon_stats,
+			  const struct ethtool_rmon_hist_range **ranges)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+
+	if (priv->chip_info->ops->stats_rmon_stats)
+		priv->chip_info->ops->stats_rmon_stats(ds, port, rmon_stats,
+						       ranges);
+}
+
+static void yt92xx_dsa_get_stats64(struct dsa_switch *ds, int port,
+				   struct rtnl_link_stats64 *stats)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+
+	if (priv->chip_info->ops->stats_stats64)
+		priv->chip_info->ops->stats_stats64(ds, port, stats);
+}
+
+static void yt92xx_dsa_get_pause_stats(struct dsa_switch *ds, int port,
+				       struct ethtool_pause_stats *pause_stats)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt92xx_port *pp = &priv->ports[port];
+	struct yt92xx_mib *mib = &pp->mib;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	if (priv->chip_info->ops->stats_read) {
+		res = priv->chip_info->ops->stats_read(ds, port);
+		if (!res) {
+			pause_stats->tx_pause_frames = mib->tx_pause;
+			pause_stats->rx_pause_frames = mib->rx_pause;
+		}
+	}
+}
+
+static int yt92xx_dsa_set_mac_eee(struct dsa_switch *ds, int port,
+				  struct ethtool_keee *e)
+{
+	/* Poor datasheet for EEE operations; don't ask if you are confused */
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	enum yt_enable enable = e->eee_enabled;
+	u16 new_mask;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	/* Enable / disable global EEE */
+	new_mask = priv->eee_ports_mask;
+	new_mask &= ~BIT(port);
+	new_mask |= !enable ? 0 : BIT(port);
+
+	res = result_convert(yt_port_mac_eee_enable_set
+			(priv->chip_info->unit, port, enable));
+	if (res)
+		goto unlock;
+
+	priv->eee_ports_mask = new_mask;
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+	return res;
+}
+
+static int yt92xx_dsa_port_change_mtu(struct dsa_switch *ds, int port,
+				      int new_mtu)
+{
+	/* Only serves as packet filter, since the frame size is always set to
+	 * maximum after reset
+	 */
+
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	int frame_size;
+	int res;
+
+	frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+	if (dsa_port_is_cpu(dp)) {
+		if (priv->chip_info->cpu_tag == CPU_TAG_4B)
+			frame_size += 4;
+		else
+			frame_size += 8;
+	}
+	mutex_lock(&priv->reg_lock);
+	res = result_convert(yt_port_jumbo_enable_set(priv->chip_info->unit,
+						      port, YT_ENABLE));
+	if (res)
+		goto unlock;
+	res = result_convert(yt_port_jumbo_size_set(priv->chip_info->unit,
+						    port, frame_size));
+	if (res)
+		goto unlock;
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+	return res;
+}
+
+static int yt92xx_dsa_port_max_mtu(struct dsa_switch *ds, int port)
+{
+	/* Only called for user ports, exclude tag len here */
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+
+	if (priv->chip_info->cpu_tag == CPU_TAG_4B)
+		return YT92XX_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - 4;
+	else
+		return YT92XX_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - 8;
+}
+
+static void yt92xx_dsa_port_policer_del(struct dsa_switch *ds, int port)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+
+	if (priv->chip_info->ops->rate_policer_del)
+		priv->chip_info->ops->rate_policer_del(ds, port);
+}
+
+static int yt92xx_dsa_port_policer_add(struct dsa_switch *ds, int port,
+				       const struct flow_action_police *police,
+				       struct netlink_ext_ack *extack)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	if (priv->chip_info->ops->rate_policer_add)
+		res = priv->chip_info->ops->rate_policer_add(ds, port, police,
+							     extack);
+
+	return res;
+}
+
+static int
+yt92xx_dsa_port_setup_tc(struct dsa_switch *ds, int port,
+			 enum tc_setup_type type, void *type_data)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	switch (type) {
+	case TC_SETUP_QDISC_TBF: {
+		const struct tc_tbf_qopt_offload *qopt = type_data;
+
+		if (priv->chip_info->ops->port_setup_tc_tbf_port)
+			res = priv->chip_info->ops->port_setup_tc_tbf_port(ds, port, qopt);
+		break;
+	}
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return res;
+}
+
+static int
+yt92xx_dsa_cls_flower_del(struct dsa_switch *ds, int port,
+			  struct flow_cls_offload *cls, bool ingress)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	if (priv->chip_info->ops->acl_add)
+		res = priv->chip_info->ops->acl_delete(priv, TC_SETUP_CLSFLOWER, cls->cookie);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int
+yt92xx_dsa_cls_flower_add(struct dsa_switch *ds, int port,
+			  struct flow_cls_offload *cls, bool ingress)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	if (priv->chip_info->ops->acl_add)
+		res = priv->chip_info->ops->acl_add(ds, port, cls, ingress);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static void yt92xx_dsa_port_mirror_del(struct dsa_switch *ds, int port,
+				       struct dsa_mall_mirror_tc_entry *mirror)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt_mirror_entry entry;
+	int res;
+
+	/* group 0 */
+	if (mirror->ingress)
+		entry.flags = YT_MIRROR_FLAG_PORT_INGRESS;
+	else
+		entry.flags = YT_MIRROR_FLAG_PORT_EGRESS;
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert
+		(yt_mirror_group_get(priv->chip_info->unit, 0, &entry));
+	if (res)
+		goto unlock;
+
+	if (mirror->ingress)
+		CLEAR_BIT(entry.rxportmask.portbits[0], port);
+	else
+		CLEAR_BIT(entry.rxportmask.portbits[0], port);
+
+	res = result_convert
+		(yt_mirror_group_set(priv->chip_info->unit, 0, &entry));
+	if (res)
+		goto unlock;
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+}
+
+static int yt92xx_dsa_port_mirror_add(struct dsa_switch *ds, int port,
+				      struct dsa_mall_mirror_tc_entry *mirror,
+				      bool ingress,
+				      struct netlink_ext_ack *extack)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt_mirror_entry entry;
+	u32 rxportmask = 0;
+	u32 txportmask = 0;
+	bool rx_check = 0;
+	bool tx_check = 0;
+	int res;
+	u32 dst;
+
+	mutex_lock(&priv->reg_lock);
+	entry.flags = YT_MIRROR_FLAG_ALL;
+	res = result_convert
+		(yt_mirror_group_get(priv->chip_info->unit, 0, &entry));
+	if (res)
+		goto unlock;
+
+	if (ingress) {
+		SET_BIT(rxportmask, port);
+		rx_check = (entry.rxportmask.portbits[0] & ~rxportmask);
+	} else {
+		SET_BIT(txportmask, port);
+		tx_check = (entry.txportmask.portbits[0] & ~txportmask);
+	}
+	dst = YT92XX_MIRROR_PORT(mirror->to_local_port);
+
+	/* other mirror tasks & different dst port -> conflict */
+	if ((rx_check || tx_check) && entry.mirrorport != dst) {
+		NL_SET_ERR_MSG_MOD
+			(extack,
+			"Sniffer port is already configured, delete existing rules & retry");
+		return -EBUSY;
+	}
+
+	if (entry.rxportmask.portbits[0] == rxportmask &&
+	    entry.txportmask.portbits[0] == txportmask &&
+	    entry.mirrorport == dst)
+		return 0;
+
+	entry.mirrorport = dst;
+	entry.rxportmask.portbits[0] = rxportmask;
+	entry.txportmask.portbits[0] = txportmask;
+
+	res = result_convert
+		(yt_mirror_group_set(priv->chip_info->unit, 0, &entry));
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+	return res;
+}
+
+static int yt92xx_dsa_port_lag_leave(struct dsa_switch *ds, int port,
+				     struct dsa_lag lag)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt_port_mask port_mask;
+	struct dsa_port *dp;
+	u32 ctrl;
+	int res;
+
+	if (!lag.id)
+		return -EINVAL;
+
+	ctrl = 0;
+	dsa_lag_foreach_port(dp, ds->dst, &lag) ctrl |= BIT(dp->index);
+
+	mutex_lock(&priv->reg_lock);
+	port_mask.portbits[0] = ctrl;
+	res = result_convert(yt_lag_group_port_set(priv->chip_info->unit,
+						   lag.id - 1, port_mask));
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_dsa_port_lag_check(struct dsa_switch *ds, struct dsa_lag lag,
+				     struct netdev_lag_upper_info *info,
+				     struct netlink_ext_ack *extack)
+{
+	unsigned int members;
+	struct dsa_port *dp;
+
+	if (!lag.id)
+		return -EINVAL;
+
+	members = 0;
+	dsa_lag_foreach_port(dp, ds->dst, &lag)
+		/* Includes the port joining the LAG */
+		members++;
+
+	if (members > YT92XX_LAG_PORT_NUM) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Cannot offload more than max LAG ports");
+		return -EOPNOTSUPP;
+	}
+
+	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
+		NL_SET_ERR_MSG_MOD(extack,
+				   "Can only offload LAG using hash TX type");
+		return -EOPNOTSUPP;
+	}
+
+	if (info->hash_type != NETDEV_LAG_HASH_L2 &&
+	    info->hash_type != NETDEV_LAG_HASH_L23 &&
+	    info->hash_type != NETDEV_LAG_HASH_L34) {
+		NL_SET_ERR_MSG_MOD
+			(extack,
+			"Can only offload L2 or L2+L3 or L3+L4 TX hash");
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int yt92xx_lag_hash(struct yt92xx_priv *priv, u32 ctrl, bool unique_lag,
+			   struct netlink_ext_ack *extack)
+{
+	u32 val;
+	int res;
+
+	/* Hash Mode is global. Make sure the same Hash Mode is set to all the
+	 * 2 possible lags.
+	 * If we are the unique LAG we can set whatever hash mode we want.
+	 * To change hash mode it's needed to remove all LAG and change the mode
+	 * with the latest.
+	 */
+	if (unique_lag) {
+		res = result_convert
+			(yt_lag_hash_sel_set(priv->chip_info->unit, ctrl));
+		if (res)
+			return res;
+	} else {
+		res = result_convert(yt_lag_hash_sel_get
+				(priv->chip_info->unit, (u8 *)&val));
+		if (res)
+			return res;
+
+		if (val != ctrl) {
+			NL_SET_ERR_MSG_MOD
+				(extack,
+				"Mismatched Hash Mode across different lags is not supported");
+			return -EOPNOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
+static int yt92xx_dsa_port_lag_join(struct dsa_switch *ds, int port,
+				    struct dsa_lag lag,
+				    struct netdev_lag_upper_info *info,
+				    struct netlink_ext_ack *extack)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt_port_mask port_mask;
+	struct dsa_port *dp;
+	bool unique_lag;
+	unsigned int i;
+	u32 ctrl;
+	int res;
+
+	res = yt92xx_dsa_port_lag_check(ds, lag, info, extack);
+	if (res)
+		return res;
+
+	ctrl = 0;
+	switch (info->hash_type) {
+	case NETDEV_LAG_HASH_L34:
+		ctrl |= YT92XX_LAG_HASH_IP_DST;
+		ctrl |= YT92XX_LAG_HASH_IP_SRC;
+		ctrl |= YT92XX_LAG_HASH_IP_PROTO;
+
+		ctrl |= YT92XX_LAG_HASH_L4_DPORT;
+		ctrl |= YT92XX_LAG_HASH_L4_SPORT;
+		break;
+	case NETDEV_LAG_HASH_L23:
+		ctrl |= YT92XX_LAG_HASH_MAC_DA;
+		ctrl |= YT92XX_LAG_HASH_MAC_SA;
+
+		ctrl |= YT92XX_LAG_HASH_IP_DST;
+		ctrl |= YT92XX_LAG_HASH_IP_SRC;
+		ctrl |= YT92XX_LAG_HASH_IP_PROTO;
+		break;
+	case NETDEV_LAG_HASH_L2:
+		ctrl |= YT92XX_LAG_HASH_MAC_DA;
+		ctrl |= YT92XX_LAG_HASH_MAC_SA;
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	/* Check if we are the unique configured LAG */
+	unique_lag = true;
+	dsa_lags_foreach_id(i, ds->dst)
+		if (i != lag.id && dsa_lag_by_id(ds->dst, i)) {
+			unique_lag = false;
+			break;
+		}
+
+	mutex_lock(&priv->reg_lock);
+	do {
+		res = yt92xx_lag_hash(priv, ctrl, unique_lag, extack);
+		if (res)
+			break;
+
+		ctrl = 0;
+		dsa_lag_foreach_port(dp, ds->dst, &lag) ctrl |= BIT(dp->index);
+		port_mask.portbits[0] = ctrl;
+		res = result_convert(yt_lag_group_port_set
+				(priv->chip_info->unit,
+				 lag.id - 1, port_mask));
+	} while (0);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_dsa_port_fdb_dump(struct dsa_switch *ds, int port,
+				    dsa_fdb_dump_cb_t *cb, void *data)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt_ucast_macaddr_info info;
+	u16 next_index = 0;
+	u16 index = 0;
+	int res;
+
+	memset(&info, 0, sizeof(struct yt_ucast_macaddr_info));
+	mutex_lock(&priv->reg_lock);
+	/* Hardware FDB is shared for fdb and mdb, "bridge fdb show"
+	 * only wants to see unicast
+	 */
+	if (index == 0) {
+		res = result_convert(yt_l2_fdb_uc_withindex_get
+				(priv->chip_info->unit, index, &info));
+
+		if (info.port == port &&
+		    !is_multicast_ether_addr(info.macaddr.addr)) {
+			res = cb(info.macaddr.addr, info.vid,
+				 info.type == YT_L2_FDB_TYPE_STATIC, data);
+			if (res)
+				goto unlock;
+		}
+	}
+
+	index = 1;
+	do {
+		res = result_convert(yt_l2_fdb_uc_withindex_getnext
+				(priv->chip_info->unit, index,
+				 &next_index, &info));
+
+		if (info.port == port &&
+		    !is_multicast_ether_addr(info.macaddr.addr)) {
+			res = cb(info.macaddr.addr, info.vid,
+				 info.type == YT_L2_FDB_TYPE_STATIC, data);
+			if (res)
+				goto unlock;
+		}
+		index = next_index;
+	} while (res == CMM_ERR_OK);
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+	return res;
+}
+
+static void yt92xx_dsa_port_fdb_flush(struct dsa_switch *ds, int port)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct device *dev = to_device(priv);
+	struct yt_l2_uc_flush_info info;
+	int res;
+
+	memset(&info, 0, sizeof(struct yt_l2_uc_flush_info));
+	mutex_lock(&priv->reg_lock);
+	info.port = port;
+	info.is_lag = 0;
+	info.flush_mode = YT_L2_FDB_FLUSH_PORT;
+	info.flush_static_en = 0;
+	res = result_convert
+		(yt_l2_fdb_ucast_flush(priv->chip_info->unit, info));
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for",
+			port, res);
+}
+
+static int yt92xx_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u32 ctrl;
+	int res;
+
+	/* AGEING reg is set in 5s step, is divided by per step in yt apis*/
+	ctrl = clamp(msecs / 1000, 1, U16_MAX);
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert
+		(yt_l2_fdb_aging_time_set(priv->chip_info->unit, ctrl));
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_fdb_leave(struct yt92xx_priv *priv, const unsigned char *addr,
+			    u16 vid, u16 port)
+{
+	struct yt_ucast_macaddr_info info;
+	struct yt_mac_addr mac;
+	int res;
+	/* vid + mac get */
+	memset(&info, 0, sizeof(struct yt_ucast_macaddr_info));
+	memcpy(mac.addr, addr, MAC_ADDR_LEN);
+	res = result_convert
+		(yt_l2_fdb_uc_get(priv->chip_info->unit, vid, mac, &info));
+	if (res)
+		return res;
+	if (info.port == port)
+		res = result_convert(yt_l2_fdb_ucast_addr_del
+				(priv->chip_info->unit, vid, mac));
+	return res;
+}
+
+static int yt92xx_dsa_port_fdb_del(struct dsa_switch *ds, int port,
+				   const unsigned char *addr, u16 vid,
+				   struct dsa_db db)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_fdb_leave(priv, addr, vid, port);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_dsa_port_fdb_add(struct dsa_switch *ds, int port,
+				   const unsigned char *addr, u16 vid,
+				   struct dsa_db db)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt_ucast_macaddr_info info;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	info.port = port;
+	info.islag = 0;
+	info.vid = vid;
+	memcpy(info.macaddr.addr, addr, MAC_ADDR_LEN);
+	info.type = YT_L2_FDB_TYPE_STATIC;
+	info.action = YT_L2_FDB_ACTION_TYPE_FORWARD;
+	info.force_flag = 1;
+	res = result_convert
+		(yt_l2_fdb_ucast_addr_add(priv->chip_info->unit, &info));
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_dsa_port_mdb_del(struct dsa_switch *ds, int port,
+				   const struct switchdev_obj_port_mdb *mdb,
+				   struct dsa_db db)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	const unsigned char *addr = mdb->addr;
+	struct yt_mcast_mac_data info;
+	struct yt_mac_addr mac;
+	u16 vid = mdb->vid;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	memset(&info, 0, sizeof(struct yt_mcast_mac_data));
+	memcpy(mac.addr, addr, MAC_ADDR_LEN);
+	res = result_convert
+		(yt_multicast_get(priv->chip_info->unit, vid, mac, &info));
+	if (res)
+		goto unlock;
+
+	if (info.port_mask.portbits[0] & BIT(port)) {
+		info.port_mask.portbits[0] &= ~BIT(port);
+		if (info.port_mask.portbits[0])
+			res = result_convert(yt_multicast_addr_add
+					(priv->chip_info->unit,
+					 vid, mac, info.port_mask));
+		else
+			res = result_convert(yt_multicast_addr_del
+				(priv->chip_info->unit, vid, mac));
+	}
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+	return res;
+}
+
+static int yt92xx_dsa_port_mdb_add(struct dsa_switch *ds, int port,
+				   const struct switchdev_obj_port_mdb *mdb,
+				   struct dsa_db db)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt_mcast_mac_data info;
+	struct yt_mac_addr mac_addr;
+	u16 vid = mdb->vid;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	memcpy(mac_addr.addr, mdb->addr, MAC_ADDR_LEN);
+	memset(&info, 0, sizeof(struct yt_mcast_mac_data));
+	res = result_convert
+		(yt_multicast_get(priv->chip_info->unit, vid, mac_addr,
+				  &info));
+	if (res)
+		goto unlock;
+	info.port_mask.portbits[0] |= BIT(port);
+	res = result_convert(yt_multicast_addr_add(priv->chip_info->unit,
+						   vid, mac_addr,
+						   info.port_mask));
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+	return res;
+}
+
+static int yt92xx_vlan_aware_set(struct yt92xx_priv *priv, int port,
+				 bool vlan_aware)
+{
+	u8 mask;
+	enum yt_vlan_type type;
+	int res;
+
+	/* Abuse SVLAN for PCP parsing without polluting the FDB - it just works
+	 * despite YT921X_VLAN_CTRL_SVLAN_EN never being set
+	 */
+	if (!vlan_aware)
+		type = YT_VLAN_TYPE_SVLAN;
+	else
+		type = YT_VLAN_TYPE_CVLAN;
+
+	mask = 1;
+	res = result_convert(yt_vlan_port_igrtpidsel_set
+			(priv->chip_info->unit, type, port, mask));
+
+	return res;
+}
+
+static int yt92xx_vlan_filtering(struct yt92xx_priv *priv, int port,
+				 bool vlan_filtering)
+{
+	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+	struct net_device *bdev;
+	enum yt_vlan_type type;
+	enum yt_vlan_aft aft;
+	u16 pvid;
+	int res;
+
+	bdev = dsa_port_bridge_dev_get(dp);
+
+	if (!bdev || !vlan_filtering)
+		pvid = YT92XX_VID_UNWARE;
+	else
+		br_vlan_get_pvid(bdev, &pvid);
+
+	/* set cvlan pvid */
+	type = YT_VLAN_TYPE_CVLAN;
+	res = result_convert(yt_vlan_port_igrpvid_set(priv->chip_info->unit,
+						      type, port, pvid));
+	if (res)
+		return res;
+
+	aft = YT_VLAN_AFT_ALL;
+	/* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */
+	if (vlan_filtering && !pvid)
+		aft = YT_VLAN_AFT_TAGGED;
+	res = result_convert(yt_vlan_port_aft_set(priv->chip_info->unit,
+						  type, port, aft));
+	if (res)
+		return res;
+
+	res = result_convert(yt_vlan_port_igrfilter_enable_set
+			(priv->chip_info->unit, port, vlan_filtering));
+	if (res)
+		return res;
+
+	res = yt92xx_vlan_aware_set(priv, port, vlan_filtering);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt92xx_dsa_port_vlan_filtering(struct dsa_switch *ds, int port,
+					  bool vlan_filtering,
+					  struct netlink_ext_ack *extack)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	if (dsa_is_cpu_port(ds, port))
+		return 0;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_vlan_filtering(priv, port, vlan_filtering);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_vlan_del(struct yt92xx_priv *priv, int port, u16 vid)
+{
+	struct yt_port_mask member;
+	struct yt_port_mask untag;
+	int res;
+
+	res = result_convert(yt_vlan_port_get(priv->chip_info->unit, vid,
+					      &member, &untag));
+	if (res)
+		return res;
+
+	member.portbits[0] &= ~BIT(port);
+	untag.portbits[0] &= ~BIT(port);
+
+	res = result_convert(yt_vlan_port_set(priv->chip_info->unit, vid,
+					      member, untag));
+
+	return res;
+}
+
+static int yt92xx_pvid_clear(struct yt92xx_priv *priv, int port)
+{
+	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+	enum yt_vlan_type type;
+	enum yt_vlan_aft aft;
+	bool vlan_filtering;
+	int res;
+	u16 vid;
+
+	vlan_filtering = dsa_port_is_vlan_filtering(dp);
+
+	/* cvlan pvid */
+	type = YT_VLAN_TYPE_CVLAN;
+	vid = vlan_filtering ? 0 : YT92XX_VID_UNWARE;
+	res = result_convert(yt_vlan_port_igrpvid_set(priv->chip_info->unit,
+						      type, port, vid));
+	if (res)
+		return res;
+
+	if (vlan_filtering) {
+		aft = YT_VLAN_AFT_TAGGED;
+		res = result_convert(yt_vlan_port_aft_set
+				(priv->chip_info->unit, type, port, aft));
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static int yt92xx_dsa_port_vlan_del(struct dsa_switch *ds, int port,
+				    const struct switchdev_obj_port_vlan *vlan)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u16 vid = vlan->vid;
+	u16 pvid;
+	int res;
+
+	if (dsa_is_cpu_port(ds, port))
+		return 0;
+
+	mutex_lock(&priv->reg_lock);
+	do {
+		struct dsa_port *dp = dsa_to_port(ds, port);
+		struct net_device *bdev;
+
+		res = yt92xx_vlan_del(priv, port, vid);
+		if (res)
+			break;
+
+		bdev = dsa_port_bridge_dev_get(dp);
+		if (bdev) {
+			br_vlan_get_pvid(bdev, &pvid);
+			if (pvid == vid)
+				res = yt92xx_pvid_clear(priv, port);
+		}
+	} while (0);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_vlan_add(struct yt92xx_priv *priv, int port, u16 vid,
+			   bool untagged)
+{
+	struct yt_port_mask member;
+	struct yt_port_mask untag;
+	int res;
+
+	/* vlan add member and untag port */
+	member.portbits[0] = BIT(port) | priv->cpu_ports_mask;
+	if (untagged)
+		untag.portbits[0] = BIT(port);
+
+	res = result_convert(yt_vlan_port_set(priv->chip_info->unit, vid,
+					      member, untag));
+
+	return res;
+}
+
+static int yt92xx_pvid_set(struct yt92xx_priv *priv, int port, u16 vid)
+{
+	/* double check */
+	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+	enum yt_vlan_type type;
+	enum yt_vlan_aft aft;
+	bool vlan_filtering;
+	int res;
+
+	vlan_filtering = dsa_port_is_vlan_filtering(dp);
+
+	if (vlan_filtering) {
+		type = YT_VLAN_TYPE_CVLAN;
+		res = result_convert(yt_vlan_port_igrpvid_set
+				(priv->chip_info->unit, type, port, vid));
+		if (res)
+			return res;
+	}
+
+	aft = YT_VLAN_AFT_ALL;
+	res = result_convert(yt_vlan_port_aft_set(priv->chip_info->unit,
+						  type, port, aft));
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt92xx_dsa_port_vlan_add(struct dsa_switch *ds, int port,
+				    const struct switchdev_obj_port_vlan *vlan,
+				    struct netlink_ext_ack *extack)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u16 vid = vlan->vid;
+	u16 pvid;
+	int res;
+
+	/* CPU port is supposed to be a member of every VLAN; see
+	 * yt921x_vlan_add() and yt921x_port_setup()
+	 */
+	if (dsa_is_cpu_port(ds, port))
+		return 0;
+
+	mutex_lock(&priv->reg_lock);
+	do {
+		struct dsa_port *dp = dsa_to_port(ds, port);
+		struct net_device *bdev;
+
+		res = yt92xx_vlan_add(priv, port, vid,
+				      vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+		if (res)
+			break;
+
+		bdev = dsa_port_bridge_dev_get(dp);
+		if (bdev) {
+			if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+				res = yt92xx_pvid_set(priv, port, vid);
+			} else {
+				br_vlan_get_pvid(bdev, &pvid);
+				if (pvid == vid)
+					res = yt92xx_pvid_clear(priv, port);
+			}
+		}
+	} while (0);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+					    struct switchdev_brport_flags flags,
+					    struct netlink_ext_ack *extack)
+{
+	if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD |
+			   BR_MCAST_FLOOD | BR_ISOLATED))
+		return -EINVAL;
+	return 0;
+}
+
+/* Make sure to include the CPU port in ports_mask, or your bridge will
+ * not have it.
+ */
+static int yt92xx_bridge(struct yt92xx_priv *priv, u16 ports_mask)
+{
+	unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask;
+	struct yt_port_mask port_mask;
+	u32 isolated_mask;
+	u32 ctrl;
+	int port;
+	int res;
+
+	isolated_mask = 0;
+	for_each_set_bit(port, &targets_mask, YT92XX_PORT_NUM) {
+		struct yt92xx_port *pp = &priv->ports[port];
+
+		if (pp->isolated)
+			isolated_mask |= BIT(port);
+	}
+
+	/* Block from non-cpu bridge ports ... */
+	for_each_set_bit(port, &targets_mask, YT92XX_PORT_NUM) {
+		struct yt92xx_port *pp = &priv->ports[port];
+
+		/* to non-bridge ports */
+		ctrl = ~ports_mask;
+		/* to isolated ports when isolated */
+		if (pp->isolated)
+			ctrl |= isolated_mask;
+		/* to itself when non-hairpin */
+		if (!pp->hairpin)
+			ctrl |= BIT(port);
+		else
+			ctrl &= ~BIT(port);
+
+		port_mask.portbits[0] = ctrl;
+		res = result_convert(yt_isolation_port_set
+				(priv->chip_info->unit, port, port_mask));
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static int yt92xx_bridge_flags(struct yt92xx_priv *priv, int port,
+			       struct switchdev_brport_flags flags)
+{
+	struct yt92xx_port *pp = &priv->ports[port];
+	bool do_flush;
+	int res;
+	/* need to add after isolation and disable learning */
+
+	if (flags.mask & BR_LEARNING) {
+		bool learning = flags.val & BR_LEARNING;
+
+		res = result_convert(yt_l2_port_learn_en_set
+				(priv->chip_info->unit, port, learning));
+		if (res)
+			return res;
+	}
+
+	/* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP
+	 * is set
+	 */
+
+	/* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
+
+	do_flush = false;
+	if (flags.mask & BR_HAIRPIN_MODE) {
+		pp->hairpin = flags.val & BR_HAIRPIN_MODE;
+		do_flush = true;
+	}
+	if (flags.mask & BR_ISOLATED) {
+		pp->isolated = flags.val & BR_ISOLATED;
+		do_flush = true;
+	}
+	if (do_flush) {
+		struct dsa_switch *ds = &priv->ds;
+		struct dsa_port *dp = dsa_to_port(ds, port);
+		struct net_device *bdev;
+
+		bdev = dsa_port_bridge_dev_get(dp);
+		if (bdev) {
+			u32 ports_mask;
+
+			ports_mask = dsa_bridge_ports(ds, bdev);
+			ports_mask |= priv->cpu_ports_mask;
+			res = yt92xx_bridge(priv, ports_mask);
+			if (res)
+				return res;
+		}
+	}
+
+	return 0;
+}
+
+static int yt92xx_dsa_port_bridge_flags(struct dsa_switch *ds, int port,
+					struct switchdev_brport_flags flags,
+					struct netlink_ext_ack *extack)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	if (dsa_is_cpu_port(ds, port))
+		return 0;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_bridge_flags(priv, port, flags);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_userport_standalone(struct yt92xx_priv *priv, int port)
+{
+	struct yt_port_mask port_mask;
+	enum yt_vlan_type type;
+	u32 ctrl;
+	int res;
+
+	ctrl = ~priv->cpu_ports_mask;
+	port_mask.portbits[0] = ctrl;
+	res = result_convert(yt_isolation_port_set(priv->chip_info->unit,
+						   port, port_mask));
+	if (res)
+		return res;
+
+	/* Turn off FDB learning to prevent FDB pollution */
+	res = result_convert(yt_l2_port_learn_en_set(priv->chip_info->unit,
+						     port, YT_DISABLE));
+	if (res)
+		return res;
+
+	/* Turn off VLAN awareness */
+	res = yt92xx_vlan_aware_set(priv, port, false);
+	if (res)
+		return res;
+
+	/* Unrelated since learning is off and all packets are trapped;
+	 * set it anyway
+	 */
+	type = YT_VLAN_TYPE_CVLAN;
+	res = result_convert(yt_vlan_port_igrpvid_set
+			(priv->chip_info->unit, type,
+			 port, YT92XX_VID_UNWARE));
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt92xx_isolate(struct yt92xx_priv *priv, int port)
+{
+	struct yt_port_mask port_mask;
+	u32 mask;
+	int res;
+
+	mask = BIT(port);
+	port_mask.portbits[0] = mask;
+	for (int i = 0; i < YT92XX_PORT_NUM; i++) {
+		if ((BIT(i) & priv->cpu_ports_mask) || i == port)
+			continue;
+
+		res = result_convert(yt_isolation_port_set
+				(priv->chip_info->unit, i, port_mask));
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static int yt92xx_bridge_leave(struct yt92xx_priv *priv, int port)
+{
+	int res;
+
+	res = yt92xx_userport_standalone(priv, port);
+	if (res)
+		return res;
+
+	res = yt92xx_isolate(priv, port);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static void yt92xx_dsa_port_bridge_leave(struct dsa_switch *ds, int port,
+					 struct dsa_bridge bridge)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct device *dev = to_device(priv);
+	int res;
+
+	if (dsa_is_cpu_port(ds, port))
+		return;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_bridge_leave(priv, port);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dev, "Failed to %s port %d: %i\n", "unbridge", port,
+			res);
+}
+
+static int yt92xx_userport_bridge(struct yt92xx_priv *priv, int port)
+{
+	int res;
+
+	res = result_convert(yt_l2_port_learn_en_set(priv->chip_info->unit,
+						     port, YT_DISABLE));
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt92xx_bridge_join(struct yt92xx_priv *priv, int port,
+			      u16 ports_mask)
+{
+	int res;
+
+	res = yt92xx_userport_bridge(priv, port);
+	if (res)
+		return res;
+
+	res = yt92xx_bridge(priv, ports_mask);
+	if (res)
+		return res;
+
+	return 0;
+}
+
+static int yt92xx_dsa_port_bridge_join(struct dsa_switch *ds, int port,
+				       struct dsa_bridge bridge,
+				       bool *tx_fwd_offload,
+				       struct netlink_ext_ack *extack)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u16 ports_mask;
+	int res;
+
+	if (dsa_is_cpu_port(ds, port))
+		return 0;
+
+	ports_mask = dsa_bridge_ports(ds, bridge.dev);
+	ports_mask |= priv->cpu_ports_mask;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_bridge_join(priv, port, ports_mask);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_dsa_port_mst_state_set(struct dsa_switch *ds, int port,
+					 const struct switchdev_mst_state *st)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	enum yt_stp_state state;
+	u32 ctrl;
+	int res;
+
+	switch (st->state) {
+	case BR_STATE_DISABLED:
+		state = STP_STATE_DISABLE;
+		break;
+	case BR_STATE_LISTENING:
+	case BR_STATE_LEARNING:
+		ctrl = STP_STATE_LEARN;
+		break;
+	case BR_STATE_FORWARDING:
+	default:
+		ctrl = STP_STATE_FORWARD;
+		break;
+	case BR_STATE_BLOCKING:
+		ctrl = STP_STATE_DISCARD;
+		break;
+	}
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert(yt_stp_state_set(priv->chip_info->unit,
+					      st->msti, port, state));
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_dsa_vlan_msti_set(struct dsa_switch *ds,
+				    struct dsa_bridge bridge,
+				    const struct switchdev_vlan_msti *msti)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+
+	int res;
+
+	if (priv->chip_info->ops->vlan_msti_set)
+		res = priv->chip_info->ops->vlan_msti_set(ds, bridge, msti);
+
+	return res;
+}
+
+static void yt92xx_dsa_port_stp_state_set(struct dsa_switch *ds, int port,
+					  u8 state)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	struct device *dev = to_device(priv);
+	enum yt_stp_state ctrl;
+	bool learning;
+	int res;
+
+	learning = false;
+	switch (state) {
+	case BR_STATE_DISABLED:
+		ctrl = STP_STATE_DISABLE;
+		break;
+	case BR_STATE_LISTENING:
+	case BR_STATE_LEARNING:
+		ctrl = STP_STATE_LEARN;
+		learning = dp->learning;
+		break;
+	case BR_STATE_FORWARDING:
+	default:
+		ctrl = STP_STATE_FORWARD;
+		learning = dp->learning;
+		break;
+	case BR_STATE_BLOCKING:
+		ctrl = STP_STATE_DISCARD;
+		break;
+	}
+
+	mutex_lock(&priv->reg_lock);
+	do {
+		res = result_convert(yt_stp_state_set(priv->chip_info->unit,
+						      0, port, ctrl));
+		if (res)
+			break;
+
+		res = result_convert(yt_l2_port_learn_en_set
+				(priv->chip_info->unit, port, learning));
+		if (res)
+			break;
+	} while (0);
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for",
+			port, res);
+}
+
+static int __maybe_unused
+yt92xx_dsa_port_get_default_prio(struct dsa_switch *ds, int port)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	enum yt_enable enable;
+	u8 pri;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert(yt_qos_intpri_portdefpri_get
+			(priv->chip_info->unit, port, &enable, &pri));
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		return res;
+
+	return pri;
+}
+
+static int __maybe_unused
+yt92xx_dsa_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	if (prio >= YT92XX_PRIO_NUM)
+		return -EINVAL;
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert(yt_qos_intpri_portdefpri_set
+			(priv->chip_info->unit, port, YT_ENABLE, prio));
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int __maybe_unused appprios_cmp(const void *a, const void *b)
+{
+	return ((const u8 *)b)[1] - ((const u8 *)a)[1];
+}
+
+static int __maybe_unused yt92xx_dsa_port_get_apptrust(struct dsa_switch *ds,
+						       int port, u8 *sel,
+						       int *nselp)
+{
+	struct yt_qos_intpri_prece prio_sel;
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u8 appprios[2][2] = {};
+	int nsel;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	prio_sel.flags = YT_QOS_SEL_PRECEDENCE_FLAG_ALL;
+	res = result_convert(yt_qos_intpri_sel_precedence_get
+			(priv->chip_info->unit, port, &prio_sel));
+	if (res)
+		goto unlock;
+
+	appprios[0][0] = IEEE_8021QAZ_APP_SEL_DSCP;
+	appprios[0][1] = prio_sel.dscp_pri;
+	appprios[1][0] = DCB_APP_SEL_PCP;
+	appprios[1][1] = prio_sel.cpri;
+	sort(appprios, ARRAY_SIZE(appprios), sizeof(appprios[0]), appprios_cmp,
+	     NULL);
+
+	nsel = 0;
+	for (int i = 0; i < ARRAY_SIZE(appprios) && appprios[i][1]; i++) {
+		sel[nsel] = appprios[i][0];
+		nsel++;
+	}
+	*nselp = nsel;
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int __maybe_unused yt92xx_dsa_port_set_apptrust(struct dsa_switch *ds,
+						       int port, const u8 *sel,
+						       int nsel)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	struct yt_qos_intpri_prece prio_sel;
+	struct device *dev = to_device(priv);
+	int res;
+
+	if (nsel > YT92XX_APP_SEL_NUM)
+		return -EINVAL;
+
+	memset(&prio_sel, 0, sizeof(struct yt_qos_intpri_prece));
+	prio_sel.flags = YT_QOS_SEL_PRECEDENCE_FLAG_ALL;
+
+	for (int i = 0; i < nsel; i++) {
+		switch (sel[i]) {
+		case IEEE_8021QAZ_APP_SEL_DSCP:
+			prio_sel.dscp_pri = 7 - i;
+			break;
+		case DCB_APP_SEL_PCP:
+			prio_sel.cpri = 7 - i;
+			prio_sel.spri = 7 - i;
+			break;
+		default:
+			dev_err(dev,
+				"Invalid apptrust selector (at %d-th). Supported: dscp, pcp\n",
+				i + 1);
+			return -EOPNOTSUPP;
+		}
+	}
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert(yt_qos_intpri_sel_precedence_set
+			(priv->chip_info->unit, port, &prio_sel));
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static enum dsa_tag_protocol
+yt92xx_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
+			    enum dsa_tag_protocol m)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	enum dsa_tag_protocol tag;
+
+	if (priv->chip_info->mode == YT9215) {
+		tag =  DSA_TAG_PROTO_YT921X;
+	} else {
+		if (priv->chip_info->cpu_tag == CPU_TAG_4B)
+			tag = DSA_TAG_PROTO_YT922X_4B;
+		else
+			tag = DSA_TAG_PROTO_YT922X_8B;
+	}
+	return tag;
+}
+
+static int yt92xx_port_setup(struct yt92xx_priv *priv, int port)
+{
+	struct yt_qos_intpri_prece pri;
+	struct dsa_switch *ds = &priv->ds;
+	struct yt_port_mask mask;
+	int res;
+
+	res = yt92xx_userport_standalone(priv, port);
+	if (res)
+		return res;
+
+	/* Clear prio order (even if DCB is not enabled) to avoid unsolicited
+	 * priorities
+	 */
+	memset(&pri, 0, sizeof(struct yt_qos_intpri_prece));
+	pri.flags = YT_QOS_SEL_PRECEDENCE_FLAG_ALL;
+	res = result_convert(yt_qos_intpri_sel_precedence_set
+			(priv->chip_info->unit, port, &pri));
+	if (res)
+		return res;
+
+	if (dsa_is_cpu_port(ds, port)) {
+		/* Egress of CPU port is supposed to be completely controlled
+		 * via tagging, so set to oneway isolated (drop all packets
+		 * without tag).
+		 */
+		mask.portbits[0] = ~(u32)0;
+		res = result_convert(yt_isolation_port_set
+				(priv->chip_info->unit, port, mask));
+		if (res)
+			return res;
+
+		/* To simplify FDB "isolation" simulation, we also disable
+		 * learning on the CPU port, and let software identify packets
+		 * towarding CPU (either trapped or a static FDB entry is
+		 * matched, no matter which bridge that entry is for), which is
+		 * already done by yt921x_userport_standalone(). As a result,
+		 * VLAN-awareness becomes unrelated on the CPU port (set to
+		 * VLAN-unaware by the way).
+		 */
+	}
+
+	return 0;
+}
+
+static int yt92xx_dsa_port_setup(struct dsa_switch *ds, int port)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = yt92xx_port_setup(priv, port);
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static void yt92xx_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
+					struct phylink_config *config)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+
+	mutex_lock(&priv->reg_lock);
+	if (priv->chip_info->ops->mac_caps)
+		priv->chip_info->ops->mac_caps(ds, port, config);
+	mutex_unlock(&priv->reg_lock);
+}
+
+/* Not "port" - DSCP mapping is global */
+static int __maybe_unused yt92xx_dsa_port_get_dscp_prio(struct dsa_switch *ds,
+							int port, u8 dscp)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u8 pri;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert(yt_qos_intpri_dscp_map_get
+			(priv->chip_info->unit, dscp, &pri));
+	mutex_unlock(&priv->reg_lock);
+
+	if (res)
+		return res;
+
+	return pri;
+}
+
+static int __maybe_unused yt92xx_dsa_port_del_dscp_prio(struct dsa_switch *ds,
+							int port, u8 dscp,
+							u8 prio)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	u8 pri;
+	int res;
+
+	mutex_lock(&priv->reg_lock);
+	/* During a "dcb app replace" command, the new app table entry will be
+	 * added first, then the old one will be deleted. But the hardware only
+	 * supports one QoS class per DSCP value (duh), so if we blindly delete
+	 * the app table entry for this DSCP value, we end up deleting the
+	 * entry with the new priority. Avoid that by checking whether user
+	 * space wants to delete the priority which is currently configured, or
+	 * something else which is no longer current.
+	 */
+	res = result_convert(yt_qos_intpri_dscp_map_get
+			(priv->chip_info->unit, dscp, &pri));
+	if (res)
+		goto unlock;
+	if (!res && pri == prio)
+		res = result_convert(yt_qos_intpri_dscp_map_set
+				(priv->chip_info->unit,
+				 dscp, IEEE8021Q_TT_BK));
+
+unlock:
+	mutex_unlock(&priv->reg_lock);
+	return res;
+}
+
+static int __maybe_unused yt92xx_dsa_port_add_dscp_prio(struct dsa_switch *ds,
+							int port, u8 dscp,
+							u8 prio)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	if (prio >= YT92XX_PRIO_NUM)
+		return -EINVAL;
+
+	mutex_lock(&priv->reg_lock);
+	res = result_convert(yt_qos_intpri_dscp_map_set
+			(priv->chip_info->unit, dscp, prio));
+	mutex_unlock(&priv->reg_lock);
+
+	return res;
+}
+
+static int yt92xx_dsa_setup(struct dsa_switch *ds)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	if (priv->chip_info->ops->dsa_setup)
+		res = priv->chip_info->ops->dsa_setup(ds);
+
+	return res;
+}
+
+static int yt92xx_dsa_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int val;
+
+	/* add read, write lock */
+	if (yt92xx_port_is_internal(priv, port)) {
+		if (priv->mbus_int && priv->mbus_int->read)
+			val = (int)priv->mbus_int->read(priv->mbus_int,
+					port, regnum);
+	} else {
+		if (priv->mbus_ext && priv->mbus_ext->read)
+			val = (int)priv->mbus_ext->read(priv->mbus_ext,
+					port, regnum);
+	}
+
+	return val;
+}
+
+static int yt92xx_dsa_phy_write(struct dsa_switch *ds, int port,
+				int regnum, u16 val)
+{
+	struct yt92xx_priv *priv = to_yt92xx_priv(ds);
+	int res;
+
+	/* add read, write lock */
+	if (yt92xx_port_is_internal(priv, port)) {
+		if (priv->mbus_int && priv->mbus_int->write)
+			res = (int)priv->mbus_int->write(priv->mbus_int, port,
+					regnum, val);
+	} else {
+		if (priv->mbus_ext && priv->mbus_ext->write)
+			res = (int)priv->mbus_ext->write(priv->mbus_ext, port,
+					regnum, val);
+	}
+
+	return res;
+}
+
+static const struct dsa_switch_ops yt92xx_dsa_ops = {
+	/* mib */
+	.get_strings = yt92xx_dsa_get_strings,
+	.get_ethtool_stats = yt92xx_dsa_get_ethtool_stats,
+	.get_sset_count = yt92xx_dsa_get_sset_count,
+	.get_eth_mac_stats = yt92xx_dsa_get_eth_mac_stats,
+	.get_eth_ctrl_stats = yt92xx_dsa_get_eth_ctrl_stats,
+	.get_rmon_stats = yt92xx_dsa_get_rmon_stats,
+	.get_stats64 = yt92xx_dsa_get_stats64,
+	.get_pause_stats = yt92xx_dsa_get_pause_stats,
+	/* eee */
+	.support_eee = dsa_supports_eee,
+	.set_mac_eee = yt92xx_dsa_set_mac_eee,
+	/* mtu */
+	.port_change_mtu = yt92xx_dsa_port_change_mtu,
+	.port_max_mtu = yt92xx_dsa_port_max_mtu,
+	/* rate */
+	.port_policer_del = yt92xx_dsa_port_policer_del,
+	.port_policer_add = yt92xx_dsa_port_policer_add,
+	.port_setup_tc		= yt92xx_dsa_port_setup_tc,
+	/* acl */
+	.cls_flower_del		= yt92xx_dsa_cls_flower_del,
+	.cls_flower_add		= yt92xx_dsa_cls_flower_add,
+	/* hsr */
+	.port_hsr_leave = dsa_port_simple_hsr_leave,
+	.port_hsr_join = dsa_port_simple_hsr_join,
+	/* mirror */
+	.port_mirror_del = yt92xx_dsa_port_mirror_del,
+	.port_mirror_add = yt92xx_dsa_port_mirror_add,
+	/* lag */
+	.port_lag_leave = yt92xx_dsa_port_lag_leave,
+	.port_lag_join = yt92xx_dsa_port_lag_join,
+	/* fdb */
+	.port_fdb_dump = yt92xx_dsa_port_fdb_dump,
+	.port_fast_age = yt92xx_dsa_port_fdb_flush,
+	.set_ageing_time = yt92xx_dsa_set_ageing_time,
+	.port_fdb_del = yt92xx_dsa_port_fdb_del,
+	.port_fdb_add = yt92xx_dsa_port_fdb_add,
+	.port_mdb_del = yt92xx_dsa_port_mdb_del,
+	.port_mdb_add = yt92xx_dsa_port_mdb_add,
+	/* vlan */
+	.port_vlan_filtering = yt92xx_dsa_port_vlan_filtering,
+	.port_vlan_del = yt92xx_dsa_port_vlan_del,
+	.port_vlan_add = yt92xx_dsa_port_vlan_add,
+	/* bridge */
+	.port_pre_bridge_flags = yt92xx_dsa_port_pre_bridge_flags,
+	.port_bridge_flags = yt92xx_dsa_port_bridge_flags,
+	.port_bridge_leave = yt92xx_dsa_port_bridge_leave,
+	.port_bridge_join = yt92xx_dsa_port_bridge_join,
+	/* mst */
+	.port_mst_state_set = yt92xx_dsa_port_mst_state_set,
+	.vlan_msti_set = yt92xx_dsa_vlan_msti_set,
+	.port_stp_state_set = yt92xx_dsa_port_stp_state_set,
+	/* phy read */
+	.phy_read = yt92xx_dsa_phy_read,
+	.phy_write = yt92xx_dsa_phy_write,
+#if IS_ENABLED(CONFIG_DCB)
+	/* dcb */
+	.port_get_default_prio = yt92xx_dsa_port_get_default_prio,
+	.port_set_default_prio = yt92xx_dsa_port_set_default_prio,
+	.port_get_apptrust = yt92xx_dsa_port_get_apptrust,
+	.port_set_apptrust = yt92xx_dsa_port_set_apptrust,
+#endif
+	/* port */
+	.get_tag_protocol = yt92xx_dsa_get_tag_protocol,
+	.phylink_get_caps = yt92xx_dsa_phylink_get_caps,
+	.port_setup = yt92xx_dsa_port_setup,
+#if IS_ENABLED(CONFIG_DCB)
+	/* dscp */
+	.port_get_dscp_prio = yt92xx_dsa_port_get_dscp_prio,
+	.port_del_dscp_prio = yt92xx_dsa_port_del_dscp_prio,
+	.port_add_dscp_prio = yt92xx_dsa_port_add_dscp_prio,
+#endif
+	/* chip */
+	.setup = yt92xx_dsa_setup,
+};
+
+static void yt92xx_mdio_shutdown(struct mdio_device *mdiodev)
+{
+	struct yt92xx_priv *priv = mdiodev_get_drvdata(mdiodev);
+
+	if (!priv)
+		return;
+
+	dsa_switch_shutdown(&priv->ds);
+}
+
+static void yt92xx_mdio_remove(struct mdio_device *mdiodev)
+{
+	struct yt92xx_priv *priv = mdiodev_get_drvdata(mdiodev);
+
+	if (!priv)
+		return;
+
+	/* chrge */
+	for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0;) {
+		struct yt92xx_port *pp = &priv->ports[i];
+
+		disable_delayed_work_sync(&pp->mib_read);
+	}
+
+	dsa_unregister_switch(&priv->ds);
+
+#ifdef CONFIG_MOTORCOMM_YT921X
+	for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) {
+		struct yt921x_acl_blk *aclblk = priv->acl_blks[i];
+
+		if (!aclblk)
+			continue;
+		for (unsigned int j = 0; j < ARRAY_SIZE(aclblk->rules); j++) {
+			struct yt921x_acl_rule *aclrule = aclblk->rules[j];
+
+			if (!aclrule)
+				continue;
+			kvfree(aclrule);
+		}
+		kvfree(aclblk);
+	}
+#endif
+	mutex_destroy(&priv->reg_lock);
+}
+
+static int yt92xx_mdio_probe(struct mdio_device *mdiodev)
+{
+	const struct yt92xx_chip_info *chip_info = NULL;
+	struct device *dev = &mdiodev->dev;
+	struct device_node *np = dev->of_node;
+	struct yt92xx_reg_mdio *mdio;
+	struct yt92xx_priv *priv;
+	struct dsa_switch *ds;
+
+	if (!np)
+		return -EINVAL;
+
+	if (np)
+		chip_info = of_device_get_match_data(dev);
+
+	if (!chip_info)
+		return -EINVAL;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);
+	if (!mdio)
+		return -ENOMEM;
+
+	mdio->bus = mdiodev->bus;
+	mdio->addr = mdiodev->addr;
+	mdio->switchid = chip_info->switchid;
+	if (chip_info->unit < YT92XX_MAX_SWITCHES)
+		yt92xx_reg_mdios[chip_info->unit] = mdio;
+	else
+		return -EINVAL;
+
+	mutex_init(&priv->reg_lock);
+	priv->reg_ctx = mdio;
+	priv->chip_info = chip_info;
+
+	/* double check */
+	for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
+		struct yt92xx_port *pp = &priv->ports[i];
+
+		pp->index = i;
+		INIT_DELAYED_WORK(&pp->mib_read, yt92xx_poll_mib);
+	}
+
+	ds = &priv->ds;
+	ds->dev = dev;
+	ds->assisted_learning_on_cpu_port = true;
+	ds->dscp_prio_mapping_is_global = true;
+	ds->priv = priv;
+	ds->ops = &yt92xx_dsa_ops;
+	ds->ageing_time_min = chip_info->ageing_time_min;
+	ds->ageing_time_max = chip_info->ageing_time_max;
+	ds->phylink_mac_ops = chip_info->mac_ops;
+	ds->num_lag_ids = chip_info->max_lag_groups;
+	ds->num_ports = chip_info->max_ports;
+
+	mdiodev_set_drvdata(mdiodev, priv);
+
+	return dsa_register_switch(ds);
+}
+
+static const struct of_device_id yt92xx_of_match[] = {
+	{
+		.compatible = "motorcomm,yt9215,8bytes",
+		.data = &yt92xx_chip_info[YT9215_8B],
+	},
+	{
+		.compatible = "motorcomm,yt9224,4bytes",
+		.data = &yt92xx_chip_info[YT9224_4B],
+	},
+	{
+		.compatible = "motorcomm,yt9224,8bytes",
+		.data = &yt92xx_chip_info[YT9224_8B],
+	},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, yt92xx_of_match);
+
+static struct mdio_driver yt92xx_mdio_driver = {
+	.probe = yt92xx_mdio_probe,
+	.remove = yt92xx_mdio_remove,
+	.shutdown = yt92xx_mdio_shutdown,
+	.mdiodrv.driver = {
+		.name = YT_NAME,
+		.of_match_table = yt92xx_of_match,
+	},
+};
+
+mdio_module_driver(yt92xx_mdio_driver);
+
+MODULE_AUTHOR("Kyle Switch <kyle.switch@motor-comm.com>");
+MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>");
+MODULE_DESCRIPTION("Driver for Motorcomm YT921x and YT922x Switch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/motorcomm/yt92xx.h b/drivers/net/dsa/motorcomm/yt92xx.h
new file mode 100644
index 000000000000..326ba32ddc15
--- /dev/null
+++ b/drivers/net/dsa/motorcomm/yt92xx.h
@@ -0,0 +1,1218 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025 David Yang
+ * Copyright (c) 2026 Kyle Switch
+ */
+
+#ifndef __YT92XX_H
+#define __YT92XX_H
+
+#include <net/dsa.h>
+/* Common */
+#define YT92XX_LAG_HASH_L4_SPORT BIT(7)
+#define YT92XX_LAG_HASH_L4_DPORT BIT(6)
+#define YT92XX_LAG_HASH_IP_PROTO BIT(5)
+#define YT92XX_LAG_HASH_IP_SRC BIT(4)
+#define YT92XX_LAG_HASH_IP_DST BIT(3)
+#define YT92XX_LAG_HASH_MAC_SA BIT(2)
+#define YT92XX_LAG_HASH_MAC_DA BIT(1)
+#define YT92XX_LAG_HASH_SRC_PORT BIT(0)
+#define YT92XX_MIRROR_PORT_M GENMASK(3, 0)
+#define YT92XX_MIRROR_PORT(x) FIELD_PREP(YT92XX_MIRROR_PORT_M, (x))
+/* fdb */
+#define YT92XX_FDB_NUM 4096
+/* VLAN */
+#define YT92XX_VID_UNWARE 4095
+/* STP */
+#define YT92XX_MSTI_NUM 16
+/* QoS */
+#define YT92XX_PRIO_NUM 8
+/* mdio */
+#define YT92XX_SMI_SWITCHID_M GENMASK(3, 2)
+#define YT92XX_SMI_SWITCHID(x) FIELD_PREP(YT92XX_SMI_SWITCHID_M, (x))
+#define YT92XX_SMI_AD BIT(1)
+#define YT92XX_SMI_ADDR 0
+#define YT92XX_SMI_DATA YT92XX_SMI_AD
+#define YT92XX_SMI_RW BIT(0)
+#define YT92XX_SMI_WRITE 0
+#define YT92XX_SMI_READ YT92XX_SMI_RW
+
+/* major */
+#define YT9215_MAJOR 0x9002
+#define YT9218_MAJOR 0x9001
+#define YT9224_MAJOR 0x9004
+#define YT9228_MAJOR 0x9004
+
+/* switch count */
+#define YT92XX_MAX_SWITCHES 4
+/* yt921x */
+#if IS_ENABLED(CONFIG_MOTORCOMM_YT921X)
+#define YT921X_SWITCHID_NUM 4
+
+#define YT921X_RST 0x80000
+#define YT921X_RST_HW BIT(31)
+#define YT921X_RST_SW BIT(1)
+#define YT921X_FUNC 0x80004
+#define YT921X_FUNC_METER BIT(4)
+#define YT921X_FUNC_ACL			BIT(2)
+#define YT921X_FUNC_MIB BIT(1)
+#define YT921X_CHIP_ID 0x80008
+#define YT921X_CHIP_ID_MAJOR GENMASK(31, 16)
+#define YT921X_EXT_CPU_PORT 0x8000c
+#define YT921X_EXT_CPU_PORT_TAG_EN BIT(15)
+#define YT921X_EXT_CPU_PORT_PORT_EN BIT(14)
+#define YT921X_EXT_CPU_PORT_PORT_M GENMASK(3, 0)
+#define YT921X_EXT_CPU_PORT_PORT(x) FIELD_PREP(YT921X_EXT_CPU_PORT_PORT_M, (x))
+#define YT921X_CPU_TAG_TPID 0x80010
+#define YT921X_CPU_TAG_TPID_TPID_M GENMASK(15, 0)
+/* Same as ETH_P_YT921X, but this represents the true HW default, while the
+ * former is a local convention chosen by us.
+ */
+#define YT921X_CPU_TAG_TPID_TPID_DEFAULT 0x9988
+#define YT921X_PVID_SEL 0x80014
+#define YT921X_PVID_SEL_SVID_PORTn(port) BIT(port)
+#define YT921X_SERDES_CTRL 0x80028
+#define YT921X_SERDES_CTRL_PORTn_TEST(port) BIT((port) - 3)
+#define YT921X_SERDES_CTRL_PORTn(port) BIT((port) - 8)
+#define YT921X_IO_LEVEL 0x80030
+#define YT9215_IO_LEVEL_NORMAL_M GENMASK(5, 4)
+#define YT9215_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9215_IO_LEVEL_NORMAL_M, (x))
+#define YT9215_IO_LEVEL_NORMAL_3V3 YT9215_IO_LEVEL_NORMAL(0)
+#define YT9215_IO_LEVEL_NORMAL_1V8 YT9215_IO_LEVEL_NORMAL(3)
+#define YT9215_IO_LEVEL_RGMII1_M GENMASK(3, 2)
+#define YT9215_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII1_M, (x))
+#define YT9215_IO_LEVEL_RGMII1_3V3 YT9215_IO_LEVEL_RGMII1(0)
+#define YT9215_IO_LEVEL_RGMII1_2V5 YT9215_IO_LEVEL_RGMII1(1)
+#define YT9215_IO_LEVEL_RGMII1_1V8 YT9215_IO_LEVEL_RGMII1(2)
+#define YT9215_IO_LEVEL_RGMII0_M GENMASK(1, 0)
+#define YT9215_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII0_M, (x))
+#define YT9215_IO_LEVEL_RGMII0_3V3 YT9215_IO_LEVEL_RGMII0(0)
+#define YT9215_IO_LEVEL_RGMII0_2V5 YT9215_IO_LEVEL_RGMII0(1)
+#define YT9215_IO_LEVEL_RGMII0_1V8 YT9215_IO_LEVEL_RGMII0(2)
+#define YT9218_IO_LEVEL_RGMII1_M GENMASK(5, 4)
+#define YT9218_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII1_M, (x))
+#define YT9218_IO_LEVEL_RGMII1_3V3 YT9218_IO_LEVEL_RGMII1(0)
+#define YT9218_IO_LEVEL_RGMII1_2V5 YT9218_IO_LEVEL_RGMII1(1)
+#define YT9218_IO_LEVEL_RGMII1_1V8 YT9218_IO_LEVEL_RGMII1(2)
+#define YT9218_IO_LEVEL_RGMII0_M GENMASK(3, 2)
+#define YT9218_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII0_M, (x))
+#define YT9218_IO_LEVEL_RGMII0_3V3 YT9218_IO_LEVEL_RGMII0(0)
+#define YT9218_IO_LEVEL_RGMII0_2V5 YT9218_IO_LEVEL_RGMII0(1)
+#define YT9218_IO_LEVEL_RGMII0_1V8 YT9218_IO_LEVEL_RGMII0(2)
+#define YT9218_IO_LEVEL_NORMAL_M GENMASK(1, 0)
+#define YT9218_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9218_IO_LEVEL_NORMAL_M, (x))
+#define YT9218_IO_LEVEL_NORMAL_3V3 YT9218_IO_LEVEL_NORMAL(0)
+#define YT9218_IO_LEVEL_NORMAL_1V8 YT9218_IO_LEVEL_NORMAL(3)
+#define YT921X_MAC_ADDR_HI2 0x80080
+#define YT921X_MAC_ADDR_LO4 0x80084
+#define YT921X_SERDESn(port) (0x8008c + 4 * ((port) - 8))
+#define YT921X_SERDES_MODE_M GENMASK(9, 7)
+#define YT921X_SERDES_MODE(x) FIELD_PREP(YT921X_SERDES_MODE_M, (x))
+#define YT921X_SERDES_MODE_SGMII YT921X_SERDES_MODE(0)
+#define YT921X_SERDES_MODE_REVSGMII YT921X_SERDES_MODE(1)
+#define YT921X_SERDES_MODE_1000BASEX YT921X_SERDES_MODE(2)
+#define YT921X_SERDES_MODE_100BASEX YT921X_SERDES_MODE(3)
+#define YT921X_SERDES_MODE_2500BASEX YT921X_SERDES_MODE(4)
+#define YT921X_SERDES_RX_PAUSE BIT(6)
+#define YT921X_SERDES_TX_PAUSE BIT(5)
+#define YT921X_SERDES_LINK BIT(4) /* force link */
+#define YT921X_SERDES_DUPLEX_FULL BIT(3)
+#define YT921X_SERDES_SPEED_M GENMASK(2, 0)
+#define YT921X_SERDES_SPEED(x) FIELD_PREP(YT921X_SERDES_SPEED_M, (x))
+#define YT921X_SERDES_SPEED_10 YT921X_SERDES_SPEED(0)
+#define YT921X_SERDES_SPEED_100 YT921X_SERDES_SPEED(1)
+#define YT921X_SERDES_SPEED_1000 YT921X_SERDES_SPEED(2)
+#define YT921X_SERDES_SPEED_10000 YT921X_SERDES_SPEED(3)
+#define YT921X_SERDES_SPEED_2500 YT921X_SERDES_SPEED(4)
+#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port))
+#define YT921X_PORT_CTRL_PAUSE_AN BIT(10)
+#define YT921X_PORTn_STATUS(port) (0x80200 + 4 * (port))
+#define YT921X_PORT_LINK BIT(9) /* CTRL: auto negotiation */
+#define YT921X_PORT_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */
+#define YT921X_PORT_DUPLEX_FULL BIT(7)
+#define YT921X_PORT_RX_PAUSE BIT(6)
+#define YT921X_PORT_TX_PAUSE BIT(5)
+#define YT921X_PORT_RX_MAC_EN BIT(4)
+#define YT921X_PORT_TX_MAC_EN BIT(3)
+#define YT921X_PORT_SPEED_M GENMASK(2, 0)
+#define YT921X_PORT_SPEED(x) FIELD_PREP(YT921X_PORT_SPEED_M, (x))
+#define YT921X_PORT_SPEED_10 YT921X_PORT_SPEED(0)
+#define YT921X_PORT_SPEED_100 YT921X_PORT_SPEED(1)
+#define YT921X_PORT_SPEED_1000 YT921X_PORT_SPEED(2)
+#define YT921X_PORT_SPEED_10000 YT921X_PORT_SPEED(3)
+#define YT921X_PORT_SPEED_2500 YT921X_PORT_SPEED(4)
+#define YT921X_PON_STRAP_FUNC 0x80320
+#define YT921X_PON_STRAP_VAL 0x80324
+#define YT921X_PON_STRAP_CAP 0x80328
+#define YT921X_PON_STRAP_EEE BIT(16)
+#define YT921X_PON_STRAP_LOOP_DETECT BIT(7)
+#define YT921X_MDIO_POLLINGn(port) (0x80364 + 4 * ((port) - 8))
+#define YT921X_MDIO_POLLING_DUPLEX_FULL BIT(4)
+#define YT921X_MDIO_POLLING_LINK BIT(3)
+#define YT921X_MDIO_POLLING_SPEED_M GENMASK(2, 0)
+#define YT921X_MDIO_POLLING_SPEED(x) \
+	FIELD_PREP(YT921X_MDIO_POLLING_SPEED_M, (x))
+#define YT921X_MDIO_POLLING_SPEED_10 YT921X_MDIO_POLLING_SPEED(0)
+#define YT921X_MDIO_POLLING_SPEED_100 YT921X_MDIO_POLLING_SPEED(1)
+#define YT921X_MDIO_POLLING_SPEED_1000 YT921X_MDIO_POLLING_SPEED(2)
+#define YT921X_MDIO_POLLING_SPEED_10000 YT921X_MDIO_POLLING_SPEED(3)
+#define YT921X_MDIO_POLLING_SPEED_2500 YT921X_MDIO_POLLING_SPEED(4)
+#define YT921X_SENSOR 0x8036c
+#define YT921X_SENSOR_TEMP BIT(18)
+#define YT921X_TEMP 0x80374
+#define YT921X_CHIP_MODE 0x80388
+#define YT921X_CHIP_MODE_MODE GENMASK(1, 0)
+#define YT921X_XMII_CTRL 0x80394
+#define YT921X_XMII_CTRL_PORTn(port) BIT(9 - (port)) /* Yes, it's reversed */
+#define YT921X_XMIIn(port) (0x80400 + 8 * ((port) - 8))
+#define YT921X_XMII_MODE_M GENMASK(31, 29)
+#define YT921X_XMII_MODE(x) FIELD_PREP(YT921X_XMII_MODE_M, (x))
+#define YT921X_XMII_MODE_MII YT921X_XMII_MODE(0)
+#define YT921X_XMII_MODE_REVMII YT921X_XMII_MODE(1)
+#define YT921X_XMII_MODE_RMII YT921X_XMII_MODE(2)
+#define YT921X_XMII_MODE_REVRMII YT921X_XMII_MODE(3)
+#define YT921X_XMII_MODE_RGMII YT921X_XMII_MODE(4)
+#define YT921X_XMII_MODE_DISABLE YT921X_XMII_MODE(5)
+#define YT921X_XMII_LINK BIT(19) /* force link */
+#define YT921X_XMII_EN BIT(18)
+#define YT921X_XMII_SOFT_RST BIT(17)
+#define YT921X_XMII_RGMII_TX_DELAY_150PS_M GENMASK(16, 13)
+#define YT921X_XMII_RGMII_TX_DELAY_150PS(x) \
+	FIELD_PREP(YT921X_XMII_RGMII_TX_DELAY_150PS_M, (x))
+#define YT921X_XMII_TX_CLK_IN BIT(11)
+#define YT921X_XMII_RX_CLK_IN BIT(10)
+#define YT921X_XMII_RGMII_TX_DELAY_2NS BIT(8)
+#define YT921X_XMII_RGMII_TX_CLK_OUT BIT(7)
+#define YT921X_XMII_RGMII_RX_DELAY_150PS_M GENMASK(6, 3)
+#define YT921X_XMII_RGMII_RX_DELAY_150PS(x) \
+	FIELD_PREP(YT921X_XMII_RGMII_RX_DELAY_150PS_M, (x))
+#define YT921X_XMII_RMII_PHY_TX_CLK_OUT BIT(2)
+#define YT921X_XMII_REVMII_TX_CLK_OUT BIT(1)
+#define YT921X_XMII_REVMII_RX_CLK_OUT BIT(0)
+
+#define YT921X_MACn_FRAME(port) (0x81008 + 0x1000 * (port))
+#define YT921X_MAC_FRAME_SIZE_M GENMASK(21, 8)
+#define YT921X_MAC_FRAME_SIZE(x) FIELD_PREP(YT921X_MAC_FRAME_SIZE_M, (x))
+
+#define YT921X_EEEn_VAL(port) (0xa0000 + 0x40 * (port))
+#define YT921X_EEE_VAL_DATA BIT(1)
+
+#define YT921X_EEE_CTRL 0xb0000
+#define YT921X_EEE_CTRL_ENn(port) BIT(port)
+
+#define YT921X_MIB_CTRL 0xc0004
+#define YT921X_MIB_CTRL_CLEAN BIT(30)
+#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3)
+#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
+#define YT921X_MIB_CTRL_ONE_PORT BIT(1)
+#define YT921X_MIB_CTRL_ALL_PORT BIT(0)
+#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port))
+#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x))
+#define YT921X_MIB_DATA_RX_BROADCAST 0x00
+#define YT921X_MIB_DATA_RX_PAUSE 0x04
+#define YT921X_MIB_DATA_RX_MULTICAST 0x08
+#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c
+
+#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10
+#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14
+#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18
+#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20
+#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24
+#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28
+#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30
+#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34
+/* 0x38: unused */
+#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c
+
+/* 0x40: 64 bytes */
+#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44
+/* 0x48: 64 bytes */
+#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c
+
+#define YT921X_MIB_DATA_RX_DROPPED 0x50
+#define YT921X_MIB_DATA_TX_BROADCAST 0x54
+#define YT921X_MIB_DATA_TX_PAUSE 0x58
+#define YT921X_MIB_DATA_TX_MULTICAST 0x5c
+
+#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60
+#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64
+#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68
+#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c
+
+#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70
+#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74
+#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78
+#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c
+
+/* 0x80: unused */
+#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84
+/* 0x88: 64 bytes */
+#define YT921X_MIB_DATA_TX_COLLISION 0x8c
+
+#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90
+#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94
+#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98
+#define YT921X_MIB_DATA_TX_PKT 0x9c
+
+#define YT921X_MIB_DATA_TX_DEFERRED 0xa0
+#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4
+#define YT921X_MIB_DATA_RX_OAM 0xa8
+#define YT921X_MIB_DATA_TX_OAM 0xac
+
+#define YT921X_EDATA_CTRL 0xe0000
+#define YT921X_EDATA_CTRL_ADDR_M GENMASK(15, 8)
+#define YT921X_EDATA_CTRL_ADDR(x) FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x))
+#define YT921X_EDATA_CTRL_OP_M GENMASK(3, 0)
+#define YT921X_EDATA_CTRL_OP(x) FIELD_PREP(YT921X_EDATA_CTRL_OP_M, (x))
+#define YT921X_EDATA_CTRL_READ YT921X_EDATA_CTRL_OP(5)
+#define YT921X_EDATA_DATA 0xe0004
+#define YT921X_EDATA_DATA_DATA_M GENMASK(31, 24)
+#define YT921X_EDATA_DATA_STATUS_M GENMASK(3, 0)
+#define YT921X_EDATA_DATA_STATUS(x) FIELD_PREP(YT921X_EDATA_DATA_STATUS_M, (x))
+#define YT921X_EDATA_DATA_IDLE YT921X_EDATA_DATA_STATUS(3)
+#define YT921X_SYS_CLK 0xe0040
+#define YT921X_SYS_CLK_SEL_M GENMASK(1, 0) /* unknown: 167M */
+#define YT9215_SYS_CLK_125M 0
+#define YT9218_SYS_CLK_167M 0
+#define YT921X_SYS_CLK_143M 1
+
+#define YT92XX_EXT_MBUS_OP		0x6a000
+#define YT92XX_INT_MBUS_OP		0xf0000
+#define YT92XX_MBUS_OP_START			BIT(0)
+#define YT92XX_EXT_MBUS_CTRL		0x6a004
+#define YT92XX_INT_MBUS_CTRL		0xf0004
+#define YT92XX_MBUS_CTRL_PORT_M		GENMASK(25, 21)
+#define YT92XX_MBUS_CTRL_PORT(x)			FIELD_PREP(YT92XX_MBUS_CTRL_PORT_M, (x))
+#define YT92XX_MBUS_CTRL_REG_M			GENMASK(20, 16)
+#define YT92XX_MBUS_CTRL_REG(x)			FIELD_PREP(YT92XX_MBUS_CTRL_REG_M, (x))
+#define YT92XX_MBUS_CTRL_TYPE_M		GENMASK(11, 8)  /* wild guess */
+#define YT92XX_MBUS_CTRL_TYPE(x)			FIELD_PREP(YT92XX_MBUS_CTRL_TYPE_M, (x))
+#define YT92XX_MBUS_CTRL_TYPE_C22			YT92XX_MBUS_CTRL_TYPE(4)
+#define YT92XX_MBUS_CTRL_OP_M			GENMASK(3, 2)  /* wild guess */
+#define YT92XX_MBUS_CTRL_OP(x)			FIELD_PREP(YT92XX_MBUS_CTRL_OP_M, (x))
+#define YT92XX_MBUS_CTRL_WRITE			YT92XX_MBUS_CTRL_OP(1)
+#define YT92XX_MBUS_CTRL_READ				YT92XX_MBUS_CTRL_OP(2)
+#define YT92XX_EXT_MBUS_DOUT		0x6a008
+#define YT92XX_INT_MBUS_DOUT		0xf0008
+#define YT92XX_EXT_MBUS_DIN		0x6a00c
+#define YT92XX_INT_MBUS_DIN		0xf000c
+
+#define YT921X_PORTn_EGR(port) (0x100000 + 4 * (port))
+#define YT921X_PORT_EGR_TPID_CTAG_M GENMASK(5, 4)
+#define YT921X_PORT_EGR_TPID_CTAG(x) \
+	FIELD_PREP(YT921X_PORT_EGR_TPID_CTAG_M, (x))
+#define YT921X_PORT_EGR_TPID_STAG_M GENMASK(3, 2)
+#define YT921X_PORT_EGR_TPID_STAG(x) \
+	FIELD_PREP(YT921X_PORT_EGR_TPID_STAG_M, (x))
+#define YT921X_TPID_EGRn(x) (0x100300 + 4 * (x)) /* [0, 3] */
+#define YT921X_TPID_EGR_TPID_M GENMASK(15, 0)
+
+#define YT92XX_IPM_DSCPn(n) (0x180000 + 4 * (n)) /* Internal Priority Map */
+#define YT92XX_IPM_PCPn(map, dei, pcp) \
+	(0x180100 + 4 * (16 * (map) + 8 * (dei) + (pcp)))
+#define YT92XX_IPM_PRIO_M GENMASK(4, 2)
+#define YT92XX_IPM_PRIO(x) FIELD_PREP(YT92XX_IPM_PRIO_M, (x))
+#define YT92XX_IPM_COLOR_M GENMASK(1, 0)
+#define YT92XX_IPM_COLOR(x) FIELD_PREP(YT92XX_IPM_COLOR_M, (x))
+#define YT92XX_IPM_COLOR_GREEN YT92XX_IPM_COLOR(0)
+#define YT92XX_IPM_COLOR_YELLOW YT92XX_IPM_COLOR(1)
+#define YT92XX_IPM_COLOR_RED YT92XX_IPM_COLOR(2)
+#define YT921X_PORTn_QOS(port) (0x180180 + 4 * (port))
+#define YT921X_PORT_QOS_CVLAN_PRIO_MAP_ID BIT(5)
+#define YT921X_PORT_QOS_SVLAN_PRIO_MAP_ID BIT(4)
+#define YT921X_PORT_QOS_PRIO_M GENMASK(3, 1)
+#define YT921X_PORT_QOS_PRIO(x) FIELD_PREP(YT921X_PORT_QOS_PRIO_M, (x))
+#define YT921X_PORT_QOS_PRIO_EN BIT(0)
+#define YT921X_PORTn_PRIO_ORD(port) (0x180200 + 4 * (port))
+#define YT921X_PORT_PRIO_ORD_APPm_M(m) GENMASK(3 * (m) + 2, 3 * (m))
+#define YT921X_PORT_PRIO_ORD_APPm(m, x) \
+	((x) << (3 * (m))) /* 0: disabled, except PORT_QOS_PRIO */
+
+#define YT92XX_APP_SEL_NUM 7
+
+#define YT921X_VLAN_CTRL_STP_ID_M GENMASK_ULL(39, 36)
+#define YT921X_VLAN_CTRL_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRL_STP_ID_M, (x))
+
+#define YT921X_VLAN_IGR_FILTER 0x180280
+#define YT921X_VLAN_IGR_FILTER_PORTn_BYPASS_IGMP(port) BIT((port) + 11)
+#define YT921X_VLAN_IGR_FILTER_PORTn(port) BIT(port)
+#define YT921X_PORTn_ISOLATION(port) (0x180294 + 4 * (port))
+#define YT921X_PORT_ISOLATION_BLOCKn(port) BIT(port)
+#define YT921X_STPn(n) (0x18038c + 4 * (n))
+#define YT921X_STP_PORTn_M(port) GENMASK(2 * (port) + 1, 2 * (port))
+#define YT921X_STP_PORTn(port, x) ((x) << (2 * (port)))
+#define YT921X_STP_PORTn_DISABLED(port) YT921X_STP_PORTn(port, 0)
+#define YT921X_STP_PORTn_LEARNING(port) YT921X_STP_PORTn(port, 1)
+#define YT921X_STP_PORTn_BLOCKING(port) YT921X_STP_PORTn(port, 2)
+#define YT921X_STP_PORTn_FORWARD(port) YT921X_STP_PORTn(port, 3)
+#define YT921X_PORTn_LEARN(port) (0x1803d0 + 4 * (port))
+#define YT921X_PORT_LEARN_VID_LEARN_MULTI_EN BIT(22)
+#define YT921X_PORT_LEARN_VID_LEARN_MODE BIT(21)
+#define YT921X_PORT_LEARN_VID_LEARN_EN BIT(20)
+#define YT921X_PORT_LEARN_SUSPEND_COPY_EN BIT(19)
+#define YT921X_PORT_LEARN_SUSPEND_DROP_EN BIT(18)
+#define YT921X_PORT_LEARN_DIS BIT(17)
+#define YT921X_PORT_LEARN_LIMIT_EN BIT(16)
+#define YT921X_PORT_LEARN_LIMIT_M GENMASK(15, 8)
+#define YT921X_PORT_LEARN_LIMIT(x) FIELD_PREP(YT921X_PORT_LEARN_LIMIT_M, (x))
+#define YT921X_PORT_LEARN_DROP_ON_EXCEEDED BIT(2)
+#define YT921X_PORT_LEARN_MODE_M GENMASK(1, 0)
+#define YT921X_PORT_LEARN_MODE(x) FIELD_PREP(YT921X_PORT_LEARN_MODE_M, (x))
+#define YT921X_PORT_LEARN_MODE_AUTO YT921X_PORT_LEARN_MODE(0)
+#define YT921X_PORT_LEARN_MODE_AUTO_AND_COPY YT921X_PORT_LEARN_MODE(1)
+#define YT921X_PORT_LEARN_MODE_CPU_CONTROL YT921X_PORT_LEARN_MODE(2)
+#define YT921X_AGEING 0x180440
+#define YT921X_AGEING_INTERVAL_M GENMASK(15, 0)
+#define YT921X_FDB_IN0 0x180454
+#define YT921X_FDB_IN1 0x180458
+#define YT921X_FDB_IN2 0x18045c
+#define YT921X_FDB_OP 0x180460
+#define YT921X_FDB_OP_INDEX_M GENMASK(22, 11)
+#define YT921X_FDB_OP_INDEX(x) FIELD_PREP(YT921X_FDB_OP_INDEX_M, (x))
+#define YT921X_FDB_OP_MODE_INDEX BIT(10) /* mac+fid / index */
+#define YT921X_FDB_OP_FLUSH_MCAST BIT(9) /* ucast / mcast */
+#define YT921X_FDB_OP_FLUSH_M GENMASK(8, 7)
+#define YT921X_FDB_OP_FLUSH(x) FIELD_PREP(YT921X_FDB_OP_FLUSH_M, (x))
+#define YT921X_FDB_OP_FLUSH_ALL YT921X_FDB_OP_FLUSH(0)
+#define YT921X_FDB_OP_FLUSH_PORT YT921X_FDB_OP_FLUSH(1)
+#define YT921X_FDB_OP_FLUSH_PORT_VID YT921X_FDB_OP_FLUSH(2)
+#define YT921X_FDB_OP_FLUSH_VID YT921X_FDB_OP_FLUSH(3)
+#define YT921X_FDB_OP_FLUSH_STATIC BIT(6)
+#define YT921X_FDB_OP_NEXT_TYPE_M GENMASK(5, 4)
+#define YT921X_FDB_OP_NEXT_TYPE(x) FIELD_PREP(YT921X_FDB_OP_NEXT_TYPE_M, (x))
+#define YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT YT921X_FDB_OP_NEXT_TYPE(0)
+#define YT921X_FDB_OP_NEXT_TYPE_UCAST_VID YT921X_FDB_OP_NEXT_TYPE(1)
+#define YT921X_FDB_OP_NEXT_TYPE_UCAST YT921X_FDB_OP_NEXT_TYPE(2)
+#define YT921X_FDB_OP_NEXT_TYPE_MCAST YT921X_FDB_OP_NEXT_TYPE(3)
+#define YT921X_FDB_OP_OP_M GENMASK(3, 1)
+#define YT921X_FDB_OP_OP(x) FIELD_PREP(YT921X_FDB_OP_OP_M, (x))
+#define YT921X_FDB_OP_OP_ADD YT921X_FDB_OP_OP(0)
+#define YT921X_FDB_OP_OP_DEL YT921X_FDB_OP_OP(1)
+#define YT921X_FDB_OP_OP_GET_ONE YT921X_FDB_OP_OP(2)
+#define YT921X_FDB_OP_OP_GET_NEXT YT921X_FDB_OP_OP(3)
+#define YT921X_FDB_OP_OP_FLUSH YT921X_FDB_OP_OP(4)
+#define YT921X_FDB_OP_START BIT(0)
+#define YT921X_FDB_RESULT 0x180464
+#define YT921X_FDB_RESULT_DONE BIT(15)
+#define YT921X_FDB_RESULT_NOTFOUND BIT(14)
+#define YT921X_FDB_RESULT_OVERWRITED BIT(13)
+#define YT921X_FDB_RESULT_INDEX_M GENMASK(11, 0)
+#define YT921X_FDB_RESULT_INDEX(x) FIELD_PREP(YT921X_FDB_RESULT_INDEX_M, (x))
+#define YT921X_FDB_OUT0 0x1804b0
+#define YT921X_FDB_IO0_ADDR_HI4_M GENMASK(31, 0)
+#define YT921X_FDB_OUT1 0x1804b4
+#define YT921X_FDB_IO1_EGR_PRIO_EN BIT(31)
+#define YT921X_FDB_IO1_STATUS_M GENMASK(30, 28)
+#define YT921X_FDB_IO1_STATUS(x) FIELD_PREP(YT921X_FDB_IO1_STATUS_M, (x))
+#define YT921X_FDB_IO1_STATUS_INVALID YT921X_FDB_IO1_STATUS(0)
+#define YT921X_FDB_IO1_STATUS_MIN_TIME YT921X_FDB_IO1_STATUS(1)
+#define YT921X_FDB_IO1_STATUS_MOVE_AGING_MAX_TIME YT921X_FDB_IO1_STATUS(3)
+#define YT921X_FDB_IO1_STATUS_MAX_TIME YT921X_FDB_IO1_STATUS(5)
+#define YT921X_FDB_IO1_STATUS_PENDING YT921X_FDB_IO1_STATUS(6)
+#define YT921X_FDB_IO1_STATUS_STATIC YT921X_FDB_IO1_STATUS(7)
+#define YT921X_FDB_IO1_FID_M GENMASK(27, 16) /* filtering ID (VID) */
+#define YT921X_FDB_IO1_FID(x) FIELD_PREP(YT921X_FDB_IO1_FID_M, (x))
+#define YT921X_FDB_IO1_ADDR_LO2_M GENMASK(15, 0)
+#define YT921X_FDB_OUT2 0x1804b8
+#define YT921X_FDB_IO2_MOVE_AGING_STATUS_M GENMASK(31, 30)
+#define YT921X_FDB_IO2_IGR_DROP BIT(29)
+#define YT921X_FDB_IO2_EGR_PORTS_M GENMASK(28, 18)
+#define YT921X_FDB_IO2_EGR_PORTS(x) FIELD_PREP(YT921X_FDB_IO2_EGR_PORTS_M, (x))
+#define YT921X_FDB_IO2_EGR_DROP BIT(17)
+#define YT921X_FDB_IO2_COPY_TO_CPU BIT(16)
+#define YT921X_FDB_IO2_IGR_PRIO_EN BIT(15)
+#define YT921X_FDB_IO2_PRIO_M GENMASK(14, 12)
+#define YT921X_FDB_IO2_PRIO(x) FIELD_PREP(YT921X_FDB_IO2_PRIO_M, (x))
+#define YT921X_FDB_IO2_NEW_VID_M GENMASK(11, 0)
+#define YT921X_FDB_IO2_NEW_VID(x) FIELD_PREP(YT921X_FDB_IO2_NEW_VID_M, (x))
+#define YT921X_FILTER_UNK_UCAST 0x180508
+#define YT921X_FILTER_UNK_MCAST 0x18050c
+#define YT921X_FILTER_MCAST 0x180510
+#define YT921X_FILTER_BCAST 0x180514
+#define YT921X_FILTER_PORTS_M GENMASK(10, 0)
+#define YT921X_FILTER_PORTS(x) FIELD_PREP(YT921X_FILTER_PORTS_M, (x))
+#define YT921X_FILTER_PORTn(port) BIT(port)
+#define YT921X_VLAN_EGR_FILTER 0x180598
+#define YT921X_VLAN_EGR_FILTER_PORTn(port) BIT(port)
+#define YT921X_LAG_GROUPn(n) (0x1805a8 + 4 * (n))
+#define YT921X_LAG_GROUP_PORTS_M GENMASK(13, 3)
+#define YT921X_LAG_GROUP_PORTS(x) FIELD_PREP(YT921X_LAG_GROUP_PORTS_M, (x))
+#define YT921X_LAG_GROUP_MEMBER_NUM_M GENMASK(2, 0)
+#define YT921X_LAG_GROUP_MEMBER_NUM(x) \
+	FIELD_PREP(YT921X_LAG_GROUP_MEMBER_NUM_M, (x))
+#define YT921X_LAG_MEMBERnm(n, m) (0x1805b0 + 4 * (4 * (n) + (m)))
+#define YT921X_LAG_MEMBER_PORT_M GENMASK(3, 0)
+#define YT921X_LAG_MEMBER_PORT(x) FIELD_PREP(YT921X_LAG_MEMBER_PORT_M, (x))
+#define YT921X_CPU_COPY 0x180690
+#define YT921X_CPU_COPY_FORCE_INT_PORT BIT(2)
+#define YT921X_CPU_COPY_TO_INT_CPU BIT(1)
+#define YT921X_CPU_COPY_TO_EXT_CPU BIT(0)
+#define YT921X_ACL_PERMIT_UNMATCH	0x1806a0
+#define YT921X_ACL_PERMIT_UNMATCH_PORTS_M	GENMASK(10, 0)
+#define YT921X_ACL_PERMIT_UNMATCH_PORTS(x)		FIELD_PREP(YT921X_ACL_PERMIT_UNMATCH_PORTS_M, (x))
+#define YT921X_ACL_PERMIT_UNMATCH_PORTn(port)	BIT(port)
+#define YT921X_ACT_UNK_UCAST 0x180734
+#define YT921X_ACT_UNK_MCAST 0x180738
+#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_RMA BIT(23)
+#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_IGMP BIT(22)
+#define YT921X_ACT_UNK_ACTn_M(port) GENMASK(2 * (port) + 1, 2 * (port))
+#define YT921X_ACT_UNK_ACTn(port, x) ((x) << (2 * (port)))
+#define YT921X_ACT_UNK_ACTn_FORWARD(port) \
+	YT921X_ACT_UNK_ACTn(port, 0) /* flood */
+#define YT921X_ACT_UNK_ACTn_TRAP(port) \
+	YT921X_ACT_UNK_ACTn(port, 1) /* steer to CPU */
+#define YT921X_ACT_UNK_ACTn_DROP(port) \
+	YT921X_ACT_UNK_ACTn(port, 2) /* discard */
+/* NEVER use this action; see comments in the tag driver */
+#define YT921X_ACT_UNK_ACTn_COPY(port) \
+	YT921X_ACT_UNK_ACTn(port, 3) /* flood and copy */
+#define YT921X_FDB_HW_FLUSH 0x180958
+#define YT921X_FDB_HW_FLUSH_ON_LINKDOWN BIT(0)
+
+#define YT92XX_VLANn_CTRL(vlan) (0x188000 + 8 * (vlan))
+#define YT921X_VLAN_CTRLb_UNTAG_PORTS_M GENMASK(18, 8)
+#define YT921X_VLAN_CTRLb_UNTAG_PORTS(x) \
+	FIELD_PREP(YT921X_VLAN_CTRLb_UNTAG_PORTS_M, (x))
+#define YT921X_VLAN_CTRLb_UNTAG_PORTn(port) BIT((port) + 8)
+#define YT92XX_VLAN_CTRLb_STP_ID_M GENMASK(7, 4)
+#define YT92XX_VLAN_CTRLb_STP_ID(x) FIELD_PREP(YT92XX_VLAN_CTRLb_STP_ID_M, (x))
+#define YT921X_VLAN_CTRLb_SVLAN_EN BIT(3)
+#define YT921X_VLAN_CTRLab_FID_M GENMASK_ULL(34, 23)
+#define YT921X_VLAN_CTRLab_FID(x) FIELD_PREP(YT921X_VLAN_CTRLab_FID_M, (x))
+#define YT921X_VLAN_CTRLa_LEARN_DIS BIT(22)
+#define YT921X_VLAN_CTRLa_PRIO_EN BIT(21)
+#define YT921X_VLAN_CTRLa_PRIO_M GENMASK(20, 18)
+#define YT921X_VLAN_CTRLa_PRIO(x) FIELD_PREP(YT921X_VLAN_CTRLa_PRIO_M, (x))
+#define YT921X_VLAN_CTRLa_PORTS_M GENMASK(17, 7)
+#define YT921X_VLAN_CTRLa_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLa_PORTS_M, (x))
+#define YT921X_VLAN_CTRLa_PORTn(port) BIT((port) + 7)
+#define YT921X_VLAN_CTRLa_BYPASS_1X_AC BIT(6)
+#define YT921X_VLAN_CTRLa_METER_EN BIT(5)
+#define YT921X_VLAN_CTRLa_METER_ID_M GENMASK(4, 0)
+
+#define YT921X_ACLn_ACT(n)		(0x1c0000 + 0x10 * (n))
+#define YT921X_ACL_ACTc_STAG_M			GENMASK(26, 25)
+#define YT921X_ACL_ACTc_STAG(x)			FIELD_PREP(YT921X_ACL_ACTc_STAG_M, (x))
+#define YT921X_ACL_ACTc_STAG_DONTCARE			YT921X_ACL_ACTc_STAG(0)
+#define YT921X_ACL_ACTc_STAG_UNTAG			YT921X_ACL_ACTc_STAG(1)
+#define YT921X_ACL_ACTc_STAG_TAG			YT921X_ACL_ACTc_STAG(2)
+#define YT921X_ACL_ACTc_STAG_KEEP			YT921X_ACL_ACTc_STAG(3)
+#define YT921X_ACL_ACTc_CTAG_M			GENMASK(24, 23)
+#define YT921X_ACL_ACTc_CTAG(x)			FIELD_PREP(YT921X_ACL_ACTc_CTAG_M, (x))
+#define YT921X_ACL_ACTc_CTAG_DONTCARE			YT921X_ACL_ACTc_CTAG(0)
+#define YT921X_ACL_ACTc_CTAG_UNTAG			YT921X_ACL_ACTc_CTAG(1)
+#define YT921X_ACL_ACTc_CTAG_TAG			YT921X_ACL_ACTc_CTAG(2)
+#define YT921X_ACL_ACTc_CTAG_KEEP			YT921X_ACL_ACTc_CTAG(3)
+#define YT921X_ACL_ACTc_FWD_M			GENMASK(22, 21)
+#define YT921X_ACL_ACTc_FWD(x)			FIELD_PREP(YT921X_ACL_ACTc_FWD_M, (x))
+#define YT921X_ACL_ACTc_FWD_FWD			YT921X_ACL_ACTc_FWD(0)
+#define YT921X_ACL_ACTc_FWD_COPY			YT921X_ACL_ACTc_FWD(1)
+#define YT921X_ACL_ACTc_FWD_REDIR			YT921X_ACL_ACTc_FWD(2)
+#define YT921X_ACL_ACTc_FWD_TRAP			YT921X_ACL_ACTc_FWD(3)
+#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M	GENMASK(20, 10)
+#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS(x)		FIELD_PREP(YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M, (x))
+#define YT921X_ACL_ACTc_FWD_REDIR_DPORTn(port)	BIT((port) + 10)
+#define YT921X_ACL_ACTc_FWD_EN			BIT(9)
+#define YT921X_ACL_ACTc_SDEI			BIT(8)
+#define YT921X_ACL_ACTc_SDEI_REPLACE		BIT(7)
+#define YT921X_ACL_ACTc_SPRI_M			GENMASK(6, 4)
+#define YT921X_ACL_ACTc_SPRI(x)			FIELD_PREP(YT921X_ACL_ACTc_SPRI_M, (x))
+#define YT921X_ACL_ACTc_SPRI_REPLACE		BIT(3)
+#define YT921X_ACL_ACTbc_SVID_M		GENMASK_ULL(34, 23)
+#define YT921X_ACL_ACTbc_SVID(x)			FIELD_PREP(YT921X_ACL_ACTbc_SVID_M, (x))
+#define YT921X_ACL_ACTb_SVID_REPLACE		BIT(22)
+#define YT921X_ACL_ACTb_CDEI			BIT(21)
+#define YT921X_ACL_ACTb_CDEI_REPLACE		BIT(20)
+#define YT921X_ACL_ACTb_CPRI_M			GENMASK(19, 17)
+#define YT921X_ACL_ACTb_CPRI(x)			FIELD_PREP(YT921X_ACL_ACTb_CPRI_M, (x))
+#define YT921X_ACL_ACTb_CPRI_REPLACE		BIT(16)
+#define YT921X_ACL_ACTb_CVID_M			GENMASK(15, 4)
+#define YT921X_ACL_ACTb_CVID(x)			FIELD_PREP(YT921X_ACL_ACTb_CVID_M, (x))
+#define YT921X_ACL_ACTb_CVID_REPLACE		BIT(3)
+#define YT921X_ACL_ACTb_PRIO_M			GENMASK(2, 0)
+#define YT921X_ACL_ACTb_PRIO(x)			FIELD_PREP(YT921X_ACL_ACTb_PRIO_M, (x))
+#define YT921X_ACL_ACTa_PRIO_EN		BIT(31)
+#define YT921X_ACL_ACTa_COLOR_M		GENMASK(30, 29)
+#define YT921X_ACL_ACTa_COLOR(x)			FIELD_PREP(YT921X_ACL_ACTa_COLOR_M, (x))
+#define YT921X_ACL_ACTa_COLOR_GREEN			YT921X_ACL_ACTa_COLOR(0)
+#define YT921X_ACL_ACTa_COLOR_YELLOW			YT921X_ACL_ACTa_COLOR(1)
+#define YT921X_ACL_ACTa_COLOR_RED			YT921X_ACL_ACTa_COLOR(2)
+#define YT921X_ACL_ACTa_COLOR_EN		BIT(28)
+#define YT921X_ACL_ACTa_DSCP_M			GENMASK(27, 22)
+#define YT921X_ACL_ACTa_DSCP(x)			FIELD_PREP(YT921X_ACL_ACTa_DSCP_M, (x))
+#define YT921X_ACL_ACTa_DSCP_REPLACE		BIT(21)
+#define YT921X_ACL_ACTa_METER_ID_M		GENMASK(20, 15)
+#define YT921X_ACL_ACTa_METER_ID(x)			FIELD_PREP(YT921X_ACL_ACTa_METER_ID_M, (x))
+#define YT921X_ACL_ACTa_METER_EN		BIT(14)
+#define YT921X_ACL_ACTa_MIRROR_EN		BIT(13)
+#define YT921X_ACL_ACTa_FLOWSTAT_EN		BIT(12)
+#define YT921X_ACL_ACTa_FLOWSTAT_ID_M		GENMASK(11, 6)
+#define YT921X_ACL_ACTa_FLOWSTAT_ID(x)		FIELD_PREP(YT921X_ACL_ACTa_FLOWSTAT_ID_M, (x))
+#define YT921X_ACL_ACTa_GPIO_EN		BIT(5)
+#define YT921X_ACL_ACTa_GPIO_PIN_M		GENMASK(4, 1)
+#define YT921X_ACL_ACTa_GPIO_PIN(x)			FIELD_PREP(YT921X_ACL_ACTa_GPIO_PIN_M, (x))
+#define YT921X_ACL_ACTa_INTR_EN		BIT(0)
+#define YT921X_ACL_BLK_KEEP		0x201000
+#define YT921X_ACL_BLK_KEEP_GRPIDn_M(bin)	(7 << (4 * (bin) + 1))
+#define YT921X_ACL_BLK_KEEP_GRPIDn(bin, x)		((x) << (4 * (bin) + 1))
+#define YT921X_ACL_BLK_KEEP_KEEPn(bin)		BIT(4 * (bin))
+#define YT921X_ACL_PORT			0x202000
+#define YT921X_ACL_PORT_PORTS_M		GENMASK(10, 0)
+#define YT921X_ACL_PORT_PORTS(x)			FIELD_PREP(YT921X_ACL_PORT_PORTS_M, (x))
+#define YT921X_ACL_PORT_PORTn(port)		BIT(port)
+#define YT921X_ACL_BLK_CMD		0x202004
+#define YT921X_ACL_BLK_CMD_BLKID_M		GENMASK(6, 1)
+#define YT921X_ACL_BLK_CMD_BLKID(x)			FIELD_PREP(YT921X_ACL_BLK_CMD_BLKID_M, (x))
+#define YT921X_ACL_BLK_CMD_MODIFY		BIT(0)
+#define YT921X_ACLn_ENTRY(blk)		(0x203000 + 4 * (blk))
+#define YT921X_ACL_ENTRY_GRPIDm_M(bin)		(7 << (4 * (bin) + 1))
+#define YT921X_ACL_ENTRY_GRPIDm(bin, x)		((x) << (4 * (bin) + 1))
+#define YT921X_ACL_ENTRY_ENm(bin)		BIT(4 * (bin))
+#define YT921X_ACLn_KEYm(blk, bin)	(0x204000 + 0x200 * (bin) + 8 * (blk))
+#define YT921X_ACL_KEYb_ORD_M			GENMASK(29, 21)
+#define YT921X_ACL_KEYb_ORD(x)			FIELD_PREP(YT921X_ACL_KEYb_ORD_M, (x))
+#define YT921X_ACL_KEYb_SPORTS_M		GENMASK(20, 10)
+#define YT921X_ACL_KEYb_SPORTS(x)			FIELD_PREP(YT921X_ACL_KEYb_SPORTS_M, (x))
+#define YT921X_ACL_KEYb_SPORTn(port)		BIT((port) + 10)
+#define YT921X_ACL_KEYb_REVERSE		BIT(9)	/* reverse match */
+#define YT921X_ACL_KEYb_TYPE_M			GENMASK(8, 4)
+#define YT921X_ACL_KEYb_TYPE(x)			FIELD_PREP(YT921X_ACL_KEYb_TYPE_M, (x))
+/* KEY_* fields need no masks */
+#define YT921X_ACLn_MASKm(blk, bin)	(0x205000 + 0x200 * (bin) + 8 * (blk))
+
+enum yt921x_acl_type {
+	YT921X_ACL_TYPE_NA,
+	YT921X_ACL_TYPE_MAC_DA0,
+	YT921X_ACL_TYPE_MAC_SA0,
+	YT921X_ACL_TYPE_MAC_DA1_SA1,
+	YT921X_ACL_TYPE_VLAN,
+	YT921X_ACL_TYPE_VTAG,
+	YT921X_ACL_TYPE_IPV4_DA,
+	YT921X_ACL_TYPE_IPV4_SA,
+	YT921X_ACL_TYPE_IPV6_DA0,
+	YT921X_ACL_TYPE_IPV6_DA1,
+	YT921X_ACL_TYPE_IPV6_DA2,
+	YT921X_ACL_TYPE_IPV6_DA3,
+	YT921X_ACL_TYPE_IPV6_SA0,
+	YT921X_ACL_TYPE_IPV6_SA1,
+	YT921X_ACL_TYPE_IPV6_SA2,
+	YT921X_ACL_TYPE_IPV6_SA3,
+	YT921X_ACL_TYPE_MISC,
+	YT921X_ACL_TYPE_L4,
+	YT921X_ACL_TYPE_UDF0,
+	YT921X_ACL_TYPE_UDF1,
+	YT921X_ACL_TYPE_UDF2,
+	YT921X_ACL_TYPE_UDF3,
+	YT921X_ACL_TYPE_UDF4,
+	YT921X_ACL_TYPE_UDF5,
+	YT921X_ACL_TYPE_UDF6,
+	YT921X_ACL_TYPE_UDF7,
+	YT921X_ACL_TYPE_ETHERTYPE,
+	YT921X_ACL_TYPE_NUM
+};
+
+/* Range: turn KEY:MASK into MIN:MAX */
+
+#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M	GENMASK(3, 0)
+#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M, (x))
+#define YT921X_ACL_BINa_MAC_xA0_MAC_xA0_M	GENMASK(31, 0)
+#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M	GENMASK(2, 0)
+#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE(x)	FIELD_PREP(YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M, (x))
+#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_DA1_M	GENMASK(31, 16)
+#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_SA1_M	GENMASK(15, 0)
+
+#define YT921X_ACL_KEYb_VLAN_SVID_RANGE_EN	BIT(31)
+#define YT921X_ACL_KEYb_VLAN_CVID_RANGE_EN	BIT(30)
+#define YT921X_ACL_BINb_VLAN_CDEI		BIT(3)
+#define YT921X_ACL_BINb_VLAN_CPRI_M		GENMASK(2, 0)
+#define YT921X_ACL_BINb_VLAN_CPRI(x)			FIELD_PREP(YT921X_ACL_BINb_VLAN_CPRI_M, (x))
+#define YT921X_ACL_BINa_VLAN_CTAG_FMT_M	GENMASK(31, 30)
+#define YT921X_ACL_BINa_VLAN_CTAG_FMT(x)		FIELD_PREP(YT921X_ACL_BINa_VLAN_CTAG_FMT_M, (x))
+#define YT921X_ACL_BINa_VLAN_SDEI		BIT(29)
+#define YT921X_ACL_BINa_VLAN_SPRI_M		GENMASK(28, 26)
+#define YT921X_ACL_BINa_VLAN_SPRI(x)			FIELD_PREP(YT921X_ACL_BINa_VLAN_SPRI_M, (x))
+#define YT921X_ACL_BINa_VLAN_STAG_FMT_M	GENMASK(25, 24)
+#define YT921X_ACL_BINa_VLAN_STAG_FMT(x)		FIELD_PREP(YT921X_ACL_BINa_VLAN_STAG_FMT_M, (x))
+#define YT921X_ACL_BINa_VLAN_SVID_M		GENMASK(23, 12)
+#define YT921X_ACL_BINa_VLAN_SVID(x)			FIELD_PREP(YT921X_ACL_BINa_VLAN_SVID_M, (x))
+#define YT921X_ACL_BINa_VLAN_CVID_M		GENMASK(11, 0)
+#define YT921X_ACL_BINa_VLAN_CVID(x)			FIELD_PREP(YT921X_ACL_BINa_VLAN_CVID_M, (x))
+
+#define YT921X_ACL_KEYb_VTAG_SVID_RANGE_EN	BIT(31)
+#define YT921X_ACL_KEYb_VTAG_CVID_RANGE_EN	BIT(30)
+#define YT921X_ACL_BINa_VTAG_CDEI		BIT(31)
+#define YT921X_ACL_BINa_VTAG_CPRI_M		GENMASK(30, 28)
+#define YT921X_ACL_BINa_VTAG_CPRI(x)			FIELD_PREP(YT921X_ACL_BINa_VTAG_CPRI_M, (x))
+#define YT921X_ACL_BINa_VTAG_SDEI		BIT(27)
+#define YT921X_ACL_BINa_VTAG_SPRI_M		GENMASK(26, 24)
+#define YT921X_ACL_BINa_VTAG_SPRI(x)			FIELD_PREP(YT921X_ACL_BINa_VTAG_SPRI_M, (x))
+#define YT921X_ACL_BINa_VTAG_SVID_M		GENMASK(23, 12)
+#define YT921X_ACL_BINa_VTAG_SVID(x)			FIELD_PREP(YT921X_ACL_BINa_VTAG_SVID_M, (x))
+#define YT921X_ACL_BINa_VTAG_CVID_M		GENMASK(11, 0)
+#define YT921X_ACL_BINa_VTAG_CVID(x)			FIELD_PREP(YT921X_ACL_BINa_VTAG_CVID_M, (x))
+
+#define YT921X_ACL_KEYb_IPV4_ADDR_RANGE_EN	BIT(30)
+#define YT921X_ACL_BINb_IPV4_FRAG		BIT(3)
+#define YT921X_ACL_BINb_IPV4_L4_TYPE_M		GENMASK(2, 0)
+#define YT921X_ACL_BINb_IPV4_L4_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_IPV4_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_IPV4_ADDR_M		GENMASK(31, 0)
+
+#define YT921X_ACL_BINb_IPV6_L4_TYPE_M		GENMASK(2, 0)
+#define YT921X_ACL_BINb_IPV6_L4_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_IPV6_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_IPV6_ADDRx_M		GENMASK(31, 0)
+
+#define YT921X_ACL_BINb_IPV6_xA1_IP_OPTION	BIT(3)
+#define YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG	BIT(3)
+#define YT921X_ACL_KEYb_IPV6_xA3_ADDR_RANGE_EN	BIT(30)
+#define YT921X_ACL_BINb_IPV6_xA3_FRAG		BIT(3)
+
+#define YT921X_ACL_BINb_MISC_FRAG		BIT(3)
+#define YT921X_ACL_BINb_MISC_L4_TYPE_M		GENMASK(2, 0)
+#define YT921X_ACL_BINb_MISC_L4_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_MISC_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_MISC_PPPOE_FLAG	BIT(30)
+#define YT921X_ACL_BINa_MISC_FIRST_FRAG	BIT(29)
+#define YT921X_ACL_BINa_MISC_IP_OPTION		BIT(28)
+#define YT921X_ACL_BINa_MISC_TCP_FLAGS_M	GENMASK(27, 20)
+#define YT921X_ACL_BINa_MISC_TCP_FLAGS(x)		FIELD_PREP(YT921X_ACL_BINa_MISC_TCP_FLAGS_M, (x))
+#define YT921X_ACL_BINa_MISC_IP_PROTO_M	GENMASK(19, 12)
+#define YT921X_ACL_BINa_MISC_IP_PROTO(x)		FIELD_PREP(YT921X_ACL_BINa_MISC_IP_PROTO_M, (x))
+#define YT921X_ACL_BINa_MISC_TOS_M		GENMASK(11, 4)
+#define YT921X_ACL_BINa_MISC_TOS(x)			FIELD_PREP(YT921X_ACL_BINa_MISC_TOS_M, (x))
+#define YT921X_ACL_BINa_MISC_L3_TYPE_M		GENMASK(3, 0)
+#define YT921X_ACL_BINa_MISC_L3_TYPE(x)		FIELD_PREP(YT921X_ACL_BINa_MISC_L3_TYPE_M, (x))
+
+#define YT921X_ACL_KEYb_L4_DPORT_RANGE_EN	BIT(31)
+#define YT921X_ACL_KEYb_L4_SPORT_RANGE_EN	BIT(30)
+#define YT921X_ACL_BINb_L4_FRAG		BIT(3)
+#define YT921X_ACL_BINb_L4_TYPE_M		GENMASK(2, 0)
+#define YT921X_ACL_BINb_L4_TYPE(x)			FIELD_PREP(YT921X_ACL_BINb_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_L4_DPORT_M		GENMASK(31, 16)
+#define YT921X_ACL_BINa_L4_SPORT_M		GENMASK(15, 0)
+
+#define YT921X_ACL_BINb_UDF_IS_IGMP		BIT(0)
+#define YT921X_ACL_BINa_UDF_UDF0_M		GENMASK(31, 16)
+#define YT921X_ACL_BINa_UDF_UDF0(x)			FIELD_PREP(YT921X_ACL_BINa_UDF_UDF0_M, (x))
+#define YT921X_ACL_BINa_UDF_UDF1_M		GENMASK(15, 0)
+#define YT921X_ACL_BINa_UDF_UDF1(x)			FIELD_PREP(YT921X_ACL_BINa_UDF_UDF1_M, (x))
+
+#define YT921X_ACL_KEYb_ETHERTYPE_ETHERTYPE_RANGE_EN	BIT(30)
+#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M	GENMASK(2, 0)
+#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M, (x))
+#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M	GENMASK(15, 0)
+#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE(x)	FIELD_PREP(YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M, (x))
+
+enum yt921x_l2_type {
+	YT921X_L2_TYPE_ETH,
+	YT921X_L2_TYPE_ETHV2,
+	YT921X_L2_TYPE_ETHSAP,
+	YT921X_L2_TYPE_ETHSNAP,
+};
+
+enum yt921x_l3_type {
+	YT921X_L3_TYPE_OTHER,
+	YT921X_L3_TYPE_IPV4,
+	YT921X_L3_TYPE_IPV6,
+	YT921X_L3_TYPE_ARP,
+	YT921X_L3_TYPE_LLDP,
+	YT921X_L3_TYPE_PAE,
+	YT921X_L3_TYPE_ERP,
+	YT921X_L3_TYPE_SLOW_PROTOCOL,
+};
+
+enum yt921x_l4_type {
+	YT921X_L4_TYPE_OTHER,
+	YT921X_L4_TYPE_TCP,
+	YT921X_L4_TYPE_UDP,
+	YT921X_L4_TYPE_UDPLITE,
+	YT921X_L4_TYPE_ICMP,
+	YT921X_L4_TYPE_IGMP,
+	YT921X_L4_TYPE_MLD,
+	YT921X_L4_TYPE_ND,
+};
+
+#define YT921X_TPID_IGRn(x) (0x210000 + 4 * (x)) /* [0, 3] */
+#define YT921X_TPID_IGR_TPID_M GENMASK(15, 0)
+#define YT921X_PORTn_IGR_TPID(port) (0x210010 + 4 * (port))
+#define YT921X_PORT_IGR_TPIDn_STAG_M GENMASK(7, 4)
+#define YT921X_PORT_IGR_TPIDn_STAG(x) BIT((x) + 4)
+#define YT921X_PORT_IGR_TPIDn_CTAG_M GENMASK(3, 0)
+#define YT921X_PORT_IGR_TPIDn_CTAG(x) BIT(x)
+#define YT921X_LAG_HASH 0x210090
+#define YT921X_LAG_HASH_L4_SPORT BIT(7)
+#define YT921X_LAG_HASH_L4_DPORT BIT(6)
+#define YT921X_LAG_HASH_IP_PROTO BIT(5)
+#define YT921X_LAG_HASH_IP_SRC BIT(4)
+#define YT921X_LAG_HASH_IP_DST BIT(3)
+#define YT921X_LAG_HASH_MAC_SA BIT(2)
+#define YT921X_LAG_HASH_MAC_DA BIT(1)
+#define YT921X_LAG_HASH_SRC_PORT BIT(0)
+
+#define YT921X_UDFn_CTRL(x)		(0x210094 + 4 * (x))
+#define YT921X_UDF_CTRL_UDF_TYPE_M		GENMASK(8, 7)
+#define YT921X_UDF_CTRL_UDF_TYPE(x)			FIELD_PREP(YT921X_UDF_CTRL_UDF_TYPE_M, (x))
+#define YT921X_UDF_CTRL_UDF_TYPE_ETH			YT921X_UDF_CTRL_UDF_TYPE(0)
+#define YT921X_UDF_CTRL_UDF_TYPE_L3			YT921X_UDF_CTRL_UDF_TYPE(1)
+#define YT921X_UDF_CTRL_UDF_TYPE_L4			YT921X_UDF_CTRL_UDF_TYPE(2)
+#define YT921X_UDF_CTRL_UDF_OFFSET_M		GENMASK(6, 0)
+#define YT921X_UDF_CTRL_UDF_OFFSET(x)			FIELD_PREP(YT921X_UDF_CTRL_UDF_OFFSET_M, (x))
+
+#define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port))
+#define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */
+#define YT921X_METER_SLOT 0x220104
+#define YT921X_METER_SLOT_SLOT_M GENMASK(11, 0)
+#define YT921X_PORTn_METER(port) (0x220108 + 4 * (port))
+#define YT921X_PORT_METER_EN BIT(4)
+#define YT921X_PORT_METER_ID_M GENMASK(3, 0)
+#define YT921X_PORT_METER_ID(x) FIELD_PREP(YT921X_PORT_METER_ID_M, (x))
+#define YT921X_METERn_CTRL(x) (0x220800 + 0x10 * (x))
+#define YT921X_METER_CTRLc_METER_EN BIT(14)
+#define YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN \
+	BIT(13) /* RFC4115: yellow use unused green bw */
+#define YT921X_METER_CTRLc_DROP_M GENMASK(12, 11)
+#define YT921X_METER_CTRLc_DROP(x) FIELD_PREP(YT921X_METER_CTRLc_DROP_M, (x))
+#define YT921X_METER_CTRLc_DROP_GYR YT921X_METER_CTRLc_DROP(0)
+#define YT921X_METER_CTRLc_DROP_YR YT921X_METER_CTRLc_DROP(1)
+#define YT921X_METER_CTRLc_DROP_R YT921X_METER_CTRLc_DROP(2)
+#define YT921X_METER_CTRLc_DROP_NONE YT921X_METER_CTRLc_DROP(3)
+#define YT921X_METER_CTRLc_COLOR_BLIND BIT(10)
+#define YT921X_METER_CTRLc_UNIT_M GENMASK(9, 7)
+#define YT921X_METER_CTRLc_UNIT(x) FIELD_PREP(YT921X_METER_CTRLc_UNIT_M, (x))
+#define YT921X_METER_CTRLc_BYTE_MODE_INCLUDE_GAP \
+	BIT(6) /* +GAP_VALUE bytes each packet */
+#define YT921X_METER_CTRLc_PKT_MODE BIT(5) /* 0: byte rate mode */
+#define YT921X_METER_CTRLc_RFC2698 BIT(4) /* 0: RFC4115 */
+#define YT921X_METER_CTRLbc_CBS_M GENMASK_ULL(35, 20)
+#define YT921X_METER_CTRLbc_CBS(x) FIELD_PREP(YT921X_METER_CTRLbc_CBS_M, (x))
+#define YT921X_METER_CTRLb_CIR_M GENMASK(19, 2)
+#define YT921X_METER_CTRLb_CIR(x) FIELD_PREP(YT921X_METER_CTRLb_CIR_M, (x))
+#define YT921X_METER_CTRLab_EBS_M GENMASK_ULL(33, 18)
+#define YT921X_METER_CTRLab_EBS(x) FIELD_PREP(YT921X_METER_CTRLab_EBS_M, (x))
+#define YT921X_METER_CTRLa_EIR_M GENMASK(17, 0)
+#define YT921X_METER_CTRLa_EIR(x) FIELD_PREP(YT921X_METER_CTRLa_EIR_M, (x))
+#define YT921X_METERn_STAT(x) (0x221000 + 8 * (x))
+
+#define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port))
+#define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_EN BIT(31)
+#define YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_EN BIT(30)
+#define YT921X_PORT_VLAN_CTRL_SVID_M GENMASK(29, 18)
+#define YT921X_PORT_VLAN_CTRL_SVID(x) \
+	FIELD_PREP(YT921X_PORT_VLAN_CTRL_SVID_M, (x))
+#define YT921X_PORT_VLAN_CTRL_CVID_M GENMASK(17, 6)
+#define YT921X_PORT_VLAN_CTRL_CVID(x) \
+	FIELD_PREP(YT921X_PORT_VLAN_CTRL_CVID_M, (x))
+#define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_M GENMASK(5, 3)
+#define YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_M GENMASK(2, 0)
+#define YT921X_PORTn_VLAN_CTRL1(port) (0x230080 + 4 * (port))
+#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_EN BIT(8)
+#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_PROFILE_ID_M GENMASK(7, 4)
+#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_TAGGED BIT(3)
+#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_UNTAGGED BIT(2)
+#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED BIT(1)
+#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED BIT(0)
+
+#define YT921X_MIRROR 0x300300
+#define YT921X_MIRROR_IGR_PORTS_M GENMASK(26, 16)
+#define YT921X_MIRROR_IGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_IGR_PORTS_M, (x))
+#define YT921X_MIRROR_IGR_PORTn(port) BIT((port) + 16)
+#define YT921X_MIRROR_EGR_PORTS_M GENMASK(14, 4)
+#define YT921X_MIRROR_EGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_EGR_PORTS_M, (x))
+#define YT921X_MIRROR_EGR_PORTn(port) BIT((port) + 4)
+#define YT921X_MIRROR_PORT_M GENMASK(3, 0)
+#define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x))
+
+#define YT921X_PORT_SHAPE_SLOT		0x34000c
+#define YT921X_PORT_SHAPE_SLOT_SLOT_M		GENMASK(11, 0)
+#define YT921X_PORTn_SHAPE_CTRL(port)	(0x354000 + 8 * (port))
+#define YT921X_PORT_SHAPE_CTRLb_EN		BIT(4)
+#define YT921X_PORT_SHAPE_CTRLb_PKT_MODE	BIT(3)	/* 0: byte rate mode */
+#define YT921X_PORT_SHAPE_CTRLb_UNIT_M		GENMASK(2, 0)
+#define YT921X_PORT_SHAPE_CTRLb_UNIT(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x))
+#define YT921X_PORT_SHAPE_CTRLa_CBS_M		GENMASK(31, 18)
+#define YT921X_PORT_SHAPE_CTRLa_CBS(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x))
+#define YT921X_PORT_SHAPE_CTRLa_CIR_M		GENMASK(17, 0)
+#define YT921X_PORT_SHAPE_CTRLa_CIR(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x))
+#define YT921X_PORTn_SHAPE_STAT(port)	(0x356000 + 4 * (port))
+
+#define YT921X_EDATA_EXTMODE 0xfb
+#define YT921X_EDATA_LEN 0x100
+
+#define YT921X_FDB_NUM 4096
+
+enum yt921x_fdb_entry_status {
+	YT921X_FDB_ENTRY_STATUS_INVALID = 0,
+	YT921X_FDB_ENTRY_STATUS_MIN_TIME = 1,
+	YT921X_FDB_ENTRY_STATUS_MOVE_AGING_MAX_TIME = 3,
+	YT921X_FDB_ENTRY_STATUS_MAX_TIME = 5,
+	YT921X_FDB_ENTRY_STATUS_PENDING = 6,
+	YT921X_FDB_ENTRY_STATUS_STATIC = 7,
+};
+
+#define YT921X_TOKEN_BYTE_C 1 /* 1 token = 2^1 byte */
+#define YT921X_TOKEN_PKT_C -6 /* 1 token = 2^-6 packets */
+#define YT921X_TOKEN_RATE_C -15
+/* Custom meters only, not including dedicated port meters (11) */
+#define YT921X_METER_NUM 64
+#define YT921X_METER_SLOT_MIN 80
+#define YT921X_METER_UNIT_MAX ((1 << 3) - 1)
+#define YT921X_METER_CIR_MAX ((1 << 18) - 1)
+#define YT921X_METER_CBS_MAX ((1 << 16) - 1)
+#define YT921X_PORT_SHAPE_SLOT_MIN	80
+#define YT921X_SHAPE_UNIT_MAX	((1 << 3) - 1)
+#define YT921X_SHAPE_CIR_MAX	((1 << 18) - 1)
+#define YT921X_SHAPE_CBS_MAX	((1 << 14) - 1)
+
+#define YT92XX_LAG_NUM 2
+#define YT92XX_LAG_PORT_NUM 4
+
+/* required for a hard reset */
+#define YT92XX_RST_DELAY_US 10000
+
+#define YT92XX_FRAME_SIZE_MAX 0x2400 /* 9216 */
+
+#define YT921X_TAG_LEN 8
+
+/* 8 internal + 2 external + 1 mcu */
+#define YT92XX_PORT_NUM 11
+
+#define YT921X_ACL_BLK_NUM	48
+#define YT921X_ACL_ENT_PER_BLK	8
+#define YT921X_ACL_NUM		(YT921X_ACL_BLK_NUM * YT921X_ACL_ENT_PER_BLK)
+#define YT921X_UDF_NUM		8
+
+struct yt921x_acl_entry {
+	u32 key[2];
+	u32 mask[2];
+};
+
+struct yt921x_acl_rule {
+	unsigned long tag;
+	enum tc_setup_type type;
+
+	u32 action[3];
+	bool sw_assisted;
+
+	u8 mask;
+	struct yt921x_acl_entry entries[YT921X_ACL_ENT_PER_BLK];
+};
+
+struct yt921x_acl_blk {
+	struct yt921x_acl_rule *rules[YT921X_ACL_ENT_PER_BLK];
+};
+
+struct yt92xx_mib {
+	u64 rx_broadcast;
+	u64 rx_pause;
+	u64 rx_multicast;
+	u64 rx_crc_errors;
+
+	u64 rx_alignment_errors;
+	u64 rx_undersize_errors;
+	u64 rx_fragment_errors;
+	u64 rx_64byte;
+
+	u64 rx_65_127byte;
+	u64 rx_128_255byte;
+	u64 rx_256_511byte;
+	u64 rx_512_1023byte;
+
+	u64 rx_1024_1518byte;
+	u64 rx_jumbo;
+	u64 rx_good_bytes;
+
+	u64 rx_bad_bytes;
+	u64 rx_oversize_errors;
+
+	u64 rx_dropped;
+	u64 tx_broadcast;
+	u64 tx_pause;
+	u64 tx_multicast;
+
+	u64 tx_undersize_errors;
+	u64 tx_64byte;
+	u64 tx_65_127byte;
+	u64 tx_128_255byte;
+
+	u64 tx_256_511byte;
+	u64 tx_512_1023byte;
+	u64 tx_1024_1518byte;
+	u64 tx_jumbo;
+
+	u64 tx_good_bytes;
+	u64 tx_collisions;
+
+	u64 tx_aborted_errors;
+	u64 tx_multiple_collisions;
+	u64 tx_single_collisions;
+	u64 tx_good;
+
+	u64 tx_deferred;
+	u64 tx_late_collisions;
+	u64 rx_oam;
+	u64 tx_oam;
+};
+
+#elif IS_ENABLED(CONFIG_MOTORCOMM_YT922X)
+/* mtu */
+#define YT92XX_FRAME_SIZE_MAX 0x2400 /* 9216 */
+#define YT92XX_PORT_NUM 9
+
+/* Lag */
+#define YT922X_LAG_HASH 0x210090
+#define YT92XX_LAG_PORT_NUM 10
+#define YT922X_LAG_HASH_L4_SPORT BIT(7)
+#define YT922X_LAG_HASH_L4_DPORT BIT(6)
+#define YT922X_LAG_HASH_IP_PROTO BIT(5)
+#define YT922X_LAG_HASH_IP_SRC BIT(4)
+#define YT922X_LAG_HASH_IP_DST BIT(3)
+#define YT922X_LAG_HASH_MAC_SA BIT(2)
+#define YT922X_LAG_HASH_MAC_DA BIT(1)
+#define YT922X_LAG_HASH_SRC_PORT BIT(0)
+#define YT92XX_LAG_NUM 4
+
+/* mirror */
+#define YT922X_MIRROR_PORT_M GENMASK(3, 0)
+#define YT922X_MIRROR_PORT(x) FIELD_PREP(YT922X_MIRROR_PORT_M, (x))
+
+/* vlan */
+#define YT922X_VID_UNWARE 4095
+
+/* Qos */
+#define YT92XX_APP_SEL_NUM 6
+
+/* MDIO */
+#define YT92XX_EXT_MBUS_OP		0xf1000
+#define YT92XX_INT_MBUS_OP		0xf0000
+#define YT92XX_MBUS_OP_START			BIT(0)
+#define YT92XX_EXT_MBUS_CTRL		0xf1004
+#define YT92XX_INT_MBUS_CTRL		0xf0004
+#define YT92XX_MBUS_CTRL_PORT_M		GENMASK(25, 21)
+#define YT92XX_MBUS_CTRL_PORT(x)			FIELD_PREP(YT92XX_MBUS_CTRL_PORT_M, (x))
+#define YT92XX_MBUS_CTRL_REG_M			GENMASK(20, 16)
+#define YT92XX_MBUS_CTRL_REG(x)			FIELD_PREP(YT92XX_MBUS_CTRL_REG_M, (x))
+#define YT92XX_MBUS_CTRL_TYPE_M		GENMASK(11, 8)  /* wild guess */
+#define YT92XX_MBUS_CTRL_TYPE(x)			FIELD_PREP(YT92XX_MBUS_CTRL_TYPE_M, (x))
+#define YT92XX_MBUS_CTRL_TYPE_C22			YT92XX_MBUS_CTRL_TYPE(4)
+#define YT92XX_MBUS_CTRL_OP_M			GENMASK(3, 2)  /* wild guess */
+#define YT92XX_MBUS_CTRL_OP(x)			FIELD_PREP(YT92XX_MBUS_CTRL_OP_M, (x))
+#define YT92XX_MBUS_CTRL_WRITE			YT92XX_MBUS_CTRL_OP(1)
+#define YT92XX_MBUS_CTRL_READ				YT92XX_MBUS_CTRL_OP(2)
+#define YT92XX_EXT_MBUS_DOUT		0xf1008
+#define YT92XX_INT_MBUS_DOUT		0xf0008
+#define YT92XX_EXT_MBUS_DIN		0xf100c
+#define YT92XX_INT_MBUS_DIN		0xf000c
+
+/* STP */
+#define YT92XX_VLAN_CTRLb_STP_ID_M GENMASK(13, 10)
+#define YT92XX_VLAN_CTRLb_STP_ID(x) FIELD_PREP(YT92XX_VLAN_CTRLb_STP_ID_M, (x))
+#define YT92XX_VLANn_CTRL(vlan) (0x188000 + 4 * (vlan))
+
+/* CHIP */
+#define YT922X_CHIP_ID 0x80008
+#define YT922X_CHIP_ID_MAJOR GENMASK(31, 16)
+
+/* CPU port */
+#define YT922X_CPU_COPY			0x181100
+#define YT922X_CPU_COPY_TO_INT_CPU		BIT(1)
+#define YT922X_CPU_COPY_TO_EXT_CPU		BIT(0)
+#define YT922X_CPU_TAG_RX_CTRL    0x80504
+#define YT922X_CPU_TAG_RX_MODE	BIT(0)
+#define YT922X_CPU_TAG_TX_CTRL    0x100710
+#define YT922X_CPU_TAG_TX_TYPE	BIT(0)
+#define YT922X_CPU_TAG_TX_MODE	BIT(1)
+#define YT922X_CPU_TAG_TX_CTAG_OP	BIT(2)
+#define YT922X_CPU_TAG_TX_STAG_OP	BIT(3)
+
+/* ctrl pkt */
+#define YT922X_FILTER_UNK_UCAST		0x180ec8
+#define YT922X_ACT_UNK_UCAST		0x180ed8
+#define YT922X_ACT_UNK_MCAST		0x180ee0
+#define YT922X_ACT_UNK_MCAST_BYPASS_DROP_PIM	BIT(22)
+#define YT922X_ACT_UNK_MCAST_BYPASS_DROP_MLD	BIT(21)
+#define YT922X_ACT_UNK_MCAST_BYPASS_DROP_IGMP	BIT(20)
+#define YT922X_ACT_UNK_ACTn_M(port)		GENMASK(2 * (port) + 1, 2 * (port))
+#define YT922X_ACT_UNK_ACTn(port, x)			((x) << (2 * (port)))
+#define YT922X_ACT_UNK_ACTn_FORWARD(port)		YT922X_ACT_UNK_ACTn(port, 0)  /* flood */
+#define YT922X_ACT_UNK_ACTn_TRAP(port)		YT922X_ACT_UNK_ACTn(port, 3)  /* steer to CPU */
+#define YT922X_ACT_UNK_ACTn_DROP(port)		YT922X_ACT_UNK_ACTn(port, 1)  /* discard */
+/* NEVER use this action; see comments in the tag driver */
+#define YT922X_ACT_UNK_ACTn_COPY(port)		YT922X_ACT_UNK_ACTn(port, 2)  /* flood and copy */
+
+/* global ctrl */
+#define YT922X_GLOBAL_CTRL1    0x80004
+#define YT922X_METER_EN    BIT(7)
+
+/* DSCP */
+#define YT92XX_IPM_DSCPn(n) (0x180500 + 4 * (n)) /* Internal Priority Map */
+#define YT92XX_IPM_PRIO_M GENMASK(4, 2)
+#define YT92XX_IPM_PRIO(x) FIELD_PREP(YT92XX_IPM_PRIO_M, (x))
+#define YT92XX_IPM_PCPn(dei, pcp)	(0x180600 + 4 * (8 * (dei) + (pcp)))
+#define YT92XX_IPM_COLOR_M			GENMASK(1, 0)
+#define YT92XX_IPM_COLOR(x)				FIELD_PREP(YT92XX_IPM_COLOR_M, (x))
+#define YT92XX_IPM_COLOR_GREEN			YT92XX_IPM_COLOR(0)
+#define YT92XX_IPM_COLOR_YELLOW			YT92XX_IPM_COLOR(1)
+#define YT92XX_IPM_COLOR_RED		    YT92XX_IPM_COLOR(2)
+
+/* required for a hard reset */
+#define YT92XX_RST_DELAY_US 50000
+
+struct yt92xx_mib {
+	u64 rx_broadcast;
+	u64 rx_pause;
+	u64 rx_unicast;
+	u64 rx_oam_counter;
+
+	u64 rx_multicast;
+	u64 rx_align_err_less_64byte;
+	u64 rx_align_err_64b_1518byte;
+	u64 rx_align_err_jumbo;
+
+	u64 rx_fcs_err_64b_1518b;
+	u64 rx_fcs_err_jumbo;
+	u64 rx_undersize_errors;
+	u64 rx_fragment_errors;
+
+	u64 rx_64byte;
+	u64 rx_65_127byte;
+	u64 rx_128_255byte;
+	u64 rx_256_511byte;
+
+	u64 rx_512_1023byte;
+	u64 rx_1024_1518byte;
+	u64 rx_jumbo;
+	u64 rx_oversize_errors;
+
+	u64 tx_broadcast;
+	u64 tx_pause;
+	u64 tx_oam_counter;
+	u64 tx_multicast;
+
+	u64 rx_fc_drop;
+	u64 tx_64byte;
+	u64 tx_65_127byte;
+	u64 tx_128_255byte;
+
+	u64 tx_256_511byte;
+	u64 tx_512_1023byte;
+	u64 tx_1024_1518byte;
+	u64 tx_jumbo;
+
+	u64 tx_oversize_errors;
+	u64 tx_late_collision;
+	u64 tx_excessive_collision;
+	u64 tx_multi_collision;
+
+	u64 tx_single_collision;
+	u64 tx_unicast;
+	u64 tx_defer;
+	u64 tx_collision;
+
+	u64 rx_discard;
+	u64 tx_queue_drop;
+	u64 tx_mac_filter;
+	u64 rx_okbyte;
+
+	u64 rx_not_okbyte;
+	u64 tx_okbyte;
+};
+#endif
+
+struct yt92xx_port {
+	unsigned char index;
+
+	bool hairpin;
+	bool isolated;
+
+	struct delayed_work mib_read;
+	struct yt92xx_mib mib;
+	u64 rx_frames;
+	u64 tx_frames;
+};
+
+enum yt92xx_chip_mode {
+	YT9215,
+	YT9224,
+	YTMAX
+};
+
+enum yt92xx_cputag_mode {
+	CPU_TAG_4B,
+	CPU_TAG_8B,
+	CPU_TAG_MAX
+};
+
+enum yt92xx_work_mode {
+	YT9215_8B,
+	YT9224_4B,
+	YT9224_8B,
+	WORK_MODE_MAX
+};
+
+struct yt92xx_chip_info {
+	u8 switchid;
+	u8 unit;
+	enum yt92xx_chip_mode mode;
+	enum yt92xx_cputag_mode cpu_tag;
+	u32 ageing_time_min;
+	u32 ageing_time_max;
+	u32 max_ports;
+	u32 max_lag_groups;
+	const struct yt92xx_switch_ops *ops;
+	const struct phylink_mac_ops *mac_ops;
+};
+
+struct yt92xx_priv {
+	struct dsa_switch ds;
+	const struct yt92xx_chip_info *chip_info;
+	const struct yt92xx_info *info;
+	unsigned int meter_slot_ns;
+	unsigned int port_shape_slot_ns;
+	/* cache of dsa_cpu_ports(ds) */
+	u16 cpu_ports_mask;
+	unsigned char cycle_ns;
+	/* protect the access to the switch registers */
+	struct mutex reg_lock;
+	void *reg_ctx;
+	/* mdio master bus */
+	struct mii_bus *mbus_int;
+	struct mii_bus *mbus_ext;
+	struct yt92xx_port ports[YT92XX_PORT_NUM];
+	u16 eee_ports_mask;
+#ifdef CONFIG_MOTORCOMM_YT921X
+	DECLARE_BITMAP(meters_map, YT921X_METER_NUM);
+
+	u8 acl_masks[YT921X_ACL_BLK_NUM];
+	struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM];
+#endif
+};
+
+struct yt92xx_switch_ops {
+	int (*stats_read)(struct dsa_switch *ds, int port);
+	int (*stats_eth_mac_stats)(struct dsa_switch *ds, int port,
+				   struct ethtool_eth_mac_stats *mac_stats);
+	int (*stats_rmon_stats)(struct dsa_switch *ds, int port,
+				struct ethtool_rmon_stats *rmon_stats,
+				const struct ethtool_rmon_hist_range **ranges);
+	int (*stats_stats64)(struct dsa_switch *ds, int port,
+			     struct rtnl_link_stats64 *stats);
+	void (*rate_policer_del)(struct dsa_switch *ds, int port);
+	int (*rate_policer_add)(struct dsa_switch *ds, int port,
+				const struct flow_action_police *police,
+				struct netlink_ext_ack *extack);
+	int (*vlan_msti_set)(struct dsa_switch *ds, struct dsa_bridge bridge,
+			     const struct switchdev_vlan_msti *msti);
+	void (*mac_caps)(struct dsa_switch *ds, int port,
+			 struct phylink_config *config);
+	int (*dsa_setup)(struct dsa_switch *ds);
+	int (*port_setup_tc_tbf_port)(struct dsa_switch *ds, int port,
+				      const struct tc_tbf_qopt_offload *qopt);
+	int (*acl_delete)(struct yt92xx_priv *priv, enum tc_setup_type type,
+			  unsigned long tag);
+	int (*acl_add)(struct dsa_switch *ds, int port,
+		       struct flow_cls_offload *cls, bool ingress);
+};
+
+#endif
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
deleted file mode 100644
index 159b16606f6c..000000000000
--- a/drivers/net/dsa/yt921x.c
+++ /dev/null
@@ -1,4982 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Driver for Motorcomm YT921x Switch
- *
- * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII,
- * be sure to do your own checks before porting to another chip.
- *
- * Copyright (c) 2025 David Yang
- */
-
-#include <linux/dcbnl.h>
-#include <linux/etherdevice.h>
-#include <linux/if_bridge.h>
-#include <linux/if_hsr.h>
-#include <linux/if_vlan.h>
-#include <linux/iopoll.h>
-#include <linux/mdio.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_mdio.h>
-#include <linux/of_net.h>
-#include <linux/sort.h>
-
-#include <net/dsa.h>
-#include <net/dscp.h>
-#include <net/ieee8021q.h>
-#include <net/pkt_cls.h>
-
-#include "yt921x.h"
-
-struct yt921x_mib_desc {
-	unsigned int size;
-	unsigned int offset;
-	const char *name;
-};
-
-#define MIB_DESC(_size, _offset, _name) \
-	{_size, _offset, _name}
-
-/* Must agree with yt921x_mib
- *
- * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
- * structured go to their *_stats() methods, but we need their sizes and offsets
- * to perform 32bit MIB overflow wraparound.
- */
-static const struct yt921x_mib_desc yt921x_mib_descs[] = {
-	MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
-
-	MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
-
-	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
-
-	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
-	MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
-
-	MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
-
-	MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
-
-	MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
-
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
-
-	MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
-
-	MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
-
-	MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
-	MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
-	MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
-};
-
-struct yt921x_info {
-	const char *name;
-	u16 major;
-	/* Unknown, seems to be plain enumeration */
-	u8 mode;
-	u8 extmode;
-	/* Ports with integral GbE PHYs, not including MCU Port 10 */
-	u16 internal_mask;
-	/* TODO: see comments in yt921x_dsa_phylink_get_caps() */
-	u16 external_mask;
-};
-
-#define YT921X_PORT_MASK_INTn(port)	BIT(port)
-#define YT921X_PORT_MASK_INT0_n(n)	GENMASK((n) - 1, 0)
-#define YT921X_PORT_MASK_EXT0		BIT(8)
-#define YT921X_PORT_MASK_EXT1		BIT(9)
-
-static const struct yt921x_info yt921x_infos[] = {
-	{
-		"YT9215SC", YT9215_MAJOR, 1, 0,
-		YT921X_PORT_MASK_INT0_n(5),
-		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
-	},
-	{
-		"YT9215S", YT9215_MAJOR, 2, 0,
-		YT921X_PORT_MASK_INT0_n(5),
-		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
-	},
-	{
-		"YT9215RB", YT9215_MAJOR, 3, 0,
-		YT921X_PORT_MASK_INT0_n(5),
-		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
-	},
-	{
-		"YT9214NB", YT9215_MAJOR, 3, 2,
-		YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
-		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
-	},
-	{
-		"YT9213NB", YT9215_MAJOR, 3, 3,
-		YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
-		YT921X_PORT_MASK_EXT1,
-	},
-	{
-		"YT9218N", YT9218_MAJOR, 0, 0,
-		YT921X_PORT_MASK_INT0_n(8),
-		0,
-	},
-	{
-		"YT9218MB", YT9218_MAJOR, 1, 0,
-		YT921X_PORT_MASK_INT0_n(8),
-		YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
-	},
-	{}
-};
-
-#define YT921X_NAME	"yt921x"
-
-#define YT921X_VID_UNWARE	4095
-
-#define YT921X_POLL_SLEEP_US	10000
-#define YT921X_POLL_TIMEOUT_US	100000
-
-/* The interval should be small enough to avoid overflow of 32bit MIBs.
- *
- * Until we can read MIBs from stats64 call directly (i.e. sleep
- * there), we have to poll stats more frequently then it is actually needed.
- * For overflow protection, normally, 100 sec interval should have been OK.
- */
-#define YT921X_STATS_INTERVAL_JIFFIES	(3 * HZ)
-
-struct yt921x_reg_mdio {
-	struct mii_bus *bus;
-	int addr;
-	/* SWITCH_ID_1 / SWITCH_ID_0 of the device
-	 *
-	 * This is a way to multiplex multiple devices on the same MII phyaddr
-	 * and should be configurable in DT. However, MDIO core simply doesn't
-	 * allow multiple devices over one reg addr, so this is a fixed value
-	 * for now until a solution is found.
-	 *
-	 * Keep this because we need switchid to form MII regaddrs anyway.
-	 */
-	unsigned char switchid;
-};
-
-/* TODO: SPI/I2C */
-
-#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)
-#define to_device(priv) ((priv)->ds.dev)
-
-static u32 ethaddr_hi4_to_u32(const unsigned char *addr)
-{
-	return (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
-}
-
-static u32 ethaddr_lo2_to_u32(const unsigned char *addr)
-{
-	return (addr[4] << 8) | addr[5];
-}
-
-static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
-{
-	WARN_ON(!mutex_is_locked(&priv->reg_lock));
-
-	return priv->reg_ops->read(priv->reg_ctx, reg, valp);
-}
-
-static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
-{
-	WARN_ON(!mutex_is_locked(&priv->reg_lock));
-
-	return priv->reg_ops->write(priv->reg_ctx, reg, val);
-}
-
-static int
-yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
-{
-	u32 val;
-	int res;
-	int ret;
-
-	ret = read_poll_timeout(yt921x_reg_read, res,
-				res || (val & mask) == *valp,
-				YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
-				false, priv, reg, &val);
-	if (ret)
-		return ret;
-	if (res)
-		return res;
-
-	*valp = val;
-	return 0;
-}
-
-static int
-yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
-{
-	int res;
-	u32 v;
-	u32 u;
-
-	res = yt921x_reg_read(priv, reg, &v);
-	if (res)
-		return res;
-
-	u = v;
-	u &= ~mask;
-	u |= val;
-	if (u == v)
-		return 0;
-
-	return yt921x_reg_write(priv, reg, u);
-}
-
-static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
-{
-	return yt921x_reg_update_bits(priv, reg, 0, mask);
-}
-
-static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
-{
-	return yt921x_reg_update_bits(priv, reg, mask, 0);
-}
-
-static int
-yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
-{
-	return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
-}
-
-/* Some multi-word registers, like VLANn_CTRL, should be treated as a single
- * long register. More specifically, writes to parts of its words won't become
- * visible, until the last word is written.
- *
- * Here we require full read and write operations over these registers to
- * eliminate potential issues, although partial reads/writes are also possible.
- */
-
-static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val)
-{
-	*lo &= ~lower_32_bits(mask);
-	*hi &= ~upper_32_bits(mask);
-	*lo |= lower_32_bits(val);
-	*hi |= upper_32_bits(val);
-}
-
-static int
-yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals,
-		 unsigned int num_regs)
-{
-	int res;
-
-	for (unsigned int i = 0; i < num_regs; i++) {
-		res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals,
-		  unsigned int num_regs)
-{
-	int res;
-
-	for (unsigned int i = 0; i < num_regs; i++) {
-		res = yt921x_reg_write(priv, reg + 4 * i, vals[i]);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
-			const u32 *vals, unsigned int num_regs)
-{
-	bool changed = false;
-	u32 vs[4];
-	int res;
-
-	BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
-
-	res = yt921x_regs_read(priv, reg, vs, num_regs);
-	if (res)
-		return res;
-
-	for (unsigned int i = 0; i < num_regs; i++) {
-		u32 u = vs[i];
-
-		u &= ~masks[i];
-		u |= vals[i];
-		if (u != vs[i])
-			changed = true;
-
-		vs[i] = u;
-	}
-
-	if (!changed)
-		return 0;
-
-	return yt921x_regs_write(priv, reg, vs, num_regs);
-}
-
-static int
-yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
-		       unsigned int num_regs)
-{
-	bool changed = false;
-	u32 vs[4];
-	int res;
-
-	BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs));
-
-	res = yt921x_regs_read(priv, reg, vs, num_regs);
-	if (res)
-		return res;
-
-	for (unsigned int i = 0; i < num_regs; i++) {
-		u32 u = vs[i];
-
-		u &= ~masks[i];
-		if (u != vs[i])
-			changed = true;
-
-		vs[i] = u;
-	}
-
-	if (!changed)
-		return 0;
-
-	return yt921x_regs_write(priv, reg, vs, num_regs);
-}
-
-static int
-yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
-{
-	return yt921x_regs_write(priv, reg, vals, 2);
-}
-
-static int
-yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks,
-			 const u32 *vals)
-{
-	return yt921x_regs_update_bits(priv, reg, masks, vals, 2);
-}
-
-static int
-yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks)
-{
-	return yt921x_regs_clear_bits(priv, reg, masks, 2);
-}
-
-static int
-yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals)
-{
-	return yt921x_regs_write(priv, reg, vals, 3);
-}
-
-static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
-{
-	struct yt921x_reg_mdio *mdio = context;
-	struct mii_bus *bus = mdio->bus;
-	int addr = mdio->addr;
-	u32 reg_addr;
-	u32 reg_data;
-	u32 val;
-	int res;
-
-	/* Hold the mdio bus lock to avoid (un)locking for 4 times */
-	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
-	reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
-		   YT921X_SMI_READ;
-	res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
-	if (res)
-		goto end;
-	res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
-	if (res)
-		goto end;
-
-	reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
-		   YT921X_SMI_READ;
-	res = __mdiobus_read(bus, addr, reg_data);
-	if (res < 0)
-		goto end;
-	val = (u16)res;
-	res = __mdiobus_read(bus, addr, reg_data);
-	if (res < 0)
-		goto end;
-	val = (val << 16) | (u16)res;
-
-	*valp = val;
-	res = 0;
-
-end:
-	mutex_unlock(&bus->mdio_lock);
-	return res;
-}
-
-static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)
-{
-	struct yt921x_reg_mdio *mdio = context;
-	struct mii_bus *bus = mdio->bus;
-	int addr = mdio->addr;
-	u32 reg_addr;
-	u32 reg_data;
-	int res;
-
-	mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
-
-	reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
-		   YT921X_SMI_WRITE;
-	res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
-	if (res)
-		goto end;
-	res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
-	if (res)
-		goto end;
-
-	reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
-		   YT921X_SMI_WRITE;
-	res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16));
-	if (res)
-		goto end;
-	res = __mdiobus_write(bus, addr, reg_data, (u16)val);
-	if (res)
-		goto end;
-
-	res = 0;
-
-end:
-	mutex_unlock(&bus->mdio_lock);
-	return res;
-}
-
-static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {
-	.read = yt921x_reg_mdio_read,
-	.write = yt921x_reg_mdio_write,
-};
-
-/* TODO: SPI/I2C */
-
-static int yt921x_intif_wait(struct yt921x_priv *priv)
-{
-	u32 val = 0;
-
-	return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START,
-			       &val);
-}
-
-static int
-yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
-{
-	struct device *dev = to_device(priv);
-	u32 mask;
-	u32 ctrl;
-	u32 val;
-	int res;
-
-	res = yt921x_intif_wait(priv);
-	if (res)
-		return res;
-
-	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
-	       YT921X_MBUS_CTRL_OP_M;
-	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
-	       YT921X_MBUS_CTRL_READ;
-	res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
-	if (res)
-		return res;
-
-	res = yt921x_intif_wait(priv);
-	if (res)
-		return res;
-	res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val);
-	if (res)
-		return res;
-
-	if ((u16)val != val)
-		dev_info(dev,
-			 "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
-			 __func__, port, reg, val);
-	*valp = (u16)val;
-	return 0;
-}
-
-static int
-yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
-{
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	res = yt921x_intif_wait(priv);
-	if (res)
-		return res;
-
-	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
-	       YT921X_MBUS_CTRL_OP_M;
-	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
-	       YT921X_MBUS_CTRL_WRITE;
-	res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
-	if (res)
-		return res;
-
-	return yt921x_intif_wait(priv);
-}
-
-static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
-{
-	struct yt921x_priv *priv = mbus->priv;
-	u16 val;
-	int res;
-
-	if (port >= YT921X_PORT_NUM)
-		return U16_MAX;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_intif_read(priv, port, reg, &val);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		return res;
-	return val;
-}
-
-static int
-yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)
-{
-	struct yt921x_priv *priv = mbus->priv;
-	int res;
-
-	if (port >= YT921X_PORT_NUM)
-		return -ENODEV;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_intif_write(priv, port, reg, data);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)
-{
-	struct device *dev = to_device(priv);
-	struct mii_bus *mbus;
-	int res;
-
-	mbus = devm_mdiobus_alloc(dev);
-	if (!mbus)
-		return -ENOMEM;
-
-	mbus->name = "YT921x internal MDIO bus";
-	snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
-	mbus->priv = priv;
-	mbus->read = yt921x_mbus_int_read;
-	mbus->write = yt921x_mbus_int_write;
-	mbus->parent = dev;
-	mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0);
-
-	res = devm_of_mdiobus_register(dev, mbus, mnp);
-	if (res)
-		return res;
-
-	priv->mbus_int = mbus;
-
-	return 0;
-}
-
-static int yt921x_extif_wait(struct yt921x_priv *priv)
-{
-	u32 val = 0;
-
-	return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START,
-			       &val);
-}
-
-static int
-yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
-{
-	struct device *dev = to_device(priv);
-	u32 mask;
-	u32 ctrl;
-	u32 val;
-	int res;
-
-	res = yt921x_extif_wait(priv);
-	if (res)
-		return res;
-
-	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
-	       YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
-	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
-	       YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ;
-	res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
-	if (res)
-		return res;
-
-	res = yt921x_extif_wait(priv);
-	if (res)
-		return res;
-	res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val);
-	if (res)
-		return res;
-
-	if ((u16)val != val)
-		dev_info(dev,
-			 "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
-			 __func__, port, reg, val);
-	*valp = (u16)val;
-	return 0;
-}
-
-static int
-yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
-{
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	res = yt921x_extif_wait(priv);
-	if (res)
-		return res;
-
-	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
-	       YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
-	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
-	       YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE;
-	res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
-	if (res)
-		return res;
-
-	return yt921x_extif_wait(priv);
-}
-
-static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg)
-{
-	struct yt921x_priv *priv = mbus->priv;
-	u16 val;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_extif_read(priv, port, reg, &val);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		return res;
-	return val;
-}
-
-static int
-yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data)
-{
-	struct yt921x_priv *priv = mbus->priv;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_extif_write(priv, port, reg, data);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
-{
-	struct device *dev = to_device(priv);
-	struct mii_bus *mbus;
-	int res;
-
-	mbus = devm_mdiobus_alloc(dev);
-	if (!mbus)
-		return -ENOMEM;
-
-	mbus->name = "YT921x external MDIO bus";
-	snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev));
-	mbus->priv = priv;
-	/* TODO: c45? */
-	mbus->read = yt921x_mbus_ext_read;
-	mbus->write = yt921x_mbus_ext_write;
-	mbus->parent = dev;
-
-	res = devm_of_mdiobus_register(dev, mbus, mnp);
-	if (res)
-		return res;
-
-	priv->mbus_ext = mbus;
-
-	return 0;
-}
-
-/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
-static int yt921x_read_mib(struct yt921x_priv *priv, int port)
-{
-	struct yt921x_port *pp = &priv->ports[port];
-	struct device *dev = to_device(priv);
-	struct yt921x_mib *mib = &pp->mib;
-	int res = 0;
-
-	/* Reading of yt921x_port::mib is not protected by a lock and it's vain
-	 * to keep its consistency, since we have to read registers one by one
-	 * and there is no way to make a snapshot of MIB stats.
-	 *
-	 * Writing (by this function only) is and should be protected by
-	 * reg_lock.
-	 */
-
-	for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
-		const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-		u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
-		u64 *valp = &((u64 *)mib)[i];
-		u32 val0;
-		u64 val;
-
-		res = yt921x_reg_read(priv, reg, &val0);
-		if (res)
-			break;
-
-		if (desc->size <= 1) {
-			u64 old_val = *valp;
-
-			val = (old_val & ~(u64)U32_MAX) | val0;
-			if (val < old_val)
-				val += 1ull << 32;
-		} else {
-			u32 val1;
-
-			res = yt921x_reg_read(priv, reg + 4, &val1);
-			if (res)
-				break;
-			val = ((u64)val1 << 32) | val0;
-		}
-
-		WRITE_ONCE(*valp, val);
-	}
-
-	pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
-			mib->rx_128_255byte + mib->rx_256_511byte +
-			mib->rx_512_1023byte + mib->rx_1024_1518byte +
-			mib->rx_jumbo;
-	pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
-			mib->tx_128_255byte + mib->tx_256_511byte +
-			mib->tx_512_1023byte + mib->tx_1024_1518byte +
-			mib->tx_jumbo;
-
-	if (res)
-		dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
-			port, res);
-	return res;
-}
-
-static void yt921x_poll_mib(struct work_struct *work)
-{
-	struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
-						    mib_read.work);
-	struct yt921x_priv *priv = (void *)(pp - pp->index) -
-				   offsetof(struct yt921x_priv, ports);
-	unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
-	int port = pp->index;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_read_mib(priv, port);
-	mutex_unlock(&priv->reg_lock);
-	if (res)
-		delay *= 4;
-
-	schedule_delayed_work(&pp->mib_read, delay);
-}
-
-static void
-yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
-		       uint8_t *data)
-{
-	if (stringset != ETH_SS_STATS)
-		return;
-
-	for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
-		const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
-		if (desc->name)
-			ethtool_puts(&data, desc->name);
-	}
-}
-
-static void
-yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct yt921x_port *pp = &priv->ports[port];
-	struct yt921x_mib *mib = &pp->mib;
-	size_t j;
-
-	mutex_lock(&priv->reg_lock);
-	yt921x_read_mib(priv, port);
-	mutex_unlock(&priv->reg_lock);
-
-	j = 0;
-	for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
-		const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
-		if (!desc->name)
-			continue;
-
-		data[j] = ((u64 *)mib)[i];
-		j++;
-	}
-}
-
-static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
-{
-	int cnt = 0;
-
-	if (sset != ETH_SS_STATS)
-		return 0;
-
-	for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
-		const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
-
-		if (desc->name)
-			cnt++;
-	}
-
-	return cnt;
-}
-
-static void
-yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
-			     struct ethtool_eth_mac_stats *mac_stats)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct yt921x_port *pp = &priv->ports[port];
-	struct yt921x_mib *mib = &pp->mib;
-
-	mutex_lock(&priv->reg_lock);
-	yt921x_read_mib(priv, port);
-	mutex_unlock(&priv->reg_lock);
-
-	mac_stats->FramesTransmittedOK = pp->tx_frames;
-	mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
-	mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
-	mac_stats->FramesReceivedOK = pp->rx_frames;
-	mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
-	mac_stats->AlignmentErrors = mib->rx_alignment_errors;
-	mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
-	mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
-	mac_stats->LateCollisions = mib->tx_late_collisions;
-	mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
-	/* mac_stats->FramesLostDueToIntMACXmitError */
-	/* mac_stats->CarrierSenseErrors */
-	mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
-	/* mac_stats->FramesLostDueToIntMACRcvError */
-	mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
-	mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
-	/* mac_stats->FramesWithExcessiveDeferral */
-	mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
-	mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
-	/* mac_stats->InRangeLengthErrors */
-	/* mac_stats->OutOfRangeLengthField */
-	mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
-}
-
-static void
-yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
-			      struct ethtool_eth_ctrl_stats *ctrl_stats)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct yt921x_port *pp = &priv->ports[port];
-	struct yt921x_mib *mib = &pp->mib;
-
-	mutex_lock(&priv->reg_lock);
-	yt921x_read_mib(priv, port);
-	mutex_unlock(&priv->reg_lock);
-
-	ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
-	ctrl_stats->MACControlFramesReceived = mib->rx_pause;
-	/* ctrl_stats->UnsupportedOpcodesReceived */
-}
-
-static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
-	{ 0, 64 },
-	{ 65, 127 },
-	{ 128, 255 },
-	{ 256, 511 },
-	{ 512, 1023 },
-	{ 1024, 1518 },
-	{ 1519, YT921X_FRAME_SIZE_MAX },
-	{}
-};
-
-static void
-yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
-			  struct ethtool_rmon_stats *rmon_stats,
-			  const struct ethtool_rmon_hist_range **ranges)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct yt921x_port *pp = &priv->ports[port];
-	struct yt921x_mib *mib = &pp->mib;
-
-	mutex_lock(&priv->reg_lock);
-	yt921x_read_mib(priv, port);
-	mutex_unlock(&priv->reg_lock);
-
-	*ranges = yt921x_rmon_ranges;
-
-	rmon_stats->undersize_pkts = mib->rx_undersize_errors;
-	rmon_stats->oversize_pkts = mib->rx_oversize_errors;
-	rmon_stats->fragments = mib->rx_alignment_errors;
-	/* rmon_stats->jabbers */
-
-	rmon_stats->hist[0] = mib->rx_64byte;
-	rmon_stats->hist[1] = mib->rx_65_127byte;
-	rmon_stats->hist[2] = mib->rx_128_255byte;
-	rmon_stats->hist[3] = mib->rx_256_511byte;
-	rmon_stats->hist[4] = mib->rx_512_1023byte;
-	rmon_stats->hist[5] = mib->rx_1024_1518byte;
-	rmon_stats->hist[6] = mib->rx_jumbo;
-
-	rmon_stats->hist_tx[0] = mib->tx_64byte;
-	rmon_stats->hist_tx[1] = mib->tx_65_127byte;
-	rmon_stats->hist_tx[2] = mib->tx_128_255byte;
-	rmon_stats->hist_tx[3] = mib->tx_256_511byte;
-	rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
-	rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
-	rmon_stats->hist_tx[6] = mib->tx_jumbo;
-}
-
-static void
-yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
-		       struct rtnl_link_stats64 *stats)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct yt921x_port *pp = &priv->ports[port];
-	struct yt921x_mib *mib = &pp->mib;
-
-	stats->rx_length_errors = mib->rx_undersize_errors +
-				  mib->rx_fragment_errors;
-	stats->rx_over_errors = mib->rx_oversize_errors;
-	stats->rx_crc_errors = mib->rx_crc_errors;
-	stats->rx_frame_errors = mib->rx_alignment_errors;
-	/* stats->rx_fifo_errors */
-	/* stats->rx_missed_errors */
-
-	stats->tx_aborted_errors = mib->tx_aborted_errors;
-	/* stats->tx_carrier_errors */
-	stats->tx_fifo_errors = mib->tx_undersize_errors;
-	/* stats->tx_heartbeat_errors */
-	stats->tx_window_errors = mib->tx_late_collisions;
-
-	stats->rx_packets = pp->rx_frames;
-	stats->tx_packets = pp->tx_frames;
-	stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
-	stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
-	stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
-			   stats->rx_crc_errors + stats->rx_frame_errors;
-	stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
-			   stats->tx_window_errors;
-	stats->rx_dropped = mib->rx_dropped;
-	/* stats->tx_dropped */
-	stats->multicast = mib->rx_multicast;
-	stats->collisions = mib->tx_collisions;
-}
-
-static void
-yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
-			   struct ethtool_pause_stats *pause_stats)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct yt921x_port *pp = &priv->ports[port];
-	struct yt921x_mib *mib = &pp->mib;
-
-	mutex_lock(&priv->reg_lock);
-	yt921x_read_mib(priv, port);
-	mutex_unlock(&priv->reg_lock);
-
-	pause_stats->tx_pause_frames = mib->tx_pause;
-	pause_stats->rx_pause_frames = mib->rx_pause;
-}
-
-static int
-yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
-{
-	/* Poor datasheet for EEE operations; don't ask if you are confused */
-
-	bool enable = e->eee_enabled;
-	u16 new_mask;
-	int res;
-
-	/* Enable / disable global EEE */
-	new_mask = priv->eee_ports_mask;
-	new_mask &= ~BIT(port);
-	new_mask |= !enable ? 0 : BIT(port);
-
-	if (!!new_mask != !!priv->eee_ports_mask) {
-		res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC,
-					     YT921X_PON_STRAP_EEE, !!new_mask);
-		if (res)
-			return res;
-		res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL,
-					     YT921X_PON_STRAP_EEE, !!new_mask);
-		if (res)
-			return res;
-	}
-
-	priv->eee_ports_mask = new_mask;
-
-	/* Enable / disable port EEE */
-	res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL,
-				     YT921X_EEE_CTRL_ENn(port), enable);
-	if (res)
-		return res;
-	res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port),
-				     YT921X_EEE_VAL_DATA, enable);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int
-yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_set_eee(priv, port, e);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int yt921x_mtu_fetch(struct yt921x_priv *priv, int port)
-{
-	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
-
-	return dp->user ? READ_ONCE(dp->user->mtu) : ETH_DATA_LEN;
-}
-
-static int
-yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
-{
-	/* Only serves as packet filter, since the frame size is always set to
-	 * maximum after reset
-	 */
-
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct dsa_port *dp = dsa_to_port(ds, port);
-	int frame_size;
-	int res;
-
-	frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN;
-	if (dsa_port_is_cpu(dp))
-		frame_size += YT921X_TAG_LEN;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port),
-				     YT921X_MAC_FRAME_SIZE_M,
-				     YT921X_MAC_FRAME_SIZE(frame_size));
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)
-{
-	/* Only called for user ports, exclude tag len here */
-	return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN;
-}
-
-/* v * 2^e */
-static u64 ldexpu64(u64 v, int e)
-{
-	return e >= 0 ? v << e : v >> -e;
-}
-
-/* slot (ns) * rate (/s) / 10^9 (ns/s) = 2^C * token * 4^unit */
-static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C)
-{
-	int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
-
-	return div_u64(ldexpu64(slot_ns * rate, -e), 1000000000);
-}
-
-static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C)
-{
-	int e = 2 * unit + C + YT921X_TOKEN_RATE_C;
-
-	return div_u64(ldexpu64(mul_u32_u32(1000000000, token), e), slot_ns);
-}
-
-/* burst = 2^C * token * 4^unit */
-static u32 burst2token(u64 burst, int unit, int C)
-{
-	return ldexpu64(burst, -(2 * unit + C));
-}
-
-static u64 token2burst(u32 token, int unit, int C)
-{
-	return ldexpu64(token, 2 * unit + C);
-}
-
-struct yt921x_marker {
-	u32 cir;
-	u32 cbs;
-	u32 ebs;
-	int unit;
-	bool pkt_mode;
-};
-
-#define YT921X_MARKER_PKT_MODE		BIT(0)
-#define YT921X_MARKER_SINGLE_BUCKET	BIT(1)
-
-static int
-yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst,
-		  unsigned int flags, unsigned int slot_ns, u32 cir_max,
-		  u32 cbs_max, int unit_max, struct yt921x_priv *priv, int port,
-		  struct netlink_ext_ack *extack)
-{
-	const int C = flags & YT921X_MARKER_PKT_MODE ? YT921X_TOKEN_PKT_C :
-		      YT921X_TOKEN_BYTE_C;
-	struct device *dev = to_device(priv);
-	struct yt921x_marker m;
-	u64 burst_est;
-	u64 burst_sug;
-	u64 burst_max;
-	u64 rate_max;
-
-	m.unit = unit_max;
-	rate_max = token2rate(cir_max, slot_ns, m.unit, C);
-	burst_max = token2burst(cbs_max, m.unit, C);
-
-	/* Check for unusual values */
-	if (rate > rate_max || burst > burst_max) {
-		NL_SET_ERR_MSG_MOD(extack, "Unexpected tremendous rate");
-		return -ERANGE;
-	}
-
-	/* Check for matching burst */
-	burst_est = div_u64(slot_ns * rate, 1000000000);
-	burst_sug = burst_est;
-	if (flags & YT921X_MARKER_PKT_MODE)
-		burst_sug++;
-	else
-		burst_sug += ETH_HLEN + yt921x_mtu_fetch(priv, port) +
-			     ETH_FCS_LEN;
-	if (burst_sug > burst)
-		NL_SET_ERR_MSG_FMT_MOD(extack,
-				       "Consider match rate %llu with burst at least %llu",
-				       rate, burst_sug);
-
-	/* Select unit */
-	for (; m.unit > 0; m.unit--) {
-		if (rate > (rate_max >> 2) || burst > (burst_max >> 2))
-			break;
-		rate_max >>= 2;
-		burst_max >>= 2;
-	}
-
-	/* Calculate information rate and bucket size */
-	m.cir = rate2token(rate, slot_ns, m.unit, C);
-	if (!m.cir)
-		m.cir = 1;
-	else if (WARN_ON(m.cir > cir_max))
-		m.cir = cir_max;
-	m.cbs = burst2token(burst, m.unit, C);
-	if (!m.cbs)
-		m.cbs = 1;
-	else if (WARN_ON(m.cbs > cbs_max))
-		m.cbs = cbs_max;
-
-	/* Cut EBS */
-	m.ebs = 0;
-	if (!(flags & YT921X_MARKER_SINGLE_BUCKET)) {
-		/* We don't have a chance to adjust rate when MTU is changed */
-		if (flags & YT921X_MARKER_PKT_MODE)
-			burst_est++;
-		else
-			burst_est += YT921X_FRAME_SIZE_MAX;
-
-		if (burst_est < burst) {
-			u32 pbs = m.cbs;
-
-			m.cbs = burst2token(burst_est, m.unit, C);
-			if (!m.cbs)
-				m.cbs = 1;
-			else if (WARN_ON(m.cbs > cbs_max))
-				m.cbs = cbs_max;
-
-			if (pbs > m.cbs)
-				m.ebs = pbs - m.cbs;
-		}
-	}
-
-	dev_dbg(dev,
-		"slot %u ns, rate %llu, burst %llu -> unit %d, cir %u, cbs %u, ebs %u\n",
-		slot_ns, rate, burst, m.unit, m.cir, m.cbs, m.ebs);
-
-	m.pkt_mode = flags & YT921X_MARKER_PKT_MODE;
-	*marker = m;
-	return 0;
-}
-
-static int
-yt921x_marker_tfm_police(struct yt921x_marker *marker,
-			 const struct flow_action_police *police,
-			 unsigned int flags, struct yt921x_priv *priv, int port,
-			 struct netlink_ext_ack *extack)
-{
-	bool pkt_mode = !!police->rate_pkt_ps;
-	u64 burst;
-	u64 rate;
-
-	rate = pkt_mode ? police->rate_pkt_ps : police->rate_bytes_ps;
-	burst = pkt_mode ? police->burst_pkt : police->burst;
-	if (pkt_mode)
-		flags |= YT921X_MARKER_PKT_MODE;
-
-	return yt921x_marker_tfm(marker, rate, burst, flags,
-				 priv->meter_slot_ns, YT921X_METER_CIR_MAX,
-				 YT921X_METER_CBS_MAX, YT921X_METER_UNIT_MAX,
-				 priv, port, extack);
-}
-
-static int
-yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst,
-			unsigned int flags, struct yt921x_priv *priv, int port,
-			struct netlink_ext_ack *extack)
-{
-	return yt921x_marker_tfm(marker, rate, burst, flags,
-				 priv->port_shape_slot_ns, YT921X_SHAPE_CIR_MAX,
-				 YT921X_SHAPE_CBS_MAX, YT921X_SHAPE_UNIT_MAX,
-				 priv, port, extack);
-}
-
-static int
-yt921x_police_validate(const struct flow_action_police *police,
-		       const struct flow_action *action,
-		       const struct flow_action_entry *act,
-		       struct netlink_ext_ack *extack)
-{
-	if (police->exceed.act_id != FLOW_ACTION_DROP) {
-		NL_SET_ERR_MSG_MOD(extack,
-				   "Offload not supported when exceed action is not drop");
-		return -EOPNOTSUPP;
-	}
-
-	if (police->notexceed.act_id != FLOW_ACTION_PIPE &&
-	    police->notexceed.act_id != FLOW_ACTION_ACCEPT) {
-		NL_SET_ERR_MSG_MOD(extack,
-				   "Offload not supported when conform action is not pipe or ok");
-		return -EOPNOTSUPP;
-	}
-
-	if (police->notexceed.act_id == FLOW_ACTION_ACCEPT && action && act &&
-	    !flow_action_is_last_entry(action, act)) {
-		NL_SET_ERR_MSG_MOD(extack,
-				   "Offload not supported when conform action is ok, but action is not last");
-		return -EOPNOTSUPP;
-	}
-
-	/* mtu defaults to unlimited but we got 2040 here, don't know why */
-	if (police->peakrate_bytes_ps || police->avrate || police->overhead) {
-		NL_SET_ERR_MSG_MOD(extack,
-				   "Offload not supported when peakrate/avrate/overhead is configured");
-		return -EOPNOTSUPP;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_meter_config(struct yt921x_priv *priv, unsigned int id,
-		    const struct yt921x_marker *marker)
-{
-	u32 ctrls[3];
-
-	ctrls[0] = 0;
-	ctrls[1] = YT921X_METER_CTRLb_CIR(marker->cir);
-	ctrls[2] = YT921X_METER_CTRLc_UNIT(marker->unit) |
-		   YT921X_METER_CTRLc_DROP_R |
-		   YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN |
-		   YT921X_METER_CTRLc_METER_EN;
-	if (marker->pkt_mode)
-		ctrls[2] |= YT921X_METER_CTRLc_PKT_MODE;
-	update_ctrls_unaligned(&ctrls[0], &ctrls[1],
-			       YT921X_METER_CTRLab_EBS_M,
-			       YT921X_METER_CTRLab_EBS(marker->ebs));
-	update_ctrls_unaligned(&ctrls[1], &ctrls[2],
-			       YT921X_METER_CTRLbc_CBS_M,
-			       YT921X_METER_CTRLbc_CBS(marker->cbs));
-
-	return yt921x_reg96_write(priv, YT921X_METERn_CTRL(id), ctrls);
-}
-
-static void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct device *dev = to_device(priv);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), 0);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		dev_err(dev, "Failed to %s port %d: %i\n", "delete policer on",
-			port, res);
-}
-
-static int
-yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port,
-			    const struct flow_action_police *police,
-			    struct netlink_ext_ack *extack)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct yt921x_marker marker;
-	u32 ctrl;
-	int res;
-
-	res = yt921x_police_validate(police, NULL, NULL, extack);
-	if (res)
-		return res;
-
-	res = yt921x_marker_tfm_police(&marker, police, 0, priv, port, extack);
-	if (res)
-		return res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_meter_config(priv, port + YT921X_METER_NUM, &marker);
-	if (res)
-		goto end;
-
-	ctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN;
-	res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), ctrl);
-end:
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port,
-				  const struct tc_tbf_qopt_offload *qopt)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct netlink_ext_ack *extack = qopt->extack;
-	u32 ctrls[2];
-	int res;
-
-	if (qopt->parent != TC_H_ROOT)
-		return -EOPNOTSUPP;
-
-	switch (qopt->command) {
-	case TC_TBF_STATS:
-		/* Unfortunately the convention for TC_*_STATS is a mess,
-		 * neither 0 nor -EOPNOTSUPP is perfect here.
-		 */
-		return -EOPNOTSUPP;
-	case TC_TBF_DESTROY:
-		ctrls[0] = 0;
-		ctrls[1] = 0;
-		break;
-	case TC_TBF_REPLACE: {
-		const struct tc_tbf_qopt_offload_replace_params *p;
-		struct yt921x_marker marker;
-
-		p = &qopt->replace_params;
-
-		res = yt921x_marker_tfm_shape(&marker, p->rate.rate_bytes_ps,
-					      p->max_size,
-					      YT921X_MARKER_SINGLE_BUCKET,
-					      priv, port, extack);
-		if (res)
-			return res;
-
-		ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) |
-			   YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs);
-		ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) |
-			   YT921X_PORT_SHAPE_CTRLb_EN;
-		break;
-	}
-	default:
-		return -EOPNOTSUPP;
-	}
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port,
-			 enum tc_setup_type type, void *type_data)
-{
-	switch (type) {
-	case TC_SETUP_QDISC_TBF: {
-		const struct tc_tbf_qopt_offload *qopt = type_data;
-
-		return yt921x_dsa_port_setup_tc_tbf_port(ds, port, qopt);
-	}
-	default:
-		return -EOPNOTSUPP;
-	}
-}
-
-/* ACL: 48 blocks * 8 entries
- *
- * One rule can span multiple entries, but within a block.
- */
-
-static void
-yt921x_acl_entry_set(struct yt921x_acl_entry *entry, unsigned int offset,
-		     u32 flags, bool set)
-{
-	if (set)
-		entry->key[offset] |= flags;
-	entry->mask[offset] |= flags;
-}
-
-static unsigned int
-yt921x_acl_entries_set_is_fragment(struct yt921x_acl_entry *entries,
-				   unsigned int size, bool set)
-{
-	for (unsigned int i = 0; i < size; i++)
-		switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
-		case YT921X_ACL_TYPE_IPV4_DA:
-		case YT921X_ACL_TYPE_IPV4_SA:
-			yt921x_acl_entry_set(&entries[i], 1,
-					     YT921X_ACL_BINb_IPV4_FRAG, set);
-			return size;
-		case YT921X_ACL_TYPE_IPV6_DA3:
-		case YT921X_ACL_TYPE_IPV6_SA3:
-			yt921x_acl_entry_set(&entries[i], 1,
-					     YT921X_ACL_BINb_IPV6_xA3_FRAG,
-					     set);
-			return size;
-		case YT921X_ACL_TYPE_MISC:
-			yt921x_acl_entry_set(&entries[i], 1,
-					     YT921X_ACL_BINb_MISC_FRAG, set);
-			return size;
-		case YT921X_ACL_TYPE_L4:
-			yt921x_acl_entry_set(&entries[i], 1,
-					     YT921X_ACL_BINb_L4_FRAG, set);
-			return size;
-		}
-
-	if (size >= YT921X_ACL_ENT_PER_BLK)
-		return 0;
-
-	entries[size] = (typeof(*entries)){};
-	entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
-	yt921x_acl_entry_set(&entries[size], 1, YT921X_ACL_BINb_MISC_FRAG, set);
-
-	return size + 1;
-}
-
-static unsigned int
-yt921x_acl_entries_set_first_frag(struct yt921x_acl_entry *entries,
-				  unsigned int size, bool set)
-{
-	for (unsigned int i = 0; i < size; i++)
-		switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
-		case YT921X_ACL_TYPE_IPV6_DA2:
-		case YT921X_ACL_TYPE_IPV6_SA2:
-			yt921x_acl_entry_set(&entries[i], 1,
-					     YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG,
-					     set);
-			return size;
-		case YT921X_ACL_TYPE_MISC:
-			yt921x_acl_entry_set(&entries[i], 0,
-					     YT921X_ACL_BINa_MISC_FIRST_FRAG,
-					     set);
-			return size;
-		}
-
-	if (size >= YT921X_ACL_ENT_PER_BLK)
-		return 0;
-
-	entries[size] = (typeof(*entries)){};
-	entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
-	yt921x_acl_entry_set(&entries[size], 0,
-			     YT921X_ACL_BINa_MISC_FIRST_FRAG, set);
-
-	return size + 1;
-}
-
-static unsigned int
-yt921x_acl_entries_set_l3_type(struct yt921x_acl_entry *entries,
-			       unsigned int size, enum yt921x_l3_type type)
-{
-	for (unsigned int i = 0; i < size; i++)
-		switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
-		case YT921X_ACL_TYPE_MAC_DA0:
-		case YT921X_ACL_TYPE_MAC_SA0:
-			entries[i].key[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE(type);
-			entries[i].mask[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M;
-			return size;
-		case YT921X_ACL_TYPE_MISC:
-			entries[i].key[0] |= YT921X_ACL_BINa_MISC_L3_TYPE(type);
-			entries[i].mask[0] |= YT921X_ACL_BINa_MISC_L3_TYPE_M;
-			return size;
-		}
-
-	if (size >= YT921X_ACL_ENT_PER_BLK)
-		return 0;
-
-	entries[size] = (typeof(*entries)){};
-	entries[size].key[0] = YT921X_ACL_BINa_MISC_L3_TYPE(type);
-	entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
-	entries[size].mask[0] = YT921X_ACL_BINa_MISC_L3_TYPE_M;
-
-	return size + 1;
-}
-
-static unsigned int
-yt921x_acl_entries_set_l4_type(struct yt921x_acl_entry *entries,
-			       unsigned int size, enum yt921x_l4_type type)
-{
-	for (unsigned int i = 0; i < size; i++)
-		switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) {
-		case YT921X_ACL_TYPE_IPV4_DA:
-		case YT921X_ACL_TYPE_IPV4_SA:
-			entries[i].key[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE(type);
-			entries[i].mask[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE_M;
-			return size;
-		case YT921X_ACL_TYPE_IPV6_DA0:
-		case YT921X_ACL_TYPE_IPV6_DA1:
-		case YT921X_ACL_TYPE_IPV6_DA2:
-		case YT921X_ACL_TYPE_IPV6_DA3:
-		case YT921X_ACL_TYPE_IPV6_SA0:
-		case YT921X_ACL_TYPE_IPV6_SA1:
-		case YT921X_ACL_TYPE_IPV6_SA2:
-		case YT921X_ACL_TYPE_IPV6_SA3:
-			entries[i].key[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE(type);
-			entries[i].mask[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE_M;
-			return size;
-		case YT921X_ACL_TYPE_L4:
-			entries[i].key[1] |= YT921X_ACL_BINb_L4_TYPE(type);
-			entries[i].mask[1] |= YT921X_ACL_BINb_L4_TYPE_M;
-			return size;
-		case YT921X_ACL_TYPE_MISC:
-			entries[i].key[1] |= YT921X_ACL_BINb_MISC_L4_TYPE(type);
-			entries[i].mask[1] |= YT921X_ACL_BINb_MISC_L4_TYPE_M;
-			return size;
-		}
-
-	if (size >= YT921X_ACL_ENT_PER_BLK)
-		return 0;
-
-	entries[size] = (typeof(*entries)){};
-	entries[size].key[1] = YT921X_ACL_BINb_MISC_L4_TYPE(type) |
-			       YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC);
-	entries[size].mask[1] = YT921X_ACL_BINb_MISC_L4_TYPE_M;
-
-	return size + 1;
-}
-
-static struct yt921x_acl_entry *
-yt921x_acl_entries_new(struct yt921x_acl_entry *entries, unsigned int *sizep,
-		       u32 type)
-{
-	unsigned int size = *sizep;
-
-	if (size >= YT921X_ACL_ENT_PER_BLK)
-		return NULL;
-
-	entries[size] = (typeof(*entries)){};
-	entries[size].key[1] = YT921X_ACL_KEYb_TYPE(type);
-
-	(*sizep)++;
-	return &entries[size];
-}
-
-static struct yt921x_acl_entry *
-yt921x_acl_entries_find(struct yt921x_acl_entry *entries, unsigned int *sizep,
-			u32 type)
-{
-	for (unsigned int i = 0; i < *sizep; i++)
-		if (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1]) ==
-		    type)
-			return &entries[i];
-	return yt921x_acl_entries_new(entries, sizep, type);
-}
-
-static void
-yt921x_acl_rule_set_ports(struct yt921x_acl_rule *aclrule, u16 ord,
-			  u16 ports_mask)
-{
-	struct yt921x_acl_entry *entries = aclrule->entries;
-
-	for (unsigned int i = 0; i < hweight8(aclrule->mask); i++) {
-		entries[i].key[1] |= YT921X_ACL_KEYb_SPORTS(ports_mask) |
-				     YT921X_ACL_KEYb_ORD(ord);
-	}
-}
-
-struct yt921x_acl_rule_ext {
-	struct yt921x_acl_rule r;
-
-	struct yt921x_marker marker;
-};
-
-static int
-yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext,
-				       const struct flow_cls_offload *cls)
-{
-	const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
-	struct yt921x_acl_entry *entries = ruleext->r.entries;
-	struct netlink_ext_ack *extack = cls->common.extack;
-	const struct flow_dissector *dissector;
-	struct yt921x_acl_entry *entry;
-	unsigned int size = 0;
-	bool use_dport;
-	bool use_sport;
-
-	/* Incomplete and probably won't, since it supports custom u32 filters.
-	 * New adapters are welcome.
-	 */
-	dissector = rule->match.dissector;
-	if (dissector->used_keys &
-	    ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
-	      BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
-	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
-	      BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
-	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
-	      BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) |
-	      BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
-	      BIT_ULL(FLOW_DISSECTOR_KEY_IP) |
-	      BIT_ULL(FLOW_DISSECTOR_KEY_TCP))) {
-		NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used");
-		return -EOPNOTSUPP;
-	}
-
-	/* Entries */
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
-		struct flow_match_ipv4_addrs match;
-
-		flow_rule_match_ipv4_addrs(rule, &match);
-
-		if (match.mask->dst) {
-			entry = yt921x_acl_entries_new(entries, &size,
-						       YT921X_ACL_TYPE_IPV4_DA);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= ntohl(match.key->dst);
-			entry->mask[0] |= ntohl(match.mask->dst);
-		}
-
-		if (match.mask->src) {
-			entry = yt921x_acl_entries_new(entries, &size,
-						       YT921X_ACL_TYPE_IPV4_SA);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= ntohl(match.key->src);
-			entry->mask[0] |= ntohl(match.mask->src);
-		}
-	}
-
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
-		struct flow_match_ipv6_addrs match;
-
-		flow_rule_match_ipv6_addrs(rule, &match);
-
-		for (unsigned int i = 0; i < 4; i++) {
-			if (!match.mask->dst.s6_addr32[i])
-				continue;
-
-			entry = yt921x_acl_entries_new(entries, &size,
-						       YT921X_ACL_TYPE_IPV6_DA0 + i);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= ntohl(match.key->dst.s6_addr32[i]);
-			entry->mask[0] |= ntohl(match.mask->dst.s6_addr32[i]);
-		}
-
-		for (unsigned int i = 0; i < 4; i++) {
-			if (!match.mask->src.s6_addr32[i])
-				continue;
-
-			entry = yt921x_acl_entries_new(entries, &size,
-						       YT921X_ACL_TYPE_IPV6_SA0 + i);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= ntohl(match.key->src.s6_addr32[i]);
-			entry->mask[0] |= ntohl(match.mask->src.s6_addr32[i]);
-		}
-	}
-
-	use_dport = false;
-	use_sport = false;
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
-		struct flow_match_ports match;
-
-		entry = yt921x_acl_entries_new(entries, &size,
-					       YT921X_ACL_TYPE_L4);
-		if (!entry)
-			goto err;
-
-		flow_rule_match_ports(rule, &match);
-
-		use_dport = !!match.mask->dst;
-		use_sport = !!match.mask->src;
-
-		entry->key[0] |= (ntohs(match.key->dst) << 16) |
-				 ntohs(match.key->src);
-		entry->mask[0] |= (ntohs(match.mask->dst) << 16) |
-				  ntohs(match.mask->src);
-	}
-
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) {
-		struct flow_match_ports_range match;
-
-		entry = yt921x_acl_entries_find(entries, &size,
-						YT921X_ACL_TYPE_L4);
-		if (!entry)
-			goto err;
-
-		flow_rule_match_ports_range(rule, &match);
-
-		if ((use_dport && match.mask->tp.dst) ||
-		    (use_sport && match.mask->tp.src)) {
-			NL_SET_ERR_MSG_MOD(extack,
-					   "Port mask and range are mutually exclusive");
-			return -EINVAL;
-		}
-
-		if (match.mask->tp.dst) {
-			entry->key[0] |= ntohs(match.key->tp_min.dst) << 16;
-			entry->key[1] |= YT921X_ACL_KEYb_L4_DPORT_RANGE_EN;
-			entry->mask[0] |= ntohs(match.key->tp_max.dst) << 16;
-		}
-
-		if (match.mask->tp.src) {
-			entry->key[0] |= ntohs(match.key->tp_min.src);
-			entry->key[1] |= YT921X_ACL_KEYb_L4_SPORT_RANGE_EN;
-			entry->mask[0] |= ntohs(match.key->tp_max.src);
-		}
-	}
-
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
-		struct flow_match_eth_addrs match;
-		u32 mask;
-
-		flow_rule_match_eth_addrs(rule, &match);
-
-		mask = ethaddr_hi4_to_u32(match.mask->dst);
-		if (mask) {
-			entry = yt921x_acl_entries_new(entries, &size,
-						       YT921X_ACL_TYPE_MAC_DA0);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= ethaddr_hi4_to_u32(match.key->dst);
-			entry->mask[0] |= mask;
-		}
-
-		mask = ethaddr_hi4_to_u32(match.mask->src);
-		if (mask) {
-			entry = yt921x_acl_entries_new(entries, &size,
-						       YT921X_ACL_TYPE_MAC_SA0);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= ethaddr_hi4_to_u32(match.key->src);
-			entry->mask[0] |= mask;
-		}
-
-		mask = (ethaddr_lo2_to_u32(match.mask->dst) << 16) |
-		       ethaddr_lo2_to_u32(match.mask->src);
-		if (mask) {
-			entry = yt921x_acl_entries_new(entries, &size,
-						       YT921X_ACL_TYPE_MAC_DA1_SA1);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= (ethaddr_lo2_to_u32(match.key->dst) << 16) |
-					 ethaddr_lo2_to_u32(match.key->src);
-			entry->mask[0] |= mask;
-		}
-	}
-
-	/* Entries + Misc */
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
-		struct flow_match_basic match;
-
-		flow_rule_match_basic(rule, &match);
-
-		if (match.mask->n_proto) {
-			enum yt921x_l3_type l3type = YT921X_L3_TYPE_OTHER;
-
-			if (match.mask->n_proto == htons(~0))
-				switch (match.key->n_proto) {
-				case htons(ETH_P_IP):
-					l3type = YT921X_L3_TYPE_IPV4;
-					break;
-				case htons(ETH_P_IPV6):
-					l3type = YT921X_L3_TYPE_IPV6;
-					break;
-				case htons(ETH_P_ARP):
-					l3type = YT921X_L3_TYPE_ARP;
-					break;
-				case htons(ETH_P_LLDP):
-					l3type = YT921X_L3_TYPE_LLDP;
-					break;
-				case htons(ETH_P_PAE):
-					l3type = YT921X_L3_TYPE_PAE;
-					break;
-				case htons(ETH_P_CFM):
-					l3type = YT921X_L3_TYPE_ERP;
-					break;
-				}
-
-			if (l3type != YT921X_L3_TYPE_OTHER) {
-				size = yt921x_acl_entries_set_l3_type(entries,
-								      size,
-								      l3type);
-				if (!size)
-					goto err;
-			} else {
-				entry = yt921x_acl_entries_new(entries, &size,
-							       YT921X_ACL_TYPE_ETHERTYPE);
-				if (!entry)
-					goto err;
-
-				entry->key[0] |= ntohs(match.key->n_proto);
-				entry->mask[0] |= ntohs(match.mask->n_proto);
-			}
-		}
-
-		if (match.mask->ip_proto) {
-			enum yt921x_l4_type l4type = YT921X_L4_TYPE_OTHER;
-
-			if (match.mask->ip_proto == (u8)~0)
-				switch (match.key->ip_proto) {
-				case IPPROTO_TCP:
-					l4type = YT921X_L4_TYPE_TCP;
-					break;
-				case IPPROTO_UDP:
-					l4type = YT921X_L4_TYPE_UDP;
-					break;
-				case IPPROTO_UDPLITE:
-					l4type = YT921X_L4_TYPE_UDPLITE;
-					break;
-				case IPPROTO_ICMP:
-					l4type = YT921X_L4_TYPE_ICMP;
-					break;
-				case IPPROTO_IGMP:
-					l4type = YT921X_L4_TYPE_IGMP;
-					break;
-				}
-
-			if (l4type != YT921X_L4_TYPE_OTHER) {
-				size = yt921x_acl_entries_set_l4_type(entries,
-								      size,
-								      l4type);
-				if (!size)
-					goto err;
-			} else {
-				entry = yt921x_acl_entries_find(entries, &size,
-								YT921X_ACL_TYPE_MISC);
-				if (!entry)
-					goto err;
-
-				entry->key[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.key->ip_proto);
-				entry->mask[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.mask->ip_proto);
-			}
-		}
-	}
-
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
-		u32 supp_flags = FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG;
-		struct flow_match_control match;
-
-		flow_rule_match_control(rule, &match);
-		if (!flow_rule_is_supp_control_flags(supp_flags,
-						     match.mask->flags, extack))
-			return -EOPNOTSUPP;
-
-		if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
-			bool set = match.key->flags & FLOW_DIS_IS_FRAGMENT;
-
-			size = yt921x_acl_entries_set_is_fragment(entries, size,
-								  set);
-			if (!size)
-				goto err;
-		}
-		if (match.mask->flags & FLOW_DIS_FIRST_FRAG) {
-			bool set = match.key->flags & FLOW_DIS_FIRST_FRAG;
-
-			size = yt921x_acl_entries_set_first_frag(entries, size,
-								 set);
-			if (!size)
-				goto err;
-		}
-	}
-
-	/* Misc only */
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
-		struct flow_match_ip match;
-
-		flow_rule_match_ip(rule, &match);
-		if (match.mask->ttl) {
-			NL_SET_ERR_MSG_MOD(extack,
-					   "Matching on TTL not supported");
-			return -EOPNOTSUPP;
-		}
-
-		if (match.mask->tos) {
-			entry = yt921x_acl_entries_find(entries, &size,
-							YT921X_ACL_TYPE_MISC);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= YT921X_ACL_BINa_MISC_TOS(match.key->tos);
-			entry->mask[0] |= YT921X_ACL_BINa_MISC_TOS(match.mask->tos);
-		}
-	}
-
-	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) {
-		struct flow_match_tcp match;
-
-		flow_rule_match_tcp(rule, &match);
-		if (match.mask->flags & htons(~0xff)) {
-			NL_SET_ERR_MSG_MOD(extack, "Unsupported TCP flags");
-			return -EOPNOTSUPP;
-		}
-
-		if (match.mask->flags) {
-			entry = yt921x_acl_entries_find(entries, &size,
-							YT921X_ACL_TYPE_MISC);
-			if (!entry)
-				goto err;
-
-			entry->key[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.key->flags));
-			entry->mask[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.mask->flags));
-		}
-	}
-
-	if (!size) {
-		NL_SET_ERR_MSG_MOD(extack, "Empty rule generated, this should not happen");
-		return -EOPNOTSUPP;
-	}
-
-	ruleext->r.mask = (1 << size) - 1;
-	return 0;
-
-err:
-	NL_SET_ERR_MSG_MOD(extack, "Rule too complex");
-	return -EOPNOTSUPP;
-}
-
-static int
-yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext,
-				      const struct flow_cls_offload *cls,
-				      struct yt921x_priv *priv, int port)
-{
-	const struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
-	const struct flow_action *flow_action = &rule->action;
-	struct netlink_ext_ack *extack = cls->common.extack;
-	enum flow_action_id redir_act = NUM_FLOW_ACTIONS;
-	const struct flow_action_entry *act;
-	u32 *action = ruleext->r.action;
-	bool seen_priority = false;
-	const char *reason = NULL;
-	bool seen_police = false;
-	unsigned int i;
-	int res;
-
-	memset(action, 0, 3 * sizeof(*action));
-	flow_action_for_each(i, act, flow_action)
-		switch (act->id) {
-		case FLOW_ACTION_ACCEPT:
-		case FLOW_ACTION_DROP:
-		case FLOW_ACTION_REDIRECT:
-			if (redir_act != NUM_FLOW_ACTIONS &&
-			    redir_act != act->id) {
-				reason = "Different redirect actions";
-				goto fallback;
-			}
-			redir_act = act->id;
-
-			switch (act->id) {
-			case FLOW_ACTION_ACCEPT:
-				action[2] |= YT921X_ACL_ACTc_FWD_EN |
-					     YT921X_ACL_ACTc_FWD_FWD;
-				break;
-			case FLOW_ACTION_DROP:
-				action[2] |= YT921X_ACL_ACTc_FWD_EN |
-					     YT921X_ACL_ACTc_FWD_REDIR;
-				break;
-			case FLOW_ACTION_REDIRECT: {
-				struct dsa_port *to_dp;
-
-				to_dp = dsa_port_from_netdev(act->dev);
-				if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) {
-					reason = "Redirect to non-local port";
-					goto fallback;
-				}
-
-				action[2] |= YT921X_ACL_ACTc_FWD_EN |
-					     YT921X_ACL_ACTc_FWD_REDIR |
-					     YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index);
-				break;
-			}
-			default:
-				break;
-			}
-			break;
-		case FLOW_ACTION_PRIORITY:
-			if (seen_priority) {
-				action[0] &= ~YT921X_ACL_ACTa_PRIO_EN;
-				action[1] &= ~YT921X_ACL_ACTb_PRIO_M;
-
-				reason = "Multiple priority actions";
-				goto fallback;
-			}
-			seen_priority = true;
-
-			if (act->priority >= YT921X_PRIO_NUM) {
-				NL_SET_ERR_MSG_MOD(extack,
-						   "Priority value is too high");
-				return -EOPNOTSUPP;
-			}
-			action[0] |= YT921X_ACL_ACTa_PRIO_EN;
-			action[1] |= YT921X_ACL_ACTb_PRIO(act->priority);
-			break;
-		case FLOW_ACTION_POLICE: {
-			const struct flow_action_police *police = &act->police;
-
-			if (seen_police) {
-				action[0] &= ~YT921X_ACL_ACTa_METER_EN;
-
-				reason = "Multiple police actions";
-				goto fallback;
-			}
-			seen_police = true;
-
-			res = yt921x_police_validate(police, flow_action, act,
-						     extack);
-			if (res)
-				return res;
-
-			res = yt921x_marker_tfm_police(&ruleext->marker, police,
-						       0, priv, port, extack);
-			if (res)
-				return res;
-
-			action[0] |= YT921X_ACL_ACTa_METER_EN;
-			break;
-		}
-		default:
-fallback:
-			if (cls->common.skip_sw) {
-				NL_SET_ERR_MSG_FMT_MOD(extack,
-						       "Action not supported when skip_sw: %s",
-						       reason);
-				return -EOPNOTSUPP;
-			}
-			fallthrough;
-		case FLOW_ACTION_TRAP:
-			redir_act = FLOW_ACTION_TRAP;
-
-			action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
-				     ~YT921X_ACL_ACTc_FWD_M;
-			action[2] |= YT921X_ACL_ACTc_FWD_EN |
-				     YT921X_ACL_ACTc_FWD_TRAP;
-			break;
-		}
-
-	ruleext->r.sw_assisted = !cls->common.skip_sw;
-	return 0;
-}
-
-static int
-yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port,
-			       const struct flow_cls_offload *cls, bool ingress,
-			       struct yt921x_priv *priv)
-{
-	struct netlink_ext_ack *extack = cls->common.extack;
-	int res;
-
-	if (!ingress) {
-		NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
-		return -EOPNOTSUPP;
-	}
-
-	if (cls->common.chain_index) {
-		NL_SET_ERR_MSG(extack, "Only chain 0 is supported");
-		return -EOPNOTSUPP;
-	}
-
-	res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port);
-	if (res)
-		return res;
-	res = yt921x_acl_rule_ext_parse_flow_entries(ruleext, cls);
-	if (res)
-		return res;
-
-	yt921x_acl_rule_set_ports(&ruleext->r, 0, BIT(port));
-	ruleext->r.tag = cls->cookie;
-	ruleext->r.type = TC_SETUP_CLSFLOWER;
-	return 0;
-}
-
-static unsigned int
-yt921x_acl_find(const struct yt921x_priv *priv, enum tc_setup_type type,
-		unsigned long tag)
-{
-	for (unsigned int blkid = 0; blkid < YT921X_ACL_BLK_NUM; blkid++) {
-		const struct yt921x_acl_blk *aclblk = priv->acl_blks[blkid];
-
-		if (!aclblk)
-			continue;
-
-		for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++)
-			if (aclblk->rules[i] && aclblk->rules[i]->tag == tag &&
-			    aclblk->rules[i]->type == type)
-				return YT921X_ACL_ENT_PER_BLK * blkid + i;
-	}
-
-	return UINT_MAX;
-}
-
-static unsigned int
-yt921x_acl_reserve(struct yt921x_priv *priv, unsigned int entscnt,
-		   struct netlink_ext_ack *extack)
-{
-	int candidates[YT921X_ACL_ENT_PER_BLK + 1];
-	unsigned int acl_used_cnt = 0;
-
-	if (WARN_ON(entscnt > YT921X_ACL_ENT_PER_BLK))
-		return UINT_MAX;
-
-	for (unsigned int i = 0; i < ARRAY_SIZE(candidates); i++)
-		candidates[i] = -1;
-	for (unsigned int i = YT921X_ACL_BLK_NUM; i-- > 0;) {
-		unsigned int blk_used_cnt = hweight8(priv->acl_masks[i]);
-
-		candidates[blk_used_cnt] = i;
-		acl_used_cnt += blk_used_cnt;
-	}
-
-	if (acl_used_cnt >= YT921X_ACL_NUM) {
-		NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
-		return UINT_MAX;
-	}
-	if (acl_used_cnt + entscnt <= YT921X_ACL_NUM)
-		for (unsigned int i = YT921X_ACL_ENT_PER_BLK - entscnt + 1;
-		     i-- > 0;)
-			if (candidates[i] >= 0)
-				return YT921X_ACL_ENT_PER_BLK * candidates[i] +
-				       ffz(priv->acl_masks[candidates[i]]);
-
-	NL_SET_ERR_MSG_MOD(extack,
-			   "ACL entry allocation failed, simplify your rules or remove existing rules");
-	return UINT_MAX;
-}
-
-static int
-yt921x_acl_commit(struct yt921x_priv *priv, unsigned int entid, u8 entsmask)
-{
-	const struct yt921x_acl_rule *aclrule;
-	const struct yt921x_acl_blk *aclblk;
-	unsigned int blkid;
-	unsigned int binid;
-	unsigned long mask;
-	u32 zeros[3] = {};
-	unsigned int i;
-	unsigned int o;
-	u32 ctrl;
-	int res;
-
-	blkid = entid / YT921X_ACL_ENT_PER_BLK;
-	binid = entid % YT921X_ACL_ENT_PER_BLK;
-	aclblk = priv->acl_blks[blkid];
-	aclrule = aclblk->rules[binid];
-
-	/* Write actions */
-	res = yt921x_reg96_write(priv, YT921X_ACLn_ACT(entid),
-				 aclrule ? aclrule->action : zeros);
-	if (res)
-		return res;
-
-	/* Select the block */
-	ctrl = YT921X_ACL_BLK_CMD_MODIFY | YT921X_ACL_BLK_CMD_BLKID(blkid);
-	res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl);
-	if (res)
-		return res;
-
-	/* Write keys and masks */
-	ctrl = 0;
-	for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++)
-		ctrl |= YT921X_ACL_BLK_KEEP_KEEPn(i);
-
-	mask = entsmask;
-	i = 0;
-	for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
-		res = yt921x_reg64_write(priv, YT921X_ACLn_KEYm(blkid, o),
-					 aclrule ? aclrule->entries[i].key :
-					 zeros);
-		if (res)
-			return res;
-
-		res = yt921x_reg64_write(priv, YT921X_ACLn_MASKm(blkid, o),
-					 aclrule ? aclrule->entries[i].mask :
-					 zeros);
-		if (res)
-			return res;
-
-		ctrl &= ~YT921X_ACL_BLK_KEEP_KEEPn(o);
-		i++;
-	}
-
-	res = yt921x_reg_write(priv, YT921X_ACL_BLK_KEEP, ctrl);
-	if (res)
-		return res;
-
-	ctrl = 0;
-	for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) {
-		const struct yt921x_acl_rule *other = aclblk->rules[i];
-
-		if (!other)
-			continue;
-
-		mask = other->mask;
-		for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK)
-			ctrl |= YT921X_ACL_ENTRY_ENm(o) |
-				YT921X_ACL_ENTRY_GRPIDm(o, i);
-	}
-	res = yt921x_reg_write(priv, YT921X_ACLn_ENTRY(blkid), ctrl);
-	if (res)
-		return res;
-
-	/* Commit the block */
-	ctrl = YT921X_ACL_BLK_CMD_BLKID(blkid);
-	res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int
-yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type,
-	       unsigned long tag)
-{
-	struct yt921x_acl_rule *aclrule;
-	struct yt921x_acl_blk *aclblk;
-	unsigned int binid;
-	unsigned int blkid;
-	unsigned int entid;
-	int res;
-
-	entid = yt921x_acl_find(priv, type, tag);
-	if (entid == UINT_MAX)
-		return -ENOENT;
-
-	blkid = entid / YT921X_ACL_ENT_PER_BLK;
-	binid = entid % YT921X_ACL_ENT_PER_BLK;
-	aclblk = priv->acl_blks[blkid];
-	aclrule = aclblk->rules[binid];
-
-	aclblk->rules[binid] = NULL;
-	res = yt921x_acl_commit(priv, entid, aclrule->mask);
-	/* the kernel never rolls back on failure */
-
-	if (aclrule->action[0] & YT921X_ACL_ACTa_METER_EN)
-		clear_bit(FIELD_GET(YT921X_ACL_ACTa_METER_ID_M,
-				    aclrule->action[0]),
-			  priv->meters_map);
-	priv->acl_masks[blkid] &= ~aclrule->mask;
-	kvfree(aclrule);
-	if (!priv->acl_masks[blkid]) {
-		kvfree(aclblk);
-		priv->acl_blks[blkid] = NULL;
-	}
-	return res;
-}
-
-static int
-yt921x_acl_add(struct yt921x_priv *priv,
-	       const struct yt921x_acl_rule_ext *ruleext,
-	       struct netlink_ext_ack *extack)
-{
-	unsigned int entscnt = hweight8(ruleext->r.mask);
-	struct yt921x_acl_rule *aclrule;
-	struct yt921x_acl_blk *aclblk;
-	bool use_trap = false;
-	unsigned int meterid;
-	unsigned long mask;
-	unsigned int binid;
-	unsigned int blkid;
-	unsigned int entid;
-	unsigned int o;
-	int res;
-
-	/* Allocate resources */
-	entid = yt921x_acl_reserve(priv, entscnt, extack);
-	if (entid == UINT_MAX)
-		return -EOPNOTSUPP;
-
-	if (!(ruleext->r.action[0] & YT921X_ACL_ACTa_METER_EN)) {
-		meterid = YT921X_METER_NUM;
-	} else {
-		meterid = find_first_zero_bit(priv->meters_map,
-					      YT921X_METER_NUM);
-		if (meterid < YT921X_METER_NUM) {
-			res = yt921x_meter_config(priv, meterid,
-						  &ruleext->marker);
-			if (res)
-				return res;
-		} else if (ruleext->r.sw_assisted) {
-			use_trap = true;
-		} else {
-			NL_SET_ERR_MSG_MOD(extack,
-					   "No more meters available");
-			return -EOPNOTSUPP;
-		}
-	}
-
-	/* Prepare acl block ctrlblk */
-	blkid = entid / YT921X_ACL_ENT_PER_BLK;
-	binid = entid % YT921X_ACL_ENT_PER_BLK;
-	aclblk = priv->acl_blks[blkid];
-	if (!aclblk) {
-		aclblk = kvzalloc_obj(*aclblk);
-		if (!aclblk)
-			return -ENOMEM;
-		priv->acl_blks[blkid] = aclblk;
-	}
-
-	/* Prepare acl rule ctrlblk */
-	aclrule = kvmemdup(&ruleext->r,
-			   offsetof(struct yt921x_acl_rule, entries[entscnt]),
-			   GFP_KERNEL);
-	if (!aclrule) {
-		res = -ENOMEM;
-		goto err;
-	}
-
-	/* Replace the placeholder resource IDs */
-	aclrule->mask = 0;
-	mask = priv->acl_masks[blkid];
-	for_each_clear_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) {
-		aclrule->mask |= BIT(o);
-		entscnt--;
-		if (!entscnt)
-			break;
-	}
-
-	if (use_trap) {
-		aclrule->action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M &
-				      ~YT921X_ACL_ACTc_FWD_M;
-		aclrule->action[2] |= YT921X_ACL_ACTc_FWD_EN |
-				      YT921X_ACL_ACTc_FWD_TRAP;
-	}
-	if (meterid < YT921X_METER_NUM)
-		aclrule->action[0] |= YT921X_ACL_ACTa_METER_ID(meterid);
-	else
-		aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN;
-
-	/* Write rules */
-	aclblk->rules[binid] = aclrule;
-	res = yt921x_acl_commit(priv, entid, aclrule->mask);
-	if (res) {
-		aclblk->rules[binid] = NULL;
-		kvfree(aclrule);
-		goto err;
-	}
-
-	if (meterid < YT921X_METER_NUM)
-		set_bit(meterid, priv->meters_map);
-	priv->acl_masks[blkid] |= aclrule->mask;
-	return 0;
-
-err:
-	if (!priv->acl_masks[blkid]) {
-		kvfree(aclblk);
-		priv->acl_blks[blkid] = NULL;
-	}
-	return res;
-}
-
-static int
-yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port,
-			  struct flow_cls_offload *cls, bool ingress)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_acl_del(priv, TC_SETUP_CLSFLOWER, cls->cookie);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port,
-			  struct flow_cls_offload *cls, bool ingress)
-{
-	struct netlink_ext_ack *extack = cls->common.extack;
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct yt921x_acl_rule_ext ruleext;
-	int res;
-
-	res = yt921x_acl_rule_ext_parse_flow(&ruleext, port, cls, ingress,
-					     priv);
-	if (res)
-		return res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_acl_add(priv, &ruleext, extack);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
-{
-	u32 mask;
-
-	if (ingress)
-		mask = YT921X_MIRROR_IGR_PORTn(port);
-	else
-		mask = YT921X_MIRROR_EGR_PORTn(port);
-	return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask);
-}
-
-static int
-yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress,
-		  int to_local_port, struct netlink_ext_ack *extack)
-{
-	u32 srcs;
-	u32 ctrl;
-	u32 val;
-	u32 dst;
-	int res;
-
-	if (ingress)
-		srcs = YT921X_MIRROR_IGR_PORTn(port);
-	else
-		srcs = YT921X_MIRROR_EGR_PORTn(port);
-	dst = YT921X_MIRROR_PORT(to_local_port);
-
-	res = yt921x_reg_read(priv, YT921X_MIRROR, &val);
-	if (res)
-		return res;
-
-	/* other mirror tasks & different dst port -> conflict */
-	if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M |
-			    YT921X_MIRROR_IGR_PORTS_M)) &&
-	    (val & YT921X_MIRROR_PORT_M) != dst) {
-		NL_SET_ERR_MSG_MOD(extack,
-				   "Sniffer port is already configured, delete existing rules & retry");
-		return -EBUSY;
-	}
-
-	ctrl = val & ~YT921X_MIRROR_PORT_M;
-	ctrl |= srcs;
-	ctrl |= dst;
-
-	if (ctrl == val)
-		return 0;
-
-	return yt921x_reg_write(priv, YT921X_MIRROR, ctrl);
-}
-
-static void
-yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port,
-			   struct dsa_mall_mirror_tc_entry *mirror)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct device *dev = to_device(priv);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_mirror_del(priv, port, mirror->ingress);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		dev_err(dev, "Failed to %s port %d: %i\n", "unmirror",
-			port, res);
-}
-
-static int
-yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port,
-			   struct dsa_mall_mirror_tc_entry *mirror,
-			   bool ingress, struct netlink_ext_ack *extack)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_mirror_add(priv, port, ingress,
-				mirror->to_local_port, extack);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int yt921x_lag_hash(struct yt921x_priv *priv, u32 ctrl, bool unique_lag,
-			   struct netlink_ext_ack *extack)
-{
-	u32 val;
-	int res;
-
-	/* Hash Mode is global. Make sure the same Hash Mode is set to all the
-	 * 2 possible lags.
-	 * If we are the unique LAG we can set whatever hash mode we want.
-	 * To change hash mode it's needed to remove all LAG and change the mode
-	 * with the latest.
-	 */
-	if (unique_lag) {
-		res = yt921x_reg_write(priv, YT921X_LAG_HASH, ctrl);
-		if (res)
-			return res;
-	} else {
-		res = yt921x_reg_read(priv, YT921X_LAG_HASH, &val);
-		if (res)
-			return res;
-
-		if (val != ctrl) {
-			NL_SET_ERR_MSG_MOD(extack,
-					   "Mismatched Hash Mode across different lags is not supported");
-			return -EOPNOTSUPP;
-		}
-	}
-
-	return 0;
-}
-
-static int yt921x_lag_set(struct yt921x_priv *priv, u8 index, u16 ports_mask)
-{
-	unsigned long targets_mask = ports_mask;
-	unsigned int cnt;
-	u32 ctrl;
-	int port;
-	int res;
-
-	cnt = 0;
-	for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
-		ctrl = YT921X_LAG_MEMBER_PORT(port);
-		res = yt921x_reg_write(priv, YT921X_LAG_MEMBERnm(index, cnt),
-				       ctrl);
-		if (res)
-			return res;
-
-		cnt++;
-	}
-
-	ctrl = YT921X_LAG_GROUP_PORTS(ports_mask) |
-	       YT921X_LAG_GROUP_MEMBER_NUM(cnt);
-	return yt921x_reg_write(priv, YT921X_LAG_GROUPn(index), ctrl);
-}
-
-static int
-yt921x_dsa_port_lag_leave(struct dsa_switch *ds, int port, struct dsa_lag lag)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct dsa_port *dp;
-	u32 ctrl;
-	int res;
-
-	if (!lag.id)
-		return -EINVAL;
-
-	ctrl = 0;
-	dsa_lag_foreach_port(dp, ds->dst, &lag)
-		ctrl |= BIT(dp->index);
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_lag_set(priv, lag.id - 1, ctrl);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_lag_check(struct dsa_switch *ds, struct dsa_lag lag,
-			  struct netdev_lag_upper_info *info,
-			  struct netlink_ext_ack *extack)
-{
-	unsigned int members;
-	struct dsa_port *dp;
-
-	if (!lag.id)
-		return -EINVAL;
-
-	members = 0;
-	dsa_lag_foreach_port(dp, ds->dst, &lag)
-		/* Includes the port joining the LAG */
-		members++;
-
-	if (members > YT921X_LAG_PORT_NUM) {
-		NL_SET_ERR_MSG_MOD(extack,
-				   "Cannot offload more than 4 LAG ports");
-		return -EOPNOTSUPP;
-	}
-
-	if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
-		NL_SET_ERR_MSG_MOD(extack,
-				   "Can only offload LAG using hash TX type");
-		return -EOPNOTSUPP;
-	}
-
-	if (info->hash_type != NETDEV_LAG_HASH_L2 &&
-	    info->hash_type != NETDEV_LAG_HASH_L23 &&
-	    info->hash_type != NETDEV_LAG_HASH_L34) {
-		NL_SET_ERR_MSG_MOD(extack,
-				   "Can only offload L2 or L2+L3 or L3+L4 TX hash");
-		return -EOPNOTSUPP;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_dsa_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag,
-			 struct netdev_lag_upper_info *info,
-			 struct netlink_ext_ack *extack)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct dsa_port *dp;
-	bool unique_lag;
-	unsigned int i;
-	u32 ctrl;
-	int res;
-
-	res = yt921x_dsa_port_lag_check(ds, lag, info, extack);
-	if (res)
-		return res;
-
-	ctrl = 0;
-	switch (info->hash_type) {
-	case NETDEV_LAG_HASH_L34:
-		ctrl |= YT921X_LAG_HASH_IP_DST;
-		ctrl |= YT921X_LAG_HASH_IP_SRC;
-		ctrl |= YT921X_LAG_HASH_IP_PROTO;
-
-		ctrl |= YT921X_LAG_HASH_L4_DPORT;
-		ctrl |= YT921X_LAG_HASH_L4_SPORT;
-		break;
-	case NETDEV_LAG_HASH_L23:
-		ctrl |= YT921X_LAG_HASH_MAC_DA;
-		ctrl |= YT921X_LAG_HASH_MAC_SA;
-
-		ctrl |= YT921X_LAG_HASH_IP_DST;
-		ctrl |= YT921X_LAG_HASH_IP_SRC;
-		ctrl |= YT921X_LAG_HASH_IP_PROTO;
-		break;
-	case NETDEV_LAG_HASH_L2:
-		ctrl |= YT921X_LAG_HASH_MAC_DA;
-		ctrl |= YT921X_LAG_HASH_MAC_SA;
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-
-	/* Check if we are the unique configured LAG */
-	unique_lag = true;
-	dsa_lags_foreach_id(i, ds->dst)
-		if (i != lag.id && dsa_lag_by_id(ds->dst, i)) {
-			unique_lag = false;
-			break;
-		}
-
-	mutex_lock(&priv->reg_lock);
-	do {
-		res = yt921x_lag_hash(priv, ctrl, unique_lag, extack);
-		if (res)
-			break;
-
-		ctrl = 0;
-		dsa_lag_foreach_port(dp, ds->dst, &lag)
-			ctrl |= BIT(dp->index);
-		res = yt921x_lag_set(priv, lag.id - 1, ctrl);
-	} while (0);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp)
-{
-	struct device *dev = to_device(priv);
-	u32 val = YT921X_FDB_RESULT_DONE;
-	int res;
-
-	res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE,
-			      &val);
-	if (res) {
-		dev_err(dev, "FDB probably stuck\n");
-		return res;
-	}
-
-	*valp = val;
-	return 0;
-}
-
-static int
-yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr,
-		u16 vid, u32 ctrl1)
-{
-	u32 ctrl;
-	int res;
-
-	ctrl = ethaddr_hi4_to_u32(addr);
-	res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl);
-	if (res)
-		return res;
-
-	ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | ethaddr_lo2_to_u32(addr);
-	return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
-}
-
-static int
-yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
-	       u16 *indexp)
-{
-	u32 ctrl;
-	u32 val;
-	int res;
-
-	res = yt921x_fdb_in01(priv, addr, vid, 0);
-	if (res)
-		return res;
-
-	ctrl = 0;
-	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
-	if (res)
-		return res;
-
-	ctrl = 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;
-	if (val & YT921X_FDB_RESULT_NOTFOUND) {
-		*indexp = YT921X_FDB_NUM;
-		return 0;
-	}
-
-	*indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
-	return 0;
-}
-
-static int
-yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp,
-		u16 *ports_maskp, u16 *indexp, u8 *statusp)
-{
-	struct device *dev = to_device(priv);
-	u16 index;
-	u32 data0;
-	u32 data1;
-	u32 data2;
-	u32 val;
-	int res;
-
-	res = yt921x_fdb_wait(priv, &val);
-	if (res)
-		return res;
-	if (val & YT921X_FDB_RESULT_NOTFOUND) {
-		*ports_maskp = 0;
-		return 0;
-	}
-	index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
-
-	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1);
-	if (res)
-		return res;
-	if ((data1 & YT921X_FDB_IO1_STATUS_M) ==
-	    YT921X_FDB_IO1_STATUS_INVALID) {
-		*ports_maskp = 0;
-		return 0;
-	}
-
-	res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0);
-	if (res)
-		return res;
-	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2);
-	if (res)
-		return res;
-
-	addr[0] = data0 >> 24;
-	addr[1] = data0 >> 16;
-	addr[2] = data0 >> 8;
-	addr[3] = data0;
-	addr[4] = data1 >> 8;
-	addr[5] = data1;
-	*vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1);
-	*indexp = index;
-	*ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2);
-	*statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1);
-
-	dev_dbg(dev,
-		"%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n",
-		__func__, *indexp, addr[0], addr[1], addr[2], addr[3],
-		addr[4], addr[5], *vidp, *ports_maskp, *statusp);
-	return 0;
-}
-
-static int
-yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask,
-		dsa_fdb_dump_cb_t *cb, void *data)
-{
-	unsigned char addr[ETH_ALEN];
-	u8 status;
-	u16 pmask;
-	u16 index;
-	u32 ctrl;
-	u16 vid;
-	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_read(priv, addr, &vid, &pmask, &index, &status);
-	if (res)
-		return res;
-	if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
-		res = cb(addr, vid,
-			 status == YT921X_FDB_ENTRY_STATUS_STATIC, data);
-		if (res)
-			return res;
-	}
-
-	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
-	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
-	if (res)
-		return res;
-
-	index = 0;
-	do {
-		ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
-		       YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT |
-		       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_read(priv, addr, &vid, &pmask, &index,
-				      &status);
-		if (res)
-			return res;
-		if (!pmask)
-			break;
-
-		if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
-			res = cb(addr, vid,
-				 status == YT921X_FDB_ENTRY_STATUS_STATIC,
-				 data);
-			if (res)
-				return res;
-		}
-
-		/* Never call GET_NEXT with 4095, otherwise it will hang
-		 * forever until a reset!
-		 */
-	} while (index < YT921X_FDB_NUM - 1);
-
-	return 0;
-}
-
-static int
-yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid,
-		     bool flush_static)
-{
-	u32 ctrl;
-	u32 val;
-	int res;
-
-	if (vid < 4096) {
-		ctrl = YT921X_FDB_IO1_FID(vid);
-		res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
-		if (res)
-			return res;
-	}
-
-	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
-	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
-	if (res)
-		return res;
-
-	ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START;
-	if (vid >= 4096)
-		ctrl |= YT921X_FDB_OP_FLUSH_PORT;
-	else
-		ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID;
-	if (flush_static)
-		ctrl |= YT921X_FDB_OP_FLUSH_STATIC;
-	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
-	if (res)
-		return res;
-
-	res = yt921x_fdb_wait(priv, &val);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int
-yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static)
-{
-	return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static);
-}
-
-static int
-yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1,
-			  u16 ctrl2)
-{
-	u32 ctrl;
-	u32 val;
-	int res;
-
-	res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2);
-	if (res)
-		return res;
-
-	ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
-	       YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
-	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
-	if (res)
-		return res;
-
-	return yt921x_fdb_wait(priv, &val);
-}
-
-static int
-yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
-	       u16 ports_mask)
-{
-	u32 ctrl;
-	u32 val;
-	int res;
-
-	ctrl = YT921X_FDB_IO1_STATUS_STATIC;
-	res = yt921x_fdb_in01(priv, addr, vid, ctrl);
-	if (res)
-		return res;
-
-	ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
-	res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
-	if (res)
-		return res;
-
-	ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
-	res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
-	if (res)
-		return res;
-
-	return yt921x_fdb_wait(priv, &val);
-}
-
-static int
-yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
-		 u16 vid, u16 ports_mask)
-{
-	u16 index;
-	u32 ctrl1;
-	u32 ctrl2;
-	u32 ctrl;
-	u32 val2;
-	u32 val;
-	int res;
-
-	/* Check for presence */
-	res = yt921x_fdb_has(priv, addr, vid, &index);
-	if (res)
-		return res;
-	if (index >= YT921X_FDB_NUM)
-		return 0;
-
-	/* Check if action required */
-	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
-	if (res)
-		return res;
-
-	ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask);
-	if (ctrl2 == val2)
-		return 0;
-	if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) {
-		ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START;
-		res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
-		if (res)
-			return res;
-
-		return yt921x_fdb_wait(priv, &val);
-	}
-
-	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1);
-	if (res)
-		return res;
-
-	return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
-}
-
-static int
-yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
-		u16 ports_mask)
-{
-	u16 index;
-	u32 ctrl1;
-	u32 ctrl2;
-	u32 val1;
-	u32 val2;
-	int res;
-
-	/* Check for presence */
-	res = yt921x_fdb_has(priv, addr, vid, &index);
-	if (res)
-		return res;
-	if (index >= YT921X_FDB_NUM)
-		return yt921x_fdb_add(priv, addr, vid, ports_mask);
-
-	/* Check if action required */
-	res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1);
-	if (res)
-		return res;
-	res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
-	if (res)
-		return res;
-
-	ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M;
-	ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC;
-	ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask);
-	if (ctrl1 == val1 && ctrl2 == val2)
-		return 0;
-
-	return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
-}
-
-static int
-yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port,
-			 dsa_fdb_dump_cb_t *cb, void *data)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	/* Hardware FDB is shared for fdb and mdb, "bridge fdb show"
-	 * only wants to see unicast
-	 */
-	res = yt921x_fdb_dump(priv, BIT(port), cb, data);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct device *dev = to_device(priv);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_fdb_flush_port(priv, port, false);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for",
-			port, res);
-}
-
-static int
-yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u32 ctrl;
-	int res;
-
-	/* AGEING reg is set in 5s step */
-	ctrl = clamp(msecs / 5000, 1, U16_MAX);
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_write(priv, YT921X_AGEING, ctrl);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port,
-			const unsigned char *addr, u16 vid, struct dsa_db db)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port,
-			const unsigned char *addr, u16 vid, struct dsa_db db)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_fdb_join(priv, addr, vid, BIT(port));
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port,
-			const struct switchdev_obj_port_mdb *mdb,
-			struct dsa_db db)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	const unsigned char *addr = mdb->addr;
-	u16 vid = mdb->vid;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port,
-			const struct switchdev_obj_port_mdb *mdb,
-			struct dsa_db db)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	const unsigned char *addr = mdb->addr;
-	u16 vid = mdb->vid;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_fdb_join(priv, addr, vid, BIT(port));
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_vlan_aware_set(struct yt921x_priv *priv, int port, bool vlan_aware)
-{
-	u32 ctrl;
-
-	/* Abuse SVLAN for PCP parsing without polluting the FDB - it just works
-	 * despite YT921X_VLAN_CTRL_SVLAN_EN never being set
-	 */
-	if (!vlan_aware)
-		ctrl = YT921X_PORT_IGR_TPIDn_STAG(0);
-	else
-		ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0);
-	return yt921x_reg_write(priv, YT921X_PORTn_IGR_TPID(port), ctrl);
-}
-
-static int
-yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid)
-{
-	u32 mask;
-	u32 ctrl;
-
-	mask = YT921X_PORT_VLAN_CTRL_CVID_M;
-	ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid);
-	return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port),
-				      mask, ctrl);
-}
-
-static int
-yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering)
-{
-	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
-	struct net_device *bdev;
-	u16 pvid;
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	bdev = dsa_port_bridge_dev_get(dp);
-
-	if (!bdev || !vlan_filtering)
-		pvid = YT921X_VID_UNWARE;
-	else
-		br_vlan_get_pvid(bdev, &pvid);
-	res = yt921x_port_set_pvid(priv, port, pvid);
-	if (res)
-		return res;
-
-	mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED |
-	       YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
-	ctrl = 0;
-	/* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */
-	if (vlan_filtering && !pvid)
-		ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
-	res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
-				     mask, ctrl);
-	if (res)
-		return res;
-
-	res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER,
-				     YT921X_VLAN_IGR_FILTER_PORTn(port),
-				     vlan_filtering);
-	if (res)
-		return res;
-
-	res = yt921x_vlan_aware_set(priv, port, vlan_filtering);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid)
-{
-	u32 masks[2];
-
-	masks[0] = YT921X_VLAN_CTRLa_PORTn(port);
-	masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port);
-
-	return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), masks);
-}
-
-static int
-yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged)
-{
-	u32 masks[2];
-	u32 ctrls[2];
-
-	masks[0] = YT921X_VLAN_CTRLa_PORTn(port) |
-		   YT921X_VLAN_CTRLa_PORTS(priv->cpu_ports_mask);
-	ctrls[0] = masks[0];
-
-	masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port);
-	ctrls[1] = untagged ? masks[1] : 0;
-
-	return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid),
-					masks, ctrls);
-}
-
-static int
-yt921x_pvid_clear(struct yt921x_priv *priv, int port)
-{
-	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
-	bool vlan_filtering;
-	u32 mask;
-	int res;
-
-	vlan_filtering = dsa_port_is_vlan_filtering(dp);
-
-	res = yt921x_port_set_pvid(priv, port,
-				   vlan_filtering ? 0 : YT921X_VID_UNWARE);
-	if (res)
-		return res;
-
-	if (vlan_filtering) {
-		mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
-		res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
-					  mask);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid)
-{
-	struct dsa_port *dp = dsa_to_port(&priv->ds, port);
-	bool vlan_filtering;
-	u32 mask;
-	int res;
-
-	vlan_filtering = dsa_port_is_vlan_filtering(dp);
-
-	if (vlan_filtering) {
-		res = yt921x_port_set_pvid(priv, port, vid);
-		if (res)
-			return res;
-	}
-
-	mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
-	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int
-yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port,
-			       bool vlan_filtering,
-			       struct netlink_ext_ack *extack)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	if (dsa_is_cpu_port(ds, port))
-		return 0;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_vlan_filtering(priv, port, vlan_filtering);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port,
-			 const struct switchdev_obj_port_vlan *vlan)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u16 vid = vlan->vid;
-	u16 pvid;
-	int res;
-
-	if (dsa_is_cpu_port(ds, port))
-		return 0;
-
-	mutex_lock(&priv->reg_lock);
-	do {
-		struct dsa_port *dp = dsa_to_port(ds, port);
-		struct net_device *bdev;
-
-		res = yt921x_vlan_del(priv, port, vid);
-		if (res)
-			break;
-
-		bdev = dsa_port_bridge_dev_get(dp);
-		if (bdev) {
-			br_vlan_get_pvid(bdev, &pvid);
-			if (pvid == vid)
-				res = yt921x_pvid_clear(priv, port);
-		}
-	} while (0);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port,
-			 const struct switchdev_obj_port_vlan *vlan,
-			 struct netlink_ext_ack *extack)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u16 vid = vlan->vid;
-	u16 pvid;
-	int res;
-
-	/* CPU port is supposed to be a member of every VLAN; see
-	 * yt921x_vlan_add() and yt921x_port_setup()
-	 */
-	if (dsa_is_cpu_port(ds, port))
-		return 0;
-
-	mutex_lock(&priv->reg_lock);
-	do {
-		struct dsa_port *dp = dsa_to_port(ds, port);
-		struct net_device *bdev;
-
-		res = yt921x_vlan_add(priv, port, vid,
-				      vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
-		if (res)
-			break;
-
-		bdev = dsa_port_bridge_dev_get(dp);
-		if (bdev) {
-			if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
-				res = yt921x_pvid_set(priv, port, vid);
-			} else {
-				br_vlan_get_pvid(bdev, &pvid);
-				if (pvid == vid)
-					res = yt921x_pvid_clear(priv, port);
-			}
-		}
-	} while (0);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
-{
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	ctrl = ~priv->cpu_ports_mask;
-	res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl);
-	if (res)
-		return res;
-
-	/* Turn off FDB learning to prevent FDB pollution */
-	mask = YT921X_PORT_LEARN_DIS;
-	res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask);
-	if (res)
-		return res;
-
-	/* Turn off VLAN awareness */
-	res = yt921x_vlan_aware_set(priv, port, false);
-	if (res)
-		return res;
-
-	/* Unrelated since learning is off and all packets are trapped;
-	 * set it anyway
-	 */
-	res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int yt921x_userport_bridge(struct yt921x_priv *priv, int port)
-{
-	u32 mask;
-	int res;
-
-	mask = YT921X_PORT_LEARN_DIS;
-	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int yt921x_isolate(struct yt921x_priv *priv, int port)
-{
-	u32 mask;
-	int res;
-
-	mask = BIT(port);
-	for (int i = 0; i < YT921X_PORT_NUM; i++) {
-		if ((BIT(i) & priv->cpu_ports_mask) || i == port)
-			continue;
-
-		res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i),
-					  mask);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-/* Make sure to include the CPU port in ports_mask, or your bridge will
- * not have it.
- */
-static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask)
-{
-	unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask;
-	u32 isolated_mask;
-	u32 ctrl;
-	int port;
-	int res;
-
-	isolated_mask = 0;
-	for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
-		struct yt921x_port *pp = &priv->ports[port];
-
-		if (pp->isolated)
-			isolated_mask |= BIT(port);
-	}
-
-	/* Block from non-cpu bridge ports ... */
-	for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
-		struct yt921x_port *pp = &priv->ports[port];
-
-		/* to non-bridge ports */
-		ctrl = ~ports_mask;
-		/* to isolated ports when isolated */
-		if (pp->isolated)
-			ctrl |= isolated_mask;
-		/* to itself when non-hairpin */
-		if (!pp->hairpin)
-			ctrl |= BIT(port);
-		else
-			ctrl &= ~BIT(port);
-
-		res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
-				       ctrl);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-static int yt921x_bridge_leave(struct yt921x_priv *priv, int port)
-{
-	int res;
-
-	res = yt921x_userport_standalone(priv, port);
-	if (res)
-		return res;
-
-	res = yt921x_isolate(priv, port);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int
-yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask)
-{
-	int res;
-
-	res = yt921x_userport_bridge(priv, port);
-	if (res)
-		return res;
-
-	res = yt921x_bridge(priv, ports_mask);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int
-yt921x_bridge_flags(struct yt921x_priv *priv, int port,
-		    struct switchdev_brport_flags flags)
-{
-	struct yt921x_port *pp = &priv->ports[port];
-	bool do_flush;
-	u32 mask;
-	int res;
-
-	if (flags.mask & BR_LEARNING) {
-		bool learning = flags.val & BR_LEARNING;
-
-		mask = YT921X_PORT_LEARN_DIS;
-		res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port),
-					     mask, !learning);
-		if (res)
-			return res;
-	}
-
-	/* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP
-	 * is set
-	 */
-
-	/* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
-
-	do_flush = false;
-	if (flags.mask & BR_HAIRPIN_MODE) {
-		pp->hairpin = flags.val & BR_HAIRPIN_MODE;
-		do_flush = true;
-	}
-	if (flags.mask & BR_ISOLATED) {
-		pp->isolated = flags.val & BR_ISOLATED;
-		do_flush = true;
-	}
-	if (do_flush) {
-		struct dsa_switch *ds = &priv->ds;
-		struct dsa_port *dp = dsa_to_port(ds, port);
-		struct net_device *bdev;
-
-		bdev = dsa_port_bridge_dev_get(dp);
-		if (bdev) {
-			u32 ports_mask;
-
-			ports_mask = dsa_bridge_ports(ds, bdev);
-			ports_mask |= priv->cpu_ports_mask;
-			res = yt921x_bridge(priv, ports_mask);
-			if (res)
-				return res;
-		}
-	}
-
-	return 0;
-}
-
-static int
-yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port,
-				 struct switchdev_brport_flags flags,
-				 struct netlink_ext_ack *extack)
-{
-	if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD |
-			   BR_MCAST_FLOOD | BR_ISOLATED))
-		return -EINVAL;
-	return 0;
-}
-
-static int
-yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port,
-			     struct switchdev_brport_flags flags,
-			     struct netlink_ext_ack *extack)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	if (dsa_is_cpu_port(ds, port))
-		return 0;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_bridge_flags(priv, port, flags);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static void
-yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port,
-			     struct dsa_bridge bridge)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct device *dev = to_device(priv);
-	int res;
-
-	if (dsa_is_cpu_port(ds, port))
-		return;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_bridge_leave(priv, port);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		dev_err(dev, "Failed to %s port %d: %i\n", "unbridge",
-			port, res);
-}
-
-static int
-yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port,
-			    struct dsa_bridge bridge, bool *tx_fwd_offload,
-			    struct netlink_ext_ack *extack)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u16 ports_mask;
-	int res;
-
-	if (dsa_is_cpu_port(ds, port))
-		return 0;
-
-	ports_mask = dsa_bridge_ports(ds, bridge.dev);
-	ports_mask |= priv->cpu_ports_mask;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_bridge_join(priv, port, ports_mask);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port,
-			      const struct switchdev_mst_state *st)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	mask = YT921X_STP_PORTn_M(port);
-	switch (st->state) {
-	case BR_STATE_DISABLED:
-		ctrl = YT921X_STP_PORTn_DISABLED(port);
-		break;
-	case BR_STATE_LISTENING:
-	case BR_STATE_LEARNING:
-		ctrl = YT921X_STP_PORTn_LEARNING(port);
-		break;
-	case BR_STATE_FORWARDING:
-	default:
-		ctrl = YT921X_STP_PORTn_FORWARD(port);
-		break;
-	case BR_STATE_BLOCKING:
-		ctrl = YT921X_STP_PORTn_BLOCKING(port);
-		break;
-	}
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int
-yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
-			 const struct switchdev_vlan_msti *msti)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u32 masks[2];
-	u32 ctrls[2];
-	int res;
-
-	if (!msti->vid)
-		return -EINVAL;
-	if (!msti->msti || msti->msti >= YT921X_MSTI_NUM)
-		return -EINVAL;
-
-	masks[0] = 0;
-	ctrls[0] = 0;
-	masks[1] = YT921X_VLAN_CTRLb_STP_ID_M;
-	ctrls[1] = YT921X_VLAN_CTRLb_STP_ID(msti->msti);
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid),
-				       masks, ctrls);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static void
-yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct dsa_port *dp = dsa_to_port(ds, port);
-	struct device *dev = to_device(priv);
-	bool learning;
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	mask = YT921X_STP_PORTn_M(port);
-	learning = false;
-	switch (state) {
-	case BR_STATE_DISABLED:
-		ctrl = YT921X_STP_PORTn_DISABLED(port);
-		break;
-	case BR_STATE_LISTENING:
-		ctrl = YT921X_STP_PORTn_LEARNING(port);
-		break;
-	case BR_STATE_LEARNING:
-		ctrl = YT921X_STP_PORTn_LEARNING(port);
-		learning = dp->learning;
-		break;
-	case BR_STATE_FORWARDING:
-	default:
-		ctrl = YT921X_STP_PORTn_FORWARD(port);
-		learning = dp->learning;
-		break;
-	case BR_STATE_BLOCKING:
-		ctrl = YT921X_STP_PORTn_BLOCKING(port);
-		break;
-	}
-
-	mutex_lock(&priv->reg_lock);
-	do {
-		res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl);
-		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);
-	} while (0);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for",
-			port, res);
-}
-
-static int __maybe_unused
-yt921x_dsa_port_get_default_prio(struct dsa_switch *ds, int port)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u32 val;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_read(priv, YT921X_PORTn_QOS(port), &val);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		return res;
-
-	return FIELD_GET(YT921X_PORT_QOS_PRIO_M, val);
-}
-
-static int __maybe_unused
-yt921x_dsa_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	if (prio >= YT921X_PRIO_NUM)
-		return -EINVAL;
-
-	mutex_lock(&priv->reg_lock);
-	mask = YT921X_PORT_QOS_PRIO_M | YT921X_PORT_QOS_PRIO_EN;
-	ctrl = YT921X_PORT_QOS_PRIO(prio) | YT921X_PORT_QOS_PRIO_EN;
-	res = yt921x_reg_update_bits(priv, YT921X_PORTn_QOS(port), mask, ctrl);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int __maybe_unused appprios_cmp(const void *a, const void *b)
-{
-	return ((const u8 *)b)[1] - ((const u8 *)a)[1];
-}
-
-static int __maybe_unused
-yt921x_dsa_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel,
-			     int *nselp)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u8 appprios[2][2] = {};
-	int nsel;
-	u32 val;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_read(priv, YT921X_PORTn_PRIO_ORD(port), &val);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		return res;
-
-	appprios[0][0] = IEEE_8021QAZ_APP_SEL_DSCP;
-	appprios[0][1] = (val >> (3 * YT921X_APP_SEL_DSCP)) & 7;
-	appprios[1][0] = DCB_APP_SEL_PCP;
-	appprios[1][1] = (val >> (3 * YT921X_APP_SEL_CVLAN_PCP)) & 7;
-	sort(appprios, ARRAY_SIZE(appprios), sizeof(appprios[0]), appprios_cmp,
-	     NULL);
-
-	nsel = 0;
-	for (int i = 0; i < ARRAY_SIZE(appprios) && appprios[i][1]; i++) {
-		sel[nsel] = appprios[i][0];
-		nsel++;
-	}
-	*nselp = nsel;
-
-	return 0;
-}
-
-static int __maybe_unused
-yt921x_dsa_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel,
-			     int nsel)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct device *dev = to_device(priv);
-	u32 ctrl;
-	int res;
-
-	if (nsel > YT921X_APP_SEL_NUM)
-		return -EINVAL;
-
-	ctrl = 0;
-	for (int i = 0; i < nsel; i++) {
-		switch (sel[i]) {
-		case IEEE_8021QAZ_APP_SEL_DSCP:
-			ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_DSCP,
-							  7 - i);
-			break;
-		case DCB_APP_SEL_PCP:
-			ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_CVLAN_PCP,
-							  7 - i);
-			ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_SVLAN_PCP,
-							  7 - i);
-			break;
-		default:
-			dev_err(dev,
-				"Invalid apptrust selector (at %d-th). Supported: dscp, pcp\n",
-				i + 1);
-			return -EOPNOTSUPP;
-		}
-	}
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), ctrl);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int yt921x_port_down(struct yt921x_priv *priv, int port)
-{
-	u32 mask;
-	int res;
-
-	mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
-	res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask);
-	if (res)
-		return res;
-
-	if (yt921x_port_is_external(port)) {
-		mask = YT921X_SERDES_LINK;
-		res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask);
-		if (res)
-			return res;
-
-		mask = YT921X_XMII_LINK;
-		res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
-	       phy_interface_t interface, int speed, int duplex,
-	       bool tx_pause, bool rx_pause)
-{
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	switch (speed) {
-	case SPEED_10:
-		ctrl = YT921X_PORT_SPEED_10;
-		break;
-	case SPEED_100:
-		ctrl = YT921X_PORT_SPEED_100;
-		break;
-	case SPEED_1000:
-		ctrl = YT921X_PORT_SPEED_1000;
-		break;
-	case SPEED_2500:
-		ctrl = YT921X_PORT_SPEED_2500;
-		break;
-	case SPEED_10000:
-		ctrl = YT921X_PORT_SPEED_10000;
-		break;
-	default:
-		return -EINVAL;
-	}
-	if (duplex == DUPLEX_FULL)
-		ctrl |= YT921X_PORT_DUPLEX_FULL;
-	if (tx_pause)
-		ctrl |= YT921X_PORT_TX_PAUSE;
-	if (rx_pause)
-		ctrl |= YT921X_PORT_RX_PAUSE;
-	ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
-	res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl);
-	if (res)
-		return res;
-
-	if (yt921x_port_is_external(port)) {
-		mask = YT921X_SERDES_SPEED_M;
-		switch (speed) {
-		case SPEED_10:
-			ctrl = YT921X_SERDES_SPEED_10;
-			break;
-		case SPEED_100:
-			ctrl = YT921X_SERDES_SPEED_100;
-			break;
-		case SPEED_1000:
-			ctrl = YT921X_SERDES_SPEED_1000;
-			break;
-		case SPEED_2500:
-			ctrl = YT921X_SERDES_SPEED_2500;
-			break;
-		case SPEED_10000:
-			ctrl = YT921X_SERDES_SPEED_10000;
-			break;
-		default:
-			return -EINVAL;
-		}
-		mask |= YT921X_SERDES_DUPLEX_FULL;
-		if (duplex == DUPLEX_FULL)
-			ctrl |= YT921X_SERDES_DUPLEX_FULL;
-		mask |= YT921X_SERDES_TX_PAUSE;
-		if (tx_pause)
-			ctrl |= YT921X_SERDES_TX_PAUSE;
-		mask |= YT921X_SERDES_RX_PAUSE;
-		if (rx_pause)
-			ctrl |= YT921X_SERDES_RX_PAUSE;
-		mask |= YT921X_SERDES_LINK;
-		ctrl |= YT921X_SERDES_LINK;
-		res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
-					     mask, ctrl);
-		if (res)
-			return res;
-
-		mask = YT921X_XMII_LINK;
-		res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask);
-		if (res)
-			return res;
-
-		switch (speed) {
-		case SPEED_10:
-			ctrl = YT921X_MDIO_POLLING_SPEED_10;
-			break;
-		case SPEED_100:
-			ctrl = YT921X_MDIO_POLLING_SPEED_100;
-			break;
-		case SPEED_1000:
-			ctrl = YT921X_MDIO_POLLING_SPEED_1000;
-			break;
-		case SPEED_2500:
-			ctrl = YT921X_MDIO_POLLING_SPEED_2500;
-			break;
-		case SPEED_10000:
-			ctrl = YT921X_MDIO_POLLING_SPEED_10000;
-			break;
-		default:
-			return -EINVAL;
-		}
-		if (duplex == DUPLEX_FULL)
-			ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL;
-		ctrl |= YT921X_MDIO_POLLING_LINK;
-		res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl);
-		if (res)
-			return res;
-	}
-
-	return 0;
-}
-
-static int
-yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
-		   phy_interface_t interface)
-{
-	struct device *dev = to_device(priv);
-	u32 mask;
-	u32 ctrl;
-	int res;
-
-	if (!yt921x_port_is_external(port)) {
-		if (interface != PHY_INTERFACE_MODE_INTERNAL) {
-			dev_err(dev, "Wrong mode %d on port %d\n",
-				interface, port);
-			return -EINVAL;
-		}
-		return 0;
-	}
-
-	switch (interface) {
-	/* SERDES */
-	case PHY_INTERFACE_MODE_SGMII:
-	case PHY_INTERFACE_MODE_100BASEX:
-	case PHY_INTERFACE_MODE_1000BASEX:
-	case PHY_INTERFACE_MODE_2500BASEX:
-		mask = YT921X_SERDES_CTRL_PORTn(port);
-		res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask);
-		if (res)
-			return res;
-
-		mask = YT921X_XMII_CTRL_PORTn(port);
-		res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask);
-		if (res)
-			return res;
-
-		mask = YT921X_SERDES_MODE_M;
-		switch (interface) {
-		case PHY_INTERFACE_MODE_SGMII:
-			ctrl = YT921X_SERDES_MODE_SGMII;
-			break;
-		case PHY_INTERFACE_MODE_100BASEX:
-			ctrl = YT921X_SERDES_MODE_100BASEX;
-			break;
-		case PHY_INTERFACE_MODE_1000BASEX:
-			ctrl = YT921X_SERDES_MODE_1000BASEX;
-			break;
-		case PHY_INTERFACE_MODE_2500BASEX:
-			ctrl = YT921X_SERDES_MODE_2500BASEX;
-			break;
-		default:
-			return -EINVAL;
-		}
-		res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
-					     mask, ctrl);
-		if (res)
-			return res;
-
-		break;
-	/* add XMII support here */
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static void
-yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
-			     phy_interface_t interface)
-{
-	struct dsa_port *dp = dsa_phylink_to_port(config);
-	struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
-	int port = dp->index;
-	int res;
-
-	/* No need to sync; port control block is hold until device remove */
-	cancel_delayed_work(&priv->ports[port].mib_read);
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_port_down(priv, port);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down",
-			port, res);
-}
-
-static void
-yt921x_phylink_mac_link_up(struct phylink_config *config,
-			   struct phy_device *phydev, unsigned int mode,
-			   phy_interface_t interface, int speed, int duplex,
-			   bool tx_pause, bool rx_pause)
-{
-	struct dsa_port *dp = dsa_phylink_to_port(config);
-	struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
-	int port = dp->index;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_port_up(priv, port, mode, interface, speed, duplex,
-			     tx_pause, rx_pause);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
-			port, res);
-
-	schedule_delayed_work(&priv->ports[port].mib_read, 0);
-}
-
-static void
-yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode,
-			  const struct phylink_link_state *state)
-{
-	struct dsa_port *dp = dsa_phylink_to_port(config);
-	struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
-	int port = dp->index;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_port_config(priv, port, mode, state->interface);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config",
-			port, res);
-}
-
-static void
-yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
-			    struct phylink_config *config)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	const struct yt921x_info *info = priv->info;
-
-	config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
-				   MAC_10 | MAC_100 | MAC_1000;
-
-	if (info->internal_mask & BIT(port)) {
-		/* Port 10 for MCU should probably go here too. But since that
-		 * is untested yet, turn it down for the moment by letting it
-		 * fall to the default branch.
-		 */
-		__set_bit(PHY_INTERFACE_MODE_INTERNAL,
-			  config->supported_interfaces);
-	} else if (info->external_mask & BIT(port)) {
-		/* TODO: external ports may support SERDES only, XMII only, or
-		 * SERDES + XMII depending on the chip. However, we can't get
-		 * the accurate config table due to lack of document, thus
-		 * we simply declare SERDES + XMII and rely on the correctness
-		 * of devicetree for now.
-		 */
-
-		/* SERDES */
-		__set_bit(PHY_INTERFACE_MODE_SGMII,
-			  config->supported_interfaces);
-		/* REVSGMII (SGMII in PHY role) should go here, once
-		 * PHY_INTERFACE_MODE_REVSGMII is introduced.
-		 */
-		__set_bit(PHY_INTERFACE_MODE_100BASEX,
-			  config->supported_interfaces);
-		__set_bit(PHY_INTERFACE_MODE_1000BASEX,
-			  config->supported_interfaces);
-		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
-			  config->supported_interfaces);
-		config->mac_capabilities |= MAC_2500FD;
-
-		/* XMII */
-
-		/* Not tested. To add support for XMII:
-		 *   - Add proper interface modes below
-		 *   - Handle them in yt921x_port_config()
-		 */
-	}
-	/* no such port: empty supported_interfaces causes phylink to turn it
-	 * down
-	 */
-}
-
-static int yt921x_port_setup(struct yt921x_priv *priv, int port)
-{
-	struct dsa_switch *ds = &priv->ds;
-	u32 ctrl;
-	int res;
-
-	res = yt921x_userport_standalone(priv, port);
-	if (res)
-		return res;
-
-	/* Clear prio order (even if DCB is not enabled) to avoid unsolicited
-	 * priorities
-	 */
-	res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), 0);
-	if (res)
-		return res;
-
-	if (dsa_is_cpu_port(ds, port)) {
-		/* Egress of CPU port is supposed to be completely controlled
-		 * via tagging, so set to oneway isolated (drop all packets
-		 * without tag).
-		 */
-		ctrl = ~(u32)0;
-		res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
-				       ctrl);
-		if (res)
-			return res;
-
-		/* To simplify FDB "isolation" simulation, we also disable
-		 * learning on the CPU port, and let software identify packets
-		 * towarding CPU (either trapped or a static FDB entry is
-		 * matched, no matter which bridge that entry is for), which is
-		 * already done by yt921x_userport_standalone(). As a result,
-		 * VLAN-awareness becomes unrelated on the CPU port (set to
-		 * VLAN-unaware by the way).
-		 */
-	}
-
-	return 0;
-}
-
-static enum dsa_tag_protocol
-yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
-			    enum dsa_tag_protocol m)
-{
-	return DSA_TAG_PROTO_YT921X;
-}
-
-static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_port_setup(priv, port);
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-/* Not "port" - DSCP mapping is global */
-static int __maybe_unused
-yt921x_dsa_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u32 val;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		return res;
-
-	return FIELD_GET(YT921X_IPM_PRIO_M, val);
-}
-
-static int __maybe_unused
-yt921x_dsa_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	u32 val;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	/* During a "dcb app replace" command, the new app table entry will be
-	 * added first, then the old one will be deleted. But the hardware only
-	 * supports one QoS class per DSCP value (duh), so if we blindly delete
-	 * the app table entry for this DSCP value, we end up deleting the
-	 * entry with the new priority. Avoid that by checking whether user
-	 * space wants to delete the priority which is currently configured, or
-	 * something else which is no longer current.
-	 */
-	res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val);
-	if (!res && FIELD_GET(YT921X_IPM_PRIO_M, val) == prio)
-		res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
-				       YT921X_IPM_PRIO(IEEE8021Q_TT_BK));
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int __maybe_unused
-yt921x_dsa_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	int res;
-
-	if (prio >= YT921X_PRIO_NUM)
-		return -EINVAL;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
-			       YT921X_IPM_PRIO(prio));
-	mutex_unlock(&priv->reg_lock);
-
-	return res;
-}
-
-static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp)
-{
-	u32 val = YT921X_EDATA_DATA_IDLE;
-	int res;
-
-	res = yt921x_reg_wait(priv, YT921X_EDATA_DATA,
-			      YT921X_EDATA_DATA_STATUS_M, &val);
-	if (res)
-		return res;
-
-	*valp = val;
-	return 0;
-}
-
-static int
-yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp)
-{
-	u32 ctrl;
-	u32 val;
-	int res;
-
-	ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ;
-	res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl);
-	if (res)
-		return res;
-	res = yt921x_edata_wait(priv, &val);
-	if (res)
-		return res;
-
-	*valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val);
-	return 0;
-}
-
-static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp)
-{
-	u32 val;
-	int res;
-
-	res = yt921x_edata_wait(priv, &val);
-	if (res)
-		return res;
-	return yt921x_edata_read_cont(priv, addr, valp);
-}
-
-static int yt921x_chip_detect(struct yt921x_priv *priv)
-{
-	struct device *dev = to_device(priv);
-	const struct yt921x_info *info;
-	u8 extmode;
-	u32 chipid;
-	u32 major;
-	u32 mode;
-	u32 val;
-	int res;
-
-	res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid);
-	if (res)
-		return res;
-
-	major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);
-
-	for (info = yt921x_infos; info->name; info++)
-		if (info->major == major)
-			break;
-	if (!info->name) {
-		dev_err(dev, "Unexpected chipid 0x%x\n", chipid);
-		return -ENODEV;
-	}
-
-	res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode);
-	if (res)
-		return res;
-	res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode);
-	if (res)
-		return res;
-
-	for (; info->name; info++)
-		if (info->major == major && info->mode == mode &&
-		    info->extmode == extmode)
-			break;
-	if (!info->name) {
-		dev_err(dev,
-			"Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n",
-			chipid, mode, extmode);
-		return -ENODEV;
-	}
-
-	res = yt921x_reg_read(priv, YT921X_SYS_CLK, &val);
-	if (res)
-		return res;
-	switch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) {
-	case 0:
-		priv->cycle_ns = info->major == YT9215_MAJOR ? 8 : 6;
-		break;
-	case YT921X_SYS_CLK_143M:
-		priv->cycle_ns = 7;
-		break;
-	default:
-		priv->cycle_ns = 8;
-	}
-
-	/* Print chipid here since we are interested in lower 16 bits */
-	dev_info(dev,
-		 "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n",
-		 info->name, chipid, mode, extmode);
-
-	priv->info = info;
-
-	return 0;
-}
-
-static int yt921x_chip_reset(struct yt921x_priv *priv)
-{
-	struct device *dev = to_device(priv);
-	u16 eth_p_tag;
-	u32 val;
-	int res;
-
-	res = yt921x_chip_detect(priv);
-	if (res)
-		return res;
-
-	/* Reset */
-	res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW);
-	if (res)
-		return res;
-
-	/* RST_HW is almost same as GPIO hard reset, so we need this delay. */
-	fsleep(YT921X_RST_DELAY_US);
-
-	val = 0;
-	res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val);
-	if (res)
-		return res;
-
-	/* Check for tag EtherType; do it after reset in case you messed it up
-	 * before.
-	 */
-	res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
-	if (res)
-		return res;
-	eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
-	if (eth_p_tag != ETH_P_YT921X) {
-		dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
-			ETH_P_YT921X);
-		/* Despite being possible, we choose not to set CPU_TAG_TPID,
-		 * since there is no way it can be different unless you have the
-		 * wrong chip.
-		 */
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
-{
-	struct dsa_switch *ds = &priv->ds;
-	unsigned long cpu_ports_mask;
-	u32 ctrls[2];
-	u32 ctrl;
-	int port;
-	int res;
-
-	/* Enable DSA */
-	priv->cpu_ports_mask = dsa_cpu_ports(ds);
-
-	ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN |
-	       YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask));
-	res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl);
-	if (res)
-		return res;
-
-	/* Setup software switch */
-	ctrl = YT921X_CPU_COPY_TO_EXT_CPU;
-	res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl);
-	if (res)
-		return res;
-
-	ctrl = GENMASK(10, 0);
-	res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl);
-	if (res)
-		return res;
-
-	/* YT921x does not support native DSA port bridging, so we use port
-	 * isolation to emulate it. However, be especially careful that port
-	 * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from
-	 * another bridge) is matched and the destination port (in another
-	 * bridge) is blocked, the packet will be dropped instead of flooding to
-	 * the "bridged" ports, thus we need to trap and handle those packets by
-	 * software.
-	 *
-	 * If there is no more than one bridge, we might be able to drop them
-	 * directly given some conditions are met, but we trap them in all cases
-	 * for now.
-	 */
-	ctrl = 0;
-	for (int i = 0; i < YT921X_PORT_NUM; i++)
-		ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i);
-	/* Except for CPU ports, if any packets are sent via CPU ports without
-	 * tag, they should be dropped.
-	 */
-	cpu_ports_mask = priv->cpu_ports_mask;
-	for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) {
-		ctrl &= ~YT921X_ACT_UNK_ACTn_M(port);
-		ctrl |= YT921X_ACT_UNK_ACTn_DROP(port);
-	}
-	res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl);
-	if (res)
-		return res;
-	res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl);
-	if (res)
-		return res;
-
-	/* Tagged VID 0 should be treated as untagged, which confuses the
-	 * hardware a lot
-	 */
-	ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M;
-	ctrls[1] = 0;
-	res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int yt921x_chip_setup_tc(struct yt921x_priv *priv)
-{
-	unsigned int op_ns;
-	u32 ctrl;
-	int res;
-
-	op_ns = 8 * priv->cycle_ns;
-
-	ctrl = max(priv->meter_slot_ns / op_ns, YT921X_METER_SLOT_MIN);
-	res = yt921x_reg_write(priv, YT921X_METER_SLOT, ctrl);
-	if (res)
-		return res;
-	priv->meter_slot_ns = ctrl * op_ns;
-
-	ctrl = max(priv->port_shape_slot_ns / op_ns,
-		   YT921X_PORT_SHAPE_SLOT_MIN);
-	res = yt921x_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl);
-	if (res)
-		return res;
-	priv->port_shape_slot_ns = ctrl * op_ns;
-
-	return 0;
-}
-
-static int yt921x_chip_setup_acl(struct yt921x_priv *priv)
-{
-	u32 ctrl;
-	int res;
-
-	ctrl = YT921X_ACL_PERMIT_UNMATCH_PORTS_M;
-	res = yt921x_reg_write(priv, YT921X_ACL_PERMIT_UNMATCH, ctrl);
-	if (res)
-		return res;
-
-	ctrl = YT921X_ACL_PORT_PORTS_M;
-	res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int __maybe_unused yt921x_chip_setup_qos(struct yt921x_priv *priv)
-{
-	u32 ctrl;
-	int res;
-
-	/* DSCP to internal priorities */
-	for (u8 dscp = 0; dscp < DSCP_MAX; dscp++) {
-		int prio = ietf_dscp_to_ieee8021q_tt(dscp);
-
-		if (prio < 0)
-			return prio;
-
-		res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp),
-				       YT921X_IPM_PRIO(prio));
-		if (res)
-			return res;
-	}
-
-	/* 802.1Q QoS to internal priorities */
-	for (u8 pcp = 0; pcp < 8; pcp++)
-		for (u8 dei = 0; dei < 2; dei++) {
-			ctrl = YT921X_IPM_PRIO(pcp);
-			if (dei)
-				/* "Red" almost means drop, so it's not that
-				 * useful. Note that tc police does not support
-				 * Three-Color very well
-				 */
-				ctrl |= YT921X_IPM_COLOR_YELLOW;
-
-			for (u8 svlan = 0; svlan < 2; svlan++) {
-				u32 reg = YT921X_IPM_PCPn(svlan, dei, pcp);
-
-				res = yt921x_reg_write(priv, reg, ctrl);
-				if (res)
-					return res;
-			}
-		}
-
-	return 0;
-}
-
-static int yt921x_chip_setup(struct yt921x_priv *priv)
-{
-	u32 ctrl;
-	int res;
-
-	ctrl = YT921X_FUNC_MIB | YT921X_FUNC_ACL | YT921X_FUNC_METER;
-	res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl);
-	if (res)
-		return res;
-
-	res = yt921x_chip_setup_dsa(priv);
-	if (res)
-		return res;
-
-	res = yt921x_chip_setup_tc(priv);
-	if (res)
-		return res;
-
-	res = yt921x_chip_setup_acl(priv);
-	if (res)
-		return res;
-
-#if IS_ENABLED(CONFIG_DCB)
-	res = yt921x_chip_setup_qos(priv);
-	if (res)
-		return res;
-#endif
-
-	/* Clear MIB */
-	ctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT;
-	res = yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl);
-	if (res)
-		return res;
-
-	/* Miscellaneous */
-	res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP);
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static int yt921x_dsa_setup(struct dsa_switch *ds)
-{
-	struct yt921x_priv *priv = to_yt921x_priv(ds);
-	struct device *dev = to_device(priv);
-	struct device_node *np = dev->of_node;
-	struct device_node *child;
-	int res;
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_chip_reset(priv);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		return res;
-
-	/* Register the internal mdio bus. Nodes for internal ports should have
-	 * proper phy-handle pointing to their PHYs. Not enabling the internal
-	 * bus is possible, though pretty wired, if internal ports are not used.
-	 */
-	child = of_get_child_by_name(np, "mdio");
-	if (child) {
-		res = yt921x_mbus_int_init(priv, child);
-		of_node_put(child);
-		if (res)
-			return res;
-	}
-
-	/* External mdio bus is optional */
-	child = of_get_child_by_name(np, "mdio-external");
-	if (child) {
-		res = yt921x_mbus_ext_init(priv, child);
-		of_node_put(child);
-		if (res)
-			return res;
-
-		dev_err(dev, "Untested external mdio bus\n");
-		return -ENODEV;
-	}
-
-	mutex_lock(&priv->reg_lock);
-	res = yt921x_chip_setup(priv);
-	mutex_unlock(&priv->reg_lock);
-
-	if (res)
-		return res;
-
-	return 0;
-}
-
-static const struct phylink_mac_ops yt921x_phylink_mac_ops = {
-	.mac_link_down	= yt921x_phylink_mac_link_down,
-	.mac_link_up	= yt921x_phylink_mac_link_up,
-	.mac_config	= yt921x_phylink_mac_config,
-};
-
-static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
-	/* mib */
-	.get_strings		= yt921x_dsa_get_strings,
-	.get_ethtool_stats	= yt921x_dsa_get_ethtool_stats,
-	.get_sset_count		= yt921x_dsa_get_sset_count,
-	.get_eth_mac_stats	= yt921x_dsa_get_eth_mac_stats,
-	.get_eth_ctrl_stats	= yt921x_dsa_get_eth_ctrl_stats,
-	.get_rmon_stats		= yt921x_dsa_get_rmon_stats,
-	.get_stats64		= yt921x_dsa_get_stats64,
-	.get_pause_stats	= yt921x_dsa_get_pause_stats,
-	/* eee */
-	.support_eee		= dsa_supports_eee,
-	.set_mac_eee		= yt921x_dsa_set_mac_eee,
-	/* mtu */
-	.port_change_mtu	= yt921x_dsa_port_change_mtu,
-	.port_max_mtu		= yt921x_dsa_port_max_mtu,
-	/* rate */
-	.port_policer_del	= yt921x_dsa_port_policer_del,
-	.port_policer_add	= yt921x_dsa_port_policer_add,
-	.port_setup_tc		= yt921x_dsa_port_setup_tc,
-	/* acl */
-	.cls_flower_del		= yt921x_dsa_cls_flower_del,
-	.cls_flower_add		= yt921x_dsa_cls_flower_add,
-	/* hsr */
-	.port_hsr_leave		= dsa_port_simple_hsr_leave,
-	.port_hsr_join		= dsa_port_simple_hsr_join,
-	/* mirror */
-	.port_mirror_del	= yt921x_dsa_port_mirror_del,
-	.port_mirror_add	= yt921x_dsa_port_mirror_add,
-	/* lag */
-	.port_lag_leave		= yt921x_dsa_port_lag_leave,
-	.port_lag_join		= yt921x_dsa_port_lag_join,
-	/* fdb */
-	.port_fdb_dump		= yt921x_dsa_port_fdb_dump,
-	.port_fast_age		= yt921x_dsa_port_fast_age,
-	.set_ageing_time	= yt921x_dsa_set_ageing_time,
-	.port_fdb_del		= yt921x_dsa_port_fdb_del,
-	.port_fdb_add		= yt921x_dsa_port_fdb_add,
-	.port_mdb_del		= yt921x_dsa_port_mdb_del,
-	.port_mdb_add		= yt921x_dsa_port_mdb_add,
-	/* vlan */
-	.port_vlan_filtering	= yt921x_dsa_port_vlan_filtering,
-	.port_vlan_del		= yt921x_dsa_port_vlan_del,
-	.port_vlan_add		= yt921x_dsa_port_vlan_add,
-	/* bridge */
-	.port_pre_bridge_flags	= yt921x_dsa_port_pre_bridge_flags,
-	.port_bridge_flags	= yt921x_dsa_port_bridge_flags,
-	.port_bridge_leave	= yt921x_dsa_port_bridge_leave,
-	.port_bridge_join	= yt921x_dsa_port_bridge_join,
-	/* mst */
-	.port_mst_state_set	= yt921x_dsa_port_mst_state_set,
-	.vlan_msti_set		= yt921x_dsa_vlan_msti_set,
-	.port_stp_state_set	= yt921x_dsa_port_stp_state_set,
-#if IS_ENABLED(CONFIG_DCB)
-	/* dcb */
-	.port_get_default_prio	= yt921x_dsa_port_get_default_prio,
-	.port_set_default_prio	= yt921x_dsa_port_set_default_prio,
-	.port_get_apptrust	= yt921x_dsa_port_get_apptrust,
-	.port_set_apptrust	= yt921x_dsa_port_set_apptrust,
-#endif
-	/* port */
-	.get_tag_protocol	= yt921x_dsa_get_tag_protocol,
-	.phylink_get_caps	= yt921x_dsa_phylink_get_caps,
-	.port_setup		= yt921x_dsa_port_setup,
-#if IS_ENABLED(CONFIG_DCB)
-	/* dscp */
-	.port_get_dscp_prio	= yt921x_dsa_port_get_dscp_prio,
-	.port_del_dscp_prio	= yt921x_dsa_port_del_dscp_prio,
-	.port_add_dscp_prio	= yt921x_dsa_port_add_dscp_prio,
-#endif
-	/* chip */
-	.setup			= yt921x_dsa_setup,
-};
-
-static void yt921x_mdio_shutdown(struct mdio_device *mdiodev)
-{
-	struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
-
-	if (!priv)
-		return;
-
-	dsa_switch_shutdown(&priv->ds);
-}
-
-static void yt921x_mdio_remove(struct mdio_device *mdiodev)
-{
-	struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
-
-	if (!priv)
-		return;
-
-	for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
-		struct yt921x_port *pp = &priv->ports[i];
-
-		disable_delayed_work_sync(&pp->mib_read);
-	}
-
-	dsa_unregister_switch(&priv->ds);
-
-	for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) {
-		struct yt921x_acl_blk *aclblk = priv->acl_blks[i];
-
-		if (!aclblk)
-			continue;
-		for (unsigned int j = 0; j < ARRAY_SIZE(aclblk->rules); j++) {
-			struct yt921x_acl_rule *aclrule = aclblk->rules[j];
-
-			if (!aclrule)
-				continue;
-			kvfree(aclrule);
-		}
-		kvfree(aclblk);
-	}
-	mutex_destroy(&priv->reg_lock);
-}
-
-static int yt921x_mdio_probe(struct mdio_device *mdiodev)
-{
-	struct device *dev = &mdiodev->dev;
-	struct yt921x_reg_mdio *mdio;
-	struct yt921x_priv *priv;
-	struct dsa_switch *ds;
-
-	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-	if (!priv)
-		return -ENOMEM;
-
-	mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);
-	if (!mdio)
-		return -ENOMEM;
-
-	mdio->bus = mdiodev->bus;
-	mdio->addr = mdiodev->addr;
-	mdio->switchid = 0;
-
-	mutex_init(&priv->reg_lock);
-
-	priv->reg_ops = &yt921x_reg_ops_mdio;
-	priv->reg_ctx = mdio;
-
-	for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
-		struct yt921x_port *pp = &priv->ports[i];
-
-		pp->index = i;
-		INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
-	}
-
-	ds = &priv->ds;
-	ds->dev = dev;
-	ds->assisted_learning_on_cpu_port = true;
-	ds->dscp_prio_mapping_is_global = true;
-	ds->priv = priv;
-	ds->ops = &yt921x_dsa_switch_ops;
-	ds->ageing_time_min = 1 * 5000;
-	ds->ageing_time_max = U16_MAX * 5000;
-	ds->phylink_mac_ops = &yt921x_phylink_mac_ops;
-	ds->num_lag_ids = YT921X_LAG_NUM;
-	ds->num_ports = YT921X_PORT_NUM;
-
-	mdiodev_set_drvdata(mdiodev, priv);
-
-	return dsa_register_switch(ds);
-}
-
-static const struct of_device_id yt921x_of_match[] = {
-	{ .compatible = "motorcomm,yt9215" },
-	{}
-};
-MODULE_DEVICE_TABLE(of, yt921x_of_match);
-
-static struct mdio_driver yt921x_mdio_driver = {
-	.probe = yt921x_mdio_probe,
-	.remove = yt921x_mdio_remove,
-	.shutdown = yt921x_mdio_shutdown,
-	.mdiodrv.driver = {
-		.name = YT921X_NAME,
-		.of_match_table = yt921x_of_match,
-	},
-};
-
-mdio_module_driver(yt921x_mdio_driver);
-
-MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>");
-MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h
deleted file mode 100644
index 555046526669..000000000000
--- a/drivers/net/dsa/yt921x.h
+++ /dev/null
@@ -1,977 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (c) 2025 David Yang
- */
-
-#ifndef __YT921X_H
-#define __YT921X_H
-
-#include <net/dsa.h>
-
-#define YT921X_SMI_SWITCHID_M		GENMASK(3, 2)
-#define  YT921X_SMI_SWITCHID(x)			FIELD_PREP(YT921X_SMI_SWITCHID_M, (x))
-#define YT921X_SMI_AD			BIT(1)
-#define  YT921X_SMI_ADDR			0
-#define  YT921X_SMI_DATA			YT921X_SMI_AD
-#define YT921X_SMI_RW			BIT(0)
-#define  YT921X_SMI_WRITE			0
-#define  YT921X_SMI_READ			YT921X_SMI_RW
-
-#define YT921X_SWITCHID_NUM		4
-
-#define YT921X_RST			0x80000
-#define  YT921X_RST_HW				BIT(31)
-#define  YT921X_RST_SW				BIT(1)
-#define YT921X_FUNC			0x80004
-#define  YT921X_FUNC_METER			BIT(4)
-#define  YT921X_FUNC_ACL			BIT(2)
-#define  YT921X_FUNC_MIB			BIT(1)
-#define YT921X_CHIP_ID			0x80008
-#define  YT921X_CHIP_ID_MAJOR			GENMASK(31, 16)
-#define YT921X_EXT_CPU_PORT		0x8000c
-#define  YT921X_EXT_CPU_PORT_TAG_EN		BIT(15)
-#define  YT921X_EXT_CPU_PORT_PORT_EN		BIT(14)
-#define  YT921X_EXT_CPU_PORT_PORT_M		GENMASK(3, 0)
-#define   YT921X_EXT_CPU_PORT_PORT(x)			FIELD_PREP(YT921X_EXT_CPU_PORT_PORT_M, (x))
-#define YT921X_CPU_TAG_TPID		0x80010
-#define  YT921X_CPU_TAG_TPID_TPID_M		GENMASK(15, 0)
-/* Same as ETH_P_YT921X, but this represents the true HW default, while the
- * former is a local convention chosen by us.
- */
-#define   YT921X_CPU_TAG_TPID_TPID_DEFAULT		0x9988
-#define YT921X_PVID_SEL			0x80014
-#define  YT921X_PVID_SEL_SVID_PORTn(port)	BIT(port)
-#define YT921X_SERDES_CTRL		0x80028
-#define  YT921X_SERDES_CTRL_PORTn_TEST(port)	BIT((port) - 3)
-#define  YT921X_SERDES_CTRL_PORTn(port)		BIT((port) - 8)
-#define YT921X_IO_LEVEL			0x80030
-#define  YT9215_IO_LEVEL_NORMAL_M		GENMASK(5, 4)
-#define   YT9215_IO_LEVEL_NORMAL(x)			FIELD_PREP(YT9215_IO_LEVEL_NORMAL_M, (x))
-#define   YT9215_IO_LEVEL_NORMAL_3V3			YT9215_IO_LEVEL_NORMAL(0)
-#define   YT9215_IO_LEVEL_NORMAL_1V8			YT9215_IO_LEVEL_NORMAL(3)
-#define  YT9215_IO_LEVEL_RGMII1_M		GENMASK(3, 2)
-#define   YT9215_IO_LEVEL_RGMII1(x)			FIELD_PREP(YT9215_IO_LEVEL_RGMII1_M, (x))
-#define   YT9215_IO_LEVEL_RGMII1_3V3			YT9215_IO_LEVEL_RGMII1(0)
-#define   YT9215_IO_LEVEL_RGMII1_2V5			YT9215_IO_LEVEL_RGMII1(1)
-#define   YT9215_IO_LEVEL_RGMII1_1V8			YT9215_IO_LEVEL_RGMII1(2)
-#define  YT9215_IO_LEVEL_RGMII0_M		GENMASK(1, 0)
-#define   YT9215_IO_LEVEL_RGMII0(x)			FIELD_PREP(YT9215_IO_LEVEL_RGMII0_M, (x))
-#define   YT9215_IO_LEVEL_RGMII0_3V3			YT9215_IO_LEVEL_RGMII0(0)
-#define   YT9215_IO_LEVEL_RGMII0_2V5			YT9215_IO_LEVEL_RGMII0(1)
-#define   YT9215_IO_LEVEL_RGMII0_1V8			YT9215_IO_LEVEL_RGMII0(2)
-#define  YT9218_IO_LEVEL_RGMII1_M		GENMASK(5, 4)
-#define   YT9218_IO_LEVEL_RGMII1(x)			FIELD_PREP(YT9218_IO_LEVEL_RGMII1_M, (x))
-#define   YT9218_IO_LEVEL_RGMII1_3V3			YT9218_IO_LEVEL_RGMII1(0)
-#define   YT9218_IO_LEVEL_RGMII1_2V5			YT9218_IO_LEVEL_RGMII1(1)
-#define   YT9218_IO_LEVEL_RGMII1_1V8			YT9218_IO_LEVEL_RGMII1(2)
-#define  YT9218_IO_LEVEL_RGMII0_M		GENMASK(3, 2)
-#define   YT9218_IO_LEVEL_RGMII0(x)			FIELD_PREP(YT9218_IO_LEVEL_RGMII0_M, (x))
-#define   YT9218_IO_LEVEL_RGMII0_3V3			YT9218_IO_LEVEL_RGMII0(0)
-#define   YT9218_IO_LEVEL_RGMII0_2V5			YT9218_IO_LEVEL_RGMII0(1)
-#define   YT9218_IO_LEVEL_RGMII0_1V8			YT9218_IO_LEVEL_RGMII0(2)
-#define  YT9218_IO_LEVEL_NORMAL_M		GENMASK(1, 0)
-#define   YT9218_IO_LEVEL_NORMAL(x)			FIELD_PREP(YT9218_IO_LEVEL_NORMAL_M, (x))
-#define   YT9218_IO_LEVEL_NORMAL_3V3			YT9218_IO_LEVEL_NORMAL(0)
-#define   YT9218_IO_LEVEL_NORMAL_1V8			YT9218_IO_LEVEL_NORMAL(3)
-#define YT921X_MAC_ADDR_HI2		0x80080
-#define YT921X_MAC_ADDR_LO4		0x80084
-#define YT921X_SERDESn(port)		(0x8008c + 4 * ((port) - 8))
-#define  YT921X_SERDES_MODE_M			GENMASK(9, 7)
-#define   YT921X_SERDES_MODE(x)				FIELD_PREP(YT921X_SERDES_MODE_M, (x))
-#define   YT921X_SERDES_MODE_SGMII			YT921X_SERDES_MODE(0)
-#define   YT921X_SERDES_MODE_REVSGMII			YT921X_SERDES_MODE(1)
-#define   YT921X_SERDES_MODE_1000BASEX			YT921X_SERDES_MODE(2)
-#define   YT921X_SERDES_MODE_100BASEX			YT921X_SERDES_MODE(3)
-#define   YT921X_SERDES_MODE_2500BASEX			YT921X_SERDES_MODE(4)
-#define  YT921X_SERDES_RX_PAUSE			BIT(6)
-#define  YT921X_SERDES_TX_PAUSE			BIT(5)
-#define  YT921X_SERDES_LINK			BIT(4)  /* force link */
-#define  YT921X_SERDES_DUPLEX_FULL		BIT(3)
-#define  YT921X_SERDES_SPEED_M			GENMASK(2, 0)
-#define   YT921X_SERDES_SPEED(x)			FIELD_PREP(YT921X_SERDES_SPEED_M, (x))
-#define   YT921X_SERDES_SPEED_10			YT921X_SERDES_SPEED(0)
-#define   YT921X_SERDES_SPEED_100			YT921X_SERDES_SPEED(1)
-#define   YT921X_SERDES_SPEED_1000			YT921X_SERDES_SPEED(2)
-#define   YT921X_SERDES_SPEED_10000			YT921X_SERDES_SPEED(3)
-#define   YT921X_SERDES_SPEED_2500			YT921X_SERDES_SPEED(4)
-#define YT921X_PORTn_CTRL(port)		(0x80100 + 4 * (port))
-#define  YT921X_PORT_CTRL_PAUSE_AN		BIT(10)
-#define YT921X_PORTn_STATUS(port)	(0x80200 + 4 * (port))
-#define  YT921X_PORT_LINK			BIT(9)  /* CTRL: auto negotiation */
-#define  YT921X_PORT_HALF_PAUSE			BIT(8)  /* Half-duplex back pressure mode */
-#define  YT921X_PORT_DUPLEX_FULL		BIT(7)
-#define  YT921X_PORT_RX_PAUSE			BIT(6)
-#define  YT921X_PORT_TX_PAUSE			BIT(5)
-#define  YT921X_PORT_RX_MAC_EN			BIT(4)
-#define  YT921X_PORT_TX_MAC_EN			BIT(3)
-#define  YT921X_PORT_SPEED_M			GENMASK(2, 0)
-#define   YT921X_PORT_SPEED(x)				FIELD_PREP(YT921X_PORT_SPEED_M, (x))
-#define   YT921X_PORT_SPEED_10				YT921X_PORT_SPEED(0)
-#define   YT921X_PORT_SPEED_100				YT921X_PORT_SPEED(1)
-#define   YT921X_PORT_SPEED_1000			YT921X_PORT_SPEED(2)
-#define   YT921X_PORT_SPEED_10000			YT921X_PORT_SPEED(3)
-#define   YT921X_PORT_SPEED_2500			YT921X_PORT_SPEED(4)
-#define YT921X_PON_STRAP_FUNC		0x80320
-#define YT921X_PON_STRAP_VAL		0x80324
-#define YT921X_PON_STRAP_CAP		0x80328
-#define  YT921X_PON_STRAP_EEE			BIT(16)
-#define  YT921X_PON_STRAP_LOOP_DETECT		BIT(7)
-#define YT921X_MDIO_POLLINGn(port)	(0x80364 + 4 * ((port) - 8))
-#define  YT921X_MDIO_POLLING_DUPLEX_FULL	BIT(4)
-#define  YT921X_MDIO_POLLING_LINK		BIT(3)
-#define  YT921X_MDIO_POLLING_SPEED_M		GENMASK(2, 0)
-#define   YT921X_MDIO_POLLING_SPEED(x)			FIELD_PREP(YT921X_MDIO_POLLING_SPEED_M, (x))
-#define   YT921X_MDIO_POLLING_SPEED_10			YT921X_MDIO_POLLING_SPEED(0)
-#define   YT921X_MDIO_POLLING_SPEED_100			YT921X_MDIO_POLLING_SPEED(1)
-#define   YT921X_MDIO_POLLING_SPEED_1000		YT921X_MDIO_POLLING_SPEED(2)
-#define   YT921X_MDIO_POLLING_SPEED_10000		YT921X_MDIO_POLLING_SPEED(3)
-#define   YT921X_MDIO_POLLING_SPEED_2500		YT921X_MDIO_POLLING_SPEED(4)
-#define YT921X_SENSOR			0x8036c
-#define  YT921X_SENSOR_TEMP			BIT(18)
-#define YT921X_TEMP			0x80374
-#define YT921X_CHIP_MODE		0x80388
-#define  YT921X_CHIP_MODE_MODE			GENMASK(1, 0)
-#define YT921X_XMII_CTRL		0x80394
-#define  YT921X_XMII_CTRL_PORTn(port)		BIT(9 - (port))  /* Yes, it's reversed */
-#define YT921X_XMIIn(port)		(0x80400 + 8 * ((port) - 8))
-#define  YT921X_XMII_MODE_M			GENMASK(31, 29)
-#define   YT921X_XMII_MODE(x)				FIELD_PREP(YT921X_XMII_MODE_M, (x))
-#define   YT921X_XMII_MODE_MII				YT921X_XMII_MODE(0)
-#define   YT921X_XMII_MODE_REVMII			YT921X_XMII_MODE(1)
-#define   YT921X_XMII_MODE_RMII				YT921X_XMII_MODE(2)
-#define   YT921X_XMII_MODE_REVRMII			YT921X_XMII_MODE(3)
-#define   YT921X_XMII_MODE_RGMII			YT921X_XMII_MODE(4)
-#define   YT921X_XMII_MODE_DISABLE			YT921X_XMII_MODE(5)
-#define  YT921X_XMII_LINK			BIT(19)  /* force link */
-#define  YT921X_XMII_EN				BIT(18)
-#define  YT921X_XMII_SOFT_RST			BIT(17)
-#define  YT921X_XMII_RGMII_TX_DELAY_150PS_M	GENMASK(16, 13)
-#define   YT921X_XMII_RGMII_TX_DELAY_150PS(x)		FIELD_PREP(YT921X_XMII_RGMII_TX_DELAY_150PS_M, (x))
-#define  YT921X_XMII_TX_CLK_IN			BIT(11)
-#define  YT921X_XMII_RX_CLK_IN			BIT(10)
-#define  YT921X_XMII_RGMII_TX_DELAY_2NS		BIT(8)
-#define  YT921X_XMII_RGMII_TX_CLK_OUT		BIT(7)
-#define  YT921X_XMII_RGMII_RX_DELAY_150PS_M	GENMASK(6, 3)
-#define   YT921X_XMII_RGMII_RX_DELAY_150PS(x)		FIELD_PREP(YT921X_XMII_RGMII_RX_DELAY_150PS_M, (x))
-#define  YT921X_XMII_RMII_PHY_TX_CLK_OUT	BIT(2)
-#define  YT921X_XMII_REVMII_TX_CLK_OUT		BIT(1)
-#define  YT921X_XMII_REVMII_RX_CLK_OUT		BIT(0)
-
-#define YT921X_MACn_FRAME(port)		(0x81008 + 0x1000 * (port))
-#define  YT921X_MAC_FRAME_SIZE_M		GENMASK(21, 8)
-#define   YT921X_MAC_FRAME_SIZE(x)			FIELD_PREP(YT921X_MAC_FRAME_SIZE_M, (x))
-
-#define YT921X_EEEn_VAL(port)		(0xa0000 + 0x40 * (port))
-#define  YT921X_EEE_VAL_DATA			BIT(1)
-
-#define YT921X_EEE_CTRL			0xb0000
-#define  YT921X_EEE_CTRL_ENn(port)		BIT(port)
-
-#define YT921X_MIB_CTRL			0xc0004
-#define  YT921X_MIB_CTRL_CLEAN			BIT(30)
-#define  YT921X_MIB_CTRL_PORT_M			GENMASK(6, 3)
-#define   YT921X_MIB_CTRL_PORT(x)			FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
-#define  YT921X_MIB_CTRL_ONE_PORT		BIT(1)
-#define  YT921X_MIB_CTRL_ALL_PORT		BIT(0)
-#define YT921X_MIBn_DATA0(port)		(0xc0100 + 0x100 * (port))
-#define YT921X_MIBn_DATAm(port, x)	(YT921X_MIBn_DATA0(port) + 4 * (x))
-#define  YT921X_MIB_DATA_RX_BROADCAST		0x00
-#define  YT921X_MIB_DATA_RX_PAUSE		0x04
-#define  YT921X_MIB_DATA_RX_MULTICAST		0x08
-#define  YT921X_MIB_DATA_RX_CRC_ERR		0x0c
-
-#define  YT921X_MIB_DATA_RX_ALIGN_ERR		0x10
-#define  YT921X_MIB_DATA_RX_UNDERSIZE_ERR	0x14
-#define  YT921X_MIB_DATA_RX_FRAG_ERR		0x18
-#define  YT921X_MIB_DATA_RX_PKT_SZ_64		0x1c
-
-#define  YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127	0x20
-#define  YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255	0x24
-#define  YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511	0x28
-#define  YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023	0x2c
-
-#define  YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518	0x30
-#define  YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX	0x34
-/* 0x38: unused */
-#define  YT921X_MIB_DATA_RX_GOOD_BYTES		0x3c
-
-/* 0x40: 64 bytes */
-#define  YT921X_MIB_DATA_RX_BAD_BYTES		0x44
-/* 0x48: 64 bytes */
-#define  YT921X_MIB_DATA_RX_OVERSIZE_ERR	0x4c
-
-#define  YT921X_MIB_DATA_RX_DROPPED		0x50
-#define  YT921X_MIB_DATA_TX_BROADCAST		0x54
-#define  YT921X_MIB_DATA_TX_PAUSE		0x58
-#define  YT921X_MIB_DATA_TX_MULTICAST		0x5c
-
-#define  YT921X_MIB_DATA_TX_UNDERSIZE_ERR	0x60
-#define  YT921X_MIB_DATA_TX_PKT_SZ_64		0x64
-#define  YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127	0x68
-#define  YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255	0x6c
-
-#define  YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511	0x70
-#define  YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023	0x74
-#define  YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518	0x78
-#define  YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX	0x7c
-
-/* 0x80: unused */
-#define  YT921X_MIB_DATA_TX_GOOD_BYTES		0x84
-/* 0x88: 64 bytes */
-#define  YT921X_MIB_DATA_TX_COLLISION		0x8c
-
-#define  YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION	0x90
-#define  YT921X_MIB_DATA_TX_MULTIPLE_COLLISION	0x94
-#define  YT921X_MIB_DATA_TX_SINGLE_COLLISION	0x98
-#define  YT921X_MIB_DATA_TX_PKT			0x9c
-
-#define  YT921X_MIB_DATA_TX_DEFERRED		0xa0
-#define  YT921X_MIB_DATA_TX_LATE_COLLISION	0xa4
-#define  YT921X_MIB_DATA_RX_OAM			0xa8
-#define  YT921X_MIB_DATA_TX_OAM			0xac
-
-#define YT921X_EDATA_CTRL		0xe0000
-#define  YT921X_EDATA_CTRL_ADDR_M		GENMASK(15, 8)
-#define   YT921X_EDATA_CTRL_ADDR(x)			FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x))
-#define  YT921X_EDATA_CTRL_OP_M			GENMASK(3, 0)
-#define   YT921X_EDATA_CTRL_OP(x)			FIELD_PREP(YT921X_EDATA_CTRL_OP_M, (x))
-#define   YT921X_EDATA_CTRL_READ			YT921X_EDATA_CTRL_OP(5)
-#define YT921X_EDATA_DATA		0xe0004
-#define  YT921X_EDATA_DATA_DATA_M			GENMASK(31, 24)
-#define  YT921X_EDATA_DATA_STATUS_M		GENMASK(3, 0)
-#define   YT921X_EDATA_DATA_STATUS(x)			FIELD_PREP(YT921X_EDATA_DATA_STATUS_M, (x))
-#define   YT921X_EDATA_DATA_IDLE			YT921X_EDATA_DATA_STATUS(3)
-#define YT921X_SYS_CLK			0xe0040
-#define  YT921X_SYS_CLK_SEL_M			GENMASK(1, 0)  /* unknown: 167M */
-#define   YT9215_SYS_CLK_125M				0
-#define   YT9218_SYS_CLK_167M				0
-#define   YT921X_SYS_CLK_143M				1
-
-#define YT921X_EXT_MBUS_OP		0x6a000
-#define YT921X_INT_MBUS_OP		0xf0000
-#define  YT921X_MBUS_OP_START			BIT(0)
-#define YT921X_EXT_MBUS_CTRL		0x6a004
-#define YT921X_INT_MBUS_CTRL		0xf0004
-#define  YT921X_MBUS_CTRL_PORT_M		GENMASK(25, 21)
-#define   YT921X_MBUS_CTRL_PORT(x)			FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (x))
-#define  YT921X_MBUS_CTRL_REG_M			GENMASK(20, 16)
-#define   YT921X_MBUS_CTRL_REG(x)			FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x))
-#define  YT921X_MBUS_CTRL_TYPE_M		GENMASK(11, 8)  /* wild guess */
-#define   YT921X_MBUS_CTRL_TYPE(x)			FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (x))
-#define   YT921X_MBUS_CTRL_TYPE_C22			YT921X_MBUS_CTRL_TYPE(4)
-#define  YT921X_MBUS_CTRL_OP_M			GENMASK(3, 2)  /* wild guess */
-#define   YT921X_MBUS_CTRL_OP(x)			FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x))
-#define   YT921X_MBUS_CTRL_WRITE			YT921X_MBUS_CTRL_OP(1)
-#define   YT921X_MBUS_CTRL_READ				YT921X_MBUS_CTRL_OP(2)
-#define YT921X_EXT_MBUS_DOUT		0x6a008
-#define YT921X_INT_MBUS_DOUT		0xf0008
-#define YT921X_EXT_MBUS_DIN		0x6a00c
-#define YT921X_INT_MBUS_DIN		0xf000c
-
-#define YT921X_PORTn_EGR(port)		(0x100000 + 4 * (port))
-#define  YT921X_PORT_EGR_TPID_CTAG_M		GENMASK(5, 4)
-#define   YT921X_PORT_EGR_TPID_CTAG(x)			FIELD_PREP(YT921X_PORT_EGR_TPID_CTAG_M, (x))
-#define  YT921X_PORT_EGR_TPID_STAG_M		GENMASK(3, 2)
-#define   YT921X_PORT_EGR_TPID_STAG(x)			FIELD_PREP(YT921X_PORT_EGR_TPID_STAG_M, (x))
-#define YT921X_TPID_EGRn(x)		(0x100300 + 4 * (x))	/* [0, 3] */
-#define  YT921X_TPID_EGR_TPID_M			GENMASK(15, 0)
-
-#define YT921X_IPM_DSCPn(n)		(0x180000 + 4 * (n))	/* Internal Priority Map */
-#define YT921X_IPM_PCPn(map, dei, pcp)	(0x180100 + 4 * (16 * (map) + 8 * (dei) + (pcp)))
-#define  YT921X_IPM_PRIO_M			GENMASK(4, 2)
-#define   YT921X_IPM_PRIO(x)				FIELD_PREP(YT921X_IPM_PRIO_M, (x))
-#define  YT921X_IPM_COLOR_M			GENMASK(1, 0)
-#define   YT921X_IPM_COLOR(x)				FIELD_PREP(YT921X_IPM_COLOR_M, (x))
-#define   YT921X_IPM_COLOR_GREEN			YT921X_IPM_COLOR(0)
-#define   YT921X_IPM_COLOR_YELLOW			YT921X_IPM_COLOR(1)
-#define   YT921X_IPM_COLOR_RED				YT921X_IPM_COLOR(2)
-#define YT921X_PORTn_QOS(port)		(0x180180 + 4 * (port))
-#define  YT921X_PORT_QOS_CVLAN_PRIO_MAP_ID	BIT(5)
-#define  YT921X_PORT_QOS_SVLAN_PRIO_MAP_ID	BIT(4)
-#define  YT921X_PORT_QOS_PRIO_M			GENMASK(3, 1)
-#define   YT921X_PORT_QOS_PRIO(x)			FIELD_PREP(YT921X_PORT_QOS_PRIO_M, (x))
-#define  YT921X_PORT_QOS_PRIO_EN		BIT(0)
-#define YT921X_PORTn_PRIO_ORD(port)	(0x180200 + 4 * (port))
-#define  YT921X_PORT_PRIO_ORD_APPm_M(m)		GENMASK(3 * (m) + 2, 3 * (m))
-#define   YT921X_PORT_PRIO_ORD_APPm(m, x)		((x) << (3 * (m)))	/* 0: disabled, except PORT_QOS_PRIO */
-
-enum yt921x_app_selector {
-	YT921X_APP_SEL_MAC_SA,
-	YT921X_APP_SEL_MAC_DA,
-	YT921X_APP_SEL_VID,
-	YT921X_APP_SEL_ACL,
-	YT921X_APP_SEL_DSCP,
-	YT921X_APP_SEL_CVLAN_PCP,
-	YT921X_APP_SEL_SVLAN_PCP,
-	/* The physical port, i.e. YT921X_PORT_QOS_PRIO */
-	YT921X_APP_SEL_PORT,
-	YT921X_APP_SEL_NUM
-};
-
-#define YT921X_VLAN_IGR_FILTER		0x180280
-#define  YT921X_VLAN_IGR_FILTER_PORTn_BYPASS_IGMP(port)	BIT((port) + 11)
-#define  YT921X_VLAN_IGR_FILTER_PORTn(port)	BIT(port)
-#define YT921X_PORTn_ISOLATION(port)	(0x180294 + 4 * (port))
-#define  YT921X_PORT_ISOLATION_BLOCKn(port)	BIT(port)
-#define YT921X_STPn(n)			(0x18038c + 4 * (n))
-#define  YT921X_STP_PORTn_M(port)		GENMASK(2 * (port) + 1, 2 * (port))
-#define   YT921X_STP_PORTn(port, x)			((x) << (2 * (port)))
-#define   YT921X_STP_PORTn_DISABLED(port)		YT921X_STP_PORTn(port, 0)
-#define   YT921X_STP_PORTn_LEARNING(port)		YT921X_STP_PORTn(port, 1)
-#define   YT921X_STP_PORTn_BLOCKING(port)		YT921X_STP_PORTn(port, 2)
-#define   YT921X_STP_PORTn_FORWARD(port)		YT921X_STP_PORTn(port, 3)
-#define YT921X_PORTn_LEARN(port)	(0x1803d0 + 4 * (port))
-#define  YT921X_PORT_LEARN_VID_LEARN_MULTI_EN	BIT(22)
-#define  YT921X_PORT_LEARN_VID_LEARN_MODE	BIT(21)
-#define  YT921X_PORT_LEARN_VID_LEARN_EN		BIT(20)
-#define  YT921X_PORT_LEARN_SUSPEND_COPY_EN	BIT(19)
-#define  YT921X_PORT_LEARN_SUSPEND_DROP_EN	BIT(18)
-#define  YT921X_PORT_LEARN_DIS			BIT(17)
-#define  YT921X_PORT_LEARN_LIMIT_EN		BIT(16)
-#define  YT921X_PORT_LEARN_LIMIT_M		GENMASK(15, 8)
-#define   YT921X_PORT_LEARN_LIMIT(x)			FIELD_PREP(YT921X_PORT_LEARN_LIMIT_M, (x))
-#define  YT921X_PORT_LEARN_DROP_ON_EXCEEDED	BIT(2)
-#define  YT921X_PORT_LEARN_MODE_M		GENMASK(1, 0)
-#define   YT921X_PORT_LEARN_MODE(x)			FIELD_PREP(YT921X_PORT_LEARN_MODE_M, (x))
-#define   YT921X_PORT_LEARN_MODE_AUTO			YT921X_PORT_LEARN_MODE(0)
-#define   YT921X_PORT_LEARN_MODE_AUTO_AND_COPY		YT921X_PORT_LEARN_MODE(1)
-#define   YT921X_PORT_LEARN_MODE_CPU_CONTROL		YT921X_PORT_LEARN_MODE(2)
-#define YT921X_AGEING			0x180440
-#define  YT921X_AGEING_INTERVAL_M		GENMASK(15, 0)
-#define YT921X_FDB_IN0			0x180454
-#define YT921X_FDB_IN1			0x180458
-#define YT921X_FDB_IN2			0x18045c
-#define YT921X_FDB_OP			0x180460
-#define  YT921X_FDB_OP_INDEX_M			GENMASK(22, 11)
-#define   YT921X_FDB_OP_INDEX(x)			FIELD_PREP(YT921X_FDB_OP_INDEX_M, (x))
-#define  YT921X_FDB_OP_MODE_INDEX		BIT(10)  /* mac+fid / index */
-#define  YT921X_FDB_OP_FLUSH_MCAST		BIT(9)  /* ucast / mcast */
-#define  YT921X_FDB_OP_FLUSH_M			GENMASK(8, 7)
-#define   YT921X_FDB_OP_FLUSH(x)			FIELD_PREP(YT921X_FDB_OP_FLUSH_M, (x))
-#define   YT921X_FDB_OP_FLUSH_ALL			YT921X_FDB_OP_FLUSH(0)
-#define   YT921X_FDB_OP_FLUSH_PORT			YT921X_FDB_OP_FLUSH(1)
-#define   YT921X_FDB_OP_FLUSH_PORT_VID			YT921X_FDB_OP_FLUSH(2)
-#define   YT921X_FDB_OP_FLUSH_VID			YT921X_FDB_OP_FLUSH(3)
-#define  YT921X_FDB_OP_FLUSH_STATIC		BIT(6)
-#define  YT921X_FDB_OP_NEXT_TYPE_M		GENMASK(5, 4)
-#define   YT921X_FDB_OP_NEXT_TYPE(x)			FIELD_PREP(YT921X_FDB_OP_NEXT_TYPE_M, (x))
-#define   YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT		YT921X_FDB_OP_NEXT_TYPE(0)
-#define   YT921X_FDB_OP_NEXT_TYPE_UCAST_VID		YT921X_FDB_OP_NEXT_TYPE(1)
-#define   YT921X_FDB_OP_NEXT_TYPE_UCAST			YT921X_FDB_OP_NEXT_TYPE(2)
-#define   YT921X_FDB_OP_NEXT_TYPE_MCAST			YT921X_FDB_OP_NEXT_TYPE(3)
-#define  YT921X_FDB_OP_OP_M			GENMASK(3, 1)
-#define   YT921X_FDB_OP_OP(x)				FIELD_PREP(YT921X_FDB_OP_OP_M, (x))
-#define   YT921X_FDB_OP_OP_ADD				YT921X_FDB_OP_OP(0)
-#define   YT921X_FDB_OP_OP_DEL				YT921X_FDB_OP_OP(1)
-#define   YT921X_FDB_OP_OP_GET_ONE			YT921X_FDB_OP_OP(2)
-#define   YT921X_FDB_OP_OP_GET_NEXT			YT921X_FDB_OP_OP(3)
-#define   YT921X_FDB_OP_OP_FLUSH			YT921X_FDB_OP_OP(4)
-#define  YT921X_FDB_OP_START			BIT(0)
-#define YT921X_FDB_RESULT		0x180464
-#define  YT921X_FDB_RESULT_DONE			BIT(15)
-#define  YT921X_FDB_RESULT_NOTFOUND		BIT(14)
-#define  YT921X_FDB_RESULT_OVERWRITED		BIT(13)
-#define  YT921X_FDB_RESULT_INDEX_M		GENMASK(11, 0)
-#define   YT921X_FDB_RESULT_INDEX(x)			FIELD_PREP(YT921X_FDB_RESULT_INDEX_M, (x))
-#define YT921X_FDB_OUT0			0x1804b0
-#define  YT921X_FDB_IO0_ADDR_HI4_M		GENMASK(31, 0)
-#define YT921X_FDB_OUT1			0x1804b4
-#define  YT921X_FDB_IO1_EGR_PRIO_EN		BIT(31)
-#define  YT921X_FDB_IO1_STATUS_M		GENMASK(30, 28)
-#define   YT921X_FDB_IO1_STATUS(x)			FIELD_PREP(YT921X_FDB_IO1_STATUS_M, (x))
-#define   YT921X_FDB_IO1_STATUS_INVALID			YT921X_FDB_IO1_STATUS(0)
-#define   YT921X_FDB_IO1_STATUS_MIN_TIME		YT921X_FDB_IO1_STATUS(1)
-#define   YT921X_FDB_IO1_STATUS_MOVE_AGING_MAX_TIME	YT921X_FDB_IO1_STATUS(3)
-#define   YT921X_FDB_IO1_STATUS_MAX_TIME		YT921X_FDB_IO1_STATUS(5)
-#define   YT921X_FDB_IO1_STATUS_PENDING			YT921X_FDB_IO1_STATUS(6)
-#define   YT921X_FDB_IO1_STATUS_STATIC			YT921X_FDB_IO1_STATUS(7)
-#define  YT921X_FDB_IO1_FID_M			GENMASK(27, 16)  /* filtering ID (VID) */
-#define   YT921X_FDB_IO1_FID(x)				FIELD_PREP(YT921X_FDB_IO1_FID_M, (x))
-#define  YT921X_FDB_IO1_ADDR_LO2_M		GENMASK(15, 0)
-#define YT921X_FDB_OUT2			0x1804b8
-#define  YT921X_FDB_IO2_MOVE_AGING_STATUS_M	GENMASK(31, 30)
-#define  YT921X_FDB_IO2_IGR_DROP		BIT(29)
-#define  YT921X_FDB_IO2_EGR_PORTS_M		GENMASK(28, 18)
-#define   YT921X_FDB_IO2_EGR_PORTS(x)			FIELD_PREP(YT921X_FDB_IO2_EGR_PORTS_M, (x))
-#define  YT921X_FDB_IO2_EGR_DROP		BIT(17)
-#define  YT921X_FDB_IO2_COPY_TO_CPU		BIT(16)
-#define  YT921X_FDB_IO2_IGR_PRIO_EN		BIT(15)
-#define  YT921X_FDB_IO2_PRIO_M			GENMASK(14, 12)
-#define   YT921X_FDB_IO2_PRIO(x)			FIELD_PREP(YT921X_FDB_IO2_PRIO_M, (x))
-#define  YT921X_FDB_IO2_NEW_VID_M		GENMASK(11, 0)
-#define   YT921X_FDB_IO2_NEW_VID(x)			FIELD_PREP(YT921X_FDB_IO2_NEW_VID_M, (x))
-#define YT921X_FILTER_UNK_UCAST		0x180508
-#define YT921X_FILTER_UNK_MCAST		0x18050c
-#define YT921X_FILTER_MCAST		0x180510
-#define YT921X_FILTER_BCAST		0x180514
-#define  YT921X_FILTER_PORTS_M			GENMASK(10, 0)
-#define   YT921X_FILTER_PORTS(x)			FIELD_PREP(YT921X_FILTER_PORTS_M, (x))
-#define  YT921X_FILTER_PORTn(port)		BIT(port)
-#define YT921X_VLAN_EGR_FILTER		0x180598
-#define  YT921X_VLAN_EGR_FILTER_PORTn(port)	BIT(port)
-#define YT921X_LAG_GROUPn(n)		(0x1805a8 + 4 * (n))
-#define  YT921X_LAG_GROUP_PORTS_M		GENMASK(13, 3)
-#define   YT921X_LAG_GROUP_PORTS(x)			FIELD_PREP(YT921X_LAG_GROUP_PORTS_M, (x))
-#define  YT921X_LAG_GROUP_MEMBER_NUM_M		GENMASK(2, 0)
-#define   YT921X_LAG_GROUP_MEMBER_NUM(x)		FIELD_PREP(YT921X_LAG_GROUP_MEMBER_NUM_M, (x))
-#define YT921X_LAG_MEMBERnm(n, m)	(0x1805b0 + 4 * (4 * (n) + (m)))
-#define  YT921X_LAG_MEMBER_PORT_M		GENMASK(3, 0)
-#define   YT921X_LAG_MEMBER_PORT(x)			FIELD_PREP(YT921X_LAG_MEMBER_PORT_M, (x))
-#define YT921X_CPU_COPY			0x180690
-#define  YT921X_CPU_COPY_FORCE_INT_PORT		BIT(2)
-#define  YT921X_CPU_COPY_TO_INT_CPU		BIT(1)
-#define  YT921X_CPU_COPY_TO_EXT_CPU		BIT(0)
-#define YT921X_ACL_PERMIT_UNMATCH	0x1806a0
-#define  YT921X_ACL_PERMIT_UNMATCH_PORTS_M	GENMASK(10, 0)
-#define   YT921X_ACL_PERMIT_UNMATCH_PORTS(x)		FIELD_PREP(YT921X_ACL_PERMIT_UNMATCH_PORTS_M, (x))
-#define  YT921X_ACL_PERMIT_UNMATCH_PORTn(port)	BIT(port)
-#define YT921X_ACT_UNK_UCAST		0x180734
-#define YT921X_ACT_UNK_MCAST		0x180738
-#define  YT921X_ACT_UNK_MCAST_BYPASS_DROP_RMA	BIT(23)
-#define  YT921X_ACT_UNK_MCAST_BYPASS_DROP_IGMP	BIT(22)
-#define  YT921X_ACT_UNK_ACTn_M(port)		GENMASK(2 * (port) + 1, 2 * (port))
-#define   YT921X_ACT_UNK_ACTn(port, x)			((x) << (2 * (port)))
-#define   YT921X_ACT_UNK_ACTn_FORWARD(port)		YT921X_ACT_UNK_ACTn(port, 0)  /* flood */
-#define   YT921X_ACT_UNK_ACTn_TRAP(port)		YT921X_ACT_UNK_ACTn(port, 1)  /* steer to CPU */
-#define   YT921X_ACT_UNK_ACTn_DROP(port)		YT921X_ACT_UNK_ACTn(port, 2)  /* discard */
-/* NEVER use this action; see comments in the tag driver */
-#define   YT921X_ACT_UNK_ACTn_COPY(port)		YT921X_ACT_UNK_ACTn(port, 3)  /* flood and copy */
-#define YT921X_FDB_HW_FLUSH		0x180958
-#define  YT921X_FDB_HW_FLUSH_ON_LINKDOWN	BIT(0)
-
-#define YT921X_VLANn_CTRL(vlan)		(0x188000 + 8 * (vlan))
-#define  YT921X_VLAN_CTRLb_UNTAG_PORTS_M	GENMASK(18, 8)
-#define   YT921X_VLAN_CTRLb_UNTAG_PORTS(x)		FIELD_PREP(YT921X_VLAN_CTRLb_UNTAG_PORTS_M, (x))
-#define  YT921X_VLAN_CTRLb_UNTAG_PORTn(port)	BIT((port) + 8)
-#define  YT921X_VLAN_CTRLb_STP_ID_M		GENMASK(7, 4)
-#define   YT921X_VLAN_CTRLb_STP_ID(x)			FIELD_PREP(YT921X_VLAN_CTRLb_STP_ID_M, (x))
-#define  YT921X_VLAN_CTRLb_SVLAN_EN		BIT(3)
-#define  YT921X_VLAN_CTRLab_FID_M		GENMASK_ULL(34, 23)
-#define   YT921X_VLAN_CTRLab_FID(x)			FIELD_PREP(YT921X_VLAN_CTRLab_FID_M, (x))
-#define  YT921X_VLAN_CTRLa_LEARN_DIS		BIT(22)
-#define  YT921X_VLAN_CTRLa_PRIO_EN		BIT(21)
-#define  YT921X_VLAN_CTRLa_PRIO_M		GENMASK(20, 18)
-#define   YT921X_VLAN_CTRLa_PRIO(x)			FIELD_PREP(YT921X_VLAN_CTRLa_PRIO_M, (x))
-#define  YT921X_VLAN_CTRLa_PORTS_M		GENMASK(17, 7)
-#define   YT921X_VLAN_CTRLa_PORTS(x)			FIELD_PREP(YT921X_VLAN_CTRLa_PORTS_M, (x))
-#define  YT921X_VLAN_CTRLa_PORTn(port)		BIT((port) + 7)
-#define  YT921X_VLAN_CTRLa_BYPASS_1X_AC		BIT(6)
-#define  YT921X_VLAN_CTRLa_METER_EN		BIT(5)
-#define  YT921X_VLAN_CTRLa_METER_ID_M		GENMASK(4, 0)
-
-#define YT921X_ACLn_ACT(n)		(0x1c0000 + 0x10 * (n))
-#define  YT921X_ACL_ACTc_STAG_M			GENMASK(26, 25)
-#define   YT921X_ACL_ACTc_STAG(x)			FIELD_PREP(YT921X_ACL_ACTc_STAG_M, (x))
-#define   YT921X_ACL_ACTc_STAG_DONTCARE			YT921X_ACL_ACTc_STAG(0)
-#define   YT921X_ACL_ACTc_STAG_UNTAG			YT921X_ACL_ACTc_STAG(1)
-#define   YT921X_ACL_ACTc_STAG_TAG			YT921X_ACL_ACTc_STAG(2)
-#define   YT921X_ACL_ACTc_STAG_KEEP			YT921X_ACL_ACTc_STAG(3)
-#define  YT921X_ACL_ACTc_CTAG_M			GENMASK(24, 23)
-#define   YT921X_ACL_ACTc_CTAG(x)			FIELD_PREP(YT921X_ACL_ACTc_CTAG_M, (x))
-#define   YT921X_ACL_ACTc_CTAG_DONTCARE			YT921X_ACL_ACTc_CTAG(0)
-#define   YT921X_ACL_ACTc_CTAG_UNTAG			YT921X_ACL_ACTc_CTAG(1)
-#define   YT921X_ACL_ACTc_CTAG_TAG			YT921X_ACL_ACTc_CTAG(2)
-#define   YT921X_ACL_ACTc_CTAG_KEEP			YT921X_ACL_ACTc_CTAG(3)
-#define  YT921X_ACL_ACTc_FWD_M			GENMASK(22, 21)
-#define   YT921X_ACL_ACTc_FWD(x)			FIELD_PREP(YT921X_ACL_ACTc_FWD_M, (x))
-#define   YT921X_ACL_ACTc_FWD_FWD			YT921X_ACL_ACTc_FWD(0)
-#define   YT921X_ACL_ACTc_FWD_COPY			YT921X_ACL_ACTc_FWD(1)
-#define   YT921X_ACL_ACTc_FWD_REDIR			YT921X_ACL_ACTc_FWD(2)
-#define   YT921X_ACL_ACTc_FWD_TRAP			YT921X_ACL_ACTc_FWD(3)
-#define  YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M	GENMASK(20, 10)
-#define   YT921X_ACL_ACTc_FWD_REDIR_DPORTS(x)		FIELD_PREP(YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M, (x))
-#define  YT921X_ACL_ACTc_FWD_REDIR_DPORTn(port)	BIT((port) + 10)
-#define  YT921X_ACL_ACTc_FWD_EN			BIT(9)
-#define  YT921X_ACL_ACTc_SDEI			BIT(8)
-#define  YT921X_ACL_ACTc_SDEI_REPLACE		BIT(7)
-#define  YT921X_ACL_ACTc_SPRI_M			GENMASK(6, 4)
-#define   YT921X_ACL_ACTc_SPRI(x)			FIELD_PREP(YT921X_ACL_ACTc_SPRI_M, (x))
-#define  YT921X_ACL_ACTc_SPRI_REPLACE		BIT(3)
-#define  YT921X_ACL_ACTbc_SVID_M		GENMASK_ULL(34, 23)
-#define   YT921X_ACL_ACTbc_SVID(x)			FIELD_PREP(YT921X_ACL_ACTbc_SVID_M, (x))
-#define  YT921X_ACL_ACTb_SVID_REPLACE		BIT(22)
-#define  YT921X_ACL_ACTb_CDEI			BIT(21)
-#define  YT921X_ACL_ACTb_CDEI_REPLACE		BIT(20)
-#define  YT921X_ACL_ACTb_CPRI_M			GENMASK(19, 17)
-#define   YT921X_ACL_ACTb_CPRI(x)			FIELD_PREP(YT921X_ACL_ACTb_CPRI_M, (x))
-#define  YT921X_ACL_ACTb_CPRI_REPLACE		BIT(16)
-#define  YT921X_ACL_ACTb_CVID_M			GENMASK(15, 4)
-#define   YT921X_ACL_ACTb_CVID(x)			FIELD_PREP(YT921X_ACL_ACTb_CVID_M, (x))
-#define  YT921X_ACL_ACTb_CVID_REPLACE		BIT(3)
-#define  YT921X_ACL_ACTb_PRIO_M			GENMASK(2, 0)
-#define   YT921X_ACL_ACTb_PRIO(x)			FIELD_PREP(YT921X_ACL_ACTb_PRIO_M, (x))
-#define  YT921X_ACL_ACTa_PRIO_EN		BIT(31)
-#define  YT921X_ACL_ACTa_COLOR_M		GENMASK(30, 29)
-#define   YT921X_ACL_ACTa_COLOR(x)			FIELD_PREP(YT921X_ACL_ACTa_COLOR_M, (x))
-#define   YT921X_ACL_ACTa_COLOR_GREEN			YT921X_ACL_ACTa_COLOR(0)
-#define   YT921X_ACL_ACTa_COLOR_YELLOW			YT921X_ACL_ACTa_COLOR(1)
-#define   YT921X_ACL_ACTa_COLOR_RED			YT921X_ACL_ACTa_COLOR(2)
-#define  YT921X_ACL_ACTa_COLOR_EN		BIT(28)
-#define  YT921X_ACL_ACTa_DSCP_M			GENMASK(27, 22)
-#define   YT921X_ACL_ACTa_DSCP(x)			FIELD_PREP(YT921X_ACL_ACTa_DSCP_M, (x))
-#define  YT921X_ACL_ACTa_DSCP_REPLACE		BIT(21)
-#define  YT921X_ACL_ACTa_METER_ID_M		GENMASK(20, 15)
-#define   YT921X_ACL_ACTa_METER_ID(x)			FIELD_PREP(YT921X_ACL_ACTa_METER_ID_M, (x))
-#define  YT921X_ACL_ACTa_METER_EN		BIT(14)
-#define  YT921X_ACL_ACTa_MIRROR_EN		BIT(13)
-#define  YT921X_ACL_ACTa_FLOWSTAT_EN		BIT(12)
-#define  YT921X_ACL_ACTa_FLOWSTAT_ID_M		GENMASK(11, 6)
-#define   YT921X_ACL_ACTa_FLOWSTAT_ID(x)		FIELD_PREP(YT921X_ACL_ACTa_FLOWSTAT_ID_M, (x))
-#define  YT921X_ACL_ACTa_GPIO_EN		BIT(5)
-#define  YT921X_ACL_ACTa_GPIO_PIN_M		GENMASK(4, 1)
-#define   YT921X_ACL_ACTa_GPIO_PIN(x)			FIELD_PREP(YT921X_ACL_ACTa_GPIO_PIN_M, (x))
-#define  YT921X_ACL_ACTa_INTR_EN		BIT(0)
-#define YT921X_ACL_BLK_KEEP		0x201000
-#define  YT921X_ACL_BLK_KEEP_GRPIDn_M(bin)	(7 << (4 * (bin) + 1))
-#define   YT921X_ACL_BLK_KEEP_GRPIDn(bin, x)		((x) << (4 * (bin) + 1))
-#define  YT921X_ACL_BLK_KEEP_KEEPn(bin)		BIT(4 * (bin))
-#define YT921X_ACL_PORT			0x202000
-#define  YT921X_ACL_PORT_PORTS_M		GENMASK(10, 0)
-#define   YT921X_ACL_PORT_PORTS(x)			FIELD_PREP(YT921X_ACL_PORT_PORTS_M, (x))
-#define  YT921X_ACL_PORT_PORTn(port)		BIT(port)
-#define YT921X_ACL_BLK_CMD		0x202004
-#define  YT921X_ACL_BLK_CMD_BLKID_M		GENMASK(6, 1)
-#define   YT921X_ACL_BLK_CMD_BLKID(x)			FIELD_PREP(YT921X_ACL_BLK_CMD_BLKID_M, (x))
-#define  YT921X_ACL_BLK_CMD_MODIFY		BIT(0)
-#define YT921X_ACLn_ENTRY(blk)		(0x203000 + 4 * (blk))
-#define  YT921X_ACL_ENTRY_GRPIDm_M(bin)		(7 << (4 * (bin) + 1))
-#define   YT921X_ACL_ENTRY_GRPIDm(bin, x)		((x) << (4 * (bin) + 1))
-#define  YT921X_ACL_ENTRY_ENm(bin)		BIT(4 * (bin))
-#define YT921X_ACLn_KEYm(blk, bin)	(0x204000 + 0x200 * (bin) + 8 * (blk))
-#define  YT921X_ACL_KEYb_ORD_M			GENMASK(29, 21)
-#define   YT921X_ACL_KEYb_ORD(x)			FIELD_PREP(YT921X_ACL_KEYb_ORD_M, (x))
-#define  YT921X_ACL_KEYb_SPORTS_M		GENMASK(20, 10)
-#define   YT921X_ACL_KEYb_SPORTS(x)			FIELD_PREP(YT921X_ACL_KEYb_SPORTS_M, (x))
-#define  YT921X_ACL_KEYb_SPORTn(port)		BIT((port) + 10)
-#define  YT921X_ACL_KEYb_REVERSE		BIT(9)	/* reverse match */
-#define  YT921X_ACL_KEYb_TYPE_M			GENMASK(8, 4)
-#define   YT921X_ACL_KEYb_TYPE(x)			FIELD_PREP(YT921X_ACL_KEYb_TYPE_M, (x))
-/* KEY_* fields need no masks */
-#define YT921X_ACLn_MASKm(blk, bin)	(0x205000 + 0x200 * (bin) + 8 * (blk))
-
-enum yt921x_acl_type {
-	YT921X_ACL_TYPE_NA,
-	YT921X_ACL_TYPE_MAC_DA0,
-	YT921X_ACL_TYPE_MAC_SA0,
-	YT921X_ACL_TYPE_MAC_DA1_SA1,
-	YT921X_ACL_TYPE_VLAN,
-	YT921X_ACL_TYPE_VTAG,
-	YT921X_ACL_TYPE_IPV4_DA,
-	YT921X_ACL_TYPE_IPV4_SA,
-	YT921X_ACL_TYPE_IPV6_DA0,
-	YT921X_ACL_TYPE_IPV6_DA1,
-	YT921X_ACL_TYPE_IPV6_DA2,
-	YT921X_ACL_TYPE_IPV6_DA3,
-	YT921X_ACL_TYPE_IPV6_SA0,
-	YT921X_ACL_TYPE_IPV6_SA1,
-	YT921X_ACL_TYPE_IPV6_SA2,
-	YT921X_ACL_TYPE_IPV6_SA3,
-	YT921X_ACL_TYPE_MISC,
-	YT921X_ACL_TYPE_L4,
-	YT921X_ACL_TYPE_UDF0,
-	YT921X_ACL_TYPE_UDF1,
-	YT921X_ACL_TYPE_UDF2,
-	YT921X_ACL_TYPE_UDF3,
-	YT921X_ACL_TYPE_UDF4,
-	YT921X_ACL_TYPE_UDF5,
-	YT921X_ACL_TYPE_UDF6,
-	YT921X_ACL_TYPE_UDF7,
-	YT921X_ACL_TYPE_ETHERTYPE,
-	YT921X_ACL_TYPE_NUM
-};
-
-/* Range: turn KEY:MASK into MIN:MAX */
-
-#define  YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M	GENMASK(3, 0)
-#define   YT921X_ACL_BINb_MAC_xA0_L3_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M, (x))
-#define  YT921X_ACL_BINa_MAC_xA0_MAC_xA0_M	GENMASK(31, 0)
-
-#define  YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M	GENMASK(2, 0)
-#define   YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE(x)	FIELD_PREP(YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M, (x))
-#define  YT921X_ACL_BINa_MAC_DA1_SA1_MAC_DA1_M	GENMASK(31, 16)
-#define  YT921X_ACL_BINa_MAC_DA1_SA1_MAC_SA1_M	GENMASK(15, 0)
-
-#define  YT921X_ACL_KEYb_VLAN_SVID_RANGE_EN	BIT(31)
-#define  YT921X_ACL_KEYb_VLAN_CVID_RANGE_EN	BIT(30)
-#define  YT921X_ACL_BINb_VLAN_CDEI		BIT(3)
-#define  YT921X_ACL_BINb_VLAN_CPRI_M		GENMASK(2, 0)
-#define   YT921X_ACL_BINb_VLAN_CPRI(x)			FIELD_PREP(YT921X_ACL_BINb_VLAN_CPRI_M, (x))
-#define  YT921X_ACL_BINa_VLAN_CTAG_FMT_M	GENMASK(31, 30)
-#define   YT921X_ACL_BINa_VLAN_CTAG_FMT(x)		FIELD_PREP(YT921X_ACL_BINa_VLAN_CTAG_FMT_M, (x))
-#define  YT921X_ACL_BINa_VLAN_SDEI		BIT(29)
-#define  YT921X_ACL_BINa_VLAN_SPRI_M		GENMASK(28, 26)
-#define   YT921X_ACL_BINa_VLAN_SPRI(x)			FIELD_PREP(YT921X_ACL_BINa_VLAN_SPRI_M, (x))
-#define  YT921X_ACL_BINa_VLAN_STAG_FMT_M	GENMASK(25, 24)
-#define   YT921X_ACL_BINa_VLAN_STAG_FMT(x)		FIELD_PREP(YT921X_ACL_BINa_VLAN_STAG_FMT_M, (x))
-#define  YT921X_ACL_BINa_VLAN_SVID_M		GENMASK(23, 12)
-#define   YT921X_ACL_BINa_VLAN_SVID(x)			FIELD_PREP(YT921X_ACL_BINa_VLAN_SVID_M, (x))
-#define  YT921X_ACL_BINa_VLAN_CVID_M		GENMASK(11, 0)
-#define   YT921X_ACL_BINa_VLAN_CVID(x)			FIELD_PREP(YT921X_ACL_BINa_VLAN_CVID_M, (x))
-
-#define  YT921X_ACL_KEYb_VTAG_SVID_RANGE_EN	BIT(31)
-#define  YT921X_ACL_KEYb_VTAG_CVID_RANGE_EN	BIT(30)
-#define  YT921X_ACL_BINa_VTAG_CDEI		BIT(31)
-#define  YT921X_ACL_BINa_VTAG_CPRI_M		GENMASK(30, 28)
-#define   YT921X_ACL_BINa_VTAG_CPRI(x)			FIELD_PREP(YT921X_ACL_BINa_VTAG_CPRI_M, (x))
-#define  YT921X_ACL_BINa_VTAG_SDEI		BIT(27)
-#define  YT921X_ACL_BINa_VTAG_SPRI_M		GENMASK(26, 24)
-#define   YT921X_ACL_BINa_VTAG_SPRI(x)			FIELD_PREP(YT921X_ACL_BINa_VTAG_SPRI_M, (x))
-#define  YT921X_ACL_BINa_VTAG_SVID_M		GENMASK(23, 12)
-#define   YT921X_ACL_BINa_VTAG_SVID(x)			FIELD_PREP(YT921X_ACL_BINa_VTAG_SVID_M, (x))
-#define  YT921X_ACL_BINa_VTAG_CVID_M		GENMASK(11, 0)
-#define   YT921X_ACL_BINa_VTAG_CVID(x)			FIELD_PREP(YT921X_ACL_BINa_VTAG_CVID_M, (x))
-
-#define  YT921X_ACL_KEYb_IPV4_ADDR_RANGE_EN	BIT(30)
-#define  YT921X_ACL_BINb_IPV4_FRAG		BIT(3)
-#define  YT921X_ACL_BINb_IPV4_L4_TYPE_M		GENMASK(2, 0)
-#define   YT921X_ACL_BINb_IPV4_L4_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_IPV4_L4_TYPE_M, (x))
-#define  YT921X_ACL_BINa_IPV4_ADDR_M		GENMASK(31, 0)
-
-#define  YT921X_ACL_BINb_IPV6_L4_TYPE_M		GENMASK(2, 0)
-#define   YT921X_ACL_BINb_IPV6_L4_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_IPV6_L4_TYPE_M, (x))
-#define  YT921X_ACL_BINa_IPV6_ADDRx_M		GENMASK(31, 0)
-
-#define  YT921X_ACL_BINb_IPV6_xA1_IP_OPTION	BIT(3)
-
-#define  YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG	BIT(3)
-
-#define  YT921X_ACL_KEYb_IPV6_xA3_ADDR_RANGE_EN	BIT(30)
-#define  YT921X_ACL_BINb_IPV6_xA3_FRAG		BIT(3)
-
-#define  YT921X_ACL_BINb_MISC_FRAG		BIT(3)
-#define  YT921X_ACL_BINb_MISC_L4_TYPE_M		GENMASK(2, 0)
-#define   YT921X_ACL_BINb_MISC_L4_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_MISC_L4_TYPE_M, (x))
-#define  YT921X_ACL_BINa_MISC_PPPOE_FLAG	BIT(30)
-#define  YT921X_ACL_BINa_MISC_FIRST_FRAG	BIT(29)
-#define  YT921X_ACL_BINa_MISC_IP_OPTION		BIT(28)
-#define  YT921X_ACL_BINa_MISC_TCP_FLAGS_M	GENMASK(27, 20)
-#define   YT921X_ACL_BINa_MISC_TCP_FLAGS(x)		FIELD_PREP(YT921X_ACL_BINa_MISC_TCP_FLAGS_M, (x))
-#define  YT921X_ACL_BINa_MISC_IP_PROTO_M	GENMASK(19, 12)
-#define   YT921X_ACL_BINa_MISC_IP_PROTO(x)		FIELD_PREP(YT921X_ACL_BINa_MISC_IP_PROTO_M, (x))
-#define  YT921X_ACL_BINa_MISC_TOS_M		GENMASK(11, 4)
-#define   YT921X_ACL_BINa_MISC_TOS(x)			FIELD_PREP(YT921X_ACL_BINa_MISC_TOS_M, (x))
-#define  YT921X_ACL_BINa_MISC_L3_TYPE_M		GENMASK(3, 0)
-#define   YT921X_ACL_BINa_MISC_L3_TYPE(x)		FIELD_PREP(YT921X_ACL_BINa_MISC_L3_TYPE_M, (x))
-
-#define  YT921X_ACL_KEYb_L4_DPORT_RANGE_EN	BIT(31)
-#define  YT921X_ACL_KEYb_L4_SPORT_RANGE_EN	BIT(30)
-#define  YT921X_ACL_BINb_L4_FRAG		BIT(3)
-#define  YT921X_ACL_BINb_L4_TYPE_M		GENMASK(2, 0)
-#define   YT921X_ACL_BINb_L4_TYPE(x)			FIELD_PREP(YT921X_ACL_BINb_L4_TYPE_M, (x))
-#define  YT921X_ACL_BINa_L4_DPORT_M		GENMASK(31, 16)
-#define  YT921X_ACL_BINa_L4_SPORT_M		GENMASK(15, 0)
-
-#define  YT921X_ACL_BINb_UDF_IS_IGMP		BIT(0)
-#define  YT921X_ACL_BINa_UDF_UDF0_M		GENMASK(31, 16)
-#define   YT921X_ACL_BINa_UDF_UDF0(x)			FIELD_PREP(YT921X_ACL_BINa_UDF_UDF0_M, (x))
-#define  YT921X_ACL_BINa_UDF_UDF1_M		GENMASK(15, 0)
-#define   YT921X_ACL_BINa_UDF_UDF1(x)			FIELD_PREP(YT921X_ACL_BINa_UDF_UDF1_M, (x))
-
-#define  YT921X_ACL_KEYb_ETHERTYPE_ETHERTYPE_RANGE_EN	BIT(30)
-#define  YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M	GENMASK(2, 0)
-#define   YT921X_ACL_BINb_ETHERTYPE_L4_TYPE(x)		FIELD_PREP(YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M, (x))
-#define  YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M	GENMASK(15, 0)
-#define   YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE(x)	FIELD_PREP(YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M, (x))
-
-enum yt921x_l2_type {
-	YT921X_L2_TYPE_ETH,
-	YT921X_L2_TYPE_ETHV2,
-	YT921X_L2_TYPE_ETHSAP,
-	YT921X_L2_TYPE_ETHSNAP,
-};
-
-enum yt921x_l3_type {
-	YT921X_L3_TYPE_OTHER,
-	YT921X_L3_TYPE_IPV4,
-	YT921X_L3_TYPE_IPV6,
-	YT921X_L3_TYPE_ARP,
-	YT921X_L3_TYPE_LLDP,
-	YT921X_L3_TYPE_PAE,
-	YT921X_L3_TYPE_ERP,
-	YT921X_L3_TYPE_SLOW_PROTOCOL,
-};
-
-enum yt921x_l4_type {
-	YT921X_L4_TYPE_OTHER,
-	YT921X_L4_TYPE_TCP,
-	YT921X_L4_TYPE_UDP,
-	YT921X_L4_TYPE_UDPLITE,
-	YT921X_L4_TYPE_ICMP,
-	YT921X_L4_TYPE_IGMP,
-	YT921X_L4_TYPE_MLD,
-	YT921X_L4_TYPE_ND,
-};
-
-#define YT921X_TPID_IGRn(x)		(0x210000 + 4 * (x))	/* [0, 3] */
-#define  YT921X_TPID_IGR_TPID_M			GENMASK(15, 0)
-#define YT921X_PORTn_IGR_TPID(port)	(0x210010 + 4 * (port))
-#define  YT921X_PORT_IGR_TPIDn_STAG_M		GENMASK(7, 4)
-#define  YT921X_PORT_IGR_TPIDn_STAG(x)		BIT((x) + 4)
-#define  YT921X_PORT_IGR_TPIDn_CTAG_M		GENMASK(3, 0)
-#define  YT921X_PORT_IGR_TPIDn_CTAG(x)		BIT(x)
-#define YT921X_LAG_HASH			0x210090
-#define  YT921X_LAG_HASH_L4_SPORT		BIT(7)
-#define  YT921X_LAG_HASH_L4_DPORT		BIT(6)
-#define  YT921X_LAG_HASH_IP_PROTO		BIT(5)
-#define  YT921X_LAG_HASH_IP_SRC			BIT(4)
-#define  YT921X_LAG_HASH_IP_DST			BIT(3)
-#define  YT921X_LAG_HASH_MAC_SA			BIT(2)
-#define  YT921X_LAG_HASH_MAC_DA			BIT(1)
-#define  YT921X_LAG_HASH_SRC_PORT		BIT(0)
-#define YT921X_UDFn_CTRL(x)		(0x210094 + 4 * (x))
-#define  YT921X_UDF_CTRL_UDF_TYPE_M		GENMASK(8, 7)
-#define   YT921X_UDF_CTRL_UDF_TYPE(x)			FIELD_PREP(YT921X_UDF_CTRL_UDF_TYPE_M, (x))
-#define   YT921X_UDF_CTRL_UDF_TYPE_ETH			YT921X_UDF_CTRL_UDF_TYPE(0)
-#define   YT921X_UDF_CTRL_UDF_TYPE_L3			YT921X_UDF_CTRL_UDF_TYPE(1)
-#define   YT921X_UDF_CTRL_UDF_TYPE_L4			YT921X_UDF_CTRL_UDF_TYPE(2)
-#define  YT921X_UDF_CTRL_UDF_OFFSET_M		GENMASK(6, 0)
-#define   YT921X_UDF_CTRL_UDF_OFFSET(x)			FIELD_PREP(YT921X_UDF_CTRL_UDF_OFFSET_M, (x))
-
-#define YT921X_PORTn_RATE(port)		(0x220000 + 4 * (port))
-#define  YT921X_PORT_RATE_GAP_VALUE		GENMASK(4, 0)	/* default 20 */
-#define YT921X_METER_SLOT		0x220104
-#define  YT921X_METER_SLOT_SLOT_M		GENMASK(11, 0)
-#define YT921X_PORTn_METER(port)	(0x220108 + 4 * (port))
-#define  YT921X_PORT_METER_EN			BIT(4)
-#define  YT921X_PORT_METER_ID_M			GENMASK(3, 0)
-#define   YT921X_PORT_METER_ID(x)			FIELD_PREP(YT921X_PORT_METER_ID_M, (x))
-#define YT921X_METERn_CTRL(x)		(0x220800 + 0x10 * (x))
-#define  YT921X_METER_CTRLc_METER_EN		BIT(14)
-#define  YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN	BIT(13)	/* RFC4115: yellow use unused green bw */
-#define  YT921X_METER_CTRLc_DROP_M		GENMASK(12, 11)
-#define   YT921X_METER_CTRLc_DROP(x)			FIELD_PREP(YT921X_METER_CTRLc_DROP_M, (x))
-#define   YT921X_METER_CTRLc_DROP_GYR			YT921X_METER_CTRLc_DROP(0)
-#define   YT921X_METER_CTRLc_DROP_YR			YT921X_METER_CTRLc_DROP(1)
-#define   YT921X_METER_CTRLc_DROP_R			YT921X_METER_CTRLc_DROP(2)
-#define   YT921X_METER_CTRLc_DROP_NONE			YT921X_METER_CTRLc_DROP(3)
-#define  YT921X_METER_CTRLc_COLOR_BLIND		BIT(10)
-#define  YT921X_METER_CTRLc_UNIT_M		GENMASK(9, 7)
-#define   YT921X_METER_CTRLc_UNIT(x)			FIELD_PREP(YT921X_METER_CTRLc_UNIT_M, (x))
-#define  YT921X_METER_CTRLc_BYTE_MODE_INCLUDE_GAP	BIT(6)	/* +GAP_VALUE bytes each packet */
-#define  YT921X_METER_CTRLc_PKT_MODE		BIT(5)	/* 0: byte rate mode */
-#define  YT921X_METER_CTRLc_RFC2698		BIT(4)	/* 0: RFC4115 */
-#define  YT921X_METER_CTRLbc_CBS_M		GENMASK_ULL(35, 20)
-#define   YT921X_METER_CTRLbc_CBS(x)			FIELD_PREP(YT921X_METER_CTRLbc_CBS_M, (x))
-#define  YT921X_METER_CTRLb_CIR_M		GENMASK(19, 2)
-#define   YT921X_METER_CTRLb_CIR(x)			FIELD_PREP(YT921X_METER_CTRLb_CIR_M, (x))
-#define  YT921X_METER_CTRLab_EBS_M		GENMASK_ULL(33, 18)
-#define   YT921X_METER_CTRLab_EBS(x)			FIELD_PREP(YT921X_METER_CTRLab_EBS_M, (x))
-#define  YT921X_METER_CTRLa_EIR_M		GENMASK(17, 0)
-#define   YT921X_METER_CTRLa_EIR(x)			FIELD_PREP(YT921X_METER_CTRLa_EIR_M, (x))
-#define YT921X_METERn_STAT(x)		(0x221000 + 8 * (x))
-
-#define YT921X_PORTn_VLAN_CTRL(port)	(0x230010 + 4 * (port))
-#define  YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_EN	BIT(31)
-#define  YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_EN	BIT(30)
-#define  YT921X_PORT_VLAN_CTRL_SVID_M		GENMASK(29, 18)
-#define   YT921X_PORT_VLAN_CTRL_SVID(x)			FIELD_PREP(YT921X_PORT_VLAN_CTRL_SVID_M, (x))
-#define  YT921X_PORT_VLAN_CTRL_CVID_M		GENMASK(17, 6)
-#define   YT921X_PORT_VLAN_CTRL_CVID(x)			FIELD_PREP(YT921X_PORT_VLAN_CTRL_CVID_M, (x))
-#define  YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_M	GENMASK(5, 3)
-#define  YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_M	GENMASK(2, 0)
-#define YT921X_PORTn_VLAN_CTRL1(port)	(0x230080 + 4 * (port))
-#define  YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_EN	BIT(8)
-#define  YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_PROFILE_ID_M	GENMASK(7, 4)
-#define  YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_TAGGED	BIT(3)
-#define  YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_UNTAGGED	BIT(2)
-#define  YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED	BIT(1)
-#define  YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED	BIT(0)
-
-#define YT921X_MIRROR			0x300300
-#define  YT921X_MIRROR_IGR_PORTS_M		GENMASK(26, 16)
-#define   YT921X_MIRROR_IGR_PORTS(x)			FIELD_PREP(YT921X_MIRROR_IGR_PORTS_M, (x))
-#define  YT921X_MIRROR_IGR_PORTn(port)		BIT((port) + 16)
-#define  YT921X_MIRROR_EGR_PORTS_M		GENMASK(14, 4)
-#define   YT921X_MIRROR_EGR_PORTS(x)			FIELD_PREP(YT921X_MIRROR_EGR_PORTS_M, (x))
-#define  YT921X_MIRROR_EGR_PORTn(port)		BIT((port) + 4)
-#define  YT921X_MIRROR_PORT_M			GENMASK(3, 0)
-#define   YT921X_MIRROR_PORT(x)				FIELD_PREP(YT921X_MIRROR_PORT_M, (x))
-
-#define YT921X_PORT_SHAPE_SLOT		0x34000c
-#define  YT921X_PORT_SHAPE_SLOT_SLOT_M		GENMASK(11, 0)
-#define YT921X_PORTn_SHAPE_CTRL(port)	(0x354000 + 8 * (port))
-#define  YT921X_PORT_SHAPE_CTRLb_EN		BIT(4)
-#define  YT921X_PORT_SHAPE_CTRLb_PKT_MODE	BIT(3)	/* 0: byte rate mode */
-#define  YT921X_PORT_SHAPE_CTRLb_UNIT_M		GENMASK(2, 0)
-#define   YT921X_PORT_SHAPE_CTRLb_UNIT(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x))
-#define  YT921X_PORT_SHAPE_CTRLa_CBS_M		GENMASK(31, 18)
-#define   YT921X_PORT_SHAPE_CTRLa_CBS(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x))
-#define  YT921X_PORT_SHAPE_CTRLa_CIR_M		GENMASK(17, 0)
-#define   YT921X_PORT_SHAPE_CTRLa_CIR(x)		FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x))
-#define YT921X_PORTn_SHAPE_STAT(port)	(0x356000 + 4 * (port))
-
-#define YT921X_EDATA_EXTMODE	0xfb
-#define YT921X_EDATA_LEN	0x100
-
-#define YT921X_FDB_NUM	4096
-
-enum yt921x_fdb_entry_status {
-	YT921X_FDB_ENTRY_STATUS_INVALID = 0,
-	YT921X_FDB_ENTRY_STATUS_MIN_TIME = 1,
-	YT921X_FDB_ENTRY_STATUS_MOVE_AGING_MAX_TIME = 3,
-	YT921X_FDB_ENTRY_STATUS_MAX_TIME = 5,
-	YT921X_FDB_ENTRY_STATUS_PENDING = 6,
-	YT921X_FDB_ENTRY_STATUS_STATIC = 7,
-};
-
-#define YT921X_MSTI_NUM		16
-
-#define YT921X_TOKEN_BYTE_C	1	/* 1 token = 2^1 byte */
-#define YT921X_TOKEN_PKT_C	-6	/* 1 token = 2^-6 packets */
-#define YT921X_TOKEN_RATE_C	-15
-/* Custom meters only, not including dedicated port meters (11) */
-#define YT921X_METER_NUM	64
-#define YT921X_METER_SLOT_MIN	80
-#define YT921X_METER_UNIT_MAX	((1 << 3) - 1)
-#define YT921X_METER_CIR_MAX	((1 << 18) - 1)
-#define YT921X_METER_CBS_MAX	((1 << 16) - 1)
-#define YT921X_PORT_SHAPE_SLOT_MIN	80
-#define YT921X_SHAPE_UNIT_MAX	((1 << 3) - 1)
-#define YT921X_SHAPE_CIR_MAX	((1 << 18) - 1)
-#define YT921X_SHAPE_CBS_MAX	((1 << 14) - 1)
-
-#define YT921X_LAG_NUM		2
-#define YT921X_LAG_PORT_NUM	4
-
-#define YT921X_PRIO_NUM	8
-
-#define YT9215_MAJOR	0x9002
-#define YT9218_MAJOR	0x9001
-
-/* required for a hard reset */
-#define YT921X_RST_DELAY_US	10000
-
-#define YT921X_FRAME_SIZE_MAX	0x2400  /* 9216 */
-
-#define YT921X_TAG_LEN	8
-
-#define YT921X_ACL_BLK_NUM	48
-#define YT921X_ACL_ENT_PER_BLK	8
-#define YT921X_ACL_NUM		(YT921X_ACL_BLK_NUM * YT921X_ACL_ENT_PER_BLK)
-#define YT921X_UDF_NUM		8
-
-/* 8 internal + 2 external + 1 mcu */
-#define YT921X_PORT_NUM			11
-
-#define yt921x_port_is_internal(port) ((port) < 8)
-#define yt921x_port_is_external(port) (8 <= (port) && (port) < 9)
-
-struct yt921x_mib {
-	u64 rx_broadcast;
-	u64 rx_pause;
-	u64 rx_multicast;
-	u64 rx_crc_errors;
-
-	u64 rx_alignment_errors;
-	u64 rx_undersize_errors;
-	u64 rx_fragment_errors;
-	u64 rx_64byte;
-
-	u64 rx_65_127byte;
-	u64 rx_128_255byte;
-	u64 rx_256_511byte;
-	u64 rx_512_1023byte;
-
-	u64 rx_1024_1518byte;
-	u64 rx_jumbo;
-	u64 rx_good_bytes;
-
-	u64 rx_bad_bytes;
-	u64 rx_oversize_errors;
-
-	u64 rx_dropped;
-	u64 tx_broadcast;
-	u64 tx_pause;
-	u64 tx_multicast;
-
-	u64 tx_undersize_errors;
-	u64 tx_64byte;
-	u64 tx_65_127byte;
-	u64 tx_128_255byte;
-
-	u64 tx_256_511byte;
-	u64 tx_512_1023byte;
-	u64 tx_1024_1518byte;
-	u64 tx_jumbo;
-
-	u64 tx_good_bytes;
-	u64 tx_collisions;
-
-	u64 tx_aborted_errors;
-	u64 tx_multiple_collisions;
-	u64 tx_single_collisions;
-	u64 tx_good;
-
-	u64 tx_deferred;
-	u64 tx_late_collisions;
-	u64 rx_oam;
-	u64 tx_oam;
-};
-
-struct yt921x_acl_entry {
-	u32 key[2];
-	u32 mask[2];
-};
-
-struct yt921x_acl_rule {
-	unsigned long tag;
-	enum tc_setup_type type;
-
-	u32 action[3];
-	bool sw_assisted;
-
-	u8 mask;
-	struct yt921x_acl_entry entries[YT921X_ACL_ENT_PER_BLK];
-};
-
-struct yt921x_acl_blk {
-	struct yt921x_acl_rule *rules[YT921X_ACL_ENT_PER_BLK];
-};
-
-struct yt921x_port {
-	unsigned char index;
-
-	bool hairpin;
-	bool isolated;
-
-	struct delayed_work mib_read;
-	struct yt921x_mib mib;
-	u64 rx_frames;
-	u64 tx_frames;
-};
-
-struct yt921x_reg_ops {
-	int (*read)(void *context, u32 reg, u32 *valp);
-	int (*write)(void *context, u32 reg, u32 val);
-};
-
-struct yt921x_priv {
-	struct dsa_switch ds;
-
-	const struct yt921x_info *info;
-	unsigned int meter_slot_ns;
-	unsigned int port_shape_slot_ns;
-	/* cache of dsa_cpu_ports(ds) */
-	u16 cpu_ports_mask;
-	unsigned char cycle_ns;
-
-	/* protect the access to the switch registers */
-	struct mutex reg_lock;
-	const struct yt921x_reg_ops *reg_ops;
-	void *reg_ctx;
-
-	/* mdio master bus */
-	struct mii_bus *mbus_int;
-	struct mii_bus *mbus_ext;
-
-	struct yt921x_port ports[YT921X_PORT_NUM];
-
-	u16 eee_ports_mask;
-
-	DECLARE_BITMAP(meters_map, YT921X_METER_NUM);
-
-	u8 acl_masks[YT921X_ACL_BLK_NUM];
-	struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM];
-};
-
-#endif
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8c16ef23cc10..bdae3ec89f86 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -59,6 +59,8 @@ struct tc_action;
 #define DSA_TAG_PROTO_MXL_GSW1XX_VALUE		31
 #define DSA_TAG_PROTO_MXL862_VALUE		32
 #define DSA_TAG_PROTO_NETC_VALUE		33
+#define DSA_TAG_PROTO_YT922X_4B_VALUE		34
+#define DSA_TAG_PROTO_YT922X_8B_VALUE		35
 
 enum dsa_tag_protocol {
 	DSA_TAG_PROTO_NONE		= DSA_TAG_PROTO_NONE_VALUE,
@@ -95,6 +97,8 @@ enum dsa_tag_protocol {
 	DSA_TAG_PROTO_MXL_GSW1XX	= DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
 	DSA_TAG_PROTO_MXL862		= DSA_TAG_PROTO_MXL862_VALUE,
 	DSA_TAG_PROTO_NETC		= DSA_TAG_PROTO_NETC_VALUE,
+	DSA_TAG_PROTO_YT922X_4B	    = DSA_TAG_PROTO_YT922X_4B_VALUE,
+	DSA_TAG_PROTO_YT922X_8B		= DSA_TAG_PROTO_YT922X_8B_VALUE,
 };
 
 struct dsa_switch;
diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h
index 1ffac52c39df..c818571eec45 100644
--- a/include/uapi/linux/if_ether.h
+++ b/include/uapi/linux/if_ether.h
@@ -118,7 +118,7 @@
 #define ETH_P_QINQ1	0x9100		/* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_QINQ2	0x9200		/* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_QINQ3	0x9300		/* deprecated QinQ VLAN [ NOT AN OFFICIALLY REGISTERED ID ] */
-#define ETH_P_YT921X	0x9988		/* Motorcomm YT921x DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_YT92XX	0x9988		/* Motorcomm YT92xx DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_EDSA	0xDADA		/* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_DSA_8021Q	0xDADB		/* Fake VLAN Header for DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
 #define ETH_P_DSA_A5PSW	0xE001		/* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index d5e725b90d78..102373553240 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -215,10 +215,10 @@ config NET_DSA_TAG_XRS700X
 	  Say Y or M if you want to enable support for tagging frames for
 	  Arrow SpeedChips XRS700x switches that use a single byte tag trailer.
 
-config NET_DSA_TAG_YT921X
-	tristate "Tag driver for Motorcomm YT921x switches"
+config NET_DSA_TAG_MOTORCOMM
+	tristate "Tag driver for Motorcomm YT92XX switches"
 	help
 	  Say Y or M if you want to enable support for tagging frames for
-	  Motorcomm YT921x switches.
+	  Motorcomm YT921x and YT922x switches.
 
 endif
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index b8c2667cd14a..1029998f27b7 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -42,7 +42,7 @@ obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
 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_MOTORCOMM) += tag_yt92xx.o
 
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c
deleted file mode 100644
index f3ced99b1c85..000000000000
--- a/net/dsa/tag_yt921x.c
+++ /dev/null
@@ -1,168 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Motorcomm YT921x Switch Extended CPU Port Tagging
- *
- * Copyright (c) 2025 David Yang <mmyangfl@gmail.com>
- *
- * +----+----+-------+-----+----+---------
- * | DA | SA | TagET | Tag | ET | Payload ...
- * +----+----+-------+-----+----+---------
- *   6    6      2      6    2       N
- *
- * Tag Ethertype: CPU_TAG_TPID_TPID (default: ETH_P_YT921X = 0x9988)
- *   * Hardcoded for the moment, but still configurable. Discuss it if there
- *     are conflicts somewhere and/or you want to change it for some reason.
- * Tag:
- *   2: VLAN Tag
- *   2:
- *     15b: Rx Port Valid
- *     14b-11b: Rx Port
- *     10b-8b: Tx/Rx Priority
- *     7b: Tx/Rx Code Valid
- *     6b-1b: Tx/Rx Code
- *     0b: ? (unset)
- *   2:
- *     15b: Tx Port(s) Valid
- *     10b-0b: Tx Port(s) Mask
- */
-
-#include <linux/etherdevice.h>
-
-#include "tag.h"
-
-#define YT921X_TAG_NAME	"yt921x"
-
-#define YT921X_TAG_LEN	8
-
-#define YT921X_TAG_PORT_EN		BIT(15)
-#define YT921X_TAG_RX_PORT_M		GENMASK(14, 11)
-#define YT921X_TAG_PRIO_M		GENMASK(10, 8)
-#define  YT921X_TAG_PRIO(x)			FIELD_PREP(YT921X_TAG_PRIO_M, (x))
-#define YT921X_TAG_CODE_EN		BIT(7)
-#define YT921X_TAG_CODE_M		GENMASK(6, 1)
-#define  YT921X_TAG_CODE(x)			FIELD_PREP(YT921X_TAG_CODE_M, (x))
-#define YT921X_TAG_TX_PORTS_M		GENMASK(10, 0)
-#define  YT921X_TAG_TX_PORTS(x)			FIELD_PREP(YT921X_TAG_TX_PORTS_M, (x))
-
-/* Incomplete. Some are configurable via RMA_CTRL_CPU_CODE, the meaning of
- * others remains unknown.
- */
-enum yt921x_tag_code {
-	YT921X_TAG_CODE_FORWARD = 0,
-	YT921X_TAG_CODE_ACL = 0x17,
-	YT921X_TAG_CODE_UNK_UCAST = 0x19,
-	YT921X_TAG_CODE_UNK_MCAST = 0x1a,
-	YT921X_TAG_CODE_PORT_COPY = 0x1b,
-	YT921X_TAG_CODE_FDB_COPY = 0x1c,
-};
-
-static struct sk_buff *
-yt921x_tag_xmit(struct sk_buff *skb, struct net_device *netdev)
-{
-	__be16 *tag;
-	u16 ctrl;
-
-	skb_push(skb, YT921X_TAG_LEN);
-	dsa_alloc_etype_header(skb, YT921X_TAG_LEN);
-
-	tag = dsa_etype_header_pos_tx(skb);
-
-	tag[0] = htons(ETH_P_YT921X);
-	/* VLAN tag unrelated when TX */
-	tag[1] = 0;
-	ctrl = YT921X_TAG_CODE(YT921X_TAG_CODE_FORWARD) | YT921X_TAG_CODE_EN |
-	       YT921X_TAG_PRIO(skb->priority);
-	tag[2] = htons(ctrl);
-	ctrl = YT921X_TAG_TX_PORTS(dsa_xmit_port_mask(skb, netdev)) |
-	       YT921X_TAG_PORT_EN;
-	tag[3] = htons(ctrl);
-
-	return skb;
-}
-
-static struct sk_buff *
-yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev)
-{
-	unsigned int port;
-	__be16 *tag;
-	u16 rx;
-
-	if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN)))
-		return NULL;
-
-	tag = dsa_etype_header_pos_rx(skb);
-
-	if (unlikely(tag[0] != htons(ETH_P_YT921X))) {
-		dev_warn_ratelimited(&netdev->dev,
-				     "Unexpected EtherType 0x%04x\n",
-				     ntohs(tag[0]));
-		return NULL;
-	}
-
-	/* Locate which port this is coming from */
-	rx = ntohs(tag[2]);
-	if (unlikely((rx & YT921X_TAG_PORT_EN) == 0)) {
-		dev_warn_ratelimited(&netdev->dev,
-				     "Unexpected rx tag 0x%04x\n", rx);
-		return NULL;
-	}
-
-	port = FIELD_GET(YT921X_TAG_RX_PORT_M, rx);
-	skb->dev = dsa_conduit_find_user(netdev, 0, port);
-	if (unlikely(!skb->dev)) {
-		dev_warn_ratelimited(&netdev->dev,
-				     "Couldn't decode source port %u\n", port);
-		return NULL;
-	}
-
-	skb->priority = FIELD_GET(YT921X_TAG_PRIO_M, rx);
-
-	if (!(rx & YT921X_TAG_CODE_EN)) {
-		dev_warn_ratelimited(&netdev->dev,
-				     "Tag code not enabled in rx packet\n");
-	} else {
-		u16 code = FIELD_GET(YT921X_TAG_CODE_M, rx);
-
-		switch (code) {
-		case YT921X_TAG_CODE_FORWARD:
-		case YT921X_TAG_CODE_PORT_COPY:
-		case YT921X_TAG_CODE_FDB_COPY:
-			/* Already forwarded by hardware */
-			dsa_default_offload_fwd_mark(skb);
-			break;
-		case YT921X_TAG_CODE_ACL:
-		case YT921X_TAG_CODE_UNK_UCAST:
-		case YT921X_TAG_CODE_UNK_MCAST:
-			/* NOTE: hardware doesn't distinguish between TRAP (copy
-			 * to CPU only) and COPY (forward and copy to CPU). In
-			 * order to perform a soft switch, NEVER use COPY action
-			 * in the switch driver.
-			 */
-			break;
-		default:
-			dev_warn_ratelimited(&netdev->dev,
-					     "Unknown code 0x%02x\n", code);
-			break;
-		}
-	}
-
-	/* Remove YT921x tag and update checksum */
-	skb_pull_rcsum(skb, YT921X_TAG_LEN);
-	dsa_strip_etype_header(skb, YT921X_TAG_LEN);
-
-	return skb;
-}
-
-static const struct dsa_device_ops yt921x_netdev_ops = {
-	.name	= YT921X_TAG_NAME,
-	.proto	= DSA_TAG_PROTO_YT921X,
-	.xmit	= yt921x_tag_xmit,
-	.rcv	= yt921x_tag_rcv,
-	.needed_headroom = YT921X_TAG_LEN,
-};
-
-MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT921x switches");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT921X, YT921X_TAG_NAME);
-
-module_dsa_tag_driver(yt921x_netdev_ops);
diff --git a/net/dsa/tag_yt92xx.c b/net/dsa/tag_yt92xx.c
new file mode 100644
index 000000000000..de28c4be67bb
--- /dev/null
+++ b/net/dsa/tag_yt92xx.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Motorcomm YT921x and YT922x Switch Extended CPU Port Tagging
+ *
+ * Copyright (c) 2025 David Yang <mmyangfl@gmail.com>
+ * Copyright (c) 2026 Kyle Switch <kyle.switch@motor-comm.com>
+ *
+ * +----+----+-------+-----+----+---------
+ * | DA | SA | TagET | Tag | ET | Payload ...
+ * +----+----+-------+-----+----+---------
+ *   6    6      2      6    2       N
+ *
+ * Tag Ethertype: CPU_TAG_TPID_TPID (default: ETH_P_YT92XX = 0x9988)
+ *   * Hardcoded for the moment, but still configurable. Discuss it if there
+ *     are conflicts somewhere and/or you want to change it for some reason.
+ * Tag:
+ *   2: VLAN Tag
+ *   2:
+ *     15b: Rx Port Valid
+ *     14b-11b: Rx Port
+ *     10b-8b: Tx/Rx Priority
+ *     7b: Tx/Rx Code Valid
+ *     6b-1b: Tx/Rx Code
+ *     0b: ? (unset)
+ *   2:
+ *     15b: Tx Port(s) Valid
+ *     10b-0b: Tx Port(s) Mask
+ */
+
+#include <linux/etherdevice.h>
+
+#include "tag.h"
+
+#define YT921X_TAG_NAME	"yt921x"
+
+#define YT921X_TAG_LEN	8
+
+#define YT921X_TAG_PORT_EN		BIT(15)
+#define YT921X_TAG_RX_PORT_M		GENMASK(14, 11)
+#define YT921X_TAG_PRIO_M		GENMASK(10, 8)
+#define YT921X_TAG_PRIO(x)		FIELD_PREP(YT921X_TAG_PRIO_M, (x))
+#define YT921X_TAG_CODE_EN		BIT(7)
+#define YT921X_TAG_CODE_M		GENMASK(6, 1)
+#define YT921X_TAG_CODE(x)		FIELD_PREP(YT921X_TAG_CODE_M, (x))
+#define YT921X_TAG_TX_PORTS_M		GENMASK(10, 0)
+#define YT921X_TAG_TX_PORTS(x)		FIELD_PREP(YT921X_TAG_TX_PORTS_M, (x))
+
+/* Incomplete. Some are configurable via RMA_CTRL_CPU_CODE, the meaning of
+ * others remains unknown.
+ */
+enum yt921x_tag_code {
+	YT921X_TAG_CODE_FORWARD = 0,
+	YT921X_TAG_CODE_ACL = 0x17,
+	YT921X_TAG_CODE_UNK_UCAST = 0x19,
+	YT921X_TAG_CODE_UNK_MCAST = 0x1a,
+	YT921X_TAG_CODE_PORT_COPY = 0x1b,
+	YT921X_TAG_CODE_FDB_COPY = 0x1c,
+};
+
+static struct sk_buff *
+yt921x_tag_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	__be16 *tag;
+	u16 ctrl;
+
+	skb_push(skb, YT921X_TAG_LEN);
+	dsa_alloc_etype_header(skb, YT921X_TAG_LEN);
+
+	tag = dsa_etype_header_pos_tx(skb);
+
+	tag[0] = htons(ETH_P_YT92XX);
+	/* VLAN tag unrelated when TX */
+	tag[1] = 0;
+	ctrl = YT921X_TAG_CODE(YT921X_TAG_CODE_FORWARD) | YT921X_TAG_CODE_EN |
+	       YT921X_TAG_PRIO(skb->priority);
+	tag[2] = htons(ctrl);
+	ctrl = YT921X_TAG_TX_PORTS(dsa_xmit_port_mask(skb, netdev)) |
+	       YT921X_TAG_PORT_EN;
+	tag[3] = htons(ctrl);
+
+	return skb;
+}
+
+static struct sk_buff *
+yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev)
+{
+	unsigned int port;
+	__be16 *tag;
+	u16 rx;
+
+	if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN)))
+		return NULL;
+
+	tag = dsa_etype_header_pos_rx(skb);
+
+	if (unlikely(tag[0] != htons(ETH_P_YT92XX))) {
+		dev_warn_ratelimited(&netdev->dev,
+				     "Unexpected EtherType 0x%04x\n",
+				     ntohs(tag[0]));
+		return NULL;
+	}
+
+	/* Locate which port this is coming from */
+	rx = ntohs(tag[2]);
+	if (unlikely((rx & YT921X_TAG_PORT_EN) == 0)) {
+		dev_warn_ratelimited(&netdev->dev,
+				     "Unexpected rx tag 0x%04x\n", rx);
+		return NULL;
+	}
+
+	port = FIELD_GET(YT921X_TAG_RX_PORT_M, rx);
+	skb->dev = dsa_conduit_find_user(netdev, 0, port);
+	if (unlikely(!skb->dev)) {
+		dev_warn_ratelimited(&netdev->dev,
+				     "Couldn't decode source port %u\n", port);
+		return NULL;
+	}
+
+	skb->priority = FIELD_GET(YT921X_TAG_PRIO_M, rx);
+
+	if (!(rx & YT921X_TAG_CODE_EN)) {
+		dev_warn_ratelimited(&netdev->dev,
+				     "Tag code not enabled in rx packet\n");
+	} else {
+		u16 code = FIELD_GET(YT921X_TAG_CODE_M, rx);
+
+		switch (code) {
+		case YT921X_TAG_CODE_FORWARD:
+		case YT921X_TAG_CODE_PORT_COPY:
+		case YT921X_TAG_CODE_FDB_COPY:
+			/* Already forwarded by hardware */
+			dsa_default_offload_fwd_mark(skb);
+			break;
+		case YT921X_TAG_CODE_ACL:
+		case YT921X_TAG_CODE_UNK_UCAST:
+		case YT921X_TAG_CODE_UNK_MCAST:
+			/* NOTE: hardware doesn't distinguish between TRAP (copy
+			 * to CPU only) and COPY (forward and copy to CPU). In
+			 * order to perform a soft switch, NEVER use COPY action
+			 * in the switch driver.
+			 */
+			break;
+		default:
+			dev_warn_ratelimited(&netdev->dev,
+					     "Unknown code 0x%02x\n", code);
+			break;
+		}
+	}
+
+	/* Remove YT921x tag and update checksum */
+	skb_pull_rcsum(skb, YT921X_TAG_LEN);
+	dsa_strip_etype_header(skb, YT921X_TAG_LEN);
+
+	return skb;
+}
+
+static const struct dsa_device_ops yt921x_netdev_ops = {
+	.name	= YT921X_TAG_NAME,
+	.proto	= DSA_TAG_PROTO_YT921X,
+	.xmit	= yt921x_tag_xmit,
+	.rcv	= yt921x_tag_rcv,
+	.needed_headroom = YT921X_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(yt921x_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT921X, YT921X_TAG_NAME);
+
+/* YT922X 4B type */
+#define YT922X_TAG_FORMAT1_NAME "yt922x-4b"
+#define YT922X_FORMAT1_TAG_LEN	4
+#define YT922X_ETH_P_MOTORCOMM 0x9988
+#define YT922X_4B_CPUTAG_SRC_PORT          GENMASK(7, 4)
+#define YT922X_4B_CPUTAG_DST_PORTMASK      GENMASK(13, 5)
+#define YT922X_4B_CPUTAG_FORCE_DST         BIT(14)
+#define YT922X_4B_CPUTAG_FORCE_DST_EN      0x1
+
+/* To define the from cpu tag format 4 bytes:
+ *
+ * 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7
+ *|<----------TPID 0x9988---------->|
+ *|<->|<----DSTPORT------>|<------->|
+ */
+static struct sk_buff *yt922x_4b_cputag_xmit(struct sk_buff *skb,
+					     struct net_device *netdev)
+{
+	__be16 *tag;
+	u16 ctrl;
+
+	skb_push(skb, YT922X_FORMAT1_TAG_LEN);
+	dsa_alloc_etype_header(skb, YT922X_FORMAT1_TAG_LEN);
+
+	tag = dsa_etype_header_pos_tx(skb);
+
+	/* Set Motorcomm EtherType */
+	tag[0] = htons(YT922X_ETH_P_MOTORCOMM);
+	/* Set Type  set RX (CPU->switch) forwarding port mask */
+	ctrl = YT922X_4B_CPUTAG_FORCE_DST |
+		FIELD_PREP(YT922X_4B_CPUTAG_DST_PORTMASK,
+			   dsa_xmit_port_mask(skb, netdev));
+	tag[1] = htons(ctrl);
+
+	return skb;
+}
+
+/* To define the to cpu tag format 4 bytes:
+ *
+ * 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7
+ *|<----------TPID 0x9988---------->|
+ *|<-------------->|<-PORT->|------>|
+ */
+static struct sk_buff *yt922x_4b_cputag_rcv(struct sk_buff *skb,
+					    struct net_device *netdev)
+{
+	unsigned int port;
+	__be16 *tag;
+
+	if (unlikely(!pskb_may_pull(skb, YT922X_FORMAT1_TAG_LEN)))
+		return NULL;
+
+	tag = dsa_etype_header_pos_rx(skb);
+
+	if (unlikely(tag[0] != htons(YT922X_ETH_P_MOTORCOMM))) {
+		dev_warn_ratelimited(&netdev->dev,
+				     "Unexpected EtherType 0x%04x\n",
+				     ntohs(tag[0]));
+		return NULL;
+	}
+
+	/* Parse Source Port (switch->CPU) */
+	port = FIELD_GET(YT922X_4B_CPUTAG_SRC_PORT, ntohs(tag[1]));
+	skb->dev = dsa_conduit_find_user(netdev, 0, port);
+	if (unlikely(!skb->dev)) {
+		dev_warn_ratelimited(&netdev->dev,
+				     "Couldn't decode source port %u\n", port);
+		return NULL;
+	}
+
+	/* Remove tag and recalculate checksum */
+	skb_pull_rcsum(skb, YT922X_FORMAT1_TAG_LEN);
+	dsa_strip_etype_header(skb, YT922X_FORMAT1_TAG_LEN);
+
+	return skb;
+}
+
+static const struct dsa_device_ops yt922x_4b_netdev_ops = {
+	.name	= YT922X_TAG_FORMAT1_NAME,
+	.proto	= DSA_TAG_PROTO_YT922X_4B,
+	.xmit	= yt922x_4b_cputag_xmit,
+	.rcv	= yt922x_4b_cputag_rcv,
+	.needed_headroom = YT922X_FORMAT1_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(yt922x_4b_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT922X_4B, YT922X_TAG_FORMAT1_NAME);
+
+/* To define the from cpu tag format 8 bytes:
+ *
+ * 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7
+ *|<----------TPID 0x9988---------->|
+ *|<--RESERVE-->|<-----DST PORT---->|
+ *|-|<---------RESERVE------------->|
+ *|<------------------------------->|
+ */
+#define YT922X_TAG_FORMAT2_NAME "yt922x-8b"
+#define YT922X_FORMAT2_TAG_LEN                  8
+#define YT922X_PKT_TYPE          GENMASK(15, 14)
+#define YT922X_8B_CPUTAG_PKT_FROM_CPU      0x1
+#define YT922X_8B_CPUTAG_SRC_PORT          GENMASK(6, 2)
+#define YT922X_8B_CPUTAG_DST_PORTMASK      GENMASK(8, 0)
+#define YT922X_8B_CPUTAG_DST_PORTMASK_0      BIT(15)
+#define YT922X_8B_CPUTAG_DST_PORTMASK_0_EN      0x1
+#define YT922X_8B_CPUTAG_FORCE_DST         BIT(9)
+#define YT922X_8B_CPUTAG_FORCE_DST_EN      0x1
+
+static struct sk_buff *yt922x_8b_cputag_xmit(struct sk_buff *skb,
+					     struct net_device *netdev)
+{
+	struct dsa_port *dp = dsa_user_to_port(netdev);
+	__be16 *tag;
+	u16 ctrl;
+
+	skb_push(skb, YT922X_FORMAT2_TAG_LEN);
+	dsa_alloc_etype_header(skb, YT922X_FORMAT2_TAG_LEN);
+
+	tag = dsa_etype_header_pos_tx(skb);
+	/* Set Motorcomm EtherType */
+	tag[0] = htons(YT922X_ETH_P_MOTORCOMM);
+	/* Set Type */
+	if (dp->index != 0) {
+		ctrl = FIELD_PREP(YT922X_PKT_TYPE,
+				  YT922X_8B_CPUTAG_PKT_FROM_CPU) |
+			FIELD_PREP(YT922X_8B_CPUTAG_FORCE_DST,
+				   YT922X_8B_CPUTAG_FORCE_DST_EN) |
+			FIELD_PREP(YT922X_8B_CPUTAG_DST_PORTMASK,
+				   dsa_xmit_port_mask(skb, netdev) - 1);
+		tag[1] = htons(ctrl);
+		tag[2] = 0;
+	} else {
+		ctrl = FIELD_PREP(YT922X_PKT_TYPE,
+				  YT922X_8B_CPUTAG_PKT_FROM_CPU) |
+			FIELD_PREP(YT922X_8B_CPUTAG_FORCE_DST,
+				   YT922X_8B_CPUTAG_FORCE_DST_EN);
+		tag[1] = htons(ctrl);
+		ctrl = FIELD_PREP(YT922X_8B_CPUTAG_DST_PORTMASK_0,
+				  YT922X_8B_CPUTAG_DST_PORTMASK_0_EN);
+		tag[2] = htons(ctrl);
+	}
+	/* set RX (CPU->switch) forwarding port mask */
+	tag[3] = 0;
+
+	return skb;
+}
+
+/* To define the to cpu tag format 8 bytes:
+ *
+ * 0 1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7
+ *|<----------TPID 0x9988---------->|
+ *|<------------RESERVE------------>|
+ *|<-----RESERVE------>|<-PORT->|<->|
+ *|<------------RESERVE------------>|
+ */
+static struct sk_buff *yt922x_8b_cputag_rcv(struct sk_buff *skb,
+					    struct net_device *netdev)
+{
+	unsigned int port;
+	__be16 *tag;
+
+	if (unlikely(!pskb_may_pull(skb, YT922X_FORMAT2_TAG_LEN)))
+		return NULL;
+
+	tag = dsa_etype_header_pos_rx(skb);
+
+	if (unlikely(tag[0] != htons(YT922X_ETH_P_MOTORCOMM))) {
+		dev_warn_ratelimited(&netdev->dev,
+				     "Unexpected EtherType 0x%04x\n",
+				     ntohs(tag[0]));
+		return NULL;
+	}
+
+	/* Parse Source Port (switch->CPU) */
+	port = FIELD_GET(YT922X_8B_CPUTAG_SRC_PORT, ntohs(tag[2]));
+	skb->dev = dsa_conduit_find_user(netdev, 0, port);
+	if (!skb->dev) {
+		dev_warn_ratelimited(&netdev->dev,
+				     "could not find slave for port %d\n",
+				     port);
+		return NULL;
+	}
+
+	skb_pull_rcsum(skb, YT922X_FORMAT2_TAG_LEN);
+	dsa_strip_etype_header(skb, YT922X_FORMAT2_TAG_LEN);
+
+	return skb;
+}
+
+static const struct dsa_device_ops yt922x_8b_netdev_ops = {
+	.name	= YT922X_TAG_FORMAT2_NAME,
+	.proto	= DSA_TAG_PROTO_YT922X_8B,
+	.xmit	= yt922x_8b_cputag_xmit,
+	.rcv	= yt922x_8b_cputag_rcv,
+	.needed_headroom = YT922X_FORMAT2_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(yt922x_8b_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_YT922X_8B, YT922X_TAG_FORMAT2_NAME);
+
+static struct dsa_tag_driver *dsa_tag_driver_array[] = {
+	&DSA_TAG_DRIVER_NAME(yt921x_netdev_ops),
+	&DSA_TAG_DRIVER_NAME(yt922x_4b_netdev_ops),
+	&DSA_TAG_DRIVER_NAME(yt922x_8b_netdev_ops),
+};
+
+module_dsa_tag_drivers(dsa_tag_driver_array);
+
+MODULE_DESCRIPTION("DSA tag driver for Motorcomm YT921x and YT922x switches");
+MODULE_LICENSE("GPL");
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [RESEND PATCH v1] net: dsa: motorcomm: add yt92xx dsa driver
  2026-06-15 11:12 [RESEND PATCH v1] net: dsa: motorcomm: add yt92xx dsa driver Kyle Switch
@ 2026-06-15 11:34 ` Andrew Lunn
  2026-06-15 15:01 ` Julian Braha
  2026-06-15 15:56 ` Andrew Lunn
  2 siblings, 0 replies; 4+ messages in thread
From: Andrew Lunn @ 2026-06-15 11:34 UTC (permalink / raw)
  To: Kyle Switch
  Cc: mmyangfl, olteanv, davem, edumazet, kuba, pabeni, horms, netdev,
	linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, de.ge

On Mon, Jun 15, 2026 at 07:12:35PM +0800, Kyle Switch wrote:
> Add yt92xx dsa driver supports yt921x and yt922x switch series.
> To support more switch series in the future (e.g., yt923x), the most common configurations are abstracted into switch operation interfaces, due to yt921x and yt922x share similar register layouts and operational logic.
> 
> - Merge drivers/net/dsa/yt921x.c and the new yt922x support into a
>   unified yt92xx.c driver.
> 
> - Add support for yt922x, which can operate with either 4 bytes or
>   8 bytes DSA tag.
> 
> - Not change yt921x behaviour but use common switch apis

A 35K line patch is way too big. Please break this up into lots of
small patches, each with good commit messages, which are obviously
correct.

    Andrew

---
pw-bot: cr

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RESEND PATCH v1] net: dsa: motorcomm: add yt92xx dsa driver
  2026-06-15 11:12 [RESEND PATCH v1] net: dsa: motorcomm: add yt92xx dsa driver Kyle Switch
  2026-06-15 11:34 ` Andrew Lunn
@ 2026-06-15 15:01 ` Julian Braha
  2026-06-15 15:56 ` Andrew Lunn
  2 siblings, 0 replies; 4+ messages in thread
From: Julian Braha @ 2026-06-15 15:01 UTC (permalink / raw)
  To: Kyle Switch, mmyangfl, andrew, olteanv, davem, edumazet, kuba,
	pabeni, horms, netdev, linux-kernel
  Cc: ming.xu, xiaolin.xu, jianmin.wang, de.ge

Hi Kyle,

On 6/15/26 12:12, Kyle Switch wrote:

> diff --git a/drivers/net/dsa/motorcomm/Kconfig b/drivers/net/dsa/motorcomm/Kconfig
> new file mode 100644
> index 000000000000..f40d75e2a3f2
> --- /dev/null
> +++ b/drivers/net/dsa/motorcomm/Kconfig
> @@ -0,0 +1,30 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +config MOTORCOMM
> +	tristate "Motorcomm YT92XX switch family support"
> +	depends on NET_DSA
> +	select FIXED_PHY
> +	help
> +	  This driver adds support for Motorcomm switch chips. It supports
> +	  YT921X and YT922X switch.
> +
> +choice
> +	prompt "Motorcomm switch series selection"
> +	depends on MOTORCOMM
> +
> +config MOTORCOMM_YT921X
> +	bool "Motorcomm YT921X series."
> +
> +config MOTORCOMM_YT922X
> +	bool "Motorcomm YT922X series."
> +endchoice
> +
> +config NET_DSA_MOTORCOMM
> +	tristate "Motorcomm YT92XX switch DSA driver"
> +	depends on MOTORCOMM
> +	depends on (MOTORCOMM_YT921X || MOTORCOMM_YT922X)
> +	select NET_DSA_TAG_MOTORCOMM
> +	select NET_IEEE8021Q_HELPERS if DCB
> +	help
> +	  Select to enable support for Motorcomm driver.
> +
> diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile
NET_DSA_MOTORCOMM already depends on:
(MOTORCOMM_YT921X || MOTORCOMM_YT922X)
with both of those options depending on MOTORCOMM due to the 'choice'
that they're in, so this additional dependency on MOTORCOMM is
unnecessary.

You could also consider using a comment to document that this option is
depended on indirectly.

- Julian Braha

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [RESEND PATCH v1] net: dsa: motorcomm: add yt92xx dsa driver
  2026-06-15 11:12 [RESEND PATCH v1] net: dsa: motorcomm: add yt92xx dsa driver Kyle Switch
  2026-06-15 11:34 ` Andrew Lunn
  2026-06-15 15:01 ` Julian Braha
@ 2026-06-15 15:56 ` Andrew Lunn
  2 siblings, 0 replies; 4+ messages in thread
From: Andrew Lunn @ 2026-06-15 15:56 UTC (permalink / raw)
  To: Kyle Switch
  Cc: mmyangfl, olteanv, davem, edumazet, kuba, pabeni, horms, netdev,
	linux-kernel, ming.xu, xiaolin.xu, jianmin.wang, de.ge

On Mon, Jun 15, 2026 at 07:12:35PM +0800, Kyle Switch wrote:
> Add yt92xx dsa driver supports yt921x and yt922x switch series.
> To support more switch series in the future (e.g., yt923x), the most common configurations are abstracted into switch operation interfaces, due to yt921x and yt922x share similar register layouts and operational logic.
> 
> - Merge drivers/net/dsa/yt921x.c and the new yt922x support into a
>   unified yt92xx.c driver.
> 
> - Add support for yt922x, which can operate with either 4 bytes or
>   8 bytes DSA tag.
> 
> - Not change yt921x behaviour but use common switch apis

You should comment about why you resent.

Also, you should of waited 24 hours.

https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html

Anyway, please don't send it yet again until you have broken it up
into lots of smaller patches.

You also have a lot of work to do to bring this code up to Mainline
standards. Please spend some time looking at other DSA drivers, and
network drivers. Anything your driver does which no other driver does,
is probably wrong. Please start with a small patchset with just the
core code, enough to have the ports act as independent interfaces,
nothing more. You can then submit further patchsets for offloading
bridges, statistics, trunks, ptp etc.

    Andrew

---
pw-bot: cr

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-06-15 15:57 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-15 11:12 [RESEND PATCH v1] net: dsa: motorcomm: add yt92xx dsa driver Kyle Switch
2026-06-15 11:34 ` Andrew Lunn
2026-06-15 15:01 ` Julian Braha
2026-06-15 15:56 ` Andrew Lunn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox