0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

PHY的控制器驱动框架分析

嵌入式悦翔园 来源:博客园 2023-07-26 11:46 次阅读

还没有学习第一篇内容的建议从第一篇开始学习会比较容易理解【以太网驱动】以太网扫盲篇一:各种网络总线 mii总线,mdio总线介绍

1. 概述

PHY芯片为OSI的最底层-物理层(Physical Layer),通过MII/GMII/RMII/SGMII/XGMII等多种媒体独立接口(介质无关接口)与数据链路层的MAC芯片相连,并通过MDIO接口实现对PHY状态的监控、配置和管理。

PHY与MAC整体的大致连接框架如下(图片来源于网络):

de843536-2b66-11ee-a368-dac502259ad0.png

PHY的整个硬件系统组成比较复杂,PHY与MAC相连(也可以通过一个中间设备相连),MAC与CPU相连(有集成在内部的,也有外接的方式)。

PHY与MAC通过MII和MDIO/MDC相连,MII是走网络数据的,MDIO/MDC是用来与PHY的寄存器通讯的,对PHY进行配置。

PHY的驱动与I2C/SPI的驱动一样,分为控制器驱动和设备器驱动。本节先讲控制器驱动。

2. PHY的控制器驱动总述

PHY的控制器驱动和SPI/I2C非常类似,控制器的核心功能是实现具体的读写功能。区别在于PHY的控制器读写功能的实现大致可以分为两种方式():

直接调用CPU的MDIO控制器(直接调用cpu对应的寄存器)的方式;通过GPIO/外围soc模拟MDIO时序的方式;PHY的控制器一般被描述为mdio_bus平台设备(注意:这是一个设备,等同于SPI/I2C中的master设备;和总线、驱动、设备中的bus不是一个概念)。

既然是平台设备,那么设备树中必定要有可以被解析为平台设备的节点,也要有对应的平台设备驱动。与SPI驱动类似,PHY设备模型也是在控制器驱动的probe函数中注册的。

3. 通过GPIO/外围soc模拟MDIO时序的方式

3.1 控制器平台设备在设备树中的大致描述方式(不完全准确,主要描述匹配的规则)

#linux-4.9.225Documentationdevicetreeindingssocfslcpm_qe
etwork.txt
*MDIO
Currentlydefinedcompatibles:fsl,pq1-fec-mdio(regissameasfirstresourceofFECdevice)fsl,cpm2-mdio-bitbang(regisportCregisters)
Propertiesforfsl,cpm2-mdio-bitbang:
fsl,mdio-pin:pinofportCcontrollingmdiodata
fsl,mdc-pin:pinofportCcontrollingmdioclock
Example:mdio@10d40{
compatible="fsl,mpc8272ads-mdio-bitbang",
"fsl,mpc8272-mdio-bitbang",
"fsl,cpm2-mdio-bitbang";
reg=<10d40 14>;
#address-cells=<1>;
#size-cells=<0>;
fsl,mdio-pin=<12>;
fsl,mdc-pin=<13>;

#linux-4.9.225Documentationdevicetreeindingsphy
xxx_phy:xxx-phy@xxx{//描述控制器下挂PHY设备的节点
reg=<0x0>;//PHY的地址
};
};

3.2 控制器平台驱动代码走读

3.2.1 控制器平台驱动的注册

staticconststructof_device_idfs_enet_mdio_bb_match[]={
{
.compatible="fsl,cpm2-mdio-bitbang",//匹配平台设备的名称
},
{},
};
MODULE_DEVICE_TABLE(of,fs_enet_mdio_bb_match);

staticstructplatform_driverfs_enet_bb_mdio_driver={
.driver={
.name="fsl-bb-mdio",
.of_match_table=fs_enet_mdio_bb_match,
},
.probe=fs_enet_mdio_probe,
.remove=fs_enet_mdio_remove,
};

module_platform_driver(fs_enet_bb_mdio_driver);//注册控制器平台设备驱动

