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 --- 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 #include #include +#include #include #include @@ -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