概述
上圖來(lái)自 瑞昱半導(dǎo)體 (RealTek) 的 RTL8201F 系列網(wǎng)卡 PHY 芯片手冊(cè)。按OSI 7層網(wǎng)絡(luò)模型劃分,網(wǎng)卡PHY 芯片(圖中的RTL8201F)位于物理層,對(duì)應(yīng)的軟件層就是本文討論的 PHY 驅(qū)動(dòng)層;而 MAC 位于 數(shù)據(jù)鏈路層,也是通常軟件上所說(shuō)的網(wǎng)卡驅(qū)動(dòng)層,它不是本文的重點(diǎn),不做展開(kāi)。另外,可通過(guò) MDIO 接口對(duì) PHY 芯片進(jìn)行配置(如PHY芯片寄存器讀寫),而 PHY 和 MAC 通過(guò) MII/RMII 進(jìn)行數(shù)據(jù)傳輸。
PHY芯片通過(guò)MII/GMII/RMII/SGMII/XGMII等多種媒體獨(dú)立接口(介質(zhì)無(wú)關(guān)接口)與數(shù)據(jù)鏈路層的MAC芯片相連,并通過(guò)MDIO接口實(shí)現(xiàn)對(duì)PHY 狀態(tài)的監(jiān)控、配置和管理。
PHY與MAC整體的連接框圖:
數(shù)據(jù)結(jié)構(gòu)
每個(gè) phy 芯片會(huì)創(chuàng)建一個(gè) struct phy_device 類型的設(shè)備,對(duì)應(yīng)的有 struct phy_driver 類型的驅(qū)動(dòng),這兩者實(shí)際上是掛載在 mdio_bus_type 總線上的,mac 會(huì)被注冊(cè)成 struct net_device。
phy_device
struct?phy_device?{
????struct?phy_driver?*drv;????????????????//?PHY設(shè)備驅(qū)動(dòng)??
????struct?mii_bus?*bus;???????????????????//?對(duì)應(yīng)的MII總線?
????struct?device?dev;?????????????????????//?設(shè)備文件??
????u32?phy_id;????????????????????????????//?PHY?ID??
????
????struct?phy_c45_device_ids?c45_ids;???????
????bool?is_c45;
????bool?is_internal;
????bool?has_fixups;
????bool?suspended;
????enum?phy_state?state;???????????????????//?PHY狀態(tài)
????u32?dev_flags;
????phy_interface_t?interface;??????????????//?PHY接口??
????int?addr;???????????????????????????????//?PHY?總線地址(0~31)?
????int?speed;??????????????????????????????//?速度??
????int?duplex;?????????????????????????????//?雙工模式??
????int?pause;??????????????????????????????//?停止??
????int?asym_pause;
????int?link;
????u32?interrupts;?????????????????????????//?中斷使能標(biāo)志??
????u32?supported;
????u32?advertising;
????u32?lp_advertising;
????int?autoneg;
????int?link_timeout;
????int?irq;?????????????????????????????????//?中斷號(hào)??????????????????????????????
????void?*priv;??????????????????????????????//?私有數(shù)據(jù)??
????struct?work_struct?phy_queue;????????????//?PHY工作隊(duì)列??
????struct?delayed_work?state_queue;?????????//?PHY延時(shí)工作隊(duì)列??
????atomic_t?irq_disable;
????struct?mutex?lock;
????struct?net_device?*attached_dev;?????????//?網(wǎng)絡(luò)設(shè)備??
????void?(*adjust_link)(struct?net_device?*dev);
};
phy_driver
struct?phy_driver?{
????struct?mdio_driver_common?mdiodrv;
????u32?phy_id;
????char?*name;
????u32?phy_id_mask;
????u32?features;
????u32?flags;
????const?void?*driver_data;
????
????int?(*soft_reset)(struct?phy_device?*phydev);
????int?(*config_init)(struct?phy_device?*phydev);
????int?(*probe)(struct?phy_device?*phydev);
????int?(*suspend)(struct?phy_device?*phydev);
????int?(*resume)(struct?phy_device?*phydev);
????int?(*config_aneg)(struct?phy_device?*phydev);
????int?(*aneg_done)(struct?phy_device?*phydev);
????int?(*read_status)(struct?phy_device?*phydev);
????int?(*ack_interrupt)(struct?phy_device?*phydev);
????int?(*config_intr)(struct?phy_device?*phydev);
????int?(*did_interrupt)(struct?phy_device?*phydev);
????void?(*remove)(struct?phy_device?*phydev);
????int?(*match_phy_device)(struct?phy_device?*phydev);
????int?(*ts_info)(struct?phy_device?*phydev,?struct?ethtool_ts_info?*ti);
????int??(*hwtstamp)(struct?phy_device?*phydev,?struct?ifreq?*ifr);
????bool?(*rxtstamp)(struct?phy_device?*dev,?struct?sk_buff?*skb,?int?type);
????void?(*txtstamp)(struct?phy_device?*dev,?struct?sk_buff?*skb,?int?type);
????int?(*set_wol)(struct?phy_device?*dev,?struct?ethtool_wolinfo?*wol);
????void?(*get_wol)(struct?phy_device?*dev,?struct?ethtool_wolinfo?*wol);
????void?(*link_change_notify)(struct?phy_device?*dev);
????int?(*read_mmd)(struct?phy_device?*dev,?int?devnum,?u16?regnum);
????int?(*write_mmd)(struct?phy_device?*dev,?int?devnum,?u16?regnum,
?????????????u16?val);
????int?(*read_page)(struct?phy_device?*dev);
????int?(*write_page)(struct?phy_device?*dev,?int?page)
????int?(*module_info)(struct?phy_device?*dev,
???????????????struct?ethtool_modinfo?*modinfo);
????int?(*module_eeprom)(struct?phy_device?*dev,
?????????????????struct?ethtool_eeprom?*ee,?u8?*data);
????int?(*get_sset_count)(struct?phy_device?*dev);
????void?(*get_strings)(struct?phy_device?*dev,?u8?*data);
????void?(*get_stats)(struct?phy_device?*dev,
??????????????struct?ethtool_stats?*stats,?u64?*data);
????int?(*get_tunable)(struct?phy_device?*dev,
???????????????struct?ethtool_tunable?*tuna,?void?*data);
????int?(*set_tunable)(struct?phy_device?*dev,
????????????????struct?ethtool_tunable?*tuna,
????????????????const?void?*data);
????int?(*set_loopback)(struct?phy_device?*dev,?bool?enable);
????ANDROID_KABI_RESERVE(1);
????ANDROID_KABI_RESERVE(2);
};
mii_bus
struct?mii_bus?{
????const?char?*name;?????????????//?總線名字
????char?id[MII_BUS_ID_SIZE];?????//?ID?MII_BUS_ID_SIZE=61
????void?*priv;???????????????????//?私有數(shù)據(jù)
????int?(*read)(struct?mii_bus?*bus,?int?phy_id,?int?regnum);??????????????//?讀方式
????int?(*write)(struct?mii_bus?*bus,?int?phy_id,?int?regnum,?u16?val);????//?寫方式
????int?(*reset)(struct?mii_bus?*bus);?????//?復(fù)位
????struct?mutex?mdio_lock;
????struct?device?*parent;????????//?父設(shè)備
????enum?{
????????MDIOBUS_ALLOCATED?=?1,
????????MDIOBUS_REGISTERED,
????????MDIOBUS_UNREGISTERED,
????????MDIOBUS_RELEASED,
????}?state;???????????????????????//?總線狀態(tài)
????struct?device?dev;?????????????//?設(shè)備文件
????struct?phy_device?*phy_map[PHY_MAX_ADDR];?//?PHY設(shè)備數(shù)組
????u32?phy_mask;
????int?*irq;???????????????????//?中斷
};
net_device
struct?net_device?{
????char??name[IFNAMSIZ];?????????/*?用于存放網(wǎng)絡(luò)設(shè)備的設(shè)備名稱?*/
????char??*ifalias;???????????????/*?網(wǎng)絡(luò)設(shè)備的別名?*/
????int???ifindex;????????????????/*?網(wǎng)絡(luò)設(shè)備的接口索引值,獨(dú)一無(wú)二的網(wǎng)絡(luò)設(shè)備標(biāo)識(shí)符?*/
????struct?hlist_node??name_hlist;??/*?這個(gè)字段用于構(gòu)建網(wǎng)絡(luò)設(shè)備名的哈希散列表,而struct?net中的name_hlist就指向每個(gè)哈希散列表的鏈表頭?*/
????struct?hlist_node??index_hlist;?/*?用于構(gòu)建網(wǎng)絡(luò)設(shè)備的接口索引值哈希散列表,在struct?net中的index_hlist用于指向接口索引值哈希散列表的鏈表頭?*/
????struct?list_head???dev_list;????/*?用于將每一個(gè)網(wǎng)絡(luò)設(shè)備加入到一個(gè)網(wǎng)絡(luò)命名空間中的網(wǎng)絡(luò)設(shè)備雙鏈表中?*/
????unsigned?int???????flags;???????/*?網(wǎng)絡(luò)設(shè)備接口的標(biāo)識(shí)符?*/
????unsigned?int???????priv_flags;??/*?網(wǎng)絡(luò)設(shè)備接口的標(biāo)識(shí)符,但對(duì)用戶空間不可見(jiàn);*/
????unsigned?short?????type;????????/*?接口硬件類型?*/
????unsigned?int???????mtu;?????????/*?網(wǎng)絡(luò)設(shè)備接口的最大傳輸單元?*/
????unsigned?short?????hard_header_len;???/*?硬件接口頭長(zhǎng)度?*/
????unsigned?char??????*dev_addr;????/*?網(wǎng)絡(luò)設(shè)備接口的MAC地址?*/
????bool???????????uc_promisc;???????/*?網(wǎng)絡(luò)設(shè)備接口的單播模式?*/
????unsigned?int???????promiscuity;??/*?網(wǎng)絡(luò)設(shè)備接口的混雜模式?*/
????unsigned?int???????allmulti;?????/*?網(wǎng)絡(luò)設(shè)備接口的全組播模式?*/
????struct?netdev_hw_addr_list??uc;??/*?輔助單播MAC地址列表?*/
????struct?netdev_hw_addr_list??mc;??/*?主mac地址列表?*/
????struct?netdev_hw_addr_list??dev_addrs;??/*?hw設(shè)備地址列表?*/
????unsigned?char??????broadcast[MAX_ADDR_LEN];???/*?hw廣播地址?*/
????struct?netdev_rx_queue??*_rx;???????/*?網(wǎng)絡(luò)設(shè)備接口的數(shù)據(jù)包接收隊(duì)列?*/
????struct?netdev_queue?*_tx????????????/*?網(wǎng)絡(luò)設(shè)備接口的數(shù)據(jù)包發(fā)送隊(duì)列?*/
????unsigned?int????????num_tx_queues;??/*?TX隊(duì)列數(shù)?*/
????unsigned?int????????real_num_tx_queues;??/*?當(dāng)前設(shè)備活動(dòng)的TX隊(duì)列數(shù)?*/
????unsigned?long??????tx_queue_len;????/*?每個(gè)隊(duì)列允許的最大幀?*/
????unsigned?long??????state;???????????/*?網(wǎng)絡(luò)設(shè)備接口的狀態(tài)?*/
????struct?net_device_stats????stats;???/*?網(wǎng)絡(luò)設(shè)備接口的統(tǒng)計(jì)情況?*/
????possible_net_t?????????nd_net;??????/*?用于執(zhí)行網(wǎng)絡(luò)設(shè)備所在的命名空間?*/
};?
phy 設(shè)備的注冊(cè)
以網(wǎng)卡 Fec 為例,網(wǎng)卡驅(qū)動(dòng)在初始化 fec_probe() 時(shí)遍歷 dts 的定義,創(chuàng)建相應(yīng) struct phy_device 類型的設(shè)備,主要步驟為:
- 注冊(cè)網(wǎng)絡(luò)設(shè)備 net_device申請(qǐng)隊(duì)列和 DMA申請(qǐng) MDIO 總線創(chuàng)建并注冊(cè) Phy 設(shè)備
fec_probe(struct?platform_device?*pdev)
????->?struct?device_node?*np?=?pdev->dev.of_node,?*phy_node;?//?獲取設(shè)備樹(shù)節(jié)點(diǎn)句柄,并創(chuàng)建一個(gè)phy的設(shè)備樹(shù)節(jié)點(diǎn)句柄
????->?fec_enet_get_queue_num(pdev,?&num_tx_qs,?&num_rx_qs);??//?從設(shè)備樹(shù)獲取fsl,num-tx-queues和fsl,num-rx-queues的屬性值
????->?ndev?=?alloc_etherdev_mqs??????????????????????????????//?申請(qǐng)net_device
????->?netdev_priv(ndev)??????????????????????????????????????//??獲取私有數(shù)據(jù)空間首地址
--------------------------------------------------------------------------------------------------------------------------
????->?of_parse_phandle(np,?"phy-handle",?0)??????????????//?從mac的設(shè)備樹(shù)節(jié)點(diǎn)中獲取phy子節(jié)點(diǎn)
????->?of_get_phy_mode(pdev->dev.of_node)?????????????????//?從設(shè)備樹(shù)節(jié)點(diǎn)中獲取phy模式,phy-mode?=?"rmii";
????->?fec_reset_phy(pdev);???????????????????????????????//?復(fù)位phy
????->?fec_enet_init(ndev)????????????????????????????????//?申請(qǐng)隊(duì)列和DMA,設(shè)置MAC地址
????->?of_property_read_u32(np,?"fsl,wakeup_irq",?&irq)???//?喚醒中斷
????->?fec_enet_mii_init(pdev);???????????????????????????//?注冊(cè)MDIO總線、注冊(cè)phy_device
????????->?fep->mii_bus?=?mdiobus_alloc()?????????????????//申請(qǐng)MDIO總線
????????->?fep->mii_bus->name?=?"fec_enet_mii_bus";???????//?總線名字
????????->?fep->mii_bus->read?=?fec_enet_mdio_read;???????//?總線的讀函數(shù)
????????->?fep->mii_bus->write?=?fec_enet_mdio_write;?????//?總線的寫函數(shù)
????????->?snprintf(fep->mii_bus->id,?MII_BUS_ID_SIZE,?"%s-%x",
pdev->name,?fep->dev_id?+?1);?????????????????????????//?總線id???????
????????->?of_get_child_by_name(pdev->dev.of_node,?"mdio");????????????//?獲取phy節(jié)點(diǎn)句柄
????????->?of_mdiobus_register??????????//?注冊(cè)mii_bus設(shè)備,并通過(guò)設(shè)備樹(shù)子節(jié)點(diǎn)創(chuàng)建PHY設(shè)備?drivers/of/of_mdio.c??????????????of_mdiobus_register(struct?mii_bus?*mdio,?struct?device_node?*np)??????
????????????->?mdio->phy_mask?=?~0;????//?屏蔽所有PHY,防止自動(dòng)探測(cè)。相反,設(shè)備樹(shù)中列出的phy將在總線注冊(cè)后填充
????????????->?mdio->dev.of_node?=?np;
????????????->?mdio->reset_delay_us?=?DEFAULT_GPIO_RESET_DELAY;
????????????->?mdiobus_register(mdio)?????????????????//?注冊(cè)MDIO總線設(shè)備
????????????????->?bus->dev.parent?=?bus->parent;
????????????????->?bus->dev.class?=?&mdio_bus_class;??//?總線設(shè)備類“/sys/bus/mdio_bus”
????????????????/*-----------------------------------------
????????????????static?struct?class?mdio_bus_class?=?{
????????????????????.name?=?"mdio_bus",
????????????????????.dev_release?=?mdiobus_release,
????????????????};
????????????????-------------------------------------------*/
????????????????->?bus->dev.groups?=?NULL;
????????????????->?dev_set_name(&bus->dev,?"%s",?bus->id);????????????//設(shè)置總線設(shè)備的名稱
????????????????->?device_register(&bus->dev);????????????????????????//?注冊(cè)總線設(shè)備
????????????????->??if?(bus->reset)??????bus->reset(bus);?????????????//?總線復(fù)位
---------------------------------------另一條分支解析(可忽略)--------------------------------------------------------
????????????????->??phydev?=?mdiobus_scan(bus,?i);??????????????????//?掃描phy設(shè)備
????????????????->?phydev?=?get_phy_device(bus,?addr);??????????//獲取創(chuàng)建phy設(shè)備
????????????????->err?=?phy_device_register(phydev);??????????//注冊(cè)phy設(shè)備
--------------------------------------------------------------------------------------------------------------------
????????????????->?for_each_available_child_of_node(np,?child)?{??????//?遍歷這個(gè)平臺(tái)設(shè)備的子節(jié)點(diǎn)并為每個(gè)phy注冊(cè)一個(gè)phy_device
????????????????->?addr?=?of_mdio_parse_addr(&mdio->dev,?child)???????//?從子節(jié)點(diǎn)的"reg"屬性中獲得PHY設(shè)備的地址?
????????????????->?of_property_read_u32(np,?"reg",?&addr)
????????????????->?if?(addr?<?0)?????scanphys?=?true;??????continue;??//?如果未獲得子節(jié)點(diǎn)的"reg"屬性,則在后面再啟用掃描可能存在的PHY的,然后注冊(cè)
????????????????->?of_mdiobus_register_phy(mdio,?child,?addr)?????}?????//?創(chuàng)建并注冊(cè)PHY設(shè)備
????????????????????->?is_c45?=?of_device_is_compatible(child,"ethernet-phy-ieee802.3-c45")?//判斷設(shè)備樹(shù)中的PHY的屬性是否指定45號(hào)條款
????????????????->?if?(!is_c45?&&?!of_get_phy_id(child,?&phy_id))??????//如果設(shè)備樹(shù)中的PHY的屬性未指定45號(hào)條款?且未通過(guò)"ethernet-phy-id%4x.%4x"屬性指定PHY的ID
????????????????->?phy?=?phy_device_create(mdio,?addr,?phy_id,?0,?NULL);
????????????????->?else????phy?=?get_phy_device(mdio,?addr,?is_c45);????//用這個(gè)分支
????????????????????????->?get_phy_id(bus,?addr,?&phy_id,?is_c45,?&c45_ids);//通過(guò)mdio得到PHY的ID
????????????????????????????->?mdiobus_read(bus,?addr,?MII_PHYSID1)
????????????????????????????????->?__mdiobus_read(bus,?addr,?regnum);
????????????????????????????????????->?bus->read(bus,?addr,?regnum)
????????????????????????????????->?mdiobus_read(bus,?addr,?MII_PHYSID2)
????????????????????????????->?phy_device_create(bus,?addr,?phy_id,?is_c45,?&c45_ids)????//?創(chuàng)建PHY設(shè)備
????????????????????????????????->?struct?phy_device?*dev;
????????????????????????????????->?dev?=?kzalloc(sizeof(*dev),?GFP_KERNEL);
????????????????????????????????????dev->dev.release?=?phy_device_release;
????????????????????????????????????dev->speed?=?0;
????????????????????????????????????dev->duplex?=?-1;
????????????????????????????????????dev->pause?=?0;
????????????????????????????????????dev->asym_pause?=?0;
????????????????????????????????????dev->link?=?1;
????????????????????????????????????dev->interface?=?PHY_INTERFACE_MODE_GMII;
????????????????????????????????????dev->autoneg?=?AUTONEG_ENABLE;??????//?默認(rèn)支持自協(xié)商(自動(dòng)使能)
????????????????????????????????????dev->is_c45?=?is_c45;
????????????????????????????????????dev->addr?=?addr;
????????????????????????????????????dev->phy_id?=?phy_id;
????????????????????????????????????if?(c45_ids)
????????????????????????????????????????dev->c45_ids?=?*c45_ids;
????????????????????????????????????dev->bus?=?bus;
????????????????????????????????????dev->dev.parent?=?bus->parent;
????????????????????????????????????dev->dev.bus?=?&mdio_bus_type;????//PHY設(shè)備和驅(qū)動(dòng)都會(huì)掛在mdio_bus下,匹配時(shí)會(huì)調(diào)用對(duì)應(yīng)的match函數(shù)??--???
????????????????????????/*----------------------------------------------------------------------------------------------------
????????????????????????????????????struct?bus_type?mdio_bus_type?=?{
????????????????????????????????????????.name???????=?"mdio_bus",????????????????//總線名稱
????????????????????????????????????????.match??????=?mdio_bus_match,????????????//用來(lái)匹配總線上設(shè)備和驅(qū)動(dòng)的函數(shù)
????????????????????????????????????????.pm?????????=?MDIO_BUS_PM_OPS,
????????????????????????????????????????.dev_groups?=?mdio_dev_groups,
????????????????????????????????????};
????????????????????????----------------------------------------------------------------------------------------------------*/
????????????????????????????????????dev->irq?=?bus->irq?!=?NULL???bus->irq[addr]?:?PHY_POLL;
????????????????????????????????????dev_set_name(&dev->dev,?PHY_ID_FMT,?bus->id,?addr);
????????????????????????????????????dev->state?=?PHY_DOWN;?????//指示PHY設(shè)備和驅(qū)動(dòng)程序尚未準(zhǔn)備就緒,在PHY驅(qū)動(dòng)的probe函數(shù)中會(huì)更改為READY
????????????????????????????????????->?INIT_DELAYED_WORK(&dev->state_queue,?phy_state_machine);??//PHY的狀態(tài)機(jī)(核心WORK)后續(xù)解析
????????????????????????????????????->?INIT_WORK(&dev->phy_queue,?phy_change);???//?由phy_interrupt?/?timer調(diào)度以處理PHY狀態(tài)的更改
????????????????????????????????????->??request_module(MDIO_MODULE_PREFIX?MDIO_ID_FMT,?MDIO_ID_ARGS(phy_id));????????????//?加載內(nèi)核模塊
????????????????????????????????????->?device_initialize(&dev->dev);?????//設(shè)備模型中的一些設(shè)備,主要是kset、kobject、ktype的設(shè)置
????????????????????????????????->?irq_of_parse_and_map(child,?0);??????//將中斷解析并映射到linux?virq空間(
????????????????????????????????->?of_node_get(child);?????????//將OF節(jié)點(diǎn)與設(shè)備結(jié)構(gòu)相關(guān)聯(lián)
????????????????????????????????->?phy->dev.of_node?=?child;
????????????????????????????????->?phy_device_register(phy)????//?注冊(cè)phy設(shè)備
????????????????????????????????????->?if?(phydev->bus->phy_map[phydev->addr])???????//判斷PHY是否已經(jīng)注冊(cè)了??
????????????????????????????????????->?phydev->bus->phy_map[phydev->addr]?=?phydev;??//添加PHY到總線的phy_map里
????????????????????????????????????->?phy_scan_fixups(phydev);?????//執(zhí)行匹配的fixups??
????????????????????????????????????->?device_add(&phydev->dev);????//?注冊(cè)到linux設(shè)備模型框架中
????????????????????????????->??if?(!scanphys)???return?0;??????????//?如果從子節(jié)點(diǎn)的"reg"屬性中獲得PHY設(shè)備的地址,scanphys=false,這里就直接返回了,因?yàn)椴恍枰賿呙枇?
------------一般來(lái)說(shuō)只要設(shè)備樹(shù)種指定了PHY設(shè)備的"reg"屬性,后面的流程可以自動(dòng)忽略?------------
????->?register_netdev(ndev)??????????????????//?向內(nèi)核注冊(cè)net_device
phy 驅(qū)動(dòng)的注冊(cè)
genphy_driver 通用驅(qū)動(dòng)
內(nèi)核中有 genphy_driver 通用驅(qū)動(dòng),以及專有驅(qū)動(dòng)(這里以 NXP TJA 驅(qū)動(dòng)為例),分別如下:
static?struct?phy_driver?genphy_driver?=?{
?.phy_id??=?0xffffffff,
?.phy_id_mask?=?0xffffffff,
?.name??=?"Generic?PHY",
?.get_features?=?genphy_read_abilities,
?.suspend?=?genphy_suspend,
?.resume??=?genphy_resume,
?.set_loopback???=?genphy_loopback,
};
static?struct?phy_driver?tja11xx_driver[]?=?{
?{
??PHY_ID_MATCH_MODEL(PHY_ID_TJA1100),
??.name??=?"NXP?TJA1100",
??.features???????=?PHY_BASIC_T1_FEATURES,
??.probe??=?tja11xx_probe,
??.soft_reset?=?tja11xx_soft_reset,
??.config_aneg?=?tja11xx_config_aneg,
??.config_init?=?tja11xx_config_init,
??.read_status?=?tja11xx_read_status,
??.get_sqi?=?tja11xx_get_sqi,
??.get_sqi_max?=?tja11xx_get_sqi_max,
??.suspend?=?genphy_suspend,
??.resume??=?genphy_resume,
??.set_loopback???=?genphy_loopback,
??/*?Statistics?*/
??.get_sset_count?=?tja11xx_get_sset_count,
??.get_strings?=?tja11xx_get_strings,
??.get_stats?=?tja11xx_get_stats,
?}
};
module_phy_driver(tja11xx_driver);
genphy_driver 的 struct phy_driver 的注冊(cè)過(guò)程如下:
phy_init
??phy_driver_register()
??????driver_register(&new_driver->mdiodrv.driver)
??????????bus_add_driver(drv)
??????????????driver_attach(drv)
??????????????????bus_for_each_dev(drv->bus,?NULL,?drv,?__driver_attach)
??????????????????????while?((dev?=?next_device(&i))?&&?!error)
??????????????????????????/*?循環(huán)到注冊(cè)的?PHY?設(shè)備時(shí)?*/
??????????????????????????fn(dev,?data)?=?__driver_attach()
??????????????????????????????/*?匹配設(shè)備和驅(qū)動(dòng)?*/
??????????????????????????????driver_match_device(drv,?dev)
??????????????????????????????????mdio_bus_match(dev,?drv)
??????????????????????????????????????phy_bus_match(dev,?drv)
??????????????????????????????????????????/*?按?phy_id?&?phy_id_mask?匹配?*/
??????????????????????????????????????????return?(phydrv->phy_id?&?phydrv->phy_id_mask)?==?(phydev->phy_id?&?phydrv->phy_id_mask);
??????????????????????????????/*?匹配到設(shè)備和驅(qū)動(dòng),加載驅(qū)動(dòng)?*/
??????????????????????????????driver_probe_device(drv,?dev)
??????????????????????????????????really_probe(dev,?drv)
??????????????????????????????????????dev->driver?=?drv;?/*?綁定設(shè)備的驅(qū)動(dòng)?*/
??????????????????????????????????????drv->probe(dev)?=?phy_probe
其中一個(gè)關(guān)鍵點(diǎn)是 mdio driver 的 probe 函數(shù)是一個(gè)通用函數(shù) phy_probe:
static?int?phy_probe(struct?device?*dev)
{
????struct?phy_device?*phydev?=?to_phy_device(dev);????????//?獲取PHY設(shè)備
????struct?device_driver?*drv?=?phydev->mdio.dev.driver;
????struct?phy_driver?*phydrv?=?to_phy_driver(drv);????????//?獲取PHY驅(qū)動(dòng)
????int?err?=?0;
????phydev->drv?=?phydrv;????/*?綁定?phy_device?和?phy_driver?*/
?????/*?PHY?中斷模式最終配置?*/
????if?(!phy_drv_supports_irq(phydrv)?&&?phy_interrupt_is_valid(phydev))????//?設(shè)置中斷方式
????????phydev->irq?=?PHY_POLL;
????if?(phydrv->flags?&?PHY_IS_INTERNAL)
????????phydev->is_internal?=?true;
????/*?Deassert?the?reset?signal?*/
????phy_device_reset(phydev,?0);
????if?(phydev->drv->probe)?{?????????????????//?判斷驅(qū)動(dòng)有probe方式
????????err?=?phydev->drv->probe(phydev);?????/*?PHY?驅(qū)動(dòng)的?probe?*/
????????if?(err)
????????????goto?out;
????}
????......
????if?(phydrv->features)?{
????????linkmode_copy(phydev->supported,?phydrv->features);
????}
????else?if?(phydrv->get_features)
????????err?=?phydrv->get_features(phydev);
????else?if?(phydev->is_c45)
????????err?=?genphy_c45_pma_read_abilities(phydev);
????else
????????err?=?genphy_read_abilities(phydev);?//讀取狀態(tài)寄存器來(lái)確定?phy?芯片的能力
????if?(err)
????????goto?out;
????if?(!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
???????????????????phydev->supported))
????????phydev->autoneg?=?0;
????if?(linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
??????????????????phydev->supported))
????????phydev->is_gigabit_capable?=?1;
????if?(linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
??????????????????phydev->supported))
????????phydev->is_gigabit_capable?=?1;
????
????/*?PHY?功能特性配置?*/
????of_set_phy_supported(phydev);
????phy_advertise_supported(phydev);
????......
????of_set_phy_eee_broken(phydev);
????......
????if?(!test_bit(ETHTOOL_LINK_MODE_Pause_BIT,?phydev->supported)?&&
????????!test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,?phydev->supported))?{
????????linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
?????????????????phydev->supported);
????????linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
?????????????????phydev->supported);
????}
????/*?Set?the?state?to?READY?by?default?*/
????phydev->state?=?PHY_READY;????/*?標(biāo)記?PHY?設(shè)備已經(jīng)就緒?*/
out:
????/*?Re-assert?the?reset?signal?on?error?*/
????if?(err)
????????phy_device_reset(phydev,?1);
????return?err;
}
其中通用 phy 驅(qū)動(dòng)會(huì)調(diào)用函數(shù) genphy_read_abilities 來(lái)讀取狀態(tài)寄存器來(lái)確定 phy 芯片的能力:
genphy_read_abilities()
???`-|?{
?????|??val?=?phy_read(phydev,?MII_BMSR);?//?讀取?mdio?0x01?寄存器來(lái)確定?phy?的?10/100M?能力
?????|??linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,?phydev->supported,?val?&?BMSR_ANEGCAPABLE);
?????|??linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,?phydev->supported,?val?&?BMSR_100FULL);
?????|??linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,?phydev->supported,?val?&?BMSR_100HALF);
?????|??linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,?phydev->supported,?val?&?BMSR_10FULL);
?????|??linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,?phydev->supported,?val?&?BMSR_10HALF);
?????|??if?(val?&?BMSR_ESTATEN)?{
?????|???val?=?phy_read(phydev,?MII_ESTATUS);?//?讀取?mdio?0x0f?寄存器來(lái)確定?phy?的?1000M?能力
?????|???linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,?phydev->supported,?val?&?ESTATUS_1000_TFULL);
?????|???linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,?phydev->supported,?val?&?ESTATUS_1000_THALF);
?????|???linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,?phydev->supported,?val?&?ESTATUS_1000_XFULL);
?????|??}
?????|?}
NXP TJA 專有驅(qū)動(dòng)
NXP TJA 驅(qū)動(dòng)的 struct phy_driver 的注冊(cè)過(guò)程如下:
#define?phy_module_driver(__phy_drivers,?__count)???????????
static?int?__init?phy_module_init(void)?????????????????
{???????????????????????????????????
????return?phy_drivers_register(__phy_drivers,?__count,?THIS_MODULE);?
}???????????????????????????????????
module_init(phy_module_init);???????????????????????
static?void?__exit?phy_module_exit(void)????????????????
{???????????????????????????????????
????phy_drivers_unregister(__phy_drivers,?__count);?????????
}???????????????????????????????????
module_exit(phy_module_exit)
#define?module_phy_driver(__phy_drivers)????????????????
????phy_module_driver(__phy_drivers,?ARRAY_SIZE(__phy_drivers))
int?phy_drivers_register(struct?phy_driver?*new_driver,?int?n,
?????????????struct?module?*owner)
{
????int?i,?ret?=?0;
????for?(i?=?0;?i?<?n;?i++)?{
????????ret?=?phy_driver_register(new_driver?+?i,?owner);????//?注冊(cè)數(shù)組中所有的phy驅(qū)動(dòng)
????????if?(ret)?{
????????????while?(i--?>?0)
????????????????phy_driver_unregister(new_driver?+?i);
????????????break;
????????}
????}
????return?ret;
}
EXPORT_SYMBOL(phy_drivers_register);
根據(jù)上面的分析,由于存在 phydev->drv->probe,所以會(huì)調(diào)用其注冊(cè)的函數(shù) tja11xx_probe。
網(wǎng)卡 fec 和 Phy 的協(xié)作
在 linux 內(nèi)核中,以太網(wǎng) mac 會(huì)被注冊(cè)成 struct net_device,phy 芯片會(huì)被注冊(cè)成 struct phy_device。 phy_device 的狀態(tài)怎么傳遞給 net_device,讓其在 link 狀態(tài)變化時(shí)做出對(duì)應(yīng)的配置改變,這個(gè)任務(wù)就落在上述的 struct phylink 中介身上。
下面就以 fec 網(wǎng)口驅(qū)動(dòng)為例,展示一下網(wǎng)卡 fec 和 phy 的協(xié)作過(guò)程。整個(gè) phy 驅(qū)動(dòng)的主要調(diào)用流程如下圖所示:
一個(gè) phy 驅(qū)動(dòng)的原理其實(shí)是非常簡(jiǎn)單的,一般流程如下:
- 用輪詢/中斷的方式通過(guò) mdio 總線讀取 phy 芯片的狀態(tài)。在 phy link 狀態(tài)變化的情況下,正確配置 mac 的狀態(tài)。(例如:根據(jù) phy 自協(xié)商的速率 10/100/1000M 把 mac 配置成對(duì)應(yīng)速率)
phy 芯片狀態(tài)在 phy 設(shè)備注冊(cè)的時(shí)候已經(jīng)體現(xiàn),這里詳細(xì)講下如何在 phy link 狀態(tài)變化的情況下,正確配置 mac 的狀態(tài)。
void?phy_state_machine(struct?work_struct?*work)
{
?old_state?=?phydev->state;
????/*?(1)?狀態(tài)機(jī)主體?*/
?switch?(phydev->state)?{
????/*?(1.1)?在?PHY_DOWN/PHY_READY?狀態(tài)下不動(dòng)作?*/
?case?PHY_DOWN:
?case?PHY_READY:
??break;
????
????/*?(1.2)?在?PHY_UP?狀態(tài)下,表明網(wǎng)口被?up?起來(lái),需要啟動(dòng)自協(xié)商并且查詢自協(xié)商后的?link?狀態(tài)
?????????????如果自協(xié)商結(jié)果是?link?up,進(jìn)入?PHY_RUNNING?狀態(tài)
?????????????如果自協(xié)商結(jié)果是?link?down,進(jìn)入?PHY_NOLINK?狀態(tài)
?????*/
?case?PHY_UP:
??needs_aneg?=?true;
??break;
????/*?(1.3)?在運(yùn)行的過(guò)程中定時(shí)輪詢?link?狀態(tài)
?????????????如果?link?up,進(jìn)入?PHY_RUNNING?狀態(tài)
?????????????如果?link?down,進(jìn)入?PHY_NOLINK?狀態(tài)
?????*/
?case?PHY_NOLINK:
?case?PHY_RUNNING:
??err?=?phy_check_link_status(phydev);
??break;
?}
????/*?(2)?如果需要,啟動(dòng)自協(xié)商配置?*/
?if?(needs_aneg)
??err?=?phy_start_aneg(phydev);
????/*?(3)?如果?phy?link?狀態(tài)有變化,通知給對(duì)應(yīng)網(wǎng)口?netdev?*/
?if?(old_state?!=?phydev->state)?{
??phydev_dbg(phydev,?"PHY?state?change?%s?->?%sn",
??????phy_state_to_str(old_state),
??????phy_state_to_str(phydev->state));
??if?(phydev->drv?&&?phydev->drv->link_change_notify)
???phydev->drv->link_change_notify(phydev);
?}
????/*?(4)?重新啟動(dòng)?work,周期為?1s?*/
?if?(phy_polling_mode(phydev)?&&?phy_is_started(phydev))
??phy_queue_state_machine(phydev,?PHY_STATE_TIME);
}
自協(xié)商配置
具體啟動(dòng) phy 自協(xié)商的代碼流程如下:
phy_state_machine()
`-|?phy_start_aneg()
???`-|?phy_config_aneg()
??????`-|?genphy_config_aneg()
?????????`-|?__genphy_config_aneg()
????????????`-|?genphy_setup_master_slave()?//?(1)?如果是千兆網(wǎng)口,配置其?master/slave
???????????????`-|?{
?????????????????|??phy_modify_changed(phydev,?MII_CTRL1000,????//?配置?mdio?0x09?寄存器
?????????????????|?????(CTL1000_ENABLE_MASTER?|?CTL1000_AS_MASTER?|?CTL1000_PREFER_MASTER),?ctl);
?????????????????|?}
??????????????|?genphy_config_advert()?//?(2)?設(shè)置本端的?advert?能力
???????????????`-|?{
?????????????????|??linkmode_and(phydev->advertising,?phydev->advertising,?phydev->supported);
?????????????????|??adv?=?linkmode_adv_to_mii_adv_t(phydev->advertising);
?????????????????|??phy_modify_changed(phydev,?MII_ADVERTISE,???//?10M/100M?能力配置到?mdio?0x04?寄存器
?????????????????|???????ADVERTISE_ALL?|?ADVERTISE_100BASE4?|
?????????????????|???????ADVERTISE_PAUSE_CAP?|?ADVERTISE_PAUSE_ASYM,?adv);
?????????????????|??if?(!(bmsr?&?BMSR_ESTATEN))?return?changed;
?????????????????|??adv?=?linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
?????????????????|??phy_modify_changed(phydev,?MII_CTRL1000,????//?1000M?能力配置到?mdio?0x09?寄存器
?????????????????|???????ADVERTISE_1000FULL?|?ADVERTISE_1000HALF,?adv);
?????????????????|?}
??????????????|?genphy_check_and_restart_aneg()
???????????????`-|?genphy_restart_aneg()?//?(3)?啟動(dòng)?phy?自協(xié)商
??????????????????`-|?{
????????????????????|??phy_modify(phydev,?MII_BMCR,?BMCR_ISOLATE,???//?配置?mdio?0x00?寄存器
????????????????????|???????BMCR_ANENABLE?|?BMCR_ANRESTART);
????????????????????|?}
link 狀態(tài)讀取
phy link 狀態(tài)讀取的代碼流程如下:
phy_state_machine()
`-|?phy_check_link_status()
???`-|?phy_read_status()????//?(1)?讀取?link?狀態(tài)?
??????`-|?genphy_read_status()
?????????`-|?{
???????????|??genphy_update_link(phydev);???//?(1.1)?更新?link?狀態(tài)
???????????|??if?(phydev->autoneg?==?AUTONEG_ENABLE?&&?old_link?&&?phydev->link)?return?0;
???????????|??genphy_read_master_slave(phydev);?//?(1.2)?如果是千兆網(wǎng)口,更新本端和對(duì)端的?master/slave
???????????|??genphy_read_lpa(phydev);??//?(1.3)?更新對(duì)端(link?partner)?聲明的能力
???????????|??if?(phydev->autoneg?==?AUTONEG_ENABLE?&&?phydev->autoneg_complete)?{
???????????|????phy_resolve_aneg_linkmode(phydev);??//?(1.4.1)?自協(xié)商模式,解析?link?結(jié)果
???????????|??}?else?if?(phydev->autoneg?==?AUTONEG_DISABLE)?{
???????????|????genphy_read_status_fixed(phydev);?//?(1.4.2)?固定模式,解析?link?結(jié)果
???????????|??}
???????????|?}
?????|?if?(phydev->link?&&?phydev->state?!=?PHY_RUNNING)?{??//?(2)?link?狀態(tài)?change?事件:變成?link?up
?????|???phydev->state?=?PHY_RUNNING;
?????|???phy_link_up(phydev);???//?link?up?事件,通知給?phylink
?????|?}?else?if?(!phydev->link?&&?phydev->state?!=?PHY_NOLINK)?{??//?(3)?link?狀態(tài)?change?事件:變成?link?down
?????|???phydev->state?=?PHY_NOLINK;
?????|???phy_link_down(phydev);?//?link?down?事件,通知給?phylink
?????|?}
link 狀態(tài)通知
phy 的 link 狀態(tài)變化怎么通知給 netdev,并且讓 mac 做出相應(yīng)的配置改變,這個(gè)是通過(guò)一個(gè)中介 phylink 來(lái)實(shí)現(xiàn)的。
phy_link_up()/phy_link_down()
`-|?phydev->phy_link_change(phydev,?true/false);
???`-|?phylink_phy_change()
??????`-|?{
????????|??pl->phy_state.speed?=?phydev->speed;?????//?(1)?把?`phy_device`??狀態(tài)更新給?`phylink`
????????|??pl->phy_state.duplex?=?phydev->duplex;
????????|??pl->phy_state.interface?=?phydev->interface;
????????|??pl->phy_state.link?=?up;
????????|??phylink_run_resolve(pl);?????//?(2)?通知?`phylink`?的輪詢?nèi)蝿?wù)啟動(dòng)
????????|?}