[LEDE-DEV] Fwd: [PATCH] [kernel] fix XW ethernet lockup ar71xx-ag71xx-ar803x

Joe Ayers joe at ayerscasa.com
Sat Nov 26 17:12:37 PST 2016


From: Joe Ayers <ae6xe at arrl.net>

Fixes openwrt 19085 nanostation m5 loco xw loses interface

Signed-off-by: Joe Ayers        <ae6xe at arrl.net>
Signed-off-by: Trevor Paskett <k7fpv at aredn.org>
Signed-off-by: Darryl  Quinn    <k5dlq at aredn.org>
Signed-off-by: Conrad Lara     <kg6jei at aredn.org>
---

I'm not entirely sure of the visibility to openwrt remaining group,
but I also sent this to the openwrt dev list.   This was produced from
the aredn.org team.  Feedback appreciated.

Test results:
[    0.000000] Linux version 4.4.14 (joe at AE6XE-PE) (gcc version 5.3.0
(OpenWrt GCC 5.3.0 49994) ) #1 Mon Nov 21 16:47:41 UTC 2016
[    0.000000] MyLoader: sysp=9f65a088, boardp=810d1261, parts=61618340
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU0 revision is: 0001974c (MIPS 74Kc)
[    0.000000] SoC: Atheros AR9342 rev 2
...
[    0.000000] Kernel command line:  board=UBNT-LOCO-XW
mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,7552k(firmware),256k(cfg)ro,64k(EEPROM)ro
console=ttyS0,115200 rootfstype=squashfs,jffs2 noinitrd
...
[    0.648684] libphy: ag71xx_mdio: probed
[    1.239012] ag71xx ag71xx.0: connected to PHY at ag71xx-mdio.0:01
[uid=004dd023, driver=Generic PHY]
[    1.249098] eth0: Atheros AG71xx at 0xb9000000, irq 4, mode:MII
...
[56312.947419] ag71xx_check_reset: expected: 004d, got: 0000
[56312.952909] ag71xx_gpio_reset triggered
[56312.961952] eth0: link down
[56312.965631] br-lan: port 1(eth0) entered disabled state
[56314.958602] eth0: link up (100Mbps/Full duplex)
[56314.963297] br-lan: port 1(eth0) entered forwarding state
[56314.968879] br-lan: port 1(eth0) entered forwarding state
[56316.967075] br-lan: port 1(eth0) entered forwarding state

741-MIPS-ath79-ag71xx-ar803x-lockup-fix.patch

--- a/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx.h
@@ -36,9 +36,10 @@
 #include <asm/mach-ath79/ar71xx_regs.h>
 #include <asm/mach-ath79/ath79.h>
 #include <asm/mach-ath79/ag71xx_platform.h>
+#include <asm/addrspace.h>

 #define AG71XX_DRV_NAME "ag71xx"
-#define AG71XX_DRV_VERSION "0.5.35"
+#define AG71XX_DRV_VERSION "0.5.36"

 #define AG71XX_NAPI_WEIGHT 64
 #define AG71XX_OOM_REFILL (1 + HZ/10)
@@ -370,6 +371,31 @@ ag71xx_ring_size_order(int size)
 #define RX_STATUS_OF BIT(2) /* Rx Overflow */
 #define RX_STATUS_BE BIT(3) /* Bus Error */

