#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");