realtek: add QoS and rate control
authorBirger Koblitz <git@birger-koblitz.de>
Thu, 21 Jan 2021 13:29:52 +0000 (14:29 +0100)
committerPetr Štetiar <ynezz@true.cz>
Tue, 26 Jan 2021 14:06:50 +0000 (15:06 +0100)
This adds support for identifying QoS information in packets
and use this and rate control information to submit to multiple
egress queues. The ethernet driver is also made to support
2 egress and up to 32 egress queues.

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/Makefile
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/qos.c [new file with mode: 0644]
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.h
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl83xx.h
target/linux/realtek/files-5.4/drivers/net/ethernet/rtl838x_eth.c

index 52cc151a56a7a9374a7ebac1ef86bd3979f34fb3..016184c3d999b125c1d7f9f41bf6758722aea52a 100644 (file)
@@ -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 rtl930x.o rtl931x.o debugfs.o qos.o
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 (file)
index 0000000..2fc8d37
--- /dev/null
@@ -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);
+       
+}
index 1ebb4dff72e75d929e3f0755a17ada750ee076d3..d5ca153a103893081a0299a1f03eb6dc5abb0954 100644 (file)
@@ -8,14 +8,18 @@
 /*
  * Register definition
  */
-#define RTL838X_CPU_PORT                       28
-#define RTL839X_CPU_PORT                       52
-
 #define RTL838X_MAC_PORT_CTRL(port)            (0xd560 + (((port) << 7)))
 #define RTL839X_MAC_PORT_CTRL(port)            (0x8004 + (((port) << 7)))
+#define RTL930X_MAC_PORT_CTRL(port)            (0x3260 + (((port) << 6)))
+#define RTL930X_MAC_L2_PORT_CTRL(port)         (0x3268 + (((port) << 6)))
+#define RTL931X_MAC_PORT_CTRL(port)            (0x6004 + (((port) << 7)))
+
 #define RTL838X_RST_GLB_CTRL_0                 (0x003c)
+
 #define RTL838X_MAC_FORCE_MODE_CTRL            (0xa104)
 #define RTL839X_MAC_FORCE_MODE_CTRL            (0x02bc)
+#define RTL930X_MAC_FORCE_MODE_CTRL            (0xCA1C)
+#define RTL931X_MAC_FORCE_MODE_CTRL            (0x0DCC)
 
 #define RTL838X_DMY_REG31                      (0x3b28)
 #define RTL838X_SDS_MODE_SEL                   (0x0028)
 #define RTL838X_INT_MODE_CTRL                  (0x005c)
 #define RTL838X_CHIP_INFO                      (0x00d8)
 #define RTL839X_CHIP_INFO                      (0x0ff4)
-#define RTL838X_SDS4_REG28                     (0xef80)
-#define RTL838X_SDS4_DUMMY0                    (0xef8c)
-#define RTL838X_SDS5_EXT_REG6                  (0xf18c)
 #define RTL838X_PORT_ISO_CTRL(port)            (0x4100 + ((port) << 2))
 #define RTL839X_PORT_ISO_CTRL(port)            (0x1400 + ((port) << 3))
-#define RTL8380_SDS4_FIB_REG0                  (0xF800)
+
+/* Packet statistics */
 #define RTL838X_STAT_PORT_STD_MIB              (0x1200)
 #define RTL839X_STAT_PORT_STD_MIB              (0xC000)
+#define RTL930X_STAT_PORT_MIB_CNTR             (0x0664)
 #define RTL838X_STAT_RST                       (0x3100)
 #define RTL839X_STAT_RST                       (0xF504)
+#define RTL930X_STAT_RST                       (0x3240)
+#define RTL931X_STAT_RST                       (0x7ef4)
 #define RTL838X_STAT_PORT_RST                  (0x3104)
 #define RTL839X_STAT_PORT_RST                  (0xF508)
+#define RTL930X_STAT_PORT_RST                  (0x3244)
+#define RTL931X_STAT_PORT_RST                  (0x7ef8)
 #define RTL838X_STAT_CTRL                      (0x3108)
 #define RTL839X_STAT_CTRL                      (0x04cc)
+#define RTL930X_STAT_CTRL                      (0x3248)
+#define RTL931X_STAT_CTRL                      (0x5720)
 
 /* Registers of the internal Serdes of the 8390 */
 #define RTL8390_SDS0_1_XSG0                    (0xA000)
 #define RTL839X_SDS12_13_PWR1                  (0xb980)
 
 /* Registers of the internal Serdes of the 8380 */
-#define MAPLE_SDS4_REG0r                       RTL838X_SDS4_REG28
-#define MAPLE_SDS5_REG0r                       (RTL838X_SDS4_REG28 + 0x100)
-#define MAPLE_SDS4_REG3r                       RTL838X_SDS4_DUMMY0
-#define MAPLE_SDS5_REG3r                       (RTL838X_SDS4_REG28 + 0x100)
-#define MAPLE_SDS4_FIB_REG0r                   (RTL838X_SDS4_REG28 + 0x880)
-#define MAPLE_SDS5_FIB_REG0r                   (RTL838X_SDS4_REG28 + 0x980)
+#define RTL838X_SDS4_FIB_REG0                  (0xF800)
+#define RTL838X_SDS4_REG28                     (0xef80)
+#define RTL838X_SDS4_DUMMY0                    (0xef8c)
+#define RTL838X_SDS5_EXT_REG6                  (0xf18c)
 
 /* 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))
+#define RTL838X_VLAN_PORT_PB_VLAN              (0x3C00)
 #define RTL838X_VLAN_PORT_IGR_FLTR(port)       (0x3A7C + (((port >> 4) << 2)))
 #define RTL838X_VLAN_PORT_IGR_FLTR_0           (0x3A7C)
 #define RTL838X_VLAN_PORT_IGR_FLTR_1           (0x3A7C + 4)
-#define RTL838X_VLAN_PORT_TAG_STS_CTRL(port)   (0xA530 + (((port) << 2)))
+#define RTL838X_VLAN_PORT_TAG_STS_CTRL         (0xA530)
+
 #define RTL839X_VLAN_PROFILE(idx)              (0x25C0 + (((idx) << 3)))
 #define RTL839X_VLAN_CTRL                      (0x26D4)
-#define RTL839X_VLAN_PORT_PB_VLAN(port)                (0x26D8 + (((port) << 2)))
+#define RTL839X_VLAN_PORT_PB_VLAN              (0x26D8)
 #define RTL839X_VLAN_PORT_IGR_FLTR(port)       (0x27B4 + (((port >> 4) << 2)))
 #define RTL839X_VLAN_PORT_EGR_FLTR(port)       (0x27C4 + (((port >> 5) << 2)))
-#define RTL839X_VLAN_PORT_TAG_STS_CTRL(port)   (0x6828 + (((port) << 2)))
+#define RTL839X_VLAN_PORT_TAG_STS_CTRL         (0x6828)
+
+#define RTL930X_VLAN_PROFILE_SET(idx)          (0x9c60 + (((idx) * 20)))
+#define RTL930X_VLAN_CTRL                      (0x82D4)
+#define RTL930X_VLAN_PORT_PB_VLAN              (0x82D8)
+#define RTL930X_VLAN_PORT_IGR_FLTR(port)       (0x83C0 + (((port >> 4) << 2)))
+#define RTL930X_VLAN_PORT_EGR_FLTR             (0x83C8)
+#define RTL930X_VLAN_PORT_TAG_STS_CTRL         (0xCE24)
+
+#define RTL931X_VLAN_PROFILE_SET(idx)          (0x9800 + (((idx) * 28)))
+#define RTL931X_VLAN_CTRL                      (0x94E4)
+#define RTL931X_VLAN_PORT_IGR_FLTR(port)       (0x96B4 + (((port >> 4) << 2)))
+#define RTL931X_VLAN_PORT_EGR_FLTR(port)       (0x96C4 + (((port >> 5) << 2)))
+#define RTL931X_VLAN_PORT_TAG_CTRL             (0x4860)
 
-/* Table 0/1 access registers */
+/* Table access registers */
 #define RTL838X_TBL_ACCESS_CTRL_0              (0x6914)
 #define RTL838X_TBL_ACCESS_DATA_0(idx)         (0x6918 + ((idx) << 2))
 #define RTL838X_TBL_ACCESS_CTRL_1              (0xA4C8)
 #define RTL838X_TBL_ACCESS_DATA_1(idx)         (0xA4CC + ((idx) << 2))
+
 #define RTL839X_TBL_ACCESS_CTRL_0              (0x1190)
 #define RTL839X_TBL_ACCESS_DATA_0(idx)         (0x1194 + ((idx) << 2))
 #define RTL839X_TBL_ACCESS_CTRL_1              (0x6b80)
 #define RTL839X_TBL_ACCESS_DATA_1(idx)         (0x6b84 + ((idx) << 2))
+#define RTL839X_TBL_ACCESS_CTRL_2              (0x611C)
+#define RTL839X_TBL_ACCESS_DATA_2(i)           (0x6120 + (((i) << 2)))
+
+#define RTL930X_TBL_ACCESS_CTRL_0              (0xB340)
+#define RTL930X_TBL_ACCESS_DATA_0(idx)         (0xB344 + ((idx) << 2))
+#define RTL930X_TBL_ACCESS_CTRL_1              (0xB3A0)
+#define RTL930X_TBL_ACCESS_DATA_1(idx)         (0xB3A4 + ((idx) << 2))
+#define RTL930X_TBL_ACCESS_CTRL_2              (0xCE04)
+#define RTL930X_TBL_ACCESS_DATA_2(i)           (0xCE08 + (((i) << 2)))
+
+#define RTL931X_TBL_ACCESS_CTRL_0              (0x8500)
+#define RTL931X_TBL_ACCESS_DATA_0(idx)         (0x8508 + ((idx) << 2))
+#define RTL931X_TBL_ACCESS_CTRL_1              (0x40C0)
+#define RTL931X_TBL_ACCESS_DATA_1(idx)         (0x40C4 + ((idx) << 2))
+#define RTL931X_TBL_ACCESS_CTRL_2              (0x8528)
+#define RTL931X_TBL_ACCESS_DATA_2(i)           (0x852C + (((i) << 2)))
+#define RTL931X_TBL_ACCESS_CTRL_3              (0x0200)
+#define RTL931X_TBL_ACCESS_DATA_3(i)           (0x0204 + (((i) << 2)))
+#define RTL931X_TBL_ACCESS_CTRL_4              (0x20DC)
+#define RTL931X_TBL_ACCESS_DATA_4(i)           (0x20E0 + (((i) << 2)))
+#define RTL931X_TBL_ACCESS_CTRL_5              (0x7E1C)
+#define RTL931X_TBL_ACCESS_DATA_5(i)           (0x7E20 + (((i) << 2)))
 
 /* MAC handling */
 #define RTL838X_MAC_LINK_STS                   (0xa188)
 #define RTL839X_MAC_LINK_STS                   (0x0390)
-#define RTL838X_MAC_LINK_SPD_STS(port)         (0xa190 + (((port >> 4) << 2)))
-#define RTL839X_MAC_LINK_SPD_STS(port)         (0x03a0 + (((port >> 4) << 2)))
+#define RTL930X_MAC_LINK_STS                   (0xCB10)
+#define RTL931X_MAC_LINK_STS                   (0x0EC0)
+#define RTL838X_MAC_LINK_SPD_STS(p)            (0xa190 + (((p >> 4) << 2)))
+#define RTL839X_MAC_LINK_SPD_STS(p)            (0x03a0 + (((p >> 4) << 2)))
+#define RTL930X_MAC_LINK_SPD_STS(p)            (0xCB18 + (((p >> 3) << 2)))
+#define RTL931X_MAC_LINK_SPD_STS(p)            (0x0ED0 + (((p >> 3) << 2)))
 #define RTL838X_MAC_LINK_DUP_STS               (0xa19c)
 #define RTL839X_MAC_LINK_DUP_STS               (0x03b0)
+#define RTL930X_MAC_LINK_DUP_STS               (0xCB28)
+#define RTL931X_MAC_LINK_DUP_STS               (0x0EF0)
 #define RTL838X_MAC_TX_PAUSE_STS               (0xa1a0)
 #define RTL839X_MAC_TX_PAUSE_STS               (0x03b8)
+#define RTL930X_MAC_TX_PAUSE_STS               (0xCB2C)
+#define RTL931X_MAC_TX_PAUSE_STS               (0x0EF8)
 #define RTL838X_MAC_RX_PAUSE_STS               (0xa1a4)
 #define RTL839X_MAC_RX_PAUSE_STS               (0x03c0)
-#define RTL838X_EEE_TX_TIMER_GIGA_CTRL         (0xaa04)
-#define RTL838X_EEE_TX_TIMER_GELITE_CTRL       (0xaa08)
-
-#define RTL838X_DMA_IF_CTRL                    (0x9f58)
+#define RTL930X_MAC_RX_PAUSE_STS               (0xCB30)
+#define RTL931X_MAC_RX_PAUSE_STS               (0x0F00)
 
 /* MAC link state bits */
 #define FORCE_EN                               (1 << 0)
 #define RTL838X_EEE_PORT_TX_EN                 (0x014c)
 #define RTL838X_EEE_PORT_RX_EN                 (0x0150)
 #define RTL838X_EEE_CLK_STOP_CTRL              (0x0148)
+#define RTL838X_EEE_TX_TIMER_GIGA_CTRL         (0xaa04)
+#define RTL838X_EEE_TX_TIMER_GELITE_CTRL       (0xaa08)
 
 /* L2 functionality */
 #define RTL838X_L2_CTRL_0                      (0x3200)
 #define RTL839X_L2_CTRL_0                      (0x3800)
+#define RTL930X_L2_CTRL                                (0x8FD8)
+#define RTL931X_L2_CTRL                                (0xC800)
 #define RTL838X_L2_CTRL_1                      (0x3204)
 #define RTL839X_L2_CTRL_1                      (0x3804)
+#define RTL930X_L2_AGE_CTRL                    (0x8FDC)
+#define RTL931X_L2_AGE_CTRL                    (0xC804)
 #define RTL838X_L2_PORT_AGING_OUT              (0x3358)
 #define RTL839X_L2_PORT_AGING_OUT              (0x3b74)
+#define        RTL930X_L2_PORT_AGE_CTRL                (0x8FE0)
+#define        RTL931X_L2_PORT_AGE_CTRL                (0xc808)
 #define RTL838X_TBL_ACCESS_L2_CTRL             (0x6900)
 #define RTL839X_TBL_ACCESS_L2_CTRL             (0x1180)
+#define RTL930X_TBL_ACCESS_L2_CTRL             (0xB320)
+#define RTL930X_TBL_ACCESS_L2_METHOD_CTRL      (0xB324)
 #define RTL838X_TBL_ACCESS_L2_DATA(idx)                (0x6908 + ((idx) << 2))
 #define RTL839X_TBL_ACCESS_L2_DATA(idx)                (0x1184 + ((idx) << 2))
