Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: phy: Introduce generic SFP handling for PHY drivers

There are currently 4 PHY drivers that can drive downstream SFPs:
marvell.c, marvell10g.c, at803x.c and marvell-88x2222.c. Most of the
logic is boilerplate, either calling into generic phylib helpers (for
SFP PHY attach, bus attach, etc.) or performing the same tasks with a
bit of validation :
- Getting the module's expected interface mode
- Making sure the PHY supports it
- Optionaly perform some configuration to make sure the PHY outputs
the right mode

This can be made more generic by leveraging the phy_port, and its
configure_mii() callback which allows setting a port's interfaces when
the port is a serdes.

Introduce a generic PHY SFP support. If a driver doesn't probe the SFP
bus itself, but an SFP phandle is found in devicetree/firmware, then the
generic PHY SFP support will be used, relying on port ops.

PHY driver need to :
- Register a .attach_port() callback
- When a serdes port is registered to the PHY, drivers must set
port->interfaces to the set of PHY_INTERFACE_MODE the port can output
- If the port has limitations regarding speed, duplex and aneg, the
port can also fine-tune the final linkmodes that can be supported
- The port may register a set of ops, including .configure_mii(), that
will be called at module_insert time to adjust the interface based on
the module detected.

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-8-maxime.chevallier@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Maxime Chevallier and committed by
Jakub Kicinski
d7c6082f 07f3ca9e

