DM9000 uboot驱动分析

今天看了一下DM9000,下面对其工作原理及驱动程序进行分析。

DM9000是一款简单的网卡芯片,内部实现MAC && PHY,阅读芯片手册和原理图应该明白DM9000的寄存器基址,基本寄存器的功能。在2440中不同的片选有不同的地址空间(不明白的搜ngcs),加上DM9000内部的0x300便宜就形成了DM9000的寄存器地址。


几个要点:

1、DM9000寄存器访问。

以DM9000_iow(DM9000_NCR, NCR_RST)为例,向NCR寄存器写入NCR_RST值,跟踪代码发现其实现方法为:(#define DM9000_outb(d,r) ( *(volatile u8 *)r = d ))

static void
DM9000_iow(int reg, u8 value)
{
	DM9000_outb(reg, DM9000_IO);
	DM9000_outb(value, DM9000_DATA);
}

向IO中写入寄存器地址,向DATA中写入数据。IO为DM9000基址,DATA为其后4字节。

上面是写寄存器的操作,读寄存器类似:

static u8
DM9000_ior(int reg)
{
	DM9000_outb(reg, DM9000_IO);
	return DM9000_inb(DM9000_DATA);
}

向IO中写入寄存器地址,读出DATA中的值即可。

注:其实上面的这种操作是以硬件连线为基础的,如果硬件没有按照常规的连接方式,DM9000的读写引脚没有与2440的读写引脚相连那么我们就得使用电平模拟的方式了(知道就好,这么费力不讨好的事情是没人做的,有时候想想都觉得费劲)。

2、PHY寄存器的访问

通过读芯片手册可以明白PHY寄存器是不能直接访问的,我们通过MAC中的相关寄存器来访问PHY寄存器。

2.1:写

1,将PHY的寄存器地址写入EPAR(EEPROM && PHY ADDRESS REGISTER);

2,将数据写入EPDRL && EPDRH(EEPROM && PHY DATA REGISTER);

3,写EPCR(EEPROM && PHY CONTROL REGISTER)为0xa;注意清0哦

2.2:读

1,将PHY的寄存器地址写入EPAR(EEPROM && PHY ADDRESS REGISTER);

2,写EPCR(EEPROM && PHY CONTROL REGISTER)为0xc;注意清0哦

3,EPDRL && EPDRH(EEPROM && PHY DATA REGISTER)读出数据

DM9000对应的驱动程序对应于uboot目录下的drivers/dm9000x.c和dm9000x.h。实现的主要功能函数包括,eth_init,eth_send,eth_rx,eth_halt。


eth_init主要实现的功能包括:

dm9000_reset();            //复位就是寄存器赋值
dm9000_probe();          //探测就是看看ID


/* NIC Type: FASTETHER, HOMERUN, LONGRUN */
identify_nic();                 //对PHY的判断,用处没看到,可能有用,填充了boardinfo结构体             


/* GPIO0 on pre-activate PHY */
DM9000_iow(DM9000_GPR, 0x00);/*REG_1F bit0 activate phyxcer */         //本人觉得可以去掉,还没在板子上验证。


/* Set PHY */
set_PHY_mode();                                       //设置自适应模式,激活PHY

                          /*以下是对寄存器的一些设置好些都是默认值,对照手册看一下就行,明白的就明白了,不明白的你也别较真儿*/
/* Program operating register */
DM9000_iow(DM9000_NCR, 0x0);/* only intern phy supported by now */
DM9000_iow(DM9000_TCR, 0);/* TX Polling clear */
DM9000_iow(DM9000_BPTR, 0x3f);/* Less 3Kb, 200us */
DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));/* Flow Control : High/Low Water */
DM9000_iow(DM9000_FCR, 0x0);/* SH FIXME: This looks strange! Flow Control */
DM9000_iow(DM9000_SMCR, 0);/* Special Mode */
DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);/* clear TX status */
DM9000_iow(DM9000_ISR, 0x0f);/* Clear interrupt status */

/*下面是MAC地址相关的东西,跟环境变量有关系*/
/* Set Node address */
//HJ_start /*   www.embedsky.net   */
char *tmp = getenv("ethaddr");
char *end;


for (i = 0; i < 6; i++)
{
bd->bi_enetaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0;
if(tmp)
tmp = (*end) ? end+1 : end;
}
//HJ_end /*   www.embedsky.net   */


printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", bd->bi_enetaddr[0],
      bd->bi_enetaddr[1], bd->bi_enetaddr[2], bd->bi_enetaddr[3],
      bd->bi_enetaddr[4], bd->bi_enetaddr[5]);
for (i = 0, oft = 0x10; i < 6; i++, oft++)
DM9000_iow(oft, bd->bi_enetaddr[i]);
for (i = 0, oft = 0x16; i < 8; i++, oft++)
DM9000_iow(oft, 0xff);                             //核心就是这几句,往寄存器写东西


/* read back mac, just to be sure */
for (i = 0, oft = 0x10; i < 6; i++, oft++)
DM9000_DBG("%02x:", DM9000_ior(oft));
DM9000_DBG("\n");
/*MAC地址部分结束*/

