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_opsops 集中。 此函式必須返回一個指向將用於請求的鏈路配置的相關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 中的繫結文件。