千兆网口Freescale ETSEC + Marvell 88E1111 uboot Linux 驱动分析.docx
- 文档编号:9056574
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:29
- 大小:96.18KB
千兆网口Freescale ETSEC + Marvell 88E1111 uboot Linux 驱动分析.docx
《千兆网口Freescale ETSEC + Marvell 88E1111 uboot Linux 驱动分析.docx》由会员分享,可在线阅读,更多相关《千兆网口Freescale ETSEC + Marvell 88E1111 uboot Linux 驱动分析.docx(29页珍藏版)》请在冰豆网上搜索。
千兆网口FreescaleETSEC+Marvell88E1111ubootLinux驱动分析
在连续两个平台的uboot和Linux系统移植过程中,在千兆网口调试这块都遇到了很大的麻烦。
由于寄存器数量庞大,千兆网口MAC和PHY内部结构复杂,MAC和PHY接口种类多,千兆以太网驱动的调试成了系统移植过程中最让人烦心的一个环节。
就像火箭队,每次都让球迷无比揪心,不是输的窝囊,就是伤兵满营,现在新赛季又两连败了,打的比勇士还勇士,后场两个比我还瘦的家伙,怎么防守。
算了,不扯这么多了,今天要说的是网口MAC+PHY的一些原理和代码分析。
(以Freescale的ETSEC和Marvell的88E1111为例。
)
1 千兆以太网的物理层
千兆以太网的物理层分为物理编码子层PCS(PhysicalCodingSublayer)、物理介质连接子层PMA(PhysicalMediumAttachment)和物理介质相关子层PMD(PhysicalMediumDependent)三层,如下图所示:
其中PCS子层负责8b10b编码,它可以把从GMII口接收到的8位并行的数据转换成10位并行的数据输出。
因为10比特的数据能有效地减小直流分量,降低误码率,另外采用8b10b编码便于在数据中提取时钟和进行首发同步。
可以把PCS两头看成GMII接口和TBI接口。
PMA子层进一步将PCS子层的编码结果向各种物理媒体传送,主要是负责完成串并转换。
PCS层以125M的速率并行传送10位代码到PMA层,由PMA层转换为1.25Gbps的串行数据流进行发送,以便实际能得到1Gbps的千兆以太网传送速率。
可以把PMA子层的两头分别看做TBI接口和SGMII接口。
PMD子层将对各种实际的物理媒体完成接口,完成真正的物理连接。
由于1000BASE-X支持多种物理媒介,如光纤和屏蔽双绞线,它们的物理接口显然不会相同。
有的要进行光电转换,有的要完成从不平衡到平衡的转换。
PMD层将对这些具体的连接器作出规定。
2Freescale 的ETSEC与PHY之间的接口
Freescale的MPC8314和P2020都自带了三速以太网控制器ETSEC,可以提供10M,100M,1000M三种速率的接口。
当作为以太网时,需要外部的PHY芯片或者Serdes设备与其相连接。
每个ETSEC都支持多标准的MII接口,总体结构如下图所示,可以提供GMII,RGMII,MII,RMII,RTBI,SGMII 六种接口,下图为从MPC8314datasheet中截取的ETSEC的结构图。
如果CPU与PHY之间是GMII接口或RGMII接口,那么PHY将提供完整的PCS,PMA,PMD三层工作;如果CPU与PHY之间是RTBI接口,那么PCS层的工作在ETSEC中已经做完了,ETSEC中的TBI模块可以做PCS层的工作,PHY只需要做PMA和PMD的工作即可;如果CPU与PHY之间是SGMII接口,那么PHY只需要完成PMD的工作,ETSEC中的PCS由TBI完成,而PMA由CPU自带的Serdes模块完成。
3BD表结构
在千兆以太网的驱动中,现在一般都使用一个叫BD表的东西来管理MAC层发送和接收的内存区域,如下图所示:
在IMMR映射的寄存器空间中有两组寄存器TBASEn和RBASEn,分别为TxBDRingn 和 RxBDRingn的指针。
MPC8314的ETSEC允许有8个TxBDRing和8个RxBDRing,他们都存放在内存的某个区域中。
每个BufferDescriptor 都是有8个字节构成,两个字节的状态,两个字节的数据长度和四个字节的数据指针,这个指针指向内存的另一块地方,这才是真正存储发送接收数据的地方。
BufferDescriptor必须在网口初始化的时候初始化,并将自己的地址赋给TBASEn和RBASEn。
在网口驱动程序中可以看到,每个BDRing中的BD数量是可变的(我们设为64),而他们之间并没有指针连接,只是一段连续的空间,顺序下来的,所谓的环只是一个虚拟的概念,在最后一个BD时,需要将BD状态位中的W位(Wrap)置一,表示这是最后一个BD,他的下一个BD就是第一个BD。
如下图所示:
下面一节将结合uboot源码分析一下网口初始化以及PHY配置的过程,再下一节会分析内核中的驱动。
为什么先说uboot,因为在我看来,驱动程序就是分为两个部分,1 按照Datasheet的说明去配置寄存器,2 添加符合操作系统规范去融入操作系统。
在uboot下系统很简单,代码一目了然,所以我们应该在boot下先把寄存器配置好,再去分析复杂的多的内核代码。
这节分析uboot中的网口驱动代码。
1 网口驱动函数列表
函数名
函数用途
tsec_initialize()
网口初始化函数
tsec_init()
网口启动函数
tsec_local_mdio_write()
MDIO口写函数
tsec_local_mdio_read()
MDIO口读函数
tsec_send()
网口发送函数
tsec_recv()
网口接收函数
tsec_configure_serdes()
配置TBIPHY的函数
fsl_serdes_init()
Serdes模块初始化函数
init_phy()
PHY初始化函数
adjust_link()
根据PHY状态配置MAC的函数
2tsec_initialize()函数
该函数为ETSEC的初始化函数,在该函数中要初始化eth_device结构和私有的tsec_private结构,并初始化PHY。
inttsec_initialize(bd_t*bis,intindex,char*devname)
{
structeth_device*dev;
inti;
structtsec_private*priv;
/*为dev分配空间*/
dev=(structeth_device*)malloc(sizeof*dev);
if(NULL==dev)
return0;
memset(dev,0,sizeof*dev);
/*为priv分配空间*/
priv=(structtsec_private*)malloc(sizeof(*priv));
if(NULL==priv)
return0;
/*从tsec_info数组中取合适的值去初始化私有结构tsec_private*/
privlist[num_tsecs++]=priv;
priv->regs=tsec_info[index].regs; //每个tsec寄存器的基址
priv->phyregs=tsec_info[index].miiregs; //PHYMDIO读写状态寄存器基址
/*TBIPHY的MDIO读写状态寄存器基址*/
priv->phyregs_sgmii=tsec_info[index].miiregs_sgmii;
priv->phyaddr=tsec_info[index].phyaddr; //PHY地址
priv->flags=tsec_info[index].flags;
priv->ID=index;
/*使用将priv结构体挂到dev结构体下,挂载tsec的打开、关闭、发送、接收函数*/
sprintf(dev->name,tsec_info[index].devname);
dev->iobase=0;
dev->priv=priv;
dev->init=tsec_init;
dev->halt=tsec_halt;
dev->send=tsec_send;
dev->recv=tsec_recv;
/*初始化IP地址*/
for(i=0;i<6;i++)
dev->enetaddr[i]=0;
/*设置当前活跃的网口名相当于setethacteTSECn,将多个网口级联*/
eth_register(dev);
/*通过设置MACCFG1寄存器重启MAC*/
priv->regs->maccfg1|=MACCFG1_SOFT_RESET;
udelay
(2); /*SoftResetmustbeassertedfor3TXclocks*/
priv->regs->maccfg1&=~(MACCFG1_SOFT_RESET);
/*挂载MII口的读写函数*/
#ifdefined(CONFIG_MII)||defined(CONFIG_CMD_MII)/
&&!
defined(BITBANGMII)
miiphy_register(dev->name,tsec_miiphy_read,tsec_miiphy_write);
#endif
/*初始化PHY,并返回*/
returninit_phy(dev);
}
3init_phy()
staticintinit_phy(structeth_device*dev)
{
structtsec_private*priv=(structtsec_private*)dev->priv;
structphy_info*curphy;
volatiletsec_t*regs=priv->regs;
/*在TBIPA的寄存器中写入TBIPHY的地址*/
regs->tbipa=CONFIG_SYS_TBIPA_VALUE+priv->ID;
asm("sync");
/*重启MII接口*/
priv->phyregs->miimcfg=MIIMCFG_RESET;
asm("sync");
priv->phyregs->miimcfg=MIIMCFG_INIT_VALUE;
asm("sync");
while(priv->phyregs->miimind&MIIMIND_BUSY);
/*通过读PHY的2号3号寄存器获得该ETSEC外连的PHY的ID,搜索phy_info数组,找到符合ID的PHY信息返回。
*/
curphy=get_phy_info(dev);
if(curphy==NULL)
{
priv->phyinfo=NULL;
printf("%s:
NoPHYfound/n",dev->name);
return0;
}
/*如果是SGMII的接口,需要使用TBIPHY,初始化TBIPHY,注意这里名字竟然叫serdes配置,Linux里面也这么叫,真是误人子弟啊。
*/
if(regs->ecntrl&ECNTRL_SGMII_MODE)
tsec_configure_serdes(priv);
/*在符合条件的PHY的phy_info数组中调用其初始化配置函数*/
priv->phyinfo=curphy;
phy_run_commands(priv,priv->phyinfo->config);
return1;
}
4phy_info结构
Uboot中使用这个结构来完成phy的操作,所有的phy都要使用这个结构表示,下面是88E1111的phy_info结构:
structphy_infophy_info_M88E1111S={
0x01410cc, //PHYID
"Marvell88E1111S", //PHY名称
4,
(structphy_cmd[])
{
/*配置数组,在调用priv->phyinfo->config时将依次调用下面的内容,每个大括号内,第一个为PHY寄存器地址,第二个为value*/
/*ResetandconfigurethePHY*/
{MIIM_CONTROL,MIIM_CONTROL_RESET,NULL},
/*DelayRGMIITXandRX*/
{MIIM_GBIT_CONTROL,MIIM_GBIT_CONTROL_INIT,NULL},
{MIIM_ANAR,MIIM_ANAR_INIT,NULL},
{MIIM_CONTROL,MIIM_CONTROL_RESET,NULL},
{MIIM_CONTROL,MIIM_CONTROL_INIT,&mii_cr_init},
{miim_end,}
},
(structphy_cmd[])
{
/*启动数组,在ETSEC启动的时候要依次调用。
*/
/*Statusisreadoncetoclearoldlinkstate*/
{MIIM_STATUS,miim_read,NULL},
/*Auto-negotiate*/
{MIIM_STATUS,miim_read,&mii_parse_sr},
/*Readthestatus*/
{MIIM_88E1011_PHY_STATUS,miim_read,&mii_parse_88E1011_psr},
{miim_end,} },
(structphy_cmd[])
{
/*shutdown*/
{miim_end,}
},
};
需要注意的是,这个数组时uboot的源码中提供的,但是由于PHY与MAC之间接口使用的不同,这个数组中的内容需要根据需要,参考相应PHY的datasheet作出一定的修改。
5tsec_init()
该函数不会在初始化的时候调用,它在每当你使用网口的时候被调用,使用网口,不管是ping,还是tftp。
inttsec_init(structeth_device*dev,bd_t*bd)
{
uinttempval;
chartmpbuf[MAC_ADDR_LEN];
inti;
structtsec_private*priv=(structtsec_private*)dev->priv;
volatiletsec_t*regs=priv->regs;
/*初始化MACCFG2和ECNTRL两个寄存器,这两个寄存器非常重要,它们主要是用来是配置MAC对PHY的接口。
在这里先给个初始化的值,默认为GMII。
*/
tsec_halt(dev);
regs->maccfg2=MACCFG2_INIT_SETTINGS;
regs->ecntrl=ECNTRL_INIT_SETTINGS;
/*配置MAC地址。
*/
for(i=0;i { tmpbuf[MAC_ADDR_LEN-1-i]=dev->enetaddr[i]; } tempval=(tmpbuf[0]<<24)|(tmpbuf[1]<<16)|(tmpbuf[2]<<8)| tmpbuf[3]; regs->macstnaddr1=tempval; tempval=*((uint*)(tmpbuf+4)); regs->macstnaddr2=tempval; /*resettheindicestozero*/ rxIdx=0; txIdx=0; /*清除其它的寄存器*/ init_registers(regs); /*启动tsec*/ startup_tsec(dev); /*Ifthere'snolink,fail*/ return(priv->link? 0: -1); } 6startup_tsec() staticvoidstartup_tsec(structeth_device*dev) { inti; structtsec_private*priv=(structtsec_private*)dev->priv; volatiletsec_t*regs=priv->regs; /*初始化BD表基址指针*/ regs->tbase=(unsignedint)(&rtx.txbd[txIdx]); regs->rbase=(unsignedint)(&rtx.rxbd[rxIdx]); /*初始化RXBD*/ for(i=0;i rtx.rxbd[i].status=(RXBD_EMPTY|RXBD_INTERRUPT); rtx.rxbd[i].length=0; rtx.rxbd[i].bufPtr=(uint)NetRxPackets[i]; } rtx.rxbd[PKTBUFSRX-1].status|=RXBD_WRAP; /*初始化TXBD*/ for(i=0;i rtx.txbd[i].status=0; rtx.txbd[i].length=0; rtx.txbd[i].bufPtr=0; } rtx.txbd[TX_BUF_CNT-1].status|=TXBD_WRAP; /*又要去找phy_info数组了,这次调用的是startup中的命令和函数*/ if(priv->phyinfo) phy_run_commands(priv,priv->phyinfo->startup); /*根据PHY的Copper侧值配置MAC寄存器*/ adjust_link(dev); /*使能MACCFG1中的发送接收使能*/ regs->maccfg1|=(MACCFG1_RX_EN|MACCFG1_TX_EN); /*让DMA知道可以准备搬运了这里的DMA是ETSEC内部的,并不是CPU中的DMA单元。 */ regs->dmactrl|=DMACTRL_INIT_SETTINGS; regs->tstat=TSTAT_CLEAR_THALT; regs->dmactrl&=~(DMACTRL_GRS|DMACTRL_GTS); } 参照上面的phy_info数组的startup中的内容得知这里phy_run_commands(priv,priv->phyinfo->st
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 千兆网口Freescale ETSEC Marvell 88E1111 uboot Linux 驱动分析 千兆 Freescale 88 E1111 驱动 分析
链接地址:https://www.bdocx.com/doc/9056574.html