二十一、Linux驱动之移植DM9000C网卡驱动(下)

    通过二十、Linux驱动之移植DM9000C网卡驱动(上)对厂家提供的网卡驱动程序dm9dev9000c.c的分析,下面将该网卡驱动移植到JZ2440开发板上(内核版本为linux-2.6.22.6)。

1. 硬件分析

    本人使用的开发板是JZ2440CPUS3C2440A,与DM9000C芯片连接如下:

    SD0~15:16位地址、数据线,由CMD引脚决定访问类型。
    CMD:命令线,当CMD为高表示传输的是数据,CMD为低表示传输的是地址
    INT:
中断引脚,接在2440GPF7脚上
    IOR#:
读引脚,接在2440nOE脚上
    IOW#:
写引脚,接在2440nWE脚上
    CS#:
片选,接在2440bank4的片选上面。

1.1 位宽选择

    DM9000C支持两种位宽模式,8位宽和16位宽。选择不同的模式引脚接法不同。如下:


    通过选择DM9000的21号引脚是接上拉电阻还是浮空输入(低电平)来选择位宽模式,对于JZ2440开发板上的DM9000C的21号引脚浮空,选择的是16位宽模式。

1.2 BANK与片选信号

    首先看到有16根数据线(地址线复用),这些数据线同样还连接到了开发板上的其他芯片(如SDRAM、NOR FLASH、NAND FLASH),当想要去读写DM9000C或者操作其他芯片时都在这些I/O引脚上发出信号,我们如何让他们之间不会互相干扰,正确控制自己想要控制的芯片呢?
    S3C2440A有一个存储控制器,如下图:

    接在地址线或数据线上的芯片可能有很多,不同的芯片都会有CE片选引脚,可以手动控制该引脚来选择操作的芯片。              
    S3C2440A帮我们省去了这一操作,S3C2440A通过将可寻址地址(物理地址)划分为8个内存块(每个内存块128MB,8*128MB=(2^27)B,所以S3C2440A地址线共有27根(LADDR0~26)),每个内存块都有一个片选信号(nGCS0~nGCS7),具体每一部分物理地址范围如上图。以第5个内存块物理地址0x2000 0000-0x2800 0000为例,当CPU读写该范围内的物理地址时,CPU会自动将nGCS4引脚拉低,挂在该片选信号线上的芯片就会被选中,本节所讲的DM9000就是挂在该内存块上,所以当CPU发出0x2000 0000-0x2800 0000的物理地址数据时,DM9000就会被选中。从这里也可以看出此时的DM9000C芯片基地址为0x2000 0000。

1.3 地址数据线共用

    对于DM9000C芯片来说,读写地址与数据使用的是同一组16个I/O引脚,DM9000C芯片如何区分CPU读写的是地址还是数据呢?
    可以看到原理图中DM9000C的第32引脚CMD命令信号线连到CPU的地址线LADDR2上,也就是说当CPU发出物理地址(0x2000 0000-0x2800 0000)|0x4时,此时CPU再发出的数据线信号对于DM9000C来说实际是地址信号。例如:
    1. 当在地址0X2000 0000上读写数据时,表示读写的数据是DM9000C的地址
    2. 当访问的地址0X2000 0004上读写数据时,表示读写的数据是DM9000C的数据

1.4 中断信号

    DM9000C芯片的34号引脚INT中断信号引脚连接到2440GPF7,也就是接到CPU的外部中断线7上。当DM9000C收到外部的数据后,会暂存到内部地址中,然后产生一个上升沿中断,等待2440读取数据;当DM9000C2440的数据转发出去后,也会产生一个上升沿中断给2440

1.5 总结

    1. DM9000C芯片物理基地址应设为0x20000000
    2. 中断号为EINT7
    3. 16
位总线宽度

2. 修改代码

2.1 修改代码为标准驱动格式

    1. 添加宏定义“#define MODULE 1”如下:



    2. 修改入口出口函数名,并增加模块装载卸载函数,如下:


2.2 修改硬件相关代码

2.2.1 修改iobase

    iobase是一个全局static变量,也就是网卡的基地址(这里是虚拟地址),例如,在dmfe_probe1()函数中有如下代码:

