PHY 抽象層¶
目的¶
大多數網路裝置都包含一組暫存器,這些暫存器提供了一個到 MAC 層的介面,MAC 層透過 PHY 與物理連線進行通訊。PHY 負責與網路連線另一端的鏈路夥伴(通常是乙太網電纜)協商鏈路引數,並提供一個暫存器介面,允許驅動程式確定選擇了哪些設定,以及配置允許哪些設定。
雖然這些裝置與網路裝置不同,並且符合暫存器的標準佈局,但將 PHY 管理程式碼與網路驅動程式整合是一種常見的做法。這導致了大量的冗餘程式碼。此外,在具有多個(有時非常不同的)乙太網控制器連線到同一管理匯流排的嵌入式系統上,很難確保匯流排的安全使用。
由於 PHY 是裝置,並且訪問它們的管理匯流排實際上是匯流排,因此 PHY 抽象層將其視為裝置和匯流排。這樣做,它有以下目標
提高程式碼重用率
提高整體程式碼可維護性
加快新網路驅動程式和新系統的開發時間
基本上,該層旨在提供一個到 PHY 裝置的介面,該介面允許網路驅動程式編寫者編寫儘可能少的程式碼,同時仍然提供完整的功能集。
MDIO 匯流排¶
大多數網路裝置透過管理匯流排連線到 PHY。不同的裝置使用不同的匯流排(儘管有些裝置共享通用介面)。為了利用 PAL,每個匯流排介面都需要註冊為一個單獨的裝置。
必須實現讀寫功能。它們的原型是
int write(struct mii_bus *bus, int mii_id, int regnum, u16 value); int read(struct mii_bus *bus, int mii_id, int regnum);
mii_id 是 PHY 在總線上的地址,regnum 是暫存器號。保證不會在中斷時呼叫這些函式,因此它們可以安全地阻塞,等待中斷訊號指示操作完成
重置功能是可選的。它用於將匯流排返回到初始化狀態。
需要一個探測函式。該函式應設定匯流排驅動程式需要的任何內容,設定 mii_bus 結構,並使用 mdiobus_register 向 PAL 註冊。類似地,有一個刪除函式可以撤消所有這些操作(使用 mdiobus_unregister)。
像任何驅動程式一樣,必須配置 device_driver 結構,並且 init exit 函式用於註冊驅動程式。
匯流排也必須在某處宣告為裝置並註冊。
作為如何實現 mdio 匯流排驅動程式的一個示例,請參見 drivers/net/ethernet/freescale/fsl_pq_mdio.c 和一個使用者的相關 DTS 檔案。(例如,“git grep fsl,.*-mdio arch/powerpc/boot/dts/”)
(RG)MII/電氣介面注意事項¶
精簡千兆媒體獨立介面 (RGMII) 是一種 12 針電氣訊號介面,使用同步 125Mhz 時鐘訊號和多條資料線。由於此設計決策,必須在時鐘線(RXC 或 TXC)和資料線之間新增 1.5ns 到 2ns 的延遲,以使 PHY(時鐘接收器)有足夠大的建立和保持時間來正確取樣資料線。PHY 庫提供不同型別的 PHY_INTERFACE_MODE_RGMII* 值,以允許 PHY 驅動程式和可選的 MAC 驅動程式實現所需的延遲。phy_interface_t 的值必須從 PHY 裝置本身的角度來理解,從而導致以下情況
PHY_INTERFACE_MODE_RGMII:PHY 不負責自行插入任何內部延遲,它假定乙太網 MAC(如果能夠)或 PCB 走線插入正確的 1.5-2ns 延遲
PHY_INTERFACE_MODE_RGMII_TXID:PHY 應為 PHY 裝置處理的傳送資料線 (TXD[3:0]) 插入內部延遲
PHY_INTERFACE_MODE_RGMII_RXID:PHY 應為 PHY 裝置處理的接收資料線 (RXD[3:0]) 插入內部延遲
PHY_INTERFACE_MODE_RGMII_ID:PHY 應為來自/到 PHY 裝置的傳送和接收資料線插入內部延遲
在可能的情況下,請使用 PHY 側 RGMII 延遲,原因如下
PHY 裝置可能提供亞納秒級的粒度,以允許指定接收器/傳送器側延遲(例如:0.5、1.0、1.5ns)。可能需要這種精度來解釋 PCB 走線長度的差異
PHY 裝置通常適用於各種應用(工業、醫療、汽車...),並且它們在溫度/壓力/電壓範圍內提供恆定且可靠的延遲
PHYLIB 中的 PHY 裝置驅動程式本質上是可重用的,能夠正確配置指定的延遲使得具有相似延遲要求的更多設計能夠正確執行
對於 PHY 無法提供此延遲,但乙太網 MAC 驅動程式能夠提供延遲的情況,正確的 phy_interface_t 值應為 PHY_INTERFACE_MODE_RGMII,並且應正確配置乙太網 MAC 驅動程式,以便從 PHY 裝置的角度來看,提供所需的傳送和/或接收側延遲。相反,如果乙太網 MAC 驅動程式檢視 phy_interface_t 值,對於除 PHY_INTERFACE_MODE_RGMII 之外的任何其他模式,它應確保停用 MAC 級別的延遲。
如果乙太網 MAC 和 PHY 都無法提供 RGMII 標準定義的所需延遲,則可能存在多種選擇
某些 SoC 可能會提供引腳焊盤/多路複用器/控制器,能夠配置給定引腳的強度、延遲和電壓;這可能是插入預期 2ns RGMII 延遲的合適選擇。
修改 PCB 設計以包括固定延遲(例如:使用專門設計的蛇形線),這可能根本不需要軟體配置。
RGMII 延遲不匹配的常見問題¶
如果乙太網 MAC 和 PHY 之間存在 RGMII 延遲不匹配,則最有可能導致時鐘和資料線訊號不穩定,當 PHY 或 MAC 獲取這些訊號的快照以將它們轉換為邏輯 1 或 0 狀態並重建正在傳送/接收的資料時。典型症狀包括
傳輸/接收部分工作,並且觀察到頻繁或偶爾的丟包
乙太網 MAC 可能會報告帶有 FCS/CRC 錯誤的某些或所有進入資料包,或者只是丟棄所有資料包
切換到較低的速度(如 10/100Mbits/秒)可以解決問題(因為在這種情況下有足夠的建立/保持時間)
連線到 PHY¶
在啟動期間的某個時間,網路驅動程式需要建立 PHY 裝置和網路裝置之間的連線。此時,PHY 的匯流排和驅動程式需要全部載入,以便準備好進行連線。此時,有幾種連線到 PHY 的方法
PAL 處理所有事情,並且僅在鏈路狀態更改時呼叫網路驅動程式,以便它可以做出反應。
PAL 處理除中斷之外的所有事情(通常是因為控制器具有中斷暫存器)。
PAL 處理所有事情,但每秒與驅動程式檢查一次,允許網路驅動程式首先對 PAL 採取任何操作之前的任何更改做出反應。
PAL 僅用作函式庫,網路裝置手動呼叫函式以更新狀態並配置 PHY
讓 PHY 抽象層完成所有事情¶
如果選擇選項 1(希望每個驅動程式都可以,但仍然對不能的驅動程式有用),則連線到 PHY 非常簡單
首先,需要一個函式來對鏈路狀態的變化做出反應。此函式遵循以下協議
static void adjust_link(struct net_device *dev);
接下來,需要知道連線到此裝置的 PHY 的裝置名稱。該名稱看起來像“0:00”,其中第一個數字是匯流排 ID,第二個數字是 PHY 在該總線上的地址。通常,匯流排負責使其 ID 唯一。
現在,要連線,只需呼叫此函式
phydev = phy_connect(dev, phy_name, &adjust_link, interface);
phydev是指向表示 PHY 的 phy_device 結構的指標。如果 phy_connect 成功,它將返回該指標。這裡的 dev 是指向 net_device 的指標。完成後,此函式將啟動 PHY 的軟體狀態機,並註冊 PHY 的中斷(如果它有中斷)。phydev 結構將填充有關當前狀態的資訊,但此時 PHY 尚未真正執行。
應在呼叫 phy_connect() 之前在 phydev->dev_flags 中設定 PHY 特定的標誌,以便底層 PHY 驅動程式可以檢查標誌並根據它們執行特定的操作。如果系統對 PHY/控制器施加了硬體限制,而 PHY 需要知道這些限制,則此功能很有用。
interface是一個 u32,它指定控制器和 PHY 之間使用的連線型別。示例包括 GMII、MII、RGMII 和 SGMII。有關完整列表,請參見下面的“PHY 介面模式”和 include/linux/phy.h。
現在只需確保 phydev->supported 和 phydev->advertising 中刪除了對控制器沒有意義的任何值(10/100 控制器可能連線到具有千兆功能的 PHY,因此需要遮蔽 SUPPORTED_1000baseT*)。有關這些位欄位的定義,請參見 include/linux/ethtool.h。請注意,除了 SUPPORTED_Pause 和 SUPPORTED_AsymPause 位(參見下文)之外,不應 SET 任何位,否則 PHY 可能會進入不受支援的狀態。
最後,一旦控制器準備好處理網路流量,就可以呼叫 phy_start(phydev)。這會告訴 PAL 您已準備就緒,並將 PHY 配置為連線到網路。如果網路驅動程式的 MAC 中斷也處理 PHY 狀態更改,只需在呼叫 phy_start 之前將 phydev->irq 設定為 PHY_MAC_INTERRUPT,然後使用網路驅動程式中的 phy_mac_interrupt()。如果不希望使用中斷,請將 phydev->irq 設定為 PHY_POLL。phy_start() 啟用 PHY 中斷(如果適用)並啟動 phylib 狀態機。
如果要斷開與網路的連線(即使只是短暫地斷開),則可以呼叫 phy_stop(phydev)。此函式還會停止 phylib 狀態機並停用 PHY 中斷。
PHY 介面模式¶
在 phy_connect() 系列函式中提供的 PHY 介面模式定義了 PHY 介面的初始操作模式。不能保證它保持恆定;有些 PHY 會動態更改其介面模式,而無需軟體互動,具體取決於協商結果。
下面描述了一些介面模式
PHY_INTERFACE_MODE_SMII這是序列 MII,時鐘頻率為 125MHz,支援 100M 和 10M 速度。可以在 https://opencores.org/ocsvn/smii/smii/trunk/doc/SMII.pdf 中找到一些詳細資訊
PHY_INTERFACE_MODE_1000BASEX這定義了 802.3 標準第 36 節定義的 1000BASE-X 單通道 serdes 鏈路。該鏈路以 1.25Gbaud 的固定位元率執行,使用 10B/8B 編碼方案,從而產生 1Gbps 的底層資料速率。資料流中嵌入了一個 16 位控制字,用於與遠端端協商雙工和暫停模式。這不包括“升頻”變體,例如 2.5Gbps 速度(參見下文)。
PHY_INTERFACE_MODE_2500BASEX這定義了 1000BASE-X 的一種變體,其時鐘頻率是 802.3 標準的 2.5 倍,從而給出 3.125Gbaud 的固定位元率。
PHY_INTERFACE_MODE_SGMII這用於 Cisco SGMII,它是 802.3 標準定義的 1000BASE-X 的修改版。SGMII 鏈路由以 1.25Gbaud 的固定位元率執行的單通道 serdes 組成,並採用 10B/8B 編碼。底層資料速率為 1Gbps,透過複製每個資料符號來實現較慢的 100Mbps 和 10Mbps 速度。重新調整 802.3 控制字的用途,以將協商的速度和雙工資訊從 MAC 傳送到 MAC,並讓 MAC 確認收到資訊。這不包括“升頻”變體,例如 2.5Gbps 速度。
注意:在某些情況下,鏈路上的 SGMII 與 1000BASE-X 配置不匹配可以成功傳遞資料,但不會正確解釋 16 位控制字,這可能會導致雙工、暫停或其他設定不匹配。這取決於 MAC 和/或 PHY 的行為。
PHY_INTERFACE_MODE_5GBASER這是 IEEE 802.3 Clause 129 定義的 5GBASE-R 協議。它與 Clause 49 中定義的 10GBASE-R 協議相同,唯一的區別是它以一半的頻率執行。有關定義,請參閱 IEEE 標準。
PHY_INTERFACE_MODE_10GBASER這是 IEEE 802.3 Clause 49 定義的 10GBASE-R 協議,用於各種不同的介質。有關定義,請參閱 IEEE 標準。
注意:10GBASE-R 只是可以與 XFI 和 SFI 一起使用的一種協議。XFI 和 SFI 允許在單個 SERDES 通道上使用多個協議,並且還定義了將主機合規性板插入主機 XFP/SFP 聯結器時訊號的電氣特性。因此,XFI 和 SFI 本身不是 PHY 介面型別。
PHY_INTERFACE_MODE_10GKR這是 IEEE 802.3 Clause 49 定義的 10GBASE-R,具有 Clause 73 自動協商。有關更多資訊,請參閱 IEEE 標準。
注意:由於歷史原因,一些 10GBASE-R 的使用錯誤地使用了此定義。
PHY_INTERFACE_MODE_25GBASER這是 IEEE 802.3 PCS Clause 107 定義的 25GBASE-R 協議。PCS 與 10GBASE-R 相同,即 64B/66B 編碼以 2.5 倍的速度執行,從而給出 25.78125 Gbaud 的固定位元率。有關更多資訊,請參閱 IEEE 標準。
PHY_INTERFACE_MODE_100BASEX這定義了 IEEE 802.3 Clause 24。該鏈路以 125Mpbs 的固定資料速率執行,使用 4B/5B 編碼方案,從而產生 100Mpbs 的底層資料速率。
PHY_INTERFACE_MODE_QUSGMII這定義了 Cisco 的 Quad USGMII 模式,它是 USGMII(通用 SGMII)鏈路的四通道變體。它與 QSGMII 非常相似,但使用資料包控制標頭 (PCH) 而不是 7 位元組的前導碼來攜帶埠 ID 以及所謂的“擴充套件”。規範中迄今為止唯一記錄的擴充套件是包含時間戳,用於啟用 PTP 的 PHY。此模式與 QSGMII 不相容,但在鏈路速度和協商方面提供相同的功能。
PHY_INTERFACE_MODE_1000BASEKX這是 IEEE 802.3 Clause 36 定義的 1000BASE-X,具有 Clause 73 自動協商。通常,它將與 Clause 70 PMD 一起使用。為了與用於 Clause 38 和 39 PMD 的 1000BASE-X phy 模式形成對比,此介面模式具有不同的自動協商,並且僅支援全雙工。
PHY_INTERFACE_MODE_PSGMII這是 Penta SGMII 模式,它類似於 QSGMII,但它將 5 條 SGMII 線組合到一個鏈路上,而 QSGMII 上有 4 條線。
PHY_INTERFACE_MODE_10G_QXGMII表示 Cisco USXGMII 多埠銅介面文件定義的 10G-QXGMII PHY-MAC 介面。它在 10.3125 GHz SerDes 通道上支援 4 個埠,每個埠的速度為 2.5G / 1G / 100M / 10M,透過符號複製實現。PCS 期望標準的 USXGMII 程式碼字。
暫停幀 / 流量控制¶
PHY 不直接參與流量控制/暫停幀,除非透過確保 MII_ADVERTISE 中設定了 SUPPORTED_Pause 和 SUPPORTED_AsymPause 位來向鏈路夥伴表明乙太網 MAC 控制器支援此類功能。由於流量控制/暫停幀生成涉及乙太網 MAC 驅動程式,因此建議該驅動程式透過相應地設定 SUPPORTED_Pause 和 SUPPORTED_AsymPause 位來負責正確指示對此類功能的通告和支援。這可以在 phy_connect() 之前或之後以及/或者作為實現 ethtool::set_pauseparam 功能的結果來完成。
密切關注 PAL¶
PAL 的內建狀態機可能需要一些幫助才能使網路裝置和 PHY 正確同步。如果是這樣,可以在連線到 PHY 時註冊一個幫助程式函式,該函式將在狀態機對任何更改做出反應之前每秒呼叫一次。為此,需要手動呼叫 phy_attach() 和 phy_prepare_link(),然後呼叫 phy_start_machine(),並將第二個引數設定為指向特殊處理程式。
目前沒有如何使用此功能的示例,並且對其的測試受到限制,因為作者沒有任何使用它的驅動程式(它們都使用選項 1)。所以,買者自負。
自己完成所有事情¶
PAL 的內建狀態機可能無法跟蹤 PHY 和網路裝置之間複雜的互動,這只是一個很小的機會。如果是這樣,只需呼叫 phy_attach(),而不要呼叫 phy_start_machine 或 phy_prepare_link()。這意味著 phydev->state 完全由您處理(phy_start 和 phy_stop 在某些狀態之間切換,因此您可能需要避免使用它們)。
已經努力確保可以在不執行狀態機的情況下訪問有用的功能,並且大多數這些功能都源於不與複雜狀態機互動的功能。但是,同樣,到目前為止,還沒有努力測試在沒有狀態機的情況下執行,因此請謹慎嘗試。
以下是這些函式的簡要概述
int phy_read(struct phy_device *phydev, u16 regnum);
int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
簡單的讀/寫原語。它們呼叫匯流排的讀/寫函式指標。
void phy_print_status(struct phy_device *phydev);
一個方便的函式,用於整齊地打印出 PHY 狀態。
void phy_request_interrupt(struct phy_device *phydev);
請求 PHY 中斷的 IRQ。
struct phy_device * phy_attach(struct net_device *dev, const char *phy_id,
phy_interface_t interface);
將網路裝置連線到特定的 PHY,如果在匯流排初始化期間未找到任何通用驅動程式,則將該 PHY 繫結到通用驅動程式。
int phy_start_aneg(struct phy_device *phydev);
使用 phydev 結構中的變數,配置廣告並重置自動協商,或者停用自動協商,並配置強制設定。
static inline int phy_read_status(struct phy_device *phydev);
使用有關 PHY 中當前設定的最新資訊填充 phydev 結構。
int phy_ethtool_ksettings_set(struct phy_device *phydev,
const struct ethtool_link_ksettings *cmd);
Ethtool 方便函式。
int phy_mii_ioctl(struct phy_device *phydev,
struct mii_ioctl_data *mii_data, int cmd);
MII ioctl。請注意,如果您寫入 BMCR、BMSR、ADVERTISE 等暫存器,此函式將完全搞亂狀態機。最好僅使用它來寫入非標準暫存器,並且不要觸發重新協商。
PHY 裝置驅動程式¶
使用 PHY 抽象層,新增對新 PHY 的支援非常容易。在某些情況下,根本不需要任何工作!但是,許多 PHY 需要一些幫助才能啟動並執行。
通用 PHY 驅動程式¶
如果所需的 PHY 沒有任何勘誤、怪癖或您想要支援的特殊功能,那麼最好不要新增支援,而讓 PHY 抽象層的通用 PHY 驅動程式完成所有工作。
編寫 PHY 驅動程式¶
如果確實需要編寫 PHY 驅動程式,首先要做的是確保它可以與合適的 PHY 裝置匹配。這在匯流排初始化期間透過讀取裝置的 UID(儲存在暫存器 2 和 3 中),然後將其與每個驅動程式的 phy_id 欄位進行比較,並透過將它與每個驅動程式的 phy_id_mask 欄位進行 AND 運算來完成。此外,它還需要一個名稱。這是一個示例
static struct phy_driver dm9161_driver = {
.phy_id = 0x0181b880,
.name = "Davicom DM9161E",
.phy_id_mask = 0x0ffffff0,
...
}
接下來,需要指定 PHY 裝置和驅動程式支援哪些功能(速度、雙工、自動協商等)。大多數 PHY 支援 PHY_BASIC_FEATURES,但可以在 include/mii.h 中查詢其他功能。
每個驅動程式都包含許多函式指標,這些函式指標記錄在 include/linux/phy.h 中的 phy_driver 結構下。
在這些函式指標中,只需要驅動程式程式碼分配 config_aneg 和 read_status。其餘的是可選的。此外,如果可能,最好使用通用 phy 驅動程式的這兩個函式的版本:genphy_read_status 和 genphy_config_aneg。如果這不可能,很可能您只需要在呼叫這些函式之前和之後執行一些操作,因此您的函式將包裝通用函式。
請隨意檢視 drivers/net/phy/ 中的 Marvell、Cicada 和 Davicom 驅動程式以獲取示例(截至本文撰寫時,尚未測試 lxt 和 qsemi 驅動程式)。
PHY 的 MMD 暫存器訪問預設情況下由 PAL 框架處理,但如果需要,可以由特定的 PHY 驅動程式覆蓋。如果在 IEEE 對 MMD PHY 暫存器定義進行標準化之前釋出了用於製造的 PHY,則可能會出現這種情況。大多數現代 PHY 將能夠使用通用 PAL 框架來訪問 PHY 的 MMD 暫存器。此類用法的一個示例是節能乙太網支援,該支援在 PAL 中實現。如果 PHY 支援 IEEE 標準訪問機制,則此支援使用 PAL 訪問 MMD 暫存器以進行 EEE 查詢和配置,如果特定的 PHY 驅動程式覆蓋了該機制,則可以使用 PHY 的特定訪問介面。有關如何實現此功能的示例,請參見 drivers/net/phy/ 中的 Micrel 驅動程式。
板級修復¶
有時,平臺和 PHY 之間的特定互動需要特殊處理。例如,更改 PHY 的時鐘輸入的位置,或新增延遲以解決資料路徑中的延遲問題。為了支援此類意外情況,PHY 層允許平臺程式碼註冊在 PHY 啟動(或隨後重置)時執行的修復。
當 PHY 層啟動 PHY 時,它會檢查是否已為其註冊任何修復,基於 UID(包含在 PHY 裝置的 phy_id 欄位中)和匯流排識別符號(包含在 phydev->dev.bus_id 中)進行匹配。兩者必須匹配,但是提供了兩個常量 PHY_ANY_ID 和 PHY_ANY_UID 作為匯流排 ID 和 UID 的萬用字元。
找到匹配項時,PHY 層將呼叫與修復關聯的執行函式。此函式將指向感興趣的 phy_device 的指標傳遞給它。因此,它應該僅對該 PHY 進行操作。
平臺程式碼可以使用 phy_register_fixup() 註冊修復
int phy_register_fixup(const char *phy_id,
u32 phy_uid, u32 phy_uid_mask,
int (*run)(struct phy_device *));
或使用兩個存根之一 phy_register_fixup_for_uid() 和 phy_register_fixup_for_id()
int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
int (*run)(struct phy_device *));
int phy_register_fixup_for_id(const char *phy_id,
int (*run)(struct phy_device *));
存根設定兩個匹配標準之一,並將另一個設定為匹配任何內容。
當在模組載入時呼叫 phy_register_fixup() 或 *_for_uid()/*_for_id() 時,模組需要在解除安裝時登出修復並釋放已分配的記憶體。
在解除安裝模組之前呼叫以下函式之一
int phy_unregister_fixup(const char *phy_id, u32 phy_uid, u32 phy_uid_mask);
int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask);
int phy_register_fixup_for_id(const char *phy_id);
標準¶
IEEE 標準 802.3:CSMA/CD 訪問方法和物理層規範,第二節:http://standards.ieee.org/getieee802/download/802.3-2008_section2.pdf
RGMII v1.3:http://web.archive.org/web/20160303212629/http://www.hp.com/rnd/pdfs/RGMIIv1_3.pdf
RGMII v2.0:http://web.archive.org/web/20160303171328/http://www.hp.com/rnd/pdfs/RGMIIv2_0_final_hp.pdf