Chinaunix首页 | 论坛 | 博客
  • 博客访问: 790068
  • 博文数量: 264
  • 博客积分: 592
  • 博客等级: 中士
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-24 22:02
文章分类

全部博文(264)

文章存档

2019年(2)

2018年(1)

2017年(1)

2016年(4)

2015年(14)

2014年(57)

2013年(88)

2012年(97)

分类: LINUX

2012-12-09 17:35:49

这节分析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寄存器。

阅读(2428) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~