s3c2440 DM9000EP驱动程序

来源:互联网 发布:淘宝怎么货到付款啊 编辑:程序博客网 时间:2024/06/15 00:16

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

#defineBWSCON   (0x48000000)
#define BANKCON4  (0x48000014)


#defineDM9000_MIN_IO   0x20000300   //--
#defineDM9000_MAX_IO   0x20000370   //--

#defineDM9000_VID_L       0x28
#defineDM9000_VID_H       0x29
#defineDM9000_PID_L       0x2A
#defineDM9000_PID_H       0x2B
#defineDM9000_NCR    0x00
#defineDM9000_PKT_MAX   1536       //Received packet max size
#defineDM9000_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{
    u32ioaddr;       // Register I/O base address
    u32iodata;       // Data I/O address
    u16irq;       // IRQ
    u8iomode;       // 0:16bits 1:word  2:byte
    u8opmode;
    u16 Preg0,Preg4;

    u16tx_pkt_cnt;
    u16sent_pkt_len, queue_pkt_len;
    u8device_wait_reset;   //device state
    u8nic_type;           // NIC type
    spinlock_tlock;
};
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, structpt_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, u16value);
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;
    u32id_val;
    unsignedlong iobase;
    structdm9000x *dm9x=netdev_priv(dev);
    unsignedchar 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);   
     
    return0;
}
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)
{
    //inti,oft;
    structdm9000x *dm9x = netdev_priv(dev);

    //set theinternal PHY power-on, GPIOs normal, and wait 2ms
    iowt(dm9x,0x1F,0);       //GPR (reg_1Fh)bit GPIO0=0 pre-activatePHY      
    iowt(dm9x,0x1e, 1);
    iowt(dm9x,0x1F, 0);

   dm9x->iomode = iord(dm9x, 0xFE)>> 6; //ISR bit[7:6] I/O mode

    //Programoperating 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);
    // ActivateDM9000
    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 initdm9000x xnte dev! \n");
}

static void do_xnet_tx(void)
{
    structnet_device *dev = xnet_dev;
    structdm9000x *dm9x = netdev_priv(dev);
    inttx_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 timestamp   
       }
       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, intcount)
{
 readsw(reg, data, (count+1)>> 1);
}
static void do_xnet_rx(void)
{
    structnet_device *dev = xnet_dev;
    structdm9000x *dm9x = netdev_priv(dev);
    structsk_buff *skb;
   struct dm9000_rxhdrrxhdr;
    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 receivecheck
           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, structpt_regs *regs)
{
    structnet_device *dev =dev_id;   
    structdm9000x *dm9x;
    intint_status;
    u8reg_save;
    unsignedlong flags;
    dm9x =netdev_priv(dev);
   spin_lock_irqsave(&dm9x->lock,flags);

    //Saveprevious register address
    reg_save =inb(dm9x->ioaddr);
    //Disableall interrupt
    iowt(dm9x,0xff, 0x80);

    //GotDM9000 interrupt status
    int_status =iord(dm9x,0xfe);       //Got ISR
    iowt(dm9x,0xfe,int_status);       //Clear ISR status
    if(int_status & 0x01){    //Receivedthe coming packet
 DBG("Receive data interrupt\n");
       do_xnet_rx();
    }
    if(int_status & 0x02){    //TrnasmitInterrupt check
 DBG("Trnasmit data interrupt\n");
       do_xnet_tx();
    }

   //Re-enable interrupt mask
    iowt(dm9x,0xff, 0x83);
    //Restoreprevious register address
   outb(reg_save, dm9x->ioaddr);
   spin_unlock_irqrestore(&dm9x->lock,flags);
   DBG("interrupt the xnet dev!  %x\n",dm9x->ioaddr);
    returnIRQ_HANDLED;
}

static int xnet_open(struct net_device *dev)
{
    structdm9000x *dm9x = netdev_priv(dev);
   
   if(request_irq(dev->irq,(irq_handler_t)&xnet_interrupt, (unsignedlong)(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("openthe xnet dev! \n");
    return0;
}

static int xnet_stop(struct net_device *dev)
{
    structdm9000x *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");
    return0;
}

static int xnet_xmit(struct sk_buff *skb, struct net_device*dev)
{
    structdm9000x *dm9x = netdev_priv(dev);
    char*data;
    int i,len;
    unsignedlong flags;
    if(dm9x->tx_pkt_cnt > 1) return1;
   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++;
    // TXcontrol: First packet immediately send, second packet queue
    if(dm9x->tx_pkt_cnt ==1){           // FirstPacket      
       // 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("hardstart xmit the xnet dev!\n");   
    return0;
}

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);
    returnreadb(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("initthe xnet dev! \n");

    return0;
}

static void __exit xnet_dev_exit(void)
{
   unregister_netdev(xnet_dev);
   kfree(netdev_priv(xnet_dev));
   memset(xnet_dev, 0, sizeof (*xnet_dev));
    printk("xnetdev exit!! \n");
}

module_init(xnet_dev_init);
module_exit(xnet_dev_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Helight.Xu");

0 0
原创粉丝点击