+#define RTL930X_TBL_ACCESS_L2_DATA(idx)                (0xab08 + ((idx) << 2))
 #define RTL838X_L2_TBL_FLUSH_CTRL              (0x3370)
 #define RTL839X_L2_TBL_FLUSH_CTRL              (0x3ba0)
+#define RTL930X_L2_TBL_FLUSH_CTRL              (0x9404)
+#define RTL931X_L2_TBL_FLUSH_CTRL              (0xCD9C)
+
 #define RTL838X_L2_PORT_NEW_SALRN(p)           (0x328c + (((p >> 4) << 2)))
 #define RTL839X_L2_PORT_NEW_SALRN(p)           (0x38F0 + (((p >> 4) << 2)))
+#define RTL930X_L2_PORT_SALRN(p)               (0x8FEC + (((p >> 4) << 2)))
+#define RTL931X_L2_PORT_NEW_SALRN(p)           (0xC820 + (((p >> 4) << 2)))
 #define RTL838X_L2_PORT_NEW_SA_FWD(p)          (0x3294 + (((p >> 4) << 2)))
 #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 RTL930X_L2_PORT_NEW_SA_FWD(p)          (0x8FF4 + (((p / 10) << 2)))
+#define RTL931X_L2_PORT_NEW_SA_FWD(p)          (0xC830 + (((p / 10) << 2)))
+
+#define RTL930X_ST_CTRL                                (0x8798)
+
+#define RTL930X_L2_PORT_SABLK_CTRL             (0x905c)
+#define RTL930X_L2_PORT_DABLK_CTRL             (0x9060)
+
+#define RTL838X_RMA_BPDU_FLD_PMSK              (0x4348)
+#define RTL930X_RMA_BPDU_FLD_PMSK              (0x9F18)
+#define RTL931X_RMA_BPDU_FLD_PMSK              (0x8950)
+#define RTL839X_RMA_BPDU_FLD_PMSK              (0x125C)
 
 /* Port Mirroring */
-#define RTL838X_MIR_CTRL(grp)                  (0x5D00 + (((grp) << 2)))
-#define RTL838X_MIR_DPM_CTRL(grp)              (0x5D20 + (((grp) << 2)))
-#define RTL838X_MIR_SPM_CTRL(grp)              (0x5D10 + (((grp) << 2)))
-#define RTL839X_MIR_CTRL(grp)                  (0x2500 + (((grp) << 2)))
-#define RTL839X_MIR_DPM_CTRL(grp)              (0x2530 + (((grp) << 2)))
-#define RTL839X_MIR_SPM_CTRL(grp)              (0x2510 + (((grp) << 2)))
-
-/* Storm control */
+#define RTL838X_MIR_CTRL                       (0x5D00)
+#define RTL838X_MIR_DPM_CTRL                   (0x5D20)
+#define RTL838X_MIR_SPM_CTRL                   (0x5D10)
+
+#define RTL839X_MIR_CTRL                       (0x2500)
+#define RTL839X_MIR_DPM_CTRL                   (0x2530)
+#define RTL839X_MIR_SPM_CTRL                   (0x2510)
+
+#define RTL930X_MIR_CTRL                       (0xA2A0)
+#define RTL930X_MIR_DPM_CTRL                   (0xA2C0)
+#define RTL930X_MIR_SPM_CTRL                   (0xA2B0)
+
+#define RTL931X_MIR_CTRL                       (0xAF00)
+#define RTL931X_MIR_DPM_CTRL                   (0xAF30)
+#define RTL931X_MIR_SPM_CTRL                   (0xAF10)
+
+/* 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)))
 #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)
 #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)
+#define RTL930X_TRK_MBR_CTRL                   (0xA41C)
+#define RTL931X_TRK_MBR_CTRL                   (0xB8D0)
 
 /* Attack prevention */
 #define RTL838X_ATK_PRVNT_PORT_EN              (0x5B00)
 #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)))
+
+/* Debug features */
+#define RTL930X_STAT_PRVTE_DROP_COUNTER0       (0xB5B8)
+
+#define MAX_LAGS 16
+#define MAX_PRIOS 8
+
 enum phy_type {
        PHY_NONE = 0,
        PHY_RTL838X_SDS = 1,
@@ -182,6 +340,9 @@ struct rtl838x_port {
        u16 pvid;
        bool eee_enabled;
        enum phy_type phy;
+       bool is10G;
+       bool is2G5;
+       u8 sds_num;
        const struct dsa_port *dp;
 };
 
@@ -217,6 +378,8 @@ struct rtl838x_l2_entry {
        bool suspended;
        bool next_hop;
        int age;
+       u8 trunk;
+       u8 stackDev;
        u16 mc_portmask_index;
 };
 
@@ -231,8 +394,12 @@ struct rtl838x_reg {
        u64 (*get_port_reg_le)(int reg);
        int stat_port_rst;
        int stat_rst;
-       int (*stat_port_std_mib)(int p);
+       int stat_port_std_mib;
        int (*port_iso_ctrl)(int p);
+       void (*traffic_enable)(int source, int dest);
+       void (*traffic_disable)(int source, int dest);
+       void (*traffic_set)(int source, u64 dest_matrix);
+       u64 (*traffic_get)(int source);
        int l2_ctrl_0;
        int l2_ctrl_1;
        int l2_port_aging_out;
@@ -248,13 +415,16 @@ struct rtl838x_reg {
        void (*vlan_tables_read)(u32 vlan, struct rtl838x_vlan_info *info);
        void (*vlan_set_tagged)(u32 vlan, struct rtl838x_vlan_info *info);
        void (*vlan_set_untagged)(u32 vlan, u64 portmask);
+       void (*vlan_profile_dump)(int index);
+       void (*stp_get)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]);
+       void (*stp_set)(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[]);
        int  (*mac_force_mode_ctrl)(int port);
        int  (*mac_port_ctrl)(int port);
        int  (*l2_port_new_salrn)(int port);
        int  (*l2_port_new_sa_fwd)(int port);
-       int  (*mir_ctrl)(int group);
-       int  (*mir_dpm)(int group);
-       int  (*mir_spm)(int group);
+       int mir_ctrl;
+       int mir_dpm;
+       int mir_spm;
        int mac_link_sts;
        int mac_link_dup_sts;
        int  (*mac_link_spd_sts)(int port);
@@ -262,11 +432,14 @@ struct rtl838x_reg {
        int mac_tx_pause_sts;
        u64 (*read_l2_entry_using_hash)(u32 hash, u32 position, struct rtl838x_l2_entry *e);
        u64 (*read_cam)(int idx, struct rtl838x_l2_entry *e);
-       int (*vlan_profile)(int profile);
-       int (*vlan_port_egr_filter)(int port);
-       int (*vlan_port_igr_filter)(int port);
-       int (*vlan_port_pb)(int port);
-       int (*vlan_port_tag_sts_ctrl)(int port);
+       int vlan_port_egr_filter;
+       int vlan_port_igr_filter;
+       int vlan_port_pb;
+       int vlan_port_tag_sts_ctrl;
+       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 {
@@ -276,7 +449,7 @@ struct rtl838x_switch_priv {
        u16 id;
        u16 family_id;
        char version;
-       struct rtl838x_port ports[54]; /* TODO: correct size! */
+       struct rtl838x_port ports[57];
        struct mutex reg_mutex;
        int link_state_irq;
        int mirror_group_ports[4];
@@ -284,8 +457,14 @@ struct rtl838x_switch_priv {
        const struct rtl838x_reg *r;
        u8 cpu_port;
        u8 port_mask;
+       u8 port_width;
+       u64 irq_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);
index 8dd123f609f1c1a7403ef5306d3b45ed195a1319..5106bd2e9d3991acc75e7ccb61dd70a930f21cbd 100644 (file)
@@ -4,53 +4,18 @@
 #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)
-{
-       sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg);
-       sw_w32_mask((u32)(clear & 0xffffffff), (u32)(set & 0xffffffff), reg + 4);
-}
-
-static inline u64 rtl839x_get_port_reg_be(int reg)
+void rtl839x_print_matrix(void)
 {
-       u64 v = sw_r32(reg);
-
-       v <<= 32;
-       v |= sw_r32(reg + 4);
-       return v;
-}
-
-static inline void rtl839x_set_port_reg_be(u64 set, int reg)
-{
-       sw_w32(set >> 32, reg);
-       sw_w32(set & 0xffffffff, reg + 4);
-}
-
-static inline void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg)
-{
-       sw_w32_mask((u32)clear, (u32)set, reg);
-       sw_w32_mask((u32)(clear >> 32), (u32)(set >> 32), reg + 4);
-}
-
-static inline void rtl839x_set_port_reg_le(u64 set, int reg)
-{
-       sw_w32(set, reg);
-       sw_w32(set >> 32, reg + 4);
-}
-
-static inline u64 rtl839x_get_port_reg_le(int reg)
-{
-       u64 v = sw_r32(reg + 4);
-
-       v <<= 32;
-       v |= sw_r32(reg);
-       return v;
-}
+       volatile u64 *ptr9;
+       int i;
 
-static inline int rtl839x_stat_port_std_mib(int p)
-{
-       return RTL839X_STAT_PORT_STD_MIB + (p << 8);
+       ptr9 = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0);
+       for (i = 0; i < 52; i += 4)
+               pr_debug("> %16llx %16llx %16llx %16llx\n",
+                       ptr9[i + 0], ptr9[i + 1], ptr9[i + 2], ptr9[i + 3]);
+       pr_debug("CPU_PORT> %16llx\n", ptr9[52]);
 }
 
 static inline int rtl839x_port_iso_ctrl(int p)
@@ -70,6 +35,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);
@@ -81,33 +52,33 @@ static void rtl839x_vlan_tables_read(u32 vlan, struct rtl838x_vlan_info *info)
        u64 v;
        u32 u, w;
 
-       cmd = BIT(16) /* Execute cmd */
+       cmd = 1 << 16 /* Execute cmd */
                | 0 << 15 /* Read */
                | 0 << 12 /* Table type 0b000 */
                | (vlan & 0xfff);
        rtl839x_exec_tbl0_cmd(cmd);
 
-       v = sw_r32(RTL838X_TBL_ACCESS_DATA_0(0));
+       v = sw_r32(RTL839X_TBL_ACCESS_DATA_0(0));
        v <<= 32;
-       u = sw_r32(RTL838X_TBL_ACCESS_DATA_0(1));
+       u = sw_r32(RTL839X_TBL_ACCESS_DATA_0(1));
        v |= u;
        info->tagged_ports = v >> 11;
 
-       w = sw_r32(RTL838X_TBL_ACCESS_DATA_0(2));
+       w = sw_r32(RTL839X_TBL_ACCESS_DATA_0(2));
 
        info->profile_id = w >> 30 | ((u & 1) << 2);
        info->hash_mc_fid = !!(u & 2);
        info->hash_uc_fid = !!(u & 4);
        info->fid = (u >> 3) & 0xff;
 
-       cmd = BIT(16) /* Execute cmd */
-               | 0 << 15 /* Read */
-               | 0 << 12 /* Table type 0b000 */
+       cmd = 1 << 15 /* Execute cmd */
+               | 0 << 14 /* Read */
+               | 0 << 12 /* Table type 0b00 */
                | (vlan & 0xfff);
        rtl839x_exec_tbl1_cmd(cmd);
-       v = sw_r32(RTL838X_TBL_ACCESS_DATA_1(0));
+       v = sw_r32(RTL839X_TBL_ACCESS_DATA_1(0));
        v <<= 32;
-       v |= sw_r32(RTL838X_TBL_ACCESS_DATA_1(1));
+       v |= sw_r32(RTL839X_TBL_ACCESS_DATA_1(1));
        info->untagged_ports = v >> 11;
 }
 
@@ -161,45 +132,18 @@ static inline int rtl839x_l2_port_new_sa_fwd(int p)
        return RTL839X_L2_PORT_NEW_SA_FWD(p);
 }
 
-static inline int rtl839x_mir_ctrl(int group)
-{
-       return RTL839X_MIR_CTRL(group);
-}
-
-static inline int rtl839x_mir_dpm(int group)
-{
-       return RTL839X_MIR_DPM_CTRL(group);
-}
-
-static inline int rtl839x_mir_spm(int group)
-{
-       return RTL839X_MIR_SPM_CTRL(group);
-}
-
 static inline int rtl839x_mac_link_spd_sts(int p)
 {
        return RTL839X_MAC_LINK_SPD_STS(p);
 }
 
-static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
+static inline int rtl839x_trk_mbr_ctr(int group)
 {
-       u64 entry;
-       u32 r[3];
-
-       /* Search in SRAM, with hash and at position in hash bucket (0-3) */
-       u32 idx = (0 << 14) | (hash << 2) | position;
-
-       u32 cmd = BIT(17) /* Execute cmd */
-               | 0 << 16 /* Read */
-               | 0 << 14 /* Table type 0b00 */
-               | (idx & 0x3fff);
-
-       sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL);
-       do { }  while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & BIT(17));
-       r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0));
-       r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1));
-       r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2));
+       return RTL839X_TRK_MBR_CTR + (group << 3);
+}
 
+static void rtl839x_fill_l2_entry(u32 r[], struct rtl838x_l2_entry *e)
+{
        /* Table contains different entry types, we need to identify the right one:
         * Check for MC entries, first
         */
@@ -220,12 +164,12 @@ static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl83
                        e->vid = (r[2] >> 4) & 0xfff;
                        e->rvid = (r[0] >> 20) & 0xfff;
                        e->port = (r[2] >> 24) & 0x3f;
-                       e->block_da = !!(r[2] & BIT(19));
-                       e->block_sa = !!(r[2] & BIT(20));
-                       e->suspended = !!(r[2] & BIT(17));
-                       e->next_hop = !!(r[2] & BIT(16));
+                       e->block_da = !!(r[2] & (1 << 19));
+                       e->block_sa = !!(r[2] & (1 << 20));
+                       e->suspended = !!(r[2] & (1 << 17));
+                       e->next_hop = !!(r[2] & (1 << 16));
                        if (e->next_hop)
-                               pr_debug("Found next hop entry, need to read data\n");
+                               pr_info("Found next hop entry, need to read data\n");
                        e->age = (r[2] >> 21) & 3;
                        e->valid = true;
                        if (!(r[2] & 0xc0fd0000)) /* Check for valid entry */
@@ -246,6 +190,28 @@ static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl83
                e->valid = true;
                e->type = IP6_MULTICAST;
        }
+}
+
+static u64 rtl839x_read_l2_entry_using_hash(u32 hash, u32 position, struct rtl838x_l2_entry *e)
+{
+       u64 entry;
+       u32 r[3];
+
+       /* Search in SRAM, with hash and at position in hash bucket (0-3) */
+       u32 idx = (0 << 14) | (hash << 2) | position;
+
+       u32 cmd = 1 << 17 /* Execute cmd */
+               | 0 << 16 /* Read */
+               | 0 << 14 /* Table type 0b00 */
+               | (idx & 0x3fff);
+
+       sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL);
+       do { }  while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17));
+       r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0));
+       r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1));
+       r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2));
+
+       rtl839x_fill_l2_entry(r, e);
 
        entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff);
        return entry;
