原文  http://blog.csdn.net/gorilla0123/article/details/5972706

千兆网口 Freescale ETSEC + Marvell 88E1111 uboot Linux 驱动分析 一

分类:  UBOOT  Linux驱动  Linux 内核和文件系统  PowerPC体系结构2010-10-28 20:37  2718人阅读  评论(7)  收藏  举报

在连续两个平台的uboot和Linux系统移植过程中,在千兆网口调试这块都遇到了很大的麻烦。由于寄存器数量庞大,千兆网口MAC和PHY内部结构复杂,MAC和PHY接口种类多,千兆以太网驱动的调试成了系统移植过程中最让人烦心的一个环节。就像火箭队,每次都让球迷无比揪心,不是输的窝囊,就是伤兵满营,现在新赛季又两连败了,打的比勇士还勇士,后场两个比我还瘦的家伙,怎么防守。算了,不扯这么多了,今天要说的是网口MAC+PHY的一些原理和代码分析。(以Freescale的ETSEC和Marvell的88E1111为例。)

1 千兆以太网的物理层

千兆以太网的物理层分为物理编码子层PCS(Physical Coding Sublayer)、物理介质连接子层PMA(Physical Medium Attachment)和物理介质相关子层PMD(Physical Medium Dependent)三层,如下图所示:

其中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层将对这些具体的连接器作出规定。

2 Freescale 的ETSEC与PHY之间的接口

Freescale的MPC8314和P2020都自带了三速以太网控制器ETSEC,可以提供10M,100M,1000M三种速率的接口。当作为以太网时,需要外部的PHY芯片或者Serdes设备与其相连接。每个ETSEC都支持多标准的MII接口,总体结构如下图所示,可以提供GMII,RGMII,MII,RMII,RTBI,SGMII 六种接口,下图为从MPC8314 datasheet中截取的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模块完成。

3 BD表结构

在千兆以太网的驱动中,现在一般都使用一个叫BD表的东西来管理MAC层发送和接收的内存区域,如下图所示:

在IMMR映射的寄存器空间中有两组寄存器TBASEn和RBASEn,分别为TxBD Ringn 和 RxBD Ringn的指针。MPC8314的ETSEC允许有8个TxBD Ring和8个RxBD Ring,他们都存放在内存的某个区域中。每个Buffer Descriptor 都是有8个字节构成,两个字节的状态,两个字节的数据长度和四个字节的数据指针,这个指针指向内存的另一块地方,这才是真正存储发送接收数据的地方。Buffer Descriptor必须在网口初始化的时候初始化,并将自己的地址赋给TBASEn和RBASEn。

在网口驱动程序中可以看到,每个BD Ring中的BD数量是可变的(我们设为64),而他们之间并没有指针连接,只是一段连续的空间,顺序下来的,所谓的环只是一个虚拟的概念,在最后一个BD时,需要将BD状态位中的W位(Wrap)置一,表示这是最后一个BD,他的下一个BD就是第一个BD。如下图所示:

下面一节将结合uboot源码分析一下网口初始化以及PHY配置的过程,再下一节会分析内核中的驱动。为什么先说uboot,因为在我看来,驱动程序就是分为两个部分,1 按照Datasheet的说明去配置寄存器,2 添加符合操作系统规范去融入操作系统。在uboot下系统很简单,代码一目了然,所以我们应该在boot下先把寄存器配置好,再去分析复杂的多的内核代码

千兆网口 Freescale ETSEC + Marvell 88E1111 uboot Linux 驱动分析 二

分类:  Linux 内核和文件系统  PowerPC体系结构  UBOOT  Linux驱动2010-10-28 20:45  2770人阅读  评论(9)  收藏  举报

这节分析uboot中的网口驱动代码。

 

1 网口驱动函数列表

 

函数名

函数用途

tsec_initialize()

网口初始化函数

tsec_init()

网口启动函数

tsec_local_mdio_write()

MDIO口写函数

tsec_local_mdio_read()

MDIO口读函数

tsec_send()

网口发送函数

tsec_recv()

网口接收函数

tsec_configure_serdes()

配置TBI PHY的函数

fsl_serdes_init()

Serdes模块初始化函数

init_phy()

PHY初始化函数

adjust_link()

根据PHY状态配置MAC的函数

 

2 tsec_initialize()函数

   

该函数为ETSEC的初始化函数,在该函数中要初始化eth_device结构和私有的tsec_private结构,并初始化PHY。

 

int tsec_initialize(bd_t * bis, int index, char *devname)

