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 属性、固定链路属性并且还将包含 sfp 属性的 DT 节点。还应删除固定链路的设置;这些由 phylink 内部处理。
of_phy_connect() 还传递了一个用于链路更新的函数指针。此函数由下面 (8) 中描述的不同形式的 MAC 更新替换。
PHY 的支持/通告的操控在 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
操作集中。此函数必须返回一个指向相关的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);
从探测函数中删除对
of_parse_phandle()
(用于 PHY) 和 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
。