[openwrt/openwrt] realtek: Add support for clause45 PHYs

LEDE Commits lede-commits at lists.infradead.org
Thu May 6 22:06:10 PDT 2021


ynezz pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/408990464c77abdb341d18346d192c0c14c9461c

commit 408990464c77abdb341d18346d192c0c14c9461c
Author: Birger Koblitz <git at birger-koblitz.de>
AuthorDate: Sat May 1 09:18:48 2021 +0200

    realtek: Add support for clause45 PHYs
    
    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 at birger-koblitz.de>
---
 .../mips/include/asm/mach-rtl838x/mach-rtl83xx.h   |  23 +-
 .../files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c    |  73 +++
 .../files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c    |  70 ++-
 .../files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c    |   2 -
 .../files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c    |  69 ++-
 .../files-5.4/drivers/net/phy/rtl83xx-phy.c        | 560 ++++++++++++---------
 6 files changed, 563 insertions(+), 234 deletions(-)

diff --git a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
index fc401e5481..0abfc6f4d2 100644
--- a/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
+++ b/target/linux/realtek/files-5.4/arch/mips/include/asm/mach-rtl838x/mach-rtl83xx.h
@@ -313,8 +313,29 @@
 #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)
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
index d0e5162b66..773225b3c4 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl838x.c
@@ -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;
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
index b9fdd2c955..b2846deb11 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl839x.c
@@ -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;
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
index 6c2fde1040..4214c7694d 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl930x.c
@@ -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
  */
diff --git a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
index a33941a0eb..f98bf7df29 100644
--- a/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
+++ b/target/linux/realtek/files-5.4/drivers/net/dsa/rtl83xx/rtl931x.c
@@ -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);
diff --git a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
index 78953c6d17..3e187228a9 100644
--- a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
+++ b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
@@ -18,12 +18,26 @@
 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),



More information about the lede-commits mailing list