the code behind is a some
error ,the hwaddr must written to the relevant
regesiters in the init function .this is important and
essential.
最近手头刚好有个开发板,也还有点时间,就试着写了一下它上面的网卡驱动,当然也是参考了其现有的驱动再写的,程序如下:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include
<linux/netdevice.h>
#include
<linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/irq.h>
#include <asm/io.h>
static void *bwscon;
static void *gpfcon;
static void *extint0;
static void *intmsk;
#define
BWSCON
(0x48000000)
#define
GPFCON
(0x56000050)
#define
EXTINT0
(0x56000088)
#define
INTMSK
(0x4A000008)
#define
BWSCON
(0x48000000)
#define
GPFCON
(0x56000050)
#define
EXTINT0
(0x56000088)
#define
INTMSK
(0x4A000008)
#define
DM9000_MIN_IO
0x20000300
//--
#define
DM9000_MAX_IO
0x20000370
//--
#define
DM9000_VID_L
0x28
#define
DM9000_VID_H
0x29
#define
DM9000_PID_L
0x2A
#define
DM9000_PID_H
0x2B
#define
DM9000_PKT_MAX
1536
//Received
packet max size
#define
DM9000_PKT_RDY
0x01
//Packet
ready to receive
#define DMFE_TIMER_WUT
jiffies+(HZ*2)
//timer wakeup time : 2 second
#define DMFE_TX_TIMEOUT
(HZ*2)
//tx packet
time-out time 1.5 s"
struct dm9000x{
u32
ioaddr;
// Register
I/O base address
u32
iodata;
// Data I/O
address
u16
irq;
//
IRQ
u8
iomode;
// 0:16bits
1:word 2:byte
u8
opmode;
u16 Preg0,
Preg4;
u16
tx_pkt_cnt;
u16
sent_pkt_len, queue_pkt_len;
u8
device_wait_reset;
//device state
u8
nic_type;
// NIC
type
spinlock_t
lock;
};
static struct net_device *xnet_dev = NULL;
int xnet_probe(struct net_device *dev);
static int xnet_open(struct net_device *dev);
static int xnet_stop(struct net_device *dev);
static int xnet_xmit(struct sk_buff *skb, struct net_device
*dev);
static irqreturn_t xnet_interrupt(int irq, void *dev_id, struct
pt_regs *regs);
static void do_init_dm9000x(struct net_device *dev);
static void do_xnet_tx(void);
static void do_xnet_rx(void);
static void iowt(struct dm9000x *dm9x, int reg, u8
value);
static u8 iord(struct dm9000x *dm9x, int reg);
static u16 phy_read(struct dm9000x *dm9x, int reg);
static void phy_write(struct dm9000x *dm9x, int reg, u16
value);
int xnet_probe(struct net_device *dev)
{
int i =
0;
u32
id_val;
u32
iobase;
struct
dm9000x *dm9x;
unsigned
char mac_add[6] = {0x00, 0x13, 0xf6, 0x6c, 0x87, 0x89};
bwscon =
ioremap_nocache(BWSCON,0x0000004);
//总线位宽和等待状态控制器
gpfcon =
ioremap_nocache(GPFCON,0x0000004);
//Port F 控制寄存器
extint0 =
ioremap_nocache(EXTINT0,0x0000004);
//外部中断控制
intmsk =
ioremap_nocache(INTMSK,0x0000004);
//中断控制
writel(readl(bwscon) | 0xc0000,
bwscon);
//允许等待,使用UB/LB
//设置GFP7为外部中断模式 EINT7
writel(
(readl(gpfcon) & ~(0x3
<< 14)) | (0x2
<< 14), gpfcon);
//设置EINT7 为
falling edge triggered
writel(
(readl(extint0) & ~(0xf
<< 28)) | (0x4
<< 28),
extint0);
//中断掩码设置1为屏蔽,这里设置开启EINT4~7
writel(
(readl(intmsk)) & ~0x80,
intmsk);
iobase =
ioremap(DM9000_MIN_IO,
0x400);
//进行地址隐射
outb(DM9000_VID_L, iobase);
id_val =
inb(iobase + 4);
outb(DM9000_VID_H, iobase);
id_val |=
inb(iobase + 4) << 8;
outb(DM9000_PID_L, iobase);
id_val |=
inb(iobase + 4) << 16;
outb(DM9000_PID_H, iobase);
id_val |=
inb(iobase + 4) << 24;
if (id_val
== 0x90000a46) {
printk("id_val: %x, iobase: %p \n", id_val, (void
*)iobase);
dm9x = (void
*)kmalloc(sizeof(struct dm9000x), GFP_KERNEL);
//memset(dm9x,'0', sizeof(struct dm9000x));
//dm9x =
(void *) (kmalloc(sizeof (struct dm9000x), GFP_KERNEL));
memset(dm9x,
0, sizeof (struct dm9000x));
dev->priv = dm9x;
dm9x->ioaddr
=
iobase;
dm9x->iodata
= iobase +
4;
ether_setup(dev);
dev->base_addr
= iobase;
dev->irq
=
IRQ_EINT7;
dev->open
=
&xnet_open;
dev->stop
=
&xnet_stop;
dev->hard_start_xmit =
&xnet_xmit;
SET_MODULE_OWNER(dev);
for (i = 0;
i < 6; i++)
dev->dev_addr[i] = mac_add[i];
request_region(iobase, 2,
dev->name);
}
return
0;
}
static void do_init_dm9000x(struct net_device *dev)
{
u16
phy_reg3;
u16 phy_reg0
= 0x1000;
//Auto-negotiation & non-duplux mode
u16 phy_reg4
= 0x01e1;
//Default non flow control
struct
dm9000x *dm9x = (struct dm9000x
*)dev->priv;
//set the
internal PHY power-on, GPIOs normal, and wait
2ms
iowt(dm9x,
0x1F, 0);
//GPR
(reg_1Fh)bit GPIO0=0 pre-activate PHY
udelay(20);
//wait 2ms
for PHY power-on ready
//0x00
network ctrl reg bit[0]:soft reset bit[1:2]:01 MAC inter
loopback
iowt(dm9x,
0x00,
3);
udelay(20);
//set
GPIO0=1 then GPIO0=0 to turn off and on the internal
PHY
iowt(dm9x,
0x1F, 1);
//GPR
(reg_1Fh) bit[0] GPIO0=1 turn-off
PHY
iowt(dm9x,
0x1F, 0);
//GPR
(reg_1Fh) bit[0] GPIO0=0 activate
PHY
udelay(1000);
//wait 4ms
linking PHY (AUTO sense) if RX/TX
udelay(1000);
udelay(1000);
udelay(1000);
dm9x->iomode = iord(dm9x, 0xFE)
>> 6; //ISR bit[7:6] I/O
mode
//Full-Duplex Mode. Read only on Internal PHY mode. R/W on External
PHY mode
iowt(dm9x,
0x00,
0x80);
phy_reg3 =
phy_read(dm9x, 3);
dm9x->nic_type = 0;
printk("IOmode: %x phy_reg3: %x \n", dm9x->iomode,
phy_reg3);
iowt(dm9x,
0x00, 0x00);
dm9x->opmode =
0;
phy_write(dm9x, 0, phy_reg0);
phy_write(dm9x, 4, 0x0400 | phy_reg4);
dm9x->Preg0 = phy_reg0;
dm9x->Preg4 = phy_reg4 + 0x0400;
//Program
operating register
iowt(dm9x,
0x00, 0x08);
iowt(dm9x,
0x02, 0x00);
iowt(dm9x,
0x2f, 0x00);
iowt(dm9x,
0x01,
0x2c);
iowt(dm9x,
0xfe,
0x0f);
iowt(dm9x,
0x08, 0x37);
iowt(dm9x,
0x09,
0x38);
iowt(dm9x,
0x0a,
0x29);
// Activate
DM9000
iowt(dm9x,
0x05, 0x30 |
1); //RX
enable
iowt(dm9x,
0xff, 0x83);
//Enable TX/RX interrupt mask
dm9x->tx_pkt_cnt
= 0;
dm9x->queue_pkt_len
= 0;
dev->trans_start
= 0;
netif_carrier_on(dev);
spin_lock_init(&dm9x->lock);
printk("do
init dm9000x xnte dev! \n");
}
static void do_xnet_tx(void)
{
struct
net_device *dev = xnet_dev;
struct
dm9000x *dm9x = (struct dm9000x
*)dev->priv;
int
tx_status = iord(dm9x,
0x01);
//Got TX
status
if
(tx_status & 0xc)
{
//One packet
sent complete
dm9x->tx_pkt_cnt--;
dev->trans_start = 0;
if
(dm9x->tx_pkt_cnt > 0)
{
//Queue
packet check & send
//Set TX
length to DM9000
iowt(dm9x,
0xfc, dm9x->queue_pkt_len &
0xff);
iowt(dm9x,
0xfd, (dm9x->queue_pkt_len
>> 8) &
0xff);
//Issue TX
polling command
iowt(dm9x,
0x2, 0x1);
//Cleared
after TX complete
dev->trans_start =
jiffies;
//saved the time stamp
}
netif_wake_queue(dev);
}
printk("xnet_tx_done the xnet dev! \n");
}
static void do_xnet_rx(void)
{
struct
net_device *dev = xnet_dev;
struct
dm9000x *dm9x = (struct dm9000x
*)dev->priv;
struct
sk_buff *skb;
u8 rxbyte,
*rdptr;
u16 i,
RxStatus, RxLen, GoodPacket, tmplen;
do
{
iord(dm9x,
0xf0);
//Dummy
read
rxbyte =
inb(dm9x->iodata);
//Got most updated data
if (rxbyte
== DM9000_PKT_RDY) { // packet ready to receive check
GoodPacket =
1;
outb(0xf2,
dm9x->ioaddr);
RxStatus =
RxLen = (u16) 0;
RxStatus =
inw(dm9x->iodata);
outb(0xf2,
dm9x->ioaddr);
RxLen =
inw(dm9x->iodata);
if (RxLen
< 0x40) {
GoodPacket =
0;
} else if
(RxLen > DM9000_PKT_MAX) {
printk("<DM9000> RST: RX
Len:%x(%x)\n", RxLen, RxStatus);
dm9x->device_wait_reset = 1;
}
if (RxStatus
& 0xbf00) GoodPacket = 0;
if
(!dm9x->device_wait_reset){
if(GoodPacket && ((skb =
dev_alloc_skb(RxLen + 4)) != NULL)){
skb->dev = dev;
skb_reserve(skb, 2);
rdptr = (u8
*) skb_put(skb, RxLen - 4);
tmplen =
(RxLen + 1) / 2;
for (i = 0;
i < tmplen; i++){
((u16 *)
rdptr)[i] = inw(dm9x->iodata);
//printk("%x
",((u16 *) rdptr)[i]);
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
} else
{
tmplen =
(RxLen + 1) / 2;
for (i = 0;
i < tmplen; i++)
inw(dm9x->iodata);
}
}
} else if
(rxbyte > DM9000_PKT_RDY) {
// Status
check: this byte must be 0 or 1
printk("RX
SRAM 1st byte(x) != 01, must reset.\n", rxbyte);
iowt(dm9x,
0x05, 0x00);
// Stop
Device
iowt(dm9x,
0xfe, 0x80);
// Stop INT
request
dm9x->device_wait_reset = 1;
//dm9x->reset_rx_status++;
}
} while
(rxbyte == DM9000_PKT_RDY &&
!dm9x->device_wait_reset);
printk("xnet_packet_receive the xnet dev! %x\n",
dm9x->ioaddr);
}
static irqreturn_t xnet_interrupt(int irq, void *dev_id, struct
pt_regs *regs)
{
struct
net_device *dev =
dev_id;
struct
dm9000x *dm9x;
int
int_status;
u8
reg_save;
dm9x =
(struct dm9000x *)dev->priv;
spin_lock_irq(&dm9x->lock);
//Save
previous register address
reg_save =
inb(dm9x->ioaddr);
//Disable
all interrupt
iowt(dm9x,
0xff, 0x80);
//Got DM9000
interrupt status
int_status =
iord(dm9x,
0xfe);
//Got
ISR
iowt(dm9x,
0xfe,
int_status);
//Clear ISR
status
if
(int_status & 0x02)
{ //Trnasmit
Interrupt check
do_xnet_tx();
}
if
(int_status & 0x01)
{ //Received
the coming packet
do_xnet_rx();
}
//Re-enable
interrupt mask
iowt(dm9x,
0xff, 0x83);
//Restore
previous register address
outb(reg_save, dm9x->ioaddr);
spin_unlock_irq(&dm9x->lock);
printk("interrupt the xnet dev! %x\n",
dm9x->ioaddr);
return
IRQ_HANDLED;
}
static int xnet_open(struct net_device *dev)
{
//struct
dm9000x *dm9x = (struct dm9000x
*)dev->priv;
if(request_irq(dev->irq,
&xnet_interrupt, SA_SHIRQ,
dev->name, dev))
return
-EAGAIN;
do_init_dm9000x(dev);
netif_start_queue(dev);
enable_irq(dev->irq);
printk("open
the xnet dev! \n");
return
0;
}
static int xnet_stop(struct net_device *dev)
{
struct
dm9000x *dm9x = (struct dm9000x
*)dev->priv;
netif_stop_queue(dev);
free_irq(dev->irq, dev);
phy_write(dm9x, 0x00,
0x8000);
iowt(dm9x,
0x1f,
0x01);
iowt(dm9x,
0xff,
0x80);
iowt(dm9x,
0x05,
0x00);
printk("stop
the xnet dev! \n");
return
0;
}
static int xnet_xmit(struct sk_buff *skb, struct net_device
*dev)
{
struct
dm9000x *dm9x = (struct dm9000x
*)dev->priv;
char
*data;
int i,
len;
if
(dm9x->tx_pkt_cnt > 1) return
1;
netif_stop_queue(dev);
iowt(dm9x,
0xff, 0x80);
//Disable all interrupt
data = (char
*)skb->data;
outb(0xf8,
dm9x->ioaddr);
len =
(skb->len + 1) / 2;
for (i = 0;
i < len; i++){
outw(((u16
*) data)[i], dm9x->iodata);
//printk("%x
",((u16 *) data)[i]);
}
// TX
control: First packet immediately send, second packet
queue
if
(dm9x->tx_pkt_cnt ==
0){
// First
Packet
dm9x->tx_pkt_cnt++;
// Set TX
length to DM9000
iowt(dm9x,
0xfc, skb->len & 0xff);
iowt(dm9x,
0xfd, (skb->len >> 8)
& 0xff);
// Issue TX
polling command
iowt(dm9x,
0x2, 0x1);
//Cleared
after TX complete
// saved the
time stamp
dev->trans_start = jiffies;
} else
{
//Second
packet
dm9x->tx_pkt_cnt++;
dm9x->queue_pkt_len =
skb->len;
}
dev_kfree_skb(skb);
if
(dm9x->tx_pkt_cnt == 1)
netif_wake_queue(dev);
//Re-enable
interrupt
iowt(dm9x,
0xff, 0x83);
printk("hard
start xmit the xnet dev!
\n");
return
0;
}
static void iowt(struct dm9000x *dm9x, int reg, u8 value)
{
outb(reg,
dm9x->ioaddr);
outb(value,
dm9x->iodata);
}
static u8 iord(struct dm9000x *dm9x, int reg)
{
outb(reg,
dm9x->ioaddr);
return
inb(dm9x->iodata);
}
//Read a word from phyxcer
static u16 phy_read(struct dm9000x *dm9x, int reg)
{
//Fill the
phyxcer register into REG_0C
//0x0C
EEPROM & PHY Address Register bit[7:6]:01 select
PHY
//bit[5:0]:address of PHY or EEPROM
iowt(dm9x,
0xc, 0x40 | reg);
iowt(dm9x,
0xb, 0xc);
//Issue phyxcer read command
udelay(100);
//Wait read
complete
iowt(dm9x,
0xb, 0x0);
//Clear phyxcer read command
//The read
data keeps on REG_0D(L) & REG_0E(H)
return
(iord(dm9x, 0xe) << 8) | iord(dm9x,
0xd);
}
static void phy_write(struct dm9000x *dm9x, int reg, u16
value)
{
//Fill the
phyxcer register into REG_0C
iowt(dm9x,
0xc, 0x40 | reg);
//Fill the
written data into REG_0D(L) & REG_0E(H)
iowt(dm9x,
0xd, (value & 0xff));
iowt(dm9x,
0xe, ((value >> 8) &
0xff));
iowt(dm9x,
0xb, 0xa);
//Issue phyxcer write command
udelay(500);
//Wait write
complete
iowt(dm9x,
0xb, 0x0);
//Clear phyxcer write command
}
static int __init xnet_dev_init(void)
{
int err =
0;
xnet_dev =
alloc_etherdev(sizeof(struct net_device));
xnet_dev->init = xnet_probe;
err =
dev_alloc_name(xnet_dev, "eth%d");
if (err
< 0)
return
err;
err =
register_netdev(xnet_dev);
if (err
< 0)
return
err;
printk("init
the xnet dev! \n");
return
0;
}
static void __exit xnet_dev_exit(void)
{
unregister_netdev(xnet_dev);
kfree(xnet_dev->priv);
memset(xnet_dev, 0, sizeof (*xnet_dev));
printk("xnet
dev exit!! \n");
}
module_init(xnet_dev_init);
module_exit(xnet_dev_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Helight.Xu");