@@ -256,33 +222,22 @@ static u64 rtl839x_read_cam(int idx, struct rtl838x_l2_entry *e)
        u64 entry;
        u32 r[3];
 
-       u32 cmd = BIT(17) /* Execute cmd */
+       u32 cmd = 1 << 17 /* Execute cmd */
                | 0 << 16 /* Read */
-               | BIT(14) /* Table type 0b01 */
+               | 1 << 14 /* Table type 0b01 */
                | (idx & 0x3f);
        sw_w32(cmd, RTL839X_TBL_ACCESS_L2_CTRL);
-       do { }  while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & BIT(17));
+       do { }  while (sw_r32(RTL839X_TBL_ACCESS_L2_CTRL) & (1 << 17));
        r[0] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(0));
        r[1] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(1));
        r[2] = sw_r32(RTL839X_TBL_ACCESS_L2_DATA(2));
 
-       e->mac[0] = (r[0] >> 12);
-       e->mac[1] = (r[0] >> 4);
-       e->mac[2] = ((r[1] >> 28) | (r[0] << 4));
-       e->mac[3] = (r[1] >> 20);
-       e->mac[4] = (r[1] >> 12);
-       e->mac[5] = (r[1] >> 4);
-       e->is_static = !!((r[2] >> 18) & 1);
-       e->vid = (r[2] >> 4) & 0xfff;
-       e->rvid = (r[0] >> 20) & 0xfff;
-       e->port = (r[2] >> 24) & 0x3f;
-
-       e->valid = true;
-       if (!(r[2] & 0x10fd0000)) /* Check for invalid entry */
-               e->valid = false;
 
+       rtl839x_fill_l2_entry(r, e);
        if (e->valid)
-               pr_debug("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
+               pr_info("Found in CAM: R1 %x R2 %x R3 %x\n", r[0], r[1], r[2]);
+       else
+               return 0;
 
        entry = (((u64) r[0]) << 12) | ((r[1] & 0xfffffff0) << 12) | ((r[2] >> 4) & 0xfff);
        return entry;
@@ -303,62 +258,25 @@ static inline int rtl839x_vlan_port_igr_filter(int port)
        return RTL839X_VLAN_PORT_IGR_FLTR(port);
 }
 
-static inline int rtl839x_vlan_port_pb(int port)
+u64 rtl839x_traffic_get(int source)
 {
-       return RTL839X_VLAN_PORT_PB_VLAN(port);
+       return rtl839x_get_port_reg_be(rtl839x_port_iso_ctrl(source));
 }
 
-static inline int rtl839x_vlan_port_tag_sts_ctrl(int port)
+void rtl839x_traffic_set(int source, u64 dest_matrix)
 {
-       return RTL839X_VLAN_PORT_TAG_STS_CTRL(port);
+       rtl839x_set_port_reg_be(dest_matrix, rtl839x_port_iso_ctrl(source));
 }
 
-const struct rtl838x_reg rtl839x_reg = {
-       .mask_port_reg_be = rtl839x_mask_port_reg_be,
-       .set_port_reg_be = rtl839x_set_port_reg_be,
-       .get_port_reg_be = rtl839x_get_port_reg_be,
-       .mask_port_reg_le = rtl839x_mask_port_reg_le,
-       .set_port_reg_le = rtl839x_set_port_reg_le,
-       .get_port_reg_le = rtl839x_get_port_reg_le,
-       .stat_port_rst = RTL839X_STAT_PORT_RST,
-       .stat_rst = RTL839X_STAT_RST,
-       .stat_port_std_mib = rtl839x_stat_port_std_mib,
-       .port_iso_ctrl = rtl839x_port_iso_ctrl,
-       .l2_ctrl_0 = RTL839X_L2_CTRL_0,
-       .l2_ctrl_1 = RTL839X_L2_CTRL_1,
-       .l2_port_aging_out = RTL839X_L2_PORT_AGING_OUT,
-       .smi_poll_ctrl = RTL839X_SMI_PORT_POLLING_CTRL,
-       .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL,
-       .exec_tbl0_cmd = rtl839x_exec_tbl0_cmd,
-       .exec_tbl1_cmd = rtl839x_exec_tbl1_cmd,
-       .tbl_access_data_0 = rtl839x_tbl_access_data_0,
-       .isr_glb_src = RTL839X_ISR_GLB_SRC,
-       .isr_port_link_sts_chg = RTL839X_ISR_PORT_LINK_STS_CHG,
-       .imr_port_link_sts_chg = RTL839X_IMR_PORT_LINK_STS_CHG,
-       .imr_glb = RTL839X_IMR_GLB,
-       .vlan_tables_read = rtl839x_vlan_tables_read,
-       .vlan_set_tagged = rtl839x_vlan_set_tagged,
-       .vlan_set_untagged = rtl839x_vlan_set_untagged,
-       .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl,
-       .mac_port_ctrl = rtl839x_mac_port_ctrl,
-       .l2_port_new_salrn = rtl839x_l2_port_new_salrn,
-       .l2_port_new_sa_fwd = rtl839x_l2_port_new_sa_fwd,
-       .mir_ctrl = rtl839x_mir_ctrl,
-       .mir_dpm = rtl839x_mir_dpm,
-       .mir_spm = rtl839x_mir_spm,
-       .mac_link_sts = RTL839X_MAC_LINK_STS,
-       .mac_link_dup_sts = RTL839X_MAC_LINK_DUP_STS,
-       .mac_link_spd_sts = rtl839x_mac_link_spd_sts,
-       .mac_rx_pause_sts = RTL839X_MAC_RX_PAUSE_STS,
-       .mac_tx_pause_sts = RTL839X_MAC_TX_PAUSE_STS,
-       .read_l2_entry_using_hash = rtl839x_read_l2_entry_using_hash,
-       .read_cam = rtl839x_read_cam,
-       .vlan_profile = rtl839x_vlan_profile,
-       .vlan_port_egr_filter = rtl839x_vlan_port_egr_filter,
-       .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,
-};
+void rtl839x_traffic_enable(int source, int dest)
+{
+       rtl839x_mask_port_reg_be(0, BIT_ULL(dest), rtl839x_port_iso_ctrl(source));
+}
+
+void rtl839x_traffic_disable(int source, int dest)
+{
+       rtl839x_mask_port_reg_be(BIT(dest), 0, rtl839x_port_iso_ctrl(source));
+}
 
 irqreturn_t rtl839x_switch_irq(int irq, void *dev_id)
 {
@@ -512,3 +430,84 @@ void rtl839x_vlan_profile_dump(int index)
                index, profile & 1, (profile >> 1) & 0xfff, (profile >> 13) & 0xfff,
                (profile1) & 0xfff);
 }
+
+static void rtl839x_stp_get(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
+{
+       int i;
+       u32 cmd = 1 << 16 /* Execute cmd */
+               | 0 << 15 /* Read */
+               | 5 << 12 /* Table type 0b101 */
+               | (msti & 0xfff);
+       priv->r->exec_tbl0_cmd(cmd);
+
+       for (i = 0; i < 4; i++)
+               port_state[i] = sw_r32(priv->r->tbl_access_data_0(i));
+}
+
+static void rtl839x_stp_set(struct rtl838x_switch_priv *priv, u16 msti, u32 port_state[])
+{
+       int i;
+       u32 cmd = 1 << 16 /* Execute cmd */
+               | 1 << 15 /* Write */
+               | 5 << 12 /* Table type 0b101 */
+               | (msti & 0xfff);
+       for (i = 0; i < 4; i++)
+               sw_w32(port_state[i], priv->r->tbl_access_data_0(i));
+       priv->r->exec_tbl0_cmd(cmd);
+}
+
+const struct rtl838x_reg rtl839x_reg = {
+       .mask_port_reg_be = rtl839x_mask_port_reg_be,
+       .set_port_reg_be = rtl839x_set_port_reg_be,
+       .get_port_reg_be = rtl839x_get_port_reg_be,
+       .mask_port_reg_le = rtl839x_mask_port_reg_le,
+       .set_port_reg_le = rtl839x_set_port_reg_le,
+       .get_port_reg_le = rtl839x_get_port_reg_le,
+       .stat_port_rst = RTL839X_STAT_PORT_RST,
+       .stat_rst = RTL839X_STAT_RST,
+       .stat_port_std_mib = RTL839X_STAT_PORT_STD_MIB,
+       .traffic_enable = rtl839x_traffic_enable,
+       .traffic_disable = rtl839x_traffic_disable,
+       .traffic_get = rtl839x_traffic_get,
+       .traffic_set = rtl839x_traffic_set,
+       .port_iso_ctrl = rtl839x_port_iso_ctrl,
+       .l2_ctrl_0 = RTL839X_L2_CTRL_0,
+       .l2_ctrl_1 = RTL839X_L2_CTRL_1,
+       .l2_port_aging_out = RTL839X_L2_PORT_AGING_OUT,
+       .smi_poll_ctrl = RTL839X_SMI_PORT_POLLING_CTRL,
+       .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL,
+       .exec_tbl0_cmd = rtl839x_exec_tbl0_cmd,
+       .exec_tbl1_cmd = rtl839x_exec_tbl1_cmd,
+       .tbl_access_data_0 = rtl839x_tbl_access_data_0,
+       .isr_glb_src = RTL839X_ISR_GLB_SRC,
+       .isr_port_link_sts_chg = RTL839X_ISR_PORT_LINK_STS_CHG,
+       .imr_port_link_sts_chg = RTL839X_IMR_PORT_LINK_STS_CHG,
+       .imr_glb = RTL839X_IMR_GLB,
+       .vlan_tables_read = rtl839x_vlan_tables_read,
+       .vlan_set_tagged = rtl839x_vlan_set_tagged,
+       .vlan_set_untagged = rtl839x_vlan_set_untagged,
+       .vlan_profile_dump = rtl839x_vlan_profile_dump,
+       .stp_get = rtl839x_stp_get,
+       .stp_set = rtl839x_stp_set,
+       .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl,
+       .mac_port_ctrl = rtl839x_mac_port_ctrl,
+       .l2_port_new_salrn = rtl839x_l2_port_new_salrn,
+       .l2_port_new_sa_fwd = rtl839x_l2_port_new_sa_fwd,
+       .mir_ctrl = RTL839X_MIR_CTRL,
+       .mir_dpm = RTL839X_MIR_DPM_CTRL,
+       .mir_spm = RTL839X_MIR_SPM_CTRL,
+       .mac_link_sts = RTL839X_MAC_LINK_STS,
+       .mac_link_dup_sts = RTL839X_MAC_LINK_DUP_STS,
+       .mac_link_spd_sts = rtl839x_mac_link_spd_sts,
+       .mac_rx_pause_sts = RTL839X_MAC_RX_PAUSE_STS,
+       .mac_tx_pause_sts = RTL839X_MAC_TX_PAUSE_STS,
+       .read_l2_entry_using_hash = rtl839x_read_l2_entry_using_hash,
+       .read_cam = rtl839x_read_cam,
+       .vlan_port_egr_filter = RTL839X_VLAN_PORT_EGR_FLTR(0),
+       .vlan_port_igr_filter = RTL839X_VLAN_PORT_IGR_FLTR(0),
+       .vlan_port_pb = RTL839X_VLAN_PORT_PB_VLAN,
+       .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,
+};
index 39535125255d9170d5965a08b46c6f22ad814690..fd0455a6cd6357d7ce8f125b774071c6db9e8221 100644 (file)
@@ -24,7 +24,71 @@ struct rtl83xx_mib_desc {
        const char *name;
 };
 
-void __init rtl83xx_storm_control_init(struct rtl838x_switch_priv *priv);
+/* API for switch table access */
+struct table_reg {
+       u16 addr;
+       u16 data;
+       u8  max_data;
+       u8 c_bit;
+       u8 t_bit;
+       u8 rmode;
+       u8 tbl;
+       struct mutex lock;
+};
+
+#define TBL_DESC(_addr, _data, _max_data, _c_bit, _t_bit, _rmode) \
+               {  .addr = _addr, .data = _data, .max_data = _max_data, .c_bit = _c_bit, \
+                   .t_bit = _t_bit, .rmode = _rmode \
+               }
+
+typedef enum {
+       RTL8380_TBL_L2 = 0,
+       RTL8380_TBL_0,
+       RTL8380_TBL_1,
+       RTL8390_TBL_L2,
+       RTL8390_TBL_0,
+       RTL8390_TBL_1,
+       RTL8390_TBL_2,
+       RTL9300_TBL_L2,
+       RTL9300_TBL_0,
+       RTL9300_TBL_1,
+       RTL9300_TBL_2,
+       RTL9300_TBL_HSB,
+       RTL9300_TBL_HSA,
+       RTL9310_TBL_0,
+       RTL9310_TBL_1,
+       RTL9310_TBL_2,
+       RTL9310_TBL_3,
+       RTL9310_TBL_4,
+       RTL9310_TBL_5,
+       RTL_TBL_END
+} rtl838x_tbl_reg_t;
+
+void rtl_table_init(void);
+struct table_reg *rtl_table_get(rtl838x_tbl_reg_t r, int t);
+void rtl_table_release(struct table_reg *r);
+void rtl_table_read(struct table_reg *r, int idx);
+void rtl_table_write(struct table_reg *r, int idx);
+inline u16 rtl_table_data(struct table_reg *r, int i);
+inline u32 rtl_table_data_r(struct table_reg *r, int i);
+inline void rtl_table_data_w(struct table_reg *r, u32 v, int i);
+
+void __init rtl83xx_setup_qos(struct rtl838x_switch_priv *priv);
+int read_phy(u32 port, u32 page, u32 reg, u32 *val);
+int write_phy(u32 port, u32 page, u32 reg, u32 val);
+
+/* Port register accessor functions for the RTL839x and RTL931X SoCs */
+void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg);
+u64 rtl839x_get_port_reg_be(int reg);
+void rtl839x_set_port_reg_be(u64 set, int reg);
+void rtl839x_mask_port_reg_le(u64 clear, u64 set, int reg);
+void rtl839x_set_port_reg_le(u64 set, int reg);
+u64 rtl839x_get_port_reg_le(int reg);
+
+/* Port register accessor functions for the RTL838x and RTL930X SoCs */
+void rtl838x_mask_port_reg(u64 clear, u64 set, int reg);
+void rtl838x_set_port_reg(u64 set, int reg);
+u64 rtl838x_get_port_reg(int reg);
 
 /* RTL838x-specific */
 u32 rtl838x_hash(struct rtl838x_switch_priv *priv, u64 seed);
@@ -32,6 +96,9 @@ irqreturn_t rtl838x_switch_irq(int irq, void *dev_id);
 void rtl8380_get_version(struct rtl838x_switch_priv *priv);
 void rtl838x_vlan_profile_dump(int index);
 int rtl83xx_dsa_phy_read(struct dsa_switch *ds, int phy_addr, int phy_reg);
+void rtl8380_sds_rst(int mac);
+int rtl8380_sds_power(int mac, int val);
+void rtl838x_print_matrix(void);
 
 /* RTL839x-specific */
 u32 rtl839x_hash(struct rtl838x_switch_priv *priv, u64 seed);