3.2.2 控制器平台驱动的probe函数走读

/**********************************************************************************************
通过GPIO/外围soc模拟MDIO时序方式的MDIO驱动(probe函数中完成PHY设备的创建和注册)
***********************************************************************************************/
#linux-4.9.225drivers
etethernetfreescalefs_enetmii-bitbang.c

fs_enet_mdio_probe(structplatform_device*ofdev)
|---bitbang=kzalloc(sizeof(structbb_info),GFP_KERNEL)
|
|---bitbang->ctrl.ops=&bb_ops----------------------------------------------->|staticstructmdiobb_opsbb_ops={
||.owner=THIS_MODULE,
||.set_mdc=mdc,
||.set_mdio_dir=mdio_dir,
||.set_mdio_data=mdio,|-->实现为GPIO的读写
||.get_mdio_data=mdio_read,
||};
|<---------------------------------------------------------|
|--- new_bus = alloc_mdio_bitbang(&bitbang->ctrl)|
||---bus=mdiobus_alloc()-----------||structmdiobb_ctrl*ctrl=bus->priv||
||---bus->read=mdiobb_read-----------||ctrl->ops->set_mdc||
||---bus->write=mdiobb_write-----------|--mdiobb_read/mdiobb_write/mdiobb_reset函数的实现-|ctrl->ops->set_mdio_dir|--|
||---bus->reset=mdiobb_reset-----------|/|ctrl->ops->set_mdio_data|
||---bus->priv=ctrl<----------------------------                                            | ctrl->ops->get_mdio_data|
|
|---fs_mii_bitbang_init//设置用来模拟mdc和mdio的管脚资源
||---of_address_to_resource(np,0,&res)//转换设备树地址并作为资源返回,设备树中指定
||
||---snprintf(bus->id,MII_BUS_ID_SIZE,"%x",res.start)//把资源的起始地址设置为bus->id
||
||---data=of_get_property(np,"fsl,mdio-pin",&len)
||---mdio_pin=*data//决定控制mdio数据的端口的引脚
||
||---data=of_get_property(np,"fsl,mdc-pin",&len)
||---mdc_pin=*data//控制mdio时钟的端口引脚
||
||---bitbang->dir=ioremap(res.start,resource_size(&res))
||
||---bitbang->dat=bitbang->dir+4
||---bitbang->mdio_msk=1<< (31 - mdio_pin)               
|    |--- bitbang->mdc_msk=1<< (31 - mdc_pin)
|    
|--- of_mdiobus_register(new_bus, ofdev->dev.of_node)//注册mii_bus设备,并通过设备树子节点创建PHY设备<===of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
|    |--- mdio->phy_mask=~0//屏蔽所有PHY,防止自动探测。相反,设备树中列出的phy将在总线注册后填充
||---mdio->dev.of_node=np
||---mdiobus_register(mdio)//@注意@注册MDIO总线设备(注意是总线设备不是总线,因为总线也是一种设备。mdio_bus是在其他地方注册的,后面会讲到)
|||---__mdiobus_register(bus,THIS_MODULE)
||||---bus->owner=owner
||||---bus->dev.parent=bus->parent
||||---bus->dev.class=&mdio_bus_class
||||---bus->dev.groups=NULL
||||---dev_set_name(&bus->dev,"%s",bus->id)//设置总线设备的名称
||||---device_register(&bus->dev)//注册总线设备
||
||---for_each_available_child_of_node(np,child)//遍历这个平台设备的子节点并为每个phy注册一个phy_device
||---addr=of_mdio_parse_addr(&mdio->dev,child)//从子节点的"reg"属性中获得PHY设备的地址
|||---of_property_read_u32(np,"reg",&addr)
||---if(addr< 0)                                //如果未获得子节点的"reg"属性,则在后面再启用扫描可能存在的PHY的,然后注册
|         |    |--- scanphys = true 
|         |    |--- continue
|         | 
|         |--- of_mdiobus_register_phy(mdio, child, addr)   //创建并注册PHY设备
|         |    |--- is_c45 = of_device_is_compatible(child,"ethernet-phy-ieee802.3-c45") //判断设备树中的PHY的属性是否指定45号条款
|         |    |
|         |    |--- if (!is_c45 && !of_get_phy_id(child, &phy_id))      //如果设备树中的PHY的属性未指定45号条款 且未通过"ethernet-phy-id%4x.%4x"属性指定PHY的ID              
|         |    |    |---phy_device_create(mdio, addr, phy_id, 0, NULL)   
|         |    |---else //我这里采用的是else分支
|         |    |    |---phy = get_phy_device(mdio, addr, is_c45)        //在@bus上的@addr处读取PHY的ID寄存器,然后分配并返回表示它的phy_device
|         |    |        |--- get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids)        //通过mdio得到PHY的ID
|         |    |        |--- phy_device_create(bus, addr, phy_id, is_c45, &c45_ids)  //创建PHY设备
|         |    |             |--- struct phy_device *dev
|         |    |             |--- struct mdio_device *mdiodev
|         |    |             |--- dev = kzalloc(sizeof(*dev), GFP_KERNEL)
|         |    |             |--- mdiodev = &dev->mdio//mdiodev是最新的内核引入,较老的版本没有这个结构
||||---mdiodev->dev.release=phy_device_release
||||---mdiodev->dev.parent=&bus->dev
||||---mdiodev->dev.bus=&mdio_bus_type//PHY设备和驱动都会挂在mdio_bus下,匹配时会调用对应的match函数---|
||||---mdiodev->bus=bus|
||||---mdiodev->pm_ops=MDIO_BUS_PHY_PM_OPS|
||||---mdiodev->bus_match=phy_bus_match//真正实现PHY设备和驱动匹配的函数<--------------------------------|
|         |    |             |--- mdiodev->addr=addr
||||---mdiodev->flags=MDIO_DEVICE_FLAG_PHY
||||---mdiodev->device_free=phy_mdio_device_free
||||---diodev->device_remove=phy_mdio_device_remove
||||---dev->speed=SPEED_UNKNOWN
||||---dev->duplex=DUPLEX_UNKNOWN
||||---dev->pause=0
||||---dev->asym_pause=0
||||---dev->link=1
||||---dev->interface=PHY_INTERFACE_MODE_GMII
||||---dev->autoneg=AUTONEG_ENABLE//默认支持自协商
||||---dev->is_c45=is_c45
||||---dev->phy_id=phy_id
||||---if(c45_ids)
|||||---dev->c45_ids=*c45_ids
||||---dev->irq=bus->irq[addr]
||||---dev_set_name(&mdiodev->dev,PHY_ID_FMT,bus->id,addr)
||||---dev->state=PHY_DOWN//指示PHY设备和驱动程序尚未准备就绪,在PHY驱动的probe函数中会更改为READY
||||---INIT_DELAYED_WORK(&dev->state_queue,phy_state_machine)//PHY的状态机(核心WORK)
||||---INIT_WORK(&dev->phy_queue,phy_change)//由phy_interrupt/timer调度以处理PHY状态的更改
||||---request_module(MDIO_MODULE_PREFIXMDIO_ID_FMT,MDIO_ID_ARGS(phy_id))//加载内核模块(这里没有细致研究过)
||||---device_initialize(&mdiodev->dev)//设备模型中的一些设备,主要是kset、kobject、ktype的设置
|||
|||---irq_of_parse_and_map(child,0)//将中断解析并映射到linuxvirq空间(未深入研究)
|||---if(of_property_read_bool(child,"broken-turn-around"))//MDIO总线中的TA(Turnaroundtime)
||||---mdio->phy_ignore_ta_mask|=1<< addr
|         |    | 
|         |    |--- of_node_get(child)//将OF节点与设备结构相关联,以便以后查找
|         |    |--- phy->mdio.dev.of_node=child
|||
|||---phy_device_register(phy)//注册PHY设备
||||---mdiobus_register_device(&phydev->mdio)//注册到mdiodev->bus,其实笔者认为这是一个虚拟的注册,仅仅是根据PHY的地址在mdiodev->bus->mdio_map数组对应位置填充这个mdiodev
|||||---mdiodev->bus->mdio_map[mdiodev->addr]=mdiodev//方便通过mdiodev->bus统一管理和查找,以及关联bus的读写函数,方便PHY的功能配置
||||
||||---device_add(&phydev->mdio.dev)//注册到linux设备模型框架中
||
||---if(!scanphys)//如果从子节点的"reg"属性中获得PHY设备的地址,scanphys=false,这里就直接返回了,因为不需要再扫描了
|||---return0
||
/******************************************************************************************************************
一般来说只要设备树种指定了PHY设备的"reg"属性,后面的流程可以自动忽略
******************************************************************************************************************
||---for_each_available_child_of_node(np,child)//自动扫描具有空"reg"属性的PHY
||---if(of_find_property(child,"reg",NULL))//跳过具有reg属性集的PHY
|||---continue
||
||---for(addr=0;addr< PHY_MAX_ADDR; addr++)         //循环遍历扫描
|                   |--- if (mdiobus_is_registered_device(mdio, addr))  //跳过已注册的PHY
|                   |    |--- continue
|                   |
|                   |--- dev_info(&mdio->dev,"scanphy%sataddress%i
",child->name,addr)//打印扫描的PHY,建议开发人员设置"reg"属性
||
||---if(of_mdiobus_child_is_phy(child))
||---of_mdiobus_register_phy(mdio,child,addr)//注册PHY设备
|
******************************************************************************************************************/

