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

📄 dm9000.txt

📁 DM9000的网卡驱动模块。开发环境是LINUX+ARM9。
💻 TXT
📖 第 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.
 *
 *        03/03/2004    Sascha Hauer <s.hauer@pengutronix.de>
 *                      Port to 2.6 kernel
 *
 *	  24-Sep-2004   Ben Dooks <ben@simtec.co.uk>
 *			Cleanup of code to remove ifdefs
 *			Allowed platform device data to influence access width
 *			Reformatting areas of code
 *
 *        17-Mar-2005   Sascha Hauer <s.hauer@pengutronix.de>
 *                      * removed 2.4 style module parameters
 *                      * removed removed unused stat counter and fixed
 *                        net_device_stats
 *                      * introduced tx_timeout function
 *                      * reworked locking
 *
 *	  01-Jul-2005   Ben Dooks <ben@simtec.co.uk>
 *			* fixed spinlock call without pointer
 *			* ensure spinlock is initialised
 *
 *	  09-Jan-2007   Si Baozhi <sbz@spgui.org>
 *	  		* fixed resource,interrupt and some register
 *	  		* for running on S3C2440 board;		
 */

#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/dm9000.h>
#include <linux/delay.h>
#include <linux/platform_device.h>

#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/io.h>

#include "dm9000.h"

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

#define CARDNAME "dm9000"
#define PFX CARDNAME ": "

#define DM9000_DEBUG 0

#if DM9000_DEBUG > 2
#define PRINTK3(args...)  printk(CARDNAME ": " args)
#else
#define PRINTK3(args...)  do { } while(0)
#endif

#if DM9000_DEBUG > 1
#define PRINTK2(args...)  printk(CARDNAME ": " args)
#else
#define PRINTK2(args...)  do { } while(0)
#endif

#if DM9000_DEBUG > 0
#define PRINTK1(args...)  printk(CARDNAME ": " args)
#define PRINTK(args...)   printk(CARDNAME ": " args)
#else
#define PRINTK1(args...)  do { } while(0)
#define PRINTK(args...)   printk(KERN_DEBUG args)
#endif

#define res_size(_r) (((_r)->end - (_r)->start) + 1)
/*
 * Transmit timeout, default 5 seconds.
 */
#if 0
static int watchdog = 5000;
module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
static void dmfe_packet_receive(unsigned long);
DECLARE_TASKLET(dmfe_rx_tasklet,dmfe_packet_receive,0);
#endif


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

	void __iomem *io_addr;	/* Register I/O base address */
	void __iomem *io_data;	/* Data I/O address */

	u16 tx_pkt_cnt;
	u16 queue_pkt_len;
	u8 io_mode;		/* 0:word, 2:byte */

	struct resource	*addr_res;   /* resources found */
	struct resource	*addr_req;   /* resources requested */
	struct resource *irq_res;

	struct timer_list timer;
	struct net_device_stats stats;
	spinlock_t lock;

        u32 runt_length_counter;        /* counter: RX length < 64byte */
        u32 long_length_counter;        /* counter: RX length > 1514byte */
        u32 reset_counter;    
        u32 reset_tx_timeout;           /* RESET caused by TX Timeout */
        u32 reset_rx_status;            /* RESET caused by RX Statsus wrong */
	  
	u8 op_mode;                     /* PHY operation mode */
	u8 nic_type;                    /* NIC type */
	     
	u8 reg0, reg5, reg8, reg9, rega;/* registers saved */
	u8 link_failed;                 /* Ever link failed */
	u8 device_wait_reset;           /* device state */
	     
} board_info_t;

/* function declaration ------------------------------------- */
static int dm9000_probe(struct platform_device *);
static int dm9000_open(struct net_device *);
static int dm9000_start_xmit(struct sk_buff *, struct net_device *);
static int dm9000_stop(struct net_device *);
static int dm9000_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static struct net_device_stats *dm9000_get_stats(struct net_device *);

static void dm9000_init_dm9000(struct net_device *);
static void dm9000_timer(unsigned long);
static irqreturn_t dm9000_interrupt(int, void *, struct pt_regs *);

static u16 phy_read(board_info_t *db, int reg);
static void phy_write(board_info_t *db, int reg, u16 value);
static void dm9000_rx(struct net_device *);
static void dm9000_tx_done(struct net_device *dev, board_info_t *db);
static void dm9000_hash_table(struct net_device *);

