phylink¶
概述¶
phylink 是一种机制,用于支持直接连接到 MAC 的热插拔网络模块,而无需在热插拔事件时重新初始化适配器。
phylink 目前支持传统的基于 phylib 的设置、固定链路设置和 SFP(小型可插拔)模块。
操作模式¶
phylink 有几种操作模式,具体取决于固件设置。
PHY 模式
在 PHY 模式下,我们使用 phylib 从 PHY 读取当前的链路设置,并将它们传递给 MAC 驱动程序。我们希望 MAC 驱动程序配置指定的模式,而不在链路上启用任何协商。
固定模式
就 MAC 驱动程序而言,固定模式与 PHY 模式相同。
带内模式
带内模式与 802.3z、SGMII 和类似的接口模式一起使用,我们期望使用和遵守通过 serdes 通道发送的带内协商或控制字。
举例来说,这意味着
ð {
phy = <&phy>;
phy-mode = "sgmii";
};
不使用带内 SGMII 信令。 PHY 应严格遵循其 mac_config()
函数中给定的设置。链路应在 mac_link_up()
和 mac_link_down()
函数中强制启动或关闭。
ð {
managed = "in-band-status";
phy = <&phy>;
phy-mode = "sgmii";
};
使用带内模式,其中来自 PHY 协商的结果通过 SGMII 控制字传递给 MAC,并且 MAC 应确认控制字。mac_link_up()
和 mac_link_down()
函数不得强制 MAC 侧链路启动和关闭。
将网络驱动程序转换为 sfp/phylink 的粗略指南¶
本指南简要介绍了如何将网络驱动程序从 phylib 转换为 sfp/phylink 支持。 请发送补丁来改进此文档。
可以选择将网络驱动程序的 phylib 更新函数拆分为处理链路断开和链路建立的两个部分。 这可以作为单独的准备提交来完成。
可以在 git commit fc548b991fb0 中找到此准备工作的一个较旧示例,尽管它是拆分为三个部分;链路建立部分现在包括配置 MAC 以进行链路设置。 有关此内容的更多信息,请参见
mac_link_up()
。替换
select FIXED_PHY select PHYLIB
用
select PHYLINK
在驱动程序的 Kconfig 节中。
添加
#include <linux/phylink.h>
到驱动程序的头文件列表中。
添加
struct phylink *phylink; struct phylink_config phylink_config;
到驱动程序的私有数据结构。 我们将驱动程序的私有数据指针称为
priv
,驱动程序的私有数据结构称为struct foo_priv
。替换以下函数
原始函数
替换函数
phy_start(phydev)
phylink_start(priv->phylink)
phy_stop(phydev)
phylink_stop(priv->phylink)
phy_mii_ioctl(phydev, ifr, cmd)
phylink_mii_ioctl(priv->phylink, ifr, cmd)
phy_ethtool_get_wol(phydev, wol)
phylink_ethtool_get_wol(priv->phylink, wol)
phy_ethtool_set_wol(phydev, wol)
phylink_ethtool_set_wol(priv->phylink, wol)
phy_disconnect(phydev)
phylink_disconnect_phy(priv->phylink)
请注意,其中一些函数必须在 rtnl 锁下调用,否则会发出警告。 通常情况下是这样,除非它们是从驱动程序的暂停/恢复路径中调用的。
使用以下方法添加/替换 ksettings 获取/设置方法
static int foo_ethtool_set_link_ksettings(struct net_device *dev, const struct ethtool_link_ksettings *cmd) { struct foo_priv *priv = netdev_priv(dev); return phylink_ethtool_ksettings_set(priv->phylink, cmd); } static int foo_ethtool_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *cmd) { struct foo_priv *priv = netdev_priv(dev); return phylink_ethtool_ksettings_get(priv->phylink, cmd); }
替换对
phy_dev = of_phy_connect(dev, node, link_func, flags, phy_interface);
以及相关代码的调用,并将其替换为对
err = phylink_of_phy_connect(priv->phylink, node, flags);
在大多数情况下,
flags
可以为零;如果在 DT 节点node
中指定了 PHY,则这些标志将传递给此函数调用中的phy_attach_direct()
。node
应该是包含网络 phy 属性、固定链路属性的 DT 节点,并且还将包含 sfp 属性。还应删除固定链路的设置; 这些由 phylink 在内部处理。
of_phy_connect() 还传递了用于链路更新的函数指针。 此函数被 (8) 中描述的不同形式的 MAC 更新所取代。
PHY 的 supported/advertised 的操作发生在 phylink 内部,基于验证回调,请参见下面的 (8)。
请注意,驱动程序不再需要存储
phy_interface
,还要注意phy_interface
变成了一个动态属性,就像速度、双工等设置一样。最后,请注意 MAC 驱动程序不再能够直接访问 PHY; 这是因为在 phylink 模型中,PHY 可以是动态的。
向驱动程序添加一个
struct phylink_mac_ops
实例,它是一个函数指针表,并实现这些函数。 用于of_phy_connect()
的旧链路更新函数变成了三个方法:mac_link_up()
、mac_link_down()
和mac_config()
。 如果执行了步骤 1,则功能将被拆分到那里。重要的是,如果使用了带内协商,则
mac_link_up()
和mac_link_down()
不会阻止带内协商完成,因为这些函数是在带内链路状态更改时调用的 - 否则链路将永远不会启动。mac_get_caps()
方法是可选的,如果提供,则应返回为传递的interface
模式支持的 phylink MAC 功能。 通常,无需实现此方法。 Phylink 将结合interface
的允许功能使用这些功能来确定允许的 ethtool 链路模式。mac_link_state()
方法用于从 MAC 读取链路状态,并报告 MAC 当前使用的设置。 这对于带内协商方法(例如 1000base-X 和 SGMII)尤其重要。mac_link_up()
方法用于通知 MAC 链路已启动。 该调用仅包括协商模式和接口以供参考。 还提供了最终的链路参数(速度、双工和流量控制/暂停启用设置),当 MAC 和 PCS 没有紧密集成的,或者当设置不是来自带内协商时,应使用这些参数来配置 MAC。mac_config()
方法用于使用请求的状态更新 MAC,并且在更改 MAC 配置时必须避免不必要地断开链路。 这意味着该函数应修改状态,并且仅在绝对需要更改 MAC 配置时才断开链路。 可以在drivers/net/ethernet/marvell/mvneta.c
中的mvneta_mac_config()
中找到有关如何执行此操作的示例。有关这些方法的更多信息,请参见
struct phylink_mac_ops
中的内联文档。使用对与您的
struct net_device
关联的struct device
的引用来填充struct phylink_config
字段priv->phylink_config.dev = &dev.dev; priv->phylink_config.type = PHYLINK_NETDEV;
填充您的 MAC 可以处理的各种速度、暂停和双工模式
priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD;
某些以太网控制器与 PCS(物理编码子层)块配对工作,该块可以处理编码/解码、链路建立检测和自动协商等。 虽然某些 MAC 具有内部 PCS,其操作是透明的,但其他一些 MAC 需要专用的 PCS 配置才能使链路正常工作。 在这种情况下,phylink 通过
struct phylink_pcs
提供 PCS 抽象。确定您的驱动程序是否有一个或多个内部 PCS 块,以及您的控制器是否可以使用可能在内部连接到您的控制器的外部 PCS 块。
如果您的控制器没有任何内部 PCS,则可以转到步骤 11。
如果您的以太网控制器包含一个或多个 PCS 块,请在驱动程序的私有数据结构中为每个 PCS 块创建一个
struct phylink_pcs
实例struct phylink_pcs pcs;
填充相关的
struct phylink_pcs_ops
以配置您的 PCS。 创建一个pcs_get_state()
函数,该函数报告带内链路状态,一个pcs_config()
函数,该函数根据 phylink 提供的参数配置您的 PCS,以及一个pcs_validate()
函数,该函数向 phylink 报告您的 PCS 接受的所有配置参数struct phylink_pcs_ops foo_pcs_ops = { .pcs_validate = foo_pcs_validate, .pcs_get_state = foo_pcs_get_state, .pcs_config = foo_pcs_config, };
安排将 PCS 链路状态中断转发到 phylink 中,通过
phylink_pcs_change(pcs, link_is_up);
其中,如果链路当前已启动,则
link_is_up
为 true,否则为 false。 如果 PCS 无法提供这些中断,则应在创建 PCS 时设置pcs->pcs_poll = true;
。如果您的控制器依赖于或接受通过其自身驱动程序控制的外部 PCS 的存在,请在您的驱动程序私有数据结构中添加一个指向 phylink_pcs 实例的指针
struct phylink_pcs *pcs;
获取实际 PCS 实例的方式取决于平台,一些 PCS 位于 MDIO 总线上,并通过传递指向相应的
struct mii_bus
的指针和该总线上 PCS 的地址来获取。 在此示例中,我们假设控制器连接到 Lynx PCS 实例priv->pcs = lynx_pcs_create_mdiodev(bus, 0);
可以基于固件信息恢复一些 PCS
priv->pcs = lynx_pcs_create_fwnode(of_fwnode_handle(node));
填充
mac_select_pcs()
回调,并将其添加到您的struct phylink_mac_ops
ops 集中。 此函数必须返回一个指向将用于请求的链路配置的相关struct phylink_pcs
的指针static struct phylink_pcs *foo_select_pcs(struct phylink_config *config, phy_interface_t interface) { struct foo_priv *priv = container_of(config, struct foo_priv, phylink_config); if ( /* 'interface' needs a PCS to function */ ) return priv->pcs; return NULL; }
有关具有多个内部 PCS 的驱动程序的示例,请参见
mvpp2_select_pcs()
。填充您的 MAC 可以输出的所有
phy_interface_t
(即,所有 MAC 到 PHY 链路模式)。 以下示例显示了可以处理所有 RGMII 模式、SGMII 和 1000BaseX 的 MAC 的配置。 您必须根据您的 MAC 和与此 MAC 关联的所有 PCS 的功能来调整这些设置,而不仅仅是您希望使用的接口phy_interface_set_rgmii(priv->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_SGMII, priv->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_1000BASEX, priv->phylink_config.supported_interfaces);
从探测函数中删除对 PHY 的
of_parse_phandle()
的调用、对固定链路的 of_phy_register_fixed_link() 等,并将其替换为struct phylink *phylink; phylink = phylink_create(&priv->phylink_config, node, phy_mode, &phylink_ops); if (IS_ERR(phylink)) { err = PTR_ERR(phylink); fail probe; } priv->phylink = phylink;
并通过调用来适当地安排在探测失败路径和移除路径中销毁 phylink
phylink_destroy(priv->phylink);
安排将 MAC 链路状态中断转发到 phylink 中,通过
phylink_mac_change(priv->phylink, link_is_up);
其中,如果链路当前已启动,则
link_is_up
为 true,否则为 false。验证驱动程序是否不调用
netif_carrier_on() netif_carrier_off()
因为这些会干扰 phylink 对链路状态的跟踪,并导致 phylink 省略通过
mac_link_up()
和mac_link_down()
方法的调用。
网络驱动程序应通过其暂停/恢复路径调用 phylink_stop()
和 phylink_start()
,这可确保根据需要调用适当的 struct phylink_mac_ops
方法。
有关在 DT 中描述 SFP 插槽的信息,请参见内核源代码树 Documentation/devicetree/bindings/net/sff,sfp.yaml
中的绑定文档。