[openwrt/openwrt] realtek: sync latest version

LEDE Commits lede-commits at lists.infradead.org
Mon Dec 28 13:35:59 EST 2020


blogic pushed a commit to openwrt/openwrt.git, branch realtek:
https://git.openwrt.org/4af1445ab671b06d7f3b25e4e0b6536857099631

commit 4af1445ab671b06d7f3b25e4e0b6536857099631
Author: Birger Koblitz <git at birger-koblitz.de>
AuthorDate: Thu Nov 26 08:41:42 2020 +0100

    realtek: sync latest version
    
    Signed-off-by: Birger Koblitz <git at birger-koblitz.de>
---
 target/linux/realtek/config-5.4                    |   1 -
 .../mips/include/asm/mach-rtl838x/mach-rtl83xx.h   |   4 +
 .../realtek/files-5.4/arch/mips/rtl838x/prom.c     |  11 +
 .../files-5.4/drivers/net/dsa/rtl83xx/Makefile     |   2 +-
 .../files-5.4/drivers/net/dsa/rtl83xx/common.c     | 163 +++++-
 .../files-5.4/drivers/net/dsa/rtl83xx/debugfs.c    | 403 +++++++++++++-
 .../files-5.4/drivers/net/dsa/rtl83xx/dsa.c        |   4 +-
 .../files-5.4/drivers/net/dsa/rtl83xx/qos.c        | 576 +++++++++++++++++++++
 .../files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c    |   8 +
 .../files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h    |  81 ++-
 .../files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c    |  16 +-
 .../files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h    |   3 +-
 .../files-5.4/drivers/net/dsa/rtl83xx/storm.c      |  64 ---
 .../files-5.4/drivers/net/ethernet/rtl838x_eth.c   | 198 +++++--
 ...t-dsa-add-rtl838x-support-for-tag-trailer.patch |  15 +-
 15 files changed, 1395 insertions(+), 154 deletions(-)

diff --git a/target/linux/realtek/config-5.4 b/target/linux/realtek/config-5.4
index 170bcb7632..8171f66529 100644
--- a/target/linux/realtek/config-5.4
+++ b/target/linux/realtek/config-5.4
@@ -167,7 +167,6 @@ CONFIG_SPI_MASTER=y
 CONFIG_SPI_MEM=y
 CONFIG_SPI_RTL838X=y
 CONFIG_SRCU=y
-CONFIG_SWCONFIG=y
 CONFIG_SWPHY=y
 CONFIG_SYSCTL_EXCEPTION_TRACE=y
 CONFIG_SYS_HAS_CPU_MIPS32_R1=y
diff --git a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
index b7d2a6f037..b4d4efcb16 100644
--- a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
+++ b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
@@ -373,6 +373,9 @@
 #define RTL838X_PLL_CML_CTRL		(0x0FF8)
 #define RTL838X_STRAP_DBG		(0x100C)
 
+#define RTL838X_CPU_PORT                28
+#define RTL839X_CPU_PORT                52
+
 /*
  * Reset
  */
@@ -446,6 +449,7 @@ struct rtl83xx_soc_info {
 	unsigned char *compatible;
 	volatile void *sw_base;
 	volatile void *icu_base;
+	int cpu_port;
 };
 
 /* rtl83xx-related functions used across subsystems */
diff --git a/target/linux/realtek/files-5.4/arch/mips/rtl838x/prom.c b/target/linux/realtek/files-5.4/arch/mips/rtl838x/prom.c
index 5278afae03..27da109b13 100644
--- a/target/linux/realtek/files-5.4/arch/mips/rtl838x/prom.c
+++ b/target/linux/realtek/files-5.4/arch/mips/rtl838x/prom.c
@@ -119,6 +119,17 @@ void __init prom_init(void)
 		soc_info.name = "DEFAULT";
 		soc_info.family = 0;
 	}
+
 	pr_info("SoC Type: %s\n", get_system_type());
+
+	switch(soc_info.family) {
+	case RTL8380_FAMILY_ID:
+		soc_info.cpu_port = RTL838X_CPU_PORT;
+		break;
+	case RTL8390_FAMILY_ID:
+		soc_info.cpu_port = RTL839X_CPU_PORT;
+		break;
+	}
+
 	prom_init_cmdline();
 }
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile
index 52cc151a56..eeab299fcb 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_NET_DSA_RTL83XX)	+= common.o dsa.o \
-	rtl838x.o rtl839x.o storm.o debugfs.o
+	rtl838x.o rtl839x.o debugfs.o qos.o
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c
index 1b57ddc92c..219117b399 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/common.c
@@ -36,8 +36,8 @@ static void dump_fdb(struct rtl838x_switch_priv *priv)
 	mutex_unlock(&priv->reg_mutex);
 }
 
-// TODO: unused
-static void rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port)
+// TODO: used only in debugfs
+void rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port)
 {
 	u32 cmd, msti = 0;
 	u32 port_state[4];
@@ -278,12 +278,152 @@ static int __init rtl83xx_get_l2aging(struct rtl838x_switch_priv *priv)
 	return t;
 }
 