{

       struct eth_device *dev;

       int i;      

       struct tsec_private *priv;     

/*为dev分配空间*/

       dev = (struct eth_device *)malloc(sizeof *dev);  

       if (NULL == dev)       

              return 0;     

       memset(dev, 0, sizeof *dev);

 /*为priv分配空间*/

       priv = (struct tsec_private *)malloc(sizeof(*priv));     

       if (NULL == priv)             

              return 0;     

 

       /*从tsec_info 数组中取合适的值去初始化私有结构tsec_private*/

       privlist[num_tsecs++] = priv;     

       priv->regs = tsec_info[index].regs;  //每个tsec寄存器的基址

       priv->phyregs = tsec_info[index].miiregs;     //PHY MDIO读写状态寄存器基址

    /*TBI PHY的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;     

       /*设置当前活跃的网口名相当于 set ethact eTSECn,将多个网口级联*/

eth_register(dev);

 

       /* 通过设置MACCFG1寄存器重启 MAC */      

       priv->regs->maccfg1 |= MACCFG1_SOFT_RESET;      

       udelay(2);  /* Soft Reset must be asserted for 3 TX clocks */ 

       priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET);

      

/*挂载MII口的读写函数*/

       #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) /  

              && !defined(BITBANGMII)    

              miiphy_register(dev->name, tsec_miiphy_read, tsec_miiphy_write);

       #endif  

 

       /* 初始化PHY,并返回 */      

       return init_phy(dev);

}

 

3 init_phy()

 

static int init_phy(struct eth_device *dev)

{

       struct tsec_private *priv = (struct tsec_private *)dev->priv;  

       struct phy_info *curphy;

       volatile tsec_t *regs = priv->regs;    

 

       /*在TBIPA的寄存器中写入TBI PHY的地址*/    

       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: No PHY found/n", dev->name);           

              return 0;     

              }

 

    /*如果是SGMII的接口,需要使用TBI PHY,初始化TBI PHY,注意这里名字竟然叫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);     

    return 1;

}

 

4 phy_info结构

 

Uboot中使用这个结构来完成phy的操作,所有的phy都要使用这个结构表示,下面是88E1111的phy_info结构:

struct phy_info phy_info_M88E1111S = {

           0x01410cc,  // PHY ID

              "Marvell 88E1111S",       // PHY名称

              4,   

              (struct phy_cmd[])

              {     

              /* 配置数组,在调用priv->phyinfo->config时将依次调用下面的内容,每个大括号内,第一个为PHY寄存器地址,第二个为value*/

              /* Reset and configure the PHY */            

              {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL},            

              /* Delay RGMII TX and RX */         

              {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,}

              },    

              (struct phy_cmd[])

              {     

              /* 启动数组,在ETSEC启动的时候要依次调用。 */             

              /* Status is read once to clear old link state */            

              {MIIM_STATUS, miim_read, NULL},           

              /* Auto-negotiate */         

              {MIIM_STATUS, miim_read, &mii_parse_sr},         

              /* Read the status */       

              {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr},       

              {miim_end,} },    

              (struct phy_cmd[])

              {     

              /* shutdown */          

              {miim_end,}

              },

};

需要注意的是,这个数组时uboot的源码中提供的,但是由于PHY与MAC之间接口使用的不同,这个数组中的内容需要根据需要,参考相应PHY的datasheet作出一定的修改。

 

5 tsec_init()

 

    该函数不会在初始化的时候调用,它在每当你使用网口的时候被调用,使用网口,不管是ping,还是tftp。

 

int tsec_init(struct eth_device *dev, bd_t * bd)

{

       uint tempval;    

       char tmpbuf[MAC_ADDR_LEN];  

       int i;      

       struct tsec_private *priv = (struct tsec_private *)dev->priv;  

       volatile tsec_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 < MAC_ADDR_LEN; 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;  

       /* reset the indices to zero */      

       rxIdx = 0;    

       txIdx = 0;     

       /* 清除其它的寄存器 */   

       init_registers(regs);

       /* 启动tsec */    

       startup_tsec(dev);  

       /* If there's no link, fail */      

       return (priv->link ? 0 : -1);

}

 

6 startup_tsec()

 

static void startup_tsec(struct eth_device *dev)

