DM9000硬件连接分析
s3c2440与DM9000的连接关系如下:
从上图可得出下面几个关键点:
1.中断线与GPF7相连,对应IRQ_EINT7
2.片选与nGCS4相连,基地址对应0x2000_0000
3.CMD线与地址线ADDR2相连,意味着要使CMD高电平,需要ADDR2 = 1,所以往0x20000004中写数据就会拉高CMD。(基地址0x2000_0000,偏移地址0x4,对应二进制"100“,所以此时地址ADDR2=1)
从DM9000数据手册可知:
当CMD为高时,DM9000认为传输的是数据;当CMD为低时,DM9000认为传输的是寄存器地址。
所以在控制DM9000时,往0x20000004地址写命令,往0x20000000地址写寄存器地址。
DM9000时序分析与S3C2440存储控制器分析
DM9000读取时序图
DM9000写时序图:
BWSCON寄存器
ST4 : UB/LB是指一个u16的数据分高低字节两次发送,由于我们是16位数据线可一次发送,所以ST4 = 0
WS4: 硬件没有连接。所以WS4 = 0
DW4: 16位数据线,所以DW4 = 01
BANKCON寄存器
通过分析DM9000读写时序图,设置参数如下:(HCLK=100MHZ, 1个时钟等于10ns)
Tacs :nGCSn前的地址建立时间,观察到DM9000的CS#与CMD为同一根线,所以Tacs = 0
Tcos:发送读命令前的片选建立时间,DM9000C的T1>=0ns,所以Tcos = 0
Tacc:读写信号脉冲时间,DM9000C的T2>=10ns = 1 clock,所以Tacc = 1
Tcoh:当读写信号(IOR# / IOW#)变为高电平后,片选信号还要维持多长时间:
写DM9000C时, IOW#变为高电平之后, 数据线上的数据最小维持3ns,T4 = 3
读DM9000C时, IOR#变为高电平之后, 数据线上的数据最大位置6ns,T4 = 6
所以Tcoh = 1 clock = 10ns
Tcah: nGCS变为高电平后,地址信号维持时间,理由同Tacs。所以Tcah = 0
Tcap: page模式下访问周期,不是用page模式,所以Tcap = 0
PMC:正常模式
网卡驱动框架
1.通过alloc_etherdev()函数分配,初始化net_device,同时分配一段私有数据空间,通过netdev_priv函数指向board_info结构体
2.设置net_device结构体
3.设置board_info结构体,以及MII接口
/* Structure/enum declaration ------------------------------- /
typedef struct board_info {
u32 io_addr; / Register I/O base address,命令寄存器IO基地址 /
u32 io_data; / Data I/O address ,数据寄存器IO地址*/
u8 op_mode; /* PHY operation mode ,PHY操作模式*/
u8 io_mode; /* 0:word, 2:byte ,IO模式,0位字,而2为字节*/
u8 Speed; /* current speed,当前的速度 /
u8 chip_revision;
int rx_csum; / 0:disable, 1:enable,接收计数使能,0为不使能,1为使能 */
u32 reset_counter; /* counter: RESET */
u32 reset_tx_timeout;/* 发送数据超时 */
int tx_pkt_cnt; /* 传输包计数 */
int cont_rx_pkt_cnt; /* 当前接受的数据包计数*/
struct net_device_stats stats; /* 设备传输统计 */
struct timer_list timer; /* 时间列表 */
unsigned char srom[128];
spinlock_t lock; /* 自旋锁 */
struct mii_if_info mii; /* MII总线信息 */
} board_info_t;
4.实现ethtool_ops结构体,该结构体为net_device结构体中的ethtool_ops成员
static struct ethtool_ops dmfe_ethtool_ops = {
.get_drvinfo = dmfe_get_drvinfo,
.get_settings = dmfe_get_settings,
.set_settings = dmfe_set_settings,
.get_link = dmfe_get_link,
.nway_reset = dmfe_nway_reset,
};
5.实现net_device_ops结构体,该结构体为net_device结构体中的netdev_ops成员
static const struct net_device_ops dm9k_netdev_ops = {
.ndo_open = dmfe_open, //打开DM9000调用的函数
.ndo_stop = dmfe_stop,
.ndo_start_xmit = dmfe_start_xmit, //发送包函数
.ndo_tx_timeout = dmfe_timeout, //当watchdog超时时调用该函数。
.ndo_set_rx_mode = dm9000_hash_table,//设置DM9000的组播地址
.ndo_do_ioctl = dmfe_do_ioctl,//dm9000的ioctl实际上是使用了mii的ioctl
.ndo_change_mtu = eth_change_mtu,
.ndo_get_stats = dmfe_get_stats,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
};
6.注册net_device结构体:register_netdev()
DM9000发包分析
DM9000共16k的SRAM中,地址0x0000~0x0BFF(3k)是TX Buffer, 地址0x0C00~0x3FFF(13k)是RX Buffer。发送一个包的具体步骤如下:
Step 1: 检查存储数据宽度。通过读取中断状态寄存器(ISR)的bit[7:6]来确定是8bit,16bit还是32bit。
Step 2: 写数据到TX SRAM中。
Step 3: 写传输长度到TXPLL和TXPLH寄存器中。
Step 4: 设置TXCR寄存器的bit[0]TXREQ来开始发送一个包。
配置使用内核自带DM9000驱动
内核配置
-> Device Drivers
-> Network device support
-> Ethernet driver support
-> DM9000 support
arch/arm/mach-s3c24xx/mach-smdk2440.c 添加资源与平台设备。
#include <linux/dm9000.h>
#define MACH_SMDK2440_DM9K_BASE (S3C2410_CS4 + 0x300)
static struct resource smdk2440_dm9k_resource[] = {
[0] = {
.start = MACH_SMDK2440_DM9K_BASE,
.end = MACH_SMDK2440_DM9K_BASE + 3,
.flags = IORESOURCE_MEM
},
[1] = {
.start = MACH_SMDK2440_DM9K_BASE + 4,
.end = MACH_SMDK2440_DM9K_BASE + 7,
.flags = IORESOURCE_MEM
},
[2] = {
.start = IRQ_EINT7,
.end = IRQ_EINT7,
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
}
};
/*
- The DM9000 has no eeprom, and it’s MAC address is set by
- the bootloader before starting the kernel.
*/
static struct dm9000_plat_data SMDK2440_dm9k_pdata = {
.flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};
static struct platform_device smdk2440_device_eth = {
.name = “dm9000”,
.id = -1,
.num_resources = ARRAY_SIZE(smdk2440_dm9k_resource),
.resource = smdk2440_dm9k_resource,
.dev = {
.platform_data = &SMDK2440_dm9k_pdata,
},
};
将定义好的平台设备添加到初始化数组当中
static struct platform_device *smdk2440_devices[] __initdata = {
…
&smdk2440_device_eth, //添加该句