net: phy: at803x: improve the WOL feature
authorLuo Jie <luoj@codeaurora.org>
Sun, 24 Oct 2021 08:27:27 +0000 (16:27 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 25 Oct 2021 13:04:18 +0000 (14:04 +0100)
The wol feature is controlled by the MMD3.8012 bit5,
need to set this bit when the wol function is enabled.

The reg18 bit0 is for enabling WOL interrupt, when wol
occurs, the wol interrupt status reg19 bit0 is set to 1.

Call phy_trigger_machine if there are any other interrupt
pending in the function set_wol.

Signed-off-by: Luo Jie <luoj@codeaurora.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/at803x.c

index cf25cd4..1363f12 100644 (file)
@@ -70,6 +70,8 @@
 #define AT803X_CDT_STATUS_DELTA_TIME_MASK      GENMASK(7, 0)
 #define AT803X_LED_CONTROL                     0x18
 
+#define AT803X_PHY_MMD3_WOL_CTRL               0x8012
+#define AT803X_WOL_EN                          BIT(5)
 #define AT803X_LOC_MAC_ADDR_0_15_OFFSET                0x804C
 #define AT803X_LOC_MAC_ADDR_16_31_OFFSET       0x804B
 #define AT803X_LOC_MAC_ADDR_32_47_OFFSET       0x804A
@@ -335,8 +337,7 @@ static int at803x_set_wol(struct phy_device *phydev,
 {
        struct net_device *ndev = phydev->attached_dev;
        const u8 *mac;
-       int ret;
-       u32 value;
+       int ret, irq_enabled;
        unsigned int i;
        const unsigned int offsets[] = {
                AT803X_LOC_MAC_ADDR_32_47_OFFSET,
@@ -357,18 +358,45 @@ static int at803x_set_wol(struct phy_device *phydev,
                        phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
                                      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
 
+               /* Enable WOL function */
+               ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_PHY_MMD3_WOL_CTRL,
+                               0, AT803X_WOL_EN);
+               if (ret)
+                       return ret;
+               /* Enable WOL interrupt */
                ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
                if (ret)
                        return ret;
-               value = phy_read(phydev, AT803X_INTR_STATUS);
        } else {
+               /* Disable WoL function */
+               ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_PHY_MMD3_WOL_CTRL,
+                               AT803X_WOL_EN, 0);
+               if (ret)
+                       return ret;
+               /* Disable WOL interrupt */
                ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
                if (ret)
                        return ret;
-               value = phy_read(phydev, AT803X_INTR_STATUS);
        }
 
-       return ret;
+       /* Clear WOL status */
+       ret = phy_read(phydev, AT803X_INTR_STATUS);
+       if (ret < 0)
+               return ret;
+
+       /* Check if there are other interrupts except for WOL triggered when PHY is
+        * in interrupt mode, only the interrupts enabled by AT803X_INTR_ENABLE can
+        * be passed up to the interrupt PIN.
+        */
+       irq_enabled = phy_read(phydev, AT803X_INTR_ENABLE);
+       if (irq_enabled < 0)
+               return irq_enabled;
+
+       irq_enabled &= ~AT803X_INTR_ENABLE_WOL;
+       if (ret & irq_enabled && !phy_polling_mode(phydev))
+               phy_trigger_machine(phydev);
+
+       return 0;
 }
 
 static void at803x_get_wol(struct phy_device *phydev,
@@ -379,8 +407,11 @@ static void at803x_get_wol(struct phy_device *phydev,
        wol->supported = WAKE_MAGIC;
        wol->wolopts = 0;
 
-       value = phy_read(phydev, AT803X_INTR_ENABLE);
-       if (value & AT803X_INTR_ENABLE_WOL)
+       value = phy_read_mmd(phydev, MDIO_MMD_PCS, AT803X_PHY_MMD3_WOL_CTRL);
+       if (value < 0)
+               return;
+
+       if (value & AT803X_WOL_EN)
                wol->wolopts |= WAKE_MAGIC;
 }