ath79: add QCA955x SGMII link loss workaround
authorDavid Bauer <mail@david-bauer.net>
Thu, 11 Apr 2019 15:59:43 +0000 (17:59 +0200)
committerAdrian Schmutzler <freifunk@adrianschmutzler.de>
Thu, 23 Jan 2020 14:28:03 +0000 (15:28 +0100)
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 <mail@david-bauer.net>
target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
target/linux/ath79/patches-4.14/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch [new file with mode: 0644]
target/linux/ath79/patches-4.19/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch [new file with mode: 0644]

index 0924b81b9238fd4b8922423f145bff7cc8513823..6123a26f2c6ea55b7a589272c2d5fe6c003ec36d 100644 (file)
@@ -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 (file)
index 0000000..111c155
--- /dev/null
@@ -0,0 +1,91 @@
+From 60efe35257b063ce584968f9f80b437030ce6ba6 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+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 <mail@david-bauer.net>
+---
+ .../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 (file)
index 0000000..111c155
--- /dev/null
@@ -0,0 +1,91 @@
+From 60efe35257b063ce584968f9f80b437030ce6ba6 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+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 <mail@david-bauer.net>
+---
+ .../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
+  */
+