@@ -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 */
@@ -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)
{