ipq40xx: qca807x: add SFP improvements
authorRobert Marko <robert.marko@sartura.hr>
Mon, 25 Oct 2021 16:30:00 +0000 (18:30 +0200)
committerDavid Bauer <mail@david-bauer.net>
Sun, 2 Oct 2022 21:04:38 +0000 (23:04 +0200)
Currently, QCA807x doesnt do any kind of validation to see whether it
actually supports the inserted module.

So lets add checks to allow only 1000BaseX and 100BaseFX based modules.

While adding validation, move fiber configuration to insert/remove events
instead of always doing it at config time.
This allows getting rid of the DT property for fiber enable and now only
the upstream sfp phandle is required.

Since we are refactoring fiber related code, lets heavily simplify the
status polling as the current logic is overcomplicated due to previous
wish to support non standard SFP cages that dont have pins properly
connected, that is removed now and only proper SFP cages will work.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
target/linux/ipq40xx/files/drivers/net/phy/qca807x.c

index 8d523548e52fc921c55718e842a2c1953c764b63..d56e9f9cda8b78c6239738fde6450d739ad09bce 100644 (file)
@@ -64,6 +64,8 @@
 
 #define QCA807X_CHIP_CONFIGURATION                             0x1f
 #define QCA807X_BT_BX_REG_SEL                                  BIT(15)
+#define QCA807X_BT_BX_REG_SEL_FIBER                            0
+#define QCA807X_BT_BX_REG_SEL_COPPER                           1
 #define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK               GENMASK(3, 0)
 #define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII           4
 #define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER           3
@@ -400,19 +402,9 @@ static int qca807x_gpio(struct phy_device *phydev)
 }
 #endif
 
-static int qca807x_read_copper_status(struct phy_device *phydev, bool combo_port)
+static int qca807x_read_copper_status(struct phy_device *phydev)
 {
-       int ss, err, page, old_link = phydev->link;
-
-       /* Only combo port has dual pages */
-       if (combo_port) {
-               /* Check whether copper page is set and set if needed */
-               page = phy_read(phydev, QCA807X_CHIP_CONFIGURATION);
-               if (!(page & QCA807X_BT_BX_REG_SEL)) {
-                       page |= QCA807X_BT_BX_REG_SEL;
-                       phy_write(phydev, QCA807X_CHIP_CONFIGURATION, page);
-               }
-       }
+       int ss, err, old_link = phydev->link;
 
        /* Update the link, but return if there was an error */
        err = genphy_update_link(phydev);
@@ -487,16 +479,9 @@ static int qca807x_read_copper_status(struct phy_device *phydev, bool combo_port
        return 0;
 }
 
-static int qca807x_read_fiber_status(struct phy_device *phydev, bool combo_port)
+static int qca807x_read_fiber_status(struct phy_device *phydev)
 {
-       int ss, err, page, lpa, old_link = phydev->link;
-
-       /* Check whether fiber page is set and set if needed */
-       page = phy_read(phydev, QCA807X_CHIP_CONFIGURATION);
-       if (page & QCA807X_BT_BX_REG_SEL) {
-               page &= ~QCA807X_BT_BX_REG_SEL;
-               phy_write(phydev, QCA807X_CHIP_CONFIGURATION, page);
-       }
+       int ss, err, lpa, old_link = phydev->link;
 
        /* Update the link, but return if there was an error */
        err = genphy_update_link(phydev);
@@ -559,28 +544,17 @@ static int qca807x_read_fiber_status(struct phy_device *phydev, bool combo_port)
 
 static int qca807x_read_status(struct phy_device *phydev)
 {
-       int val;
-
-       /* Check for Combo port */
-       if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
-               /* Check for fiber mode first */
-               if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
-                       /* Check for actual detected media */
-                       val = phy_read(phydev, QCA807X_MEDIA_SELECT_STATUS);
-                       if (val & QCA807X_MEDIA_DETECTED_COPPER) {
-                               qca807x_read_copper_status(phydev, true);
-                       } else if ((val & QCA807X_MEDIA_DETECTED_1000_BASE_X) ||
-                                  (val & QCA807X_MEDIA_DETECTED_100_BASE_FX)) {
-                               qca807x_read_fiber_status(phydev, true);
-                       }
-               } else {
-                       qca807x_read_copper_status(phydev, true);
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
+               switch (phydev->port) {
+               case PORT_FIBRE:
+                       return qca807x_read_fiber_status(phydev);
+               case PORT_TP:
+                       return qca807x_read_copper_status(phydev);
+               default:
+                       return -EINVAL;
                }
-       } else {
-               qca807x_read_copper_status(phydev, false);
-       }
-
-       return 0;
+       } else
+               return qca807x_read_copper_status(phydev);
 }
 
 static int qca807x_config_intr(struct phy_device *phydev)
