[转]inux3.1.8内核移植到gt2440---CS8900A驱动移植
一. 根据原理图,确认网卡的地址和中断号
地址:0x19000000
中断号: EINT9
二. 修改drivers/net/cs89x0.c,指定CS8900A使用的资源
#elif defined(CONFIG_ARCH_S3C2410)
#include
#include3c2410/regs-mem.h>
#define S3C24XX_PA_CS8900 0x19000000
static unsigned int netcard_portlist[] __initdate = {0, 0};
static unsigned int cs8900_irq_map[] = {IRQ_EINT9, 0, 0, 0};
#else
三. 修改入口函数cs89x0_probe
1.
#if defined(CONFIG_ARCH_S3C2410)
unsigned int oldval_bwscon;
unsigned int oldval_bankcon3;
#endif
2. 设置MAC,总线宽度等:
#if defined(CONFIG_ARCH_S3C2410)if(netcard_portlist[0])return -ENODEV;netcard_portlist[0] = (unsigned int)ioremap(S3C24XX_PA_CS8900, SZ_1M) + 0x300;
dev->dev_addr[0] = 0x08;dev->dev_addr[1] = 0x89;dev->dev_addr[2] = 0x89;dev->dev_addr[3] = 0x89;dev->dev_addr[4] = 0x89;dev->dev_addr[5] = 0x89;
oldval_bwscon = *((volatile unsigned int *)S3C2410_BWSCON);*((volatile unsigned int *)S3C2410_BWSCON) = (oldval_bwscon & ~(3<<12)) | S3C2410_BWSCON_DW3_16 | S3C2410_BWSCON_WS3 | S3C2410_BWSCON_ST3;oldval_bankcon3 = *((volatile unsigned int *)S3C2410_BANKCON3);*((volatile unsigned int *)S3C2410_BANKCON3) = 0x1f7c;
#endif
3. 出错退出
out:
#if defined(CONFIG_ARCH_S3C2410)
iounmap(netcard_portlist[0]);
netcard_portlist[0] = 0;
*((volatile unsigned int *)S3C2410_BWSCON) = oldval_bwscon;
*((volatile unsigned int *)S3C2410_BANKCON3) = oldval_bankcon3;
#endif
free_netdev(dev);
修改后函数cs89x0_probe为:
struct net_device * __init cs89x0_probe(int unit)
{
struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
unsigned *port;
int err = 0;
int irq;
unsigned int io; /*修改*/
/*添加*/
#if defined(CONFIG_ARCH_S3C2410)
unsigned int oldval_bwscon;
unsigned int oldval_bankcon3;
#endif
if (!dev)
return ERR_PTR(-ENODEV);
sprintf(dev->name, "eth%d", unit);
netdev_boot_setup_check(dev);
io = dev->base_addr;
irq = dev->irq;
/*添加*/
#if defined(CONFIG_ARCH_S3C2410)
if(netcard_portlist[0])
return -ENODEV;
netcard_portlist[0] = (unsigned int)ioremap(S3C24XX_PA_CS8900, SZ_1M) + 0x300;
dev->dev_addr[0] = 0x08;
dev->dev_addr[1] = 0x89;
dev->dev_addr[2] = 0x89;
dev->dev_addr[3] = 0x89;
dev->dev_addr[4] = 0x89;
dev->dev_addr[5] = 0x89;
oldval_bwscon = *((volatile unsigned int *)S3C2410_BWSCON);
*((volatile unsigned int *)S3C2410_BWSCON) = (oldval_bwscon & ~(3<<12)) | S3C2410_BWSCON_DW3_16 | S3C2410_BWSCON_WS3 | S3C2410_BWSCON_ST3;
oldval_bankcon3 = *((volatile unsigned int *)S3C2410_BANKCON3);
*((volatile unsigned int *)S3C2410_BANKCON3) = 0x1f7c;
#endif
if (net_debug)
printk("cs89x0:cs89x0_probe(0x%x)\n", io);
if (io > 0x1ff){/* Check a single specified location. */
err = cs89x0_probe1(dev, io, 0);
} else if (io != 0) {/* Don't probe at all. */
err = -ENXIO;
} else {
for (port = netcard_portlist; *port; port++) {
if (cs89x0_probe1(dev, *port, 0) == 0)
break;
dev->irq = irq;
}
if (!*port)
err = -ENODEV;
}
if (err)
goto out;
return dev;
out:
#if defined(CONFIG_ARCH_S3C2410) /*添加*/
iounmap(netcard_portlist[0]);
netcard_portlist[0] = 0;
*((volatile unsigned int *)S3C2410_BWSCON) = oldval_bwscon;
*((volatile unsigned int *)S3C2410_BANKCON3) = oldval_bankcon3;
#endif
free_netdev(dev);
printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
return ERR_PTR(err);
}
四. 注册中断处理程序,指定中断触发方式,修改net_open函数
#if 0
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
#endif
write_irq(dev, lp->chip_type, dev->irq);
#if defined(CONFIG_ARCH_S3C2410)
ret = request_irq(dev->irq, &net_interrupt, IRQF_TRIGGER_RISING, dev->name, dev);
#else
ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev);
#endif
五. 在drivers/net/cs89x0.c中适当的位置加上CONFIG_ARCH_S3C2410宏编译开关,可以参考CONFIG_ARCH_PNX010X:
1. net_open(struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
int result = 0;
int i;
int ret;
2. #if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X) && !defined(CONFIG_ARCH_S3C2410)
if (((1 irq) & lp->irq_map) == 0) {
printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
dev->name, dev->irq, lp->irq_map);
ret = -EAGAIN;
goto bad_out;
}
#endif
3. #if defined(CONFIG_ARCH_PNX010X) || defined(CONFIG_ARCH_S3C2410)
result = A_CNF_10B_T;
#endif
修改后net_open为:
static int
net_open(struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
int result = 0;
int i;
int ret;
if (dev->irq < 2) {
/* Allow interrupts to be generated by the chip */
/* Cirrus' release had this: */
#if 0
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );
#endif
/* And 2.3.47 had this: */
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
for (i = 2; i < CS8920_NO_INTS; i++) {
if ((1 << i) & lp->irq_map) {
if (request_irq(i, net_interrupt, 0, dev->name, dev) == 0) {
dev->irq = i;
write_irq(dev, lp->chip_type, i);
/* writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT); */
break;
}
}
}
if (i >= CS8920_NO_INTS) {
writereg(dev, PP_BusCTL, 0);/* disable interrupts. */
printk(KERN_ERR "cs89x0: can't get an interrupt\n");
ret = -EAGAIN;
goto bad_out;
}
}
else
{
#ifndef CONFIG_CS89x0_NONISA_IRQ
if (((1 << dev->irq) & lp->irq_map) == 0) {
printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
dev->name, dev->irq, lp->irq_map);
ret = -EAGAIN;
goto bad_out;
}
#endif
/* FIXME: Cirrus' release had this: */
writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );
/* And 2.3.47 had this: */
#if 0
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
#endif
write_irq(dev, lp->chip_type, dev->irq);
//ret = request_irq(dev->irq, net_interrupt, 0, dev->name, dev); /*屏蔽掉*/
#if defined(CONFIG_ARCH_S3C2410) /*添加*/
ret = request_irq(dev->irq, &net_interrupt, IRQF_TRIGGER_RISING, dev->name, dev);
#else
ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev);
#endif
if (ret) {
printk(KERN_ERR "cs89x0: request_irq(%d) failed\n", dev->irq);
goto bad_out;
}
}
#if ALLOW_DMA
if (lp->use_dma) {
if (lp->isa_config & ANY_ISA_DMA) {
unsigned long flags;
lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL,
get_order(lp->dmasize * 1024));
if (!lp->dma_buff) {
printk(KERN_ERR "%s: cannot get %dK memory for DMA\n", dev->name, lp->dmasize);
goto release_irq;
}
if (net_debug > 1) {
printk("%s: dma %lx %lx\n",
dev->name,
(unsigned long)lp->dma_buff,
(unsigned long)isa_virt_to_bus(lp->dma_buff));
}
if ((unsigned long) lp->dma_buff >= MAX_DMA_ADDRESS ||
!dma_page_eq(lp->dma_buff, lp->dma_buff+lp->dmasize*1024-1)) {
printk(KERN_ERR "%s: not usable as DMA buffer\n", dev->name);
goto release_irq;
}
memset(lp->dma_buff, 0, lp->dmasize * 1024);/* Why? */
if (request_dma(dev->dma, dev->name)) {
printk(KERN_ERR "%s: cannot get dma channel %d\n", dev->name, dev->dma);
goto release_irq;
}
write_dma(dev, lp->chip_type, dev->dma);
lp->rx_dma_ptr = lp->dma_buff;
lp->end_dma_buff = lp->dma_buff + lp->dmasize*1024;
spin_lock_irqsave(&lp->lock, flags);
disable_dma(dev->dma);
clear_dma_ff(dev->dma);
set_dma_mode(dev->dma, DMA_RX_MODE); /* auto_init as well */
set_dma_addr(dev->dma, isa_virt_to_bus(lp->dma_buff));
set_dma_count(dev->dma, lp->dmasize*1024);
enable_dma(dev->dma);
spin_unlock_irqrestore(&lp->lock, flags);
}
}
#endif/* ALLOW_DMA */
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
/* while we're testing the interface, leave interrupts disabled */
writereg(dev, PP_BusCTL, MEMORY_ON);
/* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */
if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) && (lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
lp->linectl = LOW_RX_SQUELCH;
else
lp->linectl = 0;
/* check to make sure that they have the "right" hardware available */
switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T: result = lp->adapter_cnf & A_CNF_10B_T; break;
case A_CNF_MEDIA_AUI: result = lp->adapter_cnf & A_CNF_AUI; break;
case A_CNF_MEDIA_10B_2: result = lp->adapter_cnf & A_CNF_10B_2; break;
default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2);
}
#if defined(CONFIG_ARCH_PNX010X) || defined(CONFIG_ARCH_S3C2410) /*添加*/
result = A_CNF_10B_T;
#endif
if (!result) {
printk(KERN_ERR "%s: EEPROM is configured for unavailable media\n", dev->name);
release_dma:
#if ALLOW_DMA
free_dma(dev->dma);
release_irq:
release_dma_buff(lp);
#endif
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
free_irq(dev->irq, dev);
ret = -EAGAIN;
goto bad_out;
}
/* set the hardware to the configured choice */
switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T:
result = detect_tp(dev);
if (result==DETECTED_NONE) {
printk(KERN_WARNING "%s: 10Base-T (RJ-45) has no cable\n", dev->name);
if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
}
break;
case A_CNF_MEDIA_AUI:
result = detect_aui(dev);
if (result==DETECTED_NONE) {
printk(KERN_WARNING "%s: 10Base-5 (AUI) has no cable\n", dev->name);
if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
result = DETECTED_AUI; /* Yes! I don't care if I see a carrrier */
}
break;
case A_CNF_MEDIA_10B_2:
result = detect_bnc(dev);
if (result==DETECTED_NONE) {
printk(KERN_WARNING "%s: 10Base-2 (BNC) has no cable\n", dev->name);
if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
}
break;
case A_CNF_MEDIA_AUTO:
writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
if (lp->adapter_cnf & A_CNF_10B_T)
if ((result = detect_tp(dev)) != DETECTED_NONE)
break;
if (lp->adapter_cnf & A_CNF_AUI)
if ((result = detect_aui(dev)) != DETECTED_NONE)
break;
if (lp->adapter_cnf & A_CNF_10B_2)
if ((result = detect_bnc(dev)) != DETECTED_NONE)
break;
printk(KERN_ERR "%s: no media detected\n", dev->name);
goto release_dma;
}
switch(result) {
case DETECTED_NONE:
printk(KERN_ERR "%s: no network cable attached to configured media\n", dev->name);
goto release_dma;
case DETECTED_RJ45H:
printk(KERN_INFO "%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);
break;
case DETECTED_RJ45F:
printk(KERN_INFO "%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);
break;
case DETECTED_AUI:
printk(KERN_INFO "%s: using 10Base-5 (AUI)\n", dev->name);
break;
case DETECTED_BNC:
printk(KERN_INFO "%s: using 10Base-2 (BNC)\n", dev->name);
break;
}
/* Turn on both receive and transmit operations */
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
/* Receive only error free packets addressed to this card */
lp->rx_mode = 0;
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
if (lp->isa_config & STREAM_TRANSFER)
lp->curr_rx_cfg |= RX_STREAM_ENBL;
#if ALLOW_DMA
set_dma_cfg(dev);
#endif
writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
#if ALLOW_DMA
dma_bufcfg(dev) |
#endif
TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
/* now that we've got our act together, enable everything */
writereg(dev, PP_BusCTL, ENABLE_IRQ
| (dev->mem_start?MEMORY_ON : 0) /* turn memory on */
#if ALLOW_DMA
| dma_busctl(dev)
#endif);
netif_start_queue(dev);
if (net_debug > 1)
printk("cs89x0: net_open() succeeded\n");
return 0;
bad_out:
return ret;
}
六. 修改cs89x0_probe函数中的 int io 为 unsigned int io;
七. 内核配置文件修改:
修改drivers/net/kconfig中的描述
config CS89x0
tristate "CS89x0 support"
depends on NET_ETHERNET && (ISA || EISA || MACH_IXDP2351 \
|| ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440) ||ARCH_S3C2410
---help---
Support for CS89x0 chipset based Ethernet cards. If you have a
network (Ethernet) card of this type, say Y and read the
Ethernet-HOWTO, available from
<http://www.tldp.org/docs.html#howto> as well as
<file:Documentation/networking/cs89x0.txt>.
To compile this driver as a module, choose M here. The module
will be called cs89x0.
config CS89x0_NONISA_IRQ
def_bool y
depends on CS89x0 != n
depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440 ||ARCH_S3C2410
/*必须添加ARCH_S3C2410,否则出现eth0: IRQ 53 is not in our map of allowable IRQs, which is 1c20的错误*/
八. 使用CS8900A网卡,在内核目录下执行make menuconfig后,选中
Device Drivers --->
[*] Network device support --->
[*]Ethernet (10 or 100Mbit) --->
< > DM9000 support
<*> CS89x0 support
九.编译
编译出现一下的警告:
drivers/net/cs89x0.c: In function 'cs89x0_probe':
drivers/net/cs89x0.c:335: warning: return makes pointer from integer without a cast
drivers/net/cs89x0.c:373: warning: passing argument 1 of '__iounmap' makes pointer from integer without a cast
十.测试
# ifconfig lo 127.0.0.1
#ifconfig eth0 172.19.69.211
#ping 172.19.69.254
PING 172.19.69.254 (172.19.69.254): 56 data bytes
64 bytes from 172.19.69.254: seq=0 ttl=64 time=1.552 ms
64 bytes from 172.19.69.254: seq=1 ttl=64 time=1.018 ms
64 bytes from 172.19.69.254: seq=2 ttl=64 time=1.028 ms
64 bytes from 172.19.69.254: seq=3 ttl=64 time=1.003 ms
64 bytes from 172.19.69.254: seq=4 ttl=64 time=0.998 ms
64 bytes from 172.19.69.254: seq=5 ttl=64 time=1.019 ms
64 bytes from 172.19.69.254: seq=6 ttl=64 time=0.972 ms
--- 172.19.69.254 ping statistics ---
7 packets transmitted, 7 packets received, 0% packet loss
round-trip min/avg/max = 0.972/1.084/1.552 ms
CS8900A驱动移植成功。