+/* Caller must hold priv->reg_mutex */
+int rtl83xx_lag_add(struct dsa_switch *ds, int group, int port)
+{
+	struct rtl838x_switch_priv *priv = ds->priv;
+	int i;
+
+	pr_info("%s: Adding port %d to LA-group %d\n", __func__, port, group);
+	if (group >= priv->n_lags) {
+		pr_err("Link Agrregation group too large.\n");
+		return -EINVAL;
+	}
+
+	if (port >= priv->cpu_port) {
+		pr_err("Invalid port number.\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < priv->n_lags; i++) {
+		if (priv->lags_port_members[i] & BIT_ULL(i))
+			break;
+	}
+	if (i != priv->n_lags) {
+		pr_err("%s: Port already member of LAG: %d\n", __func__, i);
+		return -ENOSPC;
+	}
+
+	priv->r->mask_port_reg_be(0, BIT_ULL(port), priv->r->trk_mbr_ctr(group));
+	priv->lags_port_members[group] |= BIT_ULL(port);
+
+	pr_info("lags_port_members %d now %016llx\n", group, priv->lags_port_members[group]);
+	return 0;
+}
+
+/* Caller must hold priv->reg_mutex */
+int rtl83xx_lag_del(struct dsa_switch *ds, int group, int port)
+{
+	struct rtl838x_switch_priv *priv = ds->priv;
+
+	pr_info("%s: Removing port %d from LA-group %d\n", __func__, port, group);
+
+	if (group >= priv->n_lags) {
+		pr_err("Link Agrregation group too large.\n");
+		return -EINVAL;
+	}
+
+	if (port >= priv->cpu_port) {
+		pr_err("Invalid port number.\n");
+		return -EINVAL;
+	}
+
+
+	if (!(priv->lags_port_members[group] & BIT_ULL(port))) {
+		pr_err("%s: Port not member of LAG: %d\n", __func__, group
+		);
+		return -ENOSPC;
+	}
+
+	priv->r->mask_port_reg_be(BIT_ULL(port), 0, priv->r->trk_mbr_ctr(group));
+	priv->lags_port_members[group] &= ~BIT_ULL(port);
+
+	pr_info("lags_port_members %d now %016llx\n", group, priv->lags_port_members[group]);
+	return 0;
+}
+
+static int rtl83xx_handle_changeupper(struct rtl838x_switch_priv *priv,
+				      struct net_device *ndev,
+				      struct netdev_notifier_changeupper_info *info)
+{
+	struct net_device *upper = info->upper_dev;
+	int i, j, err;
+
+	if (!netif_is_lag_master(upper))
+		return 0;
+
+	mutex_lock(&priv->reg_mutex);
+
+	for (i = 0; i < priv->n_lags; i++) {
+		if ((!priv->lag_devs[i]) || (priv->lag_devs[i] == upper))
+			break;
+	}
+	for (j = 0; j < priv->cpu_port; j++) {
+		if (priv->ports[j].dp->slave == ndev)
+			break;
+	}
+	if (j >= priv->cpu_port) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (info->linking) {
+		if (!priv->lag_devs[i])
+			priv->lag_devs[i] = upper;
+		err = rtl83xx_lag_add(priv->ds, i, priv->ports[j].dp->index);
+		if (err) {
+			err = -EINVAL;
+			goto out;
+		}
+	} else {
+		if (!priv->lag_devs[i])
+			err = -EINVAL;
+		err = rtl83xx_lag_del(priv->ds, i, priv->ports[j].dp->index);
+		if (err) {
+			err = -EINVAL;
+			goto out;
+		}
+		if (!priv->lags_port_members[i])
+			priv->lag_devs[i] = NULL;
+	}
+
+out:
+	mutex_unlock(&priv->reg_mutex);
+	return 0;
+}
+
+static int rtl83xx_netdevice_event(struct notifier_block *this,
+				   unsigned long event, void *ptr)
+{
+	struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
+	struct rtl838x_switch_priv *priv;
+	int err;
+
+	pr_debug("In: %s, event: %lu\n", __func__, event);
+
+	if ((event != NETDEV_CHANGEUPPER) && (event != NETDEV_CHANGELOWERSTATE))
+		return NOTIFY_DONE;
+
+	priv = container_of(this, struct rtl838x_switch_priv, nb);
+	switch (event) {
+	case NETDEV_CHANGEUPPER:
+		err = rtl83xx_handle_changeupper(priv, ndev, ptr);
+		break;
+	}
+
+	if (err)
+		return err;
+
+	return NOTIFY_DONE;
+}
+
 static int __init rtl83xx_sw_probe(struct platform_device *pdev)
 {
 	int err = 0, i;
 	struct rtl838x_switch_priv *priv;
 	struct device *dev = &pdev->dev;
 	u64 irq_mask;
+	u64 bpdu_mask;
 
 	pr_debug("Probing RTL838X switch device\n");
 	if (!pdev->dev.of_node) {
@@ -361,15 +501,26 @@ static int __init rtl83xx_sw_probe(struct platform_device *pdev)
 
 	rtl83xx_get_l2aging(priv);
 
-/*
-	if (priv->family_id == RTL8380_FAMILY_ID)
-		rtl83xx_storm_control_init(priv);
-*/
+	rtl83xx_setup_qos(priv);
 
 	/* Clear all destination ports for mirror groups */
 	for (i = 0; i < 4; i++)
 		priv->mirror_group_ports[i] = -1;
 
+	priv->nb.notifier_call = rtl83xx_netdevice_event;
+		if (register_netdevice_notifier(&priv->nb)) {
+			priv->nb.notifier_call = NULL;
+			dev_err(dev, "Failed to register LAG netdev notifier\n");
+	}
+
+	// Flood BPDUs to all ports including cpu-port
+	bpdu_mask = soc_info.family == RTL8380_FAMILY_ID ? 0x1FFFFFFF : 0x1FFFFFFFFFFFFF;
+	priv->r->set_port_reg_be(bpdu_mask, priv->r->rma_bpdu_fld_pmask);
+
+	// TRAP 802.1X frames (EAPOL) to the CPU-Port, bypass STP and VLANs
+	sw_w32(7, priv->r->spcl_trap_eapol_ctrl);
+
+
 	rtl838x_dbgfs_init(priv);
 
 	return err;
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/debugfs.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/debugfs.c
index af24e8fa42..8634cfbb80 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/debugfs.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/debugfs.c
@@ -1,12 +1,219 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
 #include <linux/debugfs.h>
+#include <linux/kernel.h>
 
 #include <asm/mach-rtl838x/mach-rtl83xx.h>
-#include "rtl838x.h"
+#include "rtl83xx.h"
 
 #define RTL838X_DRIVER_NAME "rtl838x"
 
+#define RTL8380_LED_GLB_CTRL			(0xA000)
+#define RTL8380_LED_MODE_SEL			(0x1004)
+#define RTL8380_LED_MODE_CTRL			(0xA004)
+#define RTL8380_LED_P_EN_CTRL			(0xA008)
+#define RTL8380_LED_SW_CTRL			(0xA00C)
+#define RTL8380_LED0_SW_P_EN_CTRL		(0xA010)
+#define RTL8380_LED1_SW_P_EN_CTRL		(0xA014)
+#define RTL8380_LED2_SW_P_EN_CTRL		(0xA018)
+#define RTL8380_LED_SW_P_CTRL(p)		(0xA01C + (((p) << 2)))
+
+#define RTL8390_LED_GLB_CTRL			(0x00E4)
+#define RTL8390_LED_SET_2_3_CTRL		(0x00E8)
+#define RTL8390_LED_SET_0_1_CTRL		(0x00EC)
+#define RTL8390_LED_COPR_SET_SEL_CTRL(p)	(0x00F0 + (((p >> 4) << 2)))
+#define RTL8390_LED_FIB_SET_SEL_CTRL(p)		(0x0100 + (((p >> 4) << 2)))
+#define RTL8390_LED_COPR_PMASK_CTRL(p)		(0x0110 + (((p >> 5) << 2)))
+#define RTL8390_LED_FIB_PMASK_CTRL(p)		(0x00118 + (((p >> 5) << 2)))
+#define RTL8390_LED_COMBO_CTRL(p)		(0x0120 + (((p >> 5) << 2)))
+#define RTL8390_LED_SW_CTRL			(0x0128)
+#define RTL8390_LED_SW_P_EN_CTRL(p)		(0x012C + (((p / 10) << 2)))
+#define RTL8390_LED_SW_P_CTRL(p)		(0x0144 + (((p) << 2)))
+
+#define RTL838X_MIR_QID_CTRL(grp)		(0xAD44 + (((grp) << 2)))
+#define RTL838X_MIR_RSPAN_VLAN_CTRL(grp)	(0xA340 + (((grp) << 2)))
+#define RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(grp)	(0xAA70 + (((grp) << 2)))
+#define RTL838X_MIR_RSPAN_TX_CTRL		(0xA350)
+#define RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL	(0xAA80)
+#define RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL	(0xAA84)
+#define RTL839X_MIR_RSPAN_VLAN_CTRL(grp)	(0xA340 + (((grp) << 2)))
+#define RTL839X_MIR_RSPAN_TX_CTRL		(0x69b0)
+#define RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL	(0x2550)
+#define RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL	(0x2554)
+#define RTL839X_MIR_SAMPLE_RATE_CTRL                (0x2558)
+
+int rtl83xx_port_get_stp_state(struct rtl838x_switch_priv *priv, int port);
+void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
+void rtl83xx_fast_age(struct dsa_switch *ds, int port);
+u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port);
+u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port);
+int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate);
+int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate);
+
+static ssize_t rtl838x_common_read(char __user *buffer, size_t count,
+					loff_t *ppos, unsigned int value)
+{
+	char *buf;
+	ssize_t len;
+
+	if (*ppos != 0)
+		return 0;
+
+	buf = kasprintf(GFP_KERNEL, "0x%08x\n", value);
+	if (!buf)
+		return -ENOMEM;
+
+	if (count < strlen(buf)) {
+		kfree(buf);
+		return -ENOSPC;
+	}
+
+	len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
+	kfree(buf);
+
+	return len;
+}
+
+static ssize_t rtl838x_common_write(const char __user *buffer, size_t count,
+				 loff_t *ppos, unsigned int *value)
+{
+	char b[32];
+	ssize_t len;
+	int ret;
+
+	if (*ppos != 0)
+		return -EINVAL;
+
+	if (count >= sizeof(b))
+		return -ENOSPC;
+
+	len = simple_write_to_buffer(b, sizeof(b) - 1, ppos,
+				     buffer, count);
+	if (len < 0)
+		return len;
+
+	b[len] = '\0';
+	ret = kstrtouint(b, 16, value);
+	if (ret)
+		return -EIO;
+
+	return len;
+}
+
+static ssize_t stp_state_read(struct file *filp, char __user *buffer, size_t count,
+			     loff_t *ppos)
+{
+	struct rtl838x_port *p = filp->private_data;
+	struct dsa_switch *ds = p->dp->ds;
+	int value = rtl83xx_port_get_stp_state(ds->priv, p->dp->index);
+
+	if (value < 0)
+		return -EINVAL;
+
+	return rtl838x_common_read(buffer, count, ppos, (u32)value);
+}
+
+static ssize_t stp_state_write(struct file *filp, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	struct rtl838x_port *p = filp->private_data;
+	u32 value;
+	size_t res = rtl838x_common_write(buffer, count, ppos, &value);
+	if (res < 0)
+		return res;
+
+	rtl83xx_port_stp_state_set(p->dp->ds, p->dp->index, (u8)value);
+
+	return res;
+}
+
+static const struct file_operations stp_state_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.read = stp_state_read,
+	.write = stp_state_write,
+};
+
+static ssize_t age_out_read(struct file *filp, char __user *buffer, size_t count,
+			     loff_t *ppos)
+{
+	struct rtl838x_port *p = filp->private_data;
+	struct dsa_switch *ds = p->dp->ds;
+	struct rtl838x_switch_priv *priv = ds->priv;
+	int value = sw_r32(priv->r->l2_port_aging_out);
+
+	if (value < 0)
+		return -EINVAL;
+
+	return rtl838x_common_read(buffer, count, ppos, (u32)value);
+}
+
+static ssize_t age_out_write(struct file *filp, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	struct rtl838x_port *p = filp->private_data;
+	u32 value;
+	size_t res = rtl838x_common_write(buffer, count, ppos, &value);
+	if (res < 0)
+		return res;
+
+	rtl83xx_fast_age(p->dp->ds, p->dp->index);
+
+	return res;
+}
+
+static const struct file_operations age_out_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.read = age_out_read,
+	.write = age_out_write,
+};
+
+static ssize_t port_egress_rate_read(struct file *filp, char __user *buffer, size_t count,
+				loff_t *ppos)
+{
+	struct rtl838x_port *p = filp->private_data;
+	struct dsa_switch *ds = p->dp->ds;
+	struct rtl838x_switch_priv *priv = ds->priv;
+	int value;
+	if (priv->family_id == RTL8380_FAMILY_ID)
+		value = rtl838x_get_egress_rate(priv, p->dp->index);
+	else
+		value = rtl839x_get_egress_rate(priv, p->dp->index);
+
+	if (value < 0)
+		return -EINVAL;
+
+	return rtl838x_common_read(buffer, count, ppos, (u32)value);
+}
+
+static ssize_t port_egress_rate_write(struct file *filp, const char __user *buffer,
+				size_t count, loff_t *ppos)
+{
+	struct rtl838x_port *p = filp->private_data;
+	struct dsa_switch *ds = p->dp->ds;
+	struct rtl838x_switch_priv *priv = ds->priv;
+	u32 value;
+	size_t res = rtl838x_common_write(buffer, count, ppos, &value);
+	if (res < 0)
+		return res;
+
+	if (priv->family_id == RTL8380_FAMILY_ID)
+		rtl838x_set_egress_rate(priv, p->dp->index, value);
+	else
+		rtl839x_set_egress_rate(priv, p->dp->index, value);
+
+	return res;
+}
+
+static const struct file_operations port_egress_fops = {
+	.owner = THIS_MODULE,
+	.open = simple_open,
+	.read = port_egress_rate_read,
+	.write = port_egress_rate_write,
+};
+
+
 static const struct debugfs_reg32 port_ctrl_regs[] = {
 	{ .name = "port_isolation", .offset = RTL838X_PORT_ISO_CTRL(0), },
 	{ .name = "mac_force_mode", .offset = RTL838X_MAC_FORCE_MODE_CTRL, },
@@ -27,20 +234,33 @@ static int rtl838x_dbgfs_port_init(struct dentry *parent, struct rtl838x_switch_
 
 	port_dir = debugfs_create_dir(priv->ports[port].dp->name, parent);
 
-	debugfs_create_x32("rate_uc", 0644, port_dir,
-			    (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_UC(port)));
+	if (priv->family_id == RTL8380_FAMILY_ID) {
+		debugfs_create_x32("storm_rate_uc", 0644, port_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_UC(port)));
+
+		debugfs_create_x32("storm_rate_mc", 0644, port_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_MC(port)));
 
-	debugfs_create_x32("rate_mc", 0644, port_dir,
-			    (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port)));
+		debugfs_create_x32("storm_rate_bc", 0644, port_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port)));
 
-	debugfs_create_x32("rate_bc", 0644, port_dir,
-			    (u32 *)(RTL838X_SW_BASE + RTL838X_STORM_CTRL_PORT_BC(port)));
+		debugfs_create_x32("vlan_port_tag_sts_ctrl", 0644, port_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_PORT_TAG_STS_CTRL(port)));
+	} else {
+		debugfs_create_x32("storm_rate_uc", 0644, port_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_UC_0(port)));
 
-	debugfs_create_u32("id", 0444, port_dir, &priv->ports[port].dp->index);
+		debugfs_create_x32("storm_rate_mc", 0644, port_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_MC_0(port)));
 
+		debugfs_create_x32("storm_rate_bc", 0644, port_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_STORM_CTRL_PORT_BC_0(port)));
 
-	debugfs_create_x32("vlan_port_tag_sts_ctrl", 0644, port_dir,
-			   (u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_PORT_TAG_STS_CTRL(port)));
+		debugfs_create_x32("vlan_port_tag_sts_ctrl", 0644, port_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_VLAN_PORT_TAG_STS_CTRL(port)));
+	}
+
+	debugfs_create_u32("id", 0444, port_dir, (u32 *)&priv->ports[port].dp->index);
 
 	port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL);
 	if (!port_ctrl_regset)
@@ -48,9 +268,88 @@ static int rtl838x_dbgfs_port_init(struct dentry *parent, struct rtl838x_switch_
 
 	port_ctrl_regset->regs = port_ctrl_regs;
 	port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs);
-	port_ctrl_regset->base = RTL838X_SW_BASE + (port << 2);
+	port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (port << 2));
 	debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset);
 
