⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dm9000device_driver.c

📁 用DM9000做以太网口
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
  dm9000.c: Version 1.2 03/18/2003
  
        A Davicom DM9000 ISA NIC fast Ethernet driver for Linux.
 Copyright (C) 1997  Sten Wang

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.


  (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.


V0.11 06/20/2001 REG_0A bit3=1, default enable BP with DA match
 06/22/2001  Support DM9801 progrmming 
     E3: R25 = ((R24 + NF) & 0x00ff) | 0xf000
    E4: R25 = ((R24 + NF) & 0x00ff) | 0xc200
         R17 = (R17 & 0xfff0) | NF + 3
    E5: R25 = ((R24 + NF - 3) & 0x00ff) | 0xc200
         R17 = (R17 & 0xfff0) | NF
    
v1.00                modify by simon 2001.9.5
                        change for kernel 2.4.x    
   
v1.1   11/09/2001       fix force mode bug             

v1.2   03/18/2003       Weilun Huang <weilun_huang@davicom.com.tw>: 
   Fixed phy reset.
   Added tx/rx 32 bit mode.
   Cleaned up for kernel merge.
*/

#if defined(MODVERSIONS)
#include <linux/modversions.h>
#endif
    
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/version.h>
//#include <asm/dma.h>
#include <linux/spinlock.h>
#include <../fs/jffs2/crc32.h>
#include <linux/init.h>   //modify asm/init.h as linux/init.h
#include <linux/delay.h>
//#include <asm/coldfire.h>  //note
//#include <asm/mcfsim.h> //note 
#include <linux/types.h>
#include <linux/mm.h> 
#include <linux/blkdev.h>
#include <asm/irq.h>
#include <asm/io.h>


/* Board/System/Debug information/definition ---------------- */

#define DM9000_ID  0x90000A46

#define DM9000_REG00  0x00
#define DM9000_REG05  0x30 /* SKIP_CRC/SKIP_LONG */
#define DM9000_REG08  0x27
#define DM9000_REG09  0x38
#define DM9000_REG0A  0xff 
#define DM9000_REGFF  0x83 /* IMR */

#define DM9000_PHY  0x40 /* PHY address 0x01 */
#define DM9000_PKT_MAX  1536 /* Received packet max size */
#define DM9000_PKT_RDY  0x01 /* Packet ready to receive */
#define DM9000_MIN_IO  0x19000000
#define DM9000_MAX_IO  0x19000070
#define DM9000_INT_MII  0x00
#define DM9000_EXT_MII  0x80

#define DM9000_VID_L  0x28
#define DM9000_VID_H  0x29
#define DM9000_PID_L  0x2A
#define DM9000_PID_H  0x2B

#define DM9801_NOISE_FLOOR 0x08
#define DM9802_NOISE_FLOOR 0x05

#define DMFE_SUCC        0
#define MAX_PACKET_SIZE  1514
#define DMFE_MAX_MULTICAST  14

#define DM9000_RX_INTR  0x01
#define DM9000_TX_INTR  0x02
#define DM9000_OVERFLOW_INTR 0x04

#define DM9000_DWORD_MODE 1
#define DM9000_BYTE_MODE 2
#define DM9000_WORD_MODE 0

#define TRUE   1
#define FALSE   0

#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" */
#if defined(DM9000_DEBUG)
static int dmfe_debug = 0;
static int debug      = 0;
#define DMFE_DBUG(dbug_now, msg, vaule)  printk(KERN_ERR "dmfe: %s %x\n", msg, vaule)
#else
#define DMFE_DBUG(dbug_now, msg, vaule) 
#endif

enum DM9000_PHY_mode {
 DM9000_10MHD   = 0, 
 DM9000_100MHD  = 1, 
 DM9000_10MFD   = 4,
 DM9000_100MFD  = 5, 
 DM9000_AUTO    = 8, 
 DM9000_1M_HPNA = 0x10 
};

enum DM9000_NIC_TYPE {
 FASTETHER_NIC = 0, 
 HOMERUN_NIC   = 1, 
 LONGRUN_NIC   = 2 
};

/* Structure/enum declaration ------------------------------- */
typedef struct board_info {

 u32 runt_length_counter; /* counter: RX length < 64byte */ 
 u32 long_length_counter; /* counter: RX length > 1514byte */ 
 u32 reset_counter;  /* counter: RESET */ 
 u32 reset_tx_timeout;  /* RESET caused by TX Timeout */ 
 u32 reset_rx_status;  /* RESET caused by RX Statsus wrong */ 

 u32 ioaddr;   /* Register I/O base address */
 u32 io_data;   /* Data I/O address */
 u16 irq;   /* IRQ */

 u16 tx_pkt_cnt;
 u16 queue_pkt_len;
 u16 queue_start_addr;
 u16 dbug_cnt;
 u8 reg0, reg5, reg8, reg9, rega;/* registers saved */
 u8 op_mode;   /* PHY operation mode */
 u8 io_mode;   /* 0:word, 2:byte */
 u8 phy_addr;
 u8 link_failed;   /* Ever link failed */
 u8 device_wait_reset;  /* device state */
 u8 nic_type;   /* NIC type */
 struct timer_list timer;
 struct net_device_stats stats;
 unsigned char srom[128];
 spinlock_t lock;
} board_info_t;

/* Global variable declaration ----------------------------- */
static struct net_device * dmfe_dev = NULL;

/* For module input parameter */
//static int mode       = DM9000_AUTO;
static int media_mode = DM9000_AUTO;
static u8 reg5        = DM9000_REG05;
static u8 reg8        = DM9000_REG08;
static u8 reg9        = DM9000_REG09;
static u8 rega        = DM9000_REG0A;
static u8 nfloor      = 0;
static u8 irqline     = IRQ_DM9000;

/* function declaration ------------------------------------- */
int dm9000_probe(struct net_device *);
static int dmfe_open(struct net_device *);
static int dmfe_start_xmit(struct sk_buff *, struct net_device *);
static int dmfe_stop(struct net_device *);
static struct net_device_stats * dmfe_get_stats(struct net_device *); 
static int dmfe_do_ioctl(struct net_device *, struct ifreq *, int);
static void dmfe_interrupt(int , void *, struct pt_regs *);
static void dmfe_timer(unsigned long);
static void dm9000_init(struct net_device *);
static u8 ior(board_info_t *, int);
static void iow(board_info_t *, int, u8);
static u16 phy_read(board_info_t *, int);
static void phy_write(board_info_t *, int, u16);
//static u16 read_srom_word(board_info_t *, int);
static void dmfe_packet_receive(unsigned long);
static void dm9000_hash_table(struct net_device *);

DECLARE_TASKLET(dmfe_rx_tasklet,dmfe_packet_receive,0);
#define DATA(a) (a)
#undef SWAP_HARDWARE


/*
  Search DM9000 board, allocate space and register it
*/
static unsigned char eth_mac_addr[6] = {0x80, 0xe2, 0x66, 0x60, 0x00, 0x01};
/* DM9000 network baord routine ---------------------------- */

/*
  Search DM9000 board, allocate space and register it
*/
u32 iobase;
int dm9000_probe(struct net_device *dev)
{
 struct board_info *db;    /* Point a board information structure */
 u32 id_val;
 u16 i, dm9000_found = FALSE;
 iobase = DM9000_MIN_IO;

    set_external_irq(IRQ_DM9000, EXT_RISING_EDGE, GPIO_PULLUP_DIS);

 iobase = ioremap(DM9000_MIN_IO, 0x400);
 DMFE_DBUG(0, "dm9000_probe()",0);

 /* Search All DM9000 NIC */
 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 == DM9000_ID) {
  
  printk("<DM9000> I/O: %x, VID: %x \n",iobase, id_val);
  dm9000_found = TRUE;

  /* Init network device */
  dev = init_etherdev(dev, 0);
   
  /* Allocated board information structure */
  db = (void *)(kmalloc(sizeof(*db), GFP_KERNEL));
  memset(db, 0, sizeof(*db));
  dev->priv   = db;   /* link device and board info */
  dmfe_dev    = dev;
  db->ioaddr  = iobase;
  db->io_data = iobase + 4;

  /* driver system function */
  ether_setup(dev);
   
  dev->base_addr   = iobase;
  dev->irq   = irqline;
  dev->open   = &dmfe_open;
  dev->hard_start_xmit  = &dmfe_start_xmit;
  dev->stop   = &dmfe_stop;
  dev->get_stats   = &dmfe_get_stats;
  dev->set_multicast_list = &dm9000_hash_table;
  dev->do_ioctl   = &dmfe_do_ioctl;

  SET_MODULE_OWNER(dev);
  //add by kavin, set MAC
                for (i=0; i<6; i++)
   dev->dev_addr = eth_mac_addr;

  /* Request IO from system */
  request_region(iobase, 2, dev->name);
  return 0;

 }
 else
  return -ENODEV;
}

