]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: phy: vitesse: implement downshift in vsc73xx phys
authorPawel Dembicki <paweldembicki@gmail.com>
Fri, 2 Aug 2024 05:16:09 +0000 (07:16 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 5 Aug 2024 12:46:05 +0000 (13:46 +0100)
This commit implements downshift feature in vsc73xx family phys.

By default downshift was enabled with maximum tries.

Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/vitesse.c

index 897b979ec03c81593505d24fb4a51e502ddb5e83..67ac4a2756ca2b85dd889e78166886dd2f8d3d1d 100644 (file)
 #include <linux/mii.h>
 #include <linux/ethtool.h>
 #include <linux/phy.h>
+#include <linux/bitfield.h>
 
 /* Vitesse Extended Page Magic Register(s) */
+#define MII_VSC73XX_EXT_PAGE_1E                0x01
 #define MII_VSC82X4_EXT_PAGE_16E       0x10
 #define MII_VSC82X4_EXT_PAGE_17E       0x11
 #define MII_VSC82X4_EXT_PAGE_18E       0x12
 /* Vitesse Extended Page Access Register */
 #define MII_VSC82X4_EXT_PAGE_ACCESS    0x1f
 
+/* Vitesse VSC73XX Extended Control Register */
+#define MII_VSC73XX_PHY_CTRL_EXT3              0x14
+
+#define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN BIT(4)
+#define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT        GENMASK(3, 2)
+#define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_STA        BIT(1)
+#define MII_VSC73XX_DOWNSHIFT_MAX              5
+#define MII_VSC73XX_DOWNSHIFT_INVAL            1
+
 /* Vitesse VSC8601 Extended PHY Control Register 1 */
 #define MII_VSC8601_EPHY_CTL           0x17
 #define MII_VSC8601_EPHY_CTL_RGMII_SKEW        (1 << 8)
@@ -128,6 +139,74 @@ static int vsc73xx_write_page(struct phy_device *phydev, int page)
        return __phy_write(phydev, VSC73XX_EXT_PAGE_ACCESS, page);
 }
 
+static int vsc73xx_get_downshift(struct phy_device *phydev, u8 *data)
+{
+       int val, enable, cnt;
+
+       val = phy_read_paged(phydev, MII_VSC73XX_EXT_PAGE_1E,
+                            MII_VSC73XX_PHY_CTRL_EXT3);
+       if (val < 0)
+               return val;
+
+       enable = FIELD_GET(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN, val);
+       cnt = FIELD_GET(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT, val) + 2;
+
+       *data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;
+
+       return 0;
+}
+
+static int vsc73xx_set_downshift(struct phy_device *phydev, u8 cnt)
+{
+       u16 mask, val;
+       int ret;
+
+       if (cnt > MII_VSC73XX_DOWNSHIFT_MAX)
+               return -E2BIG;
+       else if (cnt == MII_VSC73XX_DOWNSHIFT_INVAL)
+               return -EINVAL;
+
+       mask = MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN;
+
+       if (!cnt) {
+               val = 0;
+       } else {
+               mask |= MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT;
+               val = MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN |
+                     FIELD_PREP(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT,
+                                cnt - 2);
+       }
+
+       ret = phy_modify_paged(phydev, MII_VSC73XX_EXT_PAGE_1E,
+                              MII_VSC73XX_PHY_CTRL_EXT3, mask, val);
+       if (ret < 0)
+               return ret;
+
+       return genphy_soft_reset(phydev);
+}
+
+static int vsc73xx_get_tunable(struct phy_device *phydev,
+                              struct ethtool_tunable *tuna, void *data)
+{
+       switch (tuna->id) {
+       case ETHTOOL_PHY_DOWNSHIFT:
+               return vsc73xx_get_downshift(phydev, data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int vsc73xx_set_tunable(struct phy_device *phydev,
+                              struct ethtool_tunable *tuna, const void *data)
+{
+       switch (tuna->id) {
+       case ETHTOOL_PHY_DOWNSHIFT:
+               return vsc73xx_set_downshift(phydev, *(const u8 *)data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static void vsc73xx_config_init(struct phy_device *phydev)
 {
        /* Receiver init */
@@ -137,6 +216,9 @@ static void vsc73xx_config_init(struct phy_device *phydev)
 
        /* Config LEDs 0x61 */
        phy_modify(phydev, MII_TPISTATUS, 0xff00, 0x0061);
+
+       /* Enable downshift by default */
+       vsc73xx_set_downshift(phydev, MII_VSC73XX_DOWNSHIFT_MAX);
 }
 
 static int vsc738x_config_init(struct phy_device *phydev)
@@ -447,6 +529,8 @@ static struct phy_driver vsc82xx_driver[] = {
        .config_aneg    = vsc73xx_config_aneg,
        .read_page      = vsc73xx_read_page,
        .write_page     = vsc73xx_write_page,
+       .get_tunable    = vsc73xx_get_tunable,
+       .set_tunable    = vsc73xx_set_tunable,
 }, {
        .phy_id         = PHY_ID_VSC7388,
        .name           = "Vitesse VSC7388",
@@ -456,6 +540,8 @@ static struct phy_driver vsc82xx_driver[] = {
        .config_aneg    = vsc73xx_config_aneg,
        .read_page      = vsc73xx_read_page,
        .write_page     = vsc73xx_write_page,
+       .get_tunable    = vsc73xx_get_tunable,
+       .set_tunable    = vsc73xx_set_tunable,
 }, {
        .phy_id         = PHY_ID_VSC7395,
        .name           = "Vitesse VSC7395",
@@ -465,6 +551,8 @@ static struct phy_driver vsc82xx_driver[] = {
        .config_aneg    = vsc73xx_config_aneg,
        .read_page      = vsc73xx_read_page,
        .write_page     = vsc73xx_write_page,
+       .get_tunable    = vsc73xx_get_tunable,
+       .set_tunable    = vsc73xx_set_tunable,
 }, {
        .phy_id         = PHY_ID_VSC7398,
        .name           = "Vitesse VSC7398",
@@ -474,6 +562,8 @@ static struct phy_driver vsc82xx_driver[] = {
        .config_aneg    = vsc73xx_config_aneg,
        .read_page      = vsc73xx_read_page,
        .write_page     = vsc73xx_write_page,
+       .get_tunable    = vsc73xx_get_tunable,
+       .set_tunable    = vsc73xx_set_tunable,
 }, {
        .phy_id         = PHY_ID_VSC8662,
        .name           = "Vitesse VSC8662",