s3c2440 DM9000EP驱动程序

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值