gt96100eth.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,572 行 · 第 1/3 页

C
1,572
字号
/* * Copyright 2000, 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. *         	stevel@mvista.com or source@mvista.com * *  This program is free software; you can distribute it and/or modify it *  under the terms of the GNU General Public License (Version 2) as *  published by the Free Software Foundation. * *  This program is distributed in the hope 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. * *  You should have received a copy of the GNU General Public License along *  with this program; if not, write to the Free Software Foundation, Inc., *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * * Ethernet driver for the MIPS GT96100 Advanced Communication Controller. *  *  Revision history *     *    11.11.2001  Moved to 2.4.14, ppopov@mvista.com.  Modified driver to add *                proper gt96100A support. *    12.05.2001  Moved eth port 0 to irq 3 (mapped to GT_SERINT0 on EV96100A) *                in order for both ports to work. Also cleaned up boot *                option support (mac address string parsing), fleshed out *                gt96100_cleanup_module(), and other general code cleanups *                <stevel@mvista.com>. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/in.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <linux/ctype.h>#include <asm/irq.h>#include <asm/bitops.h>#include <asm/io.h>#define DESC_BE 1#define DESC_DATA_BE 1#define GT96100_DEBUG 2#include "gt96100eth.h"// prototypesstatic void* dmaalloc(size_t size, dma_addr_t *dma_handle);static void dmafree(size_t size, void *vaddr);static void gt96100_delay(int msec);static int gt96100_add_hash_entry(struct net_device *dev,				  unsigned char* addr);static void read_mib_counters(struct gt96100_private *gp);static int read_MII(int phy_addr, u32 reg);static int write_MII(int phy_addr, u32 reg, u16 data);static int gt96100_init_module(void);static void gt96100_cleanup_module(void);static void dump_MII(int dbg_lvl, struct net_device *dev);static void dump_tx_desc(int dbg_lvl, struct net_device *dev, int i);static void dump_rx_desc(int dbg_lvl, struct net_device *dev, int i);static void dump_skb(int dbg_lvl, struct net_device *dev,		     struct sk_buff *skb);static void dump_hw_addr(int dbg_lvl, struct net_device *dev,			 const char* pfx, unsigned char* addr_str);static void update_stats(struct gt96100_private *gp);static void abort(struct net_device *dev, u32 abort_bits);static void hard_stop(struct net_device *dev);static void enable_ether_irq(struct net_device *dev);static void disable_ether_irq(struct net_device *dev);static int gt96100_probe1(struct pci_dev *pci, int port_num);static void reset_tx(struct net_device *dev);static void reset_rx(struct net_device *dev);static int gt96100_check_tx_consistent(struct gt96100_private *gp);static int gt96100_init(struct net_device *dev);static int gt96100_open(struct net_device *dev);static int gt96100_close(struct net_device *dev);static int gt96100_tx(struct sk_buff *skb, struct net_device *dev);static int gt96100_rx(struct net_device *dev, u32 status);static irqreturn_t gt96100_interrupt(int irq, void *dev_id, struct pt_regs *regs);static void gt96100_tx_timeout(struct net_device *dev);static void gt96100_set_rx_mode(struct net_device *dev);static struct net_device_stats* gt96100_get_stats(struct net_device *dev);extern char * __init prom_getcmdline(void);static int max_interrupt_work = 32;#define nibswap(x) ((((x) >> 4) & 0x0f) | (((x) << 4) & 0xf0))#define RUN_AT(x) (jiffies + (x))// For reading/writing 32-bit words and half-words from/to DMA memory#ifdef DESC_BE#define cpu_to_dma32 cpu_to_be32#define dma32_to_cpu be32_to_cpu#define cpu_to_dma16 cpu_to_be16#define dma16_to_cpu be16_to_cpu#else#define cpu_to_dma32 cpu_to_le32#define dma32_to_cpu le32_to_cpu#define cpu_to_dma16 cpu_to_le16#define dma16_to_cpu le16_to_cpu#endifstatic char mac0[18] = "00.02.03.04.05.06";static char mac1[18] = "00.01.02.03.04.05";MODULE_PARM(mac0, "c18");MODULE_PARM(mac1, "c18");MODULE_PARM_DESC(mac0, "MAC address for GT96100 ethernet port 0");MODULE_PARM_DESC(mac1, "MAC address for GT96100 ethernet port 1");/* * Info for the GT96100 ethernet controller's ports. */static struct gt96100_if_t {	struct net_device *dev;	unsigned int  iobase;   // IO Base address of this port	int           irq;      // IRQ number of this port	char         *mac_str;} gt96100_iflist[NUM_INTERFACES] = {	{		NULL,		GT96100_ETH0_BASE, GT96100_ETHER0_IRQ,		mac0	},	{		NULL,		GT96100_ETH1_BASE, GT96100_ETHER1_IRQ,		mac1	}};static inline const char*chip_name(int chip_rev){	switch (chip_rev) {	case REV_GT96100:		return "GT96100";	case REV_GT96100A_1:	case REV_GT96100A:		return "GT96100A";	default:		return "Unknown GT96100";	}}/*  DMA memory allocation, derived from pci_alloc_consistent.*/static void * dmaalloc(size_t size, dma_addr_t *dma_handle){	void *ret;		ret = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA, get_order(size));		if (ret != NULL) {		dma_cache_inv((unsigned long)ret, size);		if (dma_handle != NULL)			*dma_handle = virt_to_phys(ret);		/* bump virtual address up to non-cached area */		ret = (void*)KSEG1ADDR(ret);	}	return ret;}static void dmafree(size_t size, void *vaddr){	vaddr = (void*)KSEG0ADDR(vaddr);	free_pages((unsigned long)vaddr, get_order(size));}static void gt96100_delay(int ms){	if (in_interrupt())		return;	else {		current->state = TASK_INTERRUPTIBLE;		schedule_timeout(ms*HZ/1000);	}}static intparse_mac_addr(struct net_device *dev, char* macstr){	int i, j;	unsigned char result, value;		for (i=0; i<6; i++) {		result = 0;		if (i != 5 && *(macstr+2) != '.') {			err(__FILE__ "invalid mac address format: %d %c\n",			    i, *(macstr+2));			return -EINVAL;		}				for (j=0; j<2; j++) {			if (isxdigit(*macstr) &&			    (value = isdigit(*macstr) ? *macstr-'0' : 			     toupper(*macstr)-'A'+10) < 16) {				result = result*16 + value;				macstr++;			} else {				err(__FILE__ "invalid mac address "				    "character: %c\n", *macstr);				return -EINVAL;			}		}		macstr++; // step over '.'		dev->dev_addr[i] = result;	}	return 0;}static intread_MII(int phy_addr, u32 reg){	int timedout = 20;	u32 smir = smirOpCode | (phy_addr << smirPhyAdBit) |		(reg << smirRegAdBit);	// wait for last operation to complete	while (GT96100_READ(GT96100_ETH_SMI_REG) & smirBusy) {		// snooze for 1 msec and check again		gt96100_delay(1);		if (--timedout == 0) {			printk(KERN_ERR "%s: busy timeout!!\n", __FUNCTION__);			return -ENODEV;		}	}    	GT96100_WRITE(GT96100_ETH_SMI_REG, smir);	timedout = 20;	// wait for read to complete	while (!((smir = GT96100_READ(GT96100_ETH_SMI_REG)) & smirReadValid)) {		// snooze for 1 msec and check again		gt96100_delay(1);			if (--timedout == 0) {			printk(KERN_ERR "%s: timeout!!\n", __FUNCTION__);			return -ENODEV;		}	}	return (int)(smir & smirDataMask);}static voiddump_tx_desc(int dbg_lvl, struct net_device *dev, int i){	struct gt96100_private *gp = netdev_priv(dev);	gt96100_td_t *td = &gp->tx_ring[i];	dbg(dbg_lvl, "Tx descriptor at 0x%08lx:\n", virt_to_phys(td));	dbg(dbg_lvl,	    "    cmdstat=%04x, byte_cnt=%04x, buff_ptr=%04x, next=%04x\n",	    dma32_to_cpu(td->cmdstat),	    dma16_to_cpu(td->byte_cnt),	    dma32_to_cpu(td->buff_ptr),	    dma32_to_cpu(td->next));}static voiddump_rx_desc(int dbg_lvl, struct net_device *dev, int i){	struct gt96100_private *gp = netdev_priv(dev);	gt96100_rd_t *rd = &gp->rx_ring[i];	dbg(dbg_lvl, "Rx descriptor at 0x%08lx:\n", virt_to_phys(rd));	dbg(dbg_lvl, "    cmdstat=%04x, buff_sz=%04x, byte_cnt=%04x, "	    "buff_ptr=%04x, next=%04x\n",	    dma32_to_cpu(rd->cmdstat),	    dma16_to_cpu(rd->buff_sz),	    dma16_to_cpu(rd->byte_cnt),	    dma32_to_cpu(rd->buff_ptr),	    dma32_to_cpu(rd->next));}static intwrite_MII(int phy_addr, u32 reg, u16 data){	int timedout = 20;	u32 smir = (phy_addr << smirPhyAdBit) |		(reg << smirRegAdBit) | data;	// wait for last operation to complete	while (GT96100_READ(GT96100_ETH_SMI_REG) & smirBusy) {		// snooze for 1 msec and check again		gt96100_delay(1);			if (--timedout == 0) {			printk(KERN_ERR "%s: busy timeout!!\n", __FUNCTION__);			return -1;		}	}	GT96100_WRITE(GT96100_ETH_SMI_REG, smir);	return 0;}static voiddump_MII(int dbg_lvl, struct net_device *dev){	int i, val;	struct gt96100_private *gp = netdev_priv(dev);    	if (dbg_lvl <= GT96100_DEBUG) {		for (i=0; i<7; i++) {			if ((val = read_MII(gp->phy_addr, i)) >= 0)				printk("MII Reg %d=%x\n", i, val);		}		for (i=16; i<21; i++) {			if ((val = read_MII(gp->phy_addr, i)) >= 0)				printk("MII Reg %d=%x\n", i, val);		}	}}static voiddump_hw_addr(int dbg_lvl, struct net_device *dev, const char* pfx,	     unsigned char* addr_str){	int i;	char buf[100], octet[5];    	if (dbg_lvl <= GT96100_DEBUG) {		strcpy(buf, pfx);		for (i = 0; i < 6; i++) {			sprintf(octet, "%2.2x%s",				addr_str[i], i<5 ? ":" : "\n");			strcat(buf, octet);		}		info("%s", buf);	}}static voiddump_skb(int dbg_lvl, struct net_device *dev, struct sk_buff *skb){	int i;	unsigned char* skbdata;    	if (dbg_lvl <= GT96100_DEBUG) {		dbg(dbg_lvl, "%s: skb=%p, skb->data=%p, skb->len=%d\n",		    __FUNCTION__, skb, skb->data, skb->len);		skbdata = (unsigned char*)KSEG1ADDR(skb->data);    		for (i=0; i<skb->len; i++) {			if (!(i % 16))				printk(KERN_DEBUG "\n   %3.3x: %2.2x,",				       i, skbdata[i]);			else				printk(KERN_DEBUG "%2.2x,", skbdata[i]);		}		printk(KERN_DEBUG "\n");	}}static intgt96100_add_hash_entry(struct net_device *dev, unsigned char* addr){	struct gt96100_private *gp = netdev_priv(dev);	//u16 hashResult, stmp;	//unsigned char ctmp, hash_ea[6];	u32 tblEntry1, tblEntry0, *tblEntryAddr;	int i;	tblEntry1 = hteValid | hteRD;	tblEntry1 |= (u32)addr[5] << 3;	tblEntry1 |= (u32)addr[4] << 11;	tblEntry1 |= (u32)addr[3] << 19;	tblEntry1 |= ((u32)addr[2] & 0x1f) << 27;	dbg(3, "%s: tblEntry1=%x\n", __FUNCTION__, tblEntry1);	tblEntry0 = ((u32)addr[2] >> 5) & 0x07;	tblEntry0 |= (u32)addr[1] << 3;	tblEntry0 |= (u32)addr[0] << 11;	dbg(3, "%s: tblEntry0=%x\n", __FUNCTION__, tblEntry0);#if 0	for (i=0; i<6; i++) {		// nibble swap		ctmp = nibswap(addr[i]);		// invert every nibble		hash_ea[i] = ((ctmp&1)<<3) | ((ctmp&8)>>3) |			((ctmp&2)<<1) | ((ctmp&4)>>1);		hash_ea[i] |= ((ctmp&0x10)<<3) | ((ctmp&0x80)>>3) |			((ctmp&0x20)<<1) | ((ctmp&0x40)>>1);	}	dump_hw_addr(3, dev, "%s: nib swap/invt addr=", __FUNCTION__, hash_ea);    	if (gp->hash_mode == 0) {		hashResult = ((u16)hash_ea[0] & 0xfc) << 7;		stmp = ((u16)hash_ea[0] & 0x03) |			(((u16)hash_ea[1] & 0x7f) << 2);		stmp ^= (((u16)hash_ea[1] >> 7) & 0x01) |			((u16)hash_ea[2] << 1);		stmp ^= (u16)hash_ea[3] | (((u16)hash_ea[4] & 1) << 8);		hashResult |= stmp;	} else {		return -1; // don't support hash mode 1	}	dbg(3, "%s: hashResult=%x\n", __FUNCTION__, hashResult);	tblEntryAddr =		(u32 *)(&gp->hash_table[((u32)hashResult & 0x7ff) << 3]);    	dbg(3, "%s: tblEntryAddr=%p\n", tblEntryAddr, __FUNCTION__);	for (i=0; i<HASH_HOP_NUMBER; i++) {		if ((*tblEntryAddr & hteValid) &&		    !(*tblEntryAddr & hteSkip)) {			// This entry is already occupied, go to next entry			tblEntryAddr += 2;			dbg(3, "%s: skipping to %p\n", __FUNCTION__, 			    tblEntryAddr);		} else {			memset(tblEntryAddr, 0, 8);			tblEntryAddr[1] = cpu_to_dma32(tblEntry1);			tblEntryAddr[0] = cpu_to_dma32(tblEntry0);			break;		}	}	if (i >= HASH_HOP_NUMBER) {		err("%s: expired!\n", __FUNCTION__);		return -1; // Couldn't find an unused entry	}#else	tblEntryAddr = (u32 *)gp->hash_table;	for (i=0; i<RX_HASH_TABLE_SIZE/4; i+=2) {		tblEntryAddr[i+1] = cpu_to_dma32(tblEntry1);		tblEntryAddr[i] = cpu_to_dma32(tblEntry0);	}#endif    	return 0;}static voidread_mib_counters(struct gt96100_private *gp){	u32* mib_regs = (u32*)&gp->mib;	int i;    	for (i=0; i<sizeof(mib_counters_t)/sizeof(u32); i++)		mib_regs[i] = GT96100ETH_READ(gp, GT96100_ETH_MIB_COUNT_BASE +					      i*sizeof(u32));}static voidupdate_stats(struct gt96100_private *gp){	mib_counters_t *mib = &gp->mib;	struct net_device_stats *stats = &gp->stats;    	read_mib_counters(gp);    	stats->rx_packets = mib->totalFramesReceived;	stats->tx_packets = mib->framesSent;	stats->rx_bytes = mib->totalByteReceived;	stats->tx_bytes = mib->byteSent;	stats->rx_errors = mib->totalFramesReceived - mib->framesReceived;	//the tx error counters are incremented by the ISR	//rx_dropped incremented by gt96100_rx	//tx_dropped incremented by gt96100_tx	stats->multicast = mib->multicastFramesReceived;	// collisions incremented by gt96100_tx_complete	stats->rx_length_errors = mib->oversizeFrames + mib->fragments;	// The RxError condition means the Rx DMA encountered a	// CPU owned descriptor, which, if things are working as	// they should, means the Rx ring has overflowed.	stats->rx_over_errors = mib->macRxError;	stats->rx_crc_errors = mib->cRCError;}static voidabort(struct net_device *dev, u32 abort_bits){	struct gt96100_private *gp = netdev_priv(dev);	int timedout = 100; // wait up to 100 msec for hard stop to complete	dbg(3, "%s\n", __FUNCTION__);	// Return if neither Rx or Tx abort bits are set	if (!(abort_bits & (sdcmrAR | sdcmrAT)))		return;	// make sure only the Rx/Tx abort bits are set	abort_bits &= (sdcmrAR | sdcmrAT);    	spin_lock(&gp->lock);	// abort any Rx/Tx DMA immediately	GT96100ETH_WRITE(gp, GT96100_ETH_SDMA_COMM, abort_bits);

⌨️ 快捷键说明

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