+/* GPIO phy reset registers */
+#define AR934X_REG_GPIO_OE_ADDRESS      0x18040000
+#define AR934X_REG_GPIO_OUT             0x18040008
+#define AR934X_REG_GPIO_SET             0x1804000C
+#define AR934X_REG_GPIO_CLEAR           0x18040010
+#define AR934X_EXPECTED_ID1             0x4d
+#define AR934X_PHY_ID1                  2
+
+typedef unsigned int ath_reg_t;
+
+#define ath_reg_rd(_phys)       (*(volatile ath_reg_t *)KSEG1ADDR(_phys))
+
+#define ath_reg_wr_nf(_phys, _val) \
+        ((*(volatile ath_reg_t *)KSEG1ADDR(_phys)) = (_val))
+
+#define ath_reg_wr(_phys, _val) do {    \
+        ath_reg_wr_nf(_phys, _val);     \
+        ath_reg_rd(_phys);              \
+} while(0)
+
+#define ath_reg_rmw_set(_reg, _mask)    do {                    \
+        ath_reg_wr((_reg), (ath_reg_rd((_reg)) | (_mask)));     \
+        ath_reg_rd((_reg));                                     \
+} while(0)
+
+#define ath_reg_rmw_clear(_reg, _mask)    do {                  \
+        ath_reg_wr((_reg), (ath_reg_rd((_reg)) | (_mask)));     \
+        ath_reg_rd((_reg));                                     \
+} while(0)
+
 static inline void ag71xx_check_reg_offset(struct ag71xx *ag, unsigned reg)
 {
  switch (reg) {
@@ -490,4 +516,6 @@ u16 ar7240sw_phy_read(struct mii_bus *mi
 int ar7240sw_phy_write(struct mii_bus *mii, unsigned phy_addr,
        unsigned reg_addr, u16 reg_val);

+void ag71xx_check_reset(struct ag71xx *ag, bool reset);
+
 #endif /* _AG71XX_H */
--- a/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
+++ b/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
@@ -511,6 +511,86 @@ static void ag71xx_hw_init(struct ag71xx
  ag71xx_dma_reset(ag);
 }

+static void ag71xx_gpio_reset(struct ag71xx *ag) {
+    pr_info("ag71xx_gpio_reset triggered\n");
+
+    if ((ath_reg_rd(AR934X_REG_GPIO_OUT) & BIT(0)) == 0) {
+        // Set GPIO0 to 1 (not in reset)
+        ath_reg_wr(AR934X_REG_GPIO_SET, BIT(0));
+    }
+
+    if (ath_reg_rd(AR934X_REG_GPIO_OE_ADDRESS) & BIT(0)) {
+        // Set GPIO0 as output
+        ath_reg_rmw_clear(AR934X_REG_GPIO_OE_ADDRESS, BIT(0));
+    }
+
+    ath_reg_wr(AR934X_REG_GPIO_CLEAR, BIT(0));
+    mdelay(2);
+    ath_reg_wr(AR934X_REG_GPIO_SET, BIT(0));
+    mdelay(2);
+}
+
+void ag71xx_check_reset(struct ag71xx *ag, bool reset)
+{
+    int retries;
+    struct phy_device *phydev = ag->phy_dev;
+    struct net_device *dev = ag->dev;
+    uint16_t phy_id;
+    u32 rx_ds;
+    u32 mii_reg;
+
+    if (!soc_is_ar934x()) return;
+
+    phy_id = phy_read(phydev, AR934X_PHY_ID1);
+
+    if (!reset && phy_id == AR934X_EXPECTED_ID1) {
+        //No PHY hang detected
+        return;
+    }
+
+    pr_info("ag71xx_check_reset: expected: %04x, got: %04x\n",
AR934X_EXPECTED_ID1, phy_id);
+
+    ag71xx_hw_stop(ag);
+    wmb();
+
+    mii_reg = ag71xx_rr(ag, AG71XX_REG_MII_CFG);
+    rx_ds = ag71xx_rr(ag, AG71XX_REG_RX_DESC);
+
+    ag71xx_gpio_reset(ag);
+
+    phy_id = phy_read(phydev, AR934X_PHY_ID1);
+
+    retries = 102; //To be sure last try > 10ms after reset
+
+    while (phy_id != AR934X_EXPECTED_ID1 && --retries) {
+        phy_id = phy_read(phydev, AR934X_PHY_ID1);
+        udelay(100);
+    }
+
+    phy_id = phy_read(phydev, AR934X_PHY_ID1);
+
+    if (phy_id != AR934X_EXPECTED_ID1) return;
+
+    ag71xx_dma_reset(ag);
+    ag71xx_hw_setup(ag);
+    ag71xx_tx_packets(ag, true);
+    ag->tx_ring.curr = 0;
+    ag->tx_ring.dirty = 0;
+    netdev_reset_queue(ag->dev);
+
+    /* setup max frame length */
+    ag71xx_wr(ag, AG71XX_REG_MAC_MFL,
+        ag71xx_max_frame_len(ag->dev->mtu));
+
+    ag71xx_wr(ag, AG71XX_REG_RX_DESC, rx_ds);
+    ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma);
+    ag71xx_wr(ag, AG71XX_REG_MII_CFG, mii_reg);
+
+    ag71xx_hw_set_macaddr(ag, dev->dev_addr);
+
+    return;
+}
+
 static void ag71xx_fast_reset(struct ag71xx *ag)
 {
  struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
@@ -570,6 +650,8 @@ __ag71xx_link_adjust(struct ag71xx *ag,
  u32 fifo5;
  u32 fifo3;

+        ag71xx_check_reset(ag, false);
+
  if (!ag->link && update) {
  ag71xx_hw_stop(ag);
  netif_carrier_off(ag->dev);



More information about the Lede-dev mailing list