net: phy: realtek: read actual speed to detect downshift
authorHeiner Kallweit <hkallweit1@gmail.com>
Wed, 18 Mar 2020 22:07:24 +0000 (23:07 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 19 Mar 2020 00:05:34 +0000 (17:05 -0700)
At least some integrated PHY's in RTL8168/RTL8125 chip versions support
downshift, and the actual link speed can be read from a vendor-specific
register. Info about this register was provided by Realtek.
More details about downshift configuration (e.g. number of attempts)
aren't available, therefore the downshift tunable is not implemented.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/realtek.c

index f5fa2fff3ddc893e3ff572a7aa81711483e35392..2d99e9de6ee1bb8f9879701fe09ef3fb29fbf7e7 100644 (file)
@@ -49,6 +49,8 @@
 #define RTL_LPADV_5000FULL                     BIT(6)
 #define RTL_LPADV_2500FULL                     BIT(5)
 
+#define RTLGEN_SPEED_MASK                      0x0630
+
 #define RTL_GENERIC_PHYID                      0x001cc800
 
 MODULE_DESCRIPTION("Realtek PHY driver");
@@ -309,6 +311,55 @@ static int rtl8366rb_config_init(struct phy_device *phydev)
        return ret;
 }
 
+/* get actual speed to cover the downshift case */
+static int rtlgen_get_speed(struct phy_device *phydev)
+{
+       int val;
+
+       if (!phydev->link)
+               return 0;
+
+       val = phy_read_paged(phydev, 0xa43, 0x12);
+       if (val < 0)
+               return val;
+
+       switch (val & RTLGEN_SPEED_MASK) {
+       case 0x0000:
+               phydev->speed = SPEED_10;
+               break;
+       case 0x0010:
+               phydev->speed = SPEED_100;
+               break;
+       case 0x0020:
+               phydev->speed = SPEED_1000;
+               break;
+       case 0x0200:
+               phydev->speed = SPEED_10000;
+               break;
+       case 0x0210:
+               phydev->speed = SPEED_2500;
+               break;
+       case 0x0220:
+               phydev->speed = SPEED_5000;
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int rtlgen_read_status(struct phy_device *phydev)
+{
+       int ret;
+
+       ret = genphy_read_status(phydev);
+       if (ret < 0)
+               return ret;
+
+       return rtlgen_get_speed(phydev);
+}
+
 static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
 {
        int ret;
@@ -429,6 +480,8 @@ static int rtl8125_config_aneg(struct phy_device *phydev)
 
 static int rtl8125_read_status(struct phy_device *phydev)
 {
+       int ret;
+
        if (phydev->autoneg == AUTONEG_ENABLE) {
                int lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
 
@@ -443,7 +496,11 @@ static int rtl8125_read_status(struct phy_device *phydev)
                        phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
        }
 
-       return genphy_read_status(phydev);
+       ret = genphy_read_status(phydev);
+       if (ret < 0)
+               return ret;
+
+       return rtlgen_get_speed(phydev);
 }
 
 static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
@@ -550,6 +607,7 @@ static struct phy_driver realtek_drvs[] = {
        }, {
                .name           = "Generic FE-GE Realtek PHY",
                .match_phy_device = rtlgen_match_phy_device,
+               .read_status    = rtlgen_read_status,
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
                .read_page      = rtl821x_read_page,