{

       int i;

       struct tsec_private *priv = (struct tsec_private *)dev->priv;

       volatile tsec_t *regs = priv->regs;

 

       /* 初始化BD表基址指针 */

       regs->tbase = (unsigned int)(&rtx.txbd[txIdx]);

       regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]);

 

       /* 初始化RX BD*/

       for (i = 0; i < PKTBUFSRX; 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;

 

       /*初始化TX BD*/

       for (i = 0; i < TX_BUF_CNT; 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->startup)要调用两个函数mii_parse_sr和mii_parse_88E1011_psr。

 

这两个函数主要是配置三个重要的priv结构体中的成员

priv->link

priv->speed

priv-> duplexity

分别是link状态,速率和双工。具体的代码就不分析了,主要是读PHY的Copper侧寄存器,然后根据寄存器的值去配置这三个成员,在后面的adjust_link函数中会根据这三个成员的值去配置MAC的MACCFG2和ECNTRL寄存器。

千兆网口 Freescale ETSEC + Marvell 88E1111 uboot Linux 驱动分析相关推荐

  1. 88E1111与千兆网口连接

    88E1111是一款功能强大的网卡芯片,使用的也非常广泛,前段时间一块板子上用到这一块88E1111,硬件做好了之后,运行网络通信程序就是无法通信,因为是DSP连接88E1111到千兆网口,DSP端可 ...

  2. 千兆网口、光口调试总结

     目录(?) [+] 千兆网口光口调试总结 配置 6096端 ARM端 PCS层所处位置极其意义 调试过程 iperf性能测试 千兆网口.光口调试总结 配置 6096端: 工作模式的配置方式: 1 ...

  3. 千兆以太网PHY芯片调试-88E1111(RGMII接口-数据收发ECHO测试) Verilog实现python测试

    千兆以太网PHY芯片调试-基于RGMII接口的88E1111(数据收发ECHO测试) 先放结果: Py测试代码: import socket #网络通信 TCP,UDP DST_IP = '192.1 ...

  4. 16光8电全千兆宽温工业交换机16千兆光8千兆网口机架式网管型工业级以太网交换机

    8*1000M路以太网电口,16路千兆光接口,支持SNMP网管,18ms内自愈环网保护,工作温度:-40℃-85℃.相对湿度:95% ±3RH(无凝结).传输距离40km(其它数据接入共用,实现数据上 ...

  5. 4个万兆光口+8个千兆combo光电复用口+16个千兆网口管理型万兆机架式工业级以太网交换机

    1,数据控制:支持802.3X全双工流控,支持网络风暴抑制 2,冗余网络:支持STP/RSTP/MSTP,支持符合G.8032(ERPS)标准的以太环网保护技术(自愈时间<30ms),保障网络的 ...

  6. 服务器网口修改为百兆,服务器千兆网口能否设置为百兆

    服务器千兆网口能否设置为百兆 内容精选 换一换 云解析服务的内网域名功能支持创建顶级域名(com保留域名除外).当用户购买了弹性云服务器并设置了云服务器的主机名,例如hostname.用户可以在云解析 ...

  7. 【Linux-Windows】千兆网口以及千兆网线

    [Linux-Windows]千兆网口以及千兆网线 1.背景 2.千兆网口 3.千兆网线 1.背景 千兆网线网口顾名思义,接收信号的最大速度每秒1000Mbit的数据! 主义单位是bit,即位,区别于 ...

  8. 简单使用gige千兆网口basler工业相机

    1,安装basler相机的驱动程序,选择安装pylonc.net 2,创建c#程序,项目添加引用pylonc.net.dll动态库 3,最简单使用gige千兆网口basler工业相机 a,using ...

  9. Y6000五口千兆网口5G工业路由器

    Y6000系列5G路由器 Y6000系列路由器是云联芯技有限公司基于无线网络需求,采用最新高性能硬件系统平台,使用OpenWRT(Linux)软件系统引用最新技术研发出来的一款全新的,性能更为优异的物 ...

最新文章

  1. 关于文档的基本操作---ElasticSearch
  2. 数据库引索的简单了解
  3. RTP/RTCP协议与RTSP协议
  4. 一定要知道的,那些Linux基本操作命令
  5. sendmessage()模拟鼠标点击
  6. Linux中创建 静态库和动态库(共享库) 本人亲测可行
  7. 文献笔记4 water volume variations
  8. 华为宣布了,鸿蒙 OS 2.0 开放源代码
  9. String类基本介绍及常用方法
  10. 百度 php 图片文字识别,使用百度接口实现图片识别文字
  11. 基于区块链的数字藏品管控方案
  12. 关于win 10电脑连接手机热点自动断开的问题
  13. 网络红人斌少最新资料
  14. flappy+bird+android源代码,Flappy Bird(安卓版)逆向分析(一)
  15. UNREAL ENGINE 4.12 正式发布!下载地址
  16. 【Python中字典的len()方法】
  17. 西工大机考《大学英语2》大作业网考
  18. nport串口服务器原理,MOXA串口服务器NPORT-5130详细配置
  19. 【现代卫星导航系统】之 GPS 卫星导航系统
  20. python四维数组代表图像_【Python进阶】你真的明白NumPy中的ndarray吗?

热门文章

  1. 时间序列论文: NeuralProphet: Explainable Forecasting at Scale
  2. 2022-2028年中国智慧社区建设行业市场专项调研及投资前景研究报告
  3. iOS 开发资源汇总 肯定有你想要的资源(Continuously updated)
  4. threejs导入简单地图模型
  5. 股票入门:什么是股票指数,什么是大盘指数
  6. 弘扬文明之风,彰显正能量!中山公园职工平凡岗位上的不凡坚守!
  7. 发现苹果的MacOS支持图片OCR文字识别
  8. Spring(https://www.zhihu.com/question/38597960)
  9. 什么是云存储?它的优势在哪里
  10. axios.all与Promise.all并发请求