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

Message ID 1459514507-20044-1-git-send-email-sven@open-mesh.com
State RFC
Headers show

Commit Message

Sven Eckelmann April 1, 2016, 12:41 p.m.
From: Sven Eckelmann <sven.eckelmann@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@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(-)

Comments

Sven Eckelmann April 4, 2016, 4:24 p.m. | #1
On Friday 01 April 2016 14:41:47 Sven Eckelmann wrote:
> From: Sven Eckelmann <sven.eckelmann@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@open-mesh.com>
> ---

Just did a search in the codeaurora repository. I found at least some
extra information:

* SGMII reset code (like in this "RFC"/u-boot):
  https://us.codeaurora.org/cgit/quic/qsdk/oss/system/openwrt/commit/?id=38de68c74bac2147250ce21879a6f1a7adda5d49

* The IG_ACL_CSR (AG71XX_REG_IG_ACL) change from my second hack:
  https://us.codeaurora.org/cgit/quic/qsdk/oss/system/openwrt/commit/?id=7e0e30d4a5e40927a9fa005a0c9ee9c7e16dc020
  (the information about the chip family is not that easily available
   in the original OpenWrt driver. so it is not that easy to implement
   without adding more stuff to pdata - but easy when the pdata can
   also be modified at the same time)

* The "description" of the SGMII_DEBUG register can be found in the
  u-boot sources (955x.h)

  https://us.codeaurora.org/cgit/quic/qsdk/oss/system/openwrt/commit/?id=96779a2ee2470c15135dc1a7aa80d20eced4dc89

Kind regards,
	Sven

Patch

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