[PATCH net-next 3/3] net: phy: dp83869: Support 1000Base-X SFP
From: Romain Gantois <romain.gantois@bootlin.com>
Date: 2025-11-04 08:50:45
Also in:
lkml
Subsystem:
ethernet phy library, networking drivers, the rest · Maintainers:
Andrew Lunn, Heiner Kallweit, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Associate with an SFP cage described in the device tree and provide the module_insert() callback that will set the appropriate DP83869 operation mode when an SFP module is inserted. Signed-off-by: Romain Gantois <romain.gantois@bootlin.com> --- drivers/net/phy/dp83869.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+)
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index adcd899472f2..e279dfa268a4 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c@@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/phy.h> +#include <linux/sfp.h> #include <linux/delay.h> #include <linux/bitfield.h>
@@ -875,6 +876,77 @@ static int dp83869_config_init(struct phy_device *phydev) return ret; } +static void dp83869_module_remove(void *upstream) +{ + struct phy_device *phydev = upstream; + + phydev_info(phydev, "SFP module removed\n"); + + /* Set speed and duplex to unknown to avoid downshifting warning. */ + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; +} + +static int dp83869_module_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support); + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + struct phy_device *phydev = upstream; + const struct sfp_module_caps *caps; + struct dp83869_private *dp83869; + phy_interface_t interface; + int ret; + + linkmode_zero(phy_support); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, phy_support); + + caps = sfp_get_module_caps(phydev->sfp_bus); + + linkmode_and(sfp_support, phy_support, caps->link_modes); + + if (linkmode_empty(sfp_support)) { + phydev_err(phydev, "incompatible SFP module inserted\n"); + return -EINVAL; + } + + interface = sfp_select_interface(phydev->sfp_bus, sfp_support); + + phydev_info(phydev, "%s SFP compatible link mode: %s\n", __func__, + phy_modes(interface)); + + dp83869 = phydev->priv; + + switch (interface) { + case PHY_INTERFACE_MODE_1000BASEX: + dp83869->mode = DP83869_RGMII_1000_BASE; + phydev->port = PORT_FIBRE; + break; + default: + phydev_err(phydev, "incompatible PHY-to-SFP module link mode %s!\n", + phy_modes(interface)); + return -EINVAL; + } + + ret = dp83869_configure_mode(phydev, dp83869); + if (ret) + return ret; + + /* Reconfigure advertisement */ + if (mutex_trylock(&phydev->lock)) { + ret = dp83869_config_aneg(phydev); + mutex_unlock(&phydev->lock); + } + + return ret; +} + +static const struct sfp_upstream_ops dp83869_sfp_ops = { + .attach = phy_sfp_attach, + .detach = phy_sfp_detach, + .module_insert = dp83869_module_insert, + .module_remove = dp83869_module_remove, +}; + static int dp83869_probe(struct phy_device *phydev) { struct dp83869_private *dp83869;
@@ -891,6 +963,12 @@ static int dp83869_probe(struct phy_device *phydev) if (ret) return ret; + if (of_property_read_bool(phydev->mdio.dev.of_node, "sfp")) { + ret = phy_sfp_probe(phydev, &dp83869_sfp_ops); + if (ret) + return ret; + } + if (dp83869->mode == DP83869_RGMII_100_BASE || dp83869->mode == DP83869_RGMII_1000_BASE) phydev->port = PORT_FIBRE;
--
2.51.2