mediatek: simplify MaxLinear GPY PHY driver
authorDaniel Golle <daniel@makrotopia.org>
Sat, 11 Mar 2023 03:51:00 +0000 (03:51 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Mon, 27 Mar 2023 18:07:54 +0000 (19:07 +0100)
The mxl-gpy driver apparently was built in the assumption that SGMII
auto-negotiation is always switched on at the MAC. This may be true for
few rather recent drivers (why?), but certainly isn't for most drivers
unless 'managed = "in-band-status"' is set in device tree. Add patch to
the mediatek target which reduces mxl-gpy to behave more like an
ordinary PHY driver using out-of-band status.

This allows to use these PHYs without rate-adaptation which seems to be
at least partially broken/racy in some revisions of the PHY and/or
internal PHY firmware.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
target/linux/mediatek/patches-5.15/731-net-phy-hack-mxl-gpy-disable-sgmii-an.patch [new file with mode: 0644]

diff --git a/target/linux/mediatek/patches-5.15/731-net-phy-hack-mxl-gpy-disable-sgmii-an.patch b/target/linux/mediatek/patches-5.15/731-net-phy-hack-mxl-gpy-disable-sgmii-an.patch
new file mode 100644 (file)
index 0000000..2e39ca3
--- /dev/null
@@ -0,0 +1,166 @@
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -126,6 +126,12 @@ static int gpy_config_init(struct phy_de
+       if (ret < 0)
+               return ret;
++      /* Disable SGMII auto-negotiation */
++      ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
++                           VSPEC1_SGMII_CTRL_ANEN, 0);
++      if (ret < 0)
++              return ret;
++
+       return gpy_led_write(phydev);
+ }
+@@ -151,65 +157,6 @@ static int gpy_probe(struct phy_device *
+       return 0;
+ }
+-static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
+-{
+-      int fw_ver, fw_type, fw_minor;
+-      size_t i;
+-
+-      fw_ver = phy_read(phydev, PHY_FWV);
+-      if (fw_ver < 0)
+-              return true;
+-
+-      fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver);
+-      fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver);
+-
+-      for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
+-              if (fw_type != ver_need_sgmii_reaneg[i].type)
+-                      continue;
+-              if (fw_minor < ver_need_sgmii_reaneg[i].minor)
+-                      return true;
+-              break;
+-      }
+-
+-      return false;
+-}
+-
+-static bool gpy_2500basex_chk(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = phy_read(phydev, PHY_MIISTAT);
+-      if (ret < 0) {
+-              phydev_err(phydev, "Error: MDIO register access failed: %d\n",
+-                         ret);
+-              return false;
+-      }
+-
+-      if (!(ret & PHY_MIISTAT_LS) ||
+-          FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
+-              return false;
+-
+-      phydev->speed = SPEED_2500;
+-      phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+-      phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
+-                     VSPEC1_SGMII_CTRL_ANEN, 0);
+-      return true;
+-}
+-
+-static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
+-{
+-      int ret;
+-
+-      ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
+-      if (ret < 0) {
+-              phydev_err(phydev, "Error: MMD register access failed: %d\n",
+-                         ret);
+-              return true;
+-      }
+-
+-      return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
+-}
+-
+ static int gpy_config_aneg(struct phy_device *phydev)
+ {
+       bool changed = false;
+@@ -248,53 +195,11 @@ static int gpy_config_aneg(struct phy_de
+           phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
+               return 0;
+-      /* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
+-       * disabled.
+-       */
+-      if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
+-          !gpy_sgmii_aneg_en(phydev))
+-              return 0;
+-
+-      /* There is a design constraint in GPY2xx device where SGMII AN is
+-       * only triggered when there is change of speed. If, PHY link
+-       * partner`s speed is still same even after PHY TPI is down and up
+-       * again, SGMII AN is not triggered and hence no new in-band message
+-       * from GPY to MAC side SGMII.
+-       * This could cause an issue during power up, when PHY is up prior to
+-       * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
+-       * wouldn`t receive new in-band message from GPY with correct link
+-       * status, speed and duplex info.
+-       *
+-       * 1) If PHY is already up and TPI link status is still down (such as
+-       *    hard reboot), TPI link status is polled for 4 seconds before
+-       *    retriggerring SGMII AN.
+-       * 2) If PHY is already up and TPI link status is also up (such as soft
+-       *    reboot), polling of TPI link status is not needed and SGMII AN is
+-       *    immediately retriggered.
+-       * 3) Other conditions such as PHY is down, speed change etc, skip
+-       *    retriggering SGMII AN. Note: in case of speed change, GPY FW will
+-       *    initiate SGMII AN.
+-       */
+-
+-      if (phydev->state != PHY_UP)
+-              return 0;
+-
+-      ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
+-                                  20000, 4000000, false);
+-      if (ret == -ETIMEDOUT)
+-              return 0;
+-      else if (ret < 0)
+-              return ret;
+-
+-      /* Trigger SGMII AN. */
+-      return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
+-                            VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
++      return 0;
+ }
+ static void gpy_update_interface(struct phy_device *phydev)
+ {
+-      int ret;
+-
+       /* Interface mode is fixed for USXGMII and integrated PHY */
+       if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
+           phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
+@@ -306,29 +211,11 @@ static void gpy_update_interface(struct
+       switch (phydev->speed) {
+       case SPEED_2500:
+               phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+-              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
+-                                   VSPEC1_SGMII_CTRL_ANEN, 0);
+-              if (ret < 0)
+-                      phydev_err(phydev,
+-                                 "Error: Disable of SGMII ANEG failed: %d\n",
+-                                 ret);
+               break;
+       case SPEED_1000:
+       case SPEED_100:
+       case SPEED_10:
+               phydev->interface = PHY_INTERFACE_MODE_SGMII;
+-              if (gpy_sgmii_aneg_en(phydev))
+-                      break;
+-              /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
+-               * if ANEG is disabled (in 2500-BaseX mode).
+-               */
+-              ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
+-                                   VSPEC1_SGMII_ANEN_ANRS,
+-                                   VSPEC1_SGMII_ANEN_ANRS);
+-              if (ret < 0)
+-                      phydev_err(phydev,
+-                                 "Error: Enable of SGMII ANEG failed: %d\n",
+-                                 ret);
+               break;
+       }
+ }