realtek: Add support for clause45 PHYs
authorBirger Koblitz <git@birger-koblitz.de>
Sat, 1 May 2021 07:18:48 +0000 (09:18 +0200)
committerPetr Štetiar <ynezz@true.cz>
Fri, 7 May 2021 05:05:16 +0000 (07:05 +0200)
This adds support for the MMD access registers the RTL-SoCs use to access clause 45
PHYs via mdio.
This new interface is used to add EEE-support for the RTL8226

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c

index fc401e548107b4d77ba76cee58ae5e32baadf5b1..0abfc6f4d2ae5b8e46491198ca5dfe0186a951ab 100644 (file)
 #define RTL839X_SMI_PORT_POLLING_CTRL  (0x03fc)
 #define RTL839X_PHYREG_ACCESS_CTRL     (0x03DC)
 #define RTL839X_PHYREG_CTRL            (0x03E0)
-#define RTL839X_PHYREG_PORT_CTRL(p)    (0x03E4 + ((p >> 5) << 2))
+#define RTL839X_PHYREG_PORT_CTRL       (0x03E4)
 #define RTL839X_PHYREG_DATA_CTRL       (0x03F0)
+#define RTL839X_PHYREG_MMD_CTRL                (0x3F4)
+
+#define RTL930X_SMI_GLB_CTRL           (0xCA00)
+#define RTL930X_SMI_POLL_CTRL          (0xca90)
+#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08)
+#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C)
+#define RTL930X_SMI_PORT0_5_ADDR       (0xCB80)
+#define RTL930X_SMI_ACCESS_PHY_CTRL_0  (0xCB70)
+#define RTL930X_SMI_ACCESS_PHY_CTRL_1  (0xCB74)
+#define RTL930X_SMI_ACCESS_PHY_CTRL_2  (0xCB78)
+#define RTL930X_SMI_ACCESS_PHY_CTRL_3  (0xCB7C)
+
+#define RTL931X_SMI_GLB_CTRL1          (0x0CBC)
+#define RTL931X_SMI_GLB_CTRL0          (0x0CC0)
+#define RTL931X_SMI_PORT_POLLING_CTRL  (0x0CCC)
+#define RTL931X_SMI_INDRT_ACCESS_CTRL_0        (0x0C00)
+#define RTL931X_SMI_INDRT_ACCESS_CTRL_1        (0x0C04)
+#define RTL931X_SMI_INDRT_ACCESS_CTRL_2        (0x0C08)
+#define RTL931X_SMI_INDRT_ACCESS_CTRL_3        (0x0C10)
+#define RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL (0x0C14)
+#define RTL931X_SMI_INDRT_ACCESS_MMD_CTRL (0xC18)
 
 #define RTL930X_SMI_GLB_CTRL           (0xCA00)
 #define RTL930X_SMI_POLL_CTRL          (0xca90)
index d0e5162b66f86b6470b05a8a17865bf0cafecb81..773225b3c40f633d54a1c6f51e19570644d9d649 100644 (file)
@@ -513,6 +513,79 @@ timeout:
        return -ETIMEDOUT;
 }
 
+/*
+ * Read an mmd register of a PHY
+ */
+int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val)
+{
+       u32 v;
+
+       mutex_lock(&smi_lock);
+
+       if (rtl838x_smi_wait_op(10000))
+               goto timeout;
+
+       sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
+       mdelay(10);
+
+       sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
+
+       v = addr << 16 | reg;
+       sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3);
+
+       /* mmd-access | read | cmd-start */
+       v = 1 << 1 | 0 << 2 | 1;
+       sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
+
+       if (rtl838x_smi_wait_op(10000))
+               goto timeout;
+
+       *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff;
+
+       mutex_unlock(&smi_lock);
+       return 0;
+
+timeout:
+       mutex_unlock(&smi_lock);
+       return -ETIMEDOUT;
+}
+
+/*
+ * Write to an mmd register of a PHY
+ */
+int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val)
+{
+       u32 v;
+
+       pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val);
+       val &= 0xffff;
+       mutex_lock(&smi_lock);
+
+       if (rtl838x_smi_wait_op(10000))
+               goto timeout;
+
+       sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
+       mdelay(10);
+
+       sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
+
+       sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3);
+       sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3);
+       /* mmd-access | write | cmd-start */
+       v = 1 << 1 | 1 << 2 | 1;
+       sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
+
+       if (rtl838x_smi_wait_op(10000))
+               goto timeout;
+
+       mutex_unlock(&smi_lock);
+       return 0;
+
+timeout:
+       mutex_unlock(&smi_lock);
+       return -ETIMEDOUT;
+}
+
 void rtl8380_get_version(struct rtl838x_switch_priv *priv)
 {
        u32 rw_save, info_save;
index b9fdd2c9555c99fc2561ce61a8d0fce590fc2bd2..b2846deb1136bf05c8084e9bc988c6f891181814 100644 (file)
@@ -321,6 +321,7 @@ int rtl8390_sds_power(int mac, int val)
        return 0;
 }
 