@@ -39,6 +106,20 @@ 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);
+void rtl839x_exec_tbl2_cmd(u32 cmd);
+void rtl839x_print_matrix(void);
+
+/* RTL930x-specific */
+u32 rtl930x_hash(struct rtl838x_switch_priv *priv, u64 seed);
+irqreturn_t rtl930x_switch_irq(int irq, void *dev_id);
+irqreturn_t rtl839x_switch_irq(int irq, void *dev_id);
+void rtl930x_vlan_profile_dump(int index);
+int rtl9300_sds_power(int mac, int val);
+void rtl9300_sds_rst(int sds_num, u32 mode);
+void rtl930x_print_matrix(void);
+
+/* RTL931x-specific */
+irqreturn_t rtl931x_switch_irq(int irq, void *dev_id);
 
 #endif /* _NET_DSA_RTL83XX_H */
 
index fec842674e07619e9abb051f43f23375e76709b3..7931daff07cfa3854c670020b77522f971c11750 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/of_mdio.h>
 #include <linux/module.h>
 #include <linux/phylink.h>
+#include <linux/pkt_sched.h>
 #include <net/dsa.h>
 #include <net/switchdev.h>
 #include <asm/cacheflush.h>
 extern struct rtl83xx_soc_info soc_info;
 
 /*
- * Maximum number of RX rings is 8, assigned by switch based on
- * packet/port priortity (not implemented)
- * Maximum number of TX rings is 2 (only ring 0 used)
- * RX ringlength needs to be at least 200, otherwise CPU and Switch
- * may gridlock.
+ * Maximum number of RX rings is 8 on RTL83XX and 32 on the 93XX
+ * The ring is assigned by switch based on packet/port priortity
+ * Maximum number of TX rings is 2, Ring 2 being the high priority
+ * ring on the RTL93xx SoCs. MAX_RING_SIZE * RING_BUFFER gives
+ * the memory used for the ring buffer.
  */
-#define RXRINGS                8
-#define RXRINGLEN      300
+#define MAX_RXRINGS    32
+#define MAX_RXLEN      100
+#define MAX_ENTRIES    (200 * 8)
 #define TXRINGS                2
-#define TXRINGLEN      160
+// BUG: TXRINGLEN can be 160
+#define TXRINGLEN      16
 #define NOTIFY_EVENTS  10
 #define NOTIFY_BLOCKS  10
 #define TX_EN          0x8
 #define RX_EN          0x4
+#define TX_EN_93XX     0x20
+#define RX_EN_93XX     0x10
 #define TX_DO          0x2
 #define WRAP           0x2
 
@@ -53,11 +58,10 @@ extern struct rtl83xx_soc_info soc_info;
 struct p_hdr {
        uint8_t         *buf;
        uint16_t        reserved;
-       uint16_t        size;   /* buffer size */
+       uint16_t        size;           /* buffer size */
        uint16_t        offset;
-       uint16_t        len;    /* pkt len */
-       uint16_t        reserved2;
-       uint16_t        cpu_tag[5];
+       uint16_t        len;            /* pkt len */
+       uint16_t        cpu_tag[10];
 } __packed __aligned(1);
 
 struct n_event {
@@ -70,14 +74,14 @@ struct n_event {
 } __packed __aligned(1);
 
 struct ring_b {
-       uint32_t        rx_r[RXRINGS][RXRINGLEN];
+       uint32_t        rx_r[MAX_RXRINGS][MAX_RXLEN];
        uint32_t        tx_r[TXRINGS][TXRINGLEN];
-       struct  p_hdr   rx_header[RXRINGS][RXRINGLEN];
+       struct  p_hdr   rx_header[MAX_RXRINGS][MAX_RXLEN];
        struct  p_hdr   tx_header[TXRINGS][TXRINGLEN];
-       uint32_t        c_rx[RXRINGS];
+       uint32_t        c_rx[MAX_RXRINGS];
        uint32_t        c_tx[TXRINGS];
-       uint8_t         rx_space[RXRINGS*RXRINGLEN*RING_BUFFER];
-       uint8_t         tx_space[TXRINGLEN*RING_BUFFER];
+       uint8_t         tx_space[TXRINGS * TXRINGLEN * RING_BUFFER];
+       uint8_t         *rx_space;
 };
 
 struct notify_block {
@@ -91,132 +95,230 @@ struct notify_b {
        u32                     reserved2[8];
 };
 
-inline void rtl838x_create_tx_header(struct p_hdr *h, int dest_port)
+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;
-       } else {
-               h->cpu_tag[0] = 0;
-               h->cpu_tag[1] = 0;
-               h->cpu_tag[2] = 0;
-               h->cpu_tag[3] = 0;
-               h->cpu_tag[4] = 0;
+               // cpu_tag[0] is reserved on the RTL83XX SoCs
+               h->cpu_tag[1] = 0x0400;
+               h->cpu_tag[2] = 0x0200;
+               h->cpu_tag[3] = 0x0000;
+               h->cpu_tag[4] = BIT(dest_port) >> 16;
+               h->cpu_tag[5] = BIT(dest_port) & 0xffff;
+               // Set internal priority and AS_PRIO
+               if (prio >= 0)
+                       h->cpu_tag[2] |= (prio | 0x8) << 12;
        }
 }
 
-inline void rtl839x_create_tx_header(struct p_hdr *h, int dest_port)
+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;
-       } else {
-               h->cpu_tag[0] = 0;
-               h->cpu_tag[1] = 0;
-               h->cpu_tag[2] = 0;
-               h->cpu_tag[3] = 0;
-               h->cpu_tag[4] = 0;
+               // cpu_tag[0] is reserved on the RTL83XX SoCs
+               h->cpu_tag[1] = 0x0100;
+               h->cpu_tag[2] = h->cpu_tag[3] = h->cpu_tag[4] = h->cpu_tag[5] = 0;
+               if (dest_port >= 32) {
+                       dest_port -= 32;
+                       h->cpu_tag[2] = BIT(dest_port) >> 16;
+                       h->cpu_tag[3] = BIT(dest_port) & 0xffff;
+               } else {
+                       h->cpu_tag[4] = BIT(dest_port) >> 16;
+                       h->cpu_tag[5] = BIT(dest_port) & 0xffff;
+               }
+               h->cpu_tag[6] |= BIT(21); // Enable destination port mask use
+               // Set internal priority and AS_PRIO
+               if (prio >= 0)
+                       h->cpu_tag[1] |= prio | BIT(3);
        }
 }
 
+void rtl930x_create_tx_header(struct p_hdr *h, int dest_port, int prio)
+{
+       h->cpu_tag[0] = 0x8000;
+       h->cpu_tag[1] = 0;  // TODO: Fill port and prio
+       h->cpu_tag[2] = 0;
+       h->cpu_tag[3] = 0;
+       h->cpu_tag[4] = 0;
+       h->cpu_tag[5] = 0;
+       h->cpu_tag[6] = 0;
+       h->cpu_tag[7] = 0xffff;
+}
+
+void rtl931x_create_tx_header(struct p_hdr *h, int dest_port, int prio)
+{
+       h->cpu_tag[0] = 0x8000;
+       h->cpu_tag[1] = 0;  // TODO: Fill port and prio
+       h->cpu_tag[2] = 0;
+       h->cpu_tag[3] = 0;
+       h->cpu_tag[4] = 0;
+       h->cpu_tag[5] = 0;
+       h->cpu_tag[6] = 0;
+       h->cpu_tag[7] = 0xffff;
+}
+
+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[MAX_RXRINGS];
        struct phylink *phylink;
        struct phylink_config phylink_config;
        u16 id;
        u16 family_id;
        const struct rtl838x_reg *r;
        u8 cpu_port;
-       u8 port_mask;
        u32 lastEvent;
-};
-
-static const struct rtl838x_reg rtl838x_reg = {
-       .mac_port_ctrl = rtl838x_mac_port_ctrl,
-       .dma_if_intr_sts = RTL838X_DMA_IF_INTR_STS,
-       .dma_if_intr_msk = RTL838X_DMA_IF_INTR_MSK,
-       .dma_if_ctrl = RTL838X_DMA_IF_CTRL,
-       .mac_force_mode_ctrl = rtl838x_mac_force_mode_ctrl,
-       .dma_rx_base = rtl838x_dma_rx_base,
-       .dma_tx_base = rtl838x_dma_tx_base,
-       .dma_if_rx_ring_size = rtl838x_dma_if_rx_ring_size,
-       .dma_if_rx_ring_cntr = rtl838x_dma_if_rx_ring_cntr,
-       .dma_if_rx_cur = rtl838x_dma_if_rx_cur,
-       .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0,
-       .get_mac_link_sts = rtl838x_get_mac_link_sts,
-       .get_mac_link_dup_sts = rtl838x_get_mac_link_dup_sts,
-       .get_mac_link_spd_sts = rtl838x_get_mac_link_spd_sts,
-       .get_mac_rx_pause_sts = rtl838x_get_mac_rx_pause_sts,
-       .get_mac_tx_pause_sts = rtl838x_get_mac_tx_pause_sts,
-       .mac = RTL838X_MAC,
-       .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL,
-};
-
-static const struct rtl838x_reg rtl839x_reg = {
-       .mac_port_ctrl = rtl839x_mac_port_ctrl,
-       .dma_if_intr_sts = RTL839X_DMA_IF_INTR_STS,
-       .dma_if_intr_msk = RTL839X_DMA_IF_INTR_MSK,
-       .dma_if_ctrl = RTL839X_DMA_IF_CTRL,
-       .mac_force_mode_ctrl = rtl839x_mac_force_mode_ctrl,
-       .dma_rx_base = rtl839x_dma_rx_base,
-       .dma_tx_base = rtl839x_dma_tx_base,
-       .dma_if_rx_ring_size = rtl839x_dma_if_rx_ring_size,
-       .dma_if_rx_ring_cntr = rtl839x_dma_if_rx_ring_cntr,
-       .dma_if_rx_cur = rtl839x_dma_if_rx_cur,
-       .rst_glb_ctrl = RTL839X_RST_GLB_CTRL,
-       .get_mac_link_sts = rtl839x_get_mac_link_sts,
-       .get_mac_link_dup_sts = rtl839x_get_mac_link_dup_sts,
-       .get_mac_link_spd_sts = rtl839x_get_mac_link_spd_sts,
-       .get_mac_rx_pause_sts = rtl839x_get_mac_rx_pause_sts,
-       .get_mac_tx_pause_sts = rtl839x_get_mac_tx_pause_sts,
-       .mac = RTL839X_MAC,
-       .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL,
+       u16 rxrings;
+       u16 rxringlen;
 };
 
 extern int rtl838x_phy_init(struct rtl838x_eth_priv *priv);
 extern int rtl838x_read_sds_phy(int phy_addr, int phy_reg);
 extern int rtl839x_read_sds_phy(int phy_addr, int phy_reg);
 extern int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v);
+extern int rtl930x_read_sds_phy(int phy_addr, int page, int phy_reg);
+extern int rtl930x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v);
+extern int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
+extern int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val);
+
+/*
+ * On the RTL93XX, the RTL93XX_DMA_IF_RX_RING_CNTR track the fill level of 
+ * the rings. Writing x into these registers substracts x from its content.
+ * When the content reaches the ring size, the ASIC no longer adds
+ * packets to this receive queue.
+ */
+void rtl838x_update_cntr(int r, int released)
+{
+       // This feature is not available on RTL838x SoCs
+}
+
+void rtl839x_update_cntr(int r, int released)
+{
+       // This feature is not available on RTL839x SoCs
+}
+
+void rtl930x_update_cntr(int r, int released)
+{
+       int pos = (r % 3) * 10;
+       u32 reg = RTL930X_DMA_IF_RX_RING_CNTR + ((r / 3) << 2);
+       u32 v = sw_r32(reg);
+
+       v = (v >> pos) & 0x3ff;
+       pr_debug("RX: Work done %d, old value: %d, pos %d, reg %04x\n", released, v, pos, reg);
+       sw_w32_mask(0x3ff << pos, released << pos, reg);
+       sw_w32(v, reg);
+}
+
+void rtl931x_update_cntr(int r, int released)
+{
+       int pos = (r % 3) * 10;
+       u32 reg = RTL931X_DMA_IF_RX_RING_CNTR + ((r / 3) << 2);
+
+       sw_w32_mask(0x3ff << pos, released << pos, reg);
+}
+
+struct dsa_tag {
+       u8      reason;
+       u8      queue;
+       u16     port;
+       u8      l2_offloaded;
+       u8      prio;
+};
+
+bool rtl838x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
+{
+       t->reason = h->cpu_tag[3] & 0xf;
+       if (t->reason != 15)
+       pr_debug("Reason: %d\n", t->reason);
+       t->queue = (h->cpu_tag[0] & 0xe0) >> 5;
+       if (t->reason != 4) // NIC_RX_REASON_SPECIAL_TRAP
+               t->l2_offloaded = 1;
+       else
+               t->l2_offloaded = 0;
+       t->port = h->cpu_tag[1] & 0x1f;
+
+       return t->l2_offloaded;
+}
+
+bool rtl839x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
+{
+       t->reason = h->cpu_tag[4] & 0x1f;
+       if (t->reason != 31)
+       pr_debug("Reason: %d\n", t->reason);
+               t->queue = (h->cpu_tag[3] & 0xe000) >> 13;
+       if ((t->reason != 7) && (t->reason != 8)) // NIC_RX_REASON_RMA_USR
+               t->l2_offloaded = 1;
+       else
+               t->l2_offloaded = 0;
+       
+       t->port = h->cpu_tag[1] & 0x3f;
+
+       return t->l2_offloaded;
+}
+
+bool rtl931x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
+{
+       t->reason = h->cpu_tag[7] & 0x3f;
+       pr_debug("Reason %d\n", t->reason);
+       t->queue =  (h->cpu_tag[2] >> 11) & 0x1f;
+       if (t->reason >= 19 && t->reason <= 27)
+               t->l2_offloaded = 0;
+       else
+               t->l2_offloaded = 1;
+       t->port = (h->cpu_tag[0] >> 8) & 0x3f;
+
+       return t->l2_offloaded;
+}
+
+bool rtl930x_decode_tag(struct p_hdr *h, struct dsa_tag *t)
+{
+       rtl931x_decode_tag(h, t);
+       t->port &= 0x1f;
+       return t->l2_offloaded;
+}
 
 /*
  * Discard the RX ring-buffers, called as part of the net-ISR
  * when the buffer runs over
  * Caller needs to hold priv->lock
  */
