[OpenWrt-Devel] [RFC] ar71xx: Reset QCA955x SGMII link on speed change

Sven Eckelmann sven.eckelmann at open-mesh.com
Fri Apr 1 08:41:47 EDT 2016


From: Sven Eckelmann <sven.eckelmann at open-mesh.com>

The SGMII link of the QCA955x seems to be unstable when the PHY changes the
link speed. Reseting the SGMII and the PHY management control seems to
resolve this problem.

This was observed with an AR8033 and QCA9558

The code of this RFC is not meant to be an actual patch. It should show
what the u-boot for QCA955x does and what seemed to work(tm) in my limited
tests. It would be interesting to know whether this was also noticed by
other people and how they fixed it (when they fixed it).

If it is already known than it would maybe good to find a better way to
integrate it with ag71xx. Right now it just uses the set_speed callback to
start the reset.

Signed-off-by: Sven Eckelmann <sven.eckelmann at open-mesh.com>
---
 .../linux/ar71xx/files/arch/mips/ath79/dev-eth.c   | 110 +++++++++++++++++++++
 .../601-MIPS-ath79-add-more-register-defines.patch |   2 +-
 .../601-MIPS-ath79-add-more-register-defines.patch |   2 +-
 3 files changed, 112 insertions(+), 2 deletions(-)

diff --git a/target/linux/ar71xx/files/arch/mips/ath79/dev-eth.c b/target/linux/ar71xx/files/arch/mips/ath79/dev-eth.c
index b43c80a..01c25b2 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/dev-eth.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/dev-eth.c
@@ -373,6 +373,20 @@ static void ar934x_set_speed_ge0(int speed)
 	iounmap(base);
 }
 
+#define QCA955X_GMAC_REG_SGMII_RESET	0x14
+#define QCA955X_GMAC_REG_MR_AN_CONTROL	0x1C
+#define QCA955X_GMAC_REG_SGMII_DEBUG	0x58
+#define SGMII_LINK_WAR_MAX_TRY 20
+
+#define QCA955X_GMAC_REG_MR_AN_CONTROL_AN_ENABLE BIT(12)
+#define QCA955X_GMAC_REG_MR_AN_CONTROL_PHY_RESET BIT(15)
+
+#define QCA955X_GMAC_REG_SGMII_RESET_HW_RX_125M_N BIT(4)
+#define QCA955X_GMAC_REG_SGMII_RESET_TX_125M_N BIT(3)
+#define QCA955X_GMAC_REG_SGMII_RESET_RX_125M_N BIT(2)
+#define QCA955X_GMAC_REG_SGMII_RESET_TX_CLK_N BIT(1)
+#define QCA955X_GMAC_REG_SGMII_RESET_RX_CLK_N BIT(0)
+
 static void qca955x_set_speed_xmii(int speed)
 {
 	void __iomem *base;
@@ -381,6 +395,100 @@ static void qca955x_set_speed_xmii(int speed)
 	base = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE);
 	__raw_writel(val, base + QCA955X_PLL_ETH_XMII_CONTROL_REG);
 	iounmap(base);
