diff options
| author | Maxime Chevallier <maxime.chevallier@bootlin.com> | 2026-01-08 11:00:28 +0300 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-01-14 05:52:34 +0300 |
| commit | 589e934d2735b55fbe68517128668df7af3ac4ae (patch) | |
| tree | 1edcab6d0ebd00e6151a40dcf21090d22cbd2573 /include | |
| parent | 3f25ff740950b8e850a8b6e637c48f1d23ecf388 (diff) | |
| download | linux-589e934d2735b55fbe68517128668df7af3ac4ae.tar.xz | |
net: phy: Introduce PHY ports representation
Ethernet provides a wide variety of layer 1 protocols and standards for
data transmission. The front-facing ports of an interface have their own
complexity and configurability.
Introduce a representation of these front-facing ports. The current code
is minimalistic and only support ports controlled by PHY devices, but
the plan is to extend that to SFP as well as raw Ethernet MACs that
don't use PHY devices.
This minimal port representation allows describing the media and number
of pairs of a BaseT port. From that information, we can derive the
linkmodes usable on the port, which can be used to limit the
capabilities of an interface.
For now, the port pairs and medium is derived from devicetree, defined
by the PHY driver, or populated with default values (as we assume that
all PHYs expose at least one port).
The typical example is 100M ethernet. 100BaseTX works using only 2
pairs on a Cat 5 cables. However, in the situation where a 10/100/1000
capable PHY is wired to its RJ45 port through 2 pairs only, we have no
way of detecting that. The "max-speed" DT property can be used, but a
more accurate representation can be used :
mdi {
connector-0 {
media = "BaseT";
pairs = <2>;
};
};
From that information, we can derive the max speed reachable on the
port.
Another benefit of having that is to avoid vendor-specific DT properties
(micrel,fiber-mode or ti,fiber-mode).
This basic representation is meant to be expanded, by the introduction
of port ops, userspace listing of ports, and support for multi-port
devices.
Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu>
Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20260108080041.553250-4-maxime.chevallier@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/ethtool.h | 11 | ||||
| -rw-r--r-- | include/linux/phy.h | 55 | ||||
| -rw-r--r-- | include/linux/phy_port.h | 96 |
3 files changed, 162 insertions, 0 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 37aede6af96f..798abec67a1b 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -242,6 +242,17 @@ enum ethtool_link_medium { __ETHTOOL_LINK_MEDIUM_LAST, }; +#define ETHTOOL_MEDIUM_FIBER_BITS (BIT(ETHTOOL_LINK_MEDIUM_BASES) | \ + BIT(ETHTOOL_LINK_MEDIUM_BASEL) | \ + BIT(ETHTOOL_LINK_MEDIUM_BASEF)) + +enum ethtool_link_medium ethtool_str_to_medium(const char *str); + +static inline int ethtool_linkmode_n_pairs(unsigned int mode) +{ + return link_mode_params[mode].pairs; +} + /* declare a link mode bitmap */ #define __ETHTOOL_DECLARE_LINK_MODE_MASK(name) \ DECLARE_BITMAP(name, __ETHTOOL_LINK_MODE_MASK_NBITS) diff --git a/include/linux/phy.h b/include/linux/phy.h index fbbe028cc4b7..b7e769b52e6c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -327,6 +327,7 @@ static inline long rgmii_clock(int speed) struct device; struct kernel_hwtstamp_config; struct phylink; +struct phy_port; struct sfp_bus; struct sfp_upstream_ops; struct sk_buff; @@ -645,6 +646,9 @@ struct phy_oatc14_sqi_capability { * @master_slave_state: Current master/slave configuration * @mii_ts: Pointer to time stamper callbacks * @psec: Pointer to Power Sourcing Equipment control struct + * @ports: List of PHY ports structures + * @n_ports: Number of ports currently attached to the PHY + * @max_n_ports: Max number of ports this PHY can expose * @lock: Mutex for serialization access to PHY * @state_queue: Work queue for state machine * @link_down_events: Number of times link was lost @@ -783,6 +787,10 @@ struct phy_device { struct mii_timestamper *mii_ts; struct pse_control *psec; + struct list_head ports; + int n_ports; + int max_n_ports; + u8 mdix; u8 mdix_ctrl; @@ -807,6 +815,9 @@ struct phy_device { #define to_phy_device(__dev) container_of_const(to_mdio_device(__dev), struct phy_device, mdio) +#define phy_for_each_port(phydev, port) \ + list_for_each_entry(port, &(phydev)->ports, head) + /** * struct phy_tdr_config - Configuration of a TDR raw test * @@ -1507,6 +1518,49 @@ struct phy_driver { * Returns the time in jiffies until the next update event. */ unsigned int (*get_next_update_time)(struct phy_device *dev); + + /** + * @attach_mii_port: Attach the given MII port to the PHY device + * @dev: PHY device to notify + * @port: The port being added + * + * Called when an MII port that needs to be driven by the PHY is found. + * + * The port that is being passed may or may not be initialized. If it is + * already initialized, it is by the generic port representation from + * devicetree, which superseeds any strapping or vendor-specific + * properties. + * + * If the port isn't initialized, the port->mediums and port->lanes + * fields must be set, possibly according to strapping information. + * + * The PHY driver must set the port->interfaces field to indicate the + * possible MII modes that this PHY can output on the port. + * + * Returns 0, or an error code. + */ + int (*attach_mii_port)(struct phy_device *dev, struct phy_port *port); + + /** + * @attach_mdi_port: Attach the given MII port to the PHY device + * @dev: PHY device to notify + * @port: The port being added + * + * Called when a port that needs to be driven by the PHY is found. The + * number of time this will be called depends on phydev->max_n_ports, + * which the driver can change in .probe(). + * + * The port that is being passed may or may not be initialized. If it is + * already initialized, it is by the generic port representation from + * devicetree, which superseeds any strapping or vendor-specific + * properties. + * + * If the port isn't initialized, the port->mediums and port->lanes + * fields must be set, possibly according to strapping information. + * + * Returns 0, or an error code. + */ + int (*attach_mdi_port)(struct phy_device *dev, struct phy_port *port); }; #define to_phy_driver(d) container_of_const(to_mdio_common_driver(d), \ struct phy_driver, mdiodrv) @@ -2310,6 +2364,7 @@ void phy_trigger_machine(struct phy_device *phydev); void phy_mac_interrupt(struct phy_device *phydev); void phy_start_machine(struct phy_device *phydev); void phy_stop_machine(struct phy_device *phydev); + void phy_ethtool_ksettings_get(struct phy_device *phydev, struct ethtool_link_ksettings *cmd); int phy_ethtool_ksettings_set(struct phy_device *phydev, diff --git a/include/linux/phy_port.h b/include/linux/phy_port.h new file mode 100644 index 000000000000..ce0208fbccf7 --- /dev/null +++ b/include/linux/phy_port.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __PHY_PORT_H +#define __PHY_PORT_H + +#include <linux/ethtool.h> +#include <linux/types.h> +#include <linux/phy.h> + +struct phy_port; + +/** + * enum phy_port_parent - The device this port is attached to + * + * @PHY_PORT_PHY: Indicates that the port is driven by a PHY device + */ +enum phy_port_parent { + PHY_PORT_PHY, +}; + +struct phy_port_ops { + /* Sometimes, the link state can be retrieved from physical, + * out-of-band channels such as the LOS signal on SFP. These + * callbacks allows notifying the port about state changes + */ + void (*link_up)(struct phy_port *port); + void (*link_down)(struct phy_port *port); + + /* If the port acts as a Media Independent Interface (Serdes port), + * configures the port with the relevant state and mode. When enable is + * not set, interface should be ignored + */ + int (*configure_mii)(struct phy_port *port, bool enable, phy_interface_t interface); +}; + +/** + * struct phy_port - A representation of a network device physical interface + * + * @head: Used by the port's parent to list ports + * @parent_type: The type of device this port is directly connected to + * @phy: If the parent is PHY_PORT_PHYDEV, the PHY controlling that port + * @ops: Callback ops implemented by the port controller + * @pairs: The number of pairs this port has, 0 if not applicable + * @mediums: Bitmask of the physical mediums this port provides access to + * @supported: The link modes this port can expose, if this port is MDI (not MII) + * @interfaces: The MII interfaces this port supports, if this port is MII + * @not_described: Indicates to the parent driver if this port isn't described, + * so it's up to the parent to filter its capabilities. + * @active: Indicates if the port is currently part of the active link. + * @is_mii: Indicates if this port is MII (Media Independent Interface), + * or MDI (Media Dependent Interface). + */ +struct phy_port { + struct list_head head; + enum phy_port_parent parent_type; + union { + struct phy_device *phy; + }; + + const struct phy_port_ops *ops; + + int pairs; + unsigned long mediums; + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + DECLARE_PHY_INTERFACE_MASK(interfaces); + + unsigned int not_described:1; + unsigned int active:1; + unsigned int is_mii:1; +}; + +struct phy_port *phy_port_alloc(void); +void phy_port_destroy(struct phy_port *port); + +static inline struct phy_device *port_phydev(struct phy_port *port) +{ + return port->phy; +} + +struct phy_port *phy_of_parse_port(struct device_node *dn); + +static inline bool phy_port_is_copper(struct phy_port *port) +{ + return port->mediums == BIT(ETHTOOL_LINK_MEDIUM_BASET); +} + +static inline bool phy_port_is_fiber(struct phy_port *port) +{ + return !!(port->mediums & ETHTOOL_MEDIUM_FIBER_BITS); +} + +void phy_port_update_supported(struct phy_port *port); + +int phy_port_get_type(struct phy_port *port); + +#endif |