-static void rtl838x_rb_cleanup(struct rtl838x_eth_priv *priv)
+static void rtl838x_rb_cleanup(struct rtl838x_eth_priv *priv, int status)
 {
        int r;
        u32     *last;
        struct p_hdr *h;
        struct ring_b *ring = priv->membase;
 
-       for (r = 0; r < RXRINGS; r++) {
-               last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur(r)));
+       for (r = 0; r < priv->rxrings; r++) {
+               pr_debug("In %s working on r: %d\n", __func__, r);
+               last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4));
                do {
                        if ((ring->rx_r[r][ring->c_rx[r]] & 0x1))
                                break;
+                       pr_debug("Got something: %d\n", ring->c_rx[r]);
                        h = &ring->rx_header[r][ring->c_rx[r]];
+                       memset(h, 0, sizeof(struct p_hdr));
                        h->buf = (u8 *)KSEG1ADDR(ring->rx_space
-                                       + r * ring->c_rx[r] * RING_BUFFER);
+                                       + r * priv->rxringlen * RING_BUFFER
+                                       + ring->c_rx[r] * RING_BUFFER);
                        h->size = RING_BUFFER;
-                       h->len = 0;
                        /* make sure the header is visible to the ASIC */
                        mb();
 
                        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;
+                               | (ring->c_rx[r] == (priv->rxringlen - 1) ? WRAP : 0x1);
+                       ring->c_rx[r] = (ring->c_rx[r] + 1) % priv->rxringlen;
                } while (&ring->rx_r[r][ring->c_rx[r]] != last);
        }
 }
@@ -229,25 +331,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)
@@ -288,19 +390,20 @@ static void rtl839x_l2_notification_handler(struct rtl838x_eth_priv *priv)
        priv->lastEvent = e;
 }
 
-static irqreturn_t rtl838x_net_irq(int irq, void *dev_id)
+static irqreturn_t rtl83xx_net_irq(int irq, void *dev_id)
 {
        struct net_device *dev = dev_id;
        struct rtl838x_eth_priv *priv = netdev_priv(dev);
        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);
 
+       pr_debug("IRQ: %08x\n", status);
        if (storm_uc || storm_mc || storm_bc) {
-
                pr_warn("Storm control UC: %08x, MC: %08x, BC: %08x\n",
                        storm_uc, storm_mc, storm_bc);
 
@@ -325,20 +428,23 @@ 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);
+               /* ACK and 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 < priv->rxrings; i++) {
+                       if (status & BIT(i + 8)) {
+                               pr_debug("Scheduling queue: %d\n", i);
+                               napi_schedule(&priv->rx_qs[i].napi);
+                       }
+               }
        }
 
        /* RX buffer overrun */
        if (status & 0x000ff) {
                pr_info("RX buffer overrun: status %x, mask: %x\n",
                         status, sw_r32(priv->r->dma_if_intr_msk));
-               sw_w32(0x000000ff, priv->r->dma_if_intr_sts);
-               rtl838x_rb_cleanup(priv);
+               sw_w32(status, priv->r->dma_if_intr_sts);
+               rtl838x_rb_cleanup(priv, status & 0xff);
        }
 
        if (priv->family_id == RTL8390_FAMILY_ID && status & 0x00100000) {
@@ -360,14 +466,185 @@ static irqreturn_t rtl838x_net_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t rtl93xx_net_irq(int irq, void *dev_id)
+{
+       struct net_device *dev = dev_id;
+       struct rtl838x_eth_priv *priv = netdev_priv(dev);
+       u32 status_rx_r = sw_r32(priv->r->dma_if_intr_rx_runout_sts);
+       u32 status_rx = sw_r32(priv->r->dma_if_intr_rx_done_sts);
+       u32 status_tx = sw_r32(priv->r->dma_if_intr_tx_done_sts);
+       int i;
+
+       pr_debug("In %s, status_tx: %08x, status_rx: %08x, status_rx_r: %08x\n",
+               __func__, status_tx, status_rx, status_rx_r);
+       spin_lock(&priv->lock);
+
+       /*  Ignore TX interrupt */
+       if (status_tx) {
+               /* Clear ISR */
+               pr_debug("TX done\n");
+               sw_w32(status_tx, priv->r->dma_if_intr_tx_done_sts);
+       }
+
+       /* RX interrupt */
+       if (status_rx) {
+               pr_debug("RX IRQ\n");
+               /* ACK and disable RX interrupt for given rings */
+               sw_w32(status_rx, priv->r->dma_if_intr_rx_done_sts);
+               sw_w32_mask(status_rx, 0, priv->r->dma_if_intr_rx_done_msk);
+               for (i = 0; i < priv->rxrings; i++) {
+                       if (status_rx & BIT(i)) {
+                               pr_debug("Scheduling queue: %d\n", i);
+                               napi_schedule(&priv->rx_qs[i].napi);
+                       }
+               }
+       }
+
+       /* RX buffer overrun */
+       if (status_rx_r) {
+               pr_debug("RX buffer overrun: status %x, mask: %x\n",
+                        status_rx_r, sw_r32(priv->r->dma_if_intr_rx_runout_msk));
+               sw_w32(status_rx_r, priv->r->dma_if_intr_rx_runout_sts);
+               rtl838x_rb_cleanup(priv, status_rx_r);
+       }
+
+       spin_unlock(&priv->lock);
+       return IRQ_HANDLED;
+}
+
+static const struct rtl838x_reg rtl838x_reg = {
+       .net_irq = rtl83xx_net_irq,
+       .mac_port_ctrl = rtl838x_mac_port_ctrl,
+       .dma_if_intr_sts = RTL838X_DMA_IF_INTR_STS,
+       .dma_if_intr_msk = RTL838X_DMA_IF_INTR_MSK,
+       .dma_if_ctrl = RTL838X_DMA_IF_CTRL,
+       .mac_force_mode_ctrl = RTL838X_MAC_FORCE_MODE_CTRL,
+       .dma_rx_base = RTL838X_DMA_RX_BASE,
+       .dma_tx_base = RTL838X_DMA_TX_BASE,
+       .dma_if_rx_ring_size = rtl838x_dma_if_rx_ring_size,
+       .dma_if_rx_ring_cntr = rtl838x_dma_if_rx_ring_cntr,
+       .dma_if_rx_cur = RTL838X_DMA_IF_RX_CUR,
+       .rst_glb_ctrl = RTL838X_RST_GLB_CTRL_0,
+       .get_mac_link_sts = rtl838x_get_mac_link_sts,
+       .get_mac_link_dup_sts = rtl838x_get_mac_link_dup_sts,
+       .get_mac_link_spd_sts = rtl838x_get_mac_link_spd_sts,
+       .get_mac_rx_pause_sts = rtl838x_get_mac_rx_pause_sts,
+       .get_mac_tx_pause_sts = rtl838x_get_mac_tx_pause_sts,
+       .mac = RTL838X_MAC,
+       .l2_tbl_flush_ctrl = RTL838X_L2_TBL_FLUSH_CTRL,
+       .update_cntr = rtl838x_update_cntr,
+       .create_tx_header = rtl838x_create_tx_header,
+       .decode_tag = rtl838x_decode_tag,
+};
+
+static const struct rtl838x_reg rtl839x_reg = {
+       .net_irq = rtl83xx_net_irq,
+       .mac_port_ctrl = rtl839x_mac_port_ctrl,
+       .dma_if_intr_sts = RTL839X_DMA_IF_INTR_STS,
+       .dma_if_intr_msk = RTL839X_DMA_IF_INTR_MSK,
+       .dma_if_ctrl = RTL839X_DMA_IF_CTRL,
+       .mac_force_mode_ctrl = RTL839X_MAC_FORCE_MODE_CTRL,
+       .dma_rx_base = RTL839X_DMA_RX_BASE,
+       .dma_tx_base = RTL839X_DMA_TX_BASE,
+       .dma_if_rx_ring_size = rtl839x_dma_if_rx_ring_size,
+       .dma_if_rx_ring_cntr = rtl839x_dma_if_rx_ring_cntr,
+       .dma_if_rx_cur = RTL839X_DMA_IF_RX_CUR,
+       .rst_glb_ctrl = RTL839X_RST_GLB_CTRL,
+       .get_mac_link_sts = rtl839x_get_mac_link_sts,
+       .get_mac_link_dup_sts = rtl839x_get_mac_link_dup_sts,
+       .get_mac_link_spd_sts = rtl839x_get_mac_link_spd_sts,
+       .get_mac_rx_pause_sts = rtl839x_get_mac_rx_pause_sts,
+       .get_mac_tx_pause_sts = rtl839x_get_mac_tx_pause_sts,
+       .mac = RTL839X_MAC,
+       .l2_tbl_flush_ctrl = RTL839X_L2_TBL_FLUSH_CTRL,
+       .update_cntr = rtl839x_update_cntr,
+       .create_tx_header = rtl839x_create_tx_header,
+       .decode_tag = rtl839x_decode_tag,
+};
+
+static const struct rtl838x_reg rtl930x_reg = {
+       .net_irq = rtl93xx_net_irq,
+       .mac_port_ctrl = rtl930x_mac_port_ctrl,
+       .dma_if_intr_rx_runout_sts = RTL930X_DMA_IF_INTR_RX_RUNOUT_STS,
+       .dma_if_intr_rx_done_sts = RTL930X_DMA_IF_INTR_RX_DONE_STS,
+       .dma_if_intr_tx_done_sts = RTL930X_DMA_IF_INTR_TX_DONE_STS,
+       .dma_if_intr_rx_runout_msk = RTL930X_DMA_IF_INTR_RX_RUNOUT_MSK,
+       .dma_if_intr_rx_done_msk = RTL930X_DMA_IF_INTR_RX_DONE_MSK,
+       .dma_if_intr_tx_done_msk = RTL930X_DMA_IF_INTR_TX_DONE_MSK,
+       .l2_ntfy_if_intr_sts = RTL930X_L2_NTFY_IF_INTR_STS,
+       .l2_ntfy_if_intr_msk = RTL930X_L2_NTFY_IF_INTR_MSK,
+       .dma_if_ctrl = RTL930X_DMA_IF_CTRL,
+       .mac_force_mode_ctrl = RTL930X_MAC_FORCE_MODE_CTRL,
+       .dma_rx_base = RTL930X_DMA_RX_BASE,
+       .dma_tx_base = RTL930X_DMA_TX_BASE,
+       .dma_if_rx_ring_size = rtl930x_dma_if_rx_ring_size,
+       .dma_if_rx_ring_cntr = rtl930x_dma_if_rx_ring_cntr,
+       .dma_if_rx_cur = RTL930X_DMA_IF_RX_CUR,
+       .rst_glb_ctrl = RTL930X_RST_GLB_CTRL_0,
+       .get_mac_link_sts = rtl930x_get_mac_link_sts,
+       .get_mac_link_dup_sts = rtl930x_get_mac_link_dup_sts,
+       .get_mac_link_spd_sts = rtl930x_get_mac_link_spd_sts,
+       .get_mac_rx_pause_sts = rtl930x_get_mac_rx_pause_sts,
+       .get_mac_tx_pause_sts = rtl930x_get_mac_tx_pause_sts,
+       .mac = RTL930X_MAC_L2_ADDR_CTRL,
+       .l2_tbl_flush_ctrl = RTL930X_L2_TBL_FLUSH_CTRL,
+       .update_cntr = rtl930x_update_cntr,
+       .create_tx_header = rtl930x_create_tx_header,
+       .decode_tag = rtl930x_decode_tag,
+};
+
+static const struct rtl838x_reg rtl931x_reg = {
+       .net_irq = rtl93xx_net_irq,
+       .mac_port_ctrl = rtl931x_mac_port_ctrl,
+       .dma_if_intr_rx_runout_sts = RTL931X_DMA_IF_INTR_RX_RUNOUT_STS,
+       .dma_if_intr_rx_done_sts = RTL931X_DMA_IF_INTR_RX_DONE_STS,
+       .dma_if_intr_tx_done_sts = RTL931X_DMA_IF_INTR_TX_DONE_STS,
+       .dma_if_intr_rx_runout_msk = RTL931X_DMA_IF_INTR_RX_RUNOUT_MSK,
+       .dma_if_intr_rx_done_msk = RTL931X_DMA_IF_INTR_RX_DONE_MSK,
+       .dma_if_intr_tx_done_msk = RTL931X_DMA_IF_INTR_TX_DONE_MSK,
+       .l2_ntfy_if_intr_sts = RTL931X_L2_NTFY_IF_INTR_STS,
+       .l2_ntfy_if_intr_msk = RTL931X_L2_NTFY_IF_INTR_MSK,
+       .dma_if_ctrl = RTL931X_DMA_IF_CTRL,
+       .mac_force_mode_ctrl = RTL931X_MAC_FORCE_MODE_CTRL,
+       .dma_rx_base = RTL931X_DMA_RX_BASE,
+       .dma_tx_base = RTL931X_DMA_TX_BASE,
+       .dma_if_rx_ring_size = rtl931x_dma_if_rx_ring_size,
+       .dma_if_rx_ring_cntr = rtl931x_dma_if_rx_ring_cntr,
+       .dma_if_rx_cur = RTL931X_DMA_IF_RX_CUR,
+       .rst_glb_ctrl = RTL931X_RST_GLB_CTRL,
+       .get_mac_link_sts = rtl931x_get_mac_link_sts,
+       .get_mac_link_dup_sts = rtl931x_get_mac_link_dup_sts,
+       .get_mac_link_spd_sts = rtl931x_get_mac_link_spd_sts,
+       .get_mac_rx_pause_sts = rtl931x_get_mac_rx_pause_sts,
+       .get_mac_tx_pause_sts = rtl931x_get_mac_tx_pause_sts,
+       .mac = RTL931X_MAC_L2_ADDR_CTRL,
+       .l2_tbl_flush_ctrl = RTL931X_L2_TBL_FLUSH_CTRL,
+       .update_cntr = rtl931x_update_cntr,
+       .create_tx_header = rtl931x_create_tx_header,
+       .decode_tag = rtl931x_decode_tag,
+};
+
 static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv)
 {
        u32 int_saved, nbuf;
-
+       int i, pos;
+       
        pr_info("RESETTING %x, CPU_PORT %d\n", priv->family_id, priv->cpu_port);
-       /* Stop TX/RX */
        sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(priv->cpu_port));
-       mdelay(500);
+       mdelay(100);
+
+       /* Disable and clear interrupts */
+       if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) {
+               sw_w32(0x00000000, priv->r->dma_if_intr_rx_runout_msk);
+               sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_sts);
+               sw_w32(0x00000000, priv->r->dma_if_intr_rx_done_msk);
+               sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_sts);
+               sw_w32(0x00000000, priv->r->dma_if_intr_tx_done_msk);
+               sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_sts);
+       } else {
+               sw_w32(0x00000000, priv->r->dma_if_intr_msk);
+               sw_w32(0xffffffff, priv->r->dma_if_intr_sts);
+       }
 
        if (priv->family_id == RTL8390_FAMILY_ID) {
                /* Preserve L2 notification and NBUF settings */
@@ -382,18 +659,31 @@ static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv)
                sw_w32(0xffffffff, priv->r->dma_if_intr_sts);
        }
 