/* Activate DM9000 */
DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);/* RX enable */
DM9000_iow(DM9000_IMR, IMR_PAR);/* Enable TX/RX interrupt mask */

eth_send:(参数为包起始地址,数据长度)

DM9000的发送过程比较简单,传递包长度,填充数据,时能发送就可以。

* Move data to DM9000 TX RAM */
data_ptr = (char *) packet;
DM9000_outb(DM9000_MWCMD, DM9000_IO);

#ifdef CONFIG_DM9000_USE_8BIT
/* Byte mode */
for (i = 0; i < length; i++)
DM9000_outb((data_ptr[i] & 0xff), DM9000_DATA);
#endif 
#ifdef CONFIG_DM9000_USE_16BIT
tmplen = (length + 1) / 2;
for (i = 0; i < tmplen; i++)
DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA);
#endif 
#ifdef CONFIG_DM9000_USE_32BIT
tmplen = (length + 3) / 4;
for (i = 0; i < tmplen; i++)
DM9000_outl(((u32 *) data_ptr)[i], DM9000_DATA);
#endif                                                            填充数据分为三种数据宽度模式


/* Set TX length to DM9000 */
DM9000_iow(DM9000_TXPLL, length & 0xff);
DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);                写寄存器长度


/* Issue TX polling command */
DM9000_iow(DM9000_TCR, TCR_TXREQ);/* Cleared after TX complete */    //使能发送


/* wait for end of transmission */
tmo = get_timer(0) + 5 * CFG_HZ;
while (DM9000_ior(DM9000_TCR) & TCR_TXREQ) {
if (get_timer(0) >= tmo) {
printf("transmission timeout\n");
break;
}
}                 //超时检查

eth_rx:

 

 

/* Check packet ready or not */
DM9000_ior(DM9000_MRCMDX);/* Dummy read */                                     //这里写成DM9000_outb(DM9000_MRCMDX, DM9000_IO);更加合适,因为包格式第一个字节

                                                                                                                                              01,下面的注释也说这个值不是0就是1。再说了这就是判断包呢,判断第一个字节就行了。
rxbyte = DM9000_inb(DM9000_DATA);/* Got most updated data */
if (rxbyte == 0)
return 0;


/* Status check: this byte must be 0 or 1 */
if (rxbyte > 1) {
DM9000_iow(DM9000_RCR, 0x00);/* Stop Device */
DM9000_iow(DM9000_ISR, 0x80);/* Stop INT request */
DM9000_DBG("rx status check: %d\n", rxbyte);
}
DM9000_DBG("receiving packet\n");


/* A packet ready now  & Get status/length */
DM9000_outb(DM9000_MRCMD, DM9000_IO);

       /*然后就读就行了有三种模式,先读前四个字节,状态和长度,然后是数据*/
#ifdef CONFIG_DM9000_USE_8BIT
RxStatus = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8);
RxLen = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8);


#endif /*  */
#ifdef CONFIG_DM9000_USE_16BIT
RxStatus = DM9000_inw(DM9000_DATA);
RxLen = DM9000_inw(DM9000_DATA);


#endif /*  */
#ifdef CONFIG_DM9000_USE_32BIT
tmpdata = DM9000_inl(DM9000_DATA);
RxStatus = tmpdata;
RxLen = tmpdata >> 16;


#endif /*  */
DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen);


/* Move data from DM9000 */
/* Read received packet from RX SRAM */
#ifdef CONFIG_DM9000_USE_8BIT
for (i = 0; i < RxLen; i++)
rdptr[i] = DM9000_inb(DM9000_DATA);


#endif /*  */
#ifdef CONFIG_DM9000_USE_16BIT
tmplen = (RxLen + 1) / 2;
for (i = 0; i < tmplen; i++)
((u16 *) rdptr)[i] = DM9000_inw(DM9000_DATA);


#endif /*  */
#ifdef CONFIG_DM9000_USE_32BIT
tmplen = (RxLen + 3) / 4;
for (i = 0; i < tmplen; i++)
((u32 *) rdptr)[i] = DM9000_inl(DM9000_DATA);


#endif /*  */
if ((RxStatus & 0xbf00) || (RxLen < 0x40)
   || (RxLen > DM9000_PKT_MAX)) {
if (RxStatus & 0x100) {
printf("rx fifo error\n");
}
if (RxStatus & 0x200) {
printf("rx crc error\n");
}
if (RxStatus & 0x8000) {
printf("rx length error\n");
}
if (RxLen > DM9000_PKT_MAX) {
printf("rx length too big\n");
dm9000_reset();
}
} else {


/* Pass to upper layer */
DM9000_DBG("passing packet to upper layer\n");
NetReceive(NetRxPackets[0], RxLen);           //传到上层这个函数还没分析应该就是包重组
return RxLen;
}


eth_halt:

        //断电一个,禁止一堆。

phy_write(0, 0x8000);/* PHY RESET */
DM9000_iow(DM9000_GPR, 0x01);/* Power-Down PHY */
DM9000_iow(DM9000_IMR, 0x80);/* Disable all interrupt */
DM9000_iow(DM9000_RCR, 0x00);/* Disable RX */


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值