+
 int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
 {
        u32 v;
@@ -358,10 +359,9 @@ int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val)
                return -ENOTSUPP;
 
        mutex_lock(&smi_lock);
-       /* Clear both port registers */
-       sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0));
-       sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0) + 4);
-       sw_w32_mask(0, BIT(port), RTL839X_PHYREG_PORT_CTRL(port));
+
+       // Set PHY to access
+       rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL);
 
        sw_w32_mask(0xffff0000, val << 16, RTL839X_PHYREG_DATA_CTRL);
 
@@ -383,6 +383,68 @@ int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val)
        return err;
 }
 
+/*
+ * Read an mmd register of the PHY
+ */
+int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
+{
+       int err = 0;
+       u32 v;
+
+       mutex_lock(&smi_lock);
+
+       // Set PHY to access
+       sw_w32_mask(0xffff << 16, port << 16, RTL839X_PHYREG_DATA_CTRL);
+
+       // Set MMD device number and register to write to
+       sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL);
+
+       v = BIT(2) | BIT(0); // MMD-access | EXEC
+       sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL);
+
+       do {
+               v = sw_r32(RTL839X_PHYREG_ACCESS_CTRL);
+       } while (v & BIT(0));
+       // There is no error-checking via BIT 1 of v, as it does not seem to be set correctly
+       *val = (sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff);
+       pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err);
+
+       mutex_unlock(&smi_lock);
+
+       return err;
+}
+
+/*
+ * Write to an mmd register of the PHY
+ */
+int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val)
+{
+       int err = 0;
+       u32 v;
+
+       mutex_lock(&smi_lock);
+
+       // Set PHY to access
+       rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL);
+
+       // Set data to write
+       sw_w32_mask(0xffff << 16, val << 16, RTL839X_PHYREG_DATA_CTRL);
+
+       // Set MMD device number and register to write to
+       sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL);
+
+       v = BIT(3) | BIT(2) | BIT(0); // WRITE | MMD-access | EXEC
+       sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL);
+
+       do {
+               v = sw_r32(RTL839X_PHYREG_ACCESS_CTRL);
+       } while (v & BIT(0));
+
+       pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err);
+       mutex_unlock(&smi_lock);
+       return err;
+}
+
 void rtl8390_get_version(struct rtl838x_switch_priv *priv)
 {
        u32 info;
index 6c2fde1040d487958334ba2f7402cdddb493e458..4214c7694dd45dc4bb3f0006ee46278dd2eb5c19 100644 (file)
@@ -382,7 +382,6 @@ int rtl9300_sds_power(int mac, int val)
        return 0;
 }
 
-
 int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val)
 {
        u32 v;
@@ -445,7 +444,6 @@ int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
        return err;
 }
 
-
 /*
  * Write to an mmd register of the PHY
  */
index a33941a0eb9b78da10926e0186faac85d7ac7a2e..f98bf7df29d8518aa86fbcb9f69f16b9800c2d66 100644 (file)
@@ -175,6 +175,7 @@ static u64 rtl931x_read_cam(int idx, struct rtl838x_l2_entry *e)
        // TODO: Implement
        return entry;
 }
+
 irqreturn_t rtl931x_switch_irq(int irq, void *dev_id)
 {
        struct dsa_switch *ds = dev_id;
@@ -199,7 +200,6 @@ irqreturn_t rtl931x_switch_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-
 int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val)
 {
        u32 v;
@@ -264,6 +264,73 @@ int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
        return 0;
 }
 
+/*
+ * Read an mmd register of the PHY
+ */
+int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
+{
+       int err = 0;
+       u32 v;
+       int type = 1; // TODO: For C45 PHYs need to set to 2
+
+       mutex_lock(&smi_lock);
+
+       // Set PHY to access via port-number
+       sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL);
+
+       // Set MMD device number and register to write to
+       sw_w32(devnum << 16 | (regnum & 0xffff), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL);
+
+       v = type << 2 | BIT(0); // MMD-access-type | EXEC
+       sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
+
+       do {
+               v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0);
+       } while (v & BIT(0));
+
+       // There is no error-checking via BIT 1 of v, as it does not seem to be set correctly
+
+       *val = (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) & 0xffff);
+
+       pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err);
+
+       mutex_unlock(&smi_lock);
+
+       return err;
+}
+
+/*
+ * Write to an mmd register of the PHY
+ */
+int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val)
+{
+       int err = 0;
+       u32 v;
+       int type = 1; // TODO: For C45 PHYs need to set to 2
+
+       mutex_lock(&smi_lock);
+
+       // Set PHY to access via port-number
+       sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL);
+
+       // Set data to write
+       sw_w32_mask(0xffff << 16, val << 16, RTL931X_SMI_INDRT_ACCESS_CTRL_3);
+
+       // Set MMD device number and register to write to
+       sw_w32(devnum << 16 | (regnum & 0xffff), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL);
+
+       v = BIT(4) | type << 2 | BIT(0); // WRITE | MMD-access-type | EXEC
+       sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
+
+       do {
+               v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0);
+       } while (v & BIT(0));
+
+       pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err);
+       mutex_unlock(&smi_lock);
+       return err;
+}
+
 void rtl931x_print_matrix(void)
 {
        volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0);
