]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: phy: qca807x: Support SFP through phy_port interface
authorMaxime Chevallier <maxime.chevallier@bootlin.com>
Thu, 8 Jan 2026 08:00:37 +0000 (09:00 +0100)
committerJakub Kicinski <kuba@kernel.org>
Wed, 14 Jan 2026 02:52:35 +0000 (18:52 -0800)
QCA8072/8075 may be used as combo-port PHYs, with Serdes (100/1000BaseX)
 and Copper interfaces. The PHY has the ability to read the configuration
it's in.  If the configuration indicates the PHY is in combo mode, allow
registering up to 2 ports.

Register a dedicated set of port ops to handle the serdes port, and rely
on generic phylib SFP support for the SFP handling.

Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Tested-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20260108080041.553250-13-maxime.chevallier@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/qcom/qca807x.c

index 1be8295a95cb5cacef1c169e6f2df58872e960bc..d8f1ce5a7128d66f14eecf287d0f6f4f3b3b9671 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/phy.h>
 #include <linux/bitfield.h>
 #include <linux/gpio/driver.h>
-#include <linux/sfp.h>
+#include <linux/phy_port.h>
 
 #include "../phylib.h"
 #include "qcom.h"
@@ -643,67 +643,54 @@ exit:
        return ret;
 }
 
-static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
+static int qca807x_configure_serdes(struct phy_port *port, bool enable,
+                                   phy_interface_t interface)
 {
-       struct phy_device *phydev = upstream;
-       const struct sfp_module_caps *caps;
-       phy_interface_t iface;
+       struct phy_device *phydev = port_phydev(port);
        int ret;
 
-       caps = sfp_get_module_caps(phydev->sfp_bus);
-       iface = sfp_select_interface(phydev->sfp_bus, caps->link_modes);
+       if (!phydev)
+               return -ENODEV;
 
-       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:
+       if (enable) {
                /* 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);
+               if (ret)
+                       return ret;
                /* 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;
+               if (ret)
+                       return ret;
        }
 
-       return ret;
+       phydev->port = enable ? PORT_FIBRE : PORT_TP;
+
+       return phy_modify(phydev, QCA807X_CHIP_CONFIGURATION,
+                         QCA807X_BT_BX_REG_SEL,
+                         enable ? 0 : QCA807X_BT_BX_REG_SEL);
 }
 
-static void qca807x_sfp_remove(void *upstream)
+static const struct phy_port_ops qca807x_serdes_port_ops = {
+       .configure_mii = qca807x_configure_serdes,
+};
+
+static int qca807x_attach_mii_port(struct phy_device *phydev,
+                                  struct phy_port *port)
 {
-       struct phy_device *phydev = upstream;
+       __set_bit(PHY_INTERFACE_MODE_1000BASEX, port->interfaces);
+       __set_bit(PHY_INTERFACE_MODE_100BASEX, port->interfaces);
 
-       /* Select copper page */
-       phy_set_bits(phydev,
-                    QCA807X_CHIP_CONFIGURATION,
-                    QCA807X_BT_BX_REG_SEL);
+       port->ops = &qca807x_serdes_port_ops;
 
-       phydev->port = PORT_TP;
+       return 0;
 }
 
-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,
-       .connect_phy = phy_sfp_connect_phy,
-       .disconnect_phy = phy_sfp_disconnect_phy,
-};
-
 static int qca807x_probe(struct phy_device *phydev)
 {
        struct device_node *node = phydev->mdio.dev.of_node;
@@ -744,9 +731,8 @@ static int qca807x_probe(struct phy_device *phydev)
 
        /* Attach SFP bus on combo port*/
        if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
-               ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
-               if (ret)
-                       return ret;
+               phydev->max_n_ports = 2;
+
                linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
                linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
        }
@@ -824,6 +810,7 @@ static struct phy_driver qca807x_drivers[] = {
                .get_phy_stats          = qca807x_get_phy_stats,
                .set_wol                = at8031_set_wol,
                .get_wol                = at803x_get_wol,
+               .attach_mii_port        = qca807x_attach_mii_port,
        },
        {
                PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
@@ -851,6 +838,7 @@ static struct phy_driver qca807x_drivers[] = {
                .get_phy_stats          = qca807x_get_phy_stats,
                .set_wol                = at8031_set_wol,
                .get_wol                = at803x_get_wol,
+               .attach_mii_port        = qca807x_attach_mii_port,
        },
 };
 module_phy_driver(qca807x_drivers);