一、修改内核配置
[shaocongshuai@localhost linux-3.0.2]$ make menuconfig
Device Drivers ---> [*] Network device support --->[*] Ethernet (10 or 100Mbit) ---> <*> DM9000 support
二、修改 drivers/net/dm9000.c 文件:
头文件增加:
#include <mach/regs-gpio.h>
#include <mach/irqs.h>
#include <mach/hardware.h>
在dm9000_probe 函数 开始处增加:
unsigned char ne_def_eth_mac_addr[]={0x00,0x12,0x34,0x56,0x80,0x49};
/* 设定默认的mac地址,这个是随便设(测试用)uboot阶段跟Linux阶段是两个阶段,uboot阶段是一个程序,他有自己的IP、mac。一旦启动Linux时,uboot不再运行,加载Linux配置自己的IP和mac */
static void *bwscon; /* 保存ioremap返回的寄存器的虚拟地址,下同 */
static void *gpfcon;
static void *extint0;
static void *intmsk;
#define BWSCON (0x48000000) // Bus Width & Wait Status Control
#define GPFCON (0x56000050) //Port F Control
#define EXTINT0 (0x56000088) //External Interrupt Control Register 0
#define INTMSK (0x4A000008)
/* Determine which interrupt source is masked. The masked
interrupt source will not be serviced.
0 = Interrupt service is available.
1 = Interrupt service is masked.*/
bwscon=ioremap_nocache(BWSCON,0x0000004);
/*ioremap_nocache 把内存映射到CPU空间 void __iomem * ioremap_nocache (unsigned long phys_addr, unsigned long size); phys_addr 要映射的物理地址 size 要映射资源的大小调用ioremap_nocache()函数之后,返回一个线性地址,此时CPU 可以访问设备的内存(已经将其映射到了线性地址空间中了),此时CPU可以使用访问内存的指令访问设备的内存空间(host bridge 判断访问物理内存还是设备中的内存),此时我们就可以像访问内存一样来访问设备的内存(寄存器)。*/
gpfcon=ioremap_nocache(GPFCON,0x0000004);
extint0=ioremap_nocache(EXTINT0,0x0000004); //32位地址线,每次地址以4个字节为单位增加
intmsk=ioremap_nocache(INTMSK,0x0000004);
其中BWSCON为总线宽度 等待控制寄存器
其中第[19:18]位的作用如下
下面函数中将两位设置为11,也就是WAIT使能,bank4使用UB/LB。UB/LB是高/低字节选通线,在16位宽的数据线上分开访问高/低字节时要用到。
(1)nwe位写使能信号;(2)nwbe 写字节使能信号 ,而nBE 为高/低字节选择信号。nWBE与nBE共用引脚,可以通过对相关寄存器设置来进行功能选择
nWE和nWBE都带有写使能的功能。但既然有nWE,为什么还需要nWBE?这是因为,当使用几片储存芯片进行数据位扩展时,有时需要对芯片分开写数据,此时可使用nWBE。
仅有一片8bit的ROM,因此仅需要nWE,而不需要nWBE。用了2片8bit的ROM,如果不使用nWBE,则写操作是对2片ROM同时进行的,这样当执行写字节指令时可能会 破坏另一芯片中的数据。(注意nWBE的信号是自动产生的。)从这个角度来说,nWBE有字节数据屏蔽的功能(这里nGCSn必须是相同的,区别高低位由nWBEx来决定)。
writel(readl(bwscon)|0xc0000,bwscon); /* 将BWSCON寄存器[19:18]设置为11 */
writel( (readl(gpfcon) & ~(0x3 << 14)) | (0x2 << 14), gpfcon); /* 设置GPF寄存器 */
/*GPF7 [15:14] 00 = Input 01 = Output 10 = EINT[7] 11 = Reserved*/
writel( readl(gpfcon) | (0x1 << 7), gpfcon); // Disable pull-up,不使能上拉
writel( (readl(extint0) & ~(0xf << 28)) | (0x4 << 28), extint0); //rising edge,设置上升沿触发中断
writel( (readl(intmsk)) & ~0x80, intmsk); /* 设置中断屏蔽寄存器 */
在这个函数的最后有个位置需要修改:
- if (!is_valid_ether_addr(ndev->dev_addr)) {
- /* try reading from mac */
- mac_src = "chip";
- for (i = 0; i < 6; i++)
- //ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
- ndev->dev_addr[i] = ne_def_eth_mac_addr[i];
- }
- static struct platform_device *smdk2440_devices[] __initdata = {
- &s3c_device_ohci,
- &s3c_device_lcd,
- &s3c_device_wdt,
- &s3c_device_i2c0,
- &s3c_device_iis,
- &s3c_device_rtc,
- &s3c24xx_uda134x,
- &s3c_device_dm9000,
- };
四、修改 arch/arm/plat-s3c24xx/devs.c
添加头文件
#include <linux/dm9000.h>
添加以下代码
- static struct resource s3c_dm9000_resource[] = {
- [0] = { //此空间存放的是要发送的地址
- .start = S3C24XX_PA_DM9000, //实际地址 0x20000300
- .end = S3C24XX_PA_DM9000+ 0x3, // 0x20000303
- .flags = IORESOURCE_MEM //资源标志位地址资源
- },
- [1]={ //此空间存放的是要发送的数据
- .start = S3C24XX_PA_DM9000 + 0x4, //CMD pin is A2
- .end = S3C24XX_PA_DM9000 + 0x4 + 0x7c, //0x20000380
- .flags = IORESOURCE_MEM //资源标志位地址资源
- },
- [2] = {
- .start = IRQ_EINT7, // 中断为外部7号中断
- .end = IRQ_EINT7, // 中断为外部7号中断
- .flags = IORESOURCE_IRQ //标志为中断资源
- },
- };
- static struct dm9000_plat_data s3c_device_dm9000_platdata = {
- .flags= DM9000_PLATF_16BITONLY, //传送数据总线为16位宽度
- };
- struct platform_device s3c_device_dm9000 = {
- .name= "dm9000",
- .id= 0,
- .num_resources= ARRAY_SIZE(s3c_dm9000_resource),
- .resource= s3c_dm9000_resource,
- .dev= {
- .platform_data = &s3c_device_dm9000_platdata,
- }
- };
- EXPORT_SYMBOL(s3c_device_dm9000);
DM9000可以直接与ISA总线相连,也可以与大多数CPU相连。在这里,我们当然是要让DM9000与s3c2440相连接了。
我们所说的地址:0x20000000 和 0x20000004 是由ARM芯片的地址引脚决定的,注意,这个地址表示是用的16进制,那么,0x20000004的每一位的值都可以是0~f,它由ARM的4根地址线的电平高,比如最低的一 位,就是由 ADDR3~ADDR0 这4个引脚的电平决定,如果 ADDR3~ADDR0 = 1111,则该位为f ,如果 LADDR3~LADDR0 = 0100,则该位为 4
因此,如果将 DM9000的CMD引脚接到s3c2440的ADDR2,由于CMD引脚的高低电平决定地址口和数据口,那么,ADDR2为0时,访问的就是地址口,所以地址口的起始地址为 ARRD2为0的情况,即0x20000000 ;ADDR2为1 时,(LADDR3~LADDR0 = 0100)访问的就是数据口,所以数据口的地址即 0x20000004。
看到没?就是CMD这个引脚,dm9000数据手册上有这样一句:
Command Type
When high, the access of this command cycle is DATA port
When low, tha access of this command cycle is ADDRESS port
这个CMD引脚为高电平时,是数据端口,CMD为低电平时,位地址端口。而我的开发板上CMD口接的是LADDR2,也就是第三根地址线,在bank4中,当地址小于四个字节时,该引脚为低电平,使用地址端口,而在大于四个字节而小于八个字节时是高电平,使用数据端口,这样,我们就可以很清楚的知道了,resource[0]就是定义了地址端口,而resource[0]则定义了数据端口,更进一步,只要以8为倍数的bank4的地址为一组,都可以用来使用,只要保证期间LADDR2只变化一次。看后面的例子时,这点将予以体现。
所以地址写成0x20000000-------0x20000000+128M之间都是可以的,但是注意:addr2必须为0,因为dm9000是地址线和数据线复用的, 它根本没有用到cup的地址线,他的地址只根驱动写地址命令里面的数据有关。
Addr2即是Cup的地址线addr0-----addr26中的一员: 1、它的任何一位变化都会使得当前CPU访问的地址发生变化。 2、他连接着dm9000的数据/地址选通引脚,即:addr2=0则写的是地址,addr2=1则写的是数据。 因为当我们的基址是:0x2000 0300的时候,我们现在要写数据了,这时候把addr2置高电平也就是1就开始写数据了,同时cup的访问地址发生改变,变成0x20000304了。(这样做有一个好处,就是我们要写地址了就写到0x2000 0300,要写数据了就写到0x20000300+4,而不需要特意的去改变选通脚状态。不需要写地址的时候特意置0,在写数据的时候特意置1,因为我们访问方式改变的时候选通也会随着改变。) 反过来,我们要设这个地址存放是dm9000的地址还是数据时要考虑addr2的情况。也就MACH_MINI2440_DM9K_BASE这个地址可以设addr2=0的,0x20000000-------0x20000000+128M的任何一个地址。当然不同的开发板可能会不一样,我们硬件可以换成addr2----addr26中的任何一位。 (addr0,addr1,因为一个数据需要四个字节存放,所以这两位不能做为选通引脚,不然两个存放地址就有重叠的地方了,如果一个数据是一个字节大小则可以)
比如说:我们硬件设定addr3为dm9000读写选通引脚,MACH_MINI2440_DM9K_BASE就设成0x20000000,那么可以这样写: [0] = {
.start = MACH_MINI2440_DM9K_BASE, //addr3=0,此空间存放的是要发送的地址
[1] = {
.start = MACH_MINI2440_DM9K_BASE + 8, //addr3=1,此空间存放的是要发送的数据 .end = MACH_MINI2440_DM9K_BASE + 11, .flags = IORESOURCE_MEM },
五、修改 arch/arm/plat-samsung/include/plat/devs.h 45行附近, 添加
六、修改arch/arm/mach-s3c2410/include/mach/map.h 文件
/* DM9000 */
#define S3C24XX_PA_DM9000 0x20000300
#define S3C24XX_VA_DM9000 0xE0000000