+111
+107
drivers/net/phy/phy_device.c
··· 1598 1598 } 1599 1599 EXPORT_SYMBOL(phy_sfp_detach); 1600 1600 1601 + static int phy_sfp_module_insert(void *upstream, const struct sfp_eeprom_id *id) 1602 + { 1603 + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); 1604 + struct phy_device *phydev = upstream; 1605 + const struct sfp_module_caps *caps; 1606 + struct phy_port *port; 1607 + 1608 + phy_interface_t iface; 1609 + 1610 + linkmode_zero(sfp_support); 1611 + 1612 + port = phy_get_sfp_port(phydev); 1613 + if (!port) 1614 + return -EINVAL; 1615 + 1616 + caps = sfp_get_module_caps(phydev->sfp_bus); 1617 + 1618 + linkmode_and(sfp_support, port->supported, caps->link_modes); 1619 + if (linkmode_empty(sfp_support)) { 1620 + dev_err(&phydev->mdio.dev, "incompatible SFP module inserted, no common linkmode\n"); 1621 + return -EINVAL; 1622 + } 1623 + 1624 + iface = sfp_select_interface(phydev->sfp_bus, sfp_support); 1625 + if (iface == PHY_INTERFACE_MODE_NA) { 1626 + dev_err(&phydev->mdio.dev, "PHY %s does not support the SFP module's requested MII interfaces\n", 1627 + phydev_name(phydev)); 1628 + return -EINVAL; 1629 + } 1630 + 1631 + if (phydev->n_ports == 1) 1632 + phydev->port = caps->port; 1633 + 1634 + if (port->ops && port->ops->configure_mii) 1635 + return port->ops->configure_mii(port, true, iface); 1636 + 1637 + return 0; 1638 + } 1639 + 1640 + static void phy_sfp_module_remove(void *upstream) 1641 + { 1642 + struct phy_device *phydev = upstream; 1643 + struct phy_port *port = phy_get_sfp_port(phydev); 1644 + 1645 + if (port && port->ops && port->ops->configure_mii) 1646 + port->ops->configure_mii(port, false, PHY_INTERFACE_MODE_NA); 1647 + 1648 + if (phydev->n_ports == 1) 1649 + phydev->port = PORT_NONE; 1650 + } 1651 + 1652 + static void phy_sfp_link_up(void *upstream) 1653 + { 1654 + struct phy_device *phydev = upstream; 1655 + struct phy_port *port = phy_get_sfp_port(phydev); 1656 + 1657 + if (port && port->ops && port->ops->link_up) 1658 + port->ops->link_up(port); 1659 + } 1660 + 1661 + static void phy_sfp_link_down(void *upstream) 1662 + { 1663 + struct phy_device *phydev = upstream; 1664 + struct phy_port *port = phy_get_sfp_port(phydev); 1665 + 1666 + if (port && port->ops && port->ops->link_down) 1667 + port->ops->link_down(port); 1668 + } 1669 + 1670 + static const struct sfp_upstream_ops sfp_phydev_ops = { 1671 + .attach = phy_sfp_attach, 1672 + .detach = phy_sfp_detach, 1673 + .module_insert = phy_sfp_module_insert, 1674 + .module_remove = phy_sfp_module_remove, 1675 + .link_up = phy_sfp_link_up, 1676 + .link_down = phy_sfp_link_down, 1677 + .connect_phy = phy_sfp_connect_phy, 1678 + .disconnect_phy = phy_sfp_disconnect_phy, 1679 + }; 1680 + 1601 1681 static int phy_add_port(struct phy_device *phydev, struct phy_port *port) 1602 1682 { 1603 1683 int ret = 0; ··· 1738 1658 * is a MII port. 1739 1659 */ 1740 1660 port->is_mii = true; 1661 + port->is_sfp = true; 1741 1662 1742 1663 /* The port->supported and port->interfaces list will be populated 1743 1664 * when attaching the port to the phydev. ··· 3586 3505 if (ret) 3587 3506 return ret; 3588 3507 3508 + /* Use generic SFP probing only if the driver didn't do so already */ 3509 + if (!phydev->sfp_bus) { 3510 + ret = phy_sfp_probe(phydev, &sfp_phydev_ops); 3511 + if (ret) 3512 + goto out; 3513 + } 3514 + 3589 3515 if (phydev->n_ports < phydev->max_n_ports) { 3590 3516 ret = phy_default_setup_single_port(phydev); 3591 3517 if (ret) ··· 3627 3539 phy_cleanup_ports(phydev); 3628 3540 return ret; 3629 3541 } 3542 + 3543 + /** 3544 + * phy_get_sfp_port() - Returns the first valid SFP port of a PHY 3545 + * @phydev: pointer to the PHY device to get the SFP port from 3546 + * 3547 + * Returns: The first active SFP (serdes) port of a PHY device, NULL if none 3548 + * exist. 3549 + */ 3550 + struct phy_port *phy_get_sfp_port(struct phy_device *phydev) 3551 + { 3552 + struct phy_port *port; 3553 + 3554 + list_for_each_entry(port, &phydev->ports, head) 3555 + if (port->active && port->is_sfp) 3556 + return port; 3557 + 3558 + return NULL; 3559 + } 3560 + EXPORT_SYMBOL_GPL(phy_get_sfp_port); 3630 3561 3631 3562 /** 3632 3563 * fwnode_mdio_find_device - Given a fwnode, find the mdio_device
+2
include/linux/phy.h
··· 2455 2455 struct kernel_hwtstamp_config *config, 2456 2456 struct netlink_ext_ack *extack); 2457 2457 2458 + struct phy_port *phy_get_sfp_port(struct phy_device *phydev); 2459 + 2458 2460 extern const struct bus_type mdio_bus_type; 2459 2461 extern const struct class mdio_bus_class; 2460 2462
+2
include/linux/phy_port.h
··· 49 49 * @active: Indicates if the port is currently part of the active link. 50 50 * @is_mii: Indicates if this port is MII (Media Independent Interface), 51 51 * or MDI (Media Dependent Interface). 52 + * @is_sfp: Indicates if this port drives an SFP cage. 52 53 */ 53 54 struct phy_port { 54 55 struct list_head head; ··· 68 67 unsigned int not_described:1; 69 68 unsigned int active:1; 70 69 unsigned int is_mii:1; 70 + unsigned int is_sfp:1; 71 71 }; 72 72 73 73 struct phy_port *phy_port_alloc(void);