DM9000 uboot驱动分析

来源:互联网 发布:淘宝官方店有假货吗 编辑:程序博客网 时间:2024/04/28 14:16

今天看了一下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 voidDM9000_iow(int reg, u8 value){DM9000_outb(reg, DM9000_IO);DM9000_outb(value, DM9000_DATA);}

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

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

static u8DM9000_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_16BITtmplen = (length + 1) / 2;for (i = 0; i < tmplen; i++)DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA);#endif #ifdef CONFIG_DM9000_USE_32BITtmplen = (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_8BITRxStatus = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8);RxLen = DM9000_inb(DM9000_DATA) + (DM9000_inb(DM9000_DATA) << 8);#endif /*  */#ifdef CONFIG_DM9000_USE_16BITRxStatus = DM9000_inw(DM9000_DATA);RxLen = DM9000_inw(DM9000_DATA);#endif /*  */#ifdef CONFIG_DM9000_USE_32BITtmpdata = 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_8BITfor (i = 0; i < RxLen; i++)rdptr[i] = DM9000_inb(DM9000_DATA);#endif /*  */#ifdef CONFIG_DM9000_USE_16BITtmplen = (RxLen + 1) / 2;for (i = 0; i < tmplen; i++)((u16 *) rdptr)[i] = DM9000_inw(DM9000_DATA);#endif /*  */#ifdef CONFIG_DM9000_USE_32BITtmplen = (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 */


原创粉丝点击