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.even...@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(+) 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[] = { -- 2.11.0 _______________________________________________ Lede-dev mailing list Lede-dev@lists.infradead.org http://lists.infradead.org/mailman/listinfo/lede-dev