-       /* Reset NIC and Queue */
-       sw_w32(0x08, priv->r->rst_glb_ctrl);
-       if (priv->family_id == RTL8390_FAMILY_ID)
-               sw_w32(0xffffffff, RTL839X_DMA_IF_RX_RING_CNTR);
-       do { /* Reset NIC */
-               udelay(20);
-       } while (sw_r32(priv->r->rst_glb_ctrl) & 0x08);
-       do { /* Reset Queues */
+       /* Reset NIC  */
+       if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID)
+               sw_w32(0x4, priv->r->rst_glb_ctrl);
+       else
+               sw_w32(0x8, priv->r->rst_glb_ctrl);
+
+       do { /* Wait for reset of NIC and Queues done */
                udelay(20);
-       } while (sw_r32(priv->r->rst_glb_ctrl) & 0x04);
+       } while (sw_r32(priv->r->rst_glb_ctrl) & 0xc);
        mdelay(100);
 
+       /* Setup Head of Line */
+       if (priv->family_id == RTL8380_FAMILY_ID)
+               sw_w32(0, RTL838X_DMA_IF_RX_RING_SIZE);  // Disabled on RTL8380
+       if (priv->family_id == RTL8390_FAMILY_ID)
+               sw_w32(0xffffffff, RTL839X_DMA_IF_RX_RING_CNTR);
+       if (priv->family_id == RTL9300_FAMILY_ID) {
+               for (i = 0; i < priv->rxrings; i++) {
+                       pos = (i % 3) * 10;
+                       sw_w32_mask(0x3ff << pos, 0, priv->r->dma_if_rx_ring_size(i));
+                       sw_w32_mask(0x3ff << pos, priv->rxringlen,
+                                   priv->r->dma_if_rx_ring_cntr(i));
+               }
+       }
+
        /* Re-enable link change interrupt */
        if (priv->family_id == RTL8390_FAMILY_ID) {
                sw_w32(0xffffffff, RTL839X_ISR_PORT_LINK_STS_CHG);
@@ -405,32 +695,6 @@ static void rtl838x_hw_reset(struct rtl838x_eth_priv *priv)
                sw_w32_mask(7 << 20, int_saved & (7 << 20), priv->r->dma_if_intr_msk);
                sw_w32(nbuf, RTL839X_DMA_IF_NBUF_BASE_DESC_ADDR_CTRL);
        }
-
-       /* Restart TX/RX to CPU port */
-       sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port));
-
-       if (priv->family_id == RTL8380_FAMILY_ID) {
-               /* Set Speed, duplex, flow control
-                * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL
-                * | SPD_SEL = 0b10 | FORCE_FC_EN | PHY_MASTER_SLV_MANUAL_EN
-                * | MEDIA_SEL
-                */
-               sw_w32(0x6192F, priv->r->mac_force_mode_ctrl(priv->cpu_port));
-               /* allow CRC errors on CPU-port */
-               sw_w32_mask(0, 0x8, priv->r->mac_port_ctrl(priv->cpu_port));
-       } else {
-               /* CPU port joins Lookup Miss Flooding Portmask */
-               sw_w32(0x28000, RTL839X_TBL_ACCESS_L2_CTRL);
-               sw_w32_mask(0, 0x80000000, RTL839X_TBL_ACCESS_L2_DATA(0));
-               sw_w32(0x38000, RTL839X_TBL_ACCESS_L2_CTRL);
-
-               /* Force CPU port link up */
-               sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl(priv->cpu_port));
-       }
-
-       /* Disable and clear interrupts */
-       sw_w32(0x00000000, priv->r->dma_if_intr_msk);
-       sw_w32(0xffffffff, priv->r->dma_if_intr_sts);
 }
 
 static void rtl838x_hw_ring_setup(struct rtl838x_eth_priv *priv)
@@ -438,11 +702,11 @@ static void rtl838x_hw_ring_setup(struct rtl838x_eth_priv *priv)
        int i;
        struct ring_b *ring = priv->membase;
 
-       for (i = 0; i < RXRINGS; i++)
-               sw_w32(KSEG1ADDR(&ring->rx_r[i]), priv->r->dma_rx_base(i));
+       for (i = 0; i < priv->rxrings; i++)
+               sw_w32(KSEG1ADDR(&ring->rx_r[i]), priv->r->dma_rx_base + i * 4);
 
        for (i = 0; i < TXRINGS; i++)
-               sw_w32(KSEG1ADDR(&ring->tx_r[i]), priv->r->dma_tx_base(i));
+               sw_w32(KSEG1ADDR(&ring->tx_r[i]), priv->r->dma_tx_base + i * 4);
 }
 
 static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv)
@@ -456,8 +720,19 @@ static void rtl838x_hw_en_rxtx(struct rtl838x_eth_priv *priv)
        /* Enable RX done, RX overflow and TX done interrupts */
        sw_w32(0xfffff, priv->r->dma_if_intr_msk);
 
-       /* Enable traffic, engine expects empty FCS field */
+       /* Enable DMA, engine expects empty FCS field */
        sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl);
+
+       /* Restart TX/RX to CPU port */
+       sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port));
+       /* Set Speed, duplex, flow control
+        * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL
+        * | SPD_SEL = 0b10 | FORCE_FC_EN | PHY_MASTER_SLV_MANUAL_EN
+        * | MEDIA_SEL
+        */
+       sw_w32(0x6192F, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
+       /* allow CRC errors on CPU-port */
+       sw_w32_mask(0, 0x8, priv->r->mac_port_ctrl(priv->cpu_port));
 }
 
 static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv)
@@ -468,27 +743,71 @@ static void rtl839x_hw_en_rxtx(struct rtl838x_eth_priv *priv)
        /* Enable Notify, RX done, RX overflow and TX done interrupts */
        sw_w32(0x007fffff, priv->r->dma_if_intr_msk); // Notify IRQ!
 
-       /* Enable traffic */
+       /* Enable DMA */
        sw_w32_mask(0, RX_EN | TX_EN, priv->r->dma_if_ctrl);
+
+       /* Restart TX/RX to CPU port */
+       sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port));
+
+       /* CPU port joins Lookup Miss Flooding Portmask */
+       // TODO: The code below should also work for the RTL838x
+       sw_w32(0x28000, RTL839X_TBL_ACCESS_L2_CTRL);
+       sw_w32_mask(0, 0x80000000, RTL839X_TBL_ACCESS_L2_DATA(0));
+       sw_w32(0x38000, RTL839X_TBL_ACCESS_L2_CTRL);
+
+       /* Force CPU port link up */
+       sw_w32_mask(0, 3, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
 }
 
-static void rtl838x_setup_ring_buffer(struct ring_b *ring)
+static void rtl93xx_hw_en_rxtx(struct rtl838x_eth_priv *priv)
+{
+       int i, pos;
+       u32 v;
+
+       /* Setup CPU-Port: RX Buffer truncated at 1600 Bytes */
+       sw_w32(0x06400040, priv->r->dma_if_ctrl);
+
+       for (i = 0; i < priv->rxrings; i++) {
+               pos = (i % 3) * 10;
+               sw_w32_mask(0x3ff << pos, priv->rxringlen << pos, priv->r->dma_if_rx_ring_size(i));
+
+               // Some SoCs have issues with missing underflow protection
+               v = (sw_r32(priv->r->dma_if_rx_ring_cntr(i)) >> pos) & 0x3ff;
+               sw_w32_mask(0x3ff << pos, v, priv->r->dma_if_rx_ring_cntr(i));
+       }
+
+       /* Enable Notify, RX done, RX overflow and TX done interrupts */
+       sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_msk);
+       sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_msk);
+       sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_msk);
+
+       /* Enable DMA */
+       sw_w32_mask(0, RX_EN_93XX | TX_EN_93XX, priv->r->dma_if_ctrl);
+
+       /* Restart TX/RX to CPU port */
+       sw_w32_mask(0x0, 0x3, priv->r->mac_port_ctrl(priv->cpu_port));
+
+       sw_w32_mask(0, BIT(priv->cpu_port), RTL930X_L2_UNKN_UC_FLD_PMSK);
+       sw_w32(0x217, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
+}
+
+static void rtl838x_setup_ring_buffer(struct rtl838x_eth_priv *priv, struct ring_b *ring)
 {
        int i, j;
 
        struct p_hdr *h;
 
-       for (i = 0; i < RXRINGS; i++) {
-               for (j = 0; j < RXRINGLEN; j++) {
+       for (i = 0; i < priv->rxrings; i++) {
+               for (j = 0; j < priv->rxringlen; j++) {
                        h = &ring->rx_header[i][j];
-                       h->buf = (u8 *)KSEG1ADDR(ring->rx_space + i * j * RING_BUFFER);
-                       h->reserved = 0;
+                       memset(h, 0, sizeof(struct p_hdr));
+                       h->buf = (u8 *)KSEG1ADDR(ring->rx_space
+                                       + i * priv->rxringlen * RING_BUFFER
+                                       + j * RING_BUFFER);
                        h->size = RING_BUFFER;
-                       h->offset = 0;
-                       h->len = 0;
-                       memset(&h->cpu_tag, 0, sizeof(uint16_t[5]));
                        /* All rings owned by switch, last one wraps */
-                       ring->rx_r[i][j] = KSEG1ADDR(h) | 1 | (j == (RXRINGLEN - 1) ? WRAP : 0);
+                       ring->rx_r[i][j] = KSEG1ADDR(h) | 1 
+                                          | (j == (priv->rxringlen - 1) ? WRAP : 0);
                }
                ring->c_rx[i] = 0;
        }
@@ -496,12 +815,11 @@ static void rtl838x_setup_ring_buffer(struct ring_b *ring)
        for (i = 0; i < TXRINGS; i++) {
                for (j = 0; j < TXRINGLEN; j++) {
                        h = &ring->tx_header[i][j];
-                       h->buf = (u8 *)KSEG1ADDR(ring->tx_space + i * j * RING_BUFFER);
-                       h->reserved = 0;
+                       memset(h, 0, sizeof(struct p_hdr));
+                       h->buf = (u8 *)KSEG1ADDR(ring->tx_space
+                                       + i * TXRINGLEN * RING_BUFFER
+                                       + j * RING_BUFFER);
                        h->size = RING_BUFFER;
-                       h->offset = 0;
-                       h->len = 0;
-                       memset(&h->cpu_tag, 0, sizeof(uint16_t[5]));
                        ring->tx_r[i][j] = KSEG1ADDR(&ring->tx_header[i][j]);
                }
                /* Last header is wrapping around */
@@ -535,13 +853,14 @@ 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);
+       pr_info("%s called: RX rings %d(length %d), TX rings %d(length %d)\n",
+               __func__, priv->rxrings, priv->rxringlen, TXRINGS, TXRINGLEN);
 
        spin_lock_irqsave(&priv->lock, flags);
        rtl838x_hw_reset(priv);
-       rtl838x_setup_ring_buffer(ring);
+       rtl838x_setup_ring_buffer(priv, ring);
        if (priv->family_id == RTL8390_FAMILY_ID) {
                rtl839x_setup_notify_ring_buffer(priv);
                /* Make sure the ring structure is visible to the ASIC */
@@ -550,8 +869,7 @@ static int rtl838x_eth_open(struct net_device *ndev)
        }
 
        rtl838x_hw_ring_setup(priv);
-       err = request_irq(ndev->irq, rtl838x_net_irq, IRQF_SHARED,
-                       ndev->name, ndev);
+       err = request_irq(ndev->irq, priv->r->net_irq, IRQF_SHARED, ndev->name, ndev);
        if (err) {
                netdev_err(ndev, "%s: could not acquire interrupt: %d\n",
                           __func__, err);
@@ -559,21 +877,39 @@ 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 < priv->rxrings; i++)
+               napi_enable(&priv->rx_qs[i].napi);
 
-       if (priv->family_id == RTL8380_FAMILY_ID) {
+       switch (priv->family_id) {
+       case RTL8380_FAMILY_ID:
                rtl838x_hw_en_rxtx(priv);
                /* Trap IGMP traffic to CPU-Port */
                sw_w32(0x3, RTL838X_SPCL_TRAP_IGMP_CTRL);
                /* Flush learned FDB entries on link down of a port */
-               sw_w32_mask(0, 1 << 7, RTL838X_L2_CTRL_0);
-       } else {
+               sw_w32_mask(0, BIT(7), RTL838X_L2_CTRL_0);
+               break;
+       case RTL8390_FAMILY_ID:
                rtl839x_hw_en_rxtx(priv);
                sw_w32(0x3, RTL839X_SPCL_TRAP_IGMP_CTRL);
-               sw_w32_mask(0, 1 << 7, RTL839X_L2_CTRL_0);
+               /* Flush learned FDB entries on link down of a port */
+               sw_w32_mask(0, BIT(7), RTL839X_L2_CTRL_0);
+               break;
+       case RTL9300_FAMILY_ID:
+               rtl93xx_hw_en_rxtx(priv);
+               /* Flush learned FDB entries on link down of a port */
+               sw_w32_mask(0, BIT(7), RTL930X_L2_CTRL);
+               sw_w32_mask(BIT(28), 0, RTL930X_L2_PORT_SABLK_CTRL);
+               sw_w32_mask(BIT(28), 0, RTL930X_L2_PORT_DABLK_CTRL);
+               break;
+
+       case RTL9310_FAMILY_ID:
+               rtl93xx_hw_en_rxtx(priv);
+//             TODO: Add trapping of IGMP frames to CPU-port
+               break;
        }
 
+       netif_tx_start_all_queues(ndev);
+
        spin_unlock_irqrestore(&priv->lock, flags);
 
        return 0;
@@ -581,10 +917,20 @@ static int rtl838x_eth_open(struct net_device *ndev)
 
 static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv)
 {
-       u32 force_mac = priv->family_id == RTL8380_FAMILY_ID ? 0x6192D : 0x75;
+       u32 force_mac = priv->family_id == RTL8380_FAMILY_ID ? 0x6192C : 0x75;
        u32 clear_irq = priv->family_id == RTL8380_FAMILY_ID ? 0x000fffff : 0x007fffff;
        int i;
 
+       // Disable RX/TX from/to CPU-port
+       sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(priv->cpu_port));
+
+       /* Disable traffic */
+       if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID)
+               sw_w32_mask(RX_EN_93XX | TX_EN_93XX, 0, priv->r->dma_if_ctrl);
+       else
+               sw_w32_mask(RX_EN | TX_EN, 0, priv->r->dma_if_ctrl);
+       mdelay(200); // Test, whether this is needed
+
        /* Block all ports */
        if (priv->family_id == RTL8380_FAMILY_ID) {
                sw_w32(0x03000000, RTL838X_TBL_ACCESS_DATA_0(0));
@@ -598,24 +944,33 @@ static void rtl838x_hw_stop(struct rtl838x_eth_priv *priv)
                        sw_w32(1 << 26 | 1 << 23 | i << 5, priv->r->l2_tbl_flush_ctrl);
                        do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << 26));
                }
-       } else {
+       } else if (priv->family_id == RTL8390_FAMILY_ID) {
                for (i = 0; i <= priv->cpu_port; i++) {
                        sw_w32(1 << 28 | 1 << 25 | i << 5, priv->r->l2_tbl_flush_ctrl);
                        do { } while (sw_r32(priv->r->l2_tbl_flush_ctrl) & (1 << 28));
                }
        }
+       // TODO: L2 flush register is 64 bit on RTL931X and 930X
 
        /* CPU-Port: Link down */
