提交者: a00 日期: 2009/1/20 15:20 阅读: 124
drivers/dm9000x.c eth_init()函数
这里的初始化并不复杂,首先对dm9000进行复位
static void
dm9000_reset(void)
{
DM9000_DBG("resetting\n");
DM9000_iow(DM9000_NCR, NCR_RST);
udelay(1000); /* delay 1ms */
}
这里将NRC寄存器的第0位置1,之后要保持至少20us的延时。这里延时了1ms。
int
dm9000_probe(void)
{
u32 id_val;
id_val = DM9000_ior(DM9000_VIDL);
id_val |= DM9000_ior(DM9000_VIDH) << 8;
id_val |= DM9000_ior(DM9000_PIDL) << 16;
id_val |= DM9000_ior(DM9000_PIDH) << 24;
if (id_val == DM9000_ID) {
printf("dm9000 i/o: 0x%x, id: 0x%x \n", CONFIG_DM9000_BASE,
id_val);
return 0;
} else {
printf("dm9000 not found at 0x%08x id: 0x%08x\n",
CONFIG_DM9000_BASE, id_val);
return -1;
}
}
复位结束后到网卡的vendor ID寄存器和product ID寄存器读取id,检测此网卡是否是dm9000。
static void
identify_nic(void)
{
struct board_info *db = &dmfe_info; /* Point a board information structure */
u16 phy_reg3;
DM9000_iow(DM9000_NCR, NCR_EXT_PHY);
phy_reg3 = phy_read(3);
switch (phy_reg3 & 0xfff0) {
case 0xb900:
if (phy_read(31) == 0x4404) {
db->nic_type = HOMERUN_NIC;
program_dm9801(phy_reg3);
DM9000_DBG("found homerun NIC\n");
} else {
db->nic_type = LONGRUN_NIC;
DM9000_DBG("found longrun NIC\n");
program_dm9802();
}
break;
default:
db->nic_type = FASTETHER_NIC;
break;
}
DM9000_iow(DM9000_NCR, 0);
}
接着是检测网卡类型,是FASTETHER, HOMERUN或LONGRUN类型。这里主要是通过phy_read(3)和phy_read(31)读取PHY 寄存器的值并进行比较判断。读PHY寄存器的方法将在后面介绍。
DM9000_iow(DM9000_GPR, 0x00);
将GPR寄存器的第0位置0。激活内部PHY的功能。
The default status of the DM9000 is to power down the internal PHY by setting the GPIO0.
Since the internal PHY have been powered down, the wakeup procedure will be needed to
enable the DM9000
这是官方文档对这个设置的解释。因为其初始化状态是POWER DOWN。在power down模式下,除了MDC/MDIO管理接口,其他所有的发送、接收及MII接口功能都被禁止。
static void
set_PHY_mode(void)
{
u16 phy_reg4 = 0x01e1, phy_reg0 = 0x1000;
if (!(media_mode & DM9000_AUTO)) {
switch (media_mode) {
case DM9000_10MHD:
phy_reg4 = 0x21;
phy_reg0 = 0x0000;
break;
case DM9000_10MFD:
phy_reg4 = 0x41;
phy_reg0 = 0x1100;
break;
case DM9000_100MHD:
phy_reg4 = 0x81;
phy_reg0 = 0x2000;
break;
case DM9000_100MFD:
phy_reg4 = 0x101;
phy_reg0 = 0x3100;
break;
}
phy_write(4, phy_reg4); /* Set PHY media mode */
phy_write(0, phy_reg0); /* Tmp */
}
DM9000_iow(DM9000_GPCR, 0x01); /* Let GPIO0 output */
DM9000_iow(DM9000_GPR, 0x00); /* Enable PHY */
}
通过写PHY寄存器0和4即BMCR和ANAR来设置phy的工作模式。在我的这个板子上用的是默认的配置。通过写寄存器0使能了dm9000的auto-negotiation状态;通过写寄存器4设置该网卡支持IEEE 802.3 CSMA/CD和其他一些工作模式。
DM9000_iow(DM9000_GPCR, 0x01); /* Let GPIO0 output */
DM9000_iow(DM9000_GPR, 0x00); /* Enable PHY */
这最后两行又将PHY进行了一次重启。先将PHY设置为power down,再使能。
下面是对一些控制和状态寄存器的设置,具体含意可参考dm9000 datasheet.
/* 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 */
/* Set Node address */
for (i = 0; i < 6; i++)
((u16 *) bd->bi_enetaddr)[i] = read_srom_word(i);
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);
这段代码是设置dm9000的MAC地址,选是通过read_srom_word(i)到srom中读取MAC地址值,再分别写入板子信息数据结构bd和dm9000的MAC寄存器中,再将Multicast Address Register寄存器全部置1。
看一下read_srom_word这个函数
static u16
read_srom_word(int offset)
{
DM9000_iow(DM9000_EPAR, offset);
DM9000_iow(DM9000_EPCR, 0x4);
udelay(200);
DM9000_iow(DM9000_EPCR, 0x0);
return (DM9000_ior(DM9000_EPDRL) + (DM9000_ior(DM9000_EPDRH) << 8));
}
这个函数是从srom中读一个word。首先把要读的那个寄存器的偏移地址offset写入EPAR寄存器,然后把读命令0x4写入EPCR寄存器,等待至少150us后,向EPCR寄存器写入0x0,清除读命令,最后从EPDRL和EPDRH寄存器中分别读出数据的低字节和高字节。EPAR是地址寄存器,存入要读/写的寄存器的偏移地址。EPCR是控制寄存器,通过命令控制读/写。读出的数据或要写入的数据放在PEDRL和EPDRH中。
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 */
现在开始使能网卡的接收功能,设置中断屏蔽寄存器。这里IMR_PAR=1000 0000b,将IMR寄存器的位7置“1”,使内存数据读取地址的高字节为0ch,即Memory Data Read_address Register F5。这样设置使得接收的数据从内部内存地址0x0c00处开始存放。
while (!(phy_read(1) & 0x20)) { /* auto-negotiation complete bit */
udelay(1000);
i++;
if (i == 10000) {
printf("could not establish link\n");
return 0;
}
}
读取phy寄存器1,判断auto-negotiation是否完成。我前面贴子说的起动时特别慢原因就在这里。if (i == 10000)
这里判断的次数太多,延长的等待时间。如果auto-negotiation完成的快,这里是多少也就无所谓了。
下面看一下phy_read(1)这个函数
static u16
phy_read(int reg)
{
u16 val;
/* Fill the phyxcer register into REG_0C */
DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);
DM9000_iow(DM9000_EPCR, 0xc); /* Issue phyxcer read command */
udelay(100); /* Wait read complete */
DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer read command */
val = (DM9000_ior(DM9000_EPDRH) << 8) | DM9000_ior(DM9000_EPDRL);
/* The read data keeps on REG_0D & REG_0E */
DM9000_DBG("phy_read(%d): %d\n", reg, val);
return val;
}
这段代码是从PHY寄存器中读取一个字。读取的方法和读srom是一样的。但在细节上有些差别。在写PHY寄存器地址时要保证EPAR的第7、6位的值为01b。这样就选中PHY模式。在往EPCR寄存器中写读命令时要保证其位3为1。这也是为了选中PHY模式。
最后 lnk = phy_read(17) >> 12;
printf("operating at ");
switch (lnk) {
case 1:
printf("10M half duplex ");
break;
case 2:
printf("10M full duplex ");
break;
case 4:
printf("100M half duplex ");
break;
case 8:
printf("100M full duplex ");
break;
default:
printf("unknown: %d ", lnk);
break;
}
printf("mode\n");
读PHY寄存器17,检测其工作模式。
到些网卡初始化结束。