Arm9+linux fl2440 dm9000网卡 驱动移植和分析

来源:互联网 发布:英文名起名软件 编辑:程序博客网 时间:2024/05/05 16:19

一、修改内核配置

[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);   /* 设置中断屏蔽寄存器 */  

 

在这个函数的最后有个位置需要修改:

  1. if (!is_valid_ether_addr(ndev->dev_addr)) {  
  2.        /* try reading from mac */  
  3.                   
  4.        mac_src = "chip";  
  5.        for (i = 0; i < 6; i++)  
  6.         //ndev->dev_addr[i] = ior(db, i+DM9000_PAR);   
  7.          ndev->dev_addr[i] = ne_def_eth_mac_addr[i];  
  8. }
三、修改arch/arm/mach-s3c2440/mach-smdk2440.c ,添加设备

  1. static struct platform_device *smdk2440_devices[] __initdata = {  
  2.         &s3c_device_ohci,  
  3.         &s3c_device_lcd,  
  4.         &s3c_device_wdt,  
  5.         &s3c_device_i2c0,  
  6.         &s3c_device_iis,  
  7.         &s3c_device_rtc,  
  8.         &s3c24xx_uda134x,  
  9.         &s3c_device_dm9000,  
  10. };


四、修改 arch/arm/plat-s3c24xx/devs.c  
添加头文件

#include <linux/dm9000.h>

添加以下代码

  1. static struct resource s3c_dm9000_resource[] = {   
  2.         [0] = {   //此空间存放的是要发送的地址
  3.         .start = S3C24XX_PA_DM9000,   //实际地址 0x20000300   
  4.         .end   = S3C24XX_PA_DM9000+ 0x3, // 0x20000303
  5.         .flags = IORESOURCE_MEM   //资源标志位地址资源
  6.         },   
  7.         [1]={   //此空间存放的是要发送的数据
  8.         .start = S3C24XX_PA_DM9000 + 0x4, //CMD pin is A2   
  9.         .end = S3C24XX_PA_DM9000 + 0x4 + 0x7c,   //0x20000380
  10.         .flags = IORESOURCE_MEM   //资源标志位地址资源
  11.         },   
  12.         [2] = {   
  13.         .start = IRQ_EINT7, // 中断为外部7号中断 
  14.         .end   = IRQ_EINT7,   // 中断为外部7号中断  
  15.         .flags = IORESOURCE_IRQ   //标志为中断资源
  16.         },   
  17.         };   
  18.    
  19.         static struct dm9000_plat_data s3c_device_dm9000_platdata = {   
  20.         .flags= DM9000_PLATF_16BITONLY,   //传送数据总线为16位宽度
  21.         };   
  22.    
  23.         struct platform_device s3c_device_dm9000 = {   
  24.         .name= "dm9000",   
  25.         .id= 0,   
  26.         .num_resources= ARRAY_SIZE(s3c_dm9000_resource),   
  27.         .resource= s3c_dm9000_resource,   
  28.           .dev= {   
  29.         .platform_data = &s3c_device_dm9000_platdata,   
  30.           }   
  31. };   
  32. EXPORT_SYMBOL(s3c_device_dm9000);
      网络对于嵌入式系统来说必不可少。可是s3c2440没有集成以太网接口,所以要想使s3c2440具备以太网的功能,就必须扩展网卡接口。在这里,我们外接DM9000,使其可以与以太网相连接。
       DM9000可以直接与ISA总线相连,也可以与大多数CPU相连。在这里,我们当然是要让DM9000与s3c2440相连接了。

      其实DM9000只有2个口,命令口和数据口,由CMD决定,至于你说的300基地址,是当有多个DM9000挂在同一总线上时用来区分它们的,如果你只挂了一个DM9000,就可以直接把DM9000的高地址相应位接地或VCC,低地址位用来区分寄存器地址
     “DM9000对外来说只有两个端口——地址口和数据口,地址口用于输入内部寄存器的地址,而数据口则完成对某一寄存器的读写。DM9000的CMD引脚用来区分这两个端口,当CMD引脚为0时,DM9000的数据线上传输的是寄存器地址,当CMD引脚为1时,传输的是读写数据。我们把DM9000的AEN接到s3c2440的nGCS4引脚上,则DM9000的端口基址为0x20000300,如果再把DM9000的CMD引脚接到s3c2440的ADDR2引脚上”
 
     我们所说的地址: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的地址线,他的地址只根驱动写地址命令里面的数据有关。


        这样我们把要发送的dm9000的地址信息固定放在了0x20000300,把要存放在该地址的数据存放在0x2000 0304。(因为地址和数据线复用,所以地址和数据需要分两次传送),即:数组【0】是dm9000要取的地址,数组【1】存放dm9000地址对应要存放的数据。数组[2]外部中断号 
      .end= MACH_MINI2440_DM9K_BASE + 3,为什么尾地址是+3? 
      因为一个地址需要四个字节。   
      数组【1】.start= MACH_MINI2440_DM9K_BASE + 4,为什么? 
      注意这个地址是不能随便选的,必须根据数组【0】.start地址和硬件连接情况进行对应的。
      为什么MACH_MINI2440_DM9K_BASE定义的时候addr2为能为0.? 
       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,此空间存放的是要发送的地址 
       .end = MACH_MINI2440_DM9K_BASE + 3, .flags = IORESOURCE_MEM }, 
        [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行附近,添加
extern struct platform_device s3c_device_dm9000; 

六、修改arch/arm/mach-s3c2410/include/mach/map.h 文件

/* DM9000 */ 

#define   S3C24XX_PA_DM9000 0x20000300 

#define   S3C24XX_VA_DM9000 0xE0000000

0 0
原创粉丝点击