index 78953c6d17e55228186c387a081310fb5d5b8c0c..3e187228a9a37ae3979bc998234a86403a5bf94c 100644 (file)
 extern struct rtl83xx_soc_info soc_info;
 extern struct mutex smi_lock;
 
+/*
+ * This lock protects the state of the SoC automatically polling the PHYs over the SMI
+ * bus to detect e.g. link and media changes. For operations on the PHYs such as
+ * patching or other configuration changes such as EEE, polling needs to be disabled
+ * since otherwise these operations may fails or lead to unpredictable results.
+ */
+DEFINE_MUTEX(poll_lock);
+
 static const struct firmware rtl838x_8380_fw;
 static const struct firmware rtl838x_8214fc_fw;
 static const struct firmware rtl838x_8218b_fw;
 
+int rtl838x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
+int rtl838x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
+int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
+int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
 int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
-int rtl930x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val);
+int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
+int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
+int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
 
 static int read_phy(u32 port, u32 page, u32 reg, u32 *val)
 {      switch (soc_info.family) {
@@ -54,6 +68,93 @@ static int write_phy(u32 port, u32 page, u32 reg, u32 val)
        return -1;
 }
 
+static int read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
+{
+       switch (soc_info.family) {
+       case RTL8380_FAMILY_ID:
+               return rtl838x_read_mmd_phy(port, devnum, regnum, val);
+       case RTL8390_FAMILY_ID:
+               return rtl839x_read_mmd_phy(port, devnum, regnum, val);
+       case RTL9300_FAMILY_ID:
+               return rtl930x_read_mmd_phy(port, devnum, regnum, val);
+       case RTL9310_FAMILY_ID:
+               return rtl931x_read_mmd_phy(port, devnum, regnum, val);
+       }
+       return -1;
+}
+
+int write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val)
+{
+       switch (soc_info.family) {
+       case RTL8380_FAMILY_ID:
+               return rtl838x_write_mmd_phy(port, devnum, reg, val);
+       case RTL8390_FAMILY_ID:
+               return rtl839x_write_mmd_phy(port, devnum, reg, val);
+       case RTL9300_FAMILY_ID:
+               return rtl930x_write_mmd_phy(port, devnum, reg, val);
+       case RTL9310_FAMILY_ID:
+               return rtl931x_write_mmd_phy(port, devnum, reg, val);
+       }
+       return -1;
+}
+
+static u64 disable_polling(int port)
+{
+       u64 saved_state;
+
+       mutex_lock(&poll_lock);
+
+       switch (soc_info.family) {
+       case RTL8380_FAMILY_ID:
+               saved_state = sw_r32(RTL838X_SMI_POLL_CTRL);
+               sw_w32_mask(BIT(port), 0, RTL838X_SMI_POLL_CTRL);
+               break;
+       case RTL8390_FAMILY_ID:
+               saved_state = sw_r32(RTL839X_SMI_PORT_POLLING_CTRL + 4);
+               saved_state <<= 32;
+               saved_state |= sw_r32(RTL839X_SMI_PORT_POLLING_CTRL);
+               sw_w32_mask(BIT(port % 32), 0,
+                           RTL839X_SMI_PORT_POLLING_CTRL + ((port >> 5) << 2));
+               break;
+       case RTL9300_FAMILY_ID:
+               saved_state = sw_r32(RTL930X_SMI_POLL_CTRL);
+               sw_w32_mask(BIT(port), 0, RTL930X_SMI_POLL_CTRL);
+               break;
+       case RTL9310_FAMILY_ID:
+               pr_warn("%s not implemented for RTL931X\n", __func__);
+               break;
+       }
+
+       mutex_unlock(&poll_lock);
+
+       return saved_state;
+}
+
+static int resume_polling(u64 saved_state)
+{
+       mutex_lock(&poll_lock);
+
+       switch (soc_info.family) {
+       case RTL8380_FAMILY_ID:
+               sw_w32(saved_state, RTL838X_SMI_POLL_CTRL);
+               break;
+       case RTL8390_FAMILY_ID:
+               sw_w32(saved_state >> 32, RTL839X_SMI_PORT_POLLING_CTRL + 4);
+               sw_w32(saved_state, RTL839X_SMI_PORT_POLLING_CTRL);
+               break;
+       case RTL9300_FAMILY_ID:
+               sw_w32(saved_state, RTL930X_SMI_POLL_CTRL);
+               break;
+       case RTL9310_FAMILY_ID:
+               pr_warn("%s not implemented for RTL931X\n", __func__);
+               break;
+       }
+
+       mutex_unlock(&poll_lock);
+
+       return 0;
+}
+
 static void rtl8380_int_phy_on_off(int mac, bool on)
 {
        u32 val;
@@ -94,18 +195,6 @@ static void rtl8380_phy_reset(int mac)
        write_phy(mac, 0, 0, val | BIT(15));
 }
 
-static void rtl8380_sds_rst(int mac)
-{
-       u32 offset = (mac == 24) ? 0 : 0x100;
-
-       sw_w32_mask(1 << 11, 0, RTL8380_SDS4_FIB_REG0 + offset);
-       sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset);
-       sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset);
-       sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset);
-       sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset);
-       pr_info("SERDES reset: %d\n", mac);
-}
-
 /*
  * Reset the SerDes by powering it off and set a new operations mode
  * of the SerDes. 0x1f is off. Other modes are
@@ -307,6 +396,7 @@ static int rtl8393_read_status(struct phy_device *phydev)
 
        return err;
 }
+
 static int rtl8226_read_page(struct phy_device *phydev)
 {
        return __phy_read(phydev, 0x1f);
@@ -331,20 +421,20 @@ static int rtl8226_read_status(struct phy_device *phydev)
 
        // Link status must be read twice
        for (i = 0; i < 2; i++) {
-               rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA402, &val);
+               read_mmd_phy(port, MMD_VEND2, 0xA402, &val);
        }
        phydev->link = val & BIT(2) ? 1 : 0;
        if (!phydev->link)
                goto out;
 
        // Read duplex status
-       ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
+       ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
        if (ret)
                goto out;
        phydev->duplex = !!(val & BIT(3));
 
        // Read speed
-       ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
+       ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
        switch (val & 0x0630) {
        case 0x0000:
                phydev->speed = SPEED_10;
@@ -371,7 +461,7 @@ out:
        return ret;
 }
 
-static int rtl8266_advertise_aneg(struct phy_device *phydev)
+static int rtl8226_advertise_aneg(struct phy_device *phydev)
 {
        int ret = 0;
        u32 v;
@@ -379,7 +469,7 @@ static int rtl8266_advertise_aneg(struct phy_device *phydev)
 
        pr_info("In %s\n", __func__);
 
-       ret = rtl930x_read_mmd_phy(port, MMD_AN, 16, &v);
+       ret = read_mmd_phy(port, MMD_AN, 16, &v);
        if (ret)
                goto out;
 
@@ -388,31 +478,30 @@ static int rtl8266_advertise_aneg(struct phy_device *phydev)
        v |= BIT(7); // HD 100M
        v |= BIT(8); // FD 100M
 
-       ret = rtl930x_write_mmd_phy(port, MMD_AN, 16, v);
+       ret = write_mmd_phy(port, MMD_AN, 16, v);
 
        // Allow 1GBit
-       ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA412, &v);
+       ret = read_mmd_phy(port, MMD_VEND2, 0xA412, &v);
        if (ret)
                goto out;
        v |= BIT(9); // FD 1000M
 
-       ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA412, v);
+       ret = write_mmd_phy(port, MMD_VEND2, 0xA412, v);
        if (ret)
                goto out;
 
        // Allow 2.5G
-       ret = rtl930x_read_mmd_phy(port, MMD_AN, 32, &v);
+       ret = read_mmd_phy(port, MMD_AN, 32, &v);
        if (ret)
                goto out;
 
        v |= BIT(7);
-       ret = rtl930x_write_mmd_phy(port, MMD_AN, 32, v);
+       ret = write_mmd_phy(port, MMD_AN, 32, v);
 
 out:
        return ret;
 }
 
-
 static int rtl8226_config_aneg(struct phy_device *phydev)
 {
        int ret = 0;
@@ -421,26 +510,26 @@ static int rtl8226_config_aneg(struct phy_device *phydev)
 
        pr_info("In %s\n", __func__);
        if (phydev->autoneg == AUTONEG_ENABLE) {
-               ret = rtl8266_advertise_aneg(phydev);
+               ret = rtl8226_advertise_aneg(phydev);
                if (ret)
                        goto out;
                // AutoNegotiationEnable
-               ret = rtl930x_read_mmd_phy(port, MMD_AN, 0, &v);
+               ret = read_mmd_phy(port, MMD_AN, 0, &v);
                if (ret)
                        goto out;
 
                v |= BIT(12); // Enable AN
-               ret = rtl930x_write_mmd_phy(port, MMD_AN, 0, v);
+               ret = write_mmd_phy(port, MMD_AN, 0, v);
                if (ret)
                        goto out;
 
                // RestartAutoNegotiation
-               ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA400, &v);
+               ret = read_mmd_phy(port, MMD_VEND2, 0xA400, &v);
                if (ret)
                        goto out;
                v |= BIT(9);
 
-               ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA400, v);
+               ret = write_mmd_phy(port, MMD_VEND2, 0xA400, v);
        }
 
        pr_info("%s: Ret is already: %d\n", __func__, ret);
@@ -451,6 +540,68 @@ out:
        return ret;
 }
 
+static int rtl8226_get_eee(struct phy_device *phydev,
+                                    struct ethtool_eee *e)
+{
+       u32 val;
+       int addr = phydev->mdio.addr;
+
+       pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
+
+       read_mmd_phy(addr, MMD_AN, 60, &val);
+       if (e->eee_enabled) {
+               e->eee_enabled = !!(val & BIT(1));
+               if (!e->eee_enabled) {
+                       read_mmd_phy(addr, MMD_AN, 62, &val);
+                       e->eee_enabled = !!(val & BIT(0));
+               }
+       }
+       pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
+
+       return 0;
+}
+
+static int rtl8226_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
+{
+       int port = phydev->mdio.addr;
+       u64 poll_state;
+       bool an_enabled;
+       u32 val;
+
+       pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled);
+
+       poll_state = disable_polling(port);
+
+       // Remember aneg state
+       read_mmd_phy(port, MMD_AN, 0, &val);
+       an_enabled = !!(val & BIT(12));
+
+       // Setup 100/1000MBit
+       read_mmd_phy(port, MMD_AN, 60, &val);
+       if (e->eee_enabled)
+               val |= 0x6;
+       else
+               val &= 0x6;
+       write_mmd_phy(port, MMD_AN, 60, val);
+
+       // Setup 2.5GBit
+       read_mmd_phy(port, MMD_AN, 62, &val);
+       if (e->eee_enabled)
+               val |= 0x1;
+       else
+               val &= 0x1;
+       write_mmd_phy(port, MMD_AN, 62, val);
+
+       // RestartAutoNegotiation
+       read_mmd_phy(port, MMD_VEND2, 0xA400, &val);
+       val |= BIT(9);
+       write_mmd_phy(port, MMD_VEND2, 0xA400, val);
+
+       resume_polling(poll_state);
+
+       return 0;
+}
+
 static struct fw_header *rtl838x_request_fw(struct phy_device *phydev,
                                            const struct firmware *fw,
                                            const char *name)
@@ -750,79 +901,6 @@ static int rtl8218b_ext_match_phy_device(struct phy_device *phydev)
                return phydev->phy_id == PHY_ID_RTL8218B_E;
 }
 
-/*
- * Read an mmd register of the PHY
- */
-static int rtl83xx_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val)
-{
-       u32 v;
-
-       mutex_lock(&smi_lock);
-
-       if (rtl838x_smi_wait_op(10000))
-               goto timeout;
-
-       sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
-       mdelay(10);
-
-       sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
-
-       v = addr << 16 | reg;
-       sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3);
-
-       /* mmd-access | read | cmd-start */
-       v = 1 << 1 | 0 << 2 | 1;
-       sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
-
-       if (rtl838x_smi_wait_op(10000))
-               goto timeout;
-
-       *val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff;
-
-       mutex_unlock(&smi_lock);
-       return 0;
-
-timeout:
-       mutex_unlock(&smi_lock);
-       return -ETIMEDOUT;
-}
-
-/*
- * Write to an mmd register of the PHY
- */
-static int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val)
-{
-       u32 v;
-
-       pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val);
-       val &= 0xffff;
-       mutex_lock(&smi_lock);
-
-       if (rtl838x_smi_wait_op(10000))
-               goto timeout;
-
-       sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
-       mdelay(10);
-
-       sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
-
-       sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3);
-       sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3);
-       /* mmd-access | write | cmd-start */
-       v = 1 << 1 | 1 << 2 | 1;
-       sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
-
-       if (rtl838x_smi_wait_op(10000))
-               goto timeout;
-
-       mutex_unlock(&smi_lock);
-       return 0;
-
-timeout:
-       mutex_unlock(&smi_lock);
-       return -ETIMEDOUT;
-}
-
 static int rtl8218b_read_mmd(struct phy_device *phydev,
                                     int devnum, u16 regnum)
 {
@@ -830,7 +908,7 @@ static int rtl8218b_read_mmd(struct phy_device *phydev,
        u32 val;
        int addr = phydev->mdio.addr;
 
-       ret = rtl83xx_read_mmd_phy(addr, devnum, regnum, &val);
+       ret = read_mmd_phy(addr, devnum, regnum, &val);
        if (ret)
                return ret;
        return val;
@@ -850,8 +928,7 @@ static int rtl8226_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
        int err;
        u32 val;
 
-       err = rtl930x_read_mmd_phy(port, devnum, regnum, &val);
-
+       err = read_mmd_phy(port, devnum, regnum, &val);
        if (err)
                return err;
        return val;
@@ -861,7 +938,7 @@ static int rtl8226_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
 {
        int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr
 
-       return rtl930x_write_mmd_phy(port, devnum, regnum, val);
+       return write_mmd_phy(port, devnum, regnum, val);
 }
 
 static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre)
@@ -957,90 +1034,46 @@ static int rtl8214fc_get_port(struct phy_device *phydev)
        return PORT_MII;
 }
 
-static void rtl8218b_eee_set_u_boot(int port, bool enable)
-{
-       u32 val;
-       bool an_enabled;
-
-       /* Set GPHY page to copper */
-       write_phy(port, 0, 30, 0x0001);
-       read_phy(port, 0, 0, &val);
-       an_enabled = val & (1 << 12);
-
-       if (enable) {
-               /* 100/1000M EEE Capability */
-               write_phy(port, 0, 13, 0x0007);
-               write_phy(port, 0, 14, 0x003C);
-               write_phy(port, 0, 13, 0x4007);
-               write_phy(port, 0, 14, 0x0006);
-
-               read_phy(port, 0x0A43, 25, &val);
-               val |= 1 << 4;
-               write_phy(port, 0x0A43, 25, val);
-       } else {
-               /* 100/1000M EEE Capability */
-               write_phy(port, 0, 13, 0x0007);
-               write_phy(port, 0, 14, 0x003C);
-               write_phy(port, 0, 13, 0x0007);
-               write_phy(port, 0, 14, 0x0000);
-
-               read_phy(port, 0x0A43, 25, &val);
-               val &= ~(1 << 4);
-               write_phy(port, 0x0A43, 25, val);
-       }
-
-       /* Restart AN if enabled */
-       if (an_enabled) {
-               read_phy(port, 0, 0, &val);
-               val |= (1 << 12) | (1 << 9);
-               write_phy(port, 0, 0, val);
-       }
-
-       /* GPHY page back to auto*/
-       write_phy(port, 0xa42, 29, 0);
-}
-
-// TODO: unused
-void rtl8380_rtl8218b_eee_set(int port, bool enable)
+/*
+ * Enable EEE on the RTL8218B PHYs
+ * The method used is not the preferred way (which would be based on the MAC-EEE state,
+ * but the only way that works since the kernel first enables EEE in the MAC
+ * and then sets up the PHY. The MAC-based approach would require the oppsite.
+ */
+void rtl8218d_eee_set(int port, bool enable)
 {
        u32 val;
        bool an_enabled;
 
        pr_debug("In %s %d, enable %d\n", __func__, port, enable);
        /* Set GPHY page to copper */
-       write_phy(port, 0xa42, 29, 0x0001);
+       write_phy(port, 0xa42, 30, 0x0001);
 
        read_phy(port, 0, 0, &val);
-       an_enabled = val & (1 << 12);
+       an_enabled = val & BIT(12);
 
-       /* MAC based EEE */
-       read_phy(port, 0xa43, 25, &val);
-       val &= ~(1 << 5);
-       write_phy(port, 0xa43, 25, val);
-
-       /* 100M / 1000M EEE */
-       if (enable)
-               rtl838x_write_mmd_phy(port, 7, 60, 0x6);
-       else
-               rtl838x_write_mmd_phy(port, 7, 60, 0);
+       /* Enable 100M (bit 1) / 1000M (bit 2) EEE */
+       read_mmd_phy(port, 7, 60, &val);
+       val |= BIT(2) | BIT(1);
+       write_mmd_phy(port, 7, 60, enable ? 0x6 : 0);
 
        /* 500M EEE ability */
        read_phy(port, 0xa42, 20, &val);
        if (enable)
-               val |= 1 << 7;
+               val |= BIT(7);
        else
-               val &= ~(1 << 7);
+               val &= ~BIT(7);
        write_phy(port, 0xa42, 20, val);
 
        /* Restart AN if enabled */
        if (an_enabled) {
                read_phy(port, 0, 0, &val);
-               val |= (1 << 12) | (1 << 9);
+               val |= BIT(9);
                write_phy(port, 0, 0, val);
        }
 
        /* GPHY page back to auto*/
-       write_phy(port, 0xa42, 29, 0);
+       write_phy(port, 0xa42, 30, 0);
 }
 
 static int rtl8218b_get_eee(struct phy_device *phydev,
@@ -1049,16 +1082,21 @@ static int rtl8218b_get_eee(struct phy_device *phydev,
        u32 val;
        int addr = phydev->mdio.addr;
 
-       pr_debug("In %s, port %d\n", __func__, addr);
+       pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
 
        /* Set GPHY page to copper */
        write_phy(addr, 0xa42, 29, 0x0001);
 
-       rtl83xx_read_mmd_phy(addr, 7, 60, &val);
-       if (e->eee_enabled && (!!(val & (1 << 7))))
-               e->eee_enabled = !!(val & (1 << 7));
-       else
-               e->eee_enabled = 0;
+       read_phy(addr, 7, 60, &val);
+       if (e->eee_enabled) {
+               // Verify vs MAC-based EEE
+               e->eee_enabled = !!(val & BIT(7));
+               if (!e->eee_enabled) {
+                       read_phy(addr, 0x0A43, 25, &val);
+                       e->eee_enabled = !!(val & BIT(4));
+               }
+       }
+       pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
 
        /* GPHY page to auto */
        write_phy(addr, 0xa42, 29, 0x0000);
@@ -1066,49 +1104,24 @@ static int rtl8218b_get_eee(struct phy_device *phydev,
        return 0;
 }
 
-// TODO: unused
-void rtl8380_rtl8218b_green_set(int mac, bool enable)
-{
-       u32 val;
-
-       /* Set GPHY page to copper */
-       write_phy(mac, 0xa42, 29, 0x0001);
-
-       write_phy(mac, 0, 27, 0x8011);
-       read_phy(mac, 0, 28, &val);
-       if (enable) {
-               val |= 1 << 9;
-               write_phy(mac, 0, 27, 0x8011);
-               write_phy(mac, 0, 28, val);
-       } else {
-               val &= ~(1 << 9);
-               write_phy(mac, 0, 27, 0x8011);
-               write_phy(mac, 0, 28, val);
-       }
-
-       /* GPHY page to auto */
-       write_phy(mac, 0xa42, 29, 0x0000);
-}
-
-// TODO: unused
-int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e)
+static int rtl8218d_get_eee(struct phy_device *phydev,
+                                    struct ethtool_eee *e)
 {
        u32 val;
        int addr = phydev->mdio.addr;
 
-       pr_debug("In %s %d\n", __func__, addr);
+       pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
+
        /* Set GPHY page to copper */
-       write_phy(addr, 0xa42, 29, 0x0001);
+       write_phy(addr, 0xa42, 30, 0x0001);
 
-       write_phy(addr, 0, 27, 0x8011);
-       read_phy(addr, 0, 28, &val);
-       if (e->eee_enabled && (!!(val & (1 << 9))))
-               e->eee_enabled = !!(val & (1 << 9));
-       else
-               e->eee_enabled = 0;
+       read_phy(addr, 7, 60, &val);
+       if (e->eee_enabled)
+               e->eee_enabled = !!(val & BIT(7));
+       pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
 
        /* GPHY page to auto */
-       write_phy(addr, 0xa42, 29, 0x0000);
+       write_phy(addr, 0xa42, 30, 0x0000);
 
        return 0;
 }
@@ -1116,20 +1129,56 @@ int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e
 static int rtl8214fc_set_eee(struct phy_device *phydev,
                                     struct ethtool_eee *e)
 {
-       u32 pollMask;
-       int addr = phydev->mdio.addr;
+       u32 poll_state;
+       int port = phydev->mdio.addr;
+       bool an_enabled;
+       u32 val;
 
-       pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled);
+       pr_debug("In %s port %d, enabled %d\n", __func__, port, e->eee_enabled);
 
-       if (rtl8380_rtl8214fc_media_is_fibre(addr)) {
-               netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr);
+       if (rtl8380_rtl8214fc_media_is_fibre(port)) {
+               netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", port);
                return -ENOTSUPP;
        }
 
-       pollMask = sw_r32(RTL838X_SMI_POLL_CTRL);
-       sw_w32(0, RTL838X_SMI_POLL_CTRL);
-       rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled);
-       sw_w32(pollMask, RTL838X_SMI_POLL_CTRL);
+       poll_state = disable_polling(port);
+
+       /* Set GPHY page to copper */
+       write_phy(port, 0xa42, 29, 0x0001);
+
+       // Get auto-negotiation status
+       read_phy(port, 0, 0, &val);
+       an_enabled = val & BIT(12);
+
+       pr_info("%s: aneg: %d\n", __func__, an_enabled);
+       read_phy(port, 0x0A43, 25, &val);
+       val &= ~BIT(5);  // Use MAC-based EEE
+       write_phy(port, 0x0A43, 25, val);
+
+       /* Enable 100M (bit 1) / 1000M (bit 2) EEE */
+       write_phy(port, 7, 60, e->eee_enabled ? 0x6 : 0);
+
+       /* 500M EEE ability */
+       read_phy(port, 0xa42, 20, &val);
+       if (e->eee_enabled)
+               val |= BIT(7);
+       else
+               val &= ~BIT(7);
+       write_phy(port, 0xa42, 20, val);
+
+       /* Restart AN if enabled */
+       if (an_enabled) {
+               pr_info("%s: doing aneg\n", __func__);
+               read_phy(port, 0, 0, &val);
+               val |= BIT(9);
+               write_phy(port, 0, 0, val);
+       }
+
+       /* GPHY page back to auto*/
+       write_phy(port, 0xa42, 29, 0);
+
+       resume_polling(poll_state);
+
        return 0;
 }
 
