generic: 5.10: replace ramips AR8033 fiber patch with 5.18 patches
[openwrt/staging/dedeckeh.git] / target / linux / generic / backport-5.10 / 775-v5.18-03-net-phy-at803x-support-downstream-SFP-cage.patch
diff --git a/target/linux/generic/backport-5.10/775-v5.18-03-net-phy-at803x-support-downstream-SFP-cage.patch b/target/linux/generic/backport-5.10/775-v5.18-03-net-phy-at803x-support-downstream-SFP-cage.patch
new file mode 100644 (file)
index 0000000..73dea9c
--- /dev/null
@@ -0,0 +1,95 @@
+From dc4d5fcc5d365c9f70ea3f5c09bdf70e988fad50 Mon Sep 17 00:00:00 2001
+From: Robert Hancock <robert.hancock@calian.com>
+Date: Tue, 25 Jan 2022 10:54:10 -0600
+Subject: [PATCH] net: phy: at803x: Support downstream SFP cage
+
+Add support for downstream SFP cages for AR8031 and AR8033. This is
+primarily intended for fiber modules or direct-attach cables, however
+copper modules which work in 1000Base-X mode may also function. Such
+modules are allowed with a warning.
+
+Signed-off-by: Robert Hancock <robert.hancock@calian.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 56 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 56 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -19,6 +19,8 @@
+ #include <linux/regulator/of_regulator.h>
+ #include <linux/regulator/driver.h>
+ #include <linux/regulator/consumer.h>
++#include <linux/phylink.h>
++#include <linux/sfp.h>
+ #include <dt-bindings/net/qca-ar803x.h>
+ #define AT803X_SPECIFIC_FUNCTION_CONTROL      0x10
+@@ -551,6 +553,55 @@ static bool at803x_match_phy_id(struct p
+               == (phy_id & phydev->drv->phy_id_mask);
+ }
++static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
++{
++      struct phy_device *phydev = upstream;
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
++      phy_interface_t iface;
++
++      linkmode_zero(phy_support);
++      phylink_set(phy_support, 1000baseX_Full);
++      phylink_set(phy_support, 1000baseT_Full);
++      phylink_set(phy_support, Autoneg);
++      phylink_set(phy_support, Pause);
++      phylink_set(phy_support, Asym_Pause);
++
++      linkmode_zero(sfp_support);
++      sfp_parse_support(phydev->sfp_bus, id, sfp_support);
++      /* Some modules support 10G modes as well as others we support.
++       * Mask out non-supported modes so the correct interface is picked.
++       */
++      linkmode_and(sfp_support, phy_support, sfp_support);
++
++      if (linkmode_empty(sfp_support)) {
++              dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
++              return -EINVAL;
++      }
++
++      iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
++
++      /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
++       * interface for use with SFP modules.
++       * However, some copper modules detected as having a preferred SGMII
++       * interface do default to and function in 1000Base-X mode, so just
++       * print a warning and allow such modules, as they may have some chance
++       * of working.
++       */
++      if (iface == PHY_INTERFACE_MODE_SGMII)
++              dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
++      else if (iface != PHY_INTERFACE_MODE_1000BASEX)
++              return -EINVAL;
++
++      return 0;
++}
++
++static const struct sfp_upstream_ops at803x_sfp_ops = {
++      .attach = phy_sfp_attach,
++      .detach = phy_sfp_detach,
++      .module_insert = at803x_sfp_insert,
++};
++
+ static int at803x_parse_dt(struct phy_device *phydev)
+ {
+       struct device_node *node = phydev->mdio.dev.of_node;
+@@ -639,6 +690,11 @@ static int at803x_parse_dt(struct phy_de
+                       phydev_err(phydev, "failed to get VDDIO regulator\n");
+                       return PTR_ERR(priv->vddio);
+               }
++
++              /* Only AR8031/8033 support 1000Base-X for SFP modules */
++              ret = phy_sfp_probe(phydev, &at803x_sfp_ops);
++              if (ret < 0)
++                      return ret;
+       }
+       return 0;