Dm9000aep在u-boot下网卡驱动调试笔记

来源:互联网 发布:org.json.jsonobject 编辑:程序博客网 时间:2024/05/16 02:07

作者:杨硕,华清远见嵌入式培训中心讲师。

最近在将u-boot1.3.4移植到三星s5pc100 SOC平台发现u-boot启动之后无法ping通主机,也不能用tftp下载内核。硬件平台用的网卡芯片是dm9000aep,这是一款全集成,性价比高的快速以太网MAC控制器,支持8-bit和16-bit数据宽度,带有一个通用处理器接口,EEPROM接口,10M/100M自适应,带有16KB的SRAM(13KB作为接受FIFO,3KB作为发送FIFO)。Dm9000a在嵌入式平台中应用非常广。

因为在开发过程中我们需要经常修改重新编译Linux内核,所以每次改完之后都重新烧写内核到nandflash里面很麻烦。所以一定要解决u-boot对dm9000a网卡的支持问题。

我们首先需要确定硬件上有没有问题。鉴于Linux内核启动之后可以通过nfs挂在根文件系统,并且工作正常,所以可以确定内核对dm9000a的支持是没问题的,也就是说硬件上没有问题。接下来将注意力集中到软件上。

我首先怀疑是u-boot下对dm9000a的驱动有问题。找到驱动源代码:driver/net/dm9000x.c。 在代码一开始的注释中说“Fixed the driver to work with DM9000A”。看了之后很疑惑,既然源码作者都说他的驱动已经支持了DM9000A,那为什么网卡在u-boot下还不能工作?带着半信半疑的态度我进去查看源代码。首先最重要的网卡初始化的代码,在函数eth_init()中,这里面主要是设置网卡的工作模式,数据总线宽度,设置MAC地址并使能网卡等工作。为了确认这段代码是否正确,我又查看了Linux内核中DM9000A的驱动代码dm9000.c,发现两者在关键部分的设置都是一样的,只不过内核里面采用中断的工作模式,而在u-boot中采用轮询的工作模式。如果初始化代码没有问题的话,我继续查看关键的接收以及发送函数,即eth_send()和eth_rx(),发现这两部分也和内核里面是一样的。

这下我就更加困惑了,似乎驱动代码是没有问题的,但是问题出在什么地方呢?为了深入的分析原因,我打开了DM9000a的调试接口宏:define CONFIG_DM9000_DEBUG。然后在超级终端ping我的主机,ping的时候会调用eth_init()激活网卡,终端打印出下面的初始化信息:

Dm90000 i/o : 0x88000300, id : 0x90000a46
        Dm9000 : running in 16 bit mode
        MAC : 11:22:33:44:55:66
        Operating at 100M full duplex mode

看来初始化是没有问题的,可以读出id号这说明s5pc100和dm9000a数据交互肯定没有问题。但是ping还是会失败。之后打印的信息如下所示:

Transmit done
        Receiving packet
        Rx status: 0x0101 rx len: 24158
        Rx fifo error
        Rx length too big

问题出在这里了,我们看到主机回传给开发板的数据包的长度是24158Byte,即23.59KB。从前面的介绍我们知道接收fifo只有13KB,这个长度显然太大了,所以后面有fifo error和length too big的提示。在主机端用抓包软件抓包,发现抓到的来自开发板的数据包都不对,MAC地址并不是板子的MAC地址,Protocol是unknown。

如果驱动没错的话怎么会是这样的结果?这时我想到会不会是总线宽度设置的不正确导致数据流出错?接着通过查看硬件原理图找到dm9000a接到片选1上,也就是s5pc100的SMC Bank 1上。然后在SMC的控制器中查看相应的控制寄存器,找到每个Bank的总线宽度是在SMC_BW寄存器中配置的,其中4~7位是配置Bank 1的。

这时我想到既然dm9000a在内核里面工作正常,那就应该去内核代码里面看看内核是怎么设置的。我在驱动代码里面没有找到设置Bank1的代码。一般来说如果驱动里面不设置的话,那就应该是在平台代码里面做。平台代码位于:arch/arm/mach-s5pc100/mach-smdkc100.c,在平台初始化函数smdkc100_machine_init中,我找到了smdkc100_dm9000_set函数,这个函数里面就是将SMC_BW寄存器里面的4~7位全部设置为1。我们注意这里第四位DataWidth1就是控制数据总线宽度的,设为1就是工作在16位模式。

再回到u-boot中,在u-boot的板级初始化文件:board/smdkc100/smdkc100.c中,我在board_init函数中没有找到初始化dm9000a的代码,在整个文件中也没有。因此可以确定u-boot确实没有设置Bank 1的数据总线宽度,而SMC_BW里面4~7位的默认值都是0,因此上电后默认就是8-bit的数据总线宽度。看来终于找到问题所在了,我在board_init中加了一个dm9000aep_pre_init函数,里面将总线宽度配置为16位的,重新编译u-boot,通过dnw烧写到nandflash里面。重启之后再ping主机,果然就可以ping通了。通过抓包工具我们可以清晰看到开发板和主机之间数据包的交互过程:

最后还有一个问题,每次ping结束后,网线的物理连接就断掉了,也就是说开发板和主机端的网络指示灯都灭了。跟到u-boot的实现ping的源代码里面,发现每次ping结束之前会调用一下eth_halt函数,这个函数会把网卡禁用掉。所以我在dm9000x.c中将eth_halt中禁用网卡的几行代码注释掉,再重新烧写u-boot,问题就可以解决了。

嵌入式及3G相关资源及学习请点击:嵌入式开发视频 android开发视频 android培训 3G培训 QT培训 QT开发视频 物联网培训 物联网技术视频 嵌入式学习