[LEDE-DEV] ramips: Improve stability of the mt7621 switch

Message ID 20170831082245.22255-1-kristian.evensen@gmail.com
State Not Applicable, archived
Delegated to: Piotr Dymacz
Headers show
Series
  • [LEDE-DEV] ramips: Improve stability of the mt7621 switch
Related show

Commit Message

Kristian Evensen Aug. 31, 2017, 8:22 a.m.
The switch on the mt7621 soc seems to sometimes struggle with Ethernet
pause frames. If a pause frame is received at the incorrect time, the
switch will for some reason hang and no data can be sent. Most of the
time, a router has to be rebooted before the switch will work again,
while sometimes the switch recovers by itself. I am able to reliably
trigger this error on the ZBT WG2926 and 3526 by saturating the
router/switch with small (<100B) packets, or by spamming the router with
pause frames while sending traffic. The error message looks something
like this (and will in most cases loop over and over):

[10203960.260000] mtk_soc_eth 1e100000.ethernet eth0: transmit timed out
[10203960.260000] mtk_soc_eth 1e100000.ethernet eth0: dma_cfg:80000065
[10203960.270000] mtk_soc_eth 1e100000.ethernet eth0: tx_ring=0,
base=0ef10000, max=512, ctx=118, dtx=118, fdx=117, next=118
[10203960.280000] mtk_soc_eth 1e100000.ethernet eth0: rx_ring=0,
base=0ef12000, max=512, calc=380, drx=381

This commit works around the issue by not advertising pause frame
support during auto negotiation. However, I have found at least one
switch which sends pause frames anyway. In case a pause frame is sent
and triggers the error, we reset all ports on the switch. This also
makes the switch work properly again.

Without this change, my routers would crash within 15 minutes when
testing. With the change, the same test has been running for three days
without any problems. Disabling pause frames is maybe not ideal, but
since this error only happens when the router/switch is completely
swamped, then users/protocols/applications should not notice a
difference. It might even be better to drop packets right away, than to
buffer them for a given time.

Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com>
---
 .../drivers/net/ethernet/mtk/gsw_mt7621.c          |  8 +++++++
 .../drivers/net/ethernet/mtk/mtk_eth_soc.c         |  3 +++
 .../drivers/net/ethernet/mtk/mtk_eth_soc.h         |  1 +
 .../drivers/net/ethernet/mtk/soc_mt7621.c          | 26 ++++++++++++++++++++++
 4 files changed, 38 insertions(+)

Patch

diff --git a/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/gsw_mt7621.c b/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/gsw_mt7621.c
index 3adad48c88..9814dae8fa 100644
--- a/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/gsw_mt7621.c
+++ b/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/gsw_mt7621.c
@@ -186,6 +186,14 @@  static void mt7621_hw_init(struct mt7620_gsw *gsw, struct device_node *np)
 	mt7530_mdio_w32(gsw, 0x7a74, 0x44);
 	mt7530_mdio_w32(gsw, 0x7a7c, 0x44);
 
+	/* Disable pause frame  */
+	for (i = 0; i <= 4; i++) {
+		val = _mt7620_mii_read(gsw, i, 0x04);
+		pr_info("Auto-negotiate register: %u %x\n", i, val);
+		val &= ~BIT(10);
+		_mt7620_mii_write(gsw, i, 0x04, val);
+	}
+
 	/* turn on all PHYs */
 	for (i = 0; i <= 4; i++) {
 		val = _mt7620_mii_read(gsw, i, 0);
diff --git a/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/mtk_eth_soc.c b/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/mtk_eth_soc.c
index 5f4afade06..de5b97a996 100644
--- a/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/mtk_eth_soc.c
+++ b/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/mtk_eth_soc.c
@@ -1426,6 +1426,9 @@  static void fe_reset_pending(struct fe_priv *priv)
 		dev_close(dev);
 	}
 	rtnl_unlock();
+
+	if (priv->soc->reset_ports)
+		priv->soc->reset_ports(priv);
 }
 
 static const struct fe_work_t fe_work[] = {
diff --git a/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/mtk_eth_soc.h b/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/mtk_eth_soc.h
index 05f550fa26..0539ce4761 100644
--- a/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/mtk_eth_soc.h
+++ b/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/mtk_eth_soc.h
@@ -392,6 +392,7 @@  struct fe_soc_data {
 			  u16 val);
 	int (*mdio_read)(struct mii_bus *bus, int phy_addr, int phy_reg);
 	void (*mdio_adjust_link)(struct fe_priv *priv, int port);
+	void (*reset_ports)(struct fe_priv *priv);
 
 	void *swpriv;
 	u32 pdma_glo_cfg;
diff --git a/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/soc_mt7621.c b/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/soc_mt7621.c
index ce41b342e7..8f9dc35875 100644
--- a/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/soc_mt7621.c
+++ b/target/linux/ramips/files-4.9/drivers/net/ethernet/mtk/soc_mt7621.c
@@ -16,6 +16,7 @@ 
 #include <linux/platform_device.h>
 #include <linux/if_vlan.h>
 #include <linux/of_net.h>
+#include <linux/delay.h>
 
 #include <asm/mach-ralink/ralink_regs.h>
 
@@ -157,6 +158,30 @@  static void mt7621_set_mac(struct fe_priv *priv, unsigned char *mac)
 	spin_unlock_irqrestore(&priv->page_lock, flags);
 }
 
+static void mt7621_reset_ports(struct fe_priv *priv)
+{
+	struct mt7620_gsw *gsw = priv->soc->swpriv;
+	u8 i;
+	u32 val;
+
+	/* Disable all ports */
+	for (i = 0; i <= 4; i++) {
+		val = _mt7620_mii_read(gsw, i, 0x0);
+		val |= BIT(11);
+		_mt7620_mii_write(gsw, i, 0x0, val);
+	}
+
+	/* Allow ports a (short) time to settle */
+	udelay(1000);
+
+	/* Enable ports */
+	for (i = 0; i <= 4; i++) {
+		val = _mt7620_mii_read(gsw, i, 0);
+		val &= ~BIT(11);
+		_mt7620_mii_write(gsw, i, 0, val);
+	}
+}
+
 static struct fe_soc_data mt7621_data = {
 	.init_data = mt7621_init_data,
 	.reset_fe = mt7621_fe_reset,
@@ -175,6 +200,7 @@  static struct fe_soc_data mt7621_data = {
 	.mdio_read = mt7620_mdio_read,
 	.mdio_write = mt7620_mdio_write,
 	.mdio_adjust_link = mt7620_mdio_link_adjust,
+	.reset_ports = mt7621_reset_ports,
 };
 
 const struct of_device_id of_fe_match[] = {