#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>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
#include <linux/crc32.h>
static volatile unsigned long *bwscon;
static volatile unsigned long *bankcon4;
#define
BWSCON
(0x48000000)
#define BANKCON4 (0x48000014)
#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_NCR
0x00
#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"
#define DBG(msg...) do{ \
if(debug)\
printk(KERN_INFO msg);\
}while(0)
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 int debug=0;
static struct net_device *xnet_dev = NULL;
static void dm9000_hash_table(struct net_device *dev);
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 do_xnet_reset(struct dm9000x *dm9x);
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);
static void xnet_timeout(struct net_device *dev);
static void xnet_timeout(struct net_device *dev)
{
struct dm9000x *dm9x=netdev_priv(dev);
u8 reg_save;
reg_save=readb(dm9x->ioaddr);
netif_stop_queue(dev);
do_xnet_reset(dm9x);
dev->trans_start=jiffies;
netif_wake_queue(dev);
writeb(reg_save,dm9x->ioaddr);
}
int xnet_probe(struct net_device *dev)
{
int i =
0;
u32
id_val;
unsigned
long iobase;
struct
dm9000x *dm9x=netdev_priv(dev);
unsigned
char mac_add[6] = {0x00, 0x13, 0xf6, 0x6c, 0x87, 0x89};
memset(dm9x,0,sizeof(struct dm9000x));
bwscon =
ioremap_nocache(BWSCON,0x0000004);
//总线位宽和等待状态控制器
bankcon4=
ioremap_nocache(BANKCON4,0x0000004);
iobase
=(unsigned long)ioremap(DM9000_MIN_IO,
0x400);
//进行地址隐射
writel((readl(bwscon)
&(~(0xf<<16)))|(0xD<<16),
bwscon);
//enable UB/LB enable WAIT 16-bit
writel(0x1f7c,bankcon4);
s3c2410_gpio_cfgpin(S3C2410_GPF7,S3C2410_GPF7_EINT7);
spin_lock_init(&dm9x->lock);
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) {
DBG("id_val: %x, iobase: %p \n", id_val, (void *)iobase);
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;
dev->tx_timeout=&xnet_timeout;
dev->set_multicast_list=&dm9000_hash_table;
for (i = 0; i < 6; i++)
dev->dev_addr[i] = mac_add[i];
request_region(iobase, 2,
dev->name);
}
return
0;
}
static void do_xnet_reset(struct dm9000x *dm9x)
{
iowt(dm9x,DM9000_NCR, 1<<0);
}
static void dm9000_hash_table(struct net_device *dev)
{
struct dm9000x *dm9x = netdev_priv(dev);
struct dev_mc_list *mcptr =
dev->mc_list;
int mc_cnt = dev->mc_count;
int i, oft;
u32 hash_val;
u16 hash_table[4];
u8 rcr = (1<<5) |
(1<<4)| 1;
unsigned long flags;
spin_lock_irqsave(&dm9x->lock,
flags);
for (i = 0, oft = 0x10; i < 6;
i++, oft++)
iowt(dm9x, oft,dev->dev_addr[i]);
for (i = 0; i < 4; i++)
hash_table[i] = 0x0;
hash_table[3] = 0x8000;
if (dev->flags &
0x100)
rcr |= 2;
if (dev->flags
&0x200)
rcr |=(
1<<3);
for (i = 0; i < mc_cnt; i++, mcptr
= mcptr->next) {
hash_val = ether_crc_le(6,
mcptr->dmi_addr) & 0x3f;
hash_table[hash_val / 16] |=
(u16) 1 << (hash_val % 16);
}
for (i = 0, oft = 0x16; i < 4;
i++) {
iowt(dm9x,
oft++,hash_table[i]);
iowt(dm9x,
oft++,hash_table[i]>>8);
}
iowt(dm9x, 0x05,rcr);
spin_unlock_irqrestore(&dm9x->lock,
flags);
}
static void do_init_dm9000x(struct net_device *dev)
{
//int
i,oft;
struct
dm9000x *dm9x = netdev_priv(dev);
//set the
internal PHY power-on, GPIOs normal, and wait 2ms
iowt(dm9x,
0x1F,
0);
//GPR (reg_1Fh)bit GPIO0=0 pre-activate
PHY
iowt(dm9x,
0x1e, 1);
iowt(dm9x,
0x1F, 0);
dm9x->iomode = iord(dm9x, 0xFE)
>> 6; //ISR bit[7:6] I/O mode
//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, 0x3F);
iowt(dm9x,
0x0a, 0xff);
// Activate
DM9000
iowt(dm9x,
0x05, 0x31);
//RX enable
iowt(dm9x,
0xff, 0x83);
//Enable TX/RX interrupt mask
dm9000_hash_table(dev);
dm9x->tx_pkt_cnt
= 0;
dm9x->queue_pkt_len
= 0;
dev->trans_start
= 0;
spin_lock_init(&dm9x->lock);
DBG("do init
dm9000x xnte dev! \n");
}
static void do_xnet_tx(void)
{
struct
net_device *dev = xnet_dev;
struct
dm9000x *dm9x = netdev_priv(dev);
int
tx_status = iord(dm9x,
0x01);
//Got TX status
if
(tx_status & 0xc)
{
//One packet sent complete
dm9x->tx_pkt_cnt--;
dev->stats.tx_packets++;
if (dm9x->tx_pkt_cnt > 0)
{
//Queue packet check & send
//Set TX length to DM9000
iowt(dm9x, 0xfc, dm9x->queue_pkt_len );
iowt(dm9x, 0xfd, (dm9x->queue_pkt_len
>> 8) );
//Issue TX polling command
iowt(dm9x, 0x2,
0x1);
//Cleared after TX complete
dev->trans_start =
jiffies;
//saved the time
stamp
}
netif_wake_queue(dev);
}
DBG("xnet_tx_done the xnet dev! \n");
}
struct dm9000_rxhdr {
u8 RxPktReady;
u8 RxStatus;
__le16 RxLen;
} __attribute__((__packed__));
static void dm9000_inblk_16bit(void __iomem *reg, void *data, int
count)
{
readsw(reg, data, (count+1)
>> 1);
}
static void do_xnet_rx(void)
{
struct
net_device *dev = xnet_dev;
struct
dm9000x *dm9x = netdev_priv(dev);
struct
sk_buff *skb;
struct dm9000_rxhdr
rxhdr;
u8 rxbyte,
*rdptr;
u16 i,
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);
dm9000_inblk_16bit((void
*)dm9x->iodata,&rxhdr,(int)sizeof(rxhdr));
RxLen=le16_to_cpu(rxhdr.RxLen);
if (RxLen < 0x40) {
GoodPacket = 0;
} else if (RxLen > DM9000_PKT_MAX) {
DBG("<DM9000> RST: RX Len:%x(%x)\n",
RxLen, rxhdr.RxStatus);
dm9x->device_wait_reset = 1;
}
if (rxhdr.RxStatus & 0xbf)
{
GoodPacket = 0;
if (rxhdr.RxStatus
& 0x01)
dev->stats.rx_fifo_errors++;
if (rxhdr.RxStatus
& 0x02)
dev->stats.rx_crc_errors++;
if (rxhdr.RxStatus
& 0x80)
dev->stats.rx_length_errors++;
}
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);
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
xnet_dev->stats.rx_bytes+=RxLen;
xnet_dev->stats.rx_packets++;
} 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
DBG("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;
}
} while
(rxbyte == DM9000_PKT_RDY &&
!dm9x->device_wait_reset);
DBG("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;
unsigned
long flags;
dm9x =
netdev_priv(dev);
spin_lock_irqsave(&dm9x->lock,flags);
//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 & 0x01)
{ //Received
the coming packet
DBG("Receive data interrupt\n");
do_xnet_rx();
}
if
(int_status & 0x02)
{ //Trnasmit
Interrupt check
DBG("Trnasmit data interrupt\n");
do_xnet_tx();
}
//Re-enable interrupt mask
iowt(dm9x,
0xff, 0x83);
//Restore
previous register address
outb(reg_save, dm9x->ioaddr);
spin_unlock_irqrestore(&dm9x->lock,flags);
DBG("interrupt the xnet dev! %x\n",
dm9x->ioaddr);
return
IRQ_HANDLED;
}
static int xnet_open(struct net_device *dev)
{
struct
dm9000x *dm9x = netdev_priv(dev);
if(request_irq(dev->irq,
(irq_handler_t)&xnet_interrupt, (unsigned
long)(IRQF_SHARED|IORESOURCE_IRQ|IRQF_TRIGGER_RISING)&IRQF_TRIGGER_MASK,
dev->name,(void *)dev))
return -EAGAIN;
do_xnet_reset(dm9x);
do_init_dm9000x(dev);
netif_start_queue(dev);
netif_carrier_on(dev);
printk("open
the xnet dev! \n");
return
0;
}
static int xnet_stop(struct net_device *dev)
{
struct
dm9000x *dm9x = netdev_priv(dev);
netif_stop_queue(dev);
netif_carrier_off(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 = netdev_priv(dev);
char
*data;
int i,
len;
unsigned
long flags;
if
(dm9x->tx_pkt_cnt > 1) return
1;
spin_lock_irqsave(&dm9x->lock,flags);
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);
DBG("%x ",((u16 *) data)[i]);
}
dev->stats.tx_bytes+=skb->len;
dm9x->tx_pkt_cnt++;
// TX
control: First packet immediately send, second packet queue
if
(dm9x->tx_pkt_cnt ==
1){
// First
Packet
// 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->queue_pkt_len = skb->len;
netif_stop_queue(dev);
}
spin_unlock_irqrestore(&dm9x->lock,flags);
dev_kfree_skb(skb);
DBG("hard
start xmit the xnet dev!
\n");
return
0;
}
static void iowt(struct dm9000x *dm9x, int reg, u8 value)
{
writeb(reg,
dm9x->ioaddr);
writeb(value, dm9x->iodata);
}
static u8 iord(struct dm9000x *dm9x, int reg)
{
writeb(reg,
dm9x->ioaddr);
return
readb(dm9x->iodata);
}
static int __init xnet_dev_init(void)
{
int err =
0;
xnet_dev
= alloc_etherdev(sizeof(struct dm9000x));
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(netdev_priv(xnet_dev));
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");