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

📄 dm9000x.c

📁 davicom公司的10/100M以太网接口芯片dm9000在Linux下的驱动源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
  dm9000x.c: Version 1.25 04/28/2004
  
        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>:
			Added tx/rx 32 bit mode.
			Cleaned up for kernel merge.
			
v1.21  08/26/2003       Fixed phy reset.

v1.22  09/09/2003       Fixed power-on reset.
       09/30/2003	Spenser Tsai
			Fixed 32-bit mode bug (Excessive Collision and Late Collision)

v1.23  11/05/2003	Added Auto-MDIX function
v1.24  01/15/2004	Modify Full-Duplex mode
v1.25  04/28/2004	Default Flow control function
*/

#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 <linux/crc32.h>

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

#define DM9000_ID		0x90000A46

#define DM9000_NCR		0x00
#define DM9000_NSR		0x01
#define DM9000_REG05		0x30	/* SKIP_CRC/SKIP_LONG */
#define DM9000_REG08		0x37
#define DM9000_REG09		0x38
#define DM9000_REG0A		0x29	/* Flow Control Enable */ 
#define DM9000_GPCR		0x1e	/* General purpose control register */
#define DM9000_GPR		0x1f	/* General purpose register */
#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		0x300
#define DM9000_MAX_IO		0x370
#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(AUTOMDIX)
#define DMFE_TIMER_MDIX	jiffies+(HZ*1)  /* timer wakeup time : 1 second */
#endif

#if defined(DM9000_DEBUG)
#define DMFE_DBUG(dbug_now, msg, vaule)\
if (dmfe_debug||dbug_now) printk(KERN_ERR "dmfe: %s %x\n", msg, vaule)
#else
#define DMFE_DBUG(dbug_now, msg, vaule)\
if (dbug_now) printk(KERN_ERR "dmfe: %s %x\n", 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 
};

// Active operation mode  - Spenser 10/6
enum DM9000_OP_mode {
	OP_10MHD	= 1,
	OP_10MFD	= 2,
	OP_100MHD	= 4,
	OP_100MFD	= 8
};

enum DM9000_SF_mode{
	VLAN_Enable	= 1,
	FlowControl	= 2,
	TxPausePacket	= 4
};

/* 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 */

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

	u16 tx_pkt_cnt;
	u16 sent_pkt_len, queue_pkt_len;
	u16 queue_start_addr;
	u16 dbug_cnt;
	u16 Preg0;
	u16 Preg4;

	u8 reg0, reg5, reg8, reg9, rega;/* registers saved */
	u8 op_mode;			/* PHY operation mode */
	u8 active_op_mode;	// Real PHY operation mode -Spenser 10/6
	u8 io_mode;			/* 0:word, 2:byte */
	u8 phy_addr;
	u8 link_failed;			/* Ever link failed */
	u8 nsr;				/*Network Status Register */
	u8 link_status;			/* Detect link state */
	//u8 link_states;
	u8 mdix;			/* MDIX */
	u8 device_wait_reset;		/* device state */
	u8 nic_type;			/* NIC type */
	u8 ncr;

	struct timer_list timer, mdix_timer;
	struct net_device_stats stats;
	unsigned char srom[128];
	spinlock_t lock;

} board_info_t;

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

/* For module input parameter */
static int debug      = 0;
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     = 3;

/* function declaration ------------------------------------- */
int dmfe_probe(struct net_device *);
static int dmfe_open(struct net_device *);
static int dmfe_start_xmit(struct sk_buff *, struct net_device *);
static void dmfe_tx_done(unsigned long);
static void dmfe_packet_receive(unsigned long);
#if 0
#if defined(EMU32) || (HDX32)
static int dmfe_start_xmit32(struct sk_buff *, struct net_device *);
static void dmfe_tx32_done(unsigned long);
static void dmfe_packet32_receive(unsigned long);
#endif
#endif
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 dmfe_init_dm9000(struct net_device *);
static void dmfe_reset_dm9000(struct net_device *);
static unsigned long cal_CRC(unsigned char *, unsigned int, u8);
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 *);
#if defined(AUTOMDIX)
static void dmfe_mdix_timer(unsigned long);
#endif

DECLARE_TASKLET(dmfe_rx_tasklet,dmfe_packet_receive,0);
DECLARE_TASKLET(dmfe_tx_tasklet,dmfe_tx_done,0);

/* DM9000 network baord routine ---------------------------- */

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

	DMFE_DBUG(0, "dmfe_probe()",0);


	/* Search All DM9000 NIC */
	do {
		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;
#if defined(EMU32) || (HDX32)
			dev->hard_start_xmit	= &dmfe_start_xmit32;
#endif
			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);

			/* Read SROM content */
			for (i=0; i<64; i++)
				((u16 *)db->srom)[i] = read_srom_word(db, i);

			/* Set Node Address */
			for (i=0; i<6; i++)
				dev->dev_addr[i] = db->srom[i];

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

		}
		iobase += 0x10;
	}while(!dm9000_found && iobase <= DM9000_MAX_IO);

#if 0
	if (dm9000_found)
		printk("dm9000 found\n");
	else
		printk("<dm9000 not found\n");
#endif 
	return dm9000_found ? 0:-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 */
	dmfe_init_dm9000(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);	//Move to DM9000 initiallization was finished.

#if defined(AUTOMDIX)
	/* set and active a timer process for Auto-MDIX */
	init_timer(&db->mdix_timer);
	db->mdix_timer.expires	= DMFE_TIMER_MDIX;
	db->mdix_timer.data	= (unsigned long)dev;
	db->mdix_timer.function = &dmfe_mdix_timer;
	add_timer(&db->mdix_timer);	
#endif
	
	netif_start_queue(dev);

	return 0;
}

/* Set PHY operationg mode
*/
static void set_PHY_mode(board_info_t *db)
{
	//u16 phy_reg4 = 0x05e1;
	//u16 phy_reg0 = 0x3100;
	u16 phy_reg0 = 0x1000;		/* Auto-negotiation & non-duplux mode*/
	u16 phy_reg4 = 0x01e1;		/* Default non flow control */
//	u16 mreg0;

	if ( !(db->op_mode & DM9000_AUTO) ) // op_mode didn't auto sense */
	{ 
		switch(db->op_mode) {
			case DM9000_10MHD:  phy_reg4 = 0x21; 
					    //phy_reg4 = 0x0421;
                        	            phy_reg0 = 0x0000;
					    //phy_reg0 = 0x1200;
					    break;
			case DM9000_10MFD:  phy_reg4 = 0x41; 
					    //phy_reg4 = 0x0441;
					    phy_reg0 = 0x1100;
                        	            //phy_reg0 = 0x1300; 
                                	    break;
			case DM9000_100MHD: phy_reg4 = 0x81; 
					    //phy_reg4 = 0x0481;
					    phy_reg0 = 0x2000; 
					    //phy_reg0 = 0x3200;
				    	    break;
			case DM9000_100MFD: phy_reg4 = 0x101; 
					    //phy_reg4 = 0x0501;
					    phy_reg0 = 0x3100;
				    	    //phy_reg0 =0x3300; 
				   	    break;
		} // end of switch
		//db->Preg0 = phy_reg0;
		//db->Preg4 = phy_reg4;
	
	} // end of if
	phy_write(db, 0, phy_reg0);
	phy_write(db, 4, 0x0400|phy_reg4);
	db->Preg0 = phy_reg0;
	db->Preg4 = phy_reg4 + 0x0400;

#if 0
		switch(db->sf_mode)
		{
		    case VLAN_Enable:
				        break;
		    case FlowControl:
		    case TxPausePacket:
					phy_write(db, 0, phy_reg0);
					phy_write(db, 4, 0x0400|phy_reg4);
					db->Preg0 = phy_reg0;
					db->Preg4 = phy_reg4 + 0x0400;
					break;
		    default:
					phy_write(db, 0, phy_reg0);
					phy_write(db, 4, phy_reg4);
					db->Preg0 = phy_reg0;
					db->Preg4 = phy_reg4;
					break;
		}
#endif
	//}
	//else{ // op_mode is auto sense */
	//	phy_write(db, 0, phy_reg0);
	//	phy_write(db, 4, phy_reg4);
	//}
	//db->Preg0 = phy_reg0;
	//db->Preg4 = phy_reg4;

		//phy_write(db, 0, phy_reg0);	/* Tmp */
		//phy_write(db, 4, phy_reg4);	/* Set PHY media mode */

	//iow(db, 0x08, 0x37);		/* Less 3Kb, 200us */
	//udelay(250);

⌨️ 快捷键说明

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