/*
  Open the interface.
  The interface is opened whenever "ifconfig" actives it.
*/
static int dmfe_open(struct net_device *dev)
{
 board_info_t * db = (board_info_t *)dev->priv;

 DMFE_DBUG(0, "dmfe_open", 0);

 MOD_INC_USE_COUNT;

 if (request_irq(dev->irq,&dmfe_interrupt,SA_SHIRQ,dev->name,dev)) 
  return -EAGAIN;

 /* Initilize DM910X board */
 dm9000_init(dev);
 
 /* Init driver variable */
 db->dbug_cnt   = 0;
 db->runt_length_counter = 0;
 db->long_length_counter = 0;
 db->reset_counter  = 0;

 /* set and active a timer process */
 init_timer(&db->timer);
 db->timer.expires  = DMFE_TIMER_WUT * 2;
 db->timer.data   = (unsigned long)dev;
 db->timer.function  = &dmfe_timer;
 add_timer(&db->timer);

 netif_start_queue(dev);

 return 0;
}

/* Set PHY operationg mode
*/
static void set_PHY_mode(board_info_t *db)
{
 u16 phy_reg4 = 0x01e1, phy_reg0=0x1000;

 if ( !(db->op_mode & DM9000_AUTO) ) {
  
  switch(db->op_mode) {
   case DM9000_10MHD:  phy_reg4 = 0x21; 
                                     phy_reg0 = 0x0000; 
         break;
   case DM9000_10MFD:  phy_reg4 = 0x41; 
                                     phy_reg0 = 0x1100; 
                                     break;
   case DM9000_100MHD: phy_reg4 = 0x81; 
         phy_reg0 = 0x2000; 
             break;
   case DM9000_100MFD: phy_reg4 = 0x101; 
             phy_reg0 =0x3100; 
            break;
  }
  phy_write(db, 4, phy_reg4); /* Set PHY media mode */
  phy_write(db, 0, phy_reg0); /*  Tmp */
 }

 iow(db, 0x1e, 0x01);   /* Let GPIO0 output */
 iow(db, 0x1f, 0x00);   /* Enable PHY */
}

