net: phy: marvell: add PHY tunable fast link down support for 88E1540
authorHeiner Kallweit <hkallweit1@gmail.com>
Mon, 25 Mar 2019 18:35:41 +0000 (19:35 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 27 Mar 2019 20:51:49 +0000 (13:51 -0700)
1000BaseT standard requires that a link is reported as down earliest
after 750ms. Several use case however require a much faster detecion
of a broken link. Fast Link Down supports this by intentionally
violating a the standard. This patch exposes the Fast Link Down
feature of 88E1540 and 88E6390. These PHY's can be found as internal
PHY's in several switches: 88E635288E624088E617688E6172,
and 88E6390(X). Fast Link Down and EEE are mutually exclusive.

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

index 3ccba37bd6ddeafd5c7f65e9e58f1ef5d57b2540..65350186d514ac69da3f27e35152dfc66ff00b2d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/ethtool.h>
 #include <linux/phy.h>
 #include <linux/marvell_phy.h>
+#include <linux/bitfield.h>
 #include <linux/of.h>
 
 #include <linux/io.h>
 #define MII_88E1510_TEMP_SENSOR                0x1b
 #define MII_88E1510_TEMP_SENSOR_MASK   0xff
 
+#define MII_88E1540_COPPER_CTRL3       0x1a
+#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK  GENMASK(11, 10)
+#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS  0
+#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS  1
+#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS  2
+#define MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS  3
+#define MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN                BIT(9)
+
 #define MII_88E6390_MISC_TEST          0x1b
 #define MII_88E6390_MISC_TEST_SAMPLE_1S                0
 #define MII_88E6390_MISC_TEST_SAMPLE_10MS      BIT(14)
@@ -1025,6 +1034,101 @@ static int m88e1145_config_init(struct phy_device *phydev)
        return 0;
 }
 
+static int m88e1540_get_fld(struct phy_device *phydev, u8 *msecs)
+{
+       int val;
+
+       val = phy_read(phydev, MII_88E1540_COPPER_CTRL3);
+       if (val < 0)
+               return val;
+
+       if (!(val & MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN)) {
+               *msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
+               return 0;
+       }
+
+       val = FIELD_GET(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
+
+       switch (val) {
+       case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS:
+               *msecs = 0;
+               break;
+       case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS:
+               *msecs = 10;
+               break;
+       case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS:
+               *msecs = 20;
+               break;
+       case MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS:
+               *msecs = 40;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int m88e1540_set_fld(struct phy_device *phydev, const u8 *msecs)
+{
+       struct ethtool_eee eee;
+       int val, ret;
+
+       if (*msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
+               return phy_clear_bits(phydev, MII_88E1540_COPPER_CTRL3,
+                                     MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
+
+       /* According to the Marvell data sheet EEE must be disabled for
+        * Fast Link Down detection to work properly
+        */
+       ret = phy_ethtool_get_eee(phydev, &eee);
+       if (!ret && eee.eee_enabled) {
+               phydev_warn(phydev, "Fast Link Down detection requires EEE to be disabled!\n");
+               return -EBUSY;
+       }
+
+       if (*msecs <= 5)
+               val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_00MS;
+       else if (*msecs <= 15)
+               val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_10MS;
+       else if (*msecs <= 30)
+               val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_20MS;
+       else
+               val = MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_40MS;
+
+       val = FIELD_PREP(MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
+
+       ret = phy_modify(phydev, MII_88E1540_COPPER_CTRL3,
+                        MII_88E1540_COPPER_CTRL3_LINK_DOWN_DELAY_MASK, val);
+       if (ret)
+               return ret;
+
+       return phy_set_bits(phydev, MII_88E1540_COPPER_CTRL3,
+                           MII_88E1540_COPPER_CTRL3_FAST_LINK_DOWN);
+}
+
+static int m88e1540_get_tunable(struct phy_device *phydev,
+                               struct ethtool_tunable *tuna, void *data)
+{
+       switch (tuna->id) {
+       case ETHTOOL_PHY_FAST_LINK_DOWN:
+               return m88e1540_get_fld(phydev, data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int m88e1540_set_tunable(struct phy_device *phydev,
+                               struct ethtool_tunable *tuna, const void *data)
+{
+       switch (tuna->id) {
+       case ETHTOOL_PHY_FAST_LINK_DOWN:
+               return m88e1540_set_fld(phydev, data);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 /* The VOD can be out of specification on link up. Poke an
  * undocumented register, in an undocumented page, with a magic value
  * to fix this.
@@ -2247,6 +2351,8 @@ static struct phy_driver marvell_drivers[] = {
                .get_sset_count = marvell_get_sset_count,
                .get_strings = marvell_get_strings,
                .get_stats = marvell_get_stats,
+               .get_tunable = m88e1540_get_tunable,
+               .set_tunable = m88e1540_set_tunable,
        },
        {
                .phy_id = MARVELL_PHY_ID_88E1545,
@@ -2307,6 +2413,8 @@ static struct phy_driver marvell_drivers[] = {
                .get_sset_count = marvell_get_sset_count,
                .get_strings = marvell_get_strings,
                .get_stats = marvell_get_stats,
+               .get_tunable = m88e1540_get_tunable,
+               .set_tunable = m88e1540_set_tunable,
        },
 };