[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