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-02-net-phy-at803x-add-fiber-support.patch
diff --git a/target/linux/generic/backport-5.10/775-v5.18-02-net-phy-at803x-add-fiber-support.patch b/target/linux/generic/backport-5.10/775-v5.18-02-net-phy-at803x-add-fiber-support.patch
new file mode 100644 (file)
index 0000000..1852659
--- /dev/null
@@ -0,0 +1,193 @@
+From 3265f421887847db9ae2c01a00645e33608556d8 Mon Sep 17 00:00:00 2001
+From: Robert Hancock <robert.hancock@calian.com>
+Date: Tue, 25 Jan 2022 10:54:09 -0600
+Subject: [PATCH] net: phy: at803x: add fiber support
+
+Previously this driver always forced the copper page to be selected,
+however for AR8031 in 100Base-FX or 1000Base-X modes, the fiber page
+needs to be selected. Set the appropriate mode based on the hardware
+mode_cfg strap selection.
+
+Enable the appropriate interrupt bits to detect fiber-side link up
+or down events.
+
+Update config_aneg and read_status methods to use the appropriate
+Clause 37 calls when fiber mode is in use.
+
+Signed-off-by: Robert Hancock <robert.hancock@calian.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/phy/at803x.c | 76 +++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 67 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -48,6 +48,8 @@
+ #define AT803X_INTR_ENABLE_PAGE_RECEIVED      BIT(12)
+ #define AT803X_INTR_ENABLE_LINK_FAIL          BIT(11)
+ #define AT803X_INTR_ENABLE_LINK_SUCCESS               BIT(10)
++#define AT803X_INTR_ENABLE_LINK_FAIL_BX               BIT(8)
++#define AT803X_INTR_ENABLE_LINK_SUCCESS_BX    BIT(7)
+ #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE        BIT(5)
+ #define AT803X_INTR_ENABLE_POLARITY_CHANGED   BIT(1)
+ #define AT803X_INTR_ENABLE_WOL                        BIT(0)
+@@ -82,6 +84,17 @@
+ #define AT803X_MODE_CFG_MASK                  0x0F
+ #define AT803X_MODE_CFG_SGMII                 0x01
++#define AT803X_MODE_CFG_BASET_RGMII           0x00
++#define AT803X_MODE_CFG_BASET_SGMII           0x01
++#define AT803X_MODE_CFG_BX1000_RGMII_50OHM    0x02
++#define AT803X_MODE_CFG_BX1000_RGMII_75OHM    0x03
++#define AT803X_MODE_CFG_BX1000_CONV_50OHM     0x04
++#define AT803X_MODE_CFG_BX1000_CONV_75OHM     0x05
++#define AT803X_MODE_CFG_FX100_RGMII_50OHM     0x06
++#define AT803X_MODE_CFG_FX100_CONV_50OHM      0x07
++#define AT803X_MODE_CFG_RGMII_AUTO_MDET               0x0B
++#define AT803X_MODE_CFG_FX100_RGMII_75OHM     0x0E
++#define AT803X_MODE_CFG_FX100_CONV_75OHM      0x0F
+ #define AT803X_PSSR                   0x11    /*PHY-Specific Status Register*/
+ #define AT803X_PSSR_MR_AN_COMPLETE    0x0200
+@@ -189,6 +202,8 @@ struct at803x_priv {
+ #define AT803X_KEEP_PLL_ENABLED       BIT(0)  /* don't turn off internal PLL */
+       u16 clk_25m_reg;
+       u16 clk_25m_mask;
++      bool is_fiber;
++      bool is_1000basex;
+       struct regulator_dev *vddio_rdev;
+       struct regulator_dev *vddh_rdev;
+       struct regulator *vddio;
+@@ -651,7 +666,33 @@ static int at803x_probe(struct phy_devic
+                       return ret;
+       }
++      if (phydev->drv->phy_id == ATH8031_PHY_ID) {
++              int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
++              int mode_cfg;
++
++              if (ccr < 0)
++                      goto err;
++              mode_cfg = ccr & AT803X_MODE_CFG_MASK;
++
++              switch (mode_cfg) {
++              case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
++              case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
++                      priv->is_1000basex = true;
++                      fallthrough;
++              case AT803X_MODE_CFG_FX100_RGMII_50OHM:
++              case AT803X_MODE_CFG_FX100_RGMII_75OHM:
++                      priv->is_fiber = true;
++                      break;
++              }
++      }
++
+       return 0;
++
++err:
++      if (priv->vddio)
++              regulator_disable(priv->vddio);
++
++      return ret;
+ }
+ static void at803x_remove(struct phy_device *phydev)
+@@ -664,6 +705,7 @@ static void at803x_remove(struct phy_dev
+ static int at803x_get_features(struct phy_device *phydev)
+ {
++      struct at803x_priv *priv = phydev->priv;
+       int err;
+       err = genphy_read_abilities(phydev);
+@@ -681,12 +723,13 @@ static int at803x_get_features(struct ph
+        * As a result of that, ESTATUS_1000_XFULL is set
+        * to 1 even when operating in copper TP mode.
+        *
+-       * Remove this mode from the supported link modes,
+-       * as this driver currently only supports copper
+-       * operation.
++       * Remove this mode from the supported link modes
++       * when not operating in 1000BaseX mode.
+        */
+-      linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+-                         phydev->supported);
++      if (!priv->is_1000basex)
++              linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
++                                 phydev->supported);
++
+       return 0;
+ }
+@@ -725,15 +768,18 @@ static int at8031_pll_config(struct phy_
+ static int at803x_config_init(struct phy_device *phydev)
+ {
++      struct at803x_priv *priv = phydev->priv;
+       int ret;
+       if (phydev->drv->phy_id == ATH8031_PHY_ID) {
+               /* Some bootloaders leave the fiber page selected.
+-               * Switch to the copper page, as otherwise we read
+-               * the PHY capabilities from the fiber side.
++               * Switch to the appropriate page (fiber or copper), as otherwise we
++               * read the PHY capabilities from the wrong page.
+                */
+               phy_lock_mdio_bus(phydev);
+-              ret = at803x_write_page(phydev, AT803X_PAGE_COPPER);
++              ret = at803x_write_page(phydev,
++                                      priv->is_fiber ? AT803X_PAGE_FIBER :
++                                                       AT803X_PAGE_COPPER);
+               phy_unlock_mdio_bus(phydev);
+               if (ret)
+                       return ret;
+@@ -782,6 +828,7 @@ static int at803x_ack_interrupt(struct p
+ static int at803x_config_intr(struct phy_device *phydev)
+ {
++      struct at803x_priv *priv = phydev->priv;
+       int err;
+       int value;
+@@ -793,6 +840,10 @@ static int at803x_config_intr(struct phy
+               value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
+               value |= AT803X_INTR_ENABLE_LINK_FAIL;
+               value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
++              if (priv->is_fiber) {
++                      value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
++                      value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
++              }
+               err = phy_write(phydev, AT803X_INTR_ENABLE, value);
+       }
+@@ -859,8 +910,12 @@ static int at803x_aneg_done(struct phy_d
+ static int at803x_read_status(struct phy_device *phydev)
+ {
++      struct at803x_priv *priv = phydev->priv;
+       int ss, err, old_link = phydev->link;
++      if (priv->is_1000basex)
++              return genphy_c37_read_status(phydev);
++
+       /* Update the link, but return if there was an error */
+       err = genphy_update_link(phydev);
+       if (err)
+@@ -959,6 +1014,7 @@ static int at803x_config_mdix(struct phy
+ static int at803x_config_aneg(struct phy_device *phydev)
+ {
++      struct at803x_priv *priv = phydev->priv;
+       int ret;
+       ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
+@@ -975,6 +1031,9 @@ static int at803x_config_aneg(struct phy
+                       return ret;
+       }
++      if (priv->is_1000basex)
++              return genphy_c37_config_aneg(phydev);
++
+       return genphy_config_aneg(phydev);
+ }