From: David Bauer Date: Thu, 11 Apr 2019 15:59:43 +0000 (+0200) Subject: ath79: add QCA955x SGMII link loss workaround X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=0d416a8d3b990e3b78628f0e7546527709c877f7;p=oweals%2Fopenwrt.git ath79: add QCA955x SGMII link loss workaround This commit adds a workaround for the loss of the SGMII link observed on the QCA955x generation of SoCs. The workaround originates part from the U-Boot source code, part from the implementation from AVM found in the GPL tarball for the AVM FRITZ!WLAN Repeater 450E. The bug results in a stuck SGMII link between the PHY device and the SoC side. This has only been observed with the Atheros AR8033 PHY and most likely all devices using such combination are affected. It is worked around by reading a hidden SGMII status register and issuing a SGMII PHY reset until the link becomes useable again. Signed-off-by: David Bauer --- diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c index 0924b81b92..6123a26f2c 100644 --- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c +++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c @@ -559,6 +559,112 @@ static void ath79_set_pll(struct ag71xx *ag) udelay(100); } +static void ag71xx_bit_set(void __iomem *reg, u32 bit) +{ + u32 val; + + val = __raw_readl(reg) | bit; + __raw_writel(val, reg); + __raw_readl(reg); +} + +static void ag71xx_bit_clear(void __iomem *reg, u32 bit) +{ + u32 val; + + val = __raw_readl(reg) & ~bit; + __raw_writel(val, reg); + __raw_readl(reg); +} + +static void ag71xx_sgmii_init_qca955x(struct device_node *np) +{ + struct device_node *np_dev; + void __iomem *gmac_base; + u32 mr_an_status; + u32 sgmii_status; + u8 tries = 0; + int err = 0; + + np = of_get_child_by_name(np, "gmac-config"); + if (!np) + return; + + np_dev = of_parse_phandle(np, "device", 0); + if (!np_dev) + goto out; + + gmac_base = of_iomap(np_dev, 0); + if (!gmac_base) { + pr_err("%pOF: can't map GMAC registers\n", np_dev); + err = -ENOMEM; + goto err_iomap; + } + + mr_an_status = __raw_readl(gmac_base + QCA955X_GMAC_REG_MR_AN_STATUS); + if (!(mr_an_status & QCA955X_MR_AN_STATUS_AN_ABILITY)) + goto sgmii_out; + + /* SGMII reset sequence */ + __raw_writel(QCA955X_SGMII_RESET_RX_CLK_N_RESET, + gmac_base + QCA955X_GMAC_REG_SGMII_RESET); + __raw_readl(gmac_base + QCA955X_GMAC_REG_SGMII_RESET); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_HW_RX_125M_N); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_RX_125M_N); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_TX_125M_N); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_RX_CLK_N); + udelay(10); + + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_SGMII_RESET, + QCA955X_SGMII_RESET_TX_CLK_N); + udelay(10); + + /* + * The following is what QCA has to say about what happens here: + * + * Across resets SGMII link status goes to weird state. + * If SGMII_DEBUG register reads other than 0x1f or 0x10, + * we are for sure in a bad state. + * + * Issue a PHY reset in MR_AN_CONTROL to keep going. + */ + do { + ag71xx_bit_set(gmac_base + QCA955X_GMAC_REG_MR_AN_CONTROL, + QCA955X_MR_AN_CONTROL_PHY_RESET | + QCA955X_MR_AN_CONTROL_AN_ENABLE); + udelay(200); + ag71xx_bit_clear(gmac_base + QCA955X_GMAC_REG_MR_AN_CONTROL, + QCA955X_MR_AN_CONTROL_PHY_RESET); + mdelay(300); + sgmii_status = __raw_readl(gmac_base + QCA955X_GMAC_REG_SGMII_DEBUG) & + QCA955X_SGMII_DEBUG_TX_STATE_MASK; + + if (tries++ >= 20) { + pr_err("ag71xx: max retries for SGMII fixup exceeded\n"); + break; + } + } while (!(sgmii_status == 0xf || sgmii_status == 0x10)); + +sgmii_out: + iounmap(gmac_base); +err_iomap: + of_node_put(np_dev); +out: + of_node_put(np); +} + static void ath79_mii_ctrl_set_if(struct ag71xx *ag, unsigned int mii_if) { u32 t; @@ -707,6 +813,8 @@ __ag71xx_link_adjust(struct ag71xx *ag, bool update) of_device_is_compatible(np, "qca,qca9550-eth") || of_device_is_compatible(np, "qca,qca9560-eth")) { ath79_set_pllval(ag); + if (of_property_read_bool(np, "qca955x-sgmii-fixup")) + ag71xx_sgmii_init_qca955x(np); } } diff --git a/target/linux/ath79/patches-4.14/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch b/target/linux/ath79/patches-4.14/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch new file mode 100644 index 0000000000..111c1553d3 --- /dev/null +++ b/target/linux/ath79/patches-4.14/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch @@ -0,0 +1,91 @@ +From 60efe35257b063ce584968f9f80b437030ce6ba6 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Mon, 18 Mar 2019 00:54:06 +0100 +Subject: [PATCH] MIPS: ath79: add missing QCA955x GMAC registers + +This adds missing GMAC register definitions for the Qualcomm Atheros +QCA955X series MIPS SoCs. + +They originate from the platforms U-Boot code and the AVM FRITZ!WLAN +Repeater 450E's GPL tarball. + +Signed-off-by: David Bauer +--- + .../mips/include/asm/mach-ath79/ar71xx_regs.h | 54 +++++++++++++++++++ + 1 file changed, 54 insertions(+) + +--- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h ++++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +@@ -1245,7 +1245,12 @@ + */ + + #define QCA955X_GMAC_REG_ETH_CFG 0x00 ++#define QCA955X_GMAC_REG_SGMII_RESET 0x14 + #define QCA955X_GMAC_REG_SGMII_SERDES 0x18 ++#define QCA955X_GMAC_REG_MR_AN_CONTROL 0x1c ++#define QCA955X_GMAC_REG_MR_AN_STATUS 0x20 ++#define QCA955X_GMAC_REG_SGMII_CONFIG 0x34 ++#define QCA955X_GMAC_REG_SGMII_DEBUG 0x58 + + #define QCA955X_ETH_CFG_RGMII_EN BIT(0) + #define QCA955X_ETH_CFG_MII_GE0 BIT(1) +@@ -1267,9 +1272,58 @@ + #define QCA955X_ETH_CFG_TXE_DELAY_MASK 0x3 + #define QCA955X_ETH_CFG_TXE_DELAY_SHIFT 20 + ++#define QCA955X_SGMII_RESET_RX_CLK_N_RESET 0 ++#define QCA955X_SGMII_RESET_RX_CLK_N BIT(0) ++#define QCA955X_SGMII_RESET_TX_CLK_N BIT(1) ++#define QCA955X_SGMII_RESET_RX_125M_N BIT(2) ++#define QCA955X_SGMII_RESET_TX_125M_N BIT(3) ++#define QCA955X_SGMII_RESET_HW_RX_125M_N BIT(4) ++ + #define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS BIT(15) + #define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT 23 + #define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK 0xf ++ ++#define QCA955X_MR_AN_CONTROL_SPEED_SEL1 BIT(6) ++#define QCA955X_MR_AN_CONTROL_DUPLEX_MODE BIT(8) ++#define QCA955X_MR_AN_CONTROL_RESTART_AN BIT(9) ++#define QCA955X_MR_AN_CONTROL_POWER_DOWN BIT(11) ++#define QCA955X_MR_AN_CONTROL_AN_ENABLE BIT(12) ++#define QCA955X_MR_AN_CONTROL_SPEED_SEL0 BIT(13) ++#define QCA955X_MR_AN_CONTROL_LOOPBACK BIT(14) ++#define QCA955X_MR_AN_CONTROL_PHY_RESET BIT(15) ++ ++#define QCA955X_MR_AN_STATUS_EXT_CAP BIT(0) ++#define QCA955X_MR_AN_STATUS_LINK_UP BIT(2) ++#define QCA955X_MR_AN_STATUS_AN_ABILITY BIT(3) ++#define QCA955X_MR_AN_STATUS_REMOTE_FAULT BIT(4) ++#define QCA955X_MR_AN_STATUS_AN_COMPLETE BIT(5) ++#define QCA955X_MR_AN_STATUS_NO_PREAMBLE BIT(6) ++#define QCA955X_MR_AN_STATUS_BASE_PAGE BIT(7) ++ ++#define QCA955X_SGMII_CONFIG_MODE_CTRL_SHIFT 0 ++#define QCA955X_SGMII_CONFIG_MODE_CTRL_MASK 0x7 ++#define QCA955X_SGMII_CONFIG_ENABLE_SGMII_TX_PAUSE BIT(3) ++#define QCA955X_SGMII_CONFIG_MR_REG4_CHANGED BIT(4) ++#define QCA955X_SGMII_CONFIG_FORCE_SPEED BIT(5) ++#define QCA955X_SGMII_CONFIG_SPEED_SHIFT 6 ++#define QCA955X_SGMII_CONFIG_SPEED_MASK 0xc0 ++#define QCA955X_SGMII_CONFIG_REMOTE_PHY_LOOPBACK BIT(8) ++#define QCA955X_SGMII_CONFIG_NEXT_PAGE_LOADED BIT(9) ++#define QCA955X_SGMII_CONFIG_MDIO_ENABLE BIT(10) ++#define QCA955X_SGMII_CONFIG_MDIO_PULSE BIT(11) ++#define QCA955X_SGMII_CONFIG_MDIO_COMPLETE BIT(12) ++#define QCA955X_SGMII_CONFIG_PRBS_ENABLE BIT(13) ++#define QCA955X_SGMII_CONFIG_BERT_ENABLE BIT(14) ++ ++#define QCA955X_SGMII_DEBUG_TX_STATE_MASK 0xff ++#define QCA955X_SGMII_DEBUG_TX_STATE_SHIFT 0 ++#define QCA955X_SGMII_DEBUG_RX_STATE_MASK 0xff00 ++#define QCA955X_SGMII_DEBUG_RX_STATE_SHIFT 8 ++#define QCA955X_SGMII_DEBUG_RX_SYNC_STATE_MASK 0xff0000 ++#define QCA955X_SGMII_DEBUG_RX_SYNC_STATE_SHIFT 16 ++#define QCA955X_SGMII_DEBUG_ARB_STATE_MASK 0xf000000 ++#define QCA955X_SGMII_DEBUG_ARB_STATE_SHIFT 24 ++ + /* + * QCA956X GMAC Interface + */ + diff --git a/target/linux/ath79/patches-4.19/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch b/target/linux/ath79/patches-4.19/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch new file mode 100644 index 0000000000..111c1553d3 --- /dev/null +++ b/target/linux/ath79/patches-4.19/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch @@ -0,0 +1,91 @@ +From 60efe35257b063ce584968f9f80b437030ce6ba6 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Mon, 18 Mar 2019 00:54:06 +0100 +Subject: [PATCH] MIPS: ath79: add missing QCA955x GMAC registers + +This adds missing GMAC register definitions for the Qualcomm Atheros +QCA955X series MIPS SoCs. + +They originate from the platforms U-Boot code and the AVM FRITZ!WLAN +Repeater 450E's GPL tarball. + +Signed-off-by: David Bauer +--- + .../mips/include/asm/mach-ath79/ar71xx_regs.h | 54 +++++++++++++++++++ + 1 file changed, 54 insertions(+) + +--- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h ++++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +@@ -1245,7 +1245,12 @@ + */ + + #define QCA955X_GMAC_REG_ETH_CFG 0x00 ++#define QCA955X_GMAC_REG_SGMII_RESET 0x14 + #define QCA955X_GMAC_REG_SGMII_SERDES 0x18 ++#define QCA955X_GMAC_REG_MR_AN_CONTROL 0x1c ++#define QCA955X_GMAC_REG_MR_AN_STATUS 0x20 ++#define QCA955X_GMAC_REG_SGMII_CONFIG 0x34 ++#define QCA955X_GMAC_REG_SGMII_DEBUG 0x58 + + #define QCA955X_ETH_CFG_RGMII_EN BIT(0) + #define QCA955X_ETH_CFG_MII_GE0 BIT(1) +@@ -1267,9 +1272,58 @@ + #define QCA955X_ETH_CFG_TXE_DELAY_MASK 0x3 + #define QCA955X_ETH_CFG_TXE_DELAY_SHIFT 20 + ++#define QCA955X_SGMII_RESET_RX_CLK_N_RESET 0 ++#define QCA955X_SGMII_RESET_RX_CLK_N BIT(0) ++#define QCA955X_SGMII_RESET_TX_CLK_N BIT(1) ++#define QCA955X_SGMII_RESET_RX_125M_N BIT(2) ++#define QCA955X_SGMII_RESET_TX_125M_N BIT(3) ++#define QCA955X_SGMII_RESET_HW_RX_125M_N BIT(4) ++ + #define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS BIT(15) + #define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT 23 + #define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK 0xf ++ ++#define QCA955X_MR_AN_CONTROL_SPEED_SEL1 BIT(6) ++#define QCA955X_MR_AN_CONTROL_DUPLEX_MODE BIT(8) ++#define QCA955X_MR_AN_CONTROL_RESTART_AN BIT(9) ++#define QCA955X_MR_AN_CONTROL_POWER_DOWN BIT(11) ++#define QCA955X_MR_AN_CONTROL_AN_ENABLE BIT(12) ++#define QCA955X_MR_AN_CONTROL_SPEED_SEL0 BIT(13) ++#define QCA955X_MR_AN_CONTROL_LOOPBACK BIT(14) ++#define QCA955X_MR_AN_CONTROL_PHY_RESET BIT(15) ++ ++#define QCA955X_MR_AN_STATUS_EXT_CAP BIT(0) ++#define QCA955X_MR_AN_STATUS_LINK_UP BIT(2) ++#define QCA955X_MR_AN_STATUS_AN_ABILITY BIT(3) ++#define QCA955X_MR_AN_STATUS_REMOTE_FAULT BIT(4) ++#define QCA955X_MR_AN_STATUS_AN_COMPLETE BIT(5) ++#define QCA955X_MR_AN_STATUS_NO_PREAMBLE BIT(6) ++#define QCA955X_MR_AN_STATUS_BASE_PAGE BIT(7) ++ ++#define QCA955X_SGMII_CONFIG_MODE_CTRL_SHIFT 0 ++#define QCA955X_SGMII_CONFIG_MODE_CTRL_MASK 0x7 ++#define QCA955X_SGMII_CONFIG_ENABLE_SGMII_TX_PAUSE BIT(3) ++#define QCA955X_SGMII_CONFIG_MR_REG4_CHANGED BIT(4) ++#define QCA955X_SGMII_CONFIG_FORCE_SPEED BIT(5) ++#define QCA955X_SGMII_CONFIG_SPEED_SHIFT 6 ++#define QCA955X_SGMII_CONFIG_SPEED_MASK 0xc0 ++#define QCA955X_SGMII_CONFIG_REMOTE_PHY_LOOPBACK BIT(8) ++#define QCA955X_SGMII_CONFIG_NEXT_PAGE_LOADED BIT(9) ++#define QCA955X_SGMII_CONFIG_MDIO_ENABLE BIT(10) ++#define QCA955X_SGMII_CONFIG_MDIO_PULSE BIT(11) ++#define QCA955X_SGMII_CONFIG_MDIO_COMPLETE BIT(12) ++#define QCA955X_SGMII_CONFIG_PRBS_ENABLE BIT(13) ++#define QCA955X_SGMII_CONFIG_BERT_ENABLE BIT(14) ++ ++#define QCA955X_SGMII_DEBUG_TX_STATE_MASK 0xff ++#define QCA955X_SGMII_DEBUG_TX_STATE_SHIFT 0 ++#define QCA955X_SGMII_DEBUG_RX_STATE_MASK 0xff00 ++#define QCA955X_SGMII_DEBUG_RX_STATE_SHIFT 8 ++#define QCA955X_SGMII_DEBUG_RX_SYNC_STATE_MASK 0xff0000 ++#define QCA955X_SGMII_DEBUG_RX_SYNC_STATE_SHIFT 16 ++#define QCA955X_SGMII_DEBUG_ARB_STATE_MASK 0xf000000 ++#define QCA955X_SGMII_DEBUG_ARB_STATE_SHIFT 24 ++ + /* + * QCA956X GMAC Interface + */ +