[PATCH net-next 04/13] net: phylink: Register a phy_port for MAC-driven SFP busses

Maxime Chevallier
2026-01-27T13:42:30Z

phy_port tracks the interfaces that a netdevice feeds into. SFP cages are
such ports, but so far we are only tracking the ones that are driven by
PHYs acting as media-converters.

Let's populate a phy_port for MAC driver SFP cages, handled by phylink.

This phy_port represents the SFP cage itself, and not the module that
may be plugged into it. It's therefore not an MDI interface, so only the
'interfaces' field is relevant here.

The phy_port is only populated for 'NETDEV' phylink instances, as
otherwise we don't have any topology to attach the port to.

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
drivers/net/phy/phylink.c | 53 +++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)

diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 7641f1f41e39..310af33d49a0 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -14,6 +14,8 @@
#include <linux/of_mdio.h>
#include <linux/phy.h>
#include <linux/phy_fixed.h>
+#include <linux/phy_link_topology.h>
+#include <linux/phy_port.h>
#include <linux/phylink.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
@@ -93,6 +95,7 @@ struct phylink {
DECLARE_PHY_INTERFACE_MASK(sfp_interfaces);
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
u8 sfp_port;
+ struct phy_port *sfp_bus_port;

struct eee_config eee_cfg;

@@ -1757,6 +1760,46 @@ static void phylink_fixed_poll(struct timer_list *t)

static const struct sfp_upstream_ops sfp_phylink_ops;

+static int phylink_create_sfp_port(struct phylink *pl)
+{
+ struct phy_port *port;
+ int ret = 0;
+
+ if (!pl->netdev || !pl->sfp_bus)
+ return 0;
+
+ port = phy_port_alloc();
+ if (!port)
+ return -ENOMEM;
+
+ port->is_sfp = true;
+ port->is_mii = true;
+ port->active = true;
+
+ phy_interface_and(port->interfaces, pl->config->supported_interfaces,
+ phylink_sfp_interfaces);
+ phy_port_update_supported(port);
+
+ ret = phy_link_topo_add_port(pl->netdev, port);
+ if (ret)
+ phy_port_destroy(port);
+ else
+ pl->sfp_bus_port = port;
+
+ return ret;
+}
+
+static void phylink_destroy_sfp_port(struct phylink *pl)
+{
+ if (pl->netdev && pl->sfp_bus_port)
+ phy_link_topo_del_port(pl->netdev, pl->sfp_bus_port);
+
+ if (pl->sfp_bus_port)
+ phy_port_destroy(pl->sfp_bus_port);
+
+ pl->sfp_bus_port = NULL;
+}
+
static int phylink_register_sfp(struct phylink *pl,
const struct fwnode_handle *fwnode)
{
@@ -1774,9 +1817,18 @@ static int phylink_register_sfp(struct phylink *pl,

pl->sfp_bus = bus;

+ ret = phylink_create_sfp_port(pl);
+ if (ret) {
+ sfp_bus_put(bus);
+ return ret;
+ }
+
ret = sfp_bus_add_upstream(bus, pl, &sfp_phylink_ops);
sfp_bus_put(bus);

+ if (ret)
+ phylink_destroy_sfp_port(pl);
+
return ret;
}

@@ -1938,6 +1990,7 @@ EXPORT_SYMBOL_GPL(phylink_create);
void phylink_destroy(struct phylink *pl)
{
sfp_bus_del_upstream(pl->sfp_bus);
+ phylink_destroy_sfp_port(pl);
if (pl->link_gpio)
gpiod_put(pl->link_gpio);

--
2.49.0