+	debugfs_create_file("stp_state", 0600, port_dir, &priv->ports[port], &stp_state_fops);
+	debugfs_create_file("age_out", 0600, port_dir, &priv->ports[port], &age_out_fops);
+	debugfs_create_file("port_egress_rate", 0600, port_dir, &priv->ports[port],
+			    &port_egress_fops);
+	return 0;
+}
+
+static int rtl838x_dbgfs_leds(struct dentry *parent, struct rtl838x_switch_priv *priv)
+{
+	struct dentry *led_dir;
+	int p;
+	char led_sw_p_ctrl_name[20];
+	char port_led_name[20];
+
+	led_dir = debugfs_create_dir("led", parent);
+
+	if (priv->family_id == RTL8380_FAMILY_ID) {
+		debugfs_create_x32("led_glb_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED_GLB_CTRL));
+		debugfs_create_x32("led_mode_sel", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED_MODE_SEL));
+		debugfs_create_x32("led_mode_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED_MODE_CTRL));
+		debugfs_create_x32("led_p_en_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED_P_EN_CTRL));
+		debugfs_create_x32("led_sw_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED_SW_CTRL));
+		debugfs_create_x32("led0_sw_p_en_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED0_SW_P_EN_CTRL));
+		debugfs_create_x32("led1_sw_p_en_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED1_SW_P_EN_CTRL));
+		debugfs_create_x32("led2_sw_p_en_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED2_SW_P_EN_CTRL));
+		for (p = 0; p < 28; p++) {
+			snprintf(led_sw_p_ctrl_name, sizeof(led_sw_p_ctrl_name),
+				 "led_sw_p_ctrl.%02d", p);
+			debugfs_create_x32(led_sw_p_ctrl_name, 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8380_LED_SW_P_CTRL(p)));
+		}
+	} else if (priv->family_id == RTL8390_FAMILY_ID) {
+		debugfs_create_x32("led_glb_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_GLB_CTRL));
+		debugfs_create_x32("led_set_2_3", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_2_3_CTRL));
+		debugfs_create_x32("led_set_0_1", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SET_0_1_CTRL));
+		for (p = 0; p < 4; p++) {
+			snprintf(port_led_name, sizeof(port_led_name), "led_copr_set_sel.%1d", p);
+			debugfs_create_x32(port_led_name, 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_SET_SEL_CTRL(p << 4)));
+			snprintf(port_led_name, sizeof(port_led_name), "led_fib_set_sel.%1d", p);
+			debugfs_create_x32(port_led_name, 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_SET_SEL_CTRL(p << 4)));
+		}
+		debugfs_create_x32("led_copr_pmask_ctrl_0", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(0)));
+		debugfs_create_x32("led_copr_pmask_ctrl_1", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COPR_PMASK_CTRL(32)));
+		debugfs_create_x32("led_fib_pmask_ctrl_0", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(0)));
+		debugfs_create_x32("led_fib_pmask_ctrl_1", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_FIB_PMASK_CTRL(32)));
+		debugfs_create_x32("led_combo_ctrl_0", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(0)));
+		debugfs_create_x32("led_combo_ctrl_1", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_COMBO_CTRL(32)));
+		debugfs_create_x32("led_sw_ctrl", 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_CTRL));
+		for (p = 0; p < 5; p++) {
+			snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_en_ctrl.%1d", p);
+			debugfs_create_x32(port_led_name, 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_EN_CTRL(p * 10)));
+		}
+		for (p = 0; p < 28; p++) {
+			snprintf(port_led_name, sizeof(port_led_name), "led_sw_p_ctrl.%02d", p);
+			debugfs_create_x32(port_led_name, 0644, led_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL8390_LED_SW_P_CTRL(p)));
+		}
+	}
 	return 0;
 }
 
@@ -58,9 +357,13 @@ void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv)
 {
 	struct dentry *rtl838x_dir;
 	struct dentry *port_dir;
+	struct dentry *mirror_dir;
 	struct debugfs_regset32 *port_ctrl_regset;
 	int ret, i;
+	char lag_name[10];
+	char mirror_name[10];
 
+	pr_info("%s called\n", __func__);
 	rtl838x_dir = debugfs_lookup(RTL838X_DRIVER_NAME, NULL);
 	if (!rtl838x_dir)
 		rtl838x_dir = debugfs_create_dir(RTL838X_DRIVER_NAME, NULL);
@@ -73,7 +376,6 @@ void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv)
 	/* Create one directory per port */
 	for (i = 0; i < priv->cpu_port; i++) {
 		if (priv->ports[i].phy) {
-			pr_debug("debugfs, port %d\n", i);
 			ret = rtl838x_dbgfs_port_init(rtl838x_dir, priv, i);
 			if (ret)
 				goto err;
@@ -81,7 +383,8 @@ void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv)
 	}
 
 	/* Create directory for CPU-port */
-	port_dir = debugfs_create_dir("cpu_port", rtl838x_dir);	port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL);
+	port_dir = debugfs_create_dir("cpu_port", rtl838x_dir);
+	port_ctrl_regset = devm_kzalloc(priv->dev, sizeof(*port_ctrl_regset), GFP_KERNEL);
 	if (!port_ctrl_regset) {
 		ret = -ENOMEM;
 		goto err;
@@ -89,10 +392,82 @@ void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv)
 
 	port_ctrl_regset->regs = port_ctrl_regs;
 	port_ctrl_regset->nregs = ARRAY_SIZE(port_ctrl_regs);
-	port_ctrl_regset->base = RTL838X_SW_BASE + (priv->cpu_port << 2);
+	port_ctrl_regset->base = (void *)(RTL838X_SW_BASE + (priv->cpu_port << 2));
 	debugfs_create_regset32("port_ctrl", 0400, port_dir, port_ctrl_regset);
 	debugfs_create_u8("id", 0444, port_dir, &priv->cpu_port);
 
+	/* Create entries for LAGs */
+	for (i = 0; i < priv->n_lags; i++) {
+		snprintf(lag_name, sizeof(lag_name), "lag.%02d", i);
+		if (priv->family_id == RTL8380_FAMILY_ID)
+			debugfs_create_x32(lag_name, 0644, rtl838x_dir,
+				(u32 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i)));
+		else
+			debugfs_create_x64(lag_name, 0644, rtl838x_dir,
+				(u64 *)(RTL838X_SW_BASE + priv->r->trk_mbr_ctr(i)));
+	}
+
+	/* Create directories for mirror groups */
+	for (i = 0; i < 4; i++) {
+		snprintf(mirror_name, sizeof(mirror_name), "mirror.%1d", i);
+		mirror_dir = debugfs_create_dir(mirror_name, rtl838x_dir);
+		if (priv->family_id == RTL8380_FAMILY_ID) {
+			debugfs_create_x32("ctrl", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_CTRL(i)));
+			debugfs_create_x32("ingress_pm", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + priv->r->mir_spm(i)));
+			debugfs_create_x32("egress_pm", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + priv->r->mir_dpm(i)));
+			debugfs_create_x32("qid", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_QID_CTRL(i)));
+			debugfs_create_x32("rspan_vlan", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL(i)));
+			debugfs_create_x32("rspan_vlan_mac", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_VLAN_CTRL_MAC(i)));
+			debugfs_create_x32("rspan_tx", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_CTRL));
+			debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_RM_CTRL));
+			debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_MIR_RSPAN_TX_TAG_EN_CTRL));
+		} else {
+			debugfs_create_x32("ctrl", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_CTRL(i)));
+			debugfs_create_x64("ingress_pm", 0644, mirror_dir,
+				(u64 *)(RTL838X_SW_BASE + priv->r->mir_spm(i)));
+			debugfs_create_x64("egress_pm", 0644, mirror_dir,
+				(u64 *)(RTL838X_SW_BASE + priv->r->mir_dpm(i)));
+			debugfs_create_x32("rspan_vlan", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_VLAN_CTRL(i)));
+			debugfs_create_x32("rspan_tx", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_CTRL));
+			debugfs_create_x32("rspan_tx_tag_rm", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_RM_CTRL));
+			debugfs_create_x32("rspan_tx_tag_en", 0644, mirror_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_MIR_RSPAN_TX_TAG_EN_CTRL));
+			debugfs_create_x64("sample_rate", 0644, mirror_dir,
+				(u64 *)(RTL838X_SW_BASE + RTL839X_MIR_SAMPLE_RATE_CTRL));
+		}
+	}
+
+	if (priv->family_id == RTL8380_FAMILY_ID)
+		debugfs_create_x32("bpdu_flood_mask", 0644, rtl838x_dir,
+				(u32 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask));
+	else
+		debugfs_create_x64("bpdu_flood_mask", 0644, rtl838x_dir,
+				(u64 *)(RTL838X_SW_BASE + priv->r->rma_bpdu_fld_pmask));
+
+	if (priv->family_id == RTL8380_FAMILY_ID)
+		debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL838X_VLAN_CTRL));
+	else
+		debugfs_create_x32("vlan_ctrl", 0644, rtl838x_dir,
+				(u32 *)(RTL838X_SW_BASE + RTL839X_VLAN_CTRL));
+
+	ret = rtl838x_dbgfs_leds(rtl838x_dir, priv);
+	if (ret)
+		goto err;
+
 	return;
 err:
 	rtl838x_dbgfs_cleanup(priv);
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c
index a0717e1d9a..77805e35c1 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/dsa.c
@@ -636,7 +636,7 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port,
 	mutex_unlock(&priv->reg_mutex);
 }
 
