[OpenWrt-Devel,2/4] ath79: add QCA955x SGMII link loss workaround
diff mbox series

Message ID 20190411155945.28600-2-mail@david-bauer.net
State New
Headers show
Series
  • [OpenWrt-Devel,1/4] ath79: fix QCA955x GMAC register size
Related show

Commit Message

David Bauer April 11, 2019, 3:59 p.m. UTC
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>
---
 .../net/ethernet/atheros/ag71xx/ag71xx_main.c | 108 ++++++++++++++++++
 ...9-add-missing-QCA955x-GMAC-registers.patch |  91 +++++++++++++++
 ...9-add-missing-QCA955x-GMAC-registers.patch |  91 +++++++++++++++
 3 files changed, 290 insertions(+)
 create mode 100644 target/linux/ath79/patches-4.14/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch
 create mode 100644 target/linux/ath79/patches-4.19/0038-MIPS-ath79-add-missing-QCA955x-GMAC-registers.patch

Patch
diff mbox series

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 8cff56a11a..a7565e6ffb 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;
@@ -705,6 +811,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 <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
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 <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
+  */
+