diff options
author | Birger Koblitz <git@birger-koblitz.de> | 2021-05-01 09:18:48 +0200 |
---|---|---|
committer | Petr Štetiar <ynezz@true.cz> | 2021-05-07 07:05:16 +0200 |
commit | 408990464c77abdb341d18346d192c0c14c9461c (patch) | |
tree | 618ff9a42bc6bdd8f029403dff53143fd46324aa /target/linux/realtek | |
parent | 515d9c85f22241b4ec08dbb3d63f6469ef412384 (diff) | |
download | upstream-408990464c77abdb341d18346d192c0c14c9461c.tar.gz upstream-408990464c77abdb341d18346d192c0c14c9461c.tar.bz2 upstream-408990464c77abdb341d18346d192c0c14c9461c.zip |
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@birger-koblitz.de>
Diffstat (limited to 'target/linux/realtek')
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), |