-static void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port,
+void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port,
 				       u8 state)
 {
 	u32 cmd, msti = 0;
@@ -715,7 +715,7 @@ static void rtl83xx_port_stp_state_set(struct dsa_switch *ds, int port,
 	mutex_unlock(&priv->reg_mutex);
 }
 
-static void rtl83xx_fast_age(struct dsa_switch *ds, int port)
+void rtl83xx_fast_age(struct dsa_switch *ds, int port)
 {
 	struct rtl838x_switch_priv *priv = ds->priv;
 	int s = priv->family_id == RTL8390_FAMILY_ID ? 2 : 0;
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/qos.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/qos.c
new file mode 100644
index 0000000000..2fc8d37f3e
--- /dev/null
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/qos.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <net/dsa.h>
+#include <linux/delay.h>
+
+#include <asm/mach-rtl838x/mach-rtl83xx.h>
+#include "rtl83xx.h"
+
+static struct rtl838x_switch_priv *switch_priv;
+extern struct rtl83xx_soc_info soc_info;
+
+enum scheduler_type {
+	WEIGHTED_FAIR_QUEUE = 0,
+	WEIGHTED_ROUND_ROBIN,
+};
+
+int max_available_queue[] = {0, 1, 2, 3, 4, 5, 6, 7};
+int default_queue_weights[] = {1, 1, 1, 1, 1, 1, 1, 1};
+int dot1p_priority_remapping[] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+static void rtl839x_read_scheduling_table(int port)
+{
+	u32 cmd = 1 << 9 /* Execute cmd */
+		| 0 << 8 /* Read */
+		| 0 << 6 /* Table type 0b00 */
+		| (port & 0x3f);
+	rtl839x_exec_tbl2_cmd(cmd);
+}
+
+static void rtl839x_write_scheduling_table(int port)
+{
+	u32 cmd = 1 << 9 /* Execute cmd */
+		| 1 << 8 /* Write */
+		| 0 << 6 /* Table type 0b00 */
+		| (port & 0x3f);
+	rtl839x_exec_tbl2_cmd(cmd);
+}
+
+static void rtl839x_read_out_q_table(int port)
+{
+	u32 cmd = 1 << 9 /* Execute cmd */
+		| 0 << 8 /* Read */
+		| 2 << 6 /* Table type 0b10 */
+		| (port & 0x3f);
+	rtl839x_exec_tbl2_cmd(cmd);
+}
+
+static void rtl838x_storm_enable(struct rtl838x_switch_priv *priv, int port, bool enable)
+{
+	// Enable Storm control for that port for UC, MC, and BC
+	if (enable)
+		sw_w32(0x7, RTL838X_STORM_CTRL_LB_CTRL(port));
+	else
+		sw_w32(0x0, RTL838X_STORM_CTRL_LB_CTRL(port));
+}
+
+u32 rtl838x_get_egress_rate(struct rtl838x_switch_priv *priv, int port)
+{
+	u32 rate;
+
+	if (port > priv->cpu_port)
+		return 0;
+	rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port)) & 0x3fff;
+	return rate;
+}
+
+/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */
+int rtl838x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate)
+{
+	u32 old_rate;
+
+	if (port > priv->cpu_port)
+		return -1;
+
+	old_rate = sw_r32(RTL838X_SCHED_P_EGR_RATE_CTRL(port));
+	sw_w32(rate, RTL838X_SCHED_P_EGR_RATE_CTRL(port));
+
+	return old_rate;
+}
+
+/* Set the rate limit for a particular queue in Bits/s
+ * units of the rate is 16Kbps
+ */
+void rtl838x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port,
+					    int queue, u32 rate)
+{
+	if (port > priv->cpu_port)
+		return;
+	if (queue > 7)
+		return;
+	sw_w32(rate, RTL838X_SCHED_Q_EGR_RATE_CTRL(port, queue));
+}
+
+static void rtl838x_rate_control_init(struct rtl838x_switch_priv *priv)
+{
+	int i;
+
+	pr_info("Enabling Storm control\n");
+	// TICK_PERIOD_PPS
+	if (priv->id == 0x8380)
+		sw_w32_mask(0x3ff << 20, 434 << 20, RTL838X_SCHED_LB_TICK_TKN_CTRL_0);
+
+	// Set burst rate
+	sw_w32(0x00008000, RTL838X_STORM_CTRL_BURST_0); // UC
+	sw_w32(0x80008000, RTL838X_STORM_CTRL_BURST_1); // MC and BC
+
+	// Set burst Packets per Second to 32
+	sw_w32(0x00000020, RTL838X_STORM_CTRL_BURST_PPS_0); // UC
+	sw_w32(0x00200020, RTL838X_STORM_CTRL_BURST_PPS_1); // MC and BC
+
+	// Include IFG in storm control, rate based on bytes/s (0 = packets)
+	sw_w32_mask(0, 1 << 6 | 1 << 5, RTL838X_STORM_CTRL);
+	// Bandwidth control includes preamble and IFG (10 Bytes)
+	sw_w32_mask(0, 1, RTL838X_SCHED_CTRL);
+
+	// On SoCs except RTL8382M, set burst size of port egress
+	if (priv->id != 0x8382)
+		sw_w32_mask(0xffff, 0x800, RTL838X_SCHED_LB_THR);
+
+	/* Enable storm control on all ports with a PHY and limit rates,
+	 * for UC and MC for both known and unknown addresses */
+	for (i = 0; i < priv->cpu_port; i++) {
+		if (priv->ports[i].phy) {
+			sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_UC(i));
+			sw_w32((1 << 18) | 0x8000, RTL838X_STORM_CTRL_PORT_MC(i));
+			sw_w32(0x8000, RTL838X_STORM_CTRL_PORT_BC(i));
+			rtl838x_storm_enable(priv, i, true);
+		}
+	}
+
+	// Attack prevention, enable all attack prevention measures
+	//sw_w32(0x1ffff, RTL838X_ATK_PRVNT_CTRL);
+	/* Attack prevention, drop (bit = 0) problematic packets on all ports.
+	 * Setting bit = 1 means: trap to CPU
+	 */
+	//sw_w32(0, RTL838X_ATK_PRVNT_ACT);
+	// Enable attack prevention on all ports
+	//sw_w32(0x0fffffff, RTL838X_ATK_PRVNT_PORT_EN);
+}
+
+/* Sets the rate limit, 10MBit/s is equal to a rate value of 625 */
+u32 rtl839x_get_egress_rate(struct rtl838x_switch_priv *priv, int port)
+{
+	u32 rate;
+
+	pr_debug("%s: Getting egress rate on port %d to %d\n", __func__, port, rate);
+	if (port >= priv->cpu_port)
+		return 0;
+
+	mutex_lock(&priv->reg_mutex);
+
+	rtl839x_read_scheduling_table(port);
+
+	rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7));
+	rate <<= 12;
+	rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20;
+
+	mutex_unlock(&priv->reg_mutex);
+
+	return rate;
+}
+
+/* Sets the rate limit, 10MBit/s is equal to a rate value of 625, returns previous rate */
+int rtl839x_set_egress_rate(struct rtl838x_switch_priv *priv, int port, u32 rate)
+{
+	u32 old_rate;
+
+	pr_debug("%s: Setting egress rate on port %d to %d\n", __func__, port, rate);
+	if (port >= priv->cpu_port)
+		return -1;
+
+	mutex_lock(&priv->reg_mutex);
+
+	rtl839x_read_scheduling_table(port);
+
+	old_rate = sw_r32(RTL839X_TBL_ACCESS_DATA_2(7)) & 0xff;
+	old_rate <<= 12;
+	old_rate |= sw_r32(RTL839X_TBL_ACCESS_DATA_2(8)) >> 20;
+	sw_w32_mask(0xff, (rate >> 12) & 0xff, RTL839X_TBL_ACCESS_DATA_2(7));
+	sw_w32_mask(0xfff << 20, rate << 20, RTL839X_TBL_ACCESS_DATA_2(8));
+
+	rtl839x_write_scheduling_table(port);
+	
+	mutex_unlock(&priv->reg_mutex);
+
+	return old_rate;
+}
+
+/* Set the rate limit for a particular queue in Bits/s
+ * units of the rate is 16Kbps
+ */
+void rtl839x_egress_rate_queue_limit(struct rtl838x_switch_priv *priv, int port,
+					int queue, u32 rate)
+{
+	int lsb = 128 + queue * 20;
+	int low_byte = 8 - (lsb >> 5);
+	int start_bit = lsb - (low_byte << 5);
+	u32 high_mask = 0xfffff	>> (32 - start_bit);
+
+	pr_debug("%s: Setting egress rate on port %d, queue %d to %d\n",
+		__func__, port, queue, rate);
+	if (port >= priv->cpu_port)
+		return;
+	if (queue > 7)
+		return;
+
+	mutex_lock(&priv->reg_mutex);
+
+	rtl839x_read_scheduling_table(port);
+
+	sw_w32_mask(0xfffff << start_bit, (rate & 0xfffff) << start_bit,
+		    RTL839X_TBL_ACCESS_DATA_2(low_byte));
+	if (high_mask)
+		sw_w32_mask(high_mask, (rate & 0xfffff) >> (32- start_bit),
+			    RTL839X_TBL_ACCESS_DATA_2(low_byte - 1));
+
+	rtl839x_write_scheduling_table(port);
+
+	mutex_unlock(&priv->reg_mutex);
+}
+
+static void rtl839x_rate_control_init(struct rtl838x_switch_priv *priv)
+{
+	int p, q;
+
+	pr_info("%s: enabling rate control\n", __func__);
+	/* Tick length and token size settings for SoC with 250MHz,
+	 * RTL8350 family would use 50MHz
+	 */
+	// Set the special tick period
+	sw_w32(976563, RTL839X_STORM_CTRL_SPCL_LB_TICK_TKN_CTRL);
+	// Ingress tick period and token length 10G
+	sw_w32(18 << 11 | 151, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_0);
+	// Ingress tick period and token length 1G
+	sw_w32(245 << 11 | 129, RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_1);
+	// Egress tick period 10G, bytes/token 10G and tick period 1G, bytes/token 1G
+	sw_w32(18 << 24 | 151 << 16 | 185 << 8 | 97, RTL839X_SCHED_LB_TICK_TKN_CTRL);
+	// Set the tick period of the CPU and the Token Len
+	sw_w32(3815 << 8 | 1, RTL839X_SCHED_LB_TICK_TKN_PPS_CTRL);
+
+	// Set the Weighted Fair Queueing burst size
+	sw_w32_mask(0xffff, 4500, RTL839X_SCHED_LB_THR);
+
+	// Storm-rate calculation is based on bytes/sec (bit 5), include IFG (bit 6)
+	sw_w32_mask(0, 1 << 5 | 1 << 6, RTL839X_STORM_CTRL);
+
+	/* Based on the rate control mode being bytes/s
+	 * set tick period and token length for 10G
+	 */
+	sw_w32(18 << 10 | 151, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_0);
+	/* and for 1G ports */
+	sw_w32(246 << 10 | 129, RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_1);
+
+	/* Set default burst rates on all ports (the same for 1G / 10G) with a PHY
+	 * for UC, MC and BC
+	 * For 1G port, the minimum burst rate is 1700, maximum 65535,
+	 * For 10G ports it is 2650 and 1048575 respectively */
+	for (p = 0; p < priv->cpu_port; p++) {
+		if (priv->ports[p].phy && !priv->ports[p].is10G) {
+			sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_UC_1(p));
+			sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_MC_1(p));
+			sw_w32_mask(0xffff, 0x8000, RTL839X_STORM_CTRL_PORT_BC_1(p));
+		}
+	}
+
+	/* Setup ingress/egress per-port rate control */
+	for (p = 0; p < priv->cpu_port; p++) {
+		if (!priv->ports[p].phy)
+			continue;
+
+		if (priv->ports[p].is10G)
+			rtl839x_set_egress_rate(priv, p, 625000); // 10GB/s
+		else
+			rtl839x_set_egress_rate(priv, p, 62500);  // 1GB/s
+
+		// Setup queues: all RTL83XX SoCs have 8 queues, maximum rate
+		for (q = 0; q < 8; q++)
+			rtl839x_egress_rate_queue_limit(priv, p, q, 0xfffff);
+
+		if (priv->ports[p].is10G) {
+			// Set high threshold to maximum
+			sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_10G_0(p));
+		} else {
+			// Set high threshold to maximum
+			sw_w32_mask(0xffff, 0xffff, RTL839X_IGR_BWCTRL_PORT_CTRL_1(p));
+		}
+	}
+
+	// Set global ingress low watermark rate
+	sw_w32(65532, RTL839X_IGR_BWCTRL_CTRL_LB_THR);
+}
+
+
+
+void rtl838x_setup_prio2queue_matrix(int *min_queues)
+{
+	int i;
+	u32 v;
+
+	pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL838X_QM_INTPRI2QID_CTRL));
+	for (i = 0; i < MAX_PRIOS; i++)
+		v |= i << (min_queues[i] * 3);
+	sw_w32(v, RTL838X_QM_INTPRI2QID_CTRL);
+}
+
+void rtl839x_setup_prio2queue_matrix(int *min_queues)
+{
+	int i, q;
+
+	pr_info("Current Intprio2queue setting: %08x\n", sw_r32(RTL839X_QM_INTPRI2QID_CTRL(0)));
+	for (i = 0; i < MAX_PRIOS; i++) {
+		q = min_queues[i];
+		sw_w32(i << (q * 3), RTL839X_QM_INTPRI2QID_CTRL(q));
+	}
+}
+
+/* Sets the CPU queue depending on the internal priority of a packet */
+void rtl83xx_setup_prio2queue_cpu_matrix(int *max_queues)
+{
+	int reg = soc_info.family == RTL8380_FAMILY_ID ? RTL838X_QM_PKT2CPU_INTPRI_MAP 
+					: RTL839X_QM_PKT2CPU_INTPRI_MAP;
+	int i;
+	u32 v;
+
+	pr_info("QM_PKT2CPU_INTPRI_MAP: %08x\n", sw_r32(reg));
+	for (i = 0; i < MAX_PRIOS; i++)
+		v |= max_queues[i] << (i * 3);
+	sw_w32(v, reg);
+}
+
+void rtl83xx_setup_default_prio2queue(void)
+{
+	if (soc_info.family == RTL8380_FAMILY_ID) {
+		rtl838x_setup_prio2queue_matrix(max_available_queue);
+	} else {
+		rtl839x_setup_prio2queue_matrix(max_available_queue);
+	}
+	rtl83xx_setup_prio2queue_cpu_matrix(max_available_queue);
+}
+
+/* Sets the output queue assigned to a port, the port can be the CPU-port */
+void rtl839x_set_egress_queue(int port, int queue)
+{
+	sw_w32(queue << ((port % 10) *3), RTL839X_QM_PORT_QNUM(port));
+}
+
+/* Sets the priority assigned of an ingress port, the port can be the CPU-port */
+void rtl83xx_set_ingress_priority(int port, int priority)
+{
+	if (soc_info.family == RTL8380_FAMILY_ID)
+		sw_w32(priority << ((port % 10) *3), RTL838X_PRI_SEL_PORT_PRI(port));
+	else
+		sw_w32(priority << ((port % 10) *3), RTL839X_PRI_SEL_PORT_PRI(port));
+	
+}
+
+int rtl839x_get_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port)
+{
+	u32 v;
+
+	mutex_lock(&priv->reg_mutex);
+
+	rtl839x_read_scheduling_table(port);
+	v = sw_r32(RTL839X_TBL_ACCESS_DATA_2(8));
+
+	mutex_unlock(&priv->reg_mutex);
+
+	if (v & BIT(19))
+		return WEIGHTED_ROUND_ROBIN;
+	return WEIGHTED_FAIR_QUEUE;
+}
+
+void rtl839x_set_scheduling_algorithm(struct rtl838x_switch_priv *priv, int port,
+				      enum scheduler_type sched)
+{
+	enum scheduler_type t = rtl839x_get_scheduling_algorithm(priv, port);
+	u32 v, oam_state, oam_port_state;
+	u32 count;
+	int i, egress_rate;
+
+	mutex_lock(&priv->reg_mutex);
+	/* Check whether we need to empty the egress queue of that port due to Errata E0014503 */
+	if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) {
+		// Read Operations, Adminstatrion and Management control register
+		oam_state = sw_r32(RTL839X_OAM_CTRL);
+
+		// Get current OAM state
+		oam_port_state = sw_r32(RTL839X_OAM_PORT_ACT_CTRL(port));
+	
+		// Disable OAM to block traffice
+		v = sw_r32(RTL839X_OAM_CTRL);
+		sw_w32_mask(0, 1, RTL839X_OAM_CTRL);
+		v = sw_r32(RTL839X_OAM_CTRL);
+
+		// Set to trap action OAM forward (bits 1, 2) and OAM Mux Action Drop (bit 0)
+		sw_w32(0x2, RTL839X_OAM_PORT_ACT_CTRL(port));
+
+		// Set port egress rate to unlimited
+		egress_rate = rtl839x_set_egress_rate(priv, port, 0xFFFFF);
+	
+		// Wait until the egress used page count of that port is 0
+		i = 0;
+		do {
+			usleep_range(100, 200);
+			rtl839x_read_out_q_table(port);
+			count = sw_r32(RTL839X_TBL_ACCESS_DATA_2(6));
+			count >>= 20;
+			i++;
+		} while (i < 3500 && count > 0);
+	}
+
+	// Actually set the scheduling algorithm
+	rtl839x_read_scheduling_table(port);
+	sw_w32_mask(BIT(19), sched ? BIT(19) : 0, RTL839X_TBL_ACCESS_DATA_2(8));
+	rtl839x_write_scheduling_table(port);
+
+	if (sched == WEIGHTED_FAIR_QUEUE && t == WEIGHTED_ROUND_ROBIN && port != priv->cpu_port) {
+		// Restore OAM state to control register
+		sw_w32(oam_state, RTL839X_OAM_CTRL);
+
+		// Restore trap action state
+		sw_w32(oam_port_state, RTL839X_OAM_PORT_ACT_CTRL(port));
+
+		// Restore port egress rate
+		rtl839x_set_egress_rate(priv, port, egress_rate);
+	}
+
+	mutex_unlock(&priv->reg_mutex);
+}
+
+void rtl839x_set_scheduling_queue_weights(struct rtl838x_switch_priv *priv, int port,
+					  int *queue_weights)
+{
+	int i, lsb, low_byte, start_bit, high_mask;
+
+	mutex_lock(&priv->reg_mutex);
+
+	rtl839x_read_scheduling_table(port);
+
+	for (i = 0; i < 8; i++) {
+		lsb = 48 + i * 8;
+		low_byte = 8 - (lsb >> 5);
+		start_bit = lsb - (low_byte << 5);
+		high_mask = 0x3ff >> (32 - start_bit);
+		sw_w32_mask(0x3ff << start_bit, (queue_weights[i] & 0x3ff) << start_bit,
+				RTL839X_TBL_ACCESS_DATA_2(low_byte));
+		if (high_mask)
+			sw_w32_mask(high_mask, (queue_weights[i] & 0x3ff) >> (32- start_bit),
+					RTL839X_TBL_ACCESS_DATA_2(low_byte - 1));
+	}
+
+	rtl839x_write_scheduling_table(port);
+	mutex_unlock(&priv->reg_mutex);
+}
+
+void rtl838x_config_qos(void)
+{
+	int i, p;
+	u32 v;
+
+	pr_info("Setting up RTL838X QoS\n");
+	pr_info("RTL838X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL838X_PRI_SEL_TBL_CTRL(0)));
+	rtl83xx_setup_default_prio2queue();
+
+	// Enable inner (bit 12) and outer (bit 13) priority remapping from DSCP
+	sw_w32_mask(0, BIT(12) | BIT(13), RTL838X_PRI_DSCP_INVLD_CTRL0);
+
+	/* Set default weight for calculating internal priority, in prio selection group 0
+	 * Port based (prio 3), Port outer-tag (4), DSCP (5), Inner Tag (6), Outer Tag (7)
+	 */
+	v = 3 | (4 << 3) | (5 << 6) | (6 << 9) | (7 << 12);
+	sw_w32(v, RTL838X_PRI_SEL_TBL_CTRL(0));
+
+	// Set the inner and outer priority one-to-one to re-marked outer dot1p priority
+	v = 0;
+	for (p = 0; p < 8; p++)
+		v |= p << (3 * p);
+	sw_w32(v, RTL838X_RMK_OPRI_CTRL);
+	sw_w32(v, RTL838X_RMK_IPRI_CTRL);
+
+	v = 0;
+	for (p = 0; p < 8; p++)
+		v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3);
+	sw_w32(v, RTL838X_PRI_SEL_IPRI_REMAP);
+
+	// On all ports set scheduler type to WFQ
+	for (i = 0; i <= soc_info.cpu_port; i++)
+		sw_w32(0, RTL838X_SCHED_P_TYPE_CTRL(i));
+
+	// Enable egress scheduler for CPU-Port
+	sw_w32_mask(0, BIT(8), RTL838X_SCHED_LB_CTRL(soc_info.cpu_port));
+
+	// Enable egress drop allways on
+	sw_w32_mask(0, BIT(11), RTL838X_FC_P_EGR_DROP_CTRL(soc_info.cpu_port));
+
+	// Give special trap frames priority 7 (BPDUs) and routing exceptions:
+	sw_w32_mask(0, 7 << 3 | 7, RTL838X_QM_PKT2CPU_INTPRI_2);
+	// Give RMA frames priority 7:
+	sw_w32_mask(0, 7, RTL838X_QM_PKT2CPU_INTPRI_1);
+}
+
+void rtl839x_config_qos(void)
+{
+	int port, p, q;
+	u32 v;
+	struct rtl838x_switch_priv *priv = switch_priv;
+
+	pr_info("Setting up RTL839X QoS\n");
+	pr_info("RTL839X_PRI_SEL_TBL_CTRL(i): %08x\n", sw_r32(RTL839X_PRI_SEL_TBL_CTRL(0)));
+	rtl83xx_setup_default_prio2queue();
+
+	for (port = 0; port < soc_info.cpu_port; port++)
+		sw_w32(7, RTL839X_QM_PORT_QNUM(port));
+
+	// CPU-port gets queue number 7
+	sw_w32(7, RTL839X_QM_PORT_QNUM(soc_info.cpu_port));
+
+	for (port = 0; port <= soc_info.cpu_port; port++) {
+		rtl83xx_set_ingress_priority(port, 0);
+		rtl839x_set_scheduling_algorithm(priv, port, WEIGHTED_FAIR_QUEUE);
+		rtl839x_set_scheduling_queue_weights(priv, port, default_queue_weights);
+		// Do re-marking based on outer tag
+		sw_w32_mask(0, BIT(port % 32), RTL839X_RMK_PORT_DEI_TAG_CTRL(port));
+	}
+
+	// Remap dot1p priorities to internal priority, for this the outer tag needs be re-marked
+	v = 0;
+	for (p = 0; p < 8; p++)
+		v |= (dot1p_priority_remapping[p] & 0x7) << (p * 3);
+	sw_w32(v, RTL839X_PRI_SEL_IPRI_REMAP);
+	
+	/* Configure Drop Precedence for Drop Eligible Indicator (DEI)
+	 * Index 0: 0
+	 * Index 1: 2
+	 * Each indicator is 2 bits long
+	 */
+	sw_w32(2 << 2, RTL839X_PRI_SEL_DEI2DP_REMAP);
+
+	// Re-mark DEI: 4 bit-fields of 2 bits each, field 0 is bits 0-1, ...
+	sw_w32((0x1 << 2) | (0x1 << 4), RTL839X_RMK_DEI_CTRL);
+
+	/* Set Congestion avoidance drop probability to 0 for drop precedences 0-2 (bits 24-31)
+	 * low threshold (bits 0-11) to 4095 and high threshold (bits 12-23) to 4095
+	 * Weighted Random Early Detection (WRED) is used
+	 */
+	sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(0));
+	sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(1));
+	sw_w32(4095 << 12| 4095, RTL839X_WRED_PORT_THR_CTRL(2));
+
+	/* Set queue-based congestion avoidance properties, register fields are as
+	 * for forward RTL839X_WRED_PORT_THR_CTRL
+	 */
+	for (q = 0; q < 8; q++) {
+		sw_w32(255 << 24 | 78 << 12 | 68, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
+		sw_w32(255 << 24 | 74 << 12 | 64, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
+		sw_w32(255 << 24 | 70 << 12 | 60, RTL839X_WRED_QUEUE_THR_CTRL(q, 0));
+	}
+}
+
+void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv)
+{
+	switch_priv = priv;
+
+	pr_info("In %s\n", __func__);
+
+	if (priv->family_id == RTL8380_FAMILY_ID)
+		return rtl838x_config_qos();
+	else if (priv->family_id == RTL8390_FAMILY_ID)
+		return rtl839x_config_qos();
+
+	if (priv->family_id == RTL8380_FAMILY_ID)
+		rtl838x_rate_control_init(priv);
+	else if (priv->family_id == RTL8390_FAMILY_ID)
+		rtl839x_rate_control_init(priv);
+	
+}
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
index c7b5873e99..f685b7d1c2 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
@@ -142,6 +142,11 @@ static inline int rtl838x_mac_link_spd_sts(int p)
 	return RTL838X_MAC_LINK_SPD_STS(p);
 }
 