static u8 nfloor  = 0;
static uint32_t  polling_times = 0;
static unsigned char eth_mac_addr[6] = {0x80, 0xe2, 0x66, 0x60, 0x00, 0x01};
                                                                                                                            

/* DM9000 network board routine ---------------------------- */
/**
 * Read a byte from I/O port
 **/
static u8 ior(board_info_t * db, int reg)
{
	writeb(reg, db->io_addr);
	return readb(db->io_data);
}

/**
 * Write a byte to I/O port
 **/

static void iow(board_info_t * db, int reg, int value)
{
	writeb(reg, db->io_addr);
	writeb(value, db->io_data);
}

/**
 *  Read a word from phyxcer
 **/
static u16 phy_read(board_info_t *db, int reg)
{
	/* Fill the phyxcer register into REG_0C */
	iow(db, 0xc, DM9000_PHY | reg);

	iow(db, 0xb, 0xc); 	/* Issue phyxcer read command */
	udelay(100);		/* Wait read complete */
	iow(db, 0xb, 0x0); 	/* Clear phyxcer read command */

	/* The read data keeps on REG_0D & REG_0E */
	return ( ior(db, 0xe) << 8 ) | ior(db, 0xd);
}

/**
 *  Write a word to phyxcer
 **/
static void phy_write(board_info_t *db, int reg, u16 value)
{
	/* Fill the phyxcer register into REG_0C */
	iow(db, 0xc, DM9000_PHY | reg);

	/* Fill the written data into REG_0D & REG_0E */
	iow(db, 0xd, (value & 0xff));
	iow(db, 0xe, ( (value >> 8) & 0xff));

	iow(db, 0xb, 0xa);		/* Issue phyxcer write command */
	udelay(500);			/* Wait write complete */
	iow(db, 0xb, 0x0);		/* Clear phyxcer write command */
}

/**
 * Reset the chip
 **/
static void dm9000_reset(board_info_t * db)
{
	PRINTK1("dm9000x: resetting db->io_addr = 0x%x db->id_data = 0x%x\n",(int)db->io_addr,(int)db->io_data);
	/* RESET device */
	writeb(DM9000_NCR, db->io_addr);
	udelay(200);
	writeb(NCR_RST, db->io_data);
	udelay(200);
}

/** dm9000_release_board
 *
 * release a board, and any mapped resources
 **/

static void dm9000_release_board(struct platform_device *pdev, struct board_info *db)
{
	/* unmap our resources */
	iounmap(db->io_addr);
	
	/* release the resources */
	if (db->addr_req != NULL) {
		release_resource(db->addr_req);
		kfree(db->addr_req);
	}
}