/*
 Init HomeRun DM9801
*/
static void program_dm9801(board_info_t *db, u16 HPNA_rev)
{
 __u16 reg16, reg17, reg24, reg25;

 if ( !nfloor ) 
  nfloor = DM9801_NOISE_FLOOR;

 reg16 = phy_read(db, 16);
 reg17 = phy_read(db, 17);
 reg24 = phy_read(db, 24);
 reg25 = phy_read(db, 25);

 switch(HPNA_rev) {
  case 0xb900: /* DM9801 E3 */
   reg16 |= 0x1000;
   reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xf000;
   break;
  case 0xb901: /* DM9801 E4 */
   reg25 = ( (reg24 + nfloor) & 0x00ff) | 0xc200;
   reg17 = (reg17 & 0xfff0) + nfloor + 3;
   break;
  case 0xb902: /* DM9801 E5 */
  case 0xb903: /* DM9801 E6 */
  default:
   reg16 |= 0x1000;
   reg25 = ( (reg24 + nfloor - 3) & 0x00ff) | 0xc200;
   reg17 = (reg17 & 0xfff0) + nfloor;
 }

 phy_write(db, 16, reg16);
 phy_write(db, 17, reg17);
 phy_write(db, 25, reg25);
}

/*
 Init LongRun DM9802
*/
static void program_dm9802(board_info_t *db)
{
 __u16 reg25;

 if ( !nfloor ) 
  nfloor = DM9802_NOISE_FLOOR;

 reg25 = phy_read(db, 25);
 reg25 = (reg25 & 0xff00) + nfloor;
 phy_write(db, 25, reg25);
}