4. 直接调用CPU的MDIO控制器的方式

控制器平台设备在设备树中的大致描述方式(不完全准确,主要描述匹配的规则)

#linux4.9.225Documentationdevicetreeindingspowerpcfslfman.txt
ExampleforFManv3internalMDIO:
mdio@e3120{//描述MDIO控制器驱动节点
compatible="fsl,fman-mdio";
reg=<0xe3120 0xee0>;
fsl,fman-internal-mdio;
tbi1:tbi-phy@8{//描述控制器下挂PHY设备的节点
reg=<0x8>;
device_type="tbi-phy";
};
};

控制器平台驱动的注册

#linux-4.9.225drivers
etethernetfreescalefsl_pq_mdio.c
staticconststructof_device_idfsl_pq_mdio_match[]={
......

/*NoKconfigoptionforFmansupportyet*/
{
.compatible="fsl,fman-mdio",//匹配平台设备的名称
.data=&(structfsl_pq_mdio_data){
.mii_offset=0,
/*FmanTBIoperationsarehandledelsewhere*/
},
},
......
{},
};

staticstructplatform_driverfsl_pq_mdio_driver={
.driver={
.name="fsl-pq_mdio",
.of_match_table=fsl_pq_mdio_match,
},
.probe=fsl_pq_mdio_probe,
.remove=fsl_pq_mdio_remove,
};

