diff mbox

[OpenWrt-Devel,kernel] fix XW ethernet lockup ar71xx-ag71xx-ar803x

Message ID CAALvt2PQBAwZ_x0-umCWvHHZWr2WPp8=xPjwy_ZRyHx4NbvWLA@mail.gmail.com
State Changes Requested
Delegated to: John Crispin
Headers show

Commit Message

Joe Ayers Nov. 27, 2016, 12:19 a.m. UTC
From: Joe Ayers <ae6xe@arrl. <ae6xe@aredn.org>net>

Fixes 19085 nanostation m5 loco xw loses interface

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

Test results:
[    0.000000] Linux version 4.4.14 (joe@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

  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);

Comments

Felix Fietkau Nov. 30, 2016, 11:07 a.m. UTC | #1
On 2016-11-27 01:19, Joe Ayers wrote:
> From: Joe Ayers <ae6xe@arrl.net>
> 
> Fixes 19085 nanostation m5 loco xw loses interface
> 
> Signed-off-by: Joe Ayers        <ae6xe@arrl.net>
> Signed-off-by: Trevor Paskett <k7fpv@aredn.org>
> Signed-off-by: Darryl  Quinn    <k5dlq@aredn.org>
> Signed-off-by: Conrad Lara     <kg6jei@aredn.org>
This fix is somewhat hackish, because it pushes a workaround for one
particular PHY in the ethernet driver, potentially affecting many more
devices.

I've added support for this PHY to the at803x driver, including GPIO
reset support and enabled it for this device.

I've pushed my changes to my LEDE staging tree at:
https://git.lede-project.org/?p=lede/nbd/staging.git;a=summary
git://git.lede-project.org/lede/nbd/staging.git

Please test if this resolves the issues for you as well.

Thanks,

- Felix
diff mbox

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)
 {