/* Identify NIC type
*/
static void identify_nic(board_info_t *db)
{
 u16 phy_reg3;

 iow(db, 0, DM9000_EXT_MII);
 phy_reg3 = phy_read(db, 3);
 
 switch(phy_reg3 & 0xfff0) {
  
  case 0xb900:
   
   if (phy_read(db, 31) == 0x4404) {
    db->nic_type =  HOMERUN_NIC;
    program_dm9801(db, phy_reg3);
   } else {
    db->nic_type = LONGRUN_NIC;
    program_dm9802(db);
   }
   break;
   
  default: db->nic_type = FASTETHER_NIC; 
    break;
   
 }
 
 iow(db, 0, DM9000_INT_MII); 
}

/* Initilize dm9000 board
*/
static void dm9000_init(struct net_device *dev)
{
 board_info_t *db = (board_info_t *)dev->priv;
 
 DMFE_DBUG(0, "dm9000_init()", 0);

 /* RESET device */
 iow(db, 0, 1);
 udelay(100);     /* delay 100us */

 /* I/O mode */
 db->io_mode = ior(db, 0xfe) >> 6; /* ISR bit7:6 keeps I/O mode */

 /* NIC Type: FASTETHER, HOMERUN, LONGRUN */
 identify_nic(db); 

 /* GPIO0 on pre-activate PHY */
 iow(db,0x1f,0x00);   /*REG_1F bit0 activate phyxcer*/

 /* Set PHY */
 db->op_mode = media_mode;
 set_PHY_mode(db);

 /* Init needed register value */
 db->reg0 = DM9000_REG00;
 if ( (db->nic_type != FASTETHER_NIC) && (db->op_mode & DM9000_1M_HPNA) )
  db->reg0 |= DM9000_EXT_MII;
 
 /* User passed argument */
 db->reg5 = reg5;
 db->reg8 = reg8;
 db->reg9 = reg9;
 db->rega = rega;

 /* Program operating register */
 iow(db, 0x00, db->reg0);
 iow(db, 0x02, 0);  /* TX Polling clear */
 iow(db, 0x08, 0x3f);  /* Less 3Kb, 200us */
 iow(db, 0x09, db->reg9); /* Flow Control : High/Low Water */
 iow(db, 0x0a, db->rega); /* Flow Control */
 iow(db, 0x2f, 0);  /* Special Mode */
 iow(db, 0x01, 0x2c);  /* clear TX status */
 iow(db, 0xfe, 0x0f);   /* Clear interrupt status */
 
 /* Set address filter table */
 dm9000_hash_table(dev);

 /* Activate DM9000 */
 iow(db, 0x05, db->reg5 | 1); /* RX enable */
 iow(db, 0xff, DM9000_REGFF);  /* Enable TX/RX interrupt mask */
 
 /* Init Driver variable */
 db->link_failed = 1;
 db->tx_pkt_cnt = 0;
 db->queue_pkt_len = 0;
 dev->trans_start = 0;
 spin_lock_init(&db->lock); 
}

/*
  Hardware start transmission.
  Send a packet to media from the upper layer.
*/
static int dmfe_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
 board_info_t *db = (board_info_t *)dev->priv;
 char * data_ptr;
 int i, tmplen;

 DMFE_DBUG(0, "dmfe_start_xmit", 0);

 if (db->tx_pkt_cnt > 1) 
  return 1; 
 
 netif_stop_queue(dev); 

 /* Disable all interrupt */
 iow(db, 0xff, 0x80);
 
 /* Move data to DM9000 TX RAM */
 data_ptr = (char *)skb->data;
 outb(0xf8, db->ioaddr);
 
 if (db->io_mode == DM9000_BYTE_MODE) {
  /* Byte mode */
  for (i = 0; i < skb->len; i++)
   outb((data_ptr & 0xff), db->io_data);
 } else if (db->io_mode == DM9000_WORD_MODE) {
  /* Word mode */
  tmplen = (skb->len + 1) / 2;
  for (i = 0; i < tmplen; i++)
           outw(((u16 *)data_ptr), db->io_data);
 } else {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -