diff options
Diffstat (limited to 'drivers/net/phy/realtek.c')
-rw-r--r-- | drivers/net/phy/realtek.c | 140 |
1 files changed, 118 insertions, 22 deletions
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 25e5bfbb6f89..f65d7f1f348e 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -80,18 +80,22 @@ #define RTL822X_VND2_GANLPAR 0xa414 -#define RTL822X_VND2_PHYSR 0xa434 - #define RTL8366RB_POWER_SAVE 0x15 #define RTL8366RB_POWER_SAVE_ON BIT(12) #define RTL9000A_GINMR 0x14 #define RTL9000A_GINMR_LINK_STATUS BIT(4) -#define RTLGEN_SPEED_MASK 0x0630 +#define RTL_VND2_PHYSR 0xa434 +#define RTL_VND2_PHYSR_DUPLEX BIT(3) +#define RTL_VND2_PHYSR_SPEEDL GENMASK(5, 4) +#define RTL_VND2_PHYSR_SPEEDH GENMASK(10, 9) +#define RTL_VND2_PHYSR_MASTER BIT(11) +#define RTL_VND2_PHYSR_SPEED_MASK (RTL_VND2_PHYSR_SPEEDL | RTL_VND2_PHYSR_SPEEDH) #define RTL_GENERIC_PHYID 0x001cc800 #define RTL_8211FVD_PHYID 0x001cc878 +#define RTL_8221B 0x001cc840 #define RTL_8221B_VB_CG 0x001cc849 #define RTL_8221B_VN_CG 0x001cc84a #define RTL_8251B 0x001cc862 @@ -527,6 +531,9 @@ static int rtl8211f_led_hw_control_get(struct phy_device *phydev, u8 index, { int val; + if (index >= RTL8211F_LED_COUNT) + return -EINVAL; + val = phy_read_paged(phydev, 0xd04, RTL8211F_LEDCR); if (val < 0) return val; @@ -657,9 +664,18 @@ static int rtl8366rb_config_init(struct phy_device *phydev) } /* get actual speed to cover the downshift case */ -static void rtlgen_decode_speed(struct phy_device *phydev, int val) +static void rtlgen_decode_physr(struct phy_device *phydev, int val) { - switch (val & RTLGEN_SPEED_MASK) { + /* bit 3 + * 0: Half Duplex + * 1: Full Duplex + */ + if (val & RTL_VND2_PHYSR_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + switch (val & RTL_VND2_PHYSR_SPEED_MASK) { case 0x0000: phydev->speed = SPEED_10; break; @@ -681,6 +697,19 @@ static void rtlgen_decode_speed(struct phy_device *phydev, int val) default: break; } + + /* bit 11 + * 0: Slave Mode + * 1: Master Mode + */ + if (phydev->speed >= 1000) { + if (val & RTL_VND2_PHYSR_MASTER) + phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; + else + phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; + } else { + phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED; + } } static int rtlgen_read_status(struct phy_device *phydev) @@ -698,7 +727,7 @@ static int rtlgen_read_status(struct phy_device *phydev) if (val < 0) return val; - rtlgen_decode_speed(phydev, val); + rtlgen_decode_physr(phydev, val); return 0; } @@ -921,17 +950,25 @@ static void rtl822xb_update_interface(struct phy_device *phydev) static int rtl822x_read_status(struct phy_device *phydev) { - if (phydev->autoneg == AUTONEG_ENABLE) { - int lpadv = phy_read_paged(phydev, 0xa5d, 0x13); + int lpadv, ret; - if (lpadv < 0) - return lpadv; + ret = rtlgen_read_status(phydev); + if (ret < 0) + return ret; - mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, - lpadv); + if (phydev->autoneg == AUTONEG_DISABLE || + !phydev->autoneg_complete) { + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0); + return 0; } - return rtlgen_read_status(phydev); + lpadv = phy_read_paged(phydev, 0xa5d, 0x13); + if (lpadv < 0) + return lpadv; + + mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv); + + return 0; } static int rtl822xb_read_status(struct phy_device *phydev) @@ -990,6 +1027,10 @@ static int rtl822x_c45_read_status(struct phy_device *phydev) if (ret < 0) return ret; + if (phydev->autoneg == AUTONEG_DISABLE || + !genphy_c45_aneg_done(phydev)) + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, 0); + /* Vendor register as C45 has no standardized support for 1000BaseT */ if (phydev->autoneg == AUTONEG_ENABLE) { val = phy_read_mmd(phydev, MDIO_MMD_VEND2, @@ -1004,11 +1045,11 @@ static int rtl822x_c45_read_status(struct phy_device *phydev) return 0; /* Read actual speed from vendor register. */ - val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_PHYSR); + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL_VND2_PHYSR); if (val < 0) return val; - rtlgen_decode_speed(phydev, val); + rtlgen_decode_physr(phydev, val); return 0; } @@ -1037,6 +1078,23 @@ static bool rtlgen_supports_2_5gbps(struct phy_device *phydev) return val >= 0 && val & MDIO_PMA_SPEED_2_5G; } +/* On internal PHY's MMD reads over C22 always return 0. + * Check a MMD register which is known to be non-zero. + */ +static bool rtlgen_supports_mmd(struct phy_device *phydev) +{ + int val; + + phy_lock_mdio_bus(phydev); + __phy_write(phydev, MII_MMD_CTRL, MDIO_MMD_PCS); + __phy_write(phydev, MII_MMD_DATA, MDIO_PCS_EEE_ABLE); + __phy_write(phydev, MII_MMD_CTRL, MDIO_MMD_PCS | MII_MMD_CTRL_NOINCR); + val = __phy_read(phydev, MII_MMD_DATA); + phy_unlock_mdio_bus(phydev); + + return val > 0; +} + static int rtlgen_match_phy_device(struct phy_device *phydev) { return phydev->phy_id == RTL_GENERIC_PHYID && @@ -1046,7 +1104,8 @@ static int rtlgen_match_phy_device(struct phy_device *phydev) static int rtl8226_match_phy_device(struct phy_device *phydev) { return phydev->phy_id == RTL_GENERIC_PHYID && - rtlgen_supports_2_5gbps(phydev); + rtlgen_supports_2_5gbps(phydev) && + rtlgen_supports_mmd(phydev); } static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id, @@ -1058,6 +1117,11 @@ static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id, return !is_c45 && (id == phydev->phy_id); } +static int rtl8221b_match_phy_device(struct phy_device *phydev) +{ + return phydev->phy_id == RTL_8221B && rtlgen_supports_mmd(phydev); +} + static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev) { return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false); @@ -1078,6 +1142,29 @@ static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev) return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true); } +static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev) +{ + if (phydev->is_c45) + return false; + + switch (phydev->phy_id) { + case RTL_GENERIC_PHYID: + case RTL_8221B: + case RTL_8251B: + case 0x001cc841: + break; + default: + return false; + } + + return rtlgen_supports_2_5gbps(phydev) && !rtlgen_supports_mmd(phydev); +} + +static int rtl8251b_c45_match_phy_device(struct phy_device *phydev) +{ + return rtlgen_is_c45_match(phydev, RTL_8251B, true); +} + static int rtlgen_resume(struct phy_device *phydev) { int ret = genphy_resume(phydev); @@ -1332,10 +1419,8 @@ static struct phy_driver realtek_drvs[] = { .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(0x001cc840), + .match_phy_device = rtl8221b_match_phy_device, .name = "RTL8226B_RTL8221B 2.5Gbps PHY", .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, @@ -1346,8 +1431,6 @@ static struct phy_driver realtek_drvs[] = { .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(0x001cc838), .name = "RTL8226-CG 2.5Gbps PHY", @@ -1415,7 +1498,7 @@ static struct phy_driver realtek_drvs[] = { .suspend = genphy_c45_pma_suspend, .resume = rtlgen_c45_resume, }, { - PHY_ID_MATCH_EXACT(0x001cc862), + .match_phy_device = rtl8251b_c45_match_phy_device, .name = "RTL8251B 5Gbps PHY", .get_features = rtl822x_get_features, .config_aneg = rtl822x_config_aneg, @@ -1425,6 +1508,19 @@ static struct phy_driver realtek_drvs[] = { .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", + .flags = PHY_IS_INTERNAL, + .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", .get_features = rtl822x_c45_get_features, |