/** 
 * 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, DM9000_GPCR, 0x01);/* Let GPIO0 output */
	iow(db, DM9000_GPR, 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_dm9000(struct net_device *dev)
{
	board_info_t *db = (board_info_t *) dev->priv;

	PRINTK1("entering %s\n",__FUNCTION__);

	polling_times=0;
	/* RESET device */
	iow(db,DM9000_GPR,0);
	udelay(100);	
	/* delay 100us */
	iow(db,DM9000_NCR,3);
	udelay(100);
	iow(db,DM9000_NCR,3);
	udelay(100);
	/* I/O mode */
	db->io_mode = ior(db,DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
	if(!db->io_mode) PRINTK1(" DM9000 work in 16bit bus width\n", 0);
	else if(db->io_mode==2) PRINTK1(" DM9000 work in 8bit bus width\n", 0);
	else if(db->io_mode==1) PRINTK1(" DM9000 work in 32bit bus width\n", 0);
	else PRINTK1("DM9000 work in wrong bus width,error\n", 0);
	
	/* NIC Type: FASTETHER, HOMERUN, LONGRUN */
	identify_nic(db);	

	/* Set PHY */
	db->op_mode = DM9000_AUTO;
	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 = DM9000_REG05;
	db->reg8 = DM9000_REG08;
	db->reg9 = DM9000_REG09;
	db->rega = DM9000_REG0A;

	/* Program operating register */
	iow(db, DM9000_NCR, db->reg0);
	iow(db, DM9000_TCR, 0);		/* TX Polling clear */
	iow(db, DM9000_BPTR, 0x3f);		/* Less 3Kb, 200us */
	iow(db, DM9000_FCTR, db->reg9);	/* Flow Control : High/Low Water */
	iow(db, DM9000_FCR, db->rega);	/* Flow Control */
	iow(db, DM9000_SMCR, 0);		/* Special Mode */
	iow(db, DM9000_NSR, 0x2c);		/* clear TX status */
	//iow(db,DM9000_ISR, 0x0f); 		/* Clear interrupt status */
 
	/* Set address filter table */
	dm9000_hash_table(dev);

	/* Activate DM9000 */
	iow(db, DM9000_RCR, 0x31);	/* RX enable */
	iow(db, DM9000_IMR, 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;
}

static void dm9000_shutdown(struct net_device *dev)
{
	board_info_t *db = (board_info_t *) dev->priv;

	/* RESET device */
	phy_write(db, 0x00,0x8000);	/* PHY RESET */
        iow(db, DM9000_GPR, 0x01);      /* Power-Down PHY */
        iow(db, DM9000_IMR, 0x80);            /* Disable all interrupt */
        iow(db, DM9000_RCR, 0x00);  
}

/**
 * Search DM9000 board, allocate space and register it
 **/
static int
dm9000_probe(struct platform_device *pdev)
{
	struct board_info *db;	/* Point a board information structure */
	struct net_device *ndev;
	int ret = 0;
	int i;
	u32 id_val;

	/* Init network device */
	ndev = alloc_etherdev(sizeof (struct board_info));
	if (!ndev) {
		printk("%s: could not allocate device.\n", CARDNAME);return -ENOMEM;
	}

	SET_MODULE_OWNER(ndev);
	SET_NETDEV_DEV(ndev, &pdev->dev);

	/* setup board info structure */
	db = (struct board_info *) ndev->priv;
	memset(db, 0, sizeof (*db));

	spin_lock_init(&db->lock);

        db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (db->addr_res == NULL || db->irq_res == NULL) {
		printk(KERN_ERR PFX "insufficient resources\n");
		ret = -ENOENT; goto release;
	}

		
	i = res_size(db->addr_res);
	db->addr_req = request_mem_region(db->addr_res->start, i,pdev->name);

	if (db->addr_req == NULL) {
		printk(KERN_ERR PFX "cannot claim address reg area\n");
		ret = -EIO; goto release;
	}

	db->io_addr = ioremap(db->addr_res->start, 0x400);

	if (db->io_addr == NULL) {
		printk(KERN_ERR "failed to ioremap address reg\n");
		ret = -EINVAL; goto release;
	}

	db->io_data =( void __iomem *) ((u32)db->io_addr + 4);

	ndev->base_addr = (unsigned long)db->io_addr;
	ndev->irq	= db->irq_res->start;
		
	dm9000_reset(db);

	/* try two times, DM9000 sometimes gets the first read wrong */
	for (i = 0; i < 2; i++) {
		id_val  = ior(db, DM9000_VIDL);
		id_val |= (u32)ior(db, DM9000_VIDH) << 8;
		id_val |= (u32)ior(db, DM9000_PIDL) << 16;
		id_val |= (u32)ior(db, DM9000_PIDH) << 24;

		if (id_val == DM9000_ID) break;
		printk("%s: read wrong id 0x%08x\n", CARDNAME, id_val);
	}

	if (id_val != DM9000_ID) {
		printk("%s: wrong id: 0x%08x\n", CARDNAME, id_val);
		goto release;
	}

	/* from this point we assume that we have found a DM9000 */
	/* driver system function */
	ether_setup(ndev);

	ndev->open		 = &dm9000_open;
	ndev->hard_start_xmit    = &dm9000_start_xmit;
	ndev->stop		 = &dm9000_stop;
	ndev->get_stats		 = &dm9000_get_stats;
	ndev->set_multicast_list = &dm9000_hash_table;
      	ndev->do_ioctl           = &dm9000_do_ioctl;
#if 0
	ndev->tx_timeout         = &dm9000_timeout;
	ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
#endif
	/*  set MAC */
	for (i=0; i<6; i++) {
	      ndev->dev_addr[i] = eth_mac_addr[i];
	}

        /* Request IO from system */
        iow(db,0x1d,0x80);
        /* request_region(base, 2, ndev->name); */

	platform_set_drvdata(pdev, ndev);
	ret = register_netdev(ndev);

	if (ret == 0) {
		printk("%s: dm9000 at %p,%p IRQ %d MAC: ",ndev->name,  db->io_addr, db->io_data, ndev->irq);
		for (i = 0; i < 5; i++) {
			printk("%02x:", ndev->dev_addr[i]);

⌨️ 快捷键说明

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