/*
    #define DM9KS_VID_L		0x28
    #define DM9KS_VID_H		0x29
    #define DM9KS_PID_L		0x2A
    #define DM9KS_PID_H		0x2B
*/

int __init dmfe_probe1(struct net_device *dev)
{
    ... ...

    /*获取ID*/
    outb(DM9KS_VID_L, iobase);        //地址总线输出iobase,数据总线输出0x28
    id_val = inb(iobase + 4);         //地址总线输出iobase+4,读入数据总线数据
    outb(DM9KS_VID_H, iobase);        //地址总线输出iobase,数据总线输出0x29
    id_val |= inb(iobase + 4) << 8;   //地址总线输出iobase+4,读入数据总线数据
    outb(DM9KS_PID_L, iobase);        //地址总线输出iobase,数据总线输出0x2A
    id_val |= inb(iobase + 4) << 16;  //地址总线输出iobase+4,读入数据总线数据
    outb(DM9KS_PID_H, iobase);        //地址总线输出iobase,数据总线输出0x2B
    id_val |= inb(iobase + 4) << 24;  //地址总线输出iobase+4,读入数据总线数据

    ... ...
}

    由硬件分析可知,我们的iobase的物理地址应设为0x20000000
      1. 当CPU地址总线输出iobase0x20000000(物理地址)时,DM9000Ccmd引脚为低电平,此时CPU数据总线输出的0x28对于DM9000来说表示读0x28地址
      2. 当CPU地址总线输出iobase+40x20000004(物理地址)时,DM9000Ccmd引脚为高电平,此时CPU数据总线输入输出对应读写DM9000C
    依次读地址也就是寄存器0x28、0x29、0x2A、0x2B,得到厂家ID和设备ID。所以需要我们根据硬件给它重新赋值(将实际物理地址转换为虚拟地址),在入口函数dm9dev9000c_init中添加代码如下:

iobase = (int)ioremap(0x20000000, 4096);    //重映射物理基地址0x2000 0000

    在出口函数dm9dev9000c_exit中添加代码如下:

iounmap(iobase);

2.2.2 去掉版本判断

    去掉下面dmfe_probe1()函数中这行芯片版本判断代码:   

2.2.3 修改中断

    dm9dev9000c_init函数中,添加修改中断号代码,将irq改为IRQ_EINT7,如下:

irq    = IRQ_EINT7;     //设置中断号为外部中断7

    在上一节二十、Linux驱动之移植DM9000C网卡驱动(上)中分析dmfe_open()函数时提到,申请中断函数时默认的中断触发方式设置不对,应该改为“IRQF_TRIGGER_RISING”,上升沿触发,dmfe_open()函数中修改代码如下:

2.2.4 位宽(这里只做分析,不需要修改代码)

    在dmfe_start_xmit()函数中有如下代码:

/*
    #define DM9KS_DWORD_MODE	1
    #define DM9KS_BYTE_MODE	2
    #define DM9KS_WORD_MODE	0
*/

static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    ... ...

    switch(db->io_mode)    //选择位宽模式
    {
	case DM9KS_BYTE_MODE:    //8-bit位宽模式
		for (i = 0; i < skb->len; i++)
			outb((data_ptr[i] & 0xff), db->io_data);
		break;
	case DM9KS_WORD_MODE:    //16-bit位宽模式
		tmplen = (skb->len + 1) / 2;
		for (i = 0; i < tmplen; i++)
    	outw(((u16 *)data_ptr)[i], db->io_data);
  		break;
	case DM9KS_DWORD_MODE:   //32-bit位宽模式
  		tmplen = (skb->len + 3) / 4;			
		for (i = 0; i< tmplen; i++)
			outl(((u32 *)data_ptr)[i], db->io_data);
		break;
    }

    ... ...
}

    在硬件分析1.1中,我们分析了硬件上如何选择DM9000C位宽模式,如何让CPU知道我们选择了哪种位宽模式呢?在dmfe_init_dm9000()函数中有如下代码:

    这里通过读取DM9KS_ISR(宏定义为0xfe)中断状态寄存器第7位来读取位宽模式,我们可以从DM9000CEP芯片手册中看到:

2.2.4 设置2440相关寄存器