-       sw_w32(force_mac, priv->r->mac_force_mode_ctrl(priv->cpu_port));
+       if (priv->family_id == RTL8380_FAMILY_ID || priv->family_id == RTL8390_FAMILY_ID)
+               sw_w32(force_mac, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
+       else
+               sw_w32_mask(0x3, 0, priv->r->mac_force_mode_ctrl + priv->cpu_port *4);
        mdelay(100);
 
-       /* Disable traffic */
-       sw_w32_mask(RX_EN | TX_EN, 0, priv->r->dma_if_ctrl);
-       mdelay(200); // Test, whether this is needed
-
        /* Disable all TX/RX interrupts */
-       sw_w32(0x00000000, priv->r->dma_if_intr_msk);
-       sw_w32(clear_irq, priv->r->dma_if_intr_sts);
+       if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID) {
+               sw_w32(0x00000000, priv->r->dma_if_intr_rx_runout_msk);
+               sw_w32(0xffffffff, priv->r->dma_if_intr_rx_runout_sts);
+               sw_w32(0x00000000, priv->r->dma_if_intr_rx_done_msk);
+               sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_sts);
+               sw_w32(0x00000000, priv->r->dma_if_intr_tx_done_msk);
+               sw_w32(0x0000000f, priv->r->dma_if_intr_tx_done_sts);
+       } else {
+               sw_w32(0x00000000, priv->r->dma_if_intr_msk);
+               sw_w32(clear_irq, priv->r->dma_if_intr_sts);
+       }
 
        /* Disable TX/RX DMA */
        sw_w32(0x00000000, priv->r->dma_if_ctrl);
@@ -625,6 +980,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 +989,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 < priv->rxrings; i++)
+               napi_disable(&priv->rx_qs[i].napi);
+
+       netif_tx_stop_all_queues(ndev);
+
        spin_unlock_irqrestore(&priv->lock, flags);
 
        return 0;
@@ -680,12 +1040,50 @@ static void rtl838x_eth_set_multicast_list(struct net_device *ndev)
        }
 }
 
+static void rtl930x_eth_set_multicast_list(struct net_device *ndev)
+{
+       if (!(ndev->flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+               sw_w32(0x0, RTL930X_RMA_CTRL_0);
+               sw_w32(0x0, RTL930X_RMA_CTRL_1);
+               sw_w32(0x0, RTL930X_RMA_CTRL_2);
+       }
+       if (ndev->flags & IFF_ALLMULTI) {
+               sw_w32(0x7fffffff, RTL930X_RMA_CTRL_0);
+               sw_w32(0x7fffffff, RTL930X_RMA_CTRL_1);
+               sw_w32(0x7fffffff, RTL930X_RMA_CTRL_2);
+       }
+       if (ndev->flags & IFF_PROMISC) {
+               sw_w32(0x7fffffff, RTL930X_RMA_CTRL_0);
+               sw_w32(0x7fffffff, RTL930X_RMA_CTRL_1);
+               sw_w32(0x7fffffff, RTL930X_RMA_CTRL_2);
+       }
+}
+
+static void rtl931x_eth_set_multicast_list(struct net_device *ndev)
+{
+       if (!(ndev->flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+               sw_w32(0x0, RTL931X_RMA_CTRL_0);
+               sw_w32(0x0, RTL931X_RMA_CTRL_1);
+               sw_w32(0x0, RTL931X_RMA_CTRL_2);
+       }
+       if (ndev->flags & IFF_ALLMULTI) {
+               sw_w32(0x7fffffff, RTL931X_RMA_CTRL_0);
+               sw_w32(0x7fffffff, RTL931X_RMA_CTRL_1);
+               sw_w32(0x7fffffff, RTL931X_RMA_CTRL_2);
+       }
+       if (ndev->flags & IFF_PROMISC) {
+               sw_w32(0x7fffffff, RTL931X_RMA_CTRL_0);
+               sw_w32(0x7fffffff, RTL931X_RMA_CTRL_1);
+               sw_w32(0x7fffffff, RTL931X_RMA_CTRL_2);
+       }
+}
+
 static void rtl838x_eth_tx_timeout(struct net_device *ndev)
 {
        unsigned long flags;
        struct rtl838x_eth_priv *priv = netdev_priv(ndev);
 
-       pr_info("in %s\n", __func__);
+       pr_warn("%s\n", __func__);
        spin_lock_irqsave(&priv->lock, flags);
        rtl838x_hw_stop(priv);
        rtl838x_hw_ring_setup(priv);
@@ -705,6 +1103,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) // Check for high prio queue
+               pr_debug("SKB priority: %d\n", skb->priority);
 
        spin_lock_irqsave(&priv->lock, flags);
        len = skb->len;
@@ -729,44 +1131,48 @@ 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)) {
-               /* Set descriptor for tx */
-               h = &ring->tx_header[0][ring->c_tx[0]];
+       if (!(ring->tx_r[q][ring->c_tx[q]] & 0x1)) {
 
-               h->buf = (u8 *)KSEG1ADDR(ring->tx_space);
+               /* Set descriptor for tx */
+               h = &ring->tx_header[q][ring->c_tx[q]];
                h->size = len;
                h->len = len;
 
-               /* Create cpu_tag */
-               if (priv->family_id == RTL8380_FAMILY_ID)
-                       rtl838x_create_tx_header(h, dest_port);
-               else
-                       rtl839x_create_tx_header(h, dest_port);
+               priv->r->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;
-
-               /* BUG: before tx fetch, need to make sure right data is accessed
-                * This might not be necessary on newer RTL839x, though.
-                */
-               for (i = 0; i < 10; i++) {
-                       val = sw_r32(priv->r->dma_if_ctrl);
-                       if ((val & 0xc) == 0xc)
-                               break;
+               ring->tx_r[q][ring->c_tx[q]] |= 1;
+
+               // Before starting TX, prevent a Lextra bus bug on RTL8380 SoCs
+               if (priv->family_id == RTL8380_FAMILY_ID) {
+                       for (i = 0; i < 10; i++) {
+                               val = sw_r32(priv->r->dma_if_ctrl);
+                               if ((val & 0xc) == 0xc)
+                                       break;
+                       }
                }
 
                /* Tell switch to send data */
-               sw_w32_mask(0, TX_DO, priv->r->dma_if_ctrl);
+               if (priv->family_id == RTL9310_FAMILY_ID
+                       || priv->family_id == RTL9300_FAMILY_ID) {
+                       // Ring ID q == 0: Low priority, Ring ID = 1: High prio queue
+                       if (!q)
+                               sw_w32_mask(0, BIT(2), priv->r->dma_if_ctrl);
+                       else
+                               sw_w32_mask(0, BIT(3), priv->r->dma_if_ctrl);
+               } else {
+                       sw_w32_mask(0, TX_DO, priv->r->dma_if_ctrl);
+               }
 
                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 +1183,30 @@ txdone:
        return ret;
 }
 
+/*
+ * Return queue number for TX. On the RTL83XX, these queues have equal priority
+ * so we do round-robin
+ */
+u16 rtl83xx_pick_tx_queue(struct net_device *dev, struct sk_buff *skb,
+                         struct net_device *sb_dev)
+{
+       static u8 last = 0;
+
+       last++;
+       return last % TXRINGS;
+}
+
+/*
+ * Return queue number for TX. On the RTL93XX, queue 1 is the high priority queue
+ */
+u16 rtl93xx_pick_tx_queue(struct net_device *dev, struct sk_buff *skb,
+                         struct net_device *sb_dev)
+{
+       if (skb->priority >= TC_PRIO_CONTROL)
+               return 1;
+       return 0;
+}
+
 static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
 {
        struct rtl838x_eth_priv *priv = netdev_priv(dev);
@@ -789,27 +1219,24 @@ 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);
+       struct dsa_tag tag;
 
        spin_lock_irqsave(&priv->lock, flags);
-       last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur(r)));
+       last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4));
+       pr_debug("---------------------------------------------------------- RX - %d\n", r);
 
-       if (&ring->rx_r[r][ring->c_rx[r]] == last) {
-               spin_unlock_irqrestore(&priv->lock, flags);
-               return 0;
-       }
        do {
                if ((ring->rx_r[r][ring->c_rx[r]] & 0x1)) {
-                       netdev_warn(dev, "WARNING Ring contention: ring %x, last %x, current %x, cPTR %x, ISR %x\n", r, (uint32_t)last,
-                                   (u32) &ring->rx_r[r][ring->c_rx[r]],
-                                   ring->rx_r[r][ring->c_rx[r]],
-                               sw_r32(priv->r->dma_if_intr_sts));
+                       if (&ring->rx_r[r][ring->c_rx[r]] != last) {
+                               netdev_warn(dev, "Ring contention: r: %x, last %x, cur %x\n",
+                                   r, (uint32_t)last, (u32) &ring->rx_r[r][ring->c_rx[r]]);
+                       }
                        break;
                }
 
                h = &ring->rx_header[r][ring->c_rx[r]];
                data = (u8 *)KSEG1ADDR(h->buf);
                len = h->len;
-
                if (!len)
                        break;
                work_done++;
@@ -826,7 +1253,7 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
                        /* BUG: Prevent bug on RTL838x SoCs*/
                        if (priv->family_id == RTL8380_FAMILY_ID) {
                                sw_w32(0xffffffff, priv->r->dma_if_rx_ring_size(0));
-                               for (i = 0; i < RXRINGS; i++) {
+                               for (i = 0; i < priv->rxrings; i++) {
                                        /* Update each ring cnt */
                                        val = sw_r32(priv->r->dma_if_rx_ring_cntr(i));
                                        sw_w32(val, priv->r->dma_if_rx_ring_cntr(i));
@@ -839,12 +1266,19 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
                        memcpy(skb->data, (u8 *)KSEG1ADDR(data), len);
                        /* Overwrite CRC with cpu_tag */
                        if (dsa) {
+                               priv->r->decode_tag(h, &tag);
                                skb->data[len-4] = 0x80;
-                               skb->data[len-3] = h->cpu_tag[0] & priv->port_mask;
+                               skb->data[len-3] = tag.port;
                                skb->data[len-2] = 0x10;
                                skb->data[len-1] = 0x00;
+                               if (tag.l2_offloaded)
+                                       skb->data[len-3] |= 0x40;
                        }
 
+                       if (tag.queue >= 0)
+                               pr_debug("Queue: %d, len: %d, reason %d port %d\n",
+                                        tag.queue, len, tag.reason, tag.port);
+
                        skb->protocol = eth_type_trans(skb, dev);
                        dev->stats.rx_packets++;
                        dev->stats.rx_bytes += len;
@@ -856,35 +1290,47 @@ static int rtl838x_hw_receive(struct net_device *dev, int r, int budget)
                        dev->stats.rx_dropped++;
                }
 
-               h->buf = (u8 *)KSEG1ADDR(ring->rx_space
-                               + r * ring->c_rx[r] * RING_BUFFER);
+               /* Reset header structure */
+               memset(h, 0, sizeof(struct p_hdr));
+               h->buf = data;
                h->size = RING_BUFFER;
-               h->len = 0;
-               memset(&h->cpu_tag, 0, sizeof(uint16_t[5]));
 
-               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;
+               ring->rx_r[r][ring->c_rx[r]] = KSEG1ADDR(h) | 0x1 
+                       | (ring->c_rx[r] == (priv->rxringlen - 1) ? WRAP : 0x1);
+               ring->c_rx[r] = (ring->c_rx[r] + 1) % priv->rxringlen;
+               last = (u32 *)KSEG1ADDR(sw_r32(priv->r->dma_if_rx_cur + r * 4));
        } while (&ring->rx_r[r][ring->c_rx[r]] != last && work_done < budget);
 
+       // Update counters
+       priv->r->update_cntr(r, 0);
+
        spin_unlock_irqrestore(&priv->lock, flags);
        return work_done;
 }
 
 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);
+               if (priv->family_id == RTL9300_FAMILY_ID || priv->family_id == RTL9310_FAMILY_ID)
+                       sw_w32(0xffffffff, priv->r->dma_if_intr_rx_done_msk);
+               else
+                       sw_w32_mask(0, 0xf00ff | BIT(r + 8), priv->r->dma_if_intr_msk);
        }
        return work_done;
 }
@@ -960,9 +1406,9 @@ static void rtl838x_mac_an_restart(struct phylink_config *config)
 
        pr_info("In %s\n", __func__);
        /* Restart by disabling and re-enabling link */
-       sw_w32(0x6192D, priv->r->mac_force_mode_ctrl(priv->cpu_port));
+       sw_w32(0x6192D, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
        mdelay(20);
-       sw_w32(0x6192F, priv->r->mac_force_mode_ctrl(priv->cpu_port));
+       sw_w32(0x6192F, priv->r->mac_force_mode_ctrl + priv->cpu_port * 4);
 }
 
 static int rtl838x_mac_pcs_get_state(struct phylink_config *config,
@@ -1114,94 +1560,65 @@ static int rtl838x_set_link_ksettings(struct net_device *ndev,
        return phylink_ethtool_ksettings_set(priv->phylink, cmd);
 }
 
-int rtl839x_read_sds_phy(int phy_addr, int phy_reg)
+static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 {
-       int offset = 0;
-       int reg;
        u32 val;
+       int err;
+       struct rtl838x_eth_priv *priv = bus->priv;
 
-       if (phy_addr == 49)
-               offset = 0x100;
-
-       /* For the RTL8393 internal SerDes, we simulate a PHY ID in registers 2/3
-        * which would otherwise read as 0
-        */
-       if (soc_info.id == 0x8393) {
-               if (phy_reg == 2)
-                       return 0x1c;
-               if (phy_reg == 3)
-                       return 0x8393;
-       }
-
-       reg = (phy_reg << 1) & 0xfc;
-       val = sw_r32(RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg);
-
-       if (phy_reg & 1)
-               val = (val >> 16) & 0xffff;
-       else
-               val &= 0xffff;
+       if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380)
+               return rtl838x_read_sds_phy(mii_id, regnum);
+       err = rtl838x_read_phy(mii_id, 0, regnum, &val);
+       if (err)
+               return err;
        return val;
 }
 
-int rtl838x_read_sds_phy(int phy_addr, int phy_reg)
+static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 {
-       int offset = 0;
        u32 val;
+       int err;
+       struct rtl838x_eth_priv *priv = bus->priv;
 
-       if (phy_addr == 26)
-               offset = 0x100;
-       val = sw_r32(MAPLE_SDS4_FIB_REG0r + offset + (phy_reg << 2)) & 0xffff;
+       if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393)
+               return rtl839x_read_sds_phy(mii_id, regnum);
 
+       err = rtl839x_read_phy(mii_id, 0, regnum, &val);
+       if (err)
+               return err;
        return val;
 }
 
