diff --git a/drivers/net/phy/phylib-internal.h b/drivers/net/phy/phylib-internal.h index ebda74eb60a5..dc9592c6bb8e 100644 --- a/drivers/net/phy/phylib-internal.h +++ b/drivers/net/phy/phylib-internal.h @@ -7,7 +7,6 @@ #define __PHYLIB_INTERNAL_H struct phy_device; -struct mii_bus; /* * phy_supported_speeds - return all speeds currently supported by a PHY device @@ -21,11 +20,6 @@ void of_set_phy_timing_role(struct phy_device *phydev); int phy_speed_down_core(struct phy_device *phydev); void phy_check_downshift(struct phy_device *phydev); -int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45, - int devad, u32 regnum); -int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45, - int devad, u32 regnum, u16 val); - int genphy_c45_read_eee_adv(struct phy_device *phydev, unsigned long *adv); #endif /* __PHYLIB_INTERNAL_H */ diff --git a/drivers/net/phy/phylib.h b/drivers/net/phy/phylib.h index c15484a805b3..0fba245f9745 100644 --- a/drivers/net/phy/phylib.h +++ b/drivers/net/phy/phylib.h @@ -8,6 +8,7 @@ struct device_node; struct phy_device; +struct mii_bus; struct device_node *phy_package_get_node(struct phy_device *phydev); void *phy_package_get_priv(struct phy_device *phydev); @@ -30,5 +31,9 @@ int devm_phy_package_join(struct device *dev, struct phy_device *phydev, int base_addr, size_t priv_size); int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev, size_t priv_size); +int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45, + int devad, u32 regnum); +int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45, + int devad, u32 regnum, u16 val); #endif /* __PHYLIB_H */ diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c index 6ff0385201a5..eb5b540ada0e 100644 --- a/drivers/net/phy/realtek/realtek_main.c +++ b/drivers/net/phy/realtek/realtek_main.c @@ -18,6 +18,7 @@ #include #include +#include "../phylib.h" #include "realtek.h" #define RTL8201F_IER 0x13 @@ -131,9 +132,17 @@ #define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII 0x02 #define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX 0x16 -/* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45 - * is set, they cannot be accessed by C45-over-C22. - */ +#define RTL822X_VND1_SERDES_CMD 0x7587 +#define RTL822X_VND1_SERDES_CMD_WRITE BIT(1) +#define RTL822X_VND1_SERDES_CMD_BUSY BIT(0) +#define RTL822X_VND1_SERDES_ADDR 0x7588 +#define RTL822X_VND1_SERDES_ADDR_AUTONEG 0x2 +#define RTL822X_VND1_SERDES_INBAND_DISABLE 0x71d0 +#define RTL822X_VND1_SERDES_INBAND_ENABLE 0x70d0 +#define RTL822X_VND1_SERDES_DATA 0x7589 + +#define RTL822X_VND2_TO_PAGE(reg) ((reg) >> 4) +#define RTL822X_VND2_TO_PAGE_REG(reg) (16 + (((reg) & GENMASK(3, 0)) >> 1)) #define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg)) #define RTL8221B_VND2_INER 0xa4d2 @@ -1145,7 +1154,8 @@ static int rtlgen_read_status(struct phy_device *phydev) if (!phydev->link) return 0; - val = phy_read_paged(phydev, 0xa43, 0x12); + val = phy_read_paged(phydev, RTL822X_VND2_TO_PAGE(RTL_VND2_PHYSR), + RTL822X_VND2_TO_PAGE_REG(RTL_VND2_PHYSR)); if (val < 0) return val; @@ -1238,6 +1248,79 @@ static int rtl822x_probe(struct phy_device *phydev) return 0; } +/* RTL822x cannot access MDIO_MMD_VEND2 via MII_MMD_CTRL/MII_MMD_DATA. + * A mapping to use paged access needs to be used instead. + * All other MMD devices can be accessed as usual. + */ +static int rtl822xb_read_mmd(struct phy_device *phydev, int devnum, u16 reg) +{ + int oldpage, ret, read_ret; + u16 page; + + /* Use default method for all MMDs except MDIO_MMD_VEND2 or in case + * Clause-45 access is available + */ + if (devnum != MDIO_MMD_VEND2 || phydev->is_c45) + return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr, + phydev->is_c45, devnum, reg); + + /* Use paged access for MDIO_MMD_VEND2 over Clause-22 */ + page = RTL822X_VND2_TO_PAGE(reg); + oldpage = __phy_read(phydev, RTL821x_PAGE_SELECT); + if (oldpage < 0) + return oldpage; + + if (oldpage != page) { + ret = __phy_write(phydev, RTL821x_PAGE_SELECT, page); + if (ret < 0) + return ret; + } + + read_ret = __phy_read(phydev, RTL822X_VND2_TO_PAGE_REG(reg)); + if (oldpage != page) { + ret = __phy_write(phydev, RTL821x_PAGE_SELECT, oldpage); + if (ret < 0) + return ret; + } + + return read_ret; +} + +static int rtl822xb_write_mmd(struct phy_device *phydev, int devnum, u16 reg, + u16 val) +{ + int oldpage, ret, write_ret; + u16 page; + + /* Use default method for all MMDs except MDIO_MMD_VEND2 or in case + * Clause-45 access is available + */ + if (devnum != MDIO_MMD_VEND2 || phydev->is_c45) + return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr, + phydev->is_c45, devnum, reg, val); + + /* Use paged access for MDIO_MMD_VEND2 over Clause-22 */ + page = RTL822X_VND2_TO_PAGE(reg); + oldpage = __phy_read(phydev, RTL821x_PAGE_SELECT); + if (oldpage < 0) + return oldpage; + + if (oldpage != page) { + ret = __phy_write(phydev, RTL821x_PAGE_SELECT, page); + if (ret < 0) + return ret; + } + + write_ret = __phy_write(phydev, RTL822X_VND2_TO_PAGE_REG(reg), val); + if (oldpage != page) { + ret = __phy_write(phydev, RTL821x_PAGE_SELECT, oldpage); + if (ret < 0) + return ret; + } + + return write_ret; +} + static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1) { bool has_2500, has_sgmii; @@ -1308,6 +1391,50 @@ static int rtl822xb_config_init(struct phy_device *phydev) return rtl822x_set_serdes_option_mode(phydev, false); } +static int rtl822x_serdes_write(struct phy_device *phydev, u16 reg, u16 val) +{ + int ret, poll; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_ADDR, reg); + if (ret < 0) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_DATA, val); + if (ret < 0) + return ret; + + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CMD, + RTL822X_VND1_SERDES_CMD_WRITE | + RTL822X_VND1_SERDES_CMD_BUSY); + if (ret < 0) + return ret; + + return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + RTL822X_VND1_SERDES_CMD, poll, + !(poll & RTL822X_VND1_SERDES_CMD_BUSY), + 500, 100000, false); +} + +static int rtl822x_config_inband(struct phy_device *phydev, unsigned int modes) +{ + return rtl822x_serdes_write(phydev, RTL822X_VND1_SERDES_ADDR_AUTONEG, + (modes != LINK_INBAND_DISABLE) ? + RTL822X_VND1_SERDES_INBAND_ENABLE : + RTL822X_VND1_SERDES_INBAND_DISABLE); +} + +static unsigned int rtl822x_inband_caps(struct phy_device *phydev, + phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_SGMII: + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; + default: + return 0; + } +} + static int rtl822xb_get_rate_matching(struct phy_device *phydev, phy_interface_t iface) { @@ -1976,7 +2103,7 @@ static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev) static struct phy_driver realtek_drvs[] = { { PHY_ID_MATCH_EXACT(0x00008201), - .name = "RTL8201CP Ethernet", + .name = "RTL8201CP Ethernet", .read_page = rtl821x_read_page, .write_page = rtl821x_write_page, }, { @@ -2097,126 +2224,152 @@ static struct phy_driver realtek_drvs[] = { .resume = rtlgen_resume, .read_page = rtl821x_read_page, .write_page = rtl821x_write_page, + .read_mmd = rtl822xb_read_mmd, + .write_mmd = rtl822xb_write_mmd, }, { .match_phy_device = rtl8221b_match_phy_device, .name = "RTL8226B_RTL8221B 2.5Gbps PHY", .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, - .config_init = rtl822xb_config_init, + .config_init = rtl822xb_config_init, + .inband_caps = rtl822x_inband_caps, + .config_inband = rtl822x_config_inband, .get_rate_matching = rtl822xb_get_rate_matching, .read_status = rtl822xb_read_status, .suspend = genphy_suspend, .resume = rtlgen_resume, .read_page = rtl821x_read_page, .write_page = rtl821x_write_page, + .read_mmd = rtl822xb_read_mmd, + .write_mmd = rtl822xb_write_mmd, }, { PHY_ID_MATCH_EXACT(0x001cc838), - .name = "RTL8226-CG 2.5Gbps PHY", - .soft_reset = rtl822x_c45_soft_reset, - .get_features = rtl822x_c45_get_features, - .config_aneg = rtl822x_c45_config_aneg, - .config_init = rtl822x_config_init, - .read_status = rtl822xb_c45_read_status, - .suspend = genphy_c45_pma_suspend, - .resume = rtlgen_c45_resume, + .name = "RTL8226-CG 2.5Gbps PHY", + .soft_reset = rtl822x_c45_soft_reset, + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, + .config_init = rtl822x_config_init, + .inband_caps = rtl822x_inband_caps, + .config_inband = rtl822x_config_inband, + .read_status = rtl822xb_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, + .read_mmd = rtl822xb_read_mmd, + .write_mmd = rtl822xb_write_mmd, }, { PHY_ID_MATCH_EXACT(0x001cc848), - .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", - .get_features = rtl822x_get_features, - .config_aneg = rtl822x_config_aneg, - .config_init = rtl822xb_config_init, + .name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY", + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, + .inband_caps = rtl822x_inband_caps, + .config_inband = rtl822x_config_inband, .get_rate_matching = rtl822xb_get_rate_matching, - .read_status = rtl822xb_read_status, - .suspend = genphy_suspend, - .resume = rtlgen_resume, - .read_page = rtl821x_read_page, - .write_page = rtl821x_write_page, + .read_status = rtl822xb_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + .read_mmd = rtl822xb_read_mmd, + .write_mmd = rtl822xb_write_mmd, }, { .match_phy_device = rtl8221b_vb_cg_c22_match_phy_device, - .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)", .probe = rtl822x_probe, - .get_features = rtl822x_get_features, - .config_aneg = rtl822x_config_aneg, - .config_init = rtl822xb_config_init, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, + .inband_caps = rtl822x_inband_caps, + .config_inband = rtl822x_config_inband, .get_rate_matching = rtl822xb_get_rate_matching, - .read_status = rtl822xb_read_status, - .suspend = genphy_suspend, - .resume = rtlgen_resume, - .read_page = rtl821x_read_page, - .write_page = rtl821x_write_page, + .read_status = rtl822xb_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + .read_mmd = rtl822xb_read_mmd, + .write_mmd = rtl822xb_write_mmd, }, { .match_phy_device = rtl8221b_vb_cg_c45_match_phy_device, - .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", + .name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)", .config_intr = rtl8221b_config_intr, .handle_interrupt = rtl8221b_handle_interrupt, .probe = rtl822x_probe, - .config_init = rtl822xb_config_init, + .config_init = rtl822xb_config_init, + .inband_caps = rtl822x_inband_caps, + .config_inband = rtl822x_config_inband, .get_rate_matching = rtl822xb_get_rate_matching, - .get_features = rtl822x_c45_get_features, - .config_aneg = rtl822x_c45_config_aneg, - .read_status = rtl822xb_c45_read_status, - .suspend = genphy_c45_pma_suspend, - .resume = rtlgen_c45_resume, + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, + .read_status = rtl822xb_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, }, { .match_phy_device = rtl8221b_vm_cg_c22_match_phy_device, - .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", + .name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)", .probe = rtl822x_probe, - .get_features = rtl822x_get_features, - .config_aneg = rtl822x_config_aneg, - .config_init = rtl822xb_config_init, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .config_init = rtl822xb_config_init, + .inband_caps = rtl822x_inband_caps, + .config_inband = rtl822x_config_inband, .get_rate_matching = rtl822xb_get_rate_matching, - .read_status = rtl822xb_read_status, - .suspend = genphy_suspend, - .resume = rtlgen_resume, - .read_page = rtl821x_read_page, - .write_page = rtl821x_write_page, + .read_status = rtl822xb_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + .read_mmd = rtl822xb_read_mmd, + .write_mmd = rtl822xb_write_mmd, }, { .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device, - .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)", + .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)", .config_intr = rtl8221b_config_intr, .handle_interrupt = rtl8221b_handle_interrupt, .probe = rtl822x_probe, - .config_init = rtl822xb_config_init, + .config_init = rtl822xb_config_init, + .inband_caps = rtl822x_inband_caps, + .config_inband = rtl822x_config_inband, .get_rate_matching = rtl822xb_get_rate_matching, - .get_features = rtl822x_c45_get_features, - .config_aneg = rtl822x_c45_config_aneg, - .read_status = rtl822xb_c45_read_status, - .suspend = genphy_c45_pma_suspend, - .resume = rtlgen_c45_resume, + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, + .read_status = rtl822xb_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, }, { .match_phy_device = rtl8251b_c45_match_phy_device, - .name = "RTL8251B 5Gbps PHY", + .name = "RTL8251B 5Gbps PHY", .probe = rtl822x_probe, - .get_features = rtl822x_get_features, - .config_aneg = rtl822x_config_aneg, - .read_status = rtl822x_read_status, - .suspend = genphy_suspend, - .resume = rtlgen_resume, - .read_page = rtl821x_read_page, - .write_page = rtl821x_write_page, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, }, { .match_phy_device = rtl_internal_nbaset_match_phy_device, - .name = "Realtek Internal NBASE-T PHY", + .name = "Realtek Internal NBASE-T PHY", .flags = PHY_IS_INTERNAL, .probe = rtl822x_probe, - .get_features = rtl822x_get_features, - .config_aneg = rtl822x_config_aneg, - .read_status = rtl822x_read_status, - .suspend = genphy_suspend, - .resume = rtlgen_resume, - .read_page = rtl821x_read_page, - .write_page = rtl821x_write_page, + .get_features = rtl822x_get_features, + .config_aneg = rtl822x_config_aneg, + .read_status = rtl822x_read_status, + .suspend = genphy_suspend, + .resume = rtlgen_resume, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, .read_mmd = rtl822x_read_mmd, .write_mmd = rtl822x_write_mmd, }, { PHY_ID_MATCH_EXACT(0x001ccad0), .name = "RTL8224 2.5Gbps PHY", .flags = PHY_POLL_CABLE_TEST, - .get_features = rtl822x_c45_get_features, - .config_aneg = rtl822x_c45_config_aneg, - .read_status = rtl822x_c45_read_status, - .suspend = genphy_c45_pma_suspend, - .resume = rtlgen_c45_resume, + .get_features = rtl822x_c45_get_features, + .config_aneg = rtl822x_c45_config_aneg, + .read_status = rtl822x_c45_read_status, + .suspend = genphy_c45_pma_suspend, + .resume = rtlgen_c45_resume, .cable_test_start = rtl8224_cable_test_start, .cable_test_get_status = rtl8224_cable_test_get_status, }, { @@ -2235,7 +2388,7 @@ static struct phy_driver realtek_drvs[] = { }, { PHY_ID_MATCH_EXACT(0x001ccb00), .name = "RTL9000AA_RTL9000AN Ethernet", - .features = PHY_BASIC_T1_FEATURES, + .features = PHY_BASIC_T1_FEATURES, .config_init = rtl9000a_config_init, .config_aneg = rtl9000a_config_aneg, .read_status = rtl9000a_read_status,