+
+	// TODO: find out if something like qca955x_reset_xmii(); is needed
+}
+
+static void qca955x_reset_sgmii(void)
+{
+	void __iomem *base;
+	int count = 0;
+	u32 status;
+	u32 t;
+
+	base = ioremap(QCA955X_GMAC_BASE, QCA955X_GMAC_SIZE);
+
+	/* WARNING ugly PoC code ahead */
+	printk("Resetting SGMII link\n");
+
+	t = QCA955X_GMAC_REG_MR_AN_CONTROL_AN_ENABLE |
+	    QCA955X_GMAC_REG_MR_AN_CONTROL_PHY_RESET;
+	__raw_writel(t, base + QCA955X_GMAC_REG_MR_AN_CONTROL);
+	udelay(10);
+
+	t = QCA955X_GMAC_REG_MR_AN_CONTROL_AN_ENABLE;
+	__raw_writel(t, base + QCA955X_GMAC_REG_MR_AN_CONTROL);
+
+	t = 0;
+	__raw_writel(t, base + QCA955X_GMAC_REG_SGMII_RESET);
+	udelay(10);
+
+	t = QCA955X_GMAC_REG_SGMII_RESET_HW_RX_125M_N;
+	__raw_writel(t, base + QCA955X_GMAC_REG_SGMII_RESET);
+	udelay(10);
+
+	t = QCA955X_GMAC_REG_SGMII_RESET_HW_RX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_RX_125M_N;
+	__raw_writel(t, base + QCA955X_GMAC_REG_SGMII_RESET);
+	udelay(10);
+
+	t = QCA955X_GMAC_REG_SGMII_RESET_HW_RX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_TX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_RX_125M_N;
+	__raw_writel(t, base + QCA955X_GMAC_REG_SGMII_RESET);
+	udelay(10);
+
+	t = QCA955X_GMAC_REG_SGMII_RESET_HW_RX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_TX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_RX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_RX_CLK_N;
+	__raw_writel(t, base + QCA955X_GMAC_REG_SGMII_RESET);
+	udelay(10);
+
+	t = QCA955X_GMAC_REG_SGMII_RESET_HW_RX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_TX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_RX_125M_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_RX_CLK_N |
+	    QCA955X_GMAC_REG_SGMII_RESET_TX_CLK_N;
+	__raw_writel(t, base + QCA955X_GMAC_REG_SGMII_RESET);
+	udelay(10);
+
+	/* TODO this should be an rmw */
+	t = __raw_readl(base + QCA955X_GMAC_REG_MR_AN_CONTROL);
+	t &= ~QCA955X_GMAC_REG_MR_AN_CONTROL_PHY_RESET;
+	__raw_writel(t, base + QCA955X_GMAC_REG_MR_AN_CONTROL);
+	udelay(100);
+
+	status = __raw_readl(base + QCA955X_GMAC_REG_SGMII_DEBUG);
+	status &= 0xff;
+	while (status != 0xf && status != 0x10) {
+		printk("state %#02x\n", status);
+		/* TODO this should be an rmw */
+		t = __raw_readl(base + QCA955X_GMAC_REG_MR_AN_CONTROL);
+		t |= QCA955X_GMAC_REG_MR_AN_CONTROL_PHY_RESET;
+		 __raw_writel(t, base + QCA955X_GMAC_REG_MR_AN_CONTROL);
+
+		udelay(100);
+
+		/* TODO this should be an rmw */
+		t = __raw_readl(base + QCA955X_GMAC_REG_MR_AN_CONTROL);
+		t &= ~QCA955X_GMAC_REG_MR_AN_CONTROL_PHY_RESET;
+		 __raw_writel(t, base + QCA955X_GMAC_REG_MR_AN_CONTROL);
+
+		if (count++ >= SGMII_LINK_WAR_MAX_TRY) {
+			printk("Max resets limit reached exiting...\n");
+			break;
+		}
+
+		mdelay(10);
+
+		status = __raw_readl(base + QCA955X_GMAC_REG_SGMII_DEBUG);
+		status &= 0xff;
+	}
+
+	printk("Reached count %d\n", count);
+
+	iounmap(base);
 }
 
 static void qca955x_set_speed_sgmii(int speed)
@@ -401,6 +509,8 @@ static void qca956x_set_speed_sgmii(int speed)
 	base = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE);
 	__raw_writel(val, base + QCA955X_PLL_ETH_SGMII_CONTROL_REG);
 	iounmap(base);
+
+	qca955x_reset_sgmii();
 }
 
 static void ath79_set_speed_dummy(int speed)
diff --git a/target/linux/ar71xx/patches-4.1/601-MIPS-ath79-add-more-register-defines.patch b/target/linux/ar71xx/patches-4.1/601-MIPS-ath79-add-more-register-defines.patch
index 0126f6a..2da1048 100644
--- a/target/linux/ar71xx/patches-4.1/601-MIPS-ath79-add-more-register-defines.patch
+++ b/target/linux/ar71xx/patches-4.1/601-MIPS-ath79-add-more-register-defines.patch
@@ -47,7 +47,7 @@
  #define QCA955X_PCI_CTRL_SIZE	0x100
  
 +#define QCA955X_GMAC_BASE	(AR71XX_APB_BASE + 0x00070000)
-+#define QCA955X_GMAC_SIZE	0x40
++#define QCA955X_GMAC_SIZE	0x64
  #define QCA955X_WMAC_BASE	(AR71XX_APB_BASE + 0x00100000)
  #define QCA955X_WMAC_SIZE	0x20000
  #define QCA955X_EHCI0_BASE	0x1b000000
diff --git a/target/linux/ar71xx/patches-4.4/601-MIPS-ath79-add-more-register-defines.patch b/target/linux/ar71xx/patches-4.4/601-MIPS-ath79-add-more-register-defines.patch
index 0126f6a..2da1048 100644
--- a/target/linux/ar71xx/patches-4.4/601-MIPS-ath79-add-more-register-defines.patch
+++ b/target/linux/ar71xx/patches-4.4/601-MIPS-ath79-add-more-register-defines.patch
@@ -47,7 +47,7 @@
  #define QCA955X_PCI_CTRL_SIZE	0x100
  
 +#define QCA955X_GMAC_BASE	(AR71XX_APB_BASE + 0x00070000)
-+#define QCA955X_GMAC_SIZE	0x40
++#define QCA955X_GMAC_SIZE	0x64
  #define QCA955X_WMAC_BASE	(AR71XX_APB_BASE + 0x00100000)
  #define QCA955X_WMAC_SIZE	0x20000
  #define QCA955X_EHCI0_BASE	0x1b000000
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list