CINXE.COM

LKML: Maxime Chevallier: [PATCH net-next 03/13] net: phy: Introduce PHY ports representation

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>LKML: Maxime Chevallier: [PATCH net-next 03/13] net: phy: Introduce PHY ports representation</title><link href="/css/message.css" rel="stylesheet" type="text/css" /><link href="/css/wrap.css" rel="alternate stylesheet" type="text/css" title="wrap" /><link href="/css/nowrap.css" rel="stylesheet" type="text/css" title="nowrap" /><link href="/favicon.ico" rel="shortcut icon" /><script src="/js/simple-calendar.js" type="text/javascript"></script><script src="/js/styleswitcher.js" type="text/javascript"></script><link rel="alternate" type="application/rss+xml" title="lkml.org : last 100 messages" href="/rss.php" /><link rel="alternate" type="application/rss+xml" title="lkml.org : last messages by Maxime Chevallier" href="/groupie.php?aid=" /><!--Matomo--><script> var _paq = window._paq = window._paq || []; /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(["setDoNotTrack", true]); _paq.push(["disableCookies"]); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="//m.lkml.org/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '1']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script><!--End Matomo Code--></head><body onload="es.jasper.simpleCalendar.init();" itemscope="itemscope" itemtype="http://schema.org/BlogPosting"><table border="0" cellpadding="0" cellspacing="0"><tr><td width="180" align="center"><a href="/"><img style="border:0;width:135px;height:32px" src="/images/toprowlk.gif" alt="lkml.org" /></a></td><td width="32">聽</td><td class="nb"><div><a class="nb" href="/lkml"> [lkml]</a> 聽 <a class="nb" href="/lkml/2025"> [2025]</a> 聽 <a class="nb" href="/lkml/2025/2"> [Feb]</a> 聽 <a class="nb" href="/lkml/2025/2/7"> [7]</a> 聽 <a class="nb" href="/lkml/last100"> [last100]</a> 聽 <a href="/rss.php"><img src="/images/rss-or.gif" border="0" alt="RSS Feed" /></a></div><div>Views: <a href="#" class="nowrap" onclick="setActiveStyleSheet('wrap');return false;">[wrap]</a><a href="#" class="wrap" onclick="setActiveStyleSheet('nowrap');return false;">[no wrap]</a> 聽 <a class="nb" href="/lkml/mheaders/2025/2/7/1824" onclick="this.href='/lkml/headers'+'/2025/2/7/1824';">[headers]</a>聽 <a href="/lkml/bounce/2025/2/7/1824">[forward]</a>聽 </div></td><td width="32">聽</td></tr><tr><td valign="top"><div class="es-jasper-simpleCalendar" baseurl="/lkml/"></div><div class="threadlist">Messages in this thread</div><ul class="threadlist"><li class="root"><a href="/lkml/2025/2/7/1821">First message in thread</a></li><li><a href="/lkml/2025/2/7/1821">Maxime Chevallier</a><ul><li><a href="/lkml/2025/2/7/1822">Maxime Chevallier</a></li><li><a href="/lkml/2025/2/7/1823">Maxime Chevallier</a></li><li class="origin"><a href="/lkml/2025/2/11/960">Maxime Chevallier</a><ul><li><a href="/lkml/2025/2/11/960">Kory Maincent</a><ul><li><a href="/lkml/2025/2/11/978">Maxime Chevallier</a><ul><li><a href="/lkml/2025/2/11/988">Kory Maincent</a></li></ul></li><li><a href="/lkml/2025/2/11/1014">Andrew Lunn</a><ul><li><a href="/lkml/2025/2/11/1032">Maxime Chevallier</a></li></ul></li></ul></li></ul></li><li><a href="/lkml/2025/2/7/1825">Maxime Chevallier</a></li><li><a href="/lkml/2025/2/7/1826">Maxime Chevallier</a><ul><li><a href="/lkml/2025/2/12/1162">Maxime Chevallier</a></li></ul></li><li><a href="/lkml/2025/2/7/1827">Maxime Chevallier</a><ul><li><a href="/lkml/2025/2/8/365">kernel test robot</a></li></ul></li><li><a href="/lkml/2025/2/7/1828">Maxime Chevallier</a></li><li><a href="/lkml/2025/2/7/1829">Maxime Chevallier</a></li><li><a href="/lkml/2025/2/7/1830">Maxime Chevallier</a></li><li><a href="/lkml/2025/2/7/1831">Maxime Chevallier</a></li><li><a href="/lkml/2025/2/7/1832">Maxime Chevallier</a><ul><li><a href="/lkml/2025/2/9/1">kernel test robot</a><ul><li><a href="/lkml/2025/2/11/491">Maxime Chevallier</a></li></ul></li></ul></li><li><a href="/lkml/2025/2/7/1833">Maxime Chevallier</a><ul><li><a href="/lkml/2025/2/9/2">kernel test robot</a></li></ul></li><li><a href="/lkml/2025/2/7/1834">Maxime Chevallier</a></li><li><a href="/lkml/2025/2/7/1934">Sean Anderson</a><ul><li><a href="/lkml/2025/2/10/388">Maxime Chevallier</a><ul><li><a href="/lkml/2025/2/12/1119">Sean Anderson</a><ul><li><a href="/lkml/2025/2/12/1126">Maxime Chevallier</a></li></ul></li></ul></li></ul></li><li><a href="/lkml/2025/2/12/1522">Rob Herring</a><ul><li><a href="/lkml/2025/2/13/515">Maxime Chevallier</a></li></ul></li></ul></li></ul><div class="threadlist">Patch in this message</div><ul class="threadlist"><li><a href="/lkml/diff/2025/2/7/1824/1">Get diff 1</a></li></ul></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerl.gif" width="32" height="32" alt="/" /></td><td class="c" rowspan="2" valign="top" style="padding-top: 1em"><table><tr><td><table><tr><td class="lp">From</td><td class="rp" itemprop="author">Maxime Chevallier &lt;&gt;</td></tr><tr><td class="lp">Subject</td><td class="rp" itemprop="name">[PATCH net-next 03/13] net: phy: Introduce PHY ports representation</td></tr><tr><td class="lp">Date</td><td class="rp" itemprop="datePublished">Fri, 7 Feb 2025 23:36:22 +0100</td></tr></table></td><td></td></tr></table><pre itemprop="articleBody">Ethernet provides a wide variety of layer 1 protocols and standards for<br />data transmission. The front-facing ports of an interface have their own<br />complexity and configurability.<br /><br />Introduce a representation of these front-facing ports. The current code<br />is minimalistic and only support ports controlled by PHY devices, but<br />the plan is to extend that to SFP as well as raw Ethernet MACs that<br />don't use PHY devices.<br /><br />This minimal port representation allows describing the media and number<br />of lanes of a port. From that information, we can derive the linkmodes<br />usable on the port, which can be used to limit the capabilities of an<br />interface.<br /><br />For now, the port lanes and medium is derived from devicetree, defined<br />by the PHY driver, or populated with default values (as we assume that<br />all PHYs expose at least one port).<br /><br />The typical example is 100M ethernet. 100BaseT can work using only 2<br />lanes on a Cat 5 cables. However, in the situation where a 10/100/1000<br />capable PHY is wired to its RJ45 port through 2 lanes only, we have no<br />way of detecting that. The "max-speed" DT property can be used, but a<br />more accurate representation can be used :<br /><br />mdi {<br /> port&#64;0 {<br /> media = "BaseT";<br /> lanes = &lt;2&gt;;<br /> };<br />};<br /><br />From that information, we can derive the max speed reachable on the<br />port.<br /><br />Another benefit of having that is to avoid vendor-specific DT properties<br />(micrel,fiber-mode or ti,fiber-mode).<br /><br />This basic representation is meant to be expanded, by the introduction<br />of port ops, userspace listing of ports, and support for multi-port<br />devices.<br /><br />Signed-off-by: Maxime Chevallier &lt;maxime.chevallier&#64;bootlin.com&gt;<br />---<br /> drivers/net/phy/Makefile | 2 +-<br /> drivers/net/phy/phy_device.c | 168 +++++++++++++++++++++++++++++++++++<br /> drivers/net/phy/phy_port.c | 166 ++++++++++++++++++++++++++++++++++<br /> include/linux/ethtool.h | 15 ++++<br /> include/linux/phy.h | 31 +++++++<br /> include/linux/phy_port.h | 92 +++++++++++++++++++<br /> 6 files changed, 473 insertions(+), 1 deletion(-)<br /> create mode 100644 drivers/net/phy/phy_port.c<br /> create mode 100644 include/linux/phy_port.h<br /><br />diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile<br />index c8dac6e92278..de1415a46629 100644<br />--- a/drivers/net/phy/Makefile<br />+++ b/drivers/net/phy/Makefile<br />&#64;&#64; -2,7 +2,7 &#64;&#64;<br /> # Makefile for Linux PHY drivers<br /> <br /> libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \<br />- linkmode.o phy_link_topology.o<br />+ linkmode.o phy_link_topology.o phy_port.o<br /> mdio-bus-y += mdio_bus.o mdio_device.o<br /> <br /> ifdef CONFIG_MDIO_DEVICE<br />diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c<br />index 46713d27412b..df016d344c55 100644<br />--- a/drivers/net/phy/phy_device.c<br />+++ b/drivers/net/phy/phy_device.c<br />&#64;&#64; -30,6 +30,7 &#64;&#64;<br /> #include &lt;linux/phylib_stubs.h&gt;<br /> #include &lt;linux/phy_led_triggers.h&gt;<br /> #include &lt;linux/phy_link_topology.h&gt;<br />+#include &lt;linux/phy_port.h&gt;<br /> #include &lt;linux/pse-pd/pse.h&gt;<br /> #include &lt;linux/property.h&gt;<br /> #include &lt;linux/ptp_clock_kernel.h&gt;<br />&#64;&#64; -656,6 +657,13 &#64;&#64; struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,<br /> <br /> dev-&gt;state = PHY_DOWN;<br /> INIT_LIST_HEAD(&amp;dev-&gt;leds);<br />+ INIT_LIST_HEAD(&amp;dev-&gt;ports);<br />+<br />+ /* The driver's probe function must change that to the real number<br />+ * of ports possible on the PHY. We assume by default we are dealing<br />+ * with a single-port PHY<br />+ */<br />+ dev-&gt;max_n_ports = 1;<br /> <br /> mutex_init(&amp;dev-&gt;lock);<br /> INIT_DELAYED_WORK(&amp;dev-&gt;state_queue, phy_state_machine);<br />&#64;&#64; -1412,6 +1420,46 &#64;&#64; void phy_sfp_detach(void *upstream, struct sfp_bus *bus)<br /> }<br /> EXPORT_SYMBOL(phy_sfp_detach);<br /> <br />+static int phy_add_port(struct phy_device *phydev, struct phy_port *port)<br />+{<br />+ int ret = 0;<br />+<br />+ if (phydev-&gt;n_ports == phydev-&gt;max_n_ports)<br />+ return -EBUSY;<br />+<br />+ /* We set all ports as active by default, PHY drivers may deactivate<br />+ * them (when unused)<br />+ */<br />+ port-&gt;active = true;<br />+<br />+ if (phydev-&gt;drv &amp;&amp; phydev-&gt;drv-&gt;attach_port)<br />+ ret = phydev-&gt;drv-&gt;attach_port(phydev, port);<br />+<br />+ if (ret)<br />+ return ret;<br />+<br />+ /* The PHY driver might have added, removed or set medium/lanes info,<br />+ * so update the port supported accordingly.<br />+ */<br />+ phy_port_update_supported(port);<br />+<br />+ list_add(&amp;port-&gt;head, &amp;phydev-&gt;ports);<br />+<br />+ phydev-&gt;n_ports++;<br />+<br />+ return 0;<br />+}<br />+<br />+static void phy_del_port(struct phy_device *phydev, struct phy_port *port)<br />+{<br />+ if (!phydev-&gt;n_ports)<br />+ return;<br />+<br />+ list_del(&amp;port-&gt;head);<br />+<br />+ phydev-&gt;n_ports--;<br />+}<br />+<br /> /**<br /> * phy_sfp_probe - probe for a SFP cage attached to this PHY device<br /> * &#64;phydev: Pointer to phy_device<br />&#64;&#64; -3405,6 +3453,119 &#64;&#64; static int of_phy_leds(struct phy_device *phydev)<br /> return 0;<br /> }<br /> <br />+static void phy_cleanup_ports(struct phy_device *phydev)<br />+{<br />+ struct phy_port *tmp, *port;<br />+<br />+ list_for_each_entry_safe(port, tmp, &amp;phydev-&gt;ports, head) {<br />+ phy_del_port(phydev, port);<br />+ phy_port_destroy(port);<br />+ }<br />+}<br />+<br />+static int phy_default_setup_single_port(struct phy_device *phydev)<br />+{<br />+ struct phy_port *port = phy_port_alloc();<br />+<br />+ if (!port)<br />+ return -ENOMEM;<br />+<br />+ port-&gt;parent_type = PHY_PORT_PHY;<br />+ port-&gt;phy = phydev;<br />+ linkmode_copy(port-&gt;supported, phydev-&gt;supported);<br />+<br />+ phy_add_port(phydev, port);<br />+<br />+ /* default medium is copper */<br />+ if (!port-&gt;mediums)<br />+ port-&gt;mediums |= BIT(ETHTOOL_LINK_MEDIUM_BASET);<br />+<br />+ return 0;<br />+}<br />+<br />+static int of_phy_ports(struct phy_device *phydev)<br />+{<br />+ struct device_node *node = phydev-&gt;mdio.dev.of_node;<br />+ struct device_node *mdi;<br />+ struct phy_port *port;<br />+ int err;<br />+<br />+ if (!IS_ENABLED(CONFIG_OF_MDIO))<br />+ return 0;<br />+<br />+ if (!node)<br />+ return 0;<br />+<br />+ mdi = of_get_child_by_name(node, "mdi");<br />+ if (!mdi)<br />+ return 0;<br />+<br />+ for_each_available_child_of_node_scoped(mdi, port_node) {<br />+ port = phy_of_parse_port(port_node);<br />+ if (IS_ERR(port)) {<br />+ err = PTR_ERR(port);<br />+ goto out_err;<br />+ }<br />+<br />+ port-&gt;parent_type = PHY_PORT_PHY;<br />+ port-&gt;phy = phydev;<br />+ err = phy_add_port(phydev, port);<br />+ if (err)<br />+ goto out_err;<br />+ }<br />+ of_node_put(mdi);<br />+<br />+ return 0;<br />+<br />+out_err:<br />+ phy_cleanup_ports(phydev);<br />+ of_node_put(mdi);<br />+ return err;<br />+}<br />+<br />+static int phy_setup_ports(struct phy_device *phydev)<br />+{<br />+ __ETHTOOL_DECLARE_LINK_MODE_MASK(ports_supported);<br />+ struct phy_port *port;<br />+ int ret;<br />+<br />+ ret = of_phy_ports(phydev);<br />+ if (ret)<br />+ return ret;<br />+<br />+ if (phydev-&gt;n_ports &lt; phydev-&gt;max_n_ports) {<br />+ ret = phy_default_setup_single_port(phydev);<br />+ if (ret)<br />+ goto out;<br />+ }<br />+<br />+ linkmode_zero(ports_supported);<br />+<br />+ /* Aggregate the supported modes, which are made-up of :<br />+ * - What the PHY itself supports<br />+ * - What the sum of all ports support<br />+ */<br />+ list_for_each_entry(port, &amp;phydev-&gt;ports, head)<br />+ if (port-&gt;active)<br />+ linkmode_or(ports_supported, ports_supported,<br />+ port-&gt;supported);<br />+<br />+ if (!linkmode_empty(ports_supported))<br />+ linkmode_and(phydev-&gt;supported, phydev-&gt;supported,<br />+ ports_supported);<br />+<br />+ /* For now, the phy-&gt;port field is set as the first active port's type */<br />+ list_for_each_entry(port, &amp;phydev-&gt;ports, head)<br />+ if (port-&gt;active)<br />+ phydev-&gt;port = phy_port_get_type(port);<br />+<br />+ return 0;<br />+<br />+out:<br />+ phy_cleanup_ports(phydev);<br />+ return ret;<br />+}<br />+<br /> /**<br /> * fwnode_mdio_find_device - Given a fwnode, find the mdio_device<br /> * &#64;fwnode: pointer to the mdio_device's fwnode<br />&#64;&#64; -3554,6 +3715,11 &#64;&#64; static int phy_probe(struct device *dev)<br /> phydev-&gt;is_gigabit_capable = 1;<br /> <br /> of_set_phy_supported(phydev);<br />+<br />+ err = phy_setup_ports(phydev);<br />+ if (err)<br />+ goto out;<br />+<br /> phy_advertise_supported(phydev);<br /> <br /> /* Get PHY default EEE advertising modes and handle them as potentially<br />&#64;&#64; -3630,6 +3796,8 &#64;&#64; static int phy_remove(struct device *dev)<br /> <br /> phydev-&gt;state = PHY_DOWN;<br /> <br />+ phy_cleanup_ports(phydev);<br />+<br /> sfp_bus_del_upstream(phydev-&gt;sfp_bus);<br /> phydev-&gt;sfp_bus = NULL;<br /> <br />diff --git a/drivers/net/phy/phy_port.c b/drivers/net/phy/phy_port.c<br />new file mode 100644<br />index 000000000000..3a7bdc44b556<br />--- /dev/null<br />+++ b/drivers/net/phy/phy_port.c<br />&#64;&#64; -0,0 +1,166 &#64;&#64;<br />+// SPDX-License-Identifier: GPL-2.0+<br />+/* Framework to drive Ethernet ports<br />+ *<br />+ * Copyright (c) 2024 Maxime Chevallier &lt;maxime.chevallier&#64;bootlin.com&gt;<br />+ */<br />+<br />+#include &lt;linux/linkmode.h&gt;<br />+#include &lt;linux/of.h&gt;<br />+#include &lt;linux/phy_port.h&gt;<br />+<br />+/**<br />+ * phy_port_alloc: Allocate a new phy_port<br />+ *<br />+ * Returns a newly allocated struct phy_port, or NULL.<br />+ */<br />+struct phy_port *phy_port_alloc(void)<br />+{<br />+ struct phy_port *port;<br />+<br />+ port = kzalloc(sizeof(*port), GFP_KERNEL);<br />+ if (!port)<br />+ return NULL;<br />+<br />+ linkmode_zero(port-&gt;supported);<br />+ INIT_LIST_HEAD(&amp;port-&gt;head);<br />+<br />+ return port;<br />+}<br />+EXPORT_SYMBOL_GPL(phy_port_alloc);<br />+<br />+/**<br />+ * phy_port_destroy: Free a struct phy_port<br />+ */<br />+void phy_port_destroy(struct phy_port *port)<br />+{<br />+ kfree(port);<br />+}<br />+EXPORT_SYMBOL_GPL(phy_port_destroy);<br />+<br />+static void ethtool_medium_get_supported(unsigned long *supported,<br />+ enum ethtool_link_medium medium,<br />+ int lanes)<br />+{<br />+ int i;<br />+<br />+ for (i = 0; i &lt; __ETHTOOL_LINK_MODE_MASK_NBITS; i++) {<br />+ /* Special bits such as Autoneg, Pause, Asym_pause, etc. are<br />+ * set and will be masked away by the port parent.<br />+ */<br />+ if (link_mode_params[i].medium == ETHTOOL_LINK_MEDIUM_NONE) {<br />+ linkmode_set_bit(i, supported);<br />+ continue;<br />+ }<br />+<br />+ /* For most cases, min_lanes == lanes, except for 10/100BaseT that work<br />+ * on 2 lanes but are compatible with 4 lanes mediums<br />+ */<br />+ if (link_mode_params[i].medium == medium &amp;&amp;<br />+ link_mode_params[i].lanes &gt;= lanes &amp;&amp;<br />+ link_mode_params[i].min_lanes &lt;= lanes) {<br />+ linkmode_set_bit(i, supported);<br />+ }<br />+ }<br />+}<br />+<br />+static enum ethtool_link_medium ethtool_str_to_medium(const char *str)<br />+{<br />+ int i;<br />+<br />+ for (i = 0; i &lt; __ETHTOOL_LINK_MEDIUM_LAST; i++)<br />+ if (!strcmp(phy_mediums(i), str))<br />+ return i;<br />+<br />+ return ETHTOOL_LINK_MEDIUM_NONE;<br />+}<br />+<br />+/**<br />+ * phy_of_parse_port: Create a phy_port from a firmware representation<br />+ *<br />+ * Returns a newly allocated and initialized phy_port pointer, or an ERR_PTR.<br />+ */<br />+struct phy_port *phy_of_parse_port(struct device_node *dn)<br />+{<br />+ struct fwnode_handle *fwnode = of_fwnode_handle(dn);<br />+ enum ethtool_link_medium medium;<br />+ struct phy_port *port;<br />+ struct property *prop;<br />+ const char *med_str;<br />+ u32 lanes, mediums = 0;<br />+ int ret;<br />+<br />+ ret = fwnode_property_read_u32(fwnode, "lanes", &amp;lanes);<br />+ if (ret)<br />+ lanes = 0;<br />+<br />+ ret = fwnode_property_read_string(fwnode, "media", &amp;med_str);<br />+ if (ret)<br />+ return ERR_PTR(ret);<br />+<br />+ of_property_for_each_string(to_of_node(fwnode), "media", prop, med_str) {<br />+ medium = ethtool_str_to_medium(med_str);<br />+ if (medium == ETHTOOL_LINK_MEDIUM_NONE)<br />+ return ERR_PTR(-EINVAL);<br />+<br />+ mediums |= BIT(medium);<br />+ }<br />+<br />+ if (!mediums)<br />+ return ERR_PTR(-EINVAL);<br />+<br />+ port = phy_port_alloc();<br />+ if (!port)<br />+ return ERR_PTR(-ENOMEM);<br />+<br />+ port-&gt;lanes = lanes;<br />+ port-&gt;mediums = mediums;<br />+<br />+ return port;<br />+}<br />+EXPORT_SYMBOL_GPL(phy_of_parse_port);<br />+<br />+/**<br />+ * phy_port_update_supported: Setup the port-&gt;supported field<br />+ * port: the port to update<br />+ *<br />+ * Once the port's medium list and number of lanes has been configured based<br />+ * on firmware, straps and vendor-specific properties, this function may be<br />+ * called to update the port's supported linkmodes list.<br />+ *<br />+ * Any mode that was manually set in the port's supported list remains set.<br />+ */<br />+void phy_port_update_supported(struct phy_port *port)<br />+{<br />+ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);<br />+ int i, lanes = 1;<br />+<br />+ /* If there's no lanes specified, we grab the default number of<br />+ * lanes as the max of the default lanes for each medium<br />+ */<br />+ if (!port-&gt;lanes)<br />+ for_each_set_bit(i, &amp;port-&gt;mediums, __ETHTOOL_LINK_MEDIUM_LAST)<br />+ lanes = max_t(int, lanes, phy_medium_default_lanes(i));<br />+<br />+ for_each_set_bit(i, &amp;port-&gt;mediums, __ETHTOOL_LINK_MEDIUM_LAST) {<br />+ linkmode_zero(supported);<br />+ ethtool_medium_get_supported(supported, i, port-&gt;lanes);<br />+ linkmode_or(port-&gt;supported, port-&gt;supported, supported);<br />+ }<br />+}<br />+EXPORT_SYMBOL_GPL(phy_port_update_supported);<br />+<br />+/**<br />+ * phy_port_get_type: get the PORT_* attribut for that port.<br />+ */<br />+int phy_port_get_type(struct phy_port *port)<br />+{<br />+ if (port-&gt;mediums &amp; ETHTOOL_LINK_MEDIUM_BASET)<br />+ return PORT_TP;<br />+<br />+ if (phy_port_is_fiber(port) ||<br />+ (port-&gt;mediums &amp; BIT(ETHTOOL_LINK_MEDIUM_BASEX)))<br />+ return PORT_FIBRE;<br />+<br />+ return PORT_OTHER;<br />+}<br />+EXPORT_SYMBOL_GPL(phy_port_get_type);<br />diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h<br />index 519a90ce24d3..32fe062b715c 100644<br />--- a/include/linux/ethtool.h<br />+++ b/include/linux/ethtool.h<br />&#64;&#64; -227,6 +227,10 &#64;&#64; enum ethtool_link_medium {<br /> __ETHTOOL_LINK_MEDIUM_LAST,<br /> };<br /> <br />+#define ETHTOOL_MEDIUM_FIBER_BITS (BIT(ETHTOOL_LINK_MEDIUM_BASES) | \<br />+ BIT(ETHTOOL_LINK_MEDIUM_BASEL) | \<br />+ BIT(ETHTOOL_LINK_MEDIUM_BASEF))<br />+<br /> static inline const char *phy_mediums(enum ethtool_link_medium medium)<br /> {<br /> switch (medium) {<br />&#64;&#64; -258,6 +262,17 &#64;&#64; static inline const char *phy_mediums(enum ethtool_link_medium medium)<br /> }<br /> }<br /> <br />+static inline int phy_medium_default_lanes(enum ethtool_link_medium medium)<br />+{<br />+ /* Let's consider that the default BaseT ethernet is BaseT4, i.e.<br />+ * Gigabit Ethernet.<br />+ */<br />+ if (medium == ETHTOOL_LINK_MEDIUM_BASET)<br />+ return 4;<br />+<br />+ return 1;<br />+}<br />+<br /> struct link_mode_info {<br /> int speed;<br /> u8 min_lanes;<br />diff --git a/include/linux/phy.h b/include/linux/phy.h<br />index 19f076a71f94..17bc287c1866 100644<br />--- a/include/linux/phy.h<br />+++ b/include/linux/phy.h<br />&#64;&#64; -316,6 +316,7 &#64;&#64; static inline long rgmii_clock(int speed)<br /> struct device;<br /> struct kernel_hwtstamp_config;<br /> struct phylink;<br />+struct phy_port;<br /> struct sfp_bus;<br /> struct sfp_upstream_ops;<br /> struct sk_buff;<br />&#64;&#64; -642,6 +643,9 &#64;&#64; struct macsec_ops;<br /> * &#64;master_slave_state: Current master/slave configuration<br /> * &#64;mii_ts: Pointer to time stamper callbacks<br /> * &#64;psec: Pointer to Power Sourcing Equipment control struct<br />+ * &#64;ports: List of PHY ports structures<br />+ * n_ports: Number of ports currently attached to the PHY<br />+ * &#64;max_n_ports: Max number of ports this PHY can expose<br /> * &#64;lock: Mutex for serialization access to PHY<br /> * &#64;state_queue: Work queue for state machine<br /> * &#64;link_down_events: Number of times link was lost<br />&#64;&#64; -734,6 +738,7 &#64;&#64; struct phy_device {<br /> <br /> /* Host supported PHY interface types. Should be ignored if empty. */<br /> DECLARE_PHY_INTERFACE_MASK(host_interfaces);<br />+ DECLARE_PHY_INTERFACE_MASK(sfp_bus_interfaces);<br /> <br /> #ifdef CONFIG_LED_TRIGGER_PHY<br /> struct phy_led_trigger *phy_led_triggers;<br />&#64;&#64; -776,6 +781,10 &#64;&#64; struct phy_device {<br /> struct mii_timestamper *mii_ts;<br /> struct pse_control *psec;<br /> <br />+ struct list_head ports;<br />+ int n_ports;<br />+ int max_n_ports;<br />+<br /> u8 mdix;<br /> u8 mdix_ctrl;<br /> <br />&#64;&#64; -1273,6 +1282,27 &#64;&#64; struct phy_driver {<br /> */<br /> int (*led_polarity_set)(struct phy_device *dev, int index,<br /> unsigned long modes);<br />+<br />+ /**<br />+ * &#64;attach_port: Indicates to the PHY driver that a port is detected<br />+ * &#64;dev: PHY device to notify<br />+ * &#64;port: The port being added<br />+ *<br />+ * Called when a port that needs to be driven by the PHY is found. The<br />+ * number of time this will be called depends on phydev-&gt;max_n_ports,<br />+ * which the driver can change in .probe().<br />+ *<br />+ * The port that is being passed may or may not be initialized. If it is<br />+ * already initialized, it is by the generic port representation from<br />+ * devicetree, which superseeds any strapping or vendor-specific<br />+ * properties.<br />+ *<br />+ * If the port isn't initialized, the port-&gt;mediums and port-&gt;lanes<br />+ * fields must be set, possibly according to stapping information.<br />+ *<br />+ * Returns 0, or an error code.<br />+ */<br />+ int (*attach_port)(struct phy_device *dev, struct phy_port *port);<br /> };<br /> #define to_phy_driver(d) container_of_const(to_mdio_common_driver(d), \<br /> struct phy_driver, mdiodrv)<br />&#64;&#64; -2083,6 +2113,7 &#64;&#64; void phy_trigger_machine(struct phy_device *phydev);<br /> void phy_mac_interrupt(struct phy_device *phydev);<br /> void phy_start_machine(struct phy_device *phydev);<br /> void phy_stop_machine(struct phy_device *phydev);<br />+<br /> void phy_ethtool_ksettings_get(struct phy_device *phydev,<br /> struct ethtool_link_ksettings *cmd);<br /> int phy_ethtool_ksettings_set(struct phy_device *phydev,<br />diff --git a/include/linux/phy_port.h b/include/linux/phy_port.h<br />new file mode 100644<br />index 000000000000..5dfad5157601<br />--- /dev/null<br />+++ b/include/linux/phy_port.h<br />&#64;&#64; -0,0 +1,92 &#64;&#64;<br />+/* SPDX-License-Identifier: GPL-2.0-or-later */<br />+<br />+#ifndef __PHY_PORT_H<br />+#define __PHY_PORT_H<br />+<br />+#include &lt;linux/ethtool.h&gt;<br />+#include &lt;linux/types.h&gt;<br />+#include &lt;linux/phy.h&gt;<br />+<br />+struct phy_port;<br />+<br />+/**<br />+ * enum phy_port_parent - The device this port is attached to<br />+ *<br />+ * &#64;PHY_PORT_PHY: Indicates that the port is driven by a PHY device<br />+ */<br />+enum phy_port_parent {<br />+ PHY_PORT_PHY,<br />+};<br />+<br />+struct phy_port_ops {<br />+ /* Sometimes, the link state can be retrieved from physical,<br />+ * out-of-band channels such as the LOS signal on SFP. These<br />+ * callbacks allows notifying the port about state changes<br />+ */<br />+ void (*link_up)(struct phy_port *port);<br />+ void (*link_down)(struct phy_port *port);<br />+<br />+ /* If the port acts as a Media Independent Interface (Serdes port),<br />+ * configures the port with the relevant state and mode. When enable is<br />+ * not set, interface should be ignored<br />+ */<br />+ int (*configure_mii)(struct phy_port *port, bool enable, phy_interface_t interface);<br />+};<br />+<br />+/**<br />+ * struct phy_port - A representation of a network device physical interface<br />+ *<br />+ * &#64;head: Used by the port's parent to list ports<br />+ * &#64;parent_type: The type of device this port is directly connected to<br />+ * &#64;phy: If the parent is PHY_PORT_PHYDEV, the PHY controlling that port<br />+ * &#64;lanes: The number of lanes (diff pairs) this port has, 0 if not applicable<br />+ * &#64;medium: The physical medium this port provides access to<br />+ * &#64;supported: The link modes this port can expose, if this port is MDI (not MII)<br />+ * &#64;interfaces: The MII interfaces this port supports, if this port is MII<br />+ * &#64;active: Indicates if the port is currently part of the active link.<br />+ * &#64;is_serdes: Indicates if this port is Serialised MII (Media Independent<br />+ * Interface), or an MDI (Media Dependent Interface).<br />+ */<br />+struct phy_port {<br />+ struct list_head head;<br />+ enum phy_port_parent parent_type;<br />+ union {<br />+ struct phy_device *phy;<br />+ };<br />+<br />+ const struct phy_port_ops *ops;<br />+<br />+ int lanes;<br />+ unsigned long mediums;<br />+ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);<br />+ DECLARE_PHY_INTERFACE_MASK(interfaces);<br />+<br />+ unsigned int active:1;<br />+ unsigned int is_serdes:1;<br />+};<br />+<br />+struct phy_port *phy_port_alloc(void);<br />+void phy_port_destroy(struct phy_port *port);<br />+<br />+static inline struct phy_device *port_phydev(struct phy_port *port)<br />+{<br />+ return port-&gt;phy;<br />+}<br />+<br />+struct phy_port *phy_of_parse_port(struct device_node *dn);<br />+<br />+static inline bool phy_port_is_copper(struct phy_port *port)<br />+{<br />+ return port-&gt;mediums == BIT(ETHTOOL_LINK_MEDIUM_BASET);<br />+}<br />+<br />+static inline bool phy_port_is_fiber(struct phy_port *port)<br />+{<br />+ return !!(port-&gt;mediums &amp; ETHTOOL_MEDIUM_FIBER_BITS);<br />+}<br />+<br />+void phy_port_update_supported(struct phy_port *port);<br />+<br />+int phy_port_get_type(struct phy_port *port);<br />+<br />+#endif<br />-- <br />2.48.1<br /><br /></pre></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerr.gif" width="32" height="32" alt="\" /></td></tr><tr><td align="right" valign="bottom"> 聽 </td></tr><tr><td align="right" valign="bottom">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerl.gif" width="32" height="32" alt="\" /></td><td class="c">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerr.gif" width="32" height="32" alt="/" /></td></tr><tr><td align="right" valign="top" colspan="2"> 聽 </td><td class="lm">Last update: 2025-02-07 23:38 聽聽 [from the cache]<br />漏2003-2020 <a href="http://blog.jasper.es/"><span itemprop="editor">Jasper Spaans</span></a>|hosted at <a href="https://www.digitalocean.com/?refcode=9a8e99d24cf9">Digital Ocean</a> and my Meterkast|<a href="http://blog.jasper.es/categories.html#lkml-ref">Read the blog</a></td><td>聽</td></tr></table><script language="javascript" src="/js/styleswitcher.js" type="text/javascript"></script></body></html>

Pages: 1 2 3 4 5 6 7 8 9 10