@@ -683,9 +657,63 @@ static int qca807x_led_config(struct phy_device *phydev)
                return 0;
 }
 
+static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
+{
+       struct phy_device *phydev = upstream;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
+       phy_interface_t iface;
+       int ret;
+
+       sfp_parse_support(phydev->sfp_bus, id, support);
+       iface = sfp_select_interface(phydev->sfp_bus, support);
+
+       dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));
+
+       switch (iface) {
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_100BASEX:
+               /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
+               ret = phy_modify(phydev,
+                                QCA807X_CHIP_CONFIGURATION,
+                                QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
+                                QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
+               /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
+               ret = phy_set_bits_mmd(phydev,
+                                      MDIO_MMD_AN,
+                                      QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
+                                      QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);
+               /* Select fiber page */
+               ret = phy_clear_bits(phydev,
+                                    QCA807X_CHIP_CONFIGURATION,
+                                    QCA807X_BT_BX_REG_SEL);
+
+               phydev->port = PORT_FIBRE;
+               break;
+       default:
+               dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+static void qca807x_sfp_remove(void *upstream)
+{
+       struct phy_device *phydev = upstream;
+
+       /* Select copper page */
+       phy_set_bits(phydev,
+                    QCA807X_CHIP_CONFIGURATION,
+                    QCA807X_BT_BX_REG_SEL);
+
+       phydev->port = PORT_TP;
+}
+
 static const struct sfp_upstream_ops qca807x_sfp_ops = {
        .attach = phy_sfp_attach,
        .detach = phy_sfp_detach,
+       .module_insert = qca807x_sfp_insert,
+       .module_remove = qca807x_sfp_remove,
 };
 
 static int qca807x_config(struct phy_device *phydev)
@@ -696,28 +724,7 @@ static int qca807x_config(struct phy_device *phydev)
 
        /* Check for Combo port */
        if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
-               int fiber_mode_autodect;
                int psgmii_serdes;
-               int chip_config;
-
-               if (of_property_read_bool(node, "qcom,fiber-enable")) {
-                       /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
-                       fiber_mode_autodect = phy_read_mmd(phydev, MDIO_MMD_AN,
-                                                          QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION);
-                       fiber_mode_autodect |= QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN;
-                       phy_write_mmd(phydev, MDIO_MMD_AN, QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
-                                     fiber_mode_autodect);
-
-                       /* Enable 4 copper + combo port mode */
-                       chip_config = phy_read(phydev, QCA807X_CHIP_CONFIGURATION);
-                       chip_config &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK;
-                       chip_config |= FIELD_PREP(QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
-                                                 QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
-                       phy_write(phydev, QCA807X_CHIP_CONFIGURATION, chip_config);
-
-                       linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
-                       linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
-               }
 
                /* Prevent PSGMII going into hibernation via PSGMII self test */
                psgmii_serdes = phy_read_mmd(phydev, MDIO_MMD_PCS, PSGMII_MMD3_SERDES_CONTROL);
@@ -761,9 +768,10 @@ static int qca807x_probe(struct phy_device *phydev)
        }
 
        /* Attach SFP bus on combo port*/
-       if (of_property_read_bool(node, "qcom,fiber-enable")) {
-               if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION))
-                       ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
+       if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
+               ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
+               linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
        }
 
        return ret;