1. 设置BWSCON总线宽度控制寄存器
    因为DM9000C网卡接在2440BANK4上,所以要设置2440BANK4的硬件位宽、时序等。S3C2440A芯片手册有如下:

    可得如下几点信息:
      1. 设置ST4=0,不使用UB/LB(表示高字节与低字节数据是否分开传输)。
      2. 设置WS4=0,其中WAIT引脚为PE4,而我们DM9000C没有引脚接入PE4,所以禁止。
      3. 设置DW4=0x01,我们的DM9000C的数据线为16位。
      4. BWSCON寄存器物理基地址为0x48000000。

2. 设置BANKCON4控制寄存器
    还需要设置BANKCON4控制寄存器。S3C2440A芯片手册有如下:
S3C2440A芯片手册有如下:

    可得如下几点信息:
      1. 设置Tacs=0,因为CSCMD可以同时结束(bank4地址(也就是CMD)稳定多久后,CS片选才启动)。
      2. 设置Tcos=T1=0(CS片选后,多久才能使能读写)。
      3. 设置Tacc=T2>=10ns=1,表示2个时钟 (读写使能后,多久才能访问数据)  。
      4. 设置Tcoh=T4>=3ns=1,表示1个时钟 ,因为当DM9000C的写信号取消后,数据线上的数据还需要至少3ns才消失(nOE读写取消后,片选需要维持多长时间)。
      5. 设置Tcah=0,因为 CSCMD可以同时结束 (CS片选取消后,地址(也就是CMD)需要维持多长时间)。
      6. 设置PMC为0,正常模式。
      7. BANKCON4控制寄存器物理基地址为0x48000014
   
对于该部分硬件相关的代码在dm9dev9000c_init()函数中添加如下红框部分:

   
    dm9dev9000c_init()函数完整修改代码如下:

int __init dm9dev9000c_init(void)
{
	volatile unsigned long *bwscon; // 0x48000000
	volatile unsigned long *bankcon4; // 0x48000014
	unsigned long val;

	iobase = (int)ioremap(0x20000000, 1024);  //重映射基地址
	irq    = IRQ_EINT7;   //修改中断号为IRQ_EINT7

	/* 设置S3C2440的memory controller */
	bwscon   = ioremap(0x48000000, 4);
	bankcon4 = ioremap(0x48000014, 4);

	/* DW4[17:16]: 1,16bit位宽
	 * WS4[18]   : 0,WAIT disable
	 * ST4[19]   : 0,不使用UB/LB
	 */
	val = *bwscon;
	val &= ~(0xf<<16);
	val |= (1<<16);
	*bwscon = val;

	//Tacc[10:8] : 设为1
	//Tcoh[7:6]  : 设为1
	//*bankcon4 = (1<<8)|(1<<6);	/* 对于DM9000C可以设Tacc为1, 对于DM9000E,Tacc要设大一点,比如最大值7  */
	*bankcon4 = (7<<8)|(1<<6);  /* MINI2440使用DM9000E,Tacc要设大一点 */

	iounmap(bwscon);
	iounmap(bankcon4);	
	
	switch(mode) {
		case DM9KS_10MHD:
		case DM9KS_100MHD:
		case DM9KS_10MFD:
		case DM9KS_100MFD:
			media_mode = mode;
			break;
		default:
			media_mode = DM9KS_AUTO;
	}
	dmfe_dev = dmfe_probe();
	if(IS_ERR(dmfe_dev))
		return PTR_ERR(dmfe_dev);
	return 0;
}

2.2.5 添加头文件

    添加相关头文件,代码如下:

#include <asm/delay.h>
#include <asm/irq.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/arch-s3c2410/regs-mem.h>

    这样代码就修改完成了,就下来进行编译测试。

3. 测试

内核:linux-2.6.22.6
编译器:arm-linux-gcc-3.4.5
环境:ubuntu9.10

    1. 将dm9dev9000c.c拷贝到内核的linux-2.6.22.6/drivers/net目录里。
    2. 修改该目录里的Makefile,执行命令如下(我的内核放在/work/system/下):
      vi /work/system/linux-2.6.22.6/drivers/net/Makefile    (修改如下图)
   
    3. 重新编译内核,下载启动,就可以正常使用网卡了。
      make uImage
   
如下挂载网络文件系统启动,说明移植成功了。
   

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值