module_platform_driver(fsl_pq_mdio_driver);//注册控制器平台设备驱动

控制器平台驱动的probe函数走读

/****************************************************************************************
直接调用CPU的MDIO控制器的方式的MDIO控制器驱动(probe函数中涉及PHY设备的创建和注册)
****************************************************************************************/
#linux-4.9.225drivers
etethernetfreescalefsl_pq_mdio.c

fsl_pq_mdio_probe(structplatform_device*pdev
|---structfsl_pq_mdio_priv*priv
|---structmii_bus*new_bus
|
|---new_bus=mdiobus_alloc_size(sizeof(*priv))//分配结构体
|---priv=new_bus->priv
|---new_bus->name="FreescalePowerQUICCMIIBus"
|---new_bus->read=&fsl_pq_mdio_read//总线的读接口
|---new_bus->write=&fsl_pq_mdio_write//总线的写接口
|---new_bus->reset=&fsl_pq_mdio_reset//总线的复位接口
|
|---of_address_to_resource(np,0,&res)//获取控制器地址资源
|---snprintf(bus->id,MII_BUS_ID_SIZE,"%x",res.start)//把资源的起始地址设置为bus->id
|
|---of_mdiobus_register(new_bus,np)//注册mii_bus设备,并通过设备树中控制器的子节点创建PHY设备,这一点与模拟方式流程相同

of_mdiobus_register的流程与第四小节一致,这里就不再列出。

5. 控制器的读写会在哪里得到调用?

在PHY设备的注册中(读PHY ID)、PHY的初始化、自协商、中断、状态、能力获取等流程中经常可以看到phy_read和phy_write两个函数(下一节要讲的PHY驱动),这两个函数的实现就依赖于控制器设备mii_bus的读写。

phy_read和phy_write定义在linux-4.9.225includelinuxphy.h中,如下:

staticinlineintphy_read(structphy_device*phydev,u32regnum)
{
returnmdiobus_read(phydev->mdio.bus,phydev->mdio.addr,regnum);
}

staticinlineintphy_write(structphy_device*phydev,u32regnum,u16val)
{
returnmdiobus_write(phydev->mdio.bus,phydev->mdio.addr,regnum,val);
}

其中mdiobus_read和mdiobus_write定义在linux-4.9.225drivers etphymdio_bus.c中,如下:

/**
*mdiobus_read-ConveniencefunctionforreadingagivenMIImgmtregister
*@bus:themii_busstruct
*@addr:thephyaddress
*@regnum:registernumbertoread
*
*NOTE:MUSTNOTbecalledfrominterruptcontext,
*becausethebusread/writefunctionsmaywaitforaninterrupt
*toconcludetheoperation.
*/
intmdiobus_read(structmii_bus*bus,intaddr,u32regnum)
{
intretval;

BUG_ON(in_interrupt());

mutex_lock(&bus->mdio_lock);
retval=bus->read(bus,addr,regnum);
mutex_unlock(&bus->mdio_lock);

returnretval;
}

/**
*mdiobus_write-ConveniencefunctionforwritingagivenMIImgmtregister
*@bus:themii_busstruct
*@addr:thephyaddress
*@regnum:registernumbertowrite
*@val:valuetowriteto@regnum
*
*NOTE:MUSTNOTbecalledfrominterruptcontext,
*becausethebusread/writefunctionsmaywaitforaninterrupt
*toconcludetheoperation.
*/
intmdiobus_write(structmii_bus*bus,intaddr,u32regnum,u16val)
{
interr;

BUG_ON(in_interrupt());

mutex_lock(&bus->mdio_lock);
err=bus->write(bus,addr,regnum,val);
mutex_unlock(&bus->mdio_lock);

returnerr;
}

可以清楚的看到bus->read和bus->write读写接口在这里得到调用。

6. mdio_bus总线

接下来要讲的PHY设备驱动是基于device、driver、bus的连接方式。其驱动涉及如下几个重要部分:

总线 - sturct mii_bus (mii stand for media independent interface)

设备 - struct phy_device

驱动 - struct phy_driver

关于PHY设备的创建和注册已经在第5节的probe函数中有过详细的描述(需要注意的是:phy设备不像i2c/spi有一个board_info函数进行设备的添加,而是直接读取phy中的寄存器<根据IEEE的规定,PHY芯片的前16个寄存器的内容必须是固定的>),本节就不再描述;

总线注册的入口函数

#linux-4.9.225drivers
etphyphy_device.c
staticint__initphy_init(void)
{
intrc;

rc=mdio_bus_init();//mdio_bus总线的注册
if(rc)
returnrc;

rc=phy_drivers_register(genphy_driver,ARRAY_SIZE(genphy_driver),THIS_MODULE);//通用PHY驱动
if(rc)
mdio_bus_exit();

returnrc;
}

subsys_initcall(phy_init);

subsys_initcall(phy_init) 这行的作用非常重要,这一行就决定了内核在启动的时候会调用该函数,注册完了之后紧接着又注册一个通用的PHY驱动。

总线注册函数--- mdio_bus_init解析

#linux-4.9.225drivers
etphymdio_bus.c
staticstructclassmdio_bus_class={
.name="mdio_bus",
.dev_release=mdiobus_release,
};

staticintmdio_bus_match(structdevice*dev,structdevice_driver*drv)
{
structmdio_device*mdio=to_mdio_device(dev);

if(of_driver_match_device(dev,drv))
return1;

if(mdio->bus_match)
returnmdio->bus_match(dev,drv);

return0;
}

structbus_typemdio_bus_type={
.name="mdio_bus",//总线名称
.match=mdio_bus_match,//用来匹配总线上设备和驱动的函数
.pm=MDIO_BUS_PM_OPS,
};
EXPORT_SYMBOL(mdio_bus_type);

int__initmdio_bus_init(void)
{
intret;

ret=class_register(&mdio_bus_class);//注册设备类(在linux设备模型中,我再仔细讲这个类的概念)
if(!ret){
ret=bus_register(&mdio_bus_type);//总线注册
if(ret)
class_unregister(&mdio_bus_class);
}

returnret;
}

总线中的match函数解析

/**
*mdio_bus_match-determineifgivenMDIOdriversupportsthegiven
*MDIOdevice
*@dev:targetMDIOdevice
*@drv:givenMDIOdriver
*
*Description:GivenaMDIOdevice,andaMDIOdriver,return1if
*thedriversupportsthedevice.Otherwise,return0.Thismay
*requirecallingthedevicesownmatchfunction,sincedifferentclasses
*ofMDIOdeviceshavedifferentmatchcriteria.
*/
staticintmdio_bus_match(structdevice*dev,structdevice_driver*drv)
{
structmdio_device*mdio=to_mdio_device(dev);

if(of_driver_match_device(dev,drv))
return1;

if(mdio->bus_match)//实现匹配的函数
returnmdio->bus_match(dev,drv);

return0;
}

7. 设备驱动的注册

在phy_init函数中不仅注册了mdio_bus总线,还注册了一个通用的PHY驱动作为缺省的内核PHY驱动,但是如果PHY芯片的内部寄存器和802.3定义的并不一样或者需要特殊的功能配置以实现更强的功能,这就需要专有的驱动。

关于通用PHY驱动的知识,网上有一大堆讲解,本节就不再重复的去描述。

对于市场上存在的主流PHY品牌,一般在内核源码 drivers etphy目录下都有对应的驱动。本节主要以realtek RTL8211F为例,讲述PHY的驱动,代码如下:

#linux-4.9.225drivers
etphy
ealtek.c
staticstructphy_driverrealtek_drvs[]={
......
,{
.phy_id=0x001cc916,
.name="RTL8211FGigabitEthernet",
.phy_id_mask=0x001fffff,
.features=PHY_GBIT_FEATURES,
.flags=PHY_HAS_INTERRUPT,
.config_aneg=&genphy_config_aneg,
.config_init=&rtl8211f_config_init,
.read_status=&genphy_read_status,
.ack_interrupt=&rtl8211f_ack_interrupt,
.config_intr=&rtl8211f_config_intr,
.suspend=genphy_suspend,
.resume=genphy_resume,
},
};

module_phy_driver(realtek_drvs);//注册PHY驱动

staticstructmdio_device_id__maybe_unusedrealtek_tbl[]={
{0x001cc912,0x001fffff},
{0x001cc914,0x001fffff},
{0x001cc915,0x001fffff},
{0x001cc916,0x001fffff},
{}
};

MODULE_DEVICE_TABLE(mdio,realtek_tbl);

phy驱动的注册

1、同一品牌的PHY设备有多种不同的型号,内核为了支持一次可以注册多个型号的PHY的驱动,在includelinuxphy.h中提供了用于注册PHY驱动的宏module_phy_driver。该宏的定义如下:

#linux-4.9.225includelinuxphy.h

#definephy_module_driver(__phy_drivers,__count)
staticint__initphy_module_init(void)
{
returnphy_drivers_register(__phy_drivers,__count,THIS_MODULE);
}

#definemodule_phy_driver(__phy_drivers)
phy_module_driver(__phy_drivers,ARRAY_SIZE(__phy_drivers))

2、其中phy_driver_register定义如下(注意这里与老版本内核有一定的改动)

/**
*phy_driver_register-registeraphy_driverwiththePHYlayer
*@new_driver:newphy_drivertoregister
*@owner:moduleowningthisPHY
*/
intphy_driver_register(structphy_driver*new_driver,structmodule*owner)
{
intretval;

new_driver->mdiodrv.flags|=MDIO_DEVICE_IS_PHY;
new_driver->mdiodrv.driver.name=new_driver->name;//驱动名称
new_driver->mdiodrv.driver.bus=&mdio_bus_type;//驱动挂载的总线
new_driver->mdiodrv.driver.probe=phy_probe;//PHY设备和驱动匹配后调用的probe函数
new_driver->mdiodrv.driver.remove=phy_remove;
new_driver->mdiodrv.driver.owner=owner;

retval=driver_register(&new_driver->mdiodrv.driver);//向linux设备模型框架中注册device_driver驱动
if(retval){
pr_err("%s:Error%dinregisteringdriver
",
new_driver->name,retval);

returnretval;
}

pr_debug("%s:Registerednewdriver
",new_driver->name);

return0;
}

intphy_drivers_register(structphy_driver*new_driver,intn,
structmodule*owner)
{
inti,ret=0;

for(i=0;i< n; i++) {
  ret = phy_driver_register(new_driver + i, owner);//注册数组中所有的phy驱动
  if (ret) {
   while (i-- >0)
phy_driver_unregister(new_driver+i);
break;
}
}
returnret;
}

8. 设备驱动与控制器驱动之间的关系图

dea444fc-2b66-11ee-a368-dac502259ad0.png

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 芯片
    +关注

    关注

    447

    文章

    47804

    浏览量

    409170
  • 控制器
    +关注

    关注

    112

    文章

    15235

    浏览量

    171207
  • Mac
    Mac
    +关注

    关注

    0

    文章

    1067

    浏览量

    50845
  • PHY
    PHY
    +关注

    关注

    2

    文章

    265

    浏览量

    51008

原文标题:【以太网驱动】以太网扫盲篇二:PHY的控制器驱动框架分析

文章出处:【微信号:嵌入式悦翔园,微信公众号:嵌入式悦翔园】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    一文详解Linux PCI驱动框架

    PCIe控制器驱动,各家的IP实现不一样,驱动的差异可能会很大,单独分析一个驱动毕竟只是个例,应该去掌握背后的通用
    的头像 发表于 03-02 15:39 8885次阅读

    以太网交换机芯片可用于已有PHY的微控制器

    PHY的微控制器一起使用吗?谢谢你 以上来自于百度翻译 以下为原文What can i do if i have a PCB that already has microcontroller
    发表于 11-01 17:08

    CAN通信控制器程序框架与实现

    实现的 CAN 通信控制器参照 SJA1000 CAN 通信控制器的结构,程序基本框架如图 9-9 所示。 CAN 通信控制器的具体实现各模块的组织结构如图 9-10 所示。
    发表于 12-19 17:26

    以太网控制器(MAC)的基本框架怎么搭建

    以太网标准。如图 10-6 所示,使用这个以太网控制器外部连接一块 PHY 芯片(实现了物理层功能的芯片)就可以进行数据链路层的通信,即帧通信。在此基础上可以方便、快捷地开发出更高层次的协议,实现
    发表于 12-28 17:30

    以太网控制器外部PHY芯片模拟程序代码实现

    模拟程序模拟了简化的 LXT971A 芯片(Inter 公司的外部 PHY 芯片)。PHY 芯片通过 MIIM(媒体无关接口管理模块)来连接以太网控制器,因此:• 当以太网控制器
    发表于 01-18 14:20

    PYNQ框架如何开发可重构运动控制器

    ,易于对创作者群体进行扩展。项目在PYNQ框架下开发了可重构运动控制器,所提出的体系结构依赖于使用S曲线算法来控制轨道上的运动,而另一个则是为了验证概念。  1.什么是S型运动轨迹规划  市场上发现的许多
    发表于 01-07 17:17

    步进电机驱动控制器介绍

    01步进电机驱动控制器步进电机驱动器作为实验装置可控的部分,能够提供自动测量的方法。前面已经有两款步进电机驱动器:SH-20403AXIS12双轴蓝牙移动
    发表于 08-31 08:32

    RK平台PHY驱动与NXP的整体框架有哪些区别呢

    RK平台PHY驱动与NXP的整体框架有哪些区别呢?
    发表于 02-10 06:04

    OpenHarmony系统平台驱动驱动框架概述

    UART控制器配置一个HDF设备节点服务policy大于等于1(如需对用户态可见为2,仅内核态可见为1);moduleName需要与驱动Entry中moduleName 保持一致;ServiceName必须
    发表于 03-28 10:58

    RT-Thread系统的LCD驱动框架学习

    LCD驱动:imx6ull这款芯片自带LCD控制器,所以比较方便。关于LCD驱动,从RTT的代码是没有对应的驱动框架的,所以直接对接设备
    发表于 03-31 15:59

    RK3399平台上USB控制器PHY的连接方式和配置说明

    USB3.0和两个USB2.0 Host。USB控制器PHY:rk3399平台控制器PHY的连接示意图如下:两个Type-C USB3.0控制
    发表于 05-12 17:46

    RK3568 PHY驱动框架解析

    ndev-> dwmac_rk_create_loopback_sysfs(&pdev->dev);// 相关系统文件小结RK平台PHY驱动与NXP的整体框架对比大部分一致。原作者:Bazinga bingo
    发表于 09-14 16:58

    动态偏振控制器驱动电路分析

    本文以光纤挤压型偏振控制器为研究对象,运用邦加球图示法分析了其工作原理,介绍动态偏振控制器驱动电路的工作原理及软、硬件设计。
    发表于 12-27 17:19 2386次阅读
    动态偏振<b class='flag-5'>控制器</b><b class='flag-5'>驱动</b>电路<b class='flag-5'>分析</b>

    以太网PHY控制器驱动框架分析

    PHY芯片为OSI的最底层-物理层(Physical Layer),通过MII/GMII/RMII/SGMII/XGMII等多种媒体独立接口(介质无关接口)与数据链路层的MAC芯片相连,并通过MDIO接口实现对PHY状态的监控、配置和管理。
    的头像 发表于 05-04 10:06 1059次阅读
    以太网<b class='flag-5'>PHY</b>的<b class='flag-5'>控制器</b><b class='flag-5'>驱动</b><b class='flag-5'>框架</b><b class='flag-5'>分析</b>

    rt-thread SDIO驱动框架分析(贴片SD卡flash驱动\SD Nand flash驱动

    文章目录rt-threadSDIO驱动框架分析之SD卡驱动1.前言2.SDIO通用驱动框架介绍3
    的头像 发表于 04-04 17:41 593次阅读
    rt-thread SDIO<b class='flag-5'>驱动</b><b class='flag-5'>框架</b><b class='flag-5'>分析</b>(贴片SD卡flash<b class='flag-5'>驱动</b>\SD Nand flash<b class='flag-5'>驱动</b>)