@@ -1147,18 +1196,72 @@ static int rtl8214fc_get_eee(struct phy_device *phydev,
        return rtl8218b_get_eee(phydev, e);
 }
 
-static int rtl8218b_set_eee(struct phy_device *phydev,
-                                    struct ethtool_eee *e)
+static int rtl8218b_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
+{
+       int port = phydev->mdio.addr;
+       u64 poll_state;
+       u32 val;
+       bool an_enabled;
+
+       pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled);
+
+       poll_state = disable_polling(port);
+
+       /* Set GPHY page to copper */
+       write_phy(port, 0, 30, 0x0001);
+       read_phy(port, 0, 0, &val);
+       an_enabled = val & BIT(12);
+
+       if (e->eee_enabled) {
+               /* 100/1000M EEE Capability */
+               write_phy(port, 0, 13, 0x0007);
+               write_phy(port, 0, 14, 0x003C);
+               write_phy(port, 0, 13, 0x4007);
+               write_phy(port, 0, 14, 0x0006);
+
+               read_phy(port, 0x0A43, 25, &val);
+               val |= BIT(4);
+               write_phy(port, 0x0A43, 25, val);
+       } else {
+               /* 100/1000M EEE Capability */
+               write_phy(port, 0, 13, 0x0007);
+               write_phy(port, 0, 14, 0x003C);
+               write_phy(port, 0, 13, 0x0007);
+               write_phy(port, 0, 14, 0x0000);
+
+               read_phy(port, 0x0A43, 25, &val);
+               val &= ~BIT(4);
+               write_phy(port, 0x0A43, 25, val);
+       }
+
+       /* Restart AN if enabled */
+       if (an_enabled) {
+               read_phy(port, 0, 0, &val);
+               val |= BIT(9);
+               write_phy(port, 0, 0, val);
+       }
+
+       /* GPHY page back to auto*/
+       write_phy(port, 0xa42, 30, 0);
+
+       pr_info("%s done\n", __func__);
+       resume_polling(poll_state);
+
+       return 0;
+}
+
+static int rtl8218d_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
 {
-       u32 pollMask;
        int addr = phydev->mdio.addr;
+       u64 poll_state;
 
-       pr_debug("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled);
+       pr_info("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled);
 
-       pollMask = sw_r32(RTL838X_SMI_POLL_CTRL);
-       sw_w32(0, RTL838X_SMI_POLL_CTRL);
-       rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled);
-       sw_w32(pollMask, RTL838X_SMI_POLL_CTRL);
+       poll_state = disable_polling(addr);
+
+       rtl8218d_eee_set(addr, (bool) e->eee_enabled);
+
+       resume_polling(poll_state);
 
        return 0;
 }
@@ -1791,7 +1894,10 @@ static struct phy_driver rtl83xx_phy_driver[] = {
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
                .set_loopback   = genphy_loopback,
-       },      {
+               .set_eee        = rtl8218d_set_eee,
+               .get_eee        = rtl8218d_get_eee,
+       },
+       {
                PHY_ID_MATCH_MODEL(PHY_ID_RTL8226),
                .name           = "REALTEK RTL8226",
                .features       = PHY_GBIT_FEATURES,
@@ -1805,6 +1911,8 @@ static struct phy_driver rtl83xx_phy_driver[] = {
                .write_page     = rtl8226_write_page,
                .read_status    = rtl8226_read_status,
                .config_aneg    = rtl8226_config_aneg,
+               .set_eee        = rtl8226_set_eee,
+               .get_eee        = rtl8226_get_eee,
        },
        {
                PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),