-int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v)
+static int rtl930x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 {
-       int offset = 0;
-       int reg;
        u32 val;
+       int err;
 
-       if (phy_addr == 49)
-               offset = 0x100;
+       // TODO: These are hard-coded for the 2 Fibre Ports of the XGS1210
+       if (mii_id >= 26 && mii_id <= 27)
+               return rtl930x_read_sds_phy(mii_id - 18, 0, regnum);
 
-       reg = (phy_reg << 1) & 0xfc;
-       val = v;
-       if (phy_reg & 1) {
-               val = val << 16;
-               sw_w32_mask(0xffff0000, val,
-                           RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg);
+       if (regnum & MII_ADDR_C45) {
+               regnum &= ~MII_ADDR_C45;
+               err = rtl930x_read_mmd_phy(mii_id, regnum >> 16, regnum & 0xffff, &val);
        } else {
-               sw_w32_mask(0xffff, val,
-                           RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg);
+               err = rtl930x_read_phy(mii_id, 0, regnum, &val);
        }
-
-       return 0;
-}
-
-static int rtl838x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
-{
-       u32 val;
-       int err;
-       struct rtl838x_eth_priv *priv = bus->priv;
-
-       if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380)
-               return rtl838x_read_sds_phy(mii_id, regnum);
-       err = rtl838x_read_phy(mii_id, 0, regnum, &val);
        if (err)
                return err;
        return val;
 }
 
-static int rtl839x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+static int rtl931x_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 {
        u32 val;
        int err;
-       struct rtl838x_eth_priv *priv = bus->priv;
+//     struct rtl838x_eth_priv *priv = bus->priv;
 
-       if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393)
-               return rtl839x_read_sds_phy(mii_id, regnum);
+//     if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393)
+//             return rtl839x_read_sds_phy(mii_id, regnum);
 
-       err = rtl839x_read_phy(mii_id, 0, regnum, &val);
+       err = rtl931x_read_phy(mii_id, 0, regnum, &val);
        if (err)
                return err;
        return val;
@@ -1216,7 +1633,7 @@ static int rtl838x_mdio_write(struct mii_bus *bus, int mii_id,
        if (mii_id >= 24 && mii_id <= 27 && priv->id == 0x8380) {
                if (mii_id == 26)
                        offset = 0x100;
-               sw_w32(value, MAPLE_SDS4_FIB_REG0r + offset + (regnum << 2));
+               sw_w32(value, RTL838X_SDS4_FIB_REG0 + offset + (regnum << 2));
                return 0;
        }
        return rtl838x_write_phy(mii_id, 0, regnum, value);
@@ -1233,6 +1650,32 @@ static int rtl839x_mdio_write(struct mii_bus *bus, int mii_id,
        return rtl839x_write_phy(mii_id, 0, regnum, value);
 }
 
+static int rtl930x_mdio_write(struct mii_bus *bus, int mii_id,
+                             int regnum, u16 value)
+{
+//     struct rtl838x_eth_priv *priv = bus->priv;
+
+//     if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393)
+//             return rtl839x_write_sds_phy(mii_id, regnum, value);
+       if (regnum & MII_ADDR_C45) {
+               regnum &= ~MII_ADDR_C45;
+               return rtl930x_write_mmd_phy(mii_id, regnum >> 16, regnum & 0xffff, value);
+       }
+
+       return rtl930x_write_phy(mii_id, 0, regnum, value);
+}
+
+static int rtl931x_mdio_write(struct mii_bus *bus, int mii_id,
+                             int regnum, u16 value)
+{
+//     struct rtl838x_eth_priv *priv = bus->priv;
+
+//     if (mii_id >= 48 && mii_id <= 49 && priv->id == 0x8393)
+//             return rtl839x_write_sds_phy(mii_id, regnum, value);
+
+       return rtl931x_write_phy(mii_id, 0, regnum, value);
+}
+
 static int rtl838x_mdio_reset(struct mii_bus *bus)
 {
        pr_info("%s called\n", __func__);
@@ -1262,6 +1705,62 @@ static int rtl839x_mdio_reset(struct mii_bus *bus)
        return 0;
 }
 
+static int rtl931x_mdio_reset(struct mii_bus *bus)
+{
+       sw_w32(0x00000000, RTL931X_SMI_PORT_POLLING_CTRL);
+       sw_w32(0x00000000, RTL931X_SMI_PORT_POLLING_CTRL + 4);
+
+       pr_info("%s called\n", __func__);
+
+       return 0;
+}
+
+static int rtl930x_mdio_reset(struct mii_bus *bus)
+{
+       int i;
+       int pos;
+
+       pr_info("RTL930X_SMI_PORT0_15_POLLING_SEL %08x 16-27: %08x\n",
+               sw_r32(RTL930X_SMI_PORT0_15_POLLING_SEL),
+               sw_r32(RTL930X_SMI_PORT16_27_POLLING_SEL));
+
+       pr_info("%s: Enable SMI polling on SMI bus 0, SMI1, SMI2, disable on SMI3\n", __func__);
+       sw_w32_mask(BIT(20) | BIT(21) | BIT(22), BIT(23), RTL930X_SMI_GLB_CTRL);
+
+       pr_info("RTL9300 Powering on SerDes ports\n");
+       rtl9300_sds_power(24, 1);
+       rtl9300_sds_power(25, 1);
+       rtl9300_sds_power(26, 1);
+       rtl9300_sds_power(27, 1);
+       mdelay(200);
+
+       // RTL930X_SMI_PORT0_15_POLLING_SEL 55550000 16-27: 00f9aaaa
+       // i.e SMI=0 for all ports
+       for (i = 0; i < 5; i++)
+               pr_info("port phy: %08x\n", sw_r32(RTL930X_SMI_PORT0_5_ADDR + i *4));
+
+       // 1-to-1 mapping of port to phy-address
+       for (i = 0; i < 24; i++) {
+               pos = (i % 6) * 5;
+               sw_w32_mask(0x1f << pos, i << pos, RTL930X_SMI_PORT0_5_ADDR + (i / 6) * 4);
+       }
+
+       // ports 24 and 25 have PHY addresses 8 and 9, ports 26/27 PHY 26/27
+       sw_w32(8 | 9 << 5 | 26 << 10 | 27 << 15, RTL930X_SMI_PORT0_5_ADDR + 4 * 4);
+
+       // Ports 24 and 25 live on SMI bus 1 and 2
+       sw_w32_mask(0x3 << 16, 0x1 << 16, RTL930X_SMI_PORT16_27_POLLING_SEL);
+       sw_w32_mask(0x3 << 18, 0x2 << 18, RTL930X_SMI_PORT16_27_POLLING_SEL);
+
+       // SMI bus 1 and 2 speak Clause 45 TODO: Configure from .dts
+       sw_w32_mask(0, BIT(17) | BIT(18), RTL930X_SMI_GLB_CTRL);
+
+       // Ports 24 and 25 are 2.5 Gig, set this type (1)
+       sw_w32_mask(0x7 << 12, 1 << 12, RTL930X_SMI_MAC_TYPE_CTRL);
+       sw_w32_mask(0x7 << 15, 1 << 15, RTL930X_SMI_MAC_TYPE_CTRL);
+
+       return 0;
+}
 
 static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv)
 {
@@ -1287,16 +1786,33 @@ static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv)
                goto err_put_node;
        }
 
-       if (priv->family_id == RTL8380_FAMILY_ID) {
+       switch(priv->family_id) {
+       case RTL8380_FAMILY_ID:
                priv->mii_bus->name = "rtl838x-eth-mdio";
                priv->mii_bus->read = rtl838x_mdio_read;
                priv->mii_bus->write = rtl838x_mdio_write;
                priv->mii_bus->reset = rtl838x_mdio_reset;
-       } else {
+               break;
+       case RTL8390_FAMILY_ID:
                priv->mii_bus->name = "rtl839x-eth-mdio";
                priv->mii_bus->read = rtl839x_mdio_read;
                priv->mii_bus->write = rtl839x_mdio_write;
                priv->mii_bus->reset = rtl839x_mdio_reset;
+               break;
+       case RTL9300_FAMILY_ID:
+               priv->mii_bus->name = "rtl930x-eth-mdio";
+               priv->mii_bus->read = rtl930x_mdio_read;
+               priv->mii_bus->write = rtl930x_mdio_write;
+               priv->mii_bus->reset = rtl930x_mdio_reset;
+       //      priv->mii_bus->probe_capabilities = MDIOBUS_C22_C45; TODO for linux 5.9
+               break;
+       case RTL9310_FAMILY_ID:
+               priv->mii_bus->name = "rtl931x-eth-mdio";
+               priv->mii_bus->read = rtl931x_mdio_read;
+               priv->mii_bus->write = rtl931x_mdio_write;
+               priv->mii_bus->reset = rtl931x_mdio_reset;
+//             priv->mii_bus->probe_capabilities = MDIOBUS_C22_C45;  TODO for linux 5.9
+               break;
        }
        priv->mii_bus->priv = priv;
        priv->mii_bus->parent = &priv->pdev->dev;
@@ -1325,12 +1841,46 @@ 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 = rtl83xx_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,
        .ndo_tx_timeout = rtl838x_eth_tx_timeout,
 };
 
+static const struct net_device_ops rtl839x_eth_netdev_ops = {
+       .ndo_open = rtl838x_eth_open,
+       .ndo_stop = rtl838x_eth_stop,
+       .ndo_start_xmit = rtl838x_eth_tx,
+       .ndo_select_queue = rtl83xx_pick_tx_queue,
+       .ndo_set_mac_address = rtl838x_set_mac_address,
+       .ndo_validate_addr = eth_validate_addr,
+       .ndo_set_rx_mode = rtl839x_eth_set_multicast_list,
+       .ndo_tx_timeout = rtl838x_eth_tx_timeout,
+};
+
+static const struct net_device_ops rtl930x_eth_netdev_ops = {
+       .ndo_open = rtl838x_eth_open,
+       .ndo_stop = rtl838x_eth_stop,
+       .ndo_start_xmit = rtl838x_eth_tx,
+       .ndo_select_queue = rtl93xx_pick_tx_queue,
+       .ndo_set_mac_address = rtl838x_set_mac_address,
+       .ndo_validate_addr = eth_validate_addr,
+       .ndo_set_rx_mode = rtl930x_eth_set_multicast_list,
+       .ndo_tx_timeout = rtl838x_eth_tx_timeout,
+};
+
+static const struct net_device_ops rtl931x_eth_netdev_ops = {
+       .ndo_open = rtl838x_eth_open,
+       .ndo_stop = rtl838x_eth_stop,
+       .ndo_start_xmit = rtl838x_eth_tx,
+       .ndo_select_queue = rtl93xx_pick_tx_queue,
+       .ndo_set_mac_address = rtl838x_set_mac_address,
+       .ndo_validate_addr = eth_validate_addr,
+       .ndo_set_rx_mode = rtl931x_eth_set_multicast_list,
+       .ndo_tx_timeout = rtl838x_eth_tx_timeout,
+};
+
 static const struct phylink_mac_ops rtl838x_phylink_ops = {
        .validate = rtl838x_validate,
        .mac_link_state = rtl838x_mac_pcs_get_state,
@@ -1354,7 +1904,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
        const void *mac;
        phy_interface_t phy_mode;
        struct phylink *phylink;
-       int err = 0;
+       int err = 0, i, rxrings, rxringlen;
+       struct ring_b *ring;
 
        pr_info("Probing RTL838X eth device pdev: %x, dev: %x\n",
                (u32)pdev, (u32)(&(pdev->dev)));
@@ -1364,7 +1915,13 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       dev = alloc_etherdev(sizeof(struct rtl838x_eth_priv));
+       rxrings = (soc_info.family == RTL8380_FAMILY_ID 
+                       || soc_info.family == RTL8390_FAMILY_ID) ? 8 : 32;
+       rxrings = rxrings > MAX_RXRINGS ? MAX_RXRINGS : rxrings;
+       rxringlen = MAX_ENTRIES / rxrings;
+       rxringlen = rxringlen > MAX_RXLEN ? MAX_RXLEN : rxringlen;
+
+       dev = alloc_etherdev_mqs(sizeof(struct rtl838x_eth_priv), TXRINGS, rxrings);
        if (!dev) {
                err = -ENOMEM;
                goto err_free;
@@ -1392,8 +1949,8 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
        }
 
        /* Allocate buffer memory */
-       priv->membase = dmam_alloc_coherent(&pdev->dev,
-                               sizeof(struct ring_b) + sizeof(struct notify_b),
+       priv->membase = dmam_alloc_coherent(&pdev->dev, rxrings * rxringlen * RING_BUFFER
+                               sizeof(struct ring_b) + sizeof(struct notify_b),
                                (void *)&dev->mem_start, GFP_KERNEL);
        if (!priv->membase) {
                dev_err(&pdev->dev, "cannot allocate DMA buffer\n");
@@ -1401,6 +1958,10 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
                goto err_free;
        }
 
+       // Allocate ring-buffer space at the end of the allocated memory
+       ring = priv->membase;
+       ring->rx_space = priv->membase + sizeof(struct ring_b) + sizeof(struct notify_b);
+
        spin_lock_init(&priv->lock);
 
        /* obtain device IRQ number */
@@ -1412,6 +1973,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;
@@ -1423,15 +1986,33 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       if (priv->family_id == 0x8390) {
-               priv->cpu_port = RTL839X_CPU_PORT;
-               priv->r = &rtl839x_reg;
-               priv->port_mask = 0x3f;
-       } else {
+       switch (priv->family_id) {
+       case RTL8380_FAMILY_ID:
                priv->cpu_port = RTL838X_CPU_PORT;
                priv->r = &rtl838x_reg;
-               priv->port_mask = 0x1f;
+               dev->netdev_ops = &rtl838x_eth_netdev_ops;
+               break;
+       case RTL8390_FAMILY_ID:
+               priv->cpu_port = RTL839X_CPU_PORT;
+               priv->r = &rtl839x_reg;
+               dev->netdev_ops = &rtl839x_eth_netdev_ops;
+               break;
+       case RTL9300_FAMILY_ID:
+               priv->cpu_port = RTL930X_CPU_PORT;
+               priv->r = &rtl930x_reg;
+               dev->netdev_ops = &rtl930x_eth_netdev_ops;
+               break;
+       case RTL9310_FAMILY_ID:
+               priv->cpu_port = RTL931X_CPU_PORT;
+               priv->r = &rtl931x_reg;
+               dev->netdev_ops = &rtl931x_eth_netdev_ops;
+               break;
+       default:
+               pr_err("Unknown SoC family\n");
+               return -ENODEV;
        }
+       priv->rxringlen = rxringlen;
+       priv->rxrings = rxrings;
 
        rtl8380_init_mac(priv);
 
@@ -1464,7 +2045,6 @@ static int __init rtl838x_eth_probe(struct platform_device *pdev)
        pr_info("Using MAC %08x%08x\n", sw_r32(priv->r->mac),
                                        sw_r32(priv->r->mac + 4));
        strcpy(dev->name, "eth%d");
-       dev->netdev_ops = &rtl838x_eth_netdev_ops;
        priv->pdev = pdev;
        priv->netdev = dev;
 
@@ -1476,7 +2056,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 < priv->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 +2093,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 < priv->rxrings; i++)
+                       netif_napi_del(&priv->rx_qs[i].napi);
+
                unregister_netdev(dev);
                free_netdev(dev);
        }