+inline static int rtl838x_trk_mbr_ctr(int group)
+{
+	return RTL838X_TRK_MBR_CTR + (group << 2);
+}
+
 static u64 rtl838x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
 {
 	u64 entry;
@@ -290,6 +295,9 @@ const struct rtl838x_reg rtl838x_reg = {
 	.vlan_port_igr_filter = rtl838x_vlan_port_igr_filter,
 	.vlan_port_pb = rtl838x_vlan_port_pb,
 	.vlan_port_tag_sts_ctrl = rtl838x_vlan_port_tag_sts_ctrl,
+	.trk_mbr_ctr = rtl838x_trk_mbr_ctr,
+	.rma_bpdu_fld_pmask = RTL838X_RMA_BPDU_FLD_PMSK,
+	.spcl_trap_eapol_ctrl = RTL838X_SPCL_TRAP_EAPOL_CTRL,
 };
 
 irqreturn_t rtl838x_switch_irq(int irq, void *dev_id)
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h
index 1ebb4dff72..8a4958453a 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h
@@ -55,6 +55,7 @@
 #define MAPLE_SDS5_FIB_REG0r			(RTL838X_SDS4_REG28 + 0x980)
 
 /* VLAN registers */
+#define RTL838X_VLAN_CTRL			(0x3A74)
 #define RTL838X_VLAN_PROFILE(idx)		(0x3A88 + ((idx) << 2))
 #define RTL838X_VLAN_PORT_EGR_FLTR		(0x3A84)
 #define RTL838X_VLAN_PORT_PB_VLAN(port)		(0x3C00 + ((port) << 2))
@@ -128,6 +129,8 @@
 #define RTL839X_L2_PORT_NEW_SA_FWD(p)		(0x3900 + (((p >> 4) << 2)))
 #define RTL838X_L2_PORT_SALRN(p)		(0x328c + (((p >> 4) << 2)))
 #define RTL839X_L2_PORT_SALRN(p)		(0x38F0 + (((p >> 4) << 2)))
+#define RTL838X_RMA_BPDU_FLD_PMSK		(0x4348)
+#define RTL839X_RMA_BPDU_FLD_PMSK		(0x125C)
 
 /* Port Mirroring */
 #define RTL838X_MIR_CTRL(grp)			(0x5D00 + (((grp) << 2)))
@@ -137,7 +140,7 @@
 #define RTL839X_MIR_DPM_CTRL(grp)		(0x2530 + (((grp) << 2)))
 #define RTL839X_MIR_SPM_CTRL(grp)		(0x2510 + (((grp) << 2)))
 
-/* Storm control */
+/* Storm/rate control and scheduling */
 #define RTL838X_STORM_CTRL			(0x4700)
 #define RTL839X_STORM_CTRL			(0x1800)
 #define RTL838X_STORM_CTRL_LB_CTRL(p)		(0x4884 + (((p) << 2)))
@@ -145,12 +148,23 @@
 #define RTL838X_STORM_CTRL_BURST_PPS_1		(0x4878)
 #define RTL838X_STORM_CTRL_BURST_0		(0x487c)
 #define RTL838X_STORM_CTRL_BURST_1		(0x4880)
+#define RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_0	(0x1804)
+#define RTL839X_STORM_CTRL_LB_TICK_TKN_CTRL_1	(0x1808)
 #define RTL838X_SCHED_CTRL			(0xB980)
+#define RTL839X_SCHED_CTRL			(0x60F4)
 #define RTL838X_SCHED_LB_TICK_TKN_CTRL_0	(0xAD58)
 #define RTL838X_SCHED_LB_TICK_TKN_CTRL_1	(0xAD5C)
 #define RTL839X_SCHED_LB_TICK_TKN_CTRL_0	(0x1804)
 #define RTL839X_SCHED_LB_TICK_TKN_CTRL_1	(0x1808)
+#define RTL839X_STORM_CTRL_SPCL_LB_TICK_TKN_CTRL (0x2000)
+#define RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_0	(0x1604)
+#define RTL839X_IGR_BWCTRL_LB_TICK_TKN_CTRL_1	(0x1608)
+#define RTL839X_SCHED_LB_TICK_TKN_CTRL		(0x60F8)
+#define RTL839X_SCHED_LB_TICK_TKN_PPS_CTRL	(0x6200)
 #define RTL838X_SCHED_LB_THR			(0xB984)
+#define RTL839X_SCHED_LB_THR			(0x60FC)
+#define RTL838X_SCHED_P_EGR_RATE_CTRL(p)	(0xC008 + (((p) << 7)))
+#define RTL838X_SCHED_Q_EGR_RATE_CTRL(p, q)	(0xC00C + (p << 7) + (((q) << 2)))
 #define RTL838X_STORM_CTRL_PORT_BC_EXCEED	(0x470C)
 #define RTL838X_STORM_CTRL_PORT_MC_EXCEED	(0x4710)
 #define RTL838X_STORM_CTRL_PORT_UC_EXCEED	(0x4714)
@@ -160,6 +174,23 @@
 #define RTL838X_STORM_CTRL_PORT_UC(p)		(0x4718 + (((p) << 2)))
 #define RTL838X_STORM_CTRL_PORT_MC(p)		(0x478c + (((p) << 2)))
 #define RTL838X_STORM_CTRL_PORT_BC(p)		(0x4800 + (((p) << 2)))
+#define RTL839X_STORM_CTRL_PORT_UC_0(p)		(0x185C + (((p) << 3)))
+#define RTL839X_STORM_CTRL_PORT_UC_1(p)		(0x1860 + (((p) << 3)))
+#define RTL839X_STORM_CTRL_PORT_MC_0(p)		(0x19FC + (((p) << 3)))
+#define RTL839X_STORM_CTRL_PORT_MC_1(p)		(0x1a00 + (((p) << 3)))
+#define RTL839X_STORM_CTRL_PORT_BC_0(p)		(0x1B9C + (((p) << 3)))
+#define RTL839X_STORM_CTRL_PORT_BC_1(p)		(0x1BA0 + (((p) << 3)))
+#define RTL839X_TBL_ACCESS_CTRL_2		(0x611C)
+#define RTL839X_TBL_ACCESS_DATA_2(i)		(0x6120 + (((i) << 2)))
+#define RTL839X_IGR_BWCTRL_PORT_CTRL_10G_0(p)	(0x1618 + (((p) << 3)))
+#define RTL839X_IGR_BWCTRL_PORT_CTRL_10G_1(p)	(0x161C + (((p) << 3)))
+#define RTL839X_IGR_BWCTRL_PORT_CTRL_0(p)	(0x1640 + (((p) << 3)))
+#define RTL839X_IGR_BWCTRL_PORT_CTRL_1(p)	(0x1644 + (((p) << 3)))
+#define RTL839X_IGR_BWCTRL_CTRL_LB_THR		(0x1614)
+
+/* Link aggregation (Trunking) */
+#define RTL839X_TRK_MBR_CTR			(0x2200)
+#define RTL838X_TRK_MBR_CTR			(0x3E00)
 
 /* Attack prevention */
 #define RTL838X_ATK_PRVNT_PORT_EN		(0x5B00)
@@ -167,6 +198,45 @@
 #define RTL838X_ATK_PRVNT_ACT			(0x5B08)
 #define RTL838X_ATK_PRVNT_STS			(0x5B1C)
 
+/* 802.1X */
+#define RTL838X_SPCL_TRAP_EAPOL_CTRL		(0x6988)
+#define RTL839X_SPCL_TRAP_EAPOL_CTRL		(0x105C)
+
+/* QoS */
+#define RTL838X_QM_INTPRI2QID_CTRL		(0x5F00)
+#define RTL839X_QM_INTPRI2QID_CTRL(q)		(0x1110 + (q << 2))
+#define RTL839X_QM_PORT_QNUM(p)			(0x1130 + (((p / 10) << 2)))
+#define RTL838X_PRI_SEL_PORT_PRI(p)		(0x5FB8 + (((p / 10) << 2)))
+#define RTL839X_PRI_SEL_PORT_PRI(p)		(0x10A8 + (((p / 10) << 2)))
+#define RTL838X_QM_PKT2CPU_INTPRI_MAP		(0x5F10)
+#define RTL839X_QM_PKT2CPU_INTPRI_MAP		(0x1154)
+#define RTL838X_PRI_SEL_CTRL			(0x10E0)
+#define RTL839X_PRI_SEL_CTRL			(0x10E0)
+#define RTL838X_PRI_SEL_TBL_CTRL(i)		(0x5FD8 + (((i) << 2)))
+#define RTL839X_PRI_SEL_TBL_CTRL(i)		(0x10D0 + (((i) << 2)))
+#define RTL838X_QM_PKT2CPU_INTPRI_0		(0x5F04)
+#define RTL838X_QM_PKT2CPU_INTPRI_1		(0x5F08)
+#define RTL838X_QM_PKT2CPU_INTPRI_2		(0x5F0C)
+#define RTL839X_OAM_CTRL			(0x2100)
+#define RTL839X_OAM_PORT_ACT_CTRL(p)	 	(0x2104 + (((p) << 2)))
+#define RTL839X_RMK_PORT_DEI_TAG_CTRL(p)	(0x6A9C + (((p >> 5) << 2)))
+#define RTL839X_PRI_SEL_IPRI_REMAP		(0x1080)
+#define RTL838X_PRI_SEL_IPRI_REMAP		(0x5F8C)
+#define RTL839X_PRI_SEL_DEI2DP_REMAP		(0x10EC)
+#define RTL839X_PRI_SEL_DSCP2DP_REMAP_ADDR(i)	(0x10F0 + (((i >> 4) << 2)))
+#define RTL839X_RMK_DEI_CTRL			(0x6AA4)
+#define RTL839X_WRED_PORT_THR_CTRL(i)		(0x6084 + ((i) << 2))
+#define RTL839X_WRED_QUEUE_THR_CTRL(q, i) 	(0x6090 + ((q) * 12) + ((i) << 2))
+#define RTL838X_PRI_DSCP_INVLD_CTRL0		(0x5FE8)
+#define RTL838X_RMK_IPRI_CTRL			(0xA460)
+#define RTL838X_RMK_OPRI_CTRL			(0xA464)
+#define RTL838X_SCHED_P_TYPE_CTRL(p)		(0xC04C + (((p) << 7)))
+#define RTL838X_SCHED_LB_CTRL(p)		(0xC004 + (((p) << 7)))
+#define RTL838X_FC_P_EGR_DROP_CTRL(p)		(0x6B1C + (((p) << 2)))
+
+#define MAX_LAGS 16
+#define MAX_PRIOS 8
+
 enum phy_type {
 	PHY_NONE = 0,
 	PHY_RTL838X_SDS = 1,
@@ -182,6 +252,7 @@ struct rtl838x_port {
 	u16 pvid;
 	bool eee_enabled;
 	enum phy_type phy;
+	bool is10G;
 	const struct dsa_port *dp;
 };
 
@@ -267,6 +338,10 @@ struct rtl838x_reg {
 	int (*vlan_port_igr_filter)(int port);
 	int (*vlan_port_pb)(int port);
 	int (*vlan_port_tag_sts_ctrl)(int port);
+	int (*rtl838x_vlan_port_tag_sts_ctrl)(int port);
+	int (*trk_mbr_ctr)(int group);
+	int rma_bpdu_fld_pmask;
+	int spcl_trap_eapol_ctrl;
 };
 
 struct rtl838x_switch_priv {
@@ -286,6 +361,10 @@ struct rtl838x_switch_priv {
 	u8 port_mask;
 	u32 fib_entries;
 	struct dentry *dbgfs_dir;
+	int n_lags;
+	u64 lags_port_members[MAX_LAGS];
+	struct net_device *lag_devs[MAX_LAGS];
+	struct notifier_block nb;
 };
 
 void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv);
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
index 8dd123f609..03b92a7914 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
@@ -4,7 +4,7 @@
 #include "rtl83xx.h"
 
 extern struct mutex smi_lock;
-
+extern struct rtl83xx_soc_info soc_info;
 
 static inline void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg)
 {
@@ -70,6 +70,12 @@ static inline void rtl839x_exec_tbl1_cmd(u32 cmd)
 	do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_1) & BIT(16));
 }
 
+inline void rtl839x_exec_tbl2_cmd(u32 cmd)
+{
+	sw_w32(cmd, RTL839X_TBL_ACCESS_CTRL_2);
+	do { } while (sw_r32(RTL839X_TBL_ACCESS_CTRL_2) & (1 << 9));
+}
+
 static inline int rtl839x_tbl_access_data_0(int i)
 {
 	return RTL839X_TBL_ACCESS_DATA_0(i);
@@ -181,6 +187,11 @@ static inline int rtl839x_mac_link_spd_sts(int p)
 	return RTL839X_MAC_LINK_SPD_STS(p);
 }
 
+static inline int rtl839x_trk_mbr_ctr(int group)
+{
+	return RTL839X_TRK_MBR_CTR + (group << 3);
+}
+
 static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
 {
 	u64 entry;
@@ -358,6 +369,9 @@ const struct rtl838x_reg rtl839x_reg = {
 	.vlan_port_igr_filter = rtl839x_vlan_port_igr_filter,
 	.vlan_port_pb = rtl839x_vlan_port_pb,
 	.vlan_port_tag_sts_ctrl = rtl839x_vlan_port_tag_sts_ctrl,
+	.trk_mbr_ctr = rtl839x_trk_mbr_ctr,
+	.rma_bpdu_fld_pmask = RTL839X_RMA_BPDU_FLD_PMSK,
+	.spcl_trap_eapol_ctrl = RTL839X_SPCL_TRAP_EAPOL_CTRL,
 };
 
 irqreturn_t rtl839x_switch_irq(int irq, void *dev_id)
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h
index 3953512525..23f36a85e5 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h
@@ -24,7 +24,7 @@ struct rtl83xx_mib_desc {
 	const char *name;
 };
 
-void __init rtl83xx_storm_control_init(struct rtl838x_switch_priv *priv);
+void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv);
 
 /* RTL838x-specific */
 u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed);
@@ -39,6 +39,7 @@ irqreturn_t rtl839x_switch_irq(int irq, void *dev_id);
 void rtl8390_get_version(struct rtl838x_switch_priv *priv);
 void rtl839x_vlan_profile_dump(int index);
 int rtl83xx_dsa_phy_write(struct dsa_switch *ds, int phy_addr, int phy_reg, u16 val);
+inline void rtl839x_exec_tbl2_cmd(u32 cmd);
 
 #endif /* _NET_DSA_RTL83XX_H */
 
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/storm.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/storm.c
deleted file mode 100644
index de0af033f3..0000000000
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/storm.c
+++ /dev/null
@@ -1,64 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-
-#include <asm/mach-rtl838x/mach-rtl83xx.h>
-#include "rtl83xx.h"
-
-
-static void rtl83xx_storm_enable(struct rtl838x_switch_priv *priv, int port, bool enable)
-{
-	// Enable Storm control for that port for UC, MC, and BC
-	if (enable)
-		sw_w32(0x7, RTL838X_STORM_CTRL_LB_CTRL(port));
-	else
-		sw_w32(0x0, RTL838X_STORM_CTRL_LB_CTRL(port));
-}
-
-void __init rtl83xx_storm_control_init(struct rtl838x_switch_priv *priv)
-{
-	int i;
-
-	pr_debug("Enabling Storm control\n");
-	// TICK_PERIOD_PPS
-	if (priv->id == 0x8380)
-		sw_w32_mask(0x3ff << 20, 434 << 20, RTL838X_SCHED_LB_TICK_TKN_CTRL_0);
-
-	// Set burst rate
-	sw_w32(0x00008000, RTL838X_STORM_CTRL_BURST_0); // UC
-	sw_w32(0x80008000, RTL838X_STORM_CTRL_BURST_1); // MC and BC
-
-	// Set burst Packets per Second to 32
-	sw_w32(0x00000020, RTL838X_STORM_CTRL_BURST_PPS_0); // UC
-	sw_w32(0x00200020, RTL838X_STORM_CTRL_BURST_PPS_1); // MC and BC
-
-	// Include IFG in storm control
-	sw_w32_mask(0, BIT(6), RTL838X_STORM_CTRL);
-	// Rate control is based on bytes/s (0 = packets)
-	sw_w32_mask(0, BIT(5), RTL838X_STORM_CTRL);
-	// Bandwidth control includes preamble and IFG (10 Bytes)
-	sw_w32_mask(0, 1, RTL838X_SCHED_CTRL);
-
-	// On SoCs except RTL8382M, set burst size of port egress
-	if (priv->id != 0x8382)
-		sw_w32_mask(0xffff, 0x800, RTL838X_SCHED_LB_THR);
-
-	/* Enable storm control on all ports with a PHY and limit rates,
-	 * for UC and MC for both known and unknown addresses */
-	for (i = 0; i < priv->cpu_port; i++) {
-		if (priv->ports[i].phy) {
-			sw_w32(BIT(18) | 0x8000, RTL838X_STORM_CTRL_PORT_UC(i));
-			sw_w32(BIT(18) | 0x8000, RTL838X_STORM_CTRL_PORT_MC(i));
-			sw_w32(0x000, RTL838X_STORM_CTRL_PORT_BC(i));
-			rtl83xx_storm_enable(priv, i, true);
-		}
-	}
-
-	// Attack prevention, enable all attack prevention measures
-	//sw_w32(0x1ffff, RTL838X_ATK_PRVNT_CTRL);
-	/* Attack prevention, drop (bit = 0) problematic packets on all ports.
-	 * Setting bit = 1 means: trap to CPU
-	 */
-	//sw_w32(0, RTL838X_ATK_PRVNT_ACT);
-	// Enable attack prevention on all ports
-	//sw_w32(0x0fffffff, RTL838X_ATK_PRVNT_PORT_EN);
-}
-
diff --git a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c
index fec842674e..951d936c71 100644
--- a/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c
+++ b/target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c
@@ -91,14 +91,19 @@ struct notify_b {
 	u32			reserved2[8];
 };
 
-inline void rtl838x_create_tx_header(struct p_hdr *h, int dest_port)
+inline void rtl838x_create_tx_header(struct p_hdr *h, int dest_port, int prio)
 {
+	prio &= 0x7;
+
 	if (dest_port > 0) {
 		h->cpu_tag[0] = 0x0400;
 		h->cpu_tag[1] = 0x0200;
 		h->cpu_tag[2] = 0x0000;
-		h->cpu_tag[3] = (1 << dest_port) >> 16;
-		h->cpu_tag[4] = (1 << dest_port) & 0xffff;
+		h->cpu_tag[3] = BIT(dest_port) >> 16;
+		h->cpu_tag[4] = BIT(dest_port) & 0xffff;
+		// Set internal priority and AS_PRIO
+		if (prio >= 0)
+			h->cpu_tag[1] |= (prio | 0x8) << 12;
 	} else {
 		h->cpu_tag[0] = 0;
 		h->cpu_tag[1] = 0;
@@ -108,14 +113,25 @@ inline void rtl838x_create_tx_header(struct p_hdr *h, int dest_port)
 	}
 }
 
-inline void rtl839x_create_tx_header(struct p_hdr *h, int dest_port)
+inline void rtl839x_create_tx_header(struct p_hdr *h, int dest_port, int prio)
 {
+	prio &= 0x7;
+
 	if (dest_port > 0) {
 		h->cpu_tag[0] = 0x0100;
-		h->cpu_tag[1] = ((1 << (dest_port - 32)) >> 16) | (1 << 21);
-		h->cpu_tag[2] = (1 << (dest_port - 32)) & 0xffff;
-		h->cpu_tag[3] = (1 << dest_port) >> 16;
-		h->cpu_tag[4] = (1 << dest_port) & 0xffff;
+		h->cpu_tag[1] = h->cpu_tag[2] = h->cpu_tag[3] = h->cpu_tag[4] = 0;
+		if (dest_port >= 32) {
+			dest_port -= 32;
+			h->cpu_tag[1] = BIT(dest_port) >> 16;
+			h->cpu_tag[2] = BIT(dest_port) & 0xffff;
+		} else {
+			h->cpu_tag[3] = BIT(dest_port) >> 16;
+			h->cpu_tag[4] = BIT(dest_port) & 0xffff;
+		}
+		h->cpu_tag[1] |= BIT(21); // Enable destination port mask use
+		// Set internal priority and AS_PRIO
+		if (prio >= 0)
+			h->cpu_tag[0] |= prio | BIT(3);
 	} else {
 		h->cpu_tag[0] = 0;
 		h->cpu_tag[1] = 0;
@@ -125,13 +141,19 @@ inline void rtl839x_create_tx_header(struct p_hdr *h, int dest_port)
 	}
 }
 
+struct rtl838x_rx_q {
+	int id;
+	struct rtl838x_eth_priv *priv;
+	struct napi_struct napi;
+};
+
 struct rtl838x_eth_priv {
 	struct net_device *netdev;
 	struct platform_device *pdev;
 	void		*membase;
 	spinlock_t	lock;
 	struct mii_bus	*mii_bus;
-	struct napi_struct napi;
+	struct rtl838x_rx_q rx_qs[RXRINGS];
 	struct phylink *phylink;
 	struct phylink_config phylink_config;
 	u16 id;
@@ -229,25 +251,25 @@ struct fdb_update_work {
 
 void rtl838x_fdb_sync(struct work_struct *work)
 {
-       const struct fdb_update_work *uw =
-               container_of(work, struct fdb_update_work, work);
-       struct switchdev_notifier_fdb_info info;
-       u8 addr[ETH_ALEN];
-       int i = 0;
-       int action;
-
-       while (uw->macs[i]) {
-               action = (uw->macs[i] & (1ULL << 63)) ? SWITCHDEV_FDB_ADD_TO_BRIDGE
-                               : SWITCHDEV_FDB_DEL_TO_BRIDGE;
-               u64_to_ether_addr(uw->macs[i] & 0xffffffffffffULL, addr);
-               info.addr = &addr[0];
-               info.vid = 0;
-               info.offloaded = 1;
-               pr_debug("FDB entry %d: %llx, action %d\n", i, uw->macs[0], action);
-               call_switchdev_notifiers(action, uw->ndev, &info.info, NULL);
-               i++;
-       }
-       kfree(work);
+	const struct fdb_update_work *uw =
+		container_of(work, struct fdb_update_work, work);
+	struct switchdev_notifier_fdb_info info;
+	u8 addr[ETH_ALEN];
+	int i = 0;
+	int action;
+
+	while (uw->macs[i]) {
+		action = (uw->macs[i] & (1ULL << 63)) ? SWITCHDEV_FDB_ADD_TO_BRIDGE
+				: SWITCHDEV_FDB_DEL_TO_BRIDGE;
+		u64_to_ether_addr(uw->macs[i] & 0xffffffffffffULL, addr);
+		info.addr = &addr[0];
+		info.vid = 0;
+		info.offloaded = 1;
+		pr_debug("FDB entry %d: %llx, action %d\n", i, uw->macs[0], action);
+		call_switchdev_notifiers(action, uw->ndev, &info.info, NULL);
+		i++;
+	}
+	kfree(work);
 }
 
 static void rtl839x_l2_notification_handler(struct rtl838x_eth_priv *priv)
@@ -295,6 +317,7 @@ static irqreturn_t rtl838x_net_irq(int irq, void *dev_id)
 	u32 status = sw_r32(priv->r->dma_if_intr_sts);
 	bool triggered = false;
 	u32 atk = sw_r32(RTL838X_ATK_PRVNT_STS);
+	int i;
 	u32 storm_uc = sw_r32(RTL838X_STORM_CTRL_PORT_UC_EXCEED);
 	u32 storm_mc = sw_r32(RTL838X_STORM_CTRL_PORT_MC_EXCEED);
 	u32 storm_bc = sw_r32(RTL838X_STORM_CTRL_PORT_BC_EXCEED);
@@ -325,12 +348,13 @@ static irqreturn_t rtl838x_net_irq(int irq, void *dev_id)
 
 	/* RX interrupt */
 	if (status & 0x0ff00) {
-		/* Disable RX interrupt */
-		if (triggered)
-			pr_info("RX\n");
-		sw_w32_mask(0xff00, 0, priv->r->dma_if_intr_msk);
-		sw_w32(0x0000ff00, priv->r->dma_if_intr_sts);
-		napi_schedule(&priv->napi);
+		/* Disable RX interrupt for this ring */
+		sw_w32_mask(0xff00 & status, 0, priv->r->dma_if_intr_msk);
+		sw_w32(0x0000ff00 & status, priv->r->dma_if_intr_sts);
+		for (i = 0; i < RXRINGS; i++) {
+			if (status & BIT(i + 8))
+				napi_schedule(&priv->rx_qs[i].napi);
+		}
 	}
 
 	/* RX buffer overrun */
@@ -535,7 +559,7 @@ static int rtl838x_eth_open(struct net_device *ndev)
 	unsigned long flags;
 	struct rtl838x_eth_priv *priv = netdev_priv(ndev);
 	struct ring_b *ring = priv->membase;
-	int err;
+	int i, err;
 
 	pr_info("%s called: RX rings %d, TX rings %d\n", __func__, RXRINGS, TXRINGS);
 
@@ -559,8 +583,10 @@ static int rtl838x_eth_open(struct net_device *ndev)
 	}
 	phylink_start(priv->phylink);
 
-	napi_enable(&priv->napi);
-	netif_start_queue(ndev);
+	for (i = 0; i < RXRINGS; i++)
+		napi_enable(&priv->rx_qs[i].napi);
+
+	netif_tx_start_all_queues(ndev);
 
 	if (priv->family_id == RTL8380_FAMILY_ID) {
 		rtl838x_hw_en_rxtx(priv);
@@ -625,6 +651,7 @@ static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv)
 static int rtl838x_eth_stop(struct net_device *ndev)
 {
 	unsigned long flags;
+	int i;
 	struct rtl838x_eth_priv *priv = netdev_priv(ndev);
 
 	pr_info("in %s\n", __func__);
@@ -633,8 +660,12 @@ static int rtl838x_eth_stop(struct net_device *ndev)
 	phylink_stop(priv->phylink);
 	rtl838x_hw_stop(priv);
 	free_irq(ndev->irq, ndev);
-	napi_disable(&priv->napi);
-	netif_stop_queue(ndev);
+
+	for (i = 0; i < RXRINGS; i++)
+		napi_disable(&priv->rx_qs[i].napi);
+
+	netif_tx_stop_all_queues(ndev);
+
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	return 0;
@@ -705,6 +736,10 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev)
 	unsigned long flags;
 	struct p_hdr *h;
 	int dest_port = -1;
+	int q = skb_get_queue_mapping(skb) % TXRINGS;
+
+	if (q)
+		pr_debug("SKB priority: %d\n", skb->priority);
 
 	spin_lock_irqsave(&priv->lock, flags);
 	len = skb->len;
@@ -729,9 +764,9 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev)
 	}
 
 	/* We can send this packet if CPU owns the descriptor */
-	if (!(ring->tx_r[0][ring->c_tx[0]] & 0x1)) {
+	if (!(ring->tx_r[q][ring->c_tx[q]] & 0x1)) {
 		/* Set descriptor for tx */
-		h = &ring->tx_header[0][ring->c_tx[0]];
+		h = &ring->tx_header[q][ring->c_tx[q]];
 
 		h->buf = (u8 *)KSEG1ADDR(ring->tx_space);
 		h->size = len;
@@ -739,17 +774,17 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev)
 
 		/* Create cpu_tag */
 		if (priv->family_id == RTL8380_FAMILY_ID)
-			rtl838x_create_tx_header(h, dest_port);
+			rtl838x_create_tx_header(h, dest_port, skb->priority >> 1);
 		else
-			rtl839x_create_tx_header(h, dest_port);
+			rtl839x_create_tx_header(h, dest_port, skb->priority >> 1);
 
 		/* Copy packet data to tx buffer */
 		memcpy((void *)KSEG1ADDR(h->buf), skb->data, len);
 		/* Make sure packet data is visible to ASIC */
-		mb(); /* wmb() probably works, too */
+		wmb();
 
 		/* Hand over to switch */
-		ring->tx_r[0][ring->c_tx[0]] = ring->tx_r[0][ring->c_tx[0]] | 0x1;
+		ring->tx_r[q][ring->c_tx[q]] = ring->tx_r[q][ring->c_tx[q]] | 0x1;
 
 		/* BUG: before tx fetch, need to make sure right data is accessed
 		 * This might not be necessary on newer RTL839x, though.
@@ -766,7 +801,7 @@ static int rtl838x_eth_tx(struct sk_buff *skb, struct net_device *dev)
 		dev->stats.tx_packets++;
 		dev->stats.tx_bytes += len;
 		dev_kfree_skb(skb);
-		ring->c_tx[0] = (ring->c_tx[0] + 1) % TXRINGLEN;
+		ring->c_tx[q] = (ring->c_tx[q] + 1) % TXRINGLEN;
 		ret = NETDEV_TX_OK;
 	} else {
 		dev_warn(&priv->pdev->dev, "Data is owned by switch\n");
@@ -777,6 +812,15 @@ txdone:
 	return ret;
 }
 
+u16 rtl838x_pick_tx_queue(struct net_device *dev, struct sk_buff *skb,
+			  struct net_device *sb_dev)
+{
+	static u8 last = 0;
+
+	last++;
+	return last % TXRINGS;
+}
+
 static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
 {
 	struct rtl838x_eth_priv *priv = netdev_priv(dev);
@@ -789,10 +833,12 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
 	u32	*last;
 	struct p_hdr *h;
 	bool dsa = netdev_uses_dsa(dev);
+	int reason, queue;
 
 	spin_lock_irqsave(&priv->lock, flags);
 	last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur(r)));
 
+	pr_debug("RX - %d\n", r);
 	if (&ring->rx_r[r][ring->c_rx[r]] == last) {
 		spin_unlock_irqrestore(&priv->lock, flags);
 		return 0;
@@ -845,6 +891,24 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
 				skb->data[len-1] = 0x00;
 			}
 
+			if (priv->family_id == RTL8380_FAMILY_ID) {
+				reason = h->cpu_tag[3] & 0xf;
+				if (reason != 15)
+					pr_debug("Reason: %d\n", reason);
+				queue = (h->cpu_tag[0] & 0xe0) >> 5;
+				if (reason != 4) // NIC_RX_REASON_SPECIAL_TRAP
+					skb->data[len-3] |= 0x40;
+			} else {
+				reason = h->cpu_tag[4] & 0x1f;
+				if (reason != 31)
+					pr_debug("Reason: %d\n", reason);
+				queue = (h->cpu_tag[3] & 0xe000) >> 13;
+				if ((reason != 7) && (reason != 8)) // NIC_RX_REASON_RMA_USR
+					skb->data[len-3] |= 0x40;
+			}
+			if (queue >= 2)
+				pr_debug("Queue: %d\n", queue);
+
 			skb->protocol = eth_type_trans(skb, dev);
 			dev->stats.rx_packets++;
 			dev->stats.rx_bytes += len;
@@ -865,6 +929,7 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
 		ring->rx_r[r][ring->c_rx[r]]
 			= KSEG1ADDR(h) | 0x1 | (ring->c_rx[r] == (RXRINGLEN-1) ? WRAP : 0x1);
 		ring->c_rx[r] = (ring->c_rx[r] + 1) % RXRINGLEN;
+		last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur(r)));
 	} while (&ring->rx_r[r][ring->c_rx[r]] != last && work_done < budget);
 
 	spin_unlock_irqrestore(&priv->lock, flags);
@@ -873,18 +938,23 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
 
 static int rtl838x_poll_rx(struct napi_struct *napi, int budget)
 {
-	struct rtl838x_eth_priv *priv = container_of(napi, struct rtl838x_eth_priv, napi);
-	int work_done = 0, r = 0;
-
-	while (work_done < budget && r < RXRINGS) {
-		work_done += rtl838x_hw_receive(priv->netdev, r, budget - work_done);
-		r++;
+	struct rtl838x_rx_q *rx_q = container_of(napi, struct rtl838x_rx_q, napi);
+	struct rtl838x_eth_priv *priv = rx_q->priv;
+	int work_done = 0;
+	int r = rx_q->id;
+	int work;
+
+	while (work_done < budget) {
+		work = rtl838x_hw_receive(priv->netdev, r, budget - work_done);
+		if (!work)
+			break;
+		work_done += work;
 	}
 
 	if (work_done < budget) {
 		napi_complete_done(napi, work_done);
 		/* Enable RX interrupt */
-		sw_w32_mask(0, 0xfffff, priv->r->dma_if_intr_msk);
+		sw_w32_mask(0, 0xf00ff | BIT(r + 8), priv->r->dma_if_intr_msk);
 	}
 	return work_done;
 }
@@ -1325,6 +1395,7 @@ static const struct net_device_ops rtl838x_eth_netdev_ops = {
 	.ndo_open = rtl838x_eth_open,
 	.ndo_stop = rtl838x_eth_stop,
 	.ndo_start_xmit = rtl838x_eth_tx,
+	.ndo_select_queue = rtl838x_pick_tx_queue,
 	.ndo_set_mac_address = rtl838x_set_mac_address,
 	.ndo_validate_addr = eth_validate_addr,
 	.ndo_set_rx_mode = rtl838x_eth_set_multicast_list,
@@ -1355,6 +1426,7 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
 	phy_interface_t phy_mode;
 	struct phylink *phylink;
 	int err = 0;
+	int i;
 
 	pr_info("Probing RTL838X eth device pdev: %x, dev: %x\n",
 		(u32)pdev, (u32)(&(pdev->dev)));
@@ -1364,7 +1436,7 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 
-	dev = alloc_etherdev(sizeof(struct rtl838x_eth_priv));
+	dev = alloc_etherdev_mqs(sizeof(struct rtl838x_eth_priv), TXRINGS, RXRINGS);
 	if (!dev) {
 		err = -ENOMEM;
 		goto err_free;
@@ -1412,6 +1484,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
 		dev->irq = res->start;
 	}
 	dev->ethtool_ops = &rtl838x_ethtool_ops;
+	dev->min_mtu = ETH_ZLEN;
+	dev->max_mtu = 1536;
 
 	priv->id = soc_info.id;
 	priv->family_id = soc_info.family;
@@ -1476,7 +1550,12 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
 	if (err)
 		goto err_free;
 
-	netif_napi_add(dev, &priv->napi, rtl838x_poll_rx, 64);
+	for (i = 0; i < RXRINGS; i++) {
+		priv->rx_qs[i].id = i;
+		priv->rx_qs[i].priv = priv;
+		netif_napi_add(dev, &priv->rx_qs[i].napi, rtl838x_poll_rx, 64);
+	}
+
 	platform_set_drvdata(pdev, dev);
 
 	phy_mode = of_get_phy_mode(dn);
@@ -1508,13 +1587,18 @@ static int rtl838x_eth_remove(struct platform_device *pdev)
 {
 	struct net_device *dev = platform_get_drvdata(pdev);
 	struct rtl838x_eth_priv *priv = netdev_priv(dev);
+	int i;
 
 	if (dev) {
 		pr_info("Removing platform driver for rtl838x-eth\n");
 		rtl838x_mdio_remove(priv);
 		rtl838x_hw_stop(priv);
-		netif_stop_queue(dev);
-		netif_napi_del(&priv->napi);
+
+		netif_tx_stop_all_queues(dev);
+
+		for (i = 0; i < RXRINGS; i++)
+			netif_napi_del(&priv->rx_qs[i].napi);
+
 		unregister_netdev(dev);
 		free_netdev(dev);
 	}
diff --git a/target/linux/realtek/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch b/target/linux/realtek/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch
index c8a09c50d3..07ee5307ae 100644
--- a/target/linux/realtek/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch
+++ b/target/linux/realtek/patches-5.4/701-net-dsa-add-rtl838x-support-for-tag-trailer.patch
@@ -5,25 +5,28 @@
  	trailer = skb_put(nskb, 4);
  	trailer[0] = 0x80;
 +
-+#ifdef CONFIG_NET_DSA_RTL83XX
++#ifdef CONFIG_NET_DSA_RTL838X
 +	trailer[1] = dp->index;
 +#else
  	trailer[1] = 1 << dp->index;
-+#endif /* CONFIG_NET_DSA_RTL83XX */
++#endif /* CONFIG_NET_DSA_RTL838X */
  	trailer[2] = 0x10;
  	trailer[3] = 0x00;
  
-@@ -61,12 +66,20 @@ static struct sk_buff *trailer_rcv(struc
+@@ -61,12 +69,23 @@ static struct sk_buff *trailer_rcv(struc
  		return NULL;
  
  	trailer = skb_tail_pointer(skb) - 4;
 +
-+#ifdef CONFIG_NET_DSA_RTL83XX
-+	if (trailer[0] != 0x80 || (trailer[1] & 0xe0) != 0x00 ||
++#ifdef CONFIG_NET_DSA_RTL838X
++	if (trailer[0] != 0x80 || (trailer[1] & 0x80) != 0x00 ||
 +	    (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00)
 +		return NULL;
 +
-+	source_port = trailer[1] & 0x1f;
++	if (!(trailer[1] & 0x40))
++		skb->offload_fwd_mark = 1;
++
++	source_port = trailer[1] & 0x3f;
 +#else
  	if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 ||
  	    (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00)



More information about the lede-commits mailing list