Linux phy system
PHY芯片為OSI的最底層-物理層(Physical Layer),通過MII與數(shù)據(jù)鏈路層的MAC芯片相連跳仿,對于MAC與PHY之間的一些知識可以查看Mac與Phy組成原理的簡單分析,這篇文章進行熟悉。
PHY與MAC整體的連接框架:
PHY的硬件系統(tǒng)算是比較復雜的,PHY與MAC相連灿椅,MAC與CPU相通,PHY與MAC通過MII和MDIO/MDC相連钞支,MII是走網(wǎng)絡數(shù)據(jù)的茫蛹,MDIO/MDC是用來與PHY的寄存器通訊的,對PHY進行配置烁挟。類似的SWITCH芯片一般也有兩種接口婴洼,MII用來走網(wǎng)絡數(shù)據(jù),SPI用來設置SWITCH的寄存器撼嗓。
跟以前分析I2C/SPI的驅(qū)動一樣柬采,分為控制器驅(qū)動和設備器驅(qū)動欢唾。
1、控制器驅(qū)動
控制器的驅(qū)動使用的一樣是platform總線的連接方式粉捻,在arch或dts下面進行對phy的platform進行add礁遣,platform_driver
的register一般也放在/driver/net/phy/下面,device和driver的name匹配后就會執(zhí)行platform_driver
結(jié)構(gòu)體所對應的probe接口,都大同小異肩刃,如下:
static struct platform_driver pfe_platform_driver = {
.probe = pfe_platform_probe,
.remove = pfe_platform_remove,
.driver = {
.name = "pfe",
#ifdef CONFIG_PM
.pm = &pfe_platform_pm_ops,
#endif
},
};
static int __init pfe_module_init(void)
{
return platform_driver_register(&pfe_platform_driver);
}
static void __exit pfe_module_exit(void)
{
platform_driver_unregister(&pfe_platform_driver);
}
MODULE_LICENSE("GPL");
module_init(pfe_module_init);
module_exit(pfe_module_exit);
因為phy與cpu的通訊配置使用的是MII祟霍、MDIO/MDC來進行傳輸控制的,所以probe函數(shù)里面如要對MII總線進行配置盈包,最后調(diào)用mdiobus_register()
或of_mdiobus_register()
對mdio_bus進行注冊沸呐。
of_mdiobus_register()
函數(shù)位于drivers/of/of_mdio.c
中,該函數(shù)最后還是會調(diào)用mdiobus_register()
函數(shù)呢燥,mdiobus_register()
函數(shù)位于drivers\net\phy\mdio_bus.c
中垂谢,通過一層一層的往下?lián)埽瑫腥缦陆Y(jié)構(gòu):
‐‐> mdiobus_register
‐‐> device_register
‐‐> mdiobus_scan
‐‐> get_phy_device
‐‐> get_phy_id // 讀寄存器
‐‐> phy_device_create // 創(chuàng)建phy設備
‐‐> INIT_DELAYED_WORK(&dev‐>state_queue, phy_state_machine); //初始化狀態(tài)機
這邊就是控制器和設備器的交接了疮茄,創(chuàng)建phy設備初始化PHY狀態(tài)機,接下去就看設備器的驅(qū)動根暑。
2力试、設備器驅(qū)動
設備器的驅(qū)動也是三個方面device每强、driver糯笙、bus。PHY的device接口為phy_device_register
和phy_device_release
滑频,driver接口為phy_driver_register
和phy_driver_unregister
淳地,bus接口為mdiobus_register
和mdiobus_unregister
怖糊。
這樣一分析感覺就跟清晰了,有關(guān)PHY驅(qū)動的內(nèi)容都位于/drivers/net/phy中颇象。
phy的設備驅(qū)動不像i2c/spi有一個board_info函數(shù)進行添加設備伍伤,而是直接讀取phy中的寄存器,根據(jù)IEEE的規(guī)定PHY芯片的前16個寄存器的內(nèi)容必須是固定的遣钳,如下:
其中寄存器0x02和0x03即設備ID的高低位扰魂,每一種型號的PHY有其一串ID號,這我們查看對于的手冊即可知道蕴茴。
在設備的driver中使用MODULE_DEVICE_TABLE
將對應的設備ID添加進入劝评,他的效果可以理解為board_info函數(shù)所實現(xiàn)的內(nèi)容,如broadcom的table:
static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
{ PHY_ID_BCM5411, 0xfffffff0 },
{ PHY_ID_BCM5421, 0xfffffff0 },
{ PHY_ID_BCM54210S, 0xfffffff0 },
{ PHY_ID_BCM5461, 0xfffffff0 },
{ PHY_ID_BCM5464, 0xfffffff0 },
{ PHY_ID_BCM5482, 0xfffffff0 },
{ PHY_ID_BCM5482, 0xfffffff0 },
{ PHY_ID_BCM50610, 0xfffffff0 },
{ PHY_ID_BCM50610M, 0xfffffff0 },
{ PHY_ID_BCM57780, 0xfffffff0 },
{ PHY_ID_BCMAC131, 0xfffffff0 },
{ PHY_ID_BCM5241, 0xfffffff0 },
{ }
};
MODULE_DEVICE_TABLE(mdio, broadcom_tbl);
phy_driver
的register大概為如下一個過程倦淀,進行簡單分析:
drivers/net/phy/phy_device.c
phy_init
‐‐> mdio_bus_init 注冊mdio總線
‐‐> class_register(&mdio_bus_class);
‐‐> bus_register(&mdio_bus_type);
‐‐> phy_driver_register(&genphy_driver);
在mdio總線注冊的時候會調(diào)用mdio_bus_match
蒋畜,如果match函數(shù)找到設備則會進行設備驅(qū)動的注冊,match函數(shù)位于/drivers/net/phy/mdio_bus.c
中撞叽,如下姻成,進行phy_id的打印插龄。
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
struct phy_device *phydev = to_phy_device(dev);
struct phy_driver *phydrv = to_phy_driver(drv);
printk("phydev->phy_id:%x ",phydev->phy_id);
printk("phydrv->phy_id:%x \n",phydrv->phy_id);
return ((phydrv->phy_id & phydrv->phy_id_mask) ==
(phydev->phy_id & phydrv->phy_id_mask));
}
由log可以看出,設備dev的ID為600d8595佣渴,然后就去查找對應的驅(qū)動drv的ID辫狼,知道找到600d8595則進入probe函數(shù)。
[ 23.306611] phydev->phy_id:600d8595 phydrv->phy_id:ffffffff
[ 23.312150] phydev->phy_id:600d8595 phydrv->phy_id:ffffffff
[ 23.317731] phydev->phy_id:600d8595 phydrv->phy_id:4dd072
[ 23.323084] phydev->phy_id:600d8595 phydrv->phy_id:4dd033
[ 23.328461] phydev->phy_id:600d8595 phydrv->phy_id:206070
[ 23.333812] phydev->phy_id:600d8595 phydrv->phy_id:2060e0
[ 23.339182] phydev->phy_id:600d8595 phydrv->phy_id:600d8595
這樣找到ID后就會進行設備驅(qū)動的注冊辛润,對應的phy_driver_register()
函數(shù)才能返回成功膨处,才會執(zhí)行phy_driver
結(jié)構(gòu)體下面的內(nèi)容,如下:
static struct phy_driver bcm54210s_driver = {
.phy_id = PHY_ID_BCM54210S,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54210S",
.features = PHY_GBIT_FEATURES |
SUPPORTED_Pause | SUPPORTED_Asym_Pause,
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
.config_init = bcm54210s_config_init,
.config_aneg = bcm54210s_config_aneg,
.read_status = bcm54210s_read_status,
.ack_interrupt = bcm54xx_ack_interrupt,
.config_intr = bcm54xx_config_intr,
.driver = { .owner = THIS_MODULE },
};
里面對phy的寄存器等進行初始化配置砂竖,這邊對PHY的驅(qū)動進行簡單的介紹真椿,關(guān)于PHY的內(nèi)容還有好多,比如:PHY狀態(tài)機乎澄、ethtool工具這些都是在后面應用的時候需要用到的突硝,等我自己深入研究后再進行學習總結(jié)。
Linux phy system的分析就到這邊置济,有感悟時會持續(xù)會更新解恰。
注:以上內(nèi)容都是本人在學習過程積累的一些心得,難免會有參考到其他文章的一些知識浙于,如有侵權(quán)护盈,請及時通知我,我將及時刪除或標注內(nèi)容出處羞酗,如有錯誤之處也請指出腐宋,進行探討學習。文章只是起一個引導作用檀轨,詳細的數(shù)據(jù)解析內(nèi)容還請查看Linux相關(guān)教程胸竞,感謝您的查閱。