diff options
Diffstat (limited to 'linux/src/drivers/net')
83 files changed, 89482 insertions, 0 deletions
diff --git a/linux/src/drivers/net/3c501.c b/linux/src/drivers/net/3c501.c new file mode 100644 index 0000000..200b95c --- /dev/null +++ b/linux/src/drivers/net/3c501.c @@ -0,0 +1,856 @@ +/* 3c501.c: A 3Com 3c501 ethernet driver for linux. */ +/* +    Written 1992,1993,1994  Donald Becker + +    Copyright 1993 United States Government as represented by the +    Director, National Security Agency.  This software may be used and +    distributed according to the terms of the GNU Public License, +    incorporated herein by reference. + +    This is a device driver for the 3Com Etherlink 3c501. +    Do not purchase this card, even as a joke.  It's performance is horrible, +    and it breaks in many ways.   + +    The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +    Center of Excellence in Space Data and Information Sciences +       Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 +        +    Fixed (again!) the missing interrupt locking on TX/RX shifting. +    		Alan Cox <Alan.Cox@linux.org> +    		 +    Removed calls to init_etherdev since they are no longer needed, and +    cleaned up modularization just a bit. The driver still allows only +    the default address for cards when loaded as a module, but that's +    really less braindead than anyone using a 3c501 board. :) +		    19950208 (invid@msen.com) + +    Added traps for interrupts hitting the window as we clear and TX load +    the board. Now getting 150K/second FTP with a 3c501 card. Still playing +    with a TX-TX optimisation to see if we can touch 180-200K/second as seems +    theoretically maximum. +    		19950402 Alan Cox <Alan.Cox@linux.org> +    		 +    Some notes on this thing if you have to hack it.  [Alan] +     +    1]	Some documentation is available from 3Com. Due to the boards age +    	standard responses when you ask for this will range from 'be serious' +    	to 'give it to a museum'. The documentation is incomplete and mostly +    	of historical interest anyway. +    	 +    2]  The basic system is a single buffer which can be used to receive or +    	transmit a packet. A third command mode exists when you are setting +    	things up. +    	 +    3]	If it's transmitting it's not receiving and vice versa. In fact the  +    	time to get the board back into useful state after an operation is +    	quite large. +    	 +    4]	The driver works by keeping the board in receive mode waiting for a +    	packet to arrive. When one arrives it is copied out of the buffer +    	and delivered to the kernel. The card is reloaded and off we go. +    	 +    5]	When transmitting dev->tbusy is set and the card is reset (from +    	receive mode) [possibly losing a packet just received] to command +    	mode. A packet is loaded and transmit mode triggered. The interrupt +    	handler runs different code for transmit interrupts and can handle +    	returning to receive mode or retransmissions (yes you have to help +    	out with those too). +    	 +    Problems: +    	There are a wide variety of undocumented error returns from the card +    and you basically have to kick the board and pray if they turn up. Most  +    only occur under extreme load or if you do something the board doesn't +    like (eg touching a register at the wrong time). +     +    	The driver is less efficient than it could be. It switches through +    receive mode even if more transmits are queued. If this worries you buy +    a real ethernet card. +     +    	The combination of slow receive restart and no real multicast +    filter makes the board unusable with a kernel compiled for IP +    multicasting in a real multicast environment. That's down to the board,  +    but even with no multicast programs running a multicast IP kernel is +    in group 224.0.0.1 and you will therefore be listening to all multicasts. +    One nv conference running over that ethernet and you can give up. +     +*/ + +static const char *version = +    "3c501.c: 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov).\n"; + +/* + *	Braindamage remaining: + *	The 3c501 board. + */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/fcntl.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/config.h>	/* for CONFIG_IP_MULTICAST */ + +#include <asm/bitops.h> +#include <asm/io.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#define BLOCKOUT_2 + +/* A zero-terminated list of I/O addresses to be probed. +   The 3c501 can be at many locations, but here are the popular ones. */ +static unsigned int netcard_portlist[] = +   { 0x280, 0x300, 0}; + + +/* + *	Index to functions.  + */ +  +int el1_probe(struct device *dev); +static int  el1_probe1(struct device *dev, int ioaddr); +static int  el_open(struct device *dev); +static int  el_start_xmit(struct sk_buff *skb, struct device *dev); +static void el_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void el_receive(struct device *dev); +static void el_reset(struct device *dev); +static int  el1_close(struct device *dev); +static struct enet_statistics *el1_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + +#define EL1_IO_EXTENT	16 + +#ifndef EL_DEBUG +#define EL_DEBUG  0	/* use 0 for production, 1 for devel., >2 for debug */ +#endif			/* Anything above 5 is wordy death! */ +static int el_debug = EL_DEBUG; +  +/*  + *	Board-specific info in dev->priv.  + */ +  +struct net_local  +{ +    struct enet_statistics stats; +    int tx_pkt_start;		/* The length of the current Tx packet. */ +    int collisions;		/* Tx collisions this packet */ +    int loading;		/* Spot buffer load collisions */ +}; + + +#define RX_STATUS (ioaddr + 0x06) +#define RX_CMD	  RX_STATUS +#define TX_STATUS (ioaddr + 0x07) +#define TX_CMD	  TX_STATUS +#define GP_LOW 	  (ioaddr + 0x08) +#define GP_HIGH   (ioaddr + 0x09) +#define RX_BUF_CLR (ioaddr + 0x0A) +#define RX_LOW	  (ioaddr + 0x0A) +#define RX_HIGH   (ioaddr + 0x0B) +#define SAPROM	  (ioaddr + 0x0C) +#define AX_STATUS (ioaddr + 0x0E) +#define AX_CMD	  AX_STATUS +#define DATAPORT  (ioaddr + 0x0F) +#define TX_RDY 0x08		/* In TX_STATUS */ + +#define EL1_DATAPTR	0x08 +#define EL1_RXPTR	0x0A +#define EL1_SAPROM	0x0C +#define EL1_DATAPORT 	0x0f + +/* + *	Writes to the ax command register. + */ +  +#define AX_OFF	0x00			/* Irq off, buffer access on */ +#define AX_SYS  0x40			/* Load the buffer */ +#define AX_XMIT 0x44			/* Transmit a packet */ +#define AX_RX	0x48			/* Receive a packet */ +#define AX_LOOP	0x0C			/* Loopback mode */ +#define AX_RESET 0x80 + +/* + *	Normal receive mode written to RX_STATUS.  We must intr on short packets + *	to avoid bogus rx lockups. + */ +  +#define RX_NORM 0xA8		/* 0x68 == all addrs, 0xA8 only to me. */ +#define RX_PROM 0x68		/* Senior Prom, uhmm promiscuous mode. */ +#define RX_MULT 0xE8		/* Accept multicast packets. */ +#define TX_NORM 0x0A		/* Interrupt on everything that might hang the chip */ + +/* + *	TX_STATUS register.  + */ +  +#define TX_COLLISION 0x02 +#define TX_16COLLISIONS 0x04 +#define TX_READY 0x08 + +#define RX_RUNT 0x08 +#define RX_MISSED 0x01		/* Missed a packet due to 3c501 braindamage. */ +#define RX_GOOD	0x30		/* Good packet 0x20, or simple overflow 0x10. */ + + +/* + *	The boilerplate probe code. + */ +  +#ifdef HAVE_DEVLIST +struct netdev_entry el1_drv = {"3c501", el1_probe1, EL1_IO_EXTENT, netcard_portlist}; +#else + +int el1_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)	/* Check a single specified location. */ +		return el1_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; netcard_portlist[i]; i++)  +	{ +		int ioaddr = netcard_portlist[i]; +		if (check_region(ioaddr, EL1_IO_EXTENT)) +			continue; +		if (el1_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +/* + *	The actual probe.  + */  + +static int el1_probe1(struct device *dev, int ioaddr) +{ +	const char *mname;		/* Vendor name */ +	unsigned char station_addr[6]; +	int autoirq = 0; +	int i; + +	/* +	 *	Read the station address PROM data from the special port.   +	 */ +	  +	for (i = 0; i < 6; i++)  +	{ +		outw(i, ioaddr + EL1_DATAPTR); +		station_addr[i] = inb(ioaddr + EL1_SAPROM); +	} +	/* +	 *	Check the first three octets of the S.A. for 3Com's prefix, or +	 *	for the Sager NP943 prefix.  +	 */  +	  +	if (station_addr[0] == 0x02  &&  station_addr[1] == 0x60 +		&& station_addr[2] == 0x8c)  +	{ +		mname = "3c501"; +	} else if (station_addr[0] == 0x00  &&  station_addr[1] == 0x80 +	&& station_addr[2] == 0xC8)  +	{ +		mname = "NP943"; +    	} +    	else +		return ENODEV; + +	/* +	 *	Grab the region so we can find the another board if autoIRQ fails.  +	 */ + +	request_region(ioaddr, EL1_IO_EXTENT,"3c501"); + +	/*	 +	 *	We auto-IRQ by shutting off the interrupt line and letting it float +	 *	high. +	 */ + +	if (dev->irq < 2)  +	{ +		autoirq_setup(2); +		inb(RX_STATUS);		/* Clear pending interrupts. */ +		inb(TX_STATUS); +		outb(AX_LOOP + 1, AX_CMD); + +		outb(0x00, AX_CMD); +	 +		autoirq = autoirq_report(1); + +		if (autoirq == 0)  +		{ +			printk("%s probe at %#x failed to detect IRQ line.\n", +				mname, ioaddr); +			return EAGAIN; +		} +	} + +	outb(AX_RESET+AX_LOOP, AX_CMD);			/* Loopback mode. */ +	dev->base_addr = ioaddr; +	memcpy(dev->dev_addr, station_addr, ETH_ALEN); + +	if (dev->mem_start & 0xf) +		el_debug = dev->mem_start & 0x7; +	if (autoirq) +		dev->irq = autoirq; + +	printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n", dev->name, mname, dev->base_addr, +			autoirq ? "auto":"assigned ", dev->irq); +	    +#ifdef CONFIG_IP_MULTICAST +	printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n"); +#endif     + +	if (el_debug) +		printk("%s", version); + +	/* +	 *	Initialize the device structure.  +	 */ +	  +	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); +	if (dev->priv == NULL) +		return -ENOMEM; +	memset(dev->priv, 0, sizeof(struct net_local)); + +	/* +	 *	The EL1-specific entries in the device structure.  +	 */ +	  +	dev->open = &el_open; +	dev->hard_start_xmit = &el_start_xmit; +	dev->stop = &el1_close; +	dev->get_stats = &el1_get_stats; +	dev->set_multicast_list = &set_multicast_list; + +	/* +	 *	Setup the generic properties  +	 */ + +	ether_setup(dev); + +	return 0; +} + +/* + *	Open/initialize the board.  + */ +  +static int el_open(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if (el_debug > 2) +		printk("%s: Doing el_open()...", dev->name); + +	if (request_irq(dev->irq, &el_interrupt, 0, "3c501", NULL))  +		return -EAGAIN; + +	irq2dev_map[dev->irq] = dev; +	el_reset(dev); + +	dev->start = 1; + +	outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */ +	MOD_INC_USE_COUNT; +	return 0; +} + +static int el_start_xmit(struct sk_buff *skb, struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	unsigned long flags; +	 +	if(dev->interrupt)		/* May be unloading, don't stamp on */ +		return 1;		/* the packet buffer this time      */ + +	if (dev->tbusy)  +	{ +		if (jiffies - dev->trans_start < 20)  +		{ +			if (el_debug > 2) +				printk(" transmitter busy, deferred.\n"); +			return 1; +		} +		if (el_debug) +			printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n", +				dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS)); +		lp->stats.tx_errors++; +		outb(TX_NORM, TX_CMD); +		outb(RX_NORM, RX_CMD); +		outb(AX_OFF, AX_CMD);	/* Just trigger a false interrupt. */ +		outb(AX_RX, AX_CMD);	/* Aux control, irq and receive enabled */ +		dev->tbusy = 0; +		dev->trans_start = jiffies; +	} + +	if (skb == NULL)  +	{ +		dev_tint(dev); +		return 0; +	} + +	save_flags(flags); + +	/* +	 *	Avoid incoming interrupts between us flipping tbusy and flipping +	 *	mode as the driver assumes tbusy is a faithful indicator of card +	 *	state +	 */ +	  +	cli(); +	 +	/* +	 *	Avoid timer-based retransmission conflicts.  +	 */ +	  +	if (set_bit(0, (void*)&dev->tbusy) != 0) +	{ +		restore_flags(flags); +		printk("%s: Transmitter access conflict.\n", dev->name); +	} +	else +	{ +		int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN); +		unsigned char *buf = skb->data; + +load_it_again_sam: +		lp->tx_pkt_start = gp_start; +    		lp->collisions = 0; + +		/* +		 *	Command mode with status cleared should [in theory] +		 *	mean no more interrupts can be pending on the card. +		 */ +		  +#ifdef BLOCKOUT_1 +		disable_irq(dev->irq);		  +#endif	 +		outb_p(AX_SYS, AX_CMD); +		inb_p(RX_STATUS); +		inb_p(TX_STATUS); +	 +		lp->loading=1; +	 +		/*  +		 *	Turn interrupts back on while we spend a pleasant afternoon +		 *	loading bytes into the board  +		 */ + +		restore_flags(flags); +		outw(0x00, RX_BUF_CLR);		/* Set rx packet area to 0. */ +		outw(gp_start, GP_LOW);		/* aim - packet will be loaded into buffer start */ +		outsb(DATAPORT,buf,skb->len);	/* load buffer (usual thing each byte increments the pointer) */ +		outw(gp_start, GP_LOW);		/* the board reuses the same register */ +#ifndef BLOCKOUT_1		 +		if(lp->loading==2)		/* A receive upset our load, despite our best efforts */ +		{ +			if(el_debug>2) +				printk("%s: burped during tx load.\n", dev->name); +			goto load_it_again_sam;	/* Sigh... */ +		} +#endif +		outb(AX_XMIT, AX_CMD);		/* fire ... Trigger xmit.  */ +		lp->loading=0; +#ifdef BLOCKOUT_1		 +		enable_irq(dev->irq); +#endif		 +		dev->trans_start = jiffies; +	} + +	if (el_debug > 2) +		printk(" queued xmit.\n"); +	dev_kfree_skb (skb, FREE_WRITE); +	return 0; +} + + +/* + *	The typical workload of the driver: + *	Handle the ether interface interrupts.  + */ + +static void el_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +	struct device *dev = (struct device *)(irq2dev_map[irq]); +	struct net_local *lp; +	int ioaddr; +	int axsr;			/* Aux. status reg. */ + +	if (dev == NULL  ||  dev->irq != irq)  +	{ +		printk ("3c501 driver: irq %d for unknown device.\n", irq); +		return; +	} + +	ioaddr = dev->base_addr; +	lp = (struct net_local *)dev->priv; + +	/* +	 *	What happened ? +	 */ +	  +	axsr = inb(AX_STATUS); + +	/* +	 *	Log it +	 */ + +	if (el_debug > 3) +		printk("%s: el_interrupt() aux=%#02x", dev->name, axsr); +	if (dev->interrupt) +		printk("%s: Reentering the interrupt driver!\n", dev->name); +	dev->interrupt = 1; +#ifndef BLOCKOUT_1     +        if(lp->loading==1 && !dev->tbusy) +        	printk("%s: Inconsistent state loading while not in tx\n", +        		dev->name); +#endif        		 +#ifdef BLOCKOUT_3 +	lp->loading=2;		/* So we can spot loading interruptions */ +#endif + +	if (dev->tbusy)  +	{ +     +    		/* +    		 *	Board in transmit mode. May be loading. If we are +    		 *	loading we shouldn't have got this. +    		 */ +    	  +		int txsr = inb(TX_STATUS); +#ifdef BLOCKOUT_2		 +		if(lp->loading==1) +		{ +			if(el_debug > 2) +			{ +				printk("%s: Interrupt while loading [", dev->name); +				printk(" txsr=%02x gp=%04x rp=%04x]\n", txsr, inw(GP_LOW),inw(RX_LOW)); +			} +			lp->loading=2;		/* Force a reload */ +			dev->interrupt = 0; +			return; +		} +#endif +		if (el_debug > 6) +			printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),inw(RX_LOW)); + +		if ((axsr & 0x80) && (txsr & TX_READY) == 0)  +		{ +			/* +			 *	FIXME: is there a logic to whether to keep on trying or +			 *	reset immediately ? +			 */ +			if(el_debug>1) +				printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x" +			  		" gp=%03x rp=%03x.\n", dev->name, txsr, axsr, +			inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR)); +			dev->tbusy = 0; +			mark_bh(NET_BH); +		}  +		else if (txsr & TX_16COLLISIONS)  +		{ +			/* +			 *	Timed out +			 */ +			if (el_debug) +				printk("%s: Transmit failed 16 times, ethernet jammed?\n",dev->name); +			outb(AX_SYS, AX_CMD); +			lp->stats.tx_aborted_errors++; +		} +		else if (txsr & TX_COLLISION)  +		{	 +			/* +			 *	Retrigger xmit.  +			 */ +			  +			if (el_debug > 6) +				printk(" retransmitting after a collision.\n"); +			/* +			 *	Poor little chip can't reset its own start pointer +			 */ +			 +			outb(AX_SYS, AX_CMD); +			outw(lp->tx_pkt_start, GP_LOW); +			outb(AX_XMIT, AX_CMD); +			lp->stats.collisions++; +			dev->interrupt = 0; +			return; +		} +		else +		{ +			/* +			 *	It worked.. we will now fall through and receive +			 */ +			lp->stats.tx_packets++; +			if (el_debug > 6) +				printk(" Tx succeeded %s\n", +		       			(txsr & TX_RDY) ? "." : "but tx is busy!"); +			/* +			 *	This is safe the interrupt is atomic WRT itself. +			 */ + +			dev->tbusy = 0; +			mark_bh(NET_BH);	/* In case more to transmit */ +		} +	} +	else +	{ +    		/* +    		 *	In receive mode. +    		 */ +    	  +		int rxsr = inb(RX_STATUS); +		if (el_debug > 5) +			printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),inw(RX_LOW)); +		/* +		 *	Just reading rx_status fixes most errors.  +		 */ +		if (rxsr & RX_MISSED) +			lp->stats.rx_missed_errors++; +		else if (rxsr & RX_RUNT)  +		{	/* Handled to avoid board lock-up. */ +			lp->stats.rx_length_errors++; +			if (el_debug > 5)  +				printk(" runt.\n"); +		}  +		else if (rxsr & RX_GOOD)  +		{ +			/* +			 *	Receive worked. +			 */ +			el_receive(dev); +		} +		else +		{ +			/* +			 *	Nothing?  Something is broken! +			 */ +			if (el_debug > 2) +				printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n", +					dev->name, rxsr); +			el_reset(dev); +		} +		if (el_debug > 3) +			printk(".\n"); +	} + +	/* +	 *	Move into receive mode  +	 */ + +	outb(AX_RX, AX_CMD); +	outw(0x00, RX_BUF_CLR); +	inb(RX_STATUS);		/* Be certain that interrupts are cleared. */ +	inb(TX_STATUS); +	dev->interrupt = 0; +	return; +} + + +/* + *	We have a good packet. Well, not really "good", just mostly not broken. + *	We must check everything to see if it is good.  + */ + +static void el_receive(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int pkt_len; +	struct sk_buff *skb; + +	pkt_len = inw(RX_LOW); + +	if (el_debug > 4) +		printk(" el_receive %d.\n", pkt_len); + +	if ((pkt_len < 60)  ||  (pkt_len > 1536))  +	{ +		if (el_debug) +			printk("%s: bogus packet, length=%d\n", dev->name, pkt_len); +		lp->stats.rx_over_errors++; +		return; +	} +     +	/* +	 *	Command mode so we can empty the buffer +	 */ +      +	outb(AX_SYS, AX_CMD); +	skb = dev_alloc_skb(pkt_len+2); + +	/* +	 *	Start of frame +	 */ + +	outw(0x00, GP_LOW); +	if (skb == NULL)  +	{ +		printk("%s: Memory squeeze, dropping packet.\n", dev->name); +		lp->stats.rx_dropped++; +		return; +	} +	else +	{ +    		skb_reserve(skb,2);	/* Force 16 byte alignment */ +		skb->dev = dev; +		/* +		 *	The read increments through the bytes. The interrupt +		 *	handler will fix the pointer when it returns to  +		 *	receive mode. +		 */ +		insb(DATAPORT, skb_put(skb,pkt_len), pkt_len); +		skb->protocol=eth_type_trans(skb,dev); +		netif_rx(skb); +		lp->stats.rx_packets++; +	} +	return; +} + +static void  el_reset(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if (el_debug> 2) +		printk("3c501 reset..."); +	outb(AX_RESET, AX_CMD);		/* Reset the chip */ +	outb(AX_LOOP, AX_CMD);		/* Aux control, irq and loopback enabled */ +	{ +		int i; +		for (i = 0; i < 6; i++)	/* Set the station address. */ +			outb(dev->dev_addr[i], ioaddr + i); +	} +     +	outw(0, RX_BUF_CLR);		/* Set rx packet area to 0. */ +	cli();				/* Avoid glitch on writes to CMD regs */ +	outb(TX_NORM, TX_CMD);		/* tx irq on done, collision */ +	outb(RX_NORM, RX_CMD);		/* Set Rx commands. */ +	inb(RX_STATUS);			/* Clear status. */ +	inb(TX_STATUS); +	dev->interrupt = 0; +	dev->tbusy = 0; +	sti(); +} + +static int el1_close(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if (el_debug > 2) +		printk("%s: Shutting down ethercard at %#x.\n", dev->name, ioaddr); + +	dev->tbusy = 1; +	dev->start = 0; + +	/* +	 *	Free and disable the IRQ.  +	 */ + +	free_irq(dev->irq, NULL); +	outb(AX_RESET, AX_CMD);		/* Reset the chip */ +	irq2dev_map[dev->irq] = 0; + +	MOD_DEC_USE_COUNT; +	return 0; +} + +static struct enet_statistics *el1_get_stats(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	return &lp->stats; +} + +/* + *	Set or clear the multicast filter for this adaptor. + *			best-effort filtering. + */ + +static void set_multicast_list(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if(dev->flags&IFF_PROMISC) +	{     +		outb(RX_PROM, RX_CMD); +		inb(RX_STATUS); +	} +	else if (dev->mc_list || dev->flags&IFF_ALLMULTI) +	{ +		outb(RX_MULT, RX_CMD);	/* Multicast or all multicast is the same */ +		inb(RX_STATUS);		/* Clear status. */ +	} +	else  +	{ +		outb(RX_NORM, RX_CMD); +		inb(RX_STATUS); +	} +} + +#ifdef MODULE + +static char devicename[9] = { 0, }; + +static struct device dev_3c501 =  +{ +	devicename, /* device name is inserted by linux/drivers/net/net_init.c */ +	0, 0, 0, 0, +	0x280, 5, +	0, 0, 0, NULL, el1_probe  +}; + +static int io=0x280; +static int irq=5; +	 +int init_module(void) +{ +	dev_3c501.irq=irq; +	dev_3c501.base_addr=io; +	if (register_netdev(&dev_3c501) != 0) +		return -EIO; +	return 0; +} + +void cleanup_module(void) +{ +	/* +	 *	No need to check MOD_IN_USE, as sys_delete_module() checks. +	 */ +	  +	unregister_netdev(&dev_3c501); + +	/* +	 *	Free up the private structure, or leak memory :-)  +	 */ +	  +	kfree(dev_3c501.priv); +	dev_3c501.priv = NULL;	/* gets re-allocated by el1_probe1 */ + +	/* +	 *	If we don't do this, we can't re-insmod it later.  +	 */ +	release_region(dev_3c501.base_addr, EL1_IO_EXTENT); +} + +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer  -m486 -c -o 3c501.o 3c501.c" + *  kept-new-versions: 5 + * End: + */ diff --git a/linux/src/drivers/net/3c503.c b/linux/src/drivers/net/3c503.c new file mode 100644 index 0000000..8ce488d --- /dev/null +++ b/linux/src/drivers/net/3c503.c @@ -0,0 +1,690 @@ +/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */ +/* +    Written 1992-94 by Donald Becker. + +    Copyright 1993 United States Government as represented by the +    Director, National Security Agency.  This software may be used and +    distributed according to the terms of the GNU Public License, +    incorporated herein by reference. + +    The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +    Center of Excellence in Space Data and Information Sciences +       Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +    This driver should work with the 3c503 and 3c503/16.  It should be used +    in shared memory mode for best performance, although it may also work +    in programmed-I/O mode. + +    Sources: +    EtherLink II Technical Reference Manual, +    EtherLink II/16 Technical Reference Manual Supplement, +    3Com Corporation, 5400 Bayfront Plaza, Santa Clara CA 95052-8145 +     +    The Crynwr 3c503 packet driver. + +    Changelog: + +    Paul Gortmaker	: add support for the 2nd 8kB of RAM on 16 bit cards. +    Paul Gortmaker	: multiple card support for module users. +    rjohnson@analogic.com : Fix up PIO interface for efficient operation. + +*/ + +static const char *version = +    "3c503.c:v1.10 9/23/93  Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include <asm/io.h> +#include <asm/system.h> +#include <asm/byteorder.h> + +#include "8390.h" +#include "3c503.h" +#define WRD_COUNT 4  + +int el2_probe(struct device *dev); +int el2_pio_probe(struct device *dev); +int el2_probe1(struct device *dev, int ioaddr); + +/* A zero-terminated list of I/O addresses to be probed in PIO mode. */ +static unsigned int netcard_portlist[] = +	{ 0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0}; + +#define EL2_IO_EXTENT	16 + +#ifdef HAVE_DEVLIST +/* The 3c503 uses two entries, one for the safe memory-mapped probe and +   the other for the typical I/O probe. */ +struct netdev_entry el2_drv = +{"3c503", el2_probe, EL1_IO_EXTENT, 0}; +struct netdev_entry el2pio_drv = +{"3c503pio", el2_pioprobe1, EL1_IO_EXTENT, netcard_portlist}; +#endif + +static int el2_open(struct device *dev); +static int el2_close(struct device *dev); +static void el2_reset_8390(struct device *dev); +static void el2_init_card(struct device *dev); +static void el2_block_output(struct device *dev, int count, +			     const unsigned char *buf, const int start_page); +static void el2_block_input(struct device *dev, int count, struct sk_buff *skb, +			   int ring_offset); +static void el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +			 int ring_page); + + +/* This routine probes for a memory-mapped 3c503 board by looking for +   the "location register" at the end of the jumpered boot PROM space. +   This works even if a PROM isn't there. + +   If the ethercard isn't found there is an optional probe for +   ethercard jumpered to programmed-I/O mode. +   */ +int +el2_probe(struct device *dev) +{ +    int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0}; +    int base_addr = dev->base_addr; + +    if (base_addr > 0x1ff)	/* Check a single specified location. */ +	return el2_probe1(dev, base_addr); +    else if (base_addr != 0)		/* Don't probe at all. */ +	return ENXIO; + +    for (addr = addrs; *addr; addr++) { +	int i; +	unsigned int base_bits = readb(*addr); +	/* Find first set bit. */ +	for(i = 7; i >= 0; i--, base_bits >>= 1) +	    if (base_bits & 0x1) +		break; +	if (base_bits != 1) +	    continue; +	if (check_region(netcard_portlist[i], EL2_IO_EXTENT)) +	    continue; +	if (el2_probe1(dev, netcard_portlist[i]) == 0) +	    return 0; +    } +#if ! defined(no_probe_nonshared_memory) && ! defined (HAVE_DEVLIST) +    return el2_pio_probe(dev); +#else +    return ENODEV; +#endif +} + +#ifndef HAVE_DEVLIST +/*  Try all of the locations that aren't obviously empty.  This touches +    a lot of locations, and is much riskier than the code above. */ +int +el2_pio_probe(struct device *dev) +{ +    int i; +    int base_addr = dev ? dev->base_addr : 0; + +    if (base_addr > 0x1ff)	/* Check a single specified location. */ +	return el2_probe1(dev, base_addr); +    else if (base_addr != 0)	/* Don't probe at all. */ +	return ENXIO; + +    for (i = 0; netcard_portlist[i]; i++) { +	int ioaddr = netcard_portlist[i]; +	if (check_region(ioaddr, EL2_IO_EXTENT)) +	    continue; +	if (el2_probe1(dev, ioaddr) == 0) +	    return 0; +    } + +    return ENODEV; +} +#endif + +/* Probe for the Etherlink II card at I/O port base IOADDR, +   returning non-zero on success.  If found, set the station +   address and memory parameters in DEVICE. */ +int +el2_probe1(struct device *dev, int ioaddr) +{ +    int i, iobase_reg, membase_reg, saved_406, wordlength; +    static unsigned version_printed = 0; +    unsigned long vendor_id; + +    /* Reset and/or avoid any lurking NE2000 */ +    if (inb(ioaddr + 0x408) == 0xff) { +    	udelay(1000); +	return ENODEV; +    } + +    /* We verify that it's a 3C503 board by checking the first three octets +       of its ethernet address. */ +    iobase_reg = inb(ioaddr+0x403); +    membase_reg = inb(ioaddr+0x404); +    /* ASIC location registers should be 0 or have only a single bit set. */ +    if (   (iobase_reg  & (iobase_reg - 1)) +	|| (membase_reg & (membase_reg - 1))) { +	return ENODEV; +    } +    saved_406 = inb_p(ioaddr + 0x406); +    outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */ +    outb_p(ECNTRL_THIN, ioaddr + 0x406); +    /* Map the station addr PROM into the lower I/O ports. We now check +       for both the old and new 3Com prefix */ +    outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406); +    vendor_id = inb(ioaddr)*0x10000 + inb(ioaddr + 1)*0x100 + inb(ioaddr + 2); +    if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) { +	/* Restore the register we frobbed. */ +	outb(saved_406, ioaddr + 0x406); +	return ENODEV; +    } + +    /* We should have a "dev" from Space.c or the static module table. */ +    if (dev == NULL) { +	printk("3c503.c: Passed a NULL device.\n"); +	dev = init_etherdev(0, 0); +    } + +    if (ei_debug  &&  version_printed++ == 0) +	printk("%s", version); + +    dev->base_addr = ioaddr; +    /* Allocate dev->priv and fill in 8390 specific dev fields. */ +    if (ethdev_init(dev)) { +	printk ("3c503: unable to allocate memory for dev->priv.\n"); +	return -ENOMEM; +     } + +    printk("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr); + +    /* Retrieve and print the ethernet address. */ +    for (i = 0; i < 6; i++) +	printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + +    /* Map the 8390 back into the window. */ +    outb(ECNTRL_THIN, ioaddr + 0x406); + +    /* Check for EL2/16 as described in tech. man. */ +    outb_p(E8390_PAGE0, ioaddr + E8390_CMD); +    outb_p(0, ioaddr + EN0_DCFG); +    outb_p(E8390_PAGE2, ioaddr + E8390_CMD); +    wordlength = inb_p(ioaddr + EN0_DCFG) & ENDCFG_WTS; +    outb_p(E8390_PAGE0, ioaddr + E8390_CMD); + +    /* Probe for, turn on and clear the board's shared memory. */ +    if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg); +    outb(EGACFR_NORM, ioaddr + 0x405);	/* Enable RAM */ + +    /* This should be probed for (or set via an ioctl()) at run-time. +       Right now we use a sleazy hack to pass in the interface number +       at boot-time via the low bits of the mem_end field.  That value is +       unused, and the low bits would be discarded even if it was used. */ +#if defined(EI8390_THICK) || defined(EL2_AUI) +    ei_status.interface_num = 1; +#else +    ei_status.interface_num = dev->mem_end & 0xf; +#endif +    printk(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex"); + +    if ((membase_reg & 0xf0) == 0) { +	dev->mem_start = 0; +	ei_status.name = "3c503-PIO"; +    } else { +	dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) + +	    ((membase_reg & 0xA0) ? 0x4000 : 0); + +#define EL2_MEMSIZE (EL2_MB1_STOP_PG - EL2_MB1_START_PG)*256 +#ifdef EL2MEMTEST +	/* This has never found an error, but someone might care. +	   Note that it only tests the 2nd 8kB on 16kB 3c503/16 +	   cards between card addr. 0x2000 and 0x3fff. */ +	{			/* Check the card's memory. */ +	    unsigned long mem_base = dev->mem_start; +	    unsigned int test_val = 0xbbadf00d; +	    writel(0xba5eba5e, mem_base); +	    for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) { +		writel(test_val, mem_base + i); +		if (readl(mem_base) != 0xba5eba5e +		    || readl(mem_base + i) != test_val) { +		    printk("3c503: memory failure or memory address conflict.\n"); +		    dev->mem_start = 0; +		    ei_status.name = "3c503-PIO"; +		    break; +		} +		test_val += 0x55555555; +		writel(0, mem_base + i); +	    } +	} +#endif  /* EL2MEMTEST */ + +	dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE; + +	if (wordlength) {	/* No Tx pages to skip over to get to Rx */ +		dev->rmem_start = dev->mem_start; +		ei_status.name = "3c503/16"; +	} else { +		dev->rmem_start = TX_PAGES*256 + dev->mem_start; +		ei_status.name = "3c503"; +	} +    } + +    /* +	Divide up the memory on the card. This is the same regardless of +	whether shared-mem or PIO is used. For 16 bit cards (16kB RAM), +	we use the entire 8k of bank1 for an Rx ring. We only use 3k  +	of the bank0 for 2 full size Tx packet slots. For 8 bit cards, +	(8kB RAM) we use 3kB of bank1 for two Tx slots, and the remaining  +	5kB for an Rx ring.  */ + +    if (wordlength) { +	ei_status.tx_start_page = EL2_MB0_START_PG; +	ei_status.rx_start_page = EL2_MB1_START_PG; +    } else { +	ei_status.tx_start_page = EL2_MB1_START_PG; +	ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES; +    } + +    /* Finish setting the board's parameters. */ +    ei_status.stop_page = EL2_MB1_STOP_PG; +    ei_status.word16 = wordlength; +    ei_status.reset_8390 = &el2_reset_8390; +    ei_status.get_8390_hdr = &el2_get_8390_hdr; +    ei_status.block_input = &el2_block_input; +    ei_status.block_output = &el2_block_output; + +    request_region(ioaddr, EL2_IO_EXTENT, ei_status.name); + +    if (dev->irq == 2) +	dev->irq = 9; +    else if (dev->irq > 5 && dev->irq != 9) { +	printk("3c503: configured interrupt %d invalid, will use autoIRQ.\n", +	       dev->irq); +	dev->irq = 0; +    } + +    ei_status.saved_irq = dev->irq; + +    dev->start = 0; +    dev->open = &el2_open; +    dev->stop = &el2_close; + +    if (dev->mem_start) +	printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n", +		dev->name, ei_status.name, (wordlength+1)<<3, +		dev->mem_start, dev->mem_end-1); + +    else +    { +	ei_status.tx_start_page = EL2_MB1_START_PG; +	ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES; +	printk("\n%s: %s, %dkB RAM, using programmed I/O (REJUMPER for SHARED MEMORY).\n", +	       dev->name, ei_status.name, (wordlength+1)<<3); +    } +    return 0; +} + +static int +el2_open(struct device *dev) +{ + +    if (dev->irq < 2) { +	int irqlist[] = {5, 9, 3, 4, 0}; +	int *irqp = irqlist; + +	outb(EGACFR_NORM, E33G_GACFR);	/* Enable RAM and interrupts. */ +	do { +	    if (request_irq (*irqp, NULL, 0, "bogus", NULL) != -EBUSY) { +		/* Twinkle the interrupt, and check if it's seen. */ +		autoirq_setup(0); +		outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR); +		outb_p(0x00, E33G_IDCFR); +		if (*irqp == autoirq_report(0)	 /* It's a good IRQ line! */ +		    && request_irq (dev->irq = *irqp, &ei_interrupt, 0, ei_status.name, NULL) == 0) +		    break; +	    } +	} while (*++irqp); +	if (*irqp == 0) { +	    outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */ +	    return -EAGAIN; +	} +    } else { +	if (request_irq(dev->irq, &ei_interrupt, 0, ei_status.name, NULL)) { +	    return -EAGAIN; +	} +    } + +    el2_init_card(dev); +    ei_open(dev); +    MOD_INC_USE_COUNT; +    return 0; +} + +static int +el2_close(struct device *dev) +{ +    free_irq(dev->irq, NULL); +    dev->irq = ei_status.saved_irq; +    irq2dev_map[dev->irq] = NULL; +    outb(EGACFR_IRQOFF, E33G_GACFR);	/* disable interrupts. */ + +    ei_close(dev); +    MOD_DEC_USE_COUNT; +    return 0; +} + +/* This is called whenever we have a unrecoverable failure: +       transmit timeout +       Bad ring buffer packet header + */ +static void +el2_reset_8390(struct device *dev) +{ +    if (ei_debug > 1) { +	printk("%s: Resetting the 3c503 board...", dev->name); +	printk("%#lx=%#02x %#lx=%#02x %#lx=%#02x...", E33G_IDCFR, inb(E33G_IDCFR), +	       E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR)); +    } +    outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL); +    ei_status.txing = 0; +    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); +    el2_init_card(dev); +    if (ei_debug > 1) printk("done\n"); +} + +/* Initialize the 3c503 GA registers after a reset. */ +static void +el2_init_card(struct device *dev) +{ +    /* Unmap the station PROM and select the DIX or BNC connector. */ +    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); + +    /* Set ASIC copy of rx's first and last+1 buffer pages */ +    /* These must be the same as in the 8390. */ +    outb(ei_status.rx_start_page, E33G_STARTPG); +    outb(ei_status.stop_page,  E33G_STOPPG); + +    /* Point the vector pointer registers somewhere ?harmless?. */ +    outb(0xff, E33G_VP2);	/* Point at the ROM restart location 0xffff0 */ +    outb(0xff, E33G_VP1); +    outb(0x00, E33G_VP0); +    /* Turn off all interrupts until we're opened. */ +    outb_p(0x00,  dev->base_addr + EN0_IMR); +    /* Enable IRQs iff started. */ +    outb(EGACFR_NORM, E33G_GACFR); + +    /* Set the interrupt line. */ +    outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR); +    outb_p((WRD_COUNT << 1), E33G_DRQCNT);	/* Set burst size to 8 */ +    outb_p(0x20, E33G_DMAAH);	/* Put a valid addr in the GA DMA */ +    outb_p(0x00, E33G_DMAAL); +    return;			/* We always succeed */ +} + +/* + * Either use the shared memory (if enabled on the board) or put the packet + * out through the ASIC FIFO. + */ +static void +el2_block_output(struct device *dev, int count, +		 const unsigned char *buf, const int start_page) +{ +    unsigned short int *wrd; +    int boguscount;		/* timeout counter */ +    unsigned short word;	/* temporary for better machine code */ + +    if (ei_status.word16)      /* Tx packets go into bank 0 on EL2/16 card */ +	outb(EGACFR_RSEL|EGACFR_TCM, E33G_GACFR); +    else  +	outb(EGACFR_NORM, E33G_GACFR); + +    if (dev->mem_start) {	/* Shared memory transfer */ +	unsigned long dest_addr = dev->mem_start + +	    ((start_page - ei_status.tx_start_page) << 8); +	memcpy_toio(dest_addr, buf, count); +	outb(EGACFR_NORM, E33G_GACFR);	/* Back to bank1 in case on bank0 */ +	return; +    } + +/* + *  No shared memory, put the packet out the other way. + *  Set up then start the internal memory transfer to Tx Start Page + */ + +    word = (unsigned short)start_page; +    outb(word&0xFF, E33G_DMAAH); +    outb(word>>8, E33G_DMAAL); + +    outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT +	   | ECNTRL_START, E33G_CNTRL); + +/* + *  Here I am going to write data to the FIFO as quickly as possible. + *  Note that E33G_FIFOH is defined incorrectly. It is really + *  E33G_FIFOL, the lowest port address for both the byte and + *  word write. Variable 'count' is NOT checked. Caller must supply a + *  valid count. Note that I may write a harmless extra byte to the + *  8390 if the byte-count was not even. + */ +    wrd = (unsigned short int *) buf; +    count  = (count + 1) >> 1; +    for(;;)  +    { +        boguscount = 0x1000; +        while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) +        { +            if(!boguscount--) +            { +                printk("%s: FIFO blocked in el2_block_output.\n", dev->name); +                el2_reset_8390(dev); +                goto blocked; +            } +        } +        if(count > WRD_COUNT) +        { +            outsw(E33G_FIFOH, wrd, WRD_COUNT); +            wrd   += WRD_COUNT; +            count -= WRD_COUNT; +        } +        else +        { +            outsw(E33G_FIFOH, wrd, count); +            break; +        } +    } +    blocked:; +    outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); +    return; +} + +/* Read the 4 byte, page aligned 8390 specific header. */ +static void +el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ +    int boguscount; +    unsigned long hdr_start = dev->mem_start + ((ring_page - EL2_MB1_START_PG)<<8); +    unsigned short word; + +    if (dev->mem_start) {       /* Use the shared memory. */ +	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); +	return; +    } + +/* + *  No shared memory, use programmed I/O. + */ + +    word = (unsigned short)ring_page; +    outb(word&0xFF, E33G_DMAAH); +    outb(word>>8, E33G_DMAAL); + +    outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT +	   | ECNTRL_START, E33G_CNTRL); +    boguscount = 0x1000; +    while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) +    { +        if(!boguscount--) +        { +            printk("%s: FIFO blocked in el2_get_8390_hdr.\n", dev->name); +            memset(hdr, 0x00, sizeof(struct e8390_pkt_hdr)); +            el2_reset_8390(dev); +            goto blocked; +        } +    } +    insw(E33G_FIFOH, hdr, (sizeof(struct e8390_pkt_hdr))>> 1); +    blocked:; +    outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); +} + + +static void +el2_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +    int boguscount = 0; +    unsigned short int *buf; +    unsigned short word; + +    int end_of_ring = dev->rmem_end; + +    /* Maybe enable shared memory just be to be safe... nahh.*/ +    if (dev->mem_start) {	/* Use the shared memory. */ +	ring_offset -= (EL2_MB1_START_PG<<8); +	if (dev->mem_start + ring_offset + count > end_of_ring) { +	    /* We must wrap the input move. */ +	    int semi_count = end_of_ring - (dev->mem_start + ring_offset); +	    memcpy_fromio(skb->data, dev->mem_start + ring_offset, semi_count); +	    count -= semi_count; +	    memcpy_fromio(skb->data + semi_count, dev->rmem_start, count); +	} else { +		/* Packet is in one chunk -- we can copy + cksum. */ +		eth_io_copy_and_sum(skb, dev->mem_start + ring_offset, count, 0); +	} +	return; +    } + +/* + *  No shared memory, use programmed I/O. + */ +    word = (unsigned short) ring_offset; +    outb(word>>8, E33G_DMAAH); +    outb(word&0xFF, E33G_DMAAL); + +    outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT +	   | ECNTRL_START, E33G_CNTRL); + +/* + *  Here I also try to get data as fast as possible. I am betting that I + *  can read one extra byte without clobbering anything in the kernel because + *  this would only occur on an odd byte-count and allocation of skb->data + *  is word-aligned. Variable 'count' is NOT checked. Caller must check + *  for a valid count.  + *  [This is currently quite safe.... but if one day the 3c503 explodes + *   you know where to come looking ;)] + */ + +    buf =  (unsigned short int *) skb->data; +    count =  (count + 1) >> 1; +    for(;;)  +    { +        boguscount = 0x1000; +        while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) +        { +            if(!boguscount--) +            { +                printk("%s: FIFO blocked in el2_block_input.\n", dev->name); +                el2_reset_8390(dev); +                goto blocked; +            } +        } +        if(count > WRD_COUNT) +        {  +            insw(E33G_FIFOH, buf, WRD_COUNT); +            buf   += WRD_COUNT; +            count -= WRD_COUNT; +        } +        else +        { +            insw(E33G_FIFOH, buf, count); +            break; +        } +    } +    blocked:; +    outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL); +    return; +} +#ifdef MODULE +#define MAX_EL2_CARDS	4	/* Max number of EL2 cards per module */ +#define NAMELEN 	8	/* #of chars for storing dev->name */ + +static char namelist[NAMELEN * MAX_EL2_CARDS] = { 0, }; +static struct device dev_el2[MAX_EL2_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int io[MAX_EL2_CARDS] = { 0, }; +static int irq[MAX_EL2_CARDS]  = { 0, }; +static int xcvr[MAX_EL2_CARDS] = { 0, };	/* choose int. or ext. xcvr */ + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) { +		struct device *dev = &dev_el2[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->mem_end = xcvr[this_dev];	/* low 4bits = xcvr sel. */ +		dev->init = el2_probe; +		if (io[this_dev] == 0)  { +			if (this_dev != 0) break; /* only autoprobe 1st one */ +			printk(KERN_NOTICE "3c503.c: Presently autoprobing (not recommended) for a single card.\n"); +		} +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]); +			if (found != 0) return 0;	/* Got at least one. */ +			return -ENXIO; +		} +		found++; +	} +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) { +		struct device *dev = &dev_el2[this_dev]; +		if (dev->priv != NULL) { +			/* NB: el2_close() handles free_irq + irq2dev map */ +			kfree(dev->priv); +			dev->priv = NULL; +			release_region(dev->base_addr, EL2_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + +/* + * Local variables: + *  version-control: t + *  kept-new-versions: 5 + *  c-indent-level: 4 + * End: + */ diff --git a/linux/src/drivers/net/3c503.h b/linux/src/drivers/net/3c503.h new file mode 100644 index 0000000..b9f8a46 --- /dev/null +++ b/linux/src/drivers/net/3c503.h @@ -0,0 +1,91 @@ +/* Definitions for the 3Com 3c503 Etherlink 2. */ +/* This file is distributed under the GPL. +   Many of these names and comments are directly from the Crynwr packet +   drivers, which are released under the GPL. */ + +#define EL2H (dev->base_addr + 0x400) +#define EL2L (dev->base_addr) + +/* Vendor unique hardware addr. prefix. 3Com has 2 because they ran +   out of available addresses on the first one... */ + +#define OLD_3COM_ID	0x02608c +#define NEW_3COM_ID	0x0020af + +/* Shared memory management parameters. NB: The 8 bit cards have only +   one bank (MB1) which serves both Tx and Rx packet space. The 16bit +   cards have 2 banks, MB0 for Tx packets, and MB1 for Rx packets.  +   You choose which bank appears in the sh. mem window with EGACFR_MBSn */ + +#define EL2_MB0_START_PG	(0x00)	/* EL2/16 Tx packets go in bank 0 */ +#define EL2_MB1_START_PG	(0x20)	/* First page of bank 1 */ +#define EL2_MB1_STOP_PG		(0x40)	/* Last page +1 of bank 1 */ + +/* 3Com 3c503 ASIC registers */ +#define E33G_STARTPG	(EL2H+0)	/* Start page, matching EN0_STARTPG */ +#define E33G_STOPPG	(EL2H+1)	/* Stop page, must match EN0_STOPPG */ +#define E33G_DRQCNT	(EL2H+2)	/* DMA burst count */ +#define E33G_IOBASE	(EL2H+3)	/* Read of I/O base jumpers. */ +	/* (non-useful, but it also appears at the end of EPROM space) */ +#define E33G_ROMBASE	(EL2H+4)	/* Read of memory base jumpers. */ +#define E33G_GACFR	(EL2H+5)	/* Config/setup bits for the ASIC GA */ +#define E33G_CNTRL	(EL2H+6)	/* Board's main control register */ +#define E33G_STATUS	(EL2H+7)	/* Status on completions. */ +#define E33G_IDCFR	(EL2H+8)	/* Interrupt/DMA config register */ +				/* (Which IRQ to assert, DMA chan to use) */ +#define E33G_DMAAH	(EL2H+9)	/* High byte of DMA address reg */ +#define E33G_DMAAL	(EL2H+10)	/* Low byte of DMA address reg */ +/* "Vector pointer" - if this address matches a read, the EPROM (rather than +   shared RAM) is mapped into memory space. */ +#define E33G_VP2	(EL2H+11) +#define E33G_VP1	(EL2H+12) +#define E33G_VP0	(EL2H+13) +#define E33G_FIFOH	(EL2H+14)	/* FIFO for programmed I/O moves */ +#define E33G_FIFOL	(EL2H+15)	/* ... low byte of above. */ + +/* Bits in E33G_CNTRL register: */ + +#define ECNTRL_RESET	(0x01)	/* Software reset of the ASIC and 8390 */ +#define ECNTRL_THIN	(0x02)	/* Onboard xcvr enable, AUI disable */ +#define ECNTRL_AUI	(0x00)	/* Onboard xcvr disable, AUI enable */ +#define ECNTRL_SAPROM	(0x04)	/* Map the station address prom */ +#define ECNTRL_DBLBFR	(0x20)	/* FIFO configuration bit */ +#define ECNTRL_OUTPUT	(0x40)	/* PC-to-3C503 direction if 1 */ +#define ECNTRL_INPUT	(0x00)	/* 3C503-to-PC direction if 0 */ +#define ECNTRL_START	(0x80)	/* Start the DMA logic */ + +/* Bits in E33G_STATUS register: */ + +#define ESTAT_DPRDY	(0x80)	/* Data port (of FIFO) ready */ +#define ESTAT_UFLW	(0x40)	/* Tried to read FIFO when it was empty */ +#define ESTAT_OFLW	(0x20)	/* Tried to write FIFO when it was full */ +#define ESTAT_DTC	(0x10)	/* Terminal Count from PC bus DMA logic */ +#define ESTAT_DIP	(0x08)	/* DMA In Progress */ + +/* Bits in E33G_GACFR register: */ + +#define EGACFR_NIM	(0x80)	/* NIC interrupt mask */ +#define EGACFR_TCM	(0x40)	/* DMA term. count interrupt mask */ +#define EGACFR_RSEL	(0x08)	/* Map a bank of card mem into system mem */ +#define EGACFR_MBS2	(0x04)	/* Memory bank select, bit 2. */ +#define EGACFR_MBS1	(0x02)	/* Memory bank select, bit 1. */ +#define EGACFR_MBS0	(0x01)	/* Memory bank select, bit 0. */ + +#define EGACFR_NORM	(0x49)	/* TCM | RSEL | MBS0 */ +#define EGACFR_IRQOFF	(0xc9)	/* TCM | RSEL | MBS0 | NIM */ + +/* +	MBS2	MBS1	MBS0	Sh. mem windows card mem at: +	----	----	----	----------------------------- +	0	0	0	0x0000 -- bank 0 +	0	0	1	0x2000 -- bank 1 (only choice for 8bit card) +	0	1	0	0x4000 -- bank 2, not used +	0	1	1	0x6000 -- bank 3, not used + +There was going to be a 32k card that used bank 2 and 3, but it  +never got produced. + +*/ + + +/* End of 3C503 parameter definitions */ diff --git a/linux/src/drivers/net/3c505.c b/linux/src/drivers/net/3c505.c new file mode 100644 index 0000000..d78dad5 --- /dev/null +++ b/linux/src/drivers/net/3c505.c @@ -0,0 +1,1732 @@ +/* + * Linux ethernet device driver for the 3Com Etherlink Plus (3C505) + *      By Craig Southeren, Juha Laiho and Philip Blundell + * + * 3c505.c      This module implements an interface to the 3Com + *              Etherlink Plus (3c505) ethernet card. Linux device  + *              driver interface reverse engineered from the Linux 3C509 + *              device drivers. Some 3C505 information gleaned from + *              the Crynwr packet driver. Still this driver would not + *              be here without 3C505 technical reference provided by + *              3Com. + * + * $Id: 3c505.c,v 1.1 1999/04/26 05:51:48 tb Exp $ + * + * Authors:     Linux 3c505 device driver by + *                      Craig Southeren, <craigs@ineluki.apana.org.au> + *              Final debugging by + *                      Andrew Tridgell, <tridge@nimbus.anu.edu.au> + *              Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by + *                      Juha Laiho, <jlaiho@ichaos.nullnet.fi> + *              Linux 3C509 driver by + *                      Donald Becker, <becker@super.org> + *              Crynwr packet driver by + *                      Krishnan Gopalan and Gregg Stefancik, + *                      Clemson University Engineering Computer Operations. + *                      Portions of the code have been adapted from the 3c505 + *                         driver for NCSA Telnet by Bruce Orchard and later + *                         modified by Warren Van Houten and krus@diku.dk. + *              3C505 technical information provided by + *                      Terry Murphy, of 3Com Network Adapter Division + *              Linux 1.3.0 changes by + *                      Alan Cox <Alan.Cox@linux.org> + *              More debugging and DMA version by Philip Blundell + */ + +/* Theory of operation: + + * The 3c505 is quite an intelligent board.  All communication with it is done + * by means of Primary Command Blocks (PCBs); these are transferred using PIO + * through the command register.  The card has 256k of on-board RAM, which is + * used to buffer received packets.  It might seem at first that more buffers + * are better, but in fact this isn't true.  From my tests, it seems that + * more than about 10 buffers are unnecessary, and there is a noticeable + * performance hit in having more active on the card.  So the majority of the + * card's memory isn't, in fact, used. + * + * We keep up to 4 "receive packet" commands active on the board at a time. + * When a packet comes in, so long as there is a receive command active, the + * board will send us a "packet received" PCB and then add the data for that + * packet to the DMA queue.  If a DMA transfer is not already in progress, we + * set one up to start uploading the data.  We have to maintain a list of + * backlogged receive packets, because the card may decide to tell us about + * a newly-arrived packet at any time, and we may not be able to start a DMA + * transfer immediately (ie one may already be going on).  We can't NAK the + * PCB, because then it would throw the packet away. + * + * Trying to send a PCB to the card at the wrong moment seems to have bad + * effects.  If we send it a transmit PCB while a receive DMA is happening, + * it will just NAK the PCB and so we will have wasted our time.  Worse, it + * sometimes seems to interrupt the transfer.  The majority of the low-level + * code is protected by one huge semaphore -- "busy" -- which is set whenever + * it probably isn't safe to do anything to the card.  The receive routine + * must gain a lock on "busy" before it can start a DMA transfer, and the + * transmit routine must gain a lock before it sends the first PCB to the card. + * The send_pcb() routine also has an internal semaphore to protect it against + * being re-entered (which would be disastrous) -- this is needed because + * several things can happen asynchronously (re-priming the receiver and + * asking the card for statistics, for example).  send_pcb() will also refuse + * to talk to the card at all if a DMA upload is happening.  The higher-level + * networking code will reschedule a later retry if some part of the driver + * is blocked.  In practice, this doesn't seem to happen very often. + */ + +/* This driver will not work with revision 2 hardware, because the host + * control register is write-only.  It should be fairly easy to arrange to + * keep our own soft-copy of the intended contents of this register, if + * somebody has the time.  There may be firmware differences that cause + * other problems, though, and I don't have an old card to test. + */ + +/* The driver is a mess.  I took Craig's and Juha's code, and hacked it firstly + * to make it more reliable, and secondly to add DMA mode.  Many things could + * probably be done better; the concurrency protection is particularly awful. + */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/ioport.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "3c505.h" + +#define ELP_DMA      6		/* DMA channel to use */ +#define ELP_RX_PCBS  4 + +/********************************************************* + * + *  define debug messages here as common strings to reduce space + * + *********************************************************/ + +static const char *filename = __FILE__; + +static const char *timeout_msg = "*** timeout at %s:%s (line %d) ***\n"; +#define TIMEOUT_MSG(lineno) \ +	printk(timeout_msg, filename,__FUNCTION__,(lineno)) + +static const char *invalid_pcb_msg = +"*** invalid pcb length %d at %s:%s (line %d) ***\n"; +#define INVALID_PCB_MSG(len) \ +	printk(invalid_pcb_msg, (len),filename,__FUNCTION__,__LINE__) + +static const char *search_msg = "%s: Looking for 3c505 adapter at address %#x..."; + +static const char *stilllooking_msg = "still looking..."; + +static const char *found_msg = "found.\n"; + +static const char *notfound_msg = "not found (reason = %d)\n"; + +static const char *couldnot_msg = "%s: 3c505 not found\n"; + +/********************************************************* + * + *  various other debug stuff + * + *********************************************************/ + +#ifdef ELP_DEBUG +static const int elp_debug = ELP_DEBUG; +#else +static const int elp_debug = 0; +#endif + +/* + *  0 = no messages (well, some) + *  1 = messages when high level commands performed + *  2 = messages when low level commands performed + *  3 = messages when interrupts received + */ + +/***************************************************************** + * + * useful macros + * + *****************************************************************/ + +#ifndef	TRUE +#define	TRUE	1 +#endif + +#ifndef	FALSE +#define	FALSE	0 +#endif + + +/***************************************************************** + * + * List of I/O-addresses we try to auto-sense + * Last element MUST BE 0! + *****************************************************************/ + +const int addr_list[] = {0x300, 0x280, 0x310, 0}; + +/* Dma Memory related stuff */ + +/* Pure 2^n version of get_order */ +static inline int __get_order(unsigned long size) +{ +	int order; + +	size = (size - 1) >> (PAGE_SHIFT - 1); +	order = -1; +	do { +		size >>= 1; +		order++; +	} while (size); +	return order; +} + +static unsigned long dma_mem_alloc(int size) +{ +	int order = __get_order(size); + +	return __get_dma_pages(GFP_KERNEL, order); +} + + +/***************************************************************** + * + * Functions for I/O (note the inline !) + * + *****************************************************************/ + +static inline unsigned char inb_status(unsigned int base_addr) +{ +	return inb(base_addr + PORT_STATUS); +} + +static inline unsigned char inb_control(unsigned int base_addr) +{ +	return inb(base_addr + PORT_CONTROL); +} + +static inline int inb_command(unsigned int base_addr) +{ +	return inb(base_addr + PORT_COMMAND); +} + +static inline void outb_control(unsigned char val, unsigned int base_addr) +{ +	outb(val, base_addr + PORT_CONTROL); +} + +static inline void outb_command(unsigned char val, unsigned int base_addr) +{ +	outb(val, base_addr + PORT_COMMAND); +} + +static inline unsigned int inw_data(unsigned int base_addr) +{ +	return inw(base_addr + PORT_DATA); +} + +static inline void outw_data(unsigned int val, unsigned int base_addr) +{ +	outw(val, base_addr + PORT_DATA); +} + + +/***************************************************************** + * + *  structure to hold context information for adapter + * + *****************************************************************/ + +#define DMA_BUFFER_SIZE  1600 +#define BACKLOG_SIZE      4 + +typedef struct { +	volatile short got[NUM_TRANSMIT_CMDS];	/* flags for command completion */ +	pcb_struct tx_pcb;	/* PCB for foreground sending */ +	pcb_struct rx_pcb;	/* PCB for foreground receiving */ +	pcb_struct itx_pcb;	/* PCB for background sending */ +	pcb_struct irx_pcb;	/* PCB for background receiving */ +	struct enet_statistics stats; + +	void *dma_buffer; + +	struct { +		unsigned int length[BACKLOG_SIZE]; +		unsigned int in; +		unsigned int out; +	} rx_backlog; + +	struct { +		unsigned int direction; +		unsigned int length; +		unsigned int copy_flag; +		struct sk_buff *skb; +		long int start_time; +	} current_dma; + +	/* flags */ +	unsigned long send_pcb_semaphore; +	unsigned int dmaing; +	unsigned long busy; + +	unsigned int rx_active;  /* number of receive PCBs */ +} elp_device; + +static inline unsigned int backlog_next(unsigned int n) +{ +	return (n + 1) % BACKLOG_SIZE; +} + +/***************************************************************** + * + *  useful functions for accessing the adapter + * + *****************************************************************/ + +/* + * use this routine when accessing the ASF bits as they are + * changed asynchronously by the adapter + */ + +/* get adapter PCB status */ +#define	GET_ASF(addr) \ +	(get_status(addr)&ASF_PCB_MASK) + +static inline int get_status(unsigned int base_addr) +{ +	int timeout = jiffies + 10; +	register int stat1; +	do { +		stat1 = inb_status(base_addr); +	} while (stat1 != inb_status(base_addr) && jiffies < timeout); +	if (jiffies >= timeout) +		TIMEOUT_MSG(__LINE__); +	return stat1; +} + +static inline void set_hsf(unsigned int base_addr, int hsf) +{ +	cli(); +	outb_control((inb_control(base_addr) & ~HSF_PCB_MASK) | hsf, base_addr); +	sti(); +} + +static int start_receive(struct device *, pcb_struct *); + +inline static void adapter_reset(struct device *dev) +{ +	int timeout; +	unsigned char orig_hcr = inb_control(dev->base_addr); + +	elp_device *adapter = dev->priv; + +	outb_control(0, dev->base_addr); + +	if (inb_status(dev->base_addr) & ACRF) { +		do { +			inb_command(dev->base_addr); +			timeout = jiffies + 2; +			while ((jiffies <= timeout) && !(inb_status(dev->base_addr) & ACRF)); +		} while (inb_status(dev->base_addr) & ACRF); +		set_hsf(dev->base_addr, HSF_PCB_NAK); +	} +	outb_control(inb_control(dev->base_addr) | ATTN | DIR, dev->base_addr); +	timeout = jiffies + 1; +	while (jiffies <= timeout); +	outb_control(inb_control(dev->base_addr) & ~ATTN, dev->base_addr); +	timeout = jiffies + 1; +	while (jiffies <= timeout); +	outb_control(inb_control(dev->base_addr) | FLSH, dev->base_addr); +	timeout = jiffies + 1; +	while (jiffies <= timeout); +	outb_control(inb_control(dev->base_addr) & ~FLSH, dev->base_addr); +	timeout = jiffies + 1; +	while (jiffies <= timeout); + +	outb_control(orig_hcr, dev->base_addr); +	if (!start_receive(dev, &adapter->tx_pcb)) +		printk("%s: start receive command failed \n", dev->name); +} + +/* Check to make sure that a DMA transfer hasn't timed out.  This should never happen + * in theory, but seems to occur occasionally if the card gets prodded at the wrong + * time. + */ +static inline void check_dma(struct device *dev) +{ +	elp_device *adapter = dev->priv; +	if (adapter->dmaing && (jiffies > (adapter->current_dma.start_time + 10))) { +		unsigned long flags; +		printk("%s: DMA %s timed out, %d bytes left\n", dev->name, adapter->current_dma.direction ? "download" : "upload", get_dma_residue(dev->dma)); +		save_flags(flags); +		cli(); +		adapter->dmaing = 0; +		adapter->busy = 0; +		disable_dma(dev->dma); +		if (adapter->rx_active) +			adapter->rx_active--; +		outb_control(inb_control(dev->base_addr) & ~(DMAE | TCEN | DIR), dev->base_addr); +		restore_flags(flags); +	} +} + +/* Primitive functions used by send_pcb() */ +static inline unsigned int send_pcb_slow(unsigned int base_addr, unsigned char byte) +{ +	unsigned int timeout; +	outb_command(byte, base_addr); +	for (timeout = jiffies + 5; jiffies < timeout;) { +		if (inb_status(base_addr) & HCRE) +			return FALSE; +	} +	printk("3c505: send_pcb_slow timed out\n"); +	return TRUE; +} + +static inline unsigned int send_pcb_fast(unsigned int base_addr, unsigned char byte) +{ +	unsigned int timeout; +	outb_command(byte, base_addr); +	for (timeout = 0; timeout < 40000; timeout++) { +		if (inb_status(base_addr) & HCRE) +			return FALSE; +	} +	printk("3c505: send_pcb_fast timed out\n"); +	return TRUE; +} + +/* Check to see if the receiver needs restarting, and kick it if so */ +static inline void prime_rx(struct device *dev) +{ +	elp_device *adapter = dev->priv; +	while (adapter->rx_active < ELP_RX_PCBS && dev->start) { +		if (!start_receive(dev, &adapter->itx_pcb)) +			break; +	} +} + +/***************************************************************** + * + * send_pcb + *   Send a PCB to the adapter.  + * + *	output byte to command reg  --<--+ + *	wait until HCRE is non zero      | + *	loop until all bytes sent   -->--+ + *	set HSF1 and HSF2 to 1 + *	output pcb length + *	wait until ASF give ACK or NAK + *	set HSF1 and HSF2 to 0 + * + *****************************************************************/ + +/* This can be quite slow -- the adapter is allowed to take up to 40ms + * to respond to the initial interrupt. + * + * We run initially with interrupts turned on, but with a semaphore set + * so that nobody tries to re-enter this code.  Once the first byte has + * gone through, we turn interrupts off and then send the others (the + * timeout is reduced to 500us). + */ + +static int send_pcb(struct device *dev, pcb_struct * pcb) +{ +	int i; +	int timeout; +	elp_device *adapter = dev->priv; + +	check_dma(dev); + +	if (adapter->dmaing && adapter->current_dma.direction == 0) +		return FALSE; + +	/* Avoid contention */ +	if (set_bit(1, &adapter->send_pcb_semaphore)) { +		if (elp_debug >= 3) { +			printk("%s: send_pcb entered while threaded\n", dev->name); +		} +		return FALSE; +	} +	/* +	 * load each byte into the command register and +	 * wait for the HCRE bit to indicate the adapter +	 * had read the byte +	 */ +	set_hsf(dev->base_addr, 0); + +	if (send_pcb_slow(dev->base_addr, pcb->command)) +		goto abort; + +	cli(); + +	if (send_pcb_fast(dev->base_addr, pcb->length)) +		goto sti_abort; + +	for (i = 0; i < pcb->length; i++) { +		if (send_pcb_fast(dev->base_addr, pcb->data.raw[i])) +			goto sti_abort; +	} + +	outb_control(inb_control(dev->base_addr) | 3, dev->base_addr);	/* signal end of PCB */ +	outb_command(2 + pcb->length, dev->base_addr); + +	/* now wait for the acknowledgement */ +	sti(); + +	for (timeout = jiffies + 5; jiffies < timeout;) { +		switch (GET_ASF(dev->base_addr)) { +		case ASF_PCB_ACK: +			adapter->send_pcb_semaphore = 0; +			return TRUE; +			break; +		case ASF_PCB_NAK: +			printk("%s: send_pcb got NAK\n", dev->name); +			goto abort; +			break; +		} +	} + +	if (elp_debug >= 1) +		printk("%s: timeout waiting for PCB acknowledge (status %02x)\n", dev->name, inb_status(dev->base_addr)); + +      sti_abort: +	sti(); +      abort: +	adapter->send_pcb_semaphore = 0; +	return FALSE; +} + + +/***************************************************************** + * + * receive_pcb + *   Read a PCB from the adapter + * + *	wait for ACRF to be non-zero        ---<---+ + *	input a byte                               | + *	if ASF1 and ASF2 were not both one         | + *		before byte was read, loop      --->---+ + *	set HSF1 and HSF2 for ack + * + *****************************************************************/ + +static int receive_pcb(struct device *dev, pcb_struct * pcb) +{ +	int i, j; +	int total_length; +	int stat; +	int timeout; + +	elp_device *adapter = dev->priv; + +	set_hsf(dev->base_addr, 0); + +	/* get the command code */ +	timeout = jiffies + 2; +	while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && jiffies < timeout); +	if (jiffies >= timeout) { +		TIMEOUT_MSG(__LINE__); +		return FALSE; +	} +	pcb->command = inb_command(dev->base_addr); + +	/* read the data length */ +	timeout = jiffies + 3; +	while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && jiffies < timeout); +	if (jiffies >= timeout) { +		TIMEOUT_MSG(__LINE__); +		printk("%s: status %02x\n", dev->name, stat); +		return FALSE; +	} +	pcb->length = inb_command(dev->base_addr); + +	if (pcb->length > MAX_PCB_DATA) { +		INVALID_PCB_MSG(pcb->length); +		adapter_reset(dev); +		return FALSE; +	} +	/* read the data */ +	cli(); +	i = 0; +	do { +		j = 0; +		while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && j++ < 20000); +		pcb->data.raw[i++] = inb_command(dev->base_addr); +		if (i > MAX_PCB_DATA) +			INVALID_PCB_MSG(i); +	} while ((stat & ASF_PCB_MASK) != ASF_PCB_END && j < 20000); +	sti(); +	if (j >= 20000) { +		TIMEOUT_MSG(__LINE__); +		return FALSE; +	} +	/* woops, the last "data" byte was really the length! */ +	total_length = pcb->data.raw[--i]; + +	/* safety check total length vs data length */ +	if (total_length != (pcb->length + 2)) { +		if (elp_debug >= 2) +			printk("%s: mangled PCB received\n", dev->name); +		set_hsf(dev->base_addr, HSF_PCB_NAK); +		return FALSE; +	} + +	if (pcb->command == CMD_RECEIVE_PACKET_COMPLETE) { +		if (set_bit(0, (void *) &adapter->busy)) { +			if (backlog_next(adapter->rx_backlog.in) == adapter->rx_backlog.out) { +				set_hsf(dev->base_addr, HSF_PCB_NAK); +				printk("%s: PCB rejected, transfer in progress and backlog full\n", dev->name); +				pcb->command = 0; +				return TRUE; +			} else { +				pcb->command = 0xff; +			} +		} +	} +	set_hsf(dev->base_addr, HSF_PCB_ACK); +	return TRUE; +} + +/****************************************************** + * + *  queue a receive command on the adapter so we will get an + *  interrupt when a packet is received. + * + ******************************************************/ + +static int start_receive(struct device *dev, pcb_struct * tx_pcb) +{ +	int status; +	elp_device *adapter = dev->priv; + +	if (elp_debug >= 3) +		printk("%s: restarting receiver\n", dev->name); +	tx_pcb->command = CMD_RECEIVE_PACKET; +	tx_pcb->length = sizeof(struct Rcv_pkt); +	tx_pcb->data.rcv_pkt.buf_seg +	    = tx_pcb->data.rcv_pkt.buf_ofs = 0;		/* Unused */ +	tx_pcb->data.rcv_pkt.buf_len = 1600; +	tx_pcb->data.rcv_pkt.timeout = 0;	/* set timeout to zero */ +	status = send_pcb(dev, tx_pcb); +	if (status) +		adapter->rx_active++; +	return status; +} + +/****************************************************** + * + * extract a packet from the adapter + * this routine is only called from within the interrupt + * service routine, so no cli/sti calls are needed + * note that the length is always assumed to be even + * + ******************************************************/ + +static void receive_packet(struct device *dev, int len) +{ +	int rlen; +	elp_device *adapter = dev->priv; +	unsigned long target; +	struct sk_buff *skb; + +	rlen = (len + 1) & ~1; +	skb = dev_alloc_skb(rlen + 2); + +	adapter->current_dma.copy_flag = 0; + +	if (!skb) { +	  printk("%s: memory squeeze, dropping packet\n", dev->name); +	  target = virt_to_bus(adapter->dma_buffer); +	} else { +	  skb_reserve(skb, 2); +	  target = virt_to_bus(skb_put(skb, rlen)); +	  if ((target + rlen) >= MAX_DMA_ADDRESS) { +	    target = virt_to_bus(adapter->dma_buffer); +	    adapter->current_dma.copy_flag = 1; +	  } +	} +	/* if this happens, we die */ +	if (set_bit(0, (void *) &adapter->dmaing)) +		printk("%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction); + +	adapter->current_dma.direction = 0; +	adapter->current_dma.length = rlen; +	adapter->current_dma.skb = skb; +	adapter->current_dma.start_time = jiffies; + +	outb_control(inb_control(dev->base_addr) | DIR | TCEN | DMAE, dev->base_addr); + +	disable_dma(dev->dma); +	clear_dma_ff(dev->dma); +	set_dma_mode(dev->dma, 0x04);	/* dma read */ +	set_dma_addr(dev->dma, target); +	set_dma_count(dev->dma, rlen); +	enable_dma(dev->dma); + +	if (elp_debug >= 3) { +		printk("%s: rx DMA transfer started\n", dev->name); +	} +	if (adapter->rx_active) +		adapter->rx_active--; + +	if (!adapter->busy) +		printk("%s: receive_packet called, busy not set.\n", dev->name); +} + +/****************************************************** + * + * interrupt handler + * + ******************************************************/ + +static void elp_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr) +{ +	int len; +	int dlen; +	int icount = 0; +	struct device *dev; +	elp_device *adapter; +	int timeout; + +	if (irq < 0 || irq > 15) { +		printk("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq); +		return; +	} +	dev = irq2dev_map[irq]; + +	if (dev == NULL) { +		printk("elp_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +	adapter = (elp_device *) dev->priv; + +	if (dev->interrupt) { +		printk("%s: re-entering the interrupt handler!\n", dev->name); +		return; +	} +	dev->interrupt = 1; + +	do { +		/* +		 * has a DMA transfer finished? +		 */ +		if (inb_status(dev->base_addr) & DONE) { +			if (!adapter->dmaing) { +				printk("%s: phantom DMA completed\n", dev->name); +			} +			if (elp_debug >= 3) { +				printk("%s: %s DMA complete, status %02x\n", dev->name, adapter->current_dma.direction ? "tx" : "rx", inb_status(dev->base_addr)); +			} + +			outb_control(inb_control(dev->base_addr) & ~(DMAE | TCEN | DIR), dev->base_addr); +			if (adapter->current_dma.direction) { +				dev_kfree_skb(adapter->current_dma.skb, FREE_WRITE); +			} else { +				struct sk_buff *skb = adapter->current_dma.skb; +				if (skb) { +				  skb->dev = dev; +				  if (adapter->current_dma.copy_flag) { +				    memcpy(skb_put(skb, adapter->current_dma.length), adapter->dma_buffer, adapter->current_dma.length); +				  } +				  skb->protocol = eth_type_trans(skb,dev); +				  netif_rx(skb); +				} +			} +			adapter->dmaing = 0; +			if (adapter->rx_backlog.in != adapter->rx_backlog.out) { +				int t = adapter->rx_backlog.length[adapter->rx_backlog.out]; +				adapter->rx_backlog.out = backlog_next(adapter->rx_backlog.out); +				if (elp_debug >= 2) +					printk("%s: receiving backlogged packet (%d)\n", dev->name, t); +				receive_packet(dev, t); +			} else { +				adapter->busy = 0; +			} +		} else { +			/* has one timed out? */ +			check_dma(dev); +		} + +		sti(); + +		/* +		 * receive a PCB from the adapter +		 */ +		timeout = jiffies + 3; +		while ((inb_status(dev->base_addr) & ACRF) != 0 && jiffies < timeout) { +			if (receive_pcb(dev, &adapter->irx_pcb)) { +				switch (adapter->irx_pcb.command) { +				case 0: +					break; +					/* +					 * received a packet - this must be handled fast +					 */ +				case 0xff: +				case CMD_RECEIVE_PACKET_COMPLETE: +					/* if the device isn't open, don't pass packets up the stack */ +					if (dev->start == 0) +						break; +					cli(); +					len = adapter->irx_pcb.data.rcv_resp.pkt_len; +					dlen = adapter->irx_pcb.data.rcv_resp.buf_len; +					if (adapter->irx_pcb.data.rcv_resp.timeout != 0) { +						printk("%s: interrupt - packet not received correctly\n", dev->name); +						sti(); +					} else { +						if (elp_debug >= 3) { +							sti(); +							printk("%s: interrupt - packet received of length %i (%i)\n", dev->name, len, dlen); +							cli(); +						} +						if (adapter->irx_pcb.command == 0xff) { +							if (elp_debug >= 2) +								printk("%s: adding packet to backlog (len = %d)\n", dev->name, dlen); +							adapter->rx_backlog.length[adapter->rx_backlog.in] = dlen; +							adapter->rx_backlog.in = backlog_next(adapter->rx_backlog.in); +						} else { +							receive_packet(dev, dlen); +						} +						sti(); +						if (elp_debug >= 3) +							printk("%s: packet received\n", dev->name); +					} +					break; + +					/* +					 * 82586 configured correctly +					 */ +				case CMD_CONFIGURE_82586_RESPONSE: +					adapter->got[CMD_CONFIGURE_82586] = 1; +					if (elp_debug >= 3) +						printk("%s: interrupt - configure response received\n", dev->name); +					break; + +					/* +					 * Adapter memory configuration +					 */ +				case CMD_CONFIGURE_ADAPTER_RESPONSE: +					adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1; +					if (elp_debug >= 3) +						printk("%s: Adapter memory configuration %s.\n", dev->name, +						       adapter->irx_pcb.data.failed ? "failed" : "succeeded"); +					break; + +					/* +					 * Multicast list loading +					 */ +				case CMD_LOAD_MULTICAST_RESPONSE: +					adapter->got[CMD_LOAD_MULTICAST_LIST] = 1; +					if (elp_debug >= 3) +						printk("%s: Multicast address list loading %s.\n", dev->name, +						       adapter->irx_pcb.data.failed ? "failed" : "succeeded"); +					break; + +					/* +					 * Station address setting +					 */ +				case CMD_SET_ADDRESS_RESPONSE: +					adapter->got[CMD_SET_STATION_ADDRESS] = 1; +					if (elp_debug >= 3) +						printk("%s: Ethernet address setting %s.\n", dev->name, +						       adapter->irx_pcb.data.failed ? "failed" : "succeeded"); +					break; + + +					/* +					 * received board statistics +					 */ +				case CMD_NETWORK_STATISTICS_RESPONSE: +					adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv; +					adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit; +					adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC; +					adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align; +					adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun; +					adapter->stats.rx_over_errors += adapter->irx_pcb.data.netstat.err_res; +					adapter->got[CMD_NETWORK_STATISTICS] = 1; +					if (elp_debug >= 3) +						printk("%s: interrupt - statistics response received\n", dev->name); +					break; + +					/* +					 * sent a packet +					 */ +				case CMD_TRANSMIT_PACKET_COMPLETE: +					if (elp_debug >= 3) +						printk("%s: interrupt - packet sent\n", dev->name); +					if (dev->start == 0) +						break; +					switch (adapter->irx_pcb.data.xmit_resp.c_stat) { +					case 0xffff: +						adapter->stats.tx_aborted_errors++; +						printk(KERN_INFO "%s: transmit timed out, network cable problem?\n", dev->name); +						break; +					case 0xfffe: +						adapter->stats.tx_fifo_errors++; +						printk(KERN_INFO "%s: transmit timed out, FIFO underrun\n", dev->name); +						break; +					} +					dev->tbusy = 0; +					mark_bh(NET_BH); +					break; + +					/* +					 * some unknown PCB +					 */ +				default: +					printk(KERN_DEBUG "%s: unknown PCB received - %2.2x\n", dev->name, adapter->irx_pcb.command); +					break; +				} +			} else { +				printk("%s: failed to read PCB on interrupt\n", dev->name); +				adapter_reset(dev); +			} +		} + +	} while (icount++ < 5 && (inb_status(dev->base_addr) & (ACRF | DONE))); + +	prime_rx(dev); + +	/* +	 * indicate no longer in interrupt routine +	 */ +	dev->interrupt = 0; +} + + +/****************************************************** + * + * open the board + * + ******************************************************/ + +static int elp_open(struct device *dev) +{ +	elp_device *adapter; + +	adapter = dev->priv; + +	if (elp_debug >= 3) +		printk("%s: request to open device\n", dev->name); + +	/* +	 * make sure we actually found the device +	 */ +	if (adapter == NULL) { +		printk("%s: Opening a non-existent physical device\n", dev->name); +		return -EAGAIN; +	} +	/* +	 * disable interrupts on the board +	 */ +	outb_control(0x00, dev->base_addr); + +	/* +	 * clear any pending interrupts +	 */ +	inb_command(dev->base_addr); +	adapter_reset(dev); + +	/* +	 * interrupt routine not entered +	 */ +	dev->interrupt = 0; + +	/* +	 *  transmitter not busy  +	 */ +	dev->tbusy = 0; + +	/* +	 * no receive PCBs active +	 */ +	adapter->rx_active = 0; + +	adapter->busy = 0; +	adapter->send_pcb_semaphore = 0; +	adapter->rx_backlog.in = 0; +	adapter->rx_backlog.out = 0; + +	/* +	 * make sure we can find the device header given the interrupt number +	 */ +	irq2dev_map[dev->irq] = dev; + +	/* +	 * install our interrupt service routine +	 */ +	if (request_irq(dev->irq, &elp_interrupt, 0, "3c505", NULL)) { +		irq2dev_map[dev->irq] = NULL; +		return -EAGAIN; +	} +	if (request_dma(dev->dma, "3c505")) { +		printk("%s: could not allocate DMA channel\n", dev->name); +		return -EAGAIN; +	} +	adapter->dma_buffer = (void *) dma_mem_alloc(DMA_BUFFER_SIZE); +	if (!adapter->dma_buffer) { +		printk("Could not allocate DMA buffer\n"); +	} +	adapter->dmaing = 0; + +	/* +	 * enable interrupts on the board +	 */ +	outb_control(CMDE, dev->base_addr); + +	/* +	 * device is now officially open! +	 */ +	dev->start = 1; + +	/* +	 * configure adapter memory: we need 10 multicast addresses, default==0 +	 */ +	if (elp_debug >= 3) +		printk("%s: sending 3c505 memory configuration command\n", dev->name); +	adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY; +	adapter->tx_pcb.data.memconf.cmd_q = 10; +	adapter->tx_pcb.data.memconf.rcv_q = 20; +	adapter->tx_pcb.data.memconf.mcast = 10; +	adapter->tx_pcb.data.memconf.frame = 20; +	adapter->tx_pcb.data.memconf.rcv_b = 20; +	adapter->tx_pcb.data.memconf.progs = 0; +	adapter->tx_pcb.length = sizeof(struct Memconf); +	adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0; +	if (!send_pcb(dev, &adapter->tx_pcb)) +		printk("%s: couldn't send memory configuration command\n", dev->name); +	else { +		int timeout = jiffies + TIMEOUT; +		while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && jiffies < timeout); +		if (jiffies >= timeout) +			TIMEOUT_MSG(__LINE__); +	} + + +	/* +	 * configure adapter to receive broadcast messages and wait for response +	 */ +	if (elp_debug >= 3) +		printk("%s: sending 82586 configure command\n", dev->name); +	adapter->tx_pcb.command = CMD_CONFIGURE_82586; +	adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD; +	adapter->tx_pcb.length = 2; +	adapter->got[CMD_CONFIGURE_82586] = 0; +	if (!send_pcb(dev, &adapter->tx_pcb)) +		printk("%s: couldn't send 82586 configure command\n", dev->name); +	else { +		int timeout = jiffies + TIMEOUT; +		while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout); +		if (jiffies >= timeout) +			TIMEOUT_MSG(__LINE__); +	} + +	/* enable burst-mode DMA */ +	outb(0x1, dev->base_addr + PORT_AUXDMA); + +	/* +	 * queue receive commands to provide buffering +	 */ +	prime_rx(dev); +	if (elp_debug >= 3) +		printk("%s: %d receive PCBs active\n", dev->name, adapter->rx_active); + +	MOD_INC_USE_COUNT; + +	return 0;		/* Always succeed */ +} + + +/****************************************************** + * + * send a packet to the adapter + * + ******************************************************/ + +static int send_packet(struct device *dev, struct sk_buff *skb) +{ +	elp_device *adapter = dev->priv; +	unsigned long target; + +	/* +	 * make sure the length is even and no shorter than 60 bytes +	 */ +	unsigned int nlen = (((skb->len < 60) ? 60 : skb->len) + 1) & (~1); + +	if (set_bit(0, (void *) &adapter->busy)) { +		if (elp_debug >= 2) +			printk("%s: transmit blocked\n", dev->name); +		return FALSE; +	} +	adapter = dev->priv; + +	/* +	 * send the adapter a transmit packet command. Ignore segment and offset +	 * and make sure the length is even +	 */ +	adapter->tx_pcb.command = CMD_TRANSMIT_PACKET; +	adapter->tx_pcb.length = sizeof(struct Xmit_pkt); +	adapter->tx_pcb.data.xmit_pkt.buf_ofs +	    = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0;	/* Unused */ +	adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen; + +	if (!send_pcb(dev, &adapter->tx_pcb)) { +		adapter->busy = 0; +		return FALSE; +	} +	/* if this happens, we die */ +	if (set_bit(0, (void *) &adapter->dmaing)) +		printk("%s: tx: DMA %d in progress\n", dev->name, adapter->current_dma.direction); + +	adapter->current_dma.direction = 1; +	adapter->current_dma.start_time = jiffies; + +	target = virt_to_bus(skb->data); +	if ((target + nlen) >= MAX_DMA_ADDRESS) { +		memcpy(adapter->dma_buffer, skb->data, nlen); +		target = virt_to_bus(adapter->dma_buffer); +	} +	adapter->current_dma.skb = skb; +	cli(); +	disable_dma(dev->dma); +	clear_dma_ff(dev->dma); +	set_dma_mode(dev->dma, 0x08);	/* dma memory -> io */ +	set_dma_addr(dev->dma, target); +	set_dma_count(dev->dma, nlen); +	enable_dma(dev->dma); +	outb_control(inb_control(dev->base_addr) | DMAE | TCEN, dev->base_addr); +	if (elp_debug >= 3) +		printk("%s: DMA transfer started\n", dev->name); + +	return TRUE; +} + +/****************************************************** + * + * start the transmitter + *    return 0 if sent OK, else return 1 + * + ******************************************************/ + +static int elp_start_xmit(struct sk_buff *skb, struct device *dev) +{ +	if (dev->interrupt) { +		printk("%s: start_xmit aborted (in irq)\n", dev->name); +		return 1; +	} + +	check_dma(dev); + +	/* +	 * if the transmitter is still busy, we have a transmit timeout... +	 */ +	if (dev->tbusy) { +		elp_device *adapter = dev->priv; +       		int tickssofar = jiffies - dev->trans_start; +		int stat; + +		if (tickssofar < 1000) +			return 1; + +		stat = inb_status(dev->base_addr); +		printk("%s: transmit timed out, lost %s?\n", dev->name, (stat & ACRF) ? "interrupt" : "command"); +		if (elp_debug >= 1) +			printk("%s: status %#02x\n", dev->name, stat); +		dev->trans_start = jiffies; +		dev->tbusy = 0; +		adapter->stats.tx_dropped++; +	} + +	/* Some upper layer thinks we've missed a tx-done interrupt */ +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	if (skb->len <= 0) +		return 0; + +	if (elp_debug >= 3) +		printk("%s: request to send packet of length %d\n", dev->name, (int) skb->len); + +	if (set_bit(0, (void *) &dev->tbusy)) { +		printk("%s: transmitter access conflict\n", dev->name); +		return 1; +	} +	/* +	 * send the packet at skb->data for skb->len +	 */ +	if (!send_packet(dev, skb)) { +		if (elp_debug >= 2) { +			printk("%s: failed to transmit packet\n", dev->name); +		} +		dev->tbusy = 0; +		return 1; +	} +	if (elp_debug >= 3) +		printk("%s: packet of length %d sent\n", dev->name, (int) skb->len); + +	/* +	 * start the transmit timeout +	 */ +	dev->trans_start = jiffies; + +	prime_rx(dev); + +	return 0; +} + +/****************************************************** + * + * return statistics on the board + * + ******************************************************/ + +static struct enet_statistics *elp_get_stats(struct device *dev) +{ +	elp_device *adapter = (elp_device *) dev->priv; + +	if (elp_debug >= 3) +		printk("%s: request for stats\n", dev->name); + +	/* If the device is closed, just return the latest stats we have, +	   - we cannot ask from the adapter without interrupts */ +	if (!dev->start) +		return &adapter->stats; + +	/* send a get statistics command to the board */ +	adapter->tx_pcb.command = CMD_NETWORK_STATISTICS; +	adapter->tx_pcb.length = 0; +	adapter->got[CMD_NETWORK_STATISTICS] = 0; +	if (!send_pcb(dev, &adapter->tx_pcb)) +		printk("%s: couldn't send get statistics command\n", dev->name); +	else { +		int timeout = jiffies + TIMEOUT; +		while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && jiffies < timeout); +		if (jiffies >= timeout) { +			TIMEOUT_MSG(__LINE__); +			return &adapter->stats; +		} +	} + +	/* statistics are now up to date */ +	return &adapter->stats; +} + +/****************************************************** + * + * close the board + * + ******************************************************/ + +static int elp_close(struct device *dev) +{ +	elp_device *adapter; + +	adapter = dev->priv; + +	if (elp_debug >= 3) +		printk("%s: request to close device\n", dev->name); + +	/* Someone may request the device statistic information even when +	 * the interface is closed. The following will update the statistics +	 * structure in the driver, so we'll be able to give current statistics. +	 */ +	(void) elp_get_stats(dev); + +	/* +	 * disable interrupts on the board +	 */ +	outb_control(0x00, dev->base_addr); + +	/* +	 *  flag transmitter as busy (i.e. not available) +	 */ +	dev->tbusy = 1; + +	/* +	 *  indicate device is closed +	 */ +	dev->start = 0; + +	/* +	 * release the IRQ +	 */ +	free_irq(dev->irq, NULL); + +	/* +	 * and we no longer have to map irq to dev either +	 */ +	irq2dev_map[dev->irq] = 0; + +	free_dma(dev->dma); +	free_pages((unsigned long) adapter->dma_buffer, __get_order(DMA_BUFFER_SIZE)); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + + +/************************************************************ + * + * Set multicast list + * num_addrs==0: clear mc_list + * num_addrs==-1: set promiscuous mode + * num_addrs>0: set mc_list + * + ************************************************************/ + +static void elp_set_mc_list(struct device *dev) +{ +	elp_device *adapter = (elp_device *) dev->priv; +	struct dev_mc_list *dmi = dev->mc_list; +	int i; + +	if (elp_debug >= 3) +		printk("%s: request to set multicast list\n", dev->name); + +	if (!(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) { +		/* send a "load multicast list" command to the board, max 10 addrs/cmd */ +		/* if num_addrs==0 the list will be cleared */ +		adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST; +		adapter->tx_pcb.length = 6 * dev->mc_count; +		for (i = 0; i < dev->mc_count; i++) { +			memcpy(adapter->tx_pcb.data.multicast[i], dmi->dmi_addr, 6); +			dmi = dmi->next; +		} +		adapter->got[CMD_LOAD_MULTICAST_LIST] = 0; +		if (!send_pcb(dev, &adapter->tx_pcb)) +			printk("%s: couldn't send set_multicast command\n", dev->name); +		else { +			int timeout = jiffies + TIMEOUT; +			while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && jiffies < timeout); +			if (jiffies >= timeout) { +				TIMEOUT_MSG(__LINE__); +			} +		} +		if (dev->mc_count) +			adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI; +		else		/* num_addrs == 0 */ +			adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD; +	} else +		adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_PROMISC; +	/* +	 * configure adapter to receive messages (as specified above) +	 * and wait for response +	 */ +	if (elp_debug >= 3) +		printk("%s: sending 82586 configure command\n", dev->name); +	adapter->tx_pcb.command = CMD_CONFIGURE_82586; +	adapter->tx_pcb.length = 2; +	adapter->got[CMD_CONFIGURE_82586] = 0; +	if (!send_pcb(dev, &adapter->tx_pcb)) +		printk("%s: couldn't send 82586 configure command\n", dev->name); +	else { +		int timeout = jiffies + TIMEOUT; +		while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout); +		if (jiffies >= timeout) +			TIMEOUT_MSG(__LINE__); +	} +} + +/****************************************************** + * + * initialise Etherlink Plus board + * + ******************************************************/ + +static void elp_init(struct device *dev) +{ +	elp_device *adapter = dev->priv; + +	/* +	 * set ptrs to various functions +	 */ +	dev->open = elp_open;	/* local */ +	dev->stop = elp_close;	/* local */ +	dev->get_stats = elp_get_stats;		/* local */ +	dev->hard_start_xmit = elp_start_xmit;	/* local */ +	dev->set_multicast_list = elp_set_mc_list;	/* local */ + +	/* Setup the generic properties */ +	ether_setup(dev); + +	/* +	 * setup ptr to adapter specific information +	 */ +	memset(&(adapter->stats), 0, sizeof(struct enet_statistics)); + +	/* +	 * memory information +	 */ +	dev->mem_start = dev->mem_end = dev->rmem_end = dev->rmem_start = 0; +} + +/************************************************************ + * + * A couple of tests to see if there's 3C505 or not + * Called only by elp_autodetect + ************************************************************/ + +static int elp_sense(struct device *dev) +{ +	int timeout; +	int addr = dev->base_addr; +	const char *name = dev->name; +	long flags; +	byte orig_HCR, orig_HSR; + +	if (check_region(addr, 0xf)) +		return -1; + +	orig_HCR = inb_control(addr); +	orig_HSR = inb_status(addr); + +	if (elp_debug > 0) +		printk(search_msg, name, addr); + +	if (((orig_HCR == 0xff) && (orig_HSR == 0xff)) || +	    ((orig_HCR & DIR) != (orig_HSR & DIR))) { +		if (elp_debug > 0) +			printk(notfound_msg, 1); +		return -1;	/* It can't be 3c505 if HCR.DIR != HSR.DIR */ +	} +	/* Enable interrupts - we need timers! */ +	save_flags(flags); +	sti(); + +	/* Wait for a while; the adapter may still be booting up */ +	if (elp_debug > 0) +		printk("%s", stilllooking_msg); +	if (orig_HCR & DIR) { +		/* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */ +		outb_control(orig_HCR & ~DIR, addr); +		timeout = jiffies + 30; +		while (jiffies < timeout); +		restore_flags(flags); +		if (inb_status(addr) & DIR) { +			outb_control(orig_HCR, addr); +			if (elp_debug > 0) +				printk(notfound_msg, 2); +			return -1; +		} +	} else { +		/* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */ +		outb_control(orig_HCR | DIR, addr); +		timeout = jiffies + 30; +		while (jiffies < timeout); +		restore_flags(flags); +		if (!(inb_status(addr) & DIR)) { +			outb_control(orig_HCR, addr); +			if (elp_debug > 0) +				printk(notfound_msg, 3); +			return -1; +		} +	} +	/* +	 * It certainly looks like a 3c505. If it has DMA enabled, it needs +	 * a hard reset. Also, do a hard reset if selected at the compile time. +	 */ +	if (elp_debug > 0) +		printk("%s", found_msg); + +	return 0; +} + +/************************************************************* + * + * Search through addr_list[] and try to find a 3C505 + * Called only by eplus_probe + *************************************************************/ + +static int elp_autodetect(struct device *dev) +{ +	int idx = 0; + +	/* if base address set, then only check that address +	   otherwise, run through the table */ +	if (dev->base_addr != 0) {	/* dev->base_addr == 0 ==> plain autodetect */ +		if (elp_sense(dev) == 0) +			return dev->base_addr; +	} else +		while ((dev->base_addr = addr_list[idx++])) { +			if (elp_sense(dev) == 0) +				return dev->base_addr; +		} + +	/* could not find an adapter */ +	if (elp_debug > 0) +		printk(couldnot_msg, dev->name); + +	return 0;		/* Because of this, the layer above will return -ENODEV */ +} + + +/****************************************************** + * + * probe for an Etherlink Plus board at the specified address + * + ******************************************************/ + +/* There are three situations we need to be able to detect here: + + *  a) the card is idle + *  b) the card is still booting up + *  c) the card is stuck in a strange state (some DOS drivers do this) + * + * In case (a), all is well.  In case (b), we wait 10 seconds to see if the + * card finishes booting, and carry on if so.  In case (c), we do a hard reset, + * loop round, and hope for the best. + * + * This is all very unpleasant, but hopefully avoids the problems with the old + * probe code (which had a 15-second delay if the card was idle, and didn't + * work at all if it was in a weird state). + */ + +int elplus_probe(struct device *dev) +{ +	elp_device *adapter; +	int i, tries, tries1, timeout, okay; + +	/* +	 *  setup adapter structure +	 */ + +	dev->base_addr = elp_autodetect(dev); +	if (!(dev->base_addr)) +		return -ENODEV; + +	/* +	 * setup ptr to adapter specific information +	 */ +	adapter = (elp_device *) (dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL)); +	if (adapter == NULL) { +		printk("%s: out of memory\n", dev->name); +		return -ENODEV; +        } + +	for (tries1 = 0; tries1 < 3; tries1++) { +		outb_control((inb_control(dev->base_addr) | CMDE) & ~DIR, dev->base_addr); +		/* First try to write just one byte, to see if the card is +		 * responding at all normally. +		 */ +		timeout = jiffies + 5; +		okay = 0; +		while (jiffies < timeout && !(inb_status(dev->base_addr) & HCRE)); +		if ((inb_status(dev->base_addr) & HCRE)) { +			outb_command(0, dev->base_addr);	/* send a spurious byte */ +			timeout = jiffies + 5; +			while (jiffies < timeout && !(inb_status(dev->base_addr) & HCRE)); +			if (inb_status(dev->base_addr) & HCRE) +				okay = 1; +		} +		if (!okay) { +			/* Nope, it's ignoring the command register.  This means that +			 * either it's still booting up, or it's died. +			 */ +			printk("%s: command register wouldn't drain, ", dev->name); +			if ((inb_status(dev->base_addr) & 7) == 3) { +				/* If the adapter status is 3, it *could* still be booting. +				 * Give it the benefit of the doubt for 10 seconds. +				 */ +				printk("assuming 3c505 still starting\n"); +				timeout = jiffies + 10 * HZ; +				while (jiffies < timeout && (inb_status(dev->base_addr) & 7)); +				if (inb_status(dev->base_addr) & 7) { +					printk("%s: 3c505 failed to start\n", dev->name); +				} else { +					okay = 1;  /* It started */ +				} +			} else { +				/* Otherwise, it must just be in a strange state.  We probably +				 * need to kick it. +				 */ +				printk("3c505 is sulking\n"); +			} +		} +		for (tries = 0; tries < 5 && okay; tries++) { + +			/* +			 * Try to set the Ethernet address, to make sure that the board +			 * is working. +			 */ +			adapter->tx_pcb.command = CMD_STATION_ADDRESS; +			adapter->tx_pcb.length = 0; +			autoirq_setup(0); +			if (!send_pcb(dev, &adapter->tx_pcb)) { +				printk("%s: could not send first PCB\n", dev->name); +				autoirq_report(0); +				continue; +			} +			if (!receive_pcb(dev, &adapter->rx_pcb)) { +				printk("%s: could not read first PCB\n", dev->name); +				autoirq_report(0); +				continue; +			} +			if ((adapter->rx_pcb.command != CMD_ADDRESS_RESPONSE) || +			    (adapter->rx_pcb.length != 6)) { +				printk("%s: first PCB wrong (%d, %d)\n", dev->name, adapter->rx_pcb.command, adapter->rx_pcb.length); +				autoirq_report(0); +				continue; +			} +			goto okay; +		} +		/* It's broken.  Do a hard reset to re-initialise the board, +		 * and try again. +		 */ +		printk(KERN_INFO "%s: resetting adapter\n", dev->name); +		outb_control(inb_control(dev->base_addr) | FLSH | ATTN, dev->base_addr); +		outb_control(inb_control(dev->base_addr) & ~(FLSH | ATTN), dev->base_addr); +	} +	printk("%s: failed to initialise 3c505\n", dev->name); +	return -ENODEV; + +      okay: +	if (dev->irq) {		/* Is there a preset IRQ? */ +		int rpt = autoirq_report(0); +		if (dev->irq != rpt) { +			printk("%s: warning, irq %d configured but %d detected\n", dev->name, dev->irq, rpt); +			return -ENODEV; +		} +		/* if dev->irq == autoirq_report(0), all is well */ +	} else			/* No preset IRQ; just use what we can detect */ +		dev->irq = autoirq_report(0); +	switch (dev->irq) {	/* Legal, sane? */ +	case 0: +		printk("%s: No IRQ reported by autoirq_report().\n", dev->name); +		printk("%s: Check the jumpers of your 3c505 board.\n", dev->name); +		return -ENODEV; +	case 1: +	case 6: +	case 8: +	case 13: +		printk("%s: Impossible IRQ %d reported by autoirq_report().\n", +		       dev->name, dev->irq); +		return -ENODEV; +	} +	/* +	 *  Now we have the IRQ number so we can disable the interrupts from +	 *  the board until the board is opened. +	 */ +	outb_control(inb_control(dev->base_addr) & ~CMDE, dev->base_addr); + +	/* +	 * copy ethernet address into structure +	 */ +	for (i = 0; i < 6; i++) +		dev->dev_addr[i] = adapter->rx_pcb.data.eth_addr[i]; + +	/* set up the DMA channel */ +	dev->dma = ELP_DMA; + +	/* +	 * print remainder of startup message +	 */ +	printk("%s: 3c505 at %#lx, irq %d, dma %d, ", +	       dev->name, dev->base_addr, dev->irq, dev->dma); +	printk("addr %02x:%02x:%02x:%02x:%02x:%02x, ", +	       dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], +	       dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + +	/* +	 * read more information from the adapter +	 */ + +	adapter->tx_pcb.command = CMD_ADAPTER_INFO; +	adapter->tx_pcb.length = 0; +	if (!send_pcb(dev, &adapter->tx_pcb) || +	    !receive_pcb(dev, &adapter->rx_pcb) || +	    (adapter->rx_pcb.command != CMD_ADAPTER_INFO_RESPONSE) || +	    (adapter->rx_pcb.length != 10)) { +		printk("%s: not responding to second PCB\n", dev->name); +	} +	printk("rev %d.%d, %dk\n", adapter->rx_pcb.data.info.major_vers, adapter->rx_pcb.data.info.minor_vers, adapter->rx_pcb.data.info.RAM_sz); + +	/* +	 * reconfigure the adapter memory to better suit our purposes +	 */ +	adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY; +	adapter->tx_pcb.length = 12; +	adapter->tx_pcb.data.memconf.cmd_q = 8; +	adapter->tx_pcb.data.memconf.rcv_q = 8; +	adapter->tx_pcb.data.memconf.mcast = 10; +	adapter->tx_pcb.data.memconf.frame = 10; +	adapter->tx_pcb.data.memconf.rcv_b = 10; +	adapter->tx_pcb.data.memconf.progs = 0; +	if (!send_pcb(dev, &adapter->tx_pcb) || +	    !receive_pcb(dev, &adapter->rx_pcb) || +	    (adapter->rx_pcb.command != CMD_CONFIGURE_ADAPTER_RESPONSE) || +	    (adapter->rx_pcb.length != 2)) { +		printk("%s: could not configure adapter memory\n", dev->name); +	} +	if (adapter->rx_pcb.data.configure) { +		printk("%s: adapter configuration failed\n", dev->name); +	} +	/* +	 * and reserve the address region +	 */ +	request_region(dev->base_addr, ELP_IO_EXTENT, "3c505"); + +	/* +	 * initialise the device +	 */ +	elp_init(dev); + +	return 0; +} + +#ifdef MODULE +static char devicename[9] = {0,}; +static struct device dev_3c505 = +{ +	devicename,		/* device name is inserted by linux/drivers/net/net_init.c */ +	0, 0, 0, 0, +	0, 0, +	0, 0, 0, NULL, elplus_probe}; + +int io = 0x300; +int irq = 0; + +int init_module(void) +{ +	if (io == 0) +		printk("3c505: You should not use auto-probing with insmod!\n"); +	dev_3c505.base_addr = io; +	dev_3c505.irq = irq; +	if (register_netdev(&dev_3c505) != 0) { +		return -EIO; +	} +	return 0; +} + +void cleanup_module(void) +{ +	unregister_netdev(&dev_3c505); +	kfree(dev_3c505.priv); +	dev_3c505.priv = NULL; + +	/* If we don't do this, we can't re-insmod it later. */ +	release_region(dev_3c505.base_addr, ELP_IO_EXTENT); +} + +#endif				/* MODULE */ + + +/* + * Local Variables: + *  c-file-style: "linux" + *  tab-width: 8 + *  compile-command: "gcc -D__KERNEL__ -I/discs/bibble/src/linux-1.3.69/include  -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE  -c 3c505.c" + * End: + */ diff --git a/linux/src/drivers/net/3c505.h b/linux/src/drivers/net/3c505.h new file mode 100644 index 0000000..0598ca2 --- /dev/null +++ b/linux/src/drivers/net/3c505.h @@ -0,0 +1,245 @@ +/***************************************************************** + * + *  defines for 3Com Etherlink Plus adapter + * + *****************************************************************/ + +/* + * I/O register offsets + */ +#define	PORT_COMMAND	0x00	/* read/write, 8-bit */ +#define	PORT_STATUS	0x02	/* read only, 8-bit */ +#define	PORT_AUXDMA	0x02	/* write only, 8-bit */ +#define	PORT_DATA	0x04	/* read/write, 16-bit */ +#define	PORT_CONTROL	0x06	/* read/write, 8-bit */ + +#define ELP_IO_EXTENT	0x10	/* size of used IO registers */ + +/* + * host control registers bits + */ +#define	ATTN	0x80	/* attention */ +#define	FLSH	0x40	/* flush data register */ +#define DMAE	0x20	/* DMA enable */ +#define DIR	0x10	/* direction */ +#define	TCEN	0x08	/* terminal count interrupt enable */ +#define	CMDE	0x04	/* command register interrupt enable */ +#define	HSF2	0x02	/* host status flag 2 */ +#define	HSF1	0x01	/* host status flag 1 */ + +/* + * combinations of HSF flags used for PCB transmission + */ +#define	HSF_PCB_ACK	HSF1 +#define	HSF_PCB_NAK	HSF2 +#define	HSF_PCB_END	(HSF2|HSF1) +#define	HSF_PCB_MASK	(HSF2|HSF1) + +/* + * host status register bits + */ +#define	HRDY	0x80	/* data register ready */ +#define	HCRE	0x40	/* command register empty */ +#define	ACRF	0x20	/* adapter command register full */ +/* #define DIR 	0x10	direction - same as in control register */ +#define	DONE	0x08	/* DMA done */ +#define	ASF3	0x04	/* adapter status flag 3 */ +#define	ASF2	0x02	/* adapter status flag 2 */ +#define	ASF1	0x01	/* adapter status flag 1 */ + +/* + * combinations of ASF flags used for PCB reception + */ +#define	ASF_PCB_ACK	ASF1 +#define	ASF_PCB_NAK	ASF2 +#define	ASF_PCB_END	(ASF2|ASF1) +#define	ASF_PCB_MASK	(ASF2|ASF1) + +/* + * host aux DMA register bits + */ +#define	DMA_BRST	0x01	/* DMA burst */ + +/* + * maximum amount of data allowed in a PCB + */ +#define	MAX_PCB_DATA	62 + +/***************************************************************** + * + *  timeout value + *	this is a rough value used for loops to stop them from  + *	locking up the whole machine in the case of failure or + *	error conditions + * + *****************************************************************/ + +#define	TIMEOUT	300 + +/***************************************************************** + * + * PCB commands + * + *****************************************************************/ + +enum { +  /* +   * host PCB commands +   */ +  CMD_CONFIGURE_ADAPTER_MEMORY	= 0x01, +  CMD_CONFIGURE_82586		= 0x02, +  CMD_STATION_ADDRESS		= 0x03, +  CMD_DMA_DOWNLOAD		= 0x04, +  CMD_DMA_UPLOAD		= 0x05, +  CMD_PIO_DOWNLOAD		= 0x06, +  CMD_PIO_UPLOAD		= 0x07, +  CMD_RECEIVE_PACKET		= 0x08, +  CMD_TRANSMIT_PACKET		= 0x09, +  CMD_NETWORK_STATISTICS	= 0x0a, +  CMD_LOAD_MULTICAST_LIST	= 0x0b, +  CMD_CLEAR_PROGRAM		= 0x0c, +  CMD_DOWNLOAD_PROGRAM		= 0x0d, +  CMD_EXECUTE_PROGRAM		= 0x0e, +  CMD_SELF_TEST			= 0x0f, +  CMD_SET_STATION_ADDRESS	= 0x10, +  CMD_ADAPTER_INFO		= 0x11, +  NUM_TRANSMIT_CMDS, + +  /* +   * adapter PCB commands +   */ +  CMD_CONFIGURE_ADAPTER_RESPONSE	= 0x31, +  CMD_CONFIGURE_82586_RESPONSE		= 0x32, +  CMD_ADDRESS_RESPONSE			= 0x33, +  CMD_DOWNLOAD_DATA_REQUEST		= 0x34, +  CMD_UPLOAD_DATA_REQUEST		= 0x35, +  CMD_RECEIVE_PACKET_COMPLETE		= 0x38, +  CMD_TRANSMIT_PACKET_COMPLETE		= 0x39, +  CMD_NETWORK_STATISTICS_RESPONSE	= 0x3a, +  CMD_LOAD_MULTICAST_RESPONSE		= 0x3b, +  CMD_CLEAR_PROGRAM_RESPONSE		= 0x3c, +  CMD_DOWNLOAD_PROGRAM_RESPONSE		= 0x3d, +  CMD_EXECUTE_RESPONSE			= 0x3e, +  CMD_SELF_TEST_RESPONSE		= 0x3f, +  CMD_SET_ADDRESS_RESPONSE		= 0x40, +  CMD_ADAPTER_INFO_RESPONSE		= 0x41 +}; + +/* Definitions for the PCB data structure */ + +/* Data units */ +typedef unsigned char         byte; +typedef unsigned short int    word; +typedef unsigned long int     dword; + +/* Data structures */ +struct Memconf { +	word	cmd_q, +		rcv_q, +		mcast, +		frame, +		rcv_b, +		progs; +}; + +struct Rcv_pkt { +	word	buf_ofs, +		buf_seg, +		buf_len, +		timeout; +}; + +struct Xmit_pkt { +	word	buf_ofs, +		buf_seg, +		pkt_len; +}; + +struct Rcv_resp { +	word	buf_ofs, +		buf_seg, +		buf_len, +		pkt_len, +		timeout, +		status; +	dword	timetag; +}; + +struct Xmit_resp { +	word	buf_ofs, +		buf_seg, +		c_stat, +		status; +}; + + +struct Netstat { +	dword	tot_recv, +		tot_xmit; +	word	err_CRC, +		err_align, +		err_res, +		err_ovrrun; +}; + + +struct Selftest { +	word	error; +	union { +		word ROM_cksum; +		struct { +			word ofs, seg; +		} RAM; +		word i82586; +	} failure; +}; + +struct Info { +	byte	minor_vers, +		major_vers; +	word	ROM_cksum, +		RAM_sz, +		free_ofs, +		free_seg; +}; + +struct Memdump { +       word size, +            off, +            seg; +}; + +/* +Primary Command Block. The most important data structure. All communication +between the host and the adapter is done with these. (Except for the actual +ethernet data, which has different packaging.) +*/ +typedef struct { +	byte	command; +	byte	length; +	union	{ +		struct Memconf		memconf; +		word			configure; +		struct Rcv_pkt		rcv_pkt; +		struct Xmit_pkt		xmit_pkt; +		byte			multicast[10][6]; +		byte			eth_addr[6]; +		byte			failed; +		struct Rcv_resp		rcv_resp; +		struct Xmit_resp	xmit_resp; +		struct Netstat		netstat; +		struct Selftest		selftest; +		struct Info		info; +		struct Memdump    	memdump; +		byte			raw[62]; +	} data; +} pcb_struct; + +/* These defines for 'configure' */ +#define RECV_STATION	0x00 +#define RECV_BROAD	0x01 +#define RECV_MULTI	0x02 +#define RECV_PROMISC	0x04 +#define NO_LOOPBACK	0x00 +#define INT_LOOPBACK	0x08 +#define EXT_LOOPBACK	0x10 diff --git a/linux/src/drivers/net/3c507.c b/linux/src/drivers/net/3c507.c new file mode 100644 index 0000000..58ba2d7 --- /dev/null +++ b/linux/src/drivers/net/3c507.c @@ -0,0 +1,924 @@ +/* 3c507.c: An EtherLink16 device driver for Linux. */ +/* +	Written 1993,1994 by Donald Becker. + +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings) +	and jrs@world.std.com (Rick Sladkey) for testing and bugfixes. +	Mark Salazar <leslie@access.digex.net> made the changes for cards with +	only 16K packet buffers. + +	Things remaining to do: +	Verify that the tx and rx buffers don't have fencepost errors. +	Move the theory of operation and memory map documentation. +	The statistics need to be updated correctly. +*/ + +static const char *version = +	"3c507.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + + +#include <linux/module.h> + +/* +  Sources: +	This driver wouldn't have been written with the availability of the +	Crynwr driver source code.	It provided a known-working implementation +	that filled in the gaping holes of the Intel documentation.  Three cheers +	for Russ Nelson. + +	Intel Microcommunications Databook, Vol. 1, 1990.  It provides just enough +	info that the casual reader might think that it documents the i82586 :-<. +*/ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/malloc.h> + + +/* use 0 for production, 1 for verification, 2..7 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 1 +#endif +static unsigned int net_debug = NET_DEBUG; + +/* A zero-terminated list of common I/O addresses to be probed. */ +static unsigned int netcard_portlist[] = +	{ 0x300, 0x320, 0x340, 0x280, 0}; + +static void init_rx_bufs(struct device *dev); + +/* +  			Details of the i82586. + +   You'll really need the databook to understand the details of this part, +   but the outline is that the i82586 has two separate processing units. +   Both are started from a list of three configuration tables, of which only +   the last, the System Control Block (SCB), is used after reset-time.  The SCB +   has the following fields: +		Status word +		Command word +		Tx/Command block addr. +		Rx block addr. +   The command word accepts the following controls for the Tx and Rx units: +  */ + +#define	 CUC_START	 0x0100 +#define	 CUC_RESUME	 0x0200 +#define	 CUC_SUSPEND 0x0300 +#define	 RX_START	 0x0010 +#define	 RX_RESUME	 0x0020 +#define	 RX_SUSPEND	 0x0030 + +/* The Rx unit uses a list of frame descriptors and a list of data buffer +   descriptors.  We use full-sized (1518 byte) data buffers, so there is +   a one-to-one pairing of frame descriptors to buffer descriptors. + +   The Tx ("command") unit executes a list of commands that look like: +		Status word		Written by the 82586 when the command is done. +		Command word	Command in lower 3 bits, post-command action in upper 3 +		Link word		The address of the next command. +		Parameters		(as needed). + +	Some definitions related to the Command Word are: + */ +#define CMD_EOL		0x8000			/* The last command of the list, stop. */ +#define CMD_SUSP	0x4000			/* Suspend after doing cmd. */ +#define CMD_INTR	0x2000			/* Interrupt after doing cmd. */ + +enum commands { +	CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, +	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7}; + +/* Information that need to be kept for each board. */ +struct net_local { +	struct enet_statistics stats; +	int last_restart; +	ushort rx_head; +	ushort rx_tail; +	ushort tx_head; +	ushort tx_cmd_link; +	ushort tx_reap; +}; + +/* +  		Details of the EtherLink16 Implementation +  The 3c507 is a generic shared-memory i82586 implementation. +  The host can map 16K, 32K, 48K, or 64K of the 64K memory into +  0x0[CD][08]0000, or all 64K into 0xF[02468]0000. +  */ + +/* Offsets from the base I/O address. */ +#define	SA_DATA		0	/* Station address data, or 3Com signature. */ +#define MISC_CTRL	6	/* Switch the SA_DATA banks, and bus config bits. */ +#define RESET_IRQ	10	/* Reset the latched IRQ line. */ +#define SIGNAL_CA	11	/* Frob the 82586 Channel Attention line. */ +#define ROM_CONFIG	13 +#define MEM_CONFIG	14 +#define IRQ_CONFIG	15 +#define EL16_IO_EXTENT 16 + +/* The ID port is used at boot-time to locate the ethercard. */ +#define ID_PORT		0x100 + +/* Offsets to registers in the mailbox (SCB). */ +#define iSCB_STATUS	0x8 +#define iSCB_CMD		0xA +#define iSCB_CBL		0xC	/* Command BLock offset. */ +#define iSCB_RFA		0xE	/* Rx Frame Area offset. */ + +/*  Since the 3c507 maps the shared memory window so that the last byte is +	at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or +	48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.  +	We can account for this be setting the 'SBC Base' entry in the ISCP table +	below for all the 16 bit offset addresses, and also adding the 'SCB Base' +	value to all 24 bit physical addresses (in the SCP table and the TX and RX +	Buffer Descriptors). +					-Mark	 +	*/ +#define SCB_BASE		((unsigned)64*1024 - (dev->mem_end - dev->mem_start)) +  +/* +  What follows in 'init_words[]' is the "program" that is downloaded to the +  82586 memory.	 It's mostly tables and command blocks, and starts at the +  reset address 0xfffff6.  This is designed to be similar to the EtherExpress, +  thus the unusual location of the SCB at 0x0008. + +  Even with the additional "don't care" values, doing it this way takes less +  program space than initializing the individual tables, and I feel it's much +  cleaner. + +  The databook is particularly useless for the first two structures, I had +  to use the Crynwr driver as an example. + +   The memory setup is as follows: +   */ + +#define CONFIG_CMD	0x0018 +#define SET_SA_CMD	0x0024 +#define SA_OFFSET	0x002A +#define IDLELOOP	0x30 +#define TDR_CMD		0x38 +#define TDR_TIME	0x3C +#define DUMP_CMD	0x40 +#define DIAG_CMD	0x48 +#define SET_MC_CMD	0x4E +#define DUMP_DATA	0x56	/* A 170 byte buffer for dump and Set-MC into. */ + +#define TX_BUF_START	0x0100 +#define NUM_TX_BUFS 	4 +#define TX_BUF_SIZE 	(1518+14+20+16) /* packet+header+TBD */ + +#define RX_BUF_START	0x2000 +#define RX_BUF_SIZE 	(1518+14+18)	/* packet+header+RBD */ +#define RX_BUF_END		(dev->mem_end - dev->mem_start) + +/* +  That's it: only 86 bytes to set up the beast, including every extra +  command available.  The 170 byte buffer at DUMP_DATA is shared between the +  Dump command (called only by the diagnostic program) and the SetMulticastList +  command.  + +  To complete the memory setup you only have to write the station address at +  SA_OFFSET and create the Tx & Rx buffer lists. + +  The Tx command chain and buffer list is setup as follows: +  A Tx command table, with the data buffer pointing to... +  A Tx data buffer descriptor.  The packet is in a single buffer, rather than +	chaining together several smaller buffers. +  A NoOp command, which initially points to itself, +  And the packet data. + +  A transmit is done by filling in the Tx command table and data buffer, +  re-writing the NoOp command, and finally changing the offset of the last +  command to point to the current Tx command.  When the Tx command is finished, +  it jumps to the NoOp, when it loops until the next Tx command changes the +  "link offset" in the NoOp.  This way the 82586 never has to go through the +  slow restart sequence. + +  The Rx buffer list is set up in the obvious ring structure.  We have enough +  memory (and low enough interrupt latency) that we can avoid the complicated +  Rx buffer linked lists by alway associating a full-size Rx data buffer with +  each Rx data frame. + +  I current use four transmit buffers starting at TX_BUF_START (0x0100), and +  use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers. + +  */ + +unsigned short init_words[] = { +	/*	System Configuration Pointer (SCP). */ +	0x0000,					/* Set bus size to 16 bits. */ +	0,0,					/* pad words. */ +	0x0000,0x0000,			/* ISCP phys addr, set in init_82586_mem(). */ + +	/*	Intermediate System Configuration Pointer (ISCP). */ +	0x0001,					/* Status word that's cleared when init is done. */ +	0x0008,0,0,				/* SCB offset, (skip, skip) */ + +	/* System Control Block (SCB). */ +	0,0xf000|RX_START|CUC_START,	/* SCB status and cmd. */ +	CONFIG_CMD,				/* Command list pointer, points to Configure. */ +	RX_BUF_START,				/* Rx block list. */ +	0,0,0,0,				/* Error count: CRC, align, buffer, overrun. */ + +	/* 0x0018: Configure command.  Change to put MAC data with packet. */ +	0, CmdConfigure,		/* Status, command.		*/ +	SET_SA_CMD,				/* Next command is Set Station Addr. */ +	0x0804,					/* "4" bytes of config data, 8 byte FIFO. */ +	0x2e40,					/* Magic values, including MAC data location. */ +	0,						/* Unused pad word. */ + +	/* 0x0024: Setup station address command. */ +	0, CmdSASetup, +	SET_MC_CMD,				/* Next command. */ +	0xaa00,0xb000,0x0bad,	/* Station address (to be filled in) */ + +	/* 0x0030: NOP, looping back to itself.	 Point to first Tx buffer to Tx. */ +	0, CmdNOp, IDLELOOP, 0 /* pad */, + +	/* 0x0038: A unused Time-Domain Reflectometer command. */ +	0, CmdTDR, IDLELOOP, 0, + +	/* 0x0040: An unused Dump State command. */ +	0, CmdDump, IDLELOOP, DUMP_DATA, + +	/* 0x0048: An unused Diagnose command. */ +	0, CmdDiagnose, IDLELOOP, + +	/* 0x004E: An empty set-multicast-list command. */ +	0, CmdMulticastList, IDLELOOP, 0, +}; + +/* Index to functions, as function prototypes. */ + +extern int el16_probe(struct device *dev);	/* Called from Space.c */ + +static int	el16_probe1(struct device *dev, int ioaddr); +static int	el16_open(struct device *dev); +static int	el16_send_packet(struct sk_buff *skb, struct device *dev); +static void	el16_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void el16_rx(struct device *dev); +static int	el16_close(struct device *dev); +static struct enet_statistics *el16_get_stats(struct device *dev); + +static void hardware_send_packet(struct device *dev, void *buf, short length); +void init_82586_mem(struct device *dev); + + +#ifdef HAVE_DEVLIST +struct netdev_entry netcard_drv = +{"3c507", el16_probe1, EL16_IO_EXTENT, netcard_portlist}; +#endif + +/* Check for a network adaptor of this type, and return '0' iff one exists. +	If dev->base_addr == 0, probe all likely locations. +	If dev->base_addr == 1, always return failure. +	If dev->base_addr == 2, (detachable devices only) allocate space for the +	device and return success. +	*/ +int +el16_probe(struct device *dev) +{ +	int base_addr = dev ? dev->base_addr : 0; +	int i; + +	if (base_addr > 0x1ff)	/* Check a single specified location. */ +		return el16_probe1(dev, base_addr); +	else if (base_addr != 0) +		return ENXIO;		/* Don't probe at all. */ + +	for (i = 0; netcard_portlist[i]; i++) { +		int ioaddr = netcard_portlist[i]; +		if (check_region(ioaddr, EL16_IO_EXTENT)) +			continue; +		if (el16_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} + +int el16_probe1(struct device *dev, int ioaddr) +{ +	static unsigned char init_ID_done = 0, version_printed = 0; +	int i, irq, irqval; + +	if (init_ID_done == 0) { +		ushort lrs_state = 0xff; +		/* Send the ID sequence to the ID_PORT to enable the board(s). */ +		outb(0x00, ID_PORT); +		for(i = 0; i < 255; i++) { +			outb(lrs_state, ID_PORT); +			lrs_state <<= 1; +			if (lrs_state & 0x100) +				lrs_state ^= 0xe7; +		} +		outb(0x00, ID_PORT); +		init_ID_done = 1; +	} + +	if (inb(ioaddr) == '*' && inb(ioaddr+1) == '3' +		&& inb(ioaddr+2) == 'C' && inb(ioaddr+3) == 'O') +		; +	else +		return ENODEV; + +	/* Allocate a new 'dev' if needed. */ +	if (dev == NULL) +		dev = init_etherdev(0, sizeof(struct net_local)); + +	if (net_debug  &&  version_printed++ == 0) +		printk("%s", version); + +	printk("%s: 3c507 at %#x,", dev->name, ioaddr); + +	/* We should make a few more checks here, like the first three octets of +	   the S.A. for the manufacturer's code. */  + +	irq = inb(ioaddr + IRQ_CONFIG) & 0x0f; + +	irqval = request_irq(irq, &el16_interrupt, 0, "3c507", NULL); +	if (irqval) { +		printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval); +		return EAGAIN; +	} +	 +	/* We've committed to using the board, and can start filling in *dev. */ +	request_region(ioaddr, EL16_IO_EXTENT, "3c507"); +	dev->base_addr = ioaddr; + +	outb(0x01, ioaddr + MISC_CTRL); +	for (i = 0; i < 6; i++) { +		dev->dev_addr[i] = inb(ioaddr + i); +		printk(" %02x", dev->dev_addr[i]); +	} + +	if ((dev->mem_start & 0xf) > 0) +		net_debug = dev->mem_start & 7; + +#ifdef MEM_BASE +	dev->mem_start = MEM_BASE; +	dev->mem_end = dev->mem_start + 0x10000; +#else +	{ +		int base; +		int size; +		char mem_config = inb(ioaddr + MEM_CONFIG); +		if (mem_config & 0x20) { +			size = 64*1024; +			base = 0xf00000 + (mem_config & 0x08 ? 0x080000 +							   : ((mem_config & 3) << 17)); +		} else { +			size = ((mem_config & 3) + 1) << 14; +			base = 0x0c0000 + ( (mem_config & 0x18) << 12); +		} +		dev->mem_start = base; +		dev->mem_end = base + size; +	} +#endif + +	dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0; +	dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f; + +	printk(", IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->irq, +		   dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1); + +	if (net_debug) +		printk("%s", version); + +	/* Initialize the device structure. */ +	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); +	if (dev->priv == NULL) +		return -ENOMEM; +	memset(dev->priv, 0, sizeof(struct net_local)); + +	dev->open		= el16_open; +	dev->stop		= el16_close; +	dev->hard_start_xmit = el16_send_packet; +	dev->get_stats	= el16_get_stats; + +	ether_setup(dev);	/* Generic ethernet behaviour */ +	 +	dev->flags&=~IFF_MULTICAST;	/* Multicast doesn't work */ + +	return 0; +} + + + +static int +el16_open(struct device *dev) +{ +	irq2dev_map[dev->irq] = dev; + +	/* Initialize the 82586 memory and start it. */ +	init_82586_mem(dev); + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; + +	MOD_INC_USE_COUNT; + +	return 0; +} + +static int +el16_send_packet(struct sk_buff *skb, struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	short *shmem = (short*)dev->mem_start; + +	if (dev->tbusy) { +		/* If we get here, some higher level has decided we are broken. +		   There should really be a "kick me" function call instead. */ +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < 5) +			return 1; +		if (net_debug > 1) +			printk("%s: transmit timed out, %s?  ", dev->name, +				   shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" : +				   "network cable problem"); +		/* Try to restart the adaptor. */ +		if (lp->last_restart == lp->stats.tx_packets) { +			if (net_debug > 1) printk("Resetting board.\n"); +			/* Completely reset the adaptor. */ +			init_82586_mem(dev); +		} else { +			/* Issue the channel attention signal and hope it "gets better". */ +			if (net_debug > 1) printk("Kicking board.\n"); +			shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START; +			outb(0, ioaddr + SIGNAL_CA);			/* Issue channel-attn. */ +			lp->last_restart = lp->stats.tx_packets; +		} +		dev->tbusy=0; +		dev->trans_start = jiffies; +	} + +	/* If some higher layer thinks we've missed an tx-done interrupt +	   we are passed NULL. Caution: dev_tint() handles the cli()/sti() +	   itself. */ +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	/* Block a timer-based transmit from overlapping. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) +		printk("%s: Transmitter access conflict.\n", dev->name); +	else { +		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		unsigned char *buf = skb->data; + +		/* Disable the 82586's input to the interrupt line. */ +		outb(0x80, ioaddr + MISC_CTRL); +		hardware_send_packet(dev, buf, length); +		dev->trans_start = jiffies; +		/* Enable the 82586 interrupt input. */ +		outb(0x84, ioaddr + MISC_CTRL); +	} + +	dev_kfree_skb (skb, FREE_WRITE); + +	/* You might need to clean up and record Tx statistics here. */ + +	return 0; +} + +/*	The typical workload of the driver: +	Handle the network interface interrupts. */ +static void +el16_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +	struct device *dev = (struct device *)(irq2dev_map[irq]); +	struct net_local *lp; +	int ioaddr, status, boguscount = 0; +	ushort ack_cmd = 0; +	ushort *shmem; +	 +	if (dev == NULL) { +		printk ("net_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +	dev->interrupt = 1; +	 +	ioaddr = dev->base_addr; +	lp = (struct net_local *)dev->priv; +	shmem = ((ushort*)dev->mem_start); +	 +	status = shmem[iSCB_STATUS>>1]; +	 +	if (net_debug > 4) { +		printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status); +	} + +	/* Disable the 82586's input to the interrupt line. */ +	outb(0x80, ioaddr + MISC_CTRL); + +	/* Reap the Tx packet buffers. */ +	while (lp->tx_reap != lp->tx_head) { +	  unsigned short tx_status = shmem[lp->tx_reap>>1]; + +	  if (tx_status == 0) { +		if (net_debug > 5)  printk("Couldn't reap %#x.\n", lp->tx_reap); +		break; +	  } +	  if (tx_status & 0x2000) { +		lp->stats.tx_packets++; +		lp->stats.collisions += tx_status & 0xf; +		dev->tbusy = 0; +		mark_bh(NET_BH);		/* Inform upper layers. */ +	  } else { +		lp->stats.tx_errors++; +		if (tx_status & 0x0600)  lp->stats.tx_carrier_errors++; +		if (tx_status & 0x0100)  lp->stats.tx_fifo_errors++; +		if (!(tx_status & 0x0040))  lp->stats.tx_heartbeat_errors++; +		if (tx_status & 0x0020)  lp->stats.tx_aborted_errors++; +	  } +	  if (net_debug > 5) +		  printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status); +	  lp->tx_reap += TX_BUF_SIZE; +	  if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE) +		lp->tx_reap = TX_BUF_START; +	  if (++boguscount > 4) +		break; +	} + +	if (status & 0x4000) { /* Packet received. */ +		if (net_debug > 5) +			printk("Received packet, rx_head %04x.\n", lp->rx_head); +		el16_rx(dev); +	} + +	/* Acknowledge the interrupt sources. */ +	ack_cmd = status & 0xf000; + +	if ((status & 0x0700) != 0x0200 && dev->start) { +		if (net_debug) +			printk("%s: Command unit stopped, status %04x, restarting.\n", +				   dev->name, status); +		/* If this ever occurs we should really re-write the idle loop, reset +		   the Tx list, and do a complete restart of the command unit. +		   For now we rely on the Tx timeout if the resume doesn't work. */ +		ack_cmd |= CUC_RESUME; +	} + +	if ((status & 0x0070) != 0x0040  &&  dev->start) { +		/* The Rx unit is not ready, it must be hung.  Restart the receiver by +		   initializing the rx buffers, and issuing an Rx start command. */ +		if (net_debug) +			printk("%s: Rx unit stopped, status %04x, restarting.\n", +				   dev->name, status); +		init_rx_bufs(dev); +		shmem[iSCB_RFA >> 1] = RX_BUF_START; +		ack_cmd |= RX_START; +	} + +	shmem[iSCB_CMD>>1] = ack_cmd; +	outb(0, ioaddr + SIGNAL_CA);			/* Issue channel-attn. */ + +	/* Clear the latched interrupt. */ +	outb(0, ioaddr + RESET_IRQ); + +	/* Enable the 82586's interrupt input. */ +	outb(0x84, ioaddr + MISC_CTRL); + +	return; +} + +static int +el16_close(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	ushort *shmem = (short*)dev->mem_start; + +	dev->tbusy = 1; +	dev->start = 0; + +	/* Flush the Tx and disable Rx. */ +	shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND; +	outb(0, ioaddr + SIGNAL_CA); + +	/* Disable the 82586's input to the interrupt line. */ +	outb(0x80, ioaddr + MISC_CTRL); + +	/* We always physically use the IRQ line, so we don't do free_irq(). +	   We do remove ourselves from the map. */ + +	irq2dev_map[dev->irq] = 0; + +	/* Update the statistics here. */ + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +/* Get the current statistics.	This may be called with the card open or +   closed. */ +static struct enet_statistics * +el16_get_stats(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; + +	/* ToDo: decide if there are any useful statistics from the SCB. */ + +	return &lp->stats; +} + +/* Initialize the Rx-block list. */ +static void +init_rx_bufs(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short *write_ptr; +	unsigned short SCB_base = SCB_BASE; + +	int cur_rxbuf = lp->rx_head = RX_BUF_START; +	 +	/* Initialize each Rx frame + data buffer. */ +	do {	/* While there is room for one more. */ + +	  write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf); + +		*write_ptr++ = 0x0000; 				/* Status */ +		*write_ptr++ = 0x0000;				/* Command */ +		*write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */ +		*write_ptr++ = cur_rxbuf + 22;		/* Buffer offset */ +		*write_ptr++ = 0x0000; 				/* Pad for dest addr. */ +		*write_ptr++ = 0x0000; +		*write_ptr++ = 0x0000; +		*write_ptr++ = 0x0000; 				/* Pad for source addr. */ +		*write_ptr++ = 0x0000; +		*write_ptr++ = 0x0000; +		*write_ptr++ = 0x0000;				/* Pad for protocol. */ +		 +		*write_ptr++ = 0x0000;				/* Buffer: Actual count */ +		*write_ptr++ = -1;					/* Buffer: Next (none). */ +		*write_ptr++ = cur_rxbuf + 0x20 + SCB_base;	/* Buffer: Address low */ +		*write_ptr++ = 0x0000; +		/* Finally, the number of bytes in the buffer. */ +		*write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20; +		 +		lp->rx_tail = cur_rxbuf; +		cur_rxbuf += RX_BUF_SIZE; +	} while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE); +	 +	/* Terminate the list by setting the EOL bit, and wrap the pointer to make +	   the list a ring. */ +	write_ptr = (unsigned short *) +	  (dev->mem_start + lp->rx_tail + 2); +	*write_ptr++ = 0xC000;					/* Command, mark as last. */ +	*write_ptr++ = lp->rx_head;				/* Link */ + +} + +void +init_82586_mem(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	short ioaddr = dev->base_addr; +	ushort *shmem = (short*)dev->mem_start; + +	/* Enable loopback to protect the wire while starting up, +	   and hold the 586 in reset during the memory initialization. */ +	outb(0x20, ioaddr + MISC_CTRL); + +	/* Fix the ISCP address and base. */ +	init_words[3] = SCB_BASE; +	init_words[7] = SCB_BASE; + +	/* Write the words at 0xfff6 (address-aliased to 0xfffff6). */ +	memcpy((void*)dev->mem_end-10, init_words, 10); + +	/* Write the words at 0x0000. */ +	memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10); + +	/* Fill in the station address. */ +	memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr, +		   sizeof(dev->dev_addr)); + +	/* The Tx-block list is written as needed.  We just set up the values. */ +	lp->tx_cmd_link = IDLELOOP + 4; +	lp->tx_head = lp->tx_reap = TX_BUF_START; + +	init_rx_bufs(dev); + +	/* Start the 586 by releasing the reset line, but leave loopback. */ +	outb(0xA0, ioaddr + MISC_CTRL); + +	/* This was time consuming to track down: you need to give two channel +	   attention signals to reliably start up the i82586. */ +	outb(0, ioaddr + SIGNAL_CA); + +	{ +		int boguscnt = 50; +		while (shmem[iSCB_STATUS>>1] == 0) +			if (--boguscnt == 0) { +				printk("%s: i82586 initialization timed out with status %04x," +					   "cmd %04x.\n", dev->name, +					   shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]); +				break; +			} +		/* Issue channel-attn -- the 82586 won't start. */ +		outb(0, ioaddr + SIGNAL_CA); +	} + +	/* Disable loopback and enable interrupts. */ +	outb(0x84, ioaddr + MISC_CTRL); +	if (net_debug > 4) +		printk("%s: Initialized 82586, status %04x.\n", dev->name, +			   shmem[iSCB_STATUS>>1]); +	return; +} + +static void +hardware_send_packet(struct device *dev, void *buf, short length) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	short ioaddr = dev->base_addr; +	ushort tx_block = lp->tx_head; +	ushort *write_ptr =	  (ushort *)(dev->mem_start + tx_block); + +	/* Set the write pointer to the Tx block, and put out the header. */ +	*write_ptr++ = 0x0000;				/* Tx status */ +	*write_ptr++ = CMD_INTR|CmdTx;		/* Tx command */ +	*write_ptr++ = tx_block+16;			/* Next command is a NoOp. */ +	*write_ptr++ = tx_block+8;			/* Data Buffer offset. */ + +	/* Output the data buffer descriptor. */ +	*write_ptr++ = length | 0x8000;		/* Byte count parameter. */ +	*write_ptr++ = -1;					/* No next data buffer. */ +	*write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */ +	*write_ptr++ = 0x0000;				/* Buffer address high bits (always zero). */ + +	/* Output the Loop-back NoOp command. */ +	*write_ptr++ = 0x0000;				/* Tx status */ +	*write_ptr++ = CmdNOp;				/* Tx command */ +	*write_ptr++ = tx_block+16;			/* Next is myself. */ + +	/* Output the packet at the write pointer. */ +	memcpy(write_ptr, buf, length); + +	/* Set the old command link pointing to this send packet. */ +	*(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block; +	lp->tx_cmd_link = tx_block + 20; + +	/* Set the next free tx region. */ +	lp->tx_head = tx_block + TX_BUF_SIZE; +	if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE) +		lp->tx_head = TX_BUF_START; + +	if (net_debug > 4) { +		printk("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n", +			   dev->name, ioaddr, length, tx_block, lp->tx_head); +	} + +	if (lp->tx_head != lp->tx_reap) +		dev->tbusy = 0; +} + +static void +el16_rx(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	short *shmem = (short*)dev->mem_start; +	ushort rx_head = lp->rx_head; +	ushort rx_tail = lp->rx_tail; +	ushort boguscount = 10; +	short frame_status; + +	while ((frame_status = shmem[rx_head>>1]) < 0) {   /* Command complete */ +		ushort *read_frame =  (short *)(dev->mem_start + rx_head); +		ushort rfd_cmd = read_frame[1]; +		ushort next_rx_frame = read_frame[2]; +		ushort data_buffer_addr = read_frame[3]; +		ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr); +		ushort pkt_len = data_frame[0]; + +		if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22 +			|| (pkt_len & 0xC000) != 0xC000) { +			printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x" +				   "next %04x data-buf @%04x %04x.\n", dev->name, rx_head, +				   frame_status, rfd_cmd, next_rx_frame, data_buffer_addr, +				   pkt_len); +		} else if ((frame_status & 0x2000) == 0) { +			/* Frame Rxed, but with error. */ +			lp->stats.rx_errors++; +			if (frame_status & 0x0800) lp->stats.rx_crc_errors++; +			if (frame_status & 0x0400) lp->stats.rx_frame_errors++; +			if (frame_status & 0x0200) lp->stats.rx_fifo_errors++; +			if (frame_status & 0x0100) lp->stats.rx_over_errors++; +			if (frame_status & 0x0080) lp->stats.rx_length_errors++; +		} else { +			/* Malloc up new buffer. */ +			struct sk_buff *skb; + +			pkt_len &= 0x3fff; +			skb = dev_alloc_skb(pkt_len+2); +			if (skb == NULL) { +				printk("%s: Memory squeeze, dropping packet.\n", dev->name); +				lp->stats.rx_dropped++; +				break; +			} +			 +			skb_reserve(skb,2); +			skb->dev = dev; + +			/* 'skb->data' points to the start of sk_buff data area. */ +			memcpy(skb_put(skb,pkt_len), data_frame + 5, pkt_len); +		 +			skb->protocol=eth_type_trans(skb,dev); +			netif_rx(skb); +			lp->stats.rx_packets++; +		} + +		/* Clear the status word and set End-of-List on the rx frame. */ +		read_frame[0] = 0; +		read_frame[1] = 0xC000; +		/* Clear the end-of-list on the prev. RFD. */ +		*(short*)(dev->mem_start + rx_tail + 2) = 0x0000; + +		rx_tail = rx_head; +		rx_head = next_rx_frame; +		if (--boguscount == 0) +			break; +	} + +	lp->rx_head = rx_head; +	lp->rx_tail = rx_tail; +} +#ifdef MODULE +static char devicename[9] = { 0, }; +static struct device dev_3c507 = { +	devicename, /* device name is inserted by linux/drivers/net/net_init.c */ +	0, 0, 0, 0, +	0, 0, +	0, 0, 0, NULL, el16_probe +}; + +static int io = 0x300; +static int irq = 0; + +int init_module(void) +{ +	if (io == 0) +		printk("3c507: You should not use auto-probing with insmod!\n"); +	dev_3c507.base_addr = io; +	dev_3c507.irq       = irq; +	if (register_netdev(&dev_3c507) != 0) { +		printk("3c507: register_netdev() returned non-zero.\n"); +		return -EIO; +	} +	return 0; +} + +void +cleanup_module(void) +{ +	unregister_netdev(&dev_3c507); +	kfree(dev_3c507.priv); +	dev_3c507.priv = NULL; + +	/* If we don't do this, we can't re-insmod it later. */ +	free_irq(dev_3c507.irq, NULL); +	release_region(dev_3c507.base_addr, EL16_IO_EXTENT); +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c" + *  version-control: t + *  kept-new-versions: 5 + *  tab-width: 4 + *  c-indent-level: 4 + * End: + */ diff --git a/linux/src/drivers/net/3c509.c b/linux/src/drivers/net/3c509.c new file mode 100644 index 0000000..727595c --- /dev/null +++ b/linux/src/drivers/net/3c509.c @@ -0,0 +1,842 @@ +/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ +/* +	Written 1993-1998 by Donald Becker. + +	Copyright 1994-1998 by Donald Becker. +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency.	 This software may be used and +	distributed according to the terms of the GNU Public License, +	incorporated herein by reference. + +	This driver is for the 3Com EtherLinkIII series. + +	The author may be reached as becker@cesdis.gsfc.nasa.gov or +	C/O Center of Excellence in Space Data and Information Sciences +		Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	Known limitations: +	Because of the way 3c509 ISA detection works it's difficult to predict +	a priori which of several ISA-mode cards will be detected first. + +	This driver does not use predictive interrupt mode, resulting in higher +	packet latency but lower overhead.  If interrupts are disabled for an +	unusually long time it could also result in missed packets, but in +	practice this rarely happens. + + +	FIXES: +		Alan Cox:       Removed the 'Unexpected interrupt' bug. +		Michael Meskes:	Upgraded to Donald Becker's version 1.07. +		Alan Cox:	Increased the eeprom delay. Regardless of  +				what the docs say some people definitely +				get problems with lower (but in card spec) +				delays +		v1.10 4/21/97 Fixed module code so that multiple cards may be detected, +				other cleanups.  -djb +		v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb +		v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb +		v1.15 1/31/98 Faster recovery for Tx errors. -djb +		v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb +*/ + +static char *version = "3c509.c:1.16 2/3/98 becker@cesdis.gsfc.nasa.gov\n"; +/* A few values that may be tweaked. */ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (400*HZ/1000) +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 10; + +#include <linux/module.h> + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/ioport.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/config.h>	/* for CONFIG_MCA */ +#include <linux/delay.h>	/* for udelay() */ + +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef EL3_DEBUG +int el3_debug = EL3_DEBUG; +#else +int el3_debug = 2; +#endif + +/* To minimize the size of the driver source I only define operating +   constants if they are used several times.  You'll need the manual +   anyway if you want to understand driver details. */ +/* Offsets from base I/O address. */ +#define EL3_DATA 0x00 +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e +#define	 EEPROM_READ 0x80 + +#define EL3_IO_EXTENT	16 + +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) + + +/* The top five bits written to EL3_CMD are a command, the lower +   11 bits are the parameter, if applicable. */ +enum c509cmd { +	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, +	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, +	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, +	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, +	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, +	SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, +	StatsDisable = 22<<11, StopCoax = 23<<11,}; + +enum c509status { +	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, +	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, +	IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, }; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { +	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 }; + +/* Register window 1 offsets, the window used in normal operation. */ +#define TX_FIFO		0x00 +#define RX_FIFO		0x00 +#define RX_STATUS 	0x08 +#define TX_STATUS 	0x0B +#define TX_FREE		0x0C		/* Remaining free bytes in Tx buffer. */ + +#define WN0_IRQ		0x08		/* Window 0: Set IRQ line in bits 12-15. */ +#define WN4_MEDIA	0x0A		/* Window 4: Various transcvr/media bits. */ +#define  MEDIA_TP	0x00C0		/* Enable link beat and jabber for 10baseT. */ + +/* + * Must be a power of two (we use a binary and in the + * circular queue) + */ +#define SKB_QUEUE_SIZE	64 + +struct el3_private { +	struct enet_statistics stats; +	struct device *next_dev; +	/* skb send-queue */ +	int head, size; +	struct sk_buff *queue[SKB_QUEUE_SIZE]; +}; +static int id_port = 0x110;		/* Start with 0x110 to avoid new sound cards.*/ +static struct device *el3_root_dev = NULL; + +static ushort id_read_eeprom(int index); +static ushort read_eeprom(int ioaddr, int index); +static int el3_open(struct device *dev); +static int el3_start_xmit(struct sk_buff *skb, struct device *dev); +static void el3_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void update_stats(int addr, struct device *dev); +static struct enet_statistics *el3_get_stats(struct device *dev); +static int el3_rx(struct device *dev); +static int el3_close(struct device *dev); +static void set_multicast_list(struct device *dev); + + + +int el3_probe(struct device *dev) +{ +	short lrs_state = 0xff, i; +	int ioaddr, irq, if_port; +	u16 phys_addr[3]; +	static int current_tag = 0; + +	/* First check all slots of the EISA bus.  The next slot address to +	   probe is kept in 'eisa_addr' to support multiple probe() calls. */ +	if (EISA_bus) { +		static int eisa_addr = 0x1000; +		while (eisa_addr < 0x9000) { +			ioaddr = eisa_addr; +			eisa_addr += 0x1000; + +			/* Check the standard EISA ID register for an encoded '3Com'. */ +			if (inw(ioaddr + 0xC80) != 0x6d50) +				continue; + +			/* Change the register set to the configuration window 0. */ +			outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD); + +			irq = inw(ioaddr + WN0_IRQ) >> 12; +			if_port = inw(ioaddr + 6)>>14; +			for (i = 0; i < 3; i++) +				phys_addr[i] = htons(read_eeprom(ioaddr, i)); + +			/* Restore the "Product ID" to the EEPROM read register. */ +			read_eeprom(ioaddr, 3); + +			/* Was the EISA code an add-on hack?  Nahhhhh... */ +			goto found; +		} +	} + +#ifdef CONFIG_MCA +	if (MCA_bus) { +		mca_adaptor_select_mode(1); +		for (i = 0; i < 8; i++) +			if ((mca_adaptor_id(i) | 1) == 0x627c) { +				ioaddr = mca_pos_base_addr(i); +				irq = inw(ioaddr + WN0_IRQ) >> 12; +				if_port = inw(ioaddr + 6)>>14; +				for (i = 0; i < 3; i++) +					phys_addr[i] = htons(read_eeprom(ioaddr, i)); + +				mca_adaptor_select_mode(0); +				goto found; +			} +		mca_adaptor_select_mode(0); + +	} +#endif + +	/* Reset the ISA PnP mechanism on 3c509b. */ +	outb(0x02, 0x279);           /* Select PnP config control register. */ +	outb(0x02, 0xA79);           /* Return to WaitForKey state. */ +	/* Select an open I/O location at 0x1*0 to do contention select. */ +	for ( ; id_port < 0x200; id_port += 0x10) { +		if (check_region(id_port, 1)) +			continue; +		outb(0x00, id_port); +		outb(0xff, id_port); +		if (inb(id_port) & 0x01) +			break; +	} +	if (id_port >= 0x200) {             /* GCC optimizes this test out. */ +		/* Rare -- do we really need a warning? */ +		printk(" WARNING: No I/O port available for 3c509 activation.\n"); +		return -ENODEV; +	} +	/* Next check for all ISA bus boards by sending the ID sequence to the +	   ID_PORT.  We find cards past the first by setting the 'current_tag' +	   on cards as they are found.  Cards with their tag set will not +	   respond to subsequent ID sequences. */ + +	outb(0x00, id_port); +	outb(0x00, id_port); +	for(i = 0; i < 255; i++) { +		outb(lrs_state, id_port); +		lrs_state <<= 1; +		lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state; +	} + +	/* For the first probe, clear all board's tag registers. */ +	if (current_tag == 0) +		outb(0xd0, id_port); +	else				/* Otherwise kill off already-found boards. */ +		outb(0xd8, id_port); + +	if (id_read_eeprom(7) != 0x6d50) { +		return -ENODEV; +	} + +	/* Read in EEPROM data, which does contention-select. +	   Only the lowest address board will stay "on-line". +	   3Com got the byte order backwards. */ +	for (i = 0; i < 3; i++) { +		phys_addr[i] = htons(id_read_eeprom(i)); +	} + +	{ +		unsigned int iobase = id_read_eeprom(8); +		if_port = iobase >> 14; +		ioaddr = 0x200 + ((iobase & 0x1f) << 4); +	} +	irq = id_read_eeprom(9) >> 12; + +	if (dev) {					/* Set passed-in IRQ or I/O Addr. */ +		if (dev->irq > 1  &&  dev->irq < 16) +			irq = dev->irq; + +		if (dev->base_addr) { +			if (dev->mem_end == 0x3c509 			/* Magic key */ +				&& dev->base_addr >= 0x200  &&  dev->base_addr <= 0x3e0) +				ioaddr = dev->base_addr & 0x3f0; +			else if (dev->base_addr != ioaddr) +				return -ENODEV; +		} +	} + +	/* Set the adaptor tag so that the next card can be found. */ +	outb(0xd0 + ++current_tag, id_port); + +	/* Activate the adaptor at the EEPROM location. */ +	outb((ioaddr >> 4) | 0xe0, id_port); + +	EL3WINDOW(0); +	if (inw(ioaddr) != 0x6d50) +		return -ENODEV; + +	/* Free the interrupt so that some other card can use it. */ +	outw(0x0f00, ioaddr + WN0_IRQ); + found: +	if (dev == NULL) { +		dev = init_etherdev(dev, sizeof(struct el3_private)); +	} +	memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr)); +	dev->base_addr = ioaddr; +	dev->irq = irq; +	dev->if_port = (dev->mem_start & 0x1f) ? dev->mem_start & 3 : if_port; + +	request_region(dev->base_addr, EL3_IO_EXTENT, "3c509"); + +	{ +		const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"}; +		printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ", +			   dev->name, dev->base_addr, current_tag, if_names[dev->if_port]); +	} + +	/* Read in the station address. */ +	for (i = 0; i < 6; i++) +		printk(" %2.2x", dev->dev_addr[i]); +	printk(", IRQ %d.\n", dev->irq); + +	/* Make up a EL3-specific-data structure. */ +	if (dev->priv == NULL) +		dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); +	if (dev->priv == NULL) +		return -ENOMEM; +	memset(dev->priv, 0, sizeof(struct el3_private)); + +	((struct el3_private *)dev->priv)->next_dev = el3_root_dev; +	el3_root_dev = dev; + +	if (el3_debug > 0) +		printk("%s", version); + +	/* The EL3-specific entries in the device structure. */ +	dev->open = &el3_open; +	dev->hard_start_xmit = &el3_start_xmit; +	dev->stop = &el3_close; +	dev->get_stats = &el3_get_stats; +	dev->set_multicast_list = &set_multicast_list; + +	/* Fill in the generic fields of the device structure. */ +	ether_setup(dev); +	return 0; +} + +/* Read a word from the EEPROM using the regular EEPROM access register. +   Assume that we are in register window zero. + */ +static ushort read_eeprom(int ioaddr, int index) +{ +	outw(EEPROM_READ + index, ioaddr + 10); +	/* Pause for at least 162 us. for the read to take place. */ +	udelay (500); +	return inw(ioaddr + 12); +} + +/* Read a word from the EEPROM when in the ISA ID probe state. */ +static ushort id_read_eeprom(int index) +{ +	int bit, word = 0; + +	/* Issue read command, and pause for at least 162 us. for it to complete. +	   Assume extra-fast 16Mhz bus. */ +	outb(EEPROM_READ + index, id_port); + +	/* Pause for at least 162 us. for the read to take place. */ +	udelay (500); +	 +	for (bit = 15; bit >= 0; bit--) +		word = (word << 1) + (inb(id_port) & 0x01); + +	if (el3_debug > 3) +		printk("  3c509 EEPROM word %d %#4.4x.\n", index, word); + +	return word; +} + + + +static int +el3_open(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	int i; + +	outw(TxReset, ioaddr + EL3_CMD); +	outw(RxReset, ioaddr + EL3_CMD); +	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); + +	if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) { +		return -EAGAIN; +	} + +	EL3WINDOW(0); +	if (el3_debug > 3) +		printk("%s: Opening, IRQ %d	 status@%x %4.4x.\n", dev->name, +			   dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS)); + +	/* Activate board: this is probably unnecessary. */ +	outw(0x0001, ioaddr + 4); + +	/* Set the IRQ line. */ +	outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ); + +	/* Set the station address in window 2 each time opened. */ +	EL3WINDOW(2); + +	for (i = 0; i < 6; i++) +		outb(dev->dev_addr[i], ioaddr + i); + +	if (dev->if_port == 3) +		/* Start the thinnet transceiver. We should really wait 50ms...*/ +		outw(StartCoax, ioaddr + EL3_CMD); +	else if (dev->if_port == 0) { +		/* 10baseT interface, enabled link beat and jabber check. */ +		EL3WINDOW(4); +		outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA); +	} + +	/* Switch to the stats window, and clear all stats by reading. */ +	outw(StatsDisable, ioaddr + EL3_CMD); +	EL3WINDOW(6); +	for (i = 0; i < 9; i++) +		inb(ioaddr + i); +	inw(ioaddr + 10); +	inw(ioaddr + 12); + +	/* Switch to register set 1 for normal use. */ +	EL3WINDOW(1); + +	/* Accept b-case and phys addr only. */ +	outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); +	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + +	dev->interrupt = 0; +	dev->tbusy = 0; +	dev->start = 1; + +	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ +	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ +	/* Allow status bits to be seen. */ +	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); +	/* Ack all pending events, and set active indicator mask. */ +	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, +		 ioaddr + EL3_CMD); +	outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, +		 ioaddr + EL3_CMD); + +	if (el3_debug > 3) +		printk("%s: Opened 3c509  IRQ %d  status %4.4x.\n", +			   dev->name, dev->irq, inw(ioaddr + EL3_STATUS)); + +	MOD_INC_USE_COUNT; +	return 0;					/* Always succeed */ +} + +static int +el3_start_xmit(struct sk_buff *skb, struct device *dev) +{ +	struct el3_private *lp = (struct el3_private *)dev->priv; +	int ioaddr = dev->base_addr; + +	/* Transmitter timeout, serious problems. */ +	if (dev->tbusy) { +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < TX_TIMEOUT) +			return 1; +		printk("%s: transmit timed out, Tx_status %2.2x status %4.4x " +			   "Tx FIFO room %d.\n", +			   dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS), +			   inw(ioaddr + TX_FREE)); +		lp->stats.tx_errors++; +		dev->trans_start = jiffies; +		/* Issue TX_RESET and TX_START commands. */ +		outw(TxReset, ioaddr + EL3_CMD); +		outw(TxEnable, ioaddr + EL3_CMD); +		dev->tbusy = 0; +	} + +	if (el3_debug > 4) { +		printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n", +			   dev->name, skb->len, inw(ioaddr + EL3_STATUS)); +	} +#if 0 +#ifndef final_version +	{	/* Error-checking code, delete someday. */ +		ushort status = inw(ioaddr + EL3_STATUS); +		if (status & 0x0001 		/* IRQ line active, missed one. */ +			&& inw(ioaddr + EL3_STATUS) & 1) { 			/* Make sure. */ +			printk("%s: Missed interrupt, status then %04x now %04x" +				   "  Tx %2.2x Rx %4.4x.\n", dev->name, status, +				   inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS), +				   inw(ioaddr + RX_STATUS)); +			/* Fake interrupt trigger by masking, acknowledge interrupts. */ +			outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); +			outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, +				 ioaddr + EL3_CMD); +			outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); +		} +	} +#endif +#endif +	/* Avoid timer-based retransmission conflicts. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) +		printk("%s: Transmitter access conflict.\n", dev->name); +	else { +		/* Put out the doubleword header... */ +		outw(skb->len, ioaddr + TX_FIFO); +		outw(0x00, ioaddr + TX_FIFO); +		/* ... and the packet rounded to a doubleword. */ +#ifdef  __powerpc__ +		outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +#else +		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +#endif + +		dev->trans_start = jiffies; +		if (inw(ioaddr + TX_FREE) > 1536) { +			dev->tbusy = 0; +		} else +			/* Interrupt us when the FIFO has room for max-sized packet. */ +			outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); +	} + +	dev_kfree_skb (skb, FREE_WRITE); + +	/* Clear the Tx status stack. */ +	{ +		short tx_status; +		int i = 4; + +		while (--i > 0	&&	(tx_status = inb(ioaddr + TX_STATUS)) > 0) { +			if (tx_status & 0x38) lp->stats.tx_aborted_errors++; +			if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); +			if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); +			outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ +		} +	} +	return 0; +} + +/* The EL3 interrupt handler. */ +static void +el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +	struct device *dev = (struct device *)dev_id; +	int ioaddr, status; +	int i = max_interrupt_work; + +	if (dev == NULL) { +		printk ("el3_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} + +	if (dev->interrupt) +		printk("%s: Re-entering the interrupt handler.\n", dev->name); +	dev->interrupt = 1; + +	ioaddr = dev->base_addr; +	status = inw(ioaddr + EL3_STATUS); + +	if (el3_debug > 4) +		printk("%s: interrupt, status %4.4x.\n", dev->name, status); + +	while ((status = inw(ioaddr + EL3_STATUS)) & +		   (IntLatch | RxComplete | StatsFull)) { + +		if (status & RxComplete) +			el3_rx(dev); + +		if (status & TxAvailable) { +			if (el3_debug > 5) +				printk("	TX room bit was handled.\n"); +			/* There's room in the FIFO for a full-sized packet. */ +			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); +			dev->tbusy = 0; +			mark_bh(NET_BH); +		} +		if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { +			/* Handle all uncommon interrupts. */ +			if (status & StatsFull)				/* Empty statistics. */ +				update_stats(ioaddr, dev); +			if (status & RxEarly) {				/* Rx early is unused. */ +				el3_rx(dev); +				outw(AckIntr | RxEarly, ioaddr + EL3_CMD); +			} +			if (status & TxComplete) {			/* Really Tx error. */ +				struct el3_private *lp = (struct el3_private *)dev->priv; +				short tx_status; +				int i = 4; + +				while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { +					if (tx_status & 0x38) lp->stats.tx_aborted_errors++; +					if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); +					if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); +					outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ +				} +			} +			if (status & AdapterFailure) { +				/* Adapter failure requires Rx reset and reinit. */ +				outw(RxReset, ioaddr + EL3_CMD); +				/* Set the Rx filter to the current state. */ +				outw(SetRxFilter | RxStation | RxBroadcast +					 | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0) +					 | (dev->flags & IFF_PROMISC ? RxProm : 0), +					 ioaddr + EL3_CMD); +				outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ +				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); +			} +		} + +		if (--i < 0) { +			printk("%s: Infinite loop in interrupt, status %4.4x.\n", +				   dev->name, status); +			/* Clear all interrupts. */ +			outw(AckIntr | 0xFF, ioaddr + EL3_CMD); +			break; +		} +		/* Acknowledge the IRQ. */ +		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */ +	} + +	if (el3_debug > 4) { +		printk("%s: exiting interrupt, status %4.4x.\n", dev->name, +			   inw(ioaddr + EL3_STATUS)); +	} + +	dev->interrupt = 0; +	return; +} + + +static struct enet_statistics * +el3_get_stats(struct device *dev) +{ +	struct el3_private *lp = (struct el3_private *)dev->priv; +	unsigned long flags; + +	save_flags(flags); +	cli(); +	update_stats(dev->base_addr, dev); +	restore_flags(flags); +	return &lp->stats; +} + +/*  Update statistics.  We change to register window 6, so this should be run +	single-threaded if the device is active. This is expected to be a rare +	operation, and it's simpler for the rest of the driver to assume that +	window 1 is always valid rather than use a special window-state variable. +	*/ +static void update_stats(int ioaddr, struct device *dev) +{ +	struct el3_private *lp = (struct el3_private *)dev->priv; + +	if (el3_debug > 5) +		printk("   Updating the statistics.\n"); +	/* Turn off statistics updates while reading. */ +	outw(StatsDisable, ioaddr + EL3_CMD); +	/* Switch to the stats window, and read everything. */ +	EL3WINDOW(6); +	lp->stats.tx_carrier_errors 	+= inb(ioaddr + 0); +	lp->stats.tx_heartbeat_errors	+= inb(ioaddr + 1); +	/* Multiple collisions. */	   	inb(ioaddr + 2); +	lp->stats.collisions			+= inb(ioaddr + 3); +	lp->stats.tx_window_errors		+= inb(ioaddr + 4); +	lp->stats.rx_fifo_errors		+= inb(ioaddr + 5); +	lp->stats.tx_packets			+= inb(ioaddr + 6); +	/* Rx packets	*/				inb(ioaddr + 7); +	/* Tx deferrals */				inb(ioaddr + 8); +	inw(ioaddr + 10);	/* Total Rx and Tx octets. */ +	inw(ioaddr + 12); + +	/* Back to window 1, and turn statistics back on. */ +	EL3WINDOW(1); +	outw(StatsEnable, ioaddr + EL3_CMD); +	return; +} + +static int +el3_rx(struct device *dev) +{ +	struct el3_private *lp = (struct el3_private *)dev->priv; +	int ioaddr = dev->base_addr; +	short rx_status; + +	if (el3_debug > 5) +		printk("   In rx_packet(), status %4.4x, rx_status %4.4x.\n", +			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); +	while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { +		if (rx_status & 0x4000) { /* Error, update stats. */ +			short error = rx_status & 0x3800; + +			outw(RxDiscard, ioaddr + EL3_CMD); +			lp->stats.rx_errors++; +			switch (error) { +			case 0x0000:		lp->stats.rx_over_errors++; break; +			case 0x0800:		lp->stats.rx_length_errors++; break; +			case 0x1000:		lp->stats.rx_frame_errors++; break; +			case 0x1800:		lp->stats.rx_length_errors++; break; +			case 0x2000:		lp->stats.rx_frame_errors++; break; +			case 0x2800:		lp->stats.rx_crc_errors++; break; +			} +		} else { +			short pkt_len = rx_status & 0x7ff; +			struct sk_buff *skb; + +			skb = dev_alloc_skb(pkt_len+5); +			if (el3_debug > 4) +				printk("Receiving packet size %d status %4.4x.\n", +					   pkt_len, rx_status); +			if (skb != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);     /* Align IP on 16 byte */ + +				/* 'skb->data' points to the start of sk_buff data area. */ +#ifdef  __powerpc__ +				insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len), +							   (pkt_len + 3) >> 2); +#else +				insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), +					 (pkt_len + 3) >> 2); +#endif + +				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ +				skb->protocol = eth_type_trans(skb,dev); +				netif_rx(skb); +				lp->stats.rx_packets++; +				continue; +			} +			outw(RxDiscard, ioaddr + EL3_CMD); +			lp->stats.rx_dropped++; +			if (el3_debug) +				printk("%s: Couldn't allocate a sk_buff of size %d.\n", +					   dev->name, pkt_len); +		} +		inw(ioaddr + EL3_STATUS); 				/* Delay. */ +		while (inw(ioaddr + EL3_STATUS) & 0x1000) +			printk("	Waiting for 3c509 to discard packet, status %x.\n", +				   inw(ioaddr + EL3_STATUS) ); +	} + +	return 0; +} + +/* + *     Set or clear the multicast filter for this adaptor. + */ +static void +set_multicast_list(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	if (el3_debug > 1) { +		static int old = 0; +		if (old != dev->mc_count) { +			old = dev->mc_count; +			printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count); +		} +	} +	if (dev->flags&IFF_PROMISC) { +		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, +			 ioaddr + EL3_CMD); +	} +	else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) { +		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD); +	} +	else +                outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); +} + +static int +el3_close(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if (el3_debug > 2) +		printk("%s: Shutting down ethercard.\n", dev->name); + +	dev->tbusy = 1; +	dev->start = 0; + +	/* Turn off statistics ASAP.  We update lp->stats below. */ +	outw(StatsDisable, ioaddr + EL3_CMD); + +	/* Disable the receiver and transmitter. */ +	outw(RxDisable, ioaddr + EL3_CMD); +	outw(TxDisable, ioaddr + EL3_CMD); + +	if (dev->if_port == 3) +		/* Turn off thinnet power.  Green! */ +		outw(StopCoax, ioaddr + EL3_CMD); +	else if (dev->if_port == 0) { +		/* Disable link beat and jabber, if_port may change ere next open(). */ +		EL3WINDOW(4); +		outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA); +	} + +	free_irq(dev->irq, dev); +	/* Switching back to window 0 disables the IRQ. */ +	EL3WINDOW(0); +	/* But we explicitly zero the IRQ line select anyway. */ +	outw(0x0f00, ioaddr + WN0_IRQ); + +	update_stats(ioaddr, dev); +	MOD_DEC_USE_COUNT; +	return 0; +} + +#ifdef MODULE +/* Parameters that may be passed into the module. */ +static int debug = -1; +static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +int +init_module(void) +{ +	int el3_cards = 0; + +	if (debug >= 0) +		el3_debug = debug; + +	el3_root_dev = NULL; +	while (el3_probe(0) == 0) { +		if (irq[el3_cards] > 1) +			el3_root_dev->irq = irq[el3_cards]; +		if (xcvr[el3_cards] >= 0) +			el3_root_dev->if_port = xcvr[el3_cards]; +		el3_cards++; +	} + +	return el3_cards ? 0 : -ENODEV; +} + +void +cleanup_module(void) +{ +	struct device *next_dev; + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (el3_root_dev) { +		next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev; +		unregister_netdev(el3_root_dev); +		release_region(el3_root_dev->base_addr, EL3_IO_EXTENT); +		kfree(el3_root_dev); +		el3_root_dev = next_dev; +	} +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c" + *  version-control: t + *  kept-new-versions: 5 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/3c515.c b/linux/src/drivers/net/3c515.c new file mode 100644 index 0000000..52f4703 --- /dev/null +++ b/linux/src/drivers/net/3c515.c @@ -0,0 +1,1501 @@ +/* 3c515.c: A 3Com ISA EtherLink XL "Corkscrew" ethernet driver for linux. */ +/* +	Written 1997-1998 by Donald Becker. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 +*/ + +static char *version = "3c515.c:v0.99 4/7/98 becker@cesdis.gsfc.nasa.gov\n"; +#define CORKSCREW 1 + +/* "Knobs" that adjust features and parameters. */ +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1512 effectively disables this feature. */ +static const int rx_copybreak = 200; +/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ +static const int mtu = 1500; +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Enable the automatic media selection code -- usually set. */ +#define AUTOMEDIA 1 + +/* Allow the use of fragment bus master transfers instead of only +   programmed-I/O for Vortex cards.  Full-bus-master transfers are always +   enabled by default on Boomerang cards.  If VORTEX_BUS_MASTER is defined, +   the feature may be turned on using 'options'. */ +#define VORTEX_BUS_MASTER + +/* A few values that may be tweaked. */ +/* Keep the ring sizes a power of two for efficiency. */ +#define TX_RING_SIZE	16 +#define RX_RING_SIZE	16 +#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/ + +#ifdef MODULE +#ifdef MODVERSIONS +#include <linux/modversions.h> +#endif +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/in.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/timer.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include <linux/delay.h> +#else +#define udelay(microsec)	do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#endif + +/* Kernel version compatibility functions. */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) + +#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance) +#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) + +#if (LINUX_VERSION_CODE < 0x20123) +//#define test_and_set_bit(val, addr) set_bit(val, addr) +#elif defined(MODULE) +MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>"); +MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(max_interrupt_work, "i"); +#endif + +/* "Knobs" for adjusting internal parameters. */ +/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ +#define DRIVER_DEBUG 1 +/* Some values here only for performance evaluation and path-coverage +   debugging. */ +static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0; + +/* Number of times to check to see if the Tx FIFO has space, used in some +   limited cases. */ +#define WAIT_TX_AVAIL 200 + +/* Operational parameter that usually are not changed. */ +#define TX_TIMEOUT  40		/* Time in jiffies before concluding Tx hung */ + +/* The size here is somewhat misleading: the Corkscrew also uses the ISA +   aliased registers at <base>+0x400. +   */ +#define CORKSCREW_TOTAL_SIZE 0x20 + +#ifdef HAVE_DEVLIST +struct netdev_entry tc515_drv = +{"3c515", tc515_probe, CORKSCREW_TOTAL_SIZE, NULL}; +#endif + +#ifdef DRIVER_DEBUG +int vortex_debug = DRIVER_DEBUG; +#else +int vortex_debug = 1; +#endif + +#define CORKSCREW_ID 10 + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL, +3Com's ISA bus adapter for Fast Ethernet.  Due to the unique I/O port layout, +it's not practical to integrate this driver with the other EtherLink drivers. + +II. Board-specific settings + +The Corkscrew has an EEPROM for configuration, but no special settings are +needed for Linux. + +III. Driver operation + +The 3c515 series use an interface that's very similar to the 3c900 "Boomerang" +PCI cards, with the bus master interface extensively modified to work with +the ISA bus. + +The card is capable of full-bus-master transfers with separate +lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, +DEC Tulip and Intel Speedo3. + +This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate +receive buffer.  This scheme allocates full-sized skbuffs as receive +buffers.  The value RX_COPYBREAK is used as the copying breakpoint: it is +chosen to trade-off the memory wasted by passing the full-sized skbuff to +the queue layer for all frames vs. the copying cost of copying a frame to a +correctly-sized skbuff. + + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +IV. Notes + +Thanks to Terry Murphy of 3Com for providing documentation and a development +board. + +The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com +project names.  I use these names to eliminate confusion -- 3Com product +numbers and names are very similar and often confused. + +The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes! +This driver only supports ethernet frames because of the recent MTU limit +of 1.5K, but the changes to support 4.5K are minimal. +*/ + +/* Operational definitions. +   These are not used by other compilation units and thus are not +   exported in a ".h" file. + +   First the windows.  There are eight register windows, with the command +   and status registers available in each. +   */ +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +/* The top five bits written to EL3_CMD are a command, the lower +   11 bits are the parameter, if applicable. +   Note that 11 parameters bits was fine for ethernet, but the new chips +   can handle FDDI length frames (~4500 octets) and now parameters count +   32-bit 'Dwords' rather than octets. */ + +enum vortex_cmd { +	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, +	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, +	UpStall = 6<<11, UpUnstall = (6<<11)+1, +	DownStall = (6<<11)+2, DownUnstall = (6<<11)+3, +	RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, +	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, +	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, +	SetTxThreshold = 18<<11, SetTxStart = 19<<11, +	StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11, +	StatsDisable = 22<<11, StopCoax = 23<<11,}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { +	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 }; + +/* Bits in the general status register. */ +enum vortex_status { +	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, +	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, +	IntReq = 0x0040, StatsFull = 0x0080, +	DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10, +	DMAInProgress = 1<<11,			/* DMA controller is still busy.*/ +	CmdInProgress = 1<<12,			/* EL3_CMD is still busy.*/ +}; + +/* Register window 1 offsets, the window used in normal operation. +   On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */ +enum Window1 { +	TX_FIFO = 0x10,  RX_FIFO = 0x10,  RxErrors = 0x14, +	RxStatus = 0x18,  Timer=0x1A, TxStatus = 0x1B, +	TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ +}; +enum Window0 { +  Wn0IRQ = 0x08, +#if defined(CORKSCREW) +	Wn0EepromCmd = 0x200A,		/* Corkscrew EEPROM command register. */ +	Wn0EepromData = 0x200C,		/* Corkscrew EEPROM results register. */ +#else +	Wn0EepromCmd = 10,		/* Window 0: EEPROM command register. */ +	Wn0EepromData = 12,		/* Window 0: EEPROM results register. */ +#endif +}; +enum Win0_EEPROM_bits { +	EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, +	EEPROM_EWENB = 0x30,		/* Enable erasing/writing for 10 msec. */ +	EEPROM_EWDIS = 0x00,		/* Disable EWENB before 10 msec timeout. */ +}; +/* EEPROM locations. */ +enum eeprom_offset { +	PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3, +	EtherLink3ID=7, }; + +enum Window3 {			/* Window 3: MAC/config bits. */ +	Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, +}; +union wn3_config { +	int i; +	struct w3_config_fields { +		unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2; +		int pad8:8; +		unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1; +		int pad24:7; +	} u; +}; + +enum Window4 { +	Wn4_NetDiag = 6, Wn4_Media = 10,		/* Window 4: Xcvr/media bits. */ +}; +enum Win4_Media_bits { +	Media_SQE = 0x0008,		/* Enable SQE error counting for AUI. */ +	Media_10TP = 0x00C0,	/* Enable link beat and jabber for 10baseT. */ +	Media_Lnk = 0x0080,		/* Enable just link beat for 100TX/100FX. */ +	Media_LnkBeat = 0x0800, +}; +enum Window7 {					/* Window 7: Bus Master control. */ +	Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, +}; +/* Boomerang-style bus master control registers.  Note ISA aliases! */ +enum MasterCtrl { +	PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen = 0x40c, +	TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418, +}; + +/* The Rx and Tx descriptor lists. +   Caution Alpha hackers: these types are 32 bits!  Note also the 8 byte +   alignment contraint on tx_ring[] and rx_ring[]. */ +struct boom_rx_desc { +	u32 next; +	s32 status; +	u32 addr; +	s32 length; +}; +/* Values for the Rx status entry. */ +enum rx_desc_status { +	RxDComplete=0x00008000, RxDError=0x4000, +	/* See boomerang_rx() for actual error bits */ +}; + +struct boom_tx_desc { +	u32 next; +	s32 status; +	u32 addr; +	s32 length; +}; + +struct vortex_private { +	char devname[8];			/* "ethN" string, also for kernel debug. */ +	const char *product_name; +	struct device *next_module; +	/* The Rx and Tx rings are here to keep them quad-word-aligned. */ +	struct boom_rx_desc rx_ring[RX_RING_SIZE]; +	struct boom_tx_desc tx_ring[TX_RING_SIZE]; +	/* The addresses of transmit- and receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	unsigned int cur_rx, cur_tx;		/* The next free ring entry */ +	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */ +	struct enet_statistics stats; +	struct sk_buff *tx_skb;		/* Packet being eaten by bus master ctrl.  */ +	struct timer_list timer;	/* Media selection timer. */ +	int capabilities;			/* Adapter capabilities word. */ +	int options;				/* User-settable misc. driver options. */ +	int last_rx_packets;		/* For media autoselection. */ +	unsigned int available_media:8,	/* From Wn3_Options */ +	  media_override:3, 			/* Passed-in media type. */ +	  default_media:3,			/* Read from the EEPROM. */ +	  full_duplex:1, autoselect:1, +	  bus_master:1,				/* Vortex can only do a fragment bus-m. */ +	  full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang  */ +      tx_full:1; +}; + +/* The action to take with a media selection timer tick. +   Note that we deviate from the 3Com order by checking 10base2 before AUI. + */ +enum xcvr_types { +	XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, +	XCVR_100baseFx, XCVR_MII=6, XCVR_Default=8, +}; + +static struct media_table { +  char *name; +  unsigned int media_bits:16,		/* Bits to set in Wn4_Media register. */ +	mask:8,				/* The transceiver-present bit in Wn3_Config.*/ +	next:8;				/* The media type to try next. */ +  short wait;			/* Time before we check media status. */ +} media_tbl[] = { +  {	"10baseT",   Media_10TP,0x08, XCVR_10base2, (14*HZ)/10}, +  { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10}, +  { "undefined", 0,			0x80, XCVR_10baseT, 10000}, +  { "10base2",   0,			0x10, XCVR_AUI,		(1*HZ)/10}, +  { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10}, +  { "100baseFX", Media_Lnk, 0x04, XCVR_MII,		(14*HZ)/10}, +  { "MII",		 0,			0x40, XCVR_10baseT, 3*HZ }, +  { "undefined", 0,			0x01, XCVR_10baseT, 10000}, +  { "Default",	 0,			0xFF, XCVR_10baseT, 10000}, +}; + +static int vortex_scan(struct device *dev); +static struct device *vortex_found_device(struct device *dev, int ioaddr, +										  int irq, int product_index, +										  int options); +static int vortex_probe1(struct device *dev); +static int vortex_open(struct device *dev); +static void vortex_timer(unsigned long arg); +static int vortex_start_xmit(struct sk_buff *skb, struct device *dev); +static int vortex_rx(struct device *dev); +static int boomerang_rx(struct device *dev); +static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs); +static int vortex_close(struct device *dev); +static void update_stats(int addr, struct device *dev); +static struct enet_statistics *vortex_get_stats(struct device *dev); +static void set_rx_mode(struct device *dev); + + +/* Unlike the other PCI cards the 59x cards don't need a large contiguous +   memory region, so making the driver a loadable module is feasible. + +   Unfortunately maximizing the shared code between the integrated and +   module version of the driver results in a complicated set of initialization +   procedures. +   init_module() -- modules /  tc59x_init()  -- built-in +		The wrappers for vortex_scan() +   vortex_scan()  		 The common routine that scans for PCI and EISA cards +   vortex_found_device() Allocate a device structure when we find a card. +					Different versions exist for modules and built-in. +   vortex_probe1()		Fill in the device structure -- this is separated +					so that the modules code can put it in dev->init. +*/ +/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ +/* Note: this is the only limit on the number of cards supported!! */ +static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,}; + +#ifdef MODULE +static int debug = -1; +/* A list of all installed Vortex devices, for removing the driver module. */ +static struct device *root_vortex_dev = NULL; + +int +init_module(void) +{ +	int cards_found; + +	if (debug >= 0) +		vortex_debug = debug; +	if (vortex_debug) +		printk("%s", version); + +	root_vortex_dev = NULL; +	cards_found = vortex_scan(0); +	return cards_found ? 0 : -ENODEV; +} + +#else +int tc515_probe(struct device *dev) +{ +	int cards_found = 0; + +	cards_found = vortex_scan(dev); + +	if (vortex_debug > 0  &&  cards_found) +		printk("%s", version); + +	return cards_found ? 0 : -ENODEV; +} +#endif  /* not MODULE */ + +static int vortex_scan(struct device *dev) +{ +	int cards_found = 0; +	static int ioaddr = 0x100; + +	/* Check all locations on the ISA bus -- evil! */ +	for (; ioaddr < 0x400; ioaddr += 0x20) { +	  int irq; +	  if (check_region(ioaddr, CORKSCREW_TOTAL_SIZE)) +		continue; +	  /* Check the resource configuration for a matching ioaddr. */ +	  if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) +		continue; +	  /* Verify by reading the device ID from the EEPROM. */ +	  { +		int timer; +		outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd); +		/* Pause for at least 162 us. for the read to take place. */ +		for (timer = 4; timer >= 0; timer--) { +		  udelay(162); +		  if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) +			break; +		} +		if (inw(ioaddr + Wn0EepromData) != 0x6d50) +		  continue; +	  } +	  printk("3c515 Resource configuraiton register %#4.4x, DCR %4.4x.\n", +			 inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); +	  irq = inw(ioaddr + 0x2002) & 15; +	  vortex_found_device(dev, ioaddr, irq, CORKSCREW_ID, dev && dev->mem_start +						  ? dev->mem_start : options[cards_found]); +	  dev = 0; +	  cards_found++; +	} + +	if (vortex_debug) +	  printk("%d 3c515 cards found.\n", cards_found); +	return cards_found; +} + +static struct device *vortex_found_device(struct device *dev, int ioaddr, +										  int irq, int product_index, +										  int options) +{ +	struct vortex_private *vp; + +#ifdef MODULE +	/* Allocate and fill new device structure. */ +	int dev_size = sizeof(struct device) + +		sizeof(struct vortex_private) + 15;		/* Pad for alignment */ +	 +	dev = (struct device *) kmalloc(dev_size, GFP_KERNEL); +	memset(dev, 0, dev_size); +	/* Align the Rx and Tx ring entries.  */ +	dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15); +	vp = (struct vortex_private *)dev->priv; +	dev->name = vp->devname; /* An empty string. */ +	dev->base_addr = ioaddr; +	dev->irq = irq; +	dev->dma = (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0); +	dev->init = vortex_probe1; +	vp->product_name = "3c515"; +	vp->options = options; +	if (options >= 0) { +		vp->media_override = ((options & 7) == 2)  ?  0  :  options & 7; +		vp->full_duplex = (options & 8) ? 1 : 0; +		vp->bus_master = (options & 16) ? 1 : 0; +	} else { +		vp->media_override = 7; +		vp->full_duplex = 0; +		vp->bus_master = 0; +	} +	ether_setup(dev); +	vp->next_module = root_vortex_dev; +	root_vortex_dev = dev; +	if (register_netdev(dev) != 0) +		return 0; +#else  /* not a MODULE */ +	if (dev) { +		/* Caution: quad-word alignment required for rings! */ +		dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL); +		memset(dev->priv, 0, sizeof (struct vortex_private)); +	} +	dev = init_etherdev(dev, sizeof(struct vortex_private)); +	dev->base_addr = ioaddr; +	dev->irq = irq; +	dev->dma = (product_index == CORKSCREW_ID ? inw(ioaddr + 0x2000) & 7 : 0); +	vp  = (struct vortex_private *)dev->priv; +	vp->product_name = "3c515"; +	vp->options = options; +	if (options >= 0) { +		vp->media_override = ((options & 7) == 2)  ?  0  :  options & 7; +		vp->full_duplex = (options & 8) ? 1 : 0; +		vp->bus_master = (options & 16) ? 1 : 0; +	} else { +		vp->media_override = 7; +		vp->full_duplex = 0; +		vp->bus_master = 0; +	} + +	vortex_probe1(dev); +#endif /* MODULE */ +	return dev; +} + +static int vortex_probe1(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	unsigned int eeprom[0x40], checksum = 0;		/* EEPROM contents */ +	int i; + +	printk("%s: 3Com %s at %#3x,", dev->name, +		   vp->product_name, ioaddr); + +	/* Read the station address from the EEPROM. */ +	EL3WINDOW(0); +	for (i = 0; i < 0x18; i++) { +		short *phys_addr = (short *)dev->dev_addr; +		int timer; +		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); +		/* Pause for at least 162 us. for the read to take place. */ +		for (timer = 4; timer >= 0; timer--) { +			udelay(162); +			if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) +				break; +		} +		eeprom[i] = inw(ioaddr + Wn0EepromData); +		checksum ^= eeprom[i]; +		if (i < 3) +			phys_addr[i] = htons(eeprom[i]); +	} +	checksum = (checksum ^ (checksum >> 8)) & 0xff; +	if (checksum != 0x00) +		printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); +	for (i = 0; i < 6; i++) +		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); +	if (eeprom[16] == 0x11c7) { 		/* Corkscrew */ +	  if (request_dma(dev->dma, "3c515")) { +		printk(", DMA %d allocation failed", dev->dma); +		dev->dma = 0; +	  } else  +		printk(", DMA %d", dev->dma); +	} +	printk(", IRQ %d\n", dev->irq); +	/* Tell them about an invalid IRQ. */ +	if (vortex_debug && (dev->irq <= 0 || dev->irq > 15)) +		printk(" *** Warning: this IRQ is unlikely to work! ***\n"); + +	{ +		char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; +		union wn3_config config; +		EL3WINDOW(3); +		vp->available_media = inw(ioaddr + Wn3_Options); +		config.i = inl(ioaddr + Wn3_Config); +		if (vortex_debug > 1) +			printk("  Internal config register is %4.4x, transceivers %#x.\n", +				   config.i, inw(ioaddr + Wn3_Options)); +		printk("  %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", +			   8 << config.u.ram_size, +			   config.u.ram_width ? "word" : "byte", +			   ram_split[config.u.ram_split], +			   config.u.autoselect ? "autoselect/" : "", +			   media_tbl[config.u.xcvr].name); +		dev->if_port = config.u.xcvr; +		vp->default_media = config.u.xcvr; +		vp->autoselect = config.u.autoselect; +	} +	if (vp->media_override != 7) { +		printk("  Media override to transceiver type %d (%s).\n", +			   vp->media_override, media_tbl[vp->media_override].name); +		dev->if_port = vp->media_override; +	} + +	vp->capabilities = eeprom[16]; +	vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0; +	/* Rx is broken at 10mbps, so we always disable it. */ +	/* vp->full_bus_master_rx = 0;*/ +	vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0; + +	/* We do a request_region() to register /proc/ioports info. */ +	request_region(ioaddr, CORKSCREW_TOTAL_SIZE, vp->product_name); + +	/* The 3c59x-specific entries in the device structure. */ +	dev->open = &vortex_open; +	dev->hard_start_xmit = &vortex_start_xmit; +	dev->stop = &vortex_close; +	dev->get_stats = &vortex_get_stats; +	dev->set_multicast_list = &set_rx_mode; + +	return 0; +} + + +static int +vortex_open(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	union wn3_config config; +	int i; + +	/* Before initializing select the active media port. */ +	EL3WINDOW(3); +	if (vp->full_duplex) +		outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ +	config.i = inl(ioaddr + Wn3_Config); + +	if (vp->media_override != 7) { +		if (vortex_debug > 1) +			printk("%s: Media override to transceiver %d (%s).\n", +				   dev->name, vp->media_override, +				   media_tbl[vp->media_override].name); +		dev->if_port = vp->media_override; +	} else if (vp->autoselect) { +		/* Find first available media type, starting with 100baseTx. */ +		dev->if_port = 4; +		while (! (vp->available_media & media_tbl[dev->if_port].mask)) +			dev->if_port = media_tbl[dev->if_port].next; + +		if (vortex_debug > 1) +			printk("%s: Initial media type %s.\n", +				   dev->name, media_tbl[dev->if_port].name); + +		init_timer(&vp->timer); +		vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait); +		vp->timer.data = (unsigned long)dev; +		vp->timer.function = &vortex_timer;    /* timer handler */ +		add_timer(&vp->timer); +	} else +		dev->if_port = vp->default_media; + +	config.u.xcvr = dev->if_port; +	outl(config.i, ioaddr + Wn3_Config); + +	if (vortex_debug > 1) { +		printk("%s: vortex_open() InternalConfig %8.8x.\n", +			dev->name, config.i); +	} + +	outw(TxReset, ioaddr + EL3_CMD); +	for (i = 20; i >= 0 ; i--) +		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +			break; + +	outw(RxReset, ioaddr + EL3_CMD); +	/* Wait a few ticks for the RxReset command to complete. */ +	for (i = 20; i >= 0 ; i--) +		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +			break; + +	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); + +	/* Use the now-standard shared IRQ implementation. */ +	if (vp->capabilities == 0x11c7) { +	  /* Corkscrew: Cannot share ISA resources. */ +	  if (dev->irq == 0 +		  || dev->dma == 0 +		  || request_irq(dev->irq, &vortex_interrupt, 0, +						 vp->product_name, dev)) +		return -EAGAIN; +	  enable_dma(dev->dma); +	  set_dma_mode(dev->dma, DMA_MODE_CASCADE); +	} else if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, +						   vp->product_name, dev)) { +	  return -EAGAIN; +	} + +	if (vortex_debug > 1) { +		EL3WINDOW(4); +		printk("%s: vortex_open() irq %d media status %4.4x.\n", +			   dev->name, dev->irq, inw(ioaddr + Wn4_Media)); +	} + +	/* Set the station address and mask in window 2 each time opened. */ +	EL3WINDOW(2); +	for (i = 0; i < 6; i++) +		outb(dev->dev_addr[i], ioaddr + i); +	for (; i < 12; i+=2) +		outw(0, ioaddr + i); + +	if (dev->if_port == 3) +		/* Start the thinnet transceiver. We should really wait 50ms...*/ +		outw(StartCoax, ioaddr + EL3_CMD); +	EL3WINDOW(4); +	outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) | +		 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); + +	/* Switch to the stats window, and clear all stats by reading. */ +	outw(StatsDisable, ioaddr + EL3_CMD); +	EL3WINDOW(6); +	for (i = 0; i < 10; i++)	 +		inb(ioaddr + i); +	inw(ioaddr + 10); +	inw(ioaddr + 12); +	/* New: On the Vortex we must also clear the BadSSD counter. */ +	EL3WINDOW(4); +	inb(ioaddr + 12); +	/* ..and on the Boomerang we enable the extra statistics bits. */ +	outw(0x0040, ioaddr + Wn4_NetDiag); + +	/* Switch to register set 7 for normal use. */ +	EL3WINDOW(7); + +	if (vp->full_bus_master_rx) { /* Boomerang bus master. */ +		vp->cur_rx = vp->dirty_rx = 0; +		if (vortex_debug > 2) +			printk("%s:  Filling in the Rx ring.\n", dev->name); +		for (i = 0; i < RX_RING_SIZE; i++) { +			struct sk_buff *skb; +			if (i < (RX_RING_SIZE - 1)) +			  vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]); +			else +			  vp->rx_ring[i].next = 0; +			vp->rx_ring[i].status = 0;	/* Clear complete bit. */ +			vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000; +			skb = dev_alloc_skb(PKT_BUF_SZ); +			vp->rx_skbuff[i] = skb; +			if (skb == NULL) +				break;			/* Bad news!  */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			vp->rx_ring[i].addr = virt_to_bus(skb->tail); +		} +		vp->rx_ring[i-1].next = virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ +		outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); +	} +	if (vp->full_bus_master_tx) { 		/* Boomerang bus master Tx. */ +		vp->cur_tx = vp->dirty_tx = 0; +		outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */ +		/* Clear the Tx ring. */ +		for (i = 0; i < TX_RING_SIZE; i++) +			vp->tx_skbuff[i] = 0; +		outl(0, ioaddr + DownListPtr); +	} +	/* Set reciever mode: presumably accept b-case and phys addr only. */ +	set_rx_mode(dev); +	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; + +	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ +	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ +	/* Allow status bits to be seen. */ +	outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull |  +		 (vp->full_bus_master_tx ? DownComplete : TxAvailable) | +		 (vp->full_bus_master_rx ? UpComplete : RxComplete) |  +		 (vp->bus_master ? DMADone : 0), +		 ioaddr + EL3_CMD); +	/* Ack all pending events, and set active indicator mask. */ +	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, +		 ioaddr + EL3_CMD); +	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull +		 | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, +			ioaddr + EL3_CMD); + +	MOD_INC_USE_COUNT; + +	return 0; +} + +static void vortex_timer(unsigned long data) +{ +#ifdef AUTOMEDIA +	struct device *dev = (struct device *)data; +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	int ioaddr = dev->base_addr; +	unsigned long flags; +	int ok = 0; + +	if (vortex_debug > 1) +		printk("%s: Media selection timer tick happened, %s.\n", +			   dev->name, media_tbl[dev->if_port].name); + +	save_flags(flags);	cli(); { +	  int old_window = inw(ioaddr + EL3_CMD) >> 13; +	  int media_status; +	  EL3WINDOW(4); +	  media_status = inw(ioaddr + Wn4_Media); +	  switch (dev->if_port) { +	  case 0:  case 4:  case 5:		/* 10baseT, 100baseTX, 100baseFX  */ +		if (media_status & Media_LnkBeat) { +		  ok = 1; +		  if (vortex_debug > 1) +			printk("%s: Media %s has link beat, %x.\n", +				   dev->name, media_tbl[dev->if_port].name, media_status); +		} else if (vortex_debug > 1) +		  printk("%s: Media %s is has no link beat, %x.\n", +				   dev->name, media_tbl[dev->if_port].name, media_status); +  +		break; +	  default:					/* Other media types handled by Tx timeouts. */ +		if (vortex_debug > 1) +		  printk("%s: Media %s is has no indication, %x.\n", +				 dev->name, media_tbl[dev->if_port].name, media_status); +		ok = 1; +	  } +	  if ( ! ok) { +		union wn3_config config; + +		do { +			dev->if_port = media_tbl[dev->if_port].next; +		} while ( ! (vp->available_media & media_tbl[dev->if_port].mask)); +		if (dev->if_port == 8) { /* Go back to default. */ +		  dev->if_port = vp->default_media; +		  if (vortex_debug > 1) +			printk("%s: Media selection failing, using default %s port.\n", +				   dev->name, media_tbl[dev->if_port].name); +		} else { +		  if (vortex_debug > 1) +			printk("%s: Media selection failed, now trying %s port.\n", +				   dev->name, media_tbl[dev->if_port].name); +		  vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait); +		  add_timer(&vp->timer); +		} +		outw((media_status & ~(Media_10TP|Media_SQE)) | +			 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); + +		EL3WINDOW(3); +		config.i = inl(ioaddr + Wn3_Config); +		config.u.xcvr = dev->if_port; +		outl(config.i, ioaddr + Wn3_Config); + +		outw(dev->if_port == 3 ? StartCoax : StopCoax, ioaddr + EL3_CMD); +	  } +	  EL3WINDOW(old_window); +	}   restore_flags(flags); +	if (vortex_debug > 1) +	  printk("%s: Media selection timer finished, %s.\n", +			 dev->name, media_tbl[dev->if_port].name); + +#endif /* AUTOMEDIA*/ +	return; +} + +static int +vortex_start_xmit(struct sk_buff *skb, struct device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	int ioaddr = dev->base_addr; + +	if (dev->tbusy) { +		int tickssofar = jiffies - dev->trans_start; +		int i; + +		/* Min. wait before assuming a Tx failed == 400ms. */ + +		if (tickssofar < 400*HZ/1000)		/* We probably aren't empty. */ +			return 1; +		printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n", +			   dev->name, inb(ioaddr + TxStatus), +			   inw(ioaddr + EL3_STATUS)); +		/* Slight code bloat to be user friendly. */ +		if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) +			printk("%s: Transmitter encountered 16 collisions -- network" +				   " network cable problem?\n", dev->name); +#ifndef final_version +		printk("  Flags; bus-master %d, full %d; dirty %d current %d.\n", +			   vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx); +		printk("  Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), +			   &vp->tx_ring[0]); +		for (i = 0; i < TX_RING_SIZE; i++) { +			printk("  %d: %p  length %8.8x status %8.8x\n", i, +				   &vp->tx_ring[i], +				   vp->tx_ring[i].length, +				   vp->tx_ring[i].status); +		} +#endif +		/* Issue TX_RESET and TX_START commands. */ +		outw(TxReset, ioaddr + EL3_CMD); +		for (i = 20; i >= 0 ; i--) +			if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +				break; +		outw(TxEnable, ioaddr + EL3_CMD); +		dev->trans_start = jiffies; +		/* dev->tbusy = 0;*/ +		vp->stats.tx_errors++; +		vp->stats.tx_dropped++; +		return 0;			/* Yes, silently *drop* the packet! */ +	} + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. +	   If this ever occurs the queue layer is doing something evil! */ +	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { +		printk("%s: Transmitter access conflict.\n", dev->name); +		return 1; +	} + +	if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */ +		/* Calculate the next Tx descriptor entry. */ +		int entry = vp->cur_tx % TX_RING_SIZE; +		struct boom_tx_desc *prev_entry; +		unsigned long flags, i; + +		if (vp->tx_full) /* No room to transmit with */ +		  return 1; +		if (vp->cur_tx != 0) +		  prev_entry = &vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE]; +		else +		  prev_entry = NULL; +		if (vortex_debug > 3) +			printk("%s: Trying to send a packet, Tx index %d.\n", +				   dev->name, vp->cur_tx); +		/* vp->tx_full = 1; */ +		vp->tx_skbuff[entry] = skb; +		vp->tx_ring[entry].next = 0; +		vp->tx_ring[entry].addr = virt_to_bus(skb->data); +		vp->tx_ring[entry].length = skb->len | 0x80000000; +		vp->tx_ring[entry].status = skb->len | 0x80000000; + +		save_flags(flags); +		cli(); +		outw(DownStall, ioaddr + EL3_CMD); +		/* Wait for the stall to complete. */ +		for (i = 20; i >= 0 ; i--) +			if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) +				break; +		if (prev_entry) +		  prev_entry->next = virt_to_bus(&vp->tx_ring[entry]); +		if (inl(ioaddr + DownListPtr) == 0) { +			outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr); +			queued_packet++; +		} +		outw(DownUnstall, ioaddr + EL3_CMD); +		restore_flags(flags); + +		vp->cur_tx++; +		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) +			vp->tx_full = 1; +		else {					/* Clear previous interrupt enable. */ +		  if (prev_entry) +			prev_entry->status &= ~0x80000000; +		  dev->tbusy = 0; +		} +		dev->trans_start = jiffies; +		return 0; +	} +	/* Put out the doubleword header... */ +	outl(skb->len, ioaddr + TX_FIFO); +#ifdef VORTEX_BUS_MASTER +	if (vp->bus_master) { +		/* Set the bus-master controller to transfer the packet. */ +		outl((int)(skb->data), ioaddr + Wn7_MasterAddr); +		outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); +		vp->tx_skb = skb; +		outw(StartDMADown, ioaddr + EL3_CMD); +		/* dev->tbusy will be cleared at the DMADone interrupt. */ +	} else { +		/* ... and the packet rounded to a doubleword. */ +		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +		dev_kfree_skb (skb, FREE_WRITE); +		if (inw(ioaddr + TxFree) > 1536) { +			dev->tbusy = 0; +		} else +			/* Interrupt us when the FIFO has room for max-sized packet. */ +			outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); +	} +#else +	/* ... and the packet rounded to a doubleword. */ +	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +	dev_kfree_skb (skb, FREE_WRITE); +	if (inw(ioaddr + TxFree) > 1536) { +		dev->tbusy = 0; +	} else +		/* Interrupt us when the FIFO has room for max-sized packet. */ +		outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); +#endif  /* bus master */ + +	dev->trans_start = jiffies; + +	/* Clear the Tx status stack. */ +	{ +		short tx_status; +		int i = 4; + +		while (--i > 0	&&	(tx_status = inb(ioaddr + TxStatus)) > 0) { +			if (tx_status & 0x3C) {		/* A Tx-disabling error occurred.  */ +				if (vortex_debug > 2) +				  printk("%s: Tx error, status %2.2x.\n", +						 dev->name, tx_status); +				if (tx_status & 0x04) vp->stats.tx_fifo_errors++; +				if (tx_status & 0x38) vp->stats.tx_aborted_errors++; +				if (tx_status & 0x30) { +					int j; +					outw(TxReset, ioaddr + EL3_CMD); +					for (j = 20; j >= 0 ; j--) +						if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +							break; +				} +				outw(TxEnable, ioaddr + EL3_CMD); +			} +			outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ +		} +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void vortex_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs) +{ +	/* Use the now-standard shared IRQ implementation. */ +	struct device *dev = dev_id; +	struct vortex_private *lp; +	int ioaddr, status; +	int latency; +	int i = max_interrupt_work; + +	if (test_and_set_bit(0, (void*)&dev->interrupt)) { +		printk("%s: Re-entering the interrupt handler.\n", dev->name); +		return; +	} + +	ioaddr = dev->base_addr; +	latency = inb(ioaddr + Timer); +	lp = (struct vortex_private *)dev->priv; + +	status = inw(ioaddr + EL3_STATUS); + +	if (vortex_debug > 4) +		printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name, +			   status, latency); +	if ((status & 0xE000) != 0xE000) { +		static int donedidthis=0; +		/* Some interrupt controllers store a bogus interrupt from boot-time. +		   Ignore a single early interrupt, but don't hang the machine for +		   other interrupt problems. */ +		if (donedidthis++ > 100) { +			printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", +				   dev->name, status, dev->start); +			FREE_IRQ(dev->irq, dev); +		} +	} + +	do { +		if (vortex_debug > 5) +				printk("%s: In interrupt loop, status %4.4x.\n", +					   dev->name, status); +		if (status & RxComplete) +			vortex_rx(dev); + +		if (status & TxAvailable) { +			if (vortex_debug > 5) +				printk("	TX room bit was handled.\n"); +			/* There's room in the FIFO for a full-sized packet. */ +			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); +			dev->tbusy = 0; +			mark_bh(NET_BH); +		} +		if (status & DownComplete) { +			unsigned int dirty_tx = lp->dirty_tx; + +			while (lp->cur_tx - dirty_tx > 0) { +				int entry = dirty_tx % TX_RING_SIZE; +				if (inl(ioaddr + DownListPtr) == +					virt_to_bus(&lp->tx_ring[entry])) +					break;			/* It still hasn't been processed. */ +				if (lp->tx_skbuff[entry]) { +					dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); +					lp->tx_skbuff[entry] = 0; +				} +				dirty_tx++; +			} +			lp->dirty_tx = dirty_tx; +			outw(AckIntr | DownComplete, ioaddr + EL3_CMD); +			if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { +				lp->tx_full= 0; +				dev->tbusy = 0; +				mark_bh(NET_BH); +			} +		} +#ifdef VORTEX_BUS_MASTER +		if (status & DMADone) { +			outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ +			dev->tbusy = 0; +			dev_kfree_skb (lp->tx_skb, FREE_WRITE); /* Release the transfered buffer */ +			mark_bh(NET_BH); +		} +#endif +		if (status & UpComplete) { +			boomerang_rx(dev); +			outw(AckIntr | UpComplete, ioaddr + EL3_CMD); +		} +		if (status & (AdapterFailure | RxEarly | StatsFull)) { +			/* Handle all uncommon interrupts at once. */ +			if (status & RxEarly) {				/* Rx early is unused. */ +				vortex_rx(dev); +				outw(AckIntr | RxEarly, ioaddr + EL3_CMD); +			} +			if (status & StatsFull) { 	/* Empty statistics. */ +				static int DoneDidThat = 0; +				if (vortex_debug > 4) +					printk("%s: Updating stats.\n", dev->name); +				update_stats(ioaddr, dev); +				/* DEBUG HACK: Disable statistics as an interrupt source. */ +				/* This occurs when we have the wrong media type! */ +				if (DoneDidThat == 0  && +					inw(ioaddr + EL3_STATUS) & StatsFull) { +					int win, reg; +					printk("%s: Updating stats failed, disabling stats as an" +						   " interrupt source.\n", dev->name); +					for (win = 0; win < 8; win++) { +						EL3WINDOW(win); +						printk("\n Vortex window %d:", win); +						for (reg = 0; reg < 16; reg++) +							printk(" %2.2x", inb(ioaddr+reg)); +					} +					EL3WINDOW(7); +					outw(SetIntrEnb | TxAvailable | RxComplete | AdapterFailure +						 | UpComplete | DownComplete | TxComplete, +						 ioaddr + EL3_CMD); +					DoneDidThat++; +				} +			} +			if (status & AdapterFailure) { +				/* Adapter failure requires Rx reset and reinit. */ +				outw(RxReset, ioaddr + EL3_CMD); +				/* Set the Rx filter to the current state. */ +				set_rx_mode(dev); +				outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ +				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); +			} +		} + +		if (--i < 0) { +			printk("%s: Too much work in interrupt, status %4.4x.  " +				   "Disabling functions (%4.4x).\n", +				   dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); +			/* Disable all pending interrupts. */ +			outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); +			outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); +			break; +		} +		/* Acknowledge the IRQ. */ +		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); + +	} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); + +	if (vortex_debug > 4) +		printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status); + +	dev->interrupt = 0; +	return; +} + +static int +vortex_rx(struct device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	int ioaddr = dev->base_addr; +	int i; +	short rx_status; + +	if (vortex_debug > 5) +		printk("   In rx_packet(), status %4.4x, rx_status %4.4x.\n", +			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); +	while ((rx_status = inw(ioaddr + RxStatus)) > 0) { +		if (rx_status & 0x4000) { /* Error, update stats. */ +			unsigned char rx_error = inb(ioaddr + RxErrors); +			if (vortex_debug > 2) +				printk(" Rx error: status %2.2x.\n", rx_error); +			vp->stats.rx_errors++; +			if (rx_error & 0x01)  vp->stats.rx_over_errors++; +			if (rx_error & 0x02)  vp->stats.rx_length_errors++; +			if (rx_error & 0x04)  vp->stats.rx_frame_errors++; +			if (rx_error & 0x08)  vp->stats.rx_crc_errors++; +			if (rx_error & 0x10)  vp->stats.rx_length_errors++; +		} else { +			/* The packet length: up to 4.5K!. */ +			short pkt_len = rx_status & 0x1fff; +			struct sk_buff *skb; + +			skb = DEV_ALLOC_SKB(pkt_len + 5); +			if (vortex_debug > 4) +				printk("Receiving packet size %d status %4.4x.\n", +					   pkt_len, rx_status); +			if (skb != NULL) { +				skb->dev = dev; +#if LINUX_VERSION_CODE >= 0x10300 +				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +				/* 'skb_put()' points to the start of sk_buff data area. */ +				insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len), +					 (pkt_len + 3) >> 2); +				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ +				skb->protocol = eth_type_trans(skb, dev); +#else +				skb->len = pkt_len; +				/* 'skb->data' points to the start of sk_buff data area. */ +				insl(ioaddr + RX_FIFO, skb->data, (pkt_len + 3) >> 2); +				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ +#endif  /* KERNEL_1_3_0 */ +				netif_rx(skb); +				dev->last_rx = jiffies; +				vp->stats.rx_packets++; +				/* Wait a limited time to go to next packet. */ +				for (i = 200; i >= 0; i--) +					if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +						break; +				continue; +			} else if (vortex_debug) +				printk("%s: Couldn't allocate a sk_buff of size %d.\n", +					   dev->name, pkt_len); +		} +		outw(RxDiscard, ioaddr + EL3_CMD); +		vp->stats.rx_dropped++; +		/* Wait a limited time to skip this packet. */ +		for (i = 200; i >= 0; i--) +			if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +				break; +	} + +	return 0; +} + +static int +boomerang_rx(struct device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	int entry = vp->cur_rx % RX_RING_SIZE; +	int ioaddr = dev->base_addr; +	int rx_status; + +	if (vortex_debug > 5) +		printk("   In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", +			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); +	while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { +		if (rx_status & RxDError) { /* Error, update stats. */ +			unsigned char rx_error = rx_status >> 16; +			if (vortex_debug > 2) +				printk(" Rx error: status %2.2x.\n", rx_error); +			vp->stats.rx_errors++; +			if (rx_error & 0x01)  vp->stats.rx_over_errors++; +			if (rx_error & 0x02)  vp->stats.rx_length_errors++; +			if (rx_error & 0x04)  vp->stats.rx_frame_errors++; +			if (rx_error & 0x08)  vp->stats.rx_crc_errors++; +			if (rx_error & 0x10)  vp->stats.rx_length_errors++; +		} else { +			/* The packet length: up to 4.5K!. */ +			short pkt_len = rx_status & 0x1fff; +			struct sk_buff *skb; + +			if (vortex_debug > 4) +				printk("Receiving packet size %d status %4.4x.\n", +					   pkt_len, rx_status); + +			/* Check if the packet is long enough to just accept without +			   copying to a properly sized skbuff. */ +			if (pkt_len < rx_copybreak +				&& (skb = DEV_ALLOC_SKB(pkt_len + 2)) != 0) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +				/* 'skb_put()' points to the start of sk_buff data area. */ +				memcpy(skb_put(skb, pkt_len), +					   bus_to_virt(vp->rx_ring[entry].addr), +					   pkt_len); +				rx_copy++; +			} else{ +				void *temp; +				/* Pass up the skbuff already on the Rx ring. */ +				skb = vp->rx_skbuff[entry]; +				vp->rx_skbuff[entry] = NULL; +				temp = skb_put(skb, pkt_len); +				/* Remove this checking code for final release. */ +				if (bus_to_virt(vp->rx_ring[entry].addr) != temp) +					printk("%s: Warning -- the skbuff addresses do not match" +						   " in boomerang_rx: %p vs. %p / %p.\n", dev->name, +						   bus_to_virt(vp->rx_ring[entry].addr), +						   skb->head, temp); +				rx_nocopy++; +			} +#if LINUX_VERSION_CODE > 0x10300 +			skb->protocol = eth_type_trans(skb, dev); +#else +			skb->len = pkt_len; +#endif +			netif_rx(skb); +			dev->last_rx = jiffies; +			vp->stats.rx_packets++; +		} +		entry = (++vp->cur_rx) % RX_RING_SIZE; +	} +	/* Refill the Rx ring buffers. */ +	for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) { +		struct sk_buff *skb; +		entry = vp->dirty_rx % RX_RING_SIZE; +		if (vp->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(PKT_BUF_SZ); +			if (skb == NULL) +				break;			/* Bad news!  */ +			skb->dev = dev;			/* Mark as being used by this device. */ +#if LINUX_VERSION_CODE > 0x10300 +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			vp->rx_ring[entry].addr = virt_to_bus(skb->tail); +#else +			vp->rx_ring[entry].addr = virt_to_bus(skb->data); +#endif +			vp->rx_skbuff[entry] = skb; +		} +		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */ +	} +	return 0; +} + +static int +vortex_close(struct device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	int ioaddr = dev->base_addr; +	int i; + +	dev->start = 0; +	dev->tbusy = 1; + +	if (vortex_debug > 1) { +		printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n", +			   dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus)); +		printk("%s: vortex close stats: rx_nocopy %d rx_copy %d" +			   " tx_queued %d.\n", +			   dev->name, rx_nocopy, rx_copy, queued_packet); +	} + +	del_timer(&vp->timer); + +	/* Turn off statistics ASAP.  We update lp->stats below. */ +	outw(StatsDisable, ioaddr + EL3_CMD); + +	/* Disable the receiver and transmitter. */ +	outw(RxDisable, ioaddr + EL3_CMD); +	outw(TxDisable, ioaddr + EL3_CMD); + +	if (dev->if_port == XCVR_10base2) +		/* Turn off thinnet power.  Green! */ +		outw(StopCoax, ioaddr + EL3_CMD); + +#ifdef SA_SHIRQ +	free_irq(dev->irq, dev); +#else +	free_irq(dev->irq); +	irq2dev_map[dev->irq] = 0; +#endif + +	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); + +	update_stats(ioaddr, dev); +	if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ +		outl(0, ioaddr + UpListPtr); +		for (i = 0; i < RX_RING_SIZE; i++) +			if (vp->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +				vp->rx_skbuff[i]->free = 1; +#endif +				dev_kfree_skb (vp->rx_skbuff[i], FREE_WRITE); +				vp->rx_skbuff[i] = 0; +			} +	} +	if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ +		outl(0, ioaddr + DownListPtr); +		for (i = 0; i < TX_RING_SIZE; i++) +			if (vp->tx_skbuff[i]) { +				dev_kfree_skb(vp->tx_skbuff[i], FREE_WRITE); +				vp->tx_skbuff[i] = 0; +			} +	} + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static struct enet_statistics * +vortex_get_stats(struct device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	unsigned long flags; + +	if (dev->start) { +		save_flags(flags); +		cli(); +		update_stats(dev->base_addr, dev); +		restore_flags(flags); +	} +	return &vp->stats; +} + +/*  Update statistics. +	Unlike with the EL3 we need not worry about interrupts changing +	the window setting from underneath us, but we must still guard +	against a race condition with a StatsUpdate interrupt updating the +	table.  This is done by checking that the ASM (!) code generated uses +	atomic updates with '+='. +	*/ +static void update_stats(int ioaddr, struct device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; + +	/* Unlike the 3c5x9 we need not turn off stats updates while reading. */ +	/* Switch to the stats window, and read everything. */ +	EL3WINDOW(6); +	vp->stats.tx_carrier_errors		+= inb(ioaddr + 0); +	vp->stats.tx_heartbeat_errors	+= inb(ioaddr + 1); +	/* Multiple collisions. */		inb(ioaddr + 2); +	vp->stats.collisions			+= inb(ioaddr + 3); +	vp->stats.tx_window_errors		+= inb(ioaddr + 4); +	vp->stats.rx_fifo_errors		+= inb(ioaddr + 5); +	vp->stats.tx_packets			+= inb(ioaddr + 6); +	vp->stats.tx_packets			+= (inb(ioaddr + 9)&0x30) << 4; +	/* Rx packets	*/				inb(ioaddr + 7);   /* Must read to clear */ +	/* Tx deferrals */				inb(ioaddr + 8); +	/* Don't bother with register 9, an extension of registers 6&7. +	   If we do use the 6&7 values the atomic update assumption above +	   is invalid. */ +	inw(ioaddr + 10);	/* Total Rx and Tx octets. */ +	inw(ioaddr + 12); +	/* New: On the Vortex we must also clear the BadSSD counter. */ +	EL3WINDOW(4); +	inb(ioaddr + 12); + +	/* We change back to window 7 (not 1) with the Vortex. */ +	EL3WINDOW(7); +	return; +} + +/* This new version of set_rx_mode() supports v1.4 kernels. +   The Vortex chip has no documented multicast filter, so the only +   multicast setting is to receive all multicast frames.  At least +   the chip has a very clean way to set the mode, unlike many others. */ +static void +set_rx_mode(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	short new_mode; + +	if (dev->flags & IFF_PROMISC) { +		if (vortex_debug > 3) +			printk("%s: Setting promiscuous mode.\n", dev->name); +		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm; +	} else	if ((dev->mc_list)  ||  (dev->flags & IFF_ALLMULTI)) { +		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast; +	} else +		new_mode = SetRxFilter | RxStation | RxBroadcast; + +	outw(new_mode, ioaddr + EL3_CMD); +} + +#ifdef MODULE +void +cleanup_module(void) +{ +	struct device *next_dev; + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_vortex_dev) { +		next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module; +		if (root_vortex_dev->dma) +		  free_dma(root_vortex_dev->dma); +		unregister_netdev(root_vortex_dev); +		outw(TotalReset, root_vortex_dev->base_addr + EL3_CMD); +		release_region(root_vortex_dev->base_addr, CORKSCREW_TOTAL_SIZE); +		kfree(root_vortex_dev); +		root_vortex_dev = next_dev; +	} +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c515.c" + *  c-indent-level: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/3c59x.c b/linux/src/drivers/net/3c59x.c new file mode 100644 index 0000000..a6b89cd --- /dev/null +++ b/linux/src/drivers/net/3c59x.c @@ -0,0 +1,2648 @@ +/* EtherLinkXL.c: A 3Com EtherLink PCI III/XL ethernet driver for linux. */ +/* +	Written 1996-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	This driver is for the 3Com "Vortex" and "Boomerang" series ethercards. +	Members of the series include Fast EtherLink 3c590/3c592/3c595/3c597 +	and the EtherLink XL 3c900 and 3c905 cards. + +	The original author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	410 Severn Ave., Suite 210 +	Annapolis MD 21403 + +	Support information and updates are available at +	http://www.scyld.com/network/vortex.html +*/ + +static const char versionA[] = +"3c59x.c:v0.99Za 4/17/2003 Donald Becker, becker@scyld.com\n"; +static const char versionB[] = +"  http://www.scyld.com/network/vortex.html\n"; + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* This driver uses 'options' to pass the media type, full-duplex flag, etc. +   See media_tbl[] and the web page for the possible types. +   There is no limit on card count, MAX_UNITS limits only module options. */ +#define MAX_UNITS 8 +static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1,}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1512 effectively disables this feature. */ +static const int rx_copybreak = 200; + +/* Allow setting MTU to a larger size, bypassing the normal Ethernet setup. */ +static const int mtu = 1500; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   Cyclones and later have a 64 or 256 element hash table based on the +   Ethernet CRC. */ +static int multicast_filter_limit = 64; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority. +   Do not increase the Tx ring beyond 256. +   Large receive rings waste memory and confound network buffer limits. +   These values have been carefully studied: changing these might mask a +   problem, it won't fix it. + */ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */ +#define RX_RING_SIZE	32 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  The 1536 value is not +   a limit, or directly related to MTU, but rather a way to keep a +   consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if LINUX_VERSION_CODE < 0x20300  &&  defined(MODVERSIONS) +#include <linux/module.h> +#include <linux/modversions.h> +#else +#include <linux/modversions.h> +#include <linux/module.h> +#endif + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/irq.h> +#include <asm/byteorder.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. +   Compatibility defines are now in kern_compat.h */ + +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("3Com EtherLink XL (3c590/3c900 series) driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(multicast_filter_limit, "i"); +#ifdef MODULE_PARM_DESC +MODULE_PARM_DESC(debug, "3c59x message level (0-31)"); +MODULE_PARM_DESC(options, "3c59x force fixed media type"); +MODULE_PARM_DESC(full_duplex, +				 "3c59x set to 1 to force full duplex (deprecated)"); +MODULE_PARM_DESC(rx_copybreak, +				 "3c59x copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(max_interrupt_work, +				 "3c59x maximum events handled per interrupt"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast address count before switching to Rx-all-multicast"); +#endif + +/* Operational parameter that usually are not changed. */ + +/* Set iff a MII transceiver on any interface requires mdio preamble. +   This only set with the original DP83840 on older 3c905 boards, so the extra +   code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 0; + +/* Performance and path-coverage information. */ +static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits; + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the 3Com FastEtherLink and FastEtherLink +XL, 3Com's PCI to 10/100baseT adapters.  It also works with the 10Mbs +versions of the FastEtherLink cards.  The supported product IDs are +in the pci_tbl[] list. + +The related ISA 3c515 is supported with a separate driver, 3c515.c, included +with the kernel source. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board.  The system BIOS should be set to assign the +PCI INTA signal to an otherwise unused system IRQ line. + +The EEPROM settings for media type and forced-full-duplex are observed. +The EEPROM media type should be left at the default "autoselect" unless using +10base2 or AUI connections which cannot be reliably detected. + +III. Driver operation + +The 3c59x series use an interface that's very similar to the previous 3c5x9 +series.  The primary interface is two programmed-I/O FIFOs, with an +alternate single-contiguous-region bus-master transfer (see next). + +The 3c900 "Boomerang" series uses a full-bus-master interface with separate +lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, +DEC Tulip and Intel Speedo3.  The first chip version retains a compatible +programmed-I/O interface that has been removed in 'B' and subsequent board +revisions. + +One extension that is advertised in a very large font is that the adapters +are capable of being bus masters.  On the Vortex chip this capability was +only for a single contiguous region making it far less useful than the full +bus master capability.  There is a significant performance impact of taking +an extra interrupt or polling for the completion of each transfer, as well +as difficulty sharing the single transfer engine between the transmit and +receive threads.  Using DMA transfers is a win only with large blocks or +with the flawed versions of the Intel Orion motherboard PCI controller. + +The Boomerang chip's full-bus-master interface is useful, and has the +currently-unused advantages over other similar chips that queued transmit +packets may be reordered and receive buffer groups are associated with a +single frame. + +With full-bus-master support, this driver uses a "RX_COPYBREAK" scheme. +Rather than a fixed intermediate receive buffer, this scheme allocates +full-sized skbuffs as receive buffers.  The value RX_COPYBREAK is used as +the copying breakpoint: it is chosen to trade-off the memory wasted by +passing the full-sized skbuff to the queue layer for all frames vs. the +copying cost of copying a frame to a correctly-sized skbuff. + + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +IV. Notes + +Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing development +3c590, 3c595, and 3c900 boards. +The name "Vortex" is the internal 3Com project name for the PCI ASIC, and +the EISA version is called "Demon".  According to Terry these names come +from rides at the local amusement park. + +The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes. +This driver only supports ethernet packets on some kernels because of the +skbuff allocation limit of 4K. +*/ + +/* The Vortex size is twice that of the original EtherLinkIII series: the +   runtime register window, window 1, is now always mapped in. +   The Boomerang size is twice as large as the Vortex -- it has additional +   bus master control registers. */ +#define VORTEX_SIZE 0x20 +#define BOOMERANG_SIZE 0x40 +#define CYCLONE_SIZE 0x80 +enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=0x804, IS_TORNADO=0x08, +	   HAS_PWR_CTRL=0x10, HAS_MII=0x20, HAS_NWAY=0x40, HAS_CB_FNS=0x80, +	   EEPROM_8BIT=0x200, INVERT_LED_PWR=0x400, MII_XCVR_PWR=0x4000, +	   HAS_V2_TX=0x800, WN0_XCVR_PWR=0x1000, +}; +/* Base feature sets for the generations. */ +#define FEATURE_BOOMERANG (HAS_MII)				/* 905 */ +#define FEATURE_CYCLONE  (IS_CYCLONE|HAS_V2_TX) 			/* 905B */ +#define FEATURE_TORNADO  (IS_TORNADO|HAS_NWAY|HAS_V2_TX) 	/* 905C */ + +static void *vortex_probe1(struct pci_dev *pdev, void *init_dev, +						   long ioaddr, int irq, int chip_idx, int find_cnt); +static int pwr_event(void *dev_instance, int event); +#ifdef USE_MEM_OPS +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) +#else +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO  | PCI_ADDR0) +#endif + +static struct pci_id_info pci_tbl[] = { +	{"3c590 Vortex 10Mbps", 	{ 0x590010B7, 0xffffffff }, +	 PCI_IOTYPE, VORTEX_SIZE, IS_VORTEX, }, +	{"3c595 Vortex 100baseTx",	{ 0x595010B7, 0xffffffff }, +	 PCI_IOTYPE, VORTEX_SIZE, IS_VORTEX, }, +	{"3c595 Vortex 100baseT4",	{ 0x595110B7, 0xffffffff }, +	 PCI_IOTYPE, VORTEX_SIZE, IS_VORTEX, }, +	{"3c595 Vortex 100base-MII",{ 0x595210B7, 0xffffffff }, +	 PCI_IOTYPE, VORTEX_SIZE, IS_VORTEX, }, +	/* Change EISA_scan if these move from index 4 and 5. */ +	{"3c592 EISA Vortex",		{ 0x592010B7, 0xffffffff }, +	 PCI_IOTYPE, VORTEX_SIZE, IS_VORTEX, }, +	{"3c597 EISA Vortex",		{ 0x597010B7, 0xffffffff }, +	 PCI_IOTYPE, VORTEX_SIZE, IS_VORTEX, }, +	{"Vortex (unknown)",		{ 0x590010B7, 0xff00ffff }, +	 PCI_IOTYPE, VORTEX_SIZE, IS_VORTEX, }, +	{"3c900 Boomerang 10baseT",	{ 0x900010B7, 0xffffffff }, +	 PCI_IOTYPE, BOOMERANG_SIZE, IS_BOOMERANG, }, +	{"3c900 Boomerang 10Mbps Combo", { 0x900110B7, 0xffffffff }, +	 PCI_IOTYPE,BOOMERANG_SIZE, IS_BOOMERANG, }, +	{"3c900 Cyclone 10Mbps TPO",   { 0x900410B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, IS_CYCLONE, }, +	{"3c900 Cyclone 10Mbps Combo", { 0x900510B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, IS_CYCLONE, }, +	{"3c900 Cyclone 10Mbps TPC",   { 0x900610B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, IS_CYCLONE, }, +	{"3c900B-FL Cyclone 10base-FL",{ 0x900A10B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, IS_CYCLONE, }, +	{"3c905 Boomerang 100baseTx",{ 0x905010B7, 0xffffffff }, +	 PCI_IOTYPE,BOOMERANG_SIZE, IS_BOOMERANG|HAS_MII, }, +	{"3c905 Boomerang 100baseT4",{ 0x905110B7, 0xffffffff }, +	 PCI_IOTYPE,BOOMERANG_SIZE, IS_BOOMERANG|HAS_MII, }, +	{"3c905B Cyclone 100baseTx",{ 0x905510B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, IS_CYCLONE|HAS_NWAY, }, +	{"3c905B Cyclone 10/100/BNC",{ 0x905810B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, IS_CYCLONE|HAS_NWAY, }, +	{"3c905B-FX Cyclone 100baseFx",{ 0x905A10B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, IS_CYCLONE, }, +	{"3c905C Tornado",{ 0x920010B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_TORNADO, }, +	{"3c920 Tornado",{ 0x920110B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_TORNADO, }, +	{"3c920 series Tornado",{ 0x920010B7, 0xfff0ffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_TORNADO, }, +	{"3c982 Server Tornado",{ 0x980510B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_TORNADO, }, +	{"3c980 Cyclone",{ 0x980010B7, 0xfff0ffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_CYCLONE|HAS_NWAY, }, +	{"3cSOHO100-TX Hurricane", { 0x764610B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_CYCLONE, }, +	{"3c555 Laptop Hurricane", { 0x505510B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_CYCLONE, }, +	{"3c556 Laptop Tornado",{ 0x605510B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_TORNADO|EEPROM_8BIT, }, +	{"3c556 series Laptop Tornado",{ 0x605510B7, 0xf0ffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_TORNADO|EEPROM_8BIT, }, +	{"3c1556B-5 mini-PCI",{ 0x605610B7, 0xffffffff, 0x655610b7, 0xffffffff, }, +	 PCI_IOTYPE, CYCLONE_SIZE, +	 FEATURE_TORNADO|EEPROM_8BIT|INVERT_LED_PWR|WN0_XCVR_PWR, }, +	{"3c1556B mini-PCI",{ 0x605610B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, +	 FEATURE_TORNADO|EEPROM_8BIT|HAS_CB_FNS|INVERT_LED_PWR|MII_XCVR_PWR, }, +	{"3c1556B series mini-PCI",{ 0x605610B7, 0xf0ffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, +	 FEATURE_TORNADO|EEPROM_8BIT|HAS_CB_FNS|INVERT_LED_PWR|MII_XCVR_PWR, }, +	{"3c575 Boomerang CardBus",	{ 0x505710B7, 0xffffffff }, +	 PCI_IOTYPE,BOOMERANG_SIZE, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, }, +	{"3CCFE575BT Cyclone CardBus",{ 0x515710B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, +	  FEATURE_CYCLONE | HAS_CB_FNS | EEPROM_8BIT | INVERT_LED_PWR, }, +	{"3CCFE575CT Tornado CardBus",{ 0x525710B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, +	 FEATURE_TORNADO|HAS_CB_FNS|EEPROM_8BIT|MII_XCVR_PWR, }, +	{"3CCFE656 Cyclone CardBus",{ 0x656010B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, +	 IS_CYCLONE|HAS_NWAY|HAS_CB_FNS| INVERT_LED_PWR | MII_XCVR_PWR, }, +	{"3CCFE656B Cyclone+Winmodem CardBus",{ 0x656210B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, +	 FEATURE_CYCLONE/*|HAS_NWAY*/ |HAS_CB_FNS|EEPROM_8BIT|INVERT_LED_PWR|MII_XCVR_PWR, }, +	{"3CCFE656C Tornado+Winmodem CardBus",{ 0x656410B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, +	 (FEATURE_TORNADO & ~HAS_NWAY)|HAS_CB_FNS|EEPROM_8BIT | MII_XCVR_PWR, }, +	{"3c450 HomePNA Tornado",{ 0x450010B7, 0xffffffff }, +	 PCI_IOTYPE, CYCLONE_SIZE, FEATURE_TORNADO, }, +	{"3c575 series CardBus (unknown version)", {0x505710B7, 0xf0ffffff }, +	 PCI_IOTYPE, BOOMERANG_SIZE, IS_BOOMERANG|HAS_MII, }, +	{"3Com Boomerang (unknown version)",{ 0x900010B7, 0xff00ffff }, +	 PCI_IOTYPE, BOOMERANG_SIZE, IS_BOOMERANG, }, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info vortex_drv_id = { +	"vortex", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_tbl, +	vortex_probe1, pwr_event }; + +/* This driver was written to use I/O operations. +   However there are performance benefits to using memory operations, so +   that mode is now an options. +   Compiling for memory ops turns off EISA support. +*/ +#ifdef USE_MEM_OPS +#undef inb +#undef inw +#undef inl +#undef outb +#undef outw +#undef outl +#define inb readb +#define inw readw +#define inl readl +#define outb writeb +#define outw writew +#define outl writel +#endif + +/* Operational definitions. +   These are not used by other compilation units and thus are not +   exported in a ".h" file. + +   First the windows.  There are eight register windows, with the command +   and status registers available in each. +   */ +#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e + +/* The top five bits written to EL3_CMD are a command, the lower +   11 bits are the parameter, if applicable. +   Note that 11 parameters bits was fine for ethernet, but the new chip +   can handle FDDI length frames (~4500 octets) and now parameters count +   32-bit 'Dwords' rather than octets. */ + +enum vortex_cmd { +	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, +	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, +	UpStall = 6<<11, UpUnstall = (6<<11)+1, +	DownStall = (6<<11)+2, DownUnstall = (6<<11)+3, +	RxDiscard = 8<<11, TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, +	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, +	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, +	SetTxThreshold = 18<<11, SetTxStart = 19<<11, +	StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11, +	StatsDisable = 22<<11, StopCoax = 23<<11, SetFilterBit = 25<<11,}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { +	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8, +	RxMulticastHash = 0x10, +}; + +/* Bits in the general status register. */ +enum vortex_status { +	IntLatch = 0x0001, HostError = 0x0002, TxComplete = 0x0004, +	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, +	IntReq = 0x0040, StatsFull = 0x0080, +	DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10, +	DMAInProgress = 1<<11,			/* DMA controller is still busy.*/ +	CmdInProgress = 1<<12,			/* EL3_CMD is still busy.*/ +}; + +/* Register window 1 offsets, the window used in normal operation. +   On the Vortex this window is always mapped at offsets 0x10-0x1f. */ +enum Window1 { +	TX_FIFO = 0x10,  RX_FIFO = 0x10,  RxErrors = 0x14, +	RxStatus = 0x18,  Timer=0x1A, TxStatus = 0x1B, +	TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ +}; +enum Window0 { +	Wn0EepromCmd = 10,		/* Window 0: EEPROM command register. */ +	Wn0EepromData = 12,		/* Window 0: EEPROM results register. */ +	IntrStatus=0x0E,		/* Valid in all windows. */ +}; + +/* EEPROM locations. */ +enum eeprom_offset { +	PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3, +	EtherLink3ID=7, IFXcvrIO=8, IRQLine=9, +	NodeAddr01=10, NodeAddr23=11, NodeAddr45=12, +	DriverTune=13, Checksum=15}; + +enum Window2 {			/* Window 2. */ +	Wn2_ResetOptions=12, +}; +enum Window3 {			/* Window 3: MAC/config bits. */ +	Wn3_Config=0, Wn3_MaxPktSize=4, Wn3_MAC_Ctrl=6, Wn3_Options=8, +}; + +enum Window4 {		/* Window 4: Xcvr/media bits. */ +	Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10, +}; +enum Window5 { +	Wn5_TxThreshold = 0, Wn5_RxFilter = 8, +}; +enum Win4_Media_bits { +	Media_SQE = 0x0008,		/* Enable SQE error counting for AUI. */ +	Media_10TP = 0x00C0,	/* Enable link beat and jabber for 10baseT. */ +	Media_Lnk = 0x0080,		/* Enable just link beat for 100TX/100FX. */ +	Media_LnkBeat = 0x0800, +}; +enum Window7 { +	/* Bus Master control on Vortex. */ +	Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, +	/* On Cyclone and later, VLAN and PowerMgt control. */  +	Wn7_VLAN_Mask = 0, Wn7_VLAN_EtherType = 4, Wn7_PwrMgmtEvent = 12, +}; + +/* Boomerang and Cyclone bus master control registers. */ +enum MasterCtrl { +	PktStatus = 0x20, DownListPtr = 0x24, FragAddr = 0x28, FragLen = 0x2c, +	DownPollRate = 0x2d, TxFreeThreshold = 0x2f, +	UpPktStatus = 0x30, UpListPtr = 0x38, +	/* Cyclone+. */ +	TxPktID=0x18, RxPriorityThresh = 0x3c, +}; + +/* The Rx and Tx descriptor lists. +   Caution Alpha hackers: these types are 32 bits!  Note also the 8 byte +   alignment contraint on tx_ring[] and rx_ring[]. */ +#define LAST_FRAG  0x80000000			/* Last Addr/Len pair in descriptor. */ +struct boom_rx_desc { +	u32 next;					/* Last entry points to 0.   */ +	s32 status; +	u32 addr;					/* Up to 63 addr/len pairs possible. */ +	s32 length;					/* Set LAST_FRAG to indicate last pair. */ +}; +/* Values for the Rx status entry. */ +enum rx_desc_status { +	RxDComplete=0x00008000, RxDError=0x4000, +	/* See boomerang_rx() for actual error bits */ +	IPChksumErr=1<<25, TCPChksumErr=1<<26, UDPChksumErr=1<<27, +	IPChksumValid=1<<29, TCPChksumValid=1<<30, UDPChksumValid=1<<31, +}; + +struct boom_tx_desc { +	u32 next;					/* Last entry points to 0.   */ +	s32 status;					/* bits 0:12 length, others see below.  */ +	u32 addr; +	s32 length; +}; + +/* Values for the Tx status entry. */ +enum tx_desc_status { +	CRCDisable=0x2000, TxIntrDnComplete=0x8000, TxDownComplete=0x10000, +	AddIPChksum=0x02000000, AddTCPChksum=0x04000000, AddUDPChksum=0x08000000, +	TxNoRoundup=0x10000000,		/* HAS_V2_TX should not word-pad packet.  */ +	TxIntrUploaded=0x80000000,		/* IRQ when in FIFO, but maybe not sent. */ +}; + +/* Chip features we care about in vp->capabilities, read from the EEPROM. */ +enum ChipCaps { CapBusMaster=0x20, CapNoTxLength=0x0200, CapPwrMgmt=0x2000 }; + +#define PRIV_ALIGN	15 	/* Required alignment mask */ +struct vortex_private { +	/* The Rx and Tx rings should be quad-word-aligned. */ +	struct boom_rx_desc rx_ring[RX_RING_SIZE]; +	struct boom_tx_desc tx_ring[TX_RING_SIZE]; +	/* The addresses of transmit- and receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct net_device *next_module; +	void *priv_addr; +	/* Keep the Rx and Tx variables grouped on their own cache lines. */ +	struct boom_rx_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	struct boom_tx_desc *tx_desc_tail; +	struct sk_buff *tx_skb;		/* Packet being eaten by bus master ctrl.  */ +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1, restart_tx:1; + +	long last_reset; +	spinlock_t	window_lock; +	struct net_device_stats stats; +	char *cb_fn_base;			/* CardBus function status addr space. */ +	int msg_level; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev;	/* PCI configuration space information. */ + +	/* The remainder are related to chip state, mostly media selection. */ +	int multicast_filter_limit; +	u32 mc_filter[8]; +	int max_interrupt_work; +	int rx_mode; +	struct timer_list timer;	/* Media selection timer. */ +	int options;				/* User-settable misc. driver options. */ +	unsigned int media_override:4, 			/* Passed-in media type. */ +		default_media:4,				/* Read from the EEPROM/Wn3_Config. */ +		full_duplex:1, medialock:1, autoselect:1, +		bus_master:1,				/* Vortex can only do a fragment bus-m. */ +		full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang  */ +		hw_csums:1,				/* Has hardware checksums. */ +		restore_intr_mask:1, +		polling:1; +	u16 status_enable; +	u16 intr_enable; +	u16 available_media;				/* From Wn3_Options. */ +	u16 wn3_mac_ctrl;					/* Current settings. */ +	u16 capabilities, info1, info2;		/* Various, from EEPROM. */ +	u16 advertising;					/* NWay media advertisement */ +	unsigned char phys[2];				/* MII device addresses. */ +}; + +/* The action to take with a media selection timer tick. +   Note that we deviate from the 3Com order by checking 10base2 before AUI. + */ +enum xcvr_types { +	XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, +	XCVR_100baseFx, XCVR_MII=6, XCVR_NWAY=8, XCVR_ExtMII=9, XCVR_Default=10, +}; + +static struct media_table { +	char *name; +	unsigned int media_bits:16,		/* Bits to set in Wn4_Media register. */ +		mask:8,				/* The transceiver-present bit in Wn3_Config.*/ +		next:8;				/* The media type to try next. */ +	int wait;			/* Time before we check media status. */ +} media_tbl[] = { +  {	"10baseT",   Media_10TP,0x08, XCVR_10base2, (14*HZ)/10}, +  { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10}, +  { "undefined", 0,			0x80, XCVR_10baseT, 10000}, +  { "10base2",   0,			0x10, XCVR_AUI,		(1*HZ)/10}, +  { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10}, +  { "100baseFX", Media_Lnk, 0x04, XCVR_MII,		(14*HZ)/10}, +  { "MII",		 0,			0x41, XCVR_10baseT, 3*HZ }, +  { "undefined", 0,			0x01, XCVR_10baseT, 10000}, +  { "Autonegotiate", 0,		0x41, XCVR_10baseT, 3*HZ}, +  { "MII-External",	 0,		0x41, XCVR_10baseT, 3*HZ }, +  { "Default",	 0,			0xFF, XCVR_10baseT, 10000}, +}; + +#if ! defined(CARDBUS) && ! defined(USE_MEM_OPS) +static int eisa_scan(struct net_device *dev); +#endif +static int vortex_open(struct net_device *dev); +static void set_media_type(struct net_device *dev); +static void activate_xcvr(struct net_device *dev); +static void start_operation(struct net_device *dev); +static void start_operation1(struct net_device *dev); +static void mdio_sync(long ioaddr, int bits); +static int mdio_read(long ioaddr, int phy_id, int location); +static void mdio_write(long ioaddr, int phy_id, int location, int value); +static void vortex_timer(unsigned long arg); +static void vortex_tx_timeout(struct net_device *dev); +static int vortex_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int vortex_rx(struct net_device *dev); +static int boomerang_rx(struct net_device *dev); +static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int vortex_close(struct net_device *dev); +static void update_stats(long ioaddr, struct net_device *dev); +static struct net_device_stats *vortex_get_stats(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); +static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#if defined(NO_PCI) +#define acpi_set_WOL(dev)	do {} while(0); +#define acpi_wake(pci_dev)	do {} while(0); +#define acpi_set_pwr_state(pci_dev, state)	do {} while(0); +#else +static void acpi_set_WOL(struct net_device *dev); +#endif + + +/* A list of all installed Vortex devices, for removing the driver module. */ +static struct net_device *root_vortex_dev = NULL; + + +#if defined(MODULE) && defined(CARDBUS) + +#include <pcmcia/driver_ops.h> + +static dev_node_t *vortex_attach(dev_locator_t *loc) +{ +	u32 io, pci_id; +	u8 bus, devfn, irq; +	struct net_device *dev; +	int chip_idx; + +	if (loc->bus != LOC_PCI) return NULL; +	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; +	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); +	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); +	pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &pci_id); +	printk(KERN_INFO "vortex_attach(bus %d, function %d, device %8.8x)\n", +		   bus, devfn, pci_id); +	io &= ~3; +	if (io == 0 || irq == 0) { +		printk(KERN_ERR "The 3Com CardBus Ethernet interface was not " +			   "assigned an %s.\n" KERN_ERR "  It will not be activated.\n", +			   io == 0 ? "I/O address" : "IRQ"); +		return NULL; +	} +	for (chip_idx = 0; pci_tbl[chip_idx].id.pci; chip_idx++) +		if ((pci_id & pci_tbl[chip_idx].id.pci_mask) == +			pci_tbl[chip_idx].id.pci) +			break; +	if (pci_tbl[chip_idx].id.pci == 0) { 		/* Compiled out! */ +		printk(KERN_INFO "Unable to match chip type %8.8x in " +			   "vortex_attach().\n", pci_id); +		return NULL; +	} +	dev = vortex_probe1(pci_find_slot(bus, devfn), NULL, io, irq, chip_idx, MAX_UNITS+1); +	if (dev) { +		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); +		strcpy(node->dev_name, dev->name); +		node->major = node->minor = 0; +		node->next = NULL; +		MOD_INC_USE_COUNT; +		return node; +	} +	return NULL; +} + +static void vortex_detach(dev_node_t *node) +{ +	struct net_device **devp, **next; +	printk(KERN_DEBUG "vortex_detach(%s)\n", node->dev_name); +	for (devp = &root_vortex_dev; *devp; devp = next) { +		next = &((struct vortex_private *)(*devp)->priv)->next_module; +		if (strcmp((*devp)->name, node->dev_name) == 0) break; +	} +	if (*devp) { +		struct net_device *dev = *devp; +		struct vortex_private *vp = dev->priv; +		if (dev->flags & IFF_UP) +			dev_close(dev); +		dev->flags &= ~(IFF_UP|IFF_RUNNING); +		unregister_netdev(dev); +		if (vp->cb_fn_base) iounmap(vp->cb_fn_base); +		kfree(dev); +		*devp = *next; +		kfree(vp->priv_addr); +		kfree(node); +		MOD_DEC_USE_COUNT; +	} +} + +struct driver_operations vortex_ops = { +	"3c575_cb", vortex_attach, NULL, NULL, vortex_detach +}; + +#endif  /* Old-style Cardbus module support */ + +#if defined(MODULE) || (LINUX_VERSION_CODE >= 0x020400) + +#if ! defined(MODULE)			/* Must be a 2.4 kernel */ +module_init(init_module); +module_exit(cleanup_module); +#endif + +int init_module(void) +{ +	printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); +#ifdef CARDBUS +	register_driver(&vortex_ops); +	return 0; +#else +#ifndef USE_MEM_OPS +	/* This is not quite correct, but both EISA and PCI cards is unlikely. */ +	if (eisa_scan(0) >= 0) +		return 0; +#if defined(NO_PCI) +	return 0; +#endif +#endif + +	return pci_drv_register(&vortex_drv_id, NULL); +#endif +} + +#else +int tc59x_probe(struct net_device *dev) +{ +	int retval = -ENODEV; + +	/* Allow an EISA-only driver. */ +#if ! defined(NO_PCI) +	if (pci_drv_register(&vortex_drv_id, dev) >= 0) { +		retval = 0; +		dev = 0; +	} +#endif +#ifndef USE_MEM_OPS +	if (eisa_scan(dev) >= 0) +		retval = 0; +#endif +	if (retval >= 0) +		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); +	return retval; +} +#endif  /* not MODULE */ + +#if ! defined(CARDBUS) && ! defined(USE_MEM_OPS) +static int eisa_scan(struct net_device *dev) +{ +	int cards_found = 0; + +	/* Check the slots of the EISA bus. */ +	if (EISA_bus) { +		static long ioaddr = 0x1000; +		for ( ; ioaddr < 0x9000; ioaddr += 0x1000) { +			int device_id; +			if (check_region(ioaddr, VORTEX_SIZE)) +				continue; +			/* Check the standard EISA ID register for an encoded '3Com'. */ +			if (inw(ioaddr + 0xC80) != 0x6d50) +				continue; +			/* Check for a product that we support, 3c59{2,7} any rev. */ +			device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83); +			if ((device_id & 0xFF00) != 0x5900) +				continue; +			vortex_probe1(0, dev, ioaddr, inw(ioaddr + 0xC88) >> 12, +						  (device_id & 0xfff0) == 0x5970 ? 5 : 4, cards_found); +			dev = 0; +			cards_found++; +		} +	} + +	return cards_found ? 0 : -ENODEV; +} +#endif  /* ! Cardbus */ + +static int do_eeprom_op(long ioaddr, int ee_cmd) +{ +	int timer; + +	outw(ee_cmd, ioaddr + Wn0EepromCmd); + 	/* Wait for the read to take place, worst-case 162 us. */ +	for (timer = 1620; timer >= 0; timer--) { +		if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) +			break; +	} +	return inw(ioaddr + Wn0EepromData); +} + +static void *vortex_probe1(struct pci_dev *pdev, void *init_dev, +						   long ioaddr, int irq, int chip_idx, int find_cnt) +{ +	struct net_device *dev; +	struct vortex_private *vp; +	void *priv_mem; +	int option; +	unsigned int eeprom[0x40], checksum = 0;		/* EEPROM contents */ +	int ee_read_cmd; +	int drv_flags = pci_tbl[chip_idx].drv_flags; +	int i; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +#if ! defined(NO_PCI) +	/* Check the PCI latency value.  On the 3c590 series the latency timer +	   must be set to the maximum value to avoid data corruption that occurs +	   when the timer expires during a transfer.  This bug exists the Vortex +	   chip only. */ +	if (pdev) { +		u8 pci_latency; +		u8 new_latency = (drv_flags & IS_VORTEX) ? 248 : 32; + +		pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); +		if (pci_latency < new_latency) { +			printk(KERN_INFO "%s: Overriding PCI latency" +				   " timer (CFLT) setting of %d, new value is %d.\n", +				   dev->name, pci_latency, new_latency); +			pci_write_config_byte(pdev, PCI_LATENCY_TIMER, new_latency); +		} +	} +#endif + +	printk(KERN_INFO "%s: 3Com %s at 0x%lx, ", +		   dev->name, pci_tbl[chip_idx].name, ioaddr); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*vp) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) { +		printk(" INTERFACE MEMORY ALLOCATION FAILURE.\n"); +		return NULL; +	} + +	dev->base_addr = ioaddr; +	dev->irq = irq; +	dev->mtu = mtu; + +	dev->priv = vp = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(vp, 0, sizeof(*vp)); +	vp->priv_addr = priv_mem; + +	vp->next_module = root_vortex_dev; +	root_vortex_dev = dev; + +	vp->chip_id = chip_idx; +	vp->pci_dev = pdev; +	vp->drv_flags = drv_flags; +	vp->msg_level = (1 << debug) - 1; +	vp->rx_copybreak = rx_copybreak; +	vp->max_interrupt_work = max_interrupt_work; +	vp->multicast_filter_limit = multicast_filter_limit; + +	/* The lower four bits are the media type. */ +	if (dev->mem_start) +		option = dev->mem_start; +	else if (find_cnt < MAX_UNITS) +		option = options[find_cnt]; +	else +		option = -1; + +	if (option >= 0) { +		vp->media_override = ((option & 7) == 2)  ?  0  :  option & 15; +		vp->full_duplex = (option & 0x200) ? 1 : 0; +		vp->bus_master = (option & 16) ? 1 : 0; +	} else { +		vp->media_override = 7; +		vp->full_duplex = 0; +		vp->bus_master = 0; +	} +	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0) +		vp->full_duplex = 1; + +	vp->options = option; + +	/* Read the station address from the EEPROM. */ +	EL3WINDOW(0); +	/* Figure out the size and offset of the EEPROM table. +	   This is complicated by potential discontiguous address bits.  */ + +	/* Locate the opcode bits, 0xC0 or 0x300. */ +	outw(0x5555, ioaddr + Wn0EepromData); +	ee_read_cmd = do_eeprom_op(ioaddr, 0x80) == 0x5555  ?  0x200 : 0x80; +	/* Locate the table base for CardBus cards. */ +	if (do_eeprom_op(ioaddr, ee_read_cmd + 0x37) == 0x6d50) +		ee_read_cmd += 0x30; + +	for (i = 0; i < 0x40; i++) { +		int cmd_and_addr = ee_read_cmd + i; +		if (ee_read_cmd == 0xB0) { 		/* Correct for discontinuity. */ +			int offset = 0x30 + i; +			cmd_and_addr = 0x80 + (offset & 0x3f) + ((offset<<2) & 0x0f00); +		} +		eeprom[i] = do_eeprom_op(ioaddr, cmd_and_addr); +	} +	for (i = 0; i < 0x18; i++) +		checksum ^= eeprom[i]; +	checksum = (checksum ^ (checksum >> 8)) & 0xff; +	if (checksum != 0x00) {		/* Grrr, needless incompatible change 3Com. */ +		while (i < 0x21) +			checksum ^= eeprom[i++]; +		checksum = (checksum ^ (checksum >> 8)) & 0xff; +	} +	if (checksum != 0x00  &&  !(drv_flags & IS_TORNADO)) +		printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); + +	for (i = 0; i < 3; i++) +		((u16 *)dev->dev_addr)[i] = htons(eeprom[i + 10]); +	for (i = 0; i < 6; i++) +		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); +	EL3WINDOW(2); +	for (i = 0; i < 6; i++) +		outb(dev->dev_addr[i], ioaddr + i); + +	printk(", IRQ %d\n", dev->irq); +	/* Tell them about an invalid IRQ. */ +	if (dev->irq <= 0) +		printk(KERN_WARNING " *** Warning: IRQ %d is unlikely to work! ***\n", +			   dev->irq); + +#if ! defined(NO_PCI) +	if (drv_flags & HAS_CB_FNS) { +		u32 fn_st_addr;			/* Cardbus function status space */ +		pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &fn_st_addr); +		if (fn_st_addr) +			vp->cb_fn_base = ioremap(fn_st_addr & ~3, 128); +		printk(KERN_INFO "%s: CardBus functions mapped %8.8x->%p.\n", +			   dev->name, fn_st_addr, vp->cb_fn_base); +	} +#endif + +	/* Extract our information from the EEPROM data. */ +	vp->info1 = eeprom[13]; +	vp->info2 = eeprom[15]; +	vp->capabilities = eeprom[16]; + +	if (vp->info1 & 0x8000) +		vp->full_duplex = 1; +	if (vp->full_duplex) +		vp->medialock = 1; + +	/* Turn on the transceiver. */ +	activate_xcvr(dev); + +	{ +		char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; +		int i_cfg; +		EL3WINDOW(3); +		vp->available_media = inw(ioaddr + Wn3_Options); +		if ((vp->available_media & 0xff) == 0)		/* Broken 3c916 */ +			vp->available_media = 0x40; +		i_cfg = inl(ioaddr + Wn3_Config); /* Internal Configuration */ +		vp->default_media = (i_cfg >> 20) & 15; +		if (vp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "  Internal config register is %8.8x, " +				   "transceivers %#x.\n", i_cfg, inw(ioaddr + Wn3_Options)); +		printk(KERN_INFO "  %dK buffer %s Rx:Tx split, %s%s interface.\n", +			   8 << (i_cfg & 7), +			   ram_split[(i_cfg >> 16) & 3], +			   i_cfg & 0x01000000 ? "autoselect/" : "", +			   vp->default_media > XCVR_ExtMII ? "<invalid transceiver>" : +			   media_tbl[vp->default_media].name); +		vp->autoselect = i_cfg & 0x01000000 ? 1 : 0; +	} + +	if (vp->media_override != 7) { +		printk(KERN_INFO "  Media override to transceiver type %d (%s).\n", +			   vp->media_override, media_tbl[vp->media_override].name); +		dev->if_port = vp->media_override; +	} else +		dev->if_port = vp->default_media; + +	if ((vp->available_media & 0x41) || (drv_flags & HAS_NWAY) || +		dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) { +		int phy, phy_idx = 0; +		EL3WINDOW(4); +		mii_preamble_required++; +		mdio_sync(ioaddr, 32); +		mdio_read(ioaddr, 24, 1); +		for (phy = 1; phy <= 32 && phy_idx < sizeof(vp->phys); phy++) { +			int mii_status, phyx = phy & 0x1f; +			mii_status = mdio_read(ioaddr, phyx, 1); +			if ((mii_status & 0xf800)  &&  mii_status != 0xffff) { +				vp->phys[phy_idx++] = phyx; +				printk(KERN_INFO "  MII transceiver found at address %d," +					   " status %4x.\n", phyx, mii_status); +				if ((mii_status & 0x0040) == 0) +					mii_preamble_required++; +			} +		} +		mii_preamble_required--; +		if (phy_idx == 0) { +			printk(KERN_WARNING"  ***WARNING*** No MII transceivers found!\n"); +			vp->phys[0] = 24; +		} else { +			if (mii_preamble_required == 0  && +				mdio_read(ioaddr, vp->phys[0], 1) == 0) { +				printk(KERN_INFO "%s:  MII transceiver has preamble bug.\n", +					   dev->name); +				mii_preamble_required = 1; +			} +			vp->advertising = mdio_read(ioaddr, vp->phys[0], 4); +			if (vp->full_duplex) { +				/* Only advertise the FD media types. */ +				vp->advertising &= ~0x02A0; +				mdio_write(ioaddr, vp->phys[0], 4, vp->advertising); +			} +		} +	} else { +		/* We will emulate MII management. */ +		vp->phys[0] = 32; +	} + +	if (vp->capabilities & CapBusMaster) { +		vp->full_bus_master_tx = 1; +		printk(KERN_INFO"  Using bus-master transmits and %s receives.\n", +			   (vp->info2 & 1) ? "early" : "whole-frame" ); +		vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2; +	} + +	/* We do a request_region() to register /proc/ioports info. */ +	request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); + +	/* The 3c59x-specific entries in the device structure. */ +	dev->open = &vortex_open; +	dev->hard_start_xmit = &vortex_start_xmit; +	dev->stop = &vortex_close; +	dev->get_stats = &vortex_get_stats; +	dev->do_ioctl = &vortex_ioctl; +	dev->set_multicast_list = &set_rx_mode; + +	return dev; +} + + +static int vortex_open(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	MOD_INC_USE_COUNT; + +	acpi_wake(vp->pci_dev); +	vp->window_lock = SPIN_LOCK_UNLOCKED; +	activate_xcvr(dev); + +	/* Before initializing select the active media port. */ +	if (vp->media_override != 7) { +		if (vp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Media override to transceiver %d (%s).\n", +				   dev->name, vp->media_override, +				   media_tbl[vp->media_override].name); +		dev->if_port = vp->media_override; +	} else if (vp->autoselect) { +		if (vp->drv_flags & HAS_NWAY) +			dev->if_port = XCVR_NWAY; +		else { +			/* Find first available media type, starting with 100baseTx. */ +			dev->if_port = XCVR_100baseTx; +			while (! (vp->available_media & media_tbl[dev->if_port].mask)) +				dev->if_port = media_tbl[dev->if_port].next; +		} +	} else +		dev->if_port = vp->default_media; + +	if (! vp->medialock) +		vp->full_duplex = 0; + +	vp->status_enable = SetStatusEnb | HostError|IntReq|StatsFull|TxComplete| +		(vp->full_bus_master_tx ? DownComplete : TxAvailable) | +		(vp->full_bus_master_rx ? UpComplete : RxComplete) | +		(vp->bus_master ? DMADone : 0); +	vp->intr_enable = SetIntrEnb | IntLatch | TxAvailable | RxComplete | +		StatsFull | HostError | TxComplete | IntReq +		| (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete; + +	if (vp->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Initial media type %s %s-duplex.\n", +			   dev->name, media_tbl[dev->if_port].name, +			   vp->full_duplex ? "full":"half"); + +	set_media_type(dev); +	start_operation(dev); + +	/* Use the now-standard shared IRQ implementation. */ +	if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	spin_lock(&vp->window_lock); + +	if (vp->msg_level & NETIF_MSG_IFUP) { +		EL3WINDOW(4); +		printk(KERN_DEBUG "%s: vortex_open() irq %d media status %4.4x.\n", +			   dev->name, dev->irq, inw(ioaddr + Wn4_Media)); +	} + +	/* Switch to the stats window, and clear all stats by reading. */ +	outw(StatsDisable, ioaddr + EL3_CMD); +	EL3WINDOW(6); +	for (i = 0; i < 10; i++) +		inb(ioaddr + i); +	inw(ioaddr + 10); +	inw(ioaddr + 12); +	/* New: On the Vortex we must also clear the BadSSD counter. */ +	EL3WINDOW(4); +	inb(ioaddr + 12); +	/* ..and on the Boomerang we enable the extra statistics bits. */ +	outw(0x0040, ioaddr + Wn4_NetDiag); + +	/* Switch to register set 7 for normal use. */ +	EL3WINDOW(7); +#if defined(CONFIG_VLAN) +	/* If this value is set no MTU adjustment is needed for 802.1Q. */ +	outw(0x8100, ioaddr + Wn7_VLAN_EtherType); +#endif +	spin_unlock(&vp->window_lock); + +	if (vp->full_bus_master_rx) { /* Boomerang bus master. */ +		vp->cur_rx = vp->dirty_rx = 0; +		/* Use 1518/+18 if the CRC is transferred. */ +		vp->rx_buf_sz = dev->mtu + 14; +		if (vp->rx_buf_sz < PKT_BUF_SZ) +			vp->rx_buf_sz = PKT_BUF_SZ; + +		/* Initialize the RxEarly register as recommended. */ +		outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD); +		outl(0x0020, ioaddr + PktStatus); +		for (i = 0; i < RX_RING_SIZE; i++) { +			vp->rx_ring[i].length = cpu_to_le32(vp->rx_buf_sz | LAST_FRAG); +			vp->rx_ring[i].status = 0; +			vp->rx_ring[i].next = virt_to_le32desc(&vp->rx_ring[i+1]); +			vp->rx_skbuff[i] = 0; +		} +		/* Wrap the ring. */ +		vp->rx_head_desc = &vp->rx_ring[0]; +		vp->rx_ring[i-1].next = virt_to_le32desc(&vp->rx_ring[0]); + +		for (i = 0; i < RX_RING_SIZE; i++) { +			struct sk_buff *skb = dev_alloc_skb(vp->rx_buf_sz); +			vp->rx_skbuff[i] = skb; +			if (skb == NULL) +				break;			/* Bad news!  */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			vp->rx_ring[i].addr = virt_to_le32desc(skb->tail); +		} +		outl(virt_to_bus(vp->rx_head_desc), ioaddr + UpListPtr); +	} +	if (vp->full_bus_master_tx) { 		/* Boomerang bus master Tx. */ +		dev->hard_start_xmit = &boomerang_start_xmit; +		vp->cur_tx = vp->dirty_tx = 0; +		vp->tx_desc_tail = &vp->tx_ring[TX_RING_SIZE - 1]; +		if (vp->drv_flags & IS_BOOMERANG) { +			/* Room for a packet, to avoid long DownStall delays. */ +			outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); +		} else if (vp->drv_flags & HAS_V2_TX) +			outb(20, ioaddr + DownPollRate); + +		/* Clear the Tx ring. */ +		for (i = 0; i < TX_RING_SIZE; i++) +			vp->tx_skbuff[i] = 0; +		outl(0, ioaddr + DownListPtr); +		vp->tx_full = 0; +		vp->restart_tx = 1; +	} +	/* The multicast filter is an ill-considered, write-only design. +	   The semantics are not documented, so we assume but do not rely +	   on the table being cleared with an RxReset. +	   Here we do an explicit clear of the largest known table. +	*/ +	if (vp->drv_flags & HAS_V2_TX) +		for (i = 0; i < 0x100; i++) +			outw(SetFilterBit | i, ioaddr + EL3_CMD); +	memset(vp->mc_filter, 0, sizeof vp->mc_filter); + +	/* Set receiver mode: presumably accept b-case and phys addr only. */ +	vp->rx_mode = 0; +	set_rx_mode(dev); + +	start_operation1(dev); + +	init_timer(&vp->timer); +	vp->timer.expires = jiffies + media_tbl[dev->if_port].wait; +	vp->timer.data = (unsigned long)dev; +	vp->timer.function = &vortex_timer;		/* timer handler */ +	add_timer(&vp->timer); + +	return 0; +} + +static void set_media_type(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i_cfg; + +	EL3WINDOW(3); +	i_cfg = inl(ioaddr + Wn3_Config); +	i_cfg &= ~0x00f00000; +	if (vp->drv_flags & HAS_NWAY) +		outl(i_cfg | 0x00800000, ioaddr + Wn3_Config); +	else +		outl(i_cfg | (dev->if_port << 20), ioaddr + Wn3_Config); + +	if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) { +		int mii_reg1, mii_reg5; +		EL3WINDOW(4); +		/* Read BMSR (reg1) only to clear old status. */ +		mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1); +		mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5); +		if (mii_reg5 == 0xffff  ||  mii_reg5 == 0x0000) +			;					/* No MII device or no link partner report */ +		else if ((mii_reg5 & 0x0100) != 0	/* 100baseTx-FD */ +				 || (mii_reg5 & 0x00C0) == 0x0040) /* 10T-FD, but not 100-HD */ +			vp->full_duplex = 1; +		if (vp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: MII #%d status %4.4x, link partner capability %4.4x," +				   " setting %s-duplex.\n", dev->name, vp->phys[0], +				   mii_reg1, mii_reg5, vp->full_duplex ? "full" : "half"); +		EL3WINDOW(3); +	} +	if (dev->if_port == XCVR_10base2) +		/* Start the thinnet transceiver. We should really wait 50ms...*/ +		outw(StartCoax, ioaddr + EL3_CMD); +	EL3WINDOW(4); +	if (dev->if_port != XCVR_NWAY) { +		outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP|Media_SQE)) | +			 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); +	} +	/* Do we require link beat to transmit? */ +	if (vp->info1 & 0x4000) +		outw(inw(ioaddr + Wn4_Media) & ~Media_Lnk, ioaddr + Wn4_Media); + +	/* Set the full-duplex and oversized frame bits. */ +	EL3WINDOW(3); + +	vp->wn3_mac_ctrl = vp->full_duplex ? 0x0120 : 0; +	if (dev->mtu > 1500) +		vp->wn3_mac_ctrl |= (dev->mtu == 1504  ?  0x0400 : 0x0040); +	outb(vp->wn3_mac_ctrl, ioaddr + Wn3_MAC_Ctrl); + +	if (vp->drv_flags & HAS_V2_TX) +		outw(dev->mtu + 14, ioaddr + Wn3_MaxPktSize); +} + +static void activate_xcvr(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int reset_opts; + +	/* Correct some magic bits. */ +	EL3WINDOW(2); +	reset_opts = inw(ioaddr + Wn2_ResetOptions); +	if (vp->drv_flags & INVERT_LED_PWR) +		reset_opts |= 0x0010; +	if (vp->drv_flags & MII_XCVR_PWR) +		reset_opts |= 0x4000; +	outw(reset_opts, ioaddr + Wn2_ResetOptions); +	if (vp->drv_flags & WN0_XCVR_PWR) { +		EL3WINDOW(0); +		outw(0x0900, ioaddr); +	} +} + +static void start_operation(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	outw(TxReset, ioaddr + EL3_CMD); +	for (i = 2000; i >= 0 ; i--) +		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +			break; + +	outw(RxReset | 0x04, ioaddr + EL3_CMD); +	/* Assume this cleared the filter. */ +	memset(vp->mc_filter, 0, sizeof vp->mc_filter); + +	/* Wait a few ticks for the RxReset command to complete. */ +	for (i = 0; i < 200000; i++) +		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +			break; +	if (i >= 200  &&  (vp->msg_level & NETIF_MSG_DRV)) +		printk(KERN_DEBUG "%s: Rx Reset took an unexpectedly long time" +			   " to finish, %d ticks.\n", +			   dev->name, i); + +	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); +	/* Handle VLANs and jumbo frames. */ +	if ((vp->drv_flags & HAS_V2_TX) && dev->mtu > 1500) { +		EL3WINDOW(3); +		outw(dev->mtu + 14, ioaddr + Wn3_MaxPktSize); +		if (dev->mtu > 2033) { +			outl(inl(ioaddr + Wn3_Config) | 0x0000C000, ioaddr + Wn3_Config); +			outw(SetTxStart + (2000>>2), ioaddr + EL3_CMD); +		} +	} +	/* Reset the station address and mask. */ +	EL3WINDOW(2); +	for (i = 0; i < 6; i++) +		outb(dev->dev_addr[i], ioaddr + i); +	for (; i < 12; i+=2) +		outw(0, ioaddr + i); +	if (vp->drv_flags & IS_BOOMERANG) { +		/* Room for a packet, to avoid long DownStall delays. */ +		outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); +	} else if (vp->drv_flags & HAS_V2_TX) { +		outb(20, ioaddr + DownPollRate); +		vp->restart_tx = 1; +	} +} + +static void start_operation1(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (vp->full_bus_master_rx) { /* post-Vortex bus master. */ +		/* Initialize the RxEarly register as recommended. */ +		outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD); +		outl(0x0020, ioaddr + PktStatus); +		outl(virt_to_bus(&vp->rx_ring[vp->cur_rx % RX_RING_SIZE]), +			 ioaddr + UpListPtr); +	} + +	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ +	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ +	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ +	/* Allow status bits to be seen. */ +	outw(vp->status_enable, ioaddr + EL3_CMD); +	/* Ack all pending events, and set active indicator mask. */ +	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, +		 ioaddr + EL3_CMD); +	outw(vp->intr_enable, ioaddr + EL3_CMD); +	if (vp->cb_fn_base)			/* The PCMCIA people are idiots.  */ +		writel(0x8000, vp->cb_fn_base + 4); +	netif_start_tx_queue(dev); +} + +static void vortex_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 60*HZ; +	int ok = 0; +	int media_status, old_window; + +	if (vp->msg_level & NETIF_MSG_TIMER) +		printk(KERN_DEBUG "%s: Media selection timer tick happened, " +			   "%s %s duplex.\n", +			   dev->name, media_tbl[dev->if_port].name, +			   vp->full_duplex ? "full" : "half"); + +	/* This only works with bus-master (non-3c590) chips. */ +	if (vp->cur_tx - vp->dirty_tx > 1  && +		(jiffies - dev->trans_start) > TX_TIMEOUT) { +		/* Check for blocked interrupts. */ +		if (inw(ioaddr + EL3_STATUS) & IntLatch) { +			/* We have a blocked IRQ line.  This should never happen, but +			   we recover as best we can.*/ +			if ( ! vp->polling) { +				if (jiffies - vp->last_reset > 10*HZ) { +					printk(KERN_ERR "%s: IRQ %d is physically blocked! " +						   "Failing back to low-rate polling.\n", +						   dev->name, dev->irq); +					vp->last_reset = jiffies; +				} +				vp->polling = 1; +			} +			vortex_interrupt(dev->irq, dev, 0); +			next_tick = jiffies + 2; +		} else { +			vortex_tx_timeout(dev); +			vp->last_reset = jiffies; +		} +	} + +	disable_irq(dev->irq); +	old_window = inw(ioaddr + EL3_CMD) >> 13; +	EL3WINDOW(4); +	media_status = inw(ioaddr + Wn4_Media); +	switch (dev->if_port) { +	case XCVR_10baseT:  case XCVR_100baseTx:  case XCVR_100baseFx: +		if (media_status & Media_LnkBeat) { +			ok = 1; +			if (vp->msg_level & NETIF_MSG_LINK) +				printk(KERN_DEBUG "%s: Media %s has link beat, %x.\n", +					   dev->name, media_tbl[dev->if_port].name, media_status); +		} else if (vp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: Media %s is has no link beat, %x.\n", +				   dev->name, media_tbl[dev->if_port].name, media_status); +		break; +	case XCVR_MII: case XCVR_NWAY: { +		int mii_status = mdio_read(ioaddr, vp->phys[0], 1); +		int mii_reg5, negotiated, duplex; +		ok = 1; +		if (vp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: MII transceiver has status %4.4x.\n", +				   dev->name, mii_status); +		if (vp->medialock) +			break; +		if ((mii_status & 0x0004) == 0) { +			next_tick = 5*HZ; +			break; +		} +		mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5); +		negotiated = mii_reg5 & vp->advertising; +		duplex = (negotiated & 0x0100) || (negotiated & 0x03C0) == 0x0040; +		if (mii_reg5 == 0xffff  ||  vp->full_duplex == duplex) +			break; +		if (vp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Setting %s-duplex based on " +				   "MII #%d link partner capability of %4.4x.\n", +				   dev->name, vp->full_duplex ? "full" : "half", +				   vp->phys[0], mii_reg5); +		vp->full_duplex = duplex; +		/* Set the full-duplex bit. */ +		EL3WINDOW(3); +		if (duplex) +			vp->wn3_mac_ctrl |= 0x120; +		else +			vp->wn3_mac_ctrl &= ~0x120; +		outb(vp->wn3_mac_ctrl, ioaddr + Wn3_MAC_Ctrl); +		break; +	} +	default:					/* Other media types handled by Tx timeouts. */ +		if (vp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: Media %s is has no indication, %x.\n", +				   dev->name, media_tbl[dev->if_port].name, media_status); +		ok = 1; +	} +	if ( ! ok) { +		int i_cfg; + +		do { +			dev->if_port = media_tbl[dev->if_port].next; +		} while ( ! (vp->available_media & media_tbl[dev->if_port].mask)); +		if (dev->if_port == XCVR_Default) { /* Go back to default. */ +		  dev->if_port = vp->default_media; +		  if (vp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: Media selection failing, using default " +				   "%s port.\n", +				   dev->name, media_tbl[dev->if_port].name); +		} else { +			if (vp->msg_level & NETIF_MSG_LINK) +				printk(KERN_DEBUG "%s: Media selection failed, now trying " +					   "%s port.\n", +					   dev->name, media_tbl[dev->if_port].name); +			next_tick = media_tbl[dev->if_port].wait; +		} +		outw((media_status & ~(Media_10TP|Media_SQE)) | +			 media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); + +		EL3WINDOW(3); +		i_cfg = inl(ioaddr + Wn3_Config); +		i_cfg &= ~0x00f00000; +		i_cfg |= (dev->if_port << 20); +		outl(i_cfg, ioaddr + Wn3_Config); + +		outw(dev->if_port == XCVR_10base2 ? StartCoax : StopCoax, +			 ioaddr + EL3_CMD); +	} +	EL3WINDOW(old_window); +	enable_irq(dev->irq); +	if (vp->restore_intr_mask) +		outw(FakeIntr, ioaddr + EL3_CMD); + +	if (vp->msg_level & NETIF_MSG_TIMER) +	  printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n", +			 dev->name, media_tbl[dev->if_port].name); + +	vp->timer.expires = jiffies + next_tick; +	add_timer(&vp->timer); +	return; +} + +static void vortex_tx_timeout(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int tx_status = inb(ioaddr + TxStatus); +	int intr_status = inw(ioaddr + EL3_STATUS); +	int j; + +	printk(KERN_ERR "%s: transmit timed out, tx_status %2.2x status %4.4x.\n", +		   dev->name, tx_status, intr_status); +	/* Slight code bloat to be user friendly. */ +	if ((tx_status & 0x88) == 0x88) +		printk(KERN_ERR "%s: Transmitter encountered 16 collisions --" +			   " network cable problem?\n", dev->name); +	if (intr_status & IntLatch) { +		printk(KERN_ERR "%s: Interrupt posted but not delivered --" +			   " IRQ blocked by another device?\n", dev->name); +		/* Race condition possible, but we handle a few events. */ +		vortex_interrupt(dev->irq, dev, 0); +	} + +#if ! defined(final_version) && LINUX_VERSION_CODE >= 0x10300 +	if (vp->full_bus_master_tx) { +		int i; +		printk(KERN_DEBUG "  Flags: bus-master %d full %d dirty %d " +			   "current %d restart_tx %d.\n", +			   vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx, +			   vp->restart_tx); +		printk(KERN_DEBUG "  Transmit list %8.8x vs. %p, packet ID %2.2x.\n", +			   (int)inl(ioaddr + DownListPtr), +			   &vp->tx_ring[vp->dirty_tx % TX_RING_SIZE], +			   inb(ioaddr + TxPktID)); +		for (i = 0; i < TX_RING_SIZE; i++) { +			printk(KERN_DEBUG "  %d: @%p  length %8.8x status %8.8x\n", i, +				   &vp->tx_ring[i], +				   le32_to_cpu(vp->tx_ring[i].length), +				   le32_to_cpu(vp->tx_ring[i].status)); +		} +	} +#endif +	outw(TxReset, ioaddr + EL3_CMD); +	for (j = 200; j >= 0 ; j--) +		if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +			break; + +	vp->stats.tx_errors++; + +	if (vp->full_bus_master_tx) { +		if (vp->drv_flags & HAS_V2_TX) +			outb(20, ioaddr + DownPollRate); +		if (vp->msg_level & NETIF_MSG_TX_ERR) +			printk(KERN_DEBUG "%s: Resetting the Tx ring pointer.\n", +				   dev->name); +		if (vp->cur_tx - vp->dirty_tx > 0  &&  inl(ioaddr + DownListPtr) == 0) +			outl(virt_to_bus(&vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]), +				 ioaddr + DownListPtr); +		else +			vp->restart_tx = 1; +		if (vp->drv_flags & IS_BOOMERANG) { +			/* Room for a packet, to avoid long DownStall delays. */ +			outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); +			outw(DownUnstall, ioaddr + EL3_CMD); +		} else { +			if (dev->mtu > 2033) +				outw(SetTxStart + (2000>>2), ioaddr + EL3_CMD); +		} + +		if (vp->tx_full && (vp->cur_tx - vp->dirty_tx <= TX_QUEUE_LEN - 1)) { +			vp->tx_full = 0; +			netif_unpause_tx_queue(dev); +		} +	} else { +		netif_unpause_tx_queue(dev); +		vp->stats.tx_dropped++; +	} + +	/* Issue Tx Enable */ +	outw(TxEnable, ioaddr + EL3_CMD); +	dev->trans_start = jiffies; + +	/* Switch to register set 7 for normal use. */ +	EL3WINDOW(7); +} + +/* + * Handle uncommon interrupt sources.  This is a separate routine to minimize + * the cache impact. + */ +static void +vortex_error(struct net_device *dev, int status) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int do_tx_reset = 0; +	int i; + +	if (status & TxComplete) {			/* Really "TxError" for us. */ +		unsigned char tx_status = inb(ioaddr + TxStatus); +		/* Presumably a tx-timeout. We must merely re-enable. */ +		if (vp->msg_level & NETIF_MSG_TX_ERR) +			printk(KERN_DEBUG"%s: Transmit error, Tx status register %2.2x.\n", +				   dev->name, tx_status); +		if (tx_status & 0x14)  vp->stats.tx_fifo_errors++; +		if (tx_status & 0x38)  vp->stats.tx_aborted_errors++; +		outb(0, ioaddr + TxStatus); +		if (tx_status & 0x30) +			do_tx_reset = 1; +		else {					/* Merely re-enable the transmitter. */ +			outw(TxEnable, ioaddr + EL3_CMD); +			vp->restart_tx = 1; +		} +	} +	if (status & RxEarly) {				/* Rx early is unused. */ +		vortex_rx(dev); +		outw(AckIntr | RxEarly, ioaddr + EL3_CMD); +	} +	if (status & StatsFull) {			/* Empty statistics. */ +		static int DoneDidThat = 0; +		if (vp->msg_level & NETIF_MSG_MISC) +			printk(KERN_DEBUG "%s: Updating stats.\n", dev->name); +		update_stats(ioaddr, dev); +		/* HACK: Disable statistics as an interrupt source. */ +		/* This occurs when we have the wrong media type! */ +		if (DoneDidThat == 0  && +			inw(ioaddr + EL3_STATUS) & StatsFull) { +			printk(KERN_WARNING "%s: Updating statistics failed, disabling " +				   "stats as an interrupt source.\n", dev->name); +			EL3WINDOW(5); +			outw(SetIntrEnb | (inw(ioaddr + 10) & ~StatsFull), ioaddr + EL3_CMD); +			EL3WINDOW(7); +			DoneDidThat++; +		} +	} +	if (status & IntReq) {		/* Restore all interrupt sources.  */ +		outw(vp->status_enable, ioaddr + EL3_CMD); +		outw(vp->intr_enable, ioaddr + EL3_CMD); +		vp->restore_intr_mask = 0; +	} +	if (status & HostError) { +		u16 fifo_diag; +		EL3WINDOW(4); +		fifo_diag = inw(ioaddr + Wn4_FIFODiag); +		if (vp->msg_level & NETIF_MSG_DRV) +			printk(KERN_ERR "%s: Host error, status %x, FIFO diagnostic " +				   "register %4.4x.\n", +				   dev->name, status, fifo_diag); +		/* Adapter failure requires Tx/Rx reset and reinit. */ +		if (vp->full_bus_master_tx) { +			int bus_status = inl(ioaddr + PktStatus); +			/* 0x80000000 PCI master abort. */ +			/* 0x40000000 PCI target abort. */ +			outw(TotalReset | 0xff, ioaddr + EL3_CMD); +			for (i = 2000; i >= 0 ; i--) +				if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +					break; +			if (vp->msg_level & NETIF_MSG_DRV) +				printk(KERN_ERR "%s: PCI bus error, bus status %8.8x, reset " +					   "had %d tick left.\n", +				   dev->name, bus_status, i); +			/* Re-enable the receiver. */ +			outw(RxEnable, ioaddr + EL3_CMD); +			outw(TxEnable, ioaddr + EL3_CMD); +			vp->restart_tx = 1; +		} else if (fifo_diag & 0x0400) +			do_tx_reset = 1; +		if (fifo_diag & 0x3000) { +			outw(RxReset | 7, ioaddr + EL3_CMD); +			for (i = 200000; i >= 0 ; i--) +				if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +					break; +			if ((vp->drv_flags & HAS_V2_TX) && dev->mtu > 1500) { +				EL3WINDOW(3); +				outw(dev->mtu + 14, ioaddr + Wn3_MaxPktSize); +			} +			/* Set the Rx filter to the current state. */ +			memset(vp->mc_filter, 0, sizeof vp->mc_filter); +			vp->rx_mode = 0; +			set_rx_mode(dev); +			outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ +			outw(AckIntr | HostError, ioaddr + EL3_CMD); +		} +	} +	if (do_tx_reset) { +		int j; +		outw(TxReset, ioaddr + EL3_CMD); +		for (j = 200; j >= 0 ; j--) +			if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +				break; +		outw(TxEnable, ioaddr + EL3_CMD); +		vp->restart_tx = 1; +	} + +} + + +static int +vortex_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	/* Block a timer-based transmit from overlapping.  This happens when +	   packets are presumed lost, and we use this check the Tx status. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			vortex_tx_timeout(dev); +		return 1; +	} + +	/* Put out the doubleword header... */ +	outl(skb->len, ioaddr + TX_FIFO); +	if (vp->bus_master) { +		/* Set the bus-master controller to transfer the packet. */ +		outl(virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr); +		outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); +		vp->tx_skb = skb; +		outw(StartDMADown, ioaddr + EL3_CMD); +		netif_stop_tx_queue(dev); +		/* Tx busy will be cleared at the DMADone interrupt. */ +	} else { +		/* ... and the packet rounded to a doubleword. */ +		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +		dev_free_skb(skb); +		if (inw(ioaddr + TxFree) <= 1536) { +			/* Interrupt us when the FIFO has room for max-sized packet. */ +			outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); +			netif_stop_tx_queue(dev); +		} else +			netif_unpause_tx_queue(dev);		/* Typical path */ +	} + +	dev->trans_start = jiffies; + +	/* Clear the Tx status stack. */ +	{ +		int tx_status; +		int i = 32; + +		while (--i > 0	&&	(tx_status = inb(ioaddr + TxStatus)) > 0) { +			if (tx_status & 0x3C) {		/* A Tx-disabling error occurred.  */ +				if (vp->msg_level & NETIF_MSG_TX_ERR) +				  printk(KERN_DEBUG "%s: Tx error, status %2.2x.\n", +						 dev->name, tx_status); +				if (tx_status & 0x04) vp->stats.tx_fifo_errors++; +				if (tx_status & 0x38) vp->stats.tx_aborted_errors++; +				if (tx_status & 0x30) { +					int j; +					outw(TxReset, ioaddr + EL3_CMD); +					for (j = 200; j >= 0 ; j--) +						if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +							break; +				} +				outw(TxEnable, ioaddr + EL3_CMD); +				vp->restart_tx = 1; +			} +			outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ +		} +	} +	return 0; +} + +static int +boomerang_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int entry; +	struct boom_tx_desc *prev_entry; +	unsigned long flags; +	int i; + +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			vortex_tx_timeout(dev); +		return 1; +	} + +	/* Calculate the next Tx descriptor entry. */ +	entry = vp->cur_tx % TX_RING_SIZE; +	prev_entry = &vp->tx_ring[(vp->cur_tx-1) % TX_RING_SIZE]; + +	if (vp->msg_level & NETIF_MSG_TX_QUEUED) +		printk(KERN_DEBUG "%s: Queuing Tx packet, index %d.\n", +			   dev->name, vp->cur_tx); +	/* Impossible error. */ +	if (vp->tx_full) { +		printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n", +			   dev->name); +		return 1; +	} +	vp->tx_skbuff[entry] = skb; +	vp->tx_ring[entry].next = 0; +	vp->tx_ring[entry].addr = virt_to_le32desc(skb->data); +	vp->tx_ring[entry].length = cpu_to_le32(skb->len | LAST_FRAG); +	if (vp->capabilities & CapNoTxLength) +		vp->tx_ring[entry].status = +			cpu_to_le32(TxNoRoundup | TxIntrUploaded | (entry << 2)); +	else +		vp->tx_ring[entry].status = cpu_to_le32(skb->len | TxIntrUploaded); + +	if (vp->drv_flags & IS_BOOMERANG) { +		save_flags(flags); +		cli(); +		outw(DownStall, ioaddr + EL3_CMD); +		/* Wait for the stall to complete. */ +		for (i = 600; i >= 0 ; i--) +			if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) +				break; +		vp->tx_desc_tail->next = virt_to_le32desc(&vp->tx_ring[entry]); +		vp->tx_desc_tail = &vp->tx_ring[entry]; +		if (inl(ioaddr + DownListPtr) == 0) { +			outl(virt_to_bus(&vp->tx_ring[entry]), ioaddr + DownListPtr); +			queued_packet++; +		} +		outw(DownUnstall, ioaddr + EL3_CMD); +		restore_flags(flags); +	} else { +		vp->tx_desc_tail->next = virt_to_le32desc(&vp->tx_ring[entry]); +		vp->tx_desc_tail = &vp->tx_ring[entry]; +		if (vp->restart_tx) { +			outl(virt_to_bus(vp->tx_desc_tail), ioaddr + DownListPtr); +			vp->restart_tx = 0; +			queued_packet++; +		} +	} +	vp->cur_tx++; +	if (vp->cur_tx - vp->dirty_tx >= TX_QUEUE_LEN) { +		vp->tx_full = 1; +		/* Check for a just-cleared queue. */ +		if (vp->cur_tx - (volatile unsigned int)vp->dirty_tx +			< TX_QUEUE_LEN - 2) { +			vp->tx_full = 0; +			netif_unpause_tx_queue(dev); +		} else +			netif_stop_tx_queue(dev); +	} else {					/* Clear previous interrupt enable. */ +#if defined(tx_interrupt_mitigation) +		prev_entry->status &= cpu_to_le32(~TxIntrUploaded); +#endif +		netif_unpause_tx_queue(dev);		/* Typical path */ +	} +	dev->trans_start = jiffies; +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +	struct net_device *dev = dev_id; +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr; +	int latency, status; +	int work_done = vp->max_interrupt_work; + +	ioaddr = dev->base_addr; +	latency = inb(ioaddr + Timer); +	status = inw(ioaddr + EL3_STATUS); + +	if (status == 0xffff) +		goto handler_exit; +	if (vp->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: interrupt, status %4.4x, latency %d ticks.\n", +			   dev->name, status, latency); +	do { +		if (vp->msg_level & NETIF_MSG_INTR) +				printk(KERN_DEBUG "%s: In interrupt loop, status %4.4x.\n", +					   dev->name, status); +		if (status & RxComplete) +			vortex_rx(dev); +		if (status & UpComplete) { +			outw(AckIntr | UpComplete, ioaddr + EL3_CMD); +			boomerang_rx(dev); +		} + +		if (status & TxAvailable) { +			if (vp->msg_level & NETIF_MSG_TX_DONE) +				printk(KERN_DEBUG "	TX room bit was handled.\n"); +			/* There's room in the FIFO for a full-sized packet. */ +			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); +			netif_resume_tx_queue(dev); +		} + +		if (status & DownComplete) { +			unsigned int dirty_tx = vp->dirty_tx; + +			outw(AckIntr | DownComplete, ioaddr + EL3_CMD); +			while (vp->cur_tx - dirty_tx > 0) { +				int entry = dirty_tx % TX_RING_SIZE; +				int tx_status = le32_to_cpu(vp->tx_ring[entry].status); +				if (vp->capabilities & CapNoTxLength) { +					if ( ! (tx_status & TxDownComplete)) +						break; +				} else if (inl(ioaddr + DownListPtr) == +						   virt_to_bus(&vp->tx_ring[entry])) +					break;			/* It still hasn't been processed. */ +				if (vp->msg_level & NETIF_MSG_TX_DONE) +					printk(KERN_DEBUG "%s: Transmit done, Tx status %8.8x.\n", +						   dev->name, tx_status); +				if (vp->tx_skbuff[entry]) { +					dev_free_skb_irq(vp->tx_skbuff[entry]); +					vp->tx_skbuff[entry] = 0; +				} +				/* vp->stats.tx_packets++;  Counted below. */ +				dirty_tx++; +			} +			vp->dirty_tx = dirty_tx; +			/* 4 entry hysteresis before marking the queue non-full. */ +			if (vp->tx_full && (vp->cur_tx - dirty_tx < TX_QUEUE_LEN - 4)) { +				vp->tx_full = 0; +				netif_resume_tx_queue(dev); +			} +		} +		if (status & DMADone) { +			if (inw(ioaddr + Wn7_MasterStatus) & 0x1000) { +				outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ +				/* Release the transfered buffer */ +				dev_free_skb_irq(vp->tx_skb); +				if (inw(ioaddr + TxFree) > 1536) { +					netif_resume_tx_queue(dev); +				} else /* Interrupt when FIFO has room for max-sized packet. */ +					outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); +			} +		} +		/* Check for all uncommon interrupts at once. */ +		if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) { +			if (status == 0xffff) +				break; +			vortex_error(dev, status); +		} + +		if (--work_done < 0) { +			if ((status & (0x7fe - (UpComplete | DownComplete))) == 0) { +				/* Just ack these and return. */ +				outw(AckIntr | UpComplete | DownComplete, ioaddr + EL3_CMD); +			} else { +				printk(KERN_WARNING "%s: Too much work in interrupt, status " +					   "%4.4x.  Temporarily disabling functions (%4.4x).\n", +					   dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); +				/* Disable all pending interrupts. */ +				outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); +				outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); +				/* The timer will reenable interrupts. */ +				vp->restore_intr_mask = 1; +				break; +			} +		} +		/* Acknowledge the IRQ. */ +		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); +		if (vp->cb_fn_base)			/* The PCMCIA people are idiots.  */ +			writel(0x8000, vp->cb_fn_base + 4); + +	} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); + +	if (vp->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n", +			   dev->name, status); +handler_exit: +	return; +} + +static int vortex_rx(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; +	short rx_status; + +	if (vp->msg_level & NETIF_MSG_RX_STATUS) +		printk(KERN_DEBUG"   In rx_packet(), status %4.4x, rx_status %4.4x.\n", +			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); +	while ((rx_status = inw(ioaddr + RxStatus)) > 0) { +		if (rx_status & 0x4000) { /* Error, update stats. */ +			unsigned char rx_error = inb(ioaddr + RxErrors); +			if (vp->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG " Rx error: status %2.2x.\n", rx_error); +			vp->stats.rx_errors++; +			if (rx_error & 0x01)  vp->stats.rx_over_errors++; +			if (rx_error & 0x02)  vp->stats.rx_length_errors++; +			if (rx_error & 0x04)  vp->stats.rx_frame_errors++; +			if (rx_error & 0x08)  vp->stats.rx_crc_errors++; +			if (rx_error & 0x10)  vp->stats.rx_length_errors++; +		} else { +			/* The packet length: up to 4.5K!. */ +			int pkt_len = rx_status & 0x1fff; +			struct sk_buff *skb; + +			skb = dev_alloc_skb(pkt_len + 5); +			if (vp->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n", +					   pkt_len, rx_status); +			if (skb != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +				/* 'skb_put()' points to the start of sk_buff data area. */ +				if (vp->bus_master && +					! (inw(ioaddr + Wn7_MasterStatus) & 0x8000)) { +					outl(virt_to_bus(skb_put(skb, pkt_len)), +						 ioaddr + Wn7_MasterAddr); +					outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); +					outw(StartDMAUp, ioaddr + EL3_CMD); +					while (inw(ioaddr + Wn7_MasterStatus) & 0x8000) +						; +				} else { +					insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len), +						 (pkt_len + 3) >> 2); +				} +				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ +				skb->protocol = eth_type_trans(skb, dev); +				netif_rx(skb); +				dev->last_rx = jiffies; +				vp->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +				vp->stats.rx_bytes += pkt_len; +#endif +				/* Wait a limited time to go to next packet. */ +				for (i = 200; i >= 0; i--) +					if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +						break; +				continue; +			} else if (vp->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_NOTICE "%s: No memory to allocate a sk_buff of " +					   "size %d.\n", dev->name, pkt_len); +		} +		outw(RxDiscard, ioaddr + EL3_CMD); +		vp->stats.rx_dropped++; +		/* Wait a limited time to skip this packet. */ +		for (i = 200; i >= 0; i--) +			if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) +				break; +	} + +	return 0; +} + +static int +boomerang_rx(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	int entry = vp->cur_rx % RX_RING_SIZE; +	long ioaddr = dev->base_addr; +	int rx_status; +	int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx; + +	if (vp->msg_level & NETIF_MSG_RX_STATUS) +		printk(KERN_DEBUG "  In boomerang_rx(), status %4.4x, rx_status " +			   "%8.8x.\n", +			   inw(ioaddr+EL3_STATUS), (int)inl(ioaddr+UpPktStatus)); +	while ((rx_status = le32_to_cpu(vp->rx_ring[entry].status)) & RxDComplete){ +		if (--rx_work_limit < 0) +			break; +		if (rx_status & RxDError) { /* Error, update stats. */ +			unsigned char rx_error = rx_status >> 16; +			if (vp->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG " Rx error: status %2.2x.\n", rx_error); +			vp->stats.rx_errors++; +			if (rx_error & 0x02)  vp->stats.rx_length_errors++; +			if (rx_error & 0x10)  vp->stats.rx_length_errors++; +			if (rx_error & 0x04)  vp->stats.rx_frame_errors++; +			if (rx_error & 0x08)  vp->stats.rx_crc_errors++; +			if (rx_error & 0x01) { +				vp->stats.rx_over_errors++; +				if (vp->drv_flags & HAS_V2_TX) { +					int cur_rx_thresh = inb(ioaddr + RxPriorityThresh); +					if (cur_rx_thresh < 0x20) +						outb(cur_rx_thresh + 1, ioaddr + RxPriorityThresh); +					else +						printk(KERN_WARNING "%s: Excessive PCI latency causing" +							   " packet corruption.\n", dev->name); +				} +			} +		} else { +			/* The packet length: up to 4.5K!. */ +			int pkt_len = rx_status & 0x1fff; +			struct sk_buff *skb; + +			if (vp->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n", +					   pkt_len, rx_status); + +			/* Check if the packet is long enough to just accept without +			   copying to a properly sized skbuff. */ +			if (pkt_len < vp->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != 0) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +				/* 'skb_put()' points to the start of sk_buff data area. */ +				memcpy(skb_put(skb, pkt_len), +					   le32desc_to_virt(vp->rx_ring[entry].addr), pkt_len); +				rx_copy++; +			} else { +				void *temp; +				/* Pass up the skbuff already on the Rx ring. */ +				skb = vp->rx_skbuff[entry]; +				vp->rx_skbuff[entry] = NULL; +				temp = skb_put(skb, pkt_len); +				/* Remove this checking code for final release. */ +				if (le32desc_to_virt(vp->rx_ring[entry].addr) != temp) +					printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match" +						   " in boomerang_rx: %p vs. %p.\n", dev->name, +						   bus_to_virt(le32_to_cpu(vp->rx_ring[entry].addr)), +						   temp); +				rx_nocopy++; +			} +			skb->protocol = eth_type_trans(skb, dev); +			{					/* Use hardware checksum info. */ +				int csum_bits = rx_status & 0xee000000; +				if (csum_bits && +					(csum_bits == (IPChksumValid | TCPChksumValid) || +					 csum_bits == (IPChksumValid | UDPChksumValid))) { +					skb->ip_summed = CHECKSUM_UNNECESSARY; +					rx_csumhits++; +				} +			} +			netif_rx(skb); +			dev->last_rx = jiffies; +			vp->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			vp->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++vp->cur_rx) % RX_RING_SIZE; +	} +	/* Refill the Rx ring buffers. */ +	for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) { +		struct sk_buff *skb; +		entry = vp->dirty_rx % RX_RING_SIZE; +		if (vp->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(vp->rx_buf_sz); +			if (skb == NULL) +				break;			/* Bad news!  */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			vp->rx_ring[entry].addr = virt_to_le32desc(skb->tail); +			vp->rx_skbuff[entry] = skb; +		} +		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */ +		outw(UpUnstall, ioaddr + EL3_CMD); +	} +	return 0; +} + +static void +vortex_down(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	/* Turn off statistics ASAP.  We update vp->stats below. */ +	outw(StatsDisable, ioaddr + EL3_CMD); + +	/* Disable the receiver and transmitter. */ +	outw(RxDisable, ioaddr + EL3_CMD); +	outw(TxDisable, ioaddr + EL3_CMD); + +	if (dev->if_port == XCVR_10base2) +		/* Turn off thinnet power.  Green! */ +		outw(StopCoax, ioaddr + EL3_CMD); + +	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); + +	update_stats(ioaddr, dev); +	if (vp->full_bus_master_rx) +		outl(0, ioaddr + UpListPtr); +	if (vp->full_bus_master_tx) +		outl(0, ioaddr + DownListPtr); +} + +static int +vortex_close(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	netif_stop_tx_queue(dev); + +	if (vp->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG"%s: vortex_close() status %4.4x, Tx status %2.2x.\n", +			   dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus)); +		printk(KERN_DEBUG "%s: vortex close stats: rx_nocopy %d rx_copy %d" +			   " tx_queued %d Rx pre-checksummed %d.\n", +			   dev->name, rx_nocopy, rx_copy, queued_packet, rx_csumhits); +	} + +	del_timer(&vp->timer); +	vortex_down(dev); +	free_irq(dev->irq, dev); +	outw(TotalReset | 0x34, ioaddr + EL3_CMD); + +	if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ +		for (i = 0; i < RX_RING_SIZE; i++) +			if (vp->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +				vp->rx_skbuff[i]->free = 1; +#endif +				dev_free_skb(vp->rx_skbuff[i]); +				vp->rx_skbuff[i] = 0; +			} +	} +	if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ +		for (i = 0; i < TX_RING_SIZE; i++) +			if (vp->tx_skbuff[i]) { +				dev_free_skb(vp->tx_skbuff[i]); +				vp->tx_skbuff[i] = 0; +			} +	} + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static struct net_device_stats *vortex_get_stats(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	unsigned long flags; + +	if (netif_running(dev)) { +		save_flags(flags); +		cli(); +		update_stats(dev->base_addr, dev); +		restore_flags(flags); +	} +	return &vp->stats; +} + +/*  Update statistics. +	Unlike with the EL3 we need not worry about interrupts changing +	the window setting from underneath us, but we must still guard +	against a race condition with a StatsUpdate interrupt updating the +	table.  This is done by checking that the ASM (!) code generated uses +	atomic updates with '+='. +	*/ +static void update_stats(long ioaddr, struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	int old_window = inw(ioaddr + EL3_CMD); + +	if (old_window == 0xffff)	/* Chip suspended or ejected. */ +		return; +	/* Unlike the 3c5x9 we need not turn off stats updates while reading. */ +	/* Switch to the stats window, and read everything. */ +	EL3WINDOW(6); +	vp->stats.tx_carrier_errors		+= inb(ioaddr + 0); +	vp->stats.tx_heartbeat_errors	+= inb(ioaddr + 1); +	/* Multiple collisions. */		inb(ioaddr + 2); +	vp->stats.collisions			+= inb(ioaddr + 3); +	vp->stats.tx_window_errors		+= inb(ioaddr + 4); +	vp->stats.rx_fifo_errors		+= inb(ioaddr + 5); +	vp->stats.tx_packets			+= inb(ioaddr + 6); +	vp->stats.tx_packets			+= (inb(ioaddr + 9)&0x30) << 4; +	/* Rx packets	*/				inb(ioaddr + 7);   /* Must read to clear */ +	/* Tx deferrals */				inb(ioaddr + 8); +	/* Don't bother with register 9, an extension of registers 6&7. +	   If we do use the 6&7 values the atomic update assumption above +	   is invalid. */ +	/* Rx Bytes is unreliable */	inw(ioaddr + 10); +#if LINUX_VERSION_CODE > 0x020119 +	vp->stats.tx_bytes += inw(ioaddr + 12); +#else +	inw(ioaddr + 10); +	inw(ioaddr + 12); +#endif +	/* New: On the Vortex we must also clear the BadSSD counter. */ +	EL3WINDOW(4); +	inb(ioaddr + 12); + +	/* We change back to window 7 (not 1) with the Vortex. */ +	EL3WINDOW(old_window >> 13); +	return; +} + +static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; +	int phy = vp->phys[0]; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = phy; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		if (data[0] == 32) {	/* Emulate MII for 3c59*, 3c900. */ +			data[3] = 0; +			switch (data[1]) { +			case 0: +				if (dev->if_port == XCVR_100baseTx) data[3] |= 0x2000; +				if (vp->full_duplex) data[3] |= 0x0100; +				break; +			case 1: +				if (vp->available_media & 0x02) data[3] |= 0x6000; +				if (vp->available_media & 0x08) data[3] |= 0x1800; +				spin_lock(&vp->window_lock); +				EL3WINDOW(4); +				if (inw(ioaddr + Wn4_Media) & Media_LnkBeat) data[3] |= 0x0004; +				spin_unlock(&vp->window_lock); +				break; +			case 2: data[3] = 0x0280; break;	/* OUI 00:a0:24 */ +			case 3: data[3] = 0x9000; break; +			default: break; +			} +			return 0; +		} +		spin_lock(&vp->window_lock); +		EL3WINDOW(4); +		data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); +		spin_unlock(&vp->window_lock); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == vp->phys[0]) { +			u16 value = data[2]; +			if (vp->phys[0] == 32) { +				if (data[1] == 0) { +					vp->media_override = (value & 0x2000) ? +						XCVR_100baseTx : XCVR_10baseT; +					vp->full_duplex = (value & 0x0100) ? 1 : 0; +					vp->medialock = 1; +				} +				return 0; +			} +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				vp->medialock = (value & 0x9000) ? 0 : 1; +				if (vp->medialock) +					vp->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: vp->advertising = value; break; +			} +			/* Perhaps check_duplex(dev), depending on chip semantics. */ +		} +		spin_lock(&vp->window_lock); +		EL3WINDOW(4); +		mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		spin_unlock(&vp->window_lock); +		return 0; +	case SIOCGPARAMS: +		data32[0] = vp->msg_level; +		data32[1] = vp->multicast_filter_limit; +		data32[2] = vp->max_interrupt_work; +		data32[3] = vp->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		vp->msg_level = data32[0]; +		vp->multicast_filter_limit = data32[1]; +		vp->max_interrupt_work = data32[2]; +		vp->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ +	int crc = -1; + +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 0; bit < 8; bit++, current_octet >>= 1) +			crc = (crc << 1) ^ +				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); +	} +	return crc; +} + +/* Pre-Cyclone chips have no documented multicast filter, so the only +   multicast setting is to receive all multicast frames.  Cyclone and later +   chips have a write-only table of unknown size. +   At least the chip has a very clean way to set the other filter modes. */ +static void set_rx_mode(struct net_device *dev) +{ +	struct vortex_private *vp = (void *)dev->priv; +	long ioaddr = dev->base_addr; +	int new_mode; + +	if (dev->flags & IFF_PROMISC) { +		/* Unconditionally log a net tap. */ +		printk(KERN_NOTICE "%s: Setting promiscuous mode.\n", dev->name); +		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm; +	} else if (dev->flags & IFF_ALLMULTI) { +		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast; +	} else if ((vp->drv_flags & HAS_V2_TX) && +			   dev->mc_count < vp->multicast_filter_limit) { +		struct dev_mc_list *mclist; +		int i; +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			int filter_bit = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0xff; +			if (test_bit(filter_bit, vp->mc_filter)) +				continue; +			outw(SetFilterBit | 0x0400 | filter_bit, ioaddr + EL3_CMD); +			set_bit(filter_bit, vp->mc_filter); +		} + +		new_mode = SetRxFilter|RxStation|RxMulticastHash|RxBroadcast; +	} else if (dev->mc_count) { +		new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast; +	} else +		new_mode = SetRxFilter | RxStation | RxBroadcast; + +	if (vp->rx_mode != new_mode) { +		vp->rx_mode = new_mode; +		outw(new_mode, ioaddr + EL3_CMD); +	} +} + + +/* MII transceiver control section. +   Read and write the MII registers using software-generated serial +   MDIO protocol.  See the MII specifications or DP83840A data sheet +   for details. */ + +/* The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually +   met by back-to-back PCI I/O cycles, but we insert a delay to avoid +   "overclocking" issues. */ +#define mdio_delay() inl(mdio_addr) + +#define MDIO_SHIFT_CLK	0x01 +#define MDIO_DIR_WRITE	0x04 +#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE) +#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE) +#define MDIO_DATA_READ	0x02 +#define MDIO_ENB_IN		0x00 + +/* Generate the preamble required for initial synchronization and +   a few older transceivers. */ +static void mdio_sync(long ioaddr, int bits) +{ +	long mdio_addr = ioaddr + Wn4_PhysicalMgmt; + +	/* Establish sync by sending at least 32 logic ones. */ +	while (-- bits >= 0) { +		outw(MDIO_DATA_WRITE1, mdio_addr); +		mdio_delay(); +		outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +} + +static int mdio_read(long ioaddr, int phy_id, int location) +{ +	int i; +	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; +	unsigned int retval = 0; +	long mdio_addr = ioaddr + Wn4_PhysicalMgmt; + +	if (mii_preamble_required) +		mdio_sync(ioaddr, 32); + +	/* Shift the read command bits out. */ +	for (i = 14; i >= 0; i--) { +		int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; +		outw(dataval, mdio_addr); +		mdio_delay(); +		outw(dataval | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	/* Read the two transition and 16 data bits. */ +	for (i = 18; i > 0; i--) { +		outw(MDIO_ENB_IN, mdio_addr); +		mdio_delay(); +		retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); +		outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	return retval & 0x10000 ? 0xffff : retval & 0xffff; +} + +static void mdio_write(long ioaddr, int phy_id, int location, int value) +{ +	int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; +	long mdio_addr = ioaddr + Wn4_PhysicalMgmt; +	int i; + +	if (mii_preamble_required) +		mdio_sync(ioaddr, 32); + +	/* Shift the command bits out. */ +	for (i = 31; i >= 0; i--) { +		int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; +		outw(dataval, mdio_addr); +		mdio_delay(); +		outw(dataval | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	/* Leave the interface idle. */ +	mdio_sync(ioaddr, 32); + +	return; +} + +#if ! defined(NO_PCI) +/* ACPI: Advanced Configuration and Power Interface. */ +/* Set Wake-On-LAN mode and put the board into D3 (power-down) state. */ +static void acpi_set_WOL(struct net_device *dev) +{ +	struct vortex_private *vp = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	/* Power up on: 1==Downloaded Filter, 2==Magic Packets, 4==Link Status. */ +	EL3WINDOW(7); +	outw(2, ioaddr + 0x0c); +	/* The RxFilter must accept the WOL frames. */ +	outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); +	outw(RxEnable, ioaddr + EL3_CMD); +	/* Change the power state to D3; RxEnable doesn't take effect. */ +	pci_write_config_word(vp->pci_dev, 0xe0, 0x8103); +} +#endif + +static int pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct vortex_private *np = (struct vortex_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		vortex_down(dev); +		netif_stop_tx_queue(dev); +		if (np->capabilities & CapPwrMgmt) +			acpi_set_WOL(dev); +		break; +	case DRV_RESUME: +		/* This is incomplete: the actions are very chip specific. */ +		activate_xcvr(dev); +		set_media_type(dev); +		start_operation(dev); +		np->rx_mode = 0; +		set_rx_mode(dev); +		start_operation1(dev); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_vortex_dev; *devp; devp = next) { +			next = &((struct vortex_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	case DRV_PWR_WakeOn: +		if ( ! (np->capabilities & CapPwrMgmt)) +			return -1; +		EL3WINDOW(7); +		/* Power up on: 1=Downloaded Filter, 2=Magic Packets, 4=Link Status.*/ +		outw(2, ioaddr + 12); +		/* This RxEnable doesn't take effect if we immediately change to D3. */ +		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); +		outw(RxEnable, ioaddr + EL3_CMD); +		acpi_set_pwr_state(np->pci_dev, ACPI_D3); +		break; +	} +	return 0; +} + + +#ifdef MODULE +void cleanup_module(void) +{ +	struct net_device *next_dev; + +#ifdef CARDBUS +	unregister_driver(&vortex_ops); +#elif	! defined(NO_PCI) +	pci_drv_unregister(&vortex_drv_id); +#endif + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_vortex_dev) { +		struct vortex_private *vp=(void *)(root_vortex_dev->priv); +		unregister_netdev(root_vortex_dev); +		outw(TotalReset | 0x14, root_vortex_dev->base_addr + EL3_CMD); +		if (vp->capabilities & CapPwrMgmt) +			acpi_set_WOL(root_vortex_dev); +#ifdef USE_MEM_OPS +		iounmap((char *)root_vortex_dev->base_addr); +#else +		release_region(root_vortex_dev->base_addr, +					   pci_tbl[vp->chip_id].io_size); +#endif +		next_dev = vp->next_module; +		if (vp->priv_addr) +			kfree(vp->priv_addr); +		kfree(root_vortex_dev); +		root_vortex_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` 3c59x.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c 3c59x.c" + *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c575_cb.o -I/usr/src/pcmcia/include/" + *  eisa-only-compile: "gcc -DNO_PCI -DMODULE -O6 -c 3c59x.c -o 3c597.o" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/8390.c b/linux/src/drivers/net/8390.c new file mode 100644 index 0000000..747ccb0 --- /dev/null +++ b/linux/src/drivers/net/8390.c @@ -0,0 +1,829 @@ +/* 8390.c: A general NS8390 ethernet driver core for linux. */ +/* +	Written 1992-94 by Donald Becker. +   +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 +   +  This is the chip-specific code for many 8390-based ethernet adaptors. +  This is not a complete driver, it must be combined with board-specific +  code such as ne.c, wd.c, 3c503.c, etc. + +  Seeing how at least eight drivers use this code, (not counting the +  PCMCIA ones either) it is easy to break some card by what seems like +  a simple innocent change. Please contact me or Donald if you think +  you have found something that needs changing. -- PG + + +  Changelog: + +  Paul Gortmaker	: remove set_bit lock, other cleanups. +  Paul Gortmaker	: add ei_get_8390_hdr() so we can pass skb's to  +			  ei_block_input() for eth_io_copy_and_sum(). +  Paul Gortmaker	: exchange static int ei_pingpong for a #define, +			  also add better Tx error handling. +  Paul Gortmaker	: rewrite Rx overrun handling as per NS specs. + + +  Sources: +  The National Semiconductor LAN Databook, and the 3Com 3c503 databook. + +  */ + +static const char *version = +    "8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/in.h> +#include <linux/interrupt.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include "8390.h" + +/* These are the operational function interfaces to board-specific +   routines. +	void reset_8390(struct device *dev) +		Resets the board associated with DEV, including a hardware reset of +		the 8390.  This is only called when there is a transmit timeout, and +		it is always followed by 8390_init(). +	void block_output(struct device *dev, int count, const unsigned char *buf, +					  int start_page) +		Write the COUNT bytes of BUF to the packet buffer at START_PAGE.  The +		"page" value uses the 8390's 256-byte pages. +	void get_8390_hdr(struct device *dev, struct e8390_hdr *hdr, int ring_page) +		Read the 4 byte, page aligned 8390 header. *If* there is a +		subsequent read, it will be of the rest of the packet. +	void block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +		Read COUNT bytes from the packet buffer into the skb data area. Start  +		reading from RING_OFFSET, the address as the 8390 sees it.  This will always +		follow the read of the 8390 header.  +*/ +#define ei_reset_8390 (ei_local->reset_8390) +#define ei_block_output (ei_local->block_output) +#define ei_block_input (ei_local->block_input) +#define ei_get_8390_hdr (ei_local->get_8390_hdr) + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifdef EI_DEBUG +int ei_debug = EI_DEBUG; +#else +int ei_debug = 1; +#endif + +/* Index to functions. */ +static void ei_tx_intr(struct device *dev); +static void ei_tx_err(struct device *dev); +static void ei_receive(struct device *dev); +static void ei_rx_overrun(struct device *dev); + +/* Routines generic to NS8390-based boards. */ +static void NS8390_trigger_send(struct device *dev, unsigned int length, +								int start_page); +static void set_multicast_list(struct device *dev); + + +/* Open/initialize the board.  This routine goes all-out, setting everything +   up anew at each open, even though many of these registers should only +   need to be set once at boot. +   */ +int ei_open(struct device *dev) +{ +    struct ei_device *ei_local = (struct ei_device *) dev->priv; + +    /* This can't happen unless somebody forgot to call ethdev_init(). */ +    if (ei_local == NULL) { +	printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name); +	return -ENXIO; +    } +     +    irq2dev_map[dev->irq] = dev; +    NS8390_init(dev, 1); +    dev->start = 1; +    ei_local->irqlock = 0; +    return 0; +} + +/* Opposite of above. Only used when "ifconfig <devname> down" is done. */ +int ei_close(struct device *dev) +{ +    NS8390_init(dev, 0); +    dev->start = 0; +    return 0; +} + +static int ei_start_xmit(struct sk_buff *skb, struct device *dev) +{ +    int e8390_base = dev->base_addr; +    struct ei_device *ei_local = (struct ei_device *) dev->priv; +    int length, send_length, output_page; + +/* + *  We normally shouldn't be called if dev->tbusy is set, but the + *  existing code does anyway. If it has been too long since the + *  last Tx, we assume the board has died and kick it. + */ +  +    if (dev->tbusy) {	/* Do timeouts, just like the 8003 driver. */ +		int txsr = inb(e8390_base+EN0_TSR), isr; +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < TX_TIMEOUT ||	(tickssofar < (TX_TIMEOUT+5) && ! (txsr & ENTSR_PTX))) { +			return 1; +		} +		isr = inb(e8390_base+EN0_ISR); +		if (dev->start == 0) { +			printk("%s: xmit on stopped card\n", dev->name); +			return 1; +		} + +		/* +		 * Note that if the Tx posted a TX_ERR interrupt, then the +		 * error will have been handled from the interrupt handler. +		 * and not here. +		 */ + +		printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n", +		   dev->name, (txsr & ENTSR_ABT) ? "excess collisions." : +		   (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar); + +		if (!isr && !ei_local->stat.tx_packets) { +		   /* The 8390 probably hasn't gotten on the cable yet. */ +		   ei_local->interface_num ^= 1;   /* Try a different xcvr.  */ +		} + +		/* Try to restart the card.  Perhaps the user has fixed something. */ +		ei_reset_8390(dev); +		NS8390_init(dev, 1); +		dev->trans_start = jiffies; +    } +     +    /* Sending a NULL skb means some higher layer thinks we've missed an +       tx-done interrupt. Caution: dev_tint() handles the cli()/sti() +       itself. */ +    if (skb == NULL) { +		dev_tint(dev); +		return 0; +    } +     +    length = skb->len; +    if (skb->len <= 0) +		return 0; + +    /* Mask interrupts from the ethercard. */ +    outb_p(0x00, e8390_base + EN0_IMR); +    if (dev->interrupt) { +	printk("%s: Tx request while isr active.\n",dev->name); +	outb_p(ENISR_ALL, e8390_base + EN0_IMR); +	return 1; +    } +    ei_local->irqlock = 1; + +    send_length = ETH_ZLEN < length ? length : ETH_ZLEN; + +#ifdef EI_PINGPONG + +    /* +     * We have two Tx slots available for use. Find the first free +     * slot, and then perform some sanity checks. With two Tx bufs, +     * you get very close to transmitting back-to-back packets. With +     * only one Tx buf, the transmitter sits idle while you reload the +     * card, leaving a substantial gap between each transmitted packet. +     */ + +    if (ei_local->tx1 == 0) { +	output_page = ei_local->tx_start_page; +	ei_local->tx1 = send_length; +	if (ei_debug  &&  ei_local->tx2 > 0) +		printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n", +			dev->name, ei_local->tx2, ei_local->lasttx, ei_local->txing); +    } else if (ei_local->tx2 == 0) { +	output_page = ei_local->tx_start_page + TX_1X_PAGES; +	ei_local->tx2 = send_length; +	if (ei_debug  &&  ei_local->tx1 > 0) +		printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n", +			dev->name, ei_local->tx1, ei_local->lasttx, ei_local->txing); +    } else {	/* We should never get here. */ +	if (ei_debug) +		printk("%s: No Tx buffers free! irq=%d tx1=%d tx2=%d last=%d\n", +			dev->name, dev->interrupt, ei_local->tx1, ei_local->tx2, ei_local->lasttx); +	ei_local->irqlock = 0; +	dev->tbusy = 1; +	outb_p(ENISR_ALL, e8390_base + EN0_IMR); +	return 1; +    } + +    /* +     * Okay, now upload the packet and trigger a send if the transmitter +     * isn't already sending. If it is busy, the interrupt handler will +     * trigger the send later, upon receiving a Tx done interrupt. +     */ + +    ei_block_output(dev, length, skb->data, output_page); +    if (! ei_local->txing) { +	ei_local->txing = 1; +	NS8390_trigger_send(dev, send_length, output_page); +	dev->trans_start = jiffies; +	if (output_page == ei_local->tx_start_page) { +		ei_local->tx1 = -1; +		ei_local->lasttx = -1; +	} else { +		ei_local->tx2 = -1; +		ei_local->lasttx = -2; +	} +    } else +	ei_local->txqueue++; + +    dev->tbusy = (ei_local->tx1  &&  ei_local->tx2); + +#else	/* EI_PINGPONG */ + +    /* +     * Only one Tx buffer in use. You need two Tx bufs to come close to +     * back-to-back transmits. Expect a 20 -> 25% performance hit on +     * reasonable hardware if you only use one Tx buffer. +     */ + +    ei_block_output(dev, length, skb->data, ei_local->tx_start_page); +    ei_local->txing = 1; +    NS8390_trigger_send(dev, send_length, ei_local->tx_start_page); +    dev->trans_start = jiffies; +    dev->tbusy = 1; + +#endif	/* EI_PINGPONG */ + +    /* Turn 8390 interrupts back on. */ +    ei_local->irqlock = 0; +    outb_p(ENISR_ALL, e8390_base + EN0_IMR); + +    dev_kfree_skb (skb, FREE_WRITE); +     +    return 0; +} + +/* The typical workload of the driver: +   Handle the ether interface interrupts. */ +void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +    struct device *dev = (struct device *)(irq2dev_map[irq]); +    int e8390_base; +    int interrupts, nr_serviced = 0; +    struct ei_device *ei_local; +     +    if (dev == NULL) { +		printk ("net_interrupt(): irq %d for unknown device.\n", irq); +		return; +    } +    e8390_base = dev->base_addr; +    ei_local = (struct ei_device *) dev->priv; +    if (dev->interrupt || ei_local->irqlock) { +		/* The "irqlock" check is only for testing. */ +		printk(ei_local->irqlock +			   ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n" +			   : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n", +			   dev->name, inb_p(e8390_base + EN0_ISR), +			   inb_p(e8390_base + EN0_IMR)); +		return; +    } +     +    dev->interrupt = 1; +     +    /* Change to page 0 and read the intr status reg. */ +    outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); +    if (ei_debug > 3) +		printk("%s: interrupt(isr=%#2.2x).\n", dev->name, +			   inb_p(e8390_base + EN0_ISR)); +     +    /* !!Assumption!! -- we stay in page 0.	 Don't break this. */ +    while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 +		   && ++nr_serviced < MAX_SERVICE) { +		if (dev->start == 0) { +			printk("%s: interrupt from stopped card\n", dev->name); +			interrupts = 0; +			break; +		} +		if (interrupts & ENISR_OVER) { +			ei_rx_overrun(dev); +		} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) { +			/* Got a good (?) packet. */ +			ei_receive(dev); +		} +		/* Push the next to-transmit packet through. */ +		if (interrupts & ENISR_TX) { +			ei_tx_intr(dev); +		} else if (interrupts & ENISR_TX_ERR) { +			ei_tx_err(dev); +		} + +		if (interrupts & ENISR_COUNTERS) { +			ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0); +			ei_local->stat.rx_crc_errors   += inb_p(e8390_base + EN0_COUNTER1); +			ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2); +			outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */ +		} +		 +		/* Ignore any RDC interrupts that make it back to here. */ +		if (interrupts & ENISR_RDC) { +			outb_p(ENISR_RDC, e8390_base + EN0_ISR); +		} + +		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD); +    } +     +    if (interrupts && ei_debug) { +		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD); +		if (nr_serviced >= MAX_SERVICE) { +			printk("%s: Too much work at interrupt, status %#2.2x\n", +				   dev->name, interrupts); +			outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */ +		} else { +			printk("%s: unknown interrupt %#2x\n", dev->name, interrupts); +			outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */ +		} +    } +    dev->interrupt = 0; +    return; +} + +/* + * A transmitter error has happened. Most likely excess collisions (which + * is a fairly normal condition). If the error is one where the Tx will + * have been aborted, we try and send another one right away, instead of + * letting the failed packet sit and collect dust in the Tx buffer. This + * is a much better solution as it avoids kernel based Tx timeouts, and + * an unnecessary card reset. + */ + +static void ei_tx_err(struct device *dev) +{ +    int e8390_base = dev->base_addr; +    unsigned char txsr = inb_p(e8390_base+EN0_TSR); +    unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU); +    struct ei_device *ei_local = (struct ei_device *) dev->priv; + +#ifdef VERBOSE_ERROR_DUMP +    printk(KERN_DEBUG "%s: transmitter error (%#2x): ", dev->name, txsr); +    if (txsr & ENTSR_ABT) +		printk("excess-collisions "); +    if (txsr & ENTSR_ND) +		printk("non-deferral "); +    if (txsr & ENTSR_CRS) +		printk("lost-carrier "); +    if (txsr & ENTSR_FU) +		printk("FIFO-underrun "); +    if (txsr & ENTSR_CDH) +		printk("lost-heartbeat "); +    printk("\n"); +#endif + +    outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */ + +    if (tx_was_aborted) +		ei_tx_intr(dev); + +    /* +     * Note: NCR reads zero on 16 collisions so we add them +     * in by hand. Somebody might care... +     */ +    if (txsr & ENTSR_ABT) +	ei_local->stat.collisions += 16; +	 +} + +/* We have finished a transmit: check for errors and then trigger the next +   packet to be sent. */ +static void ei_tx_intr(struct device *dev) +{ +    int e8390_base = dev->base_addr; +    int status = inb(e8390_base + EN0_TSR); +    struct ei_device *ei_local = (struct ei_device *) dev->priv; +     +    outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */ + +#ifdef EI_PINGPONG + +    /* +     * There are two Tx buffers, see which one finished, and trigger +     * the send of another one if it exists. +     */ +    ei_local->txqueue--; +    if (ei_local->tx1 < 0) { +	if (ei_local->lasttx != 1 && ei_local->lasttx != -1) +		printk("%s: bogus last_tx_buffer %d, tx1=%d.\n", +			   ei_local->name, ei_local->lasttx, ei_local->tx1); +	ei_local->tx1 = 0; +	dev->tbusy = 0; +	if (ei_local->tx2 > 0) { +		ei_local->txing = 1; +		NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); +		dev->trans_start = jiffies; +		ei_local->tx2 = -1, +		ei_local->lasttx = 2; +	} else +		ei_local->lasttx = 20, ei_local->txing = 0; +    } else if (ei_local->tx2 < 0) { +	if (ei_local->lasttx != 2  &&  ei_local->lasttx != -2) +		printk("%s: bogus last_tx_buffer %d, tx2=%d.\n", +			   ei_local->name, ei_local->lasttx, ei_local->tx2); +	ei_local->tx2 = 0; +	dev->tbusy = 0; +	if (ei_local->tx1 > 0) { +		ei_local->txing = 1; +		NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); +		dev->trans_start = jiffies; +		ei_local->tx1 = -1; +		ei_local->lasttx = 1; +	} else +		ei_local->lasttx = 10, ei_local->txing = 0; +    } else +	printk("%s: unexpected TX-done interrupt, lasttx=%d.\n", +		   dev->name, ei_local->lasttx); + +#else	/* EI_PINGPONG */ +    /* +     *  Single Tx buffer: mark it free so another packet can be loaded. +     */ +    ei_local->txing = 0; +    dev->tbusy = 0; +#endif + +    /* Minimize Tx latency: update the statistics after we restart TXing. */ +    if (status & ENTSR_COL) +	ei_local->stat.collisions++; +    if (status & ENTSR_PTX) +	ei_local->stat.tx_packets++; +    else { +	ei_local->stat.tx_errors++; +	if (status & ENTSR_ABT) ei_local->stat.tx_aborted_errors++; +	if (status & ENTSR_CRS) ei_local->stat.tx_carrier_errors++; +	if (status & ENTSR_FU)  ei_local->stat.tx_fifo_errors++; +	if (status & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++; +	if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++; +    } + +    mark_bh (NET_BH); +} + +/* We have a good packet(s), get it/them out of the buffers. */ + +static void ei_receive(struct device *dev) +{ +    int e8390_base = dev->base_addr; +    struct ei_device *ei_local = (struct ei_device *) dev->priv; +    unsigned char rxing_page, this_frame, next_frame; +    unsigned short current_offset; +    int rx_pkt_count = 0; +    struct e8390_pkt_hdr rx_frame; +    int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page; +     +    while (++rx_pkt_count < 10) { +		int pkt_len; +		 +		/* Get the rx page (incoming packet pointer). */ +		outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD); +		rxing_page = inb_p(e8390_base + EN1_CURPAG); +		outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); +		 +		/* Remove one frame from the ring.  Boundary is always a page behind. */ +		this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1; +		if (this_frame >= ei_local->stop_page) +			this_frame = ei_local->rx_start_page; +		 +		/* Someday we'll omit the previous, iff we never get this message. +		   (There is at least one clone claimed to have a problem.)  */ +		if (ei_debug > 0  &&  this_frame != ei_local->current_page) +			printk("%s: mismatched read page pointers %2x vs %2x.\n", +				   dev->name, this_frame, ei_local->current_page); +		 +		if (this_frame == rxing_page)	/* Read all the frames? */ +			break;				/* Done for now */ +		 +		current_offset = this_frame << 8; +		ei_get_8390_hdr(dev, &rx_frame, this_frame); +		 +		pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr); +		 +		next_frame = this_frame + 1 + ((pkt_len+4)>>8); +		 +		/* Check for bogosity warned by 3c503 book: the status byte is never +		   written.  This happened a lot during testing! This code should be +		   cleaned up someday. */ +		if (rx_frame.next != next_frame +			&& rx_frame.next != next_frame + 1 +			&& rx_frame.next != next_frame - num_rx_pages +			&& rx_frame.next != next_frame + 1 - num_rx_pages) { +			ei_local->current_page = rxing_page; +			outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY); +			ei_local->stat.rx_errors++; +			continue; +		} + +		if (pkt_len < 60  ||  pkt_len > 1518) { +			if (ei_debug) +				printk("%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n", +					   dev->name, rx_frame.count, rx_frame.status, +					   rx_frame.next); +			ei_local->stat.rx_errors++; +		} else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) { +			struct sk_buff *skb; +			 +			skb = dev_alloc_skb(pkt_len+2); +			if (skb == NULL) { +				if (ei_debug > 1) +					printk("%s: Couldn't allocate a sk_buff of size %d.\n", +						   dev->name, pkt_len); +				ei_local->stat.rx_dropped++; +				break; +			} else { +				skb_reserve(skb,2);	/* IP headers on 16 byte boundaries */ +				skb->dev = dev; +				skb_put(skb, pkt_len);	/* Make room */ +				ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); +				skb->protocol=eth_type_trans(skb,dev); +				netif_rx(skb); +				ei_local->stat.rx_packets++; +			} +		} else { +			int errs = rx_frame.status; +			if (ei_debug) +				printk("%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n", +					   dev->name, rx_frame.status, rx_frame.next, +					   rx_frame.count); +			if (errs & ENRSR_FO) +				ei_local->stat.rx_fifo_errors++; +		} +		next_frame = rx_frame.next; +		 +		/* This _should_ never happen: it's here for avoiding bad clones. */ +		if (next_frame >= ei_local->stop_page) { +			printk("%s: next frame inconsistency, %#2x\n", dev->name, +				   next_frame); +			next_frame = ei_local->rx_start_page; +		} +		ei_local->current_page = next_frame; +		outb_p(next_frame-1, e8390_base+EN0_BOUNDARY); +    } + +    /* We used to also ack ENISR_OVER here, but that would sometimes mask +    a real overrun, leaving the 8390 in a stopped state with rec'vr off. */ +    outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR); +    return; +} + +/*  + * We have a receiver overrun: we have to kick the 8390 to get it started + * again. Problem is that you have to kick it exactly as NS prescribes in + * the updated datasheets, or "the NIC may act in an unpredictable manner." + * This includes causing "the NIC to defer indefinitely when it is stopped + * on a busy network."  Ugh. + */ +static void ei_rx_overrun(struct device *dev) +{ +    int e8390_base = dev->base_addr; +    unsigned long wait_start_time; +    unsigned char was_txing, must_resend = 0; +    struct ei_device *ei_local = (struct ei_device *) dev->priv; +     +    /* +     * Record whether a Tx was in progress and then issue the +     * stop command. +     */ +    was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS; +    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); +     +    if (ei_debug > 1) +	printk("%s: Receiver overrun.\n", dev->name); +    ei_local->stat.rx_over_errors++; +     +    /*  +     * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total. +     * Early datasheets said to poll the reset bit, but now they say that +     * it "is not a reliable indicator and subsequently should be ignored." +     * We wait at least 10ms. +     */ +    wait_start_time = jiffies; +    while (jiffies - wait_start_time <= 1*HZ/100) +	barrier(); + +    /* +     * Reset RBCR[01] back to zero as per magic incantation. +     */ +    outb_p(0x00, e8390_base+EN0_RCNTLO); +    outb_p(0x00, e8390_base+EN0_RCNTHI); + +    /* +     * See if any Tx was interrupted or not. According to NS, this +     * step is vital, and skipping it will cause no end of havoc. +     */ +    if (was_txing) {  +	unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR); +	if (!tx_completed) must_resend = 1; +    } + +    /* +     * Have to enter loopback mode and then restart the NIC before +     * you are allowed to slurp packets up off the ring. +     */ +    outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); +    outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD); + +    /* +     * Clear the Rx ring of all the debris, and ack the interrupt. +     */ +    ei_receive(dev); +    outb_p(ENISR_OVER, e8390_base+EN0_ISR); + +    /* +     * Leave loopback mode, and resend any packet that got stopped. +     */ +    outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR);  +    if (must_resend) +    	outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD); +	 +} + +static struct enet_statistics *get_stats(struct device *dev) +{ +    short ioaddr = dev->base_addr; +    struct ei_device *ei_local = (struct ei_device *) dev->priv; +     +    /* If the card is stopped, just return the present stats. */ +    if (dev->start == 0) return &ei_local->stat; + +    /* Read the counter registers, assuming we are in page 0. */ +    ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0); +    ei_local->stat.rx_crc_errors   += inb_p(ioaddr + EN0_COUNTER1); +    ei_local->stat.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2); +     +    return &ei_local->stat; +} + +/* + *	Set or clear the multicast filter for this adaptor. + */ +  +static void set_multicast_list(struct device *dev) +{ +	short ioaddr = dev->base_addr; +     +	if(dev->flags&IFF_PROMISC) +	{ +		outb_p(E8390_RXCONFIG | 0x18, ioaddr + EN0_RXCR); +	} +	else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) +	{ +		/* The multicast-accept list is initialized to accept-all, and we +		   rely on higher-level filtering for now. */ +		outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR); +	}  +	else +		outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR); +} + +/* Initialize the rest of the 8390 device structure. */ +int ethdev_init(struct device *dev) +{ +    if (ei_debug > 1) +		printk("%s", version); +     +    if (dev->priv == NULL) { +		dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL); +		if (dev->priv == NULL) +			return -ENOMEM; +		memset(dev->priv, 0, sizeof(struct ei_device)); +    } +     +    dev->hard_start_xmit = &ei_start_xmit; +    dev->get_stats	= get_stats; +    dev->set_multicast_list = &set_multicast_list; + +    ether_setup(dev); +         +    return 0; +} + + +/* This page of functions should be 8390 generic */ +/* Follow National Semi's recommendations for initializing the "NIC". */ +void NS8390_init(struct device *dev, int startp) +{ +    int e8390_base = dev->base_addr; +    struct ei_device *ei_local = (struct ei_device *) dev->priv; +    int i; +    int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48; +    unsigned long flags; +     +    /* Follow National Semi's recommendations for initing the DP83902. */ +    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */ +    outb_p(endcfg, e8390_base + EN0_DCFG);	/* 0x48 or 0x49 */ +    /* Clear the remote byte count registers. */ +    outb_p(0x00,  e8390_base + EN0_RCNTLO); +    outb_p(0x00,  e8390_base + EN0_RCNTHI); +    /* Set to monitor and loopback mode -- this is vital!. */ +    outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */ +    outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */ +    /* Set the transmit page and receive ring. */ +    outb_p(ei_local->tx_start_page,	 e8390_base + EN0_TPSR); +    ei_local->tx1 = ei_local->tx2 = 0; +    outb_p(ei_local->rx_start_page,	 e8390_base + EN0_STARTPG); +    outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/ +    ei_local->current_page = ei_local->rx_start_page;		/* assert boundary+1 */ +    outb_p(ei_local->stop_page,	  e8390_base + EN0_STOPPG); +    /* Clear the pending interrupts and mask. */ +    outb_p(0xFF, e8390_base + EN0_ISR); +    outb_p(0x00,  e8390_base + EN0_IMR); +     +    /* Copy the station address into the DS8390 registers, +       and set the multicast hash bitmap to receive all multicasts. */ +    save_flags(flags); +    cli(); +    outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */ +    for(i = 0; i < 6; i++) { +		outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i); +    } +    /* Initialize the multicast list to accept-all.  If we enable multicast +       the higher levels can do the filtering. */ +    for(i = 0; i < 8; i++) +		outb_p(0xff, e8390_base + EN1_MULT + i); +     +    outb_p(ei_local->rx_start_page,	 e8390_base + EN1_CURPAG); +    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); +    restore_flags(flags); +    dev->tbusy = 0; +    dev->interrupt = 0; +    ei_local->tx1 = ei_local->tx2 = 0; +    ei_local->txing = 0; +    if (startp) { +		outb_p(0xff,  e8390_base + EN0_ISR); +		outb_p(ENISR_ALL,  e8390_base + EN0_IMR); +		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base); +		outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */ +		/* 3c503 TechMan says rxconfig only after the NIC is started. */ +		outb_p(E8390_RXCONFIG,	e8390_base + EN0_RXCR); /* rx on,  */ +		dev->set_multicast_list(dev);		/* Get the multicast status right if this +							   was a reset. */ +    } +    return; +} + +/* Trigger a transmit start, assuming the length is valid. */ +static void NS8390_trigger_send(struct device *dev, unsigned int length, +								int start_page) +{ +    int e8390_base = dev->base_addr; +     +    outb_p(E8390_NODMA+E8390_PAGE0, e8390_base); +     +    if (inb_p(e8390_base) & E8390_TRANS) { +		printk("%s: trigger_send() called with the transmitter busy.\n", +			   dev->name); +		return; +    } +    outb_p(length & 0xff, e8390_base + EN0_TCNTLO); +    outb_p(length >> 8, e8390_base + EN0_TCNTHI); +    outb_p(start_page, e8390_base + EN0_TPSR); +    outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base); +    return; +} + +#ifdef MODULE + +int init_module(void) +{ +     return 0; +} + +void +cleanup_module(void) +{ +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 8390.c" + *  version-control: t + *  kept-new-versions: 5 + *  c-indent-level: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/8390.h b/linux/src/drivers/net/8390.h new file mode 100644 index 0000000..9cc0ddc --- /dev/null +++ b/linux/src/drivers/net/8390.h @@ -0,0 +1,175 @@ +/* Generic NS8390 register definitions. */ +/* This file is part of Donald Becker's 8390 drivers, and is distributed +   under the same license. +   Some of these names and comments originated from the Crynwr +   packet drivers, which are distributed under the GPL. */ + +#ifndef _8390_h +#define _8390_h + +#include <linux/if_ether.h> +#include <linux/ioport.h> +#include <linux/skbuff.h> + +#define TX_2X_PAGES 12 +#define TX_1X_PAGES 6 + +/* Should always use two Tx slots to get back-to-back transmits. */ +#define EI_PINGPONG + +#ifdef EI_PINGPONG +#define TX_PAGES TX_2X_PAGES +#else +#define TX_PAGES TX_1X_PAGES +#endif + +#define ETHER_ADDR_LEN 6 + +/* The 8390 specific per-packet-header format. */ +struct e8390_pkt_hdr { +  unsigned char status; /* status */ +  unsigned char next;   /* pointer to next packet. */ +  unsigned short count; /* header + packet length in bytes */ +}; + +/* From 8390.c */ +extern int ei_debug; +extern struct sigaction ei_sigaction; + +extern int ethif_init(struct device *dev); +extern int ethdev_init(struct device *dev); +extern void NS8390_init(struct device *dev, int startp); +extern int ei_open(struct device *dev); +extern int ei_close(struct device *dev); +extern void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +#ifndef HAVE_AUTOIRQ +/* From auto_irq.c */ +extern struct device *irq2dev_map[16]; +extern int autoirq_setup(int waittime); +extern int autoirq_report(int waittime); +#endif + +/* Most of these entries should be in 'struct device' (or most of the +   things in there should be here!) */ +/* You have one of these per-board */ +struct ei_device { +  const char *name; +  void (*reset_8390)(struct device *); +  void (*get_8390_hdr)(struct device *, struct e8390_pkt_hdr *, int); +  void (*block_output)(struct device *, int, const unsigned char *, int); +  void (*block_input)(struct device *, int, struct sk_buff *, int); +  unsigned open:1; +  unsigned word16:1;  /* We have the 16-bit (vs 8-bit) version of the card. */ +  unsigned txing:1;		/* Transmit Active */ +  unsigned irqlock:1;		/* 8390's intrs disabled when '1'. */ +  unsigned dmaing:1;		/* Remote DMA Active */ +  unsigned char tx_start_page, rx_start_page, stop_page; +  unsigned char current_page;	/* Read pointer in buffer  */ +  unsigned char interface_num;	/* Net port (AUI, 10bT.) to use. */ +  unsigned char txqueue;	/* Tx Packet buffer queue length. */ +  short tx1, tx2;		/* Packet lengths for ping-pong tx. */ +  short lasttx;			/* Alpha version consistency check. */ +  unsigned char reg0;		/* Register '0' in a WD8013 */ +  unsigned char reg5;		/* Register '5' in a WD8013 */ +  unsigned char saved_irq;	/* Original dev->irq value. */ +  /* The new statistics table. */ +  struct enet_statistics stat; +}; + +/* The maximum number of 8390 interrupt service routines called per IRQ. */ +#define MAX_SERVICE 12 + +/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */ +#define TX_TIMEOUT (20*HZ/100) + +#define ei_status (*(struct ei_device *)(dev->priv)) + +/* Some generic ethernet register configurations. */ +#define E8390_TX_IRQ_MASK 0xa	/* For register EN0_ISR */ +#define E8390_RX_IRQ_MASK  0x5 +#define E8390_RXCONFIG 0x4	/* EN0_RXCR: broadcasts, no multicast,errors */ +#define E8390_RXOFF 0x20	/* EN0_RXCR: Accept no packets */ +#define E8390_TXCONFIG 0x00	/* EN0_TXCR: Normal transmit mode */ +#define E8390_TXOFF 0x02	/* EN0_TXCR: Transmitter off */ + +/*  Register accessed at EN_CMD, the 8390 base addr.  */ +#define E8390_STOP	0x01	/* Stop and reset the chip */ +#define E8390_START	0x02	/* Start the chip, clear reset */ +#define E8390_TRANS	0x04	/* Transmit a frame */ +#define E8390_RREAD	0x08	/* Remote read */ +#define E8390_RWRITE	0x10	/* Remote write  */ +#define E8390_NODMA	0x20	/* Remote DMA */ +#define E8390_PAGE0	0x00	/* Select page chip registers */ +#define E8390_PAGE1	0x40	/* using the two high-order bits */ +#define E8390_PAGE2	0x80	/* Page 3 is invalid. */ + +#define E8390_CMD	0x00	/* The command register (for all pages) */ +/* Page 0 register offsets. */ +#define EN0_CLDALO	0x01	/* Low byte of current local dma addr  RD */ +#define EN0_STARTPG	0x01	/* Starting page of ring bfr WR */ +#define EN0_CLDAHI	0x02	/* High byte of current local dma addr  RD */ +#define EN0_STOPPG	0x02	/* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY	0x03	/* Boundary page of ring bfr RD WR */ +#define EN0_TSR		0x04	/* Transmit status reg RD */ +#define EN0_TPSR	0x04	/* Transmit starting page WR */ +#define EN0_NCR		0x05	/* Number of collision reg RD */ +#define EN0_TCNTLO	0x05	/* Low  byte of tx byte count WR */ +#define EN0_FIFO	0x06	/* FIFO RD */ +#define EN0_TCNTHI	0x06	/* High byte of tx byte count WR */ +#define EN0_ISR		0x07	/* Interrupt status reg RD WR */ +#define EN0_CRDALO	0x08	/* low byte of current remote dma address RD */ +#define EN0_RSARLO	0x08	/* Remote start address reg 0 */ +#define EN0_CRDAHI	0x09	/* high byte, current remote dma address RD */ +#define EN0_RSARHI	0x09	/* Remote start address reg 1 */ +#define EN0_RCNTLO	0x0a	/* Remote byte count reg WR */ +#define EN0_RCNTHI	0x0b	/* Remote byte count reg WR */ +#define EN0_RSR		0x0c	/* rx status reg RD */ +#define EN0_RXCR	0x0c	/* RX configuration reg WR */ +#define EN0_TXCR	0x0d	/* TX configuration reg WR */ +#define EN0_COUNTER0	0x0d	/* Rcv alignment error counter RD */ +#define EN0_DCFG	0x0e	/* Data configuration reg WR */ +#define EN0_COUNTER1	0x0e	/* Rcv CRC error counter RD */ +#define EN0_IMR		0x0f	/* Interrupt mask reg WR */ +#define EN0_COUNTER2	0x0f	/* Rcv missed frame error counter RD */ + +/* Bits in EN0_ISR - Interrupt status register */ +#define ENISR_RX	0x01	/* Receiver, no error */ +#define ENISR_TX	0x02	/* Transmitter, no error */ +#define ENISR_RX_ERR	0x04	/* Receiver, with error */ +#define ENISR_TX_ERR	0x08	/* Transmitter, with error */ +#define ENISR_OVER	0x10	/* Receiver overwrote the ring */ +#define ENISR_COUNTERS	0x20	/* Counters need emptying */ +#define ENISR_RDC	0x40	/* remote dma complete */ +#define ENISR_RESET	0x80	/* Reset completed */ +#define ENISR_ALL	0x3f	/* Interrupts we will enable */ + +/* Bits in EN0_DCFG - Data config register */ +#define ENDCFG_WTS	0x01	/* word transfer mode selection */ + +/* Page 1 register offsets. */ +#define EN1_PHYS   0x01	/* This board's physical enet addr RD WR */ +#define EN1_CURPAG 0x07	/* Current memory page RD WR */ +#define EN1_MULT   0x08	/* Multicast filter mask array (8 bytes) RD WR */ + +/* Bits in received packet status byte and EN0_RSR*/ +#define ENRSR_RXOK	0x01	/* Received a good packet */ +#define ENRSR_CRC	0x02	/* CRC error */ +#define ENRSR_FAE	0x04	/* frame alignment error */ +#define ENRSR_FO	0x08	/* FIFO overrun */ +#define ENRSR_MPA	0x10	/* missed pkt */ +#define ENRSR_PHY	0x20	/* physical/multicase address */ +#define ENRSR_DIS	0x40	/* receiver disable. set in monitor mode */ +#define ENRSR_DEF	0x80	/* deferring */ + +/* Transmitted packet status, EN0_TSR. */ +#define ENTSR_PTX 0x01	/* Packet transmitted without error */ +#define ENTSR_ND  0x02	/* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04	/* The transmit collided at least once. */ +#define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10	/* The carrier sense was lost. */ +#define ENTSR_FU  0x20  /* A "FIFO underrun" occurred during transmit. */ +#define ENTSR_CDH 0x40	/* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80  /* There was an out-of-window collision. */ + +#endif /* _8390_h */ diff --git a/linux/src/drivers/net/Space.c b/linux/src/drivers/net/Space.c new file mode 100644 index 0000000..083cdeb --- /dev/null +++ b/linux/src/drivers/net/Space.c @@ -0,0 +1,541 @@ +/* + * INET		An implementation of the TCP/IP protocol suite for the LINUX + *		operating system.  INET is implemented using the  BSD Socket + *		interface as the means of communication with the user level. + * + *		Holds initial configuration information for devices. + * + * NOTE:	This file is a nice idea, but its current format does not work + *		well for drivers that support multiple units, like the SLIP + *		driver.  We should actually have only one pointer to a driver + *		here, with the driver knowing how many units it supports. + *		Currently, the SLIP driver abuses the "base_addr" integer + *		field of the 'device' structure to store the unit number... + *		-FvK + * + * Version:	@(#)Space.c	1.0.8	07/31/96 + * + * Authors:	Ross Biro, <bir7@leland.Stanford.Edu> + *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + *		Donald J. Becker, <becker@super.org> + * + *	FIXME: + *		Sort the device chain fastest first. + * + *		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. + */ +#include <linux/config.h> +#include <linux/netdevice.h> +#include <linux/errno.h> + +#define	NEXT_DEV	NULL + + +/* A unified ethernet device probe.  This is the easiest way to have every +   ethernet adaptor have the name "eth[0123...]". +   */ + +extern int tulip_probe(struct device *dev); +extern int hp100_probe(struct device *dev); +extern int ultra_probe(struct device *dev); +extern int ultra32_probe(struct device *dev); +extern int wd_probe(struct device *dev); +extern int el2_probe(struct device *dev); +extern int ne_probe(struct device *dev); +extern int ne2k_pci_probe(struct device *dev); +extern int hp_probe(struct device *dev); +extern int hp_plus_probe(struct device *dev); +extern int znet_probe(struct device *); +extern int express_probe(struct device *); +extern int eepro_probe(struct device *); +extern int el3_probe(struct device *); +extern int at1500_probe(struct device *); +extern int at1700_probe(struct device *); +extern int fmv18x_probe(struct device *); +extern int eth16i_probe(struct device *); +extern int depca_probe(struct device *); +extern int apricot_probe(struct device *); +extern int ewrk3_probe(struct device *); +extern int de4x5_probe(struct device *); +extern int el1_probe(struct device *); +extern int via_rhine_probe(struct device *); +#if	defined(CONFIG_WAVELAN) +extern int wavelan_probe(struct device *); +#endif	/* defined(CONFIG_WAVELAN) */ +extern int el16_probe(struct device *); +extern int elplus_probe(struct device *); +extern int ac3200_probe(struct device *); +extern int e2100_probe(struct device *); +extern int ni52_probe(struct device *); +extern int ni65_probe(struct device *); +extern int SK_init(struct device *); +extern int seeq8005_probe(struct device *); +extern int tc59x_probe(struct device *); +extern int dgrs_probe(struct device *); +extern int smc_init( struct device * ); +extern int sparc_lance_probe(struct device *); +extern int atarilance_probe(struct device *); +extern int a2065_probe(struct device *); +extern int ariadne_probe(struct device *); +extern int hydra_probe(struct device *); +extern int yellowfin_probe(struct device *); +extern int eepro100_probe(struct device *); +extern int epic100_probe(struct device *); +extern int rtl8139_probe(struct device *); +extern int tlan_probe(struct device *); +extern int isa515_probe(struct device *); +extern int pcnet32_probe(struct device *); +extern int lance_probe(struct device *); +/* Detachable devices ("pocket adaptors") */ +extern int atp_init(struct device *); +extern int de600_probe(struct device *); +extern int de620_probe(struct device *); +/* The shaper hook */ +extern int shaper_probe(struct device *); +/* Red Creek PCI hook */ +extern int rcpci_probe(struct device *); + +static int +ethif_probe(struct device *dev) +{ +    u_long base_addr = dev->base_addr; + +    if ((base_addr == 0xffe0)  ||  (base_addr == 1)) +	return 1;		/* ENXIO */ + +    if (1 +	/* All PCI probes are safe, and thus should be first. */ +#ifdef CONFIG_DE4X5             /* DEC DE425, DE434, DE435 adapters */ +	&& de4x5_probe(dev) +#endif +#ifdef CONFIG_DGRS +	&& dgrs_probe(dev) +#endif +#ifdef CONFIG_EEXPRESS_PRO100B	/* Intel EtherExpress Pro100B */ +	&& eepro100_probe(dev) +#endif +#ifdef CONFIG_EPIC +	&& epic100_probe(dev) +#endif +#if defined(CONFIG_HP100) +	&& hp100_probe(dev) +#endif	 +#if defined(CONFIG_NE2K_PCI) +	&& ne2k_pci_probe(dev) +#endif +#ifdef CONFIG_PCNET32 +	&& pcnet32_probe(dev) +#endif +#ifdef CONFIG_RTL8139 +	&& rtl8139_probe(dev) +#endif +#if defined(CONFIG_VIA_RHINE) +	&& via_rhine_probe(dev) +#endif +#if defined(CONFIG_VORTEX) +	&& tc59x_probe(dev) +#endif +#if defined(CONFIG_DEC_ELCP) +	&& tulip_probe(dev) +#endif +#ifdef CONFIG_YELLOWFIN +	&& yellowfin_probe(dev) +#endif +	/* Next mostly-safe EISA-only drivers. */ +#ifdef CONFIG_AC3200		/* Ansel Communications EISA 3200. */ +	&& ac3200_probe(dev) +#endif +#if defined(CONFIG_ULTRA32) +	&& ultra32_probe(dev) +#endif +	/* Third, sensitive ISA boards. */ +#ifdef CONFIG_AT1700 +	&& at1700_probe(dev) +#endif +#if defined(CONFIG_ULTRA) +	&& ultra_probe(dev) +#endif +#if defined(CONFIG_SMC9194) +	&& smc_init(dev) +#endif +#if defined(CONFIG_WD80x3) +	&& wd_probe(dev) +#endif +#if defined(CONFIG_EL2)		/* 3c503 */ +	&& el2_probe(dev) +#endif +#if defined(CONFIG_HPLAN) +	&& hp_probe(dev) +#endif +#if defined(CONFIG_HPLAN_PLUS) +	&& hp_plus_probe(dev) +#endif +#if defined(CONFIG_SEEQ8005) +	&& seeq8005_probe(dev) +#endif +#ifdef CONFIG_E2100		/* Cabletron E21xx series. */ +	&& e2100_probe(dev) +#endif +#if defined(CONFIG_NE2000) +	&& ne_probe(dev) +#endif +#ifdef CONFIG_AT1500 +	&& at1500_probe(dev) +#endif +#ifdef CONFIG_FMV18X		/* Fujitsu FMV-181/182 */ +	&& fmv18x_probe(dev) +#endif +#ifdef CONFIG_ETH16I +	&& eth16i_probe(dev)	/* ICL EtherTeam 16i/32 */ +#endif +#ifdef CONFIG_EL3		/* 3c509 */ +	&& el3_probe(dev) +#endif +#ifdef CONFIG_3C515		/* 3c515 */ +	&& tc515_probe(dev) +#endif +#ifdef CONFIG_ZNET		/* Zenith Z-Note and some IBM Thinkpads. */ +	&& znet_probe(dev) +#endif +#ifdef CONFIG_EEXPRESS		/* Intel EtherExpress */ +	&& express_probe(dev) +#endif +#ifdef CONFIG_EEXPRESS_PRO	/* Intel EtherExpress Pro/10 */ +	&& eepro_probe(dev) +#endif +#ifdef CONFIG_DEPCA		/* DEC DEPCA */ +	&& depca_probe(dev) +#endif +#ifdef CONFIG_EWRK3             /* DEC EtherWORKS 3 */ +        && ewrk3_probe(dev) +#endif +#ifdef CONFIG_APRICOT		/* Apricot I82596 */ +	&& apricot_probe(dev) +#endif +#ifdef CONFIG_EL1		/* 3c501 */ +	&& el1_probe(dev) +#endif +#if	defined(CONFIG_WAVELAN)	/* WaveLAN */ +	&& wavelan_probe(dev) +#endif	/* defined(CONFIG_WAVELAN) */ +#ifdef CONFIG_EL16		/* 3c507 */ +	&& el16_probe(dev) +#endif +#ifdef CONFIG_ELPLUS		/* 3c505 */ +	&& elplus_probe(dev) +#endif +#ifdef CONFIG_DE600		/* D-Link DE-600 adapter */ +	&& de600_probe(dev) +#endif +#ifdef CONFIG_DE620		/* D-Link DE-620 adapter */ +	&& de620_probe(dev) +#endif +#if defined(CONFIG_SK_G16) +	&& SK_init(dev) +#endif +#ifdef CONFIG_NI52 +	&& ni52_probe(dev) +#endif +#ifdef CONFIG_NI65 +	&& ni65_probe(dev) +#endif +#ifdef CONFIG_LANCE	/* ISA LANCE boards */ +	&& lance_probe(dev) +#endif +#ifdef CONFIG_ATARILANCE	/* Lance-based Atari ethernet boards */ +	&& atarilance_probe(dev) +#endif +#ifdef CONFIG_A2065		/* Commodore/Ameristar A2065 Ethernet Board */ +	&& a2065_probe(dev) +#endif +#ifdef CONFIG_ARIADNE		/* Village Tronic Ariadne Ethernet Board */ +	&& ariadne_probe(dev) +#endif +#ifdef CONFIG_HYDRA		/* Hydra Systems Amiganet Ethernet board */ +	&& hydra_probe(dev) +#endif +#ifdef CONFIG_SUNLANCE +	&& sparc_lance_probe(dev) +#endif +#ifdef CONFIG_TLAN +	&& tlan_probe(dev) +#endif +#ifdef CONFIG_LANCE +	&& lance_probe(dev) +#endif +	&& 1 ) { +	return 1;	/* -ENODEV or -EAGAIN would be more accurate. */ +    } +    return 0; +} + +#ifdef CONFIG_SDLA +    extern int sdla_init(struct device *); +    static struct device sdla0_dev = { "sdla0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, sdla_init, }; + +#   undef NEXT_DEV +#   define NEXT_DEV	(&sdla0_dev) +#endif + +/* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */ +#ifdef CONFIG_ATP		/* AT-LAN-TEC (RealTek) pocket adaptor. */ +static struct device atp_dev = { +    "atp0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, atp_init, /* ... */ }; +#   undef NEXT_DEV +#   define NEXT_DEV	(&atp_dev) +#endif + +#ifdef CONFIG_ARCNET +    extern int arcnet_probe(struct device *dev); +    static struct device arcnet_dev = { +	"arc0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, arcnet_probe, }; +#   undef	NEXT_DEV +#   define	NEXT_DEV	(&arcnet_dev) +#endif + +/* The first device defaults to I/O base '0', which means autoprobe. */ +#ifndef ETH0_ADDR +# define ETH0_ADDR 0 +#endif +#ifndef ETH0_IRQ +# define ETH0_IRQ 0 +#endif +/* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20), +   which means "don't probe".  These entries exist to only to provide empty +   slots which may be enabled at boot-time. */ + +static struct device eth7_dev = { +    "eth7", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe }; +static struct device eth6_dev = { +    "eth6", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, ð7_dev, ethif_probe }; +static struct device eth5_dev = { +    "eth5", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, ð6_dev, ethif_probe }; +static struct device eth4_dev = { +    "eth4", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, ð5_dev, ethif_probe }; +static struct device eth3_dev = { +    "eth3", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, ð4_dev, ethif_probe }; +static struct device eth2_dev = { +    "eth2", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, ð3_dev, ethif_probe }; +static struct device eth1_dev = { +    "eth1", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, ð2_dev, ethif_probe }; + +static struct device eth0_dev = { +    "eth0", 0, 0, 0, 0, ETH0_ADDR, ETH0_IRQ, 0, 0, 0, ð1_dev, ethif_probe }; + +#   undef NEXT_DEV +#   define NEXT_DEV	(ð0_dev) + +#if defined(PLIP) || defined(CONFIG_PLIP) +    extern int plip_init(struct device *); +    static struct device plip2_dev = { +	"plip2", 0, 0, 0, 0, 0x278, 2, 0, 0, 0, NEXT_DEV, plip_init, }; +    static struct device plip1_dev = { +	"plip1", 0, 0, 0, 0, 0x378, 7, 0, 0, 0, &plip2_dev, plip_init, }; +    static struct device plip0_dev = { +	"plip0", 0, 0, 0, 0, 0x3BC, 5, 0, 0, 0, &plip1_dev, plip_init, }; +#   undef NEXT_DEV +#   define NEXT_DEV	(&plip0_dev) +#endif  /* PLIP */ + +#if defined(SLIP) || defined(CONFIG_SLIP) +	/* To be exact, this node just hooks the initialization +	   routines to the device structures.			*/ +extern int slip_init_ctrl_dev(struct device *); +static struct device slip_bootstrap = { +  "slip_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, slip_init_ctrl_dev, }; +#undef NEXT_DEV +#define NEXT_DEV (&slip_bootstrap) +#endif	/* SLIP */ +   +#if defined(CONFIG_MKISS) +	/* To be exact, this node just hooks the initialization +	   routines to the device structures.			*/ +extern int mkiss_init_ctrl_dev(struct device *); +static struct device mkiss_bootstrap = { +  "mkiss_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, mkiss_init_ctrl_dev, }; +#undef NEXT_DEV +#define NEXT_DEV (&mkiss_bootstrap) +#endif	/* MKISS */ +   +#if defined(CONFIG_STRIP) +extern int strip_init_ctrl_dev(struct device *); +static struct device strip_bootstrap = { +    "strip_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, strip_init_ctrl_dev, }; +#undef NEXT_DEV +#define NEXT_DEV (&strip_bootstrap) +#endif   /* STRIP */ + +#if defined(CONFIG_SHAPER) +static struct device shaper_bootstrap = { +    "shaper", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, shaper_probe, }; +#undef NEXT_DEV +#define NEXT_DEV (&shaper_bootstrap) +#endif   /* SHAPER */ + +#if defined(CONFIG_RCPCI) +static struct device rcpci_bootstrap = { +    "rcpci", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, rcpci_probe, }; +#undef NEXT_DEV +#define NEXT_DEV (&rcpci_bootstrap) +#endif   /* RCPCI */ + +#if defined(CONFIG_PPP) +extern int ppp_init(struct device *); +static struct device ppp_bootstrap = { +    "ppp_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, ppp_init, }; +#undef NEXT_DEV +#define NEXT_DEV (&ppp_bootstrap) +#endif   /* PPP */ + +#ifdef CONFIG_DUMMY +    extern int dummy_init(struct device *dev); +    static struct device dummy_dev = { +	"dummy", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, dummy_init, }; +#   undef	NEXT_DEV +#   define	NEXT_DEV	(&dummy_dev) +#endif + +#ifdef CONFIG_EQUALIZER +extern int eql_init(struct device *dev); +struct device eql_dev = { +  "eql",			/* Master device for IP traffic load  +				   balancing */ +  0x0, 0x0, 0x0, 0x0,		/* recv end/start; mem end/start */ +  0,				/* base I/O address */ +  0,				/* IRQ */ +  0, 0, 0,			/* flags */ +  NEXT_DEV,			/* next device */ +  eql_init			/* set up the rest */ +}; +#   undef       NEXT_DEV +#   define      NEXT_DEV        (&eql_dev) +#endif + +#ifdef CONFIG_IBMTR  + +    extern int tok_probe(struct device *dev); +    static struct device ibmtr_dev1 = { +	"tr1",			/* IBM Token Ring (Non-DMA) Interface */ +	0x0,			/* recv memory end			*/ +	0x0,			/* recv memory start			*/ +	0x0,			/* memory end				*/ +	0x0,			/* memory start				*/ +	0xa24,			/* base I/O address			*/ +	0,			/* IRQ					*/ +	0, 0, 0,		/* flags				*/ +	NEXT_DEV,		/* next device				*/ +	tok_probe		/* ??? Token_init should set up the rest	*/ +    }; +#   undef	NEXT_DEV +#   define	NEXT_DEV	(&ibmtr_dev1) + + +    static struct device ibmtr_dev0 = { +	"tr0",			/* IBM Token Ring (Non-DMA) Interface */ +	0x0,			/* recv memory end			*/ +	0x0,			/* recv memory start			*/ +	0x0,			/* memory end				*/ +	0x0,			/* memory start				*/ +	0xa20,			/* base I/O address			*/ +	0,			/* IRQ					*/ +	0, 0, 0,		/* flags				*/ +	NEXT_DEV,		/* next device				*/ +	tok_probe		/* ??? Token_init should set up the rest	*/ +    }; +#   undef	NEXT_DEV +#   define	NEXT_DEV	(&ibmtr_dev0) + +#endif  + +#ifdef CONFIG_DEFXX +	extern int dfx_probe(struct device *dev); +	static struct device fddi7_dev = +		{"fddi7", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, dfx_probe}; +	static struct device fddi6_dev = +		{"fddi6", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi7_dev, dfx_probe}; +	static struct device fddi5_dev = +		{"fddi5", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi6_dev, dfx_probe}; +	static struct device fddi4_dev = +		{"fddi4", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi5_dev, dfx_probe}; +	static struct device fddi3_dev = +		{"fddi3", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi4_dev, dfx_probe}; +	static struct device fddi2_dev = +		{"fddi2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi3_dev, dfx_probe}; +	static struct device fddi1_dev = +		{"fddi1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi2_dev, dfx_probe}; +	static struct device fddi0_dev = +		{"fddi0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &fddi1_dev, dfx_probe}; + +#undef	NEXT_DEV +#define	NEXT_DEV	(&fddi0_dev) +#endif  + +#ifdef CONFIG_NET_IPIP +	extern int tunnel_init(struct device *); +	 +	static struct device tunnel_dev1 =  +	{ +		"tunl1",		/* IPIP tunnel  			*/ +		0x0,			/* recv memory end			*/ +		0x0,			/* recv memory start			*/ +		0x0,			/* memory end				*/ +		0x0,			/* memory start				*/ +		0x0,			/* base I/O address			*/ +		0,			/* IRQ					*/ +		0, 0, 0,		/* flags				*/ +		NEXT_DEV,		/* next device				*/ +		tunnel_init		/* Fill in the details			*/ +	}; + +	static struct device tunnel_dev0 =  +	{ +		"tunl0",		/* IPIP tunnel  			*/ +		0x0,			/* recv memory end			*/ +		0x0,			/* recv memory start			*/ +		0x0,			/* memory end				*/ +		0x0,			/* memory start				*/ +		0x0,			/* base I/O address			*/ +		0,			/* IRQ					*/ +		0, 0, 0,		/* flags				*/ +		&tunnel_dev1,		/* next device				*/ +		tunnel_init		/* Fill in the details			*/ +    }; +#   undef	NEXT_DEV +#   define	NEXT_DEV	(&tunnel_dev0) + +#endif + +#ifdef CONFIG_APFDDI +    extern int apfddi_init(struct device *dev); +    static struct device fddi_dev = { +	"fddi", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, apfddi_init }; +#   undef       NEXT_DEV +#   define      NEXT_DEV        (&fddi_dev) +#endif + +#ifdef CONFIG_APBIF +    extern int bif_init(struct device *dev); +    static struct device bif_dev = { +        "bif", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, bif_init }; +#   undef       NEXT_DEV +#   define      NEXT_DEV        (&bif_dev) +#endif +	 +extern int loopback_init(struct device *dev); +struct device loopback_dev = { +	"lo",			/* Software Loopback interface		*/ +	0x0,			/* recv memory end			*/ +	0x0,			/* recv memory start			*/ +	0x0,			/* memory end				*/ +	0x0,			/* memory start				*/ +	0,			/* base I/O address			*/ +	0,			/* IRQ					*/ +	0, 0, 0,		/* flags				*/ +	NEXT_DEV,		/* next device				*/ +	loopback_init		/* loopback_init should set up the rest	*/ +}; + +struct device *dev_base = &loopback_dev; diff --git a/linux/src/drivers/net/ac3200.c b/linux/src/drivers/net/ac3200.c new file mode 100644 index 0000000..600949f --- /dev/null +++ b/linux/src/drivers/net/ac3200.c @@ -0,0 +1,385 @@ +/* ac3200.c: A driver for the Ansel Communications EISA ethernet adaptor. */ +/* +	Written 1993, 1994 by Donald Becker. +	Copyright 1993 United States Government as represented by the Director, +	National Security Agency.  This software may only be used and distributed +	according to the terms of the GNU Public License as modified by SRC, +	incorporated herein by reference. + +	The author may be reached as becker@cesdis.gsfc.nasa.gov, or +    C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	This is driver for the Ansel Communications Model 3200 EISA Ethernet LAN +	Adapter.  The programming information is from the users manual, as related +	by glee@ardnassak.math.clemson.edu. +  */ + +static const char *version = +	"ac3200.c:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include <asm/system.h> +#include <asm/io.h> + +#include "8390.h" + +/* Offsets from the base address. */ +#define AC_NIC_BASE		0x00 +#define AC_SA_PROM		0x16			/* The station address PROM. */ +#define  AC_ADDR0		 0x00			/* Prefix station address values. */ +#define  AC_ADDR1		 0x40			/* !!!!These are just guesses!!!! */ +#define  AC_ADDR2		 0x90 +#define AC_ID_PORT		0xC80 +#define AC_EISA_ID		 0x0110d305 +#define AC_RESET_PORT	0xC84 +#define  AC_RESET		 0x00 +#define  AC_ENABLE		 0x01 +#define AC_CONFIG		0xC90	/* The configuration port. */ + +#define AC_IO_EXTENT 0x10		/* IS THIS REALLY TRUE ??? */ +                                /* Actually accessed is: +								 * AC_NIC_BASE (0-15) +								 * AC_SA_PROM (0-5) +								 * AC_ID_PORT (0-3) +								 * AC_RESET_PORT +								 * AC_CONFIG +								 */ + +/* Decoding of the configuration register. */ +static unsigned char config2irqmap[8] = {15, 12, 11, 10, 9, 7, 5, 3}; +static int addrmap[8] = +{0xFF0000, 0xFE0000, 0xFD0000, 0xFFF0000, 0xFFE0000, 0xFFC0000,  0xD0000, 0 }; +static const char *port_name[4] = { "10baseT", "invalid", "AUI", "10base2"}; + +#define config2irq(configval)	config2irqmap[((configval) >> 3) & 7] +#define config2mem(configval)	addrmap[(configval) & 7] +#define config2name(configval)	port_name[((configval) >> 6) & 3] + +/* First and last 8390 pages. */ +#define AC_START_PG		0x00	/* First page of 8390 TX buffer */ +#define AC_STOP_PG		0x80	/* Last page +1 of the 8390 RX ring */ + +int ac3200_probe(struct device *dev); +static int ac_probe1(int ioaddr, struct device *dev); + +static int ac_open(struct device *dev); +static void ac_reset_8390(struct device *dev); +static void ac_block_input(struct device *dev, int count, +					struct sk_buff *skb, int ring_offset); +static void ac_block_output(struct device *dev, const int count, +							const unsigned char *buf, const int start_page); +static void ac_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +					int ring_page); + +static int ac_close_card(struct device *dev); + + +/*	Probe for the AC3200. + +	The AC3200 can be identified by either the EISA configuration registers, +	or the unique value in the station address PROM. +	*/ + +int ac3200_probe(struct device *dev) +{ +	unsigned short ioaddr = dev->base_addr; + +	if (ioaddr > 0x1ff)		/* Check a single specified location. */ +		return ac_probe1(ioaddr, dev); +	else if (ioaddr > 0)		/* Don't probe at all. */ +		return ENXIO; + +	/* If you have a pre 0.99pl15 machine you should delete this line. */ +	if ( ! EISA_bus) +		return ENXIO; + +	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) { +		if (check_region(ioaddr, AC_IO_EXTENT)) +			continue; +		if (ac_probe1(ioaddr, dev) == 0) +			return 0; +	} + +	return ENODEV; +} + +static int ac_probe1(int ioaddr, struct device *dev) +{ +	int i; + +#ifndef final_version +	printk("AC3200 ethercard probe at %#3x:", ioaddr); + +	for(i = 0; i < 6; i++) +		printk(" %02x", inb(ioaddr + AC_SA_PROM + i)); +#endif + +	/* !!!!The values of AC_ADDRn (see above) should be corrected when we +	   find out the correct station address prefix!!!! */ +	if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0 +		|| inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1 +		|| inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) { +#ifndef final_version +		printk(" not found (invalid prefix).\n"); +#endif +		return ENODEV; +	} + +	/* The correct probe method is to check the EISA ID. */ +	for (i = 0; i < 4; i++) +		if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) { +			printk("EISA ID mismatch, %8x vs %8x.\n", +				   inl(ioaddr + AC_ID_PORT), AC_EISA_ID);  +			return ENODEV; +		} + + +	/* We should have a "dev" from Space.c or the static module table. */ +	if (dev == NULL) { +		printk("ac3200.c: Passed a NULL device.\n"); +		dev = init_etherdev(0, 0); +	} + +	for(i = 0; i < ETHER_ADDR_LEN; i++) +		dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i); + +#ifndef final_version +	printk("\nAC3200 ethercard configuration register is %#02x," +		   " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG), +		   inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1), +		   inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3)); +#endif + +	/* Assign and allocate the interrupt now. */ +	if (dev->irq == 0) +		dev->irq = config2irq(inb(ioaddr + AC_CONFIG)); +	else if (dev->irq == 2) +		dev->irq = 9; + +	if (request_irq(dev->irq, ei_interrupt, 0, "ac3200", NULL)) { +		printk (" unable to get IRQ %d.\n", dev->irq); +		return EAGAIN; +	} + +	/* Allocate dev->priv and fill in 8390 specific dev fields. */ +	if (ethdev_init(dev)) { +		printk (" unable to allocate memory for dev->priv.\n"); +		free_irq(dev->irq, NULL); +		return -ENOMEM; +	} + +	request_region(ioaddr, AC_IO_EXTENT, "ac3200"); + +	dev->base_addr = ioaddr; + +#ifdef notyet +	if (dev->mem_start)	{		/* Override the value from the board. */ +		for (i = 0; i < 7; i++) +			if (addrmap[i] == dev->mem_start) +				break; +		if (i >= 7) +			i = 0; +		outb((inb(ioaddr + AC_CONFIG) & ~7) | i, ioaddr + AC_CONFIG); +	} +#endif + +	dev->if_port = inb(ioaddr + AC_CONFIG) >> 6; +	dev->mem_start = config2mem(inb(ioaddr + AC_CONFIG)); +	dev->rmem_start = dev->mem_start + TX_PAGES*256; +	dev->mem_end = dev->rmem_end = dev->mem_start +		+ (AC_STOP_PG - AC_START_PG)*256; + +	ei_status.name = "AC3200"; +	ei_status.tx_start_page = AC_START_PG; +	ei_status.rx_start_page = AC_START_PG + TX_PAGES; +	ei_status.stop_page = AC_STOP_PG; +	ei_status.word16 = 1; + +	printk("\n%s: AC3200 at %#x, IRQ %d, %s port, shared memory %#lx-%#lx.\n", +		   dev->name, ioaddr, dev->irq, port_name[dev->if_port], +		   dev->mem_start, dev->mem_end-1); + +	if (ei_debug > 0) +		printk("%s", version); + +	ei_status.reset_8390 = &ac_reset_8390; +	ei_status.block_input = &ac_block_input; +	ei_status.block_output = &ac_block_output; +	ei_status.get_8390_hdr = &ac_get_8390_hdr; + +	dev->open = &ac_open; +	dev->stop = &ac_close_card; +	NS8390_init(dev, 0); +	return 0; +} + +static int ac_open(struct device *dev) +{ +#ifdef notyet +	/* Someday we may enable the IRQ and shared memory here. */ +	int ioaddr = dev->base_addr; + +	if (request_irq(dev->irq, ei_interrupt, 0, "ac3200", NULL)) +		return -EAGAIN; +#endif + +	ei_open(dev); + +	MOD_INC_USE_COUNT; + +	return 0; +} + +static void ac_reset_8390(struct device *dev) +{ +	ushort ioaddr = dev->base_addr; + +	outb(AC_RESET, ioaddr + AC_RESET_PORT); +	if (ei_debug > 1) printk("resetting AC3200, t=%ld...", jiffies); + +	ei_status.txing = 0; +	outb(AC_ENABLE, ioaddr + AC_RESET_PORT); +	if (ei_debug > 1) printk("reset done\n"); + +	return; +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but +   we don't need to be concerned with ring wrap as the header will be at +   the start of a page, so we optimize accordingly. */ + +static void +ac_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ +	unsigned long hdr_start = dev->mem_start + ((ring_page - AC_START_PG)<<8); +	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); +} + +/*  Block input and output are easy on shared memory ethercards, the only +	complication is when the ring buffer wraps. */ + +static void ac_block_input(struct device *dev, int count, struct sk_buff *skb, +						  int ring_offset) +{ +	unsigned long xfer_start = dev->mem_start + ring_offset - (AC_START_PG<<8); + +	if (xfer_start + count > dev->rmem_end) { +		/* We must wrap the input move. */ +		int semi_count = dev->rmem_end - xfer_start; +		memcpy_fromio(skb->data, xfer_start, semi_count); +		count -= semi_count; +		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count); +	} else { +		/* Packet is in one chunk -- we can copy + cksum. */ +		eth_io_copy_and_sum(skb, xfer_start, count, 0); +	} +} + +static void ac_block_output(struct device *dev, int count, +							const unsigned char *buf, int start_page) +{ +	unsigned long shmem = dev->mem_start + ((start_page - AC_START_PG)<<8); + +	memcpy_toio(shmem, buf, count); +} + +static int ac_close_card(struct device *dev) +{ +	dev->start = 0; +	dev->tbusy = 1; + +	if (ei_debug > 1) +		printk("%s: Shutting down ethercard.\n", dev->name); + +#ifdef notyet +	/* We should someday disable shared memory and interrupts. */ +	outb(0x00, ioaddr + 6);	/* Disable interrupts. */ +	free_irq(dev->irq, NULL); +	irq2dev_map[dev->irq] = 0; +#endif + +	ei_close(dev); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +#ifdef MODULE +#define MAX_AC32_CARDS	4	/* Max number of AC32 cards per module */ +#define NAMELEN		8	/* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_AC32_CARDS] = { 0, }; +static struct device dev_ac32[MAX_AC32_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int io[MAX_AC32_CARDS] = { 0, }; +static int irq[MAX_AC32_CARDS]  = { 0, }; +static int mem[MAX_AC32_CARDS] = { 0, }; + +int +init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) { +		struct device *dev = &dev_ac32[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->mem_start = mem[this_dev];		/* Currently ignored by driver */ +		dev->init = ac3200_probe; +		/* Default is to only install one card. */ +		if (io[this_dev] == 0 && this_dev != 0) break; +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]); +			if (found != 0) return 0;	/* Got at least one. */ +			return -ENXIO; +		} +		found++; +	} + +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) { +		struct device *dev = &dev_ac32[this_dev]; +		if (dev->priv != NULL) { +			kfree(dev->priv); +			dev->priv = NULL; +			/* Someday free_irq + irq2dev may be in ac_close_card() */ +			free_irq(dev->irq, NULL); +			irq2dev_map[dev->irq] = NULL; +			release_region(dev->base_addr, AC_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ac3200.c" + *  version-control: t + *  kept-new-versions: 5 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/apricot.c b/linux/src/drivers/net/apricot.c new file mode 100644 index 0000000..57fccaf --- /dev/null +++ b/linux/src/drivers/net/apricot.c @@ -0,0 +1,1046 @@ +/* apricot.c: An Apricot 82596 ethernet driver for linux. */ +/* +    Apricot +    	Written 1994 by Mark Evans. +	This driver is for the Apricot 82596 bus-master interface + +        Modularised 12/94 Mark Evans +     +    Driver skeleton  +	Written 1993 by Donald Becker. +	Copyright 1993 United States Government as represented by the Director, +	National Security Agency.  This software may only be used and distributed +	according to the terms of the GNU Public License as modified by SRC, +	incorporated herein by reference. + +	The author may be reached as becker@super.org or +	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 +     + +*/ + +static const char *version = "apricot.c:v0.2 05/12/94\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#ifndef HAVE_PORTRESERVE +#define check_region(addr, size)	0 +#define request_region(addr, size,name)	do ; while(0) +#endif + +#ifndef HAVE_ALLOC_SKB +#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority) +#define kfree_skbmem(buff, size) kfree_s(buff,size) +#endif + +#define APRICOT_DEBUG 1 + +#ifdef APRICOT_DEBUG +int i596_debug = APRICOT_DEBUG; +#else +int i596_debug = 1; +#endif + +#define APRICOT_TOTAL_SIZE 17 + +#define I596_NULL -1 + +#define CMD_EOL		0x8000	/* The last command of the list, stop. */ +#define CMD_SUSP	0x4000	/* Suspend after doing cmd. */ +#define CMD_INTR	0x2000	/* Interrupt after doing cmd. */ + +#define CMD_FLEX	0x0008	/* Enable flexible memory model */ + +enum commands { +	CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3, +	CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7}; + +#define STAT_C		0x8000	/* Set to 0 after execution */ +#define STAT_B		0x4000	/* Command being executed */ +#define STAT_OK		0x2000	/* Command executed ok */ +#define STAT_A		0x1000	/* Command aborted */ + +#define	 CUC_START	0x0100 +#define	 CUC_RESUME	0x0200 +#define	 CUC_SUSPEND    0x0300 +#define	 CUC_ABORT	0x0400 +#define	 RX_START	0x0010 +#define	 RX_RESUME	0x0020 +#define	 RX_SUSPEND	0x0030 +#define	 RX_ABORT	0x0040 + +struct i596_cmd { +    unsigned short status; +    unsigned short command; +    struct i596_cmd *next; +}; + +#define EOF		0x8000 +#define SIZE_MASK	0x3fff + +struct i596_tbd { +    unsigned short size; +    unsigned short pad; +    struct i596_tbd *next; +    unsigned char *data; +}; + +struct tx_cmd { +    struct i596_cmd cmd; +    struct i596_tbd *tbd; +    unsigned short size; +    unsigned short pad; +}; + +struct i596_rfd { +    unsigned short stat; +    unsigned short cmd; +    struct i596_rfd *next; +    long rbd;  +    unsigned short count; +    unsigned short size; +    unsigned char data[1532]; +}; + +#define RX_RING_SIZE 8 + +struct i596_scb { +    unsigned short status; +    unsigned short command; +    struct i596_cmd *cmd; +    struct i596_rfd *rfd; +    unsigned long crc_err; +    unsigned long align_err; +    unsigned long resource_err; +    unsigned long over_err; +    unsigned long rcvdt_err; +    unsigned long short_err; +    unsigned short t_on; +    unsigned short t_off; +}; + +struct i596_iscp { +    unsigned long stat; +    struct i596_scb *scb; +}; + +struct i596_scp { +    unsigned long sysbus; +    unsigned long pad; +    struct i596_iscp *iscp; +}; + +struct i596_private { +    volatile struct i596_scp scp; +    volatile struct i596_iscp iscp; +    volatile struct i596_scb scb; +    volatile struct i596_cmd set_add; +    char eth_addr[8]; +    volatile struct i596_cmd set_conf; +    char i596_config[16]; +    volatile struct i596_cmd tdr; +    unsigned long stat; +    int last_restart; +    struct i596_rfd *rx_tail; +    struct i596_cmd *cmd_tail; +    struct i596_cmd *cmd_head; +    int cmd_backlog; +    unsigned long last_cmd; +    struct enet_statistics stats; +}; + +char init_setup[] = { +	0x8E,	/* length, prefetch on */ +	0xC8,	/* fifo to 8, monitor off */ +	0x80,	/* don't save bad frames */ +	0x2E,	/* No source address insertion, 8 byte preamble */ +	0x00,	/* priority and backoff defaults */ +	0x60,	/* interframe spacing */ +	0x00,	/* slot time LSB */ +	0xf2,	/* slot time and retries */ +	0x00,	/* promiscuous mode */ +	0x00,	/* collision detect */ +	0x40,	/* minimum frame length */ +	0xff,	 +	0x00, +	0x7f	/*  *multi IA */ }; + +static int i596_open(struct device *dev); +static int i596_start_xmit(struct sk_buff *skb, struct device *dev); +static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int i596_close(struct device *dev); +static struct enet_statistics *i596_get_stats(struct device *dev); +static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd); +static void print_eth(unsigned char *); +static void set_multicast_list(struct device *dev); + + +static inline int +init_rx_bufs(struct device *dev, int num) +{ +    struct i596_private *lp = (struct i596_private *)dev->priv; +    int i; +    struct i596_rfd *rfd; + +    lp->scb.rfd = (struct i596_rfd *)I596_NULL; + +    if (i596_debug > 1) printk ("%s: init_rx_bufs %d.\n", dev->name, num); + +    for (i = 0; i < num; i++) +    { +	if (!(rfd = (struct i596_rfd *)kmalloc(sizeof(struct i596_rfd), GFP_KERNEL))) +            break; + +	rfd->stat = 0x0000; +	rfd->rbd = I596_NULL; +	rfd->count = 0; +	rfd->size = 1532; +        if (i == 0) +        { +	    rfd->cmd = CMD_EOL; +            lp->rx_tail = rfd; +        } +        else +	    rfd->cmd = 0x0000; + +        rfd->next = lp->scb.rfd; +        lp->scb.rfd = rfd; +    } + +    if (i != 0) +      lp->rx_tail->next = lp->scb.rfd; + +    return (i); +} + +static inline void +remove_rx_bufs(struct device *dev) +{ +    struct i596_private *lp = (struct i596_private *)dev->priv; +    struct i596_rfd *rfd = lp->scb.rfd; + +    lp->rx_tail->next = (struct i596_rfd *)I596_NULL; + +    do +    { +        lp->scb.rfd = rfd->next; +        kfree_s(rfd, sizeof(struct i596_rfd)); +        rfd = lp->scb.rfd; +    } +    while (rfd != lp->rx_tail); +} + +static inline void +init_i596_mem(struct device *dev) +{ +    struct i596_private *lp = (struct i596_private *)dev->priv; +    short ioaddr = dev->base_addr; +    int boguscnt = 100; + +    /* change the scp address */ +    outw(0, ioaddr); +    outw(0, ioaddr); +    outb(4, ioaddr+0xf); +    outw(((((int)&lp->scp) & 0xffff) | 2), ioaddr); +    outw((((int)&lp->scp)>>16) & 0xffff, ioaddr); + +    lp->last_cmd = jiffies; + +    lp->scp.sysbus = 0x00440000; +    lp->scp.iscp = &(lp->iscp); +    lp->iscp.scb = &(lp->scb); +    lp->iscp.stat = 0x0001; +    lp->cmd_backlog = 0; + +    lp->cmd_head = lp->scb.cmd = (struct i596_cmd *) I596_NULL; + +    if (i596_debug > 2) printk("%s: starting i82596.\n", dev->name); + +    (void) inb (ioaddr+0x10); +    outb(4, ioaddr+0xf); +    outw(0, ioaddr+4); + +    while (lp->iscp.stat) +	if (--boguscnt == 0) +	{ +	    printk("%s: i82596 initialization timed out with status %4.4x, cmd %4.4x.\n", +		   dev->name, lp->scb.status, lp->scb.command); +	    break; +    	} + +    lp->scb.command = 0; + +    memcpy (lp->i596_config, init_setup, 14); +    lp->set_conf.command = CmdConfigure; +    i596_add_cmd(dev, &lp->set_conf); + +    memcpy (lp->eth_addr, dev->dev_addr, 6); +    lp->set_add.command = CmdSASetup; +    i596_add_cmd(dev, &lp->set_add); + +    lp->tdr.command = CmdTDR; +    i596_add_cmd(dev, &lp->tdr); + +    boguscnt = 200; +    while (lp->scb.status, lp->scb.command) +	if (--boguscnt == 0) +	{ +	    printk("%s: receive unit start timed out with status %4.4x, cmd %4.4x.\n", +		   dev->name, lp->scb.status, lp->scb.command); +	    break; +    	} + +    lp->scb.command = RX_START; +    outw(0, ioaddr+4); + +    boguscnt = 200; +    while (lp->scb.status, lp->scb.command) +        if (--boguscnt == 0) +	{ +	    printk("i82596 init timed out with status %4.4x, cmd %4.4x.\n", +		lp->scb.status, lp->scb.command); +	    break; +	} + +    return; +} + +static inline int +i596_rx(struct device *dev) +{ +    struct i596_private *lp = (struct i596_private *)dev->priv; +    int frames = 0; + +    if (i596_debug > 3) printk ("i596_rx()\n"); + +    while ((lp->scb.rfd->stat) & STAT_C) +    { +        if (i596_debug >2) print_eth(lp->scb.rfd->data); + +	if ((lp->scb.rfd->stat) & STAT_OK) +	{ +	    /* a good frame */ +	    int pkt_len = lp->scb.rfd->count & 0x3fff; +	    struct sk_buff *skb = dev_alloc_skb(pkt_len); + +	    frames++; + +	    if (skb == NULL) +	    { +		printk ("%s: i596_rx Memory squeeze, dropping packet.\n", dev->name); +		lp->stats.rx_dropped++; +		break; +	    } + +  	    skb->dev = dev;		 +	    memcpy(skb_put(skb,pkt_len), lp->scb.rfd->data, pkt_len); + +	    skb->protocol=eth_type_trans(skb,dev); +	    netif_rx(skb); +	    lp->stats.rx_packets++; + +	    if (i596_debug > 4) print_eth(skb->data); +	} +	else +	{ +	    lp->stats.rx_errors++; +	    if ((lp->scb.rfd->stat) & 0x0001) lp->stats.collisions++; +	    if ((lp->scb.rfd->stat) & 0x0080) lp->stats.rx_length_errors++; +	    if ((lp->scb.rfd->stat) & 0x0100) lp->stats.rx_over_errors++; +	    if ((lp->scb.rfd->stat) & 0x0200) lp->stats.rx_fifo_errors++; +	    if ((lp->scb.rfd->stat) & 0x0400) lp->stats.rx_frame_errors++; +	    if ((lp->scb.rfd->stat) & 0x0800) lp->stats.rx_crc_errors++; +	    if ((lp->scb.rfd->stat) & 0x1000) lp->stats.rx_length_errors++; +	} + +	lp->scb.rfd->stat = 0; +	lp->rx_tail->cmd = 0; +	lp->rx_tail = lp->scb.rfd; +	lp->scb.rfd = lp->scb.rfd->next; +	lp->rx_tail->count = 0; +	lp->rx_tail->cmd = CMD_EOL; + +    } + +    if (i596_debug > 3) printk ("frames %d\n", frames); + +    return 0; +} + +static inline void +i596_cleanup_cmd(struct i596_private *lp) +{ +    struct i596_cmd *ptr; +    int boguscnt = 100; + +    if (i596_debug > 4) printk ("i596_cleanup_cmd\n"); + +    while (lp->cmd_head != (struct i596_cmd *) I596_NULL) +    { +	ptr = lp->cmd_head; + +	lp->cmd_head = lp->cmd_head->next; +	lp->cmd_backlog--; + +	switch ((ptr->command) & 0x7) +	{ +	    case CmdTx: +	    { +		struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; +		struct sk_buff *skb = ((struct sk_buff *)(tx_cmd->tbd->data)) -1; + +		dev_kfree_skb(skb, FREE_WRITE); + +		lp->stats.tx_errors++; +		lp->stats.tx_aborted_errors++; + +		ptr->next = (struct i596_cmd * ) I596_NULL; +		kfree_s((unsigned char *)tx_cmd, (sizeof (struct tx_cmd) + sizeof (struct i596_tbd))); +		break; +	    } +	    case CmdMulticastList: +	    { +		unsigned short count = *((unsigned short *) (ptr + 1)); + +		ptr->next = (struct i596_cmd * ) I596_NULL; +		kfree_s((unsigned char *)ptr, (sizeof (struct i596_cmd) + count + 2)); +		break; +	    } +	    default: +		ptr->next = (struct i596_cmd * ) I596_NULL; +	} +    } + +    while (lp->scb.status, lp->scb.command) +	if (--boguscnt == 0) +	{ +	    printk("i596_cleanup_cmd timed out with status %4.4x, cmd %4.4x.\n", +		lp->scb.status, lp->scb.command); +	    break; +    	} + +    lp->scb.cmd = lp->cmd_head; +} + +static inline void +i596_reset(struct device *dev, struct i596_private *lp, int ioaddr) +{ +    int boguscnt = 100; + +    if (i596_debug > 4) printk ("i596_reset\n"); + +    while (lp->scb.status, lp->scb.command) +        if (--boguscnt == 0) +	{ +	    printk("i596_reset timed out with status %4.4x, cmd %4.4x.\n", +		lp->scb.status, lp->scb.command); +	    break; +	} + +    dev->start = 0; +    dev->tbusy = 1; + +    lp->scb.command = CUC_ABORT|RX_ABORT; +    outw(0, ioaddr+4); + +    /* wait for shutdown */ +    boguscnt = 400; + +    while ((lp->scb.status, lp->scb.command) || lp->scb.command) +        if (--boguscnt == 0) +	{ +	    printk("i596_reset 2 timed out with status %4.4x, cmd %4.4x.\n", +		lp->scb.status, lp->scb.command); +	    break; +	} + +    i596_cleanup_cmd(lp); +    i596_rx(dev); + +    dev->start = 1; +    dev->tbusy = 0; +    dev->interrupt = 0; +    init_i596_mem(dev); +} + +static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd) +{ +    struct i596_private *lp = (struct i596_private *)dev->priv; +    int ioaddr = dev->base_addr; +    unsigned long flags; +    int boguscnt = 100; + +    if (i596_debug > 4) printk ("i596_add_cmd\n"); + +    cmd->status = 0; +    cmd->command |= (CMD_EOL|CMD_INTR); +    cmd->next = (struct i596_cmd *) I596_NULL; + +    save_flags(flags); +    cli(); +    if (lp->cmd_head != (struct i596_cmd *) I596_NULL) +	lp->cmd_tail->next = cmd; +    else  +    { +	lp->cmd_head = cmd; +	while (lp->scb.status, lp->scb.command) +	    if (--boguscnt == 0) +	    { +		printk("i596_add_cmd timed out with status %4.4x, cmd %4.4x.\n", +		   lp->scb.status, lp->scb.command); +		break; +    	    } + +	lp->scb.cmd = cmd; +	lp->scb.command = CUC_START; +        outw (0, ioaddr+4); +    } +    lp->cmd_tail = cmd; +    lp->cmd_backlog++; + +    lp->cmd_head = lp->scb.cmd; +    restore_flags(flags); + +    if (lp->cmd_backlog > 16)  +    { +	int tickssofar = jiffies - lp->last_cmd; + +	if (tickssofar < 25) return; + +	printk("%s: command unit timed out, status resetting.\n", dev->name); + +	i596_reset(dev, lp, ioaddr); +    } +} + +static int +i596_open(struct device *dev) +{ +    int i; + +    if (i596_debug > 1) +	printk("%s: i596_open() irq %d.\n", dev->name, dev->irq); + +    if (request_irq(dev->irq, &i596_interrupt, 0, "apricot", NULL)) +	return -EAGAIN; + +    irq2dev_map[dev->irq] = dev; + +    i = init_rx_bufs(dev, RX_RING_SIZE); + +    if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE) +        printk("%s: only able to allocate %d receive buffers\n", dev->name, i); + +    if (i < 4) +    { +        free_irq(dev->irq, NULL); +        irq2dev_map[dev->irq] = 0; +        return -EAGAIN; +    } + +    dev->tbusy = 0; +    dev->interrupt = 0; +    dev->start = 1; +    MOD_INC_USE_COUNT; + +    /* Initialize the 82596 memory */ +    init_i596_mem(dev); + +    return 0;			/* Always succeed */ +} + +static int +i596_start_xmit(struct sk_buff *skb, struct device *dev) +{ +    struct i596_private *lp = (struct i596_private *)dev->priv; +    int ioaddr = dev->base_addr; +    struct tx_cmd *tx_cmd; + +    if (i596_debug > 2) printk ("%s: Apricot start xmit\n", dev->name); + +    /* Transmitter timeout, serious problems. */ +    if (dev->tbusy) { +	int tickssofar = jiffies - dev->trans_start; +	if (tickssofar < 5) +	    return 1; +	printk("%s: transmit timed out, status resetting.\n", +	       dev->name); +	lp->stats.tx_errors++; +	/* Try to restart the adaptor */ +	if (lp->last_restart == lp->stats.tx_packets) { +	    if (i596_debug > 1) printk ("Resetting board.\n"); + +	    /* Shutdown and restart */ +            i596_reset(dev,lp, ioaddr); +	} else { +	    /* Issue a channel attention signal */ +	    if (i596_debug > 1) printk ("Kicking board.\n"); + +	    lp->scb.command = CUC_START|RX_START; +	    outw(0, ioaddr+4); + +	    lp->last_restart = lp->stats.tx_packets; +	} +	dev->tbusy = 0; +	dev->trans_start = jiffies; +    } + +    /* If some higher level thinks we've misses a tx-done interrupt +       we are passed NULL. n.b. dev_tint handles the cli()/sti() +       itself. */ +    if (skb == NULL) { +	dev_tint(dev); +	return 0; +    } + +    /* shouldn't happen */ +    if (skb->len <= 0) return 0; + +    if (i596_debug > 3) printk("%s: i596_start_xmit() called\n", dev->name); + +    /* Block a timer-based transmit from overlapping.  This could better be +       done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +    if (set_bit(0, (void*)&dev->tbusy) != 0) +	printk("%s: Transmitter access conflict.\n", dev->name); +    else +    { +	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +	dev->trans_start = jiffies; + +	tx_cmd = (struct tx_cmd *) kmalloc ((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC); +	if (tx_cmd == NULL) +	{ +	    printk ("%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name); +	    lp->stats.tx_dropped++; + +	    dev_kfree_skb(skb, FREE_WRITE); +	} +	else +	{ +	    tx_cmd->tbd = (struct i596_tbd *) (tx_cmd + 1); +	    tx_cmd->tbd->next = (struct i596_tbd *) I596_NULL; + +	    tx_cmd->cmd.command = CMD_FLEX|CmdTx; + +	    tx_cmd->pad = 0; +	    tx_cmd->size = 0; +	    tx_cmd->tbd->pad = 0; +	    tx_cmd->tbd->size = EOF | length; + +	    tx_cmd->tbd->data = skb->data; + +	    if (i596_debug > 3) print_eth(skb->data); + +	    i596_add_cmd(dev, (struct i596_cmd *)tx_cmd); + +	    lp->stats.tx_packets++; +	} +    } + +    dev->tbusy = 0; + +    return 0; +} + + +static void print_eth(unsigned char *add) +{ +    int i; + +    printk ("Dest  "); +    for (i = 0; i < 6; i++) +	printk(" %2.2X", add[i]); +    printk ("\n"); + +    printk ("Source"); +    for (i = 0; i < 6; i++) +	printk(" %2.2X", add[i+6]); +    printk ("\n"); +    printk ("type %2.2X%2.2X\n", add[12], add[13]); +} + +int apricot_probe(struct device *dev) +{ +    int i; +    struct i596_private *lp; +    int checksum = 0; +    int ioaddr = 0x300; +    char eth_addr[8]; +     +    /* this is easy the ethernet interface can only be at 0x300 */ +    /* first check nothing is already registered here */ + +    if (check_region(ioaddr, APRICOT_TOTAL_SIZE)) +	return ENODEV; + +    for (i = 0; i < 8; i++) +    { +    	eth_addr[i] = inb(ioaddr+8+i); +	checksum += eth_addr[i]; +     } + +    /* checksum is a multiple of 0x100, got this wrong first time +       some machines have 0x100, some 0x200. The DOS driver doesn't +       even bother with the checksum */ + +    if (checksum % 0x100) return ENODEV; + +    /* Some other boards trip the checksum.. but then appear as ether +       address 0. Trap these - AC */ +        +    if(memcmp(eth_addr,"\x00\x00\x49",3)!= 0) +    	return ENODEV; + +    request_region(ioaddr, APRICOT_TOTAL_SIZE, "apricot"); + +    dev->base_addr = ioaddr; +    ether_setup(dev); +    printk("%s: Apricot 82596 at %#3x,", dev->name, ioaddr); + +    for (i = 0; i < 6; i++) +	printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]); + +    dev->base_addr = ioaddr; +    dev->irq = 10; +    printk(" IRQ %d.\n", dev->irq); + +    if (i596_debug > 0) printk("%s", version); + +    /* The APRICOT-specific entries in the device structure. */ +    dev->open = &i596_open; +    dev->stop = &i596_close; +    dev->hard_start_xmit = &i596_start_xmit; +    dev->get_stats = &i596_get_stats; +    dev->set_multicast_list = &set_multicast_list; + +    dev->mem_start = (int)kmalloc(sizeof(struct i596_private)+ 0x0f, GFP_KERNEL); +    /* align for scp */ +    dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0); + +    lp = (struct i596_private *)dev->priv; +    memset((void *)lp, 0, sizeof(struct i596_private)); +    lp->scb.command = 0; +    lp->scb.cmd = (struct i596_cmd *) I596_NULL; +    lp->scb.rfd = (struct i596_rfd *)I596_NULL; + +    return 0; +} + +static void +i596_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +    struct device *dev = (struct device *)(irq2dev_map[irq]); +    struct i596_private *lp; +    short ioaddr; +    int boguscnt = 200; +    unsigned short status, ack_cmd = 0; + +    if (dev == NULL) { +	printk ("i596_interrupt(): irq %d for unknown device.\n", irq); +	return; +    } + +    if (i596_debug > 3) printk ("%s: i596_interrupt(): irq %d\n",dev->name, irq); + +    if (dev->interrupt) +	printk("%s: Re-entering the interrupt handler.\n", dev->name); + +    dev->interrupt = 1; + +    ioaddr = dev->base_addr; + +    lp = (struct i596_private *)dev->priv; + +    while (lp->scb.status, lp->scb.command) +	if (--boguscnt == 0) +	    { +		printk("%s: i596 interrupt, timeout status %4.4x command %4.4x.\n", dev->name, lp->scb.status, lp->scb.command); +		break; +	    } +    status = lp->scb.status; + +    if (i596_debug > 4) +	printk("%s: i596 interrupt, status %4.4x.\n", dev->name, status); + +    ack_cmd = status & 0xf000; + +    if ((status & 0x8000) || (status & 0x2000)) +    { +	struct i596_cmd *ptr; + +	if ((i596_debug > 4) && (status & 0x8000)) +	    printk("%s: i596 interrupt completed command.\n", dev->name); +	if ((i596_debug > 4) && (status & 0x2000)) +	    printk("%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700); + +	while ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (lp->cmd_head->status & STAT_C)) +	{ +	    ptr = lp->cmd_head; + +	    lp->cmd_head = lp->cmd_head->next; +	    lp->cmd_backlog--; + +	    switch ((ptr->command) & 0x7) +	    { +		case CmdTx: +		{ +		    struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; +		    struct sk_buff *skb = ((struct sk_buff *)(tx_cmd->tbd->data)) -1; + +		    dev_kfree_skb(skb, FREE_WRITE); + +		    if ((ptr->status) & STAT_OK) +		    { +	    		if (i596_debug >2) print_eth(skb->data); +		    } +		    else +		    { +			lp->stats.tx_errors++; +			if ((ptr->status) & 0x0020) lp->stats.collisions++; +			if (!((ptr->status) & 0x0040)) lp->stats.tx_heartbeat_errors++; +			if ((ptr->status) & 0x0400) lp->stats.tx_carrier_errors++; +			if ((ptr->status) & 0x0800) lp->stats.collisions++; +			if ((ptr->status) & 0x1000) lp->stats.tx_aborted_errors++; +		    } + + +		    ptr->next = (struct i596_cmd * ) I596_NULL; +		    kfree_s((unsigned char *)tx_cmd, (sizeof (struct tx_cmd) + sizeof (struct i596_tbd))); +		    break; +		} +		case CmdMulticastList: +		{ +		    unsigned short count = *((unsigned short *) (ptr + 1)); + +		    ptr->next = (struct i596_cmd * ) I596_NULL; +		    kfree_s((unsigned char *)ptr, (sizeof (struct i596_cmd) + count + 2)); +		    break; +		} +		case CmdTDR: +		{ +		    unsigned long status = *((unsigned long *) (ptr + 1)); + +		    if (status & 0x8000) +		    { +			if (i596_debug > 3) +	    		    printk("%s: link ok.\n", dev->name); +		    } +		    else +		    { +			if (status & 0x4000) +	    		    printk("%s: Transceiver problem.\n", dev->name); +			if (status & 0x2000) +	    		    printk("%s: Termination problem.\n", dev->name); +			if (status & 0x1000) +	    		    printk("%s: Short circuit.\n", dev->name); + +	    		printk("%s: Time %ld.\n", dev->name, status & 0x07ff); +		    } +		} +		default: +		    ptr->next = (struct i596_cmd * ) I596_NULL; + +		lp->last_cmd = jiffies; + 	    } +	} + +	ptr = lp->cmd_head; +	while ((ptr != (struct i596_cmd *) I596_NULL) && (ptr != lp->cmd_tail)) +	{ +	    ptr->command &= 0x1fff; +	    ptr = ptr->next; +	} + +	if ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (dev->start)) ack_cmd |= CUC_START; +	lp->scb.cmd = lp->cmd_head; +    } + +    if ((status & 0x1000) || (status & 0x4000)) +    { +	if ((i596_debug > 4) && (status & 0x4000)) +	    printk("%s: i596 interrupt received a frame.\n", dev->name); +	if ((i596_debug > 4) && (status & 0x1000)) +	    printk("%s: i596 interrupt receive unit inactive %x.\n", dev->name, status & 0x0070); + +	i596_rx(dev); + +	if (dev->start) ack_cmd |= RX_START; +    } + +    /* acknowledge the interrupt */ + +/* +    if ((lp->scb.cmd != (struct i596_cmd *) I596_NULL) && (dev->start)) ack_cmd | = CUC_START; +*/ +    boguscnt = 100; +    while (lp->scb.status, lp->scb.command) +	if (--boguscnt == 0) +	    { +		printk("%s: i596 interrupt, timeout status %4.4x command %4.4x.\n", dev->name, lp->scb.status, lp->scb.command); +		break; +	    } +    lp->scb.command = ack_cmd; + +    (void) inb (ioaddr+0x10); +    outb (4, ioaddr+0xf); +    outw (0, ioaddr+4); + +    if (i596_debug > 4) +	printk("%s: exiting interrupt.\n", dev->name); + +    dev->interrupt = 0; +    return; +} + +static int +i596_close(struct device *dev) +{ +    int ioaddr = dev->base_addr; +    struct i596_private *lp = (struct i596_private *)dev->priv; +    int boguscnt = 200; + +    dev->start = 0; +    dev->tbusy = 1; + +    if (i596_debug > 1) +	printk("%s: Shutting down ethercard, status was %4.4x.\n", +	       dev->name, lp->scb.status); + +    lp->scb.command = CUC_ABORT|RX_ABORT; +    outw(0, ioaddr+4); + +    i596_cleanup_cmd(lp); + +    while (lp->scb.status, lp->scb.command) +	if (--boguscnt == 0) +	{ +	    printk("%s: close timed out with status %4.4x, cmd %4.4x.\n", +		   dev->name, lp->scb.status, lp->scb.command); +	    break; +    	} +    free_irq(dev->irq, NULL); +    irq2dev_map[dev->irq] = 0; +    remove_rx_bufs(dev); +    MOD_DEC_USE_COUNT; + +    return 0; +} + +static struct enet_statistics * +i596_get_stats(struct device *dev) +{ +    struct i596_private *lp = (struct i596_private *)dev->priv; + +    return &lp->stats; +} + +/* + *	Set or clear the multicast filter for this adaptor. + */ +  +static void set_multicast_list(struct device *dev) +{ +	struct i596_private *lp = (struct i596_private *)dev->priv; +	struct i596_cmd *cmd; + +	if (i596_debug > 1) +		printk ("%s: set multicast list %d\n", dev->name, dev->mc_count); + +	if (dev->mc_count > 0)  +	{ +		struct dev_mc_list *dmi; +		char *cp; +		cmd = (struct i596_cmd *) kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6, GFP_ATOMIC); +		if (cmd == NULL) +		{ +			printk ("%s: set_multicast Memory squeeze.\n", dev->name); +			return; +		} +		cmd->command = CmdMulticastList; +		*((unsigned short *) (cmd + 1)) = dev->mc_count * 6; +		cp=((char *)(cmd + 1))+2; +		for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next) +		{ +			memcpy(cp, dmi,6); +			cp+=6; +		} +		print_eth (((unsigned char *)(cmd + 1)) + 2); +		i596_add_cmd(dev, cmd); +	} +	else +	{ +		if (lp->set_conf.next != (struct i596_cmd * ) I596_NULL)  +			return; +		if (dev->mc_count == 0 && !(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) +		{ +			if(dev->flags&IFF_ALLMULTI) +				dev->flags|=IFF_PROMISC; +			lp->i596_config[8] &= ~0x01; +		} +		else +			lp->i596_config[8] |= 0x01; + +		i596_add_cmd(dev, &lp->set_conf); +	} +} + +#ifdef HAVE_DEVLIST +static unsigned int apricot_portlist[] = {0x300, 0}; +struct netdev_entry apricot_drv =  +{"apricot", apricot_probe, APRICOT_TOTAL_SIZE, apricot_portlist}; +#endif + +#ifdef MODULE +static char devicename[9] = { 0, }; +static struct device dev_apricot = { +  devicename, /* device name inserted by /linux/drivers/net/net_init.c */ +  0, 0, 0, 0, +  0x300, 10, +  0, 0, 0, NULL, apricot_probe }; + +static int io = 0x300; +static int irq = 10; + +int +init_module(void) +{ +  dev_apricot.base_addr = io; +  dev_apricot.irq       = irq; +  if (register_netdev(&dev_apricot) != 0) +    return -EIO; +  return 0; +} + +void +cleanup_module(void) +{ +    unregister_netdev(&dev_apricot); +    kfree_s((void *)dev_apricot.mem_start, sizeof(struct i596_private) + 0xf); +    dev_apricot.priv = NULL; + +    /* If we don't do this, we can't re-insmod it later. */ +    release_region(dev_apricot.base_addr, APRICOT_TOTAL_SIZE); +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c apricot.c" + * End: + */ diff --git a/linux/src/drivers/net/at1700.c b/linux/src/drivers/net/at1700.c new file mode 100644 index 0000000..f4025f4 --- /dev/null +++ b/linux/src/drivers/net/at1700.c @@ -0,0 +1,756 @@ +/* at1700.c: A network device driver for  the Allied Telesis AT1700. + +	Written 1993-98 by Donald Becker. + +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	This is a device driver for the Allied Telesis AT1700, which is a +	straight-forward Fujitsu MB86965 implementation. + +  Sources: +    The Fujitsu MB86965 datasheet. + +	After the initial version of this driver was written Gerry Sawkins of +	ATI provided their EEPROM configuration code header file. +    Thanks to NIIBE Yutaka <gniibe@mri.co.jp> for bug fixes. + +  Bugs: +	The MB86965 has a design flaw that makes all probes unreliable.  Not +	only is it difficult to detect, it also moves around in I/O space in +	response to inb()s from other device probes! +*/ + +static const char *version = +	"at1700.c:v1.15 4/7/98  Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +/* Tunable parameters. */ + +/* When to switch from the 64-entry multicast filter to Rx-all-multicast. */ +#define MC_FILTERBREAK 64 + +/* These unusual address orders are used to verify the CONFIG register. */ +static int at1700_probe_list[] = +{0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0}; +static int fmv18x_probe_list[] = +{0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x300, 0x340, 0}; + + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 1 +#endif +static unsigned int net_debug = NET_DEBUG; + +typedef unsigned char uchar; + +/* Information that need to be kept for each board. */ +struct net_local { +	struct enet_statistics stats; +	unsigned char mc_filter[8]; +	uint jumpered:1;			/* Set iff the board has jumper config. */ +	uint tx_started:1;			/* Packets are on the Tx queue. */ +	uint invalid_irq:1; +	uchar tx_queue;				/* Number of packet on the Tx queue. */ +	ushort tx_queue_len;		/* Current length of the Tx queue. */ +}; + + +/* Offsets from the base address. */ +#define STATUS			0 +#define TX_STATUS		0 +#define RX_STATUS		1 +#define TX_INTR			2		/* Bit-mapped interrupt enable registers. */ +#define RX_INTR			3 +#define TX_MODE			4 +#define RX_MODE			5 +#define CONFIG_0		6		/* Misc. configuration settings. */ +#define CONFIG_1		7 +/* Run-time register bank 2 definitions. */ +#define DATAPORT		8		/* Word-wide DMA or programmed-I/O dataport. */ +#define TX_START		10 +#define MODE13			13 +/* Configuration registers only on the '865A/B chips. */ +#define EEPROM_Ctrl 	16 +#define EEPROM_Data 	17 +#define IOCONFIG		18		/* Either read the jumper, or move the I/O. */ +#define IOCONFIG1		19 +#define	SAPROM			20		/* The station address PROM, if no EEPROM. */ +#define RESET			31		/* Write to reset some parts of the chip. */ +#define AT1700_IO_EXTENT	32 +/* Index to functions, as function prototypes. */ + +extern int at1700_probe(struct device *dev); + +static int at1700_probe1(struct device *dev, int ioaddr); +static int read_eeprom(int ioaddr, int location); +static int net_open(struct device *dev); +static int	net_send_packet(struct sk_buff *skb, struct device *dev); +static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void net_rx(struct device *dev); +static int net_close(struct device *dev); +static struct enet_statistics *net_get_stats(struct device *dev); +static void set_rx_mode(struct device *dev); + + +/* Check for a network adaptor of this type, and return '0' iff one exists. +   If dev->base_addr == 0, probe all likely locations. +   If dev->base_addr == 1, always return failure. +   If dev->base_addr == 2, allocate space for the device and return success +   (detachable devices only). +   */ +#ifdef HAVE_DEVLIST +/* Support for a alternate probe manager, which will eliminate the +   boilerplate below. */ +struct netdev_entry at1700_drv = +{"at1700", at1700_probe1, AT1700_IO_EXTENT, at1700_probe_list}; +#else +int +at1700_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return at1700_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; at1700_probe_list[i]; i++) { +		int ioaddr = at1700_probe_list[i]; +		if (check_region(ioaddr, AT1700_IO_EXTENT)) +			continue; +		if (at1700_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +/* The Fujitsu datasheet suggests that the NIC be probed for by checking its +   "signature", the default bit pattern after a reset.  This *doesn't* work -- +   there is no way to reset the bus interface without a complete power-cycle! + +   It turns out that ATI came to the same conclusion I did: the only thing +   that can be done is checking a few bits and then diving right into an +   EEPROM read. */ + +int at1700_probe1(struct device *dev, int ioaddr) +{ +	char fmv_irqmap[4] = {3, 7, 10, 15}; +	char at1700_irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15}; +	unsigned int i, irq, is_fmv18x = 0, is_at1700 = 0; + +	/* Resetting the chip doesn't reset the ISA interface, so don't bother. +	   That means we have to be careful with the register values we probe for. +	   */ +#ifdef notdef +	printk("at1700 probe at %#x, eeprom is %4.4x %4.4x %4.4x ctrl %4.4x.\n", +		   ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5), +		   read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl)); +#endif +	/* We must check for the EEPROM-config boards first, else accessing +	   IOCONFIG0 will move the board! */ +	if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr +		&& read_eeprom(ioaddr, 4) == 0x0000 +		&& (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400) +		is_at1700 = 1; +	else if (fmv18x_probe_list[inb(ioaddr + IOCONFIG) & 0x07] == ioaddr +		&& inb(ioaddr + SAPROM    ) == 0x00 +		&& inb(ioaddr + SAPROM + 1) == 0x00 +		&& inb(ioaddr + SAPROM + 2) == 0x0e) +		is_fmv18x = 1; +	else +		return -ENODEV; + +	/* Reset the internal state machines. */ +	outb(0, ioaddr + RESET); + +	/* Allocate a new 'dev' if needed. */ +	if (dev == NULL) +		dev = init_etherdev(0, sizeof(struct net_local)); + +	if (is_at1700) +		irq = at1700_irqmap[(read_eeprom(ioaddr, 12)&0x04) +						   | (read_eeprom(ioaddr, 0)>>14)]; +	else +		irq = fmv_irqmap[(inb(ioaddr + IOCONFIG)>>6) & 0x03]; + +	/* Grab the region so that we can find another board if the IRQ request +	   fails. */ +	request_region(ioaddr, AT1700_IO_EXTENT, dev->name); + +	printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name, +		   ioaddr, irq); + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	for(i = 0; i < 3; i++) { +		unsigned short eeprom_val = read_eeprom(ioaddr, 4+i); +		printk("%04x", eeprom_val); +		((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val); +	} + +	/* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals, +	   rather than 150 ohm shielded twisted pair compensation. +	   0x0000 == auto-sense the interface +	   0x0800 == use TP interface +	   0x1800 == use coax interface +	   */ +	{ +		const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"}; +		ushort setup_value = read_eeprom(ioaddr, 12); + +		dev->if_port = setup_value >> 8; +		printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]); +	} + +	/* Set the station address in bank zero. */ +	outb(0xe0, ioaddr + CONFIG_1); +	for (i = 0; i < 6; i++) +		outb(dev->dev_addr[i], ioaddr + 8 + i); + +	/* Switch to bank 1 and set the multicast table to accept none. */ +	outb(0xe4, ioaddr + CONFIG_1); +	for (i = 0; i < 8; i++) +		outb(0x00, ioaddr + 8 + i); + +	/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit +	   bus access, two 4K Tx queues, and disabled Tx and Rx. */ +	outb(0xda, ioaddr + CONFIG_0); + +	/* Switch to bank 2 and lock our I/O address. */ +	outb(0xe8, ioaddr + CONFIG_1); +	outb(dev->if_port, MODE13); + +	/* Power-down the chip.  Aren't we green! */ +	outb(0x00, ioaddr + CONFIG_1); + +	if (net_debug) +		printk("%s", version); + +	/* Initialize the device structure. */ +	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); +	if (dev->priv == NULL) +		return -ENOMEM; +	memset(dev->priv, 0, sizeof(struct net_local)); + +	dev->open		= net_open; +	dev->stop		= net_close; +	dev->hard_start_xmit = net_send_packet; +	dev->get_stats	= net_get_stats; +	dev->set_multicast_list = &set_rx_mode; + +	/* Fill in the fields of 'dev' with ethernet-generic values. */ +	ether_setup(dev); + +	{ +		struct net_local *lp = (struct net_local *)dev->priv; +		lp->jumpered = is_fmv18x; +		/* Snarf the interrupt vector now. */ +		if (request_irq(irq, &net_interrupt, 0, dev->name, dev)) { +			printk ("  AT1700 at %#3x is unusable due to a conflict on" +					"IRQ %d.\n", ioaddr, irq); +			lp->invalid_irq = 1; +			return 0; +		} +	} + +	return 0; +} + + +/*  EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK	0x40	/* EEPROM shift clock, in reg. 16. */ +#define EE_CS			0x20	/* EEPROM chip select, in reg. 16. */ +#define EE_DATA_WRITE	0x80	/* EEPROM chip data in, in reg. 17. */ +#define EE_DATA_READ	0x80	/* EEPROM chip data out, in reg. 17. */ + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay()	do {} while (0); + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD	(5 << 6) +#define EE_READ_CMD		(6 << 6) +#define EE_ERASE_CMD	(7 << 6) + +static int read_eeprom(int ioaddr, int location) +{ +	int i; +	unsigned short retval = 0; +	int ee_addr = ioaddr + EEPROM_Ctrl; +	int ee_daddr = ioaddr + EEPROM_Data; +	int read_cmd = location | EE_READ_CMD; + +	/* Shift the read command bits out. */ +	for (i = 9; i >= 0; i--) { +		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; +		outb(EE_CS, ee_addr); +		outb(dataval, ee_daddr); +		eeprom_delay(); +		outb(EE_CS | EE_SHIFT_CLK, ee_addr);	/* EEPROM clock tick. */ +		eeprom_delay(); +	} +	outb(EE_DATA_WRITE, ee_daddr); +	for (i = 16; i > 0; i--) { +		outb(EE_CS, ee_addr); +		eeprom_delay(); +		outb(EE_CS | EE_SHIFT_CLK, ee_addr); +		eeprom_delay(); +		retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0); +	} + +	/* Terminate the EEPROM access. */ +	outb(EE_CS, ee_addr); +	eeprom_delay(); +	outb(EE_SHIFT_CLK, ee_addr); +	outb(0, ee_addr); +	return retval; +} + + + +static int net_open(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int i; + +	/* Powerup the chip, initialize config register 1, and select bank 0. */ +	outb(0xe0, ioaddr + CONFIG_1); + +	/* Set the station address in bank zero. */ +	for (i = 0; i < 6; i++) +		outb(dev->dev_addr[i], ioaddr + 8 + i); + +	/* Switch to bank 1 and set the multicast table to accept none. */ +	outb(0xe4, ioaddr + CONFIG_1); +	for (i = 0; i < 8; i++) +		outb(0x00, ioaddr + 8 + i); + +	/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit +	   bus access, and two 4K Tx queues. */ +	outb(0xda, ioaddr + CONFIG_0); + +	/* Switch to register bank 2, enable the Rx and Tx. */ +	outw(0xe85a, ioaddr + CONFIG_0); + +	lp->tx_started = 0; +	lp->tx_queue = 0; +	lp->tx_queue_len = 0; + +	/* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */ +	outb(0x00, ioaddr + TX_INTR); +	outb(0x81, ioaddr + RX_INTR); + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; + +	MOD_INC_USE_COUNT; + +	return 0; +} + +static int +net_send_packet(struct sk_buff *skb, struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; + +	if (dev->tbusy) { +		/* If we get here, some higher level has decided we are broken. +		   There should really be a "kick me" function call instead. */ +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < 10) +			return 1; +		printk("%s: transmit timed out with status %04x, %s?\n", dev->name, +			   inw(ioaddr + STATUS), inb(ioaddr + TX_STATUS) & 0x80 +			   ? "IRQ conflict" : "network cable problem"); +		printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n", +			   dev->name, inw(ioaddr + 0), inw(ioaddr + 2), inw(ioaddr + 4), +			   inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10), +			   inw(ioaddr + 12), inw(ioaddr + 14)); +		lp->stats.tx_errors++; +		/* ToDo: We should try to restart the adaptor... */ +		outw(0xffff, ioaddr + 24); +		outw(0xffff, ioaddr + TX_STATUS); +		outw(0xe85a, ioaddr + CONFIG_0); +		outw(0x8100, ioaddr + TX_INTR); +		dev->tbusy=0; +		dev->trans_start = jiffies; +		lp->tx_started = 0; +		lp->tx_queue = 0; +		lp->tx_queue_len = 0; +	} + +	/* If some higher layer thinks we've missed an tx-done interrupt +	   we are passed NULL. Caution: dev_tint() handles the cli()/sti() +	   itself. */ +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) +		printk("%s: Transmitter access conflict.\n", dev->name); +	else { +		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		unsigned char *buf = skb->data; + +		/* Turn off the possible Tx interrupts. */ +		outb(0x00, ioaddr + TX_INTR); + +		outw(length, ioaddr + DATAPORT); +		outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); + +		lp->tx_queue++; +		lp->tx_queue_len += length + 2; + +		if (lp->tx_started == 0) { +			/* If the Tx is idle, always trigger a transmit. */ +			outb(0x80 | lp->tx_queue, ioaddr + TX_START); +			lp->tx_queue = 0; +			lp->tx_queue_len = 0; +			dev->trans_start = jiffies; +			lp->tx_started = 1; +			dev->tbusy = 0; +		} else if (lp->tx_queue_len < 4096 - 1502) +			/* Yes, there is room for one more packet. */ +			dev->tbusy = 0; + +		/* Turn on Tx interrupts back on. */ +		outb(0x82, ioaddr + TX_INTR); +	} +	dev_kfree_skb (skb, FREE_WRITE); + +	return 0; +} + +/* The typical workload of the driver: +   Handle the network interface interrupts. */ +static void +net_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +	struct device *dev = dev_id; +	struct net_local *lp; +	int ioaddr, status; + +	if (dev == NULL) { +		printk ("at1700_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +	dev->interrupt = 1; + +	ioaddr = dev->base_addr; +	lp = (struct net_local *)dev->priv; +	status = inw(ioaddr + TX_STATUS); +	outw(status, ioaddr + TX_STATUS); + +	if (net_debug > 4) +		printk("%s: Interrupt with status %04x.\n", dev->name, status); +	if (status & 0xff00 +		||  (inb(ioaddr + RX_MODE) & 0x40) == 0) {			/* Got a packet(s). */ +		net_rx(dev); +	} +	if (status & 0x00ff) { +		if (status & 0x80) { +			lp->stats.tx_packets++; +			if (lp->tx_queue) { +				outb(0x80 | lp->tx_queue, ioaddr + TX_START); +				lp->tx_queue = 0; +				lp->tx_queue_len = 0; +				dev->trans_start = jiffies; +				dev->tbusy = 0; +				mark_bh(NET_BH);	/* Inform upper layers. */ +			} else { +				lp->tx_started = 0; +				/* Turn on Tx interrupts off. */ +				outb(0x00, ioaddr + TX_INTR); +				dev->tbusy = 0; +				mark_bh(NET_BH);	/* Inform upper layers. */ +			} +		} +	} + +	dev->interrupt = 0; +	return; +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +net_rx(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int boguscount = 5; + +	while ((inb(ioaddr + RX_MODE) & 0x40) == 0) { +		ushort status = inw(ioaddr + DATAPORT); +		ushort pkt_len = inw(ioaddr + DATAPORT); + +		if (net_debug > 4) +			printk("%s: Rxing packet mode %02x status %04x.\n", +				   dev->name, inb(ioaddr + RX_MODE), status); +#ifndef final_version +		if (status == 0) { +			outb(0x05, ioaddr + 14); +			break; +		} +#endif + +		if ((status & 0xF0) != 0x20) {	/* There was an error. */ +			lp->stats.rx_errors++; +			if (status & 0x08) lp->stats.rx_length_errors++; +			if (status & 0x04) lp->stats.rx_frame_errors++; +			if (status & 0x02) lp->stats.rx_crc_errors++; +			if (status & 0x01) lp->stats.rx_over_errors++; +		} else { +			/* Malloc up new buffer. */ +			struct sk_buff *skb; + +			if (pkt_len > 1550) { +				printk("%s: The AT1700 claimed a very large packet, size %d.\n", +					   dev->name, pkt_len); +				/* Prime the FIFO and then flush the packet. */ +				inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT); +				outb(0x05, ioaddr + 14); +				lp->stats.rx_errors++; +				break; +			} +			skb = dev_alloc_skb(pkt_len+3); +			if (skb == NULL) { +				printk("%s: Memory squeeze, dropping packet (len %d).\n", +					   dev->name, pkt_len); +				/* Prime the FIFO and then flush the packet. */ +				inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT); +				outb(0x05, ioaddr + 14); +				lp->stats.rx_dropped++; +				break; +			} +			skb->dev = dev; +			skb_reserve(skb,2); + +			insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1); +			skb->protocol=eth_type_trans(skb, dev); +			netif_rx(skb); +			lp->stats.rx_packets++; +		} +		if (--boguscount <= 0) +			break; +	} + +	/* If any worth-while packets have been received, dev_rint() +	   has done a mark_bh(NET_BH) for us and will work on them +	   when we get to the bottom-half routine. */ +	{ +		int i; +		for (i = 0; i < 20; i++) { +			if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40) +				break; +			inw(ioaddr + DATAPORT);				/* dummy status read */ +			outb(0x05, ioaddr + 14); +		} + +		if (net_debug > 5) +			printk("%s: Exint Rx packet with mode %02x after %d ticks.\n", +				   dev->name, inb(ioaddr + RX_MODE), i); +	} +	return; +} + +/* The inverse routine to net_open(). */ +static int net_close(struct device *dev) +{ +#if 0 +	struct net_local *lp = (struct net_local *)dev->priv; +#endif +	int ioaddr = dev->base_addr; + +	dev->tbusy = 1; +	dev->start = 0; + +	/* Set configuration register 0 to disable Tx and Rx. */ +	outb(0xda, ioaddr + CONFIG_0); + +	/* No statistic counters on the chip to update. */ + +#if 0 +	/* Disable the IRQ on boards where it is feasible. */ +	if (lp->jumpered) { +		outb(0x00, ioaddr + IOCONFIG1); +		free_irq(dev->irq, dev); +	} +#endif + +	/* Power-down the chip.  Green, green, green! */ +	outb(0x00, ioaddr + CONFIG_1); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +/* Get the current statistics. +   This may be called with the card open or closed. +   There are no on-chip counters, so this function is trivial. +*/ +static struct enet_statistics * +net_get_stats(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	return &lp->stats; +} + +/* +  Set the multicast/promiscuous mode for this adaptor. +*/ + +/* The little-endian AUTODIN II ethernet CRC calculation. +   N.B. Do not use for bulk data, use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ +	unsigned int crc = 0xffffffff;	/* Initial value. */ +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +	} +	return crc; +} + +static void +set_rx_mode(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned char mc_filter[8];		 /* Multicast hash filter */ +	long flags; +	int i; + +	if (dev->flags & IFF_PROMISC) { +		/* Unconditionally log net taps. */ +		printk("%s: Promiscuous mode enabled.\n", dev->name); +		memset(mc_filter, 0xff, sizeof(mc_filter)); +		outb(3, ioaddr + RX_MODE);	/* Enable promiscuous mode */ +	} else if (dev->mc_count > MC_FILTERBREAK +			   ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to filter perfectly -- accept all multicasts. */ +		memset(mc_filter, 0xff, sizeof(mc_filter)); +		outb(2, ioaddr + RX_MODE);	/* Use normal mode. */ +	} else if (dev->mc_count == 0) { +		memset(mc_filter, 0x00, sizeof(mc_filter)); +		outb(1, ioaddr + RX_MODE);	/* Ignore almost all multicasts. */ +	} else { +		struct dev_mc_list *mclist; +		int i; + +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) +			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 26, +					mc_filter); +	} + +	save_flags(flags); +	cli(); +	if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) { +		int saved_bank = inw(ioaddr + CONFIG_0); +		/* Switch to bank 1 and set the multicast table. */ +		outw((saved_bank & ~0x0C00) | 0x0480, ioaddr + CONFIG_0); +		for (i = 0; i < 8; i++) +			outb(mc_filter[i], ioaddr + 8 + i); +		memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter)); +		outw(saved_bank, ioaddr + CONFIG_0); +	} +	restore_flags(flags); +	return; +} + +#ifdef MODULE +static char devicename[9] = { 0, }; +static struct device dev_at1700 = { +	devicename, /* device name is inserted by linux/drivers/net/net_init.c */ +	0, 0, 0, 0, +	0, 0, +	0, 0, 0, NULL, at1700_probe }; + +static int io = 0x260; +static int irq = 0; + +int init_module(void) +{ +	if (io == 0) +		printk("at1700: You should not use auto-probing with insmod!\n"); +	dev_at1700.base_addr = io; +	dev_at1700.irq       = irq; +	if (register_netdev(&dev_at1700) != 0) { +		printk("at1700: register_netdev() returned non-zero.\n"); +		return -EIO; +	} +	return 0; +} + +void +cleanup_module(void) +{ +	unregister_netdev(&dev_at1700); +	kfree(dev_at1700.priv); +	dev_at1700.priv = NULL; + +	/* If we don't do this, we can't re-insmod it later. */ +	free_irq(dev_at1700.irq, NULL); +	release_region(dev_at1700.base_addr, AT1700_IO_EXTENT); +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c" + *  alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c" + *  tab-width: 4 + *  c-basic-offset: 4 + *  c-indent-level: 4 + * End: + */ diff --git a/linux/src/drivers/net/atp.c b/linux/src/drivers/net/atp.c new file mode 100644 index 0000000..a9445ea --- /dev/null +++ b/linux/src/drivers/net/atp.c @@ -0,0 +1,977 @@ +/* atp.c: Attached (pocket) ethernet adapter driver for linux. */ +/* +	This is a driver for commonly OEMed pocket (parallel port) +	ethernet adapters based on the Realtek RTL8002 and RTL8012 chips. + +	Written 1993-95,1997 by Donald Becker. + +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +		Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	The timer-based reset code was written by Bill Carlson, wwc@super.org. +*/ + +static const char *version = +	"atp.c:v1.08 4/1/97 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +/* Operational parameters that may be safely changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  ((400*HZ)/1000) + +/* +	This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket +	ethernet adapter.  This is a common low-cost OEM pocket ethernet +	adapter, sold under many names. + +  Sources: +	This driver was written from the packet driver assembly code provided by +	Vincent Bono of AT-Lan-Tec.	 Ever try to figure out how a complicated +	device works just from the assembly code?  It ain't pretty.  The following +	description is written based on guesses and writing lots of special-purpose +	code to test my theorized operation. + +	In 1997 Realtek made available the documentation for the second generation +	RTL8012 chip, which has lead to several driver improvements. +	  http://www.realtek.com.tw/cn/cn.html + +					Theory of Operation +	 +	The RTL8002 adapter seems to be built around a custom spin of the SEEQ +	controller core.  It probably has a 16K or 64K internal packet buffer, of +	which the first 4K is devoted to transmit and the rest to receive. +	The controller maintains the queue of received packet and the packet buffer +	access pointer internally, with only 'reset to beginning' and 'skip to next +	packet' commands visible.  The transmit packet queue holds two (or more?) +	packets: both 'retransmit this packet' (due to collision) and 'transmit next +	packet' commands must be started by hand. + +	The station address is stored in a standard bit-serial EEPROM which must be +	read (ughh) by the device driver.  (Provisions have been made for +	substituting a 74S288 PROM, but I haven't gotten reports of any models +	using it.)  Unlike built-in devices, a pocket adapter can temporarily lose +	power without indication to the device driver.  The major effect is that +	the station address, receive filter (promiscuous, etc.) and transceiver +	must be reset. + +	The controller itself has 16 registers, some of which use only the lower +	bits.  The registers are read and written 4 bits at a time.  The four bit +	register address is presented on the data lines along with a few additional +	timing and control bits.  The data is then read from status port or written +	to the data port. + +	Correction: the controller has two banks of 16 registers.  The second +	bank contains only the multicast filter table (now used) and the EEPROM +	access registers. + +	Since the bulk data transfer of the actual packets through the slow +	parallel port dominates the driver's running time, four distinct data +	(non-register) transfer modes are provided by the adapter, two in each +	direction.  In the first mode timing for the nibble transfers is +	provided through the data port.  In the second mode the same timing is +	provided through the control port.  In either case the data is read from +	the status port and written to the data port, just as it is accessing +	registers. + +	In addition to the basic data transfer methods, several more are modes are +	created by adding some delay by doing multiple reads of the data to allow +	it to stabilize.  This delay seems to be needed on most machines. + +	The data transfer mode is stored in the 'dev->if_port' field.  Its default +	value is '4'.  It may be overridden at boot-time using the third parameter +	to the "ether=..." initialization. + +	The header file <atp.h> provides inline functions that encapsulate the +	register and data access methods.  These functions are hand-tuned to +	generate reasonable object code.  This header file also documents my +	interpretations of the device registers. +*/ +#include <linux/config.h> +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "atp.h" + +/* Kernel compatibility defines, common to David Hind's PCMCIA package. +   This is only in the support-all-kernels source code. */ +#include <linux/version.h>		/* Evil, but neccessary */ + +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x)			/* What to put in timer->expires.  */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#define virt_to_bus(addr)  ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) + +#else  /* 1.3.0 and later */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#endif +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 +#ifdef MODULE +#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) +char kernel_version[] = UTS_RELEASE; +#endif +#else +#undef MOD_INC_USE_COUNT +#define MOD_INC_USE_COUNT +#undef MOD_DEC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif +#endif /* 1.3.38 */ + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include <linux/delay.h> +#endif + +#ifdef SA_SHIRQ +#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance) +#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define FREE_IRQ(irqnum, dev) free_irq(irqnum) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n) +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif +/* End of kernel compatibility defines. */ + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 1 +#endif +static unsigned int net_debug = NET_DEBUG; + +/* The number of low I/O ports used by the ethercard. */ +#define ETHERCARD_TOTAL_SIZE	3 + +/* Sequence to switch an 8012 from printer mux to ethernet mode. */ +static char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,}; + +/* This code, written by wwc@super.org, resets the adapter every +   TIMED_CHECKER ticks.  This recovers from an unknown error which +   hangs the device. */ +#define TIMED_CHECKER (HZ/4) +#ifdef TIMED_CHECKER +#include <linux/timer.h> +static void atp_timed_checker(unsigned long ignored); +#endif + +/* Index to functions, as function prototypes. */ + +extern int atp_init(struct device *dev); + +static int atp_probe1(struct device *dev, short ioaddr); +static void get_node_ID(struct device *dev); +static unsigned short eeprom_op(short ioaddr, unsigned int cmd); +static int net_open(struct device *dev); +static void hardware_init(struct device *dev); +static void write_packet(short ioaddr, int length, unsigned char *packet, int mode); +static void trigger_send(short ioaddr, int length); +static int	net_send_packet(struct sk_buff *skb, struct device *dev); +static void net_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs); +static void net_rx(struct device *dev); +static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode); +static int net_close(struct device *dev); +static struct enet_statistics *net_get_stats(struct device *dev); +#ifdef NEW_MULTICAST +static void set_rx_mode_8002(struct device *dev); +static void set_rx_mode_8012(struct device *dev); +#else +static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs); +static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs); +#endif + + +/* A list of all installed ATP devices, for removing the driver module. */ +static struct device *root_atp_dev = NULL; + +/* Check for a network adapter of this type, and return '0' iff one exists. +   If dev->base_addr == 0, probe all likely locations. +   If dev->base_addr == 1, always return failure. +   If dev->base_addr == 2, allocate space for the device and return success +   (detachable devices only). +   */ +int +atp_init(struct device *dev) +{ +	int *port, ports[] = {0x378, 0x278, 0x3bc, 0}; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return atp_probe1(dev, base_addr); +	else if (base_addr == 1)	/* Don't probe at all. */ +		return ENXIO; + +	for (port = ports; *port; port++) { +		int ioaddr = *port; +		outb(0x57, ioaddr + PAR_DATA); +		if (inb(ioaddr + PAR_DATA) != 0x57) +			continue; +		if (atp_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} + +static int atp_probe1(struct device *dev, short ioaddr) +{ +	struct net_local *lp; +	int saved_ctrl_reg, status, i; + +	outb(0xff, ioaddr + PAR_DATA); +	/* Save the original value of the Control register, in case we guessed +	   wrong. */ +	saved_ctrl_reg = inb(ioaddr + PAR_CONTROL); +	/* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */ +	outb(0x04, ioaddr + PAR_CONTROL); +	/* Turn off the printer multiplexer on the 8012. */ +	for (i = 0; i < 8; i++) +		outb(mux_8012[i], ioaddr + PAR_DATA); +	write_reg_high(ioaddr, CMR1, CMR1h_RESET); +	eeprom_delay(2048); +	status = read_nibble(ioaddr, CMR1); + +	if ((status & 0x78) != 0x08) { +		/* The pocket adapter probe failed, restore the control register. */ +		outb(saved_ctrl_reg, ioaddr + PAR_CONTROL); +		return 1; +	} +	status = read_nibble(ioaddr, CMR2_h); +	if ((status & 0x78) != 0x10) { +		outb(saved_ctrl_reg, ioaddr + PAR_CONTROL); +		return 1; +	} + +	dev = init_etherdev(dev, sizeof(struct net_local)); + +	/* Find the IRQ used by triggering an interrupt. */ +	write_reg_byte(ioaddr, CMR2, 0x01);			/* No accept mode, IRQ out. */ +	write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);	/* Enable Tx and Rx. */ + +	/* Omit autoIRQ routine for now. Use "table lookup" instead.  Uhgggh. */ +	if (ioaddr == 0x378) +		dev->irq = 7; +	else +		dev->irq = 5; +	write_reg_high(ioaddr, CMR1, CMR1h_TxRxOFF); /* Disable Tx and Rx units. */ +	write_reg(ioaddr, CMR2, CMR2_NULL); + +	dev->base_addr = ioaddr; + +	/* Read the station address PROM.  */ +	get_node_ID(dev); + +	printk("%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM " +		   "%02X:%02X:%02X:%02X:%02X:%02X.\n", dev->name, dev->base_addr, +		   dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], +		   dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + +	/* Reset the ethernet hardware and activate the printer pass-through. */ +    write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); + +	if (net_debug) +		printk(version); + +	/* Initialize the device structure. */ +	ether_setup(dev); +	if (dev->priv == NULL) +		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); +	if (dev->priv == NULL) +		return -ENOMEM; +	memset(dev->priv, 0, sizeof(struct net_local)); + +	lp = (struct net_local *)dev->priv; +	lp->chip_type = RTL8002; +	lp->addr_mode = CMR2h_Normal; + +	lp->next_module = root_atp_dev; +	root_atp_dev = dev; + +	/* For the ATP adapter the "if_port" is really the data transfer mode. */ +	dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4; +	if (dev->mem_end & 0xf) +		net_debug = dev->mem_end & 7; + +	dev->open		= net_open; +	dev->stop		= net_close; +	dev->hard_start_xmit = net_send_packet; +	dev->get_stats	= net_get_stats; +	dev->set_multicast_list = +	  lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012; + +	return 0; +} + +/* Read the station address PROM, usually a word-wide EEPROM. */ +static void get_node_ID(struct device *dev) +{ +	short ioaddr = dev->base_addr; +	int sa_offset = 0; +	int i; +	 +	write_reg(ioaddr, CMR2, CMR2_EEPROM);	  /* Point to the EEPROM control registers. */ +	 +	/* Some adapters have the station address at offset 15 instead of offset +	   zero.  Check for it, and fix it if needed. */ +	if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff) +		sa_offset = 15; +	 +	for (i = 0; i < 3; i++) +		((unsigned short *)dev->dev_addr)[i] = +			ntohs(eeprom_op(ioaddr, EE_READ(sa_offset + i))); +	 +	write_reg(ioaddr, CMR2, CMR2_NULL); +} + +/* +  An EEPROM read command starts by shifting out 0x60+address, and then +  shifting in the serial data. See the NatSemi databook for details. + *		   ________________ + * CS : __| + *			   ___	   ___ + * CLK: ______|	  |___|	  | + *		 __ _______ _______ + * DI :	 __X_______X_______X + * DO :	 _________X_______X + */ + +static unsigned short eeprom_op(short ioaddr, unsigned int cmd) +{ +	unsigned eedata_out = 0; +	int num_bits = EE_CMD_SIZE; +	 +	while (--num_bits >= 0) { +		char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0; +		write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW); +		eeprom_delay(5); +		write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH); +		eedata_out <<= 1; +		if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ) +			eedata_out++; +		eeprom_delay(5); +	} +	write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS); +	return eedata_out; +} + + +/* Open/initialize the board.  This is called (in the current kernel) +   sometime after booting when the 'ifconfig' program is run. + +   This routine sets everything up anew at each open, even +   registers that "should" only need to be set once at boot, so that +   there is non-reboot way to recover if something goes wrong. + +   This is an attachable device: if there is no dev->priv entry then it wasn't +   probed for at boot-time, and we need to probe for it again. +   */ +static int net_open(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; + +	/* The interrupt line is turned off (tri-stated) when the device isn't in +	   use.  That's especially important for "attached" interfaces where the +	   port or interrupt may be shared. */ +#ifndef SA_SHIRQ +	if (irq2dev_map[dev->irq] != 0 +		|| (irq2dev_map[dev->irq] = dev) == 0 +		|| REQUEST_IRQ(dev->irq, &net_interrupt, 0, "ATP", dev)) { +		return -EAGAIN; +	} +#else +	if (request_irq(dev->irq, &net_interrupt, 0, "ATP Ethernet", dev)) +		return -EAGAIN; +#endif + +	MOD_INC_USE_COUNT; +	hardware_init(dev); +	dev->start = 1; + +	init_timer(&lp->timer); +	lp->timer.expires = RUN_AT(TIMED_CHECKER); +	lp->timer.data = (unsigned long)dev; +	lp->timer.function = &atp_timed_checker;    /* timer handler */ +	add_timer(&lp->timer); + +	return 0; +} + +/* This routine resets the hardware.  We initialize everything, assuming that +   the hardware may have been temporarily detached. */ +static void hardware_init(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +    int i; + +	/* Turn off the printer multiplexer on the 8012. */ +	for (i = 0; i < 8; i++) +		outb(mux_8012[i], ioaddr + PAR_DATA); +	write_reg_high(ioaddr, CMR1, CMR1h_RESET); +	 +    for (i = 0; i < 6; i++) +		write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); + +	write_reg_high(ioaddr, CMR2, lp->addr_mode); + +	if (net_debug > 2) { +		printk("%s: Reset: current Rx mode %d.\n", dev->name, +			   (read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f); +	} + +    write_reg(ioaddr, CMR2, CMR2_IRQOUT); +    write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); + +	/* Enable the interrupt line from the serial port. */ +	outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL); + +	/* Unmask the interesting interrupts. */ +    write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); +    write_reg_high(ioaddr, IMR, ISRh_RxErr); + +	lp->tx_unit_busy = 0; +    lp->pac_cnt_in_tx_buf = 0; +	lp->saved_tx_size = 0; + +	dev->tbusy = 0; +	dev->interrupt = 0; +} + +static void trigger_send(short ioaddr, int length) +{ +	write_reg_byte(ioaddr, TxCNT0, length & 0xff); +	write_reg(ioaddr, TxCNT1, length >> 8); +	write_reg(ioaddr, CMR1, CMR1_Xmit); +} + +static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode) +{ +    length = (length + 1) & ~1;		/* Round up to word length. */ +    outb(EOC+MAR, ioaddr + PAR_DATA); +    if ((data_mode & 1) == 0) { +		/* Write the packet out, starting with the write addr. */ +		outb(WrAddr+MAR, ioaddr + PAR_DATA); +		do { +			write_byte_mode0(ioaddr, *packet++); +		} while (--length > 0) ; +    } else { +		/* Write the packet out in slow mode. */ +		unsigned char outbyte = *packet++; + +		outb(Ctrl_LNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL); +		outb(WrAddr+MAR, ioaddr + PAR_DATA); + +		outb((outbyte & 0x0f)|0x40, ioaddr + PAR_DATA); +		outb(outbyte & 0x0f, ioaddr + PAR_DATA); +		outbyte >>= 4; +		outb(outbyte & 0x0f, ioaddr + PAR_DATA); +		outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL); +		while (--length > 0) +			write_byte_mode1(ioaddr, *packet++); +    } +    /* Terminate the Tx frame.  End of write: ECB. */ +    outb(0xff, ioaddr + PAR_DATA); +    outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL); +} + +static int +net_send_packet(struct sk_buff *skb, struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; + +#ifndef final_version +	if (skb == NULL || skb->len <= 0) { +		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", +			   dev->name); +		dev_tint(dev); +		return 0; +	} +#endif + +	/* Use transmit-while-tbusy as a crude error timer. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) { +		if (jiffies - dev->trans_start < TX_TIMEOUT) +			return 1; +		printk("%s: transmit timed out, %s?\n", dev->name, +			   inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem" +			   :  "IRQ conflict"); +		lp->stats.tx_errors++; +		/* Try to restart the adapter. */ +		hardware_init(dev); +		dev->tbusy=0; +		dev->trans_start = jiffies; +		return 1; +	} else { +		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		unsigned char *buf = skb->data; +		int flags; + +		/* Disable interrupts by writing 0x00 to the Interrupt Mask Register. +		   This sequence must not be interrupted by an incoming packet. */ +		save_flags(flags); +		cli(); +		write_reg(ioaddr, IMR, 0); +		write_reg_high(ioaddr, IMR, 0); +		restore_flags(flags); + +		write_packet(ioaddr, length, buf, dev->if_port); + +		lp->pac_cnt_in_tx_buf++; +		if (lp->tx_unit_busy == 0) { +			trigger_send(ioaddr, length); +			lp->saved_tx_size = 0; 				/* Redundant */ +			lp->re_tx = 0; +			lp->tx_unit_busy = 1; +		} else +			lp->saved_tx_size = length; + +		dev->trans_start = jiffies; +		/* Re-enable the LPT interrupts. */ +		write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); +		write_reg_high(ioaddr, IMR, ISRh_RxErr); +	} + +	dev_kfree_skb (skb, FREE_WRITE); + +	return 0; +} + +/* The typical workload of the driver: +   Handle the network interface interrupts. */ +static void +net_interrupt IRQ(int irq, void *dev_instance, struct pt_regs * regs) +{ +#ifdef SA_SHIRQ +	struct device *dev = (struct device *)dev_instance; +#else +	struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif +	struct net_local *lp; +	int ioaddr, status, boguscount = 20; +	static int num_tx_since_rx = 0; + +	if (dev == NULL) { +		printk ("ATP_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +	dev->interrupt = 1; + +	ioaddr = dev->base_addr; +	lp = (struct net_local *)dev->priv; + +	/* Disable additional spurious interrupts. */ +	outb(Ctrl_SelData, ioaddr + PAR_CONTROL); + +	/* The adapter's output is currently the IRQ line, switch it to data. */ +	write_reg(ioaddr, CMR2, CMR2_NULL); +	write_reg(ioaddr, IMR, 0); + +	if (net_debug > 5) printk("%s: In interrupt ", dev->name); +    while (--boguscount > 0) { +		status = read_nibble(ioaddr, ISR); +		if (net_debug > 5) printk("loop status %02x..", status); + +		if (status & (ISR_RxOK<<3)) { +			write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */ +			do { +				int read_status = read_nibble(ioaddr, CMR1); +				if (net_debug > 6) +					printk("handling Rx packet %02x..", read_status); +				/* We acknowledged the normal Rx interrupt, so if the interrupt +				   is still outstanding we must have a Rx error. */ +				if (read_status & (CMR1_IRQ << 3)) { /* Overrun. */ +					lp->stats.rx_over_errors++; +					/* Set to no-accept mode long enough to remove a packet. */ +					write_reg_high(ioaddr, CMR2, CMR2h_OFF); +					net_rx(dev); +					/* Clear the interrupt and return to normal Rx mode. */ +					write_reg_high(ioaddr, ISR, ISRh_RxErr); +					write_reg_high(ioaddr, CMR2, lp->addr_mode); +				} else if ((read_status & (CMR1_BufEnb << 3)) == 0) { +					net_rx(dev); +					dev->last_rx = jiffies; +					num_tx_since_rx = 0; +				} else +					break; +			} while (--boguscount > 0); +		} else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) { +			if (net_debug > 6)  printk("handling Tx done.."); +			/* Clear the Tx interrupt.  We should check for too many failures +			   and reinitialize the adapter. */ +			write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK); +			if (status & (ISR_TxErr<<3)) { +				lp->stats.collisions++; +				if (++lp->re_tx > 15) { +					lp->stats.tx_aborted_errors++; +					hardware_init(dev); +					break; +				} +				/* Attempt to retransmit. */ +				if (net_debug > 6)  printk("attempting to ReTx"); +				write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit); +			} else { +				/* Finish up the transmit. */ +				lp->stats.tx_packets++; +				lp->pac_cnt_in_tx_buf--; +				if ( lp->saved_tx_size) { +					trigger_send(ioaddr, lp->saved_tx_size); +					lp->saved_tx_size = 0; +					lp->re_tx = 0; +				} else +					lp->tx_unit_busy = 0; +				dev->tbusy = 0; +				mark_bh(NET_BH);	/* Inform upper layers. */ +			} +			num_tx_since_rx++; +		} else if (num_tx_since_rx > 8 +				   && jiffies > dev->last_rx + 100) { +			if (net_debug > 2) +				printk("%s: Missed packet? No Rx after %d Tx and %ld jiffies" +					   " status %02x  CMR1 %02x.\n", dev->name, +					   num_tx_since_rx, jiffies - dev->last_rx, status, +					   (read_nibble(ioaddr, CMR1) >> 3) & 15); +			lp->stats.rx_missed_errors++; +			hardware_init(dev); +			num_tx_since_rx = 0; +			break; +		} else +			break; +    } + +	/* This following code fixes a rare (and very difficult to track down) +	   problem where the adapter forgets its ethernet address. */ +	{ +		int i; +		for (i = 0; i < 6; i++) +			write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); +	} + +	/* Tell the adapter that it can go back to using the output line as IRQ. */ +    write_reg(ioaddr, CMR2, CMR2_IRQOUT); +	/* Enable the physical interrupt line, which is sure to be low until.. */ +	outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL); +	/* .. we enable the interrupt sources. */ +	write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK); +	write_reg_high(ioaddr, IMR, ISRh_RxErr); 			/* Hmmm, really needed? */ + +	if (net_debug > 5) printk("exiting interrupt.\n"); + +	dev->interrupt = 0; + +	return; +} + +#ifdef TIMED_CHECKER +/* This following code fixes a rare (and very difficult to track down) +   problem where the adapter forgets its ethernet address. */ +static void atp_timed_checker(unsigned long data) +{ +	struct device *dev = (struct device *)data; +	int ioaddr = dev->base_addr; +	struct net_local *lp = (struct net_local *)dev->priv; +	int tickssofar = jiffies - lp->last_rx_time; +	int i; + +	if (tickssofar > 2*HZ  &&  dev->interrupt == 0) { +#if 1 +		for (i = 0; i < 6; i++) +			write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); +		lp->last_rx_time = jiffies; +#else +		for (i = 0; i < 6; i++) +			if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i]) +				{ +			struct net_local *lp = (struct net_local *)atp_timed_dev->priv; +			write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]); +			if (i == 2) +			  lp->stats.tx_errors++; +			else if (i == 3) +			  lp->stats.tx_dropped++; +			else if (i == 4) +			  lp->stats.collisions++; +			else +			  lp->stats.rx_errors++; +		  } +#endif +	} +	lp->timer.expires = RUN_AT(TIMED_CHECKER); +	add_timer(&lp->timer); +} +#endif + +/* We have a good packet(s), get it/them out of the buffers. */ +static void net_rx(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	struct rx_header rx_head; + +	/* Process the received packet. */ +	outb(EOC+MAR, ioaddr + PAR_DATA); +	read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port); +	if (net_debug > 5) +		printk(" rx_count %04x %04x %04x %04x..", rx_head.pad, +			   rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr); +	if ((rx_head.rx_status & 0x77) != 0x01) { +		lp->stats.rx_errors++; +		if (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++; +		else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++; +		if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n", +								  dev->name, rx_head.rx_status); +		if  (rx_head.rx_status & 0x0020) { +			lp->stats.rx_fifo_errors++; +			write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE); +			write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); +		} else if (rx_head.rx_status & 0x0050) +			hardware_init(dev); +		return; +	} else { +		/* Malloc up new buffer. The "-4" is omits the FCS (CRC). */ +		int pkt_len = (rx_head.rx_count & 0x7ff) - 4; +		struct sk_buff *skb; +		 +		skb = DEV_ALLOC_SKB(pkt_len + 2); +		if (skb == NULL) { +			printk("%s: Memory squeeze, dropping packet.\n", dev->name); +			lp->stats.rx_dropped++; +			goto done; +		} +		skb->dev = dev; +		 +#if LINUX_VERSION_CODE >= 0x10300 +		skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +		read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port); +		skb->protocol = eth_type_trans(skb, dev); +#else +		read_block(ioaddr, pkt_len, skb->data, dev->if_port); +		skb->len = pkt_len; +#endif +		netif_rx(skb); +		lp->stats.rx_packets++; +	} + done: +	write_reg(ioaddr, CMR1, CMR1_NextPkt); +	lp->last_rx_time = jiffies; +	return; +} + +static void read_block(short ioaddr, int length, unsigned char *p, int data_mode) +{ + +	if (data_mode <= 3) { /* Mode 0 or 1 */ +		outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL); +		outb(length == 8  ?  RdAddr | HNib | MAR  :  RdAddr | MAR, +			 ioaddr + PAR_DATA); +		if (data_mode <= 1) { /* Mode 0 or 1 */ +			do  *p++ = read_byte_mode0(ioaddr);  while (--length > 0); +		} else	/* Mode 2 or 3 */ +			do  *p++ = read_byte_mode2(ioaddr);  while (--length > 0); +	} else if (data_mode <= 5) +		do      *p++ = read_byte_mode4(ioaddr);  while (--length > 0); +	else +		do      *p++ = read_byte_mode6(ioaddr);  while (--length > 0); + +    outb(EOC+HNib+MAR, ioaddr + PAR_DATA); +	outb(Ctrl_SelData, ioaddr + PAR_CONTROL); +} + +/* The inverse routine to net_open(). */ +static int +net_close(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; + +	dev->tbusy = 1; +	dev->start = 0; + +	del_timer(&lp->timer); + +	/* Flush the Tx and disable Rx here. */ +	lp->addr_mode = CMR2h_OFF; +	write_reg_high(ioaddr, CMR2, CMR2h_OFF); + +	/* Free the IRQ line. */ +	outb(0x00, ioaddr + PAR_CONTROL); +	FREE_IRQ(dev->irq, dev); +#ifndef SA_SHIRQ +	irq2dev_map[dev->irq] = 0; +#endif + +	/* Reset the ethernet hardware and activate the printer pass-through. */ +    write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +/* Get the current statistics.	This may be called with the card open or +   closed. */ +static struct enet_statistics * +net_get_stats(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	return &lp->stats; +} + +/* + *	Set or clear the multicast filter for this adapter. + */ + +/* The little-endian AUTODIN32 ethernet CRC calculation. +   This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ +    unsigned int crc = 0xffffffff;	/* Initial value. */ +    while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +    } +    return crc; +} + +static void +#ifdef NEW_MULTICAST +set_rx_mode_8002(struct device *dev) +#else +static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs); +#endif +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	short ioaddr = dev->base_addr; + +	if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) { +		/* We must make the kernel realise we had to move +		 *	into promisc mode or we start all out war on +		 *	the cable. - AC +		 */ +		dev->flags|=IFF_PROMISC; +		lp->addr_mode = CMR2h_PROMISC; +	} else +		lp->addr_mode = CMR2h_Normal; +	write_reg_high(ioaddr, CMR2, lp->addr_mode); +} + +static void +#ifdef NEW_MULTICAST +set_rx_mode_8012(struct device *dev) +#else +static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs); +#endif +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	short ioaddr = dev->base_addr; +	unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */ +	int i; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		new_mode = CMR2h_PROMISC; +	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to filter perfectly -- accept all multicasts. */ +		memset(mc_filter, 0xff, sizeof(mc_filter)); +		new_mode = CMR2h_Normal; +	} else { +		struct dev_mc_list *mclist; + +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) +			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, +					mc_filter); +		new_mode = CMR2h_Normal; +	} +	lp->addr_mode = new_mode; +    write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */ +    for (i = 0; i < 8; i++) +		write_reg_byte(ioaddr, i, mc_filter[i]); +	if (net_debug > 2 || 1) { +		lp->addr_mode = 1; +		printk("%s: Mode %d, setting multicast filter to", +			   dev->name, lp->addr_mode); +		for (i = 0; i < 8; i++) +			printk(" %2.2x", mc_filter[i]); +		printk(".\n"); +	} + +	write_reg_high(ioaddr, CMR2, lp->addr_mode); +    write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */ +} + +#ifdef MODULE +static int debug = 1; +int +init_module(void) +{ +	net_debug = debug; +	root_atp_dev = NULL; +	atp_init(0); +	return 0; +} + +void +cleanup_module(void) +{ +	struct device *next_dev; + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	/* No need to release_region(), since we never snarf it. */ +	while (root_atp_dev) { +		next_dev = ((struct net_local *)root_atp_dev->priv)->next_module; +		unregister_netdev(root_atp_dev); +		kfree(root_atp_dev); +		root_atp_dev = next_dev; +	} +} +#endif /* MODULE */ + + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c atp.c" + *  version-control: t + *  kept-new-versions: 5 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/atp.h b/linux/src/drivers/net/atp.h new file mode 100644 index 0000000..b4d1933 --- /dev/null +++ b/linux/src/drivers/net/atp.h @@ -0,0 +1,274 @@ +/* Linux header file for the ATP pocket ethernet adapter. */ +/* v1.04 4/1/97 becker@cesdis.gsfc.nasa.gov. */ + +#include <linux/if_ether.h> +#include <linux/types.h> +#include <asm/io.h> + +struct net_local { +#ifdef __KERNEL__ +    struct enet_statistics stats; +#endif +    struct device *next_module; +    struct timer_list timer;	/* Media selection timer. */ +    long last_rx_time;		/* Last Rx, in jiffies, to handle Rx hang. */ +    ushort saved_tx_size; +    unsigned tx_unit_busy:1; +    unsigned char re_tx,	/* Number of packet retransmissions. */ +      addr_mode,		/* Current Rx filter e.g. promiscuous, etc. */ +      pac_cnt_in_tx_buf, +      chip_type; +}; + +struct rx_header { +    ushort pad;			/* Pad. */ +    ushort rx_count; +    ushort rx_status;		/* Unknown bit assignments :-<.  */ +    ushort cur_addr;		/* Apparently the current buffer address(?) */ +}; + +#define PAR_DATA	0 +#define PAR_STATUS	1 +#define PAR_CONTROL 2 + +enum chip_type { RTL8002, RTL8012 }; + +#define Ctrl_LNibRead	0x08	/* LP_PSELECP */ +#define Ctrl_HNibRead	0 +#define Ctrl_LNibWrite	0x08	/* LP_PSELECP */ +#define Ctrl_HNibWrite	0 +#define Ctrl_SelData	0x04	/* LP_PINITP */ +#define Ctrl_IRQEN	0x10	/* LP_PINTEN */ + +#define EOW	0xE0 +#define EOC	0xE0 +#define WrAddr	0x40	/* Set address of EPLC read, write register. */ +#define RdAddr	0xC0 +#define HNib	0x10 + +enum page0_regs +{ +    /* The first six registers hold the ethernet physical station address. */ +    PAR0 = 0, PAR1 = 1, PAR2 = 2, PAR3 = 3, PAR4 = 4, PAR5 = 5, +    TxCNT0 = 6, TxCNT1 = 7,		/* The transmit byte count. */ +    TxSTAT = 8, RxSTAT = 9,		/* Tx and Rx status. */ +    ISR = 10, IMR = 11,			/* Interrupt status and mask. */ +    CMR1 = 12,				/* Command register 1. */ +    CMR2 = 13,				/* Command register 2. */ +    MODSEL = 14,			/* Mode select register. */ +    MAR = 14,				/* Memory address register (?). */ +    CMR2_h = 0x1d, }; + +enum eepage_regs +{ PROM_CMD = 6, PROM_DATA = 7 };	/* Note that PROM_CMD is in the "high" bits. */ + + +#define ISR_TxOK	0x01 +#define ISR_RxOK	0x04 +#define ISR_TxErr	0x02 +#define ISRh_RxErr	0x11	/* ISR, high nibble */ + +#define CMR1h_MUX	0x08	/* Select printer multiplexor on 8012. */ +#define CMR1h_RESET	0x04	/* Reset. */ +#define CMR1h_RxENABLE	0x02	/* Rx unit enable.  */ +#define CMR1h_TxENABLE	0x01	/* Tx unit enable.  */ +#define CMR1h_TxRxOFF	0x00 +#define CMR1_ReXmit	0x08	/* Trigger a retransmit. */ +#define CMR1_Xmit	0x04	/* Trigger a transmit. */ +#define	CMR1_IRQ	0x02	/* Interrupt active. */ +#define	CMR1_BufEnb	0x01	/* Enable the buffer(?). */ +#define	CMR1_NextPkt	0x01	/* Enable the buffer(?). */ + +#define CMR2_NULL	8 +#define CMR2_IRQOUT	9 +#define CMR2_RAMTEST	10 +#define CMR2_EEPROM	12	/* Set to page 1, for reading the EEPROM. */ + +#define CMR2h_OFF	0	/* No accept mode. */ +#define CMR2h_Physical	1	/* Accept a physical address match only. */ +#define CMR2h_Normal	2	/* Accept physical and broadcast address. */ +#define CMR2h_PROMISC	3	/* Promiscuous mode. */ + +/* An inline function used below: it differs from inb() by explicitly return an unsigned +   char, saving a truncation. */ +extern inline unsigned char inbyte(unsigned short port) +{ +    unsigned char _v; +    __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port)); +    return _v; +} + +/* Read register OFFSET. +   This command should always be terminated with read_end(). */ +extern inline unsigned char read_nibble(short port, unsigned char offset) +{ +    unsigned char retval; +    outb(EOC+offset, port + PAR_DATA); +    outb(RdAddr+offset, port + PAR_DATA); +    inbyte(port + PAR_STATUS);		/* Settling time delay */ +    retval = inbyte(port + PAR_STATUS); +    outb(EOC+offset, port + PAR_DATA); + +    return retval; +} + +/* Functions for bulk data read.  The interrupt line is always disabled. */ +/* Get a byte using read mode 0, reading data from the control lines. */ +extern inline unsigned char read_byte_mode0(short ioaddr) +{ +    unsigned char low_nib; + +    outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL); +    inbyte(ioaddr + PAR_STATUS); +    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f; +    outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL); +    inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */ +    inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */ +    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0); +} + +/* The same as read_byte_mode0(), but does multiple inb()s for stability. */ +extern inline unsigned char read_byte_mode2(short ioaddr) +{ +    unsigned char low_nib; + +    outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL); +    inbyte(ioaddr + PAR_STATUS); +    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f; +    outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL); +    inbyte(ioaddr + PAR_STATUS);	/* Settling time delay -- needed!  */ +    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0); +} + +/* Read a byte through the data register. */ +extern inline unsigned char read_byte_mode4(short ioaddr) +{ +    unsigned char low_nib; + +    outb(RdAddr | MAR, ioaddr + PAR_DATA); +    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f; +    outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA); +    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0); +} + +/* Read a byte through the data register, double reading to allow settling. */ +extern inline unsigned char read_byte_mode6(short ioaddr) +{ +    unsigned char low_nib; + +    outb(RdAddr | MAR, ioaddr + PAR_DATA); +    inbyte(ioaddr + PAR_STATUS); +    low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f; +    outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA); +    inbyte(ioaddr + PAR_STATUS); +    return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0); +} + +extern inline void +write_reg(short port, unsigned char reg, unsigned char value) +{ +    unsigned char outval; +    outb(EOC | reg, port + PAR_DATA); +    outval = WrAddr | reg; +    outb(outval, port + PAR_DATA); +    outb(outval, port + PAR_DATA);	/* Double write for PS/2. */ + +    outval &= 0xf0; +    outval |= value; +    outb(outval, port + PAR_DATA); +    outval &= 0x1f; +    outb(outval, port + PAR_DATA); +    outb(outval, port + PAR_DATA); + +    outb(EOC | outval, port + PAR_DATA); +} + +extern inline void +write_reg_high(short port, unsigned char reg, unsigned char value) +{ +    unsigned char outval = EOC | HNib | reg; + +    outb(outval, port + PAR_DATA); +    outval &= WrAddr | HNib | 0x0f; +    outb(outval, port + PAR_DATA); +    outb(outval, port + PAR_DATA);	/* Double write for PS/2. */ + +    outval = WrAddr | HNib | value; +    outb(outval, port + PAR_DATA); +    outval &= HNib | 0x0f;		/* HNib | value */ +    outb(outval, port + PAR_DATA); +    outb(outval, port + PAR_DATA); + +    outb(EOC | HNib | outval, port + PAR_DATA); +} + +/* Write a byte out using nibble mode.  The low nibble is written first. */ +extern inline void +write_reg_byte(short port, unsigned char reg, unsigned char value) +{ +    unsigned char outval; +    outb(EOC | reg, port + PAR_DATA); 	/* Reset the address register. */ +    outval = WrAddr | reg; +    outb(outval, port + PAR_DATA); +    outb(outval, port + PAR_DATA);	/* Double write for PS/2. */ + +    outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA); +    outb(value & 0x0f, port + PAR_DATA); +    value >>= 4; +    outb(value, port + PAR_DATA); +    outb(0x10 | value, port + PAR_DATA); +    outb(0x10 | value, port + PAR_DATA); + +    outb(EOC  | value, port + PAR_DATA); 	/* Reset the address register. */ +} + +/* + * Bulk data writes to the packet buffer.  The interrupt line remains enabled. + * The first, faster method uses only the dataport (data modes 0, 2 & 4). + * The second (backup) method uses data and control regs (modes 1, 3 & 5). + * It should only be needed when there is skew between the individual data + * lines. + */ +extern inline void write_byte_mode0(short ioaddr, unsigned char value) +{ +    outb(value & 0x0f, ioaddr + PAR_DATA); +    outb((value>>4) | 0x10, ioaddr + PAR_DATA); +} + +extern inline void write_byte_mode1(short ioaddr, unsigned char value) +{ +    outb(value & 0x0f, ioaddr + PAR_DATA); +    outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL); +    outb((value>>4) | 0x10, ioaddr + PAR_DATA); +    outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL); +} + +/* Write 16bit VALUE to the packet buffer: the same as above just doubled. */ +extern inline void write_word_mode0(short ioaddr, unsigned short value) +{ +    outb(value & 0x0f, ioaddr + PAR_DATA); +    value >>= 4; +    outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA); +    value >>= 4; +    outb(value & 0x0f, ioaddr + PAR_DATA); +    value >>= 4; +    outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA); +} + +/*  EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */ +#define EE_CS		0x02	/* EEPROM chip select. */ +#define EE_CLK_HIGH	0x12 +#define EE_CLK_LOW	0x16 +#define EE_DATA_WRITE	0x01	/* EEPROM chip data in. */ +#define EE_DATA_READ	0x08	/* EEPROM chip data out. */ + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay(ticks) \ +do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD(offset)	(((5 << 6) + (offset)) << 17) +#define EE_READ(offset) 	(((6 << 6) + (offset)) << 17) +#define EE_ERASE(offset)	(((7 << 6) + (offset)) << 17) +#define EE_CMD_SIZE	27	/* The command+address+data size. */ diff --git a/linux/src/drivers/net/auto_irq.c b/linux/src/drivers/net/auto_irq.c new file mode 100644 index 0000000..82bc7b1 --- /dev/null +++ b/linux/src/drivers/net/auto_irq.c @@ -0,0 +1,123 @@ +/* auto_irq.c: Auto-configure IRQ lines for linux. */ +/* +    Written 1994 by Donald Becker. + +    The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +    Center of Excellence in Space Data and Information Sciences +      Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +    This code is a general-purpose IRQ line detector for devices with +    jumpered IRQ lines.  If you can make the device raise an IRQ (and +    that IRQ line isn't already being used), these routines will tell +    you what IRQ line it's using -- perfect for those oh-so-cool boot-time +    device probes! + +    To use this, first call autoirq_setup(timeout). TIMEOUT is how many +    'jiffies' (1/100 sec.) to detect other devices that have active IRQ lines, +    and can usually be zero at boot.  'autoirq_setup()' returns the bit +    vector of nominally-available IRQ lines (lines may be physically in-use, +    but not yet registered to a device). +    Next, set up your device to trigger an interrupt. +    Finally call autoirq_report(TIMEOUT) to find out which IRQ line was +    most recently active.  The TIMEOUT should usually be zero, but may +    be set to the number of jiffies to wait for a slow device to raise an IRQ. + +    The idea of using the setup timeout to filter out bogus IRQs came from +    the serial driver. +*/ + + +#ifdef version +static const char *version= +"auto_irq.c:v1.11 Donald Becker (becker@cesdis.gsfc.nasa.gov)"; +#endif + +#include <linux/sched.h> +#include <linux/delay.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <linux/netdevice.h> + +struct device *irq2dev_map[NR_IRQS] = {0, 0, /* ... zeroed */}; + +unsigned long irqs_busy = 0x2147;		/* The set of fixed IRQs (keyboard, timer, etc) */ +unsigned long irqs_used = 0x0001;		/* The set of fixed IRQs sometimes enabled. */ +unsigned long irqs_reserved = 0x0000;		/* An advisory "reserved" table. */ +unsigned long irqs_shared = 0x0000;		/* IRQ lines "shared" among conforming cards.*/ + +static volatile unsigned long irq_bitmap;	/* The irqs we actually found. */ +static unsigned long irq_handled;		/* The irq lines we have a handler on. */ +static volatile int irq_number;			/* The latest irq number we actually found. */ + +static void autoirq_probe(int irq, void *dev_id, struct pt_regs * regs) +{ +	irq_number = irq; +	set_bit(irq, (void *)&irq_bitmap);	/* irq_bitmap |= 1 << irq; */ +	/* This code used to disable the irq. However, the interrupt stub +	 * would then re-enable the interrupt with (potentially) disastrous +	 * consequences +	 */ +	free_irq(irq, dev_id); +	return; +} + +int autoirq_setup(int waittime) +{ +	int i; +	unsigned long timeout = jiffies + waittime; +	unsigned long boguscount = (waittime*loops_per_sec) / 100; + +	irq_handled = 0; +	irq_bitmap = 0; + +	for (i = 0; i < 16; i++) { +		if (test_bit(i, &irqs_busy) == 0 +			&& request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0) +			set_bit(i, (void *)&irq_handled);	/* irq_handled |= 1 << i;*/ +	} +	/* Update our USED lists. */ +	irqs_used |= ~irq_handled; + +	/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */ +	while (timeout > jiffies  &&  --boguscount > 0) +		; + +	irq_handled &= ~irq_bitmap; + +	irq_number = 0;	/* We are interested in new interrupts from now on */ + +	return irq_handled; +} + +int autoirq_report(int waittime) +{ +	int i; +	unsigned long timeout = jiffies+waittime; +	unsigned long boguscount = (waittime*loops_per_sec) / 100; + +	/* Hang out at least <waittime> jiffies waiting for the IRQ. */ + +	while (timeout > jiffies  &&  --boguscount > 0) +		if (irq_number) +			break; + +	irq_handled &= ~irq_bitmap;	/* This eliminates the already reset handlers */ + +	/* Retract the irq handlers that we installed. */ +	for (i = 0; i < 16; i++) { +		if (test_bit(i, (void *)&irq_handled)) +			free_irq(i, NULL); +	} +	return irq_number; +} + +/* + * Local variables: + *  compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c" + *  version-control: t + *  kept-new-versions: 5 + *  c-indent-level: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/cb_shim.c b/linux/src/drivers/net/cb_shim.c new file mode 100644 index 0000000..599b5bb --- /dev/null +++ b/linux/src/drivers/net/cb_shim.c @@ -0,0 +1,296 @@ +/* cb_shim.c: Linux CardBus device support code. */ +/* +	Written 1999-2002 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by +	reference.  This is not a documented interface.  Drivers incorporating +	or interacting with these functions are derivative works and thus +	are covered the GPL.  They must include an explicit GPL notice. + +	This code provides a shim to allow newer drivers to interact with the +	older Cardbus driver activation code.  The functions supported are +	attach, suspend, power-off, resume and eject. + +	The author may be reached as becker@scyld.com, or +	Donald Becker +	Scyld Computing Corporation +	410 Severn Ave., Suite 210 +	Annapolis MD 21403 + +	Support and updates available at +	http://www.scyld.com/network/drivers.html + +	Other contributers:  (none yet) +*/ + +static const char version1[] = +"cb_shim.c:v1.03 7/12/2002  Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +" http://www.scyld.com/linux/drivers.html\n"; + +/* Module options. */ +static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <asm/io.h> + +/* These might be awkward to locate. */ +#include <pcmcia/driver_ops.h> +#include "pci-scan.h" +#include "kern_compat.h" + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Hot-swap-PCI and Cardbus event dispatch"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Enable additional status messages (0-7)"); + +/* Note: this is used in a slightly sleazy manner: it is passed to routines +   that expect and return just dev_node_t.  However using the too-simple +   dev_node_t complicates devices management -- older drivers had to +   look up dev_node_t.name in their private list. */ + +struct registered_pci_device { +	struct dev_node_t node; +	int magic; +	struct registered_pci_device *next; +	struct drv_id_info *drv_info; +	struct pci_dev *pci_loc; +	void *dev_instance; +} static *root_pci_devs = 0; + +struct drv_shim { +	struct drv_id_info *did; +	struct driver_operations drv_ops; +	int magic; +	struct drv_shim *next; +} static *root_drv_id = 0; + +static void drv_power_op(struct dev_node_t *node, enum drv_pwr_action action) +{ +	struct registered_pci_device **devp, **next, *rpin = (void *)node, *rp; +	if (debug > 1) +		printk(KERN_DEBUG "power operation(%s, %d).\n", +			   rpin->drv_info->name, action); +	/* With our wrapper structure we can almost do +	   rpin->drv_info->pwr_event(rpin->dev_instance, action); +	   But the detach operation requires us to remove the object from the +	   list, so we check for uncontrolled "ghost" devices. */ +	for (devp = &root_pci_devs; *devp; devp = next) { +		rp = *devp; +		next = &rp->next; +		if (rp == rpin) { +			if (rp->drv_info->pwr_event) +				rp->drv_info->pwr_event((*devp)->dev_instance, action); +			else +				printk(KERN_ERR "No power event hander for driver %s.\n", +					   rpin->drv_info->name); +			if (action == DRV_DETACH) { +				kfree(rp); +				*devp = *next; +				MOD_DEC_USE_COUNT; +			} +			return; +		} +	} +	if (debug) +		printk(KERN_WARNING "power operation(%s, %d) for a ghost device.\n", +			   node->dev_name, action); +} +/* Wrappers / static lambdas. */ +static void drv_suspend(struct dev_node_t *node) +{ +	drv_power_op(node, DRV_SUSPEND); +} +static void drv_resume(struct dev_node_t *node) +{ +	drv_power_op(node, DRV_RESUME); +} +static void drv_detach(struct dev_node_t *node) +{ +	drv_power_op(node, DRV_DETACH); +} + +/* The CardBus interaction does not identify the driver the attach() is +   for, thus we must search for the ID in all PCI device tables. +   While ugly, we likely only have one driver loaded anyway. +*/ +static dev_node_t *drv_attach(struct dev_locator_t *loc) +{ +	struct drv_shim *dp; +	struct drv_id_info *drv_id = NULL; +	struct pci_id_info *pci_tbl = NULL; +	u32 pci_id, subsys_id, pci_rev, pciaddr; +	u8 irq; +	int chip_idx = 0, pci_flags, bus, devfn; +	long ioaddr; +	void *newdev; + +	if (debug > 1) +		printk(KERN_INFO "drv_attach()\n"); +	if (loc->bus != LOC_PCI) return NULL; +	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; +	if (debug > 1) +		printk(KERN_DEBUG "drv_attach(bus %d, function %d)\n", bus, devfn); + +	pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &pci_id); +	pcibios_read_config_dword(bus, devfn, PCI_SUBSYSTEM_ID, &subsys_id); +	pcibios_read_config_dword(bus, devfn, PCI_REVISION_ID, &pci_rev); +	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); +	for (dp = root_drv_id; dp; dp = dp->next) { +		drv_id = dp->did; +		pci_tbl = drv_id->pci_dev_tbl; +		for (chip_idx = 0; pci_tbl[chip_idx].name; chip_idx++) { +			struct pci_id_info *chip = &pci_tbl[chip_idx]; +			if ((pci_id & chip->id.pci_mask) == chip->id.pci +				&& (subsys_id & chip->id.subsystem_mask) == chip->id.subsystem +				&& (pci_rev & chip->id.revision_mask) == chip->id.revision) +				break; +		} +		if (pci_tbl[chip_idx].name) 		/* Compiled out! */ +			break; +	} +	if (dp == 0) { +		printk(KERN_WARNING "No driver match for device %8.8x at %d/%d.\n", +			   pci_id, bus, devfn); +		return 0; +	} +	pci_flags = pci_tbl[chip_idx].pci_flags; +	pcibios_read_config_dword(bus, devfn, ((pci_flags >> 2) & 0x1C) + 0x10, +							  &pciaddr); +	if ((pciaddr & PCI_BASE_ADDRESS_SPACE_IO)) { +		ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK; +	} else +		ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, +							   pci_tbl[chip_idx].io_size); +	if (ioaddr == 0 || irq == 0) { +		printk(KERN_ERR "The %s at %d/%d was not assigned an %s.\n" +			   KERN_ERR "  It will not be activated.\n", +			   pci_tbl[chip_idx].name, bus, devfn, +			   ioaddr == 0 ? "address" : "IRQ"); +		return NULL; +	} +	printk(KERN_INFO "Found a %s at %d/%d address 0x%x->0x%lx IRQ %d.\n", +		   pci_tbl[chip_idx].name, bus, devfn, pciaddr, ioaddr, irq); +	{ +		u16 pci_command; +		pcibios_read_config_word(bus, devfn, PCI_COMMAND, &pci_command); +		printk(KERN_INFO "%s at %d/%d command 0x%x.\n", +		   pci_tbl[chip_idx].name, bus, devfn, pci_command); +	} + +	newdev = drv_id->probe1(pci_find_slot(bus, devfn), 0, +							ioaddr, irq, chip_idx, 0); +	if (newdev) { +		struct registered_pci_device *hsdev = +			kmalloc(sizeof(struct registered_pci_device), GFP_KERNEL); +		if (drv_id->pci_class == PCI_CLASS_NETWORK_ETHERNET<<8) +			strcpy(hsdev->node.dev_name, ((struct net_device *)newdev)->name); +		hsdev->node.major = hsdev->node.minor = 0; +		hsdev->node.next = NULL; +		hsdev->drv_info = drv_id; +		hsdev->dev_instance = newdev; +		hsdev->next = root_pci_devs; +		root_pci_devs = hsdev; +		drv_id->pwr_event(newdev, DRV_ATTACH); +		MOD_INC_USE_COUNT; +		return &hsdev->node; +	} +	return NULL; +} + +/* Add/remove a driver ID structure to our private list of known drivers. */ +int do_cb_register(struct drv_id_info *did) +{ +	struct driver_operations *dop; +	struct drv_shim *dshim = kmalloc(sizeof(*dshim), GFP_KERNEL); +	if (dshim == 0) +		return 0; +	if (debug > 1) +		printk(KERN_INFO "Registering driver support for '%s'.\n", +			   did->name); +	MOD_INC_USE_COUNT; +	dshim->did = did; +	dop = &dshim->drv_ops; +	dop->name = (char *)did->name; +	dop->attach = drv_attach; +	dop->suspend = drv_suspend; +	dop->resume = drv_resume; +	dop->detach = drv_detach; +	dshim->next = root_drv_id; +	root_drv_id = dshim; +	return register_driver(dop); +} + +void do_cb_unregister(struct drv_id_info *did) +{ +	struct drv_shim **dp; +	for (dp = &root_drv_id; *dp; dp = &(*dp)->next) +		if ((*dp)->did == did) { +			struct drv_shim *dshim = *dp; +			unregister_driver(&dshim->drv_ops); +			*dp = dshim->next; +			kfree(dshim); +			MOD_DEC_USE_COUNT; +			return; +		} +} + +extern int (*register_hotswap_hook)(struct drv_id_info *did); +extern void (*unregister_hotswap_hook)(struct drv_id_info *did); + +int (*old_cb_hook)(struct drv_id_info *did); +void (*old_un_cb_hook)(struct drv_id_info *did); + +int init_module(void) +{ +	if (debug) +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	old_cb_hook = register_hotswap_hook; +	old_un_cb_hook = unregister_hotswap_hook; +	register_hotswap_hook = do_cb_register; +	unregister_hotswap_hook = do_cb_unregister; +	return 0; +} +void cleanup_module(void) +{ +	register_hotswap_hook = 	old_cb_hook; +	unregister_hotswap_hook = old_un_cb_hook; +	return; +} + + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c cb_shim.c -I/usr/include/ -I/usr/src/pcmcia/include/" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ + diff --git a/linux/src/drivers/net/de4x5.c b/linux/src/drivers/net/de4x5.c new file mode 100644 index 0000000..c85bcdb --- /dev/null +++ b/linux/src/drivers/net/de4x5.c @@ -0,0 +1,5942 @@ +/*  de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500 +             ethernet driver for Linux. + +    Copyright 1994, 1995 Digital Equipment Corporation. + +    Testing resources for this driver have been made available +    in part by NASA Ames Research Center (mjacob@nas.nasa.gov). + +    The author may be reached at davies@maniac.ultranet.com. + +    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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR   IMPLIED +    WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF +    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN +    NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT,  INDIRECT, +    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +    NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF +    USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +    ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT +    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +    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., +    675 Mass Ave, Cambridge, MA 02139, USA. + +    Originally,   this  driver  was    written  for the  Digital   Equipment +    Corporation series of EtherWORKS ethernet cards: + +        DE425 TP/COAX EISA +	DE434 TP PCI +	DE435 TP/COAX/AUI PCI +	DE450 TP/COAX/AUI PCI +	DE500 10/100 PCI Fasternet + +    but it  will  now attempt  to  support all  cards which   conform to the +    Digital Semiconductor   SROM   Specification.    The  driver   currently +    recognises the following chips: + +        DC21040  (no SROM)  +	DC21041[A]   +	DC21140[A]  +	DC21142  +	DC21143  + +    So far the driver is known to work with the following cards: + +        KINGSTON +	Linksys +	ZNYX342 +	SMC8432 +	SMC9332 (w/new SROM) +	ZNYX31[45] +	ZNYX346 10/100 4 port (can act as a 10/100 bridge!)  + +    The driver has been tested on a relatively busy network using the DE425, +    DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred +    16M of data to a DECstation 5000/200 as follows: + +                TCP           UDP +             TX     RX     TX     RX +    DE425   1030k  997k   1170k  1128k +    DE434   1063k  995k   1170k  1125k +    DE435   1063k  995k   1170k  1125k +    DE500   1063k  998k   1170k  1125k  in 10Mb/s mode + +    All  values are typical (in   kBytes/sec) from a  sample  of 4 for  each +    measurement. Their error is +/-20k on a quiet (private) network and also +    depend on what load the CPU has. + +    ========================================================================= +    This driver  has been written substantially  from  scratch, although its +    inheritance of style and stack interface from 'ewrk3.c' and in turn from +    Donald Becker's 'lance.c' should be obvious. With the module autoload of +    every  usable DECchip board,  I  pinched Donald's 'next_module' field to +    link my modules together. + +    Upto 15 EISA cards can be supported under this driver, limited primarily +    by the available IRQ lines.  I have  checked different configurations of +    multiple depca, EtherWORKS 3 cards and de4x5 cards and  have not found a +    problem yet (provided you have at least depca.c v0.38) ... + +    PCI support has been added  to allow the driver  to work with the DE434, +    DE435, DE450 and DE500 cards. The I/O accesses are a bit of a kludge due +    to the differences in the EISA and PCI CSR address offsets from the base +    address. + +    The ability to load this  driver as a loadable  module has been included +    and used extensively  during the driver development  (to save those long +    reboot sequences).  Loadable module support  under PCI and EISA has been +    achieved by letting the driver autoprobe as if it were compiled into the +    kernel. Do make sure  you're not sharing  interrupts with anything  that +    cannot accommodate  interrupt  sharing! + +    To utilise this ability, you have to do 8 things: + +    0) have a copy of the loadable modules code installed on your system. +    1) copy de4x5.c from the  /linux/drivers/net directory to your favourite +    temporary directory. +    2) for fixed  autoprobes (not  recommended),  edit the source code  near +    line 5594 to reflect the I/O address  you're using, or assign these when +    loading by: + +                   insmod de4x5 io=0xghh           where g = bus number +		                                        hh = device number    + +       NB: autoprobing for modules is now supported by default. You may just +           use: + +                   insmod de4x5 + +           to load all available boards. For a specific board, still use +	   the 'io=?' above. +    3) compile  de4x5.c, but include -DMODULE in  the command line to ensure +    that the correct bits are compiled (see end of source code). +    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a +    kernel with the de4x5 configuration turned off and reboot. +    5) insmod de4x5 [io=0xghh] +    6) run the net startup bits for your new eth?? interface(s) manually  +    (usually /etc/rc.inet[12] at boot time).  +    7) enjoy! + +    To unload a module, turn off the associated interface(s)  +    'ifconfig eth?? down' then 'rmmod de4x5'. + +    Automedia detection is included so that in  principal you can disconnect +    from, e.g.  TP, reconnect  to BNC  and  things will still work  (after a +    pause whilst the   driver figures out   where its media went).  My tests +    using ping showed that it appears to work.... + +    By  default,  the driver will  now   autodetect any  DECchip based card. +    Should you have a need to restrict the driver to DIGITAL only cards, you +    can compile with a  DEC_ONLY define, or if  loading as a module, use the +    'dec_only=1'  parameter.  + +    I've changed the timing routines to  use the kernel timer and scheduling +    functions  so that the  hangs  and other assorted problems that occurred +    while autosensing the  media  should be gone.  A  bonus  for the DC21040 +    auto  media sense algorithm is  that it can now  use one that is more in +    line with the  rest (the DC21040  chip doesn't  have a hardware  timer). +    The downside is the 1 'jiffies' (10ms) resolution. + +    IEEE 802.3u MII interface code has  been added in anticipation that some +    products may use it in the future. + +    The SMC9332 card  has a non-compliant SROM  which needs fixing -  I have +    patched this  driver to detect it  because the SROM format used complies +    to a previous DEC-STD format. + +    I have removed the buffer copies needed for receive on Intels.  I cannot +    remove them for   Alphas since  the  Tulip hardware   only does longword +    aligned  DMA transfers  and  the  Alphas get   alignment traps with  non +    longword aligned data copies (which makes them really slow). No comment. + +    I  have added SROM decoding  routines to make this  driver work with any +    card that  supports the Digital  Semiconductor SROM spec. This will help +    all  cards running the dc2114x  series chips in particular.  Cards using +    the dc2104x  chips should run correctly with  the basic  driver.  I'm in +    debt to <mjacob@feral.com> for the  testing and feedback that helped get +    this feature working.  So far we have  tested KINGSTON, SMC8432, SMC9332 +    (with the latest SROM complying  with the SROM spec  V3: their first was +    broken), ZNYX342  and  LinkSys. ZYNX314 (dual  21041  MAC) and  ZNYX 315 +    (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly +    wired IRQs. + +    I have added a temporary fix for interrupt problems when some SCSI cards +    share the same interrupt as the DECchip based  cards. The problem occurs +    because  the SCSI card wants to  grab the interrupt  as a fast interrupt +    (runs the   service routine with interrupts turned   off) vs.  this card +    which really needs to run the service routine with interrupts turned on. +    This driver will  now   add the interrupt service   routine  as  a  fast +    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A +    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time +    until  people   sort  out their  compatibility    issues and the  kernel +    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST +    INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not +    run on the same interrupt. PCMCIA/CardBus is another can of worms... + +    Finally, I think  I have really  fixed  the module  loading problem with +    more than one DECchip based  card.  As a  side effect, I don't mess with +    the  device structure any  more which means that  if more than 1 card in +    2.0.x is    installed (4  in   2.1.x),  the  user   will have   to  edit +    linux/drivers/net/Space.c  to make room for  them. Hence, module loading +    is  the preferred way to use   this driver, since  it  doesn't have this +    limitation. + +    Where SROM media  detection is used and  full duplex is specified in the +    SROM,  the feature is  ignored unless  lp->params.fdx  is set at compile +    time  OR during  a   module load  (insmod  de4x5   args='eth??:fdx' [see +    below]).  This is because there  is no way  to automatically detect full +    duplex   links  except through   autonegotiation.    When I  include the +    autonegotiation feature in  the SROM autoconf  code, this detection will +    occur automatically for that case. + +    Command  line arguments are  now  allowed, similar  to passing arguments +    through LILO. This will allow a per adapter board  set up of full duplex +    and media. The only lexical constraints  are: the board name (dev->name) +    appears in the list before its  parameters.  The list of parameters ends +    either at the end of the parameter list or with another board name.  The +    following parameters are allowed: + +            fdx        for full duplex +	    autosense  to set the media/speed; with the following  +	               sub-parameters: +		       TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO + +    Case sensitivity is important  for  the sub-parameters. They *must*   be +    upper case. Examples: + +        insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. + +    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g. +	DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"'  + +    Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just +    examples. By default, full duplex is turned  off and AUTO is the default +    autosense setting. In  reality, I expect only the  full duplex option to +    be used. Note the use of single quotes in the two examples above and the +    lack of commas to separate items. + +    TO DO: +    ------ + +    o check what revision numbers the 21142 and 21143 have +    o + +    Revision History +    ---------------- + +    Version   Date        Description +   +      0.1     17-Nov-94   Initial writing. ALPHA code release. +      0.2     13-Jan-95   Added PCI support for DE435's. +      0.21    19-Jan-95   Added auto media detection. +      0.22    10-Feb-95   Fix interrupt handler call <chris@cosy.sbg.ac.at>. +                          Fix recognition bug reported by <bkm@star.rl.ac.uk>. +			  Add request/release_region code. +			  Add loadable modules support for PCI. +			  Clean up loadable modules support. +      0.23    28-Feb-95   Added DC21041 and DC21140 support.  +                          Fix missed frame counter value and initialisation. +			  Fixed EISA probe. +      0.24    11-Apr-95   Change delay routine to use <linux/udelay>. +                          Change TX_BUFFS_AVAIL macro. +			  Change media autodetection to allow manual setting. +			  Completed DE500 (DC21140) support. +      0.241   18-Apr-95   Interim release without DE500 Autosense Algorithm. +      0.242   10-May-95   Minor changes. +      0.30    12-Jun-95   Timer fix for DC21140. +                          Portability changes. +			  Add ALPHA changes from <jestabro@ant.tay1.dec.com>. +			  Add DE500 semi automatic autosense. +			  Add Link Fail interrupt TP failure detection. +			  Add timer based link change detection. +			  Plugged a memory leak in de4x5_queue_pkt(). +      0.31    13-Jun-95   Fixed PCI stuff for 1.3.1. +      0.32    26-Jun-95   Added verify_area() calls in de4x5_ioctl() from a +                          suggestion by <heiko@colossus.escape.de>. +      0.33     8-Aug-95   Add shared interrupt support (not released yet). +      0.331   21-Aug-95   Fix de4x5_open() with fast CPUs. +                          Fix de4x5_interrupt(). +                          Fix dc21140_autoconf() mess. +			  No shared interrupt support. +      0.332   11-Sep-95   Added MII management interface routines. +      0.40     5-Mar-96   Fix setup frame timeout <maartenb@hpkuipc.cern.ch>. +                          Add kernel timer code (h/w is too flaky). +			  Add MII based PHY autosense. +			  Add new multicasting code. +			  Add new autosense algorithms for media/mode  +			  selection using kernel scheduling/timing. +			  Re-formatted. +			  Made changes suggested by <jeff@router.patch.net>: +			    Change driver to detect all DECchip based cards +			    with DEC_ONLY restriction a special case. +			    Changed driver to autoprobe as a module. No irq +			    checking is done now - assume BIOS is good! +			  Added SMC9332 detection <manabe@Roy.dsl.tutics.ac.jp> +      0.41    21-Mar-96   Don't check for get_hw_addr checksum unless DEC card +                          only <niles@axp745gsfc.nasa.gov> +			  Fix for multiple PCI cards reported by <jos@xos.nl> +			  Duh, put the SA_SHIRQ flag into request_interrupt(). +			  Fix SMC ethernet address in enet_det[]. +			  Print chip name instead of "UNKNOWN" during boot. +      0.42    26-Apr-96   Fix MII write TA bit error. +                          Fix bug in dc21040 and dc21041 autosense code. +			  Remove buffer copies on receive for Intels. +			  Change sk_buff handling during media disconnects to +			   eliminate DUP packets. +			  Add dynamic TX thresholding. +			  Change all chips to use perfect multicast filtering. +			  Fix alloc_device() bug <jari@markkus2.fimr.fi> +      0.43   21-Jun-96    Fix unconnected media TX retry bug. +                          Add Accton to the list of broken cards. +			  Fix TX under-run bug for non DC21140 chips. +			  Fix boot command probe bug in alloc_device() as +			   reported by <koen.gadeyne@barco.com> and  +			   <orava@nether.tky.hut.fi>. +			  Add cache locks to prevent a race condition as +			   reported by <csd@microplex.com> and  +			   <baba@beckman.uiuc.edu>. +			  Upgraded alloc_device() code. +      0.431  28-Jun-96    Fix potential bug in queue_pkt() from discussion +                          with <csd@microplex.com> +      0.44   13-Aug-96    Fix RX overflow bug in 2114[023] chips. +                          Fix EISA probe bugs reported by <os2@kpi.kharkov.ua> +			  and <michael@compurex.com>. +      0.441   9-Sep-96    Change dc21041_autoconf() to probe quiet BNC media +                           with a loopback packet. +      0.442   9-Sep-96    Include AUI in dc21041 media printout. Bug reported +                           by <bhat@mundook.cs.mu.OZ.AU> +      0.45    8-Dec-96    Include endian functions for PPC use, from work  +                           by <cort@cs.nmt.edu> and <g.thomas@opengroup.org>. +      0.451  28-Dec-96    Added fix to allow autoprobe for modules after +                           suggestion from <mjacob@feral.com>. +      0.5    30-Jan-97    Added SROM decoding functions. +                          Updated debug flags. +			  Fix sleep/wakeup calls for PCI cards, bug reported +			   by <cross@gweep.lkg.dec.com>. +			  Added multi-MAC, one SROM feature from discussion +			   with <mjacob@feral.com>. +			  Added full module autoprobe capability. +			  Added attempt to use an SMC9332 with broken SROM. +			  Added fix for ZYNX multi-mac cards that didn't +			   get their IRQs wired correctly. +      0.51   13-Feb-97    Added endian fixes for the SROM accesses from +			   <paubert@iram.es> +			  Fix init_connection() to remove extra device reset. +			  Fix MAC/PHY reset ordering in dc21140m_autoconf(). +			  Fix initialisation problem with lp->timeout in +			   typeX_infoblock() from <paubert@iram.es>. +			  Fix MII PHY reset problem from work done by +			   <paubert@iram.es>. +      0.52   26-Apr-97    Some changes may not credit the right people - +                           a disk crash meant I lost some mail. +			  Change RX interrupt routine to drop rather than  +			   defer packets to avoid hang reported by  +			   <g.thomas@opengroup.org>. +			  Fix srom_exec() to return for COMPACT and type 1 +			   infoblocks. +			  Added DC21142 and DC21143 functions. +			  Added byte counters from <phil@tazenda.demon.co.uk> +			  Added SA_INTERRUPT temporary fix from  +			   <mjacob@feral.com>. +      0.53   12-Nov-97    Fix the *_probe() to include 'eth??' name during +                           module load: bug reported by +			   <Piete.Brooks@cl.cam.ac.uk> +			  Fix multi-MAC, one SROM, to work with 2114x chips: +			   bug reported by <cmetz@inner.net>. +			  Make above search independent of BIOS device scan +			   direction. +			  Completed DC2114[23] autosense functions. +      0.531  21-Dec-97    Fix DE500-XA 100Mb/s bug reported by  +                           <robin@intercore.com +			  Fix type1_infoblock() bug introduced in 0.53, from +			   problem reports by  +			   <parmee@postecss.ncrfran.france.ncr.com> and +			   <jo@ice.dillingen.baynet.de>. +			  Added argument list to set up each board from either +			   a module's command line or a compiled in #define. +			  Added generic MII PHY functionality to deal with +			   newer PHY chips. +			  Fix the mess in 2.1.67. +      0.532   5-Jan-98    Fix bug in mii_get_phy() reported by  +                           <redhat@cococo.net>. +                          Fix bug in pci_probe() for 64 bit systems reported +			   by <belliott@accessone.com>. +      0.533   9-Jan-98    Fix more 64 bit bugs reported by <jal@cs.brown.edu>. +      0.534  24-Jan-98    Fix last (?) endian bug from  +                           <Geert.Uytterhoeven@cs.kuleuven.ac.be> +      0.535  21-Feb-98    Fix Ethernet Address PROM reset bug for DC21040. +      0.5351  4-Oct-98    Atomicize assertion of dev->interrupt for SMP (not +                           for Alpha arch.) from <lma@varesearch.com> +			  Add TP, AUI and BNC cases to 21140m_autoconf() for +			   case where a 21140 under SROM control uses, e.g. AUI +			   from problem report by <delchini@lpnp09.in2p3.fr> +			  Add MII parallel detection to 2114x_autoconf() for +			   case where no autonegotiation partner exists from +			   problem report by <mlapsley@ndirect.co.uk>. +			  Add ability to force connection type directly even +			   when using SROM control from problem report by +			   <earl@exis.net>. +			  Fix is_anc_capable() bug reported by  +			   <Austin.Donnelly@cl.cam.ac.uk>. +			  Fix type[13]_infoblock() bug: during MII search, PHY +			   lp->rst not run because lp->ibn not initialised - +			   from report & fix by <paubert@iram.es>. +			  Fix probe bug with EISA & PCI cards present from +			   report by <eirik@netcom.com>. +			  Fix compiler problems associated with i386-string +			   ops from multiple bug reports and temporary fix +			   from <paubert@iram.es>. +			  Add an_exception() for old ZYNX346 and fix compile +			   warning on PPC & SPARC, from <ecd@skynet.be>. +			  Fix lastPCI to correctly work with compiled in +			   kernels and modules from bug report by  +			   <Zlatko.Calusic@CARNet.hr> et al. +			  Fix dc2114x_autoconf() to stop multiple messages +			   when media is unconnected. +			  Change dev->interrupt to lp->interrupt to ensure +			   alignment for Alpha's and avoid their unaligned +			   access traps. This flag is merely for log messages: +			   should do something more definitive though... + +    ========================================================================= +*/ + +static const char *version = "de4x5.c:V0.5351 1998/10/4 davies@maniac.ultranet.com\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <linux/time.h> +#include <linux/types.h> +#include <linux/unistd.h> +#include <linux/ctype.h> + +#include "de4x5.h" + +#define c_char const char + +#include <linux/version.h> +#if	LINUX_VERSION_CODE < LinuxVersionCode(2,1,0) +#  define __initfunc(__arginit) __arginit +//#  define test_and_set_bit      set_bit +#  define net_device_stats      enet_statistics +#  define copy_to_user(a,b,c)   memcpy_tofs(a,b,c) +#  define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +#  define le16_to_cpu(a)        cpu_to_le16(a)  +#  define le32_to_cpu(a)        cpu_to_le32(a)  +#  ifdef __powerpc__ +#    define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8)) +#    define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\ +		            (((a) & 0x0000ff00U) <<  8) |\ +		            (((a) & 0x00ff0000U) >>  8) |\ +		            (((a) & 0xff000000U) >> 24)) +#  else +#    define cpu_to_le16(a)      (a) +#    define cpu_to_le32(a)      (a) +#  endif  /* __powerpc__ */ +#  include <asm/segment.h> +#else +#  include <asm/uaccess.h> +#  include <linux/init.h> +#endif  /* LINUX_VERSION_CODE */ +#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) + +/* +** MII Information +*/ +struct phy_table { +    int reset;              /* Hard reset required?                         */ +    int id;                 /* IEEE OUI                                     */ +    int ta;                 /* One cycle TA time - 802.3u is confusing here */ +    struct {                /* Non autonegotiation (parallel) speed det.    */ +	int reg; +	int mask; +	int value; +    } spd; +}; + +struct mii_phy { +    int reset;              /* Hard reset required?                      */ +    int id;                 /* IEEE OUI                                  */ +    int ta;                 /* One cycle TA time                         */ +    struct {                /* Non autonegotiation (parallel) speed det. */ +	int reg; +	int mask; +	int value; +    } spd; +    int addr;               /* MII address for the PHY                   */ +    u_char  *gep;           /* Start of GEP sequence block in SROM       */ +    u_char  *rst;           /* Start of reset sequence in SROM           */ +    u_int mc;               /* Media Capabilities                        */ +    u_int ana;              /* NWay Advertisement                        */ +    u_int fdx;              /* Full DupleX capabilites for each media    */ +    u_int ttm;              /* Transmit Threshold Mode for each media    */ +    u_int mci;              /* 21142 MII Connector Interrupt info        */ +}; + +#define DE4X5_MAX_PHY 8     /* Allow upto 8 attached PHY devices per board */ + +struct sia_phy { +    u_char mc;              /* Media Code                                */ +    u_char ext;             /* csr13-15 valid when set                   */ +    int csr13;              /* SIA Connectivity Register                 */ +    int csr14;              /* SIA TX/RX Register                        */ +    int csr15;              /* SIA General Register                      */ +    int gepc;               /* SIA GEP Control Information               */ +    int gep;                /* SIA GEP Data                              */ +}; + +/* +** Define the know universe of PHY devices that can be +** recognised by this driver. +*/ +static struct phy_table phy_info[] = { +    {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}},       /* National TX */ +    {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}},       /* Broadcom T4 */ +    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},       /* SEEQ T4     */ +    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}},       /* Cypress T4  */ +    {0, 0x7810     , 1, {0x05, 0x0380, 0x0380}}    /* Level One?  */ +}; + +/* +** These GENERIC values assumes that the PHY devices follow 802.3u and +** allow parallel detection to set the link partner ability register. +** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported. +*/ +#define GENERIC_REG   0x05      /* Autoneg. Link Partner Advertisement Reg. */ +#define GENERIC_MASK  MII_ANLPA_100M /* All 100Mb/s Technologies            */ +#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4       */ + +/* +** Define special SROM detection cases +*/ +static c_char enet_det[][ETH_ALEN] = { +    {0x00, 0x00, 0xc0, 0x00, 0x00, 0x00}, +    {0x00, 0x00, 0xe8, 0x00, 0x00, 0x00} +}; + +#define SMC    1 +#define ACCTON 2 + +/* +** SROM Repair definitions. If a broken SROM is detected a card may +** use this information to help figure out what to do. This is a +** "stab in the dark" and so far for SMC9332's only. +*/ +static c_char srom_repair_info[][100] = { +    {0x00,0x1e,0x00,0x00,0x00,0x08,             /* SMC9332 */ +     0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02, +     0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50, +     0x00,0x18,} +}; + + +#ifdef DE4X5_DEBUG +static int de4x5_debug = DE4X5_DEBUG; +#else +/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/ +static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION); +#endif + +/* +** Allow per adapter set up. For modules this is simply a command line +** parameter, e.g.:  +** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'. +** +** For a compiled in driver, place e.g. +** DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"'  +** in linux/drivers/net/CONFIG +*/ +#ifdef DE4X5_PARM +static char *args = DE4X5_PARM; +#else +static char *args = NULL; +#endif + +struct parameters { +    int fdx; +    int autosense; +}; + +#define DE4X5_AUTOSENSE_MS 250      /* msec autosense tick (DE500) */ + +#define DE4X5_NDA 0xffe0            /* No Device (I/O) Address */ + +/* +** Ethernet PROM defines +*/ +#define PROBE_LENGTH    32 +#define ETH_PROM_SIG    0xAA5500FFUL + +/* +** Ethernet Info +*/ +#define PKT_BUF_SZ	1536            /* Buffer size for each Tx/Rx buffer */ +#define IEEE802_3_SZ    1518            /* Packet + CRC */ +#define MAX_PKT_SZ   	1514            /* Maximum ethernet packet length */ +#define MAX_DAT_SZ   	1500            /* Maximum ethernet data length */ +#define MIN_DAT_SZ   	1               /* Minimum ethernet data length */ +#define PKT_HDR_LEN     14              /* Addresses and data length info */ +#define FAKE_FRAME_LEN  (MAX_PKT_SZ + 1) +#define QUEUE_PKT_TIMEOUT (3*HZ)        /* 3 second timeout */ + + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */ + +/* +** EISA bus defines +*/ +#define DE4X5_EISA_IO_PORTS   0x0c00    /* I/O port base address, slot 0 */ +#define DE4X5_EISA_TOTAL_SIZE 0x100     /* I/O address extent */ + +#define MAX_EISA_SLOTS 16 +#define EISA_SLOT_INC 0x1000 +#define EISA_ALLOWED_IRQ_LIST  {5, 9, 10, 11} + +#define DE4X5_SIGNATURE {"DE425","DE434","DE435","DE450","DE500"} +#define DE4X5_NAME_LENGTH 8 + +/* +** Ethernet PROM defines for DC21040 +*/ +#define PROBE_LENGTH    32 +#define ETH_PROM_SIG    0xAA5500FFUL + +/* +** PCI Bus defines +*/ +#define PCI_MAX_BUS_NUM      8 +#define DE4X5_PCI_TOTAL_SIZE 0x80       /* I/O address extent */ +#define DE4X5_CLASS_CODE     0x00020000 /* Network controller, Ethernet */ +#define NO_MORE_PCI          -2         /* PCI bus search all done */ + +/* +** Memory Alignment. Each descriptor is 4 longwords long. To force a +** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and +** DESC_ALIGN. ALIGN aligns the start address of the private memory area +** and hence the RX descriptor ring's first entry.  +*/ +#define ALIGN4      ((u_long)4 - 1)     /* 1 longword align */ +#define ALIGN8      ((u_long)8 - 1)     /* 2 longword align */ +#define ALIGN16     ((u_long)16 - 1)    /* 4 longword align */ +#define ALIGN32     ((u_long)32 - 1)    /* 8 longword align */ +#define ALIGN64     ((u_long)64 - 1)    /* 16 longword align */ +#define ALIGN128    ((u_long)128 - 1)   /* 32 longword align */ + +#define ALIGN         ALIGN32           /* Keep the DC21040 happy... */ +#define CACHE_ALIGN   CAL_16LONG +#define DESC_SKIP_LEN DSL_0             /* Must agree with DESC_ALIGN */ +/*#define DESC_ALIGN    u32 dummy[4];  / * Must agree with DESC_SKIP_LEN */ +#define DESC_ALIGN + +#ifndef DEC_ONLY                        /* See README.de4x5 for using this */ +static int dec_only = 0; +#else +static int dec_only = 1; +#endif + +/* +** DE4X5 IRQ ENABLE/DISABLE +*/ +#define ENABLE_IRQs { \ +    imr |= lp->irq_en;\ +    outl(imr, DE4X5_IMR);               /* Enable the IRQs */\ +} + +#define DISABLE_IRQs {\ +    imr = inl(DE4X5_IMR);\ +    imr &= ~lp->irq_en;\ +    outl(imr, DE4X5_IMR);               /* Disable the IRQs */\ +} + +#define UNMASK_IRQs {\ +    imr |= lp->irq_mask;\ +    outl(imr, DE4X5_IMR);               /* Unmask the IRQs */\ +} + +#define MASK_IRQs {\ +    imr = inl(DE4X5_IMR);\ +    imr &= ~lp->irq_mask;\ +    outl(imr, DE4X5_IMR);               /* Mask the IRQs */\ +} + +/* +** DE4X5 START/STOP +*/ +#define START_DE4X5 {\ +    omr = inl(DE4X5_OMR);\ +    omr |= OMR_ST | OMR_SR;\ +    outl(omr, DE4X5_OMR);               /* Enable the TX and/or RX */\ +} + +#define STOP_DE4X5 {\ +    omr = inl(DE4X5_OMR);\ +    omr &= ~(OMR_ST|OMR_SR);\ +    outl(omr, DE4X5_OMR);               /* Disable the TX and/or RX */ \ +} + +/* +** DE4X5 SIA RESET +*/ +#define RESET_SIA outl(0, DE4X5_SICR);  /* Reset SIA connectivity regs */ + +/* +** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS) +*/ +#define DE4X5_AUTOSENSE_MS  250 + +/* +** SROM Structure +*/ +struct de4x5_srom { +    char sub_vendor_id[2]; +    char sub_system_id[2]; +    char reserved[12]; +    char id_block_crc; +    char reserved2; +    char version; +    char num_controllers; +    char ieee_addr[6]; +    char info[100]; +    short chksum; +}; +#define SUB_VENDOR_ID 0x500a + +/* +** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous +** and have sizes of both a power of 2 and a multiple of 4. +** A size of 256 bytes for each buffer could be chosen because over 90% of +** all packets in our network are <256 bytes long and 64 longword alignment +** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX +** descriptors are needed for machines with an ALPHA CPU. +*/ +#define NUM_RX_DESC 8                   /* Number of RX descriptors   */ +#define NUM_TX_DESC 32                  /* Number of TX descriptors   */ +#define RX_BUFF_SZ  1536                /* Power of 2 for kmalloc and */ +                                        /* Multiple of 4 for DC21040  */ +                                        /* Allows 512 byte alignment  */ +struct de4x5_desc { +    volatile s32 status; +    u32 des1; +    u32 buf; +    u32 next; +    DESC_ALIGN +}; + +/* +** The DE4X5 private structure +*/ +#define DE4X5_PKT_STAT_SZ 16 +#define DE4X5_PKT_BIN_SZ  128            /* Should be >=100 unless you +                                            increase DE4X5_PKT_STAT_SZ */ + +struct de4x5_private { +    char adapter_name[80];                  /* Adapter name                 */ +    u_long interrupt;                       /* Aligned ISR flag             */ +    struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring           */ +    struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring           */ +    struct sk_buff *tx_skb[NUM_TX_DESC];    /* TX skb for freeing when sent */ +    struct sk_buff *rx_skb[NUM_RX_DESC];    /* RX skb's                     */ +    int rx_new, rx_old;                     /* RX descriptor ring pointers  */ +    int tx_new, tx_old;                     /* TX descriptor ring pointers  */ +    char setup_frame[SETUP_FRAME_LEN];      /* Holds MCA and PA info.       */ +    char frame[64];                         /* Min sized packet for loopback*/ +    struct net_device_stats stats;           /* Public stats                 */ +    struct { +	u_int bins[DE4X5_PKT_STAT_SZ];      /* Private stats counters       */ +	u_int unicast; +	u_int multicast; +	u_int broadcast; +	u_int excessive_collisions; +	u_int tx_underruns; +	u_int excessive_underruns; +	u_int rx_runt_frames; +	u_int rx_collision; +	u_int rx_dribble; +	u_int rx_overflow; +    } pktStats; +    char rxRingSize; +    char txRingSize; +    int  bus;                               /* EISA or PCI                  */ +    int  bus_num;                           /* PCI Bus number               */ +    int  device;                            /* Device number on PCI bus     */ +    int  state;                             /* Adapter OPENED or CLOSED     */ +    int  chipset;                           /* DC21040, DC21041 or DC21140  */ +    s32  irq_mask;                          /* Interrupt Mask (Enable) bits */ +    s32  irq_en;                            /* Summary interrupt bits       */ +    int  media;                             /* Media (eg TP), mode (eg 100B)*/ +    int  c_media;                           /* Remember the last media conn */ +    int  fdx;                               /* media full duplex flag       */ +    int  linkOK;                            /* Link is OK                   */ +    int  autosense;                         /* Allow/disallow autosensing   */ +    int  tx_enable;                         /* Enable descriptor polling    */ +    int  setup_f;                           /* Setup frame filtering type   */ +    int  local_state;                       /* State within a 'media' state */ +    struct mii_phy phy[DE4X5_MAX_PHY];      /* List of attached PHY devices */ +    struct sia_phy sia;                     /* SIA PHY Information          */ +    int  active;                            /* Index to active PHY device   */ +    int  mii_cnt;                           /* Number of attached PHY's     */ +    int  timeout;                           /* Scheduling counter           */ +    struct timer_list timer;                /* Timer info for kernel        */ +    int tmp;                                /* Temporary global per card    */ +    struct { +	void *priv;                         /* Original kmalloc'd mem addr  */ +	void *buf;                          /* Original kmalloc'd mem addr  */ +	u_long lock;                        /* Lock the cache accesses      */ +	s32 csr0;                           /* Saved Bus Mode Register      */ +	s32 csr6;                           /* Saved Operating Mode Reg.    */ +	s32 csr7;                           /* Saved IRQ Mask Register      */ +	s32 gep;                            /* Saved General Purpose Reg.   */ +	s32 gepc;                           /* Control info for GEP         */ +	s32 csr13;                          /* Saved SIA Connectivity Reg.  */ +	s32 csr14;                          /* Saved SIA TX/RX Register     */ +	s32 csr15;                          /* Saved SIA General Register   */ +	int save_cnt;                       /* Flag if state already saved  */ +	struct sk_buff *skb;                /* Save the (re-ordered) skb's  */ +    } cache; +    struct de4x5_srom srom;                 /* A copy of the SROM           */ +    struct device *next_module;             /* Link to the next module      */ +    int rx_ovf;                             /* Check for 'RX overflow' tag  */ +    int useSROM;                            /* For non-DEC card use SROM    */ +    int useMII;                             /* Infoblock using the MII      */ +    int asBitValid;                         /* Autosense bits in GEP?       */ +    int asPolarity;                         /* 0 => asserted high           */ +    int asBit;                              /* Autosense bit number in GEP  */ +    int defMedium;                          /* SROM default medium          */ +    int tcount;                             /* Last infoblock number        */ +    int infoblock_init;                     /* Initialised this infoblock?  */ +    int infoleaf_offset;                    /* SROM infoleaf for controller */ +    s32 infoblock_csr6;                     /* csr6 value in SROM infoblock */ +    int infoblock_media;                    /* infoblock media              */ +    int (*infoleaf_fn)(struct device *);    /* Pointer to infoleaf function */ +    u_char *rst;                            /* Pointer to Type 5 reset info */ +    u_char  ibn;                            /* Infoblock number             */ +    struct parameters params;               /* Command line/ #defined params */ +}; + +/* +** Kludge to get around the fact that the CSR addresses have different +** offsets in the PCI and EISA boards. Also note that the ethernet address +** PROM is accessed differently. +*/ +static struct bus_type { +    int bus; +    int bus_num; +    int device; +    int chipset; +    struct de4x5_srom srom; +    int autosense; +    int useSROM; +} bus; + +/* +** To get around certain poxy cards that don't provide an SROM +** for the second and more DECchip, I have to key off the first +** chip's address. I'll assume there's not a bad SROM iff: +** +**      o the chipset is the same +**      o the bus number is the same and > 0 +**      o the sum of all the returned hw address bytes is 0 or 0x5fa +** +** Also have to save the irq for those cards whose hardware designers +** can't follow the PCI to PCI Bridge Architecture spec. +*/ +static struct { +    int chipset; +    int bus; +    int irq; +    u_char addr[ETH_ALEN]; +} last = {0,}; + +/* +** The transmit ring full condition is described by the tx_old and tx_new +** pointers by: +**    tx_old            = tx_new    Empty ring +**    tx_old            = tx_new+1  Full ring +**    tx_old+txRingSize = tx_new+1  Full ring  (wrapped condition) +*/ +#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ +			lp->tx_old+lp->txRingSize-lp->tx_new-1:\ +			lp->tx_old               -lp->tx_new-1) + +#define TX_PKT_PENDING (lp->tx_old != lp->tx_new) + +/* +** Public Functions +*/ +static int     de4x5_open(struct device *dev); +static int     de4x5_queue_pkt(struct sk_buff *skb, struct device *dev); +static void    de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int     de4x5_close(struct device *dev); +static struct  net_device_stats *de4x5_get_stats(struct device *dev); +static void    de4x5_local_stats(struct device *dev, char *buf, int pkt_len); +static void    set_multicast_list(struct device *dev); +static int     de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd); + +/* +** Private functions +*/ +static int     de4x5_hw_init(struct device *dev, u_long iobase); +static int     de4x5_init(struct device *dev); +static int     de4x5_sw_reset(struct device *dev); +static int     de4x5_rx(struct device *dev); +static int     de4x5_tx(struct device *dev); +static int     de4x5_ast(struct device *dev); +static int     de4x5_txur(struct device *dev); +static int     de4x5_rx_ovfc(struct device *dev); + +static int     autoconf_media(struct device *dev); +static void    create_packet(struct device *dev, char *frame, int len); +static void    de4x5_us_delay(u32 usec); +static void    de4x5_ms_delay(u32 msec); +static void    load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb); +static int     dc21040_autoconf(struct device *dev); +static int     dc21041_autoconf(struct device *dev); +static int     dc21140m_autoconf(struct device *dev); +static int     dc2114x_autoconf(struct device *dev); +static int     srom_autoconf(struct device *dev); +static int     de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *)); +static int     dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int)); +static int     test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); +static int     test_for_100Mb(struct device *dev, int msec); +static int     wait_for_link(struct device *dev); +static int     test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec); +static int     is_spd_100(struct device *dev); +static int     is_100_up(struct device *dev); +static int     is_10_up(struct device *dev); +static int     is_anc_capable(struct device *dev); +static int     ping_media(struct device *dev, int msec); +static struct sk_buff *de4x5_alloc_rx_buff(struct device *dev, int index, int len); +static void    de4x5_free_rx_buffs(struct device *dev); +static void    de4x5_free_tx_buffs(struct device *dev); +static void    de4x5_save_skbs(struct device *dev); +static void    de4x5_rst_desc_ring(struct device *dev); +static void    de4x5_cache_state(struct device *dev, int flag); +static void    de4x5_put_cache(struct device *dev, struct sk_buff *skb); +static void    de4x5_putb_cache(struct device *dev, struct sk_buff *skb); +static struct  sk_buff *de4x5_get_cache(struct device *dev); +static void    de4x5_setup_intr(struct device *dev); +static void    de4x5_init_connection(struct device *dev); +static int     de4x5_reset_phy(struct device *dev); +static void    reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr); +static int     test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec); +static int     test_tp(struct device *dev, s32 msec); +static int     EISA_signature(char *name, s32 eisa_id); +static int     PCI_signature(char *name, struct bus_type *lp); +static void    DevicePresent(u_long iobase); +static void    enet_addr_rst(u_long aprom_addr); +static int     de4x5_bad_srom(struct bus_type *lp); +static short   srom_rd(u_long address, u_char offset); +static void    srom_latch(u_int command, u_long address); +static void    srom_command(u_int command, u_long address); +static void    srom_address(u_int command, u_long address, u_char offset); +static short   srom_data(u_int command, u_long address); +/*static void    srom_busy(u_int command, u_long address);*/ +static void    sendto_srom(u_int command, u_long addr); +static int     getfrom_srom(u_long addr); +static int     srom_map_media(struct device *dev); +static int     srom_infoleaf_info(struct device *dev); +static void    srom_init(struct device *dev); +static void    srom_exec(struct device *dev, u_char *p); +static int     mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr); +static void    mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr); +static int     mii_rdata(u_long ioaddr); +static void    mii_wdata(int data, int len, u_long ioaddr); +static void    mii_ta(u_long rw, u_long ioaddr); +static int     mii_swap(int data, int len); +static void    mii_address(u_char addr, u_long ioaddr); +static void    sendto_mii(u32 command, int data, u_long ioaddr); +static int     getfrom_mii(u32 command, u_long ioaddr); +static int     mii_get_oui(u_char phyaddr, u_long ioaddr); +static int     mii_get_phy(struct device *dev); +static void    SetMulticastFilter(struct device *dev); +static int     get_hw_addr(struct device *dev); +static void    srom_repair(struct device *dev, int card); +static int     test_bad_enet(struct device *dev, int status); +static int     an_exception(struct bus_type *lp); +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +static void    eisa_probe(struct device *dev, u_long iobase); +#endif +static void    pci_probe(struct device *dev, u_long iobase); +static void    srom_search(int index); +static char    *build_setup_frame(struct device *dev, int mode); +static void    disable_ast(struct device *dev); +static void    enable_ast(struct device *dev, u32 time_out); +static long    de4x5_switch_mac_port(struct device *dev); +static int     gep_rd(struct device *dev); +static void    gep_wr(s32 data, struct device *dev); +static void    timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec); +static void    yawn(struct device *dev, int state); +static void    link_modules(struct device *dev, struct device *tmp); +static void    de4x5_parse_params(struct device *dev); +static void    de4x5_dbg_open(struct device *dev); +static void    de4x5_dbg_mii(struct device *dev, int k); +static void    de4x5_dbg_media(struct device *dev); +static void    de4x5_dbg_srom(struct de4x5_srom *p); +static void    de4x5_dbg_rx(struct sk_buff *skb, int len); +static int     de4x5_strncmp(char *a, char *b, int n); +static int     dc21041_infoleaf(struct device *dev); +static int     dc21140_infoleaf(struct device *dev); +static int     dc21142_infoleaf(struct device *dev); +static int     dc21143_infoleaf(struct device *dev); +static int     type0_infoblock(struct device *dev, u_char count, u_char *p); +static int     type1_infoblock(struct device *dev, u_char count, u_char *p); +static int     type2_infoblock(struct device *dev, u_char count, u_char *p); +static int     type3_infoblock(struct device *dev, u_char count, u_char *p); +static int     type4_infoblock(struct device *dev, u_char count, u_char *p); +static int     type5_infoblock(struct device *dev, u_char count, u_char *p); +static int     compact_infoblock(struct device *dev, u_char count, u_char *p); + +#ifdef MODULE +int  init_module(void); +void cleanup_module(void); +static struct  device *unlink_modules(struct device *p); +static struct  device *insert_device(struct device *dev, u_long iobase, +				     int (*init)(struct device *)); +static int count_adapters(void); +static int loading_module = 1; +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) +MODULE_PARM(de4x5_debug, "i"); +MODULE_PARM(dec_only, "i"); +MODULE_PARM(args, "s"); +#endif /* LINUX_VERSION_CODE */ +# else +static int loading_module = 0; +#endif /* MODULE */ + +static char name[DE4X5_NAME_LENGTH + 1]; +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; +static int lastEISA = 0; +#else +static int lastEISA = MAX_EISA_SLOTS;           /* Only PCI probes */ +#endif +static int num_de4x5s = 0; +static int cfrv = 0, useSROM = 0; +static int lastPCI = -1; +static struct device *lastModule = NULL; + +/* +** List the SROM infoleaf functions and chipsets +*/ +struct InfoLeaf { +    int chipset; +    int (*fn)(struct device *); +}; +static struct InfoLeaf infoleaf_array[] = { +    {DC21041, dc21041_infoleaf}, +    {DC21140, dc21140_infoleaf}, +    {DC21142, dc21142_infoleaf}, +    {DC21143, dc21143_infoleaf} +}; +#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *))) + +/* +** List the SROM info block functions +*/ +static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = { +    type0_infoblock, +    type1_infoblock, +    type2_infoblock, +    type3_infoblock, +    type4_infoblock, +    type5_infoblock, +    compact_infoblock +}; + +#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1) + +/* +** Miscellaneous defines... +*/ +#define RESET_DE4X5 {\ +    int i;\ +    i=inl(DE4X5_BMR);\ +    de4x5_ms_delay(1);\ +    outl(i | BMR_SWR, DE4X5_BMR);\ +    de4x5_ms_delay(1);\ +    outl(i, DE4X5_BMR);\ +    de4x5_ms_delay(1);\ +    for (i=0;i<5;i++) {inl(DE4X5_BMR); de4x5_ms_delay(1);}\ +    de4x5_ms_delay(1);\ +} + +#define PHY_HARD_RESET {\ +    outl(GEP_HRST, DE4X5_GEP);           /* Hard RESET the PHY dev. */\ +    udelay(1000);                        /* Assert for 1ms */\ +    outl(0x00, DE4X5_GEP);\ +    udelay(2000);                        /* Wait for 2ms */\ +} + + +/* +** Autoprobing in modules is allowed here. See the top of the file for +** more info. +*/ +__initfunc(int +de4x5_probe(struct device *dev)) +{ +    u_long iobase = dev->base_addr; + +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +    eisa_probe(dev, iobase); +#endif +    if (lastEISA == MAX_EISA_SLOTS) { +	pci_probe(dev, iobase); +    } +     +    return (dev->priv ? 0 : -ENODEV); +} + +__initfunc(static int +de4x5_hw_init(struct device *dev, u_long iobase)) +{ +    struct bus_type *lp = &bus; +    int i, status=0; +    char *tmp; +     +    /* Ensure we're not sleeping */ +    if (lp->bus == EISA) { +	outb(WAKEUP, PCI_CFPM); +    } else { +	pcibios_write_config_byte(lp->bus_num, lp->device << 3,  +				  PCI_CFDA_PSM, WAKEUP); +    } +    de4x5_ms_delay(10); + +    RESET_DE4X5; +     +    if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) { +	return -ENXIO;                       /* Hardware could not reset */ +    } +     +    /*  +    ** Now find out what kind of DC21040/DC21041/DC21140 board we have. +    */ +    useSROM = FALSE; +    if (lp->bus == PCI) { +	PCI_signature(name, lp); +    } else { +	EISA_signature(name, EISA_ID0); +    } +     +    if (*name == '\0') {                     /* Not found a board signature */ +	return -ENXIO; +    } +     +    dev->base_addr = iobase; +    if (lp->bus == EISA) { +	printk("%s: %s at 0x%04lx (EISA slot %ld)",  +	       dev->name, name, iobase, ((iobase>>12)&0x0f)); +    } else {                                 /* PCI port address */ +	printk("%s: %s at 0x%04lx (PCI bus %d, device %d)", dev->name, name, +	       iobase, lp->bus_num, lp->device); +    } +     +    printk(", h/w address "); +    status = get_hw_addr(dev); +    for (i = 0; i < ETH_ALEN - 1; i++) {     /* get the ethernet addr. */ +	printk("%2.2x:", dev->dev_addr[i]); +    } +    printk("%2.2x,\n", dev->dev_addr[i]); +     +    if (status != 0) { +	printk("      which has an Ethernet PROM CRC error.\n"); +	return -ENXIO; +    } else { +	struct de4x5_private *lp; +	 +	/*  +	** Reserve a section of kernel memory for the adapter +	** private area and the TX/RX descriptor rings. +	*/ +	dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + ALIGN,  +				     GFP_KERNEL); +	if (dev->priv == NULL) { +	    return -ENOMEM; +	} +	 +	/* +	** Align to a longword boundary +	*/ +	tmp = dev->priv; +	dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN); +	lp = (struct de4x5_private *)dev->priv; +	memset(dev->priv, 0, sizeof(struct de4x5_private)); +	lp->bus = bus.bus; +	lp->bus_num = bus.bus_num; +	lp->device = bus.device; +	lp->chipset = bus.chipset; +	lp->cache.priv = tmp; +	lp->cache.gepc = GEP_INIT; +	lp->asBit = GEP_SLNK; +	lp->asPolarity = GEP_SLNK; +	lp->asBitValid = TRUE; +	lp->timeout = -1; +	lp->useSROM = useSROM; +	memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); +	de4x5_parse_params(dev); + +	/* +	** Choose correct autosensing in case someone messed up +	*/ +        lp->autosense = lp->params.autosense; +        if (lp->chipset != DC21140) { +            if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) { +                lp->params.autosense = TP; +            } +            if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) { +                lp->params.autosense = BNC; +            } +        } +	lp->fdx = lp->params.fdx; +	sprintf(lp->adapter_name,"%s (%s)", name, dev->name); +	 +	/* +	** Set up the RX descriptor ring (Intels) +	** Allocate contiguous receive buffers, long word aligned (Alphas)  +	*/ +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) +	for (i=0; i<NUM_RX_DESC; i++) { +	    lp->rx_ring[i].status = 0; +	    lp->rx_ring[i].des1 = RX_BUFF_SZ; +	    lp->rx_ring[i].buf = 0; +	    lp->rx_ring[i].next = 0; +	    lp->rx_skb[i] = (struct sk_buff *) 1;     /* Dummy entry */ +	} + +#else +	if ((tmp = (void *)kmalloc(RX_BUFF_SZ * NUM_RX_DESC + ALIGN,  +				   GFP_KERNEL)) == NULL) { +	    kfree(lp->cache.priv); +	    return -ENOMEM; +	} + +	lp->cache.buf = tmp; +	tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN); +	for (i=0; i<NUM_RX_DESC; i++) { +	    lp->rx_ring[i].status = 0; +	    lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); +	    lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp+i*RX_BUFF_SZ)); +	    lp->rx_ring[i].next = 0; +	    lp->rx_skb[i] = (struct sk_buff *) 1;     /* Dummy entry */ +	} +#endif + +	barrier(); +	     +	request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE : +				DE4X5_EISA_TOTAL_SIZE),  +		       lp->adapter_name); +	     +	lp->rxRingSize = NUM_RX_DESC; +	lp->txRingSize = NUM_TX_DESC; +	     +	/* Write the end of list marker to the descriptor lists */ +	lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); +	lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER); +	     +	/* Tell the adapter where the TX/RX rings are located. */ +	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA); +	outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA); +	     +	/* Initialise the IRQ mask and Enable/Disable */ +	lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM | IMR_UNM; +	lp->irq_en   = IMR_NIM | IMR_AIM; + +	/* Create a loopback packet frame for later media probing */ +	create_packet(dev, lp->frame, sizeof(lp->frame)); + +	/* Check if the RX overflow bug needs testing for */ +	i = cfrv & 0x000000fe; +	if ((lp->chipset == DC21140) && (i == 0x20)) { +	    lp->rx_ovf = 1; +	} + +	/* Initialise the SROM pointers if possible */ +	if (lp->useSROM) { +	    lp->state = INITIALISED; +	    if (srom_infoleaf_info(dev)) { +		return -ENXIO; +	    } +	    srom_init(dev); +	} + +	lp->state = CLOSED; + +	/* +	** Check for an MII interface +	*/ +	if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) { +	    mii_get_phy(dev); +	} +	 +#ifndef __sparc_v9__ +	printk("      and requires IRQ%d (provided by %s).\n", dev->irq, +#else +	printk("      and requires IRQ%x (provided by %s).\n", dev->irq, +#endif +	       ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); +    } +     +    if (de4x5_debug & DEBUG_VERSION) { +	printk("%s", version); +    } +     +    /* The DE4X5-specific entries in the device structure. */ +    dev->open = &de4x5_open; +    dev->hard_start_xmit = &de4x5_queue_pkt; +    dev->stop = &de4x5_close; +    dev->get_stats = &de4x5_get_stats; +    dev->set_multicast_list = &set_multicast_list; +    dev->do_ioctl = &de4x5_ioctl; +     +    dev->mem_start = 0; +     +    /* Fill in the generic fields of the device structure. */ +    ether_setup(dev); +     +    /* Let the adapter sleep to save power */ +    yawn(dev, SLEEP); +     +    return status; +} + + +static int +de4x5_open(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int i, status = 0; +    s32 omr; + +    /* Allocate the RX buffers */ +    for (i=0; i<lp->rxRingSize; i++) { +	if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) { +	    de4x5_free_rx_buffs(dev); +	    return -EAGAIN; +	} +    } + +    /* +    ** Wake up the adapter +    */ +    yawn(dev, WAKEUP); + +    /*  +    ** Re-initialize the DE4X5...  +    */ +    status = de4x5_init(dev); +     +    lp->state = OPEN; +    de4x5_dbg_open(dev); +     +    if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ,  +		                                     lp->adapter_name, dev)) { +	printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); +	if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ, +			                             lp->adapter_name, dev)) { +	    printk("\n              Cannot get IRQ- reconfigure your hardware.\n"); +	    disable_ast(dev); +	    de4x5_free_rx_buffs(dev); +	    de4x5_free_tx_buffs(dev); +	    yawn(dev, SLEEP); +	    lp->state = CLOSED; +	    return -EAGAIN; +	} else { +	    printk("\n              Succeeded, but you should reconfigure your hardware to avoid this.\n"); +	    printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); +	} +    } + +    dev->tbusy = 0;                          +    dev->start = 1; +    lp->interrupt = UNMASK_INTERRUPTS; +    dev->trans_start = jiffies; +     +    START_DE4X5; +	 +    de4x5_setup_intr(dev); +     +    if (de4x5_debug & DEBUG_OPEN) { +	printk("\tsts:  0x%08x\n", inl(DE4X5_STS)); +	printk("\tbmr:  0x%08x\n", inl(DE4X5_BMR)); +	printk("\timr:  0x%08x\n", inl(DE4X5_IMR)); +	printk("\tomr:  0x%08x\n", inl(DE4X5_OMR)); +	printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR)); +	printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR)); +	printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR)); +	printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR)); +    } +     +    MOD_INC_USE_COUNT; +     +    return status; +} + +/* +** Initialize the DE4X5 operating conditions. NB: a chip problem with the +** DC21140 requires using perfect filtering mode for that chip. Since I can't +** see why I'd want > 14 multicast addresses, I have changed all chips to use +** the perfect filtering mode. Keep the DMA burst length at 8: there seems +** to be data corruption problems if it is larger (UDP errors seen from a +** ttcp source). +*/ +static int +de4x5_init(struct device *dev) +{   +    /* Lock out other processes whilst setting up the hardware */ +    test_and_set_bit(0, (void *)&dev->tbusy); +     +    de4x5_sw_reset(dev); +     +    /* Autoconfigure the connected port */ +    autoconf_media(dev); +     +    return 0; +} + +static int +de4x5_sw_reset(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int i, j, status = 0; +    s32 bmr, omr; +     +    /* Select the MII or SRL port now and RESET the MAC */ +    if (!lp->useSROM) { +	if (lp->phy[lp->active].id != 0) { +	    lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD; +	} else { +	    lp->infoblock_csr6 = OMR_SDP | OMR_TTM; +	} +	de4x5_switch_mac_port(dev); +    } + +    /*  +    ** Set the programmable burst length to 8 longwords for all the DC21140 +    ** Fasternet chips and 4 longwords for all others: DMA errors result +    ** without these values. Cache align 16 long. +    */ +    bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN; +    bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0); +    outl(bmr, DE4X5_BMR); + +    omr = inl(DE4X5_OMR) & ~OMR_PR;             /* Turn off promiscuous mode */ +    if (lp->chipset == DC21140) { +	omr |= (OMR_SDP | OMR_SB); +    } +    lp->setup_f = PERFECT; +    outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA); +    outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA); +     +    lp->rx_new = lp->rx_old = 0; +    lp->tx_new = lp->tx_old = 0; +     +    for (i = 0; i < lp->rxRingSize; i++) { +	lp->rx_ring[i].status = cpu_to_le32(R_OWN); +    } +     +    for (i = 0; i < lp->txRingSize; i++) { +	lp->tx_ring[i].status = cpu_to_le32(0); +    } +     +    barrier(); + +    /* Build the setup frame depending on filtering mode */ +    SetMulticastFilter(dev); +     +    load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL); +    outl(omr|OMR_ST, DE4X5_OMR); + +    /* Poll for setup frame completion (adapter interrupts are disabled now) */ +    sti();                                       /* Ensure timer interrupts */ +    for (j=0, i=0;(i<500) && (j==0);i++) {       /* Upto 500ms delay */ +	udelay(1000); +	if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; +    } +    outl(omr, DE4X5_OMR);                        /* Stop everything! */ + +    if (j == 0) { +	printk("%s: Setup frame timed out, status %08x\n", dev->name,  +	       inl(DE4X5_STS)); +	status = -EIO; +    } +     +    lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; +    lp->tx_old = lp->tx_new; + +    return status; +} + +/*  +** Writes a socket buffer address to the next available transmit descriptor. +*/ +static int +de4x5_queue_pkt(struct sk_buff *skb, struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int status = 0; + +    test_and_set_bit(0, (void*)&dev->tbusy);     /* Stop send re-tries */ +    if (lp->tx_enable == NO) {                   /* Cannot send for now */ +	return -1;                                 +    } +     +    /* +    ** Clean out the TX ring asynchronously to interrupts - sometimes the +    ** interrupts are lost by delayed descriptor status updates relative to +    ** the irq assertion, especially with a busy PCI bus. +    */ +    cli(); +    de4x5_tx(dev); +    sti(); + +    /* Test if cache is already locked - requeue skb if so */ +    if (test_and_set_bit(0, (void *)&lp->cache.lock) && !lp->interrupt)  +	return -1; + +    /* Transmit descriptor ring full or stale skb */ +    if (dev->tbusy || lp->tx_skb[lp->tx_new]) { +	if (lp->interrupt) { +	    de4x5_putb_cache(dev, skb);          /* Requeue the buffer */ +	} else { +	    de4x5_put_cache(dev, skb); +	} +	if (de4x5_debug & DEBUG_TX) { +	    printk("%s: transmit busy, lost media or stale skb found:\n  STS:%08x\n  tbusy:%ld\n  IMR:%08x\n  OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO")); +	} +    } else if (skb->len > 0) { +	/* If we already have stuff queued locally, use that first */ +	if (lp->cache.skb && !lp->interrupt) { +	    de4x5_put_cache(dev, skb); +	    skb = de4x5_get_cache(dev); +	} + +	while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) { +	    cli(); +	    test_and_set_bit(0, (void*)&dev->tbusy); +	    load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); +#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) + 	    lp->stats.tx_bytes += skb->len; +#endif +	    outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ +		 +	    lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; +	    dev->trans_start = jiffies; +		     +	    if (TX_BUFFS_AVAIL) { +		dev->tbusy = 0;         /* Another pkt may be queued */ +	    } +	    skb = de4x5_get_cache(dev); +	    sti(); +	} +	if (skb) de4x5_putb_cache(dev, skb); +    } +     +    lp->cache.lock = 0; + +    return status; +} + +/* +** The DE4X5 interrupt handler.  +**  +** I/O Read/Writes through intermediate PCI bridges are never 'posted', +** so that the asserted interrupt always has some real data to work with - +** if these I/O accesses are ever changed to memory accesses, ensure the +** STS write is read immediately to complete the transaction if the adapter +** is not on bus 0. Lost interrupts can still occur when the PCI bus load +** is high and descriptor status bits cannot be set before the associated +** interrupt is asserted and this routine entered. +*/ +static void +de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +    struct device *dev = (struct device *)dev_id; +    struct de4x5_private *lp; +    s32 imr, omr, sts, limit; +    u_long iobase; +     +    if (dev == NULL) { +	printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq); +	return; +    } +    lp = (struct de4x5_private *)dev->priv; +    iobase = dev->base_addr; +	 +    DISABLE_IRQs;                        /* Ensure non re-entrancy */ + +    if (test_and_set_bit(MASK_INTERRUPTS, (void*) &lp->interrupt)) +	printk("%s: Re-entering the interrupt handler.\n", dev->name); + +#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) +    synchronize_irq(); +#endif +	 +    for (limit=0; limit<8; limit++) { +	sts = inl(DE4X5_STS);            /* Read IRQ status */ +	outl(sts, DE4X5_STS);            /* Reset the board interrupts */ +	     +	if (!(sts & lp->irq_mask)) break;/* All done */ +	     +	if (sts & (STS_RI | STS_RU))     /* Rx interrupt (packet[s] arrived) */ +	  de4x5_rx(dev); +	     +	if (sts & (STS_TI | STS_TU))     /* Tx interrupt (packet sent) */ +	  de4x5_tx(dev);  +	     +	if (sts & STS_LNF) {             /* TP Link has failed */ +	    lp->irq_mask &= ~IMR_LFM; +	} +	     +	if (sts & STS_UNF) {             /* Transmit underrun */ +	    de4x5_txur(dev); +	} +	     +	if (sts & STS_SE) {              /* Bus Error */ +	    STOP_DE4X5; +	    printk("%s: Fatal bus error occurred, sts=%#8x, device stopped.\n", +		   dev->name, sts); +	    return; +	} +    } + +    /* Load the TX ring with any locally stored packets */ +    if (!test_and_set_bit(0, (void *)&lp->cache.lock)) { +	while (lp->cache.skb && !dev->tbusy && lp->tx_enable) { +	    de4x5_queue_pkt(de4x5_get_cache(dev), dev); +	} +	lp->cache.lock = 0; +    } + +    lp->interrupt = UNMASK_INTERRUPTS; +    ENABLE_IRQs; +     +    return; +} + +static int +de4x5_rx(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int entry; +    s32 status; +     +    for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0; +	                                                    entry=lp->rx_new) { +	status = (s32)le32_to_cpu(lp->rx_ring[entry].status); +	 +	if (lp->rx_ovf) { +	    if (inl(DE4X5_MFC) & MFC_FOCM) { +		de4x5_rx_ovfc(dev); +		break; +	    } +	} + +	if (status & RD_FS) {                 /* Remember the start of frame */ +	    lp->rx_old = entry; +	} +	 +	if (status & RD_LS) {                 /* Valid frame status */ +	    if (lp->tx_enable) lp->linkOK++; +	    if (status & RD_ES) {	      /* There was an error. */ +		lp->stats.rx_errors++;        /* Update the error stats. */ +		if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++; +		if (status & RD_CE)           lp->stats.rx_crc_errors++; +		if (status & RD_OF)           lp->stats.rx_fifo_errors++; +		if (status & RD_TL)           lp->stats.rx_length_errors++; +		if (status & RD_RF)           lp->pktStats.rx_runt_frames++; +		if (status & RD_CS)           lp->pktStats.rx_collision++; +		if (status & RD_DB)           lp->pktStats.rx_dribble++; +		if (status & RD_OF)           lp->pktStats.rx_overflow++; +	    } else {                          /* A valid frame received */ +		struct sk_buff *skb; +		short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status) +					                            >> 16) - 4; +		 +		if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) { +		    printk("%s: Insufficient memory; nuking packet.\n",  +			                                            dev->name); +		    lp->stats.rx_dropped++; +		} else { +		    de4x5_dbg_rx(skb, pkt_len); + +		    /* Push up the protocol stack */ +		    skb->protocol=eth_type_trans(skb,dev); +		    netif_rx(skb); +		     +		    /* Update stats */ +		    lp->stats.rx_packets++; +#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) + 		    lp->stats.rx_bytes += pkt_len; +#endif +		    de4x5_local_stats(dev, skb->data, pkt_len); +		} +	    } +	     +	    /* Change buffer ownership for this frame, back to the adapter */ +	    for (;lp->rx_old!=entry;lp->rx_old=(lp->rx_old+1)%lp->rxRingSize) { +		lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN); +		barrier(); +	    } +	    lp->rx_ring[entry].status = cpu_to_le32(R_OWN); +	    barrier(); +	} +	 +	/* +	** Update entry information +	*/ +	lp->rx_new = (lp->rx_new + 1) % lp->rxRingSize; +    } +     +    return 0; +} + +/* +** Buffer sent - check for TX buffer errors. +*/ +static int +de4x5_tx(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int entry; +    s32 status; +     +    for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { +	status = (s32)le32_to_cpu(lp->tx_ring[entry].status); +	if (status < 0) {                     /* Buffer not sent yet */ +	    break; +	} else if (status != 0x7fffffff) {    /* Not setup frame */ +	    if (status & TD_ES) {             /* An error happened */ +		lp->stats.tx_errors++;  +		if (status & TD_NC) lp->stats.tx_carrier_errors++; +		if (status & TD_LC) lp->stats.tx_window_errors++; +		if (status & TD_UF) lp->stats.tx_fifo_errors++; +		if (status & TD_EC) lp->pktStats.excessive_collisions++; +		if (status & TD_DE) lp->stats.tx_aborted_errors++; +	     +		if (TX_PKT_PENDING) { +		    outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */ +		} +	    } else {                      /* Packet sent */ +		lp->stats.tx_packets++; +		if (lp->tx_enable) lp->linkOK++; +	    } +	    /* Update the collision counter */ +	    lp->stats.collisions += ((status & TD_EC) ? 16 :  +				                      ((status & TD_CC) >> 3)); + +	    /* Free the buffer. */ +	    if (lp->tx_skb[entry] != NULL) { +		dev_kfree_skb(lp->tx_skb[entry], FREE_WRITE); +		lp->tx_skb[entry] = NULL; +	    } +	} +	 +	/* Update all the pointers */ +	lp->tx_old = (lp->tx_old + 1) % lp->txRingSize; +    } + +    if (TX_BUFFS_AVAIL && dev->tbusy) {  /* Any resources available? */ +	dev->tbusy = 0;                  /* Clear TX busy flag */ +	if (lp->interrupt) mark_bh(NET_BH); +    } +	 +    return 0; +} + +static int +de4x5_ast(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int next_tick = DE4X5_AUTOSENSE_MS; +     +    disable_ast(dev); +     +    if (lp->useSROM) { +	next_tick = srom_autoconf(dev); +    } else if (lp->chipset == DC21140) { +	next_tick = dc21140m_autoconf(dev); +    } else if (lp->chipset == DC21041) { +	next_tick = dc21041_autoconf(dev); +    } else if (lp->chipset == DC21040) { +	next_tick = dc21040_autoconf(dev); +    } +    lp->linkOK = 0; +    enable_ast(dev, next_tick); +     +    return 0; +} + +static int +de4x5_txur(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int omr; + +    omr = inl(DE4X5_OMR); +    if (!(omr & OMR_SF) || (lp->chipset==DC21041) || (lp->chipset==DC21040)) { +	omr &= ~(OMR_ST|OMR_SR); +	outl(omr, DE4X5_OMR); +	while (inl(DE4X5_STS) & STS_TS); +	if ((omr & OMR_TR) < OMR_TR) { +	    omr += 0x4000; +	} else { +	    omr |= OMR_SF; +	} +	outl(omr | OMR_ST | OMR_SR, DE4X5_OMR); +    } +     +    return 0; +} + +static int  +de4x5_rx_ovfc(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int omr; + +    omr = inl(DE4X5_OMR); +    outl(omr & ~OMR_SR, DE4X5_OMR); +    while (inl(DE4X5_STS) & STS_RS); + +    for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) { +	lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN); +	lp->rx_new = (lp->rx_new + 1) % lp->rxRingSize; +    } + +    outl(omr, DE4X5_OMR); +     +    return 0; +} + +static int +de4x5_close(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    s32 imr, omr; +     +    disable_ast(dev); +    dev->start = 0; +    dev->tbusy = 1; +     +    if (de4x5_debug & DEBUG_CLOSE) { +	printk("%s: Shutting down ethercard, status was %8.8x.\n", +	       dev->name, inl(DE4X5_STS)); +    } +     +    /*  +    ** We stop the DE4X5 here... mask interrupts and stop TX & RX +    */ +    DISABLE_IRQs; +    STOP_DE4X5; +     +    /* Free the associated irq */ +    free_irq(dev->irq, dev); +    lp->state = CLOSED; + +    /* Free any socket buffers */ +    de4x5_free_rx_buffs(dev); +    de4x5_free_tx_buffs(dev); +     +    MOD_DEC_USE_COUNT; +     +    /* Put the adapter to sleep to save power */ +    yawn(dev, SLEEP); +     +    return 0; +} + +static struct net_device_stats * +de4x5_get_stats(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +     +    lp->stats.rx_missed_errors = (int)(inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR)); +     +    return &lp->stats; +} + +static void +de4x5_local_stats(struct device *dev, char *buf, int pkt_len) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int i; + +    for (i=1; i<DE4X5_PKT_STAT_SZ-1; i++) { +        if (pkt_len < (i*DE4X5_PKT_BIN_SZ)) { +	    lp->pktStats.bins[i]++; +	    i = DE4X5_PKT_STAT_SZ; +	} +    } +    if (buf[0] & 0x01) {          /* Multicast/Broadcast */ +        if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) { +	    lp->pktStats.broadcast++; +	} else { +	    lp->pktStats.multicast++; +	} +    } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) && +	       (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) { +        lp->pktStats.unicast++; +    } +		 +    lp->pktStats.bins[0]++;       /* Duplicates stats.rx_packets */ +    if (lp->pktStats.bins[0] == 0) { /* Reset counters */ +        memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats)); +    } + +    return; +} + +static void +load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +     +    lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf)); +    lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER); +    lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags); +    lp->tx_skb[lp->tx_new] = skb; +    barrier(); +    lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN); +    barrier(); +     +    return; +} + +/* +** Set or clear the multicast filter for this adaptor. +*/ +static void +set_multicast_list(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; + +    /* First, double check that the adapter is open */ +    if (lp->state == OPEN) { +	if (dev->flags & IFF_PROMISC) {         /* set promiscuous mode */ +	    u32 omr; +	    omr = inl(DE4X5_OMR); +	    omr |= OMR_PR; +	    outl(omr, DE4X5_OMR); +	} else {  +	    SetMulticastFilter(dev); +	    load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |  +			                                SETUP_FRAME_LEN, NULL); +	     +	    lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; +	    outl(POLL_DEMAND, DE4X5_TPD);       /* Start the TX */ +	    dev->trans_start = jiffies; +	} +    } +     +    return; +} + +/* +** Calculate the hash code and update the logical address filter +** from a list of ethernet multicast addresses. +** Little endian crc one liner from Matt Thomas, DEC. +*/ +static void +SetMulticastFilter(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    struct dev_mc_list *dmi=dev->mc_list; +    u_long iobase = dev->base_addr; +    int i, j, bit, byte; +    u16 hashcode; +    u32 omr, crc, poly = CRC_POLYNOMIAL_LE; +    char *pa; +    unsigned char *addrs; + +    omr = inl(DE4X5_OMR); +    omr &= ~(OMR_PR | OMR_PM); +    pa = build_setup_frame(dev, ALL);        /* Build the basic frame */ +     +    if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) { +	omr |= OMR_PM;                       /* Pass all multicasts */ +    } else if (lp->setup_f == HASH_PERF) {   /* Hash Filtering */ +	for (i=0;i<dev->mc_count;i++) {      /* for each address in the list */ +	    addrs=dmi->dmi_addr; +	    dmi=dmi->next; +	    if ((*addrs & 0x01) == 1) {      /* multicast address? */  +		crc = 0xffffffff;            /* init CRC for each address */ +		for (byte=0;byte<ETH_ALEN;byte++) {/* for each address byte */ +		                             /* process each address bit */  +		    for (bit = *addrs++,j=0;j<8;j++, bit>>=1) { +			crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0); +		    } +		} +		hashcode = crc & HASH_BITS;  /* hashcode is 9 LSb of CRC */ +		 +		byte = hashcode >> 3;        /* bit[3-8] -> byte in filter */ +		bit = 1 << (hashcode & 0x07);/* bit[0-2] -> bit in byte */ +		 +		byte <<= 1;                  /* calc offset into setup frame */ +		if (byte & 0x02) { +		    byte -= 1; +		} +		lp->setup_frame[byte] |= bit; +	    } +	} +    } else {                                 /* Perfect filtering */ +	for (j=0; j<dev->mc_count; j++) { +	    addrs=dmi->dmi_addr; +	    dmi=dmi->next; +	    for (i=0; i<ETH_ALEN; i++) {  +		*(pa + (i&1)) = *addrs++; +		if (i & 0x01) pa += 4; +	    } +	} +    } +    outl(omr, DE4X5_OMR); +     +    return; +} + +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +/* +** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually +** the motherboard. Upto 15 EISA devices are supported. +*/ +__initfunc(static void +eisa_probe(struct device *dev, u_long ioaddr)) +{ +    int i, maxSlots, status, device; +    u_char irq; +    u_short vendor; +    u32 cfid; +    u_long iobase; +    struct bus_type *lp = &bus; +    char name[DE4X5_STRLEN]; + +    if (lastEISA == MAX_EISA_SLOTS) return;/* No more EISA devices to search */ + +    lp->bus = EISA; +     +    if (ioaddr == 0) {                     /* Autoprobing */ +	iobase = EISA_SLOT_INC;            /* Get the first slot address */ +	i = 1; +	maxSlots = MAX_EISA_SLOTS; +    } else {                               /* Probe a specific location */ +	iobase = ioaddr; +	i = (ioaddr >> 12); +	maxSlots = i + 1; +    } +     +    for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) { +	if (EISA_signature(name, EISA_ID)) { +	    cfid = (u32) inl(PCI_CFID); +	    cfrv = (u_short) inl(PCI_CFRV); +	    device = (cfid >> 8) & 0x00ffff00; +	    vendor = (u_short) cfid; +	     +	    /* Read the EISA Configuration Registers */ +	    irq = inb(EISA_REG0); +	    irq = de4x5_irq[(irq >> 1) & 0x03]; + +	    if (is_DC2114x) device |= (cfrv & CFRV_RN); +	    lp->chipset = device; + +	    /* Write the PCI Configuration Registers */ +	    outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS); +	    outl(0x00006000, PCI_CFLT); +	    outl(iobase, PCI_CBIO); +	     +	    DevicePresent(EISA_APROM); +	    if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) { +		dev->irq = irq; +		if ((status = de4x5_hw_init(dev, iobase)) == 0) { +		    num_de4x5s++; +		    if (loading_module) link_modules(lastModule, dev); +		    lastEISA = i; +		    return; +		} +	    } else if (ioaddr != 0) { +		printk("%s: region already allocated at 0x%04lx.\n", dev->name,iobase); +	    } +	} +    } + +    if (ioaddr == 0) lastEISA = i; + +    return; +} +#endif          /* !(__sparc_v9__) && !(__powerpc__) && !defined(__alpha__)*/ + +/* +** PCI bus I/O device probe +** NB: PCI I/O accesses and Bus Mastering are enabled by the PCI BIOS, not +** the driver. Some PCI BIOS's, pre V2.1, need the slot + features to be +** enabled by the user first in the set up utility. Hence we just check for +** enabled features and silently ignore the card if they're not. +** +** STOP PRESS: Some BIOS's __require__ the driver to enable the bus mastering +** bit. Here, check for I/O accesses and then set BM. If you put the card in +** a non BM slot, you're on your own (and complain to the PC vendor that your +** PC doesn't conform to the PCI standard)! +*/ +#define PCI_DEVICE    (dev_num << 3) +#define PCI_LAST_DEV  32 + +__initfunc(static void +pci_probe(struct device *dev, u_long ioaddr)) +{ +    u_char pb, pbus, dev_num, dnum, dev_fn, timer, tirq; +    u_short dev_id, vendor, index, status; +    u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE; +    u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */ +    struct bus_type *lp = &bus; + +    if (lastPCI == NO_MORE_PCI) return; + +    if (!pcibios_present()) { +	lastPCI = NO_MORE_PCI; +	return;          /* No PCI bus in this machine! */ +    } +     +    lp->bus = PCI; +    lp->bus_num = 0; + +    if ((ioaddr < 0x1000) && loading_module) { +	pbus = (u_short)(ioaddr >> 8); +	dnum = (u_short)(ioaddr & 0xff); +    } else { +	pbus = 0; +	dnum = 0; +    } + +    for (index=lastPCI+1;  +	 (pcibios_find_class(class, index, &pb, &dev_fn)== PCIBIOS_SUCCESSFUL); +	 index++) { +	dev_num = PCI_SLOT(dev_fn); +	if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) { +#ifdef __sparc_v9__ +	    struct pci_dev *pdev; +	    for (pdev = pci_devices; pdev; pdev = pdev->next) { +		if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break; +	    } +#endif +	    device = 0; +	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); +	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); +	    device = dev_id; +	    device <<= 8; +	    if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) { +		continue; +	    } + +	    /* Search for an SROM on this bus */ +	    if (lp->bus_num != pb) { +		lp->bus_num = pb; +		srom_search(index); +	    } + +	    /* Get the chip configuration revision register */ +	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); + +	    /* Set the device number information */ +	    lp->device = dev_num; +	    lp->bus_num = pb; +	     +	    /* Set the chipset information */ +	    if (is_DC2114x) device |= (cfrv & CFRV_RN); +	    lp->chipset = device; + +	    /* Get the board I/O address (64 bits on sparc64) */ +#ifndef __sparc_v9__ +	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp); +	    iobase = tmp; +#else +	    iobase = pdev->base_address[0]; +#endif +	    iobase &= CBIO_MASK; + +	    /* Fetch the IRQ to be used */ +#ifndef __sparc_v9__ +	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq); +	    irq = tirq; +#else +	    irq = pdev->irq; +#endif +	    if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; +	     +	    /* Check if I/O accesses and Bus Mastering are enabled */ +	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); +#ifdef __powerpc__ +	    if (!(status & PCI_COMMAND_IO)) { +		status |= PCI_COMMAND_IO; +		pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); +		pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); +	    } +#endif /* __powerpc__ */ +	    if (!(status & PCI_COMMAND_IO)) continue; + +	    if (!(status & PCI_COMMAND_MASTER)) { +		status |= PCI_COMMAND_MASTER; +		pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status); +		pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); +	    } +	    if (!(status & PCI_COMMAND_MASTER)) continue; + +	    /* Check the latency timer for values >= 0x60 */ +	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer); +	    if (timer < 0x60) { +		pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60); +	    } + +	    DevicePresent(DE4X5_APROM); +	    if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) { +		dev->irq = irq; +		if ((status = de4x5_hw_init(dev, iobase)) == 0) { +		    num_de4x5s++; +		    lastPCI = index; +		    if (loading_module) link_modules(lastModule, dev); +		    return; +		} +	    } else if (ioaddr != 0) { +		printk("%s: region already allocated at 0x%04lx.\n", dev->name, +		       iobase); +	    } +	} +    } + +    lastPCI = NO_MORE_PCI; + +    return; +} + +/* +** This function searches the current bus (which is >0) for a DECchip with an +** SROM, so that in multiport cards that have one SROM shared between multiple  +** DECchips, we can find the base SROM irrespective of the BIOS scan direction. +** For single port cards this is a time waster... +*/ +__initfunc(static void +srom_search(int index)) +{ +    u_char pb, dev_fn, tirq; +    u_short dev_id, dev_num, vendor, status; +    u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE; +    u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */ +    int i, j; +    struct bus_type *lp = &bus; + +    for (;  +	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); +	 index++) { + +	if (lp->bus_num != pb) return; +	dev_num = PCI_SLOT(dev_fn); +#ifdef __sparc_v9__ +	struct pci_dev *pdev; +	for (pdev = pci_devices; pdev; pdev = pdev->next) { +	    if ((pdev->bus->number == pb) && (pdev->devfn == dev_fn)) break; +	} +#endif +	device = 0; +	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); +	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); +	device = dev_id; +	device <<= 8; +	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) { +	    continue; +	} + +	/* Get the chip configuration revision register */ +	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); + +	/* Set the device number information */ +	lp->device = dev_num; +	lp->bus_num = pb; +	     +	/* Set the chipset information */ +	if (is_DC2114x) device |= (cfrv & CFRV_RN); +	lp->chipset = device; + +	/* Get the board I/O address (64 bits on sparc64) */ +#ifndef __sparc_v9__ +	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp); +	iobase = tmp; +#else +	iobase = pdev->base_address[0]; +#endif +	iobase &= CBIO_MASK; + +	/* Fetch the IRQ to be used */ +#ifndef __sparc_v9__ +	pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq); +	irq = tirq; +#else +	irq = pdev->irq; +#endif +	if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue; +	     +	/* Check if I/O accesses are enabled */ +	pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status); +	if (!(status & PCI_COMMAND_IO)) continue; + +	/* Search for a valid SROM attached to this DECchip */ +	DevicePresent(DE4X5_APROM); +	for (j=0, i=0; i<ETH_ALEN; i++) { +	    j += (u_char) *((u_char *)&lp->srom + SROM_HWADD + i); +	} +	if ((j != 0) && (j != 0x5fa)) { +	    last.chipset = device; +	    last.bus = pb; +	    last.irq = irq; +	    for (i=0; i<ETH_ALEN; i++) { +		last.addr[i] = (u_char)*((u_char *)&lp->srom + SROM_HWADD + i); +	    } +	    return; +	} +    } + +    return; +} + +__initfunc(static void +link_modules(struct device *dev, struct device *tmp)) +{ +    struct device *p=dev; + +    if (p) { +	while (((struct de4x5_private *)(p->priv))->next_module) { +	    p = ((struct de4x5_private *)(p->priv))->next_module; +	} + +	if (dev != tmp) { +	    ((struct de4x5_private *)(p->priv))->next_module = tmp; +	} else { +	    ((struct de4x5_private *)(p->priv))->next_module = NULL; +	} +    } + +    return; +} + +/* +** Auto configure the media here rather than setting the port at compile +** time. This routine is called by de4x5_init() and when a loss of media is +** detected (excessive collisions, loss of carrier, no carrier or link fail +** [TP] or no recent receive activity) to check whether the user has been  +** sneaky and changed the port on us. +*/ +static int +autoconf_media(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int next_tick = DE4X5_AUTOSENSE_MS; + +    lp->linkOK = 0; +    lp->c_media = AUTO;                     /* Bogus last media */ +    disable_ast(dev); +    inl(DE4X5_MFC);                         /* Zero the lost frames counter */ +    lp->media = INIT; +    lp->tcount = 0; + +    if (lp->useSROM) { +	next_tick = srom_autoconf(dev); +    } else if (lp->chipset == DC21040) { +	next_tick = dc21040_autoconf(dev); +    } else if (lp->chipset == DC21041) { +	next_tick = dc21041_autoconf(dev); +    } else if (lp->chipset == DC21140) { +	next_tick = dc21140m_autoconf(dev); +    } + +    enable_ast(dev, next_tick); +     +    return (lp->media); +} + +/* +** Autoconfigure the media when using the DC21040. AUI cannot be distinguished +** from BNC as the port has a jumper to set thick or thin wire. When set for +** BNC, the BNC port will indicate activity if it's not terminated correctly. +** The only way to test for that is to place a loopback packet onto the +** network and watch for errors. Since we're messing with the interrupt mask +** register, disable the board interrupts and do not allow any more packets to +** be queued to the hardware. Re-enable everything only when the media is +** found. +** I may have to "age out" locally queued packets so that the higher layer +** timeouts don't effectively duplicate packets on the network. +*/ +static int +dc21040_autoconf(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int next_tick = DE4X5_AUTOSENSE_MS; +    s32 imr; +     +    switch (lp->media) { +    case INIT: +	DISABLE_IRQs; +	lp->tx_enable = NO; +	lp->timeout = -1; +	de4x5_save_skbs(dev); +	if ((lp->autosense == AUTO) || (lp->autosense == TP)) { +	    lp->media = TP; +	} else if ((lp->autosense == BNC) || (lp->autosense == AUI) || (lp->autosense == BNC_AUI)) { +	    lp->media = BNC_AUI; +	} else if (lp->autosense == EXT_SIA) { +	    lp->media = EXT_SIA; +	} else { +	    lp->media = NC; +	} +	lp->local_state = 0; +	next_tick = dc21040_autoconf(dev); +	break; +	 +    case TP: +	next_tick = dc21040_state(dev, 0x8f01, 0xffff, 0x0000, 3000, BNC_AUI,  +		                                         TP_SUSPECT, test_tp); +	break; +	 +    case TP_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21040_autoconf); +	break; +	 +    case BNC: +    case AUI: +    case BNC_AUI: +	next_tick = dc21040_state(dev, 0x8f09, 0x0705, 0x0006, 3000, EXT_SIA,  +		                                  BNC_AUI_SUSPECT, ping_media); +	break; +	 +    case BNC_AUI_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, BNC_AUI, ping_media, dc21040_autoconf); +	break; +	 +    case EXT_SIA: +	next_tick = dc21040_state(dev, 0x3041, 0x0000, 0x0006, 3000,  +		                              NC, EXT_SIA_SUSPECT, ping_media); +	break; +	 +    case EXT_SIA_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, EXT_SIA, ping_media, dc21040_autoconf); +	break; +	 +    case NC: +	/* default to TP for all */ +	reset_init_sia(dev, 0x8f01, 0xffff, 0x0000); +	if (lp->media != lp->c_media) { +	    de4x5_dbg_media(dev); +	    lp->c_media = lp->media; +	} +	lp->media = INIT; +	lp->tx_enable = NO; +	break; +    } +     +    return next_tick; +} + +static int +dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, +	      int next_state, int suspect_state,  +	      int (*fn)(struct device *, int)) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int next_tick = DE4X5_AUTOSENSE_MS; +    int linkBad; + +    switch (lp->local_state) { +    case 0: +	reset_init_sia(dev, csr13, csr14, csr15); +	lp->local_state++; +	next_tick = 500; +	break; +	     +    case 1: +	if (!lp->tx_enable) { +	    linkBad = fn(dev, timeout); +	    if (linkBad < 0) { +		next_tick = linkBad & ~TIMER_CB; +	    } else { +		if (linkBad && (lp->autosense == AUTO)) { +		    lp->local_state = 0; +		    lp->media = next_state; +		} else { +		    de4x5_init_connection(dev); +		} +	    } +	} else if (!lp->linkOK && (lp->autosense == AUTO)) { +	    lp->media = suspect_state; +	    next_tick = 3000; +	} +	break; +    } +     +    return next_tick; +} + +static int +de4x5_suspect_state(struct device *dev, int timeout, int prev_state, +		      int (*fn)(struct device *, int), +		      int (*asfn)(struct device *)) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int next_tick = DE4X5_AUTOSENSE_MS; +    int linkBad; + +    switch (lp->local_state) { +    case 1: +	if (lp->linkOK) { +	    lp->media = prev_state; +	} else { +	    lp->local_state++; +	    next_tick = asfn(dev); +	} +	break; + +    case 2: +	linkBad = fn(dev, timeout); +	if (linkBad < 0) { +	    next_tick = linkBad & ~TIMER_CB; +	} else if (!linkBad) { +	    lp->local_state--; +	    lp->media = prev_state; +	} else { +	    lp->media = INIT; +	    lp->tcount++; +	} +    } + +    return next_tick; +} + +/* +** Autoconfigure the media when using the DC21041. AUI needs to be tested +** before BNC, because the BNC port will indicate activity if it's not +** terminated correctly. The only way to test for that is to place a loopback +** packet onto the network and watch for errors. Since we're messing with +** the interrupt mask register, disable the board interrupts and do not allow +** any more packets to be queued to the hardware. Re-enable everything only +** when the media is found. +*/ +static int +dc21041_autoconf(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    s32 sts, irqs, irq_mask, imr, omr; +    int next_tick = DE4X5_AUTOSENSE_MS; +     +    switch (lp->media) { +    case INIT: +	DISABLE_IRQs; +	lp->tx_enable = NO; +	lp->timeout = -1; +	de4x5_save_skbs(dev);          /* Save non transmitted skb's */ +	if ((lp->autosense == AUTO) || (lp->autosense == TP_NW)) { +	    lp->media = TP;            /* On chip auto negotiation is broken */ +	} else if (lp->autosense == TP) { +	    lp->media = TP; +	} else if (lp->autosense == BNC) { +	    lp->media = BNC; +	} else if (lp->autosense == AUI) { +	    lp->media = AUI; +	} else { +	    lp->media = NC; +	} +	lp->local_state = 0; +	next_tick = dc21041_autoconf(dev); +	break; +	 +    case TP_NW: +	if (lp->timeout < 0) { +	    omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ +	    outl(omr | OMR_FDX, DE4X5_OMR); +	} +	irqs = STS_LNF | STS_LNP; +	irq_mask = IMR_LFM | IMR_LPM; +	sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400); +	if (sts < 0) { +	    next_tick = sts & ~TIMER_CB; +	} else { +	    if (sts & STS_LNP) { +		lp->media = ANS; +	    } else { +		lp->media = AUI; +	    } +	    next_tick = dc21041_autoconf(dev); +	} +	break; +	 +    case ANS: +	if (!lp->tx_enable) { +	    irqs = STS_LNP; +	    irq_mask = IMR_LPM; +	    sts = test_ans(dev, irqs, irq_mask, 3000); +	    if (sts < 0) { +		next_tick = sts & ~TIMER_CB; +	    } else { +		if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { +		    lp->media = TP; +		    next_tick = dc21041_autoconf(dev); +		} else { +		    lp->local_state = 1; +		    de4x5_init_connection(dev); +		} +	    } +	} else if (!lp->linkOK && (lp->autosense == AUTO)) { +	    lp->media = ANS_SUSPECT; +	    next_tick = 3000; +	} +	break; +	 +    case ANS_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, ANS, test_tp, dc21041_autoconf); +	break; +	 +    case TP: +	if (!lp->tx_enable) { +	    if (lp->timeout < 0) { +		omr = inl(DE4X5_OMR);          /* Set up half duplex for TP */ +		outl(omr & ~OMR_FDX, DE4X5_OMR); +	    } +	    irqs = STS_LNF | STS_LNP; +	    irq_mask = IMR_LFM | IMR_LPM; +	    sts = test_media(dev,irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400); +	    if (sts < 0) { +		next_tick = sts & ~TIMER_CB; +	    } else { +		if (!(sts & STS_LNP) && (lp->autosense == AUTO)) { +		    if (inl(DE4X5_SISR) & SISR_NRA) { +			lp->media = AUI;       /* Non selected port activity */ +		    } else { +			lp->media = BNC; +		    } +		    next_tick = dc21041_autoconf(dev); +		} else { +		    lp->local_state = 1; +		    de4x5_init_connection(dev); +		} +	    } +	} else if (!lp->linkOK && (lp->autosense == AUTO)) { +	    lp->media = TP_SUSPECT; +	    next_tick = 3000; +	} +	break; +	 +    case TP_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, TP, test_tp, dc21041_autoconf); +	break; +	 +    case AUI: +	if (!lp->tx_enable) { +	    if (lp->timeout < 0) { +		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */ +		outl(omr & ~OMR_FDX, DE4X5_OMR); +	    } +	    irqs = 0; +	    irq_mask = 0; +	    sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x000e, 1000); +	    if (sts < 0) { +		next_tick = sts & ~TIMER_CB; +	    } else { +		if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { +		    lp->media = BNC; +		    next_tick = dc21041_autoconf(dev); +		} else { +		    lp->local_state = 1; +		    de4x5_init_connection(dev); +		} +	    } +	} else if (!lp->linkOK && (lp->autosense == AUTO)) { +	    lp->media = AUI_SUSPECT; +	    next_tick = 3000; +	} +	break; +	 +    case AUI_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc21041_autoconf); +	break; +	 +    case BNC: +	switch (lp->local_state) { +	case 0: +	    if (lp->timeout < 0) { +		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */ +		outl(omr & ~OMR_FDX, DE4X5_OMR); +	    } +	    irqs = 0; +	    irq_mask = 0; +	    sts = test_media(dev,irqs, irq_mask, 0xef09, 0xf73d, 0x0006, 1000); +	    if (sts < 0) { +		next_tick = sts & ~TIMER_CB; +	    } else { +		lp->local_state++;             /* Ensure media connected */ +		next_tick = dc21041_autoconf(dev); +	    } +	    break; +	     +	case 1: +	    if (!lp->tx_enable) { +		if ((sts = ping_media(dev, 3000)) < 0) { +		    next_tick = sts & ~TIMER_CB; +		} else { +		    if (sts) { +			lp->local_state = 0; +			lp->media = NC; +		    } else { +			de4x5_init_connection(dev); +		    } +		} +	    } else if (!lp->linkOK && (lp->autosense == AUTO)) { +		lp->media = BNC_SUSPECT; +		next_tick = 3000; +	    } +	    break; +	} +	break; +	 +    case BNC_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc21041_autoconf); +	break; +	 +    case NC: +	omr = inl(DE4X5_OMR);    /* Set up full duplex for the autonegotiate */ +	outl(omr | OMR_FDX, DE4X5_OMR); +	reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ +	if (lp->media != lp->c_media) { +	    de4x5_dbg_media(dev); +	    lp->c_media = lp->media; +	} +	lp->media = INIT; +	lp->tx_enable = NO; +	break; +    } +     +    return next_tick; +} + +/* +** Some autonegotiation chips are broken in that they do not return the +** acknowledge bit (anlpa & MII_ANLPA_ACK) in the link partner advertisement +** register, except at the first power up negotiation. +*/ +static int +dc21140m_autoconf(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int ana, anlpa, cap, cr, slnk, sr; +    int next_tick = DE4X5_AUTOSENSE_MS; +    u_long imr, omr, iobase = dev->base_addr; + +    switch(lp->media) { +    case INIT:  +        if (lp->timeout < 0) { +	    DISABLE_IRQs; +	    lp->tx_enable = FALSE; +	    lp->linkOK = 0; +	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */ +	} +	if ((next_tick = de4x5_reset_phy(dev)) < 0) { +	    next_tick &= ~TIMER_CB; +	} else { +	    if (lp->useSROM) { +		if (srom_map_media(dev) < 0) { +		    lp->tcount++; +		    return next_tick; +		} +		srom_exec(dev, lp->phy[lp->active].gep); +		if (lp->infoblock_media == ANS) { +		    ana = lp->phy[lp->active].ana | MII_ANA_CSMA; +		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); +		} +	    } else { +		lp->tmp = MII_SR_ASSC;     /* Fake out the MII speed set */ +		SET_10Mb; +		if (lp->autosense == _100Mb) { +		    lp->media = _100Mb; +		} else if (lp->autosense == _10Mb) { +		    lp->media = _10Mb; +		} else if ((lp->autosense == AUTO) &&  +			            ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { +		    ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); +		    ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); +		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); +		    lp->media = ANS; +		} else if (lp->autosense == AUTO) { +		    lp->media = SPD_DET; +		} else if (is_spd_100(dev) && is_100_up(dev)) { +		    lp->media = _100Mb; +		} else { +		    lp->media = NC; +		} +	    } +	    lp->local_state = 0; +	    next_tick = dc21140m_autoconf(dev); +	} +	break; +	 +    case ANS: +	switch (lp->local_state) { +	case 0: +	    if (lp->timeout < 0) { +		mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); +	    } +	    cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); +	    if (cr < 0) { +		next_tick = cr & ~TIMER_CB; +	    } else { +		if (cr) { +		    lp->local_state = 0; +		    lp->media = SPD_DET; +		} else { +		    lp->local_state++; +		} +		next_tick = dc21140m_autoconf(dev); +	    } +	    break; +	     +	case 1: +	    if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { +		next_tick = sr & ~TIMER_CB; +	    } else { +		lp->media = SPD_DET; +		lp->local_state = 0; +		if (sr) {                         /* Success! */ +		    lp->tmp = MII_SR_ASSC; +		    anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); +		    ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); +		    if (!(anlpa & MII_ANLPA_RF) &&  +			 (cap = anlpa & MII_ANLPA_TAF & ana)) { +			if (cap & MII_ANA_100M) { +			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); +			    lp->media = _100Mb; +			} else if (cap & MII_ANA_10M) { +			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + +			    lp->media = _10Mb; +			} +		    } +		}                       /* Auto Negotiation failed to finish */ +		next_tick = dc21140m_autoconf(dev); +	    }                           /* Auto Negotiation failed to start */ +	    break; +	} +	break; +	 +    case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */ +        if (lp->timeout < 0) { +	    lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS :  +		                                  (~gep_rd(dev) & GEP_LNP)); +	    SET_100Mb_PDET; +	} +        if ((slnk = test_for_100Mb(dev, 6500)) < 0) { +	    next_tick = slnk & ~TIMER_CB; +	} else { +	    if (is_spd_100(dev) && is_100_up(dev)) { +		lp->media = _100Mb; +	    } else if ((!is_spd_100(dev) && (is_10_up(dev) & lp->tmp))) { +		lp->media = _10Mb; +	    } else { +		lp->media = NC; +	    } +	    next_tick = dc21140m_autoconf(dev); +	} +	break; +	 +    case _100Mb:                               /* Set 100Mb/s */ +        next_tick = 3000; +	if (!lp->tx_enable) { +	    SET_100Mb; +	    de4x5_init_connection(dev); +	} else { +	    if (!lp->linkOK && (lp->autosense == AUTO)) { +		if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { +		    lp->media = INIT; +		    lp->tcount++; +		    next_tick = DE4X5_AUTOSENSE_MS; +		} +	    } +	} +	break; +	 +    case BNC: +    case AUI: +    case _10Mb:                                /* Set 10Mb/s */ +        next_tick = 3000; +	if (!lp->tx_enable) { +	    SET_10Mb; +	    de4x5_init_connection(dev); +	} else { +	    if (!lp->linkOK && (lp->autosense == AUTO)) { +		if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { +		    lp->media = INIT; +		    lp->tcount++; +		    next_tick = DE4X5_AUTOSENSE_MS; +		} +	    } +	} +	break; +	 +    case NC: +        if (lp->media != lp->c_media) { +	    de4x5_dbg_media(dev); +	    lp->c_media = lp->media; +	} +	lp->media = INIT; +	lp->tx_enable = FALSE; +	break; +    } +     +    return next_tick; +} + +/* +** This routine may be merged into dc21140m_autoconf() sometime as I'm +** changing how I figure out the media - but trying to keep it backwards +** compatible with the de500-xa and de500-aa. +** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock +** functions and set during de4x5_mac_port() and/or de4x5_reset_phy(). +** This routine just has to figure out whether 10Mb/s or 100Mb/s is +** active. +** When autonegotiation is working, the ANS part searches the SROM for +** the highest common speed (TP) link that both can run and if that can +** be full duplex. That infoblock is executed and then the link speed set. +** +** Only _10Mb and _100Mb are tested here. +*/ +static int +dc2114x_autoconf(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts; +    int next_tick = DE4X5_AUTOSENSE_MS; + +    switch (lp->media) { +    case INIT: +        if (lp->timeout < 0) { +	    DISABLE_IRQs; +	    lp->tx_enable = FALSE; +	    lp->linkOK = 0; +            lp->timeout = -1; +	    de4x5_save_skbs(dev);            /* Save non transmitted skb's */ +	    if (lp->params.autosense & ~AUTO) { +		srom_map_media(dev);         /* Fixed media requested      */ +		if (lp->media != lp->params.autosense) { +		    lp->tcount++; +		    lp->media = INIT; +		    return next_tick; +		} +		lp->media = INIT; +	    } +	} +	if ((next_tick = de4x5_reset_phy(dev)) < 0) { +	    next_tick &= ~TIMER_CB; +	} else { +	    if (lp->autosense == _100Mb) { +		lp->media = _100Mb; +	    } else if (lp->autosense == _10Mb) { +		lp->media = _10Mb; +	    } else if (lp->autosense == TP) { +		lp->media = TP; +	    } else if (lp->autosense == BNC) { +		lp->media = BNC; +	    } else if (lp->autosense == AUI) { +		lp->media = AUI; +	    } else { +		lp->media = SPD_DET; +		if ((lp->infoblock_media == ANS) &&  +		                    ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { +		    ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); +		    ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); +		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); +		    lp->media = ANS; +		} +	    } +	    lp->local_state = 0; +	    next_tick = dc2114x_autoconf(dev); +        } +	break; +	 +    case ANS: +	switch (lp->local_state) { +	case 0: +	    if (lp->timeout < 0) { +		mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); +	    } +	    cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500); +	    if (cr < 0) { +		next_tick = cr & ~TIMER_CB; +	    } else { +		if (cr) { +		    lp->local_state = 0; +		    lp->media = SPD_DET; +		} else { +		    lp->local_state++; +		} +		next_tick = dc2114x_autoconf(dev); +	    } +	    break; +	     +	case 1: +	    if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) { +		next_tick = sr & ~TIMER_CB; +	    } else { +		lp->media = SPD_DET; +		lp->local_state = 0; +		if (sr) {                         /* Success! */ +		    lp->tmp = MII_SR_ASSC; +		    anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII); +		    ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); +		    if (!(anlpa & MII_ANLPA_RF) &&  +			 (cap = anlpa & MII_ANLPA_TAF & ana)) { +			if (cap & MII_ANA_100M) { +			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); +			    lp->media = _100Mb; +			} else if (cap & MII_ANA_10M) { +			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); +			    lp->media = _10Mb; +			} +		    } +		}                       /* Auto Negotiation failed to finish */ +		next_tick = dc2114x_autoconf(dev); +	    }                           /* Auto Negotiation failed to start  */ +	    break; +	} +	break; +	 +    case AUI: +	if (!lp->tx_enable) { +	    if (lp->timeout < 0) { +		omr = inl(DE4X5_OMR);   /* Set up half duplex for AUI        */ +		outl(omr & ~OMR_FDX, DE4X5_OMR); +	    } +	    irqs = 0; +	    irq_mask = 0; +	    sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); +	    if (sts < 0) { +		next_tick = sts & ~TIMER_CB; +	    } else { +		if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) { +		    lp->media = BNC; +		    next_tick = dc2114x_autoconf(dev); +		} else { +		    lp->local_state = 1; +		    de4x5_init_connection(dev); +		} +	    } +	} else if (!lp->linkOK && (lp->autosense == AUTO)) { +	    lp->media = AUI_SUSPECT; +	    next_tick = 3000; +	} +	break; +	 +    case AUI_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf); +	break; +	 +    case BNC: +	switch (lp->local_state) { +	case 0: +	    if (lp->timeout < 0) { +		omr = inl(DE4X5_OMR);   /* Set up half duplex for BNC        */ +		outl(omr & ~OMR_FDX, DE4X5_OMR); +	    } +	    irqs = 0; +	    irq_mask = 0; +	    sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000); +	    if (sts < 0) { +		next_tick = sts & ~TIMER_CB; +	    } else { +		lp->local_state++;      /* Ensure media connected            */ +		next_tick = dc2114x_autoconf(dev); +	    } +	    break; +	     +	case 1: +	    if (!lp->tx_enable) { +		if ((sts = ping_media(dev, 3000)) < 0) { +		    next_tick = sts & ~TIMER_CB; +		} else { +		    if (sts) { +			lp->local_state = 0; +			lp->tcount++; +			lp->media = INIT; +		    } else { +			de4x5_init_connection(dev); +		    } +		} +	    } else if (!lp->linkOK && (lp->autosense == AUTO)) { +		lp->media = BNC_SUSPECT; +		next_tick = 3000; +	    } +	    break; +	} +	break; +	 +    case BNC_SUSPECT: +	next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf); +	break; +	 +    case SPD_DET:                       /* Choose 10Mb/s or 100Mb/s          */ +	  if (srom_map_media(dev) < 0) { +	      lp->tcount++; +	      lp->media = INIT; +	      return next_tick; +	  } +	  if (lp->media == _100Mb) { +	      if ((slnk = test_for_100Mb(dev, 6500)) < 0) { +		  lp->media = SPD_DET; +		  return  (slnk & ~TIMER_CB); +	      } +	  } else { +	      if (wait_for_link(dev) < 0) { +		  lp->media = SPD_DET; +		  return PDET_LINK_WAIT; +	      } +	  }  +	  if (lp->media == ANS) {           /* Do MII parallel detection */ +	      if (is_spd_100(dev)) { +		  lp->media = _100Mb; +	      } else { +		  lp->media = _10Mb; +	      } +	      next_tick = dc2114x_autoconf(dev); +	  } else if (((lp->media == _100Mb) && is_100_up(dev)) || +		     (((lp->media == _10Mb) || (lp->media == TP) || +		       (lp->media == BNC)   || (lp->media == AUI)) &&  +		      is_10_up(dev))) { +	      next_tick = dc2114x_autoconf(dev); +	  } else { +	      lp->tcount++; +	      lp->media = INIT; +	  } +	  break; +	 +    case _10Mb: +        next_tick = 3000; +	if (!lp->tx_enable) { +	    SET_10Mb; +	    de4x5_init_connection(dev); +	} else { +	    if (!lp->linkOK && (lp->autosense == AUTO)) { +		if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) { +		    lp->media = INIT; +		    lp->tcount++; +		    next_tick = DE4X5_AUTOSENSE_MS; +		} +	    } +	} +	break; + +    case _100Mb: +        next_tick = 3000; +	if (!lp->tx_enable) { +	    SET_100Mb; +	    de4x5_init_connection(dev); +	} else { +	    if (!lp->linkOK && (lp->autosense == AUTO)) { +		if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) { +		    lp->media = INIT; +		    lp->tcount++; +		    next_tick = DE4X5_AUTOSENSE_MS; +		} +	    } +	} +	break; + +    default: +	lp->tcount++; +printk("Huh?: media:%02x\n", lp->media); +	lp->media = INIT; +	break; +    } +     +    return next_tick; +} + +static int +srom_autoconf(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + +    return lp->infoleaf_fn(dev); +} + +/* +** This mapping keeps the original media codes and FDX flag unchanged. +** While it isn't strictly necessary, it helps me for the moment... +** The early return avoids a media state / SROM media space clash. +*/ +static int +srom_map_media(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + +    lp->fdx = 0; +    if (lp->infoblock_media == lp->media)  +      return 0; + +    switch(lp->infoblock_media) { +      case SROM_10BASETF: +	if (!lp->params.fdx) return -1; +	lp->fdx = TRUE; +      case SROM_10BASET: +	if (lp->params.fdx && !lp->fdx) return -1; +	if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) { +	    lp->media = _10Mb; +	} else { +	    lp->media = TP; +	} +	break; + +      case SROM_10BASE2: +	lp->media = BNC; +	break; + +      case SROM_10BASE5: +	lp->media = AUI; +	break; + +      case SROM_100BASETF: +        if (!lp->params.fdx) return -1; +	lp->fdx = TRUE; +      case SROM_100BASET: +	if (lp->params.fdx && !lp->fdx) return -1; +	lp->media = _100Mb; +	break; + +      case SROM_100BASET4: +	lp->media = _100Mb; +	break; + +      case SROM_100BASEFF: +	if (!lp->params.fdx) return -1; +	lp->fdx = TRUE; +      case SROM_100BASEF:  +	if (lp->params.fdx && !lp->fdx) return -1; +	lp->media = _100Mb; +	break; + +      case ANS: +	lp->media = ANS; +	break; + +      default:  +	printk("%s: Bad media code [%d] detected in SROM!\n", dev->name,  +	                                                  lp->infoblock_media); +	return -1; +	break; +    } + +    return 0; +} + +static void +de4x5_init_connection(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; + +    if (lp->media != lp->c_media) { +        de4x5_dbg_media(dev); +	lp->c_media = lp->media;          /* Stop scrolling media messages */ +    } + +    cli(); +    de4x5_rst_desc_ring(dev); +    de4x5_setup_intr(dev); +    lp->tx_enable = YES; +    dev->tbusy = 0; +    sti(); +    outl(POLL_DEMAND, DE4X5_TPD); +    mark_bh(NET_BH); + +    return; +} + +/* +** General PHY reset function. Some MII devices don't reset correctly +** since their MII address pins can float at voltages that are dependent +** on the signal pin use. Do a double reset to ensure a reset. +*/ +static int +de4x5_reset_phy(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int next_tick = 0; + +    if ((lp->useSROM) || (lp->phy[lp->active].id)) { +	if (lp->timeout < 0) { +	    if (lp->useSROM) { +		if (lp->phy[lp->active].rst) { +		    srom_exec(dev, lp->phy[lp->active].rst); +		    srom_exec(dev, lp->phy[lp->active].rst); +		} else if (lp->rst) {          /* Type 5 infoblock reset */ +		    srom_exec(dev, lp->rst); +		    srom_exec(dev, lp->rst); +		} +	    } else { +		PHY_HARD_RESET; +	    } +	    if (lp->useMII) { +	        mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); +            } +        } +	if (lp->useMII) { +	    next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); +	} +    } else if (lp->chipset == DC21140) { +	PHY_HARD_RESET; +    } + +    return next_tick; +} + +static int +test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    s32 sts, csr12; +     +    if (lp->timeout < 0) { +	lp->timeout = msec/100; +	if (!lp->useSROM) {      /* Already done if by SROM, else dc2104[01] */ +	    reset_init_sia(dev, csr13, csr14, csr15); +	} + +	/* set up the interrupt mask */ +	outl(irq_mask, DE4X5_IMR); + +	/* clear all pending interrupts */ +	sts = inl(DE4X5_STS); +	outl(sts, DE4X5_STS); +	 +	/* clear csr12 NRA and SRA bits */ +	if ((lp->chipset == DC21041) || lp->useSROM) { +	    csr12 = inl(DE4X5_SISR); +	    outl(csr12, DE4X5_SISR); +	} +    } +     +    sts = inl(DE4X5_STS) & ~TIMER_CB; +     +    if (!(sts & irqs) && --lp->timeout) { +	sts = 100 | TIMER_CB; +    } else { +	lp->timeout = -1; +    } +     +    return sts; +} + +static int +test_tp(struct device *dev, s32 msec) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int sisr; +     +    if (lp->timeout < 0) { +	lp->timeout = msec/100; +    } +     +    sisr = (inl(DE4X5_SISR) & ~TIMER_CB) & (SISR_LKF | SISR_NCR); + +    if (sisr && --lp->timeout) { +	sisr = 100 | TIMER_CB; +    } else { +	lp->timeout = -1; +    } +     +    return sisr; +} + +/* +** Samples the 100Mb Link State Signal. The sample interval is important +** because too fast a rate can give erroneous results and confuse the +** speed sense algorithm. +*/ +#define SAMPLE_INTERVAL 500  /* ms */ +#define SAMPLE_DELAY    2000 /* ms */ +static int +test_for_100Mb(struct device *dev, int msec) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK); + +    if (lp->timeout < 0) { +	if ((msec/SAMPLE_INTERVAL) <= 0) return 0; +	if (msec > SAMPLE_DELAY) { +	    lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL; +	    gep = SAMPLE_DELAY | TIMER_CB; +	    return gep; +	} else { +	    lp->timeout = msec/SAMPLE_INTERVAL; +	} +    } +     +    if (lp->phy[lp->active].id || lp->useSROM) { +	gep = is_100_up(dev) | is_spd_100(dev); +    } else { +	gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP)); +    } +    if (!(gep & ret) && --lp->timeout) { +	gep = SAMPLE_INTERVAL | TIMER_CB; +    } else { +	lp->timeout = -1; +    } +     +    return gep; +} + +static int +wait_for_link(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + +    if (lp->timeout < 0) { +	lp->timeout = 1; +    } +     +    if (lp->timeout--) { +	return TIMER_CB; +    } else { +	lp->timeout = -1; +    } +     +    return 0; +} + +/* +** +** +*/ +static int +test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int test; +    u_long iobase = dev->base_addr; +     +    if (lp->timeout < 0) { +	lp->timeout = msec/100; +    } +     +    if (pol) pol = ~0; +    reg = mii_rd((u_char)reg, lp->phy[lp->active].addr, DE4X5_MII) & mask; +    test = (reg ^ pol) & mask; +     +    if (test && --lp->timeout) { +	reg = 100 | TIMER_CB; +    } else { +	lp->timeout = -1; +    } +     +    return reg; +} + +static int +is_spd_100(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int spd; +     +    if (lp->useMII) { +	spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); +	spd = ~(spd ^ lp->phy[lp->active].spd.value); +	spd &= lp->phy[lp->active].spd.mask; +    } else if (!lp->useSROM) {                      /* de500-xa */ +	spd = ((~gep_rd(dev)) & GEP_SLNK); +    } else { +	if ((lp->ibn == 2) || !lp->asBitValid) +	    return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + +	spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) | +	          (lp->linkOK & ~lp->asBitValid); +    } +     +    return spd; +} + +static int +is_100_up(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +     +    if (lp->useMII) { +	/* Double read for sticky bits & temporary drops */ +	mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); +	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); +    } else if (!lp->useSROM) {                       /* de500-xa */ +	return ((~gep_rd(dev)) & GEP_SLNK); +    } else { +	if ((lp->ibn == 2) || !lp->asBitValid) +	    return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0); + +        return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | +		(lp->linkOK & ~lp->asBitValid)); +    } +} + +static int +is_10_up(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +     +    if (lp->useMII) { +	/* Double read for sticky bits & temporary drops */ +	mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); +	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); +    } else if (!lp->useSROM) {                       /* de500-xa */ +	return ((~gep_rd(dev)) & GEP_LNP); +    } else { +	if ((lp->ibn == 2) || !lp->asBitValid) +	    return (((lp->chipset & ~0x00ff) == DC2114x) ? +		    (~inl(DE4X5_SISR)&SISR_LS10): +		    0); + +	return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) | +		(lp->linkOK & ~lp->asBitValid)); +    } +} + +static int +is_anc_capable(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +     +    if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { +	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); +    } else if ((lp->chipset & ~0x00ff) == DC2114x) { +	return (inl(DE4X5_SISR) & SISR_LPN) >> 12; +    } else { +	return 0; +    } +} + +/* +** Send a packet onto the media and watch for send errors that indicate the +** media is bad or unconnected. +*/ +static int +ping_media(struct device *dev, int msec) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int sisr; +     +    if (lp->timeout < 0) { +	lp->timeout = msec/100; +	 +	lp->tmp = lp->tx_new;                /* Remember the ring position */ +	load_packet(dev, lp->frame, TD_LS | TD_FS | sizeof(lp->frame), NULL); +	lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; +	outl(POLL_DEMAND, DE4X5_TPD); +    } +     +    sisr = inl(DE4X5_SISR); + +    if ((!(sisr & SISR_NCR)) &&  +	((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) &&  +	 (--lp->timeout)) { +	sisr = 100 | TIMER_CB; +    } else { +	if ((!(sisr & SISR_NCR)) &&  +	    !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) && +	    lp->timeout) { +	    sisr = 0; +	} else { +	    sisr = 1; +	} +	lp->timeout = -1; +    } +     +    return sisr; +} + +/* +** This function does 2 things: on Intels it kmalloc's another buffer to +** replace the one about to be passed up. On Alpha's it kmallocs a buffer +** into which the packet is copied. +*/ +static struct sk_buff * +de4x5_alloc_rx_buff(struct device *dev, int index, int len) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    struct sk_buff *p; + +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) +    struct sk_buff *ret; +    u_long i=0, tmp; + +    p = dev_alloc_skb(IEEE802_3_SZ + ALIGN + 2); +    if (!p) return NULL; + +    p->dev = dev; +    tmp = virt_to_bus(p->data); +    i = ((tmp + ALIGN) & ~ALIGN) - tmp; +    skb_reserve(p, i); +    lp->rx_ring[index].buf = tmp + i; + +    ret = lp->rx_skb[index]; +    lp->rx_skb[index] = p; + +    if ((u_long) ret > 1) { +	skb_put(ret, len); +    } + +    return ret; + +#else +    if (lp->state != OPEN) return (struct sk_buff *)1; /* Fake out the open */ + +    p = dev_alloc_skb(len + 2); +    if (!p) return NULL; + +    p->dev = dev; +    skb_reserve(p, 2);	                               /* Align */ +    if (index < lp->rx_old) {                          /* Wrapped buffer */ +	short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; +	memcpy(skb_put(p,tlen),  +	       bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),tlen); +	memcpy(skb_put(p,len-tlen),  +	       bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len-tlen); +    } else {                                           /* Linear buffer */ +	memcpy(skb_put(p,len),  +	       bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),len); +    } +		     +    return p; +#endif +} + +static void +de4x5_free_rx_buffs(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int i; + +    for (i=0; i<lp->rxRingSize; i++) { +	if ((u_long) lp->rx_skb[i] > 1) { +	    dev_kfree_skb(lp->rx_skb[i], FREE_WRITE); +	} +	lp->rx_ring[i].status = 0; +	lp->rx_skb[i] = (struct sk_buff *)1;    /* Dummy entry */ +    } + +    return; +} + +static void +de4x5_free_tx_buffs(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int i; + +    for (i=0; i<lp->txRingSize; i++) { +	if (lp->tx_skb[i]) { +	    dev_kfree_skb(lp->tx_skb[i], FREE_WRITE); +	    lp->tx_skb[i] = NULL; +	} +	lp->tx_ring[i].status = 0; +    } + +    /* Unload the locally queued packets */ +    while (lp->cache.skb) { +	dev_kfree_skb(de4x5_get_cache(dev), FREE_WRITE); +    } + +    return; +} + +/* +** When a user pulls a connection, the DECchip can end up in a +** 'running - waiting for end of transmission' state. This means that we +** have to perform a chip soft reset to ensure that we can synchronize +** the hardware and software and make any media probes using a loopback +** packet meaningful. +*/ +static void +de4x5_save_skbs(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    s32 omr; + +    if (!lp->cache.save_cnt) { +	STOP_DE4X5; +	de4x5_tx(dev);                          /* Flush any sent skb's */ +	de4x5_free_tx_buffs(dev); +	de4x5_cache_state(dev, DE4X5_SAVE_STATE); +	de4x5_sw_reset(dev); +	de4x5_cache_state(dev, DE4X5_RESTORE_STATE); +	lp->cache.save_cnt++; +	START_DE4X5; +    } + +    return; +} + +static void +de4x5_rst_desc_ring(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int i; +    s32 omr; + +    if (lp->cache.save_cnt) { +	STOP_DE4X5; +	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA); +	outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA); +     +	lp->rx_new = lp->rx_old = 0; +	lp->tx_new = lp->tx_old = 0; +     +	for (i = 0; i < lp->rxRingSize; i++) { +	    lp->rx_ring[i].status = cpu_to_le32(R_OWN); +	} +     +	for (i = 0; i < lp->txRingSize; i++) { +	    lp->tx_ring[i].status = cpu_to_le32(0); +	} +     +	barrier(); +	lp->cache.save_cnt--; +	START_DE4X5; +    } +         +    return; +} + +static void +de4x5_cache_state(struct device *dev, int flag) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; + +    switch(flag) { +      case DE4X5_SAVE_STATE: +	lp->cache.csr0 = inl(DE4X5_BMR); +	lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR)); +	lp->cache.csr7 = inl(DE4X5_IMR); +	break; + +      case DE4X5_RESTORE_STATE: +	outl(lp->cache.csr0, DE4X5_BMR); +	outl(lp->cache.csr6, DE4X5_OMR); +	outl(lp->cache.csr7, DE4X5_IMR); +	if (lp->chipset == DC21140) { +	    gep_wr(lp->cache.gepc, dev); +	    gep_wr(lp->cache.gep, dev); +	} else { +	    reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14,  +			                                      lp->cache.csr15); +	} +	break; +    } + +    return; +} + +static void +de4x5_put_cache(struct device *dev, struct sk_buff *skb) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    struct sk_buff *p; + +    if (lp->cache.skb) { +	for (p=lp->cache.skb; p->next; p=p->next); +	p->next = skb; +    } else { +	lp->cache.skb = skb; +    } +    skb->next = NULL; + +    return; +} + +static void +de4x5_putb_cache(struct device *dev, struct sk_buff *skb) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    struct sk_buff *p = lp->cache.skb; + +    lp->cache.skb = skb; +    skb->next = p; + +    return; +} + +static struct sk_buff * +de4x5_get_cache(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    struct sk_buff *p = lp->cache.skb; + +    if (p) { +	lp->cache.skb = p->next; +	p->next = NULL; +    } + +    return p; +} + +/* +** Check the Auto Negotiation State. Return OK when a link pass interrupt +** is received and the auto-negotiation status is NWAY OK. +*/ +static int +test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    s32 sts, ans; +     +    if (lp->timeout < 0) { +	lp->timeout = msec/100; +	outl(irq_mask, DE4X5_IMR); +	 +	/* clear all pending interrupts */ +	sts = inl(DE4X5_STS); +	outl(sts, DE4X5_STS); +    } +     +    ans = inl(DE4X5_SISR) & SISR_ANS; +    sts = inl(DE4X5_STS) & ~TIMER_CB; +     +    if (!(sts & irqs) && (ans ^ ANS_NWOK) && --lp->timeout) { +	sts = 100 | TIMER_CB; +    } else { +	lp->timeout = -1; +    } +     +    return sts; +} + +static void +de4x5_setup_intr(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    s32 imr, sts; +     +    if (inl(DE4X5_OMR) & OMR_SR) {   /* Only unmask if TX/RX is enabled */ +	imr = 0; +	UNMASK_IRQs; +	sts = inl(DE4X5_STS);        /* Reset any pending (stale) interrupts */ +	outl(sts, DE4X5_STS); +	ENABLE_IRQs; +    } +     +    return; +} + +/* +** +*/ +static void +reset_init_sia(struct device *dev, s32 csr13, s32 csr14, s32 csr15) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; + +    RESET_SIA; +    if (lp->useSROM) { +	if (lp->ibn == 3) { +	    srom_exec(dev, lp->phy[lp->active].rst); +	    srom_exec(dev, lp->phy[lp->active].gep); +	    outl(1, DE4X5_SICR); +	    return; +	} else { +	    csr15 = lp->cache.csr15; +	    csr14 = lp->cache.csr14; +	    csr13 = lp->cache.csr13; +	    outl(csr15 | lp->cache.gepc, DE4X5_SIGR); +	    outl(csr15 | lp->cache.gep, DE4X5_SIGR); +	} +    } else { +	outl(csr15, DE4X5_SIGR); +    } +    outl(csr14, DE4X5_STRR); +    outl(csr13, DE4X5_SICR); + +    de4x5_ms_delay(10); + +    return; +} + +/* +** Create a loopback ethernet packet +*/ +static void +create_packet(struct device *dev, char *frame, int len) +{ +    int i; +    char *buf = frame; +     +    for (i=0; i<ETH_ALEN; i++) {             /* Use this source address */ +	*buf++ = dev->dev_addr[i]; +    } +    for (i=0; i<ETH_ALEN; i++) {             /* Use this destination address */ +	*buf++ = dev->dev_addr[i]; +    } +     +    *buf++ = 0;                              /* Packet length (2 bytes) */ +    *buf++ = 1; +     +    return; +} + +/* +** Known delay in microseconds +*/ +static void +de4x5_us_delay(u32 usec) +{ +    udelay(usec); +     +    return; +} + +/* +** Known delay in milliseconds, in millisecond steps. +*/ +static void +de4x5_ms_delay(u32 msec) +{ +    u_int i; +     +    for (i=0; i<msec; i++) { +	de4x5_us_delay(1000); +    } +     +    return; +} + + +/* +** Look for a particular board name in the EISA configuration space +*/ +static int +EISA_signature(char *name, s32 eisa_id) +{ +    static c_char *signatures[] = DE4X5_SIGNATURE; +    char ManCode[DE4X5_STRLEN]; +    union { +	s32 ID; +	char Id[4]; +    } Eisa; +    int i, status = 0, siglen = sizeof(signatures)/sizeof(c_char *); +     +    *name = '\0'; +    Eisa.ID = inl(eisa_id); +     +    ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); +    ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); +    ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); +    ManCode[3]=((Eisa.Id[2]&0x0f)+0x30); +    ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); +    ManCode[5]='\0'; +     +    for (i=0;i<siglen;i++) { +	if (strstr(ManCode, signatures[i]) != NULL) { +	    strcpy(name,ManCode); +	    status = 1; +	    break; +	} +    } +     +    return status;                         /* return the device name string */ +} + +/* +** Look for a particular board name in the PCI configuration space +*/ +static int +PCI_signature(char *name, struct bus_type *lp) +{ +    static c_char *de4x5_signatures[] = DE4X5_SIGNATURE; +    int i, status = 0, siglen = sizeof(de4x5_signatures)/sizeof(c_char *); +     +    if (lp->chipset == DC21040) { +	strcpy(name, "DE434/5"); +	return status; +    } else {                           /* Search for a DEC name in the SROM */ +	int i = *((char *)&lp->srom + 19) * 3; +	strncpy(name, (char *)&lp->srom + 26 + i, 8); +    } +    name[8] = '\0'; +    for (i=0; i<siglen; i++) { +	if (strstr(name,de4x5_signatures[i])!=NULL) break; +    } +    if (i == siglen) { +	if (dec_only) { +	    *name = '\0'; +	} else {                        /* Use chip name to avoid confusion */ +	    strcpy(name, (((lp->chipset == DC21040) ? "DC21040" : +			   ((lp->chipset == DC21041) ? "DC21041" : +			    ((lp->chipset == DC21140) ? "DC21140" : +			     ((lp->chipset == DC21142) ? "DC21142" : +			      ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN" +			     ))))))); +	} +	if (lp->chipset != DC21041) { +	    useSROM = TRUE;             /* card is not recognisably DEC */ +	} +    } else if ((lp->chipset & ~0x00ff) == DC2114x) { +	useSROM = TRUE; +    } +     +    return status; +} + +/* +** Set up the Ethernet PROM counter to the start of the Ethernet address on +** the DC21040, else  read the SROM for the other chips. +** The SROM may not be present in a multi-MAC card, so first read the +** MAC address and check for a bad address. If there is a bad one then exit +** immediately with the prior srom contents intact (the h/w address will +** be fixed up later). +*/ +static void +DevicePresent(u_long aprom_addr) +{ +    int i, j=0; +    struct bus_type *lp = &bus; +     +    if (lp->chipset == DC21040) { +	if (lp->bus == EISA) { +	    enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */ +	} else { +	    outl(0, aprom_addr);       /* Reset Ethernet Address ROM Pointer */ +	} +    } else {                           /* Read new srom */ +	u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD); +	for (i=0; i<(ETH_ALEN>>1); i++) { +	    tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i); +	    *p = le16_to_cpu(tmp); +	    j += *p++; +	} +	if ((j == 0) || (j == 0x2fffd)) { +	    return; +	} + +	p=(short *)&lp->srom; +	for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { +	    tmp = srom_rd(aprom_addr, i); +	    *p++ = le16_to_cpu(tmp); +	} +	de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); +    } +     +    return; +} + +/* +** Since the write on the Enet PROM register doesn't seem to reset the PROM +** pointer correctly (at least on my DE425 EISA card), this routine should do +** it...from depca.c. +*/ +static void +enet_addr_rst(u_long aprom_addr) +{ +    union { +	struct { +	    u32 a; +	    u32 b; +	} llsig; +	char Sig[sizeof(u32) << 1]; +    } dev; +    short sigLength=0; +    s8 data; +    int i, j; +     +    dev.llsig.a = ETH_PROM_SIG; +    dev.llsig.b = ETH_PROM_SIG; +    sigLength = sizeof(u32) << 1; +     +    for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) { +	data = inb(aprom_addr); +	if (dev.Sig[j] == data) {    /* track signature */ +	    j++; +	} else {                     /* lost signature; begin search again */ +	    if (data == dev.Sig[0]) {  /* rare case.... */ +		j=1; +	    } else { +		j=0; +	    } +	} +    } +     +    return; +} + +/* +** For the bad status case and no SROM, then add one to the previous +** address. However, need to add one backwards in case we have 0xff +** as one or more of the bytes. Only the last 3 bytes should be checked +** as the first three are invariant - assigned to an organisation. +*/ +static int +get_hw_addr(struct device *dev) +{ +    u_long iobase = dev->base_addr; +    int broken, i, k, tmp, status = 0; +    u_short j,chksum; +    struct bus_type *lp = &bus; + +    broken = de4x5_bad_srom(lp); + +    for (i=0,k=0,j=0;j<3;j++) { +	k <<= 1; +	if (k > 0xffff) k-=0xffff; +	 +	if (lp->bus == PCI) { +	    if (lp->chipset == DC21040) { +		while ((tmp = inl(DE4X5_APROM)) < 0); +		k += (u_char) tmp; +		dev->dev_addr[i++] = (u_char) tmp; +		while ((tmp = inl(DE4X5_APROM)) < 0); +		k += (u_short) (tmp << 8); +		dev->dev_addr[i++] = (u_char) tmp; +	    } else if (!broken) { +		dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; +		dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++; +	    } else if ((broken == SMC) || (broken == ACCTON)) { +		dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; +		dev->dev_addr[i] = *((u_char *)&lp->srom + i); i++; +	    } +	} else { +	    k += (u_char) (tmp = inb(EISA_APROM)); +	    dev->dev_addr[i++] = (u_char) tmp; +	    k += (u_short) ((tmp = inb(EISA_APROM)) << 8); +	    dev->dev_addr[i++] = (u_char) tmp; +	} +	 +	if (k > 0xffff) k-=0xffff; +    } +    if (k == 0xffff) k=0; +     +    if (lp->bus == PCI) { +	if (lp->chipset == DC21040) { +	    while ((tmp = inl(DE4X5_APROM)) < 0); +	    chksum = (u_char) tmp; +	    while ((tmp = inl(DE4X5_APROM)) < 0); +	    chksum |= (u_short) (tmp << 8); +	    if ((k != chksum) && (dec_only)) status = -1; +	} +    } else { +	chksum = (u_char) inb(EISA_APROM); +	chksum |= (u_short) (inb(EISA_APROM) << 8); +	if ((k != chksum) && (dec_only)) status = -1; +    } + +    /* If possible, try to fix a broken card - SMC only so far */ +    srom_repair(dev, broken); + +#ifdef CONFIG_PMAC +    /*  +    ** If the address starts with 00 a0, we have to bit-reverse +    ** each byte of the address. +    */ +    if (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0xa0) { +	for (i = 0; i < ETH_ALEN; ++i) { +	    int x = dev->dev_addr[i]; +	    x = ((x & 0xf) << 4) + ((x & 0xf0) >> 4); +	    x = ((x & 0x33) << 2) + ((x & 0xcc) >> 2); +	    dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); +	} +    } +#endif /* CONFIG_PMAC */ + +    /* Test for a bad enet address */ +    status = test_bad_enet(dev, status); + +    return status; +} + +/* +** Test for enet addresses in the first 32 bytes. The built-in strncmp +** didn't seem to work here...? +*/ +static int +de4x5_bad_srom(struct bus_type *lp) +{ +    int i, status = 0; + +    for (i=0; i<sizeof(enet_det)/ETH_ALEN; i++) { +	if (!de4x5_strncmp((char *)&lp->srom, (char *)&enet_det[i], 3) && +	    !de4x5_strncmp((char *)&lp->srom+0x10, (char *)&enet_det[i], 3)) { +	    if (i == 0) { +		status = SMC; +	    } else if (i == 1) { +		status = ACCTON; +	    } +	    break; +	} +    } + +    return status; +} + +static int +de4x5_strncmp(char *a, char *b, int n) +{ +    int ret=0; + +    for (;n && !ret;n--) { +	ret = *a++ - *b++; +    } + +    return ret; +} + +static void +srom_repair(struct device *dev, int card) +{ +    struct bus_type *lp = &bus; + +    switch(card) { +      case SMC: +	memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom)); +	memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN); +	memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100); +	useSROM = TRUE; +	break; +    } + +    return; +} + +/* +** Assume that the irq's do not follow the PCI spec - this is seems +** to be true so far (2 for 2). +*/ +static int +test_bad_enet(struct device *dev, int status) +{ +    struct bus_type *lp = &bus; +    int i, tmp; + +    for (tmp=0,i=0; i<ETH_ALEN; i++) tmp += (u_char)dev->dev_addr[i]; +    if ((tmp == 0) || (tmp == 0x5fa)) { +	if ((lp->chipset == last.chipset) &&  +	    (lp->bus_num == last.bus) && (lp->bus_num > 0)) { +	    for (i=0; i<ETH_ALEN; i++) dev->dev_addr[i] = last.addr[i]; +	    for (i=ETH_ALEN-1; i>2; --i) { +		dev->dev_addr[i] += 1; +		if (dev->dev_addr[i] != 0) break; +	    } +	    for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i]; +	    if (!an_exception(lp)) { +		dev->irq = last.irq; +	    } + +	    status = 0; +	} +    } else if (!status) { +	last.chipset = lp->chipset; +	last.bus = lp->bus_num; +	last.irq = dev->irq; +	for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i]; +    } + +    return status; +} + +/* +** List of board exceptions with correctly wired IRQs +*/ +static int +an_exception(struct bus_type *lp) +{ +    if ((*(u_short *)lp->srom.sub_vendor_id == 0x00c0) &&  +	(*(u_short *)lp->srom.sub_system_id == 0x95e0)) { +	return -1; +    } + +    return 0; +} + +/* +** SROM Read +*/ +static short +srom_rd(u_long addr, u_char offset) +{ +    sendto_srom(SROM_RD | SROM_SR, addr); +     +    srom_latch(SROM_RD | SROM_SR | DT_CS, addr); +    srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr); +    srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset); +     +    return srom_data(SROM_RD | SROM_SR | DT_CS, addr); +} + +static void +srom_latch(u_int command, u_long addr) +{ +    sendto_srom(command, addr); +    sendto_srom(command | DT_CLK, addr); +    sendto_srom(command, addr); +     +    return; +} + +static void +srom_command(u_int command, u_long addr) +{ +    srom_latch(command, addr); +    srom_latch(command, addr); +    srom_latch((command & 0x0000ff00) | DT_CS, addr); +     +    return; +} + +static void +srom_address(u_int command, u_long addr, u_char offset) +{ +    int i; +    char a; +     +    a = (char)(offset << 2); +    for (i=0; i<6; i++, a <<= 1) { +	srom_latch(command | ((a < 0) ? DT_IN : 0), addr); +    } +    de4x5_us_delay(1); +     +    i = (getfrom_srom(addr) >> 3) & 0x01; +     +    return; +} + +static short +srom_data(u_int command, u_long addr) +{ +    int i; +    short word = 0; +    s32 tmp; +     +    for (i=0; i<16; i++) { +	sendto_srom(command  | DT_CLK, addr); +	tmp = getfrom_srom(addr); +	sendto_srom(command, addr); +	 +	word = (word << 1) | ((tmp >> 3) & 0x01); +    } +     +    sendto_srom(command & 0x0000ff00, addr); +     +    return word; +} + +/* +static void +srom_busy(u_int command, u_long addr) +{ +   sendto_srom((command & 0x0000ff00) | DT_CS, addr); +    +   while (!((getfrom_srom(addr) >> 3) & 0x01)) { +       de4x5_ms_delay(1); +   } +    +   sendto_srom(command & 0x0000ff00, addr); +    +   return; +} +*/ + +static void +sendto_srom(u_int command, u_long addr) +{ +    outl(command, addr); +    udelay(1); +     +    return; +} + +static int +getfrom_srom(u_long addr) +{ +    s32 tmp; +     +    tmp = inl(addr); +    udelay(1); +     +    return tmp; +} + +static int +srom_infoleaf_info(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int i, count; +    u_char *p; + +    /* Find the infoleaf decoder function that matches this chipset */ +    for (i=0; i<INFOLEAF_SIZE; i++) { +	if (lp->chipset == infoleaf_array[i].chipset) break; +    } +    if (i == INFOLEAF_SIZE) { +	lp->useSROM = FALSE; +	printk("%s: Cannot find correct chipset for SROM decoding!\n",  +	                                                          dev->name); +	return -ENXIO; +    } + +    lp->infoleaf_fn = infoleaf_array[i].fn; + +    /* Find the information offset that this function should use */ +    count = *((u_char *)&lp->srom + 19); +    p  = (u_char *)&lp->srom + 26; + +    if (count > 1) { +	for (i=count; i; --i, p+=3) { +	    if (lp->device == *p) break; +	} +	if (i == 0) { +	    lp->useSROM = FALSE; +	    printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n",  +	                                               dev->name, lp->device); +	    return -ENXIO; +	} +    } + +    lp->infoleaf_offset = TWIDDLE(p+1); + +    return 0; +} + +/* +** This routine loads any type 1 or 3 MII info into the mii device +** struct and executes any type 5 code to reset PHY devices for this +** controller. +** The info for the MII devices will be valid since the index used +** will follow the discovery process from MII address 1-31 then 0. +*/ +static void +srom_init(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; +    u_char count; + +    p+=2; +    if (lp->chipset == DC21140) { +	lp->cache.gepc = (*p++ | GEP_CTRL); +	gep_wr(lp->cache.gepc, dev); +    } + +    /* Block count */ +    count = *p++; + +    /* Jump the infoblocks to find types */ +    for (;count; --count) { +	if (*p < 128) { +	    p += COMPACT_LEN; +	} else if (*(p+1) == 5) { +	    type5_infoblock(dev, 1, p); +	    p += ((*p & BLOCK_LEN) + 1); +	} else if (*(p+1) == 4) { +	    p += ((*p & BLOCK_LEN) + 1); +	} else if (*(p+1) == 3) { +	    type3_infoblock(dev, 1, p); +	    p += ((*p & BLOCK_LEN) + 1); +	} else if (*(p+1) == 2) { +	    p += ((*p & BLOCK_LEN) + 1); +	} else if (*(p+1) == 1) { +	    type1_infoblock(dev, 1, p); +	    p += ((*p & BLOCK_LEN) + 1); +	} else { +	    p += ((*p & BLOCK_LEN) + 1); +	} +    } + +    return; +} + +/* +** A generic routine that writes GEP control, data and reset information +** to the GEP register (21140) or csr15 GEP portion (2114[23]). +*/ +static void +srom_exec(struct device *dev, u_char *p) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    u_char count = (p ? *p++ : 0); +    u_short *w = (u_short *)p; + +    if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return; + +    if (lp->chipset != DC21140) RESET_SIA; +  +    while (count--) { +	gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ?  +		                                   *p++ : TWIDDLE(w++)), dev); +	udelay(2000);                       /* 2ms per action */ +    } + +    if (lp->chipset != DC21140) { +	outl(lp->cache.csr14, DE4X5_STRR); +	outl(lp->cache.csr13, DE4X5_SICR); +    } + +    return; +} + +/* +** Basically this function is a NOP since it will never be called, +** unless I implement the DC21041 SROM functions. There's no need +** since the existing code will be satisfactory for all boards. +*/ +static int  +dc21041_infoleaf(struct device *dev) +{ +    return DE4X5_AUTOSENSE_MS; +} + +static int  +dc21140_infoleaf(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char count = 0; +    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; +    int next_tick = DE4X5_AUTOSENSE_MS; + +    /* Read the connection type */ +    p+=2; + +    /* GEP control */ +    lp->cache.gepc = (*p++ | GEP_CTRL); + +    /* Block count */ +    count = *p++; + +    /* Recursively figure out the info blocks */ +    if (*p < 128) { +	next_tick = dc_infoblock[COMPACT](dev, count, p); +    } else { +	next_tick = dc_infoblock[*(p+1)](dev, count, p); +    } + +    if (lp->tcount == count) { +	lp->media = NC; +        if (lp->media != lp->c_media) { +	    de4x5_dbg_media(dev); +	    lp->c_media = lp->media; +	} +	lp->media = INIT; +	lp->tcount = 0; +	lp->tx_enable = FALSE; +    } + +    return next_tick & ~TIMER_CB; +} + +static int  +dc21142_infoleaf(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char count = 0; +    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; +    int next_tick = DE4X5_AUTOSENSE_MS; + +    /* Read the connection type */ +    p+=2; + +    /* Block count */ +    count = *p++; + +    /* Recursively figure out the info blocks */ +    if (*p < 128) { +	next_tick = dc_infoblock[COMPACT](dev, count, p); +    } else { +	next_tick = dc_infoblock[*(p+1)](dev, count, p); +    } + +    if (lp->tcount == count) { +	lp->media = NC; +        if (lp->media != lp->c_media) { +	    de4x5_dbg_media(dev); +	    lp->c_media = lp->media; +	} +	lp->media = INIT; +	lp->tcount = 0; +	lp->tx_enable = FALSE; +    } + +    return next_tick & ~TIMER_CB; +} + +static int  +dc21143_infoleaf(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char count = 0; +    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; +    int next_tick = DE4X5_AUTOSENSE_MS; + +    /* Read the connection type */ +    p+=2; + +    /* Block count */ +    count = *p++; + +    /* Recursively figure out the info blocks */ +    if (*p < 128) { +	next_tick = dc_infoblock[COMPACT](dev, count, p); +    } else { +	next_tick = dc_infoblock[*(p+1)](dev, count, p); +    } +    if (lp->tcount == count) { +	lp->media = NC; +        if (lp->media != lp->c_media) { +	    de4x5_dbg_media(dev); +	    lp->c_media = lp->media; +	} +	lp->media = INIT; +	lp->tcount = 0; +	lp->tx_enable = FALSE; +    } + +    return next_tick & ~TIMER_CB; +} + +/* +** The compact infoblock is only designed for DC21140[A] chips, so +** we'll reuse the dc21140m_autoconf function. Non MII media only. +*/ +static int  +compact_infoblock(struct device *dev, u_char count, u_char *p) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char flags, csr6; + +    /* Recursively figure out the info blocks */ +    if (--count > lp->tcount) { +	if (*(p+COMPACT_LEN) < 128) { +	    return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN); +	} else { +	    return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN); +	} +    } + +    if ((lp->media == INIT) && (lp->timeout < 0)) { +        lp->ibn = COMPACT; +        lp->active = 0; +	gep_wr(lp->cache.gepc, dev); +	lp->infoblock_media = (*p++) & COMPACT_MC; +	lp->cache.gep = *p++; +	csr6 = *p++; +	flags = *p++; + +	lp->asBitValid = (flags & 0x80) ? 0 : -1; +	lp->defMedium = (flags & 0x40) ? -1 : 0; +	lp->asBit = 1 << ((csr6 >> 1) & 0x07); +	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; +	lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); +	lp->useMII = FALSE; + +	de4x5_switch_mac_port(dev); +    } + +    return dc21140m_autoconf(dev); +} + +/* +** This block describes non MII media for the DC21140[A] only. +*/ +static int  +type0_infoblock(struct device *dev, u_char count, u_char *p) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + +    /* Recursively figure out the info blocks */ +    if (--count > lp->tcount) { +	if (*(p+len) < 128) { +	    return dc_infoblock[COMPACT](dev, count, p+len); +	} else { +	    return dc_infoblock[*(p+len+1)](dev, count, p+len); +	} +    } + +    if ((lp->media == INIT) && (lp->timeout < 0)) { +        lp->ibn = 0; +        lp->active = 0; +        gep_wr(lp->cache.gepc, dev); +	p+=2; +	lp->infoblock_media = (*p++) & BLOCK0_MC; +	lp->cache.gep = *p++; +	csr6 = *p++; +	flags = *p++; + +	lp->asBitValid = (flags & 0x80) ? 0 : -1; +	lp->defMedium = (flags & 0x40) ? -1 : 0; +	lp->asBit = 1 << ((csr6 >> 1) & 0x07); +	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; +	lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); +	lp->useMII = FALSE; + +	de4x5_switch_mac_port(dev); +    } + +    return dc21140m_autoconf(dev); +} + +/* These functions are under construction! */ + +static int  +type1_infoblock(struct device *dev, u_char count, u_char *p) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char len = (*p & BLOCK_LEN)+1; + +    /* Recursively figure out the info blocks */ +    if (--count > lp->tcount) { +	if (*(p+len) < 128) { +	    return dc_infoblock[COMPACT](dev, count, p+len); +	} else { +	    return dc_infoblock[*(p+len+1)](dev, count, p+len); +	} +    } + +    p += 2; +    if (lp->state == INITIALISED) { +        lp->ibn = 1; +	lp->active = *p++; +	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); +	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); +	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2; +	lp->phy[lp->active].ana = TWIDDLE(p); p += 2; +	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; +	lp->phy[lp->active].ttm = TWIDDLE(p); +	return 0; +    } else if ((lp->media == INIT) && (lp->timeout < 0)) { +        lp->ibn = 1; +        lp->active = *p; +	lp->infoblock_csr6 = OMR_MII_100; +	lp->useMII = TRUE; +	lp->infoblock_media = ANS; + +	de4x5_switch_mac_port(dev); +    } + +    return dc21140m_autoconf(dev); +} + +static int  +type2_infoblock(struct device *dev, u_char count, u_char *p) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char len = (*p & BLOCK_LEN)+1; + +    /* Recursively figure out the info blocks */ +    if (--count > lp->tcount) { +	if (*(p+len) < 128) { +	    return dc_infoblock[COMPACT](dev, count, p+len); +	} else { +	    return dc_infoblock[*(p+len+1)](dev, count, p+len); +	} +    } + +    if ((lp->media == INIT) && (lp->timeout < 0)) { +        lp->ibn = 2; +        lp->active = 0; +	p += 2; +	lp->infoblock_media = (*p) & MEDIA_CODE; + +        if ((*p++) & EXT_FIELD) { +	    lp->cache.csr13 = TWIDDLE(p); p += 2; +	    lp->cache.csr14 = TWIDDLE(p); p += 2; +	    lp->cache.csr15 = TWIDDLE(p); p += 2; +	} else { +	    lp->cache.csr13 = CSR13; +	    lp->cache.csr14 = CSR14; +	    lp->cache.csr15 = CSR15; +	} +        lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; +        lp->cache.gep  = ((s32)(TWIDDLE(p)) << 16); +	lp->infoblock_csr6 = OMR_SIA; +	lp->useMII = FALSE; + +	de4x5_switch_mac_port(dev); +    } + +    return dc2114x_autoconf(dev); +} + +static int  +type3_infoblock(struct device *dev, u_char count, u_char *p) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char len = (*p & BLOCK_LEN)+1; + +    /* Recursively figure out the info blocks */ +    if (--count > lp->tcount) { +	if (*(p+len) < 128) { +	    return dc_infoblock[COMPACT](dev, count, p+len); +	} else { +	    return dc_infoblock[*(p+len+1)](dev, count, p+len); +	} +    } + +    p += 2; +    if (lp->state == INITIALISED) { +        lp->ibn = 3; +        lp->active = *p++; +	lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1); +	lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1); +	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2; +	lp->phy[lp->active].ana = TWIDDLE(p); p += 2; +	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; +	lp->phy[lp->active].ttm = TWIDDLE(p); p += 2; +	lp->phy[lp->active].mci = *p; +	return 0; +    } else if ((lp->media == INIT) && (lp->timeout < 0)) { +        lp->ibn = 3; +	lp->active = *p; +	lp->infoblock_csr6 = OMR_MII_100; +	lp->useMII = TRUE; +	lp->infoblock_media = ANS; + +	de4x5_switch_mac_port(dev); +    } + +    return dc2114x_autoconf(dev); +} + +static int  +type4_infoblock(struct device *dev, u_char count, u_char *p) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + +    /* Recursively figure out the info blocks */ +    if (--count > lp->tcount) { +	if (*(p+len) < 128) { +	    return dc_infoblock[COMPACT](dev, count, p+len); +	} else { +	    return dc_infoblock[*(p+len+1)](dev, count, p+len); +	} +    } + +    if ((lp->media == INIT) && (lp->timeout < 0)) { +        lp->ibn = 4; +        lp->active = 0; +	p+=2; +	lp->infoblock_media = (*p++) & MEDIA_CODE; +        lp->cache.csr13 = CSR13;              /* Hard coded defaults */ +	lp->cache.csr14 = CSR14; +	lp->cache.csr15 = CSR15; +        lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2; +        lp->cache.gep  = ((s32)(TWIDDLE(p)) << 16); p += 2; +	csr6 = *p++; +	flags = *p++; + +	lp->asBitValid = (flags & 0x80) ? 0 : -1; +	lp->defMedium = (flags & 0x40) ? -1 : 0; +	lp->asBit = 1 << ((csr6 >> 1) & 0x07); +	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; +	lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18); +	lp->useMII = FALSE; + +	de4x5_switch_mac_port(dev); +    } + +    return dc2114x_autoconf(dev); +} + +/* +** This block type provides information for resetting external devices +** (chips) through the General Purpose Register. +*/ +static int  +type5_infoblock(struct device *dev, u_char count, u_char *p) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_char len = (*p & BLOCK_LEN)+1; + +    /* Recursively figure out the info blocks */ +    if (--count > lp->tcount) { +	if (*(p+len) < 128) { +	    return dc_infoblock[COMPACT](dev, count, p+len); +	} else { +	    return dc_infoblock[*(p+len+1)](dev, count, p+len); +	} +    } + +    /* Must be initializing to run this code */ +    if ((lp->state == INITIALISED) || (lp->media == INIT)) { +	p+=2; +        lp->rst = p; +        srom_exec(dev, lp->rst); +    } + +    return DE4X5_AUTOSENSE_MS; +} + +/* +** MII Read/Write +*/ + +static int +mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) +{ +    mii_wdata(MII_PREAMBLE,  2, ioaddr);   /* Start of 34 bit preamble...    */ +    mii_wdata(MII_PREAMBLE, 32, ioaddr);   /* ...continued                   */ +    mii_wdata(MII_STRD, 4, ioaddr);        /* SFD and Read operation         */ +    mii_address(phyaddr, ioaddr);          /* PHY address to be accessed     */ +    mii_address(phyreg, ioaddr);           /* PHY Register to read           */ +    mii_ta(MII_STRD, ioaddr);              /* Turn around time - 2 MDC       */ +     +    return mii_rdata(ioaddr);              /* Read data                      */ +} + +static void +mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr) +{ +    mii_wdata(MII_PREAMBLE,  2, ioaddr);   /* Start of 34 bit preamble...    */ +    mii_wdata(MII_PREAMBLE, 32, ioaddr);   /* ...continued                   */ +    mii_wdata(MII_STWR, 4, ioaddr);        /* SFD and Write operation        */ +    mii_address(phyaddr, ioaddr);          /* PHY address to be accessed     */ +    mii_address(phyreg, ioaddr);           /* PHY Register to write          */ +    mii_ta(MII_STWR, ioaddr);              /* Turn around time - 2 MDC       */ +    data = mii_swap(data, 16);             /* Swap data bit ordering         */ +    mii_wdata(data, 16, ioaddr);           /* Write data                     */ +     +    return; +} + +static int +mii_rdata(u_long ioaddr) +{ +    int i; +    s32 tmp = 0; +     +    for (i=0; i<16; i++) { +	tmp <<= 1; +	tmp |= getfrom_mii(MII_MRD | MII_RD, ioaddr); +    } +     +    return tmp; +} + +static void +mii_wdata(int data, int len, u_long ioaddr) +{ +    int i; +     +    for (i=0; i<len; i++) { +	sendto_mii(MII_MWR | MII_WR, data, ioaddr); +	data >>= 1; +    } +     +    return; +} + +static void +mii_address(u_char addr, u_long ioaddr) +{ +    int i; +     +    addr = mii_swap(addr, 5); +    for (i=0; i<5; i++) { +	sendto_mii(MII_MWR | MII_WR, addr, ioaddr); +	addr >>= 1; +    } +     +    return; +} + +static void +mii_ta(u_long rw, u_long ioaddr) +{ +    if (rw == MII_STWR) { +	sendto_mii(MII_MWR | MII_WR, 1, ioaddr);   +	sendto_mii(MII_MWR | MII_WR, 0, ioaddr);   +    } else { +	getfrom_mii(MII_MRD | MII_RD, ioaddr);        /* Tri-state MDIO */ +    } +     +    return; +} + +static int +mii_swap(int data, int len) +{ +    int i, tmp = 0; +     +    for (i=0; i<len; i++) { +	tmp <<= 1; +	tmp |= (data & 1); +	data >>= 1; +    } +     +    return tmp; +} + +static void +sendto_mii(u32 command, int data, u_long ioaddr) +{ +    u32 j; +     +    j = (data & 1) << 17; +    outl(command | j, ioaddr); +    udelay(1); +    outl(command | MII_MDC | j, ioaddr); +    udelay(1); +     +    return; +} + +static int +getfrom_mii(u32 command, u_long ioaddr) +{ +    outl(command, ioaddr); +    udelay(1); +    outl(command | MII_MDC, ioaddr); +    udelay(1); +     +    return ((inl(ioaddr) >> 19) & 1); +} + +/* +** Here's 3 ways to calculate the OUI from the ID registers. +*/ +static int +mii_get_oui(u_char phyaddr, u_long ioaddr) +{ +/* +    union { +	u_short reg; +	u_char breg[2]; +    } a; +    int i, r2, r3, ret=0;*/ +    int r2, r3; + +    /* Read r2 and r3 */ +    r2 = mii_rd(MII_ID0, phyaddr, ioaddr); +    r3 = mii_rd(MII_ID1, phyaddr, ioaddr); +                                                /* SEEQ and Cypress way * / +    / * Shuffle r2 and r3 * / +    a.reg=0; +    r3 = ((r3>>10)|(r2<<6))&0x0ff; +    r2 = ((r2>>2)&0x3fff); + +    / * Bit reverse r3 * / +    for (i=0;i<8;i++) { +	ret<<=1; +	ret |= (r3&1); +	r3>>=1; +    } + +    / * Bit reverse r2 * / +    for (i=0;i<16;i++) { +	a.reg<<=1; +	a.reg |= (r2&1); +	r2>>=1; +    } + +    / * Swap r2 bytes * / +    i=a.breg[0]; +    a.breg[0]=a.breg[1]; +    a.breg[1]=i; + +    return ((a.reg<<8)|ret); */                 /* SEEQ and Cypress way */ +/*    return ((r2<<6)|(u_int)(r3>>10)); */      /* NATIONAL and BROADCOM way */ +    return r2;                                  /* (I did it) My way */ +} + +/* +** The SROM spec forces us to search addresses [1-31 0]. Bummer. +*/ +static int +mii_get_phy(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); +    int id; +     +    lp->active = 0; +    lp->useMII = TRUE; + +    /* Search the MII address space for possible PHY devices */ +    for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(i+1)%DE4X5_MAX_MII) { +	lp->phy[lp->active].addr = i; +	if (i==0) n++;                             /* Count cycles */ +	while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ +	id = mii_get_oui(i, DE4X5_MII);  +	if ((id == 0) || (id == 65535)) continue;  /* Valid ID? */ +	for (j=0; j<limit; j++) {                  /* Search PHY table */ +	    if (id != phy_info[j].id) continue;    /* ID match? */ +	    for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++); +	    if (k < DE4X5_MAX_PHY) { +		memcpy((char *)&lp->phy[k], +		       (char *)&phy_info[j], sizeof(struct phy_table)); +		lp->phy[k].addr = i; +		lp->mii_cnt++; +		lp->active++; +	    } else { +		goto purgatory;                    /* Stop the search */ +	    } +	    break; +	} +	if ((j == limit) && (i < DE4X5_MAX_MII)) { +	    for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++); +	    lp->phy[k].addr = i; +	    lp->phy[k].id = id; +	    lp->phy[k].spd.reg = GENERIC_REG;      /* ANLPA register         */ +	    lp->phy[k].spd.mask = GENERIC_MASK;    /* 100Mb/s technologies   */ +	    lp->phy[k].spd.value = GENERIC_VALUE;  /* TX & T4, H/F Duplex    */ +	    lp->mii_cnt++; +	    lp->active++; +	    printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name); +	    j = de4x5_debug; +	    de4x5_debug |= DEBUG_MII; +	    de4x5_dbg_mii(dev, k); +	    de4x5_debug = j; +	    printk("\n"); +	} +    } +  purgatory: +    lp->active = 0; +    if (lp->phy[0].id) {                           /* Reset the PHY devices */ +	for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ +	    mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII); +	    while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST); +	     +	    de4x5_dbg_mii(dev, k); +	} +    } +    if (!lp->mii_cnt) lp->useMII = FALSE; + +    return lp->mii_cnt; +} + +static char * +build_setup_frame(struct device *dev, int mode) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int i; +    char *pa = lp->setup_frame; +     +    /* Initialise the setup frame */ +    if (mode == ALL) { +	memset(lp->setup_frame, 0, SETUP_FRAME_LEN); +    } +     +    if (lp->setup_f == HASH_PERF) { +	for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; i<ETH_ALEN; i++) { +	    *(pa + i) = dev->dev_addr[i];                 /* Host address */ +	    if (i & 0x01) pa += 2; +	} +	*(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; +    } else { +	for (i=0; i<ETH_ALEN; i++) { /* Host address */ +	    *(pa + (i&1)) = dev->dev_addr[i]; +	    if (i & 0x01) pa += 4; +	} +	for (i=0; i<ETH_ALEN; i++) { /* Broadcast address */ +	    *(pa + (i&1)) = (char) 0xff; +	    if (i & 0x01) pa += 4; +	} +    } +     +    return pa;                     /* Points to the next entry */ +} + +static void +enable_ast(struct device *dev, u32 time_out) +{ +    timeout(dev, (void *)&de4x5_ast, (u_long)dev, time_out); +     +    return; +} + +static void +disable_ast(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +     +    del_timer(&lp->timer); +     +    return; +} + +static long +de4x5_switch_mac_port(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +    s32 omr; + +    STOP_DE4X5; + +    /* Assert the OMR_PS bit in CSR6 */ +    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | +			                                             OMR_FDX)); +    omr |= lp->infoblock_csr6; +    if (omr & OMR_PS) omr |= OMR_HBD; +    outl(omr, DE4X5_OMR); +     +    /* Soft Reset */ +    RESET_DE4X5; +     +    /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */ +    if (lp->chipset == DC21140) { +	gep_wr(lp->cache.gepc, dev); +	gep_wr(lp->cache.gep, dev); +    } else if ((lp->chipset & ~0x0ff) == DC2114x) { +	reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); +    } + +    /* Restore CSR6 */ +    outl(omr, DE4X5_OMR); + +    /* Reset CSR8 */ +    inl(DE4X5_MFC); + +    return omr; +} + +static void +gep_wr(s32 data, struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; + +    if (lp->chipset == DC21140) { +	outl(data, DE4X5_GEP); +    } else if ((lp->chipset & ~0x00ff) == DC2114x) { +	outl((data<<16) | lp->cache.csr15, DE4X5_SIGR); +    } + +    return; +} + +static int +gep_rd(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; + +    if (lp->chipset == DC21140) { +	return inl(DE4X5_GEP); +    } else if ((lp->chipset & ~0x00ff) == DC2114x) { +	return (inl(DE4X5_SIGR) & 0x000fffff); +    } + +    return 0; +} + +static void +timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int dt; +     +    /* First, cancel any pending timer events */ +    del_timer(&lp->timer); +     +    /* Convert msec to ticks */ +    dt = (msec * HZ) / 1000; +    if (dt==0) dt=1; +     +    /* Set up timer */ +    lp->timer.expires = jiffies + dt; +    lp->timer.function = fn; +    lp->timer.data = data; +    add_timer(&lp->timer); +     +    return; +} + +static void +yawn(struct device *dev, int state) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; + +    if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; + +    if(lp->bus == EISA) { +	switch(state) { +	  case WAKEUP: +	    outb(WAKEUP, PCI_CFPM); +	    de4x5_ms_delay(10); +	    break; + +	  case SNOOZE: +	    outb(SNOOZE, PCI_CFPM); +	    break; + +	  case SLEEP: +	    outl(0, DE4X5_SICR); +	    outb(SLEEP, PCI_CFPM); +	    break; +	} +    } else { +	switch(state) { +	  case WAKEUP: +	    pcibios_write_config_byte(lp->bus_num, lp->device << 3,  +				      PCI_CFDA_PSM, WAKEUP); +	    de4x5_ms_delay(10); +	    break; + +	  case SNOOZE: +	    pcibios_write_config_byte(lp->bus_num, lp->device << 3,  +				      PCI_CFDA_PSM, SNOOZE); +	    break; + +	  case SLEEP: +	    outl(0, DE4X5_SICR); +	    pcibios_write_config_byte(lp->bus_num, lp->device << 3,  +				      PCI_CFDA_PSM, SLEEP); +	    break; +	} +    } + +    return; +} + +static void +de4x5_parse_params(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    char *p, *q, t; + +    lp->params.fdx = 0; +    lp->params.autosense = AUTO; + +    if (args == NULL) return; + +    if ((p = strstr(args, dev->name))) { +	if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p); +	t = *q; +	*q = '\0'; + +	if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1; + +	if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) { +	    if (strstr(p, "TP")) { +		lp->params.autosense = TP; +	    } else if (strstr(p, "TP_NW")) { +		lp->params.autosense = TP_NW; +	    } else if (strstr(p, "BNC")) { +		lp->params.autosense = BNC; +	    } else if (strstr(p, "AUI")) { +		lp->params.autosense = AUI; +	    } else if (strstr(p, "BNC_AUI")) { +		lp->params.autosense = BNC; +	    } else if (strstr(p, "10Mb")) { +		lp->params.autosense = _10Mb; +	    } else if (strstr(p, "100Mb")) { +		lp->params.autosense = _100Mb; +	    } else if (strstr(p, "AUTO")) { +		lp->params.autosense = AUTO; +	    } +	} +	*q = t; +    } + +    return; +} + +static void +de4x5_dbg_open(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    int i; +     +    if (de4x5_debug & DEBUG_OPEN) { +	printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq); +	printk("\tphysical address: "); +	for (i=0;i<6;i++) { +	    printk("%2.2x:",(short)dev->dev_addr[i]); +	} +	printk("\n"); +	printk("Descriptor head addresses:\n"); +	printk("\t0x%8.8lx  0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring); +	printk("Descriptor addresses:\nRX: "); +	for (i=0;i<lp->rxRingSize-1;i++){ +	    if (i < 3) { +		printk("0x%8.8lx  ",(u_long)&lp->rx_ring[i].status); +	    } +	} +	printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status); +	printk("TX: "); +	for (i=0;i<lp->txRingSize-1;i++){ +	    if (i < 3) { +		printk("0x%8.8lx  ", (u_long)&lp->tx_ring[i].status); +	    } +	} +	printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status); +	printk("Descriptor buffers:\nRX: "); +	for (i=0;i<lp->rxRingSize-1;i++){ +	    if (i < 3) { +		printk("0x%8.8x  ",le32_to_cpu(lp->rx_ring[i].buf)); +	    } +	} +	printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf)); +	printk("TX: "); +	for (i=0;i<lp->txRingSize-1;i++){ +	    if (i < 3) { +		printk("0x%8.8x  ", le32_to_cpu(lp->tx_ring[i].buf)); +	    } +	} +	printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf)); +	printk("Ring size: \nRX: %d\nTX: %d\n",  +	       (short)lp->rxRingSize,  +	       (short)lp->txRingSize);  +    } +     +    return; +} + +static void +de4x5_dbg_mii(struct device *dev, int k) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    u_long iobase = dev->base_addr; +     +    if (de4x5_debug & DEBUG_MII) { +	printk("\nMII device address: %d\n", lp->phy[k].addr); +	printk("MII CR:  %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); +	printk("MII SR:  %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); +	printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); +	printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII)); +	if (lp->phy[k].id != BROADCOM_T4) { +	    printk("MII ANA: %x\n",mii_rd(0x04,lp->phy[k].addr,DE4X5_MII)); +	    printk("MII ANC: %x\n",mii_rd(0x05,lp->phy[k].addr,DE4X5_MII)); +	} +	printk("MII 16:  %x\n",mii_rd(0x10,lp->phy[k].addr,DE4X5_MII)); +	if (lp->phy[k].id != BROADCOM_T4) { +	    printk("MII 17:  %x\n",mii_rd(0x11,lp->phy[k].addr,DE4X5_MII)); +	    printk("MII 18:  %x\n",mii_rd(0x12,lp->phy[k].addr,DE4X5_MII)); +	} else { +	    printk("MII 20:  %x\n",mii_rd(0x14,lp->phy[k].addr,DE4X5_MII)); +	} +    } +     +    return; +} + +static void +de4x5_dbg_media(struct device *dev) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +     +    if (lp->media != lp->c_media) { +	if (de4x5_debug & DEBUG_MEDIA) { +	    printk("%s: media is %s%s\n", dev->name, +		   (lp->media == NC  ? "unconnected, link down or incompatible connection" : +		    (lp->media == TP  ? "TP" : +		     (lp->media == ANS ? "TP/Nway" : +		      (lp->media == BNC ? "BNC" :  +		       (lp->media == AUI ? "AUI" :  +			(lp->media == BNC_AUI ? "BNC/AUI" :  +			 (lp->media == EXT_SIA ? "EXT SIA" :  +			  (lp->media == _100Mb  ? "100Mb/s" : +			   (lp->media == _10Mb   ? "10Mb/s" : +			    "???" +			    ))))))))), (lp->fdx?" full duplex.":".")); +	} +	lp->c_media = lp->media; +    } +     +    return; +} + +static void +de4x5_dbg_srom(struct de4x5_srom *p) +{ +    int i; + +    if (de4x5_debug & DEBUG_SROM) { +	printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id)); +	printk("Sub-system ID:        %04x\n", *((u_short *)p->sub_system_id)); +	printk("ID Block CRC:         %02x\n", (u_char)(p->id_block_crc)); +	printk("SROM version:         %02x\n", (u_char)(p->version)); +	printk("# controllers:         %02x\n", (u_char)(p->num_controllers)); + +	printk("Hardware Address:     "); +	for (i=0;i<ETH_ALEN-1;i++) { +	    printk("%02x:", (u_char)*(p->ieee_addr+i)); +	} +	printk("%02x\n", (u_char)*(p->ieee_addr+i)); +	printk("CRC checksum:         %04x\n", (u_short)(p->chksum)); +	for (i=0; i<64; i++) { +	    printk("%3d %04x\n", i<<1, (u_short)*((u_short *)p+i)); +	} +    } + +    return; +} + +static void +de4x5_dbg_rx(struct sk_buff *skb, int len) +{ +    int i, j; + +    if (de4x5_debug & DEBUG_RX) { +	printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", +	       (u_char)skb->data[0], +	       (u_char)skb->data[1], +	       (u_char)skb->data[2], +	       (u_char)skb->data[3], +	       (u_char)skb->data[4], +	       (u_char)skb->data[5], +	       (u_char)skb->data[6], +	       (u_char)skb->data[7], +	       (u_char)skb->data[8], +	       (u_char)skb->data[9], +	       (u_char)skb->data[10], +	       (u_char)skb->data[11], +	       (u_char)skb->data[12], +	       (u_char)skb->data[13], +	       len); +	if (de4x5_debug & DEBUG_RX) { +	    for (j=0; len>0;j+=16, len-=16) { +		printk("    %03x: ",j); +		for (i=0; i<16 && i<len; i++) { +		    printk("%02x ",(u_char)skb->data[i+j]); +		} +		printk("\n"); +	    } +	} +    } + +    return; +} + +/* +** Perform IOCTL call functions here. Some are privileged operations and the +** effective uid is checked in those cases. In the normal course of events +** this function is only used for my testing. +*/ +static int +de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ +    struct de4x5_private *lp = (struct de4x5_private *)dev->priv; +    struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data; +    u_long iobase = dev->base_addr; +    int i, j, status = 0; +    s32 omr; +    union { +	u8  addr[144]; +	u16 sval[72]; +	u32 lval[36]; +    } tmp; +     +    switch(ioc->cmd) { +    case DE4X5_GET_HWADDR:           /* Get the hardware address */ +	ioc->len = ETH_ALEN; +	status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len); +	if (status) +	    break; +	for (i=0; i<ETH_ALEN; i++) { +	    tmp.addr[i] = dev->dev_addr[i]; +	} +	copy_to_user(ioc->data, tmp.addr, ioc->len); +	 +	break; +    case DE4X5_SET_HWADDR:           /* Set the hardware address */ +	status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN); +	if (status) +	    break; +	status = -EPERM; +	if (!suser()) +	    break; +	status = 0; +	copy_from_user(tmp.addr, ioc->data, ETH_ALEN); +	for (i=0; i<ETH_ALEN; i++) { +	    dev->dev_addr[i] = tmp.addr[i]; +	} +	build_setup_frame(dev, PHYS_ADDR_ONLY); +	/* Set up the descriptor and give ownership to the card */ +	while (test_and_set_bit(0, (void *)&dev->tbusy) != 0); +	load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |  +		                                        SETUP_FRAME_LEN, NULL); +	lp->tx_new = (lp->tx_new + 1) % lp->txRingSize; +	outl(POLL_DEMAND, DE4X5_TPD);                /* Start the TX */ +	dev->tbusy = 0;                              /* Unlock the TX ring */ +	 +	break; +    case DE4X5_SET_PROM:             /* Set Promiscuous Mode */ +	if (suser()) { +	    omr = inl(DE4X5_OMR); +	    omr |= OMR_PR; +	    outl(omr, DE4X5_OMR); +	    dev->flags |= IFF_PROMISC; +	} else { +	    status = -EPERM; +	} +	 +	break; +    case DE4X5_CLR_PROM:             /* Clear Promiscuous Mode */ +	if (suser()) { +	    omr = inl(DE4X5_OMR); +	    omr &= ~OMR_PR; +	    outb(omr, DE4X5_OMR); +	    dev->flags &= ~IFF_PROMISC; +	} else { +	    status = -EPERM; +	} +	 +	break; +    case DE4X5_SAY_BOO:              /* Say "Boo!" to the kernel log file */ +	printk("%s: Boo!\n", dev->name); +	 +	break; +    case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */ +	if (suser()) { +	    omr = inl(DE4X5_OMR); +	    omr |= OMR_PM; +	    outl(omr, DE4X5_OMR); +	} else { +	    status = -EPERM; +	} +	 +	break; +    case DE4X5_GET_STATS:            /* Get the driver statistics */ +	ioc->len = sizeof(lp->pktStats); +	status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len); +	if (status) +	    break; +	 +	cli(); +	copy_to_user(ioc->data, &lp->pktStats, ioc->len);  +	sti(); +	 +	break; +    case DE4X5_CLR_STATS:            /* Zero out the driver statistics */ +	if (suser()) { +	    cli(); +	    memset(&lp->pktStats, 0, sizeof(lp->pktStats)); +	    sti(); +	} else { +	    status = -EPERM; +	} +	 +	break; +    case DE4X5_GET_OMR:              /* Get the OMR Register contents */ +	tmp.addr[0] = inl(DE4X5_OMR); +	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) { +	    copy_to_user(ioc->data, tmp.addr, 1); +	} +	 +	break; +    case DE4X5_SET_OMR:              /* Set the OMR Register contents */ +	if (suser()) { +	    if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) { +		copy_from_user(tmp.addr, ioc->data, 1); +		outl(tmp.addr[0], DE4X5_OMR); +	    } +	} else { +	    status = -EPERM; +	} +	 +	break; +    case DE4X5_GET_REG:              /* Get the DE4X5 Registers */ +	j = 0; +	tmp.lval[0] = inl(DE4X5_STS); j+=4; +	tmp.lval[1] = inl(DE4X5_BMR); j+=4; +	tmp.lval[2] = inl(DE4X5_IMR); j+=4; +	tmp.lval[3] = inl(DE4X5_OMR); j+=4; +	tmp.lval[4] = inl(DE4X5_SISR); j+=4; +	tmp.lval[5] = inl(DE4X5_SICR); j+=4; +	tmp.lval[6] = inl(DE4X5_STRR); j+=4; +	tmp.lval[7] = inl(DE4X5_SIGR); j+=4; +	ioc->len = j; +	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { +	    copy_to_user(ioc->data, tmp.addr, ioc->len); +	} +	break; +	 +#define DE4X5_DUMP              0x0f /* Dump the DE4X5 Status */ +/*	 +      case DE4X5_DUMP: +	j = 0; +	tmp.addr[j++] = dev->irq; +	for (i=0; i<ETH_ALEN; i++) { +	    tmp.addr[j++] = dev->dev_addr[i]; +	} +	tmp.addr[j++] = lp->rxRingSize; +	tmp.lval[j>>2] = (long)lp->rx_ring; j+=4; +	tmp.lval[j>>2] = (long)lp->tx_ring; j+=4; +	 +	for (i=0;i<lp->rxRingSize-1;i++){ +	    if (i < 3) { +		tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; +	    } +	} +	tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4; +	for (i=0;i<lp->txRingSize-1;i++){ +	    if (i < 3) { +		tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; +	    } +	} +	tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4; +	 +	for (i=0;i<lp->rxRingSize-1;i++){ +	    if (i < 3) { +		tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; +	    } +	} +	tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; +	for (i=0;i<lp->txRingSize-1;i++){ +	    if (i < 3) { +		tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; +	    } +	} +	tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; +	 +	for (i=0;i<lp->rxRingSize;i++){ +	    tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4; +	} +	for (i=0;i<lp->txRingSize;i++){ +	    tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4; +	} +	 +	tmp.lval[j>>2] = inl(DE4X5_BMR);  j+=4; +	tmp.lval[j>>2] = inl(DE4X5_TPD);  j+=4; +	tmp.lval[j>>2] = inl(DE4X5_RPD);  j+=4; +	tmp.lval[j>>2] = inl(DE4X5_RRBA); j+=4; +	tmp.lval[j>>2] = inl(DE4X5_TRBA); j+=4; +	tmp.lval[j>>2] = inl(DE4X5_STS);  j+=4; +	tmp.lval[j>>2] = inl(DE4X5_OMR);  j+=4; +	tmp.lval[j>>2] = inl(DE4X5_IMR);  j+=4; +	tmp.lval[j>>2] = lp->chipset; j+=4;  +	if (lp->chipset == DC21140) { +	    tmp.lval[j>>2] = gep_rd(dev);  j+=4; +	} else { +	    tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4; +	    tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4; +	    tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4; +	    tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4;  +	} +	tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4;  +	if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { +	    tmp.lval[j>>2] = lp->active; j+=4;  +	    tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +	    tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +	    tmp.lval[j>>2]=mii_rd(MII_ID0,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +	    tmp.lval[j>>2]=mii_rd(MII_ID1,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +	    if (lp->phy[lp->active].id != BROADCOM_T4) { +		tmp.lval[j>>2]=mii_rd(MII_ANA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +		tmp.lval[j>>2]=mii_rd(MII_ANLPA,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +	    } +	    tmp.lval[j>>2]=mii_rd(0x10,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +	    if (lp->phy[lp->active].id != BROADCOM_T4) { +		tmp.lval[j>>2]=mii_rd(0x11,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +		tmp.lval[j>>2]=mii_rd(0x12,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +	    } else { +		tmp.lval[j>>2]=mii_rd(0x14,lp->phy[lp->active].addr,DE4X5_MII); j+=4; +	    } +	} +	 +	tmp.addr[j++] = lp->txRingSize; +	tmp.addr[j++] = dev->tbusy; +	 +	ioc->len = j; +	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { +	    copy_to_user(ioc->data, tmp.addr, ioc->len); +	} +	 +	break; +*/ +    default: +	status = -EOPNOTSUPP; +    } +     +    return status; +} + +#ifdef MODULE +/* +** Note now that module autoprobing is allowed under EISA and PCI. The +** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes +** to "do the right thing". +*/ +#define LP(a) ((struct de4x5_private *)(a)) +static struct device *mdev = NULL; +static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED        */ +#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0) +MODULE_PARM(io, "i"); +#endif /* LINUX_VERSION_CODE */ + +int +init_module(void) +{ +    int i, num, status = -EIO; +    struct device *p; + +    num = count_adapters(); + +    for (i=0; i<num; i++) { +	if ((p = insert_device(NULL, io, de4x5_probe)) == NULL)  +	    return -ENOMEM; + +	if (!mdev) mdev = p; + +	if (register_netdev(p) != 0) { +	    kfree(p); +	} else { +	    status = 0;                 /* At least one adapter will work */ +	    lastModule = p; +	} +    } + +    return status; +} + +void +cleanup_module(void) +{ +    while (mdev != NULL) { +	mdev = unlink_modules(mdev); +    } + +    return; +} + +static struct device * +unlink_modules(struct device *p) +{ +    struct device *next = NULL; + +    if (p->priv) {                          /* Private areas allocated?  */ +	struct de4x5_private *lp = (struct de4x5_private *)p->priv; + +	next = lp->next_module; +	if (lp->cache.buf) {                /* MAC buffers allocated?    */ +	    kfree(lp->cache.buf);           /* Free the MAC buffers      */ +	} +	kfree(lp->cache.priv);              /* Free the private area     */ +	release_region(p->base_addr, (lp->bus == PCI ?  +				      DE4X5_PCI_TOTAL_SIZE : +				      DE4X5_EISA_TOTAL_SIZE)); +    } +    unregister_netdev(p); +    kfree(p);                               /* Free the device structure */ +     +    return next; +} + +static int +count_adapters(void) +{ +    int i, j=0; +    u_char pb, dev_fn, dev_num; +    u_short dev_id, vendor; +    u_int class = DE4X5_CLASS_CODE; +    u_int device; + +#if !defined(__sparc_v9__) && !defined(__powerpc__) && !defined(__alpha__) +    char name[DE4X5_STRLEN]; +    u_long iobase = 0x1000; + +    for (i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) { +	if (EISA_signature(name, EISA_ID)) j++; +    } +#endif +    if (!pcibios_present()) return j; + +    for (i=0;  +	 (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); +	 i++) { +	dev_num = PCI_SLOT(dev_fn); +	device = 0; +	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); +	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); +	device = dev_id; +	device <<= 8; +	if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++; +    } + +    return j; +} + +/* +** If at end of eth device list and can't use current entry, malloc +** one up. If memory could not be allocated, print an error message. +*/ +__initfunc(static struct device * +insert_device(struct device *dev, u_long iobase, int (*init)(struct device *))) +{ +    struct device *new; + +    new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL); +    if (new == NULL) { +	printk("de4x5.c: Device not initialised, insufficient memory\n"); +	return NULL; +    } else { +	memset((char *)new, 0, sizeof(struct device)+8); +	new->name = (char *)(new + 1); +	new->base_addr = iobase;       /* assign the io address */ +	new->init = init;              /* initialisation routine */ +    } + +    return new; +} + +#endif /* MODULE */ + + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c de4x5.c" + * + *  compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c de4x5.c" + * End: + */ diff --git a/linux/src/drivers/net/de4x5.h b/linux/src/drivers/net/de4x5.h new file mode 100644 index 0000000..c0c58cc --- /dev/null +++ b/linux/src/drivers/net/de4x5.h @@ -0,0 +1,1028 @@ +/* +    Copyright 1994 Digital Equipment Corporation. + +    This software may be used and distributed according to  the terms of the +    GNU Public License, incorporated herein by reference. + +    The author may    be  reached as davies@wanton.lkg.dec.com  or   Digital +    Equipment Corporation, 550 King Street, Littleton MA 01460. + +    ========================================================================= +*/ + +/* +** DC21040 CSR<1..15> Register Address Map +*/ +#define DE4X5_BMR    iobase+(0x000 << lp->bus)  /* Bus Mode Register */ +#define DE4X5_TPD    iobase+(0x008 << lp->bus)  /* Transmit Poll Demand Reg */ +#define DE4X5_RPD    iobase+(0x010 << lp->bus)  /* Receive Poll Demand Reg */ +#define DE4X5_RRBA   iobase+(0x018 << lp->bus)  /* RX Ring Base Address Reg */ +#define DE4X5_TRBA   iobase+(0x020 << lp->bus)  /* TX Ring Base Address Reg */ +#define DE4X5_STS    iobase+(0x028 << lp->bus)  /* Status Register */ +#define DE4X5_OMR    iobase+(0x030 << lp->bus)  /* Operation Mode Register */ +#define DE4X5_IMR    iobase+(0x038 << lp->bus)  /* Interrupt Mask Register */ +#define DE4X5_MFC    iobase+(0x040 << lp->bus)  /* Missed Frame Counter */ +#define DE4X5_APROM  iobase+(0x048 << lp->bus)  /* Ethernet Address PROM */ +#define DE4X5_BROM   iobase+(0x048 << lp->bus)  /* Boot ROM Register */ +#define DE4X5_SROM   iobase+(0x048 << lp->bus)  /* Serial ROM Register */ +#define DE4X5_MII    iobase+(0x048 << lp->bus)  /* MII Interface Register */ +#define DE4X5_DDR    iobase+(0x050 << lp->bus)  /* Data Diagnostic Register */ +#define DE4X5_FDR    iobase+(0x058 << lp->bus)  /* Full Duplex Register */ +#define DE4X5_GPT    iobase+(0x058 << lp->bus)  /* General Purpose Timer Reg.*/ +#define DE4X5_GEP    iobase+(0x060 << lp->bus)  /* General Purpose Register */ +#define DE4X5_SISR   iobase+(0x060 << lp->bus)  /* SIA Status Register */ +#define DE4X5_SICR   iobase+(0x068 << lp->bus)  /* SIA Connectivity Register */ +#define DE4X5_STRR   iobase+(0x070 << lp->bus)  /* SIA TX/RX Register */ +#define DE4X5_SIGR   iobase+(0x078 << lp->bus)  /* SIA General Register */ + +/* +** EISA Register Address Map +*/ +#define EISA_ID      iobase+0x0c80   /* EISA ID Registers */  +#define EISA_ID0     iobase+0x0c80   /* EISA ID Register 0 */  +#define EISA_ID1     iobase+0x0c81   /* EISA ID Register 1 */  +#define EISA_ID2     iobase+0x0c82   /* EISA ID Register 2 */  +#define EISA_ID3     iobase+0x0c83   /* EISA ID Register 3 */  +#define EISA_CR      iobase+0x0c84   /* EISA Control Register */ +#define EISA_REG0    iobase+0x0c88   /* EISA Configuration Register 0 */ +#define EISA_REG1    iobase+0x0c89   /* EISA Configuration Register 1 */ +#define EISA_REG2    iobase+0x0c8a   /* EISA Configuration Register 2 */ +#define EISA_REG3    iobase+0x0c8f   /* EISA Configuration Register 3 */ +#define EISA_APROM   iobase+0x0c90   /* Ethernet Address PROM */ + +/* +** PCI/EISA Configuration Registers Address Map +*/ +#define PCI_CFID     iobase+0x0008   /* PCI Configuration ID Register */ +#define PCI_CFCS     iobase+0x000c   /* PCI Command/Status Register */ +#define PCI_CFRV     iobase+0x0018   /* PCI Revision Register */ +#define PCI_CFLT     iobase+0x001c   /* PCI Latency Timer Register */ +#define PCI_CBIO     iobase+0x0028   /* PCI Base I/O Register */ +#define PCI_CBMA     iobase+0x002c   /* PCI Base Memory Address Register */ +#define PCI_CBER     iobase+0x0030   /* PCI Expansion ROM Base Address Reg. */ +#define PCI_CFIT     iobase+0x003c   /* PCI Configuration Interrupt Register */ +#define PCI_CFDA     iobase+0x0040   /* PCI Driver Area Register */ +#define PCI_CFDD     iobase+0x0041   /* PCI Driver Dependent Area Register */ +#define PCI_CFPM     iobase+0x0043   /* PCI Power Management Area Register */ + +/* +** EISA Configuration Register 0 bit definitions +*/ +#define ER0_BSW       0x80           /* EISA Bus Slave Width, 1: 32 bits */ +#define ER0_BMW       0x40           /* EISA Bus Master Width, 1: 32 bits */ +#define ER0_EPT       0x20           /* EISA PREEMPT Time, 0: 23 BCLKs */ +#define ER0_ISTS      0x10           /* Interrupt Status (X) */ +#define ER0_LI        0x08           /* Latch Interrupts */ +#define ER0_INTL      0x06           /* INTerrupt Level */ +#define ER0_INTT      0x01           /* INTerrupt Type, 0: Level, 1: Edge */ + +/* +** EISA Configuration Register 1 bit definitions +*/ +#define ER1_IAM       0xe0           /* ISA Address Mode */ +#define ER1_IAE       0x10           /* ISA Addressing Enable */ +#define ER1_UPIN      0x0f           /* User Pins */ + +/* +** EISA Configuration Register 2 bit definitions +*/ +#define ER2_BRS       0xc0           /* Boot ROM Size */ +#define ER2_BRA       0x3c           /* Boot ROM Address <16:13> */ + +/* +** EISA Configuration Register 3 bit definitions +*/ +#define ER3_BWE       0x40           /* Burst Write Enable */ +#define ER3_BRE       0x04           /* Burst Read Enable */ +#define ER3_LSR       0x02           /* Local Software Reset */ + +/* +** PCI Configuration ID Register (PCI_CFID). The Device IDs are left +** shifted 8 bits to allow detection of DC21142 and DC21143 variants with +** the configuration revision register step number. +*/ +#define CFID_DID    0xff00           /* Device ID */ +#define CFID_VID    0x00ff           /* Vendor ID */ +#define DC21040_DID 0x0200           /* Unique Device ID # */ +#define DC21040_VID 0x1011           /* DC21040 Manufacturer */ +#define DC21041_DID 0x1400           /* Unique Device ID # */ +#define DC21041_VID 0x1011           /* DC21041 Manufacturer */ +#define DC21140_DID 0x0900           /* Unique Device ID # */ +#define DC21140_VID 0x1011           /* DC21140 Manufacturer */ +#define DC2114x_DID 0x1900           /* Unique Device ID # */ +#define DC2114x_VID 0x1011           /* DC2114[23] Manufacturer */ + +/* +** Chipset defines +*/ +#define DC21040     DC21040_DID +#define DC21041     DC21041_DID +#define DC21140     DC21140_DID +#define DC2114x     DC2114x_DID +#define DC21142     (DC2114x_DID | 0x0010) +#define DC21143     (DC2114x_DID | 0x0030) + +#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) +#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) +#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID)) +#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID)) +#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142)) +#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143)) + +/* +** PCI Configuration Command/Status Register (PCI_CFCS) +*/ +#define CFCS_DPE    0x80000000       /* Detected Parity Error (S) */ +#define CFCS_SSE    0x40000000       /* Signal System Error   (S) */ +#define CFCS_RMA    0x20000000       /* Receive Master Abort  (S) */ +#define CFCS_RTA    0x10000000       /* Receive Target Abort  (S) */ +#define CFCS_DST    0x06000000       /* DEVSEL Timing         (S) */ +#define CFCS_DPR    0x01000000       /* Data Parity Report    (S) */ +#define CFCS_FBB    0x00800000       /* Fast Back-To-Back     (S) */ +#define CFCS_SEE    0x00000100       /* System Error Enable   (C) */ +#define CFCS_PER    0x00000040       /* Parity Error Response (C) */ +#define CFCS_MO     0x00000004       /* Master Operation      (C) */ +#define CFCS_MSA    0x00000002       /* Memory Space Access   (C) */ +#define CFCS_IOSA   0x00000001       /* I/O Space Access      (C) */ + +/* +** PCI Configuration Revision Register (PCI_CFRV) +*/ +#define CFRV_BC     0xff000000       /* Base Class */ +#define CFRV_SC     0x00ff0000       /* Subclass */ +#define CFRV_RN     0x000000f0       /* Revision Number */ +#define CFRV_SN     0x0000000f       /* Step Number */ +#define BASE_CLASS  0x02000000       /* Indicates Network Controller */ +#define SUB_CLASS   0x00000000       /* Indicates Ethernet Controller */ +#define STEP_NUMBER 0x00000020       /* Increments for future chips */ +#define REV_NUMBER  0x00000003       /* 0x00, 0x01, 0x02, 0x03: Rev in Step */ +#define CFRV_MASK   0xffff0000       /* Register mask */ + +/* +** PCI Configuration Latency Timer Register (PCI_CFLT) +*/ +#define CFLT_BC     0x0000ff00       /* Latency Timer bits */ + +/* +** PCI Configuration Base I/O Address Register (PCI_CBIO) +*/ +#define CBIO_MASK   -128             /* Base I/O Address Mask */ +#define CBIO_IOSI   0x00000001       /* I/O Space Indicator (RO, value is 1) */ + +/* +** PCI Configuration Card Information Structure Register (PCI_CCIS) +*/ +#define CCIS_ROMI   0xf0000000       /* ROM Image */ +#define CCIS_ASO    0x0ffffff8       /* Address Space Offset */ +#define CCIS_ASI    0x00000007       /* Address Space Indicator */ + +/* +** PCI Configuration Subsystem ID Register (PCI_SSID) +*/ +#define SSID_SSID   0xffff0000       /* Subsystem ID */ +#define SSID_SVID   0x0000ffff       /* Subsystem Vendor ID */ + +/* +** PCI Configuration Expansion ROM Base Address Register (PCI_CBER) +*/ +#define CBER_MASK   0xfffffc00       /* Expansion ROM Base Address Mask */ +#define CBER_ROME   0x00000001       /* ROM Enable */ + +/* +** PCI Configuration Interrupt Register (PCI_CFIT) +*/ +#define CFIT_MXLT   0xff000000       /* MAX_LAT Value (0.25us periods) */ +#define CFIT_MNGT   0x00ff0000       /* MIN_GNT Value (0.25us periods) */ +#define CFIT_IRQP   0x0000ff00       /* Interrupt Pin */ +#define CFIT_IRQL   0x000000ff       /* Interrupt Line */ + +/* +** PCI Configuration Power Management Area Register (PCI_CFPM) +*/ +#define SLEEP       0x80             /* Power Saving Sleep Mode */ +#define SNOOZE      0x40             /* Power Saving Snooze Mode */ +#define WAKEUP      0x00             /* Power Saving Wakeup */ + +#define PCI_CFDA_DSU 0x41            /* 8 bit Configuration Space Address */ +#define PCI_CFDA_PSM 0x43            /* 8 bit Configuration Space Address */ + +/* +** DC21040 Bus Mode Register (DE4X5_BMR) +*/ +#define BMR_RML    0x00200000       /* [Memory] Read Multiple */ +#define BMR_DBO    0x00100000       /* Descriptor Byte Ordering (Endian) */ +#define BMR_TAP    0x000e0000       /* Transmit Automatic Polling */ +#define BMR_DAS    0x00010000       /* Diagnostic Address Space */ +#define BMR_CAL    0x0000c000       /* Cache Alignment */ +#define BMR_PBL    0x00003f00       /* Programmable Burst Length */ +#define BMR_BLE    0x00000080       /* Big/Little Endian */ +#define BMR_DSL    0x0000007c       /* Descriptor Skip Length */ +#define BMR_BAR    0x00000002       /* Bus ARbitration */ +#define BMR_SWR    0x00000001       /* Software Reset */ + +                                    /* Timings here are for 10BASE-T/AUI only*/ +#define TAP_NOPOLL 0x00000000       /* No automatic polling */ +#define TAP_200US  0x00020000       /* TX automatic polling every 200us */ +#define TAP_800US  0x00040000       /* TX automatic polling every 800us */ +#define TAP_1_6MS  0x00060000       /* TX automatic polling every 1.6ms */ +#define TAP_12_8US 0x00080000       /* TX automatic polling every 12.8us */ +#define TAP_25_6US 0x000a0000       /* TX automatic polling every 25.6us */ +#define TAP_51_2US 0x000c0000       /* TX automatic polling every 51.2us */ +#define TAP_102_4US 0x000e0000      /* TX automatic polling every 102.4us */ + +#define CAL_NOUSE  0x00000000       /* Not used */ +#define CAL_8LONG  0x00004000       /* 8-longword alignment */ +#define CAL_16LONG 0x00008000       /* 16-longword alignment */ +#define CAL_32LONG 0x0000c000       /* 32-longword alignment */ + +#define PBL_0      0x00000000       /*  DMA burst length = amount in RX FIFO */ +#define PBL_1      0x00000100       /*  1 longword  DMA burst length */ +#define PBL_2      0x00000200       /*  2 longwords DMA burst length */ +#define PBL_4      0x00000400       /*  4 longwords DMA burst length */ +#define PBL_8      0x00000800       /*  8 longwords DMA burst length */ +#define PBL_16     0x00001000       /* 16 longwords DMA burst length */ +#define PBL_32     0x00002000       /* 32 longwords DMA burst length */ + +#define DSL_0      0x00000000       /*  0 longword  / descriptor */ +#define DSL_1      0x00000004       /*  1 longword  / descriptor */ +#define DSL_2      0x00000008       /*  2 longwords / descriptor */ +#define DSL_4      0x00000010       /*  4 longwords / descriptor */ +#define DSL_8      0x00000020       /*  8 longwords / descriptor */ +#define DSL_16     0x00000040       /* 16 longwords / descriptor */ +#define DSL_32     0x00000080       /* 32 longwords / descriptor */ + +/* +** DC21040 Transmit Poll Demand Register (DE4X5_TPD) +*/ +#define TPD        0x00000001       /* Transmit Poll Demand */ + +/* +** DC21040 Receive Poll Demand Register (DE4X5_RPD) +*/ +#define RPD        0x00000001       /* Receive Poll Demand */ + +/* +** DC21040 Receive Ring Base Address Register (DE4X5_RRBA) +*/ +#define RRBA       0xfffffffc       /* RX Descriptor List Start Address */ + +/* +** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA) +*/ +#define TRBA       0xfffffffc       /* TX Descriptor List Start Address */ + +/* +** Status Register (DE4X5_STS) +*/ +#define STS_GPI    0x04000000       /* General Purpose Port Interrupt */ +#define STS_BE     0x03800000       /* Bus Error Bits */ +#define STS_TS     0x00700000       /* Transmit Process State */ +#define STS_RS     0x000e0000       /* Receive Process State */ +#define STS_NIS    0x00010000       /* Normal Interrupt Summary */ +#define STS_AIS    0x00008000       /* Abnormal Interrupt Summary */ +#define STS_ER     0x00004000       /* Early Receive */ +#define STS_FBE    0x00002000       /* Fatal Bus Error */ +#define STS_SE     0x00002000       /* System Error */ +#define STS_LNF    0x00001000       /* Link Fail */ +#define STS_FD     0x00000800       /* Full-Duplex Short Frame Received */ +#define STS_TM     0x00000800       /* Timer Expired (DC21041) */ +#define STS_ETI    0x00000400       /* Early Transmit Interupt */ +#define STS_AT     0x00000400       /* AUI/TP Pin */ +#define STS_RWT    0x00000200       /* Receive Watchdog Time-Out */ +#define STS_RPS    0x00000100       /* Receive Process Stopped */ +#define STS_RU     0x00000080       /* Receive Buffer Unavailable */ +#define STS_RI     0x00000040       /* Receive Interrupt */ +#define STS_UNF    0x00000020       /* Transmit Underflow */ +#define STS_LNP    0x00000010       /* Link Pass */ +#define STS_ANC    0x00000010       /* Autonegotiation Complete */ +#define STS_TJT    0x00000008       /* Transmit Jabber Time-Out */ +#define STS_TU     0x00000004       /* Transmit Buffer Unavailable */ +#define STS_TPS    0x00000002       /* Transmit Process Stopped */ +#define STS_TI     0x00000001       /* Transmit Interrupt */ + +#define EB_PAR     0x00000000       /* Parity Error */ +#define EB_MA      0x00800000       /* Master Abort */ +#define EB_TA      0x01000000       /* Target Abort */ +#define EB_RES0    0x01800000       /* Reserved */ +#define EB_RES1    0x02000000       /* Reserved */ + +#define TS_STOP    0x00000000       /* Stopped */ +#define TS_FTD     0x00100000       /* Fetch Transmit Descriptor */ +#define TS_WEOT    0x00200000       /* Wait for End Of Transmission */ +#define TS_QDAT    0x00300000       /* Queue skb data into TX FIFO */ +#define TS_RES     0x00400000       /* Reserved */ +#define TS_SPKT    0x00500000       /* Setup Packet */ +#define TS_SUSP    0x00600000       /* Suspended */ +#define TS_CLTD    0x00700000       /* Close Transmit Descriptor */ + +#define RS_STOP    0x00000000       /* Stopped */ +#define RS_FRD     0x00020000       /* Fetch Receive Descriptor */ +#define RS_CEOR    0x00040000       /* Check for End of Receive Packet */ +#define RS_WFRP    0x00060000       /* Wait for Receive Packet */ +#define RS_SUSP    0x00080000       /* Suspended */ +#define RS_CLRD    0x000a0000       /* Close Receive Descriptor */ +#define RS_FLUSH   0x000c0000       /* Flush RX FIFO */ +#define RS_QRFS    0x000e0000       /* Queue RX FIFO into RX Skb */ + +#define INT_CANCEL 0x0001ffff       /* For zeroing all interrupt sources */ + +/* +** Operation Mode Register (DE4X5_OMR) +*/ +#define OMR_SC     0x80000000       /* Special Capture Effect Enable */ +#define OMR_RA     0x40000000       /* Receive All */ +#define OMR_SDP    0x02000000       /* SD Polarity - MUST BE ASSERTED */ +#define OMR_SCR    0x01000000       /* Scrambler Mode */ +#define OMR_PCS    0x00800000       /* PCS Function */ +#define OMR_TTM    0x00400000       /* Transmit Threshold Mode */ +#define OMR_SF     0x00200000       /* Store and Forward */ +#define OMR_HBD    0x00080000       /* HeartBeat Disable */ +#define OMR_PS     0x00040000       /* Port Select */ +#define OMR_CA     0x00020000       /* Capture Effect Enable */ +#define OMR_BP     0x00010000       /* Back Pressure */ +#define OMR_TR     0x0000c000       /* Threshold Control Bits */ +#define OMR_ST     0x00002000       /* Start/Stop Transmission Command */ +#define OMR_FC     0x00001000       /* Force Collision Mode */ +#define OMR_OM     0x00000c00       /* Operating Mode */ +#define OMR_FDX    0x00000200       /* Full Duplex Mode */ +#define OMR_FKD    0x00000100       /* Flaky Oscillator Disable */ +#define OMR_PM     0x00000080       /* Pass All Multicast */ +#define OMR_PR     0x00000040       /* Promiscuous Mode */ +#define OMR_SB     0x00000020       /* Start/Stop Backoff Counter */ +#define OMR_IF     0x00000010       /* Inverse Filtering */ +#define OMR_PB     0x00000008       /* Pass Bad Frames */ +#define OMR_HO     0x00000004       /* Hash Only Filtering Mode */ +#define OMR_SR     0x00000002       /* Start/Stop Receive */ +#define OMR_HP     0x00000001       /* Hash/Perfect Receive Filtering Mode */ + +#define TR_72      0x00000000       /* Threshold set to 72 (128) bytes */ +#define TR_96      0x00004000       /* Threshold set to 96 (256) bytes */ +#define TR_128     0x00008000       /* Threshold set to 128 (512) bytes */ +#define TR_160     0x0000c000       /* Threshold set to 160 (1024) bytes */ + +#define OMR_DEF     (OMR_SDP) +#define OMR_SIA     (OMR_SDP | OMR_TTM) +#define OMR_SYM     (OMR_SDP | OMR_SCR | OMR_PCS | OMR_HBD | OMR_PS) +#define OMR_MII_10  (OMR_SDP | OMR_TTM | OMR_PS) +#define OMR_MII_100 (OMR_SDP | OMR_HBD | OMR_PS) + +/* +** DC21040 Interrupt Mask Register (DE4X5_IMR) +*/ +#define IMR_GPM    0x04000000       /* General Purpose Port Mask */ +#define IMR_NIM    0x00010000       /* Normal Interrupt Summary Mask */ +#define IMR_AIM    0x00008000       /* Abnormal Interrupt Summary Mask */ +#define IMR_ERM    0x00004000       /* Early Receive Mask */ +#define IMR_FBM    0x00002000       /* Fatal Bus Error Mask */ +#define IMR_SEM    0x00002000       /* System Error Mask */ +#define IMR_LFM    0x00001000       /* Link Fail Mask */ +#define IMR_FDM    0x00000800       /* Full-Duplex (Short Frame) Mask */ +#define IMR_TMM    0x00000800       /* Timer Expired Mask (DC21041) */ +#define IMR_ETM    0x00000400       /* Early Transmit Interrupt Mask */ +#define IMR_ATM    0x00000400       /* AUI/TP Switch Mask */ +#define IMR_RWM    0x00000200       /* Receive Watchdog Time-Out Mask */ +#define IMR_RSM    0x00000100       /* Receive Stopped Mask */ +#define IMR_RUM    0x00000080       /* Receive Buffer Unavailable Mask */ +#define IMR_RIM    0x00000040       /* Receive Interrupt Mask */ +#define IMR_UNM    0x00000020       /* Underflow Interrupt Mask */ +#define IMR_ANM    0x00000010       /* Autonegotiation Complete Mask */ +#define IMR_LPM    0x00000010       /* Link Pass */ +#define IMR_TJM    0x00000008       /* Transmit Time-Out Jabber Mask */ +#define IMR_TUM    0x00000004       /* Transmit Buffer Unavailable Mask */ +#define IMR_TSM    0x00000002       /* Transmission Stopped Mask */ +#define IMR_TIM    0x00000001       /* Transmit Interrupt Mask */ + +/* +** Missed Frames and FIFO Overflow Counters (DE4X5_MFC) +*/ +#define MFC_FOCO   0x10000000       /* FIFO Overflow Counter Overflow Bit */ +#define MFC_FOC    0x0ffe0000       /* FIFO Overflow Counter Bits */ +#define MFC_OVFL   0x00010000       /* Missed Frames Counter Overflow Bit */ +#define MFC_CNTR   0x0000ffff       /* Missed Frames Counter Bits */ +#define MFC_FOCM   0x1ffe0000       /* FIFO Overflow Counter Mask */ + +/* +** DC21040 Ethernet Address PROM (DE4X5_APROM) +*/ +#define APROM_DN   0x80000000       /* Data Not Valid */ +#define APROM_DT   0x000000ff       /* Address Byte */ + +/* +** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM) +*/ +#define BROM_MODE 0x00008000       /* MODE_1: 0,  MODE_0: 1  (read only) */ +#define BROM_RD   0x00004000       /* Read from Boot ROM */ +#define BROM_WR   0x00002000       /* Write to Boot ROM */ +#define BROM_BR   0x00001000       /* Select Boot ROM when set */ +#define BROM_SR   0x00000800       /* Select Serial ROM when set */ +#define BROM_REG  0x00000400       /* External Register Select */ +#define BROM_DT   0x000000ff       /* Data Byte */ + +/* +** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM, DE4X5_MII) +*/ +#define MII_MDI   0x00080000       /* MII Management Data In */ +#define MII_MDO   0x00060000       /* MII Management Mode/Data Out */ +#define MII_MRD   0x00040000       /* MII Management Define Read Mode */ +#define MII_MWR   0x00000000       /* MII Management Define Write Mode */ +#define MII_MDT   0x00020000       /* MII Management Data Out */ +#define MII_MDC   0x00010000       /* MII Management Clock */ +#define MII_RD    0x00004000       /* Read from MII */ +#define MII_WR    0x00002000       /* Write to MII */ +#define MII_SEL   0x00000800       /* Select MII when RESET */ + +#define SROM_MODE 0x00008000       /* MODE_1: 0,  MODE_0: 1  (read only) */ +#define SROM_RD   0x00004000       /* Read from Boot ROM */ +#define SROM_WR   0x00002000       /* Write to Boot ROM */ +#define SROM_BR   0x00001000       /* Select Boot ROM when set */ +#define SROM_SR   0x00000800       /* Select Serial ROM when set */ +#define SROM_REG  0x00000400       /* External Register Select */ +#define SROM_DT   0x000000ff       /* Data Byte */ + +#define DT_OUT    0x00000008       /* Serial Data Out */ +#define DT_IN     0x00000004       /* Serial Data In */ +#define DT_CLK    0x00000002       /* Serial ROM Clock */ +#define DT_CS     0x00000001       /* Serial ROM Chip Select */ + +#define MII_PREAMBLE 0xffffffff    /* MII Management Preamble */ +#define MII_TEST     0xaaaaaaaa    /* MII Test Signal */ +#define MII_STRD     0x06          /* Start of Frame+Op Code: use low nibble */ +#define MII_STWR     0x0a          /* Start of Frame+Op Code: use low nibble */ + +#define MII_CR       0x00          /* MII Management Control Register */ +#define MII_SR       0x01          /* MII Management Status Register */ +#define MII_ID0      0x02          /* PHY Identifier Register 0 */ +#define MII_ID1      0x03          /* PHY Identifier Register 1 */ +#define MII_ANA      0x04          /* Auto Negotiation Advertisement */ +#define MII_ANLPA    0x05          /* Auto Negotiation Link Partner Ability */ +#define MII_ANE      0x06          /* Auto Negotiation Expansion */ +#define MII_ANP      0x07          /* Auto Negotiation Next Page TX */ + +#define DE4X5_MAX_MII 32           /* Maximum address of MII PHY devices */ + +/* +** MII Management Control Register +*/ +#define MII_CR_RST  0x8000         /* RESET the PHY chip */ +#define MII_CR_LPBK 0x4000         /* Loopback enable */ +#define MII_CR_SPD  0x2000         /* 0: 10Mb/s; 1: 100Mb/s */ +#define MII_CR_10   0x0000         /* Set 10Mb/s */ +#define MII_CR_100  0x2000         /* Set 100Mb/s */ +#define MII_CR_ASSE 0x1000         /* Auto Speed Select Enable */ +#define MII_CR_PD   0x0800         /* Power Down */ +#define MII_CR_ISOL 0x0400         /* Isolate Mode */ +#define MII_CR_RAN  0x0200         /* Restart Auto Negotiation */ +#define MII_CR_FDM  0x0100         /* Full Duplex Mode */ +#define MII_CR_CTE  0x0080         /* Collision Test Enable */ + +/* +** MII Management Status Register +*/ +#define MII_SR_T4C  0x8000         /* 100BASE-T4 capable */ +#define MII_SR_TXFD 0x4000         /* 100BASE-TX Full Duplex capable */ +#define MII_SR_TXHD 0x2000         /* 100BASE-TX Half Duplex capable */ +#define MII_SR_TFD  0x1000         /* 10BASE-T Full Duplex capable */ +#define MII_SR_THD  0x0800         /* 10BASE-T Half Duplex capable */ +#define MII_SR_ASSC 0x0020         /* Auto Speed Selection Complete*/ +#define MII_SR_RFD  0x0010         /* Remote Fault Detected */ +#define MII_SR_ANC  0x0008         /* Auto Negotiation capable */ +#define MII_SR_LKS  0x0004         /* Link Status */ +#define MII_SR_JABD 0x0002         /* Jabber Detect */ +#define MII_SR_XC   0x0001         /* Extended Capabilities */ + +/* +** MII Management Auto Negotiation Advertisement Register +*/ +#define MII_ANA_TAF  0x03e0        /* Technology Ability Field */ +#define MII_ANA_T4AM 0x0200        /* T4 Technology Ability Mask */ +#define MII_ANA_TXAM 0x0180        /* TX Technology Ability Mask */ +#define MII_ANA_FDAM 0x0140        /* Full Duplex Technology Ability Mask */ +#define MII_ANA_HDAM 0x02a0        /* Half Duplex Technology Ability Mask */ +#define MII_ANA_100M 0x0380        /* 100Mb Technology Ability Mask */ +#define MII_ANA_10M  0x0060        /* 10Mb Technology Ability Mask */ +#define MII_ANA_CSMA 0x0001        /* CSMA-CD Capable */ + +/* +** MII Management Auto Negotiation Remote End Register +*/ +#define MII_ANLPA_NP   0x8000      /* Next Page (Enable) */ +#define MII_ANLPA_ACK  0x4000      /* Remote Acknowledge */ +#define MII_ANLPA_RF   0x2000      /* Remote Fault */ +#define MII_ANLPA_TAF  0x03e0      /* Technology Ability Field */ +#define MII_ANLPA_T4AM 0x0200      /* T4 Technology Ability Mask */ +#define MII_ANLPA_TXAM 0x0180      /* TX Technology Ability Mask */ +#define MII_ANLPA_FDAM 0x0140      /* Full Duplex Technology Ability Mask */ +#define MII_ANLPA_HDAM 0x02a0      /* Half Duplex Technology Ability Mask */ +#define MII_ANLPA_100M 0x0380      /* 100Mb Technology Ability Mask */ +#define MII_ANLPA_10M  0x0060      /* 10Mb Technology Ability Mask */ +#define MII_ANLPA_CSMA 0x0001      /* CSMA-CD Capable */ + +/* +** SROM Media Definitions (ABG SROM Section) +*/ +#define MEDIA_NWAY     0x0080      /* Nway (Auto Negotiation) on PHY */ +#define MEDIA_MII      0x0040      /* MII Present on the adapter */ +#define MEDIA_FIBRE    0x0008      /* Fibre Media present */ +#define MEDIA_AUI      0x0004      /* AUI Media present */ +#define MEDIA_TP       0x0002      /* TP Media present */ +#define MEDIA_BNC      0x0001      /* BNC Media present */ + +/* +** SROM Definitions (Digital Semiconductor Format) +*/ +#define SROM_SSVID     0x0000      /* Sub-system Vendor ID offset */ +#define SROM_SSID      0x0002      /* Sub-system ID offset */ +#define SROM_CISPL     0x0004      /* CardBus CIS Pointer low offset */ +#define SROM_CISPH     0x0006      /* CardBus CIS Pointer high offset */ +#define SROM_IDCRC     0x0010      /* ID Block CRC offset*/ +#define SROM_RSVD2     0x0011      /* ID Reserved 2 offset */ +#define SROM_SFV       0x0012      /* SROM Format Version offset */ +#define SROM_CCNT      0x0013      /* Controller Count offset */ +#define SROM_HWADD     0x0014      /* Hardware Address offset */ +#define SROM_MRSVD     0x007c      /* Manufacturer Reserved offset*/ +#define SROM_CRC       0x007e      /* SROM CRC offset */ + +/* +** SROM Media Connection Definitions +*/ +#define SROM_10BT      0x0000      /*  10BASE-T half duplex */ +#define SROM_10BTN     0x0100      /*  10BASE-T with Nway */ +#define SROM_10BTF     0x0204      /*  10BASE-T full duplex */ +#define SROM_10BTNLP   0x0400      /*  10BASE-T without Link Pass test */ +#define SROM_10B2      0x0001      /*  10BASE-2 (BNC) */ +#define SROM_10B5      0x0002      /*  10BASE-5 (AUI) */ +#define SROM_100BTH    0x0003      /*  100BASE-T half duplex */ +#define SROM_100BTF    0x0205      /*  100BASE-T full duplex */ +#define SROM_100BT4    0x0006      /*  100BASE-T4 */ +#define SROM_100BFX    0x0007      /*  100BASE-FX half duplex (Fiber) */ +#define SROM_M10BT     0x0009      /*  MII 10BASE-T half duplex */ +#define SROM_M10BTF    0x020a      /*  MII 10BASE-T full duplex */ +#define SROM_M100BT    0x000d      /*  MII 100BASE-T half duplex */ +#define SROM_M100BTF   0x020e      /*  MII 100BASE-T full duplex */ +#define SROM_M100BT4   0x000f      /*  MII 100BASE-T4 */ +#define SROM_M100BF    0x0010      /*  MII 100BASE-FX half duplex */ +#define SROM_M100BFF   0x0211      /*  MII 100BASE-FX full duplex */ +#define SROM_PDA       0x0800      /*  Powerup & Dynamic Autosense */ +#define SROM_PAO       0x8800      /*  Powerup Autosense Only */ +#define SROM_NSMI      0xffff      /*  No Selected Media Information */ + +/* +** SROM Media Definitions +*/ +#define SROM_10BASET   0x0000      /*  10BASE-T half duplex */ +#define SROM_10BASE2   0x0001      /*  10BASE-2 (BNC) */ +#define SROM_10BASE5   0x0002      /*  10BASE-5 (AUI) */ +#define SROM_100BASET  0x0003      /*  100BASE-T half duplex */ +#define SROM_10BASETF  0x0004      /*  10BASE-T full duplex */ +#define SROM_100BASETF 0x0005      /*  100BASE-T full duplex */ +#define SROM_100BASET4 0x0006      /*  100BASE-T4 */ +#define SROM_100BASEF  0x0007      /*  100BASE-FX half duplex */ +#define SROM_100BASEFF 0x0008      /*  100BASE-FX full duplex */ + +#define BLOCK_LEN      0x7f        /* Extended blocks length mask */ +#define EXT_FIELD      0x40        /* Extended blocks extension field bit */ +#define MEDIA_CODE     0x3f        /* Extended blocks media code mask */ + +/* +** SROM Compact Format Block Masks +*/ +#define COMPACT_FI      0x80       /* Format Indicator */ +#define COMPACT_LEN     0x04       /* Length */ +#define COMPACT_MC      0x3f       /* Media Code */ + +/* +** SROM Extended Format Block Type 0 Masks +*/ +#define BLOCK0_FI      0x80        /* Format Indicator */ +#define BLOCK0_MCS     0x80        /* Media Code byte Sign */ +#define BLOCK0_MC      0x3f        /* Media Code */ + +/* +** DC21040 Full Duplex Register (DE4X5_FDR) +*/ +#define FDR_FDACV  0x0000ffff      /* Full Duplex Auto Configuration Value */ + +/* +** DC21041 General Purpose Timer Register (DE4X5_GPT) +*/ +#define GPT_CON  0x00010000        /* One shot: 0,  Continuous: 1 */ +#define GPT_VAL  0x0000ffff        /* Timer Value */ + +/* +** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits) +*/ +/* Valid ONLY for DE500 hardware */ +#define GEP_LNP  0x00000080        /* Link Pass               (input)        */ +#define GEP_SLNK 0x00000040        /* SYM LINK                (input)        */ +#define GEP_SDET 0x00000020        /* Signal Detect           (input)        */ +#define GEP_HRST 0x00000010        /* Hard RESET (to PHY)     (output)       */ +#define GEP_FDXD 0x00000008        /* Full Duplex Disable     (output)       */ +#define GEP_PHYL 0x00000004        /* PHY Loopback            (output)       */ +#define GEP_FLED 0x00000002        /* Force Activity LED on   (output)       */ +#define GEP_MODE 0x00000001        /* 0: 10Mb/s,  1: 100Mb/s                 */ +#define GEP_INIT 0x0000011f        /* Setup inputs (0) and outputs (1)       */ +#define GEP_CTRL 0x00000100        /* GEP control bit                        */ + +/* +** SIA Register Defaults +*/ +#define CSR13 0x00000001 +#define CSR14 0x0003ff7f           /* Autonegotiation disabled               */ +#define CSR15 0x00000008 + +/* +** SIA Status Register (DE4X5_SISR) +*/ +#define SISR_LPC   0xffff0000      /* Link Partner's Code Word               */ +#define SISR_LPN   0x00008000      /* Link Partner Negotiable                */ +#define SISR_ANS   0x00007000      /* Auto Negotiation Arbitration State     */ +#define SISR_NSN   0x00000800      /* Non Stable NLPs Detected (DC21041)     */ +#define SISR_TRF   0x00000800      /* Transmit Remote Fault                  */ +#define SISR_NSND  0x00000400      /* Non Stable NLPs Detected (DC21142)     */ +#define SISR_ANR_FDS 0x00000400    /* Auto Negotiate Restart/Full Duplex Sel.*/ +#define SISR_TRA   0x00000200      /* 10BASE-T Receive Port Activity         */ +#define SISR_NRA   0x00000200      /* Non Selected Port Receive Activity     */ +#define SISR_ARA   0x00000100      /* AUI Receive Port Activity              */ +#define SISR_SRA   0x00000100      /* Selected Port Receive Activity         */ +#define SISR_DAO   0x00000080      /* PLL All One                            */ +#define SISR_DAZ   0x00000040      /* PLL All Zero                           */ +#define SISR_DSP   0x00000020      /* PLL Self-Test Pass                     */ +#define SISR_DSD   0x00000010      /* PLL Self-Test Done                     */ +#define SISR_APS   0x00000008      /* Auto Polarity State                    */ +#define SISR_LKF   0x00000004      /* Link Fail Status                       */ +#define SISR_LS10  0x00000004      /* 10Mb/s Link Fail Status                */ +#define SISR_NCR   0x00000002      /* Network Connection Error               */ +#define SISR_LS100 0x00000002      /* 100Mb/s Link Fail Status               */ +#define SISR_PAUI  0x00000001      /* AUI_TP Indication                      */ +#define SISR_MRA   0x00000001      /* MII Receive Port Activity              */ + +#define ANS_NDIS   0x00000000      /* Nway disable                           */ +#define ANS_TDIS   0x00001000      /* Transmit Disable                       */ +#define ANS_ADET   0x00002000      /* Ability Detect                         */ +#define ANS_ACK    0x00003000      /* Acknowledge                            */ +#define ANS_CACK   0x00004000      /* Complete Acknowledge                   */ +#define ANS_NWOK   0x00005000      /* Nway OK - FLP Link Good                */ +#define ANS_LCHK   0x00006000      /* Link Check                             */ + +#define SISR_RST   0x00000301      /* CSR12 reset                            */ +#define SISR_ANR   0x00001301      /* Autonegotiation restart                */ + +/* +** SIA Connectivity Register (DE4X5_SICR) +*/ +#define SICR_SDM   0xffff0000       /* SIA Diagnostics Mode */ +#define SICR_OE57  0x00008000       /* Output Enable 5 6 7 */ +#define SICR_OE24  0x00004000       /* Output Enable 2 4 */ +#define SICR_OE13  0x00002000       /* Output Enable 1 3 */ +#define SICR_IE    0x00001000       /* Input Enable */ +#define SICR_EXT   0x00000000       /* SIA MUX Select External SIA Mode */ +#define SICR_D_SIA 0x00000400       /* SIA MUX Select Diagnostics - SIA Sigs */ +#define SICR_DPLL  0x00000800       /* SIA MUX Select Diagnostics - DPLL Sigs*/ +#define SICR_APLL  0x00000a00       /* SIA MUX Select Diagnostics - DPLL Sigs*/ +#define SICR_D_RxM 0x00000c00       /* SIA MUX Select Diagnostics - RxM Sigs */ +#define SICR_M_RxM 0x00000d00       /* SIA MUX Select Diagnostics - RxM Sigs */ +#define SICR_LNKT  0x00000e00       /* SIA MUX Select Diagnostics - Link Test*/ +#define SICR_SEL   0x00000f00       /* SIA MUX Select AUI or TP with LEDs */ +#define SICR_ASE   0x00000080       /* APLL Start Enable*/ +#define SICR_SIM   0x00000040       /* Serial Interface Input Multiplexer */ +#define SICR_ENI   0x00000020       /* Encoder Input Multiplexer */ +#define SICR_EDP   0x00000010       /* SIA PLL External Input Enable */ +#define SICR_AUI   0x00000008       /* 10Base-T (0) or AUI (1) */ +#define SICR_CAC   0x00000004       /* CSR Auto Configuration */ +#define SICR_PS    0x00000002       /* Pin AUI/TP Selection */ +#define SICR_SRL   0x00000001       /* SIA Reset */ +#define SIA_RESET  0x00000000       /* SIA Reset Value */ + +/* +** SIA Transmit and Receive Register (DE4X5_STRR) +*/ +#define STRR_TAS   0x00008000       /* 10Base-T/AUI Autosensing Enable */ +#define STRR_SPP   0x00004000       /* Set Polarity Plus */ +#define STRR_APE   0x00002000       /* Auto Polarity Enable */ +#define STRR_LTE   0x00001000       /* Link Test Enable */ +#define STRR_SQE   0x00000800       /* Signal Quality Enable */ +#define STRR_CLD   0x00000400       /* Collision Detect Enable */ +#define STRR_CSQ   0x00000200       /* Collision Squelch Enable */ +#define STRR_RSQ   0x00000100       /* Receive Squelch Enable */ +#define STRR_ANE   0x00000080       /* Auto Negotiate Enable */ +#define STRR_HDE   0x00000040       /* Half Duplex Enable */ +#define STRR_CPEN  0x00000030       /* Compensation Enable */ +#define STRR_LSE   0x00000008       /* Link Pulse Send Enable */ +#define STRR_DREN  0x00000004       /* Driver Enable */ +#define STRR_LBK   0x00000002       /* Loopback Enable */ +#define STRR_ECEN  0x00000001       /* Encoder Enable */ +#define STRR_RESET 0xffffffff       /* Reset value for STRR */ + +/* +** SIA General Register (DE4X5_SIGR) +*/ +#define SIGR_RMI   0x40000000       /* Receive Match Interrupt */ +#define SIGR_GI1   0x20000000       /* General Port Interrupt 1 */ +#define SIGR_GI0   0x10000000       /* General Port Interrupt 0 */ +#define SIGR_CWE   0x08000000       /* Control Write Enable */ +#define SIGR_RME   0x04000000       /* Receive Match Enable */ +#define SIGR_GEI1  0x02000000       /* GEP Interrupt Enable on Port 1 */ +#define SIGR_GEI0  0x01000000       /* GEP Interrupt Enable on Port 0 */ +#define SIGR_LGS3  0x00800000       /* LED/GEP3 Select */ +#define SIGR_LGS2  0x00400000       /* LED/GEP2 Select */ +#define SIGR_LGS1  0x00200000       /* LED/GEP1 Select */ +#define SIGR_LGS0  0x00100000       /* LED/GEP0 Select */ +#define SIGR_MD    0x000f0000       /* General Purpose Mode and Data */ +#define SIGR_LV2   0x00008000       /* General Purpose LED2 value */ +#define SIGR_LE2   0x00004000       /* General Purpose LED2 enable */ +#define SIGR_FRL   0x00002000       /* Force Receiver Low */ +#define SIGR_DPST  0x00001000       /* PLL Self Test Start */ +#define SIGR_LSD   0x00000800       /* LED Stretch Disable */ +#define SIGR_FLF   0x00000400       /* Force Link Fail */ +#define SIGR_FUSQ  0x00000200       /* Force Unsquelch */ +#define SIGR_TSCK  0x00000100       /* Test Clock */ +#define SIGR_LV1   0x00000080       /* General Purpose LED1 value */ +#define SIGR_LE1   0x00000040       /* General Purpose LED1 enable */ +#define SIGR_RWR   0x00000020       /* Receive Watchdog Release */ +#define SIGR_RWD   0x00000010       /* Receive Watchdog Disable */ +#define SIGR_ABM   0x00000008       /* BNC: 0,  AUI:1 */ +#define SIGR_JCK   0x00000004       /* Jabber Clock */ +#define SIGR_HUJ   0x00000002       /* Host Unjab */ +#define SIGR_JBD   0x00000001       /* Jabber Disable */ +#define SIGR_RESET 0xffff0000       /* Reset value for SIGR */ + +/* +** Receive Descriptor Bit Summary +*/ +#define R_OWN      0x80000000       /* Own Bit */ +#define RD_FF      0x40000000       /* Filtering Fail */ +#define RD_FL      0x3fff0000       /* Frame Length */ +#define RD_ES      0x00008000       /* Error Summary */ +#define RD_LE      0x00004000       /* Length Error */ +#define RD_DT      0x00003000       /* Data Type */ +#define RD_RF      0x00000800       /* Runt Frame */ +#define RD_MF      0x00000400       /* Multicast Frame */ +#define RD_FS      0x00000200       /* First Descriptor */ +#define RD_LS      0x00000100       /* Last Descriptor */ +#define RD_TL      0x00000080       /* Frame Too Long */ +#define RD_CS      0x00000040       /* Collision Seen */ +#define RD_FT      0x00000020       /* Frame Type */ +#define RD_RJ      0x00000010       /* Receive Watchdog */ +#define RD_RE      0x00000008       /* Report on MII Error */ +#define RD_DB      0x00000004       /* Dribbling Bit */ +#define RD_CE      0x00000002       /* CRC Error */ +#define RD_OF      0x00000001       /* Overflow */ + +#define RD_RER     0x02000000       /* Receive End Of Ring */ +#define RD_RCH     0x01000000       /* Second Address Chained */ +#define RD_RBS2    0x003ff800       /* Buffer 2 Size */ +#define RD_RBS1    0x000007ff       /* Buffer 1 Size */ + +/* +** Transmit Descriptor Bit Summary +*/ +#define T_OWN      0x80000000       /* Own Bit */ +#define TD_ES      0x00008000       /* Error Summary */ +#define TD_TO      0x00004000       /* Transmit Jabber Time-Out */ +#define TD_LO      0x00000800       /* Loss Of Carrier */ +#define TD_NC      0x00000400       /* No Carrier */ +#define TD_LC      0x00000200       /* Late Collision */ +#define TD_EC      0x00000100       /* Excessive Collisions */ +#define TD_HF      0x00000080       /* Heartbeat Fail */ +#define TD_CC      0x00000078       /* Collision Counter */ +#define TD_LF      0x00000004       /* Link Fail */ +#define TD_UF      0x00000002       /* Underflow Error */ +#define TD_DE      0x00000001       /* Deferred */ + +#define TD_IC      0x80000000       /* Interrupt On Completion */ +#define TD_LS      0x40000000       /* Last Segment */ +#define TD_FS      0x20000000       /* First Segment */ +#define TD_FT1     0x10000000       /* Filtering Type */ +#define TD_SET     0x08000000       /* Setup Packet */ +#define TD_AC      0x04000000       /* Add CRC Disable */ +#define TD_TER     0x02000000       /* Transmit End Of Ring */ +#define TD_TCH     0x01000000       /* Second Address Chained */ +#define TD_DPD     0x00800000       /* Disabled Padding */ +#define TD_FT0     0x00400000       /* Filtering Type */ +#define TD_TBS2    0x003ff800       /* Buffer 2 Size */ +#define TD_TBS1    0x000007ff       /* Buffer 1 Size */ + +#define PERFECT_F  0x00000000 +#define HASH_F     TD_FT0 +#define INVERSE_F  TD_FT1 +#define HASH_O_F   (TD_FT1 | TD_F0) + +/* +** Media / mode state machine definitions +** User selectable: +*/ +#define TP              0x0001     /* 10Base-T                             */ +#define TP_NW           0x0002     /* 10Base-T with Nway                   */ +#define BNC             0x0004     /* Thinwire                             */ +#define AUI             0x0008     /* Thickwire                            */ +#define BNC_AUI         0x0010     /* BNC/AUI on DC21040 indistinguishable */ +#define _10Mb           0x0040     /* 10Mb/s Ethernet                      */ +#define _100Mb          0x0080     /* 100Mb/s Ethernet                     */ +#define AUTO            0x4000     /* Auto sense the media or speed        */ + +/* +** Internal states +*/ +#define NC              0x0000     /* No Connection                        */ +#define ANS             0x0020     /* Intermediate AutoNegotiation State   */ +#define SPD_DET         0x0100     /* Parallel speed detection             */ +#define INIT            0x0200     /* Initial state                        */ +#define EXT_SIA         0x0400     /* External SIA for motherboard chip    */ +#define ANS_SUSPECT     0x0802     /* Suspect the ANS (TP) port is down    */ +#define TP_SUSPECT      0x0803     /* Suspect the TP port is down          */ +#define BNC_AUI_SUSPECT 0x0804     /* Suspect the BNC or AUI port is down  */ +#define EXT_SIA_SUSPECT 0x0805     /* Suspect the EXT SIA port is down     */ +#define BNC_SUSPECT     0x0806     /* Suspect the BNC port is down         */ +#define AUI_SUSPECT     0x0807     /* Suspect the AUI port is down         */ +#define MII             0x1000     /* MII on the 21143                     */ + +#define TIMER_CB        0x80000000 /* Timer callback detection             */ + +/* +** DE4X5 DEBUG Options +*/ +#define DEBUG_NONE      0x0000     /* No DEBUG messages */ +#define DEBUG_VERSION   0x0001     /* Print version message */ +#define DEBUG_MEDIA     0x0002     /* Print media messages */ +#define DEBUG_TX        0x0004     /* Print TX (queue_pkt) messages */ +#define DEBUG_RX        0x0008     /* Print RX (de4x5_rx) messages */ +#define DEBUG_SROM      0x0010     /* Print SROM messages */ +#define DEBUG_MII       0x0020     /* Print MII messages */ +#define DEBUG_OPEN      0x0040     /* Print de4x5_open() messages */ +#define DEBUG_CLOSE     0x0080     /* Print de4x5_close() messages */ +#define DEBUG_PCICFG    0x0100 +#define DEBUG_ALL       0x01ff + +/* +** Miscellaneous +*/ +#define PCI  0 +#define EISA 1 + +#define HASH_TABLE_LEN   512       /* Bits */ +#define HASH_BITS        0x01ff    /* 9 LS bits */ + +#define SETUP_FRAME_LEN  192       /* Bytes */ +#define IMPERF_PA_OFFSET 156       /* Bytes */ + +#define POLL_DEMAND          1 + +#define LOST_MEDIA_THRESHOLD 3 + +#define MASK_INTERRUPTS      1 +#define UNMASK_INTERRUPTS    0 + +#define DE4X5_STRLEN         8 + +#define DE4X5_INIT           0     /* Initialisation time */ +#define DE4X5_RUN            1     /* Run time */ + +#define DE4X5_SAVE_STATE     0 +#define DE4X5_RESTORE_STATE  1 + +/* +** Address Filtering Modes +*/ +#define PERFECT              0     /* 16 perfect physical addresses */ +#define HASH_PERF            1     /* 1 perfect, 512 multicast addresses */ +#define PERFECT_REJ          2     /* Reject 16 perfect physical addresses */ +#define ALL_HASH             3     /* Hashes all physical & multicast addrs */ + +#define ALL                  0     /* Clear out all the setup frame */ +#define PHYS_ADDR_ONLY       1     /* Update the physical address only */ + +/* +** Booleans +*/ +#define NO                   0 +#define FALSE                0 + +#define YES                  ~0 +#define TRUE                 ~0 + +/* +** Adapter state +*/ +#define INITIALISED          0     /* After h/w initialised and mem alloc'd */ +#define CLOSED               1     /* Ready for opening */ +#define OPEN                 2     /* Running */ + +/* +** Various wait times +*/ +#define PDET_LINK_WAIT    1200    /* msecs to wait for link detect bits     */ +#define ANS_FINISH_WAIT   1000    /* msecs to wait for link detect bits     */ + +/* +** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since +** the vendors seem split 50-50 on how to calculate the OUI register values +** anyway, just reading Reg2 seems reasonable for now [see de4x5_get_oui()]. +*/ +#define NATIONAL_TX 0x2000 +#define BROADCOM_T4 0x03e0 +#define SEEQ_T4     0x0016 +#define CYPRESS_T4  0x0014 + +/* +** Speed Selection stuff +*/ +#define SET_10Mb {\ +  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ +    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ +    if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ +      mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ +    }\ +    omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\ +    outl(omr, DE4X5_OMR);\ +    if (!lp->useSROM) lp->cache.gep = 0;\ +  } else if (lp->useSROM && !lp->useMII) {\ +    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ +    omr |= (lp->fdx ? OMR_FDX : 0);\ +    outl(omr | (lp->infoblock_csr6 & ~(OMR_SCR | OMR_HBD)), DE4X5_OMR);\ +  } else {\ +    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ +    omr |= (lp->fdx ? OMR_FDX : 0);\ +    outl(omr | OMR_SDP | OMR_TTM, DE4X5_OMR);\ +    lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ +    gep_wr(lp->cache.gep, dev);\ +  }\ +} + +#define SET_100Mb {\ +  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ +    int fdx=0;\ +    if (lp->phy[lp->active].id == NATIONAL_TX) {\ +        mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\ +                      0x18, lp->phy[lp->active].addr, DE4X5_MII);\ +    }\ +    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ +    sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\ +    if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\ +    if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ +      mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ +    }\ +    if (fdx) omr |= OMR_FDX;\ +    outl(omr, DE4X5_OMR);\ +    if (!lp->useSROM) lp->cache.gep = 0;\ +  } else if (lp->useSROM && !lp->useMII) {\ +    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ +    omr |= (lp->fdx ? OMR_FDX : 0);\ +    outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ +  } else {\ +    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ +    omr |= (lp->fdx ? OMR_FDX : 0);\ +    outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ +    lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ +    gep_wr(lp->cache.gep, dev);\ +  }\ +} + +/* FIX ME so I don't jam 10Mb networks */ +#define SET_100Mb_PDET {\ +  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ +    mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ +    omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ +    outl(omr, DE4X5_OMR);\ +  } else if (lp->useSROM && !lp->useMII) {\ +    omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ +    outl(omr, DE4X5_OMR);\ +  } else {\ +    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ +    outl(omr | OMR_SDP | OMR_PS | OMR_HBD | OMR_PCS, DE4X5_OMR);\ +    lp->cache.gep = (GEP_FDXD | GEP_MODE);\ +    gep_wr(lp->cache.gep, dev);\ +  }\ +} + +/* +** Include the IOCTL stuff +*/ +#include <linux/sockios.h> + +#define	DE4X5IOCTL	SIOCDEVPRIVATE + +struct de4x5_ioctl { +	unsigned short cmd;                /* Command to run */ +	unsigned short len;                /* Length of the data buffer */ +	unsigned char  *data;              /* Pointer to the data buffer */ +}; + +/*  +** Recognised commands for the driver  +*/ +#define DE4X5_GET_HWADDR	0x01 /* Get the hardware address */ +#define DE4X5_SET_HWADDR	0x02 /* Set the hardware address */ +#define DE4X5_SET_PROM  	0x03 /* Set Promiscuous Mode */ +#define DE4X5_CLR_PROM  	0x04 /* Clear Promiscuous Mode */ +#define DE4X5_SAY_BOO	        0x05 /* Say "Boo!" to the kernel log file */ +#define DE4X5_GET_MCA   	0x06 /* Get a multicast address */ +#define DE4X5_SET_MCA   	0x07 /* Set a multicast address */ +#define DE4X5_CLR_MCA    	0x08 /* Clear a multicast address */ +#define DE4X5_MCA_EN    	0x09 /* Enable a multicast address group */ +#define DE4X5_GET_STATS  	0x0a /* Get the driver statistics */ +#define DE4X5_CLR_STATS 	0x0b /* Zero out the driver statistics */ +#define DE4X5_GET_OMR           0x0c /* Get the OMR Register contents */ +#define DE4X5_SET_OMR           0x0d /* Set the OMR Register contents */ +#define DE4X5_GET_REG           0x0e /* Get the DE4X5 Registers */ + +#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s)) diff --git a/linux/src/drivers/net/de600.c b/linux/src/drivers/net/de600.c new file mode 100644 index 0000000..ce96942 --- /dev/null +++ b/linux/src/drivers/net/de600.c @@ -0,0 +1,853 @@ +static const char *version = +	"de600.c: $Revision: 1.1 $,  Bjorn Ekwall (bj0rn@blox.se)\n"; +/* + *	de600.c + * + *	Linux driver for the D-Link DE-600 Ethernet pocket adapter. + * + *	Portions (C) Copyright 1993, 1994 by Bjorn Ekwall + *	The Author may be reached as bj0rn@blox.se + * + *	Based on adapter information gathered from DE600.ASM by D-Link Inc., + *	as included on disk C in the v.2.11 of PC/TCP from FTP Software. + *	For DE600.asm: + *		Portions (C) Copyright 1990 D-Link, Inc. + *		Copyright, 1988-1992, Russell Nelson, Crynwr Software + * + *	Adapted to the sample network driver core for linux, + *	written by: Donald Becker <becker@super.org> + *	C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 + * + *	compile-command: + *	"gcc -D__KERNEL__  -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer \ + *	 -m486 -c de600.c + * + **************************************************************/ +/* + *	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, 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. + * + *	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., 675 Mass Ave, Cambridge, MA 02139, USA.  + * + **************************************************************/ +/* Add another "; SLOW_DOWN_IO" here if your adapter won't work OK: */ +#define DE600_SLOW_DOWN SLOW_DOWN_IO; SLOW_DOWN_IO; SLOW_DOWN_IO + + /* + * If you still have trouble reading/writing to the adapter, + * modify the following "#define": (see <asm/io.h> for more info) +#define REALLY_SLOW_IO + */ +#define SLOW_IO_BY_JUMPING /* Looks "better" than dummy write to port 0x80 :-) */ + +/* + * If you want to enable automatic continuous checking for the DE600, + * keep this #define enabled. + * It doesn't cost much per packet, so I think it is worth it! + * If you disagree, comment away the #define, and live with it... + * + */ +#define CHECK_LOST_DE600 + +/* + * Enable this #define if you want the adapter to do a "ifconfig down" on + * itself when we have detected that something is possibly wrong with it. + * The default behaviour is to retry with "adapter_init()" until success. + * This should be used for debugging purposes only. + * (Depends on the CHECK_LOST_DE600 above) + * + */ +#define SHUTDOWN_WHEN_LOST + +/* + * See comment at "de600_rspace()"! + * This is an *ugly* hack, but for now it achieves its goal of + * faking a TCP flow-control that will not flood the poor DE600. + * + * Tricks TCP to announce a small max window (max 2 fast packets please :-) + * + * Comment away at your own risk! + * + * Update: Use the more general per-device maxwindow parameter instead. + */ +#undef FAKE_SMALL_MAX + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifdef DE600_DEBUG +#define PRINTK(x) if (de600_debug >= 2) printk x +#else +#define DE600_DEBUG 0 +#define PRINTK(x) /**/ +#endif +unsigned int de600_debug = DE600_DEBUG; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <linux/in.h> +#include <linux/ptrace.h> +#include <asm/system.h> +#include <linux/errno.h> + +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#ifdef FAKE_SMALL_MAX +static unsigned long de600_rspace(struct sock *sk); +#include <net/sock.h> +#endif + +#define netstats enet_statistics +typedef unsigned char byte; + +/************************************************** + *                                                * + * Definition of D-Link Ethernet Pocket adapter   * + *                                                * + **************************************************/ +/* + * D-Link Ethernet pocket adapter ports + */ +/* + * OK, so I'm cheating, but there are an awful lot of + * reads and writes in order to get anything in and out + * of the DE-600 with 4 bits at a time in the parallel port, + * so every saved instruction really helps :-) + * + * That is, I don't care what the device struct says + * but hope that Space.c will keep the rest of the drivers happy. + */ +#ifndef DE600_IO +#define DE600_IO 0x378 +#endif + +#define DATA_PORT	(DE600_IO) +#define STATUS_PORT	(DE600_IO + 1) +#define COMMAND_PORT	(DE600_IO + 2) + +#ifndef DE600_IRQ +#define DE600_IRQ	7 +#endif +/* + * It really should look like this, and autoprobing as well... + * +#define DATA_PORT	(dev->base_addr + 0) +#define STATUS_PORT	(dev->base_addr + 1) +#define COMMAND_PORT	(dev->base_addr + 2) +#define DE600_IRQ	dev->irq + */ + +/* + * D-Link COMMAND_PORT commands + */ +#define SELECT_NIC	0x04 /* select Network Interface Card */ +#define SELECT_PRN	0x1c /* select Printer */ +#define NML_PRN		0xec /* normal Printer situation */ +#define IRQEN		0x10 /* enable IRQ line */ + +/* + * D-Link STATUS_PORT + */ +#define RX_BUSY		0x80 +#define RX_GOOD		0x40 +#define TX_FAILED16	0x10 +#define TX_BUSY		0x08 + +/* + * D-Link DATA_PORT commands + * command in low 4 bits + * data in high 4 bits + * select current data nibble with HI_NIBBLE bit + */ +#define WRITE_DATA	0x00 /* write memory */ +#define READ_DATA	0x01 /* read memory */ +#define STATUS		0x02 /* read  status register */ +#define COMMAND		0x03 /* write command register (see COMMAND below) */ +#define NULL_COMMAND	0x04 /* null command */ +#define RX_LEN		0x05 /* read  received packet length */ +#define TX_ADDR		0x06 /* set adapter transmit memory address */ +#define RW_ADDR		0x07 /* set adapter read/write memory address */ +#define HI_NIBBLE	0x08 /* read/write the high nibble of data, +				or-ed with rest of command */ + +/* + * command register, accessed through DATA_PORT with low bits = COMMAND + */ +#define RX_ALL		0x01 /* PROMISCUOUS */ +#define RX_BP		0x02 /* default: BROADCAST & PHYSICAL ADDRESS */ +#define RX_MBP		0x03 /* MULTICAST, BROADCAST & PHYSICAL ADDRESS */ + +#define TX_ENABLE	0x04 /* bit 2 */ +#define RX_ENABLE	0x08 /* bit 3 */ + +#define RESET		0x80 /* set bit 7 high */ +#define STOP_RESET	0x00 /* set bit 7 low */ + +/* + * data to command register + * (high 4 bits in write to DATA_PORT) + */ +#define RX_PAGE2_SELECT	0x10 /* bit 4, only 2 pages to select */ +#define RX_BASE_PAGE	0x20 /* bit 5, always set when specifying RX_ADDR */ +#define FLIP_IRQ	0x40 /* bit 6 */ + +/* + * D-Link adapter internal memory: + * + * 0-2K 1:st transmit page (send from pointer up to 2K) + * 2-4K	2:nd transmit page (send from pointer up to 4K) + * + * 4-6K 1:st receive page (data from 4K upwards) + * 6-8K 2:nd receive page (data from 6K upwards) + * + * 8K+	Adapter ROM (contains magic code and last 3 bytes of Ethernet address) + */ +#define MEM_2K		0x0800 /* 2048 */ +#define MEM_4K		0x1000 /* 4096 */ +#define MEM_6K		0x1800 /* 6144 */ +#define NODE_ADDRESS	0x2000 /* 8192 */ + +#define RUNT 60		/* Too small Ethernet packet */ + +/************************************************** + *                                                * + *             End of definition                  * + *                                                * + **************************************************/ + +/* + * Index to functions, as function prototypes. + */ +/* Routines used internally. (See "convenience macros") */ +static byte	de600_read_status(struct device *dev); +static byte	de600_read_byte(unsigned char type, struct device *dev); + +/* Put in the device structure. */ +static int	de600_open(struct device *dev); +static int	de600_close(struct device *dev); +static struct netstats *get_stats(struct device *dev); +static int	de600_start_xmit(struct sk_buff *skb, struct device *dev); + +/* Dispatch from interrupts. */ +static void	de600_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int	de600_tx_intr(struct device *dev, int irq_status); +static void	de600_rx_intr(struct device *dev); + +/* Initialization */ +static void	trigger_interrupt(struct device *dev); +int		de600_probe(struct device *dev); +static int	adapter_init(struct device *dev); + +/* + * D-Link driver variables: + */ +static volatile int		rx_page		= 0; + +#define TX_PAGES 2 +static volatile int		tx_fifo[TX_PAGES]; +static volatile int		tx_fifo_in = 0; +static volatile int		tx_fifo_out = 0; +static volatile int		free_tx_pages = TX_PAGES; +static int			was_down = 0; + +/* + * Convenience macros/functions for D-Link adapter + */ + +#define select_prn() outb_p(SELECT_PRN, COMMAND_PORT); DE600_SLOW_DOWN +#define select_nic() outb_p(SELECT_NIC, COMMAND_PORT); DE600_SLOW_DOWN + +/* Thanks for hints from Mark Burton <markb@ordern.demon.co.uk> */ +#define de600_put_byte(data) ( \ +	outb_p(((data) << 4)   | WRITE_DATA            , DATA_PORT), \ +	outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT)) + +/* + * The first two outb_p()'s below could perhaps be deleted if there + * would be more delay in the last two. Not certain about it yet... + */ +#define de600_put_command(cmd) ( \ +	outb_p(( rx_page        << 4)   | COMMAND            , DATA_PORT), \ +	outb_p(( rx_page        & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT), \ +	outb_p(((rx_page | cmd) << 4)   | COMMAND            , DATA_PORT), \ +	outb_p(((rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT)) + +#define de600_setup_address(addr,type) ( \ +	outb_p((((addr) << 4) & 0xf0) | type            , DATA_PORT), \ +	outb_p(( (addr)       & 0xf0) | type | HI_NIBBLE, DATA_PORT), \ +	outb_p((((addr) >> 4) & 0xf0) | type            , DATA_PORT), \ +	outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT)) + +#define rx_page_adr() ((rx_page & RX_PAGE2_SELECT)?(MEM_6K):(MEM_4K)) + +/* Flip bit, only 2 pages */ +#define next_rx_page() (rx_page ^= RX_PAGE2_SELECT) + +#define tx_page_adr(a) (((a) + 1) * MEM_2K) + +static inline byte +de600_read_status(struct device *dev) +{ +	byte status; + +	outb_p(STATUS, DATA_PORT); +	status = inb(STATUS_PORT); +	outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT); + +	return status; +} + +static inline byte +de600_read_byte(unsigned char type, struct device *dev) { /* dev used by macros */ +	byte lo; + +	(void)outb_p((type), DATA_PORT); +	lo = ((unsigned char)inb(STATUS_PORT)) >> 4; +	(void)outb_p((type) | HI_NIBBLE, DATA_PORT); +	return ((unsigned char)inb(STATUS_PORT) & (unsigned char)0xf0) | lo; +} + +/* + * Open/initialize the board.  This is called (in the current kernel) + * after booting when 'ifconfig <dev->name> $IP_ADDR' is run (in rc.inet1). + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is a non-reboot way to recover if something goes wrong. + */ +static int +de600_open(struct device *dev) +{ +	if (request_irq(DE600_IRQ, de600_interrupt, 0, "de600", NULL)) { +		printk ("%s: unable to get IRQ %d\n", dev->name, DE600_IRQ); +		return 1; +	} +	irq2dev_map[DE600_IRQ] = dev; + +	MOD_INC_USE_COUNT; +	dev->start = 1; +	if (adapter_init(dev)) { +		return 1; +	} + +	return 0; +} + +/* + * The inverse routine to de600_open(). + */ +static int +de600_close(struct device *dev) +{ +	select_nic(); +	rx_page = 0; +	de600_put_command(RESET); +	de600_put_command(STOP_RESET); +	de600_put_command(0); +	select_prn(); + +	if (dev->start) { +		free_irq(DE600_IRQ, NULL); +		irq2dev_map[DE600_IRQ] = NULL; +		dev->start = 0; +		MOD_DEC_USE_COUNT; +	} +	return 0; +} + +static struct netstats * +get_stats(struct device *dev) +{ +    return (struct netstats *)(dev->priv); +} + +static inline void +trigger_interrupt(struct device *dev) +{ +	de600_put_command(FLIP_IRQ); +	select_prn(); +	DE600_SLOW_DOWN; +	select_nic(); +	de600_put_command(0); +} + +/* + * Copy a buffer to the adapter transmit page memory. + * Start sending. + */ +static int +de600_start_xmit(struct sk_buff *skb, struct device *dev) +{ +	int	transmit_from; +	int	len; +	int	tickssofar; +	byte	*buffer = skb->data; + +	/* +	 * If some higher layer thinks we've missed a +	 * tx-done interrupt we are passed NULL. +	 * Caution: dev_tint() handles the cli()/sti() itself. +	 */ + +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	if (free_tx_pages <= 0) {	/* Do timeouts, to avoid hangs. */ +		tickssofar = jiffies - dev->trans_start; + +		if (tickssofar < 5) +			return 1; + +		/* else */ +		printk("%s: transmit timed out (%d), %s?\n", +			dev->name, +			tickssofar, +			"network cable problem" +			); +		/* Restart the adapter. */ +		if (adapter_init(dev)) { +			return 1; +		} +	} + +	/* Start real output */ +	PRINTK(("de600_start_xmit:len=%d, page %d/%d\n", skb->len, tx_fifo_in, free_tx_pages)); + +	if ((len = skb->len) < RUNT) +		len = RUNT; + +	cli(); +	select_nic(); +	tx_fifo[tx_fifo_in] = transmit_from = tx_page_adr(tx_fifo_in) - len; +	tx_fifo_in = (tx_fifo_in + 1) % TX_PAGES; /* Next free tx page */ + +#ifdef CHECK_LOST_DE600 +	/* This costs about 40 instructions per packet... */ +	de600_setup_address(NODE_ADDRESS, RW_ADDR); +	de600_read_byte(READ_DATA, dev); +	if (was_down || (de600_read_byte(READ_DATA, dev) != 0xde)) { +		if (adapter_init(dev)) { +			sti(); +			return 1; +		} +	} +#endif + +	de600_setup_address(transmit_from, RW_ADDR); +	for ( ; len > 0; --len, ++buffer) +		de600_put_byte(*buffer); + +	if (free_tx_pages-- == TX_PAGES) { /* No transmission going on */ +		dev->trans_start = jiffies; +		dev->tbusy = 0;	/* allow more packets into adapter */ +		/* Send page and generate a faked interrupt */ +		de600_setup_address(transmit_from, TX_ADDR); +		de600_put_command(TX_ENABLE); +	} +	else { +		dev->tbusy = !free_tx_pages; +		select_prn(); +	} +	 +	sti(); /* interrupts back on */ +	 +#ifdef FAKE_SMALL_MAX +	/* This will "patch" the socket TCP proto at an early moment */ +	if (skb->sk && (skb->sk->protocol == IPPROTO_TCP) && +		(skb->sk->prot->rspace != &de600_rspace)) +		skb->sk->prot->rspace = de600_rspace; /* Ugh! */ +#endif + +	dev_kfree_skb (skb, FREE_WRITE); + +	return 0; +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ +static void +de600_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +	struct device	*dev = irq2dev_map[irq]; +	byte		irq_status; +	int		retrig = 0; +	int		boguscount = 0; + +	/* This might just as well be deleted now, no crummy drivers present :-) */ +	if ((dev == NULL) || (dev->start == 0) || (DE600_IRQ != irq)) { +		printk("%s: bogus interrupt %d\n", dev?dev->name:"DE-600", irq); +		return; +	} + +	dev->interrupt = 1; +	select_nic(); +	irq_status = de600_read_status(dev); + +	do { +		PRINTK(("de600_interrupt (%02X)\n", irq_status)); + +		if (irq_status & RX_GOOD) +			de600_rx_intr(dev); +		else if (!(irq_status & RX_BUSY)) +			de600_put_command(RX_ENABLE); + +		/* Any transmission in progress? */ +		if (free_tx_pages < TX_PAGES) +			retrig = de600_tx_intr(dev, irq_status); +		else +			retrig = 0; + +		irq_status = de600_read_status(dev); +	} while ( (irq_status & RX_GOOD) || ((++boguscount < 100) && retrig) ); +	/* +	 * Yeah, it _looks_ like busy waiting, smells like busy waiting +	 * and I know it's not PC, but please, it will only occur once +	 * in a while and then only for a loop or so (< 1ms for sure!) +	 */ + +	/* Enable adapter interrupts */ +	dev->interrupt = 0; +	select_prn(); + +	if (retrig) +		trigger_interrupt(dev); + +	sti(); +	return; +} + +static int +de600_tx_intr(struct device *dev, int irq_status) +{ +	/* +	 * Returns 1 if tx still not done +	 */ + +	mark_bh(NET_BH); +	/* Check if current transmission is done yet */ +	if (irq_status & TX_BUSY) +		return 1; /* tx not done, try again */ + +	/* else */ +	/* If last transmission OK then bump fifo index */ +	if (!(irq_status & TX_FAILED16)) { +		tx_fifo_out = (tx_fifo_out + 1) % TX_PAGES; +		++free_tx_pages; +		((struct netstats *)(dev->priv))->tx_packets++; +		dev->tbusy = 0; +	} + +	/* More to send, or resend last packet? */ +	if ((free_tx_pages < TX_PAGES) || (irq_status & TX_FAILED16)) { +		dev->trans_start = jiffies; +		de600_setup_address(tx_fifo[tx_fifo_out], TX_ADDR); +		de600_put_command(TX_ENABLE); +		return 1; +	} +	/* else */ + +	return 0; +} + +/* + * We have a good packet, get it out of the adapter. + */ +static void +de600_rx_intr(struct device *dev) +{ +	struct sk_buff	*skb; +	int		i; +	int		read_from; +	int		size; +	register unsigned char	*buffer; + +	cli(); +	/* Get size of received packet */ +	size = de600_read_byte(RX_LEN, dev);	/* low byte */ +	size += (de600_read_byte(RX_LEN, dev) << 8);	/* high byte */ +	size -= 4;	/* Ignore trailing 4 CRC-bytes */ + +	/* Tell adapter where to store next incoming packet, enable receiver */ +	read_from = rx_page_adr(); +	next_rx_page(); +	de600_put_command(RX_ENABLE); +	sti(); + +	if ((size < 32)  ||  (size > 1535)) { +		printk("%s: Bogus packet size %d.\n", dev->name, size); +		if (size > 10000) +			adapter_init(dev); +		return; +	} + +	skb = dev_alloc_skb(size+2); +	sti(); +	if (skb == NULL) { +		printk("%s: Couldn't allocate a sk_buff of size %d.\n", +			dev->name, size); +		return; +	} +	/* else */ + +	skb->dev = dev; +	skb_reserve(skb,2);	/* Align */ +	 +	/* 'skb->data' points to the start of sk_buff data area. */ +	buffer = skb_put(skb,size); + +	/* copy the packet into the buffer */ +	de600_setup_address(read_from, RW_ADDR); +	for (i = size; i > 0; --i, ++buffer) +		*buffer = de600_read_byte(READ_DATA, dev); +	 +	((struct netstats *)(dev->priv))->rx_packets++; /* count all receives */ + +	skb->protocol=eth_type_trans(skb,dev); +	 +	netif_rx(skb); +	/* +	 * If any worth-while packets have been received, netif_rx() +	 * has done a mark_bh(INET_BH) for us and will work on them +	 * when we get to the bottom-half routine. +	 */ +} + +int +de600_probe(struct device *dev) +{ +	int	i; +	static struct netstats de600_netstats; +	/*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/ + +	printk("%s: D-Link DE-600 pocket adapter", dev->name); +	/* Alpha testers must have the version number to report bugs. */ +	if (de600_debug > 1) +		printk("%s", version); + +	/* probe for adapter */ +	rx_page = 0; +	select_nic(); +	(void)de600_read_status(dev); +	de600_put_command(RESET); +	de600_put_command(STOP_RESET); +	if (de600_read_status(dev) & 0xf0) { +		printk(": not at I/O %#3x.\n", DATA_PORT); +		return ENODEV; +	} + +	/* +	 * Maybe we found one, +	 * have to check if it is a D-Link DE-600 adapter... +	 */ + +	/* Get the adapter ethernet address from the ROM */ +	de600_setup_address(NODE_ADDRESS, RW_ADDR); +	for (i = 0; i < ETH_ALEN; i++) { +		dev->dev_addr[i] = de600_read_byte(READ_DATA, dev); +		dev->broadcast[i] = 0xff; +	} + +	/* Check magic code */ +	if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) { +		/* OK, install real address */ +		dev->dev_addr[0] = 0x00; +		dev->dev_addr[1] = 0x80; +		dev->dev_addr[2] = 0xc8; +		dev->dev_addr[3] &= 0x0f; +		dev->dev_addr[3] |= 0x70; +	} else { +		printk(" not identified in the printer port\n"); +		return ENODEV; +	} + +#if 0 /* Not yet */ +	if (check_region(DE600_IO, 3)) { +		printk(", port 0x%x busy\n", DE600_IO); +		return EBUSY; +	} +#endif +	request_region(DE600_IO, 3, "de600"); + +	printk(", Ethernet Address: %02X", dev->dev_addr[0]); +	for (i = 1; i < ETH_ALEN; i++) +		printk(":%02X",dev->dev_addr[i]); +	printk("\n"); + +	/* Initialize the device structure. */ +	/*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/ +	dev->priv = &de600_netstats; + +	memset(dev->priv, 0, sizeof(struct netstats)); +	dev->get_stats = get_stats; + +	dev->open = de600_open; +	dev->stop = de600_close; +	dev->hard_start_xmit = &de600_start_xmit; + +	ether_setup(dev); +	 +	dev->flags&=~IFF_MULTICAST; +	 +	select_prn(); +	return 0; +} + +static int +adapter_init(struct device *dev) +{ +	int	i; +	long flags; + +	save_flags(flags); +	cli(); + +	select_nic(); +	rx_page = 0; /* used by RESET */ +	de600_put_command(RESET); +	de600_put_command(STOP_RESET); +#ifdef CHECK_LOST_DE600 +	/* Check if it is still there... */ +	/* Get the some bytes of the adapter ethernet address from the ROM */ +	de600_setup_address(NODE_ADDRESS, RW_ADDR); +	de600_read_byte(READ_DATA, dev); +	if ((de600_read_byte(READ_DATA, dev) != 0xde) || +	    (de600_read_byte(READ_DATA, dev) != 0x15)) { +	/* was: if (de600_read_status(dev) & 0xf0) { */ +		printk("Something has happened to the DE-600!  Please check it" +#ifdef SHUTDOWN_WHEN_LOST +			" and do a new ifconfig" +#endif /* SHUTDOWN_WHEN_LOST */ +			"!\n"); +#ifdef SHUTDOWN_WHEN_LOST +		/* Goodbye, cruel world... */ +		dev->flags &= ~IFF_UP; +		de600_close(dev); +#endif /* SHUTDOWN_WHEN_LOST */ +		was_down = 1; +		dev->tbusy = 1;		/* Transmit busy...  */ +		restore_flags(flags); +		return 1; /* failed */ +	} +#endif /* CHECK_LOST_DE600 */ +	if (was_down) { +		printk("Thanks, I feel much better now!\n"); +		was_down = 0; +	} + +	dev->tbusy = 0;		/* Transmit busy...  */ +	dev->interrupt = 0; +	tx_fifo_in = 0; +	tx_fifo_out = 0; +	free_tx_pages = TX_PAGES; + +	/* set the ether address. */ +	de600_setup_address(NODE_ADDRESS, RW_ADDR); +	for (i = 0; i < ETH_ALEN; i++) +		de600_put_byte(dev->dev_addr[i]); + +	/* where to start saving incoming packets */ +	rx_page = RX_BP | RX_BASE_PAGE; +	de600_setup_address(MEM_4K, RW_ADDR); +	/* Enable receiver */ +	de600_put_command(RX_ENABLE); +	select_prn(); +	restore_flags(flags); + +	return 0; /* OK */ +} + +#ifdef FAKE_SMALL_MAX +/* + *	The new router code (coming soon 8-) ) will fix this properly. + */ +#define DE600_MIN_WINDOW 1024 +#define DE600_MAX_WINDOW 2048 +#define DE600_TCP_WINDOW_DIFF 1024 +/* + * Copied from "net/inet/sock.c" + * + * Sets a lower max receive window in order to achieve <= 2 + * packets arriving at the adapter in fast succession. + * (No way that a DE-600 can keep up with a net saturated + *  with packets homing in on it :-( ) + * + * Since there are only 2 receive buffers in the DE-600 + * and it takes some time to copy from the adapter, + * this is absolutely necessary for any TCP performance whatsoever! + * + * Note that the returned window info will never be smaller than + * DE600_MIN_WINDOW, i.e. 1024 + * This differs from the standard function, that can return an + * arbitrarily small window! + */ +#define min(a,b)	((a)<(b)?(a):(b)) +static unsigned long +de600_rspace(struct sock *sk) +{ +  int amt; + +  if (sk != NULL) { +/* + * Hack! You might want to play with commenting away the following line, + * if you know what you do! +  	sk->max_unacked = DE600_MAX_WINDOW - DE600_TCP_WINDOW_DIFF; + */ + +	if (sk->rmem_alloc >= sk->rcvbuf-2*DE600_MIN_WINDOW) return(0); +	amt = min((sk->rcvbuf-sk->rmem_alloc)/2/*-DE600_MIN_WINDOW*/, DE600_MAX_WINDOW); +	if (amt < 0) return(0); +	return(amt); +  } +  return(0); +} +#endif + +#ifdef MODULE +static char nullname[8]; +static struct device de600_dev = { +	nullname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, de600_probe }; + +int +init_module(void) +{ +	if (register_netdev(&de600_dev) != 0) +		return -EIO; +	return 0; +} + +void +cleanup_module(void) +{ +	unregister_netdev(&de600_dev); +	release_region(DE600_IO, 3); +} +#endif /* MODULE */ +/* + * Local variables: + *  kernel-compile-command: "gcc -D__KERNEL__ -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c" + *  module-compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c" + *  compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c" + * End: + */ diff --git a/linux/src/drivers/net/de620.c b/linux/src/drivers/net/de620.c new file mode 100644 index 0000000..0e0c552 --- /dev/null +++ b/linux/src/drivers/net/de620.c @@ -0,0 +1,1045 @@ +/* + *	de620.c $Revision: 1.1 $ BETA + * + * + *	Linux driver for the D-Link DE-620 Ethernet pocket adapter. + * + *	Portions (C) Copyright 1993, 1994 by Bjorn Ekwall <bj0rn@blox.se> + * + *	Based on adapter information gathered from DOS packetdriver + *	sources from D-Link Inc:  (Special thanks to Henry Ngai of D-Link.) + *		Portions (C) Copyright D-Link SYSTEM Inc. 1991, 1992 + *		Copyright, 1988, Russell Nelson, Crynwr Software + * + *	Adapted to the sample network driver core for linux, + *	written by: Donald Becker <becker@super.org> + *		(Now at <becker@cesdis.gsfc.nasa.gov> + * + *	Valuable assistance from: + *		J. Joshua Kopper <kopper@rtsg.mot.com> + *		Olav Kvittem <Olav.Kvittem@uninett.no> + *		Germano Caronni <caronni@nessie.cs.id.ethz.ch> + *		Jeremy Fitzhardinge <jeremy@suite.sw.oz.au> + * + *****************************************************************************/ +/* + *	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, 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. + * + *	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., 675 Mass Ave, Cambridge, MA 02139, USA.  + * + *****************************************************************************/ +static const char *version = +	"de620.c: $Revision: 1.1 $,  Bjorn Ekwall <bj0rn@blox.se>\n"; + +/*********************************************************************** + * + * "Tuning" section. + * + * Compile-time options: (see below for descriptions) + * -DDE620_IO=0x378	(lpt1) + * -DDE620_IRQ=7	(lpt1) + * -DDE602_DEBUG=... + * -DSHUTDOWN_WHEN_LOST + * -DCOUNT_LOOPS + * -DLOWSPEED + * -DREAD_DELAY + * -DWRITE_DELAY + */ + +/* + * This driver assumes that the printer port is a "normal", + * dumb, uni-directional port! + * If your port is "fancy" in any way, please try to set it to "normal" + * with your BIOS setup.  I have no access to machines with bi-directional + * ports, so I can't test such a driver :-( + * (Yes, I _know_ it is possible to use DE620 with bidirectional ports...) + * + * There are some clones of DE620 out there, with different names. + * If the current driver does not recognize a clone, try to change + * the following #define to: + * + * #define DE620_CLONE 1 + */ +#define DE620_CLONE 0 + +/* + * If the adapter has problems with high speeds, enable this #define + * otherwise full printerport speed will be attempted. + * + * You can tune the READ_DELAY/WRITE_DELAY below if you enable LOWSPEED + * +#define LOWSPEED + */ + +#ifndef READ_DELAY +#define READ_DELAY 100	/* adapter internal read delay in 100ns units */ +#endif + +#ifndef WRITE_DELAY +#define WRITE_DELAY 100	/* adapter internal write delay in 100ns units */ +#endif + +/* + * Enable this #define if you want the adapter to do a "ifconfig down" on + * itself when we have detected that something is possibly wrong with it. + * The default behaviour is to retry with "adapter_init()" until success. + * This should be used for debugging purposes only. + * +#define SHUTDOWN_WHEN_LOST + */ + +/* + * Enable debugging by "-DDE620_DEBUG=3" when compiling, + * OR in "./CONFIG" + * OR by enabling the following #define + * + * use 0 for production, 1 for verification, >2 for debug + * +#define DE620_DEBUG 3 + */ + +#ifdef LOWSPEED +/* + * Enable this #define if you want to see debugging output that show how long + * we have to wait before the DE-620 is ready for the next read/write/command. + * +#define COUNT_LOOPS + */ +#endif + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <linux/in.h> +#include <linux/ptrace.h> +#include <asm/system.h> +#include <linux/errno.h> + +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +/* Constant definitions for the DE-620 registers, commands and bits */ +#include "de620.h" + +#define netstats enet_statistics +typedef unsigned char byte; + +/******************************************************* + *                                                     * + * Definition of D-Link DE-620 Ethernet Pocket adapter * + * See also "de620.h"                                  * + *                                                     * + *******************************************************/ +#ifndef DE620_IO /* Compile-time configurable */ +#define DE620_IO 0x378 +#endif + +#ifndef DE620_IRQ /* Compile-time configurable */ +#define DE620_IRQ	7 +#endif + +#define DATA_PORT	(dev->base_addr) +#define STATUS_PORT	(dev->base_addr + 1) +#define COMMAND_PORT	(dev->base_addr + 2) + +#define RUNT 60		/* Too small Ethernet packet */ +#define GIANT 1514	/* largest legal size packet, no fcs */ + +#ifdef DE620_DEBUG /* Compile-time configurable */ +#define PRINTK(x) if (de620_debug >= 2) printk x +#else +#define DE620_DEBUG 0 +#define PRINTK(x) /**/ +#endif + + +/* + * Force media with insmod: + *	insmod de620.o bnc=1 + * or + *	insmod de620.o utp=1 + * + * Force io and/or irq with insmod: + *	insmod de620.o io=0x378 irq=7 + * + * Make a clone skip the Ethernet-address range check: + *	insmod de620.o clone=1 + */ +static int bnc = 0; +static int utp = 0; +static int io  = DE620_IO; +static int irq = DE620_IRQ; +static int clone = DE620_CLONE; + +static unsigned int de620_debug = DE620_DEBUG; + +/*********************************************** + *                                             * + * Index to functions, as function prototypes. * + *                                             * + ***********************************************/ + +/* + * Routines used internally. (See also "convenience macros.. below") + */ + +/* Put in the device structure. */ +static int	de620_open(struct device *); +static int	de620_close(struct device *); +static struct netstats *get_stats(struct device *); +static void	de620_set_multicast_list(struct device *); +static int	de620_start_xmit(struct sk_buff *, struct device *); + +/* Dispatch from interrupts. */ +static void	de620_interrupt(int, void *, struct pt_regs *); +static int	de620_rx_intr(struct device *); + +/* Initialization */ +static int	adapter_init(struct device *); +int		de620_probe(struct device *); +static int	read_eeprom(struct device *); + + +/* + * D-Link driver variables: + */ +#define SCR_DEF NIBBLEMODE |INTON | SLEEP | AUTOTX +#define	TCR_DEF RXPB			/* not used: | TXSUCINT | T16INT */ +#define DE620_RX_START_PAGE 12		/* 12 pages (=3k) reserved for tx */ +#define DEF_NIC_CMD IRQEN | ICEN | DS1 + +static volatile byte	NIC_Cmd; +static volatile byte	next_rx_page; +static byte		first_rx_page; +static byte		last_rx_page; +static byte		EIPRegister; + +static struct nic { +	byte	NodeID[6]; +	byte	RAM_Size; +	byte	Model; +	byte	Media; +	byte	SCR; +} nic_data; + +/********************************************************** + *                                                        * + * Convenience macros/functions for D-Link DE-620 adapter * + *                                                        * + **********************************************************/ +#define de620_tx_buffs(dd) (inb(STATUS_PORT) & (TXBF0 | TXBF1)) +#define de620_flip_ds(dd) NIC_Cmd ^= DS0 | DS1; outb(NIC_Cmd, COMMAND_PORT); + +/* Check for ready-status, and return a nibble (high 4 bits) for data input */ +#ifdef COUNT_LOOPS +static int tot_cnt; +#endif +static inline byte +de620_ready(struct device *dev) +{ +	byte value; +	register short int cnt = 0; + +	while ((((value = inb(STATUS_PORT)) & READY) == 0) && (cnt <= 1000)) +		++cnt; + +#ifdef COUNT_LOOPS +	tot_cnt += cnt; +#endif +	return value & 0xf0; /* nibble */ +} + +static inline void +de620_send_command(struct device *dev, byte cmd) +{ +	de620_ready(dev); +	if (cmd == W_DUMMY) +		outb(NIC_Cmd, COMMAND_PORT); + +	outb(cmd, DATA_PORT); + +	outb(NIC_Cmd ^ CS0, COMMAND_PORT); +	de620_ready(dev); +	outb(NIC_Cmd, COMMAND_PORT); +} + +static inline void +de620_put_byte(struct device *dev, byte value) +{ +	/* The de620_ready() makes 7 loops, on the average, on a DX2/66 */ +	de620_ready(dev); +	outb(value, DATA_PORT); +	de620_flip_ds(dev); +} + +static inline byte +de620_read_byte(struct device *dev) +{ +	byte value; + +	/* The de620_ready() makes 7 loops, on the average, on a DX2/66 */ +	value = de620_ready(dev); /* High nibble */ +	de620_flip_ds(dev); +	value |= de620_ready(dev) >> 4; /* Low nibble */ +	return value; +} + +static inline void +de620_write_block(struct device *dev, byte *buffer, int count) +{ +#ifndef LOWSPEED +	byte uflip = NIC_Cmd ^ (DS0 | DS1); +	byte dflip = NIC_Cmd; +#else /* LOWSPEED */ +#ifdef COUNT_LOOPS +	int bytes = count; +#endif /* COUNT_LOOPS */ +#endif /* LOWSPEED */ + +#ifdef LOWSPEED +#ifdef COUNT_LOOPS +	tot_cnt = 0; +#endif /* COUNT_LOOPS */ +	/* No further optimization useful, the limit is in the adapter. */ +	for ( ; count > 0; --count, ++buffer) { +		de620_put_byte(dev,*buffer); +	} +	de620_send_command(dev,W_DUMMY); +#ifdef COUNT_LOOPS +	/* trial debug output: loops per byte in de620_ready() */ +	printk("WRITE(%d)\n", tot_cnt/((bytes?bytes:1))); +#endif /* COUNT_LOOPS */ +#else /* not LOWSPEED */ +	for ( ; count > 0; count -=2) { +		outb(*buffer++, DATA_PORT); +		outb(uflip, COMMAND_PORT); +		outb(*buffer++, DATA_PORT); +		outb(dflip, COMMAND_PORT); +	} +	de620_send_command(dev,W_DUMMY); +#endif /* LOWSPEED */ +} + +static inline void +de620_read_block(struct device *dev, byte *data, int count) +{ +#ifndef LOWSPEED +	byte value; +	byte uflip = NIC_Cmd ^ (DS0 | DS1); +	byte dflip = NIC_Cmd; +#else /* LOWSPEED */ +#ifdef COUNT_LOOPS +	int bytes = count; + +	tot_cnt = 0; +#endif /* COUNT_LOOPS */ +#endif /* LOWSPEED */ + +#ifdef LOWSPEED +	/* No further optimization useful, the limit is in the adapter. */ +	while (count-- > 0) { +		*data++ = de620_read_byte(dev); +		de620_flip_ds(dev); +	} +#ifdef COUNT_LOOPS +	/* trial debug output: loops per byte in de620_ready() */ +	printk("READ(%d)\n", tot_cnt/(2*(bytes?bytes:1))); +#endif /* COUNT_LOOPS */ +#else /* not LOWSPEED */ +	while (count-- > 0) { +		value = inb(STATUS_PORT) & 0xf0; /* High nibble */ +		outb(uflip, COMMAND_PORT); +		*data++ = value | inb(STATUS_PORT) >> 4; /* Low nibble */ +		outb(dflip , COMMAND_PORT); +	} +#endif /* LOWSPEED */ +} + +static inline void +de620_set_delay(struct device *dev) +{ +	de620_ready(dev); +	outb(W_DFR, DATA_PORT); +	outb(NIC_Cmd ^ CS0, COMMAND_PORT); + +	de620_ready(dev); +#ifdef LOWSPEED +	outb(WRITE_DELAY, DATA_PORT); +#else +	outb(0, DATA_PORT); +#endif +	de620_flip_ds(dev); + +	de620_ready(dev); +#ifdef LOWSPEED +	outb(READ_DELAY, DATA_PORT); +#else +	outb(0, DATA_PORT); +#endif +	de620_flip_ds(dev); +} + +static inline void +de620_set_register(struct device *dev, byte reg, byte value) +{ +	de620_ready(dev); +	outb(reg, DATA_PORT); +	outb(NIC_Cmd ^ CS0, COMMAND_PORT); + +	de620_put_byte(dev, value); +} + +static inline byte +de620_get_register(struct device *dev, byte reg) +{ +	byte value; + +	de620_send_command(dev,reg); +	value = de620_read_byte(dev); +	de620_send_command(dev,W_DUMMY); + +	return value; +} + +/********************************************************************* + * + * Open/initialize the board. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is a non-reboot way to recover if something goes wrong. + * + */ +static int +de620_open(struct device *dev) +{ +	if (request_irq(dev->irq, de620_interrupt, 0, "de620", NULL)) { +		printk ("%s: unable to get IRQ %d\n", dev->name, dev->irq); +		return 1; +	} +	irq2dev_map[dev->irq] = dev; + +	MOD_INC_USE_COUNT; +	if (adapter_init(dev)) { +		return 1; +	} +	dev->start = 1; +	return 0; +} + +/************************************************ + * + * The inverse routine to de620_open(). + * + */ +static int +de620_close(struct device *dev) +{ +	/* disable recv */ +	de620_set_register(dev, W_TCR, RXOFF); + +	free_irq(dev->irq, NULL); +	irq2dev_map[dev->irq] = NULL; + +	dev->start = 0; +	MOD_DEC_USE_COUNT; +	return 0; +} + +/********************************************* + * + * Return current statistics + * + */ +static struct netstats * +get_stats(struct device *dev) +{ +	return (struct netstats *)(dev->priv); +} + +/********************************************* + * + * Set or clear the multicast filter for this adaptor. + * (no real multicast implemented for the DE-620, but she can be promiscuous...) + * + */ + +static void de620_set_multicast_list(struct device *dev) +{ +	if (dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC))  +	{ /* Enable promiscuous mode */ +		/* +		 *	We must make the kernel realise we had to move +		 *	into promisc mode or we start all out war on +		 *	the cable. - AC +		 */ +		dev->flags|=IFF_PROMISC;		 +	 +		de620_set_register(dev, W_TCR, (TCR_DEF & ~RXPBM) | RXALL); +	} +	else  +	{ /* Disable promiscuous mode, use normal mode */ +		de620_set_register(dev, W_TCR, TCR_DEF); +	} +} + +/******************************************************* + * + * Copy a buffer to the adapter transmit page memory. + * Start sending. + */ +static int +de620_start_xmit(struct sk_buff *skb, struct device *dev) +{ +	unsigned long flags; +	int len; +	int tickssofar; +	byte *buffer = skb->data; +	byte using_txbuf; + +	/* +	 * If some higher layer thinks we've missed a +	 * tx-done interrupt we are passed NULL. +	 * Caution: dev_tint() handles the cli()/sti() itself. +	 */ + +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	using_txbuf = de620_tx_buffs(dev); /* Peek at the adapter */ +	dev->tbusy = (using_txbuf == (TXBF0 | TXBF1)); /* Boolean! */ + +	if (dev->tbusy) {	/* Do timeouts, to avoid hangs. */ +		tickssofar = jiffies - dev->trans_start; + +		if (tickssofar < 5) +			return 1; + +		/* else */ +		printk("%s: transmit timed out (%d), %s?\n", +			dev->name, +			tickssofar, +			"network cable problem" +			); +		/* Restart the adapter. */ +		if (adapter_init(dev)) /* maybe close it */ +			return 1; +	} + +	if ((len = skb->len) < RUNT) +		len = RUNT; +	if (len & 1) /* send an even number of bytes */ +		++len; + +	/* Start real output */ +	save_flags(flags); +	cli(); + +	PRINTK(("de620_start_xmit: len=%d, bufs 0x%02x\n", +		(int)skb->len, using_txbuf)); + +	/* select a free tx buffer. if there is one... */ +	switch (using_txbuf) { +	default: /* both are free: use TXBF0 */ +	case TXBF1: /* use TXBF0 */ +		de620_send_command(dev,W_CR | RW0); +		using_txbuf |= TXBF0; +		break; + +	case TXBF0: /* use TXBF1 */ +		de620_send_command(dev,W_CR | RW1); +		using_txbuf |= TXBF1; +		break; + +	case (TXBF0 | TXBF1): /* NONE!!! */ +		printk("de620: Ouch! No tx-buffer available!\n"); +		restore_flags(flags); +		return 1; +		break; +	} +	de620_write_block(dev, buffer, len); + +	dev->trans_start = jiffies; +	dev->tbusy = (using_txbuf == (TXBF0 | TXBF1)); /* Boolean! */ + +	((struct netstats *)(dev->priv))->tx_packets++; +	 +	restore_flags(flags); /* interrupts maybe back on */ +	 +	dev_kfree_skb (skb, FREE_WRITE); + +	return 0; +} + +/***************************************************** + * + * Handle the network interface interrupts. + * + */ +static void +de620_interrupt(int irq_in, void *dev_id, struct pt_regs *regs) +{ +	struct device *dev = irq2dev_map[irq_in]; +	byte irq_status; +	int bogus_count = 0; +	int again = 0; + +	/* This might be deleted now, no crummy drivers present :-) Or..? */ +	if ((dev == NULL) || (irq != irq_in)) { +		printk("%s: bogus interrupt %d\n", dev?dev->name:"de620", irq_in); +		return; +	} + +	cli(); +	dev->interrupt = 1; + +	/* Read the status register (_not_ the status port) */ +	irq_status = de620_get_register(dev, R_STS); + +	PRINTK(("de620_interrupt (%2.2X)\n", irq_status)); + +	if (irq_status & RXGOOD) { +		do { +			again = de620_rx_intr(dev); +			PRINTK(("again=%d\n", again)); +		} +		while (again && (++bogus_count < 100)); +	} + +	dev->tbusy = (de620_tx_buffs(dev) == (TXBF0 | TXBF1)); /* Boolean! */ + +	dev->interrupt = 0; +	sti(); +	return; +} + +/************************************** + * + * Get a packet from the adapter + * + * Send it "upstairs" + * + */ +static int +de620_rx_intr(struct device *dev) +{ +	struct header_buf { +		byte		status; +		byte		Rx_NextPage; +		unsigned short	Rx_ByteCount; +	} header_buf; +	struct sk_buff *skb; +	int size; +	byte *buffer; +	byte pagelink; +	byte curr_page; + +	PRINTK(("de620_rx_intr: next_rx_page = %d\n", next_rx_page)); + +	/* Tell the adapter that we are going to read data, and from where */ +	de620_send_command(dev, W_CR | RRN); +	de620_set_register(dev, W_RSA1, next_rx_page); +	de620_set_register(dev, W_RSA0, 0); + +	/* Deep breath, and away we goooooo */ +	de620_read_block(dev, (byte *)&header_buf, sizeof(struct header_buf)); +	PRINTK(("page status=0x%02x, nextpage=%d, packetsize=%d\n", +	header_buf.status, header_buf.Rx_NextPage, header_buf.Rx_ByteCount)); + +	/* Plausible page header? */ +	pagelink = header_buf.Rx_NextPage; +	if ((pagelink < first_rx_page) || (last_rx_page < pagelink)) { +		/* Ouch... Forget it! Skip all and start afresh... */ +		printk("%s: Ring overrun? Restoring...\n", dev->name); +		/* You win some, you loose some. And sometimes plenty... */ +		adapter_init(dev); +		((struct netstats *)(dev->priv))->rx_over_errors++; +		return 0; +	} + +	/* OK, this look good, so far. Let's see if it's consistent... */ +	/* Let's compute the start of the next packet, based on where we are */ +	pagelink = next_rx_page + +		((header_buf.Rx_ByteCount + (4 - 1 + 0x100)) >> 8); + +	/* Are we going to wrap around the page counter? */ +	if (pagelink > last_rx_page) +		pagelink -= (last_rx_page - first_rx_page + 1); + +	/* Is the _computed_ next page number equal to what the adapter says? */ +	if (pagelink != header_buf.Rx_NextPage) { +		/* Naah, we'll skip this packet. Probably bogus data as well */ +		printk("%s: Page link out of sync! Restoring...\n", dev->name); +		next_rx_page = header_buf.Rx_NextPage; /* at least a try... */ +		de620_send_command(dev, W_DUMMY); +		de620_set_register(dev, W_NPRF, next_rx_page); +		((struct netstats *)(dev->priv))->rx_over_errors++; +		return 0; +	} +	next_rx_page = pagelink; + +	size = header_buf.Rx_ByteCount - 4; +	if ((size < RUNT) || (GIANT < size)) { +		printk("%s: Illegal packet size: %d!\n", dev->name, size); +	} +	else { /* Good packet? */ +		skb = dev_alloc_skb(size+2); +		if (skb == NULL) { /* Yeah, but no place to put it... */ +			printk("%s: Couldn't allocate a sk_buff of size %d.\n", +				dev->name, size); +			((struct netstats *)(dev->priv))->rx_dropped++; +		} +		else { /* Yep! Go get it! */ +			skb_reserve(skb,2);	/* Align */ +			skb->dev = dev; +			skb->free = 1; +			/* skb->data points to the start of sk_buff data area */ +			buffer = skb_put(skb,size); +			/* copy the packet into the buffer */ +			de620_read_block(dev, buffer, size); +			PRINTK(("Read %d bytes\n", size)); +			skb->protocol=eth_type_trans(skb,dev); +			netif_rx(skb); /* deliver it "upstairs" */ +			/* count all receives */ +			((struct netstats *)(dev->priv))->rx_packets++; +		} +	} + +	/* Let's peek ahead to see if we have read the last current packet */ +	/* NOTE! We're _not_ checking the 'EMPTY'-flag! This seems better... */ +	curr_page = de620_get_register(dev, R_CPR); +	de620_set_register(dev, W_NPRF, next_rx_page); +	PRINTK(("next_rx_page=%d CPR=%d\n", next_rx_page, curr_page)); + +	return (next_rx_page != curr_page); /* That was slightly tricky... */ +} + +/********************************************* + * + * Reset the adapter to a known state + * + */ +static int +adapter_init(struct device *dev) +{ +	int i; +	static int was_down = 0; + +	if ((nic_data.Model == 3) || (nic_data.Model == 0)) { /* CT */ +		EIPRegister = NCTL0; +		if (nic_data.Media != 1) +			EIPRegister |= NIS0;	/* not BNC */ +	} +	else if (nic_data.Model == 2) { /* UTP */ +		EIPRegister = NCTL0 | NIS0; +	} + +	if (utp) +		EIPRegister = NCTL0 | NIS0; +	if (bnc) +		EIPRegister = NCTL0; + +	de620_send_command(dev, W_CR | RNOP | CLEAR); +	de620_send_command(dev, W_CR | RNOP); + +	de620_set_register(dev, W_SCR, SCR_DEF); +	/* disable recv to wait init */ +	de620_set_register(dev, W_TCR, RXOFF); + +	/* Set the node ID in the adapter */ +	for (i = 0; i < 6; ++i) { /* W_PARn = 0xaa + n */ +		de620_set_register(dev, W_PAR0 + i, dev->dev_addr[i]); +	} + +	de620_set_register(dev, W_EIP, EIPRegister); + +	next_rx_page = first_rx_page = DE620_RX_START_PAGE; +	if (nic_data.RAM_Size) +		last_rx_page = nic_data.RAM_Size - 1; +	else /* 64k RAM */ +		last_rx_page = 255; + +	de620_set_register(dev, W_SPR, first_rx_page); /* Start Page Register*/ +	de620_set_register(dev, W_EPR, last_rx_page);  /* End Page Register */ +	de620_set_register(dev, W_CPR, first_rx_page);/*Current Page Register*/ +	de620_send_command(dev, W_NPR | first_rx_page); /* Next Page Register*/ +	de620_send_command(dev, W_DUMMY); +	de620_set_delay(dev); + +	/* Final sanity check: Anybody out there? */ +	/* Let's hope some bits from the statusregister make a good check */ +#define CHECK_MASK (  0 | TXSUC |  T16  |  0  | RXCRC | RXSHORT |  0  |  0  ) +#define CHECK_OK   (  0 |   0   |  0    |  0  |   0   |   0     |  0  |  0  ) +        /* success:   X     0      0       X      0       0        X     X  */ +        /* ignore:   EEDI                RXGOOD                   COLS  LNKS*/ + +	if (((i = de620_get_register(dev, R_STS)) & CHECK_MASK) != CHECK_OK) { +		printk("Something has happened to the DE-620!  Please check it" +#ifdef SHUTDOWN_WHEN_LOST +			" and do a new ifconfig" +#endif +			"! (%02x)\n", i); +#ifdef SHUTDOWN_WHEN_LOST +		/* Goodbye, cruel world... */ +		dev->flags &= ~IFF_UP; +		de620_close(dev); +#endif +		was_down = 1; +		return 1; /* failed */ +	} +	if (was_down) { +		printk("Thanks, I feel much better now!\n"); +		was_down = 0; +	} + +	/* All OK, go ahead... */ +	de620_set_register(dev, W_TCR, TCR_DEF); + +	return 0; /* all ok */ +} + +/****************************************************************************** + * + * Only start-up code below + * + */ +/**************************************** + * + * Check if there is a DE-620 connected + */ +int +de620_probe(struct device *dev) +{ +	static struct netstats de620_netstats; +	int i; +	byte checkbyte = 0xa5; + +	/* +	 * This is where the base_addr and irq gets set. +	 * Tunable at compile-time and insmod-time +	 */ +	dev->base_addr = io; +	dev->irq       = irq; + +	if (de620_debug) +		printk("%s", version); + +	printk("D-Link DE-620 pocket adapter"); + +	/* Initially, configure basic nibble mode, so we can read the EEPROM */ +	NIC_Cmd = DEF_NIC_CMD; +	de620_set_register(dev, W_EIP, EIPRegister); + +	/* Anybody out there? */ +	de620_set_register(dev, W_CPR, checkbyte); +	checkbyte = de620_get_register(dev, R_CPR); + +	if ((checkbyte != 0xa5) || (read_eeprom(dev) != 0)) { +		printk(" not identified in the printer port\n"); +		return ENODEV; +	} + +#if 0 /* Not yet */ +	if (check_region(dev->base_addr, 3)) { +		printk(", port 0x%x busy\n", dev->base_addr); +		return EBUSY; +	} +#endif +	request_region(dev->base_addr, 3, "de620"); + +	/* else, got it! */ +	printk(", Ethernet Address: %2.2X", +		dev->dev_addr[0] = nic_data.NodeID[0]); +	for (i = 1; i < ETH_ALEN; i++) { +		printk(":%2.2X", dev->dev_addr[i] = nic_data.NodeID[i]); +		dev->broadcast[i] = 0xff; +	} + +	printk(" (%dk RAM,", +		(nic_data.RAM_Size) ? (nic_data.RAM_Size >> 2) : 64); + +	if (nic_data.Media == 1) +		printk(" BNC)\n"); +	else +		printk(" UTP)\n"); + +	/* Initialize the device structure. */ +	/*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/ +	dev->priv = &de620_netstats; + +	memset(dev->priv, 0, sizeof(struct netstats)); +	dev->get_stats = get_stats; +	dev->open = de620_open; +	dev->stop = de620_close; +	dev->hard_start_xmit = &de620_start_xmit; +	dev->set_multicast_list = &de620_set_multicast_list; +	/* base_addr and irq are already set, see above! */ + +	ether_setup(dev); +	 +	/* dump eeprom */ +	if (de620_debug) { +		printk("\nEEPROM contents:\n"); +		printk("RAM_Size = 0x%02X\n", nic_data.RAM_Size); +		printk("NodeID = %02X:%02X:%02X:%02X:%02X:%02X\n", +			nic_data.NodeID[0], nic_data.NodeID[1], +			nic_data.NodeID[2], nic_data.NodeID[3], +			nic_data.NodeID[4], nic_data.NodeID[5]); +		printk("Model = %d\n", nic_data.Model); +		printk("Media = %d\n", nic_data.Media); +		printk("SCR = 0x%02x\n", nic_data.SCR); +	} + +	return 0; +} + +/********************************** + * + * Read info from on-board EEPROM + * + * Note: Bitwise serial I/O to/from the EEPROM vi the status _register_! + */ +#define sendit(dev,data) de620_set_register(dev, W_EIP, data | EIPRegister); + +static unsigned short +ReadAWord(struct device *dev, int from) +{ +	unsigned short data; +	int nbits; + +	/* cs   [__~~] SET SEND STATE */ +	/* di   [____]                */ +	/* sck  [_~~_]                */ +	sendit(dev, 0); sendit(dev, 1); sendit(dev, 5); sendit(dev, 4); + +	/* Send the 9-bit address from where we want to read the 16-bit word */ +	for (nbits = 9; nbits > 0; --nbits, from <<= 1) { +		if (from & 0x0100) { /* bit set? */ +			/* cs    [~~~~] SEND 1 */ +			/* di    [~~~~]        */ +			/* sck   [_~~_]        */ +			sendit(dev, 6); sendit(dev, 7); sendit(dev, 7); sendit(dev, 6); +		} +		else { +			/* cs    [~~~~] SEND 0 */ +			/* di    [____]        */ +			/* sck   [_~~_]        */ +			sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4); +		} +	} + +	/* Shift in the 16-bit word. The bits appear serially in EEDI (=0x80) */ +	for (data = 0, nbits = 16; nbits > 0; --nbits) { +		/* cs    [~~~~] SEND 0 */ +		/* di    [____]        */ +		/* sck   [_~~_]        */ +		sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4); +		data = (data << 1) | ((de620_get_register(dev, R_STS) & EEDI) >> 7); +	} +	/* cs    [____] RESET SEND STATE */ +	/* di    [____]                  */ +	/* sck   [_~~_]                  */ +	sendit(dev, 0); sendit(dev, 1); sendit(dev, 1); sendit(dev, 0); + +	return data; +} + +static int +read_eeprom(struct device *dev) +{ +	unsigned short wrd; + +	/* D-Link Ethernet addresses are in the series  00:80:c8:7X:XX:XX:XX */ +	wrd = ReadAWord(dev, 0x1aa);	/* bytes 0 + 1 of NodeID */ +	if (!clone && (wrd != htons(0x0080))) /* Valid D-Link ether sequence? */ +		return -1; /* Nope, not a DE-620 */ +	nic_data.NodeID[0] = wrd & 0xff; +	nic_data.NodeID[1] = wrd >> 8; + +	wrd = ReadAWord(dev, 0x1ab);	/* bytes 2 + 3 of NodeID */ +	if (!clone && ((wrd & 0xff) != 0xc8)) /* Valid D-Link ether sequence? */ +		return -1; /* Nope, not a DE-620 */ +	nic_data.NodeID[2] = wrd & 0xff; +	nic_data.NodeID[3] = wrd >> 8; + +	wrd = ReadAWord(dev, 0x1ac);	/* bytes 4 + 5 of NodeID */ +	nic_data.NodeID[4] = wrd & 0xff; +	nic_data.NodeID[5] = wrd >> 8; + +	wrd = ReadAWord(dev, 0x1ad);	/* RAM size in pages (256 bytes). 0 = 64k */ +	nic_data.RAM_Size = (wrd >> 8); + +	wrd = ReadAWord(dev, 0x1ae);	/* hardware model (CT = 3) */ +	nic_data.Model = (wrd & 0xff); + +	wrd = ReadAWord(dev, 0x1af); /* media (indicates BNC/UTP) */ +	nic_data.Media = (wrd & 0xff); + +	wrd = ReadAWord(dev, 0x1a8); /* System Configuration Register */ +	nic_data.SCR = (wrd >> 8); + +	return 0; /* no errors */ +} + +/****************************************************************************** + * + * Loadable module skeleton + * + */ +#ifdef MODULE +static char nullname[8] = ""; +static struct device de620_dev = { +	nullname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, de620_probe }; + +int +init_module(void) +{ +	if (register_netdev(&de620_dev) != 0) +		return -EIO; +	return 0; +} + +void +cleanup_module(void) +{ +	unregister_netdev(&de620_dev); +	release_region(de620_dev.base_addr, 3); +} +#endif /* MODULE */ + +/* + * (add '-DMODULE' when compiling as loadable module) + * + * compile-command: + *	gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O2 \ + *	 -fomit-frame-pointer -m486 \ + *	-I/usr/src/linux/include -I../../net/inet -c de620.c +*/ +/* + * Local variables: + *  kernel-compile-command: "gcc -D__KERNEL__ -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c" + *  module-compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c" + *  compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c" + * End: + */ diff --git a/linux/src/drivers/net/de620.h b/linux/src/drivers/net/de620.h new file mode 100644 index 0000000..e8d9a88 --- /dev/null +++ b/linux/src/drivers/net/de620.h @@ -0,0 +1,117 @@ +/********************************************************* + *                                                       * + * Definition of D-Link DE-620 Ethernet Pocket adapter   * + *                                                       * + *********************************************************/ + +/* DE-620's CMD port Command */ +#define CS0		0x08	/* 1->0 command strobe */ +#define ICEN		0x04	/* 0=enable DL3520 host interface */ +#define DS0		0x02	/* 1->0 data strobe 0 */ +#define DS1		0x01	/* 1->0 data strobe 1 */ + +#define WDIR		0x20	/* general 0=read  1=write */ +#define RDIR		0x00	/*  (not 100% confirm ) */ +#define PS2WDIR		0x00	/* ps/2 mode 1=read, 0=write */ +#define PS2RDIR		0x20 + +#define IRQEN		0x10	/* 1 = enable printer IRQ line */ +#define SELECTIN	0x08	/* 1 = select printer */ +#define INITP		0x04	/* 0 = initial printer */ +#define AUTOFEED	0x02	/* 1 = printer auto form feed */ +#define STROBE		0x01	/* 0->1 data strobe */ + +#define RESET		0x08 +#define NIS0		0x20	/* 0 = BNC, 1 = UTP */ +#define NCTL0		0x10 + +/* DE-620 DIC Command */ +#define W_DUMMY		0x00	/* DIC reserved command */ +#define W_CR		0x20	/* DIC write command register */ +#define W_NPR		0x40	/* DIC write Next Page Register */ +#define W_TBR		0x60	/* DIC write Tx Byte Count 1 reg */ +#define W_RSA		0x80	/* DIC write Remote Start Addr 1 */ + +/* DE-620's STAT port bits 7-4 */ +#define EMPTY		0x80	/* 1 = receive buffer empty */ +#define INTLEVEL	0x40	/* 1 = interrupt level is high */ +#define TXBF1		0x20	/* 1 = transmit buffer 1 is in use */ +#define TXBF0		0x10	/* 1 = transmit buffer 0 is in use */ +#define READY		0x08	/* 1 = h/w ready to accept cmd/data */ + +/* IDC 1 Command */ +#define	W_RSA1		0xa0	/* write remote start address 1 */ +#define	W_RSA0		0xa1	/* write remote start address 0 */ +#define	W_NPRF		0xa2	/* write next page register NPR15-NPR8 */ +#define	W_DFR		0xa3	/* write delay factor register */ +#define	W_CPR		0xa4	/* write current page register */ +#define	W_SPR		0xa5	/* write start page register */ +#define	W_EPR		0xa6	/* write end page register */ +#define	W_SCR		0xa7	/* write system configuration register */ +#define	W_TCR		0xa8	/* write Transceiver Configuration reg */ +#define	W_EIP		0xa9	/* write EEPM Interface port */ +#define	W_PAR0		0xaa	/* write physical address register 0 */ +#define	W_PAR1		0xab	/* write physical address register 1 */ +#define	W_PAR2		0xac	/* write physical address register 2 */ +#define	W_PAR3		0xad	/* write physical address register 3 */ +#define	W_PAR4		0xae	/* write physical address register 4 */ +#define	W_PAR5		0xaf	/* write physical address register 5 */ + +/* IDC 2 Command */ +#define	R_STS		0xc0	/* read status register */ +#define	R_CPR		0xc1	/* read current page register */ +#define	R_BPR		0xc2	/* read boundary page register */ +#define	R_TDR		0xc3	/* read time domain reflectometry reg */ + +/* STATUS Register */ +#define EEDI		0x80	/* EEPM DO pin */ +#define TXSUC		0x40	/* tx success */ +#define T16		0x20	/* tx fail 16 times */ +#define TS1		0x40	/* 0=Tx success, 1=T16 */ +#define TS0		0x20	/* 0=Tx success, 1=T16 */ +#define RXGOOD		0x10	/* rx a good packet */ +#define RXCRC		0x08	/* rx a CRC error packet */ +#define RXSHORT		0x04	/* rx a short packet */ +#define COLS		0x02	/* coaxial collision status */ +#define LNKS		0x01	/* UTP link status */ + +/* Command Register */ +#define CLEAR		0x10	/* reset part of hardware */ +#define NOPER		0x08	/* No Operation */ +#define RNOP		0x08 +#define RRA		0x06	/* After RR then auto-advance NPR & BPR(=NPR-1) */ +#define RRN		0x04	/* Normal Remote Read mode */ +#define RW1		0x02	/* Remote Write tx buffer 1  ( page 6 - 11 ) */ +#define RW0		0x00	/* Remote Write tx buffer 0  ( page 0 - 5 ) */ +#define TXEN		0x01	/* 0->1 tx enable */ + +/* System Configuration Register */ +#define TESTON		0x80	/* test host data transfer reliability */ +#define SLEEP		0x40	/* sleep mode */ +#if 0 +#define FASTMODE	0x04	/* fast mode for intel 82360SL fast mode */ +#define BYTEMODE	0x02	/* byte mode */ +#else +#define FASTMODE	0x20	/* fast mode for intel 82360SL fast mode */ +#define BYTEMODE	0x10	/* byte mode */ +#endif +#define NIBBLEMODE	0x00	/* nibble mode */ +#define IRQINV		0x08	/* turn off IRQ line inverter */ +#define IRQNML		0x00	/* turn on IRQ line inverter */ +#define INTON		0x04 +#define AUTOFFSET	0x02	/* auto shift address to TPR+12 */ +#define AUTOTX		0x01	/* auto tx when leave RW mode */ + +/* Transceiver Configuration Register */ +#define JABBER		0x80	/* generate jabber condition */ +#define TXSUCINT	0x40	/* enable tx success interrupt */ +#define T16INT		0x20	/* enable T16 interrupt */ +#define RXERRPKT	0x10	/* accept CRC error or short packet */ +#define EXTERNALB2	0x0C	/* external loopback 2 */ +#define EXTERNALB1	0x08	/* external loopback 1 */ +#define INTERNALB	0x04	/* internal loopback */ +#define NMLOPERATE	0x00	/* normal operation */ +#define RXPBM		0x03	/* rx physical, broadcast, multicast */ +#define RXPB		0x02	/* rx physical, broadcast */ +#define RXALL		0x01	/* rx all packet */ +#define RXOFF		0x00	/* rx disable */ diff --git a/linux/src/drivers/net/depca.c b/linux/src/drivers/net/depca.c new file mode 100644 index 0000000..2048812 --- /dev/null +++ b/linux/src/drivers/net/depca.c @@ -0,0 +1,1890 @@ +/*  depca.c: A DIGITAL DEPCA  & EtherWORKS ethernet driver for linux. + +    Written 1994, 1995 by David C. Davies. + + +                      Copyright 1994 David C. Davies +		                   and  +			 United States Government +	 (as represented by the Director, National Security Agency).   + +               Copyright 1995  Digital Equipment Corporation. + + +    This software may be used and distributed according to the terms of +    the GNU Public License, incorporated herein by reference. + +    This driver is written for the Digital Equipment Corporation series +    of DEPCA and EtherWORKS ethernet cards: + +        DEPCA       (the original) +    	DE100 +    	DE101 +	DE200 Turbo +	DE201 Turbo +	DE202 Turbo (TP BNC) +	DE210 +	DE422       (EISA) + +    The  driver has been tested on DE100, DE200 and DE202 cards  in  a +    relatively busy network. The DE422 has been tested a little. + +    This  driver will NOT work   for the DE203,  DE204  and DE205 series  of +    cards,  since they have  a  new custom ASIC in   place of the AMD  LANCE +    chip.  See the 'ewrk3.c'   driver in the  Linux  source tree for running +    those cards. + +    I have benchmarked the driver with a  DE100 at 595kB/s to (542kB/s from) +    a DECstation 5000/200. + +    The author may be reached at davies@maniac.ultranet.com + +    ========================================================================= + +    The  driver was originally based  on   the 'lance.c' driver from  Donald +    Becker   which  is included with  the  standard  driver distribution for +    linux.  V0.4  is  a complete  re-write  with only  the kernel  interface +    remaining from the original code. + +    1) Lance.c code in /linux/drivers/net/ +    2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook", +       AMD, 1992 [(800) 222-9323]. +    3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)", +       AMD, Pub. #17881, May 1993. +    4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA", +       AMD, Pub. #16907, May 1992 +    5) "DEC EtherWORKS LC Ethernet Controller Owners Manual", +       Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003 +    6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual", +       Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003 +    7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR +       Digital Equipment Corporation, 1989 +    8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual", +       Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001 +     + +    Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this +    driver. + +    The original DEPCA  card requires that the  ethernet ROM address counter +    be enabled to count and has an 8 bit NICSR.  The ROM counter enabling is +    only  done when a  0x08 is read as the  first address octet (to minimise +    the chances  of writing over some  other hardware's  I/O register).  The +    NICSR accesses   have been changed  to  byte accesses  for all the cards +    supported by this driver, since there is only one  useful bit in the MSB +    (remote boot timeout) and it  is not used.  Also, there  is a maximum of +    only 48kB network  RAM for this  card.  My thanks  to Torbjorn Lindh for +    help debugging all this (and holding my feet to  the fire until I got it +    right). + +    The DE200  series  boards have  on-board 64kB  RAM for  use  as a shared +    memory network  buffer. Only the DE100  cards make use  of a  2kB buffer +    mode which has not  been implemented in  this driver (only the 32kB  and +    64kB modes are supported [16kB/48kB for the original DEPCA]). + +    At the most only 2 DEPCA cards can  be supported on  the ISA bus because +    there is only provision  for two I/O base addresses  on each card (0x300 +    and 0x200). The I/O address is detected by searching for a byte sequence +    in the Ethernet station address PROM at the expected I/O address for the +    Ethernet  PROM.   The shared memory  base   address  is 'autoprobed'  by +    looking  for the self  test PROM  and detecting the  card name.   When a +    second  DEPCA is  detected,  information  is   placed in the   base_addr +    variable of the  next device structure (which  is created if necessary), +    thus  enabling ethif_probe  initialization  for the device.  More than 2 +    EISA cards can  be  supported, but  care will  be  needed assigning  the +    shared memory to ensure that each slot has the  correct IRQ, I/O address +    and shared memory address assigned. + +    ************************************************************************ + +    NOTE: If you are using two  ISA DEPCAs, it is  important that you assign +    the base memory addresses correctly.   The  driver autoprobes I/O  0x300 +    then 0x200.  The  base memory address for  the first device must be less +    than that of the second so that the auto probe will correctly assign the +    I/O and memory addresses on the same card.  I can't think of a way to do +    this unambiguously at the moment, since there is nothing on the cards to +    tie I/O and memory information together. + +    I am unable  to  test  2 cards   together for now,    so this  code   is +    unchecked. All reports, good or bad, are welcome. + +    ************************************************************************ + +    The board IRQ   setting must be  at an  unused IRQ which  is auto-probed +    using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are +    {2,3,4,5,7}, whereas the  DE200 is at {5,9,10,11,15}.  Note that IRQ2 is +    really IRQ9 in machines with 16 IRQ lines. + +    No 16MB memory  limitation should exist with this  driver as DMA is  not +    used and the common memory area is in low memory on the network card (my +    current system has 20MB and I've not had problems yet). + +    The ability to load this driver as a loadable module has been added. To +    utilise this ability, you have to do <8 things: + +    0) have a copy of the loadable modules code installed on your system. +    1) copy depca.c from the  /linux/drivers/net directory to your favourite +    temporary directory. +    2) if you wish, edit the  source code near  line 1530 to reflect the I/O +    address and IRQ you're using (see also 5). +    3) compile  depca.c, but include -DMODULE in  the command line to ensure +    that the correct bits are compiled (see end of source code). +    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a +    kernel with the depca configuration turned off and reboot. +    5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100] +       [Alan Cox: Changed the code to allow command line irq/io assignments] +       [Dave Davies: Changed the code to allow command line mem/name +                                                                assignments] +    6) run the net startup bits for your eth?? interface manually  +    (usually /etc/rc.inet[12] at boot time).  +    7) enjoy! + +    Note that autoprobing is not allowed in loadable modules - the system is +    already up and running and you're messing with interrupts. + +    To unload a module, turn off the associated interface  +    'ifconfig eth?? down' then 'rmmod depca'. + +    To assign a base memory address for the shared memory  when running as a +    loadable module, see 5 above.  To include the adapter  name (if you have +    no PROM  but know the card name)  also see 5  above. Note that this last +    option  will not work  with kernel  built-in  depca's.  + +    The shared memory assignment for a loadable module  makes sense to avoid +    the 'memory autoprobe' picking the wrong shared memory  (for the case of +    2 depca's in a PC). + + +    TO DO: +    ------ + + +    Revision History +    ---------------- + +    Version   Date        Description +   +      0.1     25-jan-94   Initial writing. +      0.2     27-jan-94   Added LANCE TX hardware buffer chaining. +      0.3      1-feb-94   Added multiple DEPCA support. +      0.31     4-feb-94   Added DE202 recognition. +      0.32    19-feb-94   Tidy up. Improve multi-DEPCA support. +      0.33    25-feb-94   Fix DEPCA ethernet ROM counter enable. +                          Add jabber packet fix from murf@perftech.com +			  and becker@super.org +      0.34     7-mar-94   Fix DEPCA max network memory RAM & NICSR access. +      0.35     8-mar-94   Added DE201 recognition. Tidied up. +      0.351   30-apr-94   Added EISA support. Added DE422 recognition. +      0.36    16-may-94   DE422 fix released. +      0.37    22-jul-94   Added MODULE support +      0.38    15-aug-94   Added DBR ROM switch in depca_close().  +                          Multi DEPCA bug fix. +      0.38axp 15-sep-94   Special version for Alpha AXP Linux V1.0. +      0.381   12-dec-94   Added DE101 recognition, fix multicast bug. +      0.382    9-feb-95   Fix recognition bug reported by <bkm@star.rl.ac.uk>. +      0.383   22-feb-95   Fix for conflict with VESA SCSI reported by +                          <stromain@alf.dec.com> +      0.384   17-mar-95   Fix a ring full bug reported by <bkm@star.rl.ac.uk> +      0.385    3-apr-95   Fix a recognition bug reported by  +                                                <ryan.niemi@lastfrontier.com> +      0.386   21-apr-95   Fix the last fix...sorry, must be galloping senility +      0.40    25-May-95   Rewrite for portability & updated. +                          ALPHA support from <jestabro@amt.tay1.dec.com> +      0.41    26-Jun-95   Added verify_area() calls in depca_ioctl() from +                          suggestion by <heiko@colossus.escape.de> +      0.42    27-Dec-95   Add 'mem' shared memory assignment for loadable  +                          modules. +                          Add 'adapter_name' for loadable modules when no PROM. +			  Both above from a suggestion by  +			  <pchen@woodruffs121.residence.gatech.edu>. +			  Add new multicasting code. +      0.421   22-Apr-96	  Fix alloc_device() bug <jari@markkus2.fimr.fi> +      0.422   29-Apr-96	  Fix depca_hw_init() bug <jari@markkus2.fimr.fi> +      0.423    7-Jun-96   Fix module load bug <kmg@barco.be> +      0.43    16-Aug-96   Update alloc_device() to conform to de4x5.c + +    ========================================================================= +*/ + +static const char *version = "depca.c:v0.43 96/8/16 davies@maniac.ultranet.com\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <linux/time.h> +#include <linux/types.h> +#include <linux/unistd.h> +#include <linux/ctype.h> + +#include "depca.h" + +#ifdef DEPCA_DEBUG +static int depca_debug = DEPCA_DEBUG; +#else +static int depca_debug = 1; +#endif + +#define DEPCA_NDA 0xffe0            /* No Device Address */ + +/* +** Ethernet PROM defines +*/ +#define PROBE_LENGTH    32 +#define ETH_PROM_SIG    0xAA5500FFUL + +/* +** Set the number of Tx and Rx buffers. Ensure that the memory requested +** here is <= to the amount of shared memory set up by the board switches. +** The number of descriptors MUST BE A POWER OF 2. +** +** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ) +*/ +#define NUM_RX_DESC     8               /* Number of RX descriptors */ +#define NUM_TX_DESC     8               /* Number of TX descriptors */ +#define RX_BUFF_SZ	1536            /* Buffer size for each Rx buffer */ +#define TX_BUFF_SZ	1536            /* Buffer size for each Tx buffer */ + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */ + +/* +** EISA bus defines +*/ +#define DEPCA_EISA_IO_PORTS 0x0c00       /* I/O port base address, slot 0 */ +#define MAX_EISA_SLOTS 16 +#define EISA_SLOT_INC 0x1000 + +/* +** ISA Bus defines +*/ +#define DEPCA_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0xe0000,0x00000} +#define DEPCA_IO_PORTS {0x300, 0x200, 0} +#define DEPCA_TOTAL_SIZE 0x10 +static short mem_chkd = 0; + +/* +** Name <-> Adapter mapping +*/ +#define DEPCA_SIGNATURE {"DEPCA",\ +			 "DE100","DE101",\ +                         "DE200","DE201","DE202",\ +			 "DE210",\ +                         "DE422",\ +                         ""} +static enum {DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown} adapter; + +/* +** Miscellaneous info... +*/ +#define DEPCA_STRLEN 16 +#define MAX_NUM_DEPCAS 2 + +/* +** Memory Alignment. Each descriptor is 4 longwords long. To force a +** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and +** DESC_ALIGN. ALIGN aligns the start address of the private memory area +** and hence the RX descriptor ring's first entry.  +*/ +#define ALIGN4      ((u_long)4 - 1)       /* 1 longword align */ +#define ALIGN8      ((u_long)8 - 1)       /* 2 longword (quadword) align */ +#define ALIGN         ALIGN8              /* Keep the LANCE happy... */ + +/* +** The DEPCA Rx and Tx ring descriptors.  +*/ +struct depca_rx_desc { +    volatile s32 base; +    s16 buf_length;		/* This length is negative 2's complement! */ +    s16 msg_length;		/* This length is "normal". */ +}; + +struct depca_tx_desc { +    volatile s32 base; +    s16 length;		        /* This length is negative 2's complement! */ +    s16 misc;                   /* Errors and TDR info */ +}; + +#define LA_MASK 0x0000ffff      /* LANCE address mask for mapping network RAM +				   to LANCE memory address space */ + +/* +** The Lance initialization block, described in databook, in common memory. +*/ +struct depca_init { +    u16 mode;	                /* Mode register */ +    u8  phys_addr[ETH_ALEN];	/* Physical ethernet address */ +    u8  mcast_table[8];	        /* Multicast Hash Table. */ +    u32 rx_ring;     	        /* Rx ring base pointer & ring length */ +    u32 tx_ring;	        /* Tx ring base pointer & ring length */ +}; + +#define DEPCA_PKT_STAT_SZ 16 +#define DEPCA_PKT_BIN_SZ  128                /* Should be >=100 unless you +                                                increase DEPCA_PKT_STAT_SZ */ +struct depca_private { +    char devname[DEPCA_STRLEN];    /* Device Product String                  */ +    char adapter_name[DEPCA_STRLEN];/* /proc/ioports string                  */ +    char adapter;                  /* Adapter type                           */ +    struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */ +    struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */ +    struct depca_init	init_block;/* Shadow Initialization block            */ +    char *rx_memcpy[NUM_RX_DESC];  /* CPU virt address of sh'd memory buffs  */ +    char *tx_memcpy[NUM_TX_DESC];  /* CPU virt address of sh'd memory buffs  */ +    u_long bus_offset;             /* (E)ISA bus address offset vs LANCE     */ +    u_long sh_mem;  		   /* Physical start addr of shared mem area */ +    u_long dma_buffs;		   /* LANCE Rx and Tx buffers start address. */ +    int	rx_new, tx_new;		   /* The next free ring entry               */ +    int rx_old, tx_old;	           /* The ring entries to be free()ed.       */ +    struct enet_statistics stats; +    struct {                       /* Private stats counters                 */ +	u32 bins[DEPCA_PKT_STAT_SZ]; +	u32 unicast; +	u32 multicast; +	u32 broadcast; +	u32 excessive_collisions; +	u32 tx_underruns; +	u32 excessive_underruns; +    } pktStats; +    int txRingMask;                /* TX ring mask                           */ +    int rxRingMask;                /* RX ring mask                           */ +    s32 rx_rlen;                   /* log2(rxRingMask+1) for the descriptors */ +    s32 tx_rlen;                   /* log2(txRingMask+1) for the descriptors */ +}; + +/* +** The transmit ring full condition is described by the tx_old and tx_new +** pointers by: +**    tx_old            = tx_new    Empty ring +**    tx_old            = tx_new+1  Full ring +**    tx_old+txRingMask = tx_new    Full ring  (wrapped condition) +*/ +#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ +			 lp->tx_old+lp->txRingMask-lp->tx_new:\ +                         lp->tx_old               -lp->tx_new-1) + +/* +** Public Functions +*/ +static int    depca_open(struct device *dev); +static int    depca_start_xmit(struct sk_buff *skb, struct device *dev); +static void   depca_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static int    depca_close(struct device *dev); +static int    depca_ioctl(struct device *dev, struct ifreq *rq, int cmd); +static struct enet_statistics *depca_get_stats(struct device *dev); +static void   set_multicast_list(struct device *dev); + +/* +** Private functions +*/ +static int    depca_hw_init(struct device *dev, u_long ioaddr); +static void   depca_init_ring(struct device *dev); +static int    depca_rx(struct device *dev); +static int    depca_tx(struct device *dev); + +static void   LoadCSRs(struct device *dev); +static int    InitRestartDepca(struct device *dev); +static void   DepcaSignature(char *name, u_long paddr); +static int    DevicePresent(u_long ioaddr); +static int    get_hw_addr(struct device *dev); +static int    EISA_signature(char *name, s32 eisa_id); +static void   SetMulticastFilter(struct device *dev); +static void   isa_probe(struct device *dev, u_long iobase); +static void   eisa_probe(struct device *dev, u_long iobase); +static struct device *alloc_device(struct device *dev, u_long iobase); +static int    depca_dev_index(char *s); +static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)); +static int    load_packet(struct device *dev, struct sk_buff *skb); +static void   depca_dbg_open(struct device *dev); + +#ifdef MODULE +int           init_module(void); +void          cleanup_module(void); +static int    autoprobed = 1, loading_module = 1; +# else +static u_char de1xx_irq[] = {2,3,4,5,7,9,0}; +static u_char de2xx_irq[] = {5,9,10,11,15,0}; +static u_char de422_irq[] = {5,9,10,11,0}; +static u_char *depca_irq; +static int    autoprobed = 0, loading_module = 0; +#endif /* MODULE */ + +static char   name[DEPCA_STRLEN]; +static int    num_depcas = 0, num_eth = 0; +static int    mem=0;                       /* For loadable module assignment +                                              use insmod mem=0x????? .... */ +static char   *adapter_name = '\0';        /* If no PROM when loadable module +					      use insmod adapter_name=DE??? ... +					   */ +/* +** Miscellaneous defines... +*/ +#define STOP_DEPCA \ +    outw(CSR0, DEPCA_ADDR);\ +    outw(STOP, DEPCA_DATA) + + + +int depca_probe(struct device *dev) +{ +  int tmp = num_depcas, status = -ENODEV; +  u_long iobase = dev->base_addr; + +  if ((iobase == 0) && loading_module){ +    printk("Autoprobing is not supported when loading a module based driver.\n"); +    status = -EIO; +  } else { +    isa_probe(dev, iobase); +    eisa_probe(dev, iobase); + +    if ((tmp == num_depcas) && (iobase != 0) && loading_module) { +      printk("%s: depca_probe() cannot find device at 0x%04lx.\n", dev->name,  +	                                                               iobase); +    } + +    /* +    ** Walk the device list to check that at least one device +    ** initialised OK +    */ +    for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); + +    if (dev->priv) status = 0; +    if (iobase == 0) autoprobed = 1; +  } + +  return status; +} + +static int +depca_hw_init(struct device *dev, u_long ioaddr) +{ +  struct depca_private *lp; +  int i, j, offset, netRAM, mem_len, status=0; +  s16 nicsr; +  u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES; + +  STOP_DEPCA; + +  nicsr = inb(DEPCA_NICSR); +  nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); +  outb(nicsr, DEPCA_NICSR); + +  if (inw(DEPCA_DATA) == STOP) { +    do { +      strcpy(name, (adapter_name ? adapter_name : "")); +      mem_start = (mem ? mem & 0xf0000 : mem_base[mem_chkd++]); +      DepcaSignature(name, mem_start); +    } while (!mem && mem_base[mem_chkd] && (adapter == unknown)); + +    if ((adapter != unknown) && mem_start) {        /* found a DEPCA device */ +      dev->base_addr = ioaddr; + +      if ((ioaddr&0x0fff)==DEPCA_EISA_IO_PORTS) {/* EISA slot address */ +	printk("%s: %s at 0x%04lx (EISA slot %d)",  +	                    dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f)); +      } else {                             /* ISA port address */ +	printk("%s: %s at 0x%04lx", dev->name, name, ioaddr); +      } + +      printk(", h/w address "); +      status = get_hw_addr(dev); +      for (i=0; i<ETH_ALEN - 1; i++) { /* get the ethernet address */ +	printk("%2.2x:", dev->dev_addr[i]); +      } +      printk("%2.2x", dev->dev_addr[i]); + +      if (status == 0) { +	/* Set up the maximum amount of network RAM(kB) */ +	netRAM = ((adapter != DEPCA) ? 64 : 48); +	if ((nicsr & _128KB) && (adapter == de422)) netRAM = 128; +	offset = 0x0000; + +	/* Shared Memory Base Address */  +	if (nicsr & BUF) { +	  offset = 0x8000;              /* 32kbyte RAM offset*/ +	  nicsr &= ~BS;                 /* DEPCA RAM in top 32k */ +	  netRAM -= 32; +	} +	mem_start += offset;            /* (E)ISA start address */ +	if ((mem_len = (NUM_RX_DESC*(sizeof(struct depca_rx_desc)+RX_BUFF_SZ) + +			NUM_TX_DESC*(sizeof(struct depca_tx_desc)+TX_BUFF_SZ) + +			sizeof(struct depca_init))) <= +	    (netRAM<<10)) { +	  printk(",\n      has %dkB RAM at 0x%.5lx", netRAM, mem_start); + +	  /* Enable the shadow RAM. */ +	  if (adapter != DEPCA) { +	    nicsr |= SHE; +	    outb(nicsr, DEPCA_NICSR); +	  } +  +	  /* Define the device private memory */ +	  dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL); +	  if (dev->priv == NULL) +	    return -ENOMEM; +	  lp = (struct depca_private *)dev->priv; +	  memset((char *)dev->priv, 0, sizeof(struct depca_private)); +	  lp->adapter = adapter; +	  sprintf(lp->adapter_name,"%s (%s)", name, dev->name); +	  request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name); + +	  /* Initialisation Block */ +	  lp->sh_mem = mem_start; +	  mem_start += sizeof(struct depca_init); + +	  /* Tx & Rx descriptors (aligned to a quadword boundary) */ +	  mem_start = (mem_start + ALIGN) & ~ALIGN; +	  lp->rx_ring = (struct depca_rx_desc *)mem_start; + +	  mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC); +	  lp->tx_ring = (struct depca_tx_desc *)mem_start; + +	  mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC); +	  lp->bus_offset = mem_start & 0x00ff0000; +	  mem_start &= LA_MASK;           /* LANCE re-mapped start address */ + +	  lp->dma_buffs = mem_start; + +	  /* Finish initialising the ring information. */ +	  lp->rxRingMask = NUM_RX_DESC - 1; +	  lp->txRingMask = NUM_TX_DESC - 1; + +	  /* Calculate Tx/Rx RLEN size for the descriptors. */ +	  for (i=0, j = lp->rxRingMask; j>0; i++) { +	    j >>= 1; +	  } +	  lp->rx_rlen = (s32)(i << 29); +	  for (i=0, j = lp->txRingMask; j>0; i++) { +	    j >>= 1; +	  } +	  lp->tx_rlen = (s32)(i << 29); + +	  /* Load the initialisation block */ +	  depca_init_ring(dev); + +	  /* Initialise the control and status registers */ +	  LoadCSRs(dev); + +	  /* Enable DEPCA board interrupts for autoprobing */ +	  nicsr = ((nicsr & ~IM)|IEN); +	  outb(nicsr, DEPCA_NICSR); + +	  /* To auto-IRQ we enable the initialization-done and DMA err, +	     interrupts. For now we will always get a DMA error. */ +	  if (dev->irq < 2) { +#ifndef MODULE +	    unsigned char irqnum; +	    autoirq_setup(0); +	     +	    /* Assign the correct irq list */ +	    switch (lp->adapter) { +	    case DEPCA: +	    case de100: +	    case de101: +	      depca_irq = de1xx_irq; +	      break; +	    case de200: +	    case de201: +	    case de202: +	    case de210: +	      depca_irq = de2xx_irq; +	      break; +	    case de422: +	      depca_irq = de422_irq; +	      break; +	    } + +	    /* Trigger an initialization just for the interrupt. */ +	    outw(INEA | INIT, DEPCA_DATA); +	   +	    irqnum = autoirq_report(1); +	    if (!irqnum) { +	      printk(" and failed to detect IRQ line.\n"); +	      status = -ENXIO; +	    } else { +	      for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) { +		if (irqnum == depca_irq[i]) { +		  dev->irq = irqnum; +		  printk(" and uses IRQ%d.\n", dev->irq); +		} +	      } +	       +	      if (!dev->irq) { +		printk(" but incorrect IRQ line detected.\n"); +		status = -ENXIO; +	      } +	    } +#endif /* MODULE */ +	  } else { +	    printk(" and assigned IRQ%d.\n", dev->irq); +	  } +	  if (status) release_region(ioaddr, DEPCA_TOTAL_SIZE); +	} else { +	  printk(",\n      requests %dkB RAM: only %dkB is available!\n",  +	         	                                (mem_len>>10), netRAM); +	  status = -ENXIO; +	} +      } else { +	printk("      which has an Ethernet PROM CRC error.\n"); +	status = -ENXIO; +      } +    } else { +      status = -ENXIO; +    } +    if (!status) { +      if (depca_debug > 1) { +	printk("%s", version); +      } + +      /* The DEPCA-specific entries in the device structure. */ +      dev->open = &depca_open; +      dev->hard_start_xmit = &depca_start_xmit; +      dev->stop = &depca_close; +      dev->get_stats = &depca_get_stats; +      dev->set_multicast_list = &set_multicast_list; +      dev->do_ioctl = &depca_ioctl; + +      dev->mem_start = 0; +	 +      /* Fill in the generic field of the device structure. */ +      ether_setup(dev); +    } else {                           /* Incorrectly initialised hardware */ +      if (dev->priv) { +	kfree_s(dev->priv, sizeof(struct depca_private)); +	dev->priv = NULL; +      } +    } +  } else { +    status = -ENXIO; +  } + +  return status; +} + + +static int +depca_open(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  u_long ioaddr = dev->base_addr; +  s16 nicsr; +  int status = 0; + +  irq2dev_map[dev->irq] = dev; +  STOP_DEPCA; +  nicsr = inb(DEPCA_NICSR); + +  /* Make sure the shadow RAM is enabled */ +  if (adapter != DEPCA) { +    nicsr |= SHE; +    outb(nicsr, DEPCA_NICSR); +  } + +  /* Re-initialize the DEPCA... */ +  depca_init_ring(dev); +  LoadCSRs(dev); + +  depca_dbg_open(dev); + +  if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name, NULL)) { +    printk("depca_open(): Requested IRQ%d is busy\n",dev->irq); +    status = -EAGAIN; +  } else { + +    /* Enable DEPCA board interrupts and turn off LED */ +    nicsr = ((nicsr & ~IM & ~LED)|IEN); +    outb(nicsr, DEPCA_NICSR); +    outw(CSR0,DEPCA_ADDR); +     +    dev->tbusy = 0;                          +    dev->interrupt = 0; +    dev->start = 1; +     +    status = InitRestartDepca(dev); + +    if (depca_debug > 1){ +      printk("CSR0: 0x%4.4x\n",inw(DEPCA_DATA)); +      printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR)); +    } +  } + +  MOD_INC_USE_COUNT; +   +  return status; +} + +/* Initialize the lance Rx and Tx descriptor rings. */ +static void +depca_init_ring(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  u_int i; +  u_long p; + +  /* Lock out other processes whilst setting up the hardware */ +  set_bit(0, (void *)&dev->tbusy); + +  lp->rx_new = lp->tx_new = 0; +  lp->rx_old = lp->tx_old = 0; + +  /* Initialize the base addresses and length of each buffer in the ring */ +  for (i = 0; i <= lp->rxRingMask; i++) { +    writel((p=lp->dma_buffs+i*RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base); +    writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length); +    lp->rx_memcpy[i]=(char *)(p+lp->bus_offset); +  } +  for (i = 0; i <= lp->txRingMask; i++) { +    writel((p=lp->dma_buffs+(i+lp->txRingMask+1)*TX_BUFF_SZ) & 0x00ffffff, +	                                                 &lp->tx_ring[i].base); +    lp->tx_memcpy[i]=(char *)(p+lp->bus_offset); +  } + +  /* Set up the initialization block */ +  lp->init_block.rx_ring = ((u32)((u_long)lp->rx_ring)&LA_MASK) | lp->rx_rlen; +  lp->init_block.tx_ring = ((u32)((u_long)lp->tx_ring)&LA_MASK) | lp->tx_rlen; + +  SetMulticastFilter(dev); + +  for (i = 0; i < ETH_ALEN; i++) { +    lp->init_block.phys_addr[i] = dev->dev_addr[i]; +  } + +  lp->init_block.mode = 0x0000;            /* Enable the Tx and Rx */ + +  return; +} + +/*  +** Writes a socket buffer to TX descriptor ring and starts transmission  +*/ +static int +depca_start_xmit(struct sk_buff *skb, struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  u_long ioaddr = dev->base_addr; +  int status = 0; + +  /* Transmitter timeout, serious problems. */ +  if (dev->tbusy) { +    int tickssofar = jiffies - dev->trans_start; +    if (tickssofar < 1*HZ) { +      status = -1; +    } else { +      printk("%s: transmit timed out, status %04x, resetting.\n", +	     dev->name, inw(DEPCA_DATA)); +	 +      STOP_DEPCA; +      depca_init_ring(dev); +      LoadCSRs(dev); +      dev->interrupt = UNMASK_INTERRUPTS; +      dev->start = 1; +      dev->tbusy=0; +      dev->trans_start = jiffies; +      InitRestartDepca(dev); +      dev_kfree_skb(skb, FREE_WRITE); +    } +    return status; +  } else if (skb == NULL) { +    dev_tint(dev); +  } else if (skb->len > 0) { +    /* Enforce 1 process per h/w access */ +    if (set_bit(0, (void*)&dev->tbusy) != 0) { +      printk("%s: Transmitter access conflict.\n", dev->name); +      status = -1; +    } else { +      if (TX_BUFFS_AVAIL) {                    /* Fill in a Tx ring entry */ +	status = load_packet(dev, skb); + +	if (!status) { +	  /* Trigger an immediate send demand. */ +	  outw(CSR0, DEPCA_ADDR); +	  outw(INEA | TDMD, DEPCA_DATA); +	   +	  dev->trans_start = jiffies; +	  dev_kfree_skb(skb, FREE_WRITE); +	} +	if (TX_BUFFS_AVAIL) { +	  dev->tbusy=0; +	}   +      } else { +	status = -1; +      } +    } +  } +   +  return status; +} + +/* +** The DEPCA interrupt handler.  +*/ +static void +depca_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +  struct device *dev = (struct device *)(irq2dev_map[irq]); +  struct depca_private *lp; +  s16 csr0, nicsr; +  u_long ioaddr; + +  if (dev == NULL) { +    printk ("depca_interrupt(): irq %d for unknown device.\n", irq); +  } else { +    lp = (struct depca_private *)dev->priv; +    ioaddr = dev->base_addr; +     +    if (dev->interrupt) +      printk("%s: Re-entering the interrupt handler.\n", dev->name); + +    dev->interrupt = MASK_INTERRUPTS; + +    /* mask the DEPCA board interrupts and turn on the LED */ +    nicsr = inb(DEPCA_NICSR); +    nicsr |= (IM|LED); +    outb(nicsr, DEPCA_NICSR); + +    outw(CSR0, DEPCA_ADDR); +    csr0 = inw(DEPCA_DATA); + +    /* Acknowledge all of the current interrupt sources ASAP. */ +    outw(csr0 & INTE, DEPCA_DATA); + +    if (csr0 & RINT)		       /* Rx interrupt (packet arrived) */ +      depca_rx(dev); + +    if (csr0 & TINT) 	               /* Tx interrupt (packet sent) */ +      depca_tx(dev); + +    if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) { /* any resources available? */ +      dev->tbusy = 0;                  /* clear TX busy flag */ +      mark_bh(NET_BH); +    } + +    /* Unmask the DEPCA board interrupts and turn off the LED */ +    nicsr = (nicsr & ~IM & ~LED); +    outb(nicsr, DEPCA_NICSR); + +    dev->interrupt = UNMASK_INTERRUPTS; +  } + +  return; +} + +static int +depca_rx(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  int i, entry; +  s32 status; + +  for (entry=lp->rx_new;  +       !(readl(&lp->rx_ring[entry].base) & R_OWN); +       entry=lp->rx_new){ +    status = readl(&lp->rx_ring[entry].base) >> 16 ; +    if (status & R_STP) {                      /* Remember start of frame */ +      lp->rx_old = entry; +    } +    if (status & R_ENP) {                      /* Valid frame status */ +      if (status & R_ERR) {	               /* There was an error. */ +	lp->stats.rx_errors++;                 /* Update the error stats. */ +	if (status & R_FRAM) lp->stats.rx_frame_errors++; +	if (status & R_OFLO) lp->stats.rx_over_errors++; +	if (status & R_CRC)  lp->stats.rx_crc_errors++; +	if (status & R_BUFF) lp->stats.rx_fifo_errors++; +      } else {	 +	short len, pkt_len = readw(&lp->rx_ring[entry].msg_length); +	struct sk_buff *skb; + +	skb = dev_alloc_skb(pkt_len+2); +	if (skb != NULL) { +	  unsigned char *buf; +	  skb_reserve(skb,2);               /* 16 byte align the IP header */ +	  buf = skb_put(skb,pkt_len); +	  skb->dev = dev; +	  if (entry < lp->rx_old) {         /* Wrapped buffer */ +	    len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ; +	    memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len); +	    memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len-len); +	  } else {                          /* Linear buffer */ +	    memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len); +	  } + +	  /*  +	  ** Notify the upper protocol layers that there is another  +	  ** packet to handle +	  */ +	  skb->protocol=eth_type_trans(skb,dev); +	  netif_rx(skb); +  +	  /* +	  ** Update stats +	  */ +	  lp->stats.rx_packets++; +	  for (i=1; i<DEPCA_PKT_STAT_SZ-1; i++) { +	    if (pkt_len < (i*DEPCA_PKT_BIN_SZ)) { +	      lp->pktStats.bins[i]++; +	      i = DEPCA_PKT_STAT_SZ; +	    } +	  } +	  if (buf[0] & 0x01) {              /* Multicast/Broadcast */ +	    if ((*(s16 *)&buf[0] == -1) && +		(*(s16 *)&buf[2] == -1) && +		(*(s16 *)&buf[4] == -1)) { +	      lp->pktStats.broadcast++; +	    } else { +	      lp->pktStats.multicast++; +	    } +	  } else if ((*(s16 *)&buf[0] == *(s16 *)&dev->dev_addr[0]) && +		     (*(s16 *)&buf[2] == *(s16 *)&dev->dev_addr[2]) && +		     (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) { +	    lp->pktStats.unicast++; +	  } +	   +	  lp->pktStats.bins[0]++;           /* Duplicates stats.rx_packets */ +	  if (lp->pktStats.bins[0] == 0) {  /* Reset counters */ +	    memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats)); +	  } +	} else { +	  printk("%s: Memory squeeze, deferring packet.\n", dev->name); +	  lp->stats.rx_dropped++;	/* Really, deferred. */ +	  break; +	} +      } +      /* Change buffer ownership for this last frame, back to the adapter */ +      for (; lp->rx_old!=entry; lp->rx_old=(lp->rx_old+1)&lp->rxRingMask) { +	writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN,  +	                                        &lp->rx_ring[lp->rx_old].base); +      } +      writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base); +    } + +    /* +    ** Update entry information +    */ +    lp->rx_new = (lp->rx_new + 1) & lp->rxRingMask; +    } + +    return 0; +} + +/* +** Buffer sent - check for buffer errors. +*/ +static int +depca_tx(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  int entry; +  s32 status; +  u_long ioaddr = dev->base_addr; + +  for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { +    status = readl(&lp->tx_ring[entry].base) >> 16 ; + +    if (status < 0) {                          /* Packet not yet sent! */ +      break; +    } else if (status & T_ERR) {               /* An error occurred. */ +      status = readl(&lp->tx_ring[entry].misc); +      lp->stats.tx_errors++; +      if (status & TMD3_RTRY) lp->stats.tx_aborted_errors++; +      if (status & TMD3_LCAR) lp->stats.tx_carrier_errors++; +      if (status & TMD3_LCOL) lp->stats.tx_window_errors++; +      if (status & TMD3_UFLO) lp->stats.tx_fifo_errors++; +      if (status & (TMD3_BUFF | TMD3_UFLO)) { +	/* Trigger an immediate send demand. */ +	outw(CSR0, DEPCA_ADDR); +	outw(INEA | TDMD, DEPCA_DATA); +      } +    } else if (status & (T_MORE | T_ONE)) { +      lp->stats.collisions++; +    } else { +      lp->stats.tx_packets++; +    } + +    /* Update all the pointers */ +    lp->tx_old = (lp->tx_old + 1) & lp->txRingMask; +  } + +  return 0; +} + +static int +depca_close(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  s16 nicsr; +  u_long ioaddr = dev->base_addr; + +  dev->start = 0; +  dev->tbusy = 1; + +  outw(CSR0, DEPCA_ADDR); + +  if (depca_debug > 1) { +    printk("%s: Shutting down ethercard, status was %2.2x.\n", +	   dev->name, inw(DEPCA_DATA)); +  } + +  /*  +  ** We stop the DEPCA here -- it occasionally polls +  ** memory if we don't.  +  */ +  outw(STOP, DEPCA_DATA); + +  /* +  ** Give back the ROM in case the user wants to go to DOS +  */ +  if (lp->adapter != DEPCA) { +    nicsr = inb(DEPCA_NICSR); +    nicsr &= ~SHE; +    outb(nicsr, DEPCA_NICSR); +  } + +  /* +  ** Free the associated irq +  */ +  free_irq(dev->irq, NULL); +  irq2dev_map[dev->irq] = NULL; + +  MOD_DEC_USE_COUNT; + +  return 0; +} + +static void LoadCSRs(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  u_long ioaddr = dev->base_addr; + +  outw(CSR1, DEPCA_ADDR);                /* initialisation block address LSW */ +  outw((u16)(lp->sh_mem & LA_MASK), DEPCA_DATA); +  outw(CSR2, DEPCA_ADDR);                /* initialisation block address MSW */ +  outw((u16)((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA); +  outw(CSR3, DEPCA_ADDR);                /* ALE control */ +  outw(ACON, DEPCA_DATA); + +  outw(CSR0, DEPCA_ADDR);                /* Point back to CSR0 */ + +  return; +} + +static int InitRestartDepca(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  u_long ioaddr = dev->base_addr; +  int i, status=0; + +  /* Copy the shadow init_block to shared memory */ +  memcpy_toio((char *)lp->sh_mem, &lp->init_block, sizeof(struct depca_init)); + +  outw(CSR0, DEPCA_ADDR);                /* point back to CSR0 */ +  outw(INIT, DEPCA_DATA);                /* initialize DEPCA */ + +  /* wait for lance to complete initialisation */ +  for (i=0;(i<100) && !(inw(DEPCA_DATA) & IDON); i++);  + +  if (i!=100) { +    /* clear IDON by writing a "1", enable interrupts and start lance */ +    outw(IDON | INEA | STRT, DEPCA_DATA); +    if (depca_debug > 2) { +      printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n", +	     dev->name, i, lp->sh_mem, inw(DEPCA_DATA)); +    } +  } else { +    printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n", +	     dev->name, i, lp->sh_mem, inw(DEPCA_DATA)); +    status = -1; +  } + +  return status; +} + +static struct enet_statistics * +depca_get_stats(struct device *dev) +{ +    struct depca_private *lp = (struct depca_private *)dev->priv; + +    /* Null body since there is no framing error counter */ + +    return &lp->stats; +} + +/* +** Set or clear the multicast filter for this adaptor. +*/ +static void +set_multicast_list(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  u_long ioaddr = dev->base_addr; +   +  if (irq2dev_map[dev->irq] != NULL) { +    while(dev->tbusy);                /* Stop ring access */ +    set_bit(0, (void*)&dev->tbusy); +    while(lp->tx_old != lp->tx_new);  /* Wait for the ring to empty */ + +    STOP_DEPCA;                       /* Temporarily stop the depca.  */ +    depca_init_ring(dev);             /* Initialize the descriptor rings */ + +    if (dev->flags & IFF_PROMISC) {   /* Set promiscuous mode */ +      lp->init_block.mode |= PROM; +    } else { +      SetMulticastFilter(dev); +      lp->init_block.mode &= ~PROM;   /* Unset promiscuous mode */ +    } + +    LoadCSRs(dev);                    /* Reload CSR3 */ +    InitRestartDepca(dev);            /* Resume normal operation. */ +    dev->tbusy = 0;                   /* Unlock the TX ring */ +  } +} + +/* +** Calculate the hash code and update the logical address filter +** from a list of ethernet multicast addresses. +** Big endian crc one liner is mine, all mine, ha ha ha ha! +** LANCE calculates its hash codes big endian. +*/ +static void SetMulticastFilter(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  struct dev_mc_list *dmi=dev->mc_list; +  char *addrs; +  int i, j, bit, byte; +  u16 hashcode; +  s32 crc, poly = CRC_POLYNOMIAL_BE; + +  if (dev->flags & IFF_ALLMULTI) {         /* Set all multicast bits */ +    for (i=0; i<(HASH_TABLE_LEN>>3); i++) { +      lp->init_block.mcast_table[i] = (char)0xff; +    } +  } else { +    for (i=0; i<(HASH_TABLE_LEN>>3); i++){ /* Clear the multicast table */ +      lp->init_block.mcast_table[i]=0; +    } +                                           /* Add multicast addresses */ +    for (i=0;i<dev->mc_count;i++) {        /* for each address in the list */ +      addrs=dmi->dmi_addr; +      dmi=dmi->next; +      if ((*addrs & 0x01) == 1) {          /* multicast address? */  +	crc = 0xffffffff;                  /* init CRC for each address */ +	for (byte=0;byte<ETH_ALEN;byte++) {/* for each address byte */ +	                                   /* process each address bit */  +	  for (bit = *addrs++,j=0;j<8;j++, bit>>=1) { +	    crc = (crc << 1) ^ ((((crc<0?1:0) ^ bit) & 0x01) ? poly : 0); +	  } +	} +	hashcode = (crc & 1);              /* hashcode is 6 LSb of CRC ... */ +	for (j=0;j<5;j++) {                /* ... in reverse order. */ +	  hashcode = (hashcode << 1) | ((crc>>=1) & 1); +	}                                       +	 +	 +	byte = hashcode >> 3;              /* bit[3-5] -> byte in filter */ +	bit = 1 << (hashcode & 0x07);      /* bit[0-2] -> bit in byte */ +	lp->init_block.mcast_table[byte] |= bit; +      } +    } +  } + +  return; +} + +/* +** ISA bus I/O device probe +*/ +static void isa_probe(struct device *dev, u_long ioaddr) +{ +  int i = num_depcas, maxSlots; +  s32 ports[] = DEPCA_IO_PORTS; + +  if (!ioaddr && autoprobed) return ;          /* Been here before ! */ +  if (ioaddr > 0x400) return;                  /* EISA Address */ +  if (i >= MAX_NUM_DEPCAS) return;             /* Too many ISA adapters */ + +  if (ioaddr == 0) {                           /* Autoprobing */ +    maxSlots = MAX_NUM_DEPCAS; +  } else {                                     /* Probe a specific location */ +    ports[i] = ioaddr; +    maxSlots = i + 1; +  } + +  for (; (i<maxSlots) && (dev!=NULL) && ports[i]; i++) { +    if (DevicePresent(ports[i]) == 0) {  +      if (check_region(ports[i], DEPCA_TOTAL_SIZE) == 0) { +	if ((dev = alloc_device(dev, ports[i])) != NULL) { +	  if (depca_hw_init(dev, ports[i]) == 0) { +	    num_depcas++; +	  } +	  num_eth++; +	} +      } else if (autoprobed) { +	printk("%s: region already allocated at 0x%04x.\n", dev->name,ports[i]); +      } +    } +  } + +  return; +} + +/* +** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually +** the motherboard. Upto 15 EISA devices are supported. +*/ +static void eisa_probe(struct device *dev, u_long ioaddr) +{ +  int i, maxSlots; +  u_long iobase; +  char name[DEPCA_STRLEN]; + +  if (!ioaddr && autoprobed) return ;            /* Been here before ! */ +  if ((ioaddr < 0x400) && (ioaddr > 0)) return;  /* ISA Address */ + +  if (ioaddr == 0) {                           /* Autoprobing */ +    iobase = EISA_SLOT_INC;                    /* Get the first slot address */ +    i = 1; +    maxSlots = MAX_EISA_SLOTS; +  } else {                                     /* Probe a specific location */ +    iobase = ioaddr; +    i = (ioaddr >> 12); +    maxSlots = i + 1; +  } +  if ((iobase & 0x0fff) == 0) iobase += DEPCA_EISA_IO_PORTS; + +  for (; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) { +    if (EISA_signature(name, EISA_ID)) { +      if (DevicePresent(iobase) == 0) {  +	if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) { +	  if ((dev = alloc_device(dev, iobase)) != NULL) { +	    if (depca_hw_init(dev, iobase) == 0) { +	      num_depcas++; +	    } +	    num_eth++; +	  } +	} else if (autoprobed) { +	  printk("%s: region already allocated at 0x%04lx.\n",dev->name,iobase); +	} +      } +    } +  } + +  return; +} + +/* +** Search the entire 'eth' device list for a fixed probe. If a match isn't +** found then check for an autoprobe or unused device location. If they +** are not available then insert a new device structure at the end of +** the current list. +*/ +static struct device * +alloc_device(struct device *dev, u_long iobase) +{ +    struct device *adev = NULL; +    int fixed = 0, new_dev = 0; + +    num_eth = depca_dev_index(dev->name); +    if (loading_module) return dev; +     +    while (1) { +	if (((dev->base_addr == DEPCA_NDA) || (dev->base_addr==0)) && !adev) { +	    adev=dev; +	} else if ((dev->priv == NULL) && (dev->base_addr==iobase)) { +	    fixed = 1; +	} else { +	    if (dev->next == NULL) { +		new_dev = 1; +	    } else if (strncmp(dev->next->name, "eth", 3) != 0) { +		new_dev = 1; +	    } +	} +	if ((dev->next == NULL) || new_dev || fixed) break; +	dev = dev->next; +	num_eth++; +    } +    if (adev && !fixed) { +	dev = adev; +	num_eth = depca_dev_index(dev->name); +	new_dev = 0; +    } + +    if (((dev->next == NULL) &&   +	((dev->base_addr != DEPCA_NDA) && (dev->base_addr != 0)) && !fixed) || +	new_dev) { +	num_eth++;                         /* New device */ +	dev = insert_device(dev, iobase, depca_probe); +    } +     +    return dev; +} + +/* +** If at end of eth device list and can't use current entry, malloc +** one up. If memory could not be allocated, print an error message. +*/ +static struct device * +insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)) +{ +    struct device *new; + +    new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL); +    if (new == NULL) { +	printk("eth%d: Device not initialised, insufficient memory\n",num_eth); +	return NULL; +    } else { +	new->next = dev->next; +	dev->next = new; +	dev = dev->next;               /* point to the new device */ +	dev->name = (char *)(dev + 1); +	if (num_eth > 9999) { +	    sprintf(dev->name,"eth????");/* New device name */ +	} else { +	    sprintf(dev->name,"eth%d", num_eth);/* New device name */ +	} +	dev->base_addr = iobase;       /* assign the io address */ +	dev->init = init;              /* initialisation routine */ +    } + +    return dev; +} + +static int +depca_dev_index(char *s) +{ +    int i=0, j=0; + +    for (;*s; s++) { +	if (isdigit(*s)) { +	    j=1; +	    i = (i * 10) + (*s - '0'); +	} else if (j) break; +    } + +    return i; +} + +/* +** Look for a particular board name in the on-board Remote Diagnostics +** and Boot (readb) ROM. This will also give us a clue to the network RAM +** base address. +*/ +static void DepcaSignature(char *name, u_long paddr) +{ +  u_int i,j,k; +  const char *signatures[] = DEPCA_SIGNATURE; +  char tmpstr[16]; + +  /* Copy the first 16 bytes of ROM */ +  for (i=0;i<16;i++) { +    tmpstr[i] = readb(paddr+0xc000+i); +  } + +  /* Check if PROM contains a valid string */ +  for (i=0;*signatures[i]!='\0';i++) { +    for (j=0,k=0;j<16 && k<strlen(signatures[i]);j++) { +      if (signatures[i][k] == tmpstr[j]) {              /* track signature */ +	k++; +      } else {                     /* lost signature; begin search again */ +	k=0; +      } +    } +    if (k == strlen(signatures[i])) break; +  } + +  /* Check if name string is valid, provided there's no PROM */ +  if (*name && (i == unknown)) { +    for (i=0;*signatures[i]!='\0';i++) { +      if (strcmp(name,signatures[i]) == 0) break; +    } +  } + +  /* Update search results */ +  strcpy(name,signatures[i]); +  adapter = i; + +  return; +} + +/* +** Look for a special sequence in the Ethernet station address PROM that +** is common across all DEPCA products. Note that the original DEPCA needs +** its ROM address counter to be initialized and enabled. Only enable +** if the first address octet is a 0x08 - this minimises the chances of +** messing around with some other hardware, but it assumes that this DEPCA +** card initialized itself correctly. +**  +** Search the Ethernet address ROM for the signature. Since the ROM address +** counter can start at an arbitrary point, the search must include the entire +** probe sequence length plus the (length_of_the_signature - 1). +** Stop the search IMMEDIATELY after the signature is found so that the +** PROM address counter is correctly positioned at the start of the +** ethernet address for later read out. +*/ +static int DevicePresent(u_long ioaddr) +{ +  union { +    struct { +      u32 a; +      u32 b; +    } llsig; +    char Sig[sizeof(u32) << 1]; +  } dev; +  short sigLength=0; +  s8 data; +  s16 nicsr; +  int i, j, status = 0; + +  data = inb(DEPCA_PROM);                /* clear counter on DEPCA */ +  data = inb(DEPCA_PROM);                /* read data */ + +  if (data == 0x08) {                    /* Enable counter on DEPCA */ +    nicsr = inb(DEPCA_NICSR); +    nicsr |= AAC; +    outb(nicsr, DEPCA_NICSR); +  } +   +  dev.llsig.a = ETH_PROM_SIG; +  dev.llsig.b = ETH_PROM_SIG; +  sigLength = sizeof(u32) << 1; + +  for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) { +    data = inb(DEPCA_PROM); +    if (dev.Sig[j] == data) {    /* track signature */ +      j++; +    } else {                     /* lost signature; begin search again */ +      if (data == dev.Sig[0]) {  /* rare case.... */ +	j=1; +      } else { +	j=0; +      } +    } +  } + +  if (j!=sigLength) { +    status = -ENODEV;           /* search failed */ +  } + +  return status; +} + +/* +** The DE100 and DE101 PROM accesses were made non-standard for some bizarre +** reason: access the upper half of the PROM with x=0; access the lower half +** with x=1. +*/ +static int get_hw_addr(struct device *dev) +{ +  u_long ioaddr = dev->base_addr; +  int i, k, tmp, status = 0; +  u_short j, x, chksum; + +  x = (((adapter == de100) || (adapter == de101)) ? 1 : 0); + +  for (i=0,k=0,j=0;j<3;j++) { +    k <<= 1 ; +    if (k > 0xffff) k-=0xffff; + +    k += (u_char) (tmp = inb(DEPCA_PROM + x)); +    dev->dev_addr[i++] = (u_char) tmp; +    k += (u_short) ((tmp = inb(DEPCA_PROM + x)) << 8); +    dev->dev_addr[i++] = (u_char) tmp; + +    if (k > 0xffff) k-=0xffff; +  } +  if (k == 0xffff) k=0; + +  chksum = (u_char) inb(DEPCA_PROM + x); +  chksum |= (u_short) (inb(DEPCA_PROM + x) << 8); +  if (k != chksum) status = -1; + +  return status; +} + +/* +** Load a packet into the shared memory +*/ +static int load_packet(struct device *dev, struct sk_buff *skb) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  int i, entry, end, len, status = 0; + +  entry = lp->tx_new;  		               /* Ring around buffer number. */ +  end = (entry + (skb->len - 1) / TX_BUFF_SZ) & lp->txRingMask; +  if (!(readl(&lp->tx_ring[end].base) & T_OWN)) {/* Enough room? */ +    /*  +    ** Caution: the write order is important here... don't set up the +    ** ownership rights until all the other information is in place. +    */ +    if (end < entry) {                         /* wrapped buffer */ +      len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ; +      memcpy_toio(lp->tx_memcpy[entry], skb->data, len); +      memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len); +    } else {                                   /* linear buffer */ +      memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len); +    } + +    /* set up the buffer descriptors */ +    len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; +    for (i = entry; i != end; i = (i + 1) & lp->txRingMask) { +                                               /* clean out flags */ +      writel(readl(&lp->tx_ring[i].base) & ~T_FLAGS, &lp->tx_ring[i].base); +      writew(0x0000, &lp->tx_ring[i].misc);    /* clears other error flags */ +      writew(-TX_BUFF_SZ, &lp->tx_ring[i].length);/* packet length in buffer */ +      len -= TX_BUFF_SZ; +    } +                                               /* clean out flags */ +    writel(readl(&lp->tx_ring[end].base) & ~T_FLAGS, &lp->tx_ring[end].base); +    writew(0x0000, &lp->tx_ring[end].misc);    /* clears other error flags */ +    writew(-len, &lp->tx_ring[end].length);    /* packet length in last buff */ + +                                               /* start of packet */ +    writel(readl(&lp->tx_ring[entry].base) | T_STP, &lp->tx_ring[entry].base); +                                               /* end of packet */ +    writel(readl(&lp->tx_ring[end].base) | T_ENP, &lp->tx_ring[end].base); + +    for (i=end; i!=entry; --i) { +                                               /* ownership of packet */ +      writel(readl(&lp->tx_ring[i].base) | T_OWN, &lp->tx_ring[i].base); +      if (i == 0) i=lp->txRingMask+1; +    }    +    writel(readl(&lp->tx_ring[entry].base) | T_OWN, &lp->tx_ring[entry].base); +  +    lp->tx_new = (++end) & lp->txRingMask;     /* update current pointers */ +  } else { +    status = -1; +  } + +  return status; +} + +/* +** Look for a particular board name in the EISA configuration space +*/ +static int EISA_signature(char *name, s32 eisa_id) +{ +  u_int i; +  const char *signatures[] = DEPCA_SIGNATURE; +  char ManCode[DEPCA_STRLEN]; +  union { +    s32 ID; +    char Id[4]; +  } Eisa; +  int status = 0; + +  *name = '\0'; +  Eisa.ID = inl(eisa_id); + +  ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); +  ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); +  ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); +  ManCode[3]=(( Eisa.Id[2]&0x0f)+0x30); +  ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); +  ManCode[5]='\0'; + +  for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) { +    if (strstr(ManCode, signatures[i]) != NULL) { +      strcpy(name,ManCode); +      status = 1; +    } +  } + +  return status; +} + +static void depca_dbg_open(struct device *dev) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  u_long ioaddr = dev->base_addr; +  struct depca_init *p = (struct depca_init *)lp->sh_mem; +  int i;  + +  if (depca_debug > 1){ +    /* Copy the shadow init_block to shared memory */ +    memcpy_toio((char *)lp->sh_mem,&lp->init_block,sizeof(struct depca_init)); + +    printk("%s: depca open with irq %d\n",dev->name,dev->irq); +    printk("Descriptor head addresses:\n"); +    printk("\t0x%lx  0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring); +    printk("Descriptor addresses:\nRX: "); +    for (i=0;i<lp->rxRingMask;i++){ +      if (i < 3) { +	printk("0x%8.8lx ", (long) &lp->rx_ring[i].base); +      } +    } +    printk("...0x%8.8lx\n", (long) &lp->rx_ring[i].base); +    printk("TX: "); +    for (i=0;i<lp->txRingMask;i++){ +      if (i < 3) { +	printk("0x%8.8lx ", (long) &lp->tx_ring[i].base); +      } +    } +    printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base); +    printk("\nDescriptor buffers:\nRX: "); +    for (i=0;i<lp->rxRingMask;i++){ +      if (i < 3) { +	printk("0x%8.8x  ", readl(&lp->rx_ring[i].base)); +      } +    } +    printk("...0x%8.8x\n", readl(&lp->rx_ring[i].base)); +    printk("TX: "); +    for (i=0;i<lp->txRingMask;i++){ +      if (i < 3) { +	printk("0x%8.8x  ", readl(&lp->tx_ring[i].base)); +      } +    } +    printk("...0x%8.8x\n", readl(&lp->tx_ring[i].base)); +    printk("Initialisation block at 0x%8.8lx\n",lp->sh_mem); +    printk("\tmode: 0x%4.4x\n",readw(&p->mode)); +    printk("\tphysical address: "); +    for (i=0;i<ETH_ALEN-1;i++){ +      printk("%2.2x:",(u_char)readb(&p->phys_addr[i])); +    } +    printk("%2.2x\n",(u_char)readb(&p->phys_addr[i])); +    printk("\tmulticast hash table: "); +    for (i=0;i<(HASH_TABLE_LEN >> 3)-1;i++){ +      printk("%2.2x:",(u_char)readb(&p->mcast_table[i])); +    } +    printk("%2.2x\n",(u_char)readb(&p->mcast_table[i])); +    printk("\trx_ring at: 0x%8.8x\n",readl(&p->rx_ring)); +    printk("\ttx_ring at: 0x%8.8x\n",readl(&p->tx_ring)); +    printk("dma_buffs: 0x%8.8lx\n",lp->dma_buffs); +    printk("Ring size:\nRX: %d  Log2(rxRingMask): 0x%8.8x\n",  +	   (int)lp->rxRingMask + 1,  +	   lp->rx_rlen); +    printk("TX: %d  Log2(txRingMask): 0x%8.8x\n",  +	   (int)lp->txRingMask + 1,  +	   lp->tx_rlen); +    outw(CSR2,DEPCA_ADDR); +    printk("CSR2&1: 0x%4.4x",inw(DEPCA_DATA)); +    outw(CSR1,DEPCA_ADDR); +    printk("%4.4x\n",inw(DEPCA_DATA)); +    outw(CSR3,DEPCA_ADDR); +    printk("CSR3: 0x%4.4x\n",inw(DEPCA_DATA)); +  } + +  return; +} + +/* +** Perform IOCTL call functions here. Some are privileged operations and the +** effective uid is checked in those cases. +** All MCA IOCTLs will not work here and are for testing purposes only. +*/ +static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ +  struct depca_private *lp = (struct depca_private *)dev->priv; +  struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_data; +  int i, status = 0; +  u_long ioaddr = dev->base_addr; +  union { +    u8  addr[(HASH_TABLE_LEN * ETH_ALEN)]; +    u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; +    u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2]; +  } tmp; + +  switch(ioc->cmd) { +  case DEPCA_GET_HWADDR:             /* Get the hardware address */ +    for (i=0; i<ETH_ALEN; i++) { +      tmp.addr[i] = dev->dev_addr[i]; +    } +    ioc->len = ETH_ALEN; +    if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { +      memcpy_tofs(ioc->data, tmp.addr, ioc->len); +    } + +    break; +  case DEPCA_SET_HWADDR:             /* Set the hardware address */ +    if (suser()) { +      if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) { +	memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN); +	for (i=0; i<ETH_ALEN; i++) { +	  dev->dev_addr[i] = tmp.addr[i]; +	} +	while(dev->tbusy);              /* Stop ring access */ +	set_bit(0, (void*)&dev->tbusy); +	while(lp->tx_old != lp->tx_new);/* Wait for the ring to empty */ + +	STOP_DEPCA;                     /* Temporarily stop the depca.  */ +	depca_init_ring(dev);           /* Initialize the descriptor rings */ +	LoadCSRs(dev);                  /* Reload CSR3 */ +	InitRestartDepca(dev);          /* Resume normal operation. */ +	dev->tbusy = 0;                 /* Unlock the TX ring */ +      } +    } else { +      status = -EPERM; +    } + +    break; +  case DEPCA_SET_PROM:               /* Set Promiscuous Mode */ +    if (suser()) { +      while(dev->tbusy);                /* Stop ring access */ +      set_bit(0, (void*)&dev->tbusy); +      while(lp->tx_old != lp->tx_new);  /* Wait for the ring to empty */ + +      STOP_DEPCA;                       /* Temporarily stop the depca.  */ +      depca_init_ring(dev);             /* Initialize the descriptor rings */ +      lp->init_block.mode |= PROM;      /* Set promiscuous mode */ + +      LoadCSRs(dev);                    /* Reload CSR3 */ +      InitRestartDepca(dev);            /* Resume normal operation. */ +      dev->tbusy = 0;                   /* Unlock the TX ring */ +    } else { +      status = -EPERM; +    } + +    break; +  case DEPCA_CLR_PROM:               /* Clear Promiscuous Mode */ +    if (suser()) { +      while(dev->tbusy);                /* Stop ring access */ +      set_bit(0, (void*)&dev->tbusy); +      while(lp->tx_old != lp->tx_new);  /* Wait for the ring to empty */ + +      STOP_DEPCA;                       /* Temporarily stop the depca.  */ +      depca_init_ring(dev);             /* Initialize the descriptor rings */ +      lp->init_block.mode &= ~PROM;     /* Clear promiscuous mode */ + +      LoadCSRs(dev);                    /* Reload CSR3 */ +      InitRestartDepca(dev);            /* Resume normal operation. */ +      dev->tbusy = 0;                   /* Unlock the TX ring */ +    } else { +      status = -EPERM; +    } + +    break; +  case DEPCA_SAY_BOO:                /* Say "Boo!" to the kernel log file */ +    printk("%s: Boo!\n", dev->name); + +    break; +  case DEPCA_GET_MCA:                /* Get the multicast address table */ +    ioc->len = (HASH_TABLE_LEN >> 3); +    if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { +      memcpy_tofs(ioc->data, lp->init_block.mcast_table, ioc->len);  +    } + +    break; +  case DEPCA_SET_MCA:                /* Set a multicast address */ +    if (suser()) { +      if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) { +	memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len); +	set_multicast_list(dev); +      } +    } else { +      status = -EPERM; +    } + +    break; +  case DEPCA_CLR_MCA:                /* Clear all multicast addresses */ +    if (suser()) { +      set_multicast_list(dev); +    } else { +      status = -EPERM; +    } + +    break; +  case DEPCA_MCA_EN:                 /* Enable pass all multicast addressing */ +    if (suser()) { +      set_multicast_list(dev); +    } else { +      status = -EPERM; +    } + +    break; +  case DEPCA_GET_STATS:              /* Get the driver statistics */ +    cli(); +    ioc->len = sizeof(lp->pktStats); +    if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { +      memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);  +    } +    sti(); + +    break; +  case DEPCA_CLR_STATS:              /* Zero out the driver statistics */ +    if (suser()) { +      cli(); +      memset(&lp->pktStats, 0, sizeof(lp->pktStats)); +      sti(); +    } else { +      status = -EPERM; +    } + +    break; +  case DEPCA_GET_REG:                /* Get the DEPCA Registers */ +    i=0; +    tmp.sval[i++] = inw(DEPCA_NICSR); +    outw(CSR0, DEPCA_ADDR);              /* status register */ +    tmp.sval[i++] = inw(DEPCA_DATA); +    memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init)); +    ioc->len = i+sizeof(struct depca_init); +    if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { +      memcpy_tofs(ioc->data, tmp.addr, ioc->len); +    } + +    break; +  default: +    status = -EOPNOTSUPP; +  } + +  return status; +} + +#ifdef MODULE +static char devicename[9] = { 0, }; +static struct device thisDepca = { +  devicename,  /* device name is inserted by /linux/drivers/net/net_init.c */ +  0, 0, 0, 0, +  0x200, 7,    /* I/O address, IRQ */ +  0, 0, 0, NULL, depca_probe }; + +static int irq=7;	/* EDIT THESE LINE FOR YOUR CONFIGURATION */ +static int io=0x200;    /* Or use the irq= io= options to insmod */ + +/* See depca_probe() for autoprobe messages when a module */	 +int +init_module(void) +{ +  thisDepca.irq=irq; +  thisDepca.base_addr=io; + +  if (register_netdev(&thisDepca) != 0) +    return -EIO; + +  return 0; +} + +void +cleanup_module(void) +{ +  if (thisDepca.priv) { +    kfree(thisDepca.priv); +    thisDepca.priv = NULL; +  } +  thisDepca.irq=0; + +  unregister_netdev(&thisDepca); +  release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE); +} +#endif /* MODULE */ + + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c depca.c" + * + *  compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c depca.c" + * End: + */ diff --git a/linux/src/drivers/net/depca.h b/linux/src/drivers/net/depca.h new file mode 100644 index 0000000..012f739 --- /dev/null +++ b/linux/src/drivers/net/depca.h @@ -0,0 +1,185 @@ +/* +    Written 1994 by David C. Davies. + +    Copyright 1994 David C. Davies. This software may be used and distributed +    according to the terms of the GNU Public License, incorporated herein by +    reference. +*/ + +/* +** I/O addresses. Note that the 2k buffer option is not supported in +** this driver. +*/ +#define DEPCA_NICSR ioaddr+0x00   /* Network interface CSR */ +#define DEPCA_RBI   ioaddr+0x02   /* RAM buffer index (2k buffer mode) */ +#define DEPCA_DATA  ioaddr+0x04   /* LANCE registers' data port */ +#define DEPCA_ADDR  ioaddr+0x06   /* LANCE registers' address port */ +#define DEPCA_HBASE ioaddr+0x08   /* EISA high memory base address reg. */ +#define DEPCA_PROM  ioaddr+0x0c   /* Ethernet address ROM data port */ +#define DEPCA_CNFG  ioaddr+0x0c   /* EISA Configuration port */ +#define DEPCA_RBSA  ioaddr+0x0e   /* RAM buffer starting address (2k buff.) */ + +/* +** These are LANCE registers addressable through DEPCA_ADDR  +*/ +#define CSR0       0 +#define CSR1       1 +#define CSR2       2 +#define CSR3       3 + +/*  +** NETWORK INTERFACE CSR (NI_CSR) bit definitions  +*/ +  +#define TO       	0x0100	/* Time Out for remote boot */ +#define SHE      	0x0080  /* SHadow memory Enable */ +#define BS       	0x0040  /* Bank Select */ +#define BUF      	0x0020	/* BUFfer size (1->32k, 0->64k) */ +#define RBE      	0x0010	/* Remote Boot Enable (1->net boot) */ +#define AAC      	0x0008  /* Address ROM Address Counter (1->enable) */ +#define _128KB      	0x0008  /* 128kB Network RAM (1->enable) */ +#define IM       	0x0004	/* Interrupt Mask (1->mask) */ +#define IEN      	0x0002	/* Interrupt tristate ENable (1->enable) */ +#define LED      	0x0001	/* LED control */ + +/*  +** Control and Status Register 0 (CSR0) bit definitions  +*/ + +#define ERR     	0x8000 	/* Error summary */ +#define BABL    	0x4000 	/* Babble transmitter timeout error  */ +#define CERR    	0x2000 	/* Collision Error */ +#define MISS    	0x1000 	/* Missed packet */ +#define MERR    	0x0800 	/* Memory Error */ +#define RINT    	0x0400 	/* Receiver Interrupt */ +#define TINT    	0x0200 	/* Transmit Interrupt */ +#define IDON    	0x0100 	/* Initialization Done */ +#define INTR    	0x0080 	/* Interrupt Flag */ +#define INEA    	0x0040 	/* Interrupt Enable */ +#define RXON    	0x0020 	/* Receiver on */ +#define TXON    	0x0010 	/* Transmitter on */ +#define TDMD    	0x0008 	/* Transmit Demand */ +#define STOP    	0x0004 	/* Stop */ +#define STRT    	0x0002 	/* Start */ +#define INIT    	0x0001 	/* Initialize */ +#define INTM            0xff00  /* Interrupt Mask */ +#define INTE            0xfff0  /* Interrupt Enable */ + +/* +** CONTROL AND STATUS REGISTER 3 (CSR3) +*/ + +#define BSWP    	0x0004	/* Byte SWaP */ +#define ACON    	0x0002	/* ALE control */ +#define BCON    	0x0001	/* Byte CONtrol */ + +/* +** Initialization Block Mode Register  +*/ + +#define PROM       	0x8000 	/* Promiscuous Mode */ +#define EMBA       	0x0080	/* Enable Modified Back-off Algorithm */ +#define INTL       	0x0040 	/* Internal Loopback */ +#define DRTY       	0x0020 	/* Disable Retry */ +#define COLL       	0x0010 	/* Force Collision */ +#define DTCR       	0x0008 	/* Disable Transmit CRC */ +#define LOOP       	0x0004 	/* Loopback */ +#define DTX        	0x0002 	/* Disable the Transmitter */ +#define DRX        	0x0001 	/* Disable the Receiver */ + +/* +** Receive Message Descriptor 1 (RMD1) bit definitions.  +*/ + +#define R_OWN       0x80000000 	/* Owner bit 0 = host, 1 = lance */ +#define R_ERR     	0x4000 	/* Error Summary */ +#define R_FRAM    	0x2000 	/* Framing Error */ +#define R_OFLO    	0x1000 	/* Overflow Error */ +#define R_CRC     	0x0800 	/* CRC Error */ +#define R_BUFF    	0x0400 	/* Buffer Error */ +#define R_STP     	0x0200 	/* Start of Packet */ +#define R_ENP     	0x0100 	/* End of Packet */ + +/* +** Transmit Message Descriptor 1 (TMD1) bit definitions.  +*/ + +#define T_OWN       0x80000000 	/* Owner bit 0 = host, 1 = lance */ +#define T_ERR     	0x4000 	/* Error Summary */ +#define T_ADD_FCS 	0x2000 	/* More the 1 retry needed to Xmit */ +#define T_MORE    	0x1000	/* >1 retry to transmit packet */ +#define T_ONE     	0x0800 	/* 1 try needed to transmit the packet */ +#define T_DEF     	0x0400 	/* Deferred */ +#define T_STP       0x02000000 	/* Start of Packet */ +#define T_ENP       0x01000000	/* End of Packet */ +#define T_FLAGS     0xff000000  /* TX Flags Field */ + +/* +** Transmit Message Descriptor 3 (TMD3) bit definitions. +*/ + +#define TMD3_BUFF    0x8000	/* BUFFer error */ +#define TMD3_UFLO    0x4000	/* UnderFLOw error */ +#define TMD3_RES     0x2000	/* REServed */ +#define TMD3_LCOL    0x1000	/* Late COLlision */ +#define TMD3_LCAR    0x0800	/* Loss of CARrier */ +#define TMD3_RTRY    0x0400	/* ReTRY error */ + +/*  +** EISA configuration Register (CNFG) bit definitions  +*/ +  +#define TIMEOUT       	0x0100	/* 0:2.5 mins, 1: 30 secs */ +#define REMOTE      	0x0080  /* Remote Boot Enable -> 1 */ +#define IRQ11       	0x0040  /* Enable -> 1 */ +#define IRQ10    	0x0020	/* Enable -> 1 */ +#define IRQ9    	0x0010	/* Enable -> 1 */ +#define IRQ5      	0x0008  /* Enable -> 1 */ +#define BUFF     	0x0004	/* 0: 64kB or 128kB, 1: 32kB */ +#define PADR16   	0x0002	/* RAM on 64kB boundary */ +#define PADR17    	0x0001	/* RAM on 128kB boundary */ + +/* +** Miscellaneous +*/ +#define HASH_TABLE_LEN   64           /* Bits */ +#define HASH_BITS        0x003f       /* 6 LS bits */ + +#define MASK_INTERRUPTS   1 +#define UNMASK_INTERRUPTS 0 + +#define EISA_EN         0x0001        /* Enable EISA bus buffers */ +#define EISA_ID         iobase+0x0080 /* ID long word for EISA card */ +#define EISA_CTRL       iobase+0x0084 /* Control word for EISA card */ + +/* +** Include the IOCTL stuff +*/ +#include <linux/sockios.h> + +#define	DEPCAIOCTL	SIOCDEVPRIVATE + +struct depca_ioctl { +	unsigned short cmd;                /* Command to run */ +	unsigned short len;                /* Length of the data buffer */ +	unsigned char  *data;              /* Pointer to the data buffer */ +}; + +/*  +** Recognised commands for the driver  +*/ +#define DEPCA_GET_HWADDR	0x01 /* Get the hardware address */ +#define DEPCA_SET_HWADDR	0x02 /* Get the hardware address */ +#define DEPCA_SET_PROM  	0x03 /* Set Promiscuous Mode */ +#define DEPCA_CLR_PROM  	0x04 /* Clear Promiscuous Mode */ +#define DEPCA_SAY_BOO	        0x05 /* Say "Boo!" to the kernel log file */ +#define DEPCA_GET_MCA   	0x06 /* Get a multicast address */ +#define DEPCA_SET_MCA   	0x07 /* Set a multicast address */ +#define DEPCA_CLR_MCA    	0x08 /* Clear a multicast address */ +#define DEPCA_MCA_EN    	0x09 /* Enable a multicast address group */ +#define DEPCA_GET_STATS  	0x0a /* Get the driver statistics */ +#define DEPCA_CLR_STATS 	0x0b /* Zero out the driver statistics */ +#define DEPCA_GET_REG   	0x0c /* Get the Register contents */ +#define DEPCA_SET_REG   	0x0d /* Set the Register contents */ +#define DEPCA_DUMP              0x0f /* Dump the DEPCA Status */ + diff --git a/linux/src/drivers/net/e2100.c b/linux/src/drivers/net/e2100.c new file mode 100644 index 0000000..be4185a --- /dev/null +++ b/linux/src/drivers/net/e2100.c @@ -0,0 +1,456 @@ +/* e2100.c: A Cabletron E2100 series ethernet driver for linux. */ +/* +	Written 1993-1994 by Donald Becker. + +	Copyright 1994 by Donald Becker. +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency.  This software may be used and +	distributed according to the terms of the GNU Public License, +	incorporated herein by reference. + +	This is a driver for the Cabletron E2100 series ethercards. + +	The Author may be reached as becker@cesdis.gsfc.nasa.gov, or +	C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	The E2100 series ethercard is a fairly generic shared memory 8390 +	implementation.  The only unusual aspect is the way the shared memory +	registers are set: first you do an inb() in what is normally the +	station address region, and the low three bits of next outb() *address* +	is used	as the write value for that register.  Either someone wasn't +	too used to dem bit en bites, or they were trying to obfuscate the +	programming interface. + +	There is an additional complication when setting the window on the packet +	buffer.  You must first do a read into the packet buffer region with the +	low 8 address bits the address setting the page for the start of the packet +	buffer window, and then do the above operation.  See mem_on() for details. + +	One bug on the chip is that even a hard reset won't disable the memory +	window, usually resulting in a hung machine if mem_off() isn't called. +	If this happens, you must power down the machine for about 30 seconds. +*/ + +static const char *version = +	"e2100.c:v1.01 7/21/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include <asm/io.h> +#include <asm/system.h> + +#include "8390.h" + +static int e21_probe_list[] = {0x300, 0x280, 0x380, 0x220, 0}; + +/* Offsets from the base_addr. +   Read from the ASIC register, and the low three bits of the next outb() +   address is used to set the corresponding register. */ +#define E21_NIC_OFFSET  0		/* Offset to the 8390 NIC. */ +#define E21_ASIC		0x10 +#define E21_MEM_ENABLE	0x10 +#define  E21_MEM_ON		0x05	/* Enable memory in 16 bit mode. */ +#define  E21_MEM_ON_8	0x07	/* Enable memory in  8 bit mode. */ +#define E21_MEM_BASE	0x11	 +#define E21_IRQ_LOW		0x12	/* The low three bits of the IRQ number. */ +#define E21_IRQ_HIGH	0x14	/* The high IRQ bit and media select ...  */ +#define E21_MEDIA		0x14	/* (alias). */ +#define  E21_ALT_IFPORT 0x02	/* Set to use the other (BNC,AUI) port. */ +#define  E21_BIG_MEM	0x04	/* Use a bigger (64K) buffer (we don't) */ +#define E21_SAPROM		0x10	/* Offset to station address data. */ +#define E21_IO_EXTENT	 0x20 + +static inline void mem_on(short port, volatile char *mem_base, +						  unsigned char start_page ) +{ +	/* This is a little weird: set the shared memory window by doing a +	   read.  The low address bits specify the starting page. */ +	mem_base[start_page]; +	inb(port + E21_MEM_ENABLE); +	outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON); +} + +static inline void mem_off(short port) +{ +	inb(port + E21_MEM_ENABLE); +	outb(0x00, port + E21_MEM_ENABLE); +} + +/* In other drivers I put the TX pages first, but the E2100 window circuitry +   is designed to have a 4K Tx region last. The windowing circuitry wraps the +   window at 0x2fff->0x0000 so that the packets at e.g. 0x2f00 in the RX ring +   appear contiguously in the window. */ +#define E21_RX_START_PG		0x00	/* First page of RX buffer */ +#define E21_RX_STOP_PG		0x30	/* Last page +1 of RX ring */ +#define E21_BIG_RX_STOP_PG	0xF0	/* Last page +1 of RX ring */ +#define E21_TX_START_PG		E21_RX_STOP_PG	/* First page of TX buffer */ + +int e2100_probe(struct device *dev); +int e21_probe1(struct device *dev, int ioaddr); + +static int e21_open(struct device *dev); +static void e21_reset_8390(struct device *dev); +static void e21_block_input(struct device *dev, int count, +						   struct sk_buff *skb, int ring_offset); +static void e21_block_output(struct device *dev, int count, +							 const unsigned char *buf, const int start_page); +static void e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +							int ring_page); + +static int e21_close(struct device *dev); + + +/*  Probe for the E2100 series ethercards.  These cards have an 8390 at the +	base address and the station address at both offset 0x10 and 0x18.  I read +	the station address from offset 0x18 to avoid the dataport of NE2000 +	ethercards, and look for Ctron's unique ID (first three octets of the +	station address). + */ + +int e2100_probe(struct device *dev) +{ +	int *port; +	int base_addr = dev->base_addr; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return e21_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (port = e21_probe_list; *port; port++) { +		if (check_region(*port, E21_IO_EXTENT)) +			continue; +		if (e21_probe1(dev, *port) == 0) +			return 0; +	} + +	return ENODEV; +} + +int e21_probe1(struct device *dev, int ioaddr) +{ +	int i, status; +	unsigned char *station_addr = dev->dev_addr; +	static unsigned version_printed = 0; + +	/* First check the station address for the Ctron prefix. */ +	if (inb(ioaddr + E21_SAPROM + 0) != 0x00 +		|| inb(ioaddr + E21_SAPROM + 1) != 0x00 +		|| inb(ioaddr + E21_SAPROM + 2) != 0x1d) +		return ENODEV; + +	/* Verify by making certain that there is a 8390 at there. */ +	outb(E8390_NODMA + E8390_STOP, ioaddr); +	SLOW_DOWN_IO; +	status = inb(ioaddr); +	if (status != 0x21 && status != 0x23) +		return ENODEV; + +	/* Read the station address PROM.  */ +	for (i = 0; i < 6; i++) +		station_addr[i] = inb(ioaddr + E21_SAPROM + i); + +	inb(ioaddr + E21_MEDIA); 		/* Point to media selection. */ +	outb(0, ioaddr + E21_ASIC); 	/* and disable the secondary interface. */ + +	if (ei_debug  &&  version_printed++ == 0) +		printk("%s", version); + +	/* We should have a "dev" from Space.c or the static module table. */ +	if (dev == NULL) { +		printk("e2100.c: Passed a NULL device.\n"); +		dev = init_etherdev(0, 0); +	} + +	printk("%s: E21** at %#3x,", dev->name, ioaddr); +	for (i = 0; i < 6; i++) +		printk(" %02X", station_addr[i]); + +	if (dev->irq < 2) { +		int irqlist[] = {15,11,10,12,5,9,3,4}, i; +		for (i = 0; i < 8; i++) +			if (request_irq (irqlist[i], NULL, 0, "bogus", NULL) != -EBUSY) { +				dev->irq = irqlist[i]; +				break; +			} +		if (i >= 8) { +			printk(" unable to get IRQ %d.\n", dev->irq); +			return EAGAIN; +		} +	} else if (dev->irq == 2)	/* Fixup luser bogosity: IRQ2 is really IRQ9 */ +		dev->irq = 9; + +	/* Allocate dev->priv and fill in 8390 specific dev fields. */ +	if (ethdev_init(dev)) { +		printk (" unable to get memory for dev->priv.\n"); +		return -ENOMEM; +	} + +	/* Grab the region so we can find a different board if IRQ select fails. */ +	request_region(ioaddr, E21_IO_EXTENT, "e2100"); + +	/* The 8390 is at the base address. */ +	dev->base_addr = ioaddr; + +	ei_status.name = "E2100"; +	ei_status.word16 = 1; +	ei_status.tx_start_page = E21_TX_START_PG; +	ei_status.rx_start_page = E21_RX_START_PG; +	ei_status.stop_page = E21_RX_STOP_PG; +	ei_status.saved_irq = dev->irq; + +	/* Check the media port used.  The port can be passed in on the +	   low mem_end bits. */ +	if (dev->mem_end & 15) +		dev->if_port = dev->mem_end & 7; +	else { +		dev->if_port = 0; +		inb(ioaddr + E21_MEDIA); 	/* Turn automatic media detection on. */ +		for(i = 0; i < 6; i++) +			if (station_addr[i] != inb(ioaddr + E21_SAPROM + 8 + i)) { +				dev->if_port = 1; +				break; +			} +	} + +	/* Never map in the E21 shared memory unless you are actively using it. +	   Also, the shared memory has effective only one setting -- spread all +	   over the 128K region! */ +	if (dev->mem_start == 0) +		dev->mem_start = 0xd0000; +	 +#ifdef notdef +	/* These values are unused.  The E2100 has a 2K window into the packet +	   buffer.  The window can be set to start on any page boundary. */ +	dev->rmem_start = dev->mem_start + TX_PAGES*256; +	dev->mem_end = dev->rmem_end = dev->mem_start + 2*1024; +#endif + +	printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq, +		   dev->if_port ? "secondary" : "primary", dev->mem_start); + +	ei_status.reset_8390 = &e21_reset_8390; +	ei_status.block_input = &e21_block_input; +	ei_status.block_output = &e21_block_output; +	ei_status.get_8390_hdr = &e21_get_8390_hdr; +	dev->open = &e21_open; +	dev->stop = &e21_close; +	NS8390_init(dev, 0); + +	return 0; +} + +static int +e21_open(struct device *dev) +{ +	short ioaddr = dev->base_addr; + +	if (request_irq(dev->irq, ei_interrupt, 0, "e2100", NULL)) { +		return EBUSY; +	} +	irq2dev_map[dev->irq] = dev; + +	/* Set the interrupt line and memory base on the hardware. */ +	inb(ioaddr + E21_IRQ_LOW); +	outb(0, ioaddr + E21_ASIC + (dev->irq & 7)); +	inb(ioaddr + E21_IRQ_HIGH); 			/* High IRQ bit, and if_port. */ +	outb(0, ioaddr + E21_ASIC + (dev->irq > 7 ? 1:0) +		   + (dev->if_port ? E21_ALT_IFPORT : 0)); +	inb(ioaddr + E21_MEM_BASE); +	outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7)); + +	ei_open(dev); +	MOD_INC_USE_COUNT; +	return 0; +} + +static void +e21_reset_8390(struct device *dev) +{ +	short ioaddr = dev->base_addr; + +	outb(0x01, ioaddr); +	if (ei_debug > 1) printk("resetting the E2180x3 t=%ld...", jiffies); +	ei_status.txing = 0; + +	/* Set up the ASIC registers, just in case something changed them. */ + +	if (ei_debug > 1) printk("reset done\n"); +	return; +} + +/* Grab the 8390 specific header. We put the 2k window so the header page +   appears at the start of the shared memory. */ + +static void +e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + +	short ioaddr = dev->base_addr; +	char *shared_mem = (char *)dev->mem_start; + +	mem_on(ioaddr, shared_mem, ring_page); + +#ifdef notdef +	/* Officially this is what we are doing, but the readl() is faster */ +	memcpy_fromio(hdr, shared_mem, sizeof(struct e8390_pkt_hdr)); +#else +	((unsigned int*)hdr)[0] = readl(shared_mem); +#endif + +	/* Turn off memory access: we would need to reprogram the window anyway. */ +	mem_off(ioaddr); + +} + +/*  Block input and output are easy on shared memory ethercards. +	The E21xx makes block_input() especially easy by wrapping the top +	ring buffer to the bottom automatically. */ +static void +e21_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +	short ioaddr = dev->base_addr; +	char *shared_mem = (char *)dev->mem_start; + +	mem_on(ioaddr, shared_mem, (ring_offset>>8)); + +	/* Packet is always in one chunk -- we can copy + cksum. */ +	eth_io_copy_and_sum(skb, dev->mem_start + (ring_offset & 0xff), count, 0); + +	mem_off(ioaddr); +} + +static void +e21_block_output(struct device *dev, int count, const unsigned char *buf, +				 const int start_page) +{ +	short ioaddr = dev->base_addr; +	volatile char *shared_mem = (char *)dev->mem_start; + +	/* Set the shared memory window start by doing a read, with the low address +	   bits specifying the starting page. */ +	readb(shared_mem + start_page); +	mem_on(ioaddr, shared_mem, start_page); + +	memcpy_toio(shared_mem, buf, count); +	mem_off(ioaddr); +} + +static int +e21_close(struct device *dev) +{ +	short ioaddr = dev->base_addr; + +	if (ei_debug > 1) +		printk("%s: Shutting down ethercard.\n", dev->name); + +	free_irq(dev->irq, NULL); +	dev->irq = ei_status.saved_irq; + +	/* Shut off the interrupt line and secondary interface. */ +	inb(ioaddr + E21_IRQ_LOW); +	outb(0, ioaddr + E21_ASIC); +	inb(ioaddr + E21_IRQ_HIGH); 			/* High IRQ bit, and if_port. */ +	outb(0, ioaddr + E21_ASIC); + +	irq2dev_map[dev->irq] = NULL; + +	ei_close(dev); + +	/* Double-check that the memory has been turned off, because really +	   really bad things happen if it isn't. */ +	mem_off(ioaddr); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +#ifdef HAVE_DEVLIST +struct netdev_entry e21_drv = +{"e21", e21_probe1, E21_IO_EXTENT, e21_probe_list}; +#endif + + +#ifdef MODULE +#define MAX_E21_CARDS	4	/* Max number of E21 cards per module */ +#define NAMELEN		8	/* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_E21_CARDS] = { 0, }; +static struct device dev_e21[MAX_E21_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int io[MAX_E21_CARDS] = { 0, }; +static int irq[MAX_E21_CARDS]  = { 0, }; +static int mem[MAX_E21_CARDS] = { 0, }; +static int xcvr[MAX_E21_CARDS] = { 0, };		/* choose int. or ext. xcvr */ + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) { +		struct device *dev = &dev_e21[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->mem_start = mem[this_dev]; +		dev->mem_end = xcvr[this_dev];	/* low 4bits = xcvr sel. */ +		dev->init = e2100_probe; +		if (io[this_dev] == 0)  { +			if (this_dev != 0) break; /* only autoprobe 1st one */ +			printk(KERN_NOTICE "e2100.c: Presently autoprobing (not recommended) for a single card.\n"); +		} +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]); +			if (found != 0) return 0;	/* Got at least one. */ +			return -ENXIO; +		} +		found++; +	} + +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) { +		struct device *dev = &dev_e21[this_dev]; +		if (dev->priv != NULL) { +			/* NB: e21_close() handles free_irq + irq2dev map */ +			kfree(dev->priv); +			dev->priv = NULL; +			release_region(dev->base_addr, E21_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c e2100.c" + *  version-control: t + *  tab-width: 4 + *  kept-new-versions: 5 + * End: + */ diff --git a/linux/src/drivers/net/eepro.c b/linux/src/drivers/net/eepro.c new file mode 100644 index 0000000..3d4fc57 --- /dev/null +++ b/linux/src/drivers/net/eepro.c @@ -0,0 +1,1407 @@ +/* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */ +/* +	Written 1994-1998 by Bao C. Ha. + +	Copyright (C) 1994-1998 by Bao C. Ha. + +	This software may be used and distributed +	according to the terms of the GNU Public License, +	incorporated herein by reference. + +	The author may be reached at bao@hacom.net  +	or Hacom, 2477 Wrightsboro Rd., Augusta, GA 30904. + +	Things remaining to do: +	Better record keeping of errors. +	Eliminate transmit interrupt to reduce overhead. +	Implement "concurrent processing". I won't be doing it! + +	Bugs: + +	If you have a problem of not detecting the 82595 during a +	reboot (warm reset), disable the FLASH memory should fix it. +	This is a compatibility hardware problem. + +	Versions: + +	0.10c	Some cosmetic changes. (9/28/98, BCH) + +        0.10b	Should work now with (some) Pro/10+. At least for  +        	me (and my two cards) it does. _No_ guarantee for  +        	function with non-Pro/10+ cards! (don't have any) +        	(RMC, 9/11/96) + +	0.10	Added support for the Etherexpress Pro/10+.  The +		IRQ map was changed significantly from the old +		pro/10.  The new interrupt map was provided by +		Rainer M. Canavan (Canavan@Zeus.cs.bonn.edu). +		(BCH, 9/3/96) + +	0.09	Fixed a race condition in the transmit algorithm, +		which causes crashes under heavy load with fast +		pentium computers.  The performance should also +		improve a bit.  The size of RX buffer, and hence +		TX buffer, can also be changed via lilo or insmod. +		(BCH, 7/31/96) + +	0.08	Implement 32-bit I/O for the 82595TX and 82595FX +		based lan cards.  Disable full-duplex mode if TPE +		is not used.  (BCH, 4/8/96) + +	0.07a	Fix a stat report which counts every packet as a +		heart-beat failure. (BCH, 6/3/95) + +	0.07	Modified to support all other 82595-based lan cards.   +		The IRQ vector of the EtherExpress Pro will be set +		according to the value saved in the EEPROM.  For other +		cards, I will do autoirq_request() to grab the next +		available interrupt vector. (BCH, 3/17/95) + +	0.06a,b	Interim released.  Minor changes in the comments and +		print out format. (BCH, 3/9/95 and 3/14/95) + +	0.06	First stable release that I am comfortable with. (BCH, +		3/2/95)	 + +	0.05	Complete testing of multicast. (BCH, 2/23/95)	 + +	0.04	Adding multicast support. (BCH, 2/14/95)	 + +	0.03	First widely alpha release for public testing.  +		(BCH, 2/14/95)	 + +*/ + +static const char *version = +	"eepro.c: v0.10c 9/28/98 Bao C. Ha (bao@hacom.net)\n"; + +#include <linux/module.h> + +/* +  Sources: + +	This driver wouldn't have been written without the availability  +	of the Crynwr's Lan595 driver source code.  It helps me to  +	familiarize with the 82595 chipset while waiting for the Intel  +	documentation.  I also learned how to detect the 82595 using  +	the packet driver's technique. + +	This driver is written by cutting and pasting the skeleton.c driver +	provided by Donald Becker.  I also borrowed the EEPROM routine from +	Donald Becker's 82586 driver. + +	Datasheet for the Intel 82595 (including the TX and FX version). It  +	provides just enough info that the casual reader might think that it  +	documents the i82595. + +	The User Manual for the 82595.  It provides a lot of the missing +	information. + +*/ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +/* First, a few definitions that the brave might change. */ + +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int eepro_portlist[] = +   { 0x300, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, 0}; + +/* use 0 for production, 1 for verification, >2 for debug */ + +#ifndef NET_DEBUG +#define NET_DEBUG 1 +#endif + +static unsigned int net_debug = NET_DEBUG; + +/* The number of low I/O ports used by the ethercard. */ + +#define EEPRO_IO_EXTENT	16 + +/* Different 82595 chips */ + +#define	LAN595		0 +#define	LAN595TX	1 +#define	LAN595FX	2 + +/* Information that need to be kept for each board. */ +struct eepro_local { +	struct enet_statistics stats; +	unsigned rx_start; +	unsigned tx_start; /* start of the transmit chain */ +	int tx_last;  /* pointer to last packet in the transmit chain */ +	unsigned tx_end;   /* end of the transmit chain (plus 1) */ +	int eepro;	/* 1 for the EtherExpress Pro/10, +			   2 for the EtherExpress Pro/10+, +			   0 for other 82595-based lan cards. */ +	int version;	/* a flag to indicate if this is a TX or FX +				   version of the 82595 chip. */ +	int stepping; +}; + +/* The station (ethernet) address prefix, used for IDing the board. */ + +#define SA_ADDR0 0x00	/* Etherexpress Pro/10 */ +#define SA_ADDR1 0xaa +#define SA_ADDR2 0x00 + +#define SA2_ADDR0 0x00	/* Etherexpress Pro/10+ */ +#define SA2_ADDR1 0xa0 +#define SA2_ADDR2 0xc9 + +#define SA3_ADDR0 0x00	/* more Etherexpress Pro/10+ */ +#define SA3_ADDR1 0xaa +#define SA3_ADDR2 0x00 +#define SA3_ADDR3 0xc9 + +/* Index to functions, as function prototypes. */ + +extern int eepro_probe(struct device *dev);	 + +static int	eepro_probe1(struct device *dev, short ioaddr); +static int	eepro_open(struct device *dev); +static int	eepro_send_packet(struct sk_buff *skb, struct device *dev); +static void	eepro_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void 	eepro_rx(struct device *dev); +static void 	eepro_transmit_interrupt(struct device *dev); +static int	eepro_close(struct device *dev); +static struct enet_statistics *eepro_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + +static int read_eeprom(int ioaddr, int location); +static void hardware_send_packet(struct device *dev, void *buf, short length); +static int	eepro_grab_irq(struct device *dev); + +/* +  			Details of the i82595. + +You will need either the datasheet or the user manual to understand what +is going on here.  The 82595 is very different from the 82586, 82593. + +The receive algorithm in eepro_rx() is just an implementation of the +RCV ring structure that the Intel 82595 imposes at the hardware level. +The receive buffer is set at 24K, and the transmit buffer is 8K.  I +am assuming that the total buffer memory is 32K, which is true for the +Intel EtherExpress Pro/10.  If it is less than that on a generic card, +the driver will be broken. + +The transmit algorithm in the hardware_send_packet() is similar to the +one in the eepro_rx().  The transmit buffer is a ring linked list. +I just queue the next available packet to the end of the list.  In my +system, the 82595 is so fast that the list seems to always contain a +single packet.  In other systems with faster computers and more congested +network traffics, the ring linked list should improve performance by +allowing up to 8K worth of packets to be queued. + +The sizes of the receive and transmit buffers can now be changed via lilo  +or insmod.  Lilo uses the appended line "ether=io,irq,debug,rx-buffer,eth0" +where rx-buffer is in KB unit.  Modules uses the parameter mem which is +also in KB unit, for example "insmod io=io-address irq=0 mem=rx-buffer."   +The receive buffer has to be more than 3K or less than 29K.  Otherwise, +it is reset to the default of 24K, and, hence, 8K for the trasnmit +buffer (transmit-buffer = 32K - receive-buffer). + +*/ + +#define	RAM_SIZE	0x8000 +#define	RCV_HEADER	8 +#define RCV_RAM         0x6000  /* 24KB default for RCV buffer */ +#define RCV_LOWER_LIMIT 0x00    /* 0x0000 */ + +/* #define RCV_UPPER_LIMIT ((RCV_RAM - 2) >> 8) */    /* 0x5ffe */ +#define RCV_UPPER_LIMIT (((rcv_ram) - 2) >> 8)    + +/* #define XMT_RAM         (RAM_SIZE - RCV_RAM) */    /* 8KB for XMT buffer */ +#define XMT_RAM         (RAM_SIZE - (rcv_ram))    /* 8KB for XMT buffer */ + +/* #define XMT_LOWER_LIMIT (RCV_RAM >> 8) */  /* 0x6000 */ +#define XMT_LOWER_LIMIT ((rcv_ram) >> 8)  +#define XMT_UPPER_LIMIT ((RAM_SIZE - 2) >> 8)   /* 0x7ffe */ +#define	XMT_HEADER	8 + +#define	RCV_DONE	0x0008 +#define	RX_OK		0x2000 +#define	RX_ERROR	0x0d81 + +#define	TX_DONE_BIT	0x0080 +#define	CHAIN_BIT	0x8000 +#define	XMT_STATUS	0x02 +#define	XMT_CHAIN	0x04 +#define	XMT_COUNT	0x06 + +#define	BANK0_SELECT	0x00		 +#define	BANK1_SELECT	0x40		 +#define	BANK2_SELECT	0x80		 + +/* Bank 0 registers */ + +#define	COMMAND_REG	0x00	/* Register 0 */ +#define	MC_SETUP	0x03 +#define	XMT_CMD		0x04 +#define	DIAGNOSE_CMD	0x07 +#define	RCV_ENABLE_CMD	0x08 +#define	RCV_DISABLE_CMD	0x0a +#define	STOP_RCV_CMD	0x0b +#define	RESET_CMD	0x0e +#define	POWER_DOWN_CMD	0x18 +#define	RESUME_XMT_CMD	0x1c +#define	SEL_RESET_CMD	0x1e +#define	STATUS_REG	0x01	/* Register 1 */ +#define	RX_INT		0x02 +#define	TX_INT		0x04 +#define	EXEC_STATUS	0x30 +#define	ID_REG		0x02	/* Register 2	*/ +#define	R_ROBIN_BITS	0xc0	/* round robin counter */ +#define	ID_REG_MASK	0x2c +#define	ID_REG_SIG	0x24 +#define	AUTO_ENABLE	0x10 +#define	INT_MASK_REG	0x03	/* Register 3	*/ +#define	RX_STOP_MASK	0x01 +#define	RX_MASK		0x02 +#define	TX_MASK		0x04 +#define	EXEC_MASK	0x08 +#define	ALL_MASK	0x0f +#define	IO_32_BIT	0x10 +#define	RCV_BAR		0x04	/* The following are word (16-bit) registers */ +#define	RCV_STOP	0x06 +#define	XMT_BAR		0x0a +#define	HOST_ADDRESS_REG	0x0c +#define	IO_PORT		0x0e +#define	IO_PORT_32_BIT	0x0c + +/* Bank 1 registers */ + +#define	REG1	0x01 +#define	WORD_WIDTH	0x02 +#define	INT_ENABLE	0x80 +#define INT_NO_REG	0x02 +#define	RCV_LOWER_LIMIT_REG	0x08 +#define	RCV_UPPER_LIMIT_REG	0x09 +#define	XMT_LOWER_LIMIT_REG	0x0a +#define	XMT_UPPER_LIMIT_REG	0x0b + +/* Bank 2 registers */ + +#define	XMT_Chain_Int	0x20	/* Interrupt at the end of the transmit chain */ +#define	XMT_Chain_ErrStop	0x40 /* Interrupt at the end of the chain even if there are errors */ +#define	RCV_Discard_BadFrame	0x80 /* Throw bad frames away, and continue to receive others */ +#define	REG2		0x02 +#define	PRMSC_Mode	0x01 +#define	Multi_IA	0x20 +#define	REG3		0x03 +#define	TPE_BIT		0x04 +#define	BNC_BIT		0x20 +#define	REG13		0x0d +#define	FDX		0x00 +#define	A_N_ENABLE	0x02 + +#define	I_ADD_REG0	0x04 +#define	I_ADD_REG1	0x05 +#define	I_ADD_REG2	0x06 +#define	I_ADD_REG3	0x07 +#define	I_ADD_REG4	0x08 +#define	I_ADD_REG5	0x09 + +#define EEPROM_REG 0x0a +#define EESK 0x01 +#define EECS 0x02 +#define EEDI 0x04 +#define EEDO 0x08 + +/* Check for a network adaptor of this type, and return '0' if one exists. + +   If dev->base_addr == 0, probe all likely locations. +   If dev->base_addr == 1, always return failure. +   If dev->base_addr == 2, allocate space for the device and return success +   (detachable devices only). + +   */ + +#ifdef HAVE_DEVLIST + +/* Support for a alternate probe manager, which will eliminate the +   boilerplate below. */ + +struct netdev_entry netcard_drv = +{"eepro", eepro_probe1, EEPRO_IO_EXTENT, eepro_portlist}; + +#else + +int  +eepro_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return eepro_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; eepro_portlist[i]; i++) { +		int ioaddr = eepro_portlist[i]; +		if (check_region(ioaddr, EEPRO_IO_EXTENT)) +			continue; + +		if (eepro_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +/* This is the real probe routine.  Linux has a history of friendly device +   probes on the ISA bus.  A good device probes avoids doing writes, and +   verifies that the correct device exists and functions.  */ + +int  +eepro_probe1(struct device *dev, short ioaddr) +{ +	unsigned short station_addr[6], id, counter; +	int i; +	int eepro; +	const char *ifmap[] = {"AUI", "10Base2", "10BaseT"}; +	enum iftype { AUI=0, BNC=1, TPE=2 }; + +	/* Now, we are going to check for the signature of the +	   ID_REG (register 2 of bank 0) */ +	if (((id=inb(ioaddr + ID_REG)) & ID_REG_MASK) == ID_REG_SIG) { + +		/* We seem to have the 82595 signature, let's +		   play with its counter (last 2 bits of +		   register 2 of bank 0) to be sure. */ + +		counter = (id & R_ROBIN_BITS);	 +		if (((id=inb(ioaddr+ID_REG)) & R_ROBIN_BITS) ==  +			(counter + 0x40)) { + +			/* Yes, the 82595 has been found */ + +			/* Now, get the ethernet hardware address from +			   the EEPROM */ + +			station_addr[0] = read_eeprom(ioaddr, 2); +			station_addr[1] = read_eeprom(ioaddr, 3); +			station_addr[2] = read_eeprom(ioaddr, 4); + +			/* Check the station address for the manufacturer's code */ + +			if ((station_addr[2] == 0x00aa) && (station_addr[1]!= 0x00c9)) { +				eepro = 1; +				printk("%s: Intel EtherExpress Pro/10 ISA at %#x,",  +					dev->name, ioaddr); +			} else +			if ( (station_addr[2] == 0x00a0) +			     || ((station_addr[2] == 0x00aa) && (station_addr[1] == 0x00c9) )) { +				eepro = 2; +				printk("%s: Intel EtherExpress Pro/10+ ISA\n at %#x,",  +					dev->name, ioaddr); +			} +			else { +				eepro = 0; +				printk("%s: Intel 82595-based lan card at %#x,",  +					dev->name, ioaddr); +			} + +   			/* Fill in the 'dev' fields. */ +			dev->base_addr = ioaddr; + +			for (i=0; i < 6; i++) { +				dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i]; +				printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]); +			} + +			if ((dev->mem_end & 0x3f) < 3 ||	/* RX buffer must be more than 3K */ +				(dev->mem_end & 0x3f) > 29)	/* and less than 29K */ +				dev->mem_end = RCV_RAM;		/* or it will be set to 24K */ +			else dev->mem_end = 1024*dev->mem_end;  /* Maybe I should shift << 10 */ + +			/* From now on, dev->mem_end contains the actual size of rx buffer */ + +			if (net_debug > 3) +				printk(", %dK RCV buffer", (int)(dev->mem_end)/1024); + +			outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ +			id = inb(ioaddr + REG3); +			if (id & TPE_BIT) +				dev->if_port = TPE; +			else dev->if_port = BNC; + +			if (net_debug>3)  +				printk("id: %x\n", id); + +			if (dev->irq < 2 && eepro) { +				i = read_eeprom(ioaddr, 1); +				if (eepro == 1) +				   switch (i & 0x07) { +					case 0:	dev->irq = 9; break; +					case 1:	dev->irq = 3; break; +					case 2:	dev->irq = 5; break; +					case 3:	dev->irq = 10; break; +					case 4:	dev->irq = 11; break; +					default: /* should never get here !!!!! */ +						printk(" illegal interrupt vector stored in EEPROM.\n"); +						return ENODEV; +					} +				else switch (i & 0x07) { +					case 0:	dev->irq = 3; break; +					case 1:	dev->irq = 4; break; +					case 2:	dev->irq = 5; break; +					case 3:	dev->irq = 7; break; +					case 4:	dev->irq = 9; break; +					case 5:	dev->irq = 10; break; +					case 6:	dev->irq = 11; break; +					case 7:	dev->irq = 12; break; +					} +				} +			else if (dev->irq == 2) +				dev->irq = 9; + +			if (dev->irq > 2) { +				printk(", IRQ %d, %s.\n", dev->irq, +						ifmap[dev->if_port]); +				if (request_irq(dev->irq, &eepro_interrupt, 0, "eepro", NULL)) { +					printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq); +					return -EAGAIN; +				} +			} +			else printk(", %s.\n", ifmap[dev->if_port]); + +			if ((dev->mem_start & 0xf) > 0)	/* I don't know if this is */ +				net_debug = dev->mem_start & 7; /* still useful or not */ + +			if (net_debug > 3) { +				i = read_eeprom(ioaddr, 5); +				if (i & 0x2000) /* bit 13 of EEPROM word 5 */ +					printk("%s: Concurrent Processing is enabled but not used!\n", +						dev->name); +			} + +			if (net_debug)  +				printk("%s", version); + +			/* Grab the region so we can find another board if autoIRQ fails. */ +			request_region(ioaddr, EEPRO_IO_EXTENT, "eepro"); + +			/* Initialize the device structure */ +			dev->priv = kmalloc(sizeof(struct eepro_local), GFP_KERNEL); +			if (dev->priv == NULL) +				return -ENOMEM; +			memset(dev->priv, 0, sizeof(struct eepro_local)); + +			dev->open = eepro_open; +			dev->stop = eepro_close; +			dev->hard_start_xmit = eepro_send_packet; +			dev->get_stats = eepro_get_stats; +			dev->set_multicast_list = &set_multicast_list; + +			/* Fill in the fields of the device structure with +			   ethernet generic values */ + +			ether_setup(dev); + +			outb(RESET_CMD, ioaddr); /* RESET the 82595 */ + +			return 0; +			} +		else return ENODEV; +		} +	else if (net_debug > 3) +		printk ("EtherExpress Pro probed failed!\n"); +	return ENODEV; +} + +/* Open/initialize the board.  This is called (in the current kernel) +   sometime after booting when the 'ifconfig' program is run. + +   This routine should set everything up anew at each open, even +   registers that "should" only need to be set once at boot, so that +   there is non-reboot way to recover if something goes wrong. +   */ + +static char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1}; +static char irqrmap2[] = {-1,-1,4,0,1,2,-1,3,-1,4,5,6,7,-1,-1,-1}; + +static int	 +eepro_grab_irq(struct device *dev) +{ +	int irqlist[] = { 3, 4, 5, 7, 9, 10, 11, 12 };	 +	int *irqp = irqlist, temp_reg, ioaddr = dev->base_addr; + +	outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */ + +	/* Enable the interrupt line. */ +	temp_reg = inb(ioaddr + REG1); +	outb(temp_reg | INT_ENABLE, ioaddr + REG1);  + +	outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */ + +	/* clear all interrupts */ +	outb(ALL_MASK, ioaddr + STATUS_REG);  + +	/* Let EXEC event to interrupt */ +	outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG);  + +	do { +		outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */ +		temp_reg = inb(ioaddr + INT_NO_REG); +		outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG);  +		outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ +		if (request_irq (*irqp, NULL, 0, "bogus", NULL) != EBUSY) { +			/* Twinkle the interrupt, and check if it's seen */ +			autoirq_setup(0); +			outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */ +				 +			if (*irqp == autoirq_report(2) &&  /* It's a good IRQ line */ +				(request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro", NULL) == 0))  +					break; +			/* clear all interrupts */ +			outb(ALL_MASK, ioaddr + STATUS_REG);  +		} +	} while (*++irqp); + +	outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */ + +	/* Disable the physical interrupt line. */ +	temp_reg = inb(ioaddr + REG1); +	outb(temp_reg & 0x7f, ioaddr + REG1);  +	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ + +	/* Mask all the interrupts. */ +	outb(ALL_MASK, ioaddr + INT_MASK_REG);  + +	/* clear all interrupts */ +	outb(ALL_MASK, ioaddr + STATUS_REG);  + +	return dev->irq; +} + +static int +eepro_open(struct device *dev) +{ +	unsigned short temp_reg, old8, old9; +	int i, ioaddr = dev->base_addr, rcv_ram = dev->mem_end; +	struct eepro_local *lp = (struct eepro_local *)dev->priv; + +	if (net_debug > 3) +		printk("eepro: entering eepro_open routine.\n"); + +	if ((dev->dev_addr[0] == SA_ADDR0 && +			dev->dev_addr[1] == SA_ADDR1 && +			dev->dev_addr[2] == SA_ADDR2)&& +			(dev->dev_addr[3] != SA3_ADDR3)) +		{  +			lp->eepro = 1; +			if (net_debug > 3) printk("p->eepro = 1;\n");  +		}  /* Yes, an Intel EtherExpress Pro/10 */ + +	else if ((dev->dev_addr[0] == SA2_ADDR0 && +			dev->dev_addr[1] == SA2_ADDR1 && +			dev->dev_addr[2] == SA2_ADDR2)|| +		(dev->dev_addr[0] == SA3_ADDR0 && +			dev->dev_addr[1] == SA3_ADDR1 && +			dev->dev_addr[2] == SA3_ADDR2 && +			dev->dev_addr[3] == SA3_ADDR3)) +		{ +		  	lp->eepro = 2; /* Yes, an Intel EtherExpress Pro/10+ */ +		  	if (net_debug > 3) printk("p->eepro = 2;\n");  +		} +        +	else lp->eepro = 0; /* No, it is a generic 82585 lan card */ + +	/* Get the interrupt vector for the 82595 */	 +	if (dev->irq < 2 && eepro_grab_irq(dev) == 0) { +		printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq); +		return -EAGAIN; +	} +				 +	if (irq2dev_map[dev->irq] != 0 +		|| (irq2dev_map[dev->irq] = dev) == 0) +		return -EAGAIN; + +	/* Initialize the 82595. */ +	outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ +	temp_reg = inb(ioaddr + EEPROM_REG); +	lp->stepping = temp_reg >> 5;	/* Get the stepping number of the 595 */ +	 +	if (net_debug > 3) +		printk("The stepping of the 82595 is %d\n", lp->stepping); +	if (temp_reg & 0x10) /* Check the TurnOff Enable bit */ +		outb(temp_reg & 0xef, ioaddr + EEPROM_REG); +	for (i=0; i < 6; i++)  +		outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i);  +			 +	temp_reg = inb(ioaddr + REG1);    /* Setup Transmit Chaining */ +	outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */ +		| RCV_Discard_BadFrame, ioaddr + REG1);   +	temp_reg = inb(ioaddr + REG2); /* Match broadcast */ +	outb(temp_reg | 0x14, ioaddr + REG2); +	temp_reg = inb(ioaddr + REG3); +	outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */ + +	/* Set the receiving mode */ +	outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */ + +	/* Set the interrupt vector */	 +	temp_reg = inb(ioaddr + INT_NO_REG); +	 +	if (lp->eepro == 2) +		outb((temp_reg & 0xf8) | irqrmap2[dev->irq], ioaddr + INT_NO_REG);  +	else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);  +     +	temp_reg = inb(ioaddr + INT_NO_REG); +     +	if (lp->eepro == 2) +       		outb((temp_reg & 0xf0) | irqrmap2[dev->irq] | 0x08,ioaddr+INT_NO_REG); +    	else outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG); +     +	if (net_debug > 3) +       		printk("eepro_open: content of INT Reg is %x\n", temp_reg); +        +        +	/* Initialize the RCV and XMT upper and lower limits */ +	outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG);  +	outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG);  +	outb(XMT_LOWER_LIMIT, ioaddr + XMT_LOWER_LIMIT_REG);  +	outb(XMT_UPPER_LIMIT, ioaddr + XMT_UPPER_LIMIT_REG);  + +	/* Enable the interrupt line. */ +	temp_reg = inb(ioaddr + REG1); +	outb(temp_reg | INT_ENABLE, ioaddr + REG1);  +	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ +	 +	/* Let RX and TX events to interrupt */ +	outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);  +	 +	/* clear all interrupts */ +	outb(ALL_MASK, ioaddr + STATUS_REG);  +	 +	/* Initialize RCV */ +	outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR);  +	lp->rx_start = (RCV_LOWER_LIMIT << 8) ; +	outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP);  +	 +	/* Initialize XMT */ +	outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR);  +	 +	/* Check for the i82595TX and i82595FX */ +	old8 = inb(ioaddr + 8); +	outb(~old8, ioaddr + 8); +	 +	if ((temp_reg = inb(ioaddr + 8)) == old8) { +		if (net_debug > 3) +			printk("i82595 detected!\n"); +		lp->version = LAN595; +	} +	else { +		lp->version = LAN595TX; +		outb(old8, ioaddr + 8); +		old9 = inb(ioaddr + 9); +		outb(~old9, ioaddr + 9); +	 +		if (((temp_reg = inb(ioaddr + 9)) == ( (~old9)&0xff) )) { +			enum iftype { AUI=0, BNC=1, TPE=2 }; +	 +			if (net_debug > 3) { +			        printk("temp_reg: %#x  ~old9: %#x\n",temp_reg, ~old9); +				printk("i82595FX detected!\n"); +			} +	 +			lp->version = LAN595FX; +			outb(old9, ioaddr + 9); +	 +			if (dev->if_port != TPE) {	/* Hopefully, this will fix the +							problem of using Pentiums and +							pro/10 w/ BNC. */ +				outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ +				temp_reg = inb(ioaddr + REG13); +	 +				/* disable the full duplex mode since it is not +				applicable with the 10Base2 cable. */ +				outb(temp_reg & ~(FDX | A_N_ENABLE), REG13); +				outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */ +			} +		} +		else if (net_debug > 3) { +		        printk("temp_reg: %#x  ~old9: %#x\n",temp_reg,((~old9)&0xff)); +			printk("i82595TX detected!\n"); +		} +	} +	 +	outb(SEL_RESET_CMD, ioaddr); +	 +	/* We are supposed to wait for 2 us after a SEL_RESET */ +	SLOW_DOWN_IO; +	SLOW_DOWN_IO;	 +	 +	lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */ +	lp->tx_last = 0;   +	 +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; +	 +	if (net_debug > 3) +		printk("eepro: exiting eepro_open routine.\n"); +	 +	outb(RCV_ENABLE_CMD, ioaddr); +	MOD_INC_USE_COUNT; +	 +	return 0; +} + +static int +eepro_send_packet(struct sk_buff *skb, struct device *dev) +{ +	struct eepro_local *lp = (struct eepro_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int rcv_ram = dev->mem_end; + +	if (net_debug > 5) +		printk("eepro: entering eepro_send_packet routine.\n"); +	 +	if (dev->tbusy) { +		/* If we get here, some higher level has decided we are broken. +		   There should really be a "kick me" function call instead. */ +	 +		int tickssofar = jiffies - dev->trans_start; +	 +		if (tickssofar < 40) +			return 1; +	 +		if (net_debug > 1) +			printk("%s: transmit timed out, %s?\n", dev->name, +				   "network cable problem"); +	 +		lp->stats.tx_errors++; +	 +		/* Try to restart the adaptor. */ +		outb(SEL_RESET_CMD, ioaddr);  +	 +		/* We are supposed to wait for 2 us after a SEL_RESET */ +		SLOW_DOWN_IO; +		SLOW_DOWN_IO; +	 +		/* Do I also need to flush the transmit buffers here? YES? */ +		lp->tx_start = lp->tx_end = rcv_ram;  +		lp->tx_last = 0; +	 +		dev->tbusy=0; +		dev->trans_start = jiffies; +		outb(RCV_ENABLE_CMD, ioaddr); +	} +	 +	/* If some higher layer thinks we've missed an tx-done interrupt +	   we are passed NULL. Caution: dev_tint() handles the cli()/sti() +	   itself. */ +	 +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} +	 +	/* Block a timer-based transmit from overlapping. */ +	 +	if (set_bit(0, (void*)&dev->tbusy) != 0) +		printk("%s: Transmitter access conflict.\n", dev->name); +	else { +		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		unsigned char *buf = skb->data; +		hardware_send_packet(dev, buf, length); +		dev->trans_start = jiffies; +	} +	 +	dev_kfree_skb (skb, FREE_WRITE); +	 +	/* You might need to clean up and record Tx statistics here. */ +	/* lp->stats.tx_aborted_errors++; */ +	 +	if (net_debug > 5) +		printk("eepro: exiting eepro_send_packet routine.\n"); +	 +	return 0; +} + +/*	The typical workload of the driver: +	Handle the network interface interrupts. */ + +static void +eepro_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +	struct device *dev = (struct device *)(irq2dev_map[irq]); +	int ioaddr, status, boguscount = 20; + +	if (net_debug > 5) +		printk("eepro: entering eepro_interrupt routine.\n"); +	 +	if (dev == NULL) { +		printk ("eepro_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +	 +	dev->interrupt = 1; +	 +	ioaddr = dev->base_addr; +	 +	do {  +		status = inb(ioaddr + STATUS_REG); +		 +		if (status & RX_INT) { +			if (net_debug > 4) +				printk("eepro: packet received interrupt.\n"); +			/* Acknowledge the RX_INT */ +			outb(RX_INT, ioaddr + STATUS_REG);  +			/* Get the received packets */ +			eepro_rx(dev); +		} +		else if (status & TX_INT) { +			if (net_debug > 4) +				printk("eepro: packet transmit interrupt.\n"); +			/* Acknowledge the TX_INT */ +			outb(TX_INT, ioaddr + STATUS_REG);  +			/* Process the status of transmitted packets */ +			eepro_transmit_interrupt(dev); +		} +	 +	} while ((boguscount-- > 0) && (status & 0x06)); +	 +	dev->interrupt = 0; +		 +	if (net_debug > 5) +		printk("eepro: exiting eepro_interrupt routine.\n"); +	 +	return; +} + +static int +eepro_close(struct device *dev) +{ +	struct eepro_local *lp = (struct eepro_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int rcv_ram = dev->mem_end; +	short temp_reg; + +	dev->tbusy = 1; +	dev->start = 0; +	 +	outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */ +	 +	/* Disable the physical interrupt line. */ +	temp_reg = inb(ioaddr + REG1); +	outb(temp_reg & 0x7f, ioaddr + REG1);  +	outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */ +	 +	/* Flush the Tx and disable Rx. */ +	outb(STOP_RCV_CMD, ioaddr);  +	 +	lp->tx_start = lp->tx_end = rcv_ram ; +	lp->tx_last = 0;   +	 +	/* Mask all the interrupts. */ +	outb(ALL_MASK, ioaddr + INT_MASK_REG);  +	 +	/* clear all interrupts */ +	outb(ALL_MASK, ioaddr + STATUS_REG);  +	 +	/* Reset the 82595 */ +	outb(RESET_CMD, ioaddr);  +	 +	/* release the interrupt */ +	free_irq(dev->irq, NULL); +	irq2dev_map[dev->irq] = 0; +	 +	/* Update the statistics here. What statistics? */ +	/* We are supposed to wait for 200 us after a RESET */ +	SLOW_DOWN_IO; +	SLOW_DOWN_IO; /* May not be enough? */ +	MOD_DEC_USE_COUNT; +	 +	return 0; +} + +/* Get the current statistics.	This may be called with the card open or +   closed. */ +static struct enet_statistics * +eepro_get_stats(struct device *dev) +{ +	struct eepro_local *lp = (struct eepro_local *)dev->priv; +	return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + */ + +static void +set_multicast_list(struct device *dev) +{ +	struct eepro_local *lp = (struct eepro_local *)dev->priv; +	short ioaddr = dev->base_addr; +	unsigned short mode; +	struct dev_mc_list *dmi=dev->mc_list; +	 +	if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63)  +	{ +		/* +		 *	We must make the kernel realise we had to move +		 *	into promisc mode or we start all out war on +		 *	the cable. If it was a promisc request the +		 *	flag is already set. If not we assert it. +		 */ +		dev->flags|=IFF_PROMISC;		 +		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ +		mode = inb(ioaddr + REG2); +		outb(mode | PRMSC_Mode, ioaddr + REG2);	 +		mode = inb(ioaddr + REG3); +		outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ +		outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ +		printk("%s: promiscuous mode enabled.\n", dev->name); +	}  +	 +	else if (dev->mc_count==0 )  +	{ +		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ +		mode = inb(ioaddr + REG2); +		outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */ +		mode = inb(ioaddr + REG3); +		outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ +		outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ +	} +	 +	else  +	{ +		unsigned short status, *eaddrs; +		int i, boguscount = 0; +		 +		/* Disable RX and TX interrupts.  Necessary to avoid +		   corruption of the HOST_ADDRESS_REG by interrupt +		   service routines. */ +		outb(ALL_MASK, ioaddr + INT_MASK_REG); +		outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */ +		mode = inb(ioaddr + REG2); +		outb(mode | Multi_IA, ioaddr + REG2);	 +		mode = inb(ioaddr + REG3); +		outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */ +		outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */ +		outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG); +		outw(MC_SETUP, ioaddr + IO_PORT); +		outw(0, ioaddr + IO_PORT); +		outw(0, ioaddr + IO_PORT); +		outw(6*(dev->mc_count + 1), ioaddr + IO_PORT); +	 +		for (i = 0; i < dev->mc_count; i++)  +		{ +			eaddrs=(unsigned short *)dmi->dmi_addr; +			dmi=dmi->next; +			outw(*eaddrs++, ioaddr + IO_PORT); +			outw(*eaddrs++, ioaddr + IO_PORT); +			outw(*eaddrs++, ioaddr + IO_PORT); +		} +	 +		eaddrs = (unsigned short *) dev->dev_addr; +		outw(eaddrs[0], ioaddr + IO_PORT); +		outw(eaddrs[1], ioaddr + IO_PORT); +		outw(eaddrs[2], ioaddr + IO_PORT); +		outw(lp->tx_end, ioaddr + XMT_BAR); +		outb(MC_SETUP, ioaddr); +	 +		/* Update the transmit queue */ +		i = lp->tx_end + XMT_HEADER + 6*(dev->mc_count + 1); +	 +		if (lp->tx_start != lp->tx_end)  +		{  +			/* update the next address and the chain bit in the  +			   last packet */ +			outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG); +			outw(i, ioaddr + IO_PORT); +			outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG); +			status = inw(ioaddr + IO_PORT); +			outw(status | CHAIN_BIT, ioaddr + IO_PORT); +			lp->tx_end = i ; +		} +		else { +			lp->tx_start = lp->tx_end = i ; +		} +	 +		/* Acknowledge that the MC setup is done */ +		do { /* We should be doing this in the eepro_interrupt()! */ +			SLOW_DOWN_IO; +			SLOW_DOWN_IO; +	 +			if (inb(ioaddr + STATUS_REG) & 0x08)  +			{ +				i = inb(ioaddr); +				outb(0x08, ioaddr + STATUS_REG); +	 +				if (i & 0x20) { /* command ABORTed */ +					printk("%s: multicast setup failed.\n",  +						dev->name); +					break; +				} else if ((i & 0x0f) == 0x03)	{ /* MC-Done */ +					printk("%s: set Rx mode to %d addresses.\n",  +						dev->name, dev->mc_count); +					break; +				} +			} +		} while (++boguscount < 100); +	 +		/* Re-enable RX and TX interrupts */ +		outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);  +	 +	} +	outb(RCV_ENABLE_CMD, ioaddr); +} + +/* The horrible routine to read a word from the serial EEPROM. */ +/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */ +/* The delay between EEPROM clock transitions. */ + +#define eeprom_delay()	{ int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} +#define EE_READ_CMD (6 << 6) + +int +read_eeprom(int ioaddr, int location) +{ +	int i; +	unsigned short retval = 0; +	short ee_addr = ioaddr + EEPROM_REG; +	int read_cmd = location | EE_READ_CMD; +	short ctrl_val = EECS ; +	 +	outb(BANK2_SELECT, ioaddr); +	outb(ctrl_val, ee_addr); +	 +	/* Shift the read command bits out. */ +	for (i = 8; i >= 0; i--) { +		short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI +			: ctrl_val; +		outb(outval, ee_addr); +		outb(outval | EESK, ee_addr);	/* EEPROM clock tick. */ +		eeprom_delay(); +		outb(outval, ee_addr);	/* Finish EEPROM a clock tick. */ +		eeprom_delay(); +	} +	outb(ctrl_val, ee_addr); +	 +	for (i = 16; i > 0; i--) { +		outb(ctrl_val | EESK, ee_addr);	 eeprom_delay(); +		retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); +		outb(ctrl_val, ee_addr);  eeprom_delay(); +	} +	/* Terminate the EEPROM access. */ +	ctrl_val &= ~EECS; +	outb(ctrl_val | EESK, ee_addr); +	eeprom_delay(); +	outb(ctrl_val, ee_addr); +	eeprom_delay(); +	outb(BANK0_SELECT, ioaddr); +	return retval; +} + +static void +hardware_send_packet(struct device *dev, void *buf, short length) +{ +	struct eepro_local *lp = (struct eepro_local *)dev->priv; +	short ioaddr = dev->base_addr; +	int rcv_ram = dev->mem_end; +	unsigned status, tx_available, last, end, boguscount = 100; + +	if (net_debug > 5) +		printk("eepro: entering hardware_send_packet routine.\n"); +	 +	while (boguscount-- > 0) { +	 +		/* Disable RX and TX interrupts.  Necessary to avoid +	   	corruption of the HOST_ADDRESS_REG by interrupt +	   	service routines. */ +		outb(ALL_MASK, ioaddr + INT_MASK_REG); +	 +		if (dev->interrupt == 1) {   +			/* Enable RX and TX interrupts */ +			outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);  +			continue; +		} +	 +		/* determine how much of the transmit buffer space is available */ +		if (lp->tx_end > lp->tx_start) +			tx_available = XMT_RAM - (lp->tx_end - lp->tx_start); +		else if (lp->tx_end < lp->tx_start) +			tx_available = lp->tx_start - lp->tx_end; +		else tx_available = XMT_RAM; +	 +		if (((((length + 3) >> 1) << 1) + 2*XMT_HEADER)  +			>= tx_available)   /* No space available ??? */ +			{ +			eepro_transmit_interrupt(dev); /* Clean up the transmiting queue */ +			/* Enable RX and TX interrupts */ +			outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);  +			continue; +		} +	 +		last = lp->tx_end; +		end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; +		if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */ +	 +			if ((RAM_SIZE - last) <= XMT_HEADER) {	 +			/* Arrrr!!!, must keep the xmt header together, +			  several days were lost to chase this one down. */ +				last = rcv_ram; +				end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; +			}	 + +			else end = rcv_ram + (end - RAM_SIZE); +		} + +		outw(last, ioaddr + HOST_ADDRESS_REG); +		outw(XMT_CMD, ioaddr + IO_PORT); +		outw(0, ioaddr + IO_PORT); +		outw(end, ioaddr + IO_PORT); +		outw(length, ioaddr + IO_PORT); +	 +		if (lp->version == LAN595) +			outsw(ioaddr + IO_PORT, buf, (length + 3) >> 1); +	 +		else {	/* LAN595TX or LAN595FX, capable of 32-bit I/O processing */ +			unsigned short temp = inb(ioaddr + INT_MASK_REG); +			outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG); +			outsl(ioaddr + IO_PORT_32_BIT, buf, (length + 3) >> 2); +			outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG); +		} +	 +		/* A dummy read to flush the DRAM write pipeline */ +		status = inw(ioaddr + IO_PORT);  +	 +		if (lp->tx_start == lp->tx_end) {	 +			outw(last, ioaddr + XMT_BAR); +			outb(XMT_CMD, ioaddr); +			lp->tx_start = last;   /* I don't like to change tx_start here */ +		} +		else { +			/* update the next address and the chain bit in the  +		   	last packet */ + +			if (lp->tx_end != last) { +				outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG); +				outw(last, ioaddr + IO_PORT); +			} +	 +			outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG); +			status = inw(ioaddr + IO_PORT); +			outw(status | CHAIN_BIT, ioaddr + IO_PORT); +	 +			/* Continue the transmit command */ +			outb(RESUME_XMT_CMD, ioaddr); +		} +		lp->tx_last = last; +		lp->tx_end = end; +	 +		/* Enable RX and TX interrupts */ +		outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);  +	 +		if (dev->tbusy) { +			dev->tbusy = 0; +		} +	 +		if (net_debug > 5) +			printk("eepro: exiting hardware_send_packet routine.\n"); +	 +		return; +	} +	dev->tbusy = 1; +	 +	if (net_debug > 5) +		printk("eepro: exiting hardware_send_packet routine.\n"); +} + +static void +eepro_rx(struct device *dev) +{ +	struct eepro_local *lp = (struct eepro_local *)dev->priv; +	short ioaddr = dev->base_addr, rcv_ram = dev->mem_end; +	short boguscount = 20; +	short rcv_car = lp->rx_start; +	unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size; + +	if (net_debug > 5) +		printk("eepro: entering eepro_rx routine.\n"); +	 +	/* Set the read pointer to the start of the RCV */ +	outw(rcv_car, ioaddr + HOST_ADDRESS_REG); +	 +	rcv_event = inw(ioaddr + IO_PORT); +	while (rcv_event == RCV_DONE) { + +		rcv_status = inw(ioaddr + IO_PORT); +		rcv_next_frame = inw(ioaddr + IO_PORT); +		rcv_size = inw(ioaddr + IO_PORT); +	 +		if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) { +	 +			/* Malloc up new buffer. */ +			struct sk_buff *skb; +			rcv_size &= 0x3fff; +			skb = dev_alloc_skb(rcv_size+5); +	 +			if (skb == NULL) { +				printk("%s: Memory squeeze, dropping packet.\n", dev->name); +				lp->stats.rx_dropped++; +				break; +			} +	 +			skb->dev = dev; +			skb_reserve(skb,2); +	 +			if (lp->version == LAN595) +				insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 3) >> 1); +	 +			else {	/* LAN595TX or LAN595FX, capable of 32-bit I/O processing */ +				unsigned short temp = inb(ioaddr + INT_MASK_REG); +				outb(temp | IO_32_BIT, ioaddr + INT_MASK_REG); +				insl(ioaddr+IO_PORT_32_BIT, skb_put(skb,rcv_size), (rcv_size + 3) >> 2); +				outb(temp & ~(IO_32_BIT), ioaddr + INT_MASK_REG); +			} +	 +			skb->protocol = eth_type_trans(skb,dev);	 +			netif_rx(skb); +			lp->stats.rx_packets++; +		} +	 +		else { /* Not sure will ever reach here,  +			  I set the 595 to discard bad received frames */ +			lp->stats.rx_errors++; +	 +			if (rcv_status & 0x0100) +				lp->stats.rx_over_errors++; +	 +			else if (rcv_status & 0x0400) +				lp->stats.rx_frame_errors++; +	 +			else if (rcv_status & 0x0800) +				lp->stats.rx_crc_errors++; +	 +			printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n",  +				dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size); +		} +	 +		if (rcv_status & 0x1000) +			lp->stats.rx_length_errors++; +	 +		if (--boguscount == 0) +			break; +	 +		rcv_car = lp->rx_start + RCV_HEADER + rcv_size; +		lp->rx_start = rcv_next_frame; +		outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG); +		rcv_event = inw(ioaddr + IO_PORT); +	}  +	if (rcv_car == 0) +		rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff; +	 +	outw(rcv_car - 1, ioaddr + RCV_STOP); +	 +	if (net_debug > 5) +		printk("eepro: exiting eepro_rx routine.\n"); +} + +static void +eepro_transmit_interrupt(struct device *dev) +{ +	struct eepro_local *lp = (struct eepro_local *)dev->priv; +	short ioaddr = dev->base_addr; +	short boguscount = 20;  +	short xmt_status; + +	while (lp->tx_start != lp->tx_end) {  +	 +		outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG); +		xmt_status = inw(ioaddr+IO_PORT); +	 +		if ((xmt_status & TX_DONE_BIT) == 0) break; +	 +		xmt_status = inw(ioaddr+IO_PORT); +		lp->tx_start = inw(ioaddr+IO_PORT); +		dev->tbusy = 0; +		mark_bh(NET_BH); +	 +		if (xmt_status & 0x2000) +			lp->stats.tx_packets++;  +		else { +			lp->stats.tx_errors++; +			if (xmt_status & 0x0400) +				lp->stats.tx_carrier_errors++; +			printk("%s: XMT status = %#x\n", +				dev->name, xmt_status); +		} +	 +		if (xmt_status & 0x000f) { +			lp->stats.collisions += (xmt_status & 0x000f); +		} +	 +		if ((xmt_status & 0x0040) == 0x0) { +			lp->stats.tx_heartbeat_errors++; +		} +	 +		if (--boguscount == 0) +			break;   +	} +} + +#ifdef MODULE + +static char devicename[9] = { 0, }; +static struct device dev_eepro = { +	devicename, /* device name is inserted by linux/drivers/net/net_init.c */ +	0, 0, 0, 0, +	0, 0, +	0, 0, 0, NULL, eepro_probe }; +static int io = 0x200; +static int irq = 0; +static int mem = (RCV_RAM/1024);	/* Size of the rx buffer in KB */ + +int  +init_module(void) +{ +	if (io == 0) +		printk("eepro: You should not use auto-probing with insmod!\n"); + +	dev_eepro.base_addr = io; +	dev_eepro.irq       = irq; +	dev_eepro.mem_end   = mem; + +	if (register_netdev(&dev_eepro) != 0) +		return -EIO; + +	return 0; +} + +void +cleanup_module(void) +{ +	unregister_netdev(&dev_eepro); + +	kfree_s(dev_eepro.priv,sizeof(struct eepro_local)); +	dev_eepro.priv=NULL; + +	/* If we don't do this, we can't re-insmod it later. */ +	release_region(dev_eepro.base_addr, EEPRO_IO_EXTENT); +} +#endif /* MODULE */ diff --git a/linux/src/drivers/net/eepro100.c b/linux/src/drivers/net/eepro100.c new file mode 100644 index 0000000..d03462c --- /dev/null +++ b/linux/src/drivers/net/eepro100.c @@ -0,0 +1,2155 @@ +/* drivers/net/eepro100.c: An Intel i82557-559 Ethernet driver for Linux. */ +/* +	Written 1998-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This driver is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	This driver is for the Intel EtherExpress Pro100 (Speedo3) design. +	It should work with all i82557/558/559 boards. + +	To use as a module, use the compile-command at the end of the file. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	For updates see +		http://www.scyld.com/network/eepro100.html +	For installation instructions +		http://www.scyld.com/network/modules.html +	The information and support mailing lists are based at +		http://www.scyld.com/mailman/listinfo/ +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"eepro100.c:v1.28 7/22/2003 Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/eepro100.html\n"; + + +/* The user-configurable values. +   These may be modified when a driver module is loaded. +   The first five are undocumented and spelled per Intel recommendations. +*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +static int congenb = 0;		/* Enable congestion control in the DP83840. */ +static int txfifo = 8;		/* Tx FIFO threshold in 4 byte units, 0-15 */ +static int rxfifo = 8;		/* Rx FIFO threshold, default 32 bytes. */ +/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */ +static int txdmacount = 128; +static int rxdmacount = 0; + +/* Set the copy breakpoint for the copy-only-tiny-frame Rx method. +   Lower values use more memory, but are faster. +   Setting to > 1518 disables this feature. */ +static int rx_copybreak = 200; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */ +static int multicast_filter_limit = 64; + +/* Used to pass the media type, etc. +   Both 'options[]' and 'full_duplex[]' should exist for driver +   interoperability, however setting full_duplex[] is deprecated. +   The media type is usually passed in 'options[]'. +    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +    Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +    Use option values 0x20 and 0x200 for forcing full duplex operation. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* The ring sizes should be a power of two for efficiency. */ +#define TX_RING_SIZE	32		/* Effectively 2 entries fewer. */ +#define RX_RING_SIZE	32 +/* Actual number of TX packets queued, must be <= TX_RING_SIZE-2. */ +#define TX_QUEUE_LIMIT  12 +#define TX_QUEUE_UNFULL 8		/* Hysteresis marking queue as no longer full. */ + +/* Operational parameters that usually are not changed. */ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#if LINUX_VERSION_CODE >= 0x20300 +#include <linux/spinlock.h> +#elif LINUX_VERSION_CODE >= 0x20200 +#include <asm/spinlock.h> +#endif + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed bus+endian portability operations. */ +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Intel PCI EtherExpressPro 100 driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(congenb, "i"); +MODULE_PARM(txfifo, "i"); +MODULE_PARM(rxfifo, "i"); +MODULE_PARM(txdmacount, "i"); +MODULE_PARM(rxdmacount, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(multicast_filter_limit, "i"); +#ifdef MODULE_PARM_DESC +MODULE_PARM_DESC(debug, "EEPro100 message level (0-31)"); +MODULE_PARM_DESC(options, +				 "EEPro100: force fixed speed+duplex 0x10 0x20 0x100 0x200"); +MODULE_PARM_DESC(max_interrupt_work, +				 "EEPro100 maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, "EEPro100 set to forced full duplex when not 0" +				 " (deprecated)"); +MODULE_PARM_DESC(rx_copybreak, +				 "EEPro100 copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "EEPro100 breakpoint for switching to Rx-all-multicast"); +/* Other settings are undocumented per Intel recommendation. */ +#endif + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's +single-chip fast Ethernet controller for PCI, as used on the Intel +EtherExpress Pro 100 adapter. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board.  The system BIOS should be set to assign the +PCI INTA signal to an otherwise unused system IRQ line.  While it's +possible to share PCI interrupt lines, it negatively impacts performance and +only recent kernels support it. + +III. Driver operation + +IIIA. General +The Speedo3 is very similar to other Intel network chips, that is to say +"apparently designed on a different planet".  This chips retains the complex +Rx and Tx descriptors and multiple buffers pointers as previous chips, but +also has simplified Tx and Rx buffer modes.  This driver uses the "flexible" +Tx mode, but in a simplified lower-overhead manner: it associates only a +single buffer descriptor with each frame descriptor. + +Despite the extra space overhead in each receive skbuff, the driver must use +the simplified Rx buffer mode to assure that only a single data buffer is +associated with each RxFD. The driver implements this by reserving space +for the Rx descriptor at the head of each Rx skbuff. + +The Speedo-3 has receive and command unit base addresses that are added to +almost all descriptor pointers.  The driver sets these to zero, so that all +pointer fields are absolute addresses. + +The System Control Block (SCB) of some previous Intel chips exists on the +chip in both PCI I/O and memory space.  This driver uses the I/O space +registers, but might switch to memory mapped mode to better support non-x86 +processors. + +IIIB. Transmit structure + +The driver must use the complex Tx command+descriptor mode in order to +have a indirect pointer to the skbuff data section.  Each Tx command block +(TxCB) is associated with two immediately appended Tx Buffer Descriptor +(TxBD).  A fixed ring of these TxCB+TxBD pairs are kept as part of the +speedo_private data structure for each adapter instance. + +The i82558 and later explicitly supports this structure, and can read the two +TxBDs in the same PCI burst as the TxCB. + +This ring structure is used for all normal transmit packets, but the +transmit packet descriptors aren't long enough for most non-Tx commands such +as CmdConfigure.  This is complicated by the possibility that the chip has +already loaded the link address in the previous descriptor.  So for these +commands we convert the next free descriptor on the ring to a NoOp, and point +that descriptor's link to the complex command. + +An additional complexity of these non-transmit commands are that they may be +added asynchronous to the normal transmit queue, so we set a lock +whenever the Tx descriptor ring is manipulated. + +A notable aspect of these special configure commands is that they do +work with the normal Tx ring entry scavenge method.  The Tx ring scavenge +is done at interrupt time using the 'dirty_tx' index, and checking for the +command-complete bit.  While the setup frames may have the NoOp command on the +Tx ring marked as complete, but not have completed the setup command, this +is not a problem.  The tx_ring entry can be still safely reused, as the +tx_skbuff[] entry is always empty for config_cmd and mc_setup frames. + +Commands may have bits set e.g. CmdSuspend in the command word to either +suspend or stop the transmit/command unit.  This driver always initializes +the current command with CmdSuspend before erasing the CmdSuspend in the +previous command, and only then issues a CU_RESUME. + +Note: In previous generation Intel chips, restarting the command unit was a +notoriously slow process.  This is presumably no longer true. + +IIIC. Receive structure + +Because of the bus-master support on the Speedo3 this driver uses the +SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer. +This scheme allocates full-sized skbuffs as receive buffers.  The value +SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to +trade-off the memory wasted by passing the full-sized skbuff to the queue +layer for all frames vs. the copying cost of copying a frame to a +correctly-sized skbuff. + +For small frames the copying cost is negligible (esp. considering that we +are pre-loading the cache with immediately useful header information), so we +allocate a new, minimally-sized skbuff.  For large frames the copying cost +is non-trivial, and the larger copy might flush the cache of useful data, so +we pass up the skbuff the packet was received into. + +IIID. Synchronization +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'sp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so +we can't avoid the interrupt overhead by having the Tx routine reap the Tx +stats.)	 After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero.	 Iff the 'sp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +IV. Notes + +Thanks to Steve Williams of Intel for arranging the non-disclosure agreement +that stated that I could disclose the information.  But I still resent +having to sign an Intel NDA when I'm helping Intel sell their own product! + +*/ + +/* This table drives the PCI probe routines. */ +static void *speedo_found1(struct pci_dev *pdev, void *init_dev, +						   long ioaddr, int irq, int chip_idx, int fnd_cnt); +static int speedo_pwr_event(void *dev_instance, int event); +enum chip_capability_flags { ResetMII=1, HasChksum=2}; + +/* I/O registers beyond 0x18 do not exist on the i82557. */ +#ifdef USE_IO_OPS +#define SPEEDO_IOTYPE   PCI_USES_MASTER|PCI_USES_IO|PCI_ADDR1 +#define SPEEDO_SIZE		32 +#else +#define SPEEDO_IOTYPE   PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR0 +#define SPEEDO_SIZE		0x1000 +#endif + +struct pci_id_info static pci_id_tbl[] = { +	{"Intel PCI EtherExpress Pro100 82865",		{ 0x12278086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel PCI EtherExpress Pro100 Smart (i960RP/RD)", +	 { 0x12288086, 0xffffffff,}, SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel i82559 rev 8",			{ 0x12298086, ~0, 0,0, 8,0xff}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, HasChksum, }, +	{"Intel PCI EtherExpress Pro100",			{ 0x12298086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel EtherExpress Pro/100+ i82559ER",	{ 0x12098086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, ResetMII, }, +	{"Intel EtherExpress Pro/100 type 1029",	{ 0x10298086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel EtherExpress Pro/100 type 1030",	{ 0x10308086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 V Network",					{ 0x24498086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel PCI LAN0 Controller 82801E",		{ 0x24598086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel PCI LAN1 Controller 82801E",		{ 0x245D8086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VE (type 1031)",			{ 0x10318086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VE (type 1032)",			{ 0x10328086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VE (type 1033)",			{ 0x10338086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VE (type 1034)",			{ 0x10348086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VE (type 1035)",			{ 0x10358086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VM (type 1038)",			{ 0x10388086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VM (type 1039)",			{ 0x10398086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VM (type 103a)",			{ 0x103a8086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"HP/Compaq D510 Intel Pro/100 VM", +	 { 0x103b8086, 0xffffffff, 0x00120e11, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VM (type 103b)",			{ 0x103b8086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VE (type 103D)",			{ 0x103d8086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VE (type 103E)",			{ 0x103e8086, 0xffffffff,}, +	 SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel EtherExpress Pro/100 865G Northbridge type 1051", +	 { 0x10518086, 0xffffffff,}, SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel PCI to PCI Bridge EtherExpress Pro100 Server Adapter", +	 { 0x52008086, 0xffffffff,}, SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel PCI EtherExpress Pro100 Server Adapter", +	 { 0x52018086, 0xffffffff,}, SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 VM (unknown type series 1030)", +	 { 0x10308086, 0xfff0ffff,}, SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{"Intel Pro/100 (unknown type series 1050)", +	 { 0x10508086, 0xfff0ffff,}, SPEEDO_IOTYPE, SPEEDO_SIZE, 0, }, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info eepro100_drv_id = { +	"eepro100", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	speedo_found1, speedo_pwr_event, }; + +#ifndef USE_IO_OPS +#undef inb +#undef inw +#undef inl +#undef outb +#undef outw +#undef outl +#define inb readb +#define inw readw +#define inl readl +#define outb writeb +#define outw writew +#define outl writel +#endif + +/* Offsets to the various registers. +   All accesses need not be longword aligned. */ +enum speedo_offsets { +	SCBStatus = 0, SCBCmd = 2,	/* Rx/Command Unit command and status. */ +	SCBPointer = 4,				/* General purpose pointer. */ +	SCBPort = 8,				/* Misc. commands and operands.  */ +	SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */ +	SCBCtrlMDI = 16,			/* MDI interface control. */ +	SCBEarlyRx = 20,			/* Early receive byte count. */ +}; +/* Commands that can be put in a command list entry. */ +enum commands { +	CmdNOp = 0, CmdIASetup = 0x10000, CmdConfigure = 0x20000, +	CmdMulticastList = 0x30000, CmdTx = 0x40000, CmdTDR = 0x50000, +	CmdDump = 0x60000, CmdDiagnose = 0x70000, +	CmdSuspend = 0x40000000,	/* Suspend after completion. */ +	CmdIntr = 0x20000000,		/* Interrupt after completion. */ +	CmdTxFlex = 0x00080000,		/* Use "Flexible mode" for CmdTx command. */ +}; +/* Do atomically if possible. */ +#if defined(__i386__) +#define clear_suspend(cmd)   ((char *)(&(cmd)->cmd_status))[3] &= ~0x40 +#elif defined(__alpha__) || defined(__x86_64) || defined(__ia64) +#define clear_suspend(cmd)   clear_bit(30, &(cmd)->cmd_status) +#elif defined(__powerpc__) || defined(__sparc__) || (__BIG_ENDIAN) +#define clear_suspend(cmd)	clear_bit(6, &(cmd)->cmd_status) +#else +#warning Undefined architecture. +#define clear_suspend(cmd)	(cmd)->cmd_status &= cpu_to_le32(~CmdSuspend) +#endif + +enum SCBCmdBits { +	SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000, +	SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400, +	SCBTriggerIntr=0x0200, SCBMaskAll=0x0100, +	/* The rest are Rx and Tx commands. */ +	CUStart=0x0010, CUResume=0x0020, CUHiPriStart=0x0030, CUStatsAddr=0x0040, +	CUShowStats=0x0050, +	CUCmdBase=0x0060,  /* CU Base address (set to zero) . */ +	CUDumpStats=0x0070, /* Dump then reset stats counters. */ +	CUHiPriResume=0x00b0, /* Resume for the high priority Tx queue. */ +	RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006, +	RxResumeNoResources=0x0007, +}; + +enum intr_status_bits { +	IntrCmdDone=0x8000,  IntrRxDone=0x4000, IntrCmdIdle=0x2000, +	IntrRxSuspend=0x1000, IntrMIIDone=0x0800, IntrDrvrIntr=0x0400, +	IntrAllNormal=0xfc00, +}; + +enum SCBPort_cmds { +	PortReset=0, PortSelfTest=1, PortPartialReset=2, PortDump=3, +}; + +/* The Speedo3 Rx and Tx frame/buffer descriptors. */ +struct descriptor {			/* A generic descriptor. */ +	s32 cmd_status;			/* All command and status fields. */ +	u32 link;					/* struct descriptor *  */ +	unsigned char params[0]; +}; + +/* The Speedo3 Rx and Tx buffer descriptors. */ +struct RxFD {					/* Receive frame descriptor. */ +	s32 status; +	u32 link;					/* struct RxFD * */ +	u32 rx_buf_addr;			/* void * */ +	u32 count; +}; + +/* Selected elements of the Tx/RxFD.status word. */ +enum RxFD_bits { +	RxComplete=0x8000, RxOK=0x2000, +	RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200, RxErrSymbol=0x0010, +	RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002, +	TxUnderrun=0x1000,  StatusComplete=0x8000, +}; + +struct TxFD {					/* Transmit frame descriptor set. */ +	s32 status; +	u32 link;					/* void * */ +	u32 tx_desc_addr;			/* Always points to the tx_buf_addr element. */ +	s32 count;					/* # of TBD (=1), Tx start thresh., etc. */ +	/* This constitutes two "TBD" entries. Non-zero-copy uses only one. */ +	u32 tx_buf_addr0;			/* void *, frame to be transmitted.  */ +	s32 tx_buf_size0;			/* Length of Tx frame. */ +	u32 tx_buf_addr1;			/* Used only for zero-copy data section. */ +	s32 tx_buf_size1;			/* Length of second data buffer (0). */ +}; + +/* Elements of the dump_statistics block. This block must be lword aligned. */ +struct speedo_stats { +	u32 tx_good_frames; +	u32 tx_coll16_errs; +	u32 tx_late_colls; +	u32 tx_underruns; +	u32 tx_lost_carrier; +	u32 tx_deferred; +	u32 tx_one_colls; +	u32 tx_multi_colls; +	u32 tx_total_colls; +	u32 rx_good_frames; +	u32 rx_crc_errs; +	u32 rx_align_errs; +	u32 rx_resource_errs; +	u32 rx_overrun_errs; +	u32 rx_colls_errs; +	u32 rx_runt_errs; +	u32 done_marker; +}; + +/* Do not change the position (alignment) of the first few elements! +   The later elements are grouped for cache locality. */ +struct speedo_private { +	struct TxFD	tx_ring[TX_RING_SIZE];	/* Commands (usually CmdTxPacket). */ +	struct RxFD *rx_ringp[RX_RING_SIZE];	/* Rx descriptor, used as ring. */ +	struct speedo_stats lstats;			/* Statistics and self-test region */ + +	/* The addresses of a Tx/Rx-in-place packets/buffers. */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; + +	/* Transmit and other commands control. */ +	struct descriptor  *last_cmd;	/* Last command sent. */ +	unsigned int cur_tx, dirty_tx;	/* The ring entries to be free()ed. */ +	spinlock_t lock;				/* Group with Tx control cache line. */ +	u32 tx_threshold;					/* The value for txdesc.count. */ +	unsigned long last_cmd_time; + +	/* Rx control, one cache line. */ +	struct RxFD *last_rxf;				/* Most recent Rx frame. */ +	unsigned int cur_rx, dirty_rx;		/* The next free ring entry */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	long last_rx_time;			/* Last Rx, in jiffies, to handle Rx hang. */ +	int rx_copybreak; + +	int msg_level; +	int max_interrupt_work; +	struct net_device *next_module; +	void *priv_addr;					/* Unaligned address for kfree */ +	struct net_device_stats stats; +	int alloc_failures; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	unsigned char acpi_pwr; +	struct timer_list timer;	/* Media selection timer. */ +	/* Multicast filter command. */ +	int mc_setup_frm_len;			 	/* The length of an allocated.. */ +	struct descriptor *mc_setup_frm; 	/* ..multicast setup frame. */ +	int mc_setup_busy;					/* Avoid double-use of setup frame. */ +	int multicast_filter_limit; + +	int in_interrupt;					/* Word-aligned dev->interrupt */ +	int rx_mode;						/* Current PROMISC/ALLMULTI setting. */ +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int flow_ctrl:1;			/* Use 802.3x flow control. */ +	unsigned int rx_bug:1;				/* Work around receiver hang errata. */ +	unsigned int rx_bug10:1;			/* Receiver might hang at 10mbps. */ +	unsigned int rx_bug100:1;			/* Receiver might hang at 100mbps. */ +	unsigned int polling:1;				/* Hardware blocked interrupt line. */ +	unsigned int medialock:1;			/* The media speed/duplex is fixed. */ +	unsigned char default_port;			/* Last dev->if_port value. */ +	unsigned short phy[2];				/* PHY media interfaces available. */ +	unsigned short advertising;			/* Current PHY advertised caps. */ +	unsigned short partner;				/* Link partner caps. */ +	long last_reset; +}; + +/* Our internal RxMode state, not tied to the hardware bits. */ +enum rx_mode_bits { +	AcceptAllMulticast=0x01, AcceptAllPhys=0x02,  +	AcceptErr=0x80, AcceptRunt=0x10, +	AcceptBroadcast=0x08, AcceptMulticast=0x04, +	AcceptMyPhys=0x01, RxInvalidMode=0x7f +}; + +/* The parameters for a CmdConfigure operation. +   There are so many options that it would be difficult to document each bit. +   We mostly use the default or recommended settings. */ +const char i82557_config_cmd[22] = { +	22, 0x08, 0, 0,  0, 0, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */ +	0, 0x2E, 0,  0x60, 0, +	0xf2, 0x48,   0, 0x40, 0xf2, 0x80, 		/* 0x40=Force full-duplex */ +	0x3f, 0x05, }; +const char i82558_config_cmd[22] = { +	22, 0x08, 0, 1,  0, 0, 0x22, 0x03,  1, /* 1=Use MII  0=Use AUI */ +	0, 0x2E, 0,  0x60, 0x08, 0x88, +	0x68, 0, 0x40, 0xf2, 0xBD, 		/* 0xBD->0xFD=Force full-duplex */ +	0x31, 0x05, }; + +/* PHY media interface chips, defined by the databook. */ +static const char *phys[] = { +	"None", "i82553-A/B", "i82553-C", "i82503", +	"DP83840", "80c240", "80c24", "i82555", +	"unknown-8", "unknown-9", "DP83840A", "unknown-11", +	"unknown-12", "unknown-13", "unknown-14", "unknown-15", }; +enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240, +					 S80C24, I82555, DP83840A=10, }; +static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 }; + +/* Standard serial configuration EEPROM commands. */ +#define EE_READ_CMD		(6) + +static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len); +static int mdio_read(struct net_device *dev, int phy_id, int location); +static int mdio_write(long ioaddr, int phy_id, int location, int value); +static int speedo_open(struct net_device *dev); +static void speedo_resume(struct net_device *dev); +static void speedo_timer(unsigned long data); +static void speedo_init_rx_ring(struct net_device *dev); +static void speedo_tx_timeout(struct net_device *dev); +static int speedo_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int speedo_rx(struct net_device *dev); +static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int speedo_close(struct net_device *dev); +static struct net_device_stats *speedo_get_stats(struct net_device *dev); +static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void set_rx_mode(struct net_device *dev); + + + +#ifdef honor_default_port +/* Optional driver feature to allow forcing the transceiver setting. +   Not recommended. */ +static int mii_ctrl[8] = { 0x3300, 0x3100, 0x0000, 0x0100, +						   0x2000, 0x2100, 0x0400, 0x3100}; +#endif + +/* A list of all installed Speedo devices, for removing the driver module. */ +static struct net_device *root_speedo_dev = NULL; + +static void *speedo_found1(struct pci_dev *pdev, void *init_dev, +						   long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct speedo_private *sp; +	void *priv_mem; +	int i, option; +	u16 eeprom[0x100]; +	int acpi_idle_state = 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	if (dev->mem_start > 0) +		option = dev->mem_start; +	else if (card_idx >= 0  &&  options[card_idx] >= 0) +		option = options[card_idx]; +	else +		option = -1; + +	acpi_idle_state = acpi_set_pwr_state(pdev, ACPI_D0); + +	/* Read the station address EEPROM before doing the reset. +	   Nominally his should even be done before accepting the device, but +	   then we wouldn't have a device name with which to report the error. +	   The size test is for 6 bit vs. 8 bit address serial EEPROMs. +	*/ +	{ +		u16 sum = 0; +		int j; +		int read_cmd, ee_size; + +		if ((do_eeprom_cmd(ioaddr, EE_READ_CMD << 24, 27) & 0xffe0000) +			== 0xffe0000) { +			ee_size = 0x100; +			read_cmd = EE_READ_CMD << 24; +		} else { +			ee_size = 0x40; +			read_cmd = EE_READ_CMD << 22; +		} + +		for (j = 0, i = 0; i < ee_size; i++) { +			u16 value = do_eeprom_cmd(ioaddr, read_cmd | (i << 16), 27); +			eeprom[i] = value; +			sum += value; +			if (i < 3) { +				dev->dev_addr[j++] = value; +				dev->dev_addr[j++] = value >> 8; +			} +		} +		if (sum != 0xBABA) +			printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, " +				   "check settings before activating this device!\n", +				   dev->name, sum); +		/* Don't  unregister_netdev(dev);  as the EEPro may actually be +		   usable, especially if the MAC address is set later. */ +	} + +	/* Reset the chip: stop Tx and Rx processes and clear counters. +	   This takes less than 10usec and will easily finish before the next +	   action. */ +	outl(PortReset, ioaddr + SCBPort); + +	printk(KERN_INFO "%s: %s%s at %#3lx, ", dev->name, +		   eeprom[3] & 0x0100 ? "OEM " : "", pci_id_tbl[chip_idx].name, +		   ioaddr); + +	for (i = 0; i < 5; i++) +		printk("%2.2X:", dev->dev_addr[i]); +	printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq); + +	/* We have decided to accept this device. */ +	/* Allocate cached private storage. +	   The PCI coherent descriptor rings are allocated at each open. */ +	sp = priv_mem = kmalloc(sizeof(*sp), GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; +	dev->base_addr = ioaddr; +	dev->irq = irq; + +#ifndef kernel_bloat +	/* OK, this is pure kernel bloat.  I don't like it when other drivers +	   waste non-pageable kernel space to emit similar messages, but I need +	   them for bug reports. */ +	{ +		const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"}; +		/* The self-test results must be paragraph aligned. */ +		s32 *volatile self_test_results; +		int boguscnt = 16000;	/* Timeout for set-test. */ +		printk(KERN_INFO "  Board assembly %4.4x%2.2x-%3.3d, Physical" +			   " connectors present:", +			   eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff); +		for (i = 0; i < 4; i++) +			if (eeprom[5] & (1<<i)) +				printk("%s", connectors[i]); +		printk("\n"KERN_INFO"  Primary interface chip %s PHY #%d.\n", +			   phys[(eeprom[6]>>8)&15], eeprom[6] & 0x1f); +		if (eeprom[7] & 0x0700) +			printk(KERN_INFO "    Secondary interface chip %s.\n", +				   phys[(eeprom[7]>>8)&7]); +		if (((eeprom[6]>>8) & 0x3f) == DP83840 +			||  ((eeprom[6]>>8) & 0x3f) == DP83840A) { +			int mdi_reg23 = mdio_read(dev, eeprom[6] & 0x1f, 23) | 0x0422; +			if (congenb) +			  mdi_reg23 |= 0x0100; +			printk(KERN_INFO"  DP83840 specific setup, setting register 23 to %4.4x.\n", +				   mdi_reg23); +			mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23); +		} +		if ((option >= 0) && (option & 0x330)) { +			printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +				   (option & 0x300 ? 100 : 10), +				   (option & 0x220 ? "full" : "half")); +			mdio_write(ioaddr, eeprom[6] & 0x1f, 0, +					   ((option & 0x300) ? 0x2000 : 0) | 	/* 100mbps? */ +					   ((option & 0x220) ? 0x0100 : 0)); /* Full duplex? */ +		} else { +			int mii_bmcrctrl = mdio_read(dev, eeprom[6] & 0x1f, 0); +			/* Reset out of a transceiver left in 10baseT-fixed mode. */ +			if ((mii_bmcrctrl & 0x3100) == 0) +				mdio_write(ioaddr, eeprom[6] & 0x1f, 0, 0x8000); +		} +		if (eeprom[10] & 0x0002) +			printk(KERN_INFO "\n" KERN_INFO "  ** The configuration " +				   "EEPROM enables Sleep Mode.\n" KERN_INFO "\n" +				   "  ** This will cause PCI bus errors!\n" +				   KERN_INFO "  ** Update the configuration EEPROM " +				   "with the eepro100-diag program.\n"  ); +		if (eeprom[6] == 0) +			printk(KERN_INFO "  ** The configuration EEPROM does not have a " +				   "transceiver type set.\n" KERN_INFO "\n" +				   "  ** This will cause configuration problems and prevent " +				   "monitoring the link!\n" +				   KERN_INFO "  ** Update the configuration EEPROM " +				   "with the eepro100-diag program.\n"  ); + +		/* Perform a system self-test. */ +		self_test_results = (s32*)(&sp->lstats); +		self_test_results[0] = 0; +		self_test_results[1] = -1; +		outl(virt_to_bus(self_test_results) | PortSelfTest, ioaddr + SCBPort); +		do { +			udelay(10); +		} while (self_test_results[1] == -1  &&  --boguscnt >= 0); + +		if (boguscnt < 0) {		/* Test optimized out. */ +			printk(KERN_ERR "Self test failed, status %8.8x:\n" +				   KERN_ERR " Failure to initialize the i82557.\n" +				   KERN_ERR " Verify that the card is a bus-master" +				   " capable slot.\n", +				   self_test_results[1]); +		} else +			printk(KERN_INFO "  General self-test: %s.\n" +				   KERN_INFO "  Serial sub-system self-test: %s.\n" +				   KERN_INFO "  Internal registers self-test: %s.\n" +				   KERN_INFO "  ROM checksum self-test: %s (%#8.8x).\n", +				   self_test_results[1] & 0x1000 ? "failed" : "passed", +				   self_test_results[1] & 0x0020 ? "failed" : "passed", +				   self_test_results[1] & 0x0008 ? "failed" : "passed", +				   self_test_results[1] & 0x0004 ? "failed" : "passed", +				   self_test_results[0]); +	} +#endif  /* kernel_bloat */ + +	outl(PortReset, ioaddr + SCBPort); + +	/* Return the chip to its original power state. */ +	acpi_set_pwr_state(pdev, acpi_idle_state); + +	/* We do a request_region() only to register /proc/ioports info. */ +	request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name); + +	dev->priv = sp;				/* Allocated above. */ +	memset(sp, 0, sizeof(*sp)); +	sp->next_module = root_speedo_dev; +	root_speedo_dev = dev; + +	sp->priv_addr = priv_mem; +	sp->pci_dev = pdev; +	sp->chip_id = chip_idx; +	sp->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	sp->acpi_pwr = acpi_idle_state; +	sp->msg_level = (1 << debug) - 1; +	sp->rx_copybreak = rx_copybreak; +	sp->max_interrupt_work = max_interrupt_work; +	sp->multicast_filter_limit = multicast_filter_limit; + +	sp->full_duplex = option >= 0 && (option & 0x220) ? 1 : 0; +	if (card_idx >= 0) { +		if (full_duplex[card_idx] >= 0) +			sp->full_duplex = full_duplex[card_idx]; +	} +	sp->default_port = option >= 0 ? (option & 0x0f) : 0; +	if (sp->full_duplex) +		sp->medialock = 1; + +	sp->phy[0] = eeprom[6]; +	sp->phy[1] = eeprom[7]; +	sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1; + +	if (sp->rx_bug) +		printk(KERN_INFO "  Receiver lock-up workaround activated.\n"); + +	/* The Speedo-specific entries in the device structure. */ +	dev->open = &speedo_open; +	dev->hard_start_xmit = &speedo_start_xmit; +	dev->stop = &speedo_close; +	dev->get_stats = &speedo_get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &speedo_ioctl; + +	return dev; +} + +/* How to wait for the command unit to accept a command. +   Typically this takes 0 ticks. */ + +static inline void wait_for_cmd_done(struct net_device *dev) +{ +	long cmd_ioaddr = dev->base_addr + SCBCmd; +	int wait = 0; +	int delayed_cmd; +	do +		if (inb(cmd_ioaddr) == 0) return; +	while(++wait <= 100); +	delayed_cmd = inb(cmd_ioaddr); +	do +		if (inb(cmd_ioaddr) == 0) break; +	while(++wait <= 10000); +	printk(KERN_ERR "%s: Command %2.2x was not immediately accepted, " +		   "%d ticks!\n", +		   dev->name, delayed_cmd, wait); +} + +/* Perform a SCB command known to be slow. +   This function checks the status both before and after command execution. */ +static void do_slow_command(struct net_device *dev, int cmd) +{ +	long cmd_ioaddr = dev->base_addr + SCBCmd; +	int wait = 0; +	do +		if (inb(cmd_ioaddr) == 0) break; +	while(++wait <= 200); +	if (wait > 100) +		printk(KERN_ERR "%s: Command %4.4x was never accepted (%d polls)!\n", +			   dev->name, inb(cmd_ioaddr), wait); +	outb(cmd, cmd_ioaddr); +	for (wait = 0; wait <= 100; wait++) +		if (inb(cmd_ioaddr) == 0) return; +	for (; wait <= 20000; wait++) +		if (inb(cmd_ioaddr) == 0) return; +		else udelay(1); +	printk(KERN_ERR "%s: Command %4.4x was not accepted after %d polls!" +		   "  Current status %8.8x.\n", +		   dev->name, cmd, wait, (int)inl(dev->base_addr + SCBStatus)); +} + + +/* Serial EEPROM section. +   A "bit" grungy, but we work our way through bit-by-bit :->. */ +/*  EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK	0x01	/* EEPROM shift clock. */ +#define EE_CS			0x02	/* EEPROM chip select. */ +#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */ +#define EE_DATA_READ	0x08	/* EEPROM chip data out. */ +#define EE_ENB			(0x4800 | EE_CS) +#define EE_WRITE_0		0x4802 +#define EE_WRITE_1		0x4806 +#define EE_OFFSET		SCBeeprom + +/* Delay between EEPROM clock transitions. +   The code works with no delay on 33Mhz PCI.  */ +#ifndef USE_IO_OPS +#define eeprom_delay(ee_addr)	writew(readw(ee_addr), ee_addr) +#else +#define eeprom_delay(ee_addr)	inw(ee_addr) +#endif + +static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len) +{ +	unsigned retval = 0; +	long ee_addr = ioaddr + SCBeeprom; + +	outw(EE_ENB | EE_SHIFT_CLK, ee_addr); + +	/* Shift the command bits out. */ +	do { +		short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0; +		outw(dataval, ee_addr); +		eeprom_delay(ee_addr); +		outw(dataval | EE_SHIFT_CLK, ee_addr); +		eeprom_delay(ee_addr); +		retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0); +	} while (--cmd_len >= 0); +	outw(EE_ENB, ee_addr); + +	/* Terminate the EEPROM access. */ +	outw(EE_ENB & ~EE_CS, ee_addr); +	return retval; +} + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ +	long ioaddr = dev->base_addr; +	int val, boguscnt = 64*10;		/* <64 usec. to complete, typ 27 ticks */ + +	outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI); +	do { +		val = inl(ioaddr + SCBCtrlMDI); +		if (--boguscnt < 0) { +			printk(KERN_ERR "%s: mdio_read() timed out with val = %8.8x.\n", +				   dev->name, val); +			break; +		} +	} while (! (val & 0x10000000)); +	return val & 0xffff; +} + +static int mdio_write(long ioaddr, int phy_id, int location, int value) +{ +	int val, boguscnt = 64*10;		/* <64 usec. to complete, typ 27 ticks */ +	outl(0x04000000 | (location<<16) | (phy_id<<21) | value, +		 ioaddr + SCBCtrlMDI); +	do { +		val = inl(ioaddr + SCBCtrlMDI); +		if (--boguscnt < 0) { +			printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val); +			break; +		} +	} while (! (val & 0x10000000)); +	return val & 0xffff; +} + + +static int +speedo_open(struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	MOD_INC_USE_COUNT; +	acpi_set_pwr_state(sp->pci_dev, ACPI_D0); + +	if (sp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq); + +	/* Set up the Tx queue early.. */ +	sp->cur_tx = 0; +	sp->dirty_tx = 0; +	sp->last_cmd = 0; +	sp->tx_full = 0; +	sp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; +	sp->polling = sp->in_interrupt = 0; + +	dev->if_port = sp->default_port; + +	if ((sp->phy[0] & 0x8000) == 0) +		sp->advertising = mdio_read(dev, sp->phy[0] & 0x1f, 4); +	/* With some transceivers we must retrigger negotiation to reset +	   power-up errors. */ +	if ((sp->drv_flags & ResetMII) && +		(sp->phy[0] & 0x8000) == 0) { +		int phy_addr = sp->phy[0] & 0x1f ; +		/* Use 0x3300 for restarting NWay, other values to force xcvr: +		   0x0000 10-HD +		   0x0100 10-FD +		   0x2000 100-HD +		   0x2100 100-FD +		*/ +#ifdef honor_default_port +		mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]); +#else +		mdio_write(ioaddr, phy_addr, 0, 0x3300); +#endif +	} + +	/* We can safely take handler calls during init. +	   Doing this after speedo_init_rx_ring() results in a memory leak. */ +	if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	speedo_init_rx_ring(dev); + +	/* Fire up the hardware. */ +	speedo_resume(dev); +	netif_start_tx_queue(dev); + +	/* Setup the chip and configure the multicast list. */ +	sp->mc_setup_frm = NULL; +	sp->mc_setup_frm_len = 0; +	sp->mc_setup_busy = 0; +	sp->rx_mode = RxInvalidMode;		/* Invalid -> always reset the mode. */ +	sp->flow_ctrl = sp->partner = 0; +	set_rx_mode(dev); + +	if (sp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n", +			   dev->name, (int)inw(ioaddr + SCBStatus)); + +	/* Set the timer.  The timer serves a dual purpose: +	   1) to monitor the media interface (e.g. link beat) and perhaps switch +	   to an alternate media type +	   2) to monitor Rx activity, and restart the Rx process if the receiver +	   hangs. */ +	init_timer(&sp->timer); +	sp->timer.expires = jiffies + 3*HZ; +	sp->timer.data = (unsigned long)dev; +	sp->timer.function = &speedo_timer;					/* timer handler */ +	add_timer(&sp->timer); + +	/* No need to wait for the command unit to accept here. */ +	if ((sp->phy[0] & 0x8000) == 0) +		mdio_read(dev, sp->phy[0] & 0x1f, 0); +	return 0; +} + +/* Start the chip hardware after a full reset. */ +static void speedo_resume(struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	outw(SCBMaskAll, ioaddr + SCBCmd); + +	/* Start with a Tx threshold of 256 (0x..20.... 8 byte units). */ +	sp->tx_threshold = 0x01208000; + +	/* Set the segment registers to '0'. */ +	wait_for_cmd_done(dev); +	if (inb(ioaddr + SCBCmd)) { +		outl(PortPartialReset, ioaddr + SCBPort); +		udelay(10); +	} +	outl(0, ioaddr + SCBPointer); +	inl(ioaddr + SCBPointer);				/* Flush to PCI. */ +	udelay(10);					/* Bogus, but it avoids the bug. */ +	/* Note: these next two operations can take a while. */ +	do_slow_command(dev, RxAddrLoad); +	do_slow_command(dev, CUCmdBase); + +	/* Load the statistics block and rx ring addresses. */ +	outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer); +	inl(ioaddr + SCBPointer);				/* Flush to PCI. */ +	outb(CUStatsAddr, ioaddr + SCBCmd); +	sp->lstats.done_marker = 0; +	wait_for_cmd_done(dev); + +	outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]), +		 ioaddr + SCBPointer); +	inl(ioaddr + SCBPointer);				/* Flush to PCI. */ +	/* Note: RxStart should complete instantly. */ +	do_slow_command(dev, RxStart); +	do_slow_command(dev, CUDumpStats); + +	/* Fill the first command with our physical address. */ +	{ +		int entry = sp->cur_tx++ % TX_RING_SIZE; +		struct descriptor *cur_cmd = (struct descriptor *)&sp->tx_ring[entry]; + +		/* Avoid a bug(?!) here by marking the command already completed. */ +		cur_cmd->cmd_status = cpu_to_le32((CmdSuspend | CmdIASetup) | 0xa000); +		cur_cmd->link = +			virt_to_le32desc(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); +		memcpy(cur_cmd->params, dev->dev_addr, 6); +		if (sp->last_cmd) +			clear_suspend(sp->last_cmd); +		sp->last_cmd = cur_cmd; +	} + +	/* Start the chip's Tx process and unmask interrupts. */ +	outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), +		 ioaddr + SCBPointer); +	outw(CUStart, ioaddr + SCBCmd); +} + +/* Media monitoring and control. */ +static void speedo_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int phy_num = sp->phy[0] & 0x1f; +	int status = inw(ioaddr + SCBStatus); + +	if (sp->msg_level & NETIF_MSG_TIMER) +		printk(KERN_DEBUG "%s: Interface monitor tick, chip status %4.4x.\n", +			   dev->name, status); + +	/* Normally we check every two seconds. */ +	sp->timer.expires = jiffies + 2*HZ; + +	if (sp->polling) { +		/* Continue to be annoying. */ +		if (status & 0xfc00) { +			speedo_interrupt(dev->irq, dev, 0); +			if (jiffies - sp->last_reset > 10*HZ) { +				printk(KERN_ERR "%s: IRQ %d is still blocked!\n", +					   dev->name, dev->irq); +				sp->last_reset = jiffies; +			} +		} else if (jiffies - sp->last_reset > 10*HZ) +			sp->polling = 0; +		sp->timer.expires = jiffies + 2; +	} +	/* We have MII and lost link beat. */ +	if ((sp->phy[0] & 0x8000) == 0) { +		int partner = mdio_read(dev, phy_num, 5); +		if (partner != sp->partner) { +			int flow_ctrl = sp->advertising & partner & 0x0400 ? 1 : 0; +			sp->partner = partner; +			if (flow_ctrl != sp->flow_ctrl) { +				sp->flow_ctrl = flow_ctrl; +				sp->rx_mode = RxInvalidMode;	/* Trigger a reload. */ +			} +			/* Clear sticky bit. */ +			mdio_read(dev, phy_num, 1); +			/* If link beat has returned... */ +			if (mdio_read(dev, phy_num, 1) & 0x0004) +				netif_link_up(dev); +			else +				netif_link_down(dev); +		} +	} + +	/* This no longer has a false-trigger window. */ +	if (sp->cur_tx - sp->dirty_tx > 1 && +		(jiffies - dev->trans_start) > TX_TIMEOUT  && +		(jiffies - sp->last_cmd_time) > TX_TIMEOUT) { +		if (status == 0xffff) { +			if (jiffies - sp->last_reset > 10*HZ) { +				sp->last_reset = jiffies; +				printk(KERN_ERR "%s: The EEPro100 chip is missing!\n", +					   dev->name); +			} +		} else if (status & 0xfc00) { +			/* We have a blocked IRQ line.  This should never happen, but +			   we recover as best we can.*/ +			if ( ! sp->polling) { +				if (jiffies - sp->last_reset > 10*HZ) { +					printk(KERN_ERR "%s: IRQ %d is physically blocked! (%4.4x)" +						   "Failing back to low-rate polling.\n", +						   dev->name, dev->irq, status); +					sp->last_reset = jiffies; +				} +				sp->polling = 1; +			} +			speedo_interrupt(dev->irq, dev, 0); +			sp->timer.expires = jiffies + 2;	/* Avoid  */ +		} else { +			speedo_tx_timeout(dev); +			sp->last_reset = jiffies; +		} +	} +	if (sp->rx_mode == RxInvalidMode  || +		(sp->rx_bug  && jiffies - sp->last_rx_time > 2*HZ)) { +		/* We haven't received a packet in a Long Time.  We might have been +		   bitten by the receiver hang bug.  This can be cleared by sending +		   a set multicast list command. */ +		set_rx_mode(dev); +	} +	add_timer(&sp->timer); +} + +static void speedo_show_state(struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	int phy_num = sp->phy[0] & 0x1f; +	int i; + +	/* Print a few items for debugging. */ +	if (sp->msg_level & NETIF_MSG_DRV) { +		int i; +		printk(KERN_DEBUG "%s: Tx ring dump,  Tx queue %d / %d:\n", dev->name, +			   sp->cur_tx, sp->dirty_tx); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(KERN_DEBUG "%s: %c%c%d %8.8x.\n", dev->name, +				   i == sp->dirty_tx % TX_RING_SIZE ? '*' : ' ', +				   i == sp->cur_tx % TX_RING_SIZE ? '=' : ' ', +				   i, sp->tx_ring[i].status); +	} +	printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n", +		   dev->name, sp->cur_rx); + +	for (i = 0; i < RX_RING_SIZE; i++) +		printk(KERN_DEBUG "  Rx ring entry %d  %8.8x.\n", +			   i, sp->rx_ringp[i] ? (int)sp->rx_ringp[i]->status : 0); + +	for (i = 0; i < 16; i++) { +		if (i == 6) i = 21; +		printk(KERN_DEBUG "  PHY index %d register %d is %4.4x.\n", +			   phy_num, i, mdio_read(dev, phy_num, i)); +	} + +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +speedo_init_rx_ring(struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	struct RxFD *rxf, *last_rxf = NULL; +	int i; + +	sp->cur_rx = 0; +#if defined(CONFIG_VLAN) +	/* Note that buffer sizing is not a run-time check! */ +	sp->rx_buf_sz = dev->mtu + 14 + sizeof(struct RxFD) + 4; +#else +	sp->rx_buf_sz = dev->mtu + 14 + sizeof(struct RxFD); +#endif +	if (sp->rx_buf_sz < PKT_BUF_SZ) +		sp->rx_buf_sz = PKT_BUF_SZ; + +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb; +		skb = dev_alloc_skb(sp->rx_buf_sz); +		sp->rx_skbuff[i] = skb; +		if (skb == NULL) +			break;			/* OK.  Just initially short of Rx bufs. */ +		skb->dev = dev;			/* Mark as being used by this device. */ +		rxf = (struct RxFD *)skb->tail; +		sp->rx_ringp[i] = rxf; +		skb_reserve(skb, sizeof(struct RxFD)); +		if (last_rxf) +			last_rxf->link = virt_to_le32desc(rxf); +		last_rxf = rxf; +		rxf->status = cpu_to_le32(0x00000001);	/* '1' is flag value only. */ +		rxf->link = 0;						/* None yet. */ +		/* This field unused by i82557, we use it as a consistency check. */ +#ifdef final_version +		rxf->rx_buf_addr = 0xffffffff; +#else +		rxf->rx_buf_addr = virt_to_bus(skb->tail); +#endif +		rxf->count = cpu_to_le32((sp->rx_buf_sz - sizeof(struct RxFD)) << 16); +	} +	sp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); +	/* Mark the last entry as end-of-list. */ +	last_rxf->status = cpu_to_le32(0xC0000002);	/* '2' is flag value only. */ +	sp->last_rxf = last_rxf; +} + +static void speedo_tx_timeout(struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int status = inw(ioaddr + SCBStatus); + +	printk(KERN_WARNING "%s: Transmit timed out: status %4.4x " +		   " %4.4x at %d/%d commands %8.8x %8.8x %8.8x.\n", +		   dev->name, status, (int)inw(ioaddr + SCBCmd), +		   sp->dirty_tx, sp->cur_tx, +		   sp->tx_ring[(sp->dirty_tx+0) % TX_RING_SIZE].status, +		   sp->tx_ring[(sp->dirty_tx+1) % TX_RING_SIZE].status, +		   sp->tx_ring[(sp->dirty_tx+2) % TX_RING_SIZE].status); + +	/* Trigger a stats dump to give time before the reset. */ +	speedo_get_stats(dev); + +	speedo_show_state(dev); +	if ((status & 0x00C0) != 0x0080 +		&&  (status & 0x003C) == 0x0010  &&  0) { +		/* Only the command unit has stopped. */ +		printk(KERN_WARNING "%s: Trying to restart the transmitter...\n", +			   dev->name); +		outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]), +			 ioaddr + SCBPointer); +		outw(CUStart, ioaddr + SCBCmd); +	} else { +		printk(KERN_WARNING "%s: Restarting the chip...\n", +			   dev->name); +		/* Reset the Tx and Rx units. */ +		outl(PortReset, ioaddr + SCBPort); +		if (sp->msg_level & NETIF_MSG_TX_ERR) +			speedo_show_state(dev); +		udelay(10); +		speedo_resume(dev); +	} +	/* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */ +	if ((sp->phy[0] & 0x8000) == 0) { +		int phy_addr = sp->phy[0] & 0x1f; +		int advertising = mdio_read(dev, phy_addr, 4); +		int mii_bmcr = mdio_read(dev, phy_addr, 0); +		mdio_write(ioaddr, phy_addr, 0, 0x0400); +		mdio_write(ioaddr, phy_addr, 1, 0x0000); +		mdio_write(ioaddr, phy_addr, 4, 0x0000); +		mdio_write(ioaddr, phy_addr, 0, 0x8000); +#ifdef honor_default_port +		mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]); +#else +		mdio_read(dev, phy_addr, 0); +		mdio_write(ioaddr, phy_addr, 0, mii_bmcr); +		mdio_write(ioaddr, phy_addr, 4, advertising); +#endif +	} +	sp->stats.tx_errors++; +	dev->trans_start = jiffies; +	return; +} + +/* Handle the interrupt cases when something unexpected happens. */ +static void speedo_intr_error(struct net_device *dev, int intr_status) +{ +	long ioaddr = dev->base_addr; +	struct speedo_private *sp = (struct speedo_private *)dev->priv; + +	if (intr_status & IntrRxSuspend) { +		if ((intr_status & 0x003c) == 0x0028) /* No more Rx buffers. */ +			outb(RxResumeNoResources, ioaddr + SCBCmd); +		else if ((intr_status & 0x003c) == 0x0008) { /* No resources (why?!) */ +			printk(KERN_DEBUG "%s: Unknown receiver error, status=%#4.4x.\n", +				   dev->name, intr_status); +			/* No idea of what went wrong.  Restart the receiver. */ +			outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]), +				 ioaddr + SCBPointer); +			outb(RxStart, ioaddr + SCBCmd); +		} +		sp->stats.rx_errors++; +	} +} + + +static int +speedo_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int entry; + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. +	   If this ever occurs the queue layer is doing something evil! */ +	if (netif_pause_tx_queue(dev) != 0) { +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < TX_TIMEOUT - 2) +			return 1; +		if (tickssofar < TX_TIMEOUT) { +			/* Reap sent packets from the full Tx queue. */ +			outw(SCBTriggerIntr, ioaddr + SCBCmd); +			return 1; +		} +		speedo_tx_timeout(dev); +		return 1; +	} + +	/* Caution: the write order is important here, set the base address +	   with the "ownership" bits last. */ + +	{	/* Prevent interrupts from changing the Tx ring from underneath us. */ +		unsigned long flags; + +		spin_lock_irqsave(&sp->lock, flags); +		/* Calculate the Tx descriptor entry. */ +		entry = sp->cur_tx % TX_RING_SIZE; + +		sp->tx_skbuff[entry] = skb; +		/* Todo: be a little more clever about setting the interrupt bit. */ +		sp->tx_ring[entry].status = +			cpu_to_le32(CmdSuspend | CmdTx | CmdTxFlex); +		sp->cur_tx++; +		sp->tx_ring[entry].link = +			virt_to_le32desc(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]); +		/* We may nominally release the lock here. */ +		sp->tx_ring[entry].tx_desc_addr = +			virt_to_le32desc(&sp->tx_ring[entry].tx_buf_addr0); +		/* The data region is always in one buffer descriptor. */ +		sp->tx_ring[entry].count = cpu_to_le32(sp->tx_threshold); +		sp->tx_ring[entry].tx_buf_addr0 = virt_to_le32desc(skb->data); +		sp->tx_ring[entry].tx_buf_size0 = cpu_to_le32(skb->len); +		/* Todo: perhaps leave the interrupt bit set if the Tx queue is more +		   than half full.  Argument against: we should be receiving packets +		   and scavenging the queue.  Argument for: if so, it shouldn't +		   matter. */ +		{ +			struct descriptor *last_cmd = sp->last_cmd; +			sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; +			clear_suspend(last_cmd); +		} +		if (sp->cur_tx - sp->dirty_tx >= TX_QUEUE_LIMIT) { +			sp->tx_full = 1; +			netif_stop_tx_queue(dev); +		} else +			netif_unpause_tx_queue(dev); +		spin_unlock_irqrestore(&sp->lock, flags); +	} +	wait_for_cmd_done(dev); +	outb(CUResume, ioaddr + SCBCmd); +	dev->trans_start = jiffies; + +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct speedo_private *sp; +	long ioaddr; +	int work_limit; +	u16 status; + +	ioaddr = dev->base_addr; +	sp = (struct speedo_private *)dev->priv; +	work_limit = sp->max_interrupt_work; +#ifndef final_version +	/* A lock to prevent simultaneous entry on SMP machines. */ +	if (test_and_set_bit(0, (void*)&sp->in_interrupt)) { +		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", +			   dev->name); +		sp->in_interrupt = 0;	/* Avoid halting machine. */ +		return; +	} +#endif + +	do { +		status = inw(ioaddr + SCBStatus); + +		if ((status & IntrAllNormal) == 0  ||  status == 0xffff) +			break; +		/* Acknowledge all of the current interrupt sources ASAP. */ +		outw(status & IntrAllNormal, ioaddr + SCBStatus); + +		if (sp->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: interrupt  status=%#4.4x.\n", +				   dev->name, status); + +		if (status & (IntrRxDone|IntrRxSuspend)) +			speedo_rx(dev); + +		/* The command unit did something, scavenge finished Tx entries. */ +		if (status & (IntrCmdDone | IntrCmdIdle | IntrDrvrIntr)) { +			unsigned int dirty_tx; +			/* We should nominally not need this lock. */ +			spin_lock(&sp->lock); + +			dirty_tx = sp->dirty_tx; +			while (sp->cur_tx - dirty_tx > 0) { +				int entry = dirty_tx % TX_RING_SIZE; +				int status = le32_to_cpu(sp->tx_ring[entry].status); + +				if (sp->msg_level & NETIF_MSG_INTR) +					printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n", +						   entry, status); +				if ((status & StatusComplete) == 0) { +					/* Special case error check: look for descriptor that the +					   chip skipped(?). */ +					if (sp->cur_tx - dirty_tx > 2  && +						(sp->tx_ring[(dirty_tx+1) % TX_RING_SIZE].status +						 & cpu_to_le32(StatusComplete))) { +						printk(KERN_ERR "%s: Command unit failed to mark " +							   "command %8.8x as complete at %d.\n", +							   dev->name, status, dirty_tx); +					} else +						break;			/* It still hasn't been processed. */ +				} +				if ((status & TxUnderrun) && +					(sp->tx_threshold < 0x01e08000)) { +					sp->tx_threshold += 0x00040000; +					if (sp->msg_level & NETIF_MSG_TX_ERR) +						printk(KERN_DEBUG "%s: Tx threshold increased, " +							   "%#8.8x.\n", dev->name, sp->tx_threshold); +				} +				/* Free the original skb. */ +				if (sp->tx_skbuff[entry]) { +					sp->stats.tx_packets++;	/* Count only user packets. */ +#if LINUX_VERSION_CODE > 0x20127 +					sp->stats.tx_bytes += sp->tx_skbuff[entry]->len; +#endif +					dev_free_skb_irq(sp->tx_skbuff[entry]); +					sp->tx_skbuff[entry] = 0; +				} else if ((status & 0x70000) == CmdNOp) +					sp->mc_setup_busy = 0; +				dirty_tx++; +			} + +#ifndef final_version +			if (sp->cur_tx - dirty_tx > TX_RING_SIZE) { +				printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d," +					   " full=%d.\n", +					   dirty_tx, sp->cur_tx, sp->tx_full); +				dirty_tx += TX_RING_SIZE; +			} +#endif + +			sp->dirty_tx = dirty_tx; +			if (sp->tx_full +				&&  sp->cur_tx - dirty_tx < TX_QUEUE_UNFULL) { +				/* The ring is no longer full, clear tbusy. */ +				sp->tx_full = 0; +				netif_resume_tx_queue(dev); +			} +			spin_unlock(&sp->lock); +		} + +		if (status & IntrRxSuspend) +			speedo_intr_error(dev, status); + +		if (--work_limit < 0) { +			printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n", +				   dev->name, status); +			/* Clear all interrupt sources. */ +			outl(0xfc00, ioaddr + SCBStatus); +			break; +		} +	} while (1); + +	if (sp->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)inw(ioaddr + SCBStatus)); + +	clear_bit(0, (void*)&sp->in_interrupt); +	return; +} + +static int +speedo_rx(struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	int entry = sp->cur_rx % RX_RING_SIZE; +	int status; +	int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx; + +	if (sp->msg_level & NETIF_MSG_RX_STATUS) +		printk(KERN_DEBUG " In speedo_rx().\n"); +	/* If we own the next entry, it's a new packet. Send it up. */ +	while (sp->rx_ringp[entry] != NULL && +		   (status = le32_to_cpu(sp->rx_ringp[entry]->status)) & RxComplete) { +		int desc_count = le32_to_cpu(sp->rx_ringp[entry]->count); +		int pkt_len = desc_count & 0x07ff; + +		if (--rx_work_limit < 0) +			break; +		if (sp->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  speedo_rx() status %8.8x len %d.\n", status, +				   pkt_len); +		if ((status & (RxErrTooBig|RxOK|0x0f90)) != RxOK) { +			if (status & RxErrTooBig) +				printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, " +					   "status %8.8x!\n", dev->name, status); +			else if ( ! (status & RxOK)) { +				/* There was a fatal error.  This *should* be impossible. */ +				sp->stats.rx_errors++; +				printk(KERN_ERR "%s: Anomalous event in speedo_rx(), " +					   "status %8.8x.\n", dev->name, status); +			} +		} else { +			struct sk_buff *skb; + +			if (sp->drv_flags & HasChksum) +				pkt_len -= 2; + +			/* Check if the packet is long enough to just accept without +			   copying to a properly sized skbuff. */ +			if (pkt_len < sp->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != 0) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +				/* 'skb_put()' points to the start of sk_buff data area. */ +				/* Packet is in one chunk -- we can copy + cksum. */ +				eth_copy_and_sum(skb, sp->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +			} else { +				void *temp; +				/* Pass up the already-filled skbuff. */ +				skb = sp->rx_skbuff[entry]; +				if (skb == NULL) { +					printk(KERN_ERR "%s: Inconsistent Rx descriptor chain.\n", +						   dev->name); +					break; +				} +				sp->rx_skbuff[entry] = NULL; +				temp = skb_put(skb, pkt_len); +#if !defined(final_version) && !defined(__powerpc__) +				if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp) +					printk(KERN_ERR "%s: Rx consistency error -- the skbuff " +						   "addresses do not match in speedo_rx: %p vs. %p " +						   "/ %p.\n", dev->name, +						   bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), +						   skb->head, temp); +#endif +				sp->rx_ringp[entry] = NULL; +			} +			skb->protocol = eth_type_trans(skb, dev); +			if (sp->drv_flags & HasChksum) { +#if 0 +				u16 csum = get_unaligned((u16*)(skb->head + pkt_len)) +				if (desc_count & 0x8000) +					skb->ip_summed = CHECKSUM_UNNECESSARY; +#endif +			} +			netif_rx(skb); +			sp->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			sp->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++sp->cur_rx) % RX_RING_SIZE; +	} + +	/* Refill the Rx ring buffers. */ +	for (; sp->cur_rx - sp->dirty_rx > 0; sp->dirty_rx++) { +		struct RxFD *rxf; +		entry = sp->dirty_rx % RX_RING_SIZE; +		if (sp->rx_skbuff[entry] == NULL) { +			struct sk_buff *skb; +			/* Get a fresh skbuff to replace the consumed one. */ +			skb = dev_alloc_skb(sp->rx_buf_sz); +			sp->rx_skbuff[entry] = skb; +			if (skb == NULL) { +				sp->rx_ringp[entry] = NULL; +				sp->alloc_failures++; +				break;			/* Better luck next time!  */ +			} +			rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail; +			skb->dev = dev; +			skb_reserve(skb, sizeof(struct RxFD)); +			rxf->rx_buf_addr = virt_to_le32desc(skb->tail); +		} else { +			rxf = sp->rx_ringp[entry]; +		} +		rxf->status = cpu_to_le32(0xC0000001); 	/* '1' for driver use only. */ +		rxf->link = 0;			/* None yet. */ +		rxf->count = cpu_to_le32((sp->rx_buf_sz - sizeof(struct RxFD)) << 16); +		sp->last_rxf->link = virt_to_le32desc(rxf); +		sp->last_rxf->status &= cpu_to_le32(~0xC0000000); +		sp->last_rxf = rxf; +	} + +	sp->last_rx_time = jiffies; +	return 0; +} + +static int +speedo_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (sp->msg_level & NETIF_MSG_IFDOWN) +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n" +			   KERN_DEBUG "%s:   Cumlative allocation failures: %d.\n", +			   dev->name, (int)inw(ioaddr + SCBStatus), +			   dev->name, sp->alloc_failures); + +	/* Shut off the media monitoring timer. */ +	del_timer(&sp->timer); + +	/* Shutting down the chip nicely fails to disable flow control. So.. */ +	outl(PortPartialReset, ioaddr + SCBPort); + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx and Tx queues. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = sp->rx_skbuff[i]; +		sp->rx_skbuff[i] = 0; +		/* Clear the Rx descriptors. */ +		if (skb) { +#if LINUX_VERSION_CODE < 0x20100 +			skb->free = 1; +#endif +			dev_free_skb(skb); +		} +	} + +	for (i = 0; i < TX_RING_SIZE; i++) { +		struct sk_buff *skb = sp->tx_skbuff[i]; +		sp->tx_skbuff[i] = 0; +		/* Clear the Tx descriptors. */ +		if (skb) +			dev_free_skb(skb); +	} +	if (sp->mc_setup_frm) { +		kfree(sp->mc_setup_frm); +		sp->mc_setup_frm_len = 0; +	} + +	/* Print a few items for debugging. */ +	if (sp->msg_level & NETIF_MSG_IFDOWN) +		speedo_show_state(dev); + +	/* Alt: acpi_set_pwr_state(pdev, sp->acpi_pwr); */ +	acpi_set_pwr_state(sp->pci_dev, ACPI_D2); +	MOD_DEC_USE_COUNT; + +	return 0; +} + +/* The Speedo-3 has an especially awkward and unusable method of getting +   statistics out of the chip.  It takes an unpredictable length of time +   for the dump-stats command to complete.  To avoid a busy-wait loop we +   update the stats with the previous dump results, and then trigger a +   new dump. + +   These problems are mitigated by the current /proc implementation, which +   calls this routine first to judge the output length, and then to emit the +   output. + +   Oh, and incoming frames are dropped while executing dump-stats! +   */ +static struct net_device_stats *speedo_get_stats(struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	/* Update only if the previous dump finished. */ +	if (sp->lstats.done_marker == le32_to_cpu(0xA007)) { +		sp->stats.tx_aborted_errors += le32_to_cpu(sp->lstats.tx_coll16_errs); +		sp->stats.tx_window_errors += le32_to_cpu(sp->lstats.tx_late_colls); +		sp->stats.tx_fifo_errors += le32_to_cpu(sp->lstats.tx_underruns); +		sp->stats.tx_fifo_errors += le32_to_cpu(sp->lstats.tx_lost_carrier); +		/*sp->stats.tx_deferred += le32_to_cpu(sp->lstats.tx_deferred);*/ +		sp->stats.collisions += le32_to_cpu(sp->lstats.tx_total_colls); +		sp->stats.rx_crc_errors += le32_to_cpu(sp->lstats.rx_crc_errs); +		sp->stats.rx_frame_errors += le32_to_cpu(sp->lstats.rx_align_errs); +		sp->stats.rx_over_errors += le32_to_cpu(sp->lstats.rx_resource_errs); +		sp->stats.rx_fifo_errors += le32_to_cpu(sp->lstats.rx_overrun_errs); +		sp->stats.rx_length_errors += le32_to_cpu(sp->lstats.rx_runt_errs); +		sp->lstats.done_marker = 0x0000; +		if (netif_running(dev)) { +			wait_for_cmd_done(dev); +			outb(CUDumpStats, ioaddr + SCBCmd); +		} +	} +	return &sp->stats; +} + +static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; +	int phy = sp->phy[0] & 0x1f; +	int saved_acpi; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = phy; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		saved_acpi = acpi_set_pwr_state(sp->pci_dev, ACPI_D0); +		data[3] = mdio_read(dev, data[0], data[1]); +		acpi_set_pwr_state(sp->pci_dev, saved_acpi); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == sp->phy[0]) { +			u16 value = data[2]; +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				sp->medialock = (value & 0x9000) ? 0 : 1; +				if (sp->medialock) { +					sp->full_duplex = (value & 0x0100) ? 1 : 0; +					sp->rx_mode = RxInvalidMode; +				} +				break; +			case 4: sp->advertising = value; break; +			} +		} +		saved_acpi = acpi_set_pwr_state(sp->pci_dev, ACPI_D0); +		mdio_write(ioaddr, data[0], data[1], data[2]); +		acpi_set_pwr_state(sp->pci_dev, saved_acpi); +		return 0; +	case SIOCGPARAMS: +		data32[0] = sp->msg_level; +		data32[1] = sp->multicast_filter_limit; +		data32[2] = sp->max_interrupt_work; +		data32[3] = sp->rx_copybreak; +#if 0 +		/* No room in the ioctl() to set these. */ +		data32[4] = txfifo; +		data32[5] = rxfifo; +#endif +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		sp->msg_level = data32[0]; +		sp->multicast_filter_limit = data32[1]; +		sp->max_interrupt_work = data32[2]; +		sp->rx_copybreak = data32[3]; +#if 0 +		/* No room in the ioctl() to set these. */ +		if (data32[4] < 16) +			txfifo = data32[4]; +		if (data32[5] < 16) +			rxfifo = data32[5]; +#endif +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +/* Set or clear the multicast filter for this adaptor. +   This is very ugly with Intel chips -- we usually have to execute an +   entire configuration command, plus process a multicast command. +   This is complicated.  We must put a large configuration command and +   an arbitrarily-sized multicast command in the transmit list. +   To minimize the disruption -- the previous command might have already +   loaded the link -- we convert the current command block, normally a Tx +   command, into a no-op and link it to the new command. +*/ +static void set_rx_mode(struct net_device *dev) +{ +	struct speedo_private *sp = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; +	struct descriptor *last_cmd; +	char new_rx_mode; +	unsigned long flags; +	int entry, i; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		new_rx_mode = AcceptAllMulticast | AcceptAllPhys; +	} else if ((dev->flags & IFF_ALLMULTI)  || +			   dev->mc_count > sp->multicast_filter_limit) { +		new_rx_mode = AcceptAllMulticast; +	} else +		new_rx_mode = 0; + +	if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) { +	  /* The Tx ring is full -- don't add anything!  Presumably the new mode +		 is in config_cmd_data and will be added anyway, otherwise we wait +		 for a timer tick or the mode to change again. */ +		sp->rx_mode = RxInvalidMode; +		return; +	} + +	if (new_rx_mode != sp->rx_mode) { +		u8 *config_cmd_data; + +		spin_lock_irqsave(&sp->lock, flags); +		entry = sp->cur_tx % TX_RING_SIZE; +		last_cmd = sp->last_cmd; +		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + +		sp->tx_skbuff[entry] = 0;			/* Redundant. */ +		sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdConfigure); +		sp->cur_tx++; +		sp->tx_ring[entry].link = +			virt_to_le32desc(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]); +		/* We may nominally release the lock here. */ + +		config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr; +		/* Construct a full CmdConfig frame. */ +		memcpy(config_cmd_data, i82558_config_cmd, sizeof(i82558_config_cmd)); +		config_cmd_data[1] = (txfifo << 4) | rxfifo; +		config_cmd_data[4] = rxdmacount; +		config_cmd_data[5] = txdmacount + 0x80; +		config_cmd_data[6] |= (new_rx_mode & AcceptErr) ? 0x80 : 0; +		config_cmd_data[7] &= (new_rx_mode & AcceptRunt) ? ~0x01 : ~0; +		if (sp->drv_flags & HasChksum) +			config_cmd_data[9] |= 1; +		config_cmd_data[15] |= (new_rx_mode & AcceptAllPhys) ? 1 : 0; +		config_cmd_data[19] = sp->flow_ctrl ? 0xBD : 0x80; +		config_cmd_data[19] |= sp->full_duplex ? 0x40 : 0; +		config_cmd_data[21] = (new_rx_mode & AcceptAllMulticast) ? 0x0D : 0x05; +		if (sp->phy[0] & 0x8000) {			/* Use the AUI port instead. */ +			config_cmd_data[15] |= 0x80; +			config_cmd_data[8] = 0; +		} +		/* Trigger the command unit resume. */ +		wait_for_cmd_done(dev); +		clear_suspend(last_cmd); +		outb(CUResume, ioaddr + SCBCmd); +		spin_unlock_irqrestore(&sp->lock, flags); +		sp->last_cmd_time = jiffies; +	} + +	if (new_rx_mode == 0  &&  dev->mc_count < 4) { +		/* The simple case of 0-3 multicast list entries occurs often, and +		   fits within one tx_ring[] entry. */ +		struct dev_mc_list *mclist; +		u16 *setup_params, *eaddrs; + +		spin_lock_irqsave(&sp->lock, flags); +		entry = sp->cur_tx % TX_RING_SIZE; +		last_cmd = sp->last_cmd; +		sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry]; + +		sp->tx_skbuff[entry] = 0; +		sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdMulticastList); +		sp->cur_tx++; +		sp->tx_ring[entry].link = +			virt_to_le32desc(&sp->tx_ring[(entry + 1) % TX_RING_SIZE]); +		/* We may nominally release the lock here. */ +		sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */ +		setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr; +		*setup_params++ = cpu_to_le16(dev->mc_count*6); +		/* Fill in the multicast addresses. */ +		for (i = 0, mclist = dev->mc_list; i < dev->mc_count; +			 i++, mclist = mclist->next) { +			eaddrs = (u16 *)mclist->dmi_addr; +			*setup_params++ = *eaddrs++; +			*setup_params++ = *eaddrs++; +			*setup_params++ = *eaddrs++; +		} + +		wait_for_cmd_done(dev); +		clear_suspend(last_cmd); +		/* Immediately trigger the command unit resume. */ +		outb(CUResume, ioaddr + SCBCmd); +		spin_unlock_irqrestore(&sp->lock, flags); +		sp->last_cmd_time = jiffies; +	} else if (new_rx_mode == 0) { +		struct dev_mc_list *mclist; +		u16 *setup_params, *eaddrs; +		struct descriptor *mc_setup_frm = sp->mc_setup_frm; +		int i; + +		if (sp->mc_setup_frm_len < 10 + dev->mc_count*6 +			|| sp->mc_setup_frm == NULL) { +			/* Allocate a full setup frame, 10bytes + <max addrs>. */ +			if (sp->mc_setup_frm) +				kfree(sp->mc_setup_frm); +			sp->mc_setup_busy = 0; +			sp->mc_setup_frm_len = 10 + sp->multicast_filter_limit*6; +			sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC); +			if (sp->mc_setup_frm == NULL) { +				printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", +					   dev->name); +				sp->rx_mode = RxInvalidMode; /* We failed, try again. */ +				return; +			} +		} +		/* If we are busy, someone might be quickly adding to the MC list. +		   Try again later when the list updates stop. */ +		if (sp->mc_setup_busy) { +			sp->rx_mode = RxInvalidMode; +			return; +		} +		mc_setup_frm = sp->mc_setup_frm; +		/* Fill the setup frame. */ +		if (sp->msg_level & NETIF_MSG_RXFILTER) +			printk(KERN_DEBUG "%s: Constructing a setup frame at %p, " +				   "%d bytes.\n", +				   dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len); +		mc_setup_frm->cmd_status = +			cpu_to_le32(CmdSuspend | CmdIntr | CmdMulticastList); +		/* Link set below. */ +		setup_params = (u16 *)&mc_setup_frm->params; +		*setup_params++ = cpu_to_le16(dev->mc_count*6); +		/* Fill in the multicast addresses. */ +		for (i = 0, mclist = dev->mc_list; i < dev->mc_count; +			 i++, mclist = mclist->next) { +			eaddrs = (u16 *)mclist->dmi_addr; +			*setup_params++ = *eaddrs++; +			*setup_params++ = *eaddrs++; +			*setup_params++ = *eaddrs++; +		} + +		/* Disable interrupts while playing with the Tx Cmd list. */ +		spin_lock_irqsave(&sp->lock, flags); +		entry = sp->cur_tx % TX_RING_SIZE; +		last_cmd = sp->last_cmd; +		sp->last_cmd = mc_setup_frm; +		sp->mc_setup_busy++; + +		/* Change the command to a NoOp, pointing to the CmdMulti command. */ +		sp->tx_skbuff[entry] = 0; +		sp->tx_ring[entry].status = cpu_to_le32(CmdNOp); +		sp->cur_tx++; +		sp->tx_ring[entry].link = virt_to_le32desc(mc_setup_frm); +		/* We may nominally release the lock here. */ + +		/* Set the link in the setup frame. */ +		mc_setup_frm->link = +			virt_to_le32desc(&(sp->tx_ring[(entry+1) % TX_RING_SIZE])); + +		wait_for_cmd_done(dev); +		clear_suspend(last_cmd); +		/* Immediately trigger the command unit resume. */ +		outb(CUResume, ioaddr + SCBCmd); +		spin_unlock_irqrestore(&sp->lock, flags); +		sp->last_cmd_time = jiffies; +		if (sp->msg_level & NETIF_MSG_RXFILTER) +			printk(KERN_DEBUG " CmdMCSetup frame length %d in entry %d.\n", +				   dev->mc_count, entry); +	} + +	sp->rx_mode = new_rx_mode; +} + +static int speedo_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct speedo_private *np = (struct speedo_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		outl(PortPartialReset, ioaddr + SCBPort); +		break; +	case DRV_RESUME: +		speedo_resume(dev); +		np->rx_mode = RxInvalidMode; +		np->flow_ctrl = np->partner = 0; +		set_rx_mode(dev); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_speedo_dev; *devp; devp = next) { +			next = &((struct speedo_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	case DRV_PWR_DOWN: +	case DRV_PWR_UP: +		acpi_set_pwr_state(np->pci_dev, event==DRV_PWR_DOWN ? ACPI_D3:ACPI_D0); +		break; +	case DRV_PWR_WakeOn: +	default: +		return -1; +	} + +	return 0; +} + + +#if defined(MODULE) || (LINUX_VERSION_CODE >= 0x020400) + +int init_module(void) +{ +	int cards_found; + +	/* Emit version even if no cards detected. */ +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	cards_found = pci_drv_register(&eepro100_drv_id, NULL); +	if (cards_found < 0) +		printk(KERN_INFO "eepro100: No cards found, driver not installed.\n"); +	return cards_found; +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&eepro100_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_speedo_dev) { +		struct speedo_private *sp = (void *)root_speedo_dev->priv; +		unregister_netdev(root_speedo_dev); +#ifdef USE_IO_OPS +		release_region(root_speedo_dev->base_addr, +					   pci_id_tbl[sp->chip_id].io_size); +#else +		iounmap((char *)root_speedo_dev->base_addr); +#endif +		acpi_set_pwr_state(sp->pci_dev, sp->acpi_pwr); +		next_dev = sp->next_module; +		if (sp->priv_addr) +			kfree(sp->priv_addr); +		kfree(root_speedo_dev); +		root_speedo_dev = next_dev; +	} +} + +#if (LINUX_VERSION_CODE >= 0x020400)  && 0 +module_init(init_module); +module_exit(cleanup_module); +#endif + +#else   /* not MODULE */ + +int eepro100_probe(struct net_device *dev) +{ +	int cards_found =  pci_drv_register(&eepro100_drv_id, dev); + +	/* Only emit the version if the driver is being used. */ +	if (cards_found >= 0) +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); + +	return cards_found; +} +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` eepro100.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c eepro100.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c eepro100.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/eexpress.c b/linux/src/drivers/net/eexpress.c new file mode 100644 index 0000000..9c816ee --- /dev/null +++ b/linux/src/drivers/net/eexpress.c @@ -0,0 +1,1285 @@ +/* $Id: eexpress.c,v 1.1 1999/04/26 05:52:09 tb Exp $ + * + * Intel EtherExpress device driver for Linux + * + * Original version written 1993 by Donald Becker + * Modularized by Pauline Middelink <middelin@polyware.iaf.nl> + * Changed to support io= irq= by Alan Cox <Alan.Cox@linux.org> + * Reworked 1995 by John Sullivan <js10039@cam.ac.uk> + * More fixes by Philip Blundell <pjb27@cam.ac.uk> + * Added the Compaq LTE  Alan Cox <alan@redhat.com> + * + * Note - this driver is experimental still - it has problems on faster + * machines. Someone needs to sit down and go through it line by line with + * a databook... + */ + +/* + * The original EtherExpress driver was just about usable, but + * suffered from a long startup delay, a hard limit of 16k memory + * usage on the card (EtherExpress 16s have either 32k or 64k), + * and random locks under load. The last was particularly annoying + * and made running eXceed/W preferable to Linux/XFree. After hacking + * through the driver for a couple of days, I had fixed most of the + * card handling errors, at the expense of turning the code into + * a complete jungle, but still hadn't tracked down the lock-ups. + * I had hoped these would be an IP bug, but failed to reproduce them + * under other drivers, so decided to start from scratch and rewrite + * the driver cleanly. And here it is. + * + * It's still not quite there, but self-corrects a lot more problems. + * the 'CU wedged, resetting...' message shouldn't happen at all, but + * at least we recover. It still locks occasionally, any ideas welcome. + * + * The original startup delay experienced by some people was due to the + * first ARP request for the address of the default router getting lost. + * (mostly the reply we were getting back was arriving before our + * hardware address was set up, or before the configuration sequence + * had told the card NOT to strip of the frame header). If you a long + * startup delay, you may have lost this ARP request/reply, although + * the original cause has been fixed. However, it is more likely that + * you've just locked under this version. + * + * The main changes are in the 586 initialization procedure (which was + * just broken before - the EExp is a strange beasty and needs careful + * handling) the receive buffer handling (we now use a non-terminating + * circular list of buffers, which stops the card giving us out-of- + * resources errors), and the transmit code. The driver is also more + * structured, and I have tried to keep the kernel interface separate + * from the hardware interface (although some routines naturally want + * to do both). + * + * John Sullivan + * + * 18/5/95: + * + * The lock-ups seem to happen when you access card memory after a 586 + * reset. This happens only 1 in 12 resets, on a random basis, and + * completely locks the machine. As far as I can see there is no + * workaround possible - the only thing to be done is make sure we + * never reset the card *after* booting the kernel - once at probe time + * must be sufficient, and we'll just have to put up with that failing + * occasionally (or buy a new NIC). By the way, this looks like a  + * definite card bug, since Intel's own driver for DOS does exactly the + * same. + * + * This bug makes switching in and out of promiscuous mode a risky + * business, since we must do a 586 reset each time. + */ + +/* + * Sources: + * + * The original eexpress.c by Donald Becker + *   Sources: the Crynwr EtherExpress driver source. + *            the Intel Microcommunications Databook Vol.1 1990 + * + * wavelan.c and i82586.h + *   This was invaluable for the complete '586 configuration details + *   and command format. + * + * The Crynwr sources (again) + *   Not as useful as the Wavelan driver, but then I had eexpress.c to + *   go off. + * + * The Intel EtherExpress 16 ethernet card + *   Provided the only reason I want to see a working etherexpress driver. + *   A lot of fixes came from just observing how the card (mis)behaves when + *   you prod it. + * + */ + +static char version[] =  +"eexpress.c: v0.10 04-May-95 John Sullivan <js10039@cam.ac.uk>\n" +"            v0.14 19-May-96 Philip Blundell <phil@tazenda.demon.co.uk>\n" +"            v0.15 04-Aug-98 Alan Cox <alan@redhat.com>\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/string.h> +#include <linux/in.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/delay.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/malloc.h> + +/* + * Not actually used yet - may be implemented when the driver has + * been debugged! + * + * Debug Level		Driver Status + *	0		Final release + *	1		Beta test + *	2 + *	3 + * 	4		Report timeouts & 586 errors (normal debug level) + *	5		Report all major events + *	6		Dump sent/received packet contents + *	7		Report function entry/exit + */ + +#ifndef NET_DEBUG +#define NET_DEBUG 4 +#endif +static unsigned int net_debug = NET_DEBUG; + +#undef F_DEB + +#include "eth82586.h" + +#define PRIV(x)         ((struct net_local *)(x)->priv) +#define EEXP_IO_EXTENT  16 + +/* + * Private data declarations + */ + +struct net_local  +{ +	struct enet_statistics stats; +	unsigned long init_time;        /* jiffies when eexp_hw_init586 called */ +	unsigned short rx_first;        /* first rx buf, same as RX_BUF_START */ +	unsigned short rx_last;         /* last rx buf */ +	unsigned short tx_head;         /* next free tx buf */ +	unsigned short tx_reap;         /* first in-use tx buf */ +	unsigned short tx_tail;         /* previous tx buf to tx_head */ +	unsigned short tx_link;         /* last known-executing tx buf */ +	unsigned short last_tx_restart; /* set to tx_link when we restart the CU */ +	unsigned char started; +	unsigned char promisc; +	unsigned short rx_buf_start; +	unsigned short rx_buf_end; +	unsigned short num_tx_bufs; +	unsigned short num_rx_bufs; +}; + +unsigned short start_code[] = { +	0x0000,                 /* SCP: set bus to 16 bits */ +	0x0000,0x0000,          /* junk */ +	0x0000,0x0000,          /* address of ISCP (lo,hi) */ + +	0x0001,                 /* ISCP: busy - cleared after reset */ +	0x0008,0x0000,0x0000,   /* offset,address (lo,hi) of SCB */ + +	0x0000,0x0000,          /* SCB: status, commands */ +	0x0000,0x0000,          /* links to first command block, first receive descriptor */ +	0x0000,0x0000,          /* CRC error, alignment error counts */ +	0x0000,0x0000,          /* out of resources, overrun error counts */ + +	0x0000,0x0000,          /* pad */ +	0x0000,0x0000, + +	0x0000,Cmd_Config,      /* startup configure sequence, at 0x0020 */ +	0x0032,                 /* link to next command */ +	0x080c,                 /* 12 bytes follow : fifo threshold=8 */ +	0x2e40,                 /* don't rx bad frames : SRDY/ARDY => ext. sync. : preamble len=8 +	                         * take addresses from data buffers : 6 bytes/address */ +	0x6000,                 /* default backoff method & priority : interframe spacing = 0x60 */ +	0xf200,                 /* slot time=0x200 : max collision retry = 0xf */ +	0x0000,                 /* no HDLC : normal CRC : enable broadcast : disable promiscuous/multicast modes */ +	0x003c,                 /* minimum frame length = 60 octets) */ + +	0x0000,Cmd_INT|Cmd_SetAddr, +	0x003e,                 /* link to next command */ +	0x0000,0x0000,0x0000,   /* hardware address placed here, 0x0038 */ +	0x0000,Cmd_END|Cmd_Nop, /* end of configure sequence */ +	0x003e, + +	0x0000 + +}; + +#define CONF_LINK 0x0020 +#define CONF_HW_ADDR 0x0038 + +/* maps irq number to EtherExpress magic value */ +static char irqrmap[] = { 0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0 }; + +/* + * Prototypes for Linux interface + */ + +extern int                  express_probe(struct device *dev); +static int                     eexp_open (struct device *dev); +static int                     eexp_close(struct device *dev); +static struct enet_statistics *eexp_stats(struct device *dev); +static int                     eexp_xmit (struct sk_buff *buf, struct device *dev); + +static void                    eexp_irq  (int irq, void *dev_addr, struct pt_regs *regs); +static void                    eexp_set_multicast(struct device *dev); + +/* + * Prototypes for hardware access functions + */ + +static void           eexp_hw_rx        (struct device *dev); +static void           eexp_hw_tx        (struct device *dev, unsigned short *buf, unsigned short len); +static int            eexp_hw_probe     (struct device *dev,unsigned short ioaddr); +static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, unsigned char location); + +static unsigned short eexp_hw_lasttxstat(struct device *dev); +static void           eexp_hw_txrestart (struct device *dev); + +static void           eexp_hw_txinit    (struct device *dev); +static void           eexp_hw_rxinit    (struct device *dev); + +static void           eexp_hw_init586   (struct device *dev); +static void           eexp_hw_ASICrst   (struct device *dev); + +/* + * Linux interface + */ + +/* + * checks for presence of EtherExpress card + */ + +int express_probe(struct device *dev) +{ +	unsigned short *port,ports[] = { 0x0300,0x0270,0x0320,0x0340,0 }; +	unsigned short ioaddr = dev->base_addr; + +	if (ioaddr&0xfe00) +		return eexp_hw_probe(dev,ioaddr); +	else if (ioaddr) +		return ENXIO; + +	for ( port=&ports[0] ; *port ; port++ )  +	{ +		unsigned short sum = 0; +		int i; +		for ( i=0 ; i<4 ; i++ )  +		{ +			unsigned short t; +			t = inb(*port + ID_PORT); +			sum |= (t>>4) << ((t & 0x03)<<2); +		} +		if (sum==0xbaba && !eexp_hw_probe(dev,*port))  +			return 0; +	} +	return ENODEV; +} + +/* + * open and initialize the adapter, ready for use + */ + +static int eexp_open(struct device *dev) +{ +	int irq = dev->irq; +	unsigned short ioaddr = dev->base_addr; + +#if NET_DEBUG > 6 +	printk(KERN_DEBUG "%s: eexp_open()\n", dev->name); +#endif + +	if (!irq || !irqrmap[irq])  +		return -ENXIO; + +	if (irq2dev_map[irq] || +	      /* more consistent, surely? */ +	   ((irq2dev_map[irq]=dev),0) || +	     request_irq(irq,&eexp_irq,0,"eexpress",NULL))  +		return -EAGAIN; + +	request_region(ioaddr, EEXP_IO_EXTENT, "eexpress"); +	dev->tbusy = 0; +	dev->interrupt = 0; +	eexp_hw_init586(dev); +	dev->start = 1; +	MOD_INC_USE_COUNT; +#if NET_DEBUG > 6 +	printk(KERN_DEBUG "%s: leaving eexp_open()\n", dev->name); +#endif +	return 0; +} + +/* + * close and disable the interface, leaving + * the 586 in reset + */ +static int eexp_close(struct device *dev) +{ +	unsigned short ioaddr = dev->base_addr; +	int irq = dev->irq; + +	dev->tbusy = 1;  +	dev->start = 0; +   +	outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ); +	PRIV(dev)->started = 0; +	outw(SCB_CUsuspend|SCB_RUsuspend,ioaddr+SCB_CMD); +	outb(0,ioaddr+SIGNAL_CA); +	free_irq(irq,NULL); +	irq2dev_map[irq] = NULL; +	outb(i586_RST,ioaddr+EEPROM_Ctrl); +	release_region(ioaddr,16); +	MOD_DEC_USE_COUNT; +	return 0; +} + +/* + * Return interface stats + */ + +static struct enet_statistics *eexp_stats(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; + +	/*  +	 * Hmmm, this looks a little too easy... The card maintains +	 * some stats in the SCB, and I'm not convinced we're +	 * incrementing the most sensible statistics when the card +	 * returns an error (esp. slow DMA, out-of-resources) +	 */ +	return &lp->stats; +} + +/* + * Called to transmit a packet, or to allow us to right ourselves + * if the kernel thinks we've died. + */ + +static int eexp_xmit(struct sk_buff *buf, struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short ioaddr = dev->base_addr; + +#if NET_DEBUG > 6 +	printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name); +#endif + +	outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); +	if (dev->tbusy)  +	{ +		/* This will happen, but hopefully not as often as when +		 * tbusy==0. If it happens too much, we probably ought +		 * to think about unwedging ourselves... +		 */ +		if (test_bit(0,(void *)&PRIV(dev)->started))  +		{ +			if ((jiffies - dev->trans_start)>5)  +			{ +				if (lp->tx_link==lp->last_tx_restart)  +				{ +					unsigned short boguscount=200,rsst; +					printk(KERN_WARNING "%s: Retransmit timed out, status %04x, resetting...\n", +						dev->name,inw(ioaddr+SCB_STATUS)); +					eexp_hw_txinit(dev); +					lp->last_tx_restart = 0; +					outw(lp->tx_link,ioaddr+SCB_CBL); +					outw(0,ioaddr+SCB_STATUS); +					outw(SCB_CUstart,ioaddr+SCB_CMD); +					outb(0,ioaddr+SIGNAL_CA); +					while (!SCB_complete(rsst=inw(ioaddr+SCB_STATUS)))  +					{ +						if (!--boguscount)  +						{ +							boguscount=200; +							printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n", +								dev->name,rsst); +							outw(lp->tx_link,ioaddr+SCB_CBL); +							outw(0,ioaddr+SCB_STATUS); +							outw(SCB_CUstart,ioaddr+SCB_CMD); +							outb(0,ioaddr+SIGNAL_CA); +						} +					} +					dev->tbusy = 0; +					mark_bh(NET_BH); +				} +				else +				{ +					unsigned short status = inw(ioaddr+SCB_STATUS); +					if (SCB_CUdead(status))  +					{ +						unsigned short txstatus = eexp_hw_lasttxstat(dev); +						printk(KERN_WARNING "%s: Transmit timed out, CU not active status %04x %04x, restarting...\n", +							dev->name, status, txstatus); +						eexp_hw_txrestart(dev); +					} +					else +					{ +						unsigned short txstatus = eexp_hw_lasttxstat(dev); +						if (dev->tbusy && !txstatus)  +						{ +							printk(KERN_WARNING "%s: CU wedged, status %04x %04x, resetting...\n", +								dev->name,status,txstatus); +							eexp_hw_init586(dev);  +							dev->tbusy = 0; +							mark_bh(NET_BH); +						} +					} +				} +			} +		} +		else +		{ +			if ((jiffies-lp->init_time)>10) +			{ +				unsigned short status = inw(ioaddr+SCB_STATUS); +				printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n", +					dev->name, status); +				eexp_hw_init586(dev); +				dev->tbusy = 0; +				mark_bh(NET_BH); +			} +		} +	} + +	if (buf==NULL)  +	{ +		unsigned short status = inw(ioaddr+SCB_STATUS); +		unsigned short txstatus = eexp_hw_lasttxstat(dev); +		if (SCB_CUdead(status))  +		{ +			printk(KERN_WARNING "%s: CU has died! status %04x %04x, attempting to restart...\n", +				dev->name, status, txstatus); +			lp->stats.tx_errors++; +			eexp_hw_txrestart(dev); +		} +		dev_tint(dev); +		outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); +		dev_kfree_skb(buf, FREE_WRITE); +		return 0; +	} + +	if (set_bit(0,(void *)&dev->tbusy))  +	{ +		lp->stats.tx_dropped++; +	} +	else +	{ +		unsigned short length = (ETH_ZLEN < buf->len) ? buf->len : ETH_ZLEN; +		unsigned short *data = (unsigned short *)buf->data; + +		outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); +		eexp_hw_tx(dev,data,length); +		outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); +	} +	dev_kfree_skb(buf, FREE_WRITE); +	outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); +	return 0; +} + +/* + * Handle an EtherExpress interrupt + * If we've finished initializing, start the RU and CU up. + * If we've already started, reap tx buffers, handle any received packets, + * check to make sure we've not become wedged. + */ + +static void eexp_irq(int irq, void *dev_info, struct pt_regs *regs) +{ +	struct device *dev = irq2dev_map[irq]; +	struct net_local *lp; +	unsigned short ioaddr,status,ack_cmd; +	unsigned short old_rp,old_wp; + +	if (dev==NULL)  +	{ +		printk(KERN_WARNING "net_interrupt(): irq %d for unknown device caught by EExpress\n",irq); +		return; +	} + +#if NET_DEBUG > 6 +	printk(KERN_DEBUG "%s: interrupt\n", dev->name); +#endif + +	dev->interrupt = 1; /* should this be reset on exit? */ +   +	lp = (struct net_local *)dev->priv; +	ioaddr = dev->base_addr; + +	outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ); +	old_rp = inw(ioaddr+READ_PTR); +	old_wp = inw(ioaddr+WRITE_PTR); +	status = inw(ioaddr+SCB_STATUS); +	ack_cmd = SCB_ack(status); + +	if (PRIV(dev)->started==0 && SCB_complete(status))  +	{ +#if NET_DEBUG > 4 +		printk(KERN_DEBUG "%s: SCBcomplete event received\n", dev->name); +#endif +		while (SCB_CUstat(status)==2) +			status = inw_p(ioaddr+SCB_STATUS); +#if NET_DEBUG > 4 +                printk(KERN_DEBUG "%s: CU went non-active (status = %08x)\n", dev->name, status); +#endif +		PRIV(dev)->started=1; +		outw_p(lp->tx_link,ioaddr+SCB_CBL); +		outw_p(PRIV(dev)->rx_buf_start,ioaddr+SCB_RFA); +		ack_cmd |= SCB_CUstart | SCB_RUstart; +	} +	else if (PRIV(dev)->started)  +	{ +		unsigned short txstatus; +		txstatus = eexp_hw_lasttxstat(dev); +	} +   +	if (SCB_rxdframe(status))  +	{ +		eexp_hw_rx(dev); +	} + +	if ((PRIV(dev)->started&2)!=0 && SCB_RUstat(status)!=4)  +	{ +		printk(KERN_WARNING "%s: RU stopped status %04x, restarting...\n", +			dev->name,status); +		lp->stats.rx_errors++; +		eexp_hw_rxinit(dev); +		outw(PRIV(dev)->rx_buf_start,ioaddr+SCB_RFA); +		ack_cmd |= SCB_RUstart; +	}  +	else if (PRIV(dev)->started==1 && SCB_RUstat(status)==4)  +		PRIV(dev)->started|=2; + +	outw(ack_cmd,ioaddr+SCB_CMD); +	outb(0,ioaddr+SIGNAL_CA); +	outw(old_rp,ioaddr+READ_PTR); +	outw(old_wp,ioaddr+WRITE_PTR); +	outb(SIRQ_en|irqrmap[irq],ioaddr+SET_IRQ); +	dev->interrupt = 0; +#if NET_DEBUG > 6 +        printk(KERN_DEBUG "%s: leaving eexp_irq()\n", dev->name); +#endif +	return; +} + +/* + * Hardware access functions + */ + +/* + * Check all the receive buffers, and hand any received packets + * to the upper levels. Basic sanity check on each frame + * descriptor + */ +  +static void eexp_hw_rx(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short ioaddr = dev->base_addr; +	unsigned short old_wp = inw(ioaddr+WRITE_PTR); +	unsigned short old_rp = inw(ioaddr+READ_PTR); +	unsigned short rx_block = lp->rx_first; +	unsigned short boguscount = lp->num_rx_bufs; + +#if NET_DEBUG > 6 +	printk(KERN_DEBUG "%s: eexp_hw_rx()\n", dev->name); +#endif + +	while (outw(rx_block,ioaddr+READ_PTR),boguscount--)  +	{ +		unsigned short status = inw(ioaddr); +		unsigned short rfd_cmd = inw(ioaddr); +		unsigned short rx_next = inw(ioaddr); +		unsigned short pbuf = inw(ioaddr); +		unsigned short pkt_len; + +		if (FD_Done(status))  +		{ +			outw(pbuf,ioaddr+READ_PTR); +			pkt_len = inw(ioaddr); + +			if (rfd_cmd!=0x0000 || pbuf!=rx_block+0x16 +				|| (pkt_len & 0xc000)!=0xc000)  +			{ +				printk(KERN_WARNING "%s: Rx frame at %04x corrupted, status %04x, cmd %04x, " +					"next %04x, pbuf %04x, len %04x\n",dev->name,rx_block, +					status,rfd_cmd,rx_next,pbuf,pkt_len); +				boguscount++; +				continue; +			} +			else if (!FD_OK(status))  +			{ +				lp->stats.rx_errors++; +				if (FD_CRC(status))  +					lp->stats.rx_crc_errors++; +				if (FD_Align(status)) +					lp->stats.rx_frame_errors++; +				if (FD_Resrc(status)) +					lp->stats.rx_fifo_errors++; +				if (FD_DMA(status)) +					lp->stats.rx_over_errors++; +				if (FD_Short(status)) +					lp->stats.rx_length_errors++; +			} +			else +			{ +				struct sk_buff *skb; +				pkt_len &= 0x3fff; +				skb = dev_alloc_skb(pkt_len+16); +				if (skb == NULL)  +				{ +					printk(KERN_WARNING "%s: Memory squeeze, dropping packet\n",dev->name); +					lp->stats.rx_dropped++; +					break; +				} +				skb->dev = dev; +				skb_reserve(skb, 2); +				outw(pbuf+10,ioaddr+READ_PTR); +				insw(ioaddr,skb_put(skb,pkt_len),(pkt_len+1)>>1); +				skb->protocol = eth_type_trans(skb,dev); +				netif_rx(skb); +				lp->stats.rx_packets++; +			} +			outw(rx_block,ioaddr+WRITE_PTR); +			outw(0x0000,ioaddr); +			outw(0x0000,ioaddr); +		} +		rx_block = rx_next; +	} +	outw(old_rp,ioaddr+READ_PTR); +	outw(old_wp,ioaddr+WRITE_PTR); +} + +/* + * Hand a packet to the card for transmission + * If we get here, we MUST have already checked + * to make sure there is room in the transmit + * buffer region + */ + +static void eexp_hw_tx(struct device *dev, unsigned short *buf, unsigned short len) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short ioaddr = dev->base_addr; +	unsigned short old_wp = inw(ioaddr+WRITE_PTR); + +	outw(lp->tx_head,ioaddr+WRITE_PTR); +	outw(0x0000,ioaddr); +	outw(Cmd_INT|Cmd_Xmit,ioaddr); +	outw(lp->tx_head+0x08,ioaddr); +	outw(lp->tx_head+0x0e,ioaddr); +	outw(0x0000,ioaddr); +	outw(0x0000,ioaddr); +	outw(lp->tx_head+0x08,ioaddr); +	outw(0x8000|len,ioaddr); +	outw(-1,ioaddr); +	outw(lp->tx_head+0x16,ioaddr); +	outw(0,ioaddr); +	outsw(ioaddr,buf,(len+1)>>1); +	outw(lp->tx_tail+0x0c,ioaddr+WRITE_PTR); +	outw(lp->tx_head,ioaddr); +	dev->trans_start = jiffies; +	lp->tx_tail = lp->tx_head; +	if (lp->tx_head==TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE))  +		lp->tx_head = TX_BUF_START; +	else  +		lp->tx_head += TX_BUF_SIZE; +	if (lp->tx_head != lp->tx_reap)  +		dev->tbusy = 0; +	outw(old_wp,ioaddr+WRITE_PTR); +} + +/* + * Sanity check the suspected EtherExpress card + * Read hardware address, reset card, size memory and + * initialize buffer memory pointers. These should + * probably be held in dev->priv, in case someone has 2 + * differently configured cards in their box (Arghhh!) + */ + +static int eexp_hw_probe(struct device *dev, unsigned short ioaddr) +{ +	unsigned short hw_addr[3]; +	int i; +	unsigned char *chw_addr = (unsigned char *)hw_addr; + +	printk("%s: EtherExpress at %#x, ",dev->name,ioaddr); + +	hw_addr[0] = eexp_hw_readeeprom(ioaddr,2); +	hw_addr[1] = eexp_hw_readeeprom(ioaddr,3); +	hw_addr[2] = eexp_hw_readeeprom(ioaddr,4); + +	/* Standard Address or Compaq LTE Address */ +	if (!((hw_addr[2]==0x00aa && ((hw_addr[1] & 0xff00)==0x0000)) || +	      (hw_addr[2]==0x0080 && ((hw_addr[1] & 0xff00)==0x5F00))))  +	{ +		printk("rejected: invalid address %04x%04x%04x\n", +			hw_addr[2],hw_addr[1],hw_addr[0]); +		return -ENODEV; +	} + +	dev->base_addr = ioaddr; +	for ( i=0 ; i<6 ; i++ )  +		dev->dev_addr[i] = chw_addr[5-i]; + +	{ +		char irqmap[]={0, 9, 3, 4, 5, 10, 11, 0}; +		char *ifmap[]={"AUI", "BNC", "10baseT"}; +		enum iftype {AUI=0, BNC=1, TP=2}; +		unsigned short setupval = eexp_hw_readeeprom(ioaddr,0); + +		dev->irq = irqmap[setupval>>13]; +		dev->if_port = !(setupval & 0x1000) ? AUI : +			eexp_hw_readeeprom(ioaddr,5) & 0x1 ? TP : BNC; + +		printk("IRQ %d, Interface %s, ",dev->irq,ifmap[dev->if_port]); + +		outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); +		outb(0,ioaddr+SET_IRQ); +	} + +	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); +	if (!dev->priv)  +		return -ENOMEM; + +	memset(dev->priv, 0, sizeof(struct net_local)); + +	eexp_hw_ASICrst(dev); + +	{ +		unsigned short i586mso = 0x023e; +		unsigned short old_wp,old_rp,old_a0,old_a1; +		unsigned short a0_0,a1_0,a0_1,a1_1; + +		old_wp = inw(ioaddr+WRITE_PTR); +		old_rp = inw(ioaddr+READ_PTR); +		outw(0x8000+i586mso,ioaddr+READ_PTR); +		old_a1 = inw(ioaddr); +		outw(i586mso,ioaddr+READ_PTR); +		old_a0 = inw(ioaddr); +		outw(i586mso,ioaddr+WRITE_PTR); +		outw(0x55aa,ioaddr); +		outw(i586mso,ioaddr+READ_PTR); +		a0_0 = inw(ioaddr); +		outw(0x8000+i586mso,ioaddr+WRITE_PTR); +		outw(0x5a5a,ioaddr); +		outw(0x8000+i586mso,ioaddr+READ_PTR); +		a1_0 = inw(ioaddr); +		outw(i586mso,ioaddr+READ_PTR); +		a0_1 = inw(ioaddr); +		outw(i586mso,ioaddr+WRITE_PTR); +		outw(0x1234,ioaddr); +		outw(0x8000+i586mso,ioaddr+READ_PTR); +		a1_1 = inw(ioaddr); + +		if ((a0_0 != a0_1) || (a1_0 != a1_1) || +			(a1_0 != 0x5a5a) || (a0_0 != 0x55aa))  +		{ +			printk("32k\n"); +			PRIV(dev)->rx_buf_end = 0x7ff6; +			PRIV(dev)->num_tx_bufs = 4; +		} +		else +		{ +			printk("64k\n"); +			PRIV(dev)->num_tx_bufs = 8; +			PRIV(dev)->rx_buf_start = TX_BUF_START + (PRIV(dev)->num_tx_bufs*TX_BUF_SIZE); +			PRIV(dev)->rx_buf_end = 0xfff6; +		} + +		outw(0x8000+i586mso,ioaddr+WRITE_PTR); +		outw(old_a1,ioaddr); +		outw(i586mso,ioaddr+WRITE_PTR); +		outw(old_a0,ioaddr); +		outw(old_wp,ioaddr+WRITE_PTR); +		outw(old_rp,ioaddr+READ_PTR); +	} +   +	if (net_debug)  +		printk("%s", version); +	dev->open = eexp_open; +	dev->stop = eexp_close; +	dev->hard_start_xmit = eexp_xmit; +	dev->get_stats = eexp_stats; +	dev->set_multicast_list = &eexp_set_multicast; +	ether_setup(dev); +	return 0; +} + +/* + *	Read a word from eeprom location (0-63?) + */ +static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, unsigned char location) +{ +	unsigned short cmd = 0x180|(location&0x7f); +	unsigned short rval = 0,wval = EC_CS|i586_RST; +	int i; +  +	outb(EC_CS|i586_RST,ioaddr+EEPROM_Ctrl); +	for ( i=0x100 ; i ; i>>=1 )  +	{ +		if (cmd&i)  +			wval |= EC_Wr; +		else  +			wval &= ~EC_Wr; + +		outb(wval,ioaddr+EEPROM_Ctrl); +		outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl); +		eeprom_delay(); +		outb(wval,ioaddr+EEPROM_Ctrl); +		eeprom_delay(); +	}	 +	wval &= ~EC_Wr; +	outb(wval,ioaddr+EEPROM_Ctrl); +	for ( i=0x8000 ; i ; i>>=1 )  +	{ +		outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl); +		eeprom_delay(); +		if (inb(ioaddr+EEPROM_Ctrl)&EC_Rd)  +			rval |= i; +		outb(wval,ioaddr+EEPROM_Ctrl); +		eeprom_delay(); +	} +	wval &= ~EC_CS; +	outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl); +	eeprom_delay(); +	outb(wval,ioaddr+EEPROM_Ctrl); +	eeprom_delay(); +	return rval; +} + +/* + * Reap tx buffers and return last transmit status. + * if ==0 then either: + *    a) we're not transmitting anything, so why are we here? + *    b) we've died. + * otherwise, Stat_Busy(return) means we've still got some packets + * to transmit, Stat_Done(return) means our buffers should be empty + * again + */ + +static unsigned short eexp_hw_lasttxstat(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short ioaddr = dev->base_addr; +	unsigned short old_rp = inw(ioaddr+READ_PTR); +	unsigned short old_wp = inw(ioaddr+WRITE_PTR); +	unsigned short tx_block = lp->tx_reap; +	unsigned short status; +   +	if (!test_bit(0,(void *)&dev->tbusy) && lp->tx_head==lp->tx_reap)  +		return 0x0000; + +	do +	{ +		outw(tx_block,ioaddr+READ_PTR); +		status = inw(ioaddr); +		if (!Stat_Done(status))  +		{ +			lp->tx_link = tx_block; +			outw(old_rp,ioaddr+READ_PTR); +			outw(old_wp,ioaddr+WRITE_PTR); +			return status; +		} +		else  +		{ +			lp->last_tx_restart = 0; +			lp->stats.collisions += Stat_NoColl(status); +			if (!Stat_OK(status))  +			{ +				if (Stat_Abort(status))  +					lp->stats.tx_aborted_errors++; +				if (Stat_TNoCar(status) || Stat_TNoCTS(status))  +					lp->stats.tx_carrier_errors++; +				if (Stat_TNoDMA(status))  +					lp->stats.tx_fifo_errors++; +			} +			else +				lp->stats.tx_packets++; +		} +		if (tx_block == TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE))  +			lp->tx_reap = tx_block = TX_BUF_START; +		else +			lp->tx_reap = tx_block += TX_BUF_SIZE; +		dev->tbusy = 0; +		mark_bh(NET_BH); +	} +	while (lp->tx_reap != lp->tx_head); + +	lp->tx_link = lp->tx_tail + 0x08; +	outw(old_rp,ioaddr+READ_PTR); +	outw(old_wp,ioaddr+WRITE_PTR); + +	return status; +} + +/*  + * This should never happen. It is called when some higher + * routine detects the CU has stopped, to try to restart + * it from the last packet we knew we were working on, + * or the idle loop if we had finished for the time. + */ + +static void eexp_hw_txrestart(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short ioaddr = dev->base_addr; +   +	lp->last_tx_restart = lp->tx_link; +	outw(lp->tx_link,ioaddr+SCB_CBL); +	outw(SCB_CUstart,ioaddr+SCB_CMD); +	outw(0,ioaddr+SCB_STATUS); +	outb(0,ioaddr+SIGNAL_CA); + +	{ +		unsigned short boguscount=50,failcount=5; +		while (!inw(ioaddr+SCB_STATUS))  +		{ +			if (!--boguscount)  +			{ +				if (--failcount)  +				{ +					printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n", +						dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD)); +					outw(lp->tx_link,ioaddr+SCB_CBL); +					outw(0,ioaddr+SCB_STATUS); +					outw(SCB_CUstart,ioaddr+SCB_CMD); +					outb(0,ioaddr+SIGNAL_CA); +					boguscount = 100; +				} +				else +				{ +					printk(KERN_WARNING "%s: Failed to restart CU, resetting board...\n",dev->name); +					eexp_hw_init586(dev); +					dev->tbusy = 0; +					mark_bh(NET_BH); +					return; +				} +			} +		} +	} +} + +/* + * Writes down the list of transmit buffers into card + * memory. Initial separate, repeated transmits link + * them into a circular list, such that the CU can + * be constantly active, and unlink them as we reap + * transmitted packet buffers, so the CU doesn't loop + * and endlessly transmit packets. (Try hacking the driver + * to send continuous broadcast messages, say ARP requests + * on a subnet with Windows boxes running on Novell and + * LAN Workplace with EMM386. Amusing to watch them all die + * horribly leaving the Linux boxes up!) + */ + +static void eexp_hw_txinit(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short ioaddr = dev->base_addr; +	unsigned short old_wp = inw(ioaddr+WRITE_PTR); +	unsigned short tx_block = TX_BUF_START; +	unsigned short curtbuf; + +	for ( curtbuf=0 ; curtbuf<lp->num_tx_bufs ; curtbuf++ )  +	{ +		outw(tx_block,ioaddr+WRITE_PTR); +		outw(0x0000,ioaddr); +		outw(Cmd_INT|Cmd_Xmit,ioaddr); +		outw(tx_block+0x08,ioaddr); +		outw(tx_block+0x0e,ioaddr); +		outw(0x0000,ioaddr); +		outw(0x0000,ioaddr); +		outw(tx_block+0x08,ioaddr); +		outw(0x8000,ioaddr); +		outw(-1,ioaddr); +		outw(tx_block+0x16,ioaddr); +		outw(0x0000,ioaddr); +		tx_block += TX_BUF_SIZE; +	} +	lp->tx_head = TX_BUF_START; +	lp->tx_reap = TX_BUF_START; +	lp->tx_tail = tx_block - TX_BUF_SIZE; +	lp->tx_link = lp->tx_tail + 0x08; +	lp->rx_buf_start = tx_block; +	outw(old_wp,ioaddr+WRITE_PTR); +} + +/* is this a standard test pattern, or dbecker randomness? */ + +unsigned short rx_words[] =  +{ +	0xfeed,0xf00d,0xf001,0x0505,0x2424,0x6565,0xdeaf +}; + +/* + * Write the circular list of receive buffer descriptors to + * card memory. Note, we no longer mark the end of the list, + * so if all the buffers fill up, the 82586 will loop until + * we free one. This may sound dodgy, but it works, and + * it makes the error detection in the interrupt handler + * a lot simpler. + */ + +static void eexp_hw_rxinit(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short ioaddr = dev->base_addr; +	unsigned short old_wp = inw(ioaddr+WRITE_PTR); +	unsigned short rx_block = lp->rx_buf_start; + +	lp->num_rx_bufs = 0; +	lp->rx_first = rx_block; +	do  +	{ +		lp->num_rx_bufs++; +		outw(rx_block,ioaddr+WRITE_PTR); +		outw(0x0000,ioaddr); +		outw(0x0000,ioaddr); +		outw(rx_block+RX_BUF_SIZE,ioaddr); +		outw(rx_block+0x16,ioaddr); +		outsw(ioaddr, rx_words, sizeof(rx_words)>>1); +		outw(0x8000,ioaddr); +		outw(-1,ioaddr); +		outw(rx_block+0x20,ioaddr); +		outw(0x0000,ioaddr); +		outw(0x8000|(RX_BUF_SIZE-0x20),ioaddr); +		lp->rx_last = rx_block; +		rx_block += RX_BUF_SIZE; +	} while (rx_block <= lp->rx_buf_end-RX_BUF_SIZE); + +	outw(lp->rx_last+4,ioaddr+WRITE_PTR); +	outw(lp->rx_first,ioaddr); + +	outw(old_wp,ioaddr+WRITE_PTR); +} + +/* + * Reset the 586, fill memory (including calls to + * eexp_hw_[(rx)(tx)]init()) unreset, and start + * the configuration sequence. We don't wait for this + * to finish, but allow the interrupt handler to start + * the CU and RU for us. We can't start the receive/ + * transmission system up before we know that the + * hardware is configured correctly + */ +static void eexp_hw_init586(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	unsigned short ioaddr = dev->base_addr; + +#if NET_DEBUG > 6 +        printk("%s: eexp_hw_init586()\n", dev->name); +#endif + +	lp->started = 0; +	set_loopback; + +	outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); +	outb_p(i586_RST,ioaddr+EEPROM_Ctrl); +	udelay(2000);  /* delay 20ms */ +        { +		unsigned long ofs; +		for (ofs = 0; ofs < lp->rx_buf_end; ofs += 32) { +			unsigned long i; +			outw_p(ofs, ioaddr+SM_PTR); +			for (i = 0; i < 16; i++) { +				outw_p(0, ioaddr+SM_ADDR(i<<1)); +			} +		} +	} + +	outw_p(lp->rx_buf_end,ioaddr+WRITE_PTR); +	start_code[28] = (dev->flags & IFF_PROMISC)?(start_code[28] | 1):(start_code[28] & ~1); +	lp->promisc = dev->flags & IFF_PROMISC; +	/* We may die here */ +	outsw(ioaddr, start_code, sizeof(start_code)>>1); +	outw(CONF_HW_ADDR,ioaddr+WRITE_PTR); +	outsw(ioaddr,dev->dev_addr,3); +	eexp_hw_txinit(dev); +	eexp_hw_rxinit(dev); +	outw(0,ioaddr+WRITE_PTR); +	outw(1,ioaddr); +	outb(0,ioaddr+EEPROM_Ctrl); +	outw(0,ioaddr+SCB_CMD); +	outb(0,ioaddr+SIGNAL_CA); +	{ +		unsigned short rboguscount=50,rfailcount=5; +		while (outw(0,ioaddr+READ_PTR),inw(ioaddr))  +		{ +			if (!--rboguscount)  +			{ +				printk(KERN_WARNING "%s: i82586 reset timed out, kicking...\n", +					dev->name); +				outw(0,ioaddr+SCB_CMD); +				outb(0,ioaddr+SIGNAL_CA); +				rboguscount = 100; +				if (!--rfailcount)  +				{ +					printk(KERN_WARNING "%s: i82586 not responding, giving up.\n", +						dev->name); +					return; +				} +			} +		} +	} + +	outw(CONF_LINK,ioaddr+SCB_CBL); +	outw(0,ioaddr+SCB_STATUS); +	outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD); +	outb(0,ioaddr+SIGNAL_CA); +	{ +		unsigned short iboguscount=50,ifailcount=5; +		while (!inw(ioaddr+SCB_STATUS))  +		{ +			if (!--iboguscount)  +			{ +				if (--ifailcount)  +				{ +					printk(KERN_WARNING "%s: i82586 initialization timed out, status %04x, cmd %04x\n", +						dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD)); +					outw(CONF_LINK,ioaddr+SCB_CBL); +					outw(0,ioaddr+SCB_STATUS); +					outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD); +					outb(0,ioaddr+SIGNAL_CA); +					iboguscount = 100; +				} +				else  +				{ +					printk(KERN_WARNING "%s: Failed to initialize i82586, giving up.\n",dev->name); +					return; +				} +			} +		} +	} +   +	outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ); +	clear_loopback; +	lp->init_time = jiffies; +#if NET_DEBUG > 6 +        printk("%s: leaving eexp_hw_init586()\n", dev->name); +#endif +	return; +} + +/*  + * completely reset the EtherExpress hardware. We will most likely get + * an interrupt during this whether we want one or not. It is best, + * therefore, to call this while we don't have a request_irq() on. + */ + +static void eexp_hw_ASICrst(struct device *dev) +{ +	unsigned short ioaddr = dev->base_addr; +	unsigned short wrval = 0x0001,succount=0,boguscount=500; + +	outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ); + +	PRIV(dev)->started = 0; +	outb(ASIC_RST|i586_RST,ioaddr+EEPROM_Ctrl); +	while (succount<20)  +	{ +		if (wrval == 0xffff)  +			wrval = 0x0001; +		outw(0,ioaddr+WRITE_PTR); +		outw(wrval,ioaddr); +		outw(0,ioaddr+READ_PTR); +		if (wrval++ == inw(ioaddr))  +			succount++; +		else  +		{ +			succount = 0; +			if (!boguscount--)  +			{ +				boguscount = 500; +				printk("%s: Having problems resetting EtherExpress ASIC, continuing...\n", +					dev->name); +				wrval = 0x0001; +				outb(ASIC_RST|i586_RST,ioaddr+EEPROM_Ctrl); +			} +		} +	} +	outb(i586_RST,ioaddr+EEPROM_Ctrl); +} + + +/* + * Set or clear the multicast filter for this adaptor. + * We have to do a complete 586 restart for this to take effect. + * At the moment only promiscuous mode is supported. + */ +static void +eexp_set_multicast(struct device *dev) +{ +	if ((dev->flags & IFF_PROMISC) != PRIV(dev)->promisc) +		eexp_hw_init586(dev); +} + + +/* + * MODULE stuff + */ +#ifdef MODULE + +#define EEXP_MAX_CARDS     4    /* max number of cards to support */ +#define NAMELEN            8    /* max length of dev->name (inc null) */ + +static char namelist[NAMELEN * EEXP_MAX_CARDS] = { 0, }; + +static struct device dev_eexp[EEXP_MAX_CARDS] =  +{ +        { NULL,         /* will allocate dynamically */ +	  0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, express_probe },   +}; + +int irq[EEXP_MAX_CARDS] = {0, }; +int io[EEXP_MAX_CARDS] = {0, }; + +/* Ideally the user would give us io=, irq= for every card.  If any parameters + * are specified, we verify and then use them.  If no parameters are given, we + * autoprobe for one card only. + */ +int init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < EEXP_MAX_CARDS; this_dev++) { +		struct device *dev = &dev_eexp[this_dev]; +		dev->name = namelist + (NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		if (io[this_dev] == 0) { +			if (this_dev) break; +			printk(KERN_NOTICE "eexpress.c: Module autoprobe not recommended, give io=xx.\n"); +		} +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "eexpress.c: Failed to register card at 0x%x.\n", io[this_dev]); +			if (found != 0) return 0; +			return -ENXIO; +		} +		found++; +	} +	return 0; +} + +void cleanup_module(void) +{ +	int this_dev; +         +	for (this_dev = 0; this_dev < EEXP_MAX_CARDS; this_dev++) { +		struct device *dev = &dev_eexp[this_dev]; +		if (dev->priv != NULL) { +			kfree(dev->priv); +			dev->priv = NULL; +			release_region(dev->base_addr, EEXP_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif + +/* + * Local Variables: + *  c-file-style: "linux" + *  tab-width: 8 + *  compile-command: "gcc -D__KERNEL__ -I/discs/bibble/src/linux-1.3.69/include  -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE  -c 3c505.c" + * End: + */ diff --git a/linux/src/drivers/net/epic100.c b/linux/src/drivers/net/epic100.c new file mode 100644 index 0000000..b44f291 --- /dev/null +++ b/linux/src/drivers/net/epic100.c @@ -0,0 +1,1560 @@ +/* epic100.c: A SMC 83c170 EPIC/100 Fast Ethernet driver for Linux. */ +/* +	Written/copyright 1997-2002 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	This driver is for the SMC83c170/175 "EPIC" series, as used on the +	SMC EtherPower II 9432 PCI adapter, and several CardBus cards. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Information and updates available at +	http://www.scyld.com/network/epic100.html +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version[] = +"epic100.c:v1.18 7/22/2003 Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/epic100.html\n"; + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 32; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   This chip uses a 64 element hash table based on the Ethernet CRC.  */ +static int multicast_filter_limit = 32; + +/* Used to set a special media speed or duplex. +   Both 'options[]' and 'full_duplex[]' should exist for driver +   interoperability. +   The media type is usually passed in 'options[]'. +    The default is autonegotation for speed and duplex. +	This should rarely be overridden. +    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +    Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +    Use option values 0x20 and 0x200 for forcing full duplex operation. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for operational efficiency. +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority. +   Too-large receive rings only waste memory. */ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */ +#define RX_RING_SIZE	32 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +/* Bytes transferred to chip before transmission starts. */ +/* Initial threshold, increased on underflow, rounded down to 4 byte units. */ +#define TX_FIFO_THRESH 256 +#define RX_FIFO_THRESH 1		/* 0-3, 0==32, 64,96, or 3==128 bytes  */ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#if LINUX_VERSION_CODE >= 0x20300 +#include <linux/spinlock.h> +#elif LINUX_VERSION_CODE >= 0x20200 +#include <asm/spinlock.h> +#endif + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex.\n" +"Values are 0x10/0x20/0x100/0x200."); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, "Non-zero to set forced full duplex."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the SMC "EPIC/100", the SMC +single-chip Ethernet controllers for PCI.  This chip is used on +the SMC EtherPower II boards. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board.  The system BIOS will assign the +PCI INTA signal to a (preferably otherwise unused) system IRQ line. +Note: Kernel versions earlier than 1.3.73 do not support shared PCI +interrupt lines. + +III. Driver operation + +IIIa. Ring buffers + +IVb. References + +http://www.smsc.com/main/datasheets/83c171.pdf +http://www.smsc.com/main/datasheets/83c175.pdf +http://scyld.com/expert/NWay.html +http://www.national.com/pf/DP/DP83840A.html + +IVc. Errata + +*/ + +static void *epic_probe1(struct pci_dev *pdev, void *init_dev, +						 long ioaddr, int irq, int chip_idx, int find_cnt); +static int epic_pwr_event(void *dev_instance, int event); + +enum chip_capability_flags { MII_PWRDWN=1, TYPE2_INTR=2, NO_MII=4 }; + +#define EPIC_TOTAL_SIZE 0x100 +#ifdef USE_IO_OPS +#define EPIC_IOTYPE PCI_USES_MASTER|PCI_USES_IO|PCI_ADDR0 +#else +#define EPIC_IOTYPE PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR1 +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{"SMSC EPIC 83c172", {0x000510B8, 0xffffffff, 0,0, 9,0xff}, +	 EPIC_IOTYPE, EPIC_TOTAL_SIZE, TYPE2_INTR | MII_PWRDWN, }, +	{"SMSC EPIC 83c171", {0x000510B8, 0xffffffff, 0,0, 6,0xff}, +	 EPIC_IOTYPE, EPIC_TOTAL_SIZE, TYPE2_INTR | MII_PWRDWN, }, +	{"SMSC EPIC/100 83c170", {0x000510B8, 0xffffffff, 0x0ab41092, 0xffffffff}, +	 EPIC_IOTYPE, EPIC_TOTAL_SIZE, TYPE2_INTR | NO_MII | MII_PWRDWN, }, +	{"SMSC EPIC/100 83c170", {0x000510B8, 0xffffffff}, +	 EPIC_IOTYPE, EPIC_TOTAL_SIZE, TYPE2_INTR, }, +	{"SMSC EPIC/C 83c175", {0x000610B8, 0xffffffff}, +	 EPIC_IOTYPE, EPIC_TOTAL_SIZE, TYPE2_INTR | MII_PWRDWN, }, +	{0,}, +}; + +struct drv_id_info epic_drv_id = { +	"epic100", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	epic_probe1, epic_pwr_event }; + +#ifndef USE_IO_OPS +#undef inb +#undef inw +#undef inl +#undef outb +#undef outw +#undef outl +#define inb readb +#define inw readw +#define inl readl +#define outb writeb +#define outw writew +#define outl writel +#endif + +/* Offsets to registers, using the (ugh) SMC names. */ +enum epic_registers { +  COMMAND=0, INTSTAT=4, INTMASK=8, GENCTL=0x0C, NVCTL=0x10, EECTL=0x14, +  PCIBurstCnt=0x18, +  TEST1=0x1C, CRCCNT=0x20, ALICNT=0x24, MPCNT=0x28,	/* Rx error counters. */ +  MIICtrl=0x30, MIIData=0x34, MIICfg=0x38, +  LAN0=64,						/* MAC address. */ +  MC0=80,						/* Multicast filter table. */ +  RxCtrl=96, TxCtrl=112, TxSTAT=0x74, +  PRxCDAR=0x84, RxSTAT=0xA4, EarlyRx=0xB0, PTxCDAR=0xC4, TxThresh=0xDC, +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatus { +	TxIdle=0x40000, RxIdle=0x20000, IntrSummary=0x010000, +	PCIBusErr170=0x7000, PCIBusErr175=0x1000, PhyEvent175=0x8000, +	RxStarted=0x0800, RxEarlyWarn=0x0400, CntFull=0x0200, TxUnderrun=0x0100, +	TxEmpty=0x0080, TxDone=0x0020, RxError=0x0010, +	RxOverflow=0x0008, RxFull=0x0004, RxHeader=0x0002, RxDone=0x0001, +}; +enum CommandBits { +	StopRx=1, StartRx=2, TxQueued=4, RxQueued=8, +	StopTxDMA=0x20, StopRxDMA=0x40, RestartTx=0x80, +}; + +/* The EPIC100 Rx and Tx buffer descriptors. */ + +struct epic_tx_desc { +	u32 txstatus; +	u32 bufaddr; +	u32 buflength; +	u32 next; +}; + +struct epic_rx_desc { +	u32 rxstatus; +	u32 bufaddr; +	u32 buflength; +	u32 next; +}; + +enum desc_status_bits { +	DescOwn=0x8000, +}; + +#define PRIV_ALIGN	15 	/* Required alignment mask */ +struct epic_private { +	/* Tx and Rx rings first so that they remain paragraph aligned. */ +	struct epic_rx_desc rx_ring[RX_RING_SIZE]; +	struct epic_tx_desc tx_ring[TX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for skfree(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; + +	struct net_device *next_module; +	void *priv_addr;					/* Unaligned address for kfree */ + +	/* Ring pointers. */ +	spinlock_t lock;				/* Group with Tx control cache line. */ +	unsigned int cur_tx, dirty_tx; +	struct descriptor  *last_tx_desc; + +	unsigned int cur_rx, dirty_rx; +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	struct descriptor  *last_rx_desc; +	long last_rx_time;					/* Last Rx, in jiffies. */ +	int rx_copybreak; + +	int msg_level; +	int max_interrupt_work; +	struct pci_dev *pci_dev;			/* PCI bus location. */ +	int chip_id, chip_flags; + +	struct net_device_stats stats; +	struct timer_list timer;			/* Media selection timer. */ +	int tx_threshold; +	int genctl;							/* Including Rx threshold. */ +	u32 cur_rx_mode; +	unsigned char mc_filter[8]; +	int multicast_filter_limit; + +	signed char phys[4];				/* MII device addresses. */ +	u16 mii_bmcr;						/* MII control register */ +	u16 advertising;					/* NWay media advertisement */ +	int mii_phy_cnt; +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	unsigned int full_duplex:1;			/* Current duplex setting. */ +	unsigned int duplex_lock:1;			/* Duplex forced by the user. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	unsigned int media2:4;				/* Secondary monitored media port. */ +	unsigned int medialock:1;			/* Don't sense media type. */ +	unsigned int mediasense:1;			/* Media sensing in progress. */ +}; + +static int epic_open(struct net_device *dev); +static int read_eeprom(long ioaddr, int location); +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int loc, int val); +static void epic_start(struct net_device *dev, int restart); +static void check_media(struct net_device *dev); +static void epic_timer(unsigned long data); +static void epic_tx_timeout(struct net_device *dev); +static void epic_init_ring(struct net_device *dev); +static int epic_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int epic_rx(struct net_device *dev); +static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int epic_close(struct net_device *dev); +static struct net_device_stats *epic_get_stats(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); + + +/* A list of all installed EPIC devices, for removing the driver module. */ +static struct net_device *root_epic_dev = NULL; + +static void *epic_probe1(struct pci_dev *pdev, void *init_dev, +						 long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct epic_private *ep; +	void *priv_mem; +	int i, option = 0, duplex = 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	if (dev->mem_start) { +		option = dev->mem_start; +		duplex = (dev->mem_start & 16) ? 1 : 0; +	} else if (card_idx >= 0  &&  card_idx < MAX_UNITS) { +		if (options[card_idx] >= 0) +			option = options[card_idx]; +		if (full_duplex[card_idx] >= 0) +			duplex = full_duplex[card_idx]; +	} + +	dev->base_addr = ioaddr; +	dev->irq = irq; +	printk(KERN_INFO "%s: %s at %#lx, %2.2x:%2.2x IRQ %d, ", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr, +		   pci_bus_number(pdev), pci_devfn(pdev)>>3, dev->irq); + +	/* Bring the chip out of low-power mode. */ +	outl(0x4200, ioaddr + GENCTL); +	/* Magic from SMSC app note 7.15 */ +	outl(0x0008, ioaddr + TEST1); + +	/* Turn on the MII transceiver. */ +	outl(0x12, ioaddr + MIICfg); +	if (pci_id_tbl[chip_idx].drv_flags & NO_MII) +		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); +	outl(0x0200, ioaddr + GENCTL); + +	if (((1 << debug) - 1) & NETIF_MSG_MISC) { +		printk(KERN_DEBUG "%s: EEPROM contents\n", dev->name); +		for (i = 0; i < 64; i++) +			printk(" %4.4x%s", read_eeprom(ioaddr, i), +				   i % 16 == 15 ? "\n" : ""); +	} + +	/* Note: the '175 does not have a serial EEPROM. */ +	for (i = 0; i < 3; i++) +		((u16 *)dev->dev_addr)[i] = le16_to_cpu(inw(ioaddr + LAN0 + i*4)); + +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x.\n", dev->dev_addr[i]); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*ep) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	/* We do a request_region() to register /proc/ioports info. */ +	request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name); + +	dev->priv = ep = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(ep, 0, sizeof(*ep)); +	ep->priv_addr = priv_mem; + +	ep->next_module = root_epic_dev; +	root_epic_dev = dev; + +	ep->pci_dev = pdev; +	ep->chip_id = chip_idx; +	ep->chip_flags = pci_id_tbl[chip_idx].drv_flags; +	ep->msg_level = (1 << debug) - 1; +	ep->rx_copybreak = rx_copybreak; +	ep->max_interrupt_work = max_interrupt_work; +	ep->multicast_filter_limit = multicast_filter_limit; + +	/* The lower four bits are non-TP media types. */ +	if (option > 0) { +		if (option & 0x220) +			ep->duplex_lock = ep->full_duplex = 1; +		ep->default_port = option & 0xFFFF; +		ep->medialock = 1; +	} +	if (duplex) { +		ep->duplex_lock = ep->full_duplex = 1; +		printk(KERN_INFO "%s:  Forced full duplex operation requested.\n", +			   dev->name); +	} +	dev->if_port = ep->default_port; + +	/* Find the connected MII xcvrs. +	   Doing this in open() would allow detecting external xcvrs later, but +	   takes much time and no cards have external MII. */ +	{ +		int phy, phy_idx = 0; +		for (phy = 1; phy < 32 && phy_idx < sizeof(ep->phys); phy++) { +			int mii_status = mdio_read(dev, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				ep->phys[phy_idx++] = phy; +				printk(KERN_INFO "%s: Located MII transceiver #%d control " +					   "%4.4x status %4.4x.\n", +					   dev->name, phy, mdio_read(dev, phy, 0), mii_status); +			} +		} +		ep->mii_phy_cnt = phy_idx; +	} +	if (ep->mii_phy_cnt == 0  &&  ! (ep->chip_flags & NO_MII)) { +		printk(KERN_WARNING "%s: ***WARNING***: No MII transceiver found!\n", +			   dev->name); +		/* Use the known PHY address of the EPII. */ +		ep->phys[0] = 3; +	} + +	if (ep->mii_phy_cnt) { +		int phy = ep->phys[0]; +		int xcvr = ep->default_port & 0x330; +		if (xcvr) { +			printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +				   (xcvr & 0x300 ? 100 : 10), +				   (xcvr & 0x220 ? "full" : "half")); +			ep->mii_bmcr  = xcvr & 0x300 ? 0x2000 : 0;	/* 10/100mbps? */ +			ep->mii_bmcr |= xcvr & 0x220 ? 0x0100 : 0; 	/* duplex */ +			mdio_write(dev, phy, 0, ep->mii_bmcr); +		} else { +			ep->mii_bmcr = 0x3000; +			ep->advertising = mdio_read(dev, phy, 4); +			printk(KERN_INFO "%s: Autonegotiation advertising %4.4x link " +				   "partner %4.4x.\n", +				   dev->name, ep->advertising, mdio_read(dev, phy, 5)); +		} +	} + +#if EPIC_POWER_SAVE +	/* Turn off the MII xcvr (175 only!), leave the chip in low-power mode. */ +	if (ep->chip_flags & MII_PWRDWN) +		outl(inl(ioaddr + NVCTL) & ~0x483C, ioaddr + NVCTL); +#endif +	outl(0x0008, ioaddr + GENCTL); + +	/* The Epic-specific entries in the device structure. */ +	dev->open = &epic_open; +	dev->hard_start_xmit = &epic_start_xmit; +	dev->stop = &epic_close; +	dev->get_stats = &epic_get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; + +	return dev; +} + +/* Serial EEPROM section. */ + +/*  EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */ +#define EE_CS			0x02	/* EEPROM chip select. */ +#define EE_DATA_WRITE	0x08	/* EEPROM chip data in. */ +#define EE_WRITE_0		0x01 +#define EE_WRITE_1		0x09 +#define EE_DATA_READ	0x10	/* EEPROM chip data out. */ +#define EE_ENB			(0x0001 | EE_CS) + +/* Delay between EEPROM clock transitions. +   This serves to flush the operation to the PCI bus. + */ + +#define eeprom_delay()	inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD	(5 << 6) +#define EE_READ64_CMD	(6 << 6) +#define EE_READ256_CMD	(6 << 8) +#define EE_ERASE_CMD	(7 << 6) + +static int read_eeprom(long ioaddr, int location) +{ +	int i; +	int retval = 0; +	long ee_addr = ioaddr + EECTL; +	int read_cmd = location | +		(inl(ee_addr) & 0x40 ? EE_READ64_CMD : EE_READ256_CMD); + +	outl(EE_ENB & ~EE_CS, ee_addr); +	outl(EE_ENB, ee_addr); + +	/* Shift the read command bits out. */ +	for (i = 12; i >= 0; i--) { +		short dataval = (read_cmd & (1 << i)) ? EE_WRITE_1 : EE_WRITE_0; +		outl(EE_ENB | dataval, ee_addr); +		eeprom_delay(); +		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); +		eeprom_delay(); +	} +	outl(EE_ENB, ee_addr); + +	for (i = 16; i > 0; i--) { +		outl(EE_ENB | EE_SHIFT_CLK, ee_addr); +		eeprom_delay(); +		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); +		outl(EE_ENB, ee_addr); +		eeprom_delay(); +	} + +	/* Terminate the EEPROM access. */ +	outl(EE_ENB & ~EE_CS, ee_addr); +	return retval; +} + +#define MII_READOP		1 +#define MII_WRITEOP		2 +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ +	long ioaddr = dev->base_addr; +	int read_cmd = (phy_id << 9) | (location << 4) | MII_READOP; +	int i; + +	outl(read_cmd, ioaddr + MIICtrl); +	/* Typical operation takes 25 loops. */ +	for (i = 400; i > 0; i--) +		if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0) { +			/* Work around read failure bug. */ +			if (phy_id == 1 && location < 6 +				&& inw(ioaddr + MIIData) == 0xffff) { +				outl(read_cmd, ioaddr + MIICtrl); +				continue; +			} +			return inw(ioaddr + MIIData); +		} +	return 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int loc, int value) +{ +	long ioaddr = dev->base_addr; +	int i; + +	outw(value, ioaddr + MIIData); +	outl((phy_id << 9) | (loc << 4) | MII_WRITEOP, ioaddr + MIICtrl); +	for (i = 10000; i > 0; i--) { +		if ((inl(ioaddr + MIICtrl) & MII_WRITEOP) == 0) +			break; +	} +	return; +} + + +static int epic_open(struct net_device *dev) +{ +	struct epic_private *ep = (struct epic_private *)dev->priv; + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &epic_interrupt, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	epic_init_ring(dev); +	check_media(dev); +	epic_start(dev, 0); + +	/* Set the timer to switch to check for link beat and perhaps switch +	   to an alternate media type. */ +	init_timer(&ep->timer); +	ep->timer.expires = jiffies + 3*HZ; +	ep->timer.data = (unsigned long)dev; +	ep->timer.function = &epic_timer;				/* timer handler */ +	add_timer(&ep->timer); + +	return 0; +} + +/* Reset the chip to recover from a PCI transaction error. +   This may occur at interrupt time. */ +static void epic_pause(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct epic_private *ep = (struct epic_private *)dev->priv; + +	/* Disable interrupts by clearing the interrupt mask. */ +	outl(0x00000000, ioaddr + INTMASK); +	/* Stop the chip's Tx and Rx DMA processes. */ +	outw(StopRx | StopTxDMA | StopRxDMA, ioaddr + COMMAND); + +	/* Update the error counts. */ +	if (inw(ioaddr + COMMAND) != 0xffff) { +		ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); +		ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); +		ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); +	} + +	/* Remove the packets on the Rx queue. */ +	epic_rx(dev); +} + +static void epic_start(struct net_device *dev, int restart) +{ +	long ioaddr = dev->base_addr; +	struct epic_private *ep = (struct epic_private *)dev->priv; +	int i; + +	if (restart) { +		/* Soft reset the chip. */ +		outl(0x4001, ioaddr + GENCTL); +		printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n", +			   dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx); +		udelay(1); + +		/* This magic is documented in SMSC app note 7.15 */ +		for (i = 16; i > 0; i--) +			outl(0x0008, ioaddr + TEST1); +	} + +#if defined(__powerpc__) || defined(__sparc__) || defined(__BIG_ENDIAN) +	ep->genctl = 0x0432 | (RX_FIFO_THRESH<<8); +#elif defined(__LITTLE_ENDIAN) || defined(__i386__) +	ep->genctl = 0x0412 | (RX_FIFO_THRESH<<8); +#else +#error The byte order of this architecture is not defined. +#endif + +	/* Power and reset the PHY. */ +	if (ep->chip_flags & MII_PWRDWN) +		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); +	if (restart) { +		outl(ep->genctl | 0x4000, ioaddr + GENCTL); +		inl(ioaddr + GENCTL); +	} +	outl(ep->genctl, ioaddr + GENCTL); + +	if (dev->if_port == 2 || dev->if_port == 5) {	/* 10base2 or AUI */ +		outl(0x13, ioaddr + MIICfg); +		printk(KERN_INFO "%s: Disabling MII PHY to use 10base2/AUI.\n", +			   dev->name); +		mdio_write(dev, ep->phys[0], 0, 0x0C00); +	} else { +		outl(0x12, ioaddr + MIICfg); +		mdio_write(dev, ep->phys[0], 0, ep->advertising); +		mdio_write(dev, ep->phys[0], 0, ep->mii_bmcr); +		check_media(dev); +	} + +	for (i = 0; i < 3; i++) +		outl(cpu_to_le16(((u16*)dev->dev_addr)[i]), ioaddr + LAN0 + i*4); + +	ep->tx_threshold = TX_FIFO_THRESH; +	outl(ep->tx_threshold, ioaddr + TxThresh); +	outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); +	outl(virt_to_bus(&ep->rx_ring[ep->cur_rx % RX_RING_SIZE]), +		 ioaddr + PRxCDAR); +	outl(virt_to_bus(&ep->tx_ring[ep->dirty_tx % TX_RING_SIZE]), +		 ioaddr + PTxCDAR); + +	/* Start the chip's Rx process. */ +	set_rx_mode(dev); +	outl(StartRx | RxQueued, ioaddr + COMMAND); + +	if ( ! restart) +		netif_start_tx_queue(dev); + +	/* Enable interrupts by setting the interrupt mask. */ +	outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170) +		 | CntFull | TxUnderrun | TxDone | TxEmpty +		 | RxError | RxOverflow | RxFull | RxHeader | RxDone, +		 ioaddr + INTMASK); +	if (ep->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: epic_start() done, cmd status %4.4x, " +			   "ctl %4.4x interrupt %4.4x.\n", +			   dev->name, (int)inl(ioaddr + COMMAND), +			   (int)inl(ioaddr + GENCTL), (int)inl(ioaddr + INTSTAT)); +	return; +} + +static void check_media(struct net_device *dev) +{ +	struct epic_private *ep = (struct epic_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int mii_reg5 = ep->mii_phy_cnt ? mdio_read(dev, ep->phys[0], 5) : 0; +	int negotiated = mii_reg5 & ep->advertising; +	int duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; + +	if (ep->duplex_lock) +		return; +	if (mii_reg5 == 0xffff)		/* Bogus read */ +		return; +	if (ep->full_duplex != duplex) { +		ep->full_duplex = duplex; +		printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" +			   " partner capability of %4.4x.\n", dev->name, +			   ep->full_duplex ? "full" : "half", ep->phys[0], mii_reg5); +		outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl); +	} +} + +static void epic_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct epic_private *ep = (struct epic_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 5*HZ; + +	if (ep->msg_level & NETIF_MSG_TIMER) { +		printk(KERN_DEBUG "%s: Media monitor tick, Tx status %8.8x.\n", +			   dev->name, (int)inl(ioaddr + TxSTAT)); +		printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x " +			   "IntStatus %4.4x RxStatus %4.4x.\n", +			   dev->name, (int)inl(ioaddr + INTMASK), +			   (int)inl(ioaddr + INTSTAT), (int)inl(ioaddr + RxSTAT)); +	} + +	if (ep->cur_tx - ep->dirty_tx > 1  && +		jiffies - dev->trans_start > TX_TIMEOUT) { +		printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n", +			   dev->name, ep->cur_tx, ep->dirty_tx); +		epic_tx_timeout(dev); +	} + +	check_media(dev); + +	ep->timer.expires = jiffies + next_tick; +	add_timer(&ep->timer); +} + +static void epic_tx_timeout(struct net_device *dev) +{ +	struct epic_private *ep = (struct epic_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int tx_status = inw(ioaddr + TxSTAT); + +	printk(KERN_WARNING "%s: EPIC transmit timeout, Tx status %4.4x.\n", +		   dev->name, tx_status); +	if (ep->msg_level & NETIF_MSG_TX_ERR) +		printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n", +			   dev->name, ep->dirty_tx, ep->cur_tx); +	if (tx_status & 0x10) {		/* Tx FIFO underflow. */ +		ep->stats.tx_fifo_errors++; +		outl(RestartTx, ioaddr + COMMAND); +	} else { +		epic_start(dev, 1); +		outl(TxQueued, dev->base_addr + COMMAND); +	} + +	dev->trans_start = jiffies; +	ep->stats.tx_errors++; +	return; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void epic_init_ring(struct net_device *dev) +{ +	struct epic_private *ep = (struct epic_private *)dev->priv; +	int i; + +	ep->tx_full = 0; +	ep->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; +	ep->dirty_tx = ep->cur_tx = 0; +	ep->cur_rx = ep->dirty_rx = 0; +	ep->last_rx_time = jiffies; +	ep->rx_buf_sz = (dev->mtu <= 1522 ? PKT_BUF_SZ : dev->mtu + 14); + +	/* Initialize all Rx descriptors. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		ep->rx_ring[i].rxstatus = 0; +		ep->rx_ring[i].buflength = ep->rx_buf_sz; +		ep->rx_ring[i].next = virt_to_bus(&ep->rx_ring[i+1]); +		ep->rx_skbuff[i] = 0; +	} +	/* Mark the last entry as wrapping the ring. */ +	ep->rx_ring[i-1].next = virt_to_bus(&ep->rx_ring[0]); + +	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = dev_alloc_skb(ep->rx_buf_sz); +		ep->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		skb_reserve(skb, 2);	/* 16 byte align the IP header. */ +		ep->rx_ring[i].bufaddr = virt_to_bus(skb->tail); +		ep->rx_ring[i].rxstatus = DescOwn; +	} +	ep->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + +	/* The Tx buffer descriptor is filled in as needed, but we +	   do need to clear the ownership bit. */ +	for (i = 0; i < TX_RING_SIZE; i++) { +		ep->tx_skbuff[i] = 0; +		ep->tx_ring[i].txstatus = 0x0000; +		ep->tx_ring[i].next = virt_to_bus(&ep->tx_ring[i+1]); +	} +	ep->tx_ring[i-1].next = virt_to_bus(&ep->tx_ring[0]); +	return; +} + +static int epic_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct epic_private *ep = (struct epic_private *)dev->priv; +	int entry, free_count; +	u32 ctrl_word; +	unsigned long flags; + +	/* Block a timer-based transmit from overlapping. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			epic_tx_timeout(dev); +		return 1; +	} + +	/* Caution: the write order is important here, set the field with the +	   "ownership" bit last. */ + +	/* Calculate the next Tx descriptor entry. */ +	spin_lock_irqsave(&ep->lock, flags); +	free_count = ep->cur_tx - ep->dirty_tx; +	entry = ep->cur_tx % TX_RING_SIZE; + +	ep->tx_skbuff[entry] = skb; +	ep->tx_ring[entry].bufaddr = virt_to_bus(skb->data); + +	if (free_count < TX_QUEUE_LEN/2) {/* Typical path */ +		ctrl_word = 0x100000; /* No interrupt */ +	} else if (free_count == TX_QUEUE_LEN/2) { +		ctrl_word = 0x140000; /* Tx-done intr. */ +	} else if (free_count < TX_QUEUE_LEN - 1) { +		ctrl_word = 0x100000; /* No Tx-done intr. */ +	} else { +		/* Leave room for an additional entry. */ +		ctrl_word = 0x140000; /* Tx-done intr. */ +		ep->tx_full = 1; +	} +	ep->tx_ring[entry].buflength = ctrl_word | skb->len; +	ep->tx_ring[entry].txstatus = +		((skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN) << 16) +		| DescOwn; + +	ep->cur_tx++; +	if (ep->tx_full) { +		/* Check for a just-cleared queue. */ +		if (ep->cur_tx - (volatile int)ep->dirty_tx < TX_QUEUE_LEN - 2) { +			netif_unpause_tx_queue(dev); +			ep->tx_full = 0; +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev); + +	spin_unlock_irqrestore(&ep->lock, flags); +	/* Trigger an immediate transmit demand. */ +	outl(TxQueued, dev->base_addr + COMMAND); + +	dev->trans_start = jiffies; +	if (ep->msg_level & NETIF_MSG_TX_QUEUED) +		printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, " +			   "flag %2.2x Tx status %8.8x.\n", +			   dev->name, (int)skb->len, entry, ctrl_word, +			   (int)inl(dev->base_addr + TxSTAT)); + +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void epic_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct epic_private *ep = (struct epic_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int status, boguscnt = max_interrupt_work; + +	do { +		status = inl(ioaddr + INTSTAT); +		/* Acknowledge all of the current interrupt sources ASAP. */ +		outl(status & 0x00007fff, ioaddr + INTSTAT); + +		if (ep->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt, status=%#8.8x new " +				   "intstat=%#8.8x.\n", +				   dev->name, status, (int)inl(ioaddr + INTSTAT)); + +		if ((status & IntrSummary) == 0) +			break; + +		if (status & (RxDone | RxStarted | RxEarlyWarn | RxOverflow)) +			epic_rx(dev); + +		if (status & (TxEmpty | TxDone)) { +			unsigned int dirty_tx, cur_tx; + +			/* Note: if this lock becomes a problem we can narrow the locked +			   region at the cost of occasionally grabbing the lock more +			   times. */ +			spin_lock(&ep->lock); +			cur_tx = ep->cur_tx; +			dirty_tx = ep->dirty_tx; +			for (; cur_tx - dirty_tx > 0; dirty_tx++) { +				int entry = dirty_tx % TX_RING_SIZE; +				int txstatus = ep->tx_ring[entry].txstatus; + +				if (txstatus & DescOwn) +					break;			/* It still hasn't been Txed */ + +				if ( ! (txstatus & 0x0001)) { +					/* There was an major error, log it. */ +#ifndef final_version +					if (ep->msg_level & NETIF_MSG_TX_ERR) +						printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", +							   dev->name, txstatus); +#endif +					ep->stats.tx_errors++; +					if (txstatus & 0x1050) ep->stats.tx_aborted_errors++; +					if (txstatus & 0x0008) ep->stats.tx_carrier_errors++; +					if (txstatus & 0x0040) ep->stats.tx_window_errors++; +					if (txstatus & 0x0010) ep->stats.tx_fifo_errors++; +#ifdef ETHER_STATS +					if (txstatus & 0x1000) ep->stats.collisions16++; +#endif +				} else { +					if (ep->msg_level & NETIF_MSG_TX_DONE) +						printk(KERN_DEBUG "%s: Transmit done, Tx status " +							   "%8.8x.\n", dev->name, txstatus); +#ifdef ETHER_STATS +					if ((txstatus & 0x0002) != 0) ep->stats.tx_deferred++; +#endif +					ep->stats.collisions += (txstatus >> 8) & 15; +					ep->stats.tx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +					ep->stats.tx_bytes += ep->tx_skbuff[entry]->len; +#endif +				} + +				/* Free the original skb. */ +				dev_free_skb_irq(ep->tx_skbuff[entry]); +				ep->tx_skbuff[entry] = 0; +			} + +#ifndef final_version +			if (cur_tx - dirty_tx > TX_RING_SIZE) { +				printk(KERN_WARNING "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", +					   dev->name, dirty_tx, cur_tx, ep->tx_full); +				dirty_tx += TX_RING_SIZE; +			} +#endif +			ep->dirty_tx = dirty_tx; +			if (ep->tx_full +				&& cur_tx - dirty_tx < TX_QUEUE_LEN - 4) { +				/* The ring is no longer full, allow new TX entries. */ +				ep->tx_full = 0; +				spin_unlock(&ep->lock); +				netif_resume_tx_queue(dev); +			} else +				spin_unlock(&ep->lock); +		} + +		/* Check uncommon events all at once. */ +		if (status & (CntFull | TxUnderrun | RxOverflow | RxFull | +					  PCIBusErr170 | PCIBusErr175)) { +			if (status == 0xffffffff) /* Chip failed or removed (CardBus). */ +				break; +			/* Always update the error counts to avoid overhead later. */ +			ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); +			ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); +			ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); + +			if (status & TxUnderrun) { /* Tx FIFO underflow. */ +				ep->stats.tx_fifo_errors++; +				outl(ep->tx_threshold += 128, ioaddr + TxThresh); +				/* Restart the transmit process. */ +				outl(RestartTx, ioaddr + COMMAND); +			} +			if (status & RxOverflow) {		/* Missed a Rx frame. */ +				ep->stats.rx_errors++; +			} +			if (status & (RxOverflow | RxFull)) +				outw(RxQueued, ioaddr + COMMAND); +			if (status & PCIBusErr170) { +				printk(KERN_ERR "%s: PCI Bus Error!  EPIC status %4.4x.\n", +					   dev->name, status); +				epic_pause(dev); +				epic_start(dev, 1); +			} +			/* Clear all error sources. */ +			outl(status & 0x7f18, ioaddr + INTSTAT); +		} +		if (--boguscnt < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "IntrStatus=0x%8.8x.\n", +				   dev->name, status); +			/* Clear all interrupt sources. */ +			outl(0x0001ffff, ioaddr + INTSTAT); +			/* Ill-advised: Slowly stop emitting this message. */ +			max_interrupt_work++; +			break; +		} +	} while (1); + +	if (ep->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: Exiting interrupt, intr_status=%#4.4x.\n", +			   dev->name, status); + +	return; +} + +static int epic_rx(struct net_device *dev) +{ +	struct epic_private *ep = (struct epic_private *)dev->priv; +	int entry = ep->cur_rx % RX_RING_SIZE; +	int rx_work_limit = ep->dirty_rx + RX_RING_SIZE - ep->cur_rx; +	int work_done = 0; + +	if (ep->msg_level & NETIF_MSG_RX_STATUS) +		printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry, +			   ep->rx_ring[entry].rxstatus); +	/* If we own the next entry, it's a new packet. Send it up. */ +	while ((ep->rx_ring[entry].rxstatus & DescOwn) == 0) { +		int status = ep->rx_ring[entry].rxstatus; + +		if (ep->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  epic_rx() status was %8.8x.\n", status); +		if (--rx_work_limit < 0) +			break; +		if (status & 0x2006) { +			if (ep->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG "%s: epic_rx() error status was %8.8x.\n", +					   dev->name, status); +			if (status & 0x2000) { +				printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " +					   "multiple buffers, status %4.4x!\n", dev->name, status); +				ep->stats.rx_length_errors++; +			} else if (status & 0x0006) +				/* Rx Frame errors are counted in hardware. */ +				ep->stats.rx_errors++; +		} else { +			/* Malloc up new buffer, compatible with net-2e. */ +			/* Omit the four octet CRC from the length. */ +			short pkt_len = (status >> 16) - 4; +			struct sk_buff *skb; + +			if (pkt_len > PKT_BUF_SZ - 4) { +				printk(KERN_ERR "%s: Oversized Ethernet frame, status %x " +					   "%d bytes.\n", +					   dev->name, pkt_len, status); +				pkt_len = 1514; +			} +			if (ep->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d" +					   ", bogus_cnt %d.\n", pkt_len, rx_work_limit); +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +#if 1 /* HAS_IP_COPYSUM */ +				eth_copy_and_sum(skb, ep->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), ep->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +			} else { +				skb_put(skb = ep->rx_skbuff[entry], pkt_len); +				ep->rx_skbuff[entry] = NULL; +			} +			skb->protocol = eth_type_trans(skb, dev); +			netif_rx(skb); +			ep->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			ep->stats.rx_bytes += pkt_len; +#endif +		} +		work_done++; +		entry = (++ep->cur_rx) % RX_RING_SIZE; +	} + +	/* Refill the Rx ring buffers. */ +	for (; ep->cur_rx - ep->dirty_rx > 0; ep->dirty_rx++) { +		entry = ep->dirty_rx % RX_RING_SIZE; +		if (ep->rx_skbuff[entry] == NULL) { +			struct sk_buff *skb; +			skb = ep->rx_skbuff[entry] = dev_alloc_skb(ep->rx_buf_sz); +			if (skb == NULL) +				break; +			skb->dev = dev;			/* Mark as being used by this device. */ +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			ep->rx_ring[entry].bufaddr = virt_to_bus(skb->tail); +			work_done++; +		} +		ep->rx_ring[entry].rxstatus = DescOwn; +	} +	return work_done; +} + +static int epic_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct epic_private *ep = (struct epic_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (ep->msg_level & NETIF_MSG_IFDOWN) +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x.\n", +			   dev->name, (int)inl(ioaddr + INTSTAT)); + +	epic_pause(dev); +	del_timer(&ep->timer); +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = ep->rx_skbuff[i]; +		ep->rx_skbuff[i] = 0; +		ep->rx_ring[i].rxstatus = 0;		/* Not owned by Epic chip. */ +		ep->rx_ring[i].buflength = 0; +		ep->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */ +		if (skb) { +#if LINUX_VERSION_CODE < 0x20100 +			skb->free = 1; +#endif +			dev_free_skb(skb); +		} +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (ep->tx_skbuff[i]) +			dev_free_skb(ep->tx_skbuff[i]); +		ep->tx_skbuff[i] = 0; +	} + +	/* Green! Leave the chip in low-power mode. */ +	outl(0x440008, ioaddr + GENCTL); + +	MOD_DEC_USE_COUNT; +	return 0; +} + +static struct net_device_stats *epic_get_stats(struct net_device *dev) +{ +	struct epic_private *ep = (struct epic_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (netif_running(dev)) { +		/* Update the error counts. */ +		ep->stats.rx_missed_errors += inb(ioaddr + MPCNT); +		ep->stats.rx_frame_errors += inb(ioaddr + ALICNT); +		ep->stats.rx_crc_errors += inb(ioaddr + CRCCNT); +	} + +	return &ep->stats; +} + +/* Set or clear the multicast filter for this adaptor. +   Note that we only use exclusion around actually queueing the +   new frame, not around filling ep->setup_frame.  This is non-deterministic +   when re-entered but still correct. */ + +/* The little-endian AUTODIN II ethernet CRC calculation. +   N.B. Do not use for bulk data, use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ +	unsigned int crc = 0xffffffff;	/* Initial value. */ +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct epic_private *ep = (struct epic_private *)dev->priv; +	unsigned char mc_filter[8];		 /* Multicast hash filter */ +	u32 new_rx_mode; +	int i; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		new_rx_mode = 0x002C; +		/* Unconditionally log net taps. */ +		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); +		memset(mc_filter, 0xff, sizeof(mc_filter)); +	} else if ((dev->mc_count > 0)  ||  (dev->flags & IFF_ALLMULTI)) { +		/* There is apparently a chip bug, so the multicast filter +		   is never enabled. */ +		/* Too many to filter perfectly -- accept all multicasts. */ +		memset(mc_filter, 0xff, sizeof(mc_filter)); +		new_rx_mode = 0x000C; +	} else if (dev->mc_count == 0) { +		memset(mc_filter, 0, sizeof(mc_filter)); +		new_rx_mode = 0x0004; +	} else {					/* Never executed, for now. */ +		struct dev_mc_list *mclist; + +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) +			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, +					mc_filter); +		new_rx_mode = 0x000C; +	} +	if (ep->cur_rx_mode != new_rx_mode) { +		ep->cur_rx_mode = new_rx_mode; +		outl(new_rx_mode, ioaddr + RxCtrl); +	} +	/* ToDo: perhaps we need to stop the Tx and Rx process here? */ +	if (memcmp(mc_filter, ep->mc_filter, sizeof(mc_filter))) { +		for (i = 0; i < 4; i++) +			outw(((u16 *)mc_filter)[i], ioaddr + MC0 + i*4); +		memcpy(ep->mc_filter, mc_filter, sizeof(mc_filter)); +	} +	return; +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct epic_private *ep = (void *)dev->priv; +	long ioaddr = dev->base_addr; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = ep->phys[0] & 0x1f; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		if (! netif_running(dev)) { +			outl(0x0200, ioaddr + GENCTL); +			outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); +		} +		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); +#if defined(PWRDWN_AFTER_IOCTL) +		if (! netif_running(dev)) { +			outl(0x0008, ioaddr + GENCTL); +			outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL); +		} +#endif +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (! netif_running(dev)) { +			outl(0x0200, ioaddr + GENCTL); +			outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL); +		} +		if (data[0] == ep->phys[0]) { +			u16 value = data[2]; +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				ep->duplex_lock = (value & 0x9000) ? 0 : 1; +				if (ep->duplex_lock) +					ep->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: ep->advertising = value; break; +			} +			/* Perhaps check_duplex(dev), depending on chip semantics. */ +		} +		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); +#if defined(PWRDWN_AFTER_IOCTL) +		if (! netif_running(dev)) { +			outl(0x0008, ioaddr + GENCTL); +			outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL); +		} +#endif +		return 0; +	case SIOCGPARAMS: +		data32[0] = ep->msg_level; +		data32[1] = ep->multicast_filter_limit; +		data32[2] = ep->max_interrupt_work; +		data32[3] = ep->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		ep->msg_level = data32[0]; +		ep->multicast_filter_limit = data32[1]; +		ep->max_interrupt_work = data32[2]; +		ep->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static int epic_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct epic_private *ep = (struct epic_private *)dev->priv; +	long ioaddr = dev->base_addr; +	if (ep->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_SUSPEND: +		epic_pause(dev); +		/* Put the chip into low-power mode. */ +		outl(0x0008, ioaddr + GENCTL); +		break; +	case DRV_RESUME: +		epic_start(dev, 1); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[ep->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_epic_dev; *devp; devp = next) { +			next = &((struct epic_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (ep->priv_addr) +			kfree(ep->priv_addr); +		kfree(dev); +		/*MOD_DEC_USE_COUNT;*/ +		break; +	} +	} + +	return 0; +} + + +#ifdef CARDBUS + +#include <pcmcia/driver_ops.h> + +static dev_node_t *epic_attach(dev_locator_t *loc) +{ +	struct net_device *dev; +	u16 dev_id; +	u32 pciaddr; +	u8 bus, devfn, irq; +	long ioaddr; + +	if (loc->bus != LOC_PCI) return NULL; +	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; +	printk(KERN_DEBUG "epic_attach(bus %d, function %d)\n", bus, devfn); +#ifdef USE_IO_OPS +	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &pciaddr); +	ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK; +#else +	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_1, &pciaddr); +	ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, +						   pci_id_tbl[1].io_size); +#endif +	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); +	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); +	if (ioaddr == 0 || irq == 0) { +		printk(KERN_ERR "The EPIC/C CardBus Ethernet interface at %d/%d was " +			   "not assigned an %s.\n" +			   KERN_ERR "  It will not be activated.\n", +			   bus, devfn, ioaddr == 0 ? "address" : "IRQ"); +		return NULL; +	} +	dev = epic_probe1(pci_find_slot(bus, devfn), NULL, ioaddr, irq, 1, 0); +	if (dev) { +		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); +		strcpy(node->dev_name, dev->name); +		node->major = node->minor = 0; +		node->next = NULL; +		MOD_INC_USE_COUNT; +		return node; +	} +	return NULL; +} + +static void epic_suspend(dev_node_t *node) +{ +	struct net_device **devp, **next; +	printk(KERN_INFO "epic_suspend(%s)\n", node->dev_name); +	for (devp = &root_epic_dev; *devp; devp = next) { +		next = &((struct epic_private *)(*devp)->priv)->next_module; +		if (strcmp((*devp)->name, node->dev_name) == 0) break; +	} +	if (*devp) { +		long ioaddr = (*devp)->base_addr; +		epic_pause(*devp); +		/* Put the chip into low-power mode. */ +		outl(0x0008, ioaddr + GENCTL); +	} +} +static void epic_resume(dev_node_t *node) +{ +	struct net_device **devp, **next; +	printk(KERN_INFO "epic_resume(%s)\n", node->dev_name); +	for (devp = &root_epic_dev; *devp; devp = next) { +		next = &((struct epic_private *)(*devp)->priv)->next_module; +		if (strcmp((*devp)->name, node->dev_name) == 0) break; +	} +	if (*devp) { +		epic_start(*devp, 1); +	} +} +static void epic_detach(dev_node_t *node) +{ +	struct net_device **devp, **next; +	printk(KERN_INFO "epic_detach(%s)\n", node->dev_name); +	for (devp = &root_epic_dev; *devp; devp = next) { +		next = &((struct epic_private *)(*devp)->priv)->next_module; +		if (strcmp((*devp)->name, node->dev_name) == 0) break; +	} +	if (*devp) { +		unregister_netdev(*devp); +		release_region((*devp)->base_addr, EPIC_TOTAL_SIZE); +#ifndef USE_IO_OPS +		iounmap((char *)(*devp)->base_addr); +#endif +		kfree(*devp); +		*devp = *next; +		kfree(node); +		MOD_DEC_USE_COUNT; +	} +} + +struct driver_operations epic_ops = { +	"epic_cb", epic_attach, epic_suspend, epic_resume, epic_detach +}; + +#endif  /* Cardbus support */ + + +#ifdef MODULE + +int init_module(void) +{ +	/* Emit version even if no cards detected. */ +	printk(KERN_INFO "%s", version); + +#ifdef CARDBUS +	register_driver(&epic_ops); +	return 0; +#else +	return pci_drv_register(&epic_drv_id, NULL); +#endif +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +#ifdef CARDBUS +	unregister_driver(&epic_ops); +#else +	pci_drv_unregister(&epic_drv_id); +#endif + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_epic_dev) { +		struct epic_private *ep = (struct epic_private *)root_epic_dev->priv; +		unregister_netdev(root_epic_dev); +		release_region(root_epic_dev->base_addr, pci_id_tbl[ep->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)root_epic_dev->base_addr); +#endif +		next_dev = ep->next_module; +		if (ep->priv_addr) +			kfree(ep->priv_addr); +		kfree(root_epic_dev); +		root_epic_dev = next_dev; +	} +} +#else +int epic100_probe(struct net_device *dev) +{ +	int retval = pci_drv_register(&epic_drv_id, dev); +	if (retval >= 0) +		printk(KERN_INFO "%s", version); +	return retval; +} +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c epic100.c" + *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -Wall -Wstrict-prototypes -O6 -c epic100.c -o epic_cb.o -I/usr/src/pcmcia/include/" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/eth16i.c b/linux/src/drivers/net/eth16i.c new file mode 100644 index 0000000..244c3e7 --- /dev/null +++ b/linux/src/drivers/net/eth16i.c @@ -0,0 +1,1604 @@ +/* eth16i.c An ICL EtherTeam 16i and 32 EISA ethernet driver for Linux + +   Written 1994-1998 by Mika Kuoppala + +   Copyright (C) 1994-1998 by Mika Kuoppala +   Based on skeleton.c and heavily on at1700.c by Donald Becker + +   This software may be used and distributed according to the terms +   of the GNU Public Licence, incorporated herein by reference. + +   The author may be reached as miku@iki.fi + +   This driver supports following cards : +	- ICL EtherTeam 16i +	- ICL EtherTeam 32 EISA +	  (Uses true 32 bit transfers rather than 16i compability mode) + +   Example Module usage: +        insmod eth16i.o ioaddr=0x2a0 mediatype=bnc + +	mediatype can be one of the following: bnc,tp,dix,auto,eprom + +	'auto' will try to autoprobe mediatype. +	'eprom' will use whatever type defined in eprom. + +   I have benchmarked driver with PII/300Mhz as a ftp client +   and 486/33Mhz as a ftp server. Top speed was 1128.37 kilobytes/sec. + +   Sources: +     - skeleton.c  a sample network driver core for linux, +       written by Donald Becker <becker@CESDIS.gsfc.nasa.gov> +     - at1700.c a driver for Allied Telesis AT1700, written +       by Donald Becker. +     - e16iSRV.asm a Netware 3.X Server Driver for ICL EtherTeam16i +       written by Markku Viima +     - The Fujitsu MB86965 databook. + +   Author thanks following persons due to their valueble assistance: +        Markku Viima (ICL) +	Ari Valve (ICL) +	Donald Becker +	Kurt Huwig <kurt@huwig.de> + +   Revision history: + +   Version	Date		Description + +   0.01         15.12-94        Initial version (card detection) +   0.02         23.01-95        Interrupt is now hooked correctly +   0.03         01.02-95        Rewrote initialization part +   0.04         07.02-95        Base skeleton done... +                                Made a few changes to signature checking +                                to make it a bit reliable. +                                - fixed bug in tx_buf mapping +                                - fixed bug in initialization (DLC_EN +                                  wasn't enabled when initialization +                                  was done.) +   0.05         08.02-95        If there were more than one packet to send, +                                transmit was jammed due to invalid +                                register write...now fixed +   0.06         19.02-95        Rewrote interrupt handling +   0.07         13.04-95        Wrote EEPROM read routines +                                Card configuration now set according to +                                data read from EEPROM +   0.08         23.06-95        Wrote part that tries to probe used interface +                                port if AUTO is selected + +   0.09         01.09-95        Added module support + +   0.10         04.09-95        Fixed receive packet allocation to work +                                with kernels > 1.3.x + +   0.20		20.09-95	Added support for EtherTeam32 EISA + +   0.21         17.10-95        Removed the unnecessary extern +				init_etherdev() declaration. Some +				other cleanups. + +   0.22		22.02-96	Receive buffer was not flushed +				correctly when faulty packet was +				received. Now fixed. + +   0.23		26.02-96	Made resetting the adapter +			 	more reliable. + +   0.24		27.02-96	Rewrote faulty packet handling in eth16i_rx + +   0.25		22.05-96	kfree() was missing from cleanup_module. + +   0.26		11.06-96	Sometimes card was not found by +				check_signature(). Now made more reliable. + +   0.27		23.06-96	Oops. 16 consecutive collisions halted +				adapter. Now will try to retransmit +				MAX_COL_16 times before finally giving up. + +   0.28	        28.10-97	Added dev_id parameter (NULL) for free_irq + +   0.29         29.10-97        Multiple card support for module users + +   0.30         30.10-97        Fixed irq allocation bug. +                                (request_irq moved from probe to open) + +   0.30a        21.08-98        Card detection made more relaxed. Driver +                                had problems with some TCP/IP-PROM boots +				to find the card. Suggested by +				Kurt Huwig <kurt@huwig.de> + +   0.31         28.08-98        Media interface port can now be selected +                                with module parameters or kernel +				boot parameters. + +   0.32         31.08-98        IRQ was never freed if open/close +                                pair wasn't called. Now fixed. + +   0.33         10.09-98        When eth16i_open() was called after +                                eth16i_close() chip never recovered. +				Now more shallow reset is made on +				close. + +   Bugs: +	In some cases the media interface autoprobing code doesn't find +	the correct interface type. In this case you can +	manually choose the interface type in DOS with E16IC.EXE which is +	configuration software for EtherTeam16i and EtherTeam32 cards. +	This is also true for IRQ setting. You cannot use module +	parameter to configure IRQ of the card (yet). + +   To do: +	- Real multicast support +	- Rewrite the media interface autoprobing code. Its _horrible_ ! +	- Possibly merge all the MB86965 specific code to external +	  module for use by eth16.c and Donald's at1700.c +	- IRQ configuration with module parameter. I will do +	  this when i will get enough info about setting +	  irq without configuration utility. +*/ + +static char *version = +    "eth16i.c: v0.33 10-09-98 Mika Kuoppala (miku@iki.fi)\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#ifndef LINUX_VERSION_CODE +#include <linux/version.h> +#endif + +#if LINUX_VERSION_CODE >= 0x20123 +#include <linux/init.h> +#else +#define __init +#define __initdata +#define __initfunc(x) x +#endif + +#if LINUX_VERSION_CODE < 0x20138 +//#define test_and_set_bit(val,addr) set_bit(val,addr) +#endif + +#if LINUX_VERSION_CODE < 0x020100 +typedef struct enet_statistics eth16i_stats_type; +#else +typedef struct net_device_stats eth16i_stats_type; +#endif + +/* Few macros */ +#define BIT(a)		       ( (1 << (a)) ) +#define BITSET(ioaddr, bnum)   ((outb(((inb(ioaddr)) | (bnum)), ioaddr))) +#define BITCLR(ioaddr, bnum)   ((outb(((inb(ioaddr)) & (~(bnum))), ioaddr))) + +/* This is the I/O address space for Etherteam 16i adapter. */ +#define ETH16I_IO_EXTENT       32 + +/* Ticks before deciding that transmit has timed out */ +#define TX_TIMEOUT             (400*HZ/1000) + +/* Maximum loop count when receiving packets */ +#define MAX_RX_LOOP            20 + +/* Some interrupt masks */ +#define ETH16I_INTR_ON	       0xef8a       /* Higher is receive mask */ +#define ETH16I_INTR_OFF	       0x0000 + +/* Buffers header status byte meanings */ +#define PKT_GOOD               BIT(5) +#define PKT_GOOD_RMT           BIT(4) +#define PKT_SHORT              BIT(3) +#define PKT_ALIGN_ERR          BIT(2) +#define PKT_CRC_ERR            BIT(1) +#define PKT_RX_BUF_OVERFLOW    BIT(0) + +/* Transmit status register (DLCR0) */ +#define TX_STATUS_REG          0 +#define TX_DONE                BIT(7) +#define NET_BUSY               BIT(6) +#define TX_PKT_RCD             BIT(5) +#define CR_LOST                BIT(4) +#define TX_JABBER_ERR	       BIT(3) +#define COLLISION              BIT(2) +#define COLLISIONS_16          BIT(1) + +/* Receive status register (DLCR1) */ +#define RX_STATUS_REG          1 +#define RX_PKT                 BIT(7)  /* Packet received */ +#define BUS_RD_ERR             BIT(6) +#define SHORT_PKT_ERR          BIT(3) +#define ALIGN_ERR              BIT(2) +#define CRC_ERR                BIT(1) +#define RX_BUF_OVERFLOW        BIT(0) + +/* Transmit Interrupt Enable Register (DLCR2) */ +#define TX_INTR_REG            2 +#define TX_INTR_DONE           BIT(7) +#define TX_INTR_COL            BIT(2) +#define TX_INTR_16_COL         BIT(1) + +/* Receive Interrupt Enable Register (DLCR3) */ +#define RX_INTR_REG            3 +#define RX_INTR_RECEIVE        BIT(7) +#define RX_INTR_SHORT_PKT      BIT(3) +#define RX_INTR_CRC_ERR        BIT(1) +#define RX_INTR_BUF_OVERFLOW   BIT(0) + +/* Transmit Mode Register (DLCR4) */ +#define TRANSMIT_MODE_REG      4 +#define LOOPBACK_CONTROL       BIT(1) +#define CONTROL_OUTPUT         BIT(2) + +/* Receive Mode Register (DLCR5) */ +#define RECEIVE_MODE_REG       5 +#define RX_BUFFER_EMPTY        BIT(6) +#define ACCEPT_BAD_PACKETS     BIT(5) +#define RECEIVE_SHORT_ADDR     BIT(4) +#define ACCEPT_SHORT_PACKETS   BIT(3) +#define REMOTE_RESET           BIT(2) + +#define ADDRESS_FILTER_MODE    BIT(1) | BIT(0) +#define REJECT_ALL             0 +#define ACCEPT_ALL             3 +#define MODE_1                 1            /* NODE ID, BC, MC, 2-24th bit */ +#define MODE_2                 2            /* NODE ID, BC, MC, Hash Table */ + +/* Configuration Register 0 (DLCR6) */ +#define CONFIG_REG_0           6 +#define DLC_EN                 BIT(7) +#define SRAM_CYCLE_TIME_100NS  BIT(6) +#define SYSTEM_BUS_WIDTH_8     BIT(5)       /* 1 = 8bit, 0 = 16bit */ +#define BUFFER_WIDTH_8         BIT(4)       /* 1 = 8bit, 0 = 16bit */ +#define TBS1                   BIT(3) +#define TBS0                   BIT(2) +#define SRAM_BS1               BIT(1)       /* 00=8kb,  01=16kb  */ +#define SRAM_BS0               BIT(0)       /* 10=32kb, 11=64kb  */ + +#ifndef ETH16I_TX_BUF_SIZE                   /* 0 = 2kb, 1 = 4kb  */ +#define ETH16I_TX_BUF_SIZE     3             /* 2 = 8kb, 3 = 16kb */ +#endif +#define TX_BUF_1x2048          0 +#define TX_BUF_2x2048          1 +#define TX_BUF_2x4098          2 +#define TX_BUF_2x8192          3 + +/* Configuration Register 1 (DLCR7) */ +#define CONFIG_REG_1           7 +#define POWERUP                BIT(5) + +/* Transmit start register */ +#define TRANSMIT_START_REG     10 +#define TRANSMIT_START_RB      2 +#define TX_START               BIT(7)       /* Rest of register bit indicate*/ +                                            /* number of packets in tx buffer*/ +/* Node ID registers (DLCR8-13) */ +#define NODE_ID_0              8 +#define NODE_ID_RB             0 + +/* Hash Table registers (HT8-15) */ +#define HASH_TABLE_0           8 +#define HASH_TABLE_RB          1 + +/* Buffer memory ports */ +#define BUFFER_MEM_PORT_LB     8 +#define DATAPORT               BUFFER_MEM_PORT_LB +#define BUFFER_MEM_PORT_HB     9 + +/* 16 Collision control register (BMPR11) */ +#define COL_16_REG             11 +#define HALT_ON_16             0x00 +#define RETRANS_AND_HALT_ON_16 0x02 + +/* Maximum number of attempts to send after 16 concecutive collisions */ +#define MAX_COL_16	       10 + +/* DMA Burst and Transceiver Mode Register (BMPR13) */ +#define TRANSCEIVER_MODE_REG   13 +#define TRANSCEIVER_MODE_RB    2 +#define IO_BASE_UNLOCK	       BIT(7) +#define LOWER_SQUELCH_TRESH    BIT(6) +#define LINK_TEST_DISABLE      BIT(5) +#define AUI_SELECT             BIT(4) +#define DIS_AUTO_PORT_SEL      BIT(3) + +/* Filter Self Receive Register (BMPR14)  */ +#define FILTER_SELF_RX_REG     14 +#define SKIP_RX_PACKET         BIT(2) +#define FILTER_SELF_RECEIVE    BIT(0) + +/* EEPROM Control Register (BMPR 16) */ +#define EEPROM_CTRL_REG        16 + +/* EEPROM Data Register (BMPR 17) */ +#define EEPROM_DATA_REG        17 + +/* NMC93CSx6 EEPROM Control Bits */ +#define CS_0                   0x00 +#define CS_1                   0x20 +#define SK_0                   0x00 +#define SK_1                   0x40 +#define DI_0                   0x00 +#define DI_1                   0x80 + +/* NMC93CSx6 EEPROM Instructions */ +#define EEPROM_READ            0x80 + +/* NMC93CSx6 EEPROM Addresses */ +#define E_NODEID_0             0x02 +#define E_NODEID_1             0x03 +#define E_NODEID_2             0x04 +#define E_PORT_SELECT          0x14 +  #define E_PORT_BNC           0x00 +  #define E_PORT_DIX           0x01 +  #define E_PORT_TP            0x02 +  #define E_PORT_AUTO          0x03 +  #define E_PORT_FROM_EPROM    0x04 +#define E_PRODUCT_CFG          0x30 + + +/* Macro to slow down io between EEPROM clock transitions */ +#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { inb(0x80); }}while(0) + +/* Jumperless Configuration Register (BMPR19) */ +#define JUMPERLESS_CONFIG      19 + +/* ID ROM registers, writing to them also resets some parts of chip */ +#define ID_ROM_0               24 +#define ID_ROM_7               31 +#define RESET                  ID_ROM_0 + +/* This is the I/O address list to be probed when seeking the card */ +static unsigned int eth16i_portlist[] = +   { 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300, 0 }; + +static unsigned int eth32i_portlist[] = +   { 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000, +     0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0 }; + +/* This is the Interrupt lookup table for Eth16i card */ +static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15, 0 }; +#define NUM_OF_ISA_IRQS    4 + +/* This is the Interrupt lookup table for Eth32i card */ +static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15, 0 }; +#define EISA_IRQ_REG	0xc89 +#define NUM_OF_EISA_IRQS   8 + +static unsigned int eth16i_tx_buf_map[] = { 2048, 2048, 4096, 8192 }; +static unsigned int boot = 1; + +/* Use 0 for production, 1 for verification, >2 for debug */ +#ifndef ETH16I_DEBUG +#define ETH16I_DEBUG 0 +#endif +static unsigned int eth16i_debug = ETH16I_DEBUG; + +/* Information for each board */ + +struct eth16i_local { +	eth16i_stats_type stats; +	unsigned char     tx_started; +	unsigned char     tx_buf_busy; +	unsigned short    tx_queue;  /* Number of packets in transmit buffer */ +	unsigned short    tx_queue_len; +	unsigned int      tx_buf_size; +	unsigned long     open_time; +	unsigned long     tx_buffered_packets; +	unsigned long     col_16; +}; + +/* Function prototypes */ + +extern int     eth16i_probe(struct device *dev); + +static int     eth16i_probe1(struct device *dev, int ioaddr); +static int     eth16i_check_signature(int ioaddr); +static int     eth16i_probe_port(int ioaddr); +static void    eth16i_set_port(int ioaddr, int porttype); +static int     eth16i_send_probe_packet(int ioaddr, unsigned char *b, int l); +static int     eth16i_receive_probe_packet(int ioaddr); +static int     eth16i_get_irq(int ioaddr); +static int     eth16i_read_eeprom(int ioaddr, int offset); +static int     eth16i_read_eeprom_word(int ioaddr); +static void    eth16i_eeprom_cmd(int ioaddr, unsigned char command); +static int     eth16i_open(struct device *dev); +static int     eth16i_close(struct device *dev); +static int     eth16i_tx(struct sk_buff *skb, struct device *dev); +static void    eth16i_rx(struct device *dev); +static void    eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void    eth16i_reset(struct device *dev); +static void    eth16i_skip_packet(struct device *dev); +static void    eth16i_multicast(struct device *dev); +static void    eth16i_select_regbank(unsigned char regbank, int ioaddr); +static void    eth16i_initialize(struct device *dev); + +#if 0 +static int     eth16i_set_irq(struct device *dev); +#endif + +#ifdef MODULE +static ushort  eth16i_parse_mediatype(const char* s); +#endif + +static struct enet_statistics *eth16i_get_stats(struct device *dev); + +static char *cardname = "ICL EtherTeam 16i/32"; + +#ifdef HAVE_DEVLIST + +/* Support for alternate probe manager */ +/struct netdev_entry eth16i_drv = + {"eth16i", eth16i_probe1, ETH16I_IO_EXTENT, eth16i_probe_list}; + +#else  /* Not HAVE_DEVLIST */ + +__initfunc(int eth16i_probe(struct device *dev)) +{ +	int i; +	int ioaddr; +	int base_addr = dev ? dev->base_addr : 0; + +	if(eth16i_debug > 4) +		printk(KERN_DEBUG "Probing started for %s\n", cardname); + +	if(base_addr > 0x1ff)           /* Check only single location */ +		return eth16i_probe1(dev, base_addr); +	else if(base_addr != 0)         /* Don't probe at all */ +		return ENXIO; + +	/* Seek card from the ISA io address space */ +	for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++) { +		if(check_region(ioaddr, ETH16I_IO_EXTENT)) +			continue; +		if(eth16i_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	/* Seek card from the EISA io address space */ +	for(i = 0; (ioaddr = eth32i_portlist[i]) ; i++) { +		if(check_region(ioaddr, ETH16I_IO_EXTENT)) +			continue; +		if(eth16i_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif  /* Not HAVE_DEVLIST */ + +__initfunc(static int eth16i_probe1(struct device *dev, int ioaddr)) +{ +	static unsigned version_printed = 0; +	boot = 1;  /* To inform initilization that we are in boot probe */ + +	/* +	  The MB86985 chip has on register which holds information in which +	  io address the chip lies. First read this register and compare +	  it to our current io address and if match then this could +	  be our chip. +	  */ + +	if(ioaddr < 0x1000) { + +		if(eth16i_portlist[(inb(ioaddr + JUMPERLESS_CONFIG) & 0x07)] +		   != ioaddr) +			return -ENODEV; +	} + +	/* Now we will go a bit deeper and try to find the chip's signature */ + +	if(eth16i_check_signature(ioaddr) != 0) +		return -ENODEV; + +	/* +	   Now it seems that we have found a ethernet chip in this particular +	   ioaddr. The MB86985 chip has this feature, that when you read a +	   certain register it will increase it's io base address to next +	   configurable slot. Now when we have found the chip, first thing is +	   to make sure that the chip's ioaddr will hold still here. +	   */ + +	eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); +	outb(0x00, ioaddr + TRANSCEIVER_MODE_REG); + +	outb(0x00, ioaddr + RESET);             /* Reset some parts of chip */ +	BITSET(ioaddr + CONFIG_REG_0, BIT(7));  /* Disable the data link */ + +	if(dev == NULL) +		dev = init_etherdev(0, 0); + +	if( (eth16i_debug & version_printed++) == 0) +		printk(KERN_INFO "%s", version); + +	dev->base_addr = ioaddr; + +#if 0 +	if(dev->irq) { +		if(eth16i_set_irq(dev)) { +			dev->irq = eth16i_get_irq(ioaddr); +		} + +	} +	else { +#endif + +	dev->irq = eth16i_get_irq(ioaddr); + +	/* Try to obtain interrupt vector */ + +	if (request_irq(dev->irq, (void *)ð16i_interrupt, 0, "eth16i", dev)) { +		printk(KERN_WARNING "%s: %s at %#3x, but is unusable due conflicting IRQ %d.\n", +		       dev->name, cardname, ioaddr, dev->irq); +		return -EAGAIN; +	} + +#if 0 +	irq2dev_map[dev->irq] = dev; +#endif + +	printk(KERN_INFO "%s: %s at %#3x, IRQ %d, ", +	       dev->name, cardname, ioaddr, dev->irq); + +	/* Let's grab the region */ +	request_region(ioaddr, ETH16I_IO_EXTENT, "eth16i"); + +	/* Now we will have to lock the chip's io address */ +	eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); +	outb(0x38, ioaddr + TRANSCEIVER_MODE_REG); + +	eth16i_initialize(dev);   /* Initialize rest of the chip's registers */ + +	/* Now let's same some energy by shutting down the chip ;) */ +	BITCLR(ioaddr + CONFIG_REG_1, POWERUP); + +	/* Initialize the device structure */ +	if(dev->priv == NULL) { +		dev->priv = kmalloc(sizeof(struct eth16i_local), GFP_KERNEL); +		if(dev->priv == NULL) +			return -ENOMEM; +	} + +	memset(dev->priv, 0, sizeof(struct eth16i_local)); + +	dev->open               = eth16i_open; +	dev->stop               = eth16i_close; +	dev->hard_start_xmit    = eth16i_tx; +	dev->get_stats          = eth16i_get_stats; +	dev->set_multicast_list = ð16i_multicast; + +	/* Fill in the fields of the device structure with ethernet values. */ +	ether_setup(dev); + +	boot = 0; + +	return 0; +} + + +static void eth16i_initialize(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	int i, node_w = 0; +	unsigned char node_byte = 0; + +	/* Setup station address */ +	eth16i_select_regbank(NODE_ID_RB, ioaddr); +	for(i = 0 ; i < 3 ; i++) { +		unsigned short node_val = eth16i_read_eeprom(ioaddr, E_NODEID_0 + i); +		((unsigned short *)dev->dev_addr)[i] = ntohs(node_val); +	} + +	for(i = 0; i < 6; i++) { +		outb( ((unsigned char *)dev->dev_addr)[i], ioaddr + NODE_ID_0 + i); +		if(boot) { +			printk("%02x", inb(ioaddr + NODE_ID_0 + i)); +			if(i != 5) +				printk(":"); +		} +	} + +	/* Now we will set multicast addresses to accept none */ +	eth16i_select_regbank(HASH_TABLE_RB, ioaddr); +	for(i = 0; i < 8; i++) +		outb(0x00, ioaddr + HASH_TABLE_0 + i); + +	/* +	  Now let's disable the transmitter and receiver, set the buffer ram +	  cycle time, bus width and buffer data path width. Also we shall +	  set transmit buffer size and total buffer size. +	  */ + +	eth16i_select_regbank(2, ioaddr); + +	node_byte = 0; +	node_w = eth16i_read_eeprom(ioaddr, E_PRODUCT_CFG); + +	if( (node_w & 0xFF00) == 0x0800) +		node_byte |= BUFFER_WIDTH_8; + +	node_byte |= SRAM_BS1; + +	if( (node_w & 0x00FF) == 64) +		node_byte |= SRAM_BS0; + +	node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2); + +	outb(node_byte, ioaddr + CONFIG_REG_0); + +	/* We shall halt the transmitting, if 16 collisions are detected */ +	outb(HALT_ON_16, ioaddr + COL_16_REG); + +#ifdef MODULE +	/* if_port already set by init_module() */ +#else +	dev->if_port = (dev->mem_start < E_PORT_FROM_EPROM) ? +		dev->mem_start : E_PORT_FROM_EPROM; +#endif + +	/* Set interface port type */ +	if(boot) { +		char *porttype[] = {"BNC", "DIX", "TP", "AUTO", "FROM_EPROM" }; + +		switch(dev->if_port) +		{ + +		case E_PORT_FROM_EPROM: +			dev->if_port = eth16i_read_eeprom(ioaddr, E_PORT_SELECT); +			break; + +		case E_PORT_AUTO: +			dev->if_port = eth16i_probe_port(ioaddr); +			break; + +		case E_PORT_BNC: +		case E_PORT_TP: +		case E_PORT_DIX: +			break; +		} + +		printk(" %s interface.\n", porttype[dev->if_port]); + +		eth16i_set_port(ioaddr, dev->if_port); +	} + +	/* Set Receive Mode to normal operation */ +	outb(MODE_2, ioaddr + RECEIVE_MODE_REG); +} + +static int eth16i_probe_port(int ioaddr) +{ +	int i; +	int retcode; +	unsigned char dummy_packet[64] = { 0 }; + +	/* Powerup the chip */ +	outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1); + +	BITSET(ioaddr + CONFIG_REG_0, DLC_EN); + +	eth16i_select_regbank(NODE_ID_RB, ioaddr); + +	for(i = 0; i < 6; i++) { +		dummy_packet[i] = inb(ioaddr + NODE_ID_0 + i); +		dummy_packet[i+6] = inb(ioaddr + NODE_ID_0 + i); +	} + +	dummy_packet[12] = 0x00; +	dummy_packet[13] = 0x04; + +	eth16i_select_regbank(2, ioaddr); + +	for(i = 0; i < 3; i++) { +		BITSET(ioaddr + CONFIG_REG_0, DLC_EN); +		BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); +		eth16i_set_port(ioaddr, i); + +		if(eth16i_debug > 1) +			printk(KERN_DEBUG "Set port number %d\n", i); + +		retcode = eth16i_send_probe_packet(ioaddr, dummy_packet, 64); +		if(retcode == 0) { +			retcode = eth16i_receive_probe_packet(ioaddr); +			if(retcode != -1) { +				if(eth16i_debug > 1) +					printk(KERN_DEBUG "Eth16i interface port found at %d\n", i); +				return i; +			} +		} +		else { +			if(eth16i_debug > 1) +				printk(KERN_DEBUG "TRANSMIT_DONE timeout when probing interface port\n"); +		} +	} + +	if( eth16i_debug > 1) +		printk(KERN_DEBUG "Using default port\n"); + +	return E_PORT_BNC; +} + +static void eth16i_set_port(int ioaddr, int porttype) +{ +	unsigned short temp = 0; + +	eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr); +	outb(LOOPBACK_CONTROL, ioaddr + TRANSMIT_MODE_REG); + +	temp |= DIS_AUTO_PORT_SEL; + +	switch(porttype) { + +	case E_PORT_BNC : +		temp |= AUI_SELECT; +		break; + +	case E_PORT_TP : +		break; + +	case E_PORT_DIX : +		temp |= AUI_SELECT; +		BITSET(ioaddr + TRANSMIT_MODE_REG, CONTROL_OUTPUT); +		break; +	} + +	outb(temp, ioaddr + TRANSCEIVER_MODE_REG); + +	if(eth16i_debug > 1) { +		printk(KERN_DEBUG "TRANSMIT_MODE_REG = %x\n", inb(ioaddr + TRANSMIT_MODE_REG)); +		printk(KERN_DEBUG "TRANSCEIVER_MODE_REG = %x\n", +		       inb(ioaddr+TRANSCEIVER_MODE_REG)); +	} +} + +static int eth16i_send_probe_packet(int ioaddr, unsigned char *b, int l) +{ +	int starttime; + +	outb(0xff, ioaddr + TX_STATUS_REG); + +	outw(l, ioaddr + DATAPORT); +	outsw(ioaddr + DATAPORT, (unsigned short *)b, (l + 1) >> 1); + +	starttime = jiffies; +	outb(TX_START | 1, ioaddr + TRANSMIT_START_REG); + +	while( (inb(ioaddr + TX_STATUS_REG) & 0x80) == 0) { +		if( (jiffies - starttime) > TX_TIMEOUT) { +			return -1; +		} +	} + +	return 0; +} + +static int eth16i_receive_probe_packet(int ioaddr) +{ +	int starttime; + +	starttime = jiffies; + +	while((inb(ioaddr + TX_STATUS_REG) & 0x20) == 0) { +		if( (jiffies - starttime) > TX_TIMEOUT) { + +			if(eth16i_debug > 1) +				printk(KERN_DEBUG "Timeout occured waiting transmit packet received\n"); +			starttime = jiffies; +			while((inb(ioaddr + RX_STATUS_REG) & 0x80) == 0) { +				if( (jiffies - starttime) > TX_TIMEOUT) { +					if(eth16i_debug > 1) +						printk(KERN_DEBUG "Timeout occured waiting receive packet\n"); +					return -1; +				} +			} + +			if(eth16i_debug > 1) +				printk(KERN_DEBUG "RECEIVE_PACKET\n"); +			return(0); /* Found receive packet */ +		} +	} + +	if(eth16i_debug > 1) { +		printk(KERN_DEBUG "TRANSMIT_PACKET_RECEIVED %x\n", inb(ioaddr + TX_STATUS_REG)); +		printk(KERN_DEBUG "RX_STATUS_REG = %x\n", inb(ioaddr + RX_STATUS_REG)); +	} + +	return(0); /* Return success */ +} + +#if 0 +static int eth16i_set_irq(struct device* dev) +{ +	const int ioaddr = dev->base_addr; +	const int irq = dev->irq; +	int i = 0; + +	if(ioaddr < 0x1000) { +		while(eth16i_irqmap[i] && eth16i_irqmap[i] != irq) +			i++; + +		if(i < NUM_OF_ISA_IRQS) { +			u8 cbyte = inb(ioaddr + JUMPERLESS_CONFIG); +			cbyte = (cbyte & 0x3F) | (i << 6); +			outb(cbyte, ioaddr + JUMPERLESS_CONFIG); +			return 0; +		} +	} +	else { +		printk(KERN_NOTICE "%s: EISA Interrupt cannot be set. Use EISA Configuration utility.\n", dev->name); +	} + +	return -1; + +} +#endif + +static int eth16i_get_irq(int ioaddr) +{ +	unsigned char cbyte; + +	if( ioaddr < 0x1000) { +		cbyte = inb(ioaddr + JUMPERLESS_CONFIG); +		return( eth16i_irqmap[ ((cbyte & 0xC0) >> 6) ] ); +	} else {  /* Oh..the card is EISA so method getting IRQ different */ +		unsigned short index = 0; +		cbyte = inb(ioaddr + EISA_IRQ_REG); +		while( (cbyte & 0x01) == 0) { +			cbyte = cbyte >> 1; +			index++; +		} +		return( eth32i_irqmap[ index ] ); +	} +} + +static int eth16i_check_signature(int ioaddr) +{ +	int i; +	unsigned char creg[4] = { 0 }; + +	for(i = 0; i < 4 ; i++) { + +		creg[i] = inb(ioaddr + TRANSMIT_MODE_REG + i); + +		if(eth16i_debug > 1) +			printk("eth16i: read signature byte %x at %x\n", +			       creg[i], +			       ioaddr + TRANSMIT_MODE_REG + i); +	} + +	creg[0] &= 0x0F;      /* Mask collision cnr */ +	creg[2] &= 0x7F;      /* Mask DCLEN bit */ + +#if 0 +	/* +	   This was removed because the card was sometimes left to state +	   from which it couldn't be find anymore. If there is need +	   to more strict check still this have to be fixed. +	   */ +	if( ! ((creg[0] == 0x06) && (creg[1] == 0x41)) ) { +		if(creg[1] != 0x42) +			return -1; +	} +#endif + +	if( !((creg[2] == 0x36) && (creg[3] == 0xE0)) ) { +		creg[2] &= 0x40; +		creg[3] &= 0x03; + +		if( !((creg[2] == 0x40) && (creg[3] == 0x00)) ) +			return -1; +	} + +	if(eth16i_read_eeprom(ioaddr, E_NODEID_0) != 0) +		return -1; + +	if((eth16i_read_eeprom(ioaddr, E_NODEID_1) & 0xFF00) != 0x4B00) +		return -1; + +	return 0; +} + +static int eth16i_read_eeprom(int ioaddr, int offset) +{ +	int data = 0; + +	eth16i_eeprom_cmd(ioaddr, EEPROM_READ | offset); +	outb(CS_1, ioaddr + EEPROM_CTRL_REG); +	data = eth16i_read_eeprom_word(ioaddr); +	outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG); + +	return(data); +} + +static int eth16i_read_eeprom_word(int ioaddr) +{ +	int i; +	int data = 0; + +	for(i = 16; i > 0; i--) { +		outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); +		eeprom_slow_io(); +		outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); +		eeprom_slow_io(); +		data = (data << 1) | +			((inb(ioaddr + EEPROM_DATA_REG) & DI_1) ? 1 : 0); + +		eeprom_slow_io(); +	} + +	return(data); +} + +static void eth16i_eeprom_cmd(int ioaddr, unsigned char command) +{ +	int i; + +	outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG); +	outb(DI_0, ioaddr + EEPROM_DATA_REG); +	outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); +	outb(DI_1, ioaddr + EEPROM_DATA_REG); +	outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); + +	for(i = 7; i >= 0; i--) { +		short cmd = ( (command & (1 << i)) ? DI_1 : DI_0 ); +		outb(cmd, ioaddr + EEPROM_DATA_REG); +		outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG); +		eeprom_slow_io(); +		outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG); +		eeprom_slow_io(); +	} +} + +static int eth16i_open(struct device *dev) +{ +	struct eth16i_local *lp = (struct eth16i_local *)dev->priv; +	int ioaddr = dev->base_addr; + +	/* Powerup the chip */ +	outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1); + +	/* Initialize the chip */ +	eth16i_initialize(dev); + +	/* Set the transmit buffer size */ +	lp->tx_buf_size = eth16i_tx_buf_map[ETH16I_TX_BUF_SIZE & 0x03]; + +	if(eth16i_debug > 0) +		printk(KERN_DEBUG "%s: transmit buffer size %d\n", +		       dev->name, lp->tx_buf_size); + +	/* Now enable Transmitter and Receiver sections */ +	BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); + +	/* Now switch to register bank 2, for run time operation */ +	eth16i_select_regbank(2, ioaddr); + +	lp->open_time = jiffies; +	lp->tx_started = 0; +	lp->tx_queue = 0; +	lp->tx_queue_len = 0; + +	/* Turn on interrupts*/ +	outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; + +	MOD_INC_USE_COUNT; + +	return 0; +} + +static int eth16i_close(struct device *dev) +{ +	struct eth16i_local *lp = (struct eth16i_local *)dev->priv; +	int ioaddr = dev->base_addr; + +	eth16i_reset(dev); + +	/* Turn off interrupts*/ +	outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); + +	dev->start = 0; +	dev->tbusy = 1; + +	lp->open_time = 0; + +	/* Disable transmit and receive */ +	BITSET(ioaddr + CONFIG_REG_0, DLC_EN); + +	/* Reset the chip */ +	/* outb(0xff, ioaddr + RESET); */ +	/* outw(0xffff, ioaddr + TX_STATUS_REG);    */ + +	outb(0x00, ioaddr + CONFIG_REG_1); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static int eth16i_tx(struct sk_buff *skb, struct device *dev) +{ +	struct eth16i_local *lp = (struct eth16i_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int status = 0; + +	if(dev->tbusy) { + +		/* +		   If we get here, some higher level has decided that +		   we are broken. There should really be a "kick me" +		   function call instead. +		   */ + +		int tickssofar = jiffies - dev->trans_start; +		if(tickssofar < TX_TIMEOUT) +			return 1; + +		outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); + +		printk(KERN_WARNING "%s: transmit timed out with status %04x, %s ?\n", +		       dev->name, +		       inw(ioaddr + TX_STATUS_REG), +		       (inb(ioaddr + TX_STATUS_REG) & TX_DONE) ? +		       "IRQ conflict" : "network cable problem"); + +		dev->trans_start = jiffies; + +		/* Let's dump all registers */ +		if(eth16i_debug > 0) { +			printk(KERN_DEBUG "%s: timeout: %02x %02x %02x %02x %02x %02x %02x %02x.\n", +			       dev->name, inb(ioaddr + 0), +			       inb(ioaddr + 1), inb(ioaddr + 2), +			       inb(ioaddr + 3), inb(ioaddr + 4), +			       inb(ioaddr + 5), +			       inb(ioaddr + 6), inb(ioaddr + 7)); + +			printk(KERN_DEBUG "%s: transmit start reg: %02x. collision reg %02x\n", +			       dev->name, inb(ioaddr + TRANSMIT_START_REG), +			       inb(ioaddr + COL_16_REG)); + +			printk(KERN_DEBUG "lp->tx_queue = %d\n", lp->tx_queue); +			printk(KERN_DEBUG "lp->tx_queue_len = %d\n", lp->tx_queue_len); +			printk(KERN_DEBUG "lp->tx_started = %d\n", lp->tx_started); + +		} + +		lp->stats.tx_errors++; + +		eth16i_reset(dev); + +		dev->trans_start = jiffies; + +		outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); + +	} + +	/* +	   If some higher layer thinks we've missed an tx-done interrupt +	   we are passed NULL. Caution: dev_tint() handles the cli()/sti() +	   itself +	   */ + +	if(skb == NULL) { +#if LINUX_VERSION_CODE < 0x020100 +		dev_tint(dev); +#endif +		if(eth16i_debug > 0) +			printk(KERN_WARNING "%s: Missed tx-done interrupt.\n", dev->name); +		return 0; +	} + +	/* Block a timer based transmitter from overlapping. +	   This could better be done with atomic_swap(1, dev->tbusy), +	   but set_bit() works as well. */ + +	set_bit(0, (void *)&lp->tx_buf_busy); + +	/* Turn off TX interrupts */ +	outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); + +	if(test_and_set_bit(0, (void *)&dev->tbusy) != 0) { +		printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); +		status = -1; +	} +	else { +		ushort length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		unsigned char *buf = skb->data; + +		if( (length + 2) > (lp->tx_buf_size - lp->tx_queue_len)) { +			if(eth16i_debug > 0) +				printk(KERN_WARNING "%s: Transmit buffer full.\n", dev->name); +		} +		else { +			outw(length, ioaddr + DATAPORT); + +			if( ioaddr < 0x1000 ) +				outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); +			else { +				unsigned char frag = length % 4; + +				outsl(ioaddr + DATAPORT, buf, length >> 2); + +				if( frag != 0 ) { +					outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1); +					if( frag == 3 ) +						outsw(ioaddr + DATAPORT, +						      (buf + (length & 0xFFFC) + 2), 1); +				} +			} + +			lp->tx_buffered_packets++; +			lp->tx_queue++; +			lp->tx_queue_len += length + 2; + +		} + +		lp->tx_buf_busy = 0; + +		if(lp->tx_started == 0) { +			/* If the transmitter is idle..always trigger a transmit */ +			outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG); +			lp->tx_queue = 0; +			lp->tx_queue_len = 0; +			dev->trans_start = jiffies; +			lp->tx_started = 1; +			dev->tbusy = 0; +		} +		else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) { +			/* There is still more room for one more packet in tx buffer */ +			dev->tbusy = 0; +		} + +		outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); + +		/* Turn TX interrupts back on */ +		/* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */ +		status = 0; +	} + +#if LINUX_VERSION_CODE >= 0x020100 +	dev_kfree_skb(skb); +#else +	dev_kfree_skb(skb, FREE_WRITE); +#endif + +	return status; +} + +static void eth16i_rx(struct device *dev) +{ +	struct eth16i_local *lp = (struct eth16i_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int boguscount = MAX_RX_LOOP; + +	/* Loop until all packets have been read */ +	while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) { + +		/* Read status byte from receive buffer */ +		ushort status = inw(ioaddr + DATAPORT); + +		/* Get the size of the packet from receive buffer */ +		ushort pkt_len = inw(ioaddr + DATAPORT); + +		if(eth16i_debug > 4) +			printk(KERN_DEBUG "%s: Receiving packet mode %02x status %04x.\n", +			       dev->name, +			       inb(ioaddr + RECEIVE_MODE_REG), status); + +		if( !(status & PKT_GOOD) ) { +			lp->stats.rx_errors++; + +			if( (pkt_len < ETH_ZLEN) || (pkt_len > ETH_FRAME_LEN) ) { +				lp->stats.rx_length_errors++; +				eth16i_reset(dev); +				return; +			} +			else { +				eth16i_skip_packet(dev); +				lp->stats.rx_dropped++; +			} +		} +		else {   /* Ok so now we should have a good packet */ +			struct sk_buff *skb; + +			skb = dev_alloc_skb(pkt_len + 3); +			if( skb == NULL ) { +				printk(KERN_WARNING "%s: Could'n allocate memory for packet (len %d)\n", +				       dev->name, pkt_len); +				eth16i_skip_packet(dev); +				lp->stats.rx_dropped++; +				break; +			} + +			skb->dev = dev; +			skb_reserve(skb,2); + +			/* +			   Now let's get the packet out of buffer. +			   size is (pkt_len + 1) >> 1, cause we are now reading words +			   and it have to be even aligned. +			   */ + +			if(ioaddr < 0x1000) +				insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), +				     (pkt_len + 1) >> 1); +			else { +				unsigned char *buf = skb_put(skb, pkt_len); +				unsigned char frag = pkt_len % 4; + +				insl(ioaddr + DATAPORT, buf, pkt_len >> 2); + +				if(frag != 0) { +					unsigned short rest[2]; +					rest[0] = inw( ioaddr + DATAPORT ); +					if(frag == 3) +						rest[1] = inw( ioaddr + DATAPORT ); + +					memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag); +				} +			} + +			skb->protocol=eth_type_trans(skb, dev); +			netif_rx(skb); +			lp->stats.rx_packets++; + +			if( eth16i_debug > 5 ) { +				int i; +				printk(KERN_DEBUG "%s: Received packet of length %d.\n", +				       dev->name, pkt_len); +				for(i = 0; i < 14; i++) +					printk(KERN_DEBUG " %02x", skb->data[i]); +				printk(KERN_DEBUG ".\n"); +			} + +		} /* else */ + +		if(--boguscount <= 0) +			break; + +	} /* while */ + +#if 0 +	{ +		int i; + +		for(i = 0; i < 20; i++) { +			if( (inb(ioaddr+RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == +			    RX_BUFFER_EMPTY) +				break; +			inw(ioaddr + DATAPORT); +			outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG); +		} + +		if(eth16i_debug > 1) +			printk(KERN_DEBUG "%s: Flushed receive buffer.\n", dev->name); +	} +#endif + +	return; +} + +static void eth16i_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +	struct device *dev = dev_id; +	struct eth16i_local *lp; +	int ioaddr = 0, +		status; + +	if(dev == NULL) { +		printk(KERN_WARNING "eth16i_interrupt(): irq %d for unknown device. \n", irq); +		return; +	} + +	/* Turn off all interrupts from adapter */ +	outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG); + +	set_bit(0, (void *)&dev->tbusy);  	/* Set the device busy so that */ +	/* eth16i_tx wont be called */ + +	if(dev->interrupt) +		printk(KERN_WARNING "%s: Re-entering the interrupt handler.\n", dev->name); +	dev->interrupt = 1; + +	ioaddr = dev->base_addr; +	lp = (struct eth16i_local *)dev->priv; +	status = inw(ioaddr + TX_STATUS_REG);      /* Get the status */ +	outw(status, ioaddr + TX_STATUS_REG);      /* Clear status bits */ + +	if(eth16i_debug > 3) +		printk(KERN_DEBUG "%s: Interrupt with status %04x.\n", dev->name, status); + +	if( status & 0x7f00 ) { + +		lp->stats.rx_errors++; + +		if(status & (BUS_RD_ERR << 8) ) +			printk(KERN_WARNING "%s: Bus read error.\n",dev->name); +		if(status & (SHORT_PKT_ERR << 8) )   lp->stats.rx_length_errors++; +		if(status & (ALIGN_ERR << 8) )       lp->stats.rx_frame_errors++; +		if(status & (CRC_ERR << 8) )	    lp->stats.rx_crc_errors++; +		if(status & (RX_BUF_OVERFLOW << 8) ) lp->stats.rx_over_errors++; +	} +	if( status & 0x001a) { + +		lp->stats.tx_errors++; + +		if(status & CR_LOST) lp->stats.tx_carrier_errors++; +		if(status & TX_JABBER_ERR) lp->stats.tx_window_errors++; + +#if 0 +		if(status & COLLISION) { +			lp->stats.collisions += +				((inb(ioaddr+TRANSMIT_MODE_REG) & 0xF0) >> 4); +		} +#endif +		if(status & COLLISIONS_16) { +			if(lp->col_16 < MAX_COL_16) { +				lp->col_16++; +				lp->stats.collisions++; +				/* Resume transmitting, skip failed packet */ +				outb(0x02, ioaddr + COL_16_REG); +			} +			else { +				printk(KERN_WARNING "%s: bailing out due to many consecutive 16-in-a-row collisions. Network cable problem?\n", dev->name); +			} +		} +	} + +	if( status & 0x00ff ) {          /* Let's check the transmit status reg */ + +		if(status & TX_DONE) {         /* The transmit has been done */ +			lp->stats.tx_packets = lp->tx_buffered_packets; +			lp->col_16 = 0; + +			if(lp->tx_queue) {           /* Is there still packets ? */ +				/* There was packet(s) so start transmitting and write also +				   how many packets there is to be sended */ +				outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG); +				lp->tx_queue = 0; +				lp->tx_queue_len = 0; +				lp->tx_started = 1; +				dev->trans_start = jiffies; +				mark_bh(NET_BH); +			} +			else { +				lp->tx_started = 0; +				mark_bh(NET_BH); +			} +		} +	} + +	if( ( status & 0x8000 ) || +	    ( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) { +		eth16i_rx(dev);  /* We have packet in receive buffer */ +	} + +	dev->interrupt = 0; + +	/* Turn interrupts back on */ +	outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG); + +	if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) { +		/* There is still more room for one more packet in tx buffer */ +		dev->tbusy = 0; +	} + +	return; +} + +static void eth16i_skip_packet(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	inw(ioaddr + DATAPORT); +	inw(ioaddr + DATAPORT); +	inw(ioaddr + DATAPORT); + +	outb(SKIP_RX_PACKET, ioaddr + FILTER_SELF_RX_REG); +	while( inb( ioaddr + FILTER_SELF_RX_REG ) != 0); +} + +static void eth16i_reset(struct device *dev) +{ +	struct eth16i_local *lp = (struct eth16i_local *)dev->priv; +	int ioaddr = dev->base_addr; + +	if(eth16i_debug > 1) +		printk(KERN_DEBUG "%s: Resetting device.\n", dev->name); + +	BITSET(ioaddr + CONFIG_REG_0, DLC_EN); +	outw(0xffff, ioaddr + TX_STATUS_REG); +	eth16i_select_regbank(2, ioaddr); + +	lp->tx_started = 0; +	lp->tx_buf_busy = 0; +	lp->tx_queue = 0; +	lp->tx_queue_len = 0; + +	dev->interrupt = 0; +	dev->start = 1; +	dev->tbusy = 0; +	BITCLR(ioaddr + CONFIG_REG_0, DLC_EN); +} + +static void eth16i_multicast(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) +	{ +		dev->flags|=IFF_PROMISC;	/* Must do this */ +		outb(3, ioaddr + RECEIVE_MODE_REG); +	} else { +		outb(2, ioaddr + RECEIVE_MODE_REG); +	} +} + +static struct enet_statistics *eth16i_get_stats(struct device *dev) +{ +	struct eth16i_local *lp = (struct eth16i_local *)dev->priv; + +	return &lp->stats; +} + +static void eth16i_select_regbank(unsigned char banknbr, int ioaddr) +{ +	unsigned char data; + +	data = inb(ioaddr + CONFIG_REG_1); +	outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1); +} + +#ifdef MODULE + +static ushort eth16i_parse_mediatype(const char* s) +{ +	if(!s) +		return E_PORT_FROM_EPROM; + +        if (!strncmp(s, "bnc", 3)) +		return E_PORT_BNC; +        else if (!strncmp(s, "tp", 2)) +                return E_PORT_TP; +        else if (!strncmp(s, "dix", 3)) +                return E_PORT_DIX; +        else if (!strncmp(s, "auto", 4)) +		return E_PORT_AUTO; +	else +		return E_PORT_FROM_EPROM; +} + +#define MAX_ETH16I_CARDS 4  /* Max number of Eth16i cards per module */ +#define NAMELEN          8  /* number of chars for storing dev->name */ + +static char namelist[NAMELEN * MAX_ETH16I_CARDS] = { 0, }; +static struct device dev_eth16i[MAX_ETH16I_CARDS] = { +	{ +		NULL, +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int ioaddr[MAX_ETH16I_CARDS] = { 0, }; +#if 0 +static int irq[MAX_ETH16I_CARDS] = { 0, }; +#endif +static char* mediatype[MAX_ETH16I_CARDS] = { 0, }; +static int debug = -1; + +#if (LINUX_VERSION_CODE >= 0x20115) +MODULE_AUTHOR("Mika Kuoppala <miku@iki.fi>"); +MODULE_DESCRIPTION("ICL EtherTeam 16i/32 driver"); + +MODULE_PARM(ioaddr, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "i"); +MODULE_PARM_DESC(ioaddr, "eth16i io base address"); + +#if 0 +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "i"); +MODULE_PARM_DESC(irq, "eth16i interrupt request number"); +#endif + +MODULE_PARM(mediatype, "1-" __MODULE_STRING(MAX_ETH16I_CARDS) "s"); +MODULE_PARM_DESC(mediatype, "eth16i interfaceport mediatype"); + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "eth16i debug level (0-4)"); +#endif + +int init_module(void) +{ +	int this_dev, found = 0; + +	for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) +	{ +		struct device *dev = &dev_eth16i[this_dev]; + +		dev->name = namelist + (NAMELEN*this_dev); +		dev->irq = 0; /* irq[this_dev]; */ +		dev->base_addr = ioaddr[this_dev]; +		dev->init = eth16i_probe; + +	        if(debug != -1) +			eth16i_debug = debug; + +		if(eth16i_debug > 1) +			printk(KERN_NOTICE "eth16i(%d): interface type %s\n", this_dev, mediatype[this_dev] ? mediatype[this_dev] : "none" ); + +		dev->if_port = eth16i_parse_mediatype(mediatype[this_dev]); + +		if(ioaddr[this_dev] == 0) +		{ +			if(this_dev != 0) break; /* Only autoprobe 1st one */ + +			printk(KERN_NOTICE "eth16i.c: Presently autoprobing (not recommended) for a single card.\n"); +		} + +		if(register_netdev(dev) != 0) +		{ +			printk(KERN_WARNING "eth16i.c No Eth16i card found (i/o = 0x%x).\n", +			       ioaddr[this_dev]); + +			if(found != 0) return 0; +			return -ENXIO; +		} + +		found++; +	} +	return 0; +} + +void cleanup_module(void) +{ +	int this_dev; + +	for(this_dev = 0; this_dev < MAX_ETH16I_CARDS; this_dev++) +	{ +		struct device* dev = &dev_eth16i[this_dev]; + +		if(dev->priv != NULL) +		{ +			unregister_netdev(dev); +			kfree(dev->priv); +			dev->priv = NULL; + +			free_irq(dev->irq, dev); +			release_region(dev->base_addr, ETH16I_IO_EXTENT); + +		} +	} +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c eth16i.c" + *  alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict -prototypes -O6 -c eth16i.c" + *  tab-width: 8 + *  c-basic-offset: 8 + *  c-indent-level: 8 + * End: + */ + +/* End of file eth16i.c */ diff --git a/linux/src/drivers/net/eth82586.h b/linux/src/drivers/net/eth82586.h new file mode 100644 index 0000000..c2178ff --- /dev/null +++ b/linux/src/drivers/net/eth82586.h @@ -0,0 +1,172 @@ +/* + * eth82586.h: Intel EtherExpress defines + * + * Written 1995 by John Sullivan + * See eexpress.c for further details + * documentation and usage to do. + */ + +/* + * EtherExpress card register addresses + * as offsets from the base IO region (dev->base_addr) + */ + +#define DATAPORT 0x0000 +#define WRITE_PTR 0x0002 +#define READ_PTR 0x0004 +#define SIGNAL_CA 0x0006 +#define SET_IRQ 0x0007 +#define SM_PTR 0x0008 +#define MEM_Ctrl 0x000b +#define MEM_Page_Ctrl 0x000c +#define Config 0x000d +#define EEPROM_Ctrl 0x000e +#define ID_PORT 0x000f + +/* + * offset to shadowed memory, 0 <= x <= 31. We don't use this yet, + * but may in the future. Is shadow memory access any faster than + * dataport access? + */ +#define SM_ADDR(x) (0x4000+((x&0x10)<<10)+(x&0xf)) + +/* Always mirrors eexp-memory at 0x0008-0x000f */ +#define SCB_STATUS 0xc008 +#define SCB_CMD 0xc00a +#define SCB_CBL 0xc00c +#define SCB_RFA 0xc00e + + + +/* + * card register defines + */ + +/* SET_IRQ */ +#define SIRQ_en 0x08 +#define SIRQ_dis 0x00 + +/* Config */ +#define set_loopback outb(inb(ioaddr+Config)|0x02,ioaddr+Config) +#define clear_loopback outb(inb(ioaddr+Config)&0xfd,ioaddr+Config) + +/* EEPROM_Ctrl */ +#define EC_Clk 0x01 +#define EC_CS  0x02 +#define EC_Wr  0x04 +#define EC_Rd  0x08 +#define ASIC_RST 0x40 +#define i586_RST  0x80 + +#define eeprom_delay() { int _i = 40; while (--_i>0) { __SLOW_DOWN_IO; }} + +/* + * i82586 Memory Configuration + */ + +/* (System Configuration Pointer) System start up block, read after 586_RST */ +#define SCP_START 0xfff6 + + +/* Intermediate System Configuration Pointer */ +#define ISCP_START 0x0000 +/* System Command Block */ +#define SCB_START 0x0008 + +/* + * Start of buffer region. If we have 64k memory, eexp_hw_probe() may raise + * NUM_TX_BUFS. RX_BUF_END is set to the end of memory, and all space between + * the transmit buffer region and end of memory used for as many receive buffers + * as we can fit. See eexp_hw_[(rx)(tx)]init(). + */ +#define TX_BUF_START 0x0100 +#define TX_BUF_SIZE ((24+ETH_FRAME_LEN+31)&~0x1f) +#define RX_BUF_SIZE ((32+ETH_FRAME_LEN+31)&~0x1f) + + + +/* + * SCB defines + */ + +/* these functions take the SCB status word and test the relevant status bit */ +#define SCB_complete(s) ((s&0x8000)!=0) +#define SCB_rxdframe(s) ((s&0x4000)!=0) +#define SCB_CUdead(s) ((s&0x2000)!=0) +#define SCB_RUdead(s) ((s&0x1000)!=0) +#define SCB_ack(s) (s & 0xf000) + +/* Command unit status: 0=idle, 1=suspended, 2=active */ +#define SCB_CUstat(s) ((s&0x0300)>>8) + +/* Receive unit status: 0=idle, 1=suspended, 2=out of resources, 4=ready */ +#define SCB_RUstat(s) ((s&0x0070)>>4) + +/* SCB commands */ +#define SCB_CUnop     0x0000 +#define SCB_CUstart   0x0100 +#define SCB_CUresume  0x0200 +#define SCB_CUsuspend 0x0300 +#define SCB_CUabort   0x0400 + +/* ? */ +#define SCB_resetchip 0x0080 + +#define SCB_RUnop     0x0000 +#define SCB_RUstart   0x0010 +#define SCB_RUresume  0x0020 +#define SCB_RUsuspend 0x0030 +#define SCB_RUabort   0x0040 + + +/* + * Command block defines + */ + +#define Stat_Done(s)   ((s&0x8000)!=0) +#define Stat_Busy(s)   ((s&0x4000)!=0) +#define Stat_OK(s)     ((s&0x2000)!=0) +#define Stat_Abort(s)  ((s&0x1000)!=0) +#define Stat_STFail    ((s&0x0800)!=0) +#define Stat_TNoCar(s) ((s&0x0400)!=0) +#define Stat_TNoCTS(s) ((s&0x0200)!=0) +#define Stat_TNoDMA(s) ((s&0x0100)!=0) +#define Stat_TDefer(s) ((s&0x0080)!=0) +#define Stat_TColl(s)  ((s&0x0040)!=0) +#define Stat_TXColl(s) ((s&0x0020)!=0) +#define Stat_NoColl(s) (s&0x000f) + +/* Cmd_END will end AFTER the command if this is the first + * command block after an SCB_CUstart, but BEFORE the command + * for all subsequent commands. Best strategy is to place + * Cmd_INT on the last command in the sequence, followed by a + * dummy Cmd_Nop with Cmd_END after this. + */ +#define Cmd_END     0x8000 +#define Cmd_SUS     0x4000 +#define Cmd_INT     0x2000 + +#define Cmd_Nop     0x0000 +#define Cmd_SetAddr 0x0001 +#define Cmd_Config  0x0002 +#define Cmd_MCast   0x0003 +#define Cmd_Xmit    0x0004 +#define Cmd_TDR     0x0005 +#define Cmd_Dump    0x0006 +#define Cmd_Diag    0x0007 + + +/* + * Frame Descriptor (Receive block) defines + */ + +#define FD_Done(s)  ((s&0x8000)!=0) +#define FD_Busy(s)  ((s&0x4000)!=0) +#define FD_OK(s)    ((s&0x2000)!=0) + +#define FD_CRC(s)   ((s&0x0800)!=0) +#define FD_Align(s) ((s&0x0400)!=0) +#define FD_Resrc(s) ((s&0x0200)!=0) +#define FD_DMA(s)   ((s&0x0100)!=0) +#define FD_Short(s) ((s&0x0080)!=0) +#define FD_NoEOF(s) ((s&0x0040)!=0) diff --git a/linux/src/drivers/net/ewrk3.c b/linux/src/drivers/net/ewrk3.c new file mode 100644 index 0000000..07b0f13 --- /dev/null +++ b/linux/src/drivers/net/ewrk3.c @@ -0,0 +1,1920 @@ +/*  ewrk3.c: A DIGITAL EtherWORKS 3 ethernet driver for Linux. + +    Written 1994 by David C. Davies. + +    Copyright 1994 Digital Equipment Corporation. + +    This software may be used and distributed according to the terms of +    the GNU Public License, incorporated herein by reference. + +    This driver is written for the Digital Equipment Corporation series +    of EtherWORKS ethernet cards: + +	DE203 Turbo (BNC) +	DE204 Turbo (TP) +	DE205 Turbo (TP BNC) + +    The driver has been tested on a relatively busy  network using the DE205 +    card and benchmarked with 'ttcp': it transferred 16M  of data at 975kB/s +    (7.8Mb/s) to a DECstation 5000/200. + +    The author may be reached at davies@maniac.ultranet.com. + +    ========================================================================= +    This driver has been written  substantially  from scratch, although  its +    inheritance of style and stack interface from 'depca.c' and in turn from +    Donald Becker's 'lance.c' should be obvious. + +    The  DE203/4/5 boards  all  use a new proprietary   chip in place of the +    LANCE chip used in prior cards  (DEPCA, DE100, DE200/1/2, DE210, DE422). +    Use the depca.c driver in the standard distribution  for the LANCE based +    cards from DIGITAL; this driver will not work with them. + +    The DE203/4/5 cards have 2  main modes: shared memory  and I/O only. I/O +    only makes  all the card accesses through  I/O transactions and  no high +    (shared)  memory is used. This  mode provides a >48% performance penalty +    and  is deprecated in this  driver,  although allowed to provide initial +    setup when hardstrapped. + +    The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is +    no point in using any mode other than the 2kB  mode - their performances +    are virtually identical, although the driver has  been tested in the 2kB +    and 32kB modes. I would suggest you uncomment the line: + +                             FORCE_2K_MODE; + +    to allow the driver to configure the card as a  2kB card at your current +    base  address, thus leaving more  room to clutter  your  system box with +    other memory hungry boards. + +    As many ISA  and EISA cards  can be supported  under this driver  as you +    wish, limited primarily  by the available IRQ lines,  rather than by the +    available I/O addresses  (24 ISA,  16 EISA).   I have  checked different +    configurations of  multiple  depca cards and  ewrk3 cards  and have  not +    found a problem yet (provided you have at least depca.c v0.38) ... + +    The board IRQ setting   must be at  an unused  IRQ which is  auto-probed +    using  Donald  Becker's autoprobe  routines.   All  these cards   are at +    {5,10,11,15}. + +    No 16MB memory  limitation should exist with this  driver as DMA is  not +    used and the common memory area is in low memory on the network card (my +    current system has 20MB and I've not had problems yet). + +    The ability to load  this driver as a  loadable module has been included +    and used  extensively during the  driver development (to save those long +    reboot sequences). To utilise this ability, you have to do 8 things: + +    0) have a copy of the loadable modules code installed on your system. +    1) copy ewrk3.c from the  /linux/drivers/net directory to your favourite +    temporary directory. +    2) edit the  source code near  line 1898 to reflect  the I/O address and +    IRQ you're using. +    3) compile  ewrk3.c, but include -DMODULE in  the command line to ensure +    that the correct bits are compiled (see end of source code). +    4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a +    kernel with the ewrk3 configuration turned off and reboot. +    5) insmod ewrk3.o +          [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y] +    6) run the net startup bits for your new eth?? interface manually  +    (usually /etc/rc.inet[12] at boot time).  +    7) enjoy! + +    Note that autoprobing is not allowed in loadable modules - the system is +    already up and running and you're messing with interrupts. + +    To unload a module, turn off the associated interface  +    'ifconfig eth?? down' then 'rmmod ewrk3'. + +    Promiscuous   mode has been  turned  off  in this driver,   but  all the +    multicast  address bits  have been   turned on. This  improved the  send +    performance on a busy network by about 13%. + +    Ioctl's have now been provided (primarily because  I wanted to grab some +    packet size statistics). They  are patterned after 'plipconfig.c' from a +    suggestion by Alan Cox.  Using these  ioctls, you can enable promiscuous +    mode, add/delete multicast  addresses, change the hardware address,  get +    packet size distribution statistics and muck around with the control and +    status register. I'll add others if and when the need arises. + +    TO DO: +    ------ + + +    Revision History +    ---------------- + +    Version   Date        Description +   +      0.1     26-aug-94   Initial writing. ALPHA code release. +      0.11    31-aug-94   Fixed: 2k mode memory base calc.,  +                                 LeMAC version calc., +				 IRQ vector assignments during autoprobe. +      0.12    31-aug-94   Tested working on LeMAC2 (DE20[345]-AC) card. +                          Fixed up MCA hash table algorithm. +      0.20     4-sep-94   Added IOCTL functionality. +      0.21    14-sep-94   Added I/O mode. +      0.21axp 15-sep-94   Special version for ALPHA AXP Linux V1.0. +      0.22    16-sep-94   Added more IOCTLs & tidied up. +      0.23    21-sep-94   Added transmit cut through. +      0.24    31-oct-94   Added uid checks in some ioctls. +      0.30     1-nov-94   BETA code release. +      0.31     5-dec-94   Added check/allocate region code. +      0.32    16-jan-95   Broadcast packet fix. +      0.33    10-Feb-95   Fix recognition bug reported by <bkm@star.rl.ac.uk>. +      0.40    27-Dec-95   Rationalise MODULE and autoprobe code. +                          Rewrite for portability & updated. +                          ALPHA support from <jestabro@amt.tay1.dec.com> +                          Added verify_area() calls in ewrk3_ioctl() from +                          suggestion by <heiko@colossus.escape.de>. +			  Add new multicasting code. +      0.41    20-Jan-96   Fix IRQ set up problem reported by  +                          <kenneth@bbs.sas.ntu.ac.sg>. +      0.42    22-Apr-96	  Fix alloc_device() bug <jari@markkus2.fimr.fi> +      0.43    16-Aug-96	  Update alloc_device() to conform to de4x5.c + +    ========================================================================= +*/ + +static const char *version = "ewrk3.c:v0.43 96/8/16 davies@maniac.ultranet.com\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/segment.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <linux/time.h> +#include <linux/types.h> +#include <linux/unistd.h> +#include <linux/ctype.h> + +#include "ewrk3.h" + +#ifdef EWRK3_DEBUG +static int ewrk3_debug = EWRK3_DEBUG; +#else +static int ewrk3_debug = 1; +#endif + +#define EWRK3_NDA 0xffe0            /* No Device Address */ + +#define PROBE_LENGTH    32 +#define ETH_PROM_SIG    0xAA5500FFUL + +#ifndef EWRK3_SIGNATURE +#define EWRK3_SIGNATURE {"DE203","DE204","DE205",""} +#define EWRK3_STRLEN 8 +#endif + +#ifndef EWRK3_RAM_BASE_ADDRESSES +#define EWRK3_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0x00000} +#endif + +/* +** Sets up the I/O area for the autoprobe. +*/ +#define EWRK3_IO_BASE 0x100             /* Start address for probe search */ +#define EWRK3_IOP_INC 0x20              /* I/O address increment */ +#define EWRK3_TOTAL_SIZE 0x20           /* required I/O address length */ + +#ifndef MAX_NUM_EWRK3S +#define MAX_NUM_EWRK3S 21 +#endif + +#ifndef EWRK3_EISA_IO_PORTS  +#define EWRK3_EISA_IO_PORTS 0x0c00      /* I/O port base address, slot 0 */ +#endif + +#ifndef MAX_EISA_SLOTS +#define MAX_EISA_SLOTS 16 +#define EISA_SLOT_INC 0x1000 +#endif + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL   /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL   /* Ethernet CRC, little endian */ + +#define QUEUE_PKT_TIMEOUT (1*HZ)         /* Jiffies */ + +/* +** EtherWORKS 3 shared memory window sizes +*/ +#define IO_ONLY         0x00 +#define SHMEM_2K        0x800 +#define SHMEM_32K       0x8000 +#define SHMEM_64K       0x10000 + +/* +** EtherWORKS 3 IRQ ENABLE/DISABLE +*/ +#define ENABLE_IRQs { \ +  icr |= lp->irq_mask;\ +  outb(icr, EWRK3_ICR);                     /* Enable the IRQs */\ +} + +#define DISABLE_IRQs { \ +  icr = inb(EWRK3_ICR);\ +  icr &= ~lp->irq_mask;\ +  outb(icr, EWRK3_ICR);                     /* Disable the IRQs */\ +} + +/* +** EtherWORKS 3 START/STOP +*/ +#define START_EWRK3 { \ +  csr = inb(EWRK3_CSR);\ +  csr &= ~(CSR_TXD|CSR_RXD);\ +  outb(csr, EWRK3_CSR);                     /* Enable the TX and/or RX */\ +} + +#define STOP_EWRK3 { \ +  csr = (CSR_TXD|CSR_RXD);\ +  outb(csr, EWRK3_CSR);                     /* Disable the TX and/or RX */\ +} + +/* +** The EtherWORKS 3 private structure +*/ +#define EWRK3_PKT_STAT_SZ 16 +#define EWRK3_PKT_BIN_SZ  128           /* Should be >=100 unless you +                                           increase EWRK3_PKT_STAT_SZ */ + +struct ewrk3_private { +    char adapter_name[80];              /* Name exported to /proc/ioports */ +    u_long shmem_base;                  /* Shared memory start address */ +    u_long shmem_length;                /* Shared memory window length */ +    struct enet_statistics stats;       /* Public stats */ +    struct { +      u32 bins[EWRK3_PKT_STAT_SZ]; /* Private stats counters */ +      u32 unicast; +      u32 multicast; +      u32 broadcast; +      u32 excessive_collisions; +      u32 tx_underruns; +      u32 excessive_underruns; +    } pktStats; +    u_char irq_mask;                    /* Adapter IRQ mask bits */ +    u_char mPage;                       /* Maximum 2kB Page number */ +    u_char lemac;                       /* Chip rev. level */ +    u_char hard_strapped;               /* Don't allow a full open */ +    u_char lock;                        /* Lock the page register */ +    u_char txc;                         /* Transmit cut through */ +    u_char *mctbl;                      /* Pointer to the multicast table */ +}; + +/* +** Force the EtherWORKS 3 card to be in 2kB MODE +*/ +#define FORCE_2K_MODE { \ +  shmem_length = SHMEM_2K;\ +  outb(((mem_start - 0x80000) >> 11), EWRK3_MBR);\ +} + +/* +** Public Functions +*/ +static int    ewrk3_open(struct device *dev); +static int    ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev); +static void   ewrk3_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int    ewrk3_close(struct device *dev); +static struct enet_statistics *ewrk3_get_stats(struct device *dev); +static void   set_multicast_list(struct device *dev); +static int    ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd); + +/* +** Private functions +*/ +static int    ewrk3_hw_init(struct device *dev, u_long iobase); +static void   ewrk3_init(struct device *dev); +static int    ewrk3_rx(struct device *dev); +static int    ewrk3_tx(struct device *dev); + +static void   EthwrkSignature(char * name, char *eeprom_image); +static int    DevicePresent(u_long iobase); +static void   SetMulticastFilter(struct device *dev); +static int    EISA_signature(char *name, s32 eisa_id); + +static int    Read_EEPROM(u_long iobase, u_char eaddr); +static int    Write_EEPROM(short data, u_long iobase, u_char eaddr); +static u_char get_hw_addr (struct device *dev, u_char *eeprom_image, char chipType); + +static void   isa_probe(struct device *dev, u_long iobase); +static void   eisa_probe(struct device *dev, u_long iobase); +static struct device *alloc_device(struct device *dev, u_long iobase); +static int    ewrk3_dev_index(char *s); +static struct device *insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)); + + +#ifdef MODULE +int  init_module(void); +void cleanup_module(void); +static int autoprobed = 1, loading_module = 1; + +# else +static u_char irq[] = {5,0,10,3,11,9,15,12}; +static int autoprobed = 0, loading_module = 0; + +#endif /* MODULE */ + +static char name[EWRK3_STRLEN + 1]; +static int num_ewrk3s = 0, num_eth = 0; + +/* +** Miscellaneous defines... +*/ +#define INIT_EWRK3 {\ +    outb(EEPROM_INIT, EWRK3_IOPR);\ +    udelay(1000);\ +} + + + + +int ewrk3_probe(struct device *dev) +{ +  int tmp = num_ewrk3s, status = -ENODEV; +  u_long iobase = dev->base_addr; + +  if ((iobase == 0) && loading_module){ +    printk("Autoprobing is not supported when loading a module based driver.\n"); +    status = -EIO; +  } else {                              /* First probe for the Ethernet */ +	                                /* Address PROM pattern */ +    isa_probe(dev, iobase); +    eisa_probe(dev, iobase); + +    if ((tmp == num_ewrk3s) && (iobase != 0) && loading_module) { +      printk("%s: ewrk3_probe() cannot find device at 0x%04lx.\n", dev->name,  +	                                                               iobase); +    } + +    /* +    ** Walk the device list to check that at least one device +    ** initialised OK +    */ +    for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next); + +    if (dev->priv) status = 0; +    if (iobase == 0) autoprobed = 1; +  } + +  return status; +} + +static int +ewrk3_hw_init(struct device *dev, u_long iobase) +{ +  struct ewrk3_private *lp; +  int i, status=0; +  u_long mem_start, shmem_length; +  u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0; +  u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0; + +  /* +  ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot. +  ** This also disables the EISA_ENABLE bit in the EISA Control Register. +  */ +  if (iobase > 0x400) eisa_cr = inb(EISA_CR); +  INIT_EWRK3; + +  nicsr = inb(EWRK3_CSR); + +  icr = inb(EWRK3_ICR); +  icr &= 0x70; +  outb(icr, EWRK3_ICR);                           /* Disable all the IRQs */ + +  if (nicsr == (CSR_TXD|CSR_RXD)) { + +    /* Check that the EEPROM is alive and well and not living on Pluto... */ +    for (chksum=0, i=0; i<EEPROM_MAX; i+=2) { +      union { +	short val; +	char c[2]; +      } tmp; + +      tmp.val = (short)Read_EEPROM(iobase, (i>>1)); +      eeprom_image[i] = tmp.c[0]; +      eeprom_image[i+1] = tmp.c[1]; +      chksum += eeprom_image[i] + eeprom_image[i+1]; +    } + +    if (chksum != 0) {                             /* Bad EEPROM Data! */ +      printk("%s: Device has a bad on-board EEPROM.\n", dev->name); +      status = -ENXIO; +    } else { +      EthwrkSignature(name, eeprom_image); +      if (*name != '\0') {                         /* found a EWRK3 device */ +	dev->base_addr = iobase; +       +	if (iobase > 0x400) { +	  outb(eisa_cr, EISA_CR);                  /* Rewrite the EISA CR */ +	} + +	lemac = eeprom_image[EEPROM_CHIPVER]; +	cmr = inb(EWRK3_CMR); + +	if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) || +	    ((lemac == LeMAC2) && !(cmr & CMR_HS))) { +	  printk("%s: %s at %#4lx", dev->name, name, iobase); +	  hard_strapped = 1; +	} else if ((iobase&0x0fff)==EWRK3_EISA_IO_PORTS) { +	                                           /* EISA slot address */ +	  printk("%s: %s at %#4lx (EISA slot %ld)",  +	                         dev->name, name, iobase, ((iobase>>12)&0x0f)); +	} else {                                   /* ISA port address */ +	  printk("%s: %s at %#4lx", dev->name, name, iobase); +	} +	 +	if (!status) { +	  printk(", h/w address "); +	  if (lemac!=LeMAC2) DevicePresent(iobase);/* need after EWRK3_INIT */ +	  status = get_hw_addr(dev, eeprom_image, lemac); +	  for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */ +	    printk("%2.2x:", dev->dev_addr[i]); +	  } +	  printk("%2.2x,\n", dev->dev_addr[i]); + +	  if (status) { +	    printk("      which has an EEPROM CRC error.\n"); +	    status = -ENXIO; +	  } else { +	    if (lemac == LeMAC2) {            /* Special LeMAC2 CMR things */ +	      cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS); +	      if (eeprom_image[EEPROM_MISC0] & READ_AHEAD)    cmr |= CMR_RA; +	      if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND)  cmr |= CMR_WB; +	      if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL)  cmr |= CMR_POLARITY; +	      if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) cmr |= CMR_LINK; +	      if (eeprom_image[EEPROM_MISC0] & _0WS_ENA)      cmr |= CMR_0WS; +	    } +	    if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM)      cmr |= CMR_DRAM; +	    outb(cmr, EWRK3_CMR); + +	    cr = inb(EWRK3_CR);               /* Set up the Control Register */ +	    cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD; +	    if (cr & SETUP_APD) cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS; +	    cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS; +	    cr |= eeprom_image[EEPROM_MISC0] & ENA_16; +	    outb(cr, EWRK3_CR); + +	    /*  +	    ** Determine the base address and window length for the EWRK3 +	    ** RAM from the memory base register. +	    */ +	    mem_start = inb(EWRK3_MBR); +	    shmem_length = 0; +	    if (mem_start != 0) { +	      if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) { +		mem_start *= SHMEM_64K; +		shmem_length = SHMEM_64K; +	      } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) { +		mem_start *= SHMEM_32K; +		shmem_length = SHMEM_32K; +	      } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) { +		mem_start = mem_start * SHMEM_2K + 0x80000; +		shmem_length = SHMEM_2K; +	      } else { +		status = -ENXIO; +	      } +	    } +	   +	    /* +	    ** See the top of this source code for comments about +	    ** uncommenting this line. +	    */ +/*	    FORCE_2K_MODE;*/ + +	    if (!status) { +	      if (hard_strapped) { +		printk("      is hard strapped.\n"); +	      } else if (mem_start) { +		printk("      has a %dk RAM window", (int)(shmem_length >> 10)); +		printk(" at 0x%.5lx", mem_start); +	      } else { +		printk("      is in I/O only mode"); +	      } +	     +	      /* private area & initialise */ +	      dev->priv = (void *) kmalloc(sizeof(struct ewrk3_private),  +					                           GFP_KERNEL); +	      if (dev->priv == NULL) { +		return -ENOMEM; +	      } +	      lp = (struct ewrk3_private *)dev->priv; +	      memset(dev->priv, 0, sizeof(struct ewrk3_private)); +	      lp->shmem_base = mem_start; +	      lp->shmem_length = shmem_length; +	      lp->lemac = lemac; +	      lp->hard_strapped = hard_strapped; + +	      lp->mPage = 64; +	      if (cmr & CMR_DRAM) lp->mPage <<= 1 ;/* 2 DRAMS on module */  + +	      sprintf(lp->adapter_name,"%s (%s)", name, dev->name); +	      request_region(iobase, EWRK3_TOTAL_SIZE, lp->adapter_name); + +	      lp->irq_mask = ICR_TNEM|ICR_TXDM|ICR_RNEM|ICR_RXDM; +	       +	      if (!hard_strapped) { +		/* +		** Enable EWRK3 board interrupts for autoprobing +		*/ +		icr |= ICR_IE;	                   /* Enable interrupts */ +		outb(icr, EWRK3_ICR); +	     +		/* The DMA channel may be passed in on this parameter. */ +		dev->dma = 0; +	 +		/* To auto-IRQ we enable the initialization-done and DMA err, +		   interrupts. For now we will always get a DMA error. */ +		if (dev->irq < 2) { +#ifndef MODULE +		  u_char irqnum; +	       +		  autoirq_setup(0); + +		  /*  +		  ** Trigger a TNE interrupt. +		  */ +		  icr |=ICR_TNEM; +		  outb(1,EWRK3_TDQ);          /* Write to the TX done queue */ +		  outb(icr, EWRK3_ICR);       /* Unmask the TXD interrupt */ +	       +		  irqnum = irq[((icr & IRQ_SEL) >> 4)]; +	       +		  dev->irq = autoirq_report(1); +		  if ((dev->irq) && (irqnum == dev->irq)) { +		    printk(" and uses IRQ%d.\n", dev->irq); +		  } else { +		    if (!dev->irq) { +		      printk(" and failed to detect IRQ line.\n"); +		    } else if ((irqnum == 1) && (lemac == LeMAC2)) { +		      printk(" and an illegal IRQ line detected.\n"); +		    } else { +		      printk(", but incorrect IRQ line detected.\n"); +		    } +		    status = -ENXIO; +		  } +		 +		  DISABLE_IRQs;                 /* Mask all interrupts */ + +#endif /* MODULE */ +		} else { +		  printk(" and requires IRQ%d.\n", dev->irq); +		} +	      } +	      if (status) release_region(iobase, EWRK3_TOTAL_SIZE); +	    } else { +	      status = -ENXIO; +	    } +	  } +	} +      } else { +	status = -ENXIO; +      } +    } + +    if (!status) { +      if (ewrk3_debug > 1) { +	printk("%s", version); +      } +       +      /* The EWRK3-specific entries in the device structure. */ +      dev->open = &ewrk3_open; +      dev->hard_start_xmit = &ewrk3_queue_pkt; +      dev->stop = &ewrk3_close; +      dev->get_stats = &ewrk3_get_stats; +      dev->set_multicast_list = &set_multicast_list; +      dev->do_ioctl = &ewrk3_ioctl; + +      dev->mem_start = 0; +	 +      /* Fill in the generic field of the device structure. */ +      ether_setup(dev); +    } +  } else { +    status = -ENXIO; +  } + +  return status; +} + + +static int +ewrk3_open(struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  u_long iobase = dev->base_addr; +  int i, status = 0; +  u_char icr, csr; + +  /* +  ** Stop the TX and RX... +  */ +  STOP_EWRK3; + +  if (!lp->hard_strapped) { +    irq2dev_map[dev->irq] = dev;                   /* For latched interrupts */ + +    if (request_irq(dev->irq, (void *)ewrk3_interrupt, 0, "ewrk3", NULL)) { +      printk("ewrk3_open(): Requested IRQ%d is busy\n",dev->irq); +      status = -EAGAIN; +    } else { + +      /*  +      ** Re-initialize the EWRK3...  +      */ +      ewrk3_init(dev); + +      if (ewrk3_debug > 1){ +	printk("%s: ewrk3 open with irq %d\n",dev->name,dev->irq); +	printk("  physical address: "); +	for (i=0;i<5;i++){ +	  printk("%2.2x:",(u_char)dev->dev_addr[i]); +	} +	printk("%2.2x\n",(u_char)dev->dev_addr[i]); +	if (lp->shmem_length == 0) { +	  printk("  no shared memory, I/O only mode\n"); +	} else { +	  printk("  start of shared memory: 0x%08lx\n",lp->shmem_base); +	  printk("  window length: 0x%04lx\n",lp->shmem_length); +	} +	printk("  # of DRAMS: %d\n",((inb(EWRK3_CMR) & 0x02) ? 2 : 1)); +	printk("  csr:  0x%02x\n", inb(EWRK3_CSR)); +	printk("  cr:   0x%02x\n", inb(EWRK3_CR)); +	printk("  icr:  0x%02x\n", inb(EWRK3_ICR)); +	printk("  cmr:  0x%02x\n", inb(EWRK3_CMR)); +	printk("  fmqc: 0x%02x\n", inb(EWRK3_FMQC)); +      } + +      dev->tbusy = 0;                          +      dev->start = 1; +      dev->interrupt = UNMASK_INTERRUPTS; + +      /* +      ** Unmask EWRK3 board interrupts +      */ +      icr = inb(EWRK3_ICR); +      ENABLE_IRQs; + +    } +  } else { +    dev->start = 0; +    dev->tbusy = 1; +    printk("%s: ewrk3 available for hard strapped set up only.\n", dev->name); +    printk("      Run the 'ewrk3setup' utility or remove the hard straps.\n"); +  } + +  MOD_INC_USE_COUNT; + +  return status; +} + +/* +** Initialize the EtherWORKS 3 operating conditions +*/ +static void +ewrk3_init(struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  u_char csr, page; +  u_long iobase = dev->base_addr; +   +  /*  +  ** Enable any multicasts  +  */ +  set_multicast_list(dev); + +  /* +  ** Clean out any remaining entries in all the queues here +  */ +  while (inb(EWRK3_TQ)); +  while (inb(EWRK3_TDQ)); +  while (inb(EWRK3_RQ)); +  while (inb(EWRK3_FMQ)); + +  /* +  ** Write a clean free memory queue +  */ +  for (page=1;page<lp->mPage;page++) {      /* Write the free page numbers */ +    outb(page, EWRK3_FMQ);                  /* to the Free Memory Queue */ +  } + +  lp->lock = 0;                             /* Ensure there are no locks */ + +  START_EWRK3;                              /* Enable the TX and/or RX */ +} + +/*  +** Writes a socket buffer to the free page queue +*/ +static int +ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  u_long iobase = dev->base_addr; +  int status = 0; +  u_char icr, csr; + +  /* Transmitter timeout, serious problems. */ +  if (dev->tbusy || lp->lock) { +    int tickssofar = jiffies - dev->trans_start; +    if (tickssofar < QUEUE_PKT_TIMEOUT) { +      status = -1; +    } else if (!lp->hard_strapped) { +      printk("%s: transmit timed/locked out, status %04x, resetting.\n", +	                                           dev->name, inb(EWRK3_CSR)); +	 +      /* +      ** Mask all board interrupts +      */ +      DISABLE_IRQs; + +      /* +      ** Stop the TX and RX... +      */ +      STOP_EWRK3; + +      ewrk3_init(dev); + +      /* +      ** Unmask EWRK3 board interrupts +      */ +      ENABLE_IRQs; + +      dev->tbusy=0; +      dev->trans_start = jiffies; +      dev_kfree_skb(skb, FREE_WRITE); +    } +  } else if (skb == NULL) { +    dev_tint(dev); +  } else if (skb->len > 0) { + +    /*  +    ** Block a timer-based transmit from overlapping.  This could better be +    ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well.  +    */ +    if (set_bit(0, (void*)&dev->tbusy) != 0) +      printk("%s: Transmitter access conflict.\n", dev->name); + +    DISABLE_IRQs;                      /* So that the page # remains correct */ +     +    /*  +    ** Get a free page from the FMQ when resources are available +    */ +    if (inb(EWRK3_FMQC) > 0) { +      u_long buf = 0; +      u_char page; + +      if ((page = inb(EWRK3_FMQ)) < lp->mPage) { +	/* +	** Set up shared memory window and pointer into the window +	*/ +	while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ +	if (lp->shmem_length == IO_ONLY) { +	  outb(page, EWRK3_IOPR); +	} else if (lp->shmem_length == SHMEM_2K) { +	  buf = lp->shmem_base; +	  outb(page, EWRK3_MPR); +	} else if (lp->shmem_length == SHMEM_32K) { +	  buf = ((((short)page << 11) & 0x7800) + lp->shmem_base); +	  outb((page >> 4), EWRK3_MPR); +	} else if (lp->shmem_length == SHMEM_64K) { +	  buf = ((((short)page << 11) & 0xf800) + lp->shmem_base); +	  outb((page >> 5), EWRK3_MPR); +	} else { +	  status = -1; +	  printk("%s: Oops - your private data area is hosed!\n",dev->name); +	} + +	if (!status) { + +          /*  +	  ** Set up the buffer control structures and copy the data from +	  ** the socket buffer to the shared memory . +	  */ + +	  if (lp->shmem_length == IO_ONLY) { +	    int i; +	    u_char *p = skb->data; +	     +	    outb((char)(TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA); +	    outb((char)(skb->len & 0xff), EWRK3_DATA); +	    outb((char)((skb->len >> 8) & 0xff), EWRK3_DATA); +	    outb((char)0x04, EWRK3_DATA); +	    for (i=0; i<skb->len; i++) { +	      outb(*p++, EWRK3_DATA); +	    } +	    outb(page, EWRK3_TQ);                      /* Start sending pkt */ +	  } else { +	    writeb((char)(TCR_QMODE|TCR_PAD|TCR_IFC), (char *)buf);/* ctrl byte*/ +	    buf+=1; +	    writeb((char)(skb->len & 0xff), (char *)buf);/* length (16 bit xfer)*/ +	    buf+=1; +	    if (lp->txc) { +	      writeb((char)(((skb->len >> 8) & 0xff) | XCT), (char *)buf); +	      buf+=1; +	      writeb(0x04, (char *)buf);               /* index byte */ +	      buf+=1; +	      writeb(0x00, (char *)(buf + skb->len));  /* Write the XCT flag */ +	      memcpy_toio(buf, skb->data, PRELOAD);/* Write PRELOAD bytes*/ +	      outb(page, EWRK3_TQ);                    /* Start sending pkt */ +	      memcpy_toio(buf+PRELOAD, skb->data+PRELOAD, skb->len-PRELOAD); +	      writeb(0xff, (char *)(buf + skb->len));  /* Write the XCT flag */ +	    } else { +	      writeb((char)((skb->len >> 8) & 0xff), (char *)buf); +	      buf+=1; +	      writeb(0x04, (char *)buf);               /* index byte */ +	      buf+=1; +	      memcpy_toio((char *)buf, skb->data, skb->len);/* Write data bytes */ +	      outb(page, EWRK3_TQ);                    /* Start sending pkt */ +	    } +	  } + +	  dev->trans_start = jiffies; +	  dev_kfree_skb (skb, FREE_WRITE); + +        } else {              /* return unused page to the free memory queue */ +	  outb(page, EWRK3_FMQ); +	} +	lp->lock = 0;         /* unlock the page register */ +      } else { +	printk("ewrk3_queue_pkt(): Invalid free memory page (%d).\n", +	                                                 (u_char) page); +      } +    } else { +      printk("ewrk3_queue_pkt(): No free resources...\n"); +      printk("ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC)); +    } +     +    /* Check for free resources: clear 'tbusy' if there are some */ +    if (inb(EWRK3_FMQC) > 0) { +      dev->tbusy = 0; +    } + +    ENABLE_IRQs; +  } + +  return status; +} + +/* +** The EWRK3 interrupt handler.  +*/ +static void +ewrk3_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +    struct device *dev = (struct device *)(irq2dev_map[irq]); +    struct ewrk3_private *lp; +    u_long iobase; +    u_char icr, cr, csr; + +    if (dev == NULL) { +	printk ("ewrk3_interrupt(): irq %d for unknown device.\n", irq); +    } else { +      lp = (struct ewrk3_private *)dev->priv; +      iobase = dev->base_addr; + +      if (dev->interrupt) +	printk("%s: Re-entering the interrupt handler.\n", dev->name); + +      dev->interrupt = MASK_INTERRUPTS; + +      /* get the interrupt information */ +      csr = inb(EWRK3_CSR); + +      /*  +      ** Mask the EWRK3 board interrupts and turn on the LED  +      */ +      DISABLE_IRQs; + +      cr = inb(EWRK3_CR); +      cr |= CR_LED; +      outb(cr, EWRK3_CR); + +      if (csr & CSR_RNE)	  /* Rx interrupt (packet[s] arrived) */ +	ewrk3_rx(dev); + +      if (csr & CSR_TNE)          /* Tx interrupt (packet sent) */ +        ewrk3_tx(dev); + +      /* +      ** Now deal with the TX/RX disable flags. These are set when there +      ** are no more resources. If resources free up then enable these +      ** interrupts, otherwise mask them - failure to do this will result +      ** in the system hanging in an interrupt loop. +      */ +      if (inb(EWRK3_FMQC)) {      /* any resources available? */ +	lp->irq_mask |= ICR_TXDM|ICR_RXDM;/* enable the interrupt source */ +	csr &= ~(CSR_TXD|CSR_RXD);/* ensure restart of a stalled TX or RX */ +	outb(csr, EWRK3_CSR); +	dev->tbusy = 0;           /* clear TX busy flag */ +	mark_bh(NET_BH); +      } else { +	lp->irq_mask &= ~(ICR_TXDM|ICR_RXDM);/* disable the interrupt source */ +      } + +      /* Unmask the EWRK3 board interrupts and turn off the LED */ +      cr &= ~CR_LED; +      outb(cr, EWRK3_CR); + +      dev->interrupt = UNMASK_INTERRUPTS; +      ENABLE_IRQs; +    } + +    return; +} + +static int +ewrk3_rx(struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  u_long iobase = dev->base_addr; +  int i, status = 0; +  u_char page, tmpPage = 0, tmpLock = 0; +  u_long buf = 0; + +  while (inb(EWRK3_RQC) && !status) {        /* Whilst there's incoming data */ +    if ((page = inb(EWRK3_RQ)) < lp->mPage) {/* Get next entry's buffer page */ +      /* +      ** Preempt any process using the current page register. Check for +      ** an existing lock to reduce time taken in I/O transactions. +      */ +      if ((tmpLock = set_bit(0, (void *)&lp->lock)) == 1) {   /* Assert lock */ +	if (lp->shmem_length == IO_ONLY) {              /* Get existing page */ +	  tmpPage = inb(EWRK3_IOPR); +	} else { +	  tmpPage = inb(EWRK3_MPR); +	} +      } + +      /* +      ** Set up shared memory window and pointer into the window +      */ +      if (lp->shmem_length == IO_ONLY) { +	outb(page, EWRK3_IOPR); +      } else if (lp->shmem_length == SHMEM_2K) { +	buf = lp->shmem_base; +	outb(page, EWRK3_MPR); +      } else if (lp->shmem_length == SHMEM_32K) { +	buf = ((((short)page << 11) & 0x7800) + lp->shmem_base); +	outb((page >> 4), EWRK3_MPR); +      } else if (lp->shmem_length == SHMEM_64K) { +	buf = ((((short)page << 11) & 0xf800) + lp->shmem_base); +	outb((page >> 5), EWRK3_MPR); +      } else { +	status = -1; +	printk("%s: Oops - your private data area is hosed!\n",dev->name); +      } + +      if (!status) { +	char rx_status; +	int pkt_len; + +	if (lp->shmem_length == IO_ONLY) { +	  rx_status = inb(EWRK3_DATA); +	  pkt_len = inb(EWRK3_DATA); +	  pkt_len |= ((u_short)inb(EWRK3_DATA) << 8); +	} else { +	  rx_status = readb(buf); +	  buf+=1; +	  pkt_len = readw(buf); +	  buf+=3; +	} + +	if (!(rx_status & R_ROK)) {	    /* There was an error. */ +	  lp->stats.rx_errors++;            /* Update the error stats. */ +	  if (rx_status & R_DBE) lp->stats.rx_frame_errors++; +	  if (rx_status & R_CRC) lp->stats.rx_crc_errors++; +	  if (rx_status & R_PLL) lp->stats.rx_fifo_errors++; +	} else { +	  struct sk_buff *skb; + +          if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) { +            unsigned char *p; +	    skb->dev = dev; +	    skb_reserve(skb,2);             /* Align to 16 bytes */ +	    p = skb_put(skb,pkt_len); + +	    if (lp->shmem_length == IO_ONLY) { +	      *p = inb(EWRK3_DATA);         /* dummy read */ +	      for (i=0; i<pkt_len; i++) { +		*p++ = inb(EWRK3_DATA); +	      } +	    } else { +	      memcpy_fromio(p, buf, pkt_len); +	    } + +	    /*  +	    ** Notify the upper protocol layers that there is another  +	    ** packet to handle +	    */ +	    skb->protocol=eth_type_trans(skb,dev); +	    netif_rx(skb); + +	    /* +	    ** Update stats +	    */ +	    lp->stats.rx_packets++; +	    for (i=1; i<EWRK3_PKT_STAT_SZ-1; i++) { +	      if (pkt_len < i*EWRK3_PKT_BIN_SZ) { +		lp->pktStats.bins[i]++; +		i = EWRK3_PKT_STAT_SZ; +	      } +	    } +	    p = skb->data;                  /* Look at the dest addr */ +	    if (p[0] & 0x01) {              /* Multicast/Broadcast */ +	      if ((*(s32 *)&p[0] == -1) && (*(s16 *)&p[4] == -1)) { +		lp->pktStats.broadcast++; +	      } else { +		lp->pktStats.multicast++; +	      } +	    } else if ((*(s32 *)&p[0] == *(s32 *)&dev->dev_addr[0]) && +		       (*(s16 *)&p[4] == *(s16 *)&dev->dev_addr[4])) { +	      lp->pktStats.unicast++; +	    } + +	    lp->pktStats.bins[0]++;           /* Duplicates stats.rx_packets */ +	    if (lp->pktStats.bins[0] == 0) {  /* Reset counters */ +	      memset(&lp->pktStats, 0, sizeof(lp->pktStats)); +	    } +	  } else { +	    printk("%s: Insufficient memory; nuking packet.\n", dev->name); +	    lp->stats.rx_dropped++;	      /* Really, deferred. */ +	    break; +	  } +        } +      } +      /* +      ** Return the received buffer to the free memory queue +      */ +      outb(page, EWRK3_FMQ); + +      if (tmpLock) {                          /* If a lock was preempted */ +	if (lp->shmem_length == IO_ONLY) {    /* Replace old page */ +	  outb(tmpPage, EWRK3_IOPR); +	} else { +	  outb(tmpPage, EWRK3_MPR); +	} +      } +      lp->lock = 0;                           /* Unlock the page register */ +    } else { +      printk("ewrk3_rx(): Illegal page number, page %d\n",page); +      printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC)); +    } +  } +  return status; +} + +/* +** Buffer sent - check for TX buffer errors. +*/ +static int +ewrk3_tx(struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  u_long iobase = dev->base_addr; +  u_char tx_status; + +  while ((tx_status = inb(EWRK3_TDQ)) > 0) {  /* Whilst there's old buffers */ +    if (tx_status & T_VSTS) {                 /* The status is valid */ +      if (tx_status & T_TXE) { +	lp->stats.tx_errors++; +	if (tx_status & T_NCL)    lp->stats.tx_carrier_errors++; +	if (tx_status & T_LCL)    lp->stats.tx_window_errors++; +	if (tx_status & T_CTU) { +	  if ((tx_status & T_COLL) ^ T_XUR) { +	    lp->pktStats.tx_underruns++; +	  } else { +	    lp->pktStats.excessive_underruns++; +	  } +	} else 	if (tx_status & T_COLL) { +	  if ((tx_status & T_COLL) ^ T_XCOLL) { +	    lp->stats.collisions++; +	  } else { +	    lp->pktStats.excessive_collisions++; +	  } +	} +      } else { +	lp->stats.tx_packets++; +      } +    } +  } + +  return 0; +} + +static int +ewrk3_close(struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  u_long iobase = dev->base_addr; +  u_char icr, csr; + +  dev->start = 0; +  dev->tbusy = 1; + +  if (ewrk3_debug > 1) { +    printk("%s: Shutting down ethercard, status was %2.2x.\n", +	   dev->name, inb(EWRK3_CSR)); +  } + +  /*  +  ** We stop the EWRK3 here... mask interrupts and stop TX & RX +  */ +  DISABLE_IRQs; + +  STOP_EWRK3; + +  /* +  ** Clean out the TX and RX queues here (note that one entry +  ** may get added to either the TXD or RX queues if the TX or RX +  ** just starts processing a packet before the STOP_EWRK3 command +  ** is received. This will be flushed in the ewrk3_open() call). +  */ +  while (inb(EWRK3_TQ)); +  while (inb(EWRK3_TDQ)); +  while (inb(EWRK3_RQ)); + +  if (!lp->hard_strapped) { +    free_irq(dev->irq, NULL); +     +    irq2dev_map[dev->irq] = 0; +  } + +  MOD_DEC_USE_COUNT; + +  return 0; +} + +static struct enet_statistics * +ewrk3_get_stats(struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; + +  /* Null body since there is no framing error counter */ +     +  return &lp->stats; +} + +/* +** Set or clear the multicast filter for this adapter. +*/ +static void +set_multicast_list(struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  u_long iobase = dev->base_addr; +  u_char csr; + +  if (irq2dev_map[dev->irq] != NULL) { +    csr = inb(EWRK3_CSR); +     +    if (lp->shmem_length == IO_ONLY) { +      lp->mctbl = (char *) PAGE0_HTE; +    } else { +      lp->mctbl = (char *)(lp->shmem_base + PAGE0_HTE); +    } + +    csr &= ~(CSR_PME | CSR_MCE); +    if (dev->flags & IFF_PROMISC) {         /* set promiscuous mode */ +      csr |= CSR_PME; +      outb(csr, EWRK3_CSR); +    } else { +      SetMulticastFilter(dev); +      csr |= CSR_MCE; +      outb(csr, EWRK3_CSR); +    } +  } +} + +/* +** Calculate the hash code and update the logical address filter +** from a list of ethernet multicast addresses. +** Little endian crc one liner from Matt Thomas, DEC. +** +** Note that when clearing the table, the broadcast bit must remain asserted +** to receive broadcast messages. +*/ +static void SetMulticastFilter(struct device *dev) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  struct dev_mc_list *dmi=dev->mc_list; +  u_long iobase = dev->base_addr; +  int i; +  char *addrs, j, bit, byte; +  short *p = (short *) lp->mctbl; +  u16 hashcode; +  s32 crc, poly = CRC_POLYNOMIAL_LE; + +  while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ + +  if (lp->shmem_length == IO_ONLY) { +    outb(0, EWRK3_IOPR); +    outw(EEPROM_OFFSET(lp->mctbl), EWRK3_PIR1); +  } else { +    outb(0, EWRK3_MPR); +  } + +  if (dev->flags & IFF_ALLMULTI) { +    for (i=0; i<(HASH_TABLE_LEN >> 3); i++) { +      if (lp->shmem_length == IO_ONLY) { +	outb(0xff, EWRK3_DATA); +      } else {                /* memset didn't work here */ +	writew(0xffff, p); +	p++; i++; +      } +    } +  } else { +    /* Clear table except for broadcast bit */ +    if (lp->shmem_length == IO_ONLY) { +      for (i=0; i<(HASH_TABLE_LEN >> 4) - 1; i++) { +	outb(0x00, EWRK3_DATA); +      }  +      outb(0x80, EWRK3_DATA); i++;           /* insert the broadcast bit */ +      for (; i<(HASH_TABLE_LEN >> 3); i++) { +	outb(0x00, EWRK3_DATA); +      }  +    } else { +      memset_io(lp->mctbl, 0, (HASH_TABLE_LEN >> 3)); +      writeb(0x80, (char *)(lp->mctbl + (HASH_TABLE_LEN >> 4) - 1)); +    } + +    /* Update table */ +    for (i=0;i<dev->mc_count;i++) {          /* for each address in the list */ +      addrs=dmi->dmi_addr; +      dmi=dmi->next; +      if ((*addrs & 0x01) == 1) {            /* multicast address? */  +	crc = 0xffffffff;                    /* init CRC for each address */ +	for (byte=0;byte<ETH_ALEN;byte++) {  /* for each address byte */ +	                                     /* process each address bit */  +	  for (bit = *addrs++,j=0;j<8;j++, bit>>=1) { +	    crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0); +	  } +	} +	hashcode = crc & ((1 << 9) - 1);     /* hashcode is 9 LSb of CRC */ + +	byte = hashcode >> 3;                /* bit[3-8] -> byte in filter */ +	bit = 1 << (hashcode & 0x07);        /* bit[0-2] -> bit in byte */ + +	if (lp->shmem_length == IO_ONLY) { +	  u_char tmp; + +	  outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1); +	  tmp = inb(EWRK3_DATA); +	  tmp |= bit; +	  outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1); +	  outb(tmp, EWRK3_DATA);  +	} else { +	  writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte); +	} +      } +    } +  } + +  lp->lock = 0;                              /* Unlock the page register */ + +  return; +} + +/* +** ISA bus I/O device probe +*/ +static void isa_probe(struct device *dev, u_long ioaddr) +{ +  int i = num_ewrk3s, maxSlots; +  u_long iobase; + +  if (!ioaddr && autoprobed) return ;            /* Been here before ! */ +  if (ioaddr >= 0x400) return;                   /* Not ISA */ + +  if (ioaddr == 0) {                     /* Autoprobing */ +    iobase = EWRK3_IO_BASE;              /* Get the first slot address */ +    maxSlots = 24; +  } else {                               /* Probe a specific location */ +    iobase = ioaddr; +    maxSlots = i + 1; +  } + +  for (; (i<maxSlots) && (dev!=NULL);iobase+=EWRK3_IOP_INC, i++) { +    if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {     +      if (DevicePresent(iobase) == 0) { +	if ((dev = alloc_device(dev, iobase)) != NULL) { +	  if (ewrk3_hw_init(dev, iobase) == 0) { +	    num_ewrk3s++; +	  } +	  num_eth++; +	} +      } +    } else if (autoprobed) { +      printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase); +    } +  } + +  return; +} + +/* +** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually +** the motherboard. +*/ +static void eisa_probe(struct device *dev, u_long ioaddr) +{ +  int i, maxSlots; +  u_long iobase; +  char name[EWRK3_STRLEN]; + +  if (!ioaddr && autoprobed) return ;            /* Been here before ! */ +  if (ioaddr < 0x1000) return;                   /* Not EISA */ + +  if (ioaddr == 0) {                     /* Autoprobing */ +    iobase = EISA_SLOT_INC;              /* Get the first slot address */ +    i = 1; +    maxSlots = MAX_EISA_SLOTS; +  } else {                               /* Probe a specific location */ +    iobase = ioaddr; +    i = (ioaddr >> 12); +    maxSlots = i + 1; +  } + +  for (i=1; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) { +    if (EISA_signature(name, EISA_ID) == 0) { +      if (!check_region(iobase, EWRK3_TOTAL_SIZE)) { +	if (DevicePresent(iobase) == 0) { +	  if ((dev = alloc_device(dev, iobase)) != NULL) { +	    if (ewrk3_hw_init(dev, iobase) == 0) { +	      num_ewrk3s++; +	    } +	    num_eth++; +	  } +	} +      } else if (autoprobed) { +	printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase); +      } +    } +  } + +  return; +} + +/* +** Search the entire 'eth' device list for a fixed probe. If a match isn't +** found then check for an autoprobe or unused device location. If they +** are not available then insert a new device structure at the end of +** the current list. +*/ +static struct device * +alloc_device(struct device *dev, u_long iobase) +{ +    struct device *adev = NULL; +    int fixed = 0, new_dev = 0; + +    num_eth = ewrk3_dev_index(dev->name); +    if (loading_module) return dev; +     +    while (1) { +	if (((dev->base_addr == EWRK3_NDA) || (dev->base_addr==0)) && !adev) { +	    adev=dev; +	} else if ((dev->priv == NULL) && (dev->base_addr==iobase)) { +	    fixed = 1; +	} else { +	    if (dev->next == NULL) { +		new_dev = 1; +	    } else if (strncmp(dev->next->name, "eth", 3) != 0) { +		new_dev = 1; +	    } +	} +	if ((dev->next == NULL) || new_dev || fixed) break; +	dev = dev->next; +	num_eth++; +    } +    if (adev && !fixed) { +	dev = adev; +	num_eth = ewrk3_dev_index(dev->name); +	new_dev = 0; +    } + +    if (((dev->next == NULL) &&   +	((dev->base_addr != EWRK3_NDA) && (dev->base_addr != 0)) && !fixed) || +	new_dev) { +	num_eth++;                         /* New device */ +	dev = insert_device(dev, iobase, ewrk3_probe); +    } +     +    return dev; +} + +/* +** If at end of eth device list and can't use current entry, malloc +** one up. If memory could not be allocated, print an error message. +*/ +static struct device * +insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)) +{ +    struct device *new; + +    new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL); +    if (new == NULL) { +	printk("eth%d: Device not initialised, insufficient memory\n",num_eth); +	return NULL; +    } else { +	new->next = dev->next; +	dev->next = new; +	dev = dev->next;               /* point to the new device */ +	dev->name = (char *)(dev + 1); +	if (num_eth > 9999) { +	    sprintf(dev->name,"eth????");/* New device name */ +	} else { +	    sprintf(dev->name,"eth%d", num_eth);/* New device name */ +	} +	dev->base_addr = iobase;       /* assign the io address */ +	dev->init = init;              /* initialisation routine */ +    } + +    return dev; +} + +static int +ewrk3_dev_index(char *s) +{ +    int i=0, j=0; + +    for (;*s; s++) { +	if (isdigit(*s)) { +	    j=1; +	    i = (i * 10) + (*s - '0'); +	} else if (j) break; +    } + +    return i; +} + +/* +** Read the EWRK3 EEPROM using this routine +*/ +static int Read_EEPROM(u_long iobase, u_char eaddr) +{ +  int i; + +  outb((eaddr & 0x3f), EWRK3_PIR1);     /* set up 6 bits of address info */ +  outb(EEPROM_RD, EWRK3_IOPR);          /* issue read command */ +  for (i=0;i<5000;i++) inb(EWRK3_CSR);  /* wait 1msec */ + +  return inw(EWRK3_EPROM1);             /* 16 bits data return */ +} + +/* +** Write the EWRK3 EEPROM using this routine +*/ +static int Write_EEPROM(short data, u_long iobase, u_char eaddr) +{ +  int i; + +  outb(EEPROM_WR_EN, EWRK3_IOPR);       /* issue write enable command */ +  for (i=0;i<5000;i++) inb(EWRK3_CSR);  /* wait 1msec */ +  outw(data, EWRK3_EPROM1);             /* write data to register */ +  outb((eaddr & 0x3f), EWRK3_PIR1);     /* set up 6 bits of address info */ +  outb(EEPROM_WR, EWRK3_IOPR);          /* issue write command */ +  for (i=0;i<75000;i++) inb(EWRK3_CSR); /* wait 15msec */ +  outb(EEPROM_WR_DIS, EWRK3_IOPR);      /* issue write disable command */ +  for (i=0;i<5000;i++) inb(EWRK3_CSR);  /* wait 1msec */ + +  return 0; +} + +/* +** Look for a particular board name in the on-board EEPROM. +*/ +static void EthwrkSignature(char *name, char *eeprom_image) +{ +  u_long i,j,k; +  char *signatures[] = EWRK3_SIGNATURE; + +  strcpy(name, ""); +  for (i=0;*signatures[i] != '\0' && *name == '\0';i++) { +    for (j=EEPROM_PNAME7,k=0;j<=EEPROM_PNAME0 && k<strlen(signatures[i]);j++) { +      if (signatures[i][k] == eeprom_image[j]) {          /* track signature */ +	k++; +      } else {                         /* lost signature; begin search again */ +	k=0; +      } +    } +    if (k == strlen(signatures[i])) { +      for (k=0; k<EWRK3_STRLEN; k++) { +	name[k] = eeprom_image[EEPROM_PNAME7 + k]; +	name[EWRK3_STRLEN] = '\0'; +      } +    } +  } + +  return;                                   /* return the device name string */ +} + +/* +** Look for a special sequence in the Ethernet station address PROM that +** is common across all EWRK3 products. +**  +** Search the Ethernet address ROM for the signature. Since the ROM address +** counter can start at an arbitrary point, the search must include the entire +** probe sequence length plus the (length_of_the_signature - 1). +** Stop the search IMMEDIATELY after the signature is found so that the +** PROM address counter is correctly positioned at the start of the +** ethernet address for later read out. +*/ + +static int DevicePresent(u_long iobase) +{ +  union { +    struct { +      u32 a; +      u32 b; +    } llsig; +    char Sig[sizeof(u32) << 1]; +  } dev; +  short sigLength; +  char data; +  int i, j, status = 0; + +  dev.llsig.a = ETH_PROM_SIG; +  dev.llsig.b = ETH_PROM_SIG; +  sigLength = sizeof(u32) << 1; + +  for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) { +    data = inb(EWRK3_APROM); +    if (dev.Sig[j] == data) {   /* track signature */ +      j++; +    } else {                    /* lost signature; begin search again */ +      if (data == dev.Sig[0]) { +	j=1; +      } else { +	j=0; +      } +    } +  } + +  if (j!=sigLength) { +    status = -ENODEV;           /* search failed */ +  } + +  return status; +} + +static u_char get_hw_addr(struct device *dev, u_char *eeprom_image, char chipType) +{ +  int i, j, k; +  u_short chksum; +  u_char crc, lfsr, sd, status = 0; +  u_long iobase = dev->base_addr; +  u16 tmp; + +  if (chipType == LeMAC2) { +    for (crc=0x6a, j=0; j<ETH_ALEN; j++) { +      sd = dev->dev_addr[j] = eeprom_image[EEPROM_PADDR0 + j]; +      outb(dev->dev_addr[j], EWRK3_PAR0 + j); +      for (k=0; k<8; k++, sd >>= 1) { +	lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7; +	crc = (crc >> 1) + lfsr; +      } +    } +    if (crc != eeprom_image[EEPROM_PA_CRC]) status = -1; +  } else { +    for (i=0,k=0;i<ETH_ALEN;) { +      k <<= 1 ; +      if (k > 0xffff) k-=0xffff; + +      k += (u_char) (tmp = inb(EWRK3_APROM)); +      dev->dev_addr[i] = (u_char) tmp; +      outb(dev->dev_addr[i], EWRK3_PAR0 + i); +      i++; +      k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8); +      dev->dev_addr[i] = (u_char) tmp; +      outb(dev->dev_addr[i], EWRK3_PAR0 + i); +      i++; + +      if (k > 0xffff) k-=0xffff; +    } +    if (k == 0xffff) k=0; +    chksum = inb(EWRK3_APROM); +    chksum |= (inb(EWRK3_APROM)<<8); +    if (k != chksum) status = -1; +  } + +  return status; +} + +/* +** Look for a particular board name in the EISA configuration space +*/ +static int EISA_signature(char *name, s32 eisa_id) +{ +  u_long i; +  char *signatures[] = EWRK3_SIGNATURE; +  char ManCode[EWRK3_STRLEN]; +  union { +    s32 ID; +    char Id[4]; +  } Eisa; +  int status = 0; + +  *name = '\0'; +  for (i=0; i<4; i++) { +    Eisa.Id[i] = inb(eisa_id + i); +  } + +  ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40); +  ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40); +  ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30); +  ManCode[3]=((Eisa.Id[2]&0x0f)+0x30); +  ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30); +  ManCode[5]='\0'; + +  for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) { +    if (strstr(ManCode, signatures[i]) != NULL) { +      strcpy(name,ManCode); +      status = 1; +    } +  } + +  return status;                           /* return the device name string */ +} + +/* +** Perform IOCTL call functions here. Some are privileged operations and the +** effective uid is checked in those cases. +*/ +static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ +  struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv; +  struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_data; +  u_long iobase = dev->base_addr; +  int i, j, status = 0; +  u_char csr; +  union { +    u_char addr[HASH_TABLE_LEN * ETH_ALEN]; +    u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1]; +  } tmp; + +  switch(ioc->cmd) { +  case EWRK3_GET_HWADDR:             /* Get the hardware address */ +    for (i=0; i<ETH_ALEN; i++) { +      tmp.addr[i] = dev->dev_addr[i]; +    } +    ioc->len = ETH_ALEN; +    if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { +      memcpy_tofs(ioc->data, tmp.addr, ioc->len); +    } + +    break; +  case EWRK3_SET_HWADDR:             /* Set the hardware address */ +    if (suser()) { +      if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) { +	csr = inb(EWRK3_CSR); +	csr |= (CSR_TXD|CSR_RXD); +	outb(csr, EWRK3_CSR);                  /* Disable the TX and RX */ + +	memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN); +	for (i=0; i<ETH_ALEN; i++) { +	  dev->dev_addr[i] = tmp.addr[i]; +	  outb(tmp.addr[i], EWRK3_PAR0 + i); +	} + +	csr &= ~(CSR_TXD|CSR_RXD);             /* Enable the TX and RX */ +	outb(csr, EWRK3_CSR); +      } +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_SET_PROM:               /* Set Promiscuous Mode */ +    if (suser()) { +      csr = inb(EWRK3_CSR); +      csr |= CSR_PME; +      csr &= ~CSR_MCE; +      outb(csr, EWRK3_CSR); +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_CLR_PROM:               /* Clear Promiscuous Mode */ +    if (suser()) { +      csr = inb(EWRK3_CSR); +      csr &= ~CSR_PME; +      outb(csr, EWRK3_CSR); +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_SAY_BOO:                /* Say "Boo!" to the kernel log file */ +    printk("%s: Boo!\n", dev->name); + +    break; +  case EWRK3_GET_MCA:                /* Get the multicast address table */ +    if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { +      while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */ +      if (lp->shmem_length == IO_ONLY) { +	outb(0, EWRK3_IOPR); +	outw(PAGE0_HTE, EWRK3_PIR1); +	for (i=0; i<(HASH_TABLE_LEN >> 3); i++) { +	  tmp.addr[i] = inb(EWRK3_DATA); +	} +      } else { +	outb(0, EWRK3_MPR); +	memcpy_fromio(tmp.addr, (char *)(lp->shmem_base + PAGE0_HTE), (HASH_TABLE_LEN >> 3)); +      } +      ioc->len = (HASH_TABLE_LEN >> 3); +      memcpy_tofs(ioc->data, tmp.addr, ioc->len);  +    } +    lp->lock = 0;                               /* Unlock the page register */ + +    break; +  case EWRK3_SET_MCA:                /* Set a multicast address */ +    if (suser()) { +      if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) { +	memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len); +	set_multicast_list(dev); +      } +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_CLR_MCA:                /* Clear all multicast addresses */ +    if (suser()) { +      set_multicast_list(dev); +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_MCA_EN:                 /* Enable multicast addressing */ +    if (suser()) { +      csr = inb(EWRK3_CSR); +      csr |= CSR_MCE; +      csr &= ~CSR_PME; +      outb(csr, EWRK3_CSR); +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_GET_STATS:              /* Get the driver statistics */ +    cli(); +    ioc->len = sizeof(lp->pktStats); +    if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { +      memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);  +    } +    sti(); + +    break; +  case EWRK3_CLR_STATS:              /* Zero out the driver statistics */ +    if (suser()) { +      cli(); +      memset(&lp->pktStats, 0, sizeof(lp->pktStats)); +      sti(); +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_GET_CSR:                /* Get the CSR Register contents */ +    tmp.addr[0] = inb(EWRK3_CSR); +    ioc->len = 1; +    if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { +      memcpy_tofs(ioc->data, tmp.addr, ioc->len); +    } + +    break; +  case EWRK3_SET_CSR:                /* Set the CSR Register contents */ +    if (suser()) { +      if (!(status=verify_area(VERIFY_READ, ioc->data, 1))) { +	memcpy_fromfs(tmp.addr, ioc->data, 1); +	outb(tmp.addr[0], EWRK3_CSR); +      } +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_GET_EEPROM:             /* Get the EEPROM contents */ +    if (suser()) { +      for (i=0; i<(EEPROM_MAX>>1); i++) { +	tmp.val[i] = (short)Read_EEPROM(iobase, i); +      } +      i = EEPROM_MAX; +      tmp.addr[i++] = inb(EWRK3_CMR);            /* Config/Management Reg. */ +      for (j=0;j<ETH_ALEN;j++) { +	tmp.addr[i++] = inb(EWRK3_PAR0 + j); +      } +      ioc->len = EEPROM_MAX + 1 + ETH_ALEN; +      if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { +	memcpy_tofs(ioc->data, tmp.addr, ioc->len); +      } +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_SET_EEPROM:             /* Set the EEPROM contents */ +    if (suser()) { +      if (!(status=verify_area(VERIFY_READ, ioc->data, EEPROM_MAX))) { +	memcpy_fromfs(tmp.addr, ioc->data, EEPROM_MAX); +	for (i=0; i<(EEPROM_MAX>>1); i++) { +	  Write_EEPROM(tmp.val[i], iobase, i); +	} +      } +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_GET_CMR:                /* Get the CMR Register contents */ +    tmp.addr[0] = inb(EWRK3_CMR); +    ioc->len = 1; +    if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) { +      memcpy_tofs(ioc->data, tmp.addr, ioc->len); +    } + +    break; +  case EWRK3_SET_TX_CUT_THRU:        /* Set TX cut through mode */ +    if (suser()) { +      lp->txc = 1; +    } else { +      status = -EPERM; +    } + +    break; +  case EWRK3_CLR_TX_CUT_THRU:        /* Clear TX cut through mode */ +    if (suser()) { +      lp->txc = 0; +    } else { +      status = -EPERM; +    } + +    break; +  default: +    status = -EOPNOTSUPP; +  } + +  return status; +} + +#ifdef MODULE +static char devicename[9] = { 0, }; +static struct device thisEthwrk = { +  devicename, /* device name is inserted by /linux/drivers/net/net_init.c */ +  0, 0, 0, 0, +  0x300, 5,   /* I/O address, IRQ */ +  0, 0, 0, NULL, ewrk3_probe }; +	 +static int io=0x300;	/* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */ +static int irq=5;	/* or use the insmod io= irq= options 		*/ + +int +init_module(void) +{ +  thisEthwrk.base_addr=io; +  thisEthwrk.irq=irq; +  if (register_netdev(&thisEthwrk) != 0) +    return -EIO; +  return 0; +} + +void +cleanup_module(void) +{ +  if (thisEthwrk.priv) { +    kfree(thisEthwrk.priv); +    thisEthwrk.priv = NULL; +  } +  thisEthwrk.irq = 0; + +  unregister_netdev(&thisEthwrk); +  release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE); +} +#endif /* MODULE */ + + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c ewrk3.c" + * + *  compile-command: "gcc -D__KERNEL__ -DMODULE -I/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -fno-strength-reduce -malign-loops=2 -malign-jumps=2 -malign-functions=2 -O2 -m486 -c ewrk3.c" + * End: + */ + diff --git a/linux/src/drivers/net/ewrk3.h b/linux/src/drivers/net/ewrk3.h new file mode 100644 index 0000000..554a18a --- /dev/null +++ b/linux/src/drivers/net/ewrk3.h @@ -0,0 +1,322 @@ +/* +    Written 1994 by David C. Davies. + +    Copyright 1994 Digital Equipment Corporation. + +    This software may be used and distributed according to  the terms of the +    GNU Public License, incorporated herein by reference. + +    The author may    be  reached as davies@wanton.lkg.dec.com  or   Digital +    Equipment Corporation, 550 King Street, Littleton MA 01460. + +    ========================================================================= +*/ + +/* +** I/O Address Register Map +*/ +#define EWRK3_CSR    iobase+0x00   /* Control and Status Register */ +#define EWRK3_CR     iobase+0x01   /* Control Register */ +#define EWRK3_ICR    iobase+0x02   /* Interrupt Control Register */ +#define EWRK3_TSR    iobase+0x03   /* Transmit Status Register */ +#define EWRK3_RSVD1  iobase+0x04   /* RESERVED */ +#define EWRK3_RSVD2  iobase+0x05   /* RESERVED */ +#define EWRK3_FMQ    iobase+0x06   /* Free Memory Queue */ +#define EWRK3_FMQC   iobase+0x07   /* Free Memory Queue Counter */ +#define EWRK3_RQ     iobase+0x08   /* Receive Queue */ +#define EWRK3_RQC    iobase+0x09   /* Receive Queue Counter */ +#define EWRK3_TQ     iobase+0x0a   /* Transmit Queue */ +#define EWRK3_TQC    iobase+0x0b   /* Transmit Queue Counter */ +#define EWRK3_TDQ    iobase+0x0c   /* Transmit Done Queue */ +#define EWRK3_TDQC   iobase+0x0d   /* Transmit Done Queue Counter */ +#define EWRK3_PIR1   iobase+0x0e   /* Page Index Register 1 */ +#define EWRK3_PIR2   iobase+0x0f   /* Page Index Register 2 */ +#define EWRK3_DATA   iobase+0x10   /* Data Register */ +#define EWRK3_IOPR   iobase+0x11   /* I/O Page Register */ +#define EWRK3_IOBR   iobase+0x12   /* I/O Base Register */ +#define EWRK3_MPR    iobase+0x13   /* Memory Page Register */ +#define EWRK3_MBR    iobase+0x14   /* Memory Base Register */ +#define EWRK3_APROM  iobase+0x15   /* Address PROM */ +#define EWRK3_EPROM1 iobase+0x16   /* EEPROM Data Register 1 */ +#define EWRK3_EPROM2 iobase+0x17   /* EEPROM Data Register 2 */ +#define EWRK3_PAR0   iobase+0x18   /* Physical Address Register 0 */ +#define EWRK3_PAR1   iobase+0x19   /* Physical Address Register 1 */ +#define EWRK3_PAR2   iobase+0x1a   /* Physical Address Register 2 */ +#define EWRK3_PAR3   iobase+0x1b   /* Physical Address Register 3 */ +#define EWRK3_PAR4   iobase+0x1c   /* Physical Address Register 4 */ +#define EWRK3_PAR5   iobase+0x1d   /* Physical Address Register 5 */ +#define EWRK3_CMR    iobase+0x1e   /* Configuration/Management Register */ + +/* +** Control Page Map +*/ +#define PAGE0_FMQ     0x000         /* Free Memory Queue */ +#define PAGE0_RQ      0x080         /* Receive Queue */ +#define PAGE0_TQ      0x100         /* Transmit Queue */ +#define PAGE0_TDQ     0x180         /* Transmit Done Queue */ +#define PAGE0_HTE     0x200         /* Hash Table Entries */ +#define PAGE0_RSVD    0x240         /* RESERVED */ +#define PAGE0_USRD    0x600         /* User Data */ + +/* +** Control and Status Register bit definitions (EWRK3_CSR) +*/ +#define CSR_RA		0x80	    /* Runt Accept */ +#define CSR_PME		0x40	    /* Promiscuous Mode Enable */ +#define CSR_MCE		0x20	    /* Multicast Enable */  +#define CSR_TNE		0x08	    /* TX Done Queue Not Empty */ +#define CSR_RNE		0x04	    /* RX Queue Not Empty */ +#define CSR_TXD		0x02	    /* TX Disable */ +#define CSR_RXD		0x01	    /* RX Disable */ + +/* +** Control Register bit definitions (EWRK3_CR) +*/ +#define CR_APD		0x80	/* Auto Port Disable */ +#define CR_PSEL		0x40	/* Port Select (0->TP port) */ +#define CR_LBCK		0x20	/* LoopBaCK enable */ +#define CR_FDUP		0x10	/* Full DUPlex enable */ +#define CR_FBUS		0x08	/* Fast BUS enable (ISA clk > 8.33MHz) */ +#define CR_EN_16	0x04	/* ENable 16 bit memory accesses */ +#define CR_LED		0x02	/* LED (1-> turn on) */ + +/* +** Interrupt Control Register bit definitions (EWRK3_ICR) +*/ +#define ICR_IE		0x80	/* Interrupt Enable */ +#define ICR_IS		0x60	/* Interrupt Selected */ +#define ICR_TNEM	0x08	/* TNE Mask (0->mask) */ +#define ICR_RNEM	0x04	/* RNE Mask (0->mask) */ +#define ICR_TXDM	0x02	/* TXD Mask (0->mask) */ +#define ICR_RXDM	0x01	/* RXD Mask (0->mask) */ + +/* +** Transmit Status Register bit definitions (EWRK3_TSR) +*/ +#define TSR_NCL		0x80	/* No Carrier Loopback */ +#define TSR_ID		0x40	/* Initially Deferred */ +#define TSR_LCL		0x20	/* Late CoLlision */ +#define TSR_ECL		0x10	/* Excessive CoLlisions */ +#define TSR_RCNTR	0x0f	/* Retries CouNTeR */ + +/* +** I/O Page Register bit definitions (EWRK3_IOPR) +*/ +#define EEPROM_INIT	0xc0	/* EEPROM INIT command */ +#define EEPROM_WR_EN	0xc8	/* EEPROM WRITE ENABLE command */ +#define EEPROM_WR	0xd0	/* EEPROM WRITE command */ +#define EEPROM_WR_DIS	0xd8	/* EEPROM WRITE DISABLE command */ +#define EEPROM_RD	0xe0	/* EEPROM READ command */ + +/* +** I/O Base Register bit definitions (EWRK3_IOBR) +*/ +#define EISA_REGS_EN	0x20	/* Enable EISA ID and Control Registers */ +#define EISA_IOB        0x1f	/* Compare bits for I/O Base Address */ + +/* +** I/O Configuration/Management Register bit definitions (EWRK3_CMR) +*/ +#define CMR_RA          0x80    /* Read Ahead */ +#define CMR_WB          0x40    /* Write Behind */ +#define CMR_LINK        0x20	/* 0->TP */ +#define CMR_POLARITY    0x10	/* Informational */ +#define CMR_NO_EEPROM	0x0c	/* NO_EEPROM<1:0> pin status */ +#define CMR_HS          0x08	/* Hard Strapped pin status (LeMAC2) */ +#define CMR_PNP         0x04    /* Plug 'n Play */ +#define CMR_DRAM        0x02	/* 0-> 1DRAM, 1-> 2 DRAM on board */ +#define CMR_0WS         0x01    /* Zero Wait State */ + +/*  +** MAC Receive Status Register bit definitions +*/ + +#define R_ROK     	0x80 	/* Receive OK summary */ +#define R_IAM     	0x10 	/* Individual Address Match */ +#define R_MCM     	0x08 	/* MultiCast Match */ +#define R_DBE     	0x04 	/* Dribble Bit Error */ +#define R_CRC     	0x02 	/* CRC error */ +#define R_PLL     	0x01 	/* Phase Lock Lost */ + +/*  +** MAC Transmit Control Register bit definitions +*/ + +#define TCR_SQEE    	0x40 	/* SQE Enable - look for heartbeat  */ +#define TCR_SED     	0x20 	/* Stop when Error Detected */ +#define TCR_QMODE     	0x10 	/* Q_MODE */ +#define TCR_LAB         0x08 	/* Less Aggressive Backoff */ +#define TCR_PAD     	0x04 	/* PAD Runt Packets */ +#define TCR_IFC     	0x02 	/* Insert Frame Check */ +#define TCR_ISA     	0x01 	/* Insert Source Address */ + +/*  +** MAC Transmit Status Register bit definitions +*/ + +#define T_VSTS    	0x80 	/* Valid STatuS */ +#define T_CTU     	0x40 	/* Cut Through Used */ +#define T_SQE     	0x20 	/* Signal Quality Error */ +#define T_NCL     	0x10 	/* No Carrier Loopback */ +#define T_LCL           0x08 	/* Late Collision */ +#define T_ID      	0x04 	/* Initially Deferred */ +#define T_COLL     	0x03 	/* COLLision status */ +#define T_XCOLL         0x03    /* Excessive Collisions */ +#define T_MCOLL         0x02    /* Multiple Collisions */ +#define T_OCOLL         0x01    /* One Collision */ +#define T_NOCOLL        0x00    /* No Collisions */ +#define T_XUR           0x03    /* Excessive Underruns */ +#define T_TXE           0x7f    /* TX Errors */ + +/*  +** EISA Configuration Register bit definitions  +*/ + +#define EISA_ID       iobase + 0x0c80  /* EISA ID Registers */  +#define EISA_ID0      iobase + 0x0c80  /* EISA ID Register 0 */  +#define EISA_ID1      iobase + 0x0c81  /* EISA ID Register 1 */  +#define EISA_ID2      iobase + 0x0c82  /* EISA ID Register 2 */  +#define EISA_ID3      iobase + 0x0c83  /* EISA ID Register 3 */  +#define EISA_CR       iobase + 0x0c84  /* EISA Control Register */ + +/* +** EEPROM BYTES +*/ +#define EEPROM_MEMB     0x00 +#define EEPROM_IOB      0x01 +#define EEPROM_EISA_ID0 0x02 +#define EEPROM_EISA_ID1 0x03 +#define EEPROM_EISA_ID2 0x04 +#define EEPROM_EISA_ID3 0x05 +#define EEPROM_MISC0    0x06 +#define EEPROM_MISC1    0x07 +#define EEPROM_PNAME7   0x08 +#define EEPROM_PNAME6   0x09 +#define EEPROM_PNAME5   0x0a +#define EEPROM_PNAME4   0x0b +#define EEPROM_PNAME3   0x0c +#define EEPROM_PNAME2   0x0d +#define EEPROM_PNAME1   0x0e +#define EEPROM_PNAME0   0x0f +#define EEPROM_SWFLAGS  0x10 +#define EEPROM_HWCAT    0x11 +#define EEPROM_NETMAN2  0x12 +#define EEPROM_REVLVL   0x13 +#define EEPROM_NETMAN0  0x14 +#define EEPROM_NETMAN1  0x15 +#define EEPROM_CHIPVER  0x16 +#define EEPROM_SETUP    0x17 +#define EEPROM_PADDR0   0x18 +#define EEPROM_PADDR1   0x19 +#define EEPROM_PADDR2   0x1a +#define EEPROM_PADDR3   0x1b +#define EEPROM_PADDR4   0x1c +#define EEPROM_PADDR5   0x1d +#define EEPROM_PA_CRC   0x1e +#define EEPROM_CHKSUM   0x1f + +/* +** EEPROM bytes for checksumming +*/ +#define EEPROM_MAX      32             /* bytes */ + +/* +** EEPROM MISCELLANEOUS FLAGS +*/ +#define RBE_SHADOW	0x0100	/* Remote Boot Enable Shadow */  +#define READ_AHEAD      0x0080  /* Read Ahead feature */ +#define IRQ_SEL2        0x0070  /* IRQ line selection (LeMAC2) */ +#define IRQ_SEL         0x0060  /* IRQ line selection */ +#define FAST_BUS        0x0008  /* ISA Bus speeds > 8.33MHz */ +#define ENA_16          0x0004  /* Enables 16 bit memory transfers */ +#define WRITE_BEHIND    0x0002  /* Write Behind feature */ +#define _0WS_ENA        0x0001  /* Zero Wait State Enable */ + +/* +** EEPROM NETWORK MANAGEMENT FLAGS +*/ +#define NETMAN_POL      0x04    /* Polarity defeat */ +#define NETMAN_LINK     0x02    /* Link defeat */ +#define NETMAN_CCE      0x01    /* Custom Counters Enable */ + +/* +** EEPROM SW FLAGS +*/ +#define SW_SQE		0x10	/* Signal Quality Error */  +#define SW_LAB		0x08	/* Less Aggressive Backoff */ +#define SW_INIT		0x04	/* Initialized */ +#define SW_TIMEOUT     	0x02	/* 0:2.5 mins, 1: 30 secs */ +#define SW_REMOTE      	0x01    /* Remote Boot Enable -> 1 */ + +/* +** EEPROM SETUP FLAGS +*/ +#define SETUP_APD	0x80	/* AutoPort Disable */ +#define SETUP_PS	0x40	/* Port Select */ +#define SETUP_MP	0x20	/* MultiPort */ +#define SETUP_1TP	0x10	/* 1 port, TP */ +#define SETUP_1COAX	0x00	/* 1 port, Coax */ +#define SETUP_DRAM	0x02	/* Number of DRAMS on board */ + +/* +** EEPROM MANAGEMENT FLAGS +*/ +#define MGMT_CCE	0x01	/* Custom Counters Enable */ + +/* +** EEPROM VERSIONS +*/ +#define LeMAC           0x11 +#define LeMAC2          0x12 + +/* +** Miscellaneous +*/ + +#define EEPROM_WAIT_TIME 1000    /* Number of microseconds */ +#define EISA_EN         0x0001   /* Enable EISA bus buffers */ + +#define HASH_TABLE_LEN   512     /* Bits */ + +#define XCT 0x80                 /* Transmit Cut Through */ +#define PRELOAD 16               /* 4 long words */ + +#define MASK_INTERRUPTS   1 +#define UNMASK_INTERRUPTS 0 + +#define EEPROM_OFFSET(a) ((u_short)((u_long)(a))) + +/* +** Include the IOCTL stuff +*/ +#include <linux/sockios.h> + +#define	EWRK3IOCTL	SIOCDEVPRIVATE + +struct ewrk3_ioctl { +	unsigned short cmd;                /* Command to run */ +	unsigned short len;                /* Length of the data buffer */ +	unsigned char  *data;              /* Pointer to the data buffer */ +}; + +/*  +** Recognised commands for the driver  +*/ +#define EWRK3_GET_HWADDR	0x01 /* Get the hardware address */ +#define EWRK3_SET_HWADDR	0x02 /* Get the hardware address */ +#define EWRK3_SET_PROM  	0x03 /* Set Promiscuous Mode */ +#define EWRK3_CLR_PROM  	0x04 /* Clear Promiscuous Mode */ +#define EWRK3_SAY_BOO	        0x05 /* Say "Boo!" to the kernel log file */ +#define EWRK3_GET_MCA   	0x06 /* Get a multicast address */ +#define EWRK3_SET_MCA   	0x07 /* Set a multicast address */ +#define EWRK3_CLR_MCA    	0x08 /* Clear a multicast address */ +#define EWRK3_MCA_EN    	0x09 /* Enable a multicast address group */ +#define EWRK3_GET_STATS  	0x0a /* Get the driver statistics */ +#define EWRK3_CLR_STATS 	0x0b /* Zero out the driver statistics */ +#define EWRK3_GET_CSR   	0x0c /* Get the CSR Register contents */ +#define EWRK3_SET_CSR   	0x0d /* Set the CSR Register contents */ +#define EWRK3_GET_EEPROM   	0x0e /* Get the EEPROM contents */ +#define EWRK3_SET_EEPROM	0x0f /* Set the EEPROM contents */ +#define EWRK3_GET_CMR   	0x10 /* Get the CMR Register contents */ +#define EWRK3_CLR_TX_CUT_THRU  	0x11 /* Clear the TX cut through mode */ +#define EWRK3_SET_TX_CUT_THRU	0x12 /* Set the TX cut through mode */ diff --git a/linux/src/drivers/net/fmv18x.c b/linux/src/drivers/net/fmv18x.c new file mode 100644 index 0000000..b29ddf0 --- /dev/null +++ b/linux/src/drivers/net/fmv18x.c @@ -0,0 +1,664 @@ +/* fmv18x.c: A network device driver for the Fujitsu FMV-181/182/183/184. + +	Original: at1700.c (1993-94 by Donald Becker). +		Copyright 1993 United States Government as represented by the +		Director, National Security Agency. +		The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +		Center of Excellence in Space Data and Information Sciences +		   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	Modified by Yutaka TAMIYA (tamy@flab.fujitsu.co.jp) +		Copyright 1994 Fujitsu Laboratories Ltd. +	Special thanks to: +		Masayoshi UTAKA (utaka@ace.yk.fujitsu.co.jp) +			for testing this driver. +		H. NEGISHI (agy, negishi@sun45.psd.cs.fujitsu.co.jp) +			for suggestion of some program modification. +		Masahiro SEKIGUCHI <seki@sysrap.cs.fujitsu.co.jp> +			for suggestion of some program modification. +		Kazutoshi MORIOKA (morioka@aurora.oaks.cs.fujitsu.co.jp) +			for testing this driver. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	This is a device driver for the Fujitsu FMV-181/182/183/184, which +	is a straight-forward Fujitsu MB86965 implementation. + +  Sources: +    at1700.c +    The Fujitsu MB86965 datasheet. +    The Fujitsu FMV-181/182 user's guide +*/ + +static const char *version = +	"fmv18x.c:v1.3.71e 03/04/96  Yutaka TAMIYA (tamy@flab.fujitsu.co.jp)\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/delay.h> + +static int fmv18x_probe_list[] = +{0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x300, 0x340, 0}; + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 1 +#endif +static unsigned int net_debug = NET_DEBUG; + +typedef unsigned char uchar; + +/* Information that need to be kept for each board. */ +struct net_local { +	struct enet_statistics stats; +	long open_time;				/* Useless example local info. */ +	uint tx_started:1;			/* Number of packet on the Tx queue. */ +	uchar tx_queue;				/* Number of packet on the Tx queue. */ +	ushort tx_queue_len;		/* Current length of the Tx queue. */ +}; + + +/* Offsets from the base address. */ +#define STATUS			0 +#define TX_STATUS		0 +#define RX_STATUS		1 +#define TX_INTR			2		/* Bit-mapped interrupt enable registers. */ +#define RX_INTR			3 +#define TX_MODE			4 +#define RX_MODE			5 +#define CONFIG_0		6		/* Misc. configuration settings. */ +#define CONFIG_1		7 +/* Run-time register bank 2 definitions. */ +#define DATAPORT		8		/* Word-wide DMA or programmed-I/O dataport. */ +#define TX_START		10 +#define COL16CNTL		11 +#define MODE13			13 +/* Fujitsu FMV-18x Card Configuration */ +#define	FJ_STATUS0		0x10 +#define	FJ_STATUS1		0x11 +#define	FJ_CONFIG0		0x12 +#define	FJ_CONFIG1		0x13 +#define	FJ_MACADDR		0x14	/* 0x14 - 0x19 */ +#define	FJ_BUFCNTL		0x1A +#define	FJ_BUFDATA		0x1C +#define FMV18X_IO_EXTENT	32 + +/* Index to functions, as function prototypes. */ + +extern int fmv18x_probe(struct device *dev); + +static int fmv18x_probe1(struct device *dev, short ioaddr); +static int net_open(struct device *dev); +static int	net_send_packet(struct sk_buff *skb, struct device *dev); +static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void net_rx(struct device *dev); +static int net_close(struct device *dev); +static struct enet_statistics *net_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + + +/* Check for a network adaptor of this type, and return '0' iff one exists. +   If dev->base_addr == 0, probe all likely locations. +   If dev->base_addr == 1, always return failure. +   If dev->base_addr == 2, allocate space for the device and return success +   (detachable devices only). +   */ +#ifdef HAVE_DEVLIST +/* Support for a alternate probe manager, which will eliminate the +   boilerplate below. */ +struct netdev_entry fmv18x_drv = +{"fmv18x", fmv18x_probe1, FMV18X_IO_EXTENT, fmv18x_probe_list}; +#else +int +fmv18x_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return fmv18x_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; fmv18x_probe_list[i]; i++) { +		int ioaddr = fmv18x_probe_list[i]; + 		if (check_region(ioaddr, FMV18X_IO_EXTENT)) +			continue; +		if (fmv18x_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +/* The Fujitsu datasheet suggests that the NIC be probed for by checking its +   "signature", the default bit pattern after a reset.  This *doesn't* work -- +   there is no way to reset the bus interface without a complete power-cycle! + +   It turns out that ATI came to the same conclusion I did: the only thing +   that can be done is checking a few bits and then diving right into MAC +   address check. */ + +int fmv18x_probe1(struct device *dev, short ioaddr) +{ +	char irqmap[4] = {3, 7, 10, 15}; +	unsigned int i, irq; + +	/* Resetting the chip doesn't reset the ISA interface, so don't bother. +	   That means we have to be careful with the register values we probe for. +	   */ + +	/* Check I/O address configuration and Fujitsu vendor code */ +	if (fmv18x_probe_list[inb(ioaddr + FJ_CONFIG0) & 0x07] != ioaddr + 	||  inb(ioaddr+FJ_MACADDR  ) != 0x00 +	||  inb(ioaddr+FJ_MACADDR+1) != 0x00 +	||  inb(ioaddr+FJ_MACADDR+2) != 0x0e) +		return -ENODEV; + +	irq = irqmap[(inb(ioaddr + FJ_CONFIG0)>>6) & 0x03]; + +	/* Snarf the interrupt vector now. */ +	if (request_irq(irq, &net_interrupt, 0, "fmv18x", NULL)) { +		printk ("FMV-18x found at %#3x, but it's unusable due to a conflict on" +				"IRQ %d.\n", ioaddr, irq); +		return EAGAIN; +	} + +	/* Allocate a new 'dev' if needed. */ +	if (dev == NULL) +		dev = init_etherdev(0, sizeof(struct net_local)); + +	/* Grab the region so that we can find another board if the IRQ request +	   fails. */ + 	request_region(ioaddr, FMV18X_IO_EXTENT, "fmv18x"); + +	printk("%s: FMV-18x found at %#3x, IRQ %d, address ", dev->name, +		   ioaddr, irq); + +	dev->base_addr = ioaddr; +	dev->irq = irq; +	irq2dev_map[irq] = dev; + +	for(i = 0; i < 6; i++) { +		unsigned char val = inb(ioaddr + FJ_MACADDR + i); +		printk("%02x", val); +		dev->dev_addr[i] = val; +	} + +	/* "FJ_STATUS0" 12 bit 0x0400 means use regular 100 ohm 10baseT signals, +	   rather than 150 ohm shielded twisted pair compensation. +	   0x0000 == auto-sense the interface +	   0x0800 == use TP interface +	   0x1800 == use coax interface +	   */ +	{ +		const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2/5"}; +		ushort setup_value = inb(ioaddr + FJ_STATUS0); + +		switch( setup_value & 0x07 ){ +		case 0x01 /* 10base5 */: +		case 0x02 /* 10base2 */: dev->if_port = 0x18; break; +		case 0x04 /* 10baseT */: dev->if_port = 0x08; break; +		default /* auto-sense*/: dev->if_port = 0x00; break; +		} +		printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]); +	} + +	/* Initialize LAN Controller and LAN Card */ +	outb(0xda, ioaddr + CONFIG_0);	 /* Initialize LAN Controller */ +	outb(0x00, ioaddr + CONFIG_1);	 /* Stand by mode */ +	outb(0x00, ioaddr + FJ_CONFIG1); /* Disable IRQ of LAN Card */ +	outb(0x00, ioaddr + FJ_BUFCNTL); /* Reset ? I'm not sure (TAMIYA) */ + +	/* wait for a while */ +	udelay(200); + +	/* Set the station address in bank zero. */ +	outb(0x00, ioaddr + CONFIG_1); +	for (i = 0; i < 6; i++) +		outb(dev->dev_addr[i], ioaddr + 8 + i); + +	/* Switch to bank 1 and set the multicast table to accept none. */ +	outb(0x04, ioaddr + CONFIG_1); +	for (i = 0; i < 8; i++) +		outb(0x00, ioaddr + 8 + i); + +	/* Switch to bank 2 and lock our I/O address. */ +	outb(0x08, ioaddr + CONFIG_1); +	outb(dev->if_port, ioaddr + MODE13); + +	if (net_debug) +		printk("%s", version); + +	/* Initialize the device structure. */ +	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); +	if (dev->priv == NULL) +		return -ENOMEM; +	memset(dev->priv, 0, sizeof(struct net_local)); + +	dev->open		= net_open; +	dev->stop		= net_close; +	dev->hard_start_xmit = net_send_packet; +	dev->get_stats	= net_get_stats; +	dev->set_multicast_list = &set_multicast_list; + +	/* Fill in the fields of 'dev' with ethernet-generic values. */ +	    +	ether_setup(dev); +	return 0; +} + + +static int net_open(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; + +	/* Set the configuration register 0 to 32K 100ns. byte-wide memory, +	   16 bit bus access, and two 4K Tx, enable the Rx and Tx. */ +	outb(0x5a, ioaddr + CONFIG_0); + +	/* Powerup and switch to register bank 2 for the run-time registers. */ +	outb(0xe8, ioaddr + CONFIG_1); + +	lp->tx_started = 0; +	lp->tx_queue = 0; +	lp->tx_queue_len = 0; + +	/* Clear Tx and Rx Status */ +	outb(0xff, ioaddr + TX_STATUS); +	outb(0xff, ioaddr + RX_STATUS); +	lp->open_time = jiffies; + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; + +	/* Enable the IRQ of the LAN Card */ +	outb(0x80, ioaddr + FJ_CONFIG1); + +	/* Enable both Tx and Rx interrupts */ +	outw(0x8182, ioaddr+TX_INTR); + +	MOD_INC_USE_COUNT; + +	return 0; +} + +static int +net_send_packet(struct sk_buff *skb, struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; + +	if (dev->tbusy) { +		/* If we get here, some higher level has decided we are broken. +		   There should really be a "kick me" function call instead. */ +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < 10) +			return 1; +		printk("%s: transmit timed out with status %04x, %s?\n", dev->name, +			   htons(inw(ioaddr + TX_STATUS)), +			   inb(ioaddr + TX_STATUS) & 0x80 +			   ? "IRQ conflict" : "network cable problem"); +		printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n", +			   dev->name, htons(inw(ioaddr + 0)), +			   htons(inw(ioaddr + 2)), htons(inw(ioaddr + 4)), +			   htons(inw(ioaddr + 6)), htons(inw(ioaddr + 8)), +			   htons(inw(ioaddr +10)), htons(inw(ioaddr +12)), +			   htons(inw(ioaddr +14))); +		printk("eth card: %04x %04x\n", +			htons(inw(ioaddr+FJ_STATUS0)), +			htons(inw(ioaddr+FJ_CONFIG0))); +		lp->stats.tx_errors++; +		/* ToDo: We should try to restart the adaptor... */ +		cli(); + +		/* Initialize LAN Controller and LAN Card */ +		outb(0xda, ioaddr + CONFIG_0);   /* Initialize LAN Controller */ +		outb(0x00, ioaddr + CONFIG_1);   /* Stand by mode */ +		outb(0x00, ioaddr + FJ_CONFIG1); /* Disable IRQ of LAN Card */ +		outb(0x00, ioaddr + FJ_BUFCNTL); /* Reset ? I'm not sure */ +		net_open(dev); + +		sti(); +	} + +	/* If some higher layer thinks we've missed an tx-done interrupt +	   we are passed NULL. Caution: dev_tint() handles the cli()/sti() +	   itself. */ +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) +		printk("%s: Transmitter access conflict.\n", dev->name); +	else { +		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		unsigned char *buf = skb->data; + +		if (length > ETH_FRAME_LEN) { +			if (net_debug) +				printk("%s: Attempting to send a large packet (%d bytes).\n", +					dev->name, length); +			return 1; +		} + +		if (net_debug > 4) +			printk("%s: Transmitting a packet of length %lu.\n", dev->name, +				   (unsigned long)skb->len); + +		/* Disable both interrupts. */ +		outw(0x0000, ioaddr + TX_INTR); +		 +		outw(length, ioaddr + DATAPORT); +		outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); + +		lp->tx_queue++; +		lp->tx_queue_len += length + 2; + +		if (lp->tx_started == 0) { +			/* If the Tx is idle, always trigger a transmit. */ +			outb(0x80 | lp->tx_queue, ioaddr + TX_START); +			lp->tx_queue = 0; +			lp->tx_queue_len = 0; +			dev->trans_start = jiffies; +			lp->tx_started = 1; +			dev->tbusy = 0; +		} else if (lp->tx_queue_len < 4096 - 1502) +			/* Yes, there is room for one more packet. */ +			dev->tbusy = 0; + +		/* Re-enable interrupts */ +		outw(0x8182, ioaddr + TX_INTR); +	} +	dev_kfree_skb (skb, FREE_WRITE); + +	return 0; +} + +/* The typical workload of the driver: +   Handle the network interface interrupts. */ +static void +net_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +	struct device *dev = (struct device *)(irq2dev_map[irq]); +	struct net_local *lp; +	int ioaddr, status; + +	if (dev == NULL) { +		printk ("fmv18x_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +	dev->interrupt = 1; + +	ioaddr = dev->base_addr; +	lp = (struct net_local *)dev->priv; + +	/* Avoid multiple interrupts. */ +	outw(0x0000, ioaddr + TX_INTR); + +        status = inw(ioaddr + TX_STATUS); +	outw(status, ioaddr + TX_STATUS); + +	if (net_debug > 4) +		printk("%s: Interrupt with status %04x.\n", dev->name, status); +	if (status & 0xff00 +		||  (inb(ioaddr + RX_MODE) & 0x40) == 0) {			/* Got a packet(s). */ +		net_rx(dev); +	} +	if (status & 0x00ff) { +		if (status & 0x80) { +			lp->stats.tx_packets++; +			if (lp->tx_queue) { +				outb(0x80 | lp->tx_queue, ioaddr + TX_START); +				lp->tx_queue = 0; +				lp->tx_queue_len = 0; +				dev->trans_start = jiffies; +				dev->tbusy = 0; +				mark_bh(NET_BH);	/* Inform upper layers. */ +			} else { +				lp->tx_started = 0; +				dev->tbusy = 0; +				mark_bh(NET_BH);	/* Inform upper layers. */ +			} +		} +		if (status & 0x02 ) { +			if (net_debug > 4) +				printk("%s: 16 Collision occur during Txing.\n", dev->name); +			/* Retry to send the packet */ +			outb(0x02, ioaddr + COL16CNTL); +		} +	} + +	dev->interrupt = 0; +	outw(0x8182, ioaddr + TX_INTR); +	return; +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +net_rx(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int boguscount = 10;	/* 5 -> 10: by agy 19940922 */ + +	while ((inb(ioaddr + RX_MODE) & 0x40) == 0) { +		/* Clear PKT_RDY bit: by agy 19940922 */ +		/* outb(0x80, ioaddr + RX_STATUS); */ +		ushort status = inw(ioaddr + DATAPORT); + +		if (net_debug > 4) +			printk("%s: Rxing packet mode %02x status %04x.\n", +				   dev->name, inb(ioaddr + RX_MODE), status); +#ifndef final_version +		if (status == 0) { +			outb(0x05, ioaddr + 14); +			break; +		} +#endif + +		if ((status & 0xF0) != 0x20) {	/* There was an error. */ +			lp->stats.rx_errors++; +			if (status & 0x08) lp->stats.rx_length_errors++; +			if (status & 0x04) lp->stats.rx_frame_errors++; +			if (status & 0x02) lp->stats.rx_crc_errors++; +			if (status & 0x01) lp->stats.rx_over_errors++; +		} else { +			ushort pkt_len = inw(ioaddr + DATAPORT); +			/* Malloc up new buffer. */ +			struct sk_buff *skb; + +			if (pkt_len > 1550) { +				printk("%s: The FMV-18x claimed a very large packet, size %d.\n", +					   dev->name, pkt_len); +				outb(0x05, ioaddr + 14); +				lp->stats.rx_errors++; +				break; +			} +			skb = dev_alloc_skb(pkt_len+3); +			if (skb == NULL) { +				printk("%s: Memory squeeze, dropping packet (len %d).\n", +					   dev->name, pkt_len); +				outb(0x05, ioaddr + 14); +				lp->stats.rx_dropped++; +				break; +			} +			skb->dev = dev; +			skb_reserve(skb,2); + +			insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1); + +			if (net_debug > 5) { +				int i; +				printk("%s: Rxed packet of length %d: ", dev->name, pkt_len); +				for (i = 0; i < 14; i++) +					printk(" %02x", skb->data[i]); +				printk(".\n"); +			} + +			skb->protocol=eth_type_trans(skb, dev); +			netif_rx(skb); +			lp->stats.rx_packets++; +		} +		if (--boguscount <= 0) +			break; +	} + +	/* If any worth-while packets have been received, dev_rint() +	   has done a mark_bh(NET_BH) for us and will work on them +	   when we get to the bottom-half routine. */ +	{ +		int i; +		for (i = 0; i < 20; i++) { +			if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40) +				break; +			(void)inw(ioaddr + DATAPORT);				/* dummy status read */ +			outb(0x05, ioaddr + 14); +		} + +		if (net_debug > 5 && i > 0) +			printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",  +				   dev->name, inb(ioaddr + RX_MODE), i); +	} + +	return; +} + +/* The inverse routine to net_open(). */ +static int net_close(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	((struct net_local *)dev->priv)->open_time = 0; + +	dev->tbusy = 1; +	dev->start = 0; + +	/* Set configuration register 0 to disable Tx and Rx. */ +	outb(0xda, ioaddr + CONFIG_0); + +	/* Update the statistics -- ToDo. */ + +	/* Power-down the chip.  Green, green, green! */ +	outb(0x00, ioaddr + CONFIG_1); + +	MOD_DEC_USE_COUNT; + +	/* Set the ethernet adaptor disable IRQ */ +	outb(0x00, ioaddr + FJ_CONFIG1); + +	return 0; +} + +/* Get the current statistics.	This may be called with the card open or +   closed. */ +static struct enet_statistics * +net_get_stats(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; + +	cli(); +	/* ToDo: Update the statistics from the device registers. */ +	sti(); + +	return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. +   num_addrs == -1	Promiscuous mode, receive all packets +   num_addrs == 0	Normal mode, clear multicast list +   num_addrs > 0	Multicast mode, receive normal and MC packets, and do +			best-effort filtering. + */ +static void +set_multicast_list(struct device *dev) +{ +	short ioaddr = dev->base_addr; +	if (dev->mc_count || dev->flags&(IFF_PROMISC|IFF_ALLMULTI))  +	{ +		/* +		 *	We must make the kernel realise we had to move +		 *	into promisc mode or we start all out war on +		 *	the cable. - AC +		 */ +		dev->flags|=IFF_PROMISC;		 +	 +		outb(3, ioaddr + RX_MODE);	/* Enable promiscuous mode */ +	}  +	else +		outb(2, ioaddr + RX_MODE);	/* Disable promiscuous, use normal mode */ +} + +#ifdef MODULE +static char devicename[9] = { 0, }; +static struct device dev_fmv18x = { +	devicename, /* device name is inserted by linux/drivers/net/net_init.c */ +	0, 0, 0, 0, +	0, 0, +	0, 0, 0, NULL, fmv18x_probe }; + +static int io = 0x220; +static int irq = 0; + +int init_module(void) +{ +	if (io == 0) +		printk("fmv18x: You should not use auto-probing with insmod!\n"); +	dev_fmv18x.base_addr = io; +	dev_fmv18x.irq       = irq; +	if (register_netdev(&dev_fmv18x) != 0) { +		printk("fmv18x: register_netdev() returned non-zero.\n"); +		return -EIO; +	} +	return 0; +} + +void +cleanup_module(void) +{ +	unregister_netdev(&dev_fmv18x); +	kfree(dev_fmv18x.priv); +	dev_fmv18x.priv = NULL; + +	/* If we don't do this, we can't re-insmod it later. */ +	free_irq(dev_fmv18x.irq, NULL); +	irq2dev_map[dev_fmv18x.irq] = NULL; +	release_region(dev_fmv18x.base_addr, FMV18X_IO_EXTENT); +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c fmv18x.c" + *  version-control: t + *  kept-new-versions: 5 + *  tab-width: 4 + *  c-indent-level: 4 + * End: + */ diff --git a/linux/src/drivers/net/hamachi.c b/linux/src/drivers/net/hamachi.c new file mode 100644 index 0000000..fdcf43d --- /dev/null +++ b/linux/src/drivers/net/hamachi.c @@ -0,0 +1,1315 @@ +/* hamachi.c: A Packet Engines GNIC-II Gigabit Ethernet driver for Linux. */ +/* +	Written 1998-2002 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	410 Severn Ave., Suite 210 +	Annapolis MD 21403 + +	This driver is for the Packet Engines GNIC-II PCI Gigabit Ethernet +	adapter. + +	Support and updates available at +	http://www.scyld.com/network/hamachi.html +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"hamachi.c:v1.04 11/17/2002  Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/hamachi.html\n"; + +/* Automatically extracted configuration info: +probe-func: hamachi_probe +config-in: tristate 'Packet Engines "Hamachi" PCI Gigabit Ethernet support' CONFIG_HAMACHI +c-help-name: Packet Engines "Hamachi" PCI Gigabit Ethernet support +c-help-symbol: CONFIG_HAMACHI +c-help: This driver is for the Packet Engines "Hamachi" GNIC-2 Gigabit Ethernet +c-help: adapter. +c-help: Usage information and updates are available from +c-help: http://www.scyld.com/network/hamachi.html +*/ + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 40; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   The Hamachi has a 64 element perfect filter.  */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* A override for the hardware detection of bus width. +   Set to 1 to force 32 bit PCI bus detection.  Set to 4 to force 64 bit. +   Add 2 to disable parity detection. +*/ +static int force32 = 0; + +/* Used to pass the media type, etc. +   These exist for driver interoperability. +   Only 1 Gigabit is supported by the chip. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority. +   There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE	64 +#define TX_QUEUE_LEN	60		/* Limit ring entries actually used.  */ +#define RX_RING_SIZE	128 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/unaligned.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. */ +#if ADDRLEN == 64 +#define virt_to_desc(addr)  cpu_to_le64(virt_to_bus(addr)) +#else +#define virt_to_desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) +#endif + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Packet Engines 'Hamachi' GNIC-II Gigabit Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(force32, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, +				 "Non-zero to force full duplex, non-negotiated link " +				 "(unused, deprecated)."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); +MODULE_PARM_DESC(force32, "Set to 1 to force 32 bit PCI bus use."); + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the Packet Engines "Hamachi" +Gigabit Ethernet chip.  The only PCA currently supported is the GNIC-II 64-bit +66Mhz PCI card. + +II. Board-specific settings + +No jumpers exist on the board.  The chip supports software correction of +various motherboard wiring errors, however this driver does not support +that feature. + +III. Driver operation + +IIIa. Ring buffers + +The Hamachi uses a typical descriptor based bus-master architecture. +The descriptor list is similar to that used by the Digital Tulip. +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE. + +This driver uses a zero-copy receive and transmit scheme similar my other +network drivers. +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the Hamachi as receive data +buffers.  When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack and replaced by a newly allocated skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  Gigabit cards are typically used on generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets. + +IIIb/c. Transmit/Receive Structure + +The Rx and Tx descriptor structure are straight-forward, with no historical +baggage that must be explained.  Unlike the awkward DBDMA structure, there +are no unused fields or option bits that had only one allowable setting. + +Two details should be noted about the descriptors: The chip supports both 32 +bit and 64 bit address structures, and the length field is overwritten on +the receive descriptors.  The descriptor length is set in the control word +for each channel. The development driver uses 32 bit addresses only, however +64 bit addresses may be enabled for 64 bit architectures e.g. the Alpha. + +IIId. Synchronization + +This driver is very similar to my other network drivers. +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'hmp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'hmp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +Thanks to Kim Stearns of Packet Engines for providing a pair of GNIC-II boards. + +IVb. References + +Hamachi Engineering Design Specification, 5/15/97 +(Note: This version was marked "Confidential".) + +IVc. Errata + +None noted. +*/ + + +/* The table for PCI detection and activation. */ + +static void *hamachi_probe1(struct pci_dev *pdev, void *init_dev, +							long ioaddr, int irq, int chip_idx, int find_cnt); +enum chip_capability_flags { CanHaveMII=1, }; + +static struct pci_id_info pci_id_tbl[] = { +	{"Packet Engines GNIC-II \"Hamachi\"", { 0x09111318, 0xffffffff,}, +	 PCI_USES_MEM | PCI_USES_MASTER | PCI_ADDR0 | PCI_ADDR_64BITS, 0x400, 0, }, +	{ 0,}, +}; + +struct drv_id_info hamachi_drv_id = { +	"hamachi", 0, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	hamachi_probe1, 0, +}; + +/* Offsets to the Hamachi registers.  Various sizes. */ +enum hamachi_offsets { +	TxDMACtrl=0x00, TxCmd=0x04, TxStatus=0x06, TxPtr=0x08, TxCurPtr=0x10, +	RxDMACtrl=0x20, RxCmd=0x24, RxStatus=0x26, RxPtr=0x28, RxCurPtr=0x30, +	PCIClkMeas=0x060, MiscStatus=0x066, ChipRev=0x68, ChipReset=0x06B, +	LEDCtrl=0x06C, VirtualJumpers=0x06D, +	TxChecksum=0x074, RxChecksum=0x076, +	TxIntrCtrl=0x078, RxIntrCtrl=0x07C, +	InterruptEnable=0x080, InterruptClear=0x084, IntrStatus=0x088, +	EventStatus=0x08C, +	MACCnfg=0x0A0, FrameGap0=0x0A2, FrameGap1=0x0A4, +	/* See enum MII_offsets below. */ +	MACCnfg2=0x0B0, RxDepth=0x0B8, FlowCtrl=0x0BC, MaxFrameSize=0x0CE, +	AddrMode=0x0D0, StationAddr=0x0D2, +	/* Gigabit AutoNegotiation. */ +	ANCtrl=0x0E0, ANStatus=0x0E2, ANXchngCtrl=0x0E4, ANAdvertise=0x0E8, +	ANLinkPartnerAbility=0x0EA, +	EECmdStatus=0x0F0, EEData=0x0F1, EEAddr=0x0F2, +	FIFOcfg=0x0F8, +}; + +/* Offsets to the MII-mode registers. */ +enum MII_offsets { +	MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC, +	MII_Status=0xAE, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrRxDone=0x01, IntrRxPCIFault=0x02, IntrRxPCIErr=0x04, +	IntrTxDone=0x100, IntrTxPCIFault=0x200, IntrTxPCIErr=0x400, +	LinkChange=0x10000, NegotiationChange=0x20000, StatsMax=0x40000, }; + +/* The Hamachi Rx and Tx buffer descriptors. */ +struct hamachi_desc { +	u32 status_n_length; +#if ADDRLEN == 64 +	u32 pad; +	u64 addr; +#else +	u32 addr; +#endif +}; + +/* Bits in hamachi_desc.status */ +enum desc_status_bits { +	DescOwn=0x80000000, DescEndPacket=0x40000000, DescEndRing=0x20000000, +	DescIntr=0x10000000, +}; + +#define PRIV_ALIGN	15 	/* Required alignment mask */ +struct hamachi_private { +	/* Descriptor rings first for alignment.  Tx requires a second descriptor +	   for status. */ +	struct hamachi_desc rx_ring[RX_RING_SIZE]; +	struct hamachi_desc tx_ring[TX_RING_SIZE]; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for skfree(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct net_device *next_module; +	void *priv_addr;					/* Unaligned address for kfree */ +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; + +	/* Frequently used and paired value: keep adjacent for cache effect. */ +	int msg_level; +	int max_interrupt_work; +	long in_interrupt; + +	struct hamachi_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; +	int multicast_filter_limit; +	int rx_mode; + +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	/* MII transceiver section. */ +	int mii_cnt;						/* MII device addresses. */ +	u16 advertising;					/* NWay media advertisement */ +	unsigned char phys[2];				/* MII device addresses. */ +}; + +static int read_eeprom(struct net_device *dev, int location); +static int mdio_read(long ioaddr, int phy_id, int location); +static void mdio_write(long ioaddr, int phy_id, int location, int value); +static int hamachi_open(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#ifdef HAVE_CHANGE_MTU +static int change_mtu(struct net_device *dev, int new_mtu); +#endif +static void hamachi_timer(unsigned long data); +static void hamachi_tx_timeout(struct net_device *dev); +static void hamachi_init_ring(struct net_device *dev); +static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int hamachi_rx(struct net_device *dev); +static void hamachi_error(struct net_device *dev, int intr_status); +static int hamachi_close(struct net_device *dev); +static struct net_device_stats *hamachi_get_stats(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_hamachi_dev = NULL; + +#ifndef MODULE +int hamachi_probe(struct net_device *dev) +{ +	if (pci_drv_register(&hamachi_drv_id, dev) < 0) +		return -ENODEV; +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif + +static void *hamachi_probe1(struct pci_dev *pdev, void *init_dev, +							long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct hamachi_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	printk(KERN_INFO "%s: %s type %x at 0x%lx, ", +		   dev->name, pci_id_tbl[chip_idx].name, (int)readl(ioaddr + ChipRev), +		   ioaddr); + +	for (i = 0; i < 6; i++) +		dev->dev_addr[i] = read_eeprom(dev, 4 + i); +	/* Alternate:  readb(ioaddr + StationAddr + i); */ +	for (i = 0; i < 5; i++) +			printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	i = readb(ioaddr + PCIClkMeas); +	printk(KERN_INFO "%s:  %d-bit %d Mhz PCI bus (%d), Virtual Jumpers " +		   "%2.2x, LPA %4.4x.\n", +		   dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32, +		   i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers), +		   (int)readw(ioaddr + ANLinkPartnerAbility)); + +	/* Hmmm, do we really need to reset the chip???. */ +	writeb(1, ioaddr + ChipReset); + +	/* If the bus size is misidentified, do the following. */ +	if (force32) +		writeb(force32, ioaddr + VirtualJumpers); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_hamachi_dev; +	root_hamachi_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = +		multicast_filter_limit < 64 ? multicast_filter_limit : 64; + +	if (dev->mem_start) +		option = dev->mem_start; + +	/* The lower four bits are the media type. */ +	if (option > 0) { +		if (option & 0x2220) +			np->full_duplex = 1; +		np->default_port = option & 15; +		if (np->default_port & 0x3330) +			np->medialock = 1; +	} +	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) { +		if (np->msg_level & NETIF_MSG_PROBE) +			printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" +				   " disabled.\n", dev->name); +		np->duplex_lock = 1; +	} + +	/* The Hamachi-specific entries in the device structure. */ +	dev->open = &hamachi_open; +	dev->hard_start_xmit = &hamachi_start_xmit; +	dev->stop = &hamachi_close; +	dev->get_stats = &hamachi_get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; +#ifdef HAVE_CHANGE_MTU +	dev->change_mtu = change_mtu; +#endif + +	if (np->drv_flags & CanHaveMII) { +		int phy, phy_idx = 0; +		for (phy = 0; phy < 32 && phy_idx < 4; phy++) { +			int mii_status = mdio_read(ioaddr, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				np->phys[phy_idx++] = phy; +				np->advertising = mdio_read(ioaddr, phy, 4); +				printk(KERN_INFO "%s: MII PHY found at address %d, status " +					   "0x%4.4x advertising %4.4x.\n", +					   dev->name, phy, mii_status, np->advertising); +			} +		} +		np->mii_cnt = phy_idx; +	} +#ifdef notyet	 +	/* Disable PCI Parity Error (0x02) or PCI 64 Bit (0x01) for miswired +	   motherboards. */ +	if (readb(ioaddr + VirtualJumpers) != 0x30) +		writeb(0x33, ioaddr + VirtualJumpers) +#endif +	/* Configure gigabit autonegotiation. */ +	writew(0x0400, ioaddr + ANXchngCtrl);	/* Enable legacy links. */ +	writew(0x08e0, ioaddr + ANAdvertise);	/* Set our advertise word. */ +	writew(0x1000, ioaddr + ANCtrl);		/* Enable negotiation */ + +	return dev; +} + +static int read_eeprom(struct net_device *dev, int location) +{ +	struct hamachi_private *np = (void *)dev->priv; +	long ioaddr = dev->base_addr; +	int bogus_cnt = 1000; + +	writew(location, ioaddr + EEAddr); +	writeb(0x02, ioaddr + EECmdStatus); +	while ((readb(ioaddr + EECmdStatus) & 0x40)  && --bogus_cnt > 0) +		; +	if (np->msg_level & NETIF_MSG_MISC) +		printk(KERN_DEBUG "   EEPROM status is %2.2x after %d ticks.\n", +			   (int)readb(ioaddr + EECmdStatus), 1000- bogus_cnt); +	return readb(ioaddr + EEData); +} + +/* MII Managemen Data I/O accesses. +   These routines assume the MDIO controller is idle, and do not exit until +   the command is finished. */ + +static int mdio_read(long ioaddr, int phy_id, int location) +{ +	int i; + +	writew((phy_id<<8) + location, ioaddr + MII_Addr); +	writew(1, ioaddr + MII_Cmd); +	for (i = 10000; i >= 0; i--) +		if ((readw(ioaddr + MII_Status) & 1) == 0) +			break; +	return readw(ioaddr + MII_Rd_Data); +} + +static void mdio_write(long ioaddr, int phy_id, int location, int value) +{ +	int i; + +	writew((phy_id<<8) + location, ioaddr + MII_Addr); +	writew(value, ioaddr + MII_Wr_Data); + +	/* Wait for the command to finish. */ +	for (i = 10000; i >= 0; i--) +		if ((readw(ioaddr + MII_Status) & 1) == 0) +			break; +	return; +} + + +static int hamachi_open(struct net_device *dev) +{ +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	/* Do we need to reset the chip??? */ + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &hamachi_interrupt, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	if (hmp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: hamachi_open() irq %d.\n", +			   dev->name, dev->irq); + +	hamachi_init_ring(dev); + +#if ADDRLEN == 64 +	writel(virt_to_bus(hmp->rx_ring), ioaddr + RxPtr); +	writel(virt_to_bus(hmp->rx_ring) >> 32, ioaddr + RxPtr + 4); +	writel(virt_to_bus(hmp->tx_ring), ioaddr + TxPtr); +	writel(virt_to_bus(hmp->tx_ring) >> 32, ioaddr + TxPtr + 4); +#else +	writel(virt_to_bus(hmp->rx_ring), ioaddr + RxPtr); +	writel(virt_to_bus(hmp->tx_ring), ioaddr + TxPtr); +#endif + +	for (i = 0; i < 6; i++) +		writeb(dev->dev_addr[i], ioaddr + StationAddr + i); + +	/* Initialize other registers: with so many this eventually this will +	   converted to an offset/value list. */ +	/* Configure the FIFO for 512K external, 16K used for Tx. */ +	writew(0x0028, ioaddr + FIFOcfg); + +	if (dev->if_port == 0) +		dev->if_port = hmp->default_port; +	hmp->in_interrupt = 0; + +	/* Setting the Rx mode will start the Rx process. */ +	/* We are always in full-duplex mode with gigabit! */ +	hmp->full_duplex = 1; +	writew(0x0001, ioaddr + RxChecksum); /* Enable Rx IP partial checksum. */ +	writew(0x8000, ioaddr + MACCnfg); /* Soft reset the MAC */ +	writew(0x215F, ioaddr + MACCnfg); +	writew(0x000C, ioaddr + FrameGap0); /* 0060/4060 for non-MII 10baseT */ +	writew(0x1018, ioaddr + FrameGap1); +	writew(0x2780, ioaddr + MACCnfg2); /* Upper 16 bits control LEDs. */ +	/* Enable automatic generation of flow control frames, period 0xffff. */ +	writel(0x0030FFFF, ioaddr + FlowCtrl); +	writew(dev->mtu+19, ioaddr + MaxFrameSize); 	/* hmp->rx_buf_sz ??? */ + +	/* Enable legacy links. */ +	writew(0x0400, ioaddr + ANXchngCtrl);	/* Enable legacy links. */ +	/* Initial Link LED to blinking red. */ +	writeb(0x03, ioaddr + LEDCtrl); + +	/* Configure interrupt mitigation.  This has a great effect on +	   performance, so systems tuning should start here!. */ +	writel(0x00080000, ioaddr + TxIntrCtrl); +	writel(0x00000020, ioaddr + RxIntrCtrl); + +	hmp->rx_mode = 0;			/* Force Rx mode write. */ +	set_rx_mode(dev); +	netif_start_tx_queue(dev); + +	/* Enable interrupts by setting the interrupt mask. */ +	writel(0x80878787, ioaddr + InterruptEnable); +	writew(0x0000, ioaddr + EventStatus);	/* Clear non-interrupting events */ + +	/* Configure and start the DMA channels. */ +	/* Burst sizes are in the low three bits: size = 4<<(val&7) */ +#if ADDRLEN == 64 +	writew(0x0055, ioaddr + RxDMACtrl); 		/* 128 dword bursts */ +	writew(0x0055, ioaddr + TxDMACtrl); +#else +	writew(0x0015, ioaddr + RxDMACtrl); +	writew(0x0015, ioaddr + TxDMACtrl); +#endif +	writew(1, dev->base_addr + RxCmd); + +	if (hmp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done hamachi_open(), status: Rx %x Tx %x.\n", +			   dev->name, (int)readw(ioaddr + RxStatus), +			   (int)readw(ioaddr + TxStatus)); + +	/* Set the timer to check for link beat. */ +	init_timer(&hmp->timer); +	hmp->timer.expires = jiffies + 3*HZ; +	hmp->timer.data = (unsigned long)dev; +	hmp->timer.function = &hamachi_timer;				/* timer handler */ +	add_timer(&hmp->timer); + +	return 0; +} + +static void hamachi_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 10*HZ; + +	if (hmp->msg_level & NETIF_MSG_TIMER) { +		printk(KERN_INFO "%s: Hamachi Autonegotiation status %4.4x, LPA " +			   "%4.4x.\n", dev->name, (int)readw(ioaddr + ANStatus), +			   (int)readw(ioaddr + ANLinkPartnerAbility)); +		printk(KERN_INFO "%s: Autonegotiation regs %4.4x %4.4x %4.4x " +			   "%4.4x %4.4x %4.4x.\n", dev->name, +		       (int)readw(ioaddr + 0x0e0), +			   (int)readw(ioaddr + 0x0e2), +			   (int)readw(ioaddr + 0x0e4), +			   (int)readw(ioaddr + 0x0e6), +			   (int)readw(ioaddr + 0x0e8), +			   (int)readw(ioaddr + 0x0eA)); +	} +	/* This has a small false-trigger window. */ +	if (netif_queue_paused(dev) && +		(jiffies - dev->trans_start) > TX_TIMEOUT +		&& hmp->cur_tx - hmp->dirty_tx > 1) { +		hamachi_tx_timeout(dev); +	} +	/* We could do something here... nah. */ +	hmp->timer.expires = jiffies + next_tick; +	add_timer(&hmp->timer); +} + +static void hamachi_tx_timeout(struct net_device *dev) +{ +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Hamachi transmit timed out, status %8.8x," +		   " resetting...\n", dev->name, (int)readw(ioaddr + TxStatus)); + +	if (hmp->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk(KERN_DEBUG "  Rx ring %p: ", hmp->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)hmp->rx_ring[i].status_n_length); +		printk("\n"KERN_DEBUG"  Tx ring %p: ", hmp->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %4.4x", hmp->tx_ring[i].status_n_length); +		printk("\n"); +	} + +	/* Perhaps we should reinitialize the hardware here. */ +	dev->if_port = 0; +	/* Stop and restart the chip's Tx processes . */ + +	/* Trigger an immediate transmit demand. */ +	writew(2, dev->base_addr + TxCmd); +	writew(1, dev->base_addr + TxCmd); +	writew(1, dev->base_addr + RxCmd); + +	dev->trans_start = jiffies; +	hmp->stats.tx_errors++; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void hamachi_init_ring(struct net_device *dev) +{ +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; +	int i; + +	hmp->tx_full = 0; +	hmp->cur_rx = hmp->cur_tx = 0; +	hmp->dirty_rx = hmp->dirty_tx = 0; + +	/* Size of each temporary Rx buffer.  Add 8 if you do Rx checksumming! */ +	hmp->rx_buf_sz = dev->mtu + 18 + 8; +	/* Match other driver's allocation size when possible. */ +	if (hmp->rx_buf_sz < PKT_BUF_SZ) +		hmp->rx_buf_sz = PKT_BUF_SZ; +	hmp->rx_head_desc = &hmp->rx_ring[0]; + +	/* Initialize all Rx descriptors. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		hmp->rx_ring[i].status_n_length = 0; +		hmp->rx_skbuff[i] = 0; +	} +	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = dev_alloc_skb(hmp->rx_buf_sz); +		hmp->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		skb_reserve(skb, 2);	/* 16 byte align the IP header. */ +		hmp->rx_ring[i].addr = virt_to_desc(skb->tail); +		hmp->rx_ring[i].status_n_length = +			cpu_to_le32(DescOwn | DescEndPacket | DescIntr | hmp->rx_buf_sz); +	} +	hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); +	/* Mark the last entry as wrapping the ring. */ +	hmp->rx_ring[i-1].status_n_length |= cpu_to_le32(DescEndRing); + +	for (i = 0; i < TX_RING_SIZE; i++) { +		hmp->tx_skbuff[i] = 0; +		hmp->tx_ring[i].status_n_length = 0; +	} +	return; +} + +static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; +	unsigned entry; + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			hamachi_tx_timeout(dev); +		return 1; +	} + +	/* Note: Ordering is important here, set the field with the +	   "ownership" bit last, and only then increment cur_tx. */ + +	/* Calculate the next Tx descriptor entry. */ +	entry = hmp->cur_tx % TX_RING_SIZE; + +	hmp->tx_skbuff[entry] = skb; + +	hmp->tx_ring[entry].addr = virt_to_desc(skb->data); +	if (entry >= TX_RING_SIZE-1)		 /* Wrap ring */ +		hmp->tx_ring[entry].status_n_length = +			cpu_to_le32(DescOwn|DescEndPacket|DescEndRing|DescIntr | skb->len); +	else +		hmp->tx_ring[entry].status_n_length = +			cpu_to_le32(DescOwn|DescEndPacket | skb->len); +	hmp->cur_tx++; + +	/* Architecture-specific: explicitly flush cache lines here. */ + +	/* Wake the potentially-idle transmit channel. */ +	writew(1, dev->base_addr + TxCmd); + +	if (hmp->cur_tx - hmp->dirty_tx >= TX_QUEUE_LEN - 1) { +		hmp->tx_full = 1; +		if (hmp->cur_tx - hmp->dirty_tx < TX_QUEUE_LEN - 1) { +			netif_unpause_tx_queue(dev); +			hmp->tx_full = 0; +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ +	dev->trans_start = jiffies; + +	if (hmp->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Hamachi transmit frame #%d length %d queued " +			   "in slot %d.\n", dev->name, hmp->cur_tx, (int)skb->len, entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct hamachi_private *hmp; +	long ioaddr; +	int boguscnt = max_interrupt_work; + +#ifndef final_version			/* Can never occur. */ +	if (dev == NULL) { +		printk (KERN_ERR "hamachi_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +#endif + +	ioaddr = dev->base_addr; +	hmp = (struct hamachi_private *)dev->priv; +	if (test_and_set_bit(0, (void*)&hmp->in_interrupt)) { +		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); +		hmp->in_interrupt = 0;	/* Avoid future hang on bug */ +		return; +	} + +	do { +		u32 intr_status = readl(ioaddr + InterruptClear); + +		if (hmp->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Hamachi interrupt, status %4.4x.\n", +				   dev->name, intr_status); + +		if (intr_status == 0) +			break; + +		if (intr_status & IntrRxDone) +			hamachi_rx(dev); + +		for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++) { +			int entry = hmp->dirty_tx % TX_RING_SIZE; +			if (!(hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn))) +				break; +			if (hmp->msg_level & NETIF_MSG_TX_DONE) +				printk(KERN_DEBUG "%s: Transmit done, Tx status %8.8x.\n", +					   dev->name, hmp->tx_ring[entry].status_n_length); +			/* Free the original skb. */ +			dev_free_skb_irq(hmp->tx_skbuff[entry]); +			hmp->tx_skbuff[entry] = 0; +			hmp->stats.tx_packets++; +		} +		if (hmp->tx_full +			&& hmp->cur_tx - hmp->dirty_tx < TX_QUEUE_LEN - 4) { +			/* The ring is no longer full, clear tbusy. */ +			hmp->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & +			(IntrTxPCIFault | IntrTxPCIErr | IntrRxPCIFault | IntrRxPCIErr | +			 LinkChange | NegotiationChange | StatsMax)) +			hamachi_error(dev, intr_status); + +		if (--boguscnt < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", +				   dev->name, intr_status); +			break; +		} +	} while (1); + +	if (hmp->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); +	clear_bit(0, (void*)&hmp->in_interrupt); +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int hamachi_rx(struct net_device *dev) +{ +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; +	int entry = hmp->cur_rx % RX_RING_SIZE; +	int boguscnt = hmp->dirty_rx + RX_RING_SIZE - hmp->cur_rx; + +	if (hmp->msg_level & NETIF_MSG_RX_STATUS) { +		printk(KERN_DEBUG " In hamachi_rx(), entry %d status %4.4x.\n", +			   entry, hmp->rx_ring[entry].status_n_length); +	} + +	/* If EOP is set on the next entry, it's a new packet. Send it up. */ +	while ( ! (hmp->rx_head_desc->status_n_length & cpu_to_le32(DescOwn))) { +		struct hamachi_desc *desc = hmp->rx_head_desc; +		u32 desc_status = le32_to_cpu(desc->status_n_length); +		u16 data_size = desc_status; 		/* Implicit truncate */ +		u8 *buf_addr = hmp->rx_skbuff[entry]->tail; +		s32 frame_status = +			le32_to_cpu(get_unaligned((s32*)&(buf_addr[data_size - 12]))); + +		if (hmp->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  hamachi_rx() status was %8.8x.\n", +				   frame_status); +		if (--boguscnt < 0) +			break; +		if ( ! (desc_status & DescEndPacket)) { +			printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " +				   "multiple buffers, entry %#x length %d status %4.4x!\n", +				   dev->name, hmp->cur_rx, data_size, desc_status); +			printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", +				   dev->name, desc, &hmp->rx_ring[hmp->cur_rx % RX_RING_SIZE]); +			printk(KERN_WARNING "%s: Oversized Ethernet frame -- next status" +				   " %x last status %x.\n", dev->name, +				   hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length, +				   hmp->rx_ring[(hmp->cur_rx-1) % RX_RING_SIZE].status_n_length); +			hmp->stats.rx_length_errors++; +		} /* else  Omit for prototype errata??? */ +		if (frame_status & 0x00380000) { +			/* There was a error. */ +			if (hmp->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG "  hamachi_rx() Rx error was %8.8x.\n", +					   frame_status); +			hmp->stats.rx_errors++; +			if (frame_status & 0x00600000) hmp->stats.rx_length_errors++; +			if (frame_status & 0x00080000) hmp->stats.rx_frame_errors++; +			if (frame_status & 0x00100000) hmp->stats.rx_crc_errors++; +			if (frame_status < 0) hmp->stats.rx_dropped++; +		} else { +			struct sk_buff *skb; +			u16 pkt_len = (frame_status & 0x07ff) - 4;	/* Omit CRC */ + +#if ! defined(final_version)  &&  0 +			if (hmp->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "  hamachi_rx() normal Rx pkt length %d" +					   " of %d, bogus_cnt %d.\n", +					   pkt_len, data_size, boguscnt); +			if (hmp->msg_level & NETIF_MSG_PKTDATA) +				printk(KERN_DEBUG"%s:  rx status %8.8x %8.8x %8.8x %8.8x %8.8x.\n", +					   dev->name, +					   *(s32*)&(buf_addr[data_size - 20]), +					   *(s32*)&(buf_addr[data_size - 16]), +					   *(s32*)&(buf_addr[data_size - 12]), +					   *(s32*)&(buf_addr[data_size - 8]), +					   *(s32*)&(buf_addr[data_size - 4])); +#endif +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +				eth_copy_and_sum(skb, hmp->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +			} else { +				char *temp = skb_put(skb = hmp->rx_skbuff[entry], pkt_len); +				hmp->rx_skbuff[entry] = NULL; +#if ! defined(final_version) +				if (bus_to_virt(desc->addr) != temp) +					printk(KERN_ERR "%s: Internal fault: The skbuff addresses " +						   "do not match in hamachi_rx: %p vs. %p / %p.\n", +						   dev->name, bus_to_virt(desc->addr), +						   skb->head, temp); +#endif +			} +			skb->protocol = eth_type_trans(skb, dev); +			/* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */ +			netif_rx(skb); +			dev->last_rx = jiffies; +			hmp->stats.rx_packets++; +		} +		entry = (++hmp->cur_rx) % RX_RING_SIZE; +		hmp->rx_head_desc = &hmp->rx_ring[entry]; +	} + +	/* Refill the Rx ring buffers. */ +	for (; hmp->cur_rx - hmp->dirty_rx > 0; hmp->dirty_rx++) { +		struct sk_buff *skb; +		entry = hmp->dirty_rx % RX_RING_SIZE; +		if (hmp->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(hmp->rx_buf_sz); +			hmp->rx_skbuff[entry] = skb; +			if (skb == NULL) +				break;			/* Better luck next round. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			hmp->rx_ring[entry].addr = virt_to_desc(skb->tail); +		} +		if (entry >= RX_RING_SIZE-1)		 /* Wrap ring */ +			hmp->rx_ring[entry].status_n_length = +				cpu_to_le32(DescOwn|DescEndPacket|DescEndRing|DescIntr | hmp->rx_buf_sz); +		else +			hmp->rx_ring[entry].status_n_length = +				cpu_to_le32(DescOwn|DescEndPacket|DescIntr | hmp->rx_buf_sz); +	} + +	/* Restart Rx engine if stopped. */ +	writew(1, dev->base_addr + RxCmd); +	return 0; +} + +/* This is more properly named "uncommon interrupt events", as it covers more +   than just errors. */ +static void hamachi_error(struct net_device *dev, int intr_status) +{ +	long ioaddr = dev->base_addr; +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; + +	if (intr_status & (LinkChange|NegotiationChange)) { +		if (hmp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Link changed: AutoNegotiation Ctrl" +				   " %4.4x, Status %4.4x %4.4x Intr status %4.4x.\n", +				   dev->name, (int)readw(ioaddr + 0x0E0), +				   (int)readw(ioaddr + 0x0E2), +				   (int)readw(ioaddr + ANLinkPartnerAbility), +				   (int)readl(ioaddr + IntrStatus)); +		if (readw(ioaddr + ANStatus) & 0x20) { +			writeb(0x01, ioaddr + LEDCtrl); +			netif_link_up(dev); +		} else { +			writeb(0x03, ioaddr + LEDCtrl); +			netif_link_down(dev); +		} +	} +	if (intr_status & StatsMax) { +		hamachi_get_stats(dev); +		/* Read the overflow bits to clear. */ +		readl(ioaddr + 0x36C); +		readl(ioaddr + 0x3F0); +	} +	if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange)) +		&& (hmp->msg_level & NETIF_MSG_DRV)) +		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", +			   dev->name, intr_status); +	/* Hmmmmm, it's not clear how to recover from PCI faults. */ +	if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) +		hmp->stats.tx_fifo_errors++; +	if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) +		hmp->stats.rx_fifo_errors++; +} + +static int hamachi_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (hmp->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x " +			   "Rx %4.4x Int %2.2x.\n", +			   dev->name, (int)readw(ioaddr + TxStatus), +			   (int)readw(ioaddr + RxStatus), (int)readl(ioaddr + IntrStatus)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, hmp->cur_tx, hmp->dirty_tx, hmp->cur_rx, +			   hmp->dirty_rx); +	} + +	/* Disable interrupts by clearing the interrupt mask. */ +	writel(0x0000, ioaddr + InterruptEnable); + +	/* Stop the chip's Tx and Rx processes. */ +	writel(2, ioaddr + RxCmd); +	writew(2, ioaddr + TxCmd); + +	del_timer(&hmp->timer); + +#ifdef __i386__ +	if (hmp->msg_level & NETIF_MSG_IFDOWN) { +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(hmp->tx_ring)); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %c #%d desc. %8.8x %8.8x.\n", +				   readl(ioaddr + TxCurPtr) == (long)&hmp->tx_ring[i] ? '>' : ' ', +				   i, hmp->tx_ring[i].status_n_length, hmp->tx_ring[i].addr); +		printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n", +			   (int)virt_to_bus(hmp->rx_ring)); +		for (i = 0; i < RX_RING_SIZE; i++) { +			printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x\n", +				   readl(ioaddr + RxCurPtr) == (long)&hmp->rx_ring[i] ? '>' : ' ', +				   i, hmp->rx_ring[i].status_n_length, hmp->rx_ring[i].addr); +			if (*(u8*)hmp->rx_ring[i].addr != 0x69) { +				int j; +				for (j = 0; j < 0x50; j++) +					printk(" %4.4x", ((u16*)hmp->rx_ring[i].addr)[j]); +				printk("\n"); +			} +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		hmp->rx_ring[i].status_n_length = 0; +		hmp->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ +		if (hmp->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			hmp->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(hmp->rx_skbuff[i]); +		} +		hmp->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (hmp->tx_skbuff[i]) +			dev_free_skb(hmp->tx_skbuff[i]); +		hmp->tx_skbuff[i] = 0; +	} + +	writeb(0x00, ioaddr + LEDCtrl); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static struct net_device_stats *hamachi_get_stats(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct hamachi_private *hmp = (struct hamachi_private *)dev->priv; + +	/* We should lock this segment of code for SMP eventually, although +	   the vulnerability window is very small and statistics are +	   non-critical. */ +#if LINUX_VERSION_CODE >= 0x20119 +	hmp->stats.rx_bytes += readl(ioaddr + 0x330); /* Total Uni+Brd+Multi */ +	hmp->stats.tx_bytes += readl(ioaddr + 0x3B0); /* Total Uni+Brd+Multi */ +#endif +	hmp->stats.multicast		+= readl(ioaddr + 0x320); /* Multicast Rx */ + +	hmp->stats.rx_length_errors	+= readl(ioaddr + 0x368); /* Over+Undersized */ +	hmp->stats.rx_over_errors	+= readl(ioaddr + 0x35C); /* Jabber */ +	hmp->stats.rx_crc_errors	+= readl(ioaddr + 0x360); +	hmp->stats.rx_frame_errors	+= readl(ioaddr + 0x364); /* Symbol Errs */ +	hmp->stats.rx_missed_errors	+= readl(ioaddr + 0x36C); /* Dropped */ + +	return &hmp->stats; +} + +static void set_rx_mode(struct net_device *dev) +{ +	struct hamachi_private *np = (void *)dev->priv; +	long ioaddr = dev->base_addr; +	int new_rx_mode; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		new_rx_mode = 0x000F; +	} else if (dev->mc_count > np->multicast_filter_limit || +			   (dev->flags & IFF_ALLMULTI)) { +		/* Too many to match, or accept all multicasts. */ +		new_rx_mode = 0x000B; +	} else if (dev->mc_count > 0) { /* Must use the CAM filter. */ +		struct dev_mc_list *mclist; +		int i; +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			writel(*(u32*)(mclist->dmi_addr), ioaddr + 0x100 + i*8); +			writel(0x20000 | (*(u16*)&mclist->dmi_addr[4]), +				   ioaddr + 0x104 + i*8); +		} +		/* Clear remaining entries. */ +		for (; i < 64; i++) +			writel(0, ioaddr + 0x104 + i*8); +		new_rx_mode = 0x0003; +	} else {					/* Normal, unicast/broadcast-only mode. */ +		new_rx_mode = 0x0001; +	} +	if (np->rx_mode != new_rx_mode) { +		np->rx_mode = new_rx_mode; +		writew(new_rx_mode, ioaddr + AddrMode); +	} +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct hamachi_private *np = (void *)dev->priv; +	long ioaddr = dev->base_addr; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = np->phys[0] & 0x1f; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		/* We are always full duplex.  Skip recording the advertised value. */ +		mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: { +		/* Set rx,tx intr params, from Eric Kasten. */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->max_interrupt_work = data32[2]; +		writel(data32[1], dev->base_addr + TxIntrCtrl); +		writel(data32[3], dev->base_addr + RxIntrCtrl); +		printk(KERN_INFO "%s: Set interrupt mitigate paramters tx %08x, " +			   "rx %08x.\n", dev->name, +			   (int) readl(dev->base_addr + TxIntrCtrl), +			   (int) readl(dev->base_addr + RxIntrCtrl)); +		return 0; +	} +	default: +		return -EOPNOTSUPP; +	} +} + +#ifdef HAVE_CHANGE_MTU +static int change_mtu(struct net_device *dev, int new_mtu) +{ +	if ((new_mtu < 68) || (new_mtu > 1536)) +		return -EINVAL; +	if (netif_running(dev)) +		return -EBUSY; +	printk(KERN_NOTICE "%s: Changing MTU to %d.\n", dev->name, new_mtu); +	dev->mtu = new_mtu; +	return 0; +} +#endif + + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return pci_drv_register(&hamachi_drv_id, NULL); +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&hamachi_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_hamachi_dev) { +		struct hamachi_private *hmp = (void *)(root_hamachi_dev->priv); +		unregister_netdev(root_hamachi_dev); +		iounmap((char *)root_hamachi_dev->base_addr); +		next_dev = hmp->next_module; +		if (hmp->priv_addr) +			kfree(hmp->priv_addr); +		kfree(root_hamachi_dev); +		root_hamachi_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` hamachi.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c hamachi.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c hamachi.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/hp-plus.c b/linux/src/drivers/net/hp-plus.c new file mode 100644 index 0000000..c2b7116 --- /dev/null +++ b/linux/src/drivers/net/hp-plus.c @@ -0,0 +1,483 @@ +/* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */ +/* +	Written 1994 by Donald Becker. + +	This driver is for the Hewlett Packard PC LAN (27***) plus ethercards. +	These cards are sold under several model numbers, usually 2724*. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O + +	Center of Excellence in Space Data and Information Sciences +		Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	As is often the case, a great deal of credit is owed to Russ Nelson. +	The Crynwr packet driver was my primary source of HP-specific +	programming information. +*/ + +static const char *version = +"hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include <linux/module.h> + +#include <linux/string.h>		/* Important -- this inlines word moves. */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include <asm/system.h> +#include <asm/io.h> + + +#include "8390.h" + +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int hpplus_portlist[] = +{0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0}; + +/* +   The HP EtherTwist chip implementation is a fairly routine DP8390 +   implementation.  It allows both shared memory and programmed-I/O buffer +   access, using a custom interface for both.  The programmed-I/O mode is +   entirely implemented in the HP EtherTwist chip, bypassing the problem +   ridden built-in 8390 facilities used on NE2000 designs.  The shared +   memory mode is likewise special, with an offset register used to make +   packets appear at the shared memory base.  Both modes use a base and bounds +   page register to hide the Rx ring buffer wrap -- a packet that spans the +   end of physical buffer memory appears continuous to the driver. (c.f. the +   3c503 and Cabletron E2100) + +   A special note: the internal buffer of the board is only 8 bits wide. +   This lays several nasty traps for the unaware: +   - the 8390 must be programmed for byte-wide operations +   - all I/O and memory operations must work on whole words (the access +     latches are serially preloaded and have no byte-swapping ability). + +   This board is laid out in I/O space much like the earlier HP boards: +   the first 16 locations are for the board registers, and the second 16 are +   for the 8390.  The board is easy to identify, with both a dedicated 16 bit +   ID register and a constant 0x530* value in the upper bits of the paging +   register. +*/ + +#define HP_ID			0x00	/* ID register, always 0x4850. */ +#define HP_PAGING		0x02	/* Registers visible @ 8-f, see PageName. */  +#define HPP_OPTION		0x04	/* Bitmapped options, see HP_Option.	*/ +#define HPP_OUT_ADDR	0x08	/* I/O output location in Perf_Page.	*/ +#define HPP_IN_ADDR		0x0A	/* I/O input location in Perf_Page.		*/ +#define HP_DATAPORT		0x0c	/* I/O data transfer in Perf_Page.		*/ +#define NIC_OFFSET		0x10	/* Offset to the 8390 registers.		*/ +#define HP_IO_EXTENT	32 + +#define HP_START_PG		0x00	/* First page of TX buffer */ +#define HP_STOP_PG		0x80	/* Last page +1 of RX ring */ + +/* The register set selected in HP_PAGING. */ +enum PageName { +	Perf_Page = 0,				/* Normal operation. */ +	MAC_Page = 1,				/* The ethernet address (+checksum). */ +	HW_Page = 2,				/* EEPROM-loaded hardware parameters. */ +	LAN_Page = 4,				/* Transceiver selection, testing, etc. */ +	ID_Page = 6 };  + +/* The bit definitions for the HPP_OPTION register. */ +enum HP_Option { +	NICReset = 1, ChipReset = 2, 	/* Active low, really UNreset. */ +	EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20, +	MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, }; + +int hp_plus_probe(struct device *dev); +int hpp_probe1(struct device *dev, int ioaddr); + +static void hpp_reset_8390(struct device *dev); +static int hpp_open(struct device *dev); +static int hpp_close(struct device *dev); +static void hpp_mem_block_input(struct device *dev, int count, +						  struct sk_buff *skb, int ring_offset); +static void hpp_mem_block_output(struct device *dev, int count, +							const unsigned char *buf, const int start_page); +static void hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +						  int ring_page); +static void hpp_io_block_input(struct device *dev, int count, +						  struct sk_buff *skb, int ring_offset); +static void hpp_io_block_output(struct device *dev, int count, +							const unsigned char *buf, const int start_page); +static void hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +						  int ring_page); + + +/*	Probe a list of addresses for an HP LAN+ adaptor. +	This routine is almost boilerplate. */ +#ifdef HAVE_DEVLIST +/* Support for a alternate probe manager, which will eliminate the +   boilerplate below. */ +struct netdev_entry hpplus_drv = +{"hpplus", hpp_probe1, HP_IO_EXTENT, hpplus_portlist}; +#else + +int hp_plus_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return hpp_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; hpplus_portlist[i]; i++) { +		int ioaddr = hpplus_portlist[i]; +		if (check_region(ioaddr, HP_IO_EXTENT)) +			continue; +		if (hpp_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +/* Do the interesting part of the probe at a single address. */ +int hpp_probe1(struct device *dev, int ioaddr) +{ +	int i; +	unsigned char checksum = 0; +	const char *name = "HP-PC-LAN+"; +	int mem_start; +	static unsigned version_printed = 0; + +	/* Check for the HP+ signature, 50 48 0x 53. */ +	if (inw(ioaddr + HP_ID) != 0x4850 +		|| (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300) +		return ENODEV; + +	/* We should have a "dev" from Space.c or the static module table. */ +	if (dev == NULL) { +		printk("hp-plus.c: Passed a NULL device.\n"); +		dev = init_etherdev(0, 0); +	} + +	if (ei_debug  &&  version_printed++ == 0) +		printk("%s", version); + +	printk("%s: %s at %#3x,", dev->name, name, ioaddr); + +	/* Retrieve and checksum the station address. */ +	outw(MAC_Page, ioaddr + HP_PAGING); + +	for(i = 0; i < ETHER_ADDR_LEN; i++) { +		unsigned char inval = inb(ioaddr + 8 + i); +		dev->dev_addr[i] = inval; +		checksum += inval; +		printk(" %2.2x", inval); +	} +	checksum += inb(ioaddr + 14); + +	if (checksum != 0xff) { +		printk(" bad checksum %2.2x.\n", checksum); +		return ENODEV; +	} else { +		/* Point at the Software Configuration Flags. */ +		outw(ID_Page, ioaddr + HP_PAGING); +		printk(" ID %4.4x", inw(ioaddr + 12)); +	} + +	/* Allocate dev->priv and fill in 8390 specific dev fields. */ +	if (ethdev_init(dev)) { +		printk ("hp-plus.c: unable to allocate memory for dev->priv.\n"); +		return -ENOMEM; +	 } + +	/* Grab the region so we can find another board if something fails. */ +	request_region(ioaddr, HP_IO_EXTENT,"hp-plus"); + +	/* Read the IRQ line. */ +	outw(HW_Page, ioaddr + HP_PAGING); +	{ +		int irq = inb(ioaddr + 13) & 0x0f; +		int option = inw(ioaddr + HPP_OPTION); + +		dev->irq = irq; +		if (option & MemEnable) { +			mem_start = inw(ioaddr + 9) << 8; +			printk(", IRQ %d, memory address %#x.\n", irq, mem_start); +		} else { +			mem_start = 0; +			printk(", IRQ %d, programmed-I/O mode.\n", irq); +		} +	} + +	/* Set the wrap registers for string I/O reads.   */ +	outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14); + +	/* Set the base address to point to the NIC, not the "real" base! */ +	dev->base_addr = ioaddr + NIC_OFFSET; + +	dev->open = &hpp_open; +	dev->stop = &hpp_close; + +	ei_status.name = name; +	ei_status.word16 = 0;		/* Agggghhhhh! Debug time: 2 days! */ +	ei_status.tx_start_page = HP_START_PG; +	ei_status.rx_start_page = HP_START_PG + TX_2X_PAGES; +	ei_status.stop_page = HP_STOP_PG; + +	ei_status.reset_8390 = &hpp_reset_8390; +	ei_status.block_input = &hpp_io_block_input; +	ei_status.block_output = &hpp_io_block_output; +	ei_status.get_8390_hdr = &hpp_io_get_8390_hdr; + +	/* Check if the memory_enable flag is set in the option register. */ +	if (mem_start) { +		ei_status.block_input = &hpp_mem_block_input; +		ei_status.block_output = &hpp_mem_block_output; +		ei_status.get_8390_hdr = &hpp_mem_get_8390_hdr; +		dev->mem_start = mem_start; +		dev->rmem_start = dev->mem_start + TX_2X_PAGES*256; +		dev->mem_end = dev->rmem_end +			= dev->mem_start + (HP_STOP_PG - HP_START_PG)*256; +	} + +	outw(Perf_Page, ioaddr + HP_PAGING); +	NS8390_init(dev, 0); +	/* Leave the 8390 and HP chip reset. */ +	outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION); + +	return 0; +} + +static int +hpp_open(struct device *dev) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; +	int option_reg; + +	if (request_irq(dev->irq, &ei_interrupt, 0, "hp-plus", NULL)) { +	    return -EAGAIN; +	} + +	/* Reset the 8390 and HP chip. */ +	option_reg = inw(ioaddr + HPP_OPTION); +	outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION); +	SLOW_DOWN_IO; SLOW_DOWN_IO; +	/* Unreset the board and enable interrupts. */ +	outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION); + +	/* Set the wrap registers for programmed-I/O operation.   */ +	outw(HW_Page, ioaddr + HP_PAGING); +	outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14); + +	/* Select the operational page. */ +	outw(Perf_Page, ioaddr + HP_PAGING); + +	ei_open(dev); +	MOD_INC_USE_COUNT; +	return 0; +} + +static int +hpp_close(struct device *dev) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; +	int option_reg = inw(ioaddr + HPP_OPTION); + +	free_irq(dev->irq, NULL); +	irq2dev_map[dev->irq] = NULL; +	ei_close(dev); +	outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset, +		 ioaddr + HPP_OPTION); + +	MOD_DEC_USE_COUNT; +	return 0; +} + +static void +hpp_reset_8390(struct device *dev) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; +	int option_reg = inw(ioaddr + HPP_OPTION); + +	if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies); + +	outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION); +	/* Pause a few cycles for the hardware reset to take place. */ +	SLOW_DOWN_IO; +	SLOW_DOWN_IO; +	ei_status.txing = 0; +	outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION); + +	SLOW_DOWN_IO; SLOW_DOWN_IO; + + +	if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0) +		printk("%s: hp_reset_8390() did not complete.\n", dev->name); + +	if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies); +	return; +} + +/* The programmed-I/O version of reading the 4 byte 8390 specific header. +   Note that transfer with the EtherTwist+ must be on word boundaries. */ + +static void +hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; + +	outw((ring_page<<8), ioaddr + HPP_IN_ADDR); +	insw(ioaddr + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); +} + +/* Block input and output, similar to the Crynwr packet driver. */ + +static void +hpp_io_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; +	char *buf = skb->data; + +	outw(ring_offset, ioaddr + HPP_IN_ADDR); +	insw(ioaddr + HP_DATAPORT, buf, count>>1); +	if (count & 0x01) +        buf[count-1] = inw(ioaddr + HP_DATAPORT); +} + +/* The corresponding shared memory versions of the above 2 functions. */ + +static void +hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; +	int option_reg = inw(ioaddr + HPP_OPTION); + +	outw((ring_page<<8), ioaddr + HPP_IN_ADDR); +	outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION); +	memcpy_fromio(hdr, dev->mem_start, sizeof(struct e8390_pkt_hdr)); +	outw(option_reg, ioaddr + HPP_OPTION); +	hdr->count = (hdr->count + 3) & ~3;	/* Round up allocation. */ +} + +static void +hpp_mem_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; +	int option_reg = inw(ioaddr + HPP_OPTION); + +	outw(ring_offset, ioaddr + HPP_IN_ADDR); + +	outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION); + +	/* Caution: this relies on get_8390_hdr() rounding up count! +	   Also note that we *can't* use eth_io_copy_and_sum() because +	   it will not always copy "count" bytes (e.g. padded IP).  */ + +	memcpy_fromio(skb->data, dev->mem_start, count); +	outw(option_reg, ioaddr + HPP_OPTION); +} + +/* A special note: we *must* always transfer >=16 bit words. +   It's always safe to round up, so we do. */ +static void +hpp_io_block_output(struct device *dev, int count, +					const unsigned char *buf, const int start_page) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; +	outw(start_page << 8, ioaddr + HPP_OUT_ADDR); +	outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2); +	return; +} + +static void +hpp_mem_block_output(struct device *dev, int count, +				const unsigned char *buf, const int start_page) +{ +	int ioaddr = dev->base_addr - NIC_OFFSET; +	int option_reg = inw(ioaddr + HPP_OPTION); + +	outw(start_page << 8, ioaddr + HPP_OUT_ADDR); +	outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION); +	memcpy_toio(dev->mem_start, buf, (count + 3) & ~3); +	outw(option_reg, ioaddr + HPP_OPTION); + +	return; +} + + +#ifdef MODULE +#define MAX_HPP_CARDS	4	/* Max number of HPP cards per module */ +#define NAMELEN		8	/* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_HPP_CARDS] = { 0, }; +static struct device dev_hpp[MAX_HPP_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int io[MAX_HPP_CARDS] = { 0, }; +static int irq[MAX_HPP_CARDS]  = { 0, }; + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) { +		struct device *dev = &dev_hpp[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->init = hp_plus_probe; +		if (io[this_dev] == 0)  { +			if (this_dev != 0) break; /* only autoprobe 1st one */ +			printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n"); +		} +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]); +			if (found != 0) return 0;	/* Got at least one. */ +			return -ENXIO; +		} +		found++; +	} + +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) { +		struct device *dev = &dev_hpp[this_dev]; +		if (dev->priv != NULL) { +			/* NB: hpp_close() handles free_irq + irq2dev map */ +			int ioaddr = dev->base_addr - NIC_OFFSET; +			kfree(dev->priv); +			dev->priv = NULL; +			release_region(ioaddr, HP_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp-plus.c" + * version-control: t + * kept-new-versions: 5 + * tab-width: 4 + * c-indent-level: 4 + * End: + */ diff --git a/linux/src/drivers/net/hp.c b/linux/src/drivers/net/hp.c new file mode 100644 index 0000000..6ddbfd2 --- /dev/null +++ b/linux/src/drivers/net/hp.c @@ -0,0 +1,451 @@ +/* hp.c: A HP LAN ethernet driver for linux. */ +/* +	Written 1993-94 by Donald Becker. + +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	This is a driver for the HP PC-LAN adaptors. + +	Sources: +	  The Crynwr packet driver. +*/ + +static const char *version = +	"hp.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +#include <asm/system.h> +#include <asm/io.h> + +#include "8390.h" + +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int hppclan_portlist[] = +{ 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0}; + +#define HP_IO_EXTENT	32 + +#define HP_DATAPORT		0x0c	/* "Remote DMA" data port. */ +#define HP_ID			0x07 +#define HP_CONFIGURE	0x08	/* Configuration register. */ +#define	 HP_RUN			0x01	/* 1 == Run, 0 == reset. */ +#define	 HP_IRQ			0x0E	/* Mask for software-configured IRQ line. */ +#define	 HP_DATAON		0x10	/* Turn on dataport */ +#define NIC_OFFSET		0x10	/* Offset the 8390 registers. */ + +#define HP_START_PG		0x00	/* First page of TX buffer */ +#define HP_8BSTOP_PG	0x80	/* Last page +1 of RX ring */ +#define HP_16BSTOP_PG	0xFF	/* Same, for 16 bit cards. */ + +int hp_probe(struct device *dev); +int hp_probe1(struct device *dev, int ioaddr); + +static int hp_open(struct device *dev); +static int hp_close(struct device *dev); +static void hp_reset_8390(struct device *dev); +static void hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +					int ring_page); +static void hp_block_input(struct device *dev, int count, +					struct sk_buff *skb , int ring_offset); +static void hp_block_output(struct device *dev, int count, +							const unsigned char *buf, const int start_page); + +static void hp_init_card(struct device *dev); + +/* The map from IRQ number to HP_CONFIGURE register setting. */ +/* My default is IRQ5	   0  1	 2  3  4  5  6	7  8  9 10 11 */ +static char irqmap[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0}; + + +/*	Probe for an HP LAN adaptor. +	Also initialize the card and fill in STATION_ADDR with the station +	address. */ +#ifdef HAVE_DEVLIST +struct netdev_entry netcard_drv = +{"hp", hp_probe1, HP_IO_EXTENT, hppclan_portlist}; +#else + +int hp_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return hp_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; hppclan_portlist[i]; i++) { +		int ioaddr = hppclan_portlist[i]; +		if (check_region(ioaddr, HP_IO_EXTENT)) +			continue; +		if (hp_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +int hp_probe1(struct device *dev, int ioaddr) +{ +	int i, board_id, wordmode; +	const char *name; +	static unsigned version_printed = 0; + +	/* Check for the HP physical address, 08 00 09 xx xx xx. */ +	/* This really isn't good enough: we may pick up HP LANCE boards +	   also!  Avoid the lance 0x5757 signature. */ +	if (inb(ioaddr) != 0x08 +		|| inb(ioaddr+1) != 0x00 +		|| inb(ioaddr+2) != 0x09 +		|| inb(ioaddr+14) == 0x57) +		return ENODEV; + +	/* Set up the parameters based on the board ID. +	   If you have additional mappings, please mail them to me -djb. */ +	if ((board_id = inb(ioaddr + HP_ID)) & 0x80) { +		name = "HP27247"; +		wordmode = 1; +	} else { +		name = "HP27250"; +		wordmode = 0; +	} + +	/* We should have a "dev" from Space.c or the static module table. */ +	if (dev == NULL) { +		printk("hp.c: Passed a NULL device.\n"); +		dev = init_etherdev(0, 0); +	} + +	if (ei_debug  &&  version_printed++ == 0) +		printk("%s", version); +  +	printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr); + +	for(i = 0; i < ETHER_ADDR_LEN; i++) +		printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + +	/* Snarf the interrupt now.  Someday this could be moved to open(). */ +	if (dev->irq < 2) { +		int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0}; +		int irq_8list[] = { 7, 5, 3, 4, 9, 0}; +		int *irqp = wordmode ? irq_16list : irq_8list; +		do { +			int irq = *irqp; +			if (request_irq (irq, NULL, 0, "bogus", NULL) != -EBUSY) { +				autoirq_setup(0); +				/* Twinkle the interrupt, and check if it's seen. */ +				outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE); +				outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE); +				if (irq == autoirq_report(0)		 /* It's a good IRQ line! */ +					&& request_irq (irq, &ei_interrupt, 0, "hp", NULL) == 0) { +					printk(" selecting IRQ %d.\n", irq); +					dev->irq = *irqp; +					break; +				} +			} +		} while (*++irqp); +		if (*irqp == 0) { +			printk(" no free IRQ lines.\n"); +			return EBUSY; +		} +	} else { +		if (dev->irq == 2) +			dev->irq = 9; +		if (request_irq(dev->irq, ei_interrupt, 0, "hp", NULL)) { +			printk (" unable to get IRQ %d.\n", dev->irq); +			return EBUSY; +		} +	} + +	/* Allocate dev->priv and fill in 8390 specific dev fields. */ +	if (ethdev_init(dev)) { +		printk (" unable to get memory for dev->priv.\n"); +		free_irq(dev->irq, NULL); +		return -ENOMEM; +	} + +	/* Grab the region so we can find another board if something fails. */ +	request_region(ioaddr, HP_IO_EXTENT,"hp"); + +	/* Set the base address to point to the NIC, not the "real" base! */ +	dev->base_addr = ioaddr + NIC_OFFSET; +	dev->open = &hp_open; +	dev->stop = &hp_close; + +	ei_status.name = name; +	ei_status.word16 = wordmode; +	ei_status.tx_start_page = HP_START_PG; +	ei_status.rx_start_page = HP_START_PG + TX_PAGES; +	ei_status.stop_page = wordmode ? HP_16BSTOP_PG : HP_8BSTOP_PG; + +	ei_status.reset_8390 = &hp_reset_8390; +	ei_status.get_8390_hdr = &hp_get_8390_hdr; +	ei_status.block_input = &hp_block_input; +	ei_status.block_output = &hp_block_output; +	hp_init_card(dev); + +	return 0; +} + +static int +hp_open(struct device *dev) +{ +	ei_open(dev); +	MOD_INC_USE_COUNT; +	return 0; +} + +static int +hp_close(struct device *dev) +{ +	ei_close(dev); +	MOD_DEC_USE_COUNT; +	return 0; +} + +static void +hp_reset_8390(struct device *dev) +{ +	int hp_base = dev->base_addr - NIC_OFFSET; +	int saved_config = inb_p(hp_base + HP_CONFIGURE); + +	if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies); +	outb_p(0x00, hp_base + HP_CONFIGURE); +	ei_status.txing = 0; +	/* Pause just a few cycles for the hardware reset to take place. */ +	SLOW_DOWN_IO; +	SLOW_DOWN_IO; + +	outb_p(saved_config, hp_base + HP_CONFIGURE); +	SLOW_DOWN_IO; SLOW_DOWN_IO; +	 +	if ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0) +		printk("%s: hp_reset_8390() did not complete.\n", dev->name); + +	if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies); +	return; +} + +static void +hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ +	int nic_base = dev->base_addr; +	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE); + +	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE); +	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base); +	outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); +	outb_p(0, nic_base + EN0_RCNTHI); +	outb_p(0, nic_base + EN0_RSARLO);	/* On page boundary */ +	outb_p(ring_page, nic_base + EN0_RSARHI); +	outb_p(E8390_RREAD+E8390_START, nic_base); + +	if (ei_status.word16)  +	  insw(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); +	else  +	  insb(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)); + +	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE); +} +	 +/* Block input and output, similar to the Crynwr packet driver. If you are +   porting to a new ethercard look at the packet driver source for hints. +   The HP LAN doesn't use shared memory -- we put the packet +   out through the "remote DMA" dataport. */ + +static void +hp_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +	int nic_base = dev->base_addr; +	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE); +	int xfer_count = count; +	char *buf = skb->data; + +	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE); +	outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base); +	outb_p(count & 0xff, nic_base + EN0_RCNTLO); +	outb_p(count >> 8, nic_base + EN0_RCNTHI); +	outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); +	outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); +	outb_p(E8390_RREAD+E8390_START, nic_base); +	if (ei_status.word16) { +	  insw(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1); +	  if (count & 0x01) +		buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++; +	} else { +		insb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count); +	} +	/* This is for the ALPHA version only, remove for later releases. */ +	if (ei_debug > 0) {			/* DMA termination address check... */ +	  int high = inb_p(nic_base + EN0_RSARHI); +	  int low = inb_p(nic_base + EN0_RSARLO); +	  int addr = (high << 8) + low; +	  /* Check only the lower 8 bits so we can ignore ring wrap. */ +	  if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff)) +		printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n", +			   dev->name, ring_offset + xfer_count, addr); +	} +	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE); +} + +static void +hp_block_output(struct device *dev, int count, +				const unsigned char *buf, const int start_page) +{ +	int nic_base = dev->base_addr; +	int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE); + +	outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE); +	/* Round the count up for word writes.	Do we need to do this? +	   What effect will an odd byte count have on the 8390? +	   I should check someday. */ +	if (ei_status.word16 && (count & 0x01)) +	  count++; +	/* We should already be in page 0, but to be safe... */ +	outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base); + +#ifdef NE8390_RW_BUGFIX +	/* Handle the read-before-write bug the same way as the +	   Crynwr packet driver -- the NatSemi method doesn't work. */ +	outb_p(0x42, nic_base + EN0_RCNTLO); +	outb_p(0,	nic_base + EN0_RCNTHI); +	outb_p(0xff, nic_base + EN0_RSARLO); +	outb_p(0x00, nic_base + EN0_RSARHI); +#define NE_CMD	 	0x00 +	outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); +	/* Make certain that the dummy read has occurred. */ +	inb_p(0x61); +	inb_p(0x61); +#endif + +	outb_p(count & 0xff, nic_base + EN0_RCNTLO); +	outb_p(count >> 8,	 nic_base + EN0_RCNTHI); +	outb_p(0x00, nic_base + EN0_RSARLO); +	outb_p(start_page, nic_base + EN0_RSARHI); + +	outb_p(E8390_RWRITE+E8390_START, nic_base); +	if (ei_status.word16) { +		/* Use the 'rep' sequence for 16 bit boards. */ +		outsw(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1); +	} else { +		outsb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count); +	} + +	/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */ + +	/* This is for the ALPHA version only, remove for later releases. */ +	if (ei_debug > 0) {			/* DMA termination address check... */ +	  int high = inb_p(nic_base + EN0_RSARHI); +	  int low  = inb_p(nic_base + EN0_RSARLO); +	  int addr = (high << 8) + low; +	  if ((start_page << 8) + count != addr) +		printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n", +			   dev->name, (start_page << 8) + count, addr); +	} +	outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE); +	return; +} + +/* This function resets the ethercard if something screws up. */ +static void +hp_init_card(struct device *dev) +{ +	int irq = dev->irq; +	NS8390_init(dev, 0); +	outb_p(irqmap[irq&0x0f] | HP_RUN, +		   dev->base_addr - NIC_OFFSET + HP_CONFIGURE); +	return; +} + +#ifdef MODULE +#define MAX_HP_CARDS	4	/* Max number of HP cards per module */ +#define NAMELEN		8	/* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_HP_CARDS] = { 0, }; +static struct device dev_hp[MAX_HP_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int io[MAX_HP_CARDS] = { 0, }; +static int irq[MAX_HP_CARDS]  = { 0, }; + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) { +		struct device *dev = &dev_hp[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->init = hp_probe; +		if (io[this_dev] == 0)  { +			if (this_dev != 0) break; /* only autoprobe 1st one */ +			printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n"); +		} +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]); +			if (found != 0) return 0;	/* Got at least one. */ +			return -ENXIO; +		} +		found++; +	} + +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) { +		struct device *dev = &dev_hp[this_dev]; +		if (dev->priv != NULL) { +			int ioaddr = dev->base_addr - NIC_OFFSET; +			kfree(dev->priv); +			dev->priv = NULL; +			free_irq(dev->irq, NULL); +			irq2dev_map[dev->irq] = NULL; +			release_region(ioaddr, HP_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp.c" + * version-control: t + * kept-new-versions: 5 + * tab-width: 4 + * c-indent-level: 4 + * End: + */ diff --git a/linux/src/drivers/net/hp100.c b/linux/src/drivers/net/hp100.c new file mode 100644 index 0000000..0b86ef4 --- /dev/null +++ b/linux/src/drivers/net/hp100.c @@ -0,0 +1,3121 @@ +/* +** hp100.c  +** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters +** +** $Id: hp100.c,v 1.1.4.1 2005/06/02 18:52:39 ams Exp $ +** +** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz> +** Extended for new busmaster capable chipsets by  +** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de> +** +** Maintained by: Jaroslav Kysela <perex@jcu.cz> +**  +** This driver has only been tested with +** -- HP J2585B 10/100 Mbit/s PCI Busmaster +** -- HP J2585A 10/100 Mbit/s PCI  +** -- HP J2970  10 Mbit/s PCI Combo 10base-T/BNC +** -- HP J2973  10 Mbit/s PCI 10base-T +** -- HP J2573  10/100 ISA +** -- Compex ReadyLink ENET100-VG4  10/100 Mbit/s PCI / EISA +** -- Compex FreedomLine 100/VG  10/100 Mbit/s ISA / EISA / PCI +**  +** but it should also work with the other CASCADE based adapters. +** +** TODO: +**       -  J2573 seems to hang sometimes when in shared memory mode. +**       -  Mode for Priority TX +**       -  Check PCI registers, performance might be improved? +**       -  To reduce interrupt load in busmaster, one could switch off +**          the interrupts that are used to refill the queues whenever the +**          queues are filled up to more than a certain threshold. +**       -  some updates for EISA version of card +** +** +** This source/code is public free; you can distribute it and/or modify  +** it under terms of the GNU General Public License (published by the +** Free Software Foundation) either version two of this License, or any  +** later version. +** +** 1.55 -> 1.56 +**   - removed printk in misc. interrupt and update statistics to allow +**     monitoring of card status +**   - timing changes in xmit routines, relogin to 100VG hub added when +**     driver does reset +**   - included fix for Compex FreedomLine PCI adapter +**  +** 1.54 -> 1.55 +**   - fixed bad initialization in init_module +**   - added Compex FreedomLine adapter +**   - some fixes in card initialization +** +** 1.53 -> 1.54 +**   - added hardware multicast filter support (doesn't work) +**   - little changes in hp100_sense_lan routine  +**     - added support for Coax and AUI (J2970) +**   - fix for multiple cards and hp100_mode parameter (insmod) +**   - fix for shared IRQ  +** +** 1.52 -> 1.53 +**   - fixed bug in multicast support +** +*/ + +#define HP100_DEFAULT_PRIORITY_TX 0  + +#undef HP100_DEBUG +#undef HP100_DEBUG_B           /* Trace  */ +#undef HP100_DEBUG_BM          /* Debug busmaster code (PDL stuff) */ + +#undef HP100_DEBUG_TRAINING    /* Debug login-to-hub procedure */ +#undef HP100_DEBUG_TX    +#undef HP100_DEBUG_IRQ  +#undef HP100_DEBUG_RX  + +#undef HP100_MULTICAST_FILTER  /* Need to be debugged... */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <linux/types.h> +#include <linux/config.h>  /* for CONFIG_PCI */ +#include <linux/delay.h> + +#if LINUX_VERSION_CODE < 0x020100 +#define ioremap vremap +#define iounmap vfree +typedef struct enet_statistics hp100_stats_t; +#else +#define LINUX_2_1 +typedef struct net_device_stats hp100_stats_t; +#endif + +#ifndef __initfunc +#define __initfunc(__initarg) __initarg +#else +#include <linux/init.h> +#endif + +#include "hp100.h" + +/* + *  defines + */ + +#define HP100_BUS_ISA     0 +#define HP100_BUS_EISA    1 +#define HP100_BUS_PCI     2 + +#ifndef PCI_DEVICE_ID_HP_J2585B +#define PCI_DEVICE_ID_HP_J2585B 0x1031 +#endif +#ifndef PCI_VENDOR_ID_COMPEX +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#endif +#ifndef PCI_DEVICE_ID_COMPEX_ENET100VG4 +#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 +#endif +#ifndef PCI_VENDOR_ID_COMPEX2 +#define PCI_VENDOR_ID_COMPEX2 0x101a +#endif +#ifndef PCI_DEVICE_ID_COMPEX2_100VG +#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005 +#endif + +#define HP100_REGION_SIZE  0x20 /* for ioports */ + +#define HP100_MAX_PACKET_SIZE  (1536+4) +#define HP100_MIN_PACKET_SIZE  60 + +#ifndef HP100_DEFAULT_RX_RATIO +/* default - 75% onboard memory on the card are used for RX packets */ +#define HP100_DEFAULT_RX_RATIO  75 +#endif + +#ifndef HP100_DEFAULT_PRIORITY_TX +/* default - don't enable transmit outgoing packets as priority */ +#define HP100_DEFAULT_PRIORITY_TX 0 +#endif + +/* + *  structures + */ + +struct hp100_eisa_id { +  u_int id; +  const char *name; +  u_char bus; +}; + +struct hp100_pci_id { +  u_short vendor; +  u_short device; +}; + +struct hp100_private { +  struct hp100_eisa_id *id; +  u_short chip; +  u_short soft_model; +  u_int memory_size;  +  u_int virt_memory_size; +  u_short rx_ratio;       /* 1 - 99 */ +  u_short priority_tx;    /* != 0 - priority tx */ +  u_short mode;           /* PIO, Shared Mem or Busmaster */ +  u_char bus; +  u_char pci_bus; +  u_char pci_device_fn; +  short mem_mapped;	  /* memory mapped access */ +  u_int *mem_ptr_virt;    /* virtual memory mapped area, maybe NULL */ +  u_int *mem_ptr_phys;	  /* physical memory mapped area */ +  short lan_type;	  /* 10Mb/s, 100Mb/s or -1 (error) */ +  int hub_status;	  /* was login to hub successful? */ +  u_char mac1_mode; +  u_char mac2_mode; +  u_char hash_bytes[ 8 ]; +  hp100_stats_t stats; + +  /* Rings for busmaster mode: */ +  hp100_ring_t *rxrhead;  /* Head (oldest) index into rxring */ +  hp100_ring_t *rxrtail;  /* Tail (newest) index into rxring */ +  hp100_ring_t *txrhead;  /* Head (oldest) index into txring */ +  hp100_ring_t *txrtail;  /* Tail (newest) index into txring */ + +  hp100_ring_t rxring[ MAX_RX_PDL ]; +  hp100_ring_t txring[ MAX_TX_PDL ]; + +  u_int *page_vaddr;      /* Virtual address of allocated page */ +  u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */ +  int rxrcommit;          /* # Rx PDLs commited to adapter */ +  int txrcommit;          /* # Tx PDLs commited to adapter */ +}; + +/* + *  variables + */ + +static struct hp100_eisa_id hp100_eisa_ids[] = { + +  /* 10/100 EISA card with revision A Cascade chip */ +  { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA }, + +  /* 10/100 ISA card with revision A Cascade chip */ +  { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA }, + +  /* 10 only EISA card with Cascade chip */ +  { 0x2019F022, "HP 27248B",      HP100_BUS_EISA }, + +  /* 10/100 EISA card with Cascade chip */ +  { 0x4019F022, "HP J2577",       HP100_BUS_EISA }, + +  /* 10/100 ISA card with Cascade chip */ +  { 0x5019F022, "HP J2573",       HP100_BUS_ISA }, + +  /* 10/100 PCI card - old J2585A */ +  { 0x1030103c, "HP J2585A", 	    HP100_BUS_PCI }, + +  /* 10/100 PCI card - new J2585B - master capable */ +  { 0x1041103c, "HP J2585B",      HP100_BUS_PCI }, + +  /* 10 Mbit Combo Adapter */ +  { 0x1042103c, "HP J2970",       HP100_BUS_PCI }, + +  /* 10 Mbit 10baseT Adapter */ +  { 0x1040103c, "HP J2973",       HP100_BUS_PCI }, + +  /* 10/100 EISA card from Compex */ +  { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA }, + +  /* 10/100 EISA card from Compex - FreedomLine (sq5bpf) */ +  /* Note: plhbrod@mbox.vol.cz reported that same ID have ISA */ +  /*       version of adapter, too... */ +  { 0x0104180e, "FreedomLine 100/VG", HP100_BUS_EISA }, + +  /* 10/100 PCI card from Compex - FreedomLine +   * +   * I think this card doesn't like aic7178 scsi controller, but +   * I haven't tested this much. It works fine on diskless machines. +   *                            Jacek Lipkowski <sq5bpf@acid.ch.pw.edu.pl> +   */ +  { 0x021211f6, "FreedomLine 100/VG", HP100_BUS_PCI }, +   +  /* 10/100 PCI card from Compex (J2585A compatible) */ +  { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI } + +}; + +#define HP100_EISA_IDS_SIZE	(sizeof(hp100_eisa_ids)/sizeof(struct hp100_eisa_id)) + +static struct hp100_pci_id hp100_pci_ids[] = { +  { PCI_VENDOR_ID_HP, 		PCI_DEVICE_ID_HP_J2585A }, +  { PCI_VENDOR_ID_HP,		PCI_DEVICE_ID_HP_J2585B }, +  { PCI_VENDOR_ID_COMPEX,	PCI_DEVICE_ID_COMPEX_ENET100VG4 }, +  { PCI_VENDOR_ID_COMPEX2,	PCI_DEVICE_ID_COMPEX2_100VG } +}; + +#define HP100_PCI_IDS_SIZE	(sizeof(hp100_pci_ids)/sizeof(struct hp100_pci_id)) + +static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO; +static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX; +static int hp100_mode = 1; + +#ifdef LINUX_2_1 +MODULE_PARM( hp100_rx_ratio, "1i" ); +MODULE_PARM( hp100_priority_tx, "1i" ); +MODULE_PARM( hp100_mode, "1i" ); +#endif + +/* + *  prototypes + */ + +static int  hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn  ); +static int  hp100_open( struct device *dev ); +static int  hp100_close( struct device *dev ); +static int  hp100_start_xmit( struct sk_buff *skb, struct device *dev ); +static int  hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev ); +static void hp100_rx( struct device *dev ); +static hp100_stats_t *hp100_get_stats( struct device *dev ); +static void hp100_misc_interrupt( struct device *dev ); +static void hp100_update_stats( struct device *dev ); +static void hp100_clear_stats( int ioaddr ); +static void hp100_set_multicast_list( struct device *dev); +static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ); +static void hp100_start_interface( struct device *dev ); +static void hp100_stop_interface( struct device *dev ); +static void hp100_load_eeprom( struct device *dev, u_short ioaddr ); +static int  hp100_sense_lan( struct device *dev ); +static int  hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ); +static int  hp100_down_vg_link( struct device *dev ); +static void hp100_cascade_reset( struct device *dev, u_short enable ); +static void hp100_BM_shutdown( struct device *dev ); +static void hp100_mmuinit( struct device *dev ); +static void hp100_init_pdls( struct device *dev ); +static int  hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr); +static int  hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr); +static void hp100_rxfill( struct device *dev ); +static void hp100_hwinit( struct device *dev ); +static void hp100_clean_txring( struct device *dev ); +#ifdef HP100_DEBUG +static void hp100_RegisterDump( struct device *dev ); +#endif + +/* TODO: This function should not really be needed in a good design... */ +static void wait( void ) +{ +  udelay( 1000 ); +} + +/* + *  probe functions + *  These functions should - if possible - avoid doing write operations + *  since this could cause problems when the card is not installed. + */ +  +__initfunc(int hp100_probe( struct device *dev )) +{ +  int base_addr = dev ? dev -> base_addr : 0; +  int ioaddr = 0; +#ifdef CONFIG_PCI +  int pci_start_index = 0; +#endif + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4200, TRACE ); +  printk( "hp100: %s: probe\n", dev->name ); +#endif + +  if ( base_addr > 0xff )	/* Check a single specified location. */ +    { +      if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL; +      if ( base_addr < 0x400 ) +        return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 ); +      if ( EISA_bus && base_addr >= 0x1c38 && ( (base_addr - 0x1c38) & 0x3ff ) == 0 ) +        return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 ); +#ifdef CONFIG_PCI +      printk( "hp100: %s: You may specify card # in i/o address parameter for PCI bus...", dev->name ); +      return hp100_probe1( dev, base_addr, HP100_BUS_PCI, 0, 0 ); +#else +      return -ENODEV; +#endif +    } +  else +#ifdef CONFIG_PCI +    if ( base_addr > 0 && base_addr < 8 + 1 ) +      pci_start_index = 0x100 | ( base_addr - 1 ); +    else +#endif +      if ( base_addr != 0 ) return -ENXIO; + +  /* at first - scan PCI bus(es) */ + +#ifdef CONFIG_PCI +  if ( pcibios_present() ) +    { +      int pci_index; + +#ifdef HP100_DEBUG_PCI +      printk( "hp100: %s: PCI BIOS is present, checking for devices..\n", dev->name ); +#endif +      for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ ) +        { +          u_char pci_bus, pci_device_fn; +          u_short pci_command; +          int pci_id_index; + +	  for ( pci_id_index = 0; pci_id_index < HP100_PCI_IDS_SIZE; pci_id_index++ ) +            if ( pcibios_find_device( hp100_pci_ids[ pci_id_index ].vendor, +            			      hp100_pci_ids[ pci_id_index ].device, +                                      pci_index, &pci_bus, +                                      &pci_device_fn ) == 0 ) goto __pci_found; +          break; + +	  __pci_found: +          pcibios_read_config_dword( pci_bus, pci_device_fn, +                                     PCI_BASE_ADDRESS_0, &ioaddr ); + +          ioaddr &= ~3;    /* remove I/O space marker in bit 0. */ + +          if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; + +          pcibios_read_config_word( pci_bus, pci_device_fn, +                                    PCI_COMMAND, &pci_command ); +          if ( !( pci_command & PCI_COMMAND_IO ) ) +            { +#ifdef HP100_DEBUG +              printk( "hp100: %s: PCI I/O Bit has not been set. Setting...\n", dev->name ); +#endif +              pci_command |= PCI_COMMAND_IO; +              pcibios_write_config_word( pci_bus, pci_device_fn, +                                         PCI_COMMAND, pci_command ); +            } +          if ( !( pci_command & PCI_COMMAND_MASTER ) ) +            { +#ifdef HP100_DEBUG +              printk( "hp100: %s: PCI Master Bit has not been set. Setting...\n", dev->name ); +#endif +              pci_command |= PCI_COMMAND_MASTER; +              pcibios_write_config_word( pci_bus, pci_device_fn, +                                         PCI_COMMAND, pci_command ); +            } +#ifdef HP100_DEBUG +          printk( "hp100: %s: PCI adapter found at 0x%x\n", dev->name, ioaddr ); +#endif +	  if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 ) +	    return 0; +        } +    } +  if ( pci_start_index > 0 ) return -ENODEV; +#endif /* CONFIG_PCI */ + +  /* Second: Probe all EISA possible port regions (if EISA bus present) */ +  for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 ) +    { +      if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; +      if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0; +    } + +  /* Third Probe all ISA possible port regions */ +  for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 ) +    { +      if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue; +      if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0; +    } + +  return -ENODEV; +} + + +__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn )) +{ +  int i; + +  u_char uc, uc_1; +  u_int eisa_id; +  u_int chip; +  u_int memory_size = 0, virt_memory_size = 0; +  u_short local_mode, lsw; +  short mem_mapped; +  u_int *mem_ptr_phys, *mem_ptr_virt; +  struct hp100_private *lp; +  struct hp100_eisa_id *eid; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4201, TRACE ); +  printk("hp100: %s: probe1\n",dev->name); +#endif + +  if ( dev == NULL ) +    { +#ifdef HP100_DEBUG +      printk( "hp100_probe1: %s: dev == NULL ?\n", dev->name ); +#endif +      return EIO; +    } +   +  if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE )  +    { +      return -ENODEV; +    } +  else +    { +      chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK; +#ifdef HP100_DEBUG +      if ( chip == HP100_CHIPID_SHASTA ) +        printk("hp100: %s: Shasta Chip detected. (This is a pre 802.12 chip)\n", dev->name); +      else if ( chip == HP100_CHIPID_RAINIER ) +        printk("hp100: %s: Rainier Chip detected. (This is a pre 802.12 chip)\n", dev->name); +      else if ( chip == HP100_CHIPID_LASSEN ) +        printk("hp100: %s: Lassen Chip detected.\n", dev->name); +      else +        printk("hp100: %s: Warning: Unknown CASCADE chip (id=0x%.4x).\n",dev->name,chip); +#endif  +    } + +  dev->base_addr = ioaddr; + +  hp100_page( ID_MAC_ADDR ); +  for ( i = uc = eisa_id = 0; i < 4; i++ ) +    { +      eisa_id >>= 8; +      uc_1 = hp100_inb( BOARD_ID + i ); +      eisa_id |= uc_1 << 24; +      uc += uc_1; +    } +  uc += hp100_inb( BOARD_ID + 4 ); + +  if ( uc != 0xff )    /* bad checksum? */ +    { +      printk("hp100_probe: %s: bad EISA ID checksum at base port 0x%x\n", dev->name, ioaddr ); +      return -ENODEV; +    } + +  for ( i=0; i < HP100_EISA_IDS_SIZE; i++) +    if ( hp100_eisa_ids[ i ].id == eisa_id ) +      break; +  if ( i >= HP100_EISA_IDS_SIZE ) { +    for ( i = 0; i < HP100_EISA_IDS_SIZE; i++) +      if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) ) +        break; +    if ( i >= HP100_EISA_IDS_SIZE ) { +      printk( "hp100_probe: %s: card at port 0x%x isn't known (id = 0x%x)\n", dev -> name, ioaddr, eisa_id ); +        return -ENODEV; +    } +  } +  eid = &hp100_eisa_ids[ i ]; +  if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) ) +    { +      printk( "hp100_probe: %s: newer version of card %s at port 0x%x - unsupported\n", +	      dev->name, eid->name, ioaddr ); +      return -ENODEV; +    } + +  for ( i = uc = 0; i < 7; i++ ) +    uc += hp100_inb( LAN_ADDR + i ); +  if ( uc != 0xff ) +    { +      printk("hp100_probe: %s: bad lan address checksum (card %s at port 0x%x)\n", +	     dev->name, eid->name, ioaddr ); +      return -EIO; +    } + +  /* Make sure, that all registers are correctly updated... */ + +  hp100_load_eeprom( dev, ioaddr ); +  wait(); + +  /* +   * Determine driver operation mode +   * +   * Use the variable "hp100_mode" upon insmod or as kernel parameter to +   * force driver modes: +   * hp100_mode=1 -> default, use busmaster mode if configured. +   * hp100_mode=2 -> enable shared memory mode  +   * hp100_mode=3 -> force use of i/o mapped mode. +   * hp100_mode=4 -> same as 1, but re-set the enable bit on the card. +   */ + +  /* +   * LSW values: +   *   0x2278 -> J2585B, PnP shared memory mode +   *   0x2270 -> J2585B, shared memory mode, 0xdc000 +   *   0xa23c -> J2585B, I/O mapped mode +   *   0x2240 -> EISA COMPEX, BusMaster (Shasta Chip) +   *   0x2220 -> EISA HP, I/O (Shasta Chip) +   *   0x2260 -> EISA HP, BusMaster (Shasta Chip) +   */ + +#if 0 +  local_mode = 0x2270; +  hp100_outw(0xfefe,OPTION_LSW); +  hp100_outw(local_mode|HP100_SET_LB|HP100_SET_HB,OPTION_LSW); +#endif + +  /* hp100_mode value maybe used in future by another card */ +  local_mode=hp100_mode; +  if ( local_mode < 1 || local_mode > 4 ) +    local_mode = 1;		/* default */ +#ifdef HP100_DEBUG +  printk( "hp100: %s: original LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif + +  if(local_mode==3) +    { +      hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW); +      hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW); +      hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); +      printk("hp100: %s: IO mapped mode forced.\n", dev->name); +    } +  else if(local_mode==2) +    { +      hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); +      hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW); +      hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW); +      printk("hp100: %s: Shared memory mode requested.\n", dev->name); +    }  +  else if(local_mode==4) +    { +      if(chip==HP100_CHIPID_LASSEN) +	{ +	  hp100_outw(HP100_BM_WRITE| +		     HP100_BM_READ | HP100_SET_HB, OPTION_LSW); +	  hp100_outw(HP100_IO_EN   |  +		     HP100_MEM_EN  | HP100_RESET_LB, OPTION_LSW); +	  printk("hp100: %s: Busmaster mode requested.\n",dev->name); +	} +      local_mode=1; +    } +		 +  if(local_mode==1) /* default behaviour */ +    { +      lsw = hp100_inw(OPTION_LSW); +     +      if ( (lsw & HP100_IO_EN) && +	   (~lsw & HP100_MEM_EN) && +	   (~lsw & (HP100_BM_WRITE|HP100_BM_READ)) ) +	{ +#ifdef HP100_DEBUG +	  printk("hp100: %s: IO_EN bit is set on card.\n",dev->name); +#endif +	  local_mode=3; +	} +      else if ( chip == HP100_CHIPID_LASSEN && +	        ( lsw & (HP100_BM_WRITE|HP100_BM_READ) ) == +	                (HP100_BM_WRITE|HP100_BM_READ) ) +	{ +	  printk("hp100: %s: Busmaster mode enabled.\n",dev->name); +	  hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); +	} +      else +	{ +#ifdef HP100_DEBUG +	  printk("hp100: %s: Card not configured for BM or BM not supported with this card.\n", dev->name ); +	  printk("hp100: %s: Trying shared memory mode.\n", dev->name); +#endif +	  /* In this case, try shared memory mode */ +	  local_mode=2; +	  hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW); +	  /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */ +	} +    } + +#ifdef HP100_DEBUG +  printk( "hp100: %s: new LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif + +  /* Check for shared memory on the card, eventually remap it */ +  hp100_page( HW_MAP ); +  mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0); +  mem_ptr_phys = mem_ptr_virt = NULL; +  memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07)); +  virt_memory_size = 0; + +  /* For memory mapped or busmaster mode, we want the memory address */ +  if ( mem_mapped || (local_mode==1)) +    { +      mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) |  +				( hp100_inw( MEM_MAP_MSW ) << 16 ) ); +      mem_ptr_phys = (u_int *) ((u_int) mem_ptr_phys & ~0x1fff);  /* 8k alignment */ +      if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 ) +        { +	  printk("hp100: %s: Can only use programmed i/o mode.\n", dev->name); +          mem_ptr_phys = NULL; +          mem_mapped = 0; +	  local_mode=3; /* Use programmed i/o */ +        } +						 +      /* We do not need access to shared memory in busmaster mode */ +      /* However in slave mode we need to remap high (>1GB) card memory  */ +      if(local_mode!=1) /* = not busmaster */ +	{ +	  if ( bus == HP100_BUS_PCI && mem_ptr_phys >= (u_int *)0x100000 ) +	    { +	      /* We try with smaller memory sizes, if ioremap fails */ +	      for(virt_memory_size = memory_size; virt_memory_size>16383; virt_memory_size>>=1) +		{ +		  if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,virt_memory_size))==NULL) +		    { +#ifdef HP100_DEBUG +		      printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys ); +#endif +		    }	 +		  else +		    { +#ifdef HP100_DEBUG +		      printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt); +#endif +		      break; +		    } +		} +														 +	      if(mem_ptr_virt==NULL) /* all ioremap tries failed */ +		{ +		  printk("hp100: %s: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n", dev->name); +		  local_mode=3; +		  virt_memory_size = 0; +		} +	    } +	} +						 +    } + +  if(local_mode==3) /* io mapped forced */ +    { +      mem_mapped = 0; +      mem_ptr_phys = mem_ptr_virt = NULL; +      printk("hp100: %s: Using (slow) programmed i/o mode.\n", dev->name); +    } + +  /* Initialise the "private" data structure for this card. */ +  if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL) +    return -ENOMEM; +  memset( dev->priv, 0, sizeof(struct hp100_private) ); + +  lp = (struct hp100_private *)dev->priv; +  lp->id = eid; +  lp->chip = chip; +  lp->mode = local_mode; +  lp->pci_bus = pci_bus; +  lp->bus = bus; +  lp->pci_device_fn = pci_device_fn; +  lp->priority_tx = hp100_priority_tx; +  lp->rx_ratio = hp100_rx_ratio; +  lp->mem_ptr_phys = mem_ptr_phys; +  lp->mem_ptr_virt = mem_ptr_virt; +  hp100_page( ID_MAC_ADDR ); +  lp->soft_model = hp100_inb( SOFT_MODEL ); +  lp->mac1_mode = HP100_MAC1MODE3; +  lp->mac2_mode = HP100_MAC2MODE3; +  memset( &lp->hash_bytes, 0x00, 8 ); + +  dev->base_addr = ioaddr; + +  lp->memory_size = memory_size; +  lp->virt_memory_size = virt_memory_size; +  lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */ + +  /* memory region for programmed i/o */ +  request_region( dev->base_addr, HP100_REGION_SIZE, eid->name ); + +  dev->open = hp100_open; +  dev->stop = hp100_close; + +  if (lp->mode==1) /* busmaster */ +    dev->hard_start_xmit = hp100_start_xmit_bm; +  else +    dev->hard_start_xmit = hp100_start_xmit; + +  dev->get_stats = hp100_get_stats; +  dev->set_multicast_list = &hp100_set_multicast_list; + +  /* Ask the card for which IRQ line it is configured */ +  hp100_page( HW_MAP ); +  dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK; +  if ( dev->irq == 2 ) +    dev->irq = 9; + +  if(lp->mode==1) /* busmaster */ +    dev->dma=4;  + +  /* Ask the card for its MAC address and store it for later use. */ +  hp100_page( ID_MAC_ADDR ); +  for ( i = uc = 0; i < 6; i++ ) +    dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i ); + +  /* Reset statistics (counters) */ +  hp100_clear_stats( ioaddr ); + +  ether_setup( dev ); + +  /* If busmaster mode is wanted, a dma-capable memory area is needed for +   * the rx and tx PDLs  +   * PCI cards can access the whole PC memory. Therefore GFP_DMA is not +   * needed for the allocation of the memory area.  +   */ + +  /* TODO: We do not need this with old cards, where PDLs are stored +   * in the cards shared memory area. But currently, busmaster has been +   * implemented/tested only with the lassen chip anyway... */ +  if(lp->mode==1) /* busmaster */ +    { +      /* Get physically continous memory for TX & RX PDLs    */ +      if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL) +        return -ENOMEM; +      lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f)); +      memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f); + +#ifdef HP100_DEBUG_BM +      printk("hp100: %s: Reserved DMA memory from 0x%x to 0x%x\n", +      	     dev->name, +             (u_int)lp->page_vaddr_algn, +             (u_int)lp->page_vaddr_algn+MAX_RINGSIZE); +#endif +      lp->rxrcommit  = lp->txrcommit = 0; +      lp->rxrhead    = lp->rxrtail   = &(lp->rxring[0]); +      lp->txrhead    = lp->txrtail   = &(lp->txring[0]);  +    } + +  /* Initialise the card. */ +  /* (I'm not really sure if it's a good idea to do this during probing, but  +   * like this it's assured that the lan connection type can be sensed +   * correctly) +   */ +  hp100_hwinit( dev ); + +  /* Try to find out which kind of LAN the card is connected to. */ +  lp->lan_type = hp100_sense_lan( dev ); +      +  /* Print out a message what about what we think we have probed. */ +  printk( "hp100: %s: %s at 0x%x, IRQ %d, ", +          dev->name, lp->id->name, ioaddr, dev->irq ); +  switch ( bus ) { +  case HP100_BUS_EISA: printk( "EISA" ); break; +  case HP100_BUS_PCI:  printk( "PCI" );  break; +  default:     printk( "ISA" );  break; +  } +  printk( " bus, %dk SRAM (rx/tx %d%%).\n", +          lp->memory_size >> 10, lp->rx_ratio ); + +  if ( lp->mode==2 ) /* memory mapped */  +    { +      printk( "hp100: %s: Memory area at 0x%lx-0x%lx", +              dev->name,(u_long)mem_ptr_phys, +              ((u_long)mem_ptr_phys+(mem_ptr_phys>(u_int *)0x100000?(u_long)lp->memory_size:16*1024))-1 ); +      if ( mem_ptr_virt ) +	printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt ); +      printk( ".\n" ); + +      /* Set for info when doing ifconfig */ +      dev->mem_start = (u_long)mem_ptr_phys; +      dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size; +    } +  printk( "hp100: %s: ", dev->name ); +  if ( lp->lan_type != HP100_LAN_ERR ) +    printk( "Adapter is attached to " ); +  switch ( lp->lan_type ) { +  case HP100_LAN_100: +    printk( "100Mb/s Voice Grade AnyLAN network.\n" ); +    break; +  case HP100_LAN_10: +    printk( "10Mb/s network.\n" ); +    break; +  default: +    printk( "Warning! Link down.\n" ); +  } + +  return 0; +} + + +/* This procedure puts the card into a stable init state */ +static void hp100_hwinit( struct device *dev ) +{ +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4202, TRACE ); +  printk("hp100: %s: hwinit\n", dev->name); +#endif + +  /* Initialise the card. -------------------------------------------- */ + +  /* Clear all pending Ints and disable Ints */ +  hp100_page( PERFORMANCE ); +  hp100_outw( 0xfefe, IRQ_MASK );    /* mask off all ints */ +  hp100_outw( 0xffff, IRQ_STATUS );  /* clear all pending ints */ + +  hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); +  hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW ); + +  if(lp->mode==1) +    {  +      hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */ +      wait(); +    } +  else +    { +      hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW ); +      hp100_cascade_reset( dev, TRUE ); +      hp100_page( MAC_CTRL ); +      hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1);  +    } +		 +  /* Initiate EEPROM reload */ +  hp100_load_eeprom( dev, 0 ); +		 +  wait(); +		 +  /* Go into reset again. */ +  hp100_cascade_reset( dev, TRUE ); +		 +  /* Set Option Registers to a safe state  */ +  hp100_outw( HP100_DEBUG_EN | +              HP100_RX_HDR   | +              HP100_EE_EN    | +              HP100_BM_WRITE | +              HP100_BM_READ  | HP100_RESET_HB | +              HP100_FAKE_INT | +              HP100_INT_EN   | +	      HP100_MEM_EN   | +              HP100_IO_EN    | HP100_RESET_LB, OPTION_LSW); +		 +  hp100_outw( HP100_TRI_INT  | +              HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); + +  hp100_outb( HP100_PRIORITY_TX | +              HP100_ADV_NXT_PKT | +              HP100_TX_CMD      | HP100_RESET_LB, OPTION_MSW ); + +  /* TODO: Configure MMU for Ram Test. */ +  /* TODO: Ram Test. */ + +  /* Re-check if adapter is still at same i/o location      */ +  /* (If the base i/o in eeprom has been changed but the    */ +  /* registers had not been changed, a reload of the eeprom */ +  /* would move the adapter to the address stored in eeprom */ +   +  /* TODO: Code to implement. */ +		 +  /* Until here it was code from HWdiscover procedure. */ +  /* Next comes code from mmuinit procedure of SCO BM driver which is +   * called from HWconfigure in the SCO driver.  */ + +  /* Initialise MMU, eventually switch on Busmaster Mode, initialise  +   * multicast filter... +   */ +  hp100_mmuinit( dev ); + +  /* We don't turn the interrupts on here - this is done by start_interface. */ +  wait(); /* TODO: Do we really need this? */ + +  /* Enable Hardware (e.g. unreset) */ +  hp100_cascade_reset( dev, FALSE ); + +  /* ------- initialisation complete ----------- */ + +  /* Finally try to log in the Hub if there may be a VG connection. */ +  if( lp->lan_type != HP100_LAN_10 ) +    hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ +} + + +/*  + * mmuinit - Reinitialise Cascade MMU and MAC settings. + * Note: Must already be in reset and leaves card in reset.  + */ +static void hp100_mmuinit( struct device *dev ) +{ +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  int i; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4203, TRACE ); +  printk("hp100: %s: mmuinit\n",dev->name); +#endif + +#ifdef HP100_DEBUG +  if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) ) +    { +      printk("hp100: %s: Not in reset when entering mmuinit. Fix me.\n",dev->name); +      return; +    } +#endif + +  /* Make sure IRQs are masked off and ack'ed. */ +  hp100_page( PERFORMANCE ); +  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */ +  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */ + +  /* +   * Enable Hardware  +   * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En +   * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable +   * - Clear Priority, Advance Pkt and Xmit Cmd +   */ + +  hp100_outw( HP100_DEBUG_EN | +              HP100_RX_HDR   | +              HP100_EE_EN    | HP100_RESET_HB | +              HP100_IO_EN    | +              HP100_FAKE_INT | +              HP100_INT_EN   | HP100_RESET_LB, OPTION_LSW ); +  	 +  hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW); +		 +  if(lp->mode==1) /* busmaster */ +    { +      hp100_outw( HP100_BM_WRITE |  +		  HP100_BM_READ  | +		  HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); +    } +  else if(lp->mode==2) /* memory mapped */ +    { +      hp100_outw( HP100_BM_WRITE | +		  HP100_BM_READ  | HP100_RESET_HB, OPTION_LSW ); +      hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); +      hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW ); +      hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW ); +    } +  else if( lp->mode==3 ) /* i/o mapped mode */ +    { +      hp100_outw( HP100_MMAP_DIS | HP100_SET_HB |  +                  HP100_IO_EN    | HP100_SET_LB, OPTION_LSW ); +    } + +  hp100_page( HW_MAP ); +  hp100_outb( 0, EARLYRXCFG ); +  hp100_outw( 0, EARLYTXCFG ); +   +  /* +   * Enable Bus Master mode +   */ +  if(lp->mode==1) /* busmaster */ +    { +      /* Experimental: Set some PCI configuration bits */ +      hp100_page( HW_MAP ); +      hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */ +      hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */ + +      /* PCI Bus failures should result in a Misc. Interrupt */ +      hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2); +					 +      hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW ); +      hp100_page( HW_MAP );					 +      /* Use Burst Mode and switch on PAGE_CK */ +      hp100_orb( HP100_BM_BURST_RD | +                 HP100_BM_BURST_WR, BM); +      if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) +	hp100_orb( HP100_BM_PAGE_CK, BM ); +      hp100_orb( HP100_BM_MASTER, BM );		 +    } +  else /* not busmaster */ +    { +      hp100_page(HW_MAP); +      hp100_andb(~HP100_BM_MASTER, BM ); +    } + +  /* +   * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs +   */   +  hp100_page( MMU_CFG ); +  if(lp->mode==1) /* only needed for Busmaster */ +    { +      int xmit_stop, recv_stop; + +      if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA)) +        { +          int pdl_stop; +           +          /* +           * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and +           * 4 bytes for header). We will leave NUM_RXPDLS * 508 (rounded +           * to the next higher 1k boundary) bytes for the rx-pdl's +	   * Note: For non-etr chips the transmit stop register must be +	   * programmed on a 1k boundary, i.e. bits 9:0 must be zero.  +	   */ +          pdl_stop  = lp->memory_size; +          xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff); +          recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff); +          hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP ); +#ifdef HP100_DEBUG_BM +          printk("hp100: %s: PDL_STOP = 0x%x\n", dev->name, pdl_stop); +#endif +        } +      else /* ETR chip (Lassen) in busmaster mode */ +        { +          xmit_stop = ( lp->memory_size ) - 1; +          recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff); +        } + +      hp100_outw( xmit_stop>>4 , TX_MEM_STOP ); +      hp100_outw( recv_stop>>4 , RX_MEM_STOP ); +#ifdef HP100_DEBUG_BM +      printk("hp100: %s: TX_STOP  = 0x%x\n",dev->name,xmit_stop>>4); +      printk("hp100: %s: RX_STOP  = 0x%x\n",dev->name,recv_stop>>4); +#endif +    }   +  else /* Slave modes (memory mapped and programmed io)  */ +    { +      hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP ); +      hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP );   +#ifdef HP100_DEBUG +      printk("hp100: %s: TX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(TX_MEM_STOP)); +      printk("hp100: %s: RX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(RX_MEM_STOP)); +#endif +    } + +  /* Write MAC address into page 1 */ +  hp100_page( MAC_ADDRESS ); +  for ( i = 0; i < 6; i++ ) +    hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i ); +   +  /* Zero the multicast hash registers */ +  for ( i = 0; i < 8; i++ ) +    hp100_outb( 0x0, HASH_BYTE0 + i );   +   +  /* Set up MAC defaults */ +  hp100_page( MAC_CTRL ); +  +  /* Go to LAN Page and zero all filter bits */ +  /* Zero accept error, accept multicast, accept broadcast and accept */ +  /* all directed packet bits */ +  hp100_andb( ~(HP100_RX_EN| +		HP100_TX_EN| +		HP100_ACC_ERRORED| +		HP100_ACC_MC| +		HP100_ACC_BC| +		HP100_ACC_PHY),   MAC_CFG_1 ); + +  hp100_outb( 0x00, MAC_CFG_2 ); + +  /* Zero the frame format bit. This works around a training bug in the */ +  /* new hubs. */ +  hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */	 + +  if(lp->priority_tx) +    hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW ); +  else +    hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW ); +	 +  hp100_outb( HP100_ADV_NXT_PKT | +	      HP100_TX_CMD      | HP100_RESET_LB, OPTION_MSW ); +	 +  /* If busmaster, initialize the PDLs */ +  if(lp->mode==1) +    hp100_init_pdls( dev ); + +  /* Go to performance page and initalize isr and imr registers */ +  hp100_page( PERFORMANCE ); +  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */ +  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */ +} + + +/* + *  open/close functions + */ + +static int hp100_open( struct device *dev ) +{ +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +#ifdef HP100_DEBUG_B +  int ioaddr=dev->base_addr; +#endif + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4204, TRACE ); +  printk("hp100: %s: open\n",dev->name); +#endif +		 +  /* New: if bus is PCI or EISA, interrupts might be shared interrupts */ +  if ( request_irq(dev->irq, hp100_interrupt, +  		   lp->bus==HP100_BUS_PCI||lp->bus==HP100_BUS_EISA?SA_SHIRQ:SA_INTERRUPT, +  		   lp->id->name, dev)) +    { +      printk( "hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq ); +      return -EAGAIN; +    } + +  MOD_INC_USE_COUNT; + +  dev->tbusy = 0; +  dev->trans_start = jiffies; +  dev->interrupt = 0; +  dev->start = 1; + +  lp->lan_type = hp100_sense_lan( dev ); +  lp->mac1_mode = HP100_MAC1MODE3; +  lp->mac2_mode = HP100_MAC2MODE3; +  memset( &lp->hash_bytes, 0x00, 8 ); + +  hp100_stop_interface( dev ); +  +  hp100_hwinit( dev ); + +  hp100_start_interface( dev ); /* sets mac modes, enables interrupts */ + +  return 0; +} + + +/* The close function is called when the interface is to be brought down */ +static int hp100_close( struct device *dev ) +{ +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4205, TRACE ); +  printk("hp100: %s: close\n", dev->name); +#endif + +  hp100_page( PERFORMANCE ); +  hp100_outw( 0xfefe, IRQ_MASK );    /* mask off all IRQs */ + +  hp100_stop_interface( dev ); + +  if ( lp->lan_type == HP100_LAN_100 )  +    lp->hub_status=hp100_login_to_vg_hub( dev, FALSE ); + +  dev->tbusy = 1; +  dev->start = 0; + +  free_irq( dev->irq, dev ); + +#ifdef HP100_DEBUG +  printk( "hp100: %s: close LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) ); +#endif + +  MOD_DEC_USE_COUNT; +  return 0; +} + + +/* + * Configure the PDL Rx rings and LAN  + */ +static void hp100_init_pdls( struct device *dev ) +{ +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  hp100_ring_t         *ringptr; +  u_int                *pageptr; +  int                  i; + +#ifdef HP100_DEBUG_B +  int ioaddr = dev->base_addr; +#endif + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4206, TRACE ); +  printk("hp100: %s: init pdls\n", dev->name); +#endif + +  if(0==lp->page_vaddr_algn) +    printk("hp100: %s: Warning: lp->page_vaddr_algn not initialised!\n",dev->name); +  else +    { +      /* pageptr shall point into the DMA accessible memory region  */ +      /* we use this pointer to status the upper limit of allocated */ +      /* memory in the allocated page. */ +      /* note: align the pointers to the pci cache line size */ +      memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero  Rx/Tx ring page */ +      pageptr=lp->page_vaddr_algn; +						 +      lp->rxrcommit =0; +      ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]); +       +      /* Initialise Rx Ring */ +      for (i=MAX_RX_PDL-1; i>=0; i--) +        { +          lp->rxring[i].next = ringptr; +          ringptr=&(lp->rxring[i]); +          pageptr+=hp100_init_rxpdl(dev, ringptr, pageptr); +        } +       +      /* Initialise Tx Ring */ +      lp->txrcommit = 0; +      ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]); +      for (i=MAX_TX_PDL-1; i>=0; i--) +        { +          lp->txring[i].next = ringptr; +          ringptr=&(lp->txring[i]); +          pageptr+=hp100_init_txpdl(dev, ringptr, pageptr); +        } +    } +} + + +/* These functions "format" the entries in the pdl structure   */ +/* They return how much memory the fragments need.            */ +static int hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr ) +{ +  /* pdlptr is starting adress for this pdl */ + +  if( 0!=( ((unsigned)pdlptr) & 0xf) ) +    printk("hp100: %s: Init rxpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned)pdlptr); + +  ringptr->pdl       = pdlptr+1;  +  ringptr->pdl_paddr = virt_to_bus(pdlptr+1); +  ringptr->skb       = (void *) NULL;  + +  /*  +   * Write address and length of first PDL Fragment (which is used for +   * storing the RX-Header +   * We use the 4 bytes _before_ the PDH in the pdl memory area to  +   * store this information. (PDH is at offset 0x04) +   */ +  /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */ + +  *(pdlptr+2) =(u_int) virt_to_bus(pdlptr);  /* Address Frag 1 */  +  *(pdlptr+3) = 4;                           /* Length  Frag 1 */ + +  return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 ); +} + + +static int hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr ) +{ +  if( 0!=( ((unsigned)pdlptr) & 0xf) ) +    printk("hp100: %s: Init txpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned) pdlptr); + +  ringptr->pdl       = pdlptr; /* +1; */   +  ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */ +  ringptr->skb = (void *) NULL; +   +  return((((MAX_TX_FRAG*2+2)+3)/4)*4); +} + + +/* + * hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes  + * for possible odd word alignment rounding up to next dword and set PDL + * address for fragment#2  + * Returns: 0 if unable to allocate skb_buff + *          1 if successful + */ +int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev ) +{ +#ifdef HP100_DEBUG_B +  int ioaddr = dev->base_addr; +#endif +#ifdef HP100_DEBUG_BM +  u_int *p; +#endif + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4207, TRACE ); +  printk("hp100: %s: build rx pdl\n", dev->name); +#endif + +  /* Allocate skb buffer of maximum size */ +  /* Note: This depends on the alloc_skb functions allocating more  +   * space than requested, i.e. aligning to 16bytes */ + +  ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 ); +		 +  if(NULL!=ringptr->skb) +    { +      /*  +       * Reserve 2 bytes at the head of the buffer to land the IP header +       * on a long word boundary (According to the Network Driver section +       * in the Linux KHG, this should help to increase performance.) +       */ +      skb_reserve(ringptr->skb, 2); + +      ringptr->skb->dev=dev;  +      ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE ); +						 +      /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */ +      /* Note: 1st Fragment is used for the 4 byte packet status +       * (receive header). Its PDL entries are set up by init_rxpdl. So  +       * here we only have to set up the PDL fragment entries for the data +       * part. Those 4 bytes will be stored in the DMA memory region  +       * directly before the PDL.  +       */ +#ifdef HP100_DEBUG_BM +      printk("hp100: %s: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n", +      	     dev->name, +	     (u_int) ringptr->pdl, +	     ((MAX_ETHER_SIZE+2+3)/4)*4, +	     (unsigned int) ringptr->skb->data); +#endif + +      ringptr->pdl[0] = 0x00020000;                          /* Write PDH */ +      ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data));  +      ringptr->pdl[4] = MAX_ETHER_SIZE;                 /* Length of Data */ +						 +#ifdef HP100_DEBUG_BM +      for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++) +        printk("hp100: %s: Adr 0x%.8x = 0x%.8x\n",dev->name,(u_int) p,(u_int) *p ); +#endif +      return(1); +    } +  /* else: */ +  /* alloc_skb failed (no memory) -> still can receive the header +   * fragment into PDL memory. make PDL safe by clearing msgptr and +   * making the PDL only 1 fragment (i.e. the 4 byte packet status) +   */ +#ifdef HP100_DEBUG_BM +  printk("hp100: %s: build_rx_pdl: PDH@0x%x, No space for skb.\n", +  	 dev->name, +	 (u_int) ringptr->pdl); +#endif + +  ringptr->pdl[0]=0x00010000;   /* PDH: Count=1 Fragment */ + +  return(0); +} + + +/* + *  hp100_rxfill - attempt to fill the Rx Ring will empty skb's + * + * Makes assumption that skb's are always contiguous memory areas and + * therefore PDLs contain only 2 physical fragments. + * -  While the number of Rx PDLs with buffers is less than maximum + *      a.  Get a maximum packet size skb + *      b.  Put the physical address of the buffer into the PDL. + *      c.  Output physical address of PDL to adapter. + */ +static void hp100_rxfill( struct device *dev ) +{ +  int ioaddr=dev->base_addr;  + +  struct hp100_private  *lp      = (struct hp100_private *)dev->priv; +  hp100_ring_t    *ringptr; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4208, TRACE ); +  printk("hp100: %s: rxfill\n",dev->name); +#endif +		 +  hp100_page( PERFORMANCE ); + +  while (lp->rxrcommit < MAX_RX_PDL) +    { +      /* +      ** Attempt to get a buffer and build a Rx PDL. +      */ +      ringptr = lp->rxrtail; +      if (0 == hp100_build_rx_pdl( ringptr, dev )) +        { +          return;      /* None available, return */ +        } +       +      /* Hand this PDL over to the card */ +      /* Note: This needs performance page selected! */ +#ifdef HP100_DEBUG_BM +      printk("hp100: %s: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n", +      	     dev->name, +             lp->rxrcommit, +             (u_int)ringptr->pdl, +             (u_int)ringptr->pdl_paddr, +             (u_int)ringptr->pdl[3]); +#endif + +      hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA);  +       +      lp->rxrcommit += 1; +      lp->rxrtail = ringptr->next; +    } +} + + +/* + * BM_shutdown - shutdown bus mastering and leave chip in reset state + */ + +static void hp100_BM_shutdown( struct device *dev ) +{ +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  unsigned long time; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4209, TRACE ); +  printk("hp100: %s: bm shutdown\n",dev->name); +#endif + +  hp100_page( PERFORMANCE ); +  hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */ +  hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */ + +  /* Ensure Interrupts are off */ +  hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW ); + +  /* Disable all MAC activity */ +  hp100_page( MAC_CTRL ); +  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );  /* stop rx/tx */ + +  /* If cascade MMU is not already in reset */ +  if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) ) +    { +      /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so +       * MMU pointers will not be reset out from underneath +       */ +      hp100_page( MAC_CTRL ); +      for(time=0; time<5000; time++) +        { +          if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))== +              (HP100_TX_IDLE|HP100_RX_IDLE) ) break; +        } +       +      /* Shutdown algorithm depends on the generation of Cascade */ +      if( lp->chip==HP100_CHIPID_LASSEN ) +        { /* ETR shutdown/reset */ +          /* Disable Busmaster mode and wait for bit to go to zero. */ +          hp100_page(HW_MAP); +          hp100_andb( ~HP100_BM_MASTER, BM ); +          /* 100 ms timeout */ +          for(time=0; time<32000; time++) +            { +              if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break; +            } +        } +      else +        { /* Shasta or Rainier Shutdown/Reset */ +          /* To ensure all bus master inloading activity has ceased, +           * wait for no Rx PDAs or no Rx packets on card.  +           */ +          hp100_page( PERFORMANCE ); +          /* 100 ms timeout */ +          for(time=0; time<10000; time++) +            { +              /* RX_PDL: PDLs not executed. */ +              /* RX_PKT_CNT: RX'd packets on card. */ +              if ( (hp100_inb( RX_PDL ) == 0) && +                   (hp100_inb( RX_PKT_CNT ) == 0) ) break; +            } +           +          if(time>=10000) +            printk("hp100: %s: BM shutdown error.\n", dev->name); +           +          /* To ensure all bus master outloading activity has ceased, +           * wait until the Tx PDA count goes to zero or no more Tx space +           * available in the Tx region of the card.  +           */ +          /* 100 ms timeout */ +          for(time=0; time<10000; time++) { +            if ( (0 == hp100_inb( TX_PKT_CNT )) && +                 (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break; +          }  +           +          /* Disable Busmaster mode */ +          hp100_page(HW_MAP); +          hp100_andb( ~HP100_BM_MASTER, BM ); +        } /* end of shutdown procedure for non-etr parts */   +						 +      hp100_cascade_reset( dev, TRUE ); +    } +  hp100_page( PERFORMANCE ); +  /* hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW ); */ +  /* Busmaster mode should be shut down now. */ +} + + + +/*  + *  transmit functions + */ + +/* tx function for busmaster mode */ +static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev ) +{ +  unsigned long flags; +  int i, ok_flag; +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  hp100_ring_t *ringptr; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4210, TRACE ); +  printk("hp100: %s: start_xmit_bm\n",dev->name); +#endif + +  if ( skb==NULL ) +    { +#ifndef LINUX_2_1 +      dev_tint( dev ); +#endif +      return 0; +    } +	 +  if ( skb->len <= 0 ) return 0; +	 +  /* Get Tx ring tail pointer */ +  if( lp->txrtail->next==lp->txrhead ) +    { +      /* No memory. */ +#ifdef HP100_DEBUG +      printk("hp100: %s: start_xmit_bm: No TX PDL available.\n", dev->name); +#endif +      /* not waited long enough since last tx? */ +      if ( jiffies - dev->trans_start < HZ ) return -EAGAIN; + +      if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ +	{ +	  hp100_stop_interface( dev ); +	  if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) +	    { +	      printk( "hp100: %s: no connection found - check wire\n", dev->name ); +	      hp100_start_interface( dev );  /* 10Mb/s RX pkts maybe handled */ +	      return -EIO; +	    } +	  if ( lp->lan_type == HP100_LAN_100 ) +	    lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ +	  hp100_start_interface( dev ); +	} +				 +      if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) +	/* we have a 100Mb/s adapter but it isn't connected to hub */ +	{ +	  printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name ); +	  hp100_stop_interface( dev ); +	  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); +	  hp100_start_interface( dev ); +	} +      else +	{ +	  hp100_ints_off(); +	  i = hp100_sense_lan( dev ); +	  hp100_ints_on(); +	  if ( i == HP100_LAN_ERR ) +	    printk( "hp100: %s: link down detected\n", dev->name ); +	  else +	    if ( lp->lan_type != i ) /* cable change! */ +	      { +		/* it's very hard - all network setting must be changed!!! */ +		printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); +		lp->lan_type = i; +		hp100_stop_interface( dev ); +		if ( lp->lan_type == HP100_LAN_100 ) +		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); +		hp100_start_interface( dev ); +	      } +	    else +	      { +		printk( "hp100: %s: interface reset\n", dev->name ); +		hp100_stop_interface( dev ); +		if ( lp->lan_type == HP100_LAN_100 ) +		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); +		hp100_start_interface( dev ); +	      } +	} + +      dev->trans_start = jiffies; +      return -EAGAIN; +    } +	 +  /* +   * we have to turn int's off before modifying this, otherwise +   * a tx_pdl_cleanup could occur at the same time +   */ +  save_flags( flags ); +  cli(); +  ringptr=lp->txrtail; +  lp->txrtail=ringptr->next; +	 +  /* Check whether packet has minimal packet size */ +  ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; +  i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; +					 +  ringptr->skb=skb; +  ringptr->pdl[0]=((1<<16) | i);                /* PDH: 1 Fragment & length */ +  ringptr->pdl[1]=(u32)virt_to_bus(skb->data);  /* 1st Frag: Adr. of data */ +  if(lp->chip==HP100_CHIPID_SHASTA) +    { +      /* TODO:Could someone who has the EISA card please check if this works? */ +      ringptr->pdl[2]=i; +    } +  else /* Lassen */ +    { +      /* In the PDL, don't use the padded size but the real packet size: */ +      ringptr->pdl[2]=skb->len;              /* 1st Frag: Length of frag */ +    } + +  /* Hand this PDL to the card. */ +  hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */ +	 +  lp->txrcommit++; +  restore_flags( flags ); +	 +  /* Update statistics */	 +  lp->stats.tx_packets++; +#ifdef LINUX_2_1 +  lp->stats.tx_bytes += skb->len; +#endif +  dev->trans_start = jiffies; +	 +  return 0; +} + + +/* clean_txring checks if packets have been sent by the card by reading + * the TX_PDL register from the performance page and comparing it to the + * number of commited packets. It then frees the skb's of the packets that + * obviously have been sent to the network. + * + * Needs the PERFORMANCE page selected.  + */ +static void hp100_clean_txring( struct device *dev ) +{ +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  int    ioaddr = dev->base_addr; +  int    donecount; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4211, TRACE ); +  printk("hp100: %s: clean txring\n", dev->name); +#endif + +  /* How many PDLs have been transmitted? */ +  donecount=(lp->txrcommit)-hp100_inb(TX_PDL); + +#ifdef HP100_DEBUG +  if(donecount>MAX_TX_PDL) +    printk("hp100: %s: Warning: More PDLs transmitted than commited to card???\n",dev->name); +#endif + +  for( ; 0!=donecount; donecount-- ) +    { +#ifdef HP100_DEBUG_BM +      printk("hp100: %s: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n", +             dev->name, +	     (u_int) lp->txrhead->skb->data, +	     lp->txrcommit, +	     hp100_inb(TX_PDL), +	     donecount); +#endif +#ifdef LINUX_2_1 +      dev_kfree_skb( lp->txrhead->skb ); +#else +      dev_kfree_skb( lp->txrhead->skb, FREE_WRITE ); +#endif +      lp->txrhead->skb=(void *)NULL; +      lp->txrhead=lp->txrhead->next; +      lp->txrcommit--; +    } +} + + +/* tx function for slave modes */ +static int hp100_start_xmit( struct sk_buff *skb, struct device *dev ) +{ +  int i, ok_flag; +  int ioaddr = dev->base_addr; +  u_short val; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4212, TRACE ); +  printk("hp100: %s: start_xmit\n", dev->name); +#endif + +  if ( skb==NULL ) +    { +#ifndef LINUX_2_1 +      dev_tint( dev ); +#endif +      return 0; +    } +	 +  if ( skb->len <= 0 ) return 0; +	 +  if ( lp->lan_type < 0 ) /* no LAN type detected yet? */ +    { +      hp100_stop_interface( dev ); +      if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 ) +        { +          printk( "hp100: %s: no connection found - check wire\n", dev->name ); +          hp100_start_interface( dev );  /* 10Mb/s RX packets maybe handled */ +          return -EIO; +        } +      if ( lp->lan_type == HP100_LAN_100 ) +        lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */ +      hp100_start_interface( dev ); +    } + +  /* If there is not enough free memory on the card... */ +  i=hp100_inl(TX_MEM_FREE)&0x7fffffff; +  if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) ) +    { +#ifdef HP100_DEBUG +      printk( "hp100: %s: start_xmit: tx free mem = 0x%x\n", dev->name, i ); +#endif +      /* not waited long enough since last failed tx try? */ +      if ( jiffies - dev->trans_start < HZ )  +	{ +#ifdef HP100_DEBUG +	  printk("hp100: %s: trans_start timing problem\n", dev->name); +#endif +	  return -EAGAIN; +	} +      if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 ) +	/* we have a 100Mb/s adapter but it isn't connected to hub */ +        { +          printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name ); +          hp100_stop_interface( dev ); +          lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); +          hp100_start_interface( dev ); +        } +      else +        { +          hp100_ints_off(); +          i = hp100_sense_lan( dev ); +          hp100_ints_on(); +          if ( i == HP100_LAN_ERR ) +            printk( "hp100: %s: link down detected\n", dev->name ); +	  else +	    if ( lp->lan_type != i ) /* cable change! */ +	      { +		/* it's very hard - all network setting must be changed!!! */ +		printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name ); +		lp->lan_type = i; +		hp100_stop_interface( dev ); +		if ( lp->lan_type == HP100_LAN_100 ) +		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); +		hp100_start_interface( dev ); +	      } +	    else +	      { +		printk( "hp100: %s: interface reset\n", dev->name ); +		hp100_stop_interface( dev ); +		if ( lp->lan_type == HP100_LAN_100 ) +		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); +		hp100_start_interface( dev ); +		udelay(1000); +	      } +        } +      dev->trans_start = jiffies; +      return -EAGAIN; +    } + +  for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ ) +    { +#ifdef HP100_DEBUG_TX +      printk( "hp100: %s: start_xmit: busy\n", dev->name ); +#endif +    } +	 +  hp100_ints_off(); +  val = hp100_inw( IRQ_STATUS ); +  /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set +   * when the current packet being transmitted on the wire is completed. */ +  hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS );  +#ifdef HP100_DEBUG_TX +  printk("hp100: %s: start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",dev->name,val,hp100_inw(IRQ_MASK),(int)skb->len ); +#endif + +  ok_flag = skb->len >= HP100_MIN_PACKET_SIZE; +  i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE; + +  hp100_outw( i, DATA32 );       /* tell card the total packet length */ +  hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length    */ +	 +  if ( lp->mode==2 ) /* memory mapped */ +    { +      if ( lp->mem_ptr_virt ) /* high pci memory was remapped */ +	{ +	  /* Note: The J2585B needs alignment to 32bits here!  */ +	  memcpy( lp->mem_ptr_virt, skb->data, ( skb->len + 3 ) & ~3 ); +	  if ( !ok_flag ) +	    memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len ); +	} +      else +	{ +	  /* Note: The J2585B needs alignment to 32bits here!  */ +	  memcpy_toio( lp->mem_ptr_phys, skb->data, (skb->len + 3) & ~3 ); +	  if ( !ok_flag ) +	    memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len ); +	} +    } +  else /* programmed i/o */ +    { +      outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 ); +      if ( !ok_flag ) +	for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 ) +	  hp100_outl( 0, DATA32 ); +    } +	 +  hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */ +	 +  lp->stats.tx_packets++; +#ifdef LINUX_2_1 +  lp->stats.tx_bytes += skb->len; +#endif +  dev->trans_start=jiffies; +  hp100_ints_on(); +	 +#ifdef LINUX_2_1 +  dev_kfree_skb( skb ); +#else +  dev_kfree_skb( skb, FREE_WRITE ); +#endif +	 +#ifdef HP100_DEBUG_TX +  printk( "hp100: %s: start_xmit: end\n", dev->name ); +#endif +	 +  return 0; +} + + +/* + * Receive Function (Non-Busmaster mode) + * Called when an "Receive Packet" interrupt occurs, i.e. the receive  + * packet counter is non-zero. + * For non-busmaster, this function does the whole work of transfering + * the packet to the host memory and then up to higher layers via skb + * and netif_rx.  + */ + +static void hp100_rx( struct device *dev ) +{ +  int packets, pkt_len; +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  u_int header; +  struct sk_buff *skb; + +#ifdef DEBUG_B +  hp100_outw( 0x4213, TRACE ); +  printk("hp100: %s: rx\n", dev->name); +#endif + +  /* First get indication of received lan packet */ +  /* RX_PKT_CND indicates the number of packets which have been fully */ +  /* received onto the card but have not been fully transfered of the card */ +  packets = hp100_inb( RX_PKT_CNT ); +#ifdef HP100_DEBUG_RX +  if ( packets > 1 ) +    printk( "hp100: %s: rx: waiting packets = %d\n", dev->name,packets ); +#endif + +  while ( packets-- > 0 ) +    { +      /* If ADV_NXT_PKT is still set, we have to wait until the card has */ +      /* really advanced to the next packet. */ +      for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT); +	   pkt_len++ ) +        { +#ifdef HP100_DEBUG_RX +          printk( "hp100: %s: rx: busy, remaining packets = %d\n", dev->name, packets ); +#endif     +        } + +      /* First we get the header, which contains information about the */ +      /* actual length of the received packet. */ +      if( lp->mode==2 ) /* memory mapped mode */ +        { +          if ( lp->mem_ptr_virt )    /* if memory was remapped */ +            header = *(__u32 *)lp->mem_ptr_virt; +          else +            header = readl( lp->mem_ptr_phys ); +        } +      else /* programmed i/o */ +        header = hp100_inl( DATA32 ); +       +      pkt_len = ((header & HP100_PKT_LEN_MASK) + 3) & ~3; + +#ifdef HP100_DEBUG_RX +      printk( "hp100: %s: rx: new packet - length=%d, errors=0x%x, dest=0x%x\n", +      	      dev->name, +              header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8, +              (header>>16)&7); +#endif +     +      /* Now we allocate the skb and transfer the data into it. */   +      skb = dev_alloc_skb( pkt_len ); +      if ( skb == NULL ) /* Not enough memory->drop packet */ +	{ +#ifdef HP100_DEBUG +	  printk( "hp100: %s: rx: couldn't allocate a sk_buff of size %d\n", dev->name, pkt_len ); +#endif +	  lp->stats.rx_dropped++; +	} +      else /* skb successfully allocated */ +	{ +	  u_char *ptr; +       +	  skb->dev = dev; +       +	  /* ptr to start of the sk_buff data area */ +	  ptr = (u_char *)skb_put( skb, pkt_len ); +       +	  /* Now transfer the data from the card into that area */ +	  if ( lp->mode==2 ) +            { +              if ( lp->mem_ptr_virt ) +                memcpy( ptr, lp->mem_ptr_virt, pkt_len ); +              /* Note alignment to 32bit transfers */ +              else +                memcpy_fromio( ptr, lp->mem_ptr_phys, pkt_len ); +            } +	  else /* io mapped */ +	    insl( ioaddr + HP100_REG_DATA32, ptr, pkt_len >> 2 ); +       +	  skb->protocol = eth_type_trans( skb, dev ); + +	  netif_rx( skb ); +	  lp->stats.rx_packets++; +#ifdef LINUX_2_1 +	  lp->stats.rx_bytes += skb->len; +#endif +       +#ifdef HP100_DEBUG_RX +	  printk( "hp100: %s: rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", +	  	  dev->name, +		  ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ], +		  ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] ); +#endif +	} +   +      /* Indicate the card that we have got the packet */ +      hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW ); + +      switch ( header & 0x00070000 ) { +      case (HP100_MULTI_ADDR_HASH<<16): +      case (HP100_MULTI_ADDR_NO_HASH<<16): +	lp->stats.multicast++; break; +      } +    } /* end of while(there are packets) loop */ +#ifdef HP100_DEBUG_RX +  printk( "hp100_rx: %s: end\n", dev->name ); +#endif +} + + +/*  + * Receive Function for Busmaster Mode + */ +static void hp100_rx_bm( struct device *dev ) +{ +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  hp100_ring_t *ptr; +  u_int header; +  int pkt_len; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4214, TRACE ); +  printk("hp100: %s: rx_bm\n", dev->name); +#endif + +#ifdef HP100_DEBUG +  if(0==lp->rxrcommit) +    { +      printk("hp100: %s: rx_bm called although no PDLs were committed to adapter?\n", dev->name);  +      return; +    } +  else + +    /* RX_PKT_CNT states how many PDLs are currently formatted and available to  +     * the cards BM engine */ +    if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit) +      { +	printk("hp100: %s: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", dev->name, hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit); +	return; +      } +#endif + +  while( (lp->rxrcommit > hp100_inb(RX_PDL)) ) +    { +      /* +       * The packet was received into the pdl pointed to by lp->rxrhead ( +       * the oldest pdl in the ring  +       */ +						 +      /* First we get the header, which contains information about the */ +      /* actual length of the received packet. */ +       +      ptr=lp->rxrhead; +       +      header = *(ptr->pdl-1); +      pkt_len = (header & HP100_PKT_LEN_MASK); + +#ifdef HP100_DEBUG_BM +      printk( "hp100: %s: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n", +      	      dev->name, +              (u_int) (ptr->pdl-1),(u_int) header, +              pkt_len,  +              (header>>16)&0xfff8, +              (header>>16)&7); +      printk( "hp100: %s: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n", +      	      dev->name, +	      hp100_inb( RX_PDL ), +	      hp100_inb( TX_PDL ), +	      hp100_inb( RX_PKT_CNT ), +	      (u_int) *(ptr->pdl), +	      (u_int) *(ptr->pdl+3), +	      (u_int) *(ptr->pdl+4)); +#endif +       +      if( (pkt_len>=MIN_ETHER_SIZE) && +          (pkt_len<=MAX_ETHER_SIZE) )   +        { +	  if(ptr->skb==NULL) +	    { +	      printk("hp100: %s: rx_bm: skb null\n", dev->name); +	      /* can happen if we only allocated room for the pdh due to memory shortage. */ +	      lp->stats.rx_dropped++; +	    } +	  else +	    { +	      skb_trim( ptr->skb, pkt_len );     /* Shorten it */ +	      ptr->skb->protocol = eth_type_trans( ptr->skb, dev ); +														 +	      netif_rx( ptr->skb );              /* Up and away... */ + +	      lp->stats.rx_packets++; +#ifdef LINUX_2_1 +	      lp->stats.rx_bytes += ptr->skb->len; +#endif +	    } + +          switch ( header & 0x00070000 ) { +          case (HP100_MULTI_ADDR_HASH<<16): +          case (HP100_MULTI_ADDR_NO_HASH<<16): +            lp->stats.multicast++; break; +          } +        } +      else +        { +#ifdef HP100_DEBUG +          printk("hp100: %s: rx_bm: Received bad packet (length=%d)\n",dev->name,pkt_len); +#endif +	  if(ptr->skb!=NULL) +#ifdef LINUX_2_1 +	    dev_kfree_skb( ptr->skb ); +#else +	    dev_kfree_skb( ptr->skb, FREE_READ );					 +#endif +          lp->stats.rx_errors++; +        } +						 +      lp->rxrhead=lp->rxrhead->next; + +      /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */ +      if (0 == hp100_build_rx_pdl( lp->rxrtail, dev )) +        { +	  /* No space for skb, header can still be received. */ +#ifdef HP100_DEBUG +          printk("hp100: %s: rx_bm: No space for new PDL.\n", dev->name); +#endif +	  return; +        }  +      else +        { /* successfully allocated new PDL - put it in ringlist at tail. */ +	  hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA); +          lp->rxrtail=lp->rxrtail->next; +	} +						 +    } +} + + + +/* + *  statistics + */ +static hp100_stats_t *hp100_get_stats( struct device *dev ) +{ +  int ioaddr = dev->base_addr; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4215, TRACE ); +#endif + +  hp100_ints_off(); +  hp100_update_stats( dev ); +  hp100_ints_on(); +  return &((struct hp100_private *)dev->priv)->stats; +} + +static void hp100_update_stats( struct device *dev ) +{ +  int ioaddr = dev->base_addr; +  u_short val; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4216, TRACE ); +  printk("hp100: %s: update-stats\n", dev->name); +#endif + +  /* Note: Statistics counters clear when read. */ +  hp100_page( MAC_CTRL );  +  val = hp100_inw( DROPPED ) & 0x0fff; +  lp->stats.rx_errors += val; +  lp->stats.rx_over_errors += val; +  val = hp100_inb( CRC ); +  lp->stats.rx_errors += val; +  lp->stats.rx_crc_errors += val; +  val = hp100_inb( ABORT ); +  lp->stats.tx_errors += val; +  lp->stats.tx_aborted_errors += val; +  hp100_page( PERFORMANCE ); +} + +static void hp100_misc_interrupt( struct device *dev ) +{ +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4216, TRACE ); +  printk("hp100: %s: misc_interrupt\n", dev->name); +#endif + +  /* Note: Statistics counters clear when read. */ +  lp->stats.rx_errors++; +  lp->stats.tx_errors++; +} + +static void hp100_clear_stats( int ioaddr ) +{ +  unsigned long flags; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4217, TRACE ); +  printk("hp100: %s: clear_stats\n", dev->name); +#endif + +  save_flags( flags ); +  cli(); +  hp100_page( MAC_CTRL );    /* get all statistics bytes */ +  hp100_inw( DROPPED ); +  hp100_inb( CRC ); +  hp100_inb( ABORT ); +  hp100_page( PERFORMANCE ); +  restore_flags( flags ); +} + + +/* + *  multicast setup + */ + +/* + *  Set or clear the multicast filter for this adapter. + */ +                                                           +static void hp100_set_multicast_list( struct device *dev ) +{ +  unsigned long flags; +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4218, TRACE ); +  printk("hp100: %s: set_mc_list\n", dev->name); +#endif + +  save_flags( flags ); +  cli(); +  hp100_ints_off(); +  hp100_page( MAC_CTRL ); +  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );  /* stop rx/tx */ + +  if ( dev->flags & IFF_PROMISC ) +    { +      lp->mac2_mode = HP100_MAC2MODE6;  /* promiscuous mode = get all good */ +      lp->mac1_mode = HP100_MAC1MODE6;  /* packets on the net */ +      memset( &lp->hash_bytes, 0xff, 8 ); +    } +  else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) ) +    { +      lp->mac2_mode = HP100_MAC2MODE5;  /* multicast mode = get packets for */ +      lp->mac1_mode = HP100_MAC1MODE5;  /* me, broadcasts and all multicasts */ +#ifdef HP100_MULTICAST_FILTER		/* doesn't work!!! */ +      if ( dev -> flags & IFF_ALLMULTI ) +        { +          /* set hash filter to receive all multicast packets */ +          memset( &lp->hash_bytes, 0xff, 8 ); +        } +       else +        { +          int i, j, idx; +          u_char *addrs; +          struct dev_mc_list *dmi; + +          memset( &lp->hash_bytes, 0x00, 8 ); +#ifdef HP100_DEBUG +          printk("hp100: %s: computing hash filter - mc_count = %i\n", dev -> name, dev -> mc_count ); +#endif  +          for ( i = 0, dmi = dev -> mc_list; i < dev -> mc_count; i++, dmi = dmi -> next ) +            { +              addrs = dmi -> dmi_addr; +              if ( ( *addrs & 0x01 ) == 0x01 )	/* multicast address? */ +                { +#ifdef HP100_DEBUG +                  printk("hp100: %s: multicast = %02x:%02x:%02x:%02x:%02x:%02x, ", +                  	dev -> name, +        		addrs[ 0 ], addrs[ 1 ], addrs[ 2 ], +        		addrs[ 3 ], addrs[ 4 ], addrs[ 5 ] ); +#endif  +                  for ( j = idx = 0; j < 6; j++ ) +                    { +                      idx ^= *addrs++ & 0x3f; +                      printk( ":%02x:", idx ); +                    } +#ifdef HP100_DEBUG +                  printk("idx = %i\n", idx ); +#endif +		  lp->hash_bytes[ idx >> 3 ] |= ( 1 << ( idx & 7 ) ); +                } +            } +        } +#else +      memset( &lp->hash_bytes, 0xff, 8 ); +#endif +    } +  else +    { +      lp->mac2_mode = HP100_MAC2MODE3;  /* normal mode = get packets for me */ +      lp->mac1_mode = HP100_MAC1MODE3;  /* and broadcasts */ +      memset( &lp->hash_bytes, 0x00, 8 ); +    } + +  if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) || +        ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) )  +    { +      int i; +     +      hp100_outb( lp->mac2_mode, MAC_CFG_2 ); +      hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */ +      hp100_orb( lp->mac1_mode, MAC_CFG_1 );       /* and set the new mode */ + +      hp100_page( MAC_ADDRESS ); +      for ( i = 0; i < 8; i++ ) +        hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i ); +#ifdef HP100_DEBUG +      printk("hp100: %s: mac1 = 0x%x, mac2 = 0x%x, multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",  +      		dev->name, lp->mac1_mode, lp->mac2_mode, +      		lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ], +      		lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ], +      		lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ], +      		lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ] +      		); +#endif  + +      if(lp->lan_type==HP100_LAN_100) +        { +#ifdef HP100_DEBUG +  	  printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name); +#endif  +	  lp->hub_status=hp100_login_to_vg_hub( dev, TRUE );  /* force a relogin to the hub */ +        } +    } +   else +    { +      int i; +      u_char old_hash_bytes[ 8 ]; + +      hp100_page( MAC_ADDRESS ); +      for ( i = 0; i < 8; i++ ) +        old_hash_bytes[ i ] = hp100_inb( HASH_BYTE0 + i ); +      if ( memcmp( old_hash_bytes, &lp->hash_bytes, 8 ) ) +        { +          for ( i = 0; i < 8; i++ ) +            hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i ); +#ifdef HP100_DEBUG +          printk("hp100: %s: multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",  +          	dev->name, +      		lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ], +      		lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ], +      		lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ], +      		lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ] +      		); +#endif  + +          if(lp->lan_type==HP100_LAN_100) +            { +#ifdef HP100_DEBUG +  	      printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name); +#endif  +   	      lp->hub_status=hp100_login_to_vg_hub( dev, TRUE );  /* force a relogin to the hub */ +            } +        } +    } + +  hp100_page( MAC_CTRL ); +  hp100_orb( HP100_RX_EN | HP100_RX_IDLE |              /* enable rx */ +	     HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );  /* enable tx */ + +  hp100_page( PERFORMANCE ); +  hp100_ints_on(); +  restore_flags( flags ); +} + + +/* + *  hardware interrupt handling + */ + +static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs ) +{ +  struct device *dev = (struct device *)dev_id; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +  int ioaddr; +  u_int val; + +  if ( dev == NULL ) return; +  ioaddr = dev->base_addr; + +  if ( dev->interrupt ) +    printk( "hp100: %s: re-entering the interrupt handler\n", dev->name ); +  hp100_ints_off(); +  dev->interrupt = 1;          /* mark that we are inside the handler */ + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4219, TRACE ); +#endif + +  /*  hp100_page( PERFORMANCE ); */ +  val = hp100_inw( IRQ_STATUS ); +#ifdef HP100_DEBUG_IRQ +  printk( "hp100: %s: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n", +  	  dev->name, +          lp->mode, +	  (u_int)val, +          hp100_inb( RX_PKT_CNT ), +          hp100_inb( RX_PDL ), +	  hp100_inb( TX_PKT_CNT ), +	  hp100_inb( TX_PDL ) +	  ); +#endif + +  if(val==0) /* might be a shared interrupt */  +    { +      dev->interrupt=0; +      hp100_ints_on(); +      return; +    } +  /* We're only interested in those interrupts we really enabled. */ +  /* val &= hp100_inw( IRQ_MASK ); */ + +  /*  +   * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL  +   * is considered executed whenever the RX_PDL data structure is no longer  +   * needed. +   */ +  if ( val & HP100_RX_PDL_FILL_COMPL ) +    { +      if(lp->mode==1) +	hp100_rx_bm( dev ); +      else +	{ +	  printk("hp100: %s: rx_pdl_fill_compl interrupt although not busmaster?\n", dev->name); +	} +    } +		 +  /*  +   * The RX_PACKET interrupt is set, when the receive packet counter is +   * non zero. We use this interrupt for receiving in slave mode. In +   * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill +   * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then +   * we somehow have missed a rx_pdl_fill_compl interrupt. +   */ + +  if ( val & HP100_RX_PACKET  ) /* Receive Packet Counter is non zero */ +    { +      if(lp->mode!=1) /* non busmaster */ +        hp100_rx( dev ); +      else if ( !(val & HP100_RX_PDL_FILL_COMPL )) +	{ +	  /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt?  */ +	  hp100_rx_bm( dev ); +	} +    } + +  /* +   * Ack. that we have noticed the interrupt and thereby allow next one. +   * Note that this is now done after the slave rx function, since first +   * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt +   * on the J2573. +   */ +  hp100_outw( val, IRQ_STATUS ); + +  /* +   * RX_ERROR is set when a packet is dropped due to no memory resources on  +   * the card or when a RCV_ERR occurs.  +   * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists   +   * only in the 802.3 MAC and happens when 16 collisions occur during a TX  +   */ +  if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) ) +    { +#ifdef HP100_DEBUG_IRQ +      printk("hp100: %s: TX/RX Error IRQ\n", dev->name); +#endif +      hp100_update_stats( dev ); +      if(lp->mode==1) +	{ +	  hp100_rxfill( dev ); +	  hp100_clean_txring( dev ); +	} +    } +				 +  /*  +   * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero.  +   */ +  if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) ) +    hp100_rxfill( dev ); +		 +  /*  +   * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire  +   * is completed  +   */ +  if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) ) +    hp100_clean_txring( dev ); + +  /*  +   * MISC_ERROR is set when either the LAN link goes down or a detected +   * bus error occurs. +   */ +  if ( val & HP100_MISC_ERROR ) /* New for J2585B */ +    { +#ifdef HP100_DEBUG_IRQ +      printk("hp100: %s: Misc. Error Interrupt - Check cabling.\n", dev->name); +#endif +      if(lp->mode==1) +	{ +	  hp100_clean_txring( dev ); +	  hp100_rxfill( dev ); +	} +      hp100_misc_interrupt( dev ); +    } +	 +  dev->interrupt = 0; +  hp100_ints_on(); +} + + +/* + *  some misc functions + */ + +static void hp100_start_interface( struct device *dev ) +{ +  unsigned long flags; +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4220, TRACE ); +  printk("hp100: %s: hp100_start_interface\n",dev->name); +#endif + +  save_flags( flags ); +  cli(); + +  /* Ensure the adapter does not want to request an interrupt when */ +  /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */ +  hp100_page( PERFORMANCE ); +  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */ +  hp100_outw( 0xffff, IRQ_STATUS );  /* ack all IRQs */ +  hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW); +  /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */ +  hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW );  + +  if(lp->mode==1) +    { +      /* Make sure BM bit is set... */ +      hp100_page(HW_MAP); +      hp100_orb( HP100_BM_MASTER, BM ); +      hp100_rxfill( dev ); +    } +  else if(lp->mode==2) +    { +      /* Enable memory mapping. Note: Don't do this when busmaster. */ +      hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW ); +    } + +  hp100_page(PERFORMANCE); +  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */ +  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */ + +  /* enable a few interrupts: */ +  if(lp->mode==1) /* busmaster mode */ +    { +      hp100_outw( HP100_RX_PDL_FILL_COMPL | +		  HP100_RX_PDA_ZERO  |   +		  HP100_RX_ERROR     |   +		  /* HP100_RX_PACKET    | */     +		  /* HP100_RX_EARLY_INT |  */     HP100_SET_HB  |   +		  /* HP100_TX_PDA_ZERO  |  */ +		  HP100_TX_COMPLETE  |   +		  /* HP100_MISC_ERROR   |  */ +		  HP100_TX_ERROR     | HP100_SET_LB, IRQ_MASK ); +    }  +  else +    {   +      hp100_outw( HP100_RX_PACKET  | +		  HP100_RX_ERROR   | HP100_SET_HB | +                  HP100_TX_ERROR   | HP100_SET_LB , IRQ_MASK ); +    } +	 +  /* Enable MAC Tx and RX, set MAC modes, ... */ +  hp100_set_multicast_list( dev ); + +  restore_flags( flags ); +} + + +static void hp100_stop_interface( struct device *dev ) +{ +  struct hp100_private *lp = (struct hp100_private *)dev->priv;  +  int ioaddr = dev->base_addr; +  u_int val; + +#ifdef HP100_DEBUG_B +  printk("hp100: %s: hp100_stop_interface\n",dev->name); +  hp100_outw( 0x4221, TRACE ); +#endif + +  if (lp->mode==1)  +    hp100_BM_shutdown( dev ); +  else +    { +      /* Note: MMAP_DIS will be reenabled by start_interface */ +      hp100_outw( HP100_INT_EN | HP100_RESET_LB |  +                  HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW ); +      val = hp100_inw( OPTION_LSW ); +    +      hp100_page( MAC_CTRL ); +      hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); + +      if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */ +      /* ... else: busy wait until idle */ +      for ( val = 0; val < 6000; val++ ) +	if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) == +	     (HP100_TX_IDLE | HP100_RX_IDLE) ) +	  { +	    hp100_page(PERFORMANCE); +	    return; +	  } +      printk( "hp100: %s: hp100_stop_interface - timeout\n", dev->name ); +      hp100_page(PERFORMANCE); +    } +} + + +static void hp100_load_eeprom( struct device *dev, u_short probe_ioaddr ) +{ +  int i; +  int ioaddr = probe_ioaddr > 0 ? probe_ioaddr : dev->base_addr; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4222, TRACE ); +#endif + +  hp100_page( EEPROM_CTRL ); +  hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL ); +  hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL ); +  for ( i = 0; i < 10000; i++ ) +    if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return; +  printk( "hp100: %s: hp100_load_eeprom - timeout\n", dev->name ); +} + + +/*  Sense connection status. + *  return values: LAN_10  - Connected to 10Mbit/s network + *                 LAN_100 - Connected to 100Mbit/s network + *                 LAN_ERR - not connected or 100Mbit/s Hub down + */ +static int hp100_sense_lan( struct device *dev ) +{ +  int ioaddr = dev->base_addr; +  u_short val_VG, val_10; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4223, TRACE ); +#endif + +  hp100_page( MAC_CTRL ); +  val_10 = hp100_inb( 10_LAN_CFG_1 ); +  val_VG = hp100_inb( VG_LAN_CFG_1 ); +  hp100_page( PERFORMANCE ); +#ifdef HP100_DEBUG +  printk( "hp100: %s: sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", dev->name, val_VG, val_10 ); +#endif + +  if ( val_10 & HP100_LINK_BEAT_ST )	/* 10Mb connection is active */ +    return HP100_LAN_10; + +  if ( val_10 & HP100_AUI_ST )		/* have we BNC or AUI onboard? */ +    { +      val_10 |= HP100_AUI_SEL | HP100_LOW_TH; +      hp100_page( MAC_CTRL ); +      hp100_outb( val_10, 10_LAN_CFG_1 ); +      hp100_page( PERFORMANCE ); +      return HP100_LAN_10; +    } + +  if ( (lp->id->id == 0x02019F022) ||  +       (lp->id->id == 0x01042103c) || +       (lp->id->id == 0x01040103c) ) +    return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */ + +  if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */  +    return HP100_LAN_100; +  return HP100_LAN_ERR; +} + + + +static int hp100_down_vg_link( struct device *dev ) +{ +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  int ioaddr = dev->base_addr; +  unsigned long time; +  long savelan, newlan; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4224, TRACE ); +  printk("hp100: %s: down_vg_link\n", dev->name); +#endif + +  hp100_page( MAC_CTRL ); +  time=jiffies+(HZ/4); +  do{ +    if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; +  } while (time>jiffies); + +  if ( jiffies >= time )       /* no signal->no logout */ +    return 0; + +  /* Drop the VG Link by clearing the link up cmd and load addr.*/ + +  hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1);  +  hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1);  + +  /* Conditionally stall for >250ms on Link-Up Status (to go down) */ +  time=jiffies+(HZ/2); +  do{ +    if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break; +  } while(time>jiffies); + +#ifdef HP100_DEBUG +  if (jiffies>=time) +    printk("hp100: %s: down_vg_link: Link does not go down?\n", dev->name); +#endif + +  /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */ +  /* logout under traffic (even though all the status bits are cleared),  */ +  /* do this workaround to get the Rev 1 MAC in its idle state */ +  if ( lp->chip==HP100_CHIPID_LASSEN ) +    { +      /* Reset VG MAC to insure it leaves the logoff state even if */ +      /* the Hub is still emitting tones */ +      hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1); +      udelay(1500); /* wait for >1ms */ +      hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */ +      udelay(1500); +    } + +  /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */ +  /* to get the VG mac to full reset. This is not req.d with later chips */ +  /* Note: It will take the between 1 and 2 seconds for the VG mac to be */ +  /* selected again! This will be left to the connect hub function to */ +  /* perform if desired.  */ +  if (lp->chip==HP100_CHIPID_LASSEN) +    { +      /* Have to write to 10 and 100VG control registers simultaneously */ +      savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */ +      newlan &= ~(HP100_VG_SEL<<16); +      newlan |= (HP100_DOT3_MAC)<<8; +      hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */ +      hp100_outl(newlan, 10_LAN_CFG_1); + +      /* Conditionally stall for 5sec on VG selected. */ +      time=jiffies+(HZ*5); +      do{ +        if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break; +      } while(time>jiffies); + +      hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */ +      hp100_outl(savelan, 10_LAN_CFG_1); +    } + +  time=jiffies+(3*HZ); /* Timeout 3s */ +  do { +    if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break; +  } while (time>jiffies); +   +  if(time<=jiffies) +    { +#ifdef HP100_DEBUG +      printk( "hp100: %s: down_vg_link: timeout\n", dev->name ); +#endif +      return -EIO; +    } +   +  time=jiffies+(2*HZ); /* This seems to take a while.... */ +  do {} while (time>jiffies); +   +  return 0; +} + + +static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin ) +{ +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  u_short val=0; +  unsigned long time; +  int startst; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4225, TRACE ); +  printk("hp100: %s: login_to_vg_hub\n", dev->name); +#endif + +  /* Initiate a login sequence iff VG MAC is enabled and either Load Address +   * bit is zero or the force relogin flag is set (e.g. due to MAC address or +   * promiscuous mode change) +   */ +  hp100_page( MAC_CTRL ); +  startst=hp100_inb( VG_LAN_CFG_1 ); +  if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST)) +    { +#ifdef HP100_DEBUG_TRAINING +      printk("hp100: %s: Start training\n", dev->name); +#endif + +      /* Ensure VG Reset bit is 1 (i.e., do not reset)*/ +      hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 ); + +      /* If Lassen AND auto-select-mode AND VG tones were sensed on */ +      /* entry then temporarily put them into force 100Mbit mode */ +      if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) ) +        hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 ); +						 +      /* Drop the VG link by zeroing Link Up Command and Load Address  */ +      hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1); + +#ifdef HP100_DEBUG_TRAINING +      printk("hp100: %s: Bring down the link\n", dev->name); +#endif + +      /* Wait for link to drop */ +      time = jiffies + (HZ/10);  +      do { +        if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break; +      } while (time>jiffies); + +      /* Start an addressed training and optionally request promiscuous port */ +      if ( (dev->flags) & IFF_PROMISC ) +	{ +	  hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2); +	  if(lp->chip==HP100_CHIPID_LASSEN) +	    hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST ); +	} +      else +	{ +	  hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2); +	  /* For ETR parts we need to reset the prom. bit in the training +	   * register, otherwise promiscious mode won't be disabled. +	   */ +	  if(lp->chip==HP100_CHIPID_LASSEN) +	    { +	      hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST ); +	    } +	} + +      /* With ETR parts, frame format request bits can be set. */ +      if(lp->chip==HP100_CHIPID_LASSEN) +        hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST); + +      hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1); + +      /* Note: Next wait could be omitted for Hood and earlier chips under */ +      /* certain circumstances */ +      /* TODO: check if hood/earlier and skip wait. */ + +      /* Wait for either short timeout for VG tones or long for login    */ +      /* Wait for the card hardware to signalise link cable status ok... */ +      hp100_page( MAC_CTRL ); +      time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */ +      do { +        if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break; +      } while ( jiffies < time ); +       +      if ( jiffies >= time ) +	{ +#ifdef HP100_DEBUG_TRAINING +	  printk( "hp100: %s: Link cable status not ok? Training aborted.\n", dev->name ); +#endif   +	} +      else +	{ +#ifdef HP100_DEBUG_TRAINING +	  printk( "hp100: %s: HUB tones detected. Trying to train.\n", dev->name); +#endif + +	  time = jiffies + ( 2*HZ ); /* again a timeout */ +	  do { +	    val = hp100_inb( VG_LAN_CFG_1 ); +	    if ( (val & ( HP100_LINK_UP_ST )) ) +	      { +#ifdef HP100_DEBUG_TRAINING +		printk( "hp100: %s: Passed training.\n", dev->name); +#endif +		break; +	      } +	  } while ( time > jiffies ); +	} +       +      /* If LINK_UP_ST is set, then we are logged into the hub. */ +      if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) ) +        { +#ifdef HP100_DEBUG_TRAINING +          printk( "hp100: %s: Successfully logged into the HUB.\n", dev->name); +          if(lp->chip==HP100_CHIPID_LASSEN) +            { +	      val = hp100_inw(TRAIN_ALLOW); +              printk( "hp100: %s: Card supports 100VG MAC Version \"%s\" ", +              	      dev->name,(hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre"); +	      printk( "Driver will use MAC Version \"%s\"\n", +                      ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" );  +              printk( "hp100: %s: Frame format is %s.\n",dev->name,(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3"); +            } +#endif +        } +      else +        { +          /* If LINK_UP_ST is not set, login was not successful */ +          printk("hp100: %s: Problem logging into the HUB.\n",dev->name); +          if(lp->chip==HP100_CHIPID_LASSEN) +            { +              /* Check allowed Register to find out why there is a problem. */ +              val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */ +#ifdef HP100_DEBUG_TRAINING +              printk("hp100: %s: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", dev->name, hp100_inw(TRAIN_REQUEST), val); +#endif +              if ( val & HP100_MALLOW_ACCDENIED ) +                printk("hp100: %s: HUB access denied.\n", dev->name); +              if ( val & HP100_MALLOW_CONFIGURE ) +                printk("hp100: %s: MAC Configuration is incompatible with the Network.\n", dev->name); +              if ( val & HP100_MALLOW_DUPADDR ) +                printk("hp100: %s: Duplicate MAC Address on the Network.\n", dev->name); +            } +        } +       +      /* If we have put the chip into forced 100 Mbit mode earlier, go back */ +      /* to auto-select mode */ +       +      if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) ) +        { +          hp100_page( MAC_CTRL ); +          hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 ); +        } +      +      val=hp100_inb(VG_LAN_CFG_1); + +      /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */ +      hp100_page(PERFORMANCE); +      hp100_outw( HP100_MISC_ERROR, IRQ_STATUS); +					 +      if (val&HP100_LINK_UP_ST) +        return(0); /* login was ok */ +      else +        { +          printk("hp100: %s: Training failed.\n", dev->name); +          hp100_down_vg_link( dev ); +          return -EIO; +        } +    } +  /* no forced relogin & already link there->no training. */ +  return -EIO; +} + + +static void hp100_cascade_reset( struct device *dev, u_short enable ) +{ +  int ioaddr = dev->base_addr; +  struct hp100_private *lp = (struct hp100_private *)dev->priv; +  int i; + +#ifdef HP100_DEBUG_B +  hp100_outw( 0x4226, TRACE ); +  printk("hp100: %s: cascade_reset\n", dev->name); +#endif + +  if (enable==TRUE)  +    { +      hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW ); +      if(lp->chip==HP100_CHIPID_LASSEN) +	{ +	  /* Lassen requires a PCI transmit fifo reset */ +	  hp100_page( HW_MAP ); +	  hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); +	  hp100_orb( HP100_PCI_RESET, PCICTRL2 ); +	  /* Wait for min. 300 ns */ +	  /* we cant use jiffies here, because it may be */ +	  /* that we have disabled the timer... */ +	  for (i=0; i<0xffff; i++); +	  hp100_andb( ~HP100_PCI_RESET, PCICTRL2 ); +	  hp100_page( PERFORMANCE ); +	}	 +    } +  else +    { /* bring out of reset */ +      hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW); +      for (i=0; i<0xffff; i++ ); +      hp100_page(PERFORMANCE); +    } +} + +#ifdef HP100_DEBUG		 +void hp100_RegisterDump( struct device *dev ) +{ +  int ioaddr=dev->base_addr; +  int Page; +  int Register; + +  /* Dump common registers */ +  printk("hp100: %s: Cascade Register Dump\n", dev->name); +  printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID)); +  printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING)); +  printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW)); +  printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW)); + +  /* Dump paged registers */ +  for (Page = 0; Page < 8; Page++)  +    { +      /* Dump registers */ +      printk("page: 0x%.2x\n",Page); +      outw( Page, ioaddr+0x02); +      for (Register = 0x8; Register < 0x22; Register += 2) +	{ +	  /* Display Register contents except data port */ +	  if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) +	    { +	      printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register)); +	    } +	} +    } +  hp100_page(PERFORMANCE); +} +#endif + + + +/* + *  module section + */ +  +#ifdef MODULE + +/* Parameters set by insmod */ +int hp100_port[5] = { 0, -1, -1, -1, -1 }; +#ifdef LINUX_2_1 +MODULE_PARM(hp100_port, "1-5i"); +#endif + +#ifdef LINUX_2_1 +char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" }; +MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ)); +#else +static char devname[5][IFNAMSIZ] = { "", "", "", "", "" }; +static char *hp100_name[5] = { devname[0], devname[1], +                               devname[2], devname[3], +                               devname[4] }; +#endif + +/* List of devices */ +static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL }; + +/* + * Note: if you have more than five 100vg cards in your pc, feel free to + * increase this value  + */ + +/* + * Note: to register three eisa or pci devices, use: + * option hp100 hp100_port=0,0,0 + *        to register one card at io 0x280 as eth239, use: + * option hp100 hp100_port=0x280 hp100_name=eth239 + */ + +int init_module( void ) +{ +  int	i, cards; + +  if (hp100_port == 0 && !EISA_bus && !pcibios_present()) +    printk("hp100: You should not use auto-probing with insmod!\n"); + +  /* Loop on all possible base addresses */ +  i = -1; cards = 0; +  while((hp100_port[++i] != -1) && (i < 5)) +    { +      /* Create device and set basics args */ +      hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL); +      memset(hp100_devlist[i], 0x00, sizeof(struct device)); +      hp100_devlist[i]->name = hp100_name[i]; +      hp100_devlist[i]->base_addr = hp100_port[i]; +      hp100_devlist[i]->init = &hp100_probe; + +      /* Try to create the device */ +      if(register_netdev(hp100_devlist[i]) != 0) +	{ +	  /* DeAllocate everything */ +	  /* Note: if dev->priv is mallocated, there is no way to fail */ +	  kfree_s(hp100_devlist[i], sizeof(struct device)); +	  hp100_devlist[i] = (struct device *) NULL; +	} +       else +        cards++; +    }			/* Loop over all devices */ + +  return cards > 0 ? 0 : -ENODEV; +} + +void cleanup_module( void ) +{ +  int		i; + +  /* TODO: Check if all skb's are released/freed. */ +  for(i = 0; i < 5; i++) +    if(hp100_devlist[i] != (struct device *) NULL) +      { +	unregister_netdev( hp100_devlist[i] ); +	release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE ); +	if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */ +	  kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f);  +	if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ) +	  iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt ); +	kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) ); +	hp100_devlist[i]->priv = NULL; +	kfree_s(hp100_devlist[i], sizeof(struct device)); +	hp100_devlist[i] = (struct device *) NULL; +      } +} + +#endif		/* MODULE */ + + + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c" + *  c-indent-level: 2 + *  tab-width: 8 + * End: + */ diff --git a/linux/src/drivers/net/hp100.h b/linux/src/drivers/net/hp100.h new file mode 100644 index 0000000..e1884aa --- /dev/null +++ b/linux/src/drivers/net/hp100.h @@ -0,0 +1,626 @@ +/* + * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux. + * + * $Id: hp100.h,v 1.1 1999/04/26 05:52:20 tb Exp $ + * + * Authors:  Jaroslav Kysela, <perex@pf.jcu.cz> + *           Siegfried Loeffler <floeff@tunix.mathematik.uni-stuttgart.de> + * + * This driver is based on the 'hpfepkt' crynwr packet driver. + * + * This source/code is public free; you can distribute it and/or modify  + * it under terms of the GNU General Public License (published by the + * Free Software Foundation) either version two of this License, or any  + * later version. + */ + +/**************************************************************************** + *  Hardware Constants + ****************************************************************************/ + +/*  + * Page Identifiers + * (Swap Paging Register, PAGING, bits 3:0, Offset 0x02) + */ + +#define HP100_PAGE_PERFORMANCE	0x0	/* Page 0 */ +#define HP100_PAGE_MAC_ADDRESS	0x1	/* Page 1 */ +#define HP100_PAGE_HW_MAP	0x2	/* Page 2 */ +#define HP100_PAGE_EEPROM_CTRL	0x3	/* Page 3 */ +#define HP100_PAGE_MAC_CTRL	0x4	/* Page 4 */ +#define HP100_PAGE_MMU_CFG	0x5	/* Page 5 */ +#define HP100_PAGE_ID_MAC_ADDR	0x6	/* Page 6 */ +#define HP100_PAGE_MMU_POINTER	0x7	/* Page 7 */ + + +/* Registers that are present on all pages  */ + +#define HP100_REG_HW_ID		0x00	/* R:  (16) Unique card ID           */ +#define HP100_REG_TRACE		0x00	/* W:  (16) Used for debug output    */ +#define HP100_REG_PAGING	0x02	/* R:  (16),15:4 Card ID             */ +                                        /* W:  (16),3:0 Switch pages         */ +#define HP100_REG_OPTION_LSW	0x04	/* RW: (16) Select card functions    */ +#define HP100_REG_OPTION_MSW	0x06	/* RW: (16) Select card functions    */ +                                         +/*  Page 0 - Performance  */ + +#define HP100_REG_IRQ_STATUS	0x08	/* RW: (16) Which ints are pending   */ +#define HP100_REG_IRQ_MASK	0x0a	/* RW: (16) Select ints to allow     */ +#define HP100_REG_FRAGMENT_LEN	0x0c	/* W: (16)12:0 Current fragment len */ +/* Note: For 32 bit systems, fragment len and offset registers are available */ +/*       at offset 0x28 and 0x2c, where they can be written as 32bit values. */ +#define HP100_REG_OFFSET	0x0e	/* RW: (16)12:0 Offset to start read */ +#define HP100_REG_DATA32	0x10	/* RW: (32) I/O mode data port       */ +#define HP100_REG_DATA16	0x12	/* RW: WORDs must be read from here  */ +#define HP100_REG_TX_MEM_FREE	0x14	/* RD: (32) Amount of free Tx mem    */ +#define HP100_REG_TX_PDA_L      0x14    /* W: (32) BM: Ptr to PDL, Low Pri  */ +#define HP100_REG_TX_PDA_H      0x1c    /* W: (32) BM: Ptr to PDL, High Pri */ +#define HP100_REG_RX_PKT_CNT	0x18	/* RD: (8) Rx count of pkts on card  */ +#define HP100_REG_TX_PKT_CNT	0x19	/* RD: (8) Tx count of pkts on card  */ +#define HP100_REG_RX_PDL        0x1a    /* R: (8) BM: # rx pdl not executed */ +#define HP100_REG_TX_PDL        0x1b    /* R: (8) BM: # tx pdl not executed */ +#define HP100_REG_RX_PDA        0x18    /* W: (32) BM: Up to 31 addresses */ +                                        /*             which point to a PDL */ +#define HP100_REG_SL_EARLY      0x1c    /*    (32) Enhanced Slave Early Rx */ +#define HP100_REG_STAT_DROPPED  0x20    /* R (12) Dropped Packet Counter */ +#define HP100_REG_STAT_ERRORED  0x22    /* R (8) Errored Packet Counter */ +#define HP100_REG_STAT_ABORT    0x23    /* R (8) Abort Counter/OW Coll. Flag */ +#define HP100_REG_RX_RING       0x24    /* W (32) Slave: RX Ring Pointers */ +#define HP100_REG_32_FRAGMENT_LEN 0x28  /* W (13) Slave: Fragment Length Reg */ +#define HP100_REG_32_OFFSET     0x2c    /* W (16) Slave: Offset Register */ + +/*  Page 1 - MAC Address/Hash Table  */ + +#define HP100_REG_MAC_ADDR	0x08	/* RW: (8) Cards MAC address	     */ +#define HP100_REG_HASH_BYTE0	0x10	/* RW: (8) Cards multicast filter    */ +                                         +/*  Page 2 - Hardware Mapping  */ + +#define HP100_REG_MEM_MAP_LSW	0x08	/* RW: (16) LSW of cards mem addr    */ +#define HP100_REG_MEM_MAP_MSW	0x0a	/* RW: (16) MSW of cards mem addr    */ +#define HP100_REG_IO_MAP	0x0c	/* RW: (8) Cards I/O address         */ +#define HP100_REG_IRQ_CHANNEL	0x0d	/* RW: (8) IRQ and edge/level int    */ +#define HP100_REG_SRAM		0x0e	/* RW: (8) How much RAM on card      */ +#define HP100_REG_BM		0x0f	/* RW: (8) Controls BM functions     */ + +/* New on Page 2 for ETR chips: */ +#define HP100_REG_MODECTRL1     0x10    /* RW: (8) Mode Control 1 */ +#define HP100_REG_MODECTRL2     0x11    /* RW: (8) Mode Control 2 */ +#define HP100_REG_PCICTRL1      0x12    /* RW: (8) PCI Cfg 1 */ +#define HP100_REG_PCICTRL2      0x13    /* RW: (8) PCI Cfg 2 */ +#define HP100_REG_PCIBUSMLAT    0x15    /* RW: (8) PCI Bus Master Latency */ +#define HP100_REG_EARLYTXCFG    0x16    /* RW: (16) Early TX Cfg/Cntrl Reg */ +#define HP100_REG_EARLYRXCFG    0x18    /* RW: (8) Early RX Cfg/Cntrl Reg */ +#define HP100_REG_ISAPNPCFG1    0x1a    /* RW: (8) ISA PnP Cfg/Cntrl Reg 1 */ +#define HP100_REG_ISAPNPCFG2    0x1b    /* RW: (8) ISA PnP Cfg/Cntrl Reg 2 */ +                                         +/*  Page 3 - EEPROM/Boot ROM  */ + +#define HP100_REG_EEPROM_CTRL	0x08	/* RW: (16) Used to load EEPROM      */ +#define HP100_REG_BOOTROM_CTRL  0x0a +                                         +/*  Page 4 - LAN Configuration  (MAC_CTRL) */ + +#define HP100_REG_10_LAN_CFG_1	0x08	/* RW: (8) Set 10M XCVR functions   */ +#define HP100_REG_10_LAN_CFG_2  0x09    /* RW: (8)     10M XCVR functions   */ +#define HP100_REG_VG_LAN_CFG_1	0x0a	/* RW: (8) Set 100M XCVR functions  */ +#define HP100_REG_VG_LAN_CFG_2  0x0b    /* RW: (8) 100M LAN Training cfgregs */ +#define HP100_REG_MAC_CFG_1	0x0c	/* RW: (8) Types of pkts to accept   */ +#define HP100_REG_MAC_CFG_2	0x0d	/* RW: (8) Misc MAC functions        */ +#define HP100_REG_MAC_CFG_3     0x0e    /* RW: (8) Misc MAC functions */ +#define HP100_REG_MAC_CFG_4     0x0f    /* R:  (8) Misc MAC states */ +#define HP100_REG_DROPPED	0x10	/* R:  (16),11:0 Pkts cant fit in mem*/ +#define HP100_REG_CRC		0x12	/* R:  (8) Pkts with CRC             */ +#define HP100_REG_ABORT		0x13	/* R:  (8) Aborted Tx pkts           */ +#define HP100_REG_TRAIN_REQUEST 0x14    /* RW: (16) Endnode MAC register.*/ +#define HP100_REG_TRAIN_ALLOW   0x16    /* R:  (16) Hub allowed register */ +                     +/*  Page 5 - MMU  */ + +#define HP100_REG_RX_MEM_STOP	0x0c	/* RW: (16) End of Rx ring addr      */ +#define HP100_REG_TX_MEM_STOP	0x0e	/* RW: (16) End of Tx ring addr      */ +#define HP100_REG_PDL_MEM_STOP  0x10    /* Not used by 802.12 devices */ +#define HP100_REG_ECB_MEM_STOP  0x14    /* I've no idea what this is */ +           +/*  Page 6 - Card ID/Physical LAN Address  */ + +#define HP100_REG_BOARD_ID	0x08	/* R:  (8) EISA/ISA card ID          */ +#define HP100_REG_BOARD_IO_CHCK 0x0c	/* R:  (8) Added to ID to get FFh    */ +#define HP100_REG_SOFT_MODEL	0x0d	/* R:  (8) Config program defined    */ +#define HP100_REG_LAN_ADDR	0x10	/* R:  (8) MAC addr of card          */ +#define HP100_REG_LAN_ADDR_CHCK 0x16	/* R:  (8) Added to addr to get FFh  */ +                                         +/*  Page 7 - MMU Current Pointers  */ + +#define HP100_REG_PTR_RXSTART	0x08	/* R:  (16) Current begin of Rx ring */ +#define HP100_REG_PTR_RXEND	0x0a	/* R:  (16) Current end of Rx ring   */ +#define HP100_REG_PTR_TXSTART	0x0c	/* R:  (16) Current begin of Tx ring */ +#define HP100_REG_PTR_TXEND	0x0e	/* R:  (16) Current end of Rx ring   */ +#define HP100_REG_PTR_RPDLSTART 0x10 +#define HP100_REG_PTR_RPDLEND   0x12 +#define HP100_REG_PTR_RINGPTRS  0x14 +#define HP100_REG_PTR_MEMDEBUG  0x1a +/* ------------------------------------------------------------------------ */ + + +/*  + * Hardware ID Register I (Always available, HW_ID, Offset 0x00) + */ +#define HP100_HW_ID_CASCADE     0x4850  /* Identifies Cascade Chip */ + +/*  + * Hardware ID Register 2 & Paging Register + * (Always available, PAGING, Offset 0x02) + * Bits 15:4 are for the Chip ID  + */ +#define HP100_CHIPID_MASK        0xFFF0 +#define HP100_CHIPID_SHASTA      0x5350  /* Not 802.12 compliant */ +                                         /* EISA BM/SL, MCA16/32 SL, ISA SL */ +#define HP100_CHIPID_RAINIER     0x5360  /* Not 802.12 compliant EISA BM,*/ +                                         /* PCI SL, MCA16/32 SL, ISA SL */ +#define HP100_CHIPID_LASSEN      0x5370  /* 802.12 compliant PCI BM, PCI SL */ +                                         /* LRF supported */ + +/* + *  Option Registers I and II  + * (Always available, OPTION_LSW, Offset 0x04-0x05) + */ +#define HP100_DEBUG_EN		0x8000	/* 0:Dis., 1:Enable Debug Dump Ptr. */ +#define HP100_RX_HDR		0x4000	/* 0:Dis., 1:Enable putting pkt into */ +                                        /*   system mem. before Rx interrupt */ +#define HP100_MMAP_DIS		0x2000	/* 0:Enable, 1:Disable mem.mapping. */ +                                        /*   MMAP_DIS must be 0 and MEM_EN */ +                                        /*   must be 1 for memory-mapped */ +                                        /*   mode to be enabled */ +#define HP100_EE_EN		0x1000	/* 0:Disable,1:Enable EEPROM writing */ +#define HP100_BM_WRITE		0x0800	/* 0:Slave, 1:Bus Master for Tx data */ +#define HP100_BM_READ		0x0400	/* 0:Slave, 1:Bus Master for Rx data */ +#define HP100_TRI_INT		0x0200	/* 0:Don't, 1:Do tri-state the int */ +#define HP100_MEM_EN		0x0040	/* Config program set this to */ +                                        /*   0:Disable, 1:Enable mem map. */ +                                        /*   See MMAP_DIS. */ +#define HP100_IO_EN		0x0020	/* 1:Enable I/O transfers */ +#define HP100_BOOT_EN		0x0010	/* 1:Enable boot ROM access */ +#define HP100_FAKE_INT		0x0008	/* 1:int */ +#define HP100_INT_EN		0x0004	/* 1:Enable ints from card */ +#define HP100_HW_RST		0x0002	/* 0:Reset, 1:Out of reset */ +                                        /* NIC reset on 0 to 1 transition */ + +/* + *  Option Register III  + * (Always available, OPTION_MSW, Offset 0x06) + */ +#define HP100_PRIORITY_TX	0x0080	/* 1:Do all Tx pkts as priority */ +#define HP100_EE_LOAD		0x0040	/* 1:EEPROM loading, 0 when done */ +#define HP100_ADV_NXT_PKT	0x0004	/* 1:Advance to next pkt in Rx queue */ +                                        /*   h/w will set to 0 when done */ +#define HP100_TX_CMD		0x0002	/* 1:Tell h/w download done, h/w */ +                                        /*   will set to 0 when done */ + +/* + * Interrupt Status Registers I and II + * (Page PERFORMANCE, IRQ_STATUS, Offset 0x08-0x09) + * Note: With old chips, these Registers will clear when 1 is written to them + *       with new chips this depends on setting of CLR_ISMODE + */ +#define HP100_RX_EARLY_INT      0x2000 +#define HP100_RX_PDA_ZERO       0x1000 +#define HP100_RX_PDL_FILL_COMPL 0x0800 +#define HP100_RX_PACKET		0x0400	/* 0:No, 1:Yes pkt has been Rx */ +#define HP100_RX_ERROR		0x0200	/* 0:No, 1:Yes Rx pkt had error */ +#define HP100_TX_PDA_ZERO       0x0020  /* 1 when PDA count goes to zero */ +#define HP100_TX_SPACE_AVAIL	0x0010	/* 0:<8192, 1:>=8192 Tx free bytes */ +#define HP100_TX_COMPLETE	0x0008	/* 0:No, 1:Yes a Tx has completed */ +#define HP100_MISC_ERROR        0x0004  /* 0:No, 1:Lan Link down or bus error*/ +#define HP100_TX_ERROR		0x0002	/* 0:No, 1:Yes Tx pkt had error */ + +/* + * Xmit Memory Free Count + * (Page PERFORMANCE, TX_MEM_FREE, Offset 0x14) (Read only, 32bit) + */ +#define HP100_AUTO_COMPARE	0x80000000	/* Tx Space avail & pkts<255 */ +#define HP100_FREE_SPACE	0x7fffffe0	/* Tx free memory */ + +/* + *  IRQ Channel + * (Page HW_MAP, IRQ_CHANNEL, Offset 0x0d) + */ +#define HP100_ZERO_WAIT_EN	0x80	/* 0:No, 1:Yes asserts NOWS signal */ +#define HP100_IRQ_SCRAMBLE      0x40 +#define HP100_BOND_HP           0x20 +#define HP100_LEVEL_IRQ		0x10	/* 0:Edge, 1:Level type interrupts. */ +                                        /* (Only valid on EISA cards) */ +#define HP100_IRQMASK		0x0F	/* Isolate the IRQ bits */ + +/* + * SRAM Parameters + * (Page HW_MAP, SRAM, Offset 0x0e) + */ +#define HP100_RAM_SIZE_MASK	0xe0	/* AND to get SRAM size index */ +#define HP100_RAM_SIZE_SHIFT	0x05	/* Shift count(put index in lwr bits)*/ + +/* + * Bus Master Register + * (Page HW_MAP, BM, Offset 0x0f) + */ +#define HP100_BM_BURST_RD       0x01    /* EISA only: 1=Use burst trans. fm system */ +                                        /* memory to chip (tx) */ +#define HP100_BM_BURST_WR       0x02    /* EISA only: 1=Use burst trans. fm system */ +                                        /* memory to chip (rx) */ +#define HP100_BM_MASTER		0x04	/* 0:Slave, 1:BM mode */ +#define HP100_BM_PAGE_CK        0x08    /* This bit should be set whenever in*/ +                                        /* an EISA system */ +#define HP100_BM_PCI_8CLK       0x40    /* ... cycles 8 clocks apart */ + + +/*  + * Mode Control Register I + * (Page HW_MAP, MODECTRL1, Offset0x10) + */ +#define HP100_TX_DUALQ          0x10 +   /* If set and BM -> dual tx pda queues*/ +#define HP100_ISR_CLRMODE       0x02   /* If set ISR will clear all pending */ +                                       /* interrupts on read (etr only?) */ +#define HP100_EE_NOLOAD         0x04   /* Status whether res will be loaded */ +                                       /* from the eeprom */ +#define HP100_TX_CNT_FLG        0x08   /* Controls Early TX Reg Cnt Field */ +#define HP100_PDL_USE3          0x10   /* If set BM engine will read only */ +                                       /* first three data elements of a PDL */ +                                       /* on the first access. */ +#define HP100_BUSTYPE_MASK      0xe0   /* Three bit bus type info */ + +/* + * Mode Control Register II + * (Page HW_MAP, MODECTRL2, Offset0x11) + */ +#define HP100_EE_MASK           0x0f   /* Tell EEPROM circuit not to load */  +                                       /* certain resources */ +#define HP100_DIS_CANCEL        0x20   /* For tx dualq mode operation */ +#define HP100_EN_PDL_WB         0x40   /* 1: Status of PDL completion may be */ +                                       /* written back to system mem */ +#define HP100_EN_BUS_FAIL       0x80   /* Enables bus-fail portion of misc */ +                                       /* interrupt */ + +/*  + * PCI Configuration and Control Register I + * (Page HW_MAP, PCICTRL1, Offset 0x12) + */ +#define HP100_LO_MEM            0x01   /* 1: Mapped Mem requested below 1MB */ +#define HP100_NO_MEM            0x02   /* 1: Disables Req for sysmem to PCI */  +                                       /* bios */ +#define HP100_USE_ISA           0x04   /* 1: isa type decodes will occur */ +                                       /* simultaneously with PCI decodes */ +#define HP100_IRQ_HI_MASK       0xf0   /* pgmed by pci bios */ +#define HP100_PCI_IRQ_HI_MASK   0x78    /* Isolate 4 bits for PCI IRQ  */ +  +/* + * PCI Configuration and Control Register II + * (Page HW_MAP, PCICTRL2, Offset 0x13) + */ +#define HP100_RD_LINE_PDL       0x01   /* 1: PCI command Memory Read Line en */ +#define HP100_RD_TX_DATA_MASK   0x06   /* choose PCI memread cmds for TX */  +#define HP100_MWI               0x08   /* 1: en. PCI memory write invalidate */ +#define HP100_ARB_MODE          0x10   /* Select PCI arbitor type */ +#define HP100_STOP_EN           0x20   /* Enables PCI state machine to issue */ +                                       /* pci stop if cascade not ready */ +#define HP100_IGNORE_PAR        0x40   /* 1: PCI state machine ignores parity*/ +#define HP100_PCI_RESET         0x80   /* 0->1: Reset PCI block */ + +/* + * Early TX Configuration and Control Register + * (Page HW_MAP, EARLYTXCFG, Offset 0x16) + */ +#define HP100_EN_EARLY_TX       0x8000  /* 1=Enable Early TX */ +#define HP100_EN_ADAPTIVE       0x4000  /* 1=Enable adaptive mode */ +#define HP100_EN_TX_UR_IRQ      0x2000  /* reserved, must be 0 */ +#define HP100_EN_LOW_TX         0x1000  /* reserved, must be 0 */ +#define HP100_ET_CNT_MASK       0x0fff  /* bits 11..0: ET counters */ + +/* + * Early RX Configuration and Control Register + * (Page HW_MAP, EARLYRXCFG, Offset 0x18) + */ +#define HP100_EN_EARLY_RX       0x80     /* 1=Enable Early RX */ +#define HP100_EN_LOW_RX         0x40     /* reserved, must be 0 */ +#define HP100_RX_TRIP_MASK      0x1f     /* bits 4..0: threshold at which the +					  * early rx circuit will start the +					  * dma of received packet into system +					  * memory for BM */ + +/* + *  Serial Devices Control Register + * (Page EEPROM_CTRL, EEPROM_CTRL, Offset 0x08) + */ +#define HP100_EEPROM_LOAD	0x0001	/* 0->1 loads EEPROM into registers. */ +                                        /* When it goes back to 0, load is   */ +                                        /* complete. This should take ~600us.*/ + +/* + * 10MB LAN Control and Configuration Register I + * (Page MAC_CTRL, 10_LAN_CFG_1, Offset 0x08) + */ +#define HP100_MAC10_SEL		0xc0	/* Get bits to indicate MAC */ +#define HP100_AUI_SEL		0x20	/* Status of AUI selection */ +#define HP100_LOW_TH		0x10	/* 0:No, 1:Yes allow better cabling */ +#define HP100_LINK_BEAT_DIS	0x08	/* 0:Enable, 1:Disable link beat */ +#define HP100_LINK_BEAT_ST	0x04	/* 0:No, 1:Yes link beat being Rx */ +#define HP100_R_ROL_ST		0x02	/* 0:No, 1:Yes Rx twisted pair has */ +                                        /*             been reversed */ +#define HP100_AUI_ST		0x01	/* 0:No, 1:Yes use AUI on TP card */ + +/* + * 10 MB LAN Control and Configuration Register II + * (Page MAC_CTRL, 10_LAN_CFG_2, Offset 0x09) + */ +#define HP100_SQU_ST		0x01	/* 0:No, 1:Yes collision signal sent */ +                                        /*       after Tx.Only used for AUI. */ +#define HP100_FULLDUP           0x02    /* 1: LXT901 XCVR fullduplx enabled */ +#define HP100_DOT3_MAC          0x04    /* 1: DOT 3 Mac sel. unless Autosel */ + +/* + * MAC Selection, use with MAC10_SEL bits + */ +#define HP100_AUTO_SEL_10	0x0	/* Auto select */ +#define HP100_XCVR_LXT901_10	0x1	/* LXT901 10BaseT transceiver */ +#define HP100_XCVR_7213		0x2	/* 7213 transceiver */ +#define HP100_XCVR_82503	0x3	/* 82503 transceiver */ + +/* + *  100MB LAN Training Register + * (Page MAC_CTRL, VG_LAN_CFG_2, Offset 0x0b) (old, pre 802.12) + */ +#define HP100_FRAME_FORMAT	0x08	/* 0:802.3, 1:802.5 frames */ +#define HP100_BRIDGE		0x04	/* 0:No, 1:Yes tell hub i am a bridge */ +#define HP100_PROM_MODE		0x02	/* 0:No, 1:Yes tell hub card is */ +                                        /*         promiscuous */ +#define HP100_REPEATER		0x01	/* 0:No, 1:Yes tell hub MAC wants to */ +                                        /*         be a cascaded repeater */ + +/* + * 100MB LAN Control and Configuration Register + * (Page MAC_CTRL, VG_LAN_CFG_1, Offset 0x0a)  + */ +#define HP100_VG_SEL	        0x80	/* 0:No, 1:Yes use 100 Mbit MAC */ +#define HP100_LINK_UP_ST	0x40	/* 0:No, 1:Yes endnode logged in */ +#define HP100_LINK_CABLE_ST	0x20	/* 0:No, 1:Yes cable can hear tones */ +                                        /*         from  hub */ +#define HP100_LOAD_ADDR		0x10	/* 0->1 card addr will be sent  */ +                                        /* 100ms later the link status  */ +                                        /* bits are valid */ +#define HP100_LINK_CMD		0x08	/* 0->1 link will attempt to log in. */ +                                        /* 100ms later the link status */ +                                        /* bits are valid */ +#define HP100_TRN_DONE          0x04    /* NEW ETR-Chips only: Will be reset */ +                                        /* after LinkUp Cmd is given and set */ +                                        /* when training has completed. */ +#define HP100_LINK_GOOD_ST	0x02	/* 0:No, 1:Yes cable passed training */ +#define HP100_VG_RESET		0x01	/* 0:Yes, 1:No reset the 100VG MAC */ + + +/* + *  MAC Configuration Register I + * (Page MAC_CTRL, MAC_CFG_1, Offset 0x0c) + */ +#define HP100_RX_IDLE		0x80	/* 0:Yes, 1:No currently receiving pkts */ +#define HP100_TX_IDLE		0x40	/* 0:Yes, 1:No currently Txing pkts */ +#define HP100_RX_EN		0x20	/* 1: allow receiving of pkts */ +#define HP100_TX_EN		0x10	/* 1: allow transmitting of pkts */ +#define HP100_ACC_ERRORED	0x08	/* 0:No, 1:Yes allow Rx of errored pkts */ +#define HP100_ACC_MC		0x04	/* 0:No, 1:Yes allow Rx of multicast pkts */ +#define HP100_ACC_BC		0x02	/* 0:No, 1:Yes allow Rx of broadcast pkts */ +#define HP100_ACC_PHY		0x01	/* 0:No, 1:Yes allow Rx of ALL phys. pkts */ +#define HP100_MAC1MODEMASK	0xf0	/* Hide ACC bits */ +#define HP100_MAC1MODE1		0x00	/* Receive nothing, must also disable RX */ +#define HP100_MAC1MODE2		0x00 +#define HP100_MAC1MODE3		HP100_MAC1MODE2 | HP100_ACC_BC +#define HP100_MAC1MODE4		HP100_MAC1MODE3 | HP100_ACC_MC +#define HP100_MAC1MODE5		HP100_MAC1MODE4 /* set mc hash to all ones also */ +#define HP100_MAC1MODE6		HP100_MAC1MODE5 | HP100_ACC_PHY	/* Promiscuous */ +/* Note MODE6 will receive all GOOD packets on the LAN. This really needs +   a mode 7 defined to be LAN Analyzer mode, which will receive errored and +   runt packets, and keep the CRC bytes. */ +#define HP100_MAC1MODE7		HP100_MAC1MODE6 | HP100_ACC_ERRORED + +/* + *  MAC Configuration Register II  + * (Page MAC_CTRL, MAC_CFG_2, Offset 0x0d) + */ +#define HP100_TR_MODE		0x80	/* 0:No, 1:Yes support Token Ring formats */ +#define HP100_TX_SAME		0x40	/* 0:No, 1:Yes Tx same packet continuous */ +#define HP100_LBK_XCVR		0x20	/* 0:No, 1:Yes loopback through MAC & */ +                                        /*   transceiver */ +#define HP100_LBK_MAC		0x10	/* 0:No, 1:Yes loopback through MAC */ +#define HP100_CRC_I		0x08	/* 0:No, 1:Yes inhibit CRC on Tx packets */ +#define HP100_ACCNA             0x04    /* 1: For 802.5: Accept only token ring +					 * group addr that maches NA mask */ +#define HP100_KEEP_CRC		0x02	/* 0:No, 1:Yes keep CRC on Rx packets. */ +                                        /*   The length will reflect this. */ +#define HP100_ACCFA             0x01    /* 1: For 802.5: Accept only functional +					 * addrs that match FA mask (page1) */ +#define HP100_MAC2MODEMASK	0x02 +#define HP100_MAC2MODE1		0x00 +#define HP100_MAC2MODE2		0x00 +#define HP100_MAC2MODE3		0x00 +#define HP100_MAC2MODE4		0x00 +#define HP100_MAC2MODE5		0x00 +#define HP100_MAC2MODE6		0x00 +#define HP100_MAC2MODE7		KEEP_CRC + +/* + * MAC Configuration Register III  + * (Page MAC_CTRL, MAC_CFG_3, Offset 0x0e)  + */ +#define HP100_PACKET_PACE       0x03     /* Packet Pacing: +					  * 00: No packet pacing +                                          * 01: 8 to 16 uS delay +                                          * 10: 16 to 32 uS delay +                                          * 11: 32 to 64 uS delay +					  */ +#define HP100_LRF_EN            0x04     /* 1: External LAN Rcv Filter and +					  * TCP/IP Checksumming enabled. */ +#define HP100_AUTO_MODE         0x10     /* 1: AutoSelect between 10/100 */ + +/* + * MAC Configuration Register IV  + * (Page MAC_CTRL, MAC_CFG_4, Offset 0x0f) + */ +#define HP100_MAC_SEL_ST        0x01      /* (R): Status of external VGSEL +					   * Signal, 1=100VG, 0=10Mbit sel. */ +#define HP100_LINK_FAIL_ST      0x02      /* (R): Status of Link Fail portion +                                           * of the Misc. Interrupt */ + +/*  + *  100 MB LAN Training Request/Allowed Registers  + * (Page MAC_CTRL, TRAIN_REQUEST and TRAIN_ALLOW, Offset 0x14-0x16)(ETR parts only) + */ +#define HP100_MACRQ_REPEATER         0x0001  /* 1: MAC tells HUB it wants to be  +				              *    a cascaded repeater +					      * 0: ... wants to be a DTE */ +#define HP100_MACRQ_PROMSC           0x0006  /* 2 bits: Promiscious mode +					      * 00: Rcv only unicast packets +					      *     specifically addr to this +					      *     endnode +					      * 10: Rcv all pckts fwded by  +					      *     the local repeater */ +#define HP100_MACRQ_FRAMEFMT_EITHER  0x0018  /* 11: either format allowed */ +#define HP100_MACRQ_FRAMEFMT_802_3   0x0000  /* 00: 802.3 is requested */ +#define HP100_MACRQ_FRAMEFMT_802_5   0x0010  /* 10: 802.5 format is requested */ +#define HP100_CARD_MACVER            0xe000  /* R: 3 bit Cards 100VG MAC version */ +#define HP100_MALLOW_REPEATER        0x0001  /* If reset, requested access as an +					      * end node is allowed */ +#define HP100_MALLOW_PROMSC          0x0004  /* 2 bits: Promiscious mode +					      * 00: Rcv only unicast packets +					      *     specifically addr to this +					      *     endnode +					      * 10: Rcv all pckts fwded by  +					      *     the local repeater */ +#define HP100_MALLOW_FRAMEFMT        0x00e0  /* 2 bits: Frame Format +					      * 00: 802.3 format will be used +					      * 10: 802.5 format will be used */ +#define HP100_MALLOW_ACCDENIED       0x0400  /* N bit */ +#define HP100_MALLOW_CONFIGURE       0x0f00  /* C bit */ +#define HP100_MALLOW_DUPADDR         0x1000  /* D bit */ +#define HP100_HUB_MACVER             0xe000  /* R: 3 bit 802.12 MAC/RMAC training */ +                                             /*    protocol of repeater */ + +/* ****************************************************************************** */ + +/* + *  Set/Reset bits + */ +#define HP100_SET_HB		0x0100	/* 0:Set fields to 0 whose mask is 1 */ +#define HP100_SET_LB		0x0001	/* HB sets upper byte, LB sets lower byte */ +#define HP100_RESET_HB		0x0000	/* For readability when resetting bits */ +#define HP100_RESET_LB		0x0000	/* For readability when resetting bits */ + +/* + *  Misc. Constants + */ +#define HP100_LAN_100		100     /* lan_type value for VG */ +#define HP100_LAN_10		10	/* lan_type value for 10BaseT */ +#define HP100_LAN_ERR		(-1)	/* lan_type value for link down */ + +#define TRUE 1 +#define FALSE 0 + + +/*  + * Bus Master Data Structures  ---------------------------------------------- + */ + +#define MAX_RX_PDL              30   /* Card limit = 31 */ +#define MAX_RX_FRAG             2    /* Don't need more... */ +#define MAX_TX_PDL              29 +#define MAX_TX_FRAG             2   /* Limit = 31 */ + +/* Define total PDL area size in bytes (should be 4096) */ +/* This is the size of kernel (dma) memory that will be allocated. */ +#define MAX_RINGSIZE ((MAX_RX_FRAG*8+4+4)*MAX_RX_PDL+(MAX_TX_FRAG*8+4+4)*MAX_TX_PDL)+16 + +/* Ethernet Packet Sizes */ +#define MIN_ETHER_SIZE          60 +#define MAX_ETHER_SIZE          1514    /* Needed for preallocation of */ +                                        /* skb buffer when busmastering */ + +/* Tx or Rx Ring Entry */ +typedef struct hp100_ring { +  u_int                     *pdl;       /* Address of PDLs PDH, dword before +					 * this address is used for rx hdr */ +  u_int                     pdl_paddr;  /* Physical address of PDL */ +  struct sk_buff            *skb; +  struct hp100_ring         *next;       +} hp100_ring_t; + + + +/* Mask for Header Descriptor */ +#define HP100_PKT_LEN_MASK	0x1FFF	/* AND with RxLength to get length */ + + +/* Receive Packet Status.  Note, the error bits are only valid if ACC_ERRORED  +   bit in the MAC Configuration Register 1 is set. */ +#define HP100_RX_PRI		0x8000	/* 0:No, 1:Yes packet is priority */ +#define HP100_SDF_ERR		0x4000	/* 0:No, 1:Yes start of frame error */ +#define HP100_SKEW_ERR		0x2000	/* 0:No, 1:Yes skew out of range */ +#define HP100_BAD_SYMBOL_ERR	0x1000	/* 0:No, 1:Yes invalid symbol received */ +#define HP100_RCV_IPM_ERR	0x0800	/* 0:No, 1:Yes pkt had an invalid packet */ +                                        /*   marker */ +#define HP100_SYMBOL_BAL_ERR	0x0400	/* 0:No, 1:Yes symbol balance error */ +#define HP100_VG_ALN_ERR	0x0200	/* 0:No, 1:Yes non-octet received */ +#define HP100_TRUNC_ERR		0x0100	/* 0:No, 1:Yes the packet was truncated */ +#define HP100_RUNT_ERR		0x0040	/* 0:No, 1:Yes pkt length < Min Pkt */ +                                        /*   Length Reg. */ +#define HP100_ALN_ERR		0x0010	/* 0:No, 1:Yes align error. */ +#define HP100_CRC_ERR		0x0008	/* 0:No, 1:Yes CRC occurred. */ + +/* The last three bits indicate the type of destination address */ + +#define HP100_MULTI_ADDR_HASH	0x0006	/* 110: Addr multicast, matched hash */ +#define HP100_BROADCAST_ADDR	0x0003	/* x11: Addr broadcast */ +#define HP100_MULTI_ADDR_NO_HASH 0x0002	/* 010: Addr multicast, didn't match hash */ +#define HP100_PHYS_ADDR_MATCH	0x0001	/* x01: Addr was physical and mine */ +#define HP100_PHYS_ADDR_NO_MATCH 0x0000	/* x00: Addr was physical but not mine */ + +/* + *  macros + */ + +#define hp100_inb( reg ) \ +        inb( ioaddr + HP100_REG_##reg ) +#define hp100_inw( reg ) \ +	inw( ioaddr + HP100_REG_##reg ) +#define hp100_inl( reg ) \ +	inl( ioaddr + HP100_REG_##reg ) +#define hp100_outb( data, reg ) \ +	outb( data, ioaddr + HP100_REG_##reg ) +#define hp100_outw( data, reg ) \ +	outw( data, ioaddr + HP100_REG_##reg ) +#define hp100_outl( data, reg ) \ +	outl( data, ioaddr + HP100_REG_##reg ) +#define hp100_orb( data, reg ) \ +	outb( inb( ioaddr + HP100_REG_##reg ) | (data), ioaddr + HP100_REG_##reg ) +#define hp100_orw( data, reg ) \ +	outw( inw( ioaddr + HP100_REG_##reg ) | (data), ioaddr + HP100_REG_##reg ) +#define hp100_andb( data, reg ) \ +	outb( inb( ioaddr + HP100_REG_##reg ) & (data), ioaddr + HP100_REG_##reg ) +#define hp100_andw( data, reg ) \ +	outw( inw( ioaddr + HP100_REG_##reg ) & (data), ioaddr + HP100_REG_##reg ) + +#define hp100_page( page ) \ +	outw( HP100_PAGE_##page, ioaddr + HP100_REG_PAGING ) +#define hp100_ints_off() \ +	outw( HP100_INT_EN | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW ) +#define hp100_ints_on() \ +	outw( HP100_INT_EN | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW ) +#define hp100_mem_map_enable() \ +	outw( HP100_MMAP_DIS | HP100_RESET_HB, ioaddr + HP100_REG_OPTION_LSW ) +#define hp100_mem_map_disable() \ +	outw( HP100_MMAP_DIS | HP100_SET_HB, ioaddr + HP100_REG_OPTION_LSW ) + + +/* + * Local variables: + *  c-indent-level: 2 + *  tab-width: 8 + * End: +*/ diff --git a/linux/src/drivers/net/i82586.h b/linux/src/drivers/net/i82586.h new file mode 100644 index 0000000..d41702e --- /dev/null +++ b/linux/src/drivers/net/i82586.h @@ -0,0 +1,413 @@ +/* + * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor. + * + * See: + *	Intel Microcommunications 1991 + *	p1-1 to p1-37 + *	Intel order No. 231658 + *	ISBN 1-55512-119-5 + * + *     Unfortunately, the above chapter mentions neither + * the System Configuration Pointer (SCP) nor the + * Intermediate System Configuration Pointer (ISCP), + * so we probably need to look elsewhere for the + * whole story -- some recommend the "Intel LAN + * Components manual" but I have neither a copy + * nor a full reference.  But "elsewhere" may be + * in the same publication... + *     The description of a later device, the + * "82596CA High-Performance 32-Bit Local Area Network + * Coprocessor", (ibid. p1-38 to p1-109) does mention + * the SCP and ISCP and also has an i82586 compatibility + * mode.  Even more useful is "AP-235 An 82586 Data Link + * Driver" (ibid. p1-337 to p1-417). + */ + +#define	I82586_MEMZ	(64 * 1024) + +#define	I82586_SCP_ADDR	(I82586_MEMZ - sizeof(scp_t)) + +#define	ADDR_LEN	6 +#define	I82586NULL	0xFFFF + +#define	toff(t,p,f) 	(unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0) + +/* + * System Configuration Pointer (SCP). + */ +typedef struct scp_t	scp_t; +struct scp_t +{ +	unsigned short	scp_sysbus;	/* 82586 bus width:	*/ +#define		SCP_SY_16BBUS	(0x0 << 0)	/* 16 bits */ +#define		SCP_SY_8BBUS	(0x1 << 0)	/*  8 bits. */ +	unsigned short	scp_junk[2];	/* Unused */ +	unsigned short	scp_iscpl;	/* lower 16 bits of ISCP_ADDR */ +	unsigned short	scp_iscph;	/* upper 16 bits of ISCP_ADDR */ +}; + +/* + * Intermediate System Configuration Pointer (ISCP). + */ +typedef struct iscp_t	iscp_t; +struct iscp_t +{ +	unsigned short	iscp_busy;	/* set by CPU before first CA,	*/ +					/* cleared by 82586 after read.	*/ +	unsigned short	iscp_offset;	/* offset of SCB		*/ +	unsigned short	iscp_basel;	/* base of SCB			*/ +	unsigned short	iscp_baseh;	/*  "				*/ +}; + +/* + * System Control Block (SCB). + *	The 82586 writes its status to scb_status and then + *	raises an interrupt to alert the CPU. + *	The CPU writes a command to scb_command and + *	then issues a Channel Attention (CA) to alert the 82586. + */ +typedef struct scb_t	scb_t; +struct scb_t +{ +	unsigned short	scb_status;	/* Status of 82586		*/ +#define		SCB_ST_INT	(0xF << 12)	/* Some of:		*/ +#define		SCB_ST_CX	(0x1 << 15)	/* Cmd completed	*/ +#define		SCB_ST_FR	(0x1 << 14)	/* Frame received	*/ +#define		SCB_ST_CNA	(0x1 << 13)	/* Cmd unit not active	*/ +#define		SCB_ST_RNR	(0x1 << 12)	/* Rcv unit not ready	*/ +#define		SCB_ST_JUNK0	(0x1 << 11)	/* 0			*/ +#define		SCB_ST_CUS	(0x7 <<  8)	/* Cmd unit status	*/ +#define			SCB_ST_CUS_IDLE	(0 << 8)	/* Idle		*/ +#define			SCB_ST_CUS_SUSP	(1 << 8)	/* Suspended	*/ +#define			SCB_ST_CUS_ACTV	(2 << 8)	/* Active	*/ +#define		SCB_ST_JUNK1	(0x1 <<  7)	/* 0			*/ +#define		SCB_ST_RUS	(0x7 <<  4)	/* Rcv unit status	*/ +#define			SCB_ST_RUS_IDLE	(0 << 4)	/* Idle		*/ +#define			SCB_ST_RUS_SUSP	(1 << 4)	/* Suspended	*/ +#define			SCB_ST_RUS_NRES	(2 << 4)	/* No resources	*/ +#define			SCB_ST_RUS_RDY	(4 << 4)	/* Ready	*/ +	unsigned short	scb_command;	/* Next command			*/ +#define		SCB_CMD_ACK_CX	(0x1 << 15)	/* Ack cmd completion	*/ +#define		SCB_CMD_ACK_FR	(0x1 << 14)	/* Ack frame received	*/ +#define		SCB_CMD_ACK_CNA	(0x1 << 13)	/* Ack CU not active	*/ +#define		SCB_CMD_ACK_RNR	(0x1 << 12)	/* Ack RU not ready	*/ +#define		SCB_CMD_JUNKX	(0x1 << 11)	/* Unused		*/ +#define		SCB_CMD_CUC	(0x7 <<  8)	/* Command Unit command	*/ +#define			SCB_CMD_CUC_NOP	(0 << 8)	/* Nop		*/ +#define			SCB_CMD_CUC_GO	(1 << 8)	/* Start cbl_offset */ +#define			SCB_CMD_CUC_RES	(2 << 8)	/* Resume execution */ +#define			SCB_CMD_CUC_SUS	(3 << 8)	/* Suspend   "	*/ +#define			SCB_CMD_CUC_ABT	(4 << 8)	/* Abort     "	*/ +#define		SCB_CMD_RESET	(0x1 <<  7)	/* Reset chip (hardware) */ +#define		SCB_CMD_RUC	(0x7 <<  4)	/* Receive Unit command	*/ +#define			SCB_CMD_RUC_NOP	(0 << 4)	/* Nop		*/ +#define			SCB_CMD_RUC_GO	(1 << 4)	/* Start rfa_offset */ +#define			SCB_CMD_RUC_RES	(2 << 4)	/* Resume reception */ +#define			SCB_CMD_RUC_SUS	(3 << 4)	/* Suspend   "	*/ +#define			SCB_CMD_RUC_ABT	(4 << 4)	/* Abort     "	*/ +	unsigned short	scb_cbl_offset;	/* Offset of first command unit	*/ +					/* Action Command		*/ +	unsigned short	scb_rfa_offset;	/* Offset of first Receive	*/ +					/* Frame Descriptor in the	*/ +					/* Receive Frame Area		*/ +	unsigned short	scb_crcerrs;	/* Properly aligned frames	*/ +					/* received with a CRC error	*/ +	unsigned short	scb_alnerrs;	/* Misaligned frames received	*/ +					/* with a CRC error		*/ +	unsigned short	scb_rscerrs;	/* Frames lost due to no space	*/ +	unsigned short	scb_ovrnerrs;	/* Frames lost due to slow bus	*/ +}; + +#define	scboff(p,f) 	toff(scb_t, p, f) + +/* + * The eight Action Commands. + */ +typedef enum acmd_e	acmd_e; +enum acmd_e +{ +	acmd_nop	= 0,	/* Do nothing				*/ +	acmd_ia_setup	= 1,	/* Load an (ethernet) address into the	*/ +				/* 82586				*/ +	acmd_configure	= 2,	/* Update the 82586 operating parameters */ +	acmd_mc_setup	= 3,	/* Load a list of (ethernet) multicast	*/ +				/* addresses into the 82586		*/ +	acmd_transmit	= 4,	/* Transmit a frame			*/ +	acmd_tdr	= 5,	/* Perform a Time Domain Reflectometer	*/ +				/* test on the serial link		*/ +	acmd_dump	= 6,	/* Copy 82586 registers to memory	*/ +	acmd_diagnose	= 7,	/* Run an internal self test		*/ +}; + +/* + * Generic Action Command header. + */ +typedef struct ach_t	ach_t; +struct ach_t +{ +	unsigned short	ac_status;		/* Command status:	*/ +#define		AC_SFLD_C	(0x1 << 15)	/* Command completed	*/ +#define		AC_SFLD_B	(0x1 << 14)	/* Busy executing	*/ +#define		AC_SFLD_OK	(0x1 << 13)	/* Completed error free	*/ +#define		AC_SFLD_A	(0x1 << 12)	/* Command aborted	*/ +#define		AC_SFLD_FAIL	(0x1 << 11)	/* Selftest failed	*/ +#define		AC_SFLD_S10	(0x1 << 10)	/* No carrier sense	*/ +						/* during transmission	*/ +#define		AC_SFLD_S9	(0x1 <<  9)	/* Tx unsuccessful:	*/ +						/* (stopped) lost CTS	*/ +#define		AC_SFLD_S8	(0x1 <<  8)	/* Tx unsuccessful:	*/ +						/* (stopped) slow DMA	*/ +#define		AC_SFLD_S7	(0x1 <<  7)	/* Tx deferred:		*/ +						/* other link traffic	*/ +#define		AC_SFLD_S6	(0x1 <<  6)	/* Heart Beat: collision */ +						/* detect after last tx	*/ +#define		AC_SFLD_S5	(0x1 <<  5)	/* Tx stopped:		*/ +						/* excessive collisions	*/ +#define		AC_SFLD_MAXCOL	(0xF <<  0)	/* Collision count  	*/ +	unsigned short	ac_command;		/* Command specifier:	*/ +#define		AC_CFLD_EL	(0x1 << 15)	/* End of command list	*/ +#define		AC_CFLD_S	(0x1 << 14)	/* Suspend on completion */ +#define		AC_CFLD_I	(0x1 << 13)	/* Interrupt on completion */ +#define		AC_CFLD_CMD	(0x7 <<  0)	/* acmd_e		*/ +	unsigned short	ac_link;		/* Next Action Command	*/ +}; + +#define	acoff(p,f) 	toff(ach_t, p, f) + +/* + * The Nop Action Command. + */ +typedef struct ac_nop_t	ac_nop_t; +struct ac_nop_t +{ +	ach_t	nop_h; +}; + +/* + * The IA-Setup Action Command. + */ +typedef struct ac_ias_t	ac_ias_t; +struct ac_ias_t +{ +	ach_t		ias_h; +	unsigned char	ias_addr[ADDR_LEN]; /* The (ethernet) address	*/ +}; + +/* + * The Configure Action Command. + */ +typedef struct ac_cfg_t	ac_cfg_t; +struct ac_cfg_t +{ +	ach_t		cfg_h; +	unsigned char	cfg_byte_cnt;	/* Size foll data: 4-12	*/ +#define	AC_CFG_BYTE_CNT(v)	(((v) & 0xF) << 0) +	unsigned char	cfg_fifolim;	/* FIFO threshold	*/ +#define	AC_CFG_FIFOLIM(v)	(((v) & 0xF) << 0) +	unsigned char	cfg_byte8; +#define	AC_CFG_SAV_BF(v) 	(((v) & 0x1) << 7)	/* Save rxd bad frames	*/ +#define	AC_CFG_SRDY(v) 		(((v) & 0x1) << 6)	/* SRDY/ARDY pin means	*/ +							/* external sync.	*/ +	unsigned char	cfg_byte9; +#define	AC_CFG_ELPBCK(v)	(((v) & 0x1) << 7)	/* External loopback	*/ +#define	AC_CFG_ILPBCK(v)	(((v) & 0x1) << 6)	/* Internal loopback	*/ +#define	AC_CFG_PRELEN(v)	(((v) & 0x3) << 4)	/* Preamble length	*/ +#define		AC_CFG_PLEN_2		0		/*  2 bytes	*/ +#define		AC_CFG_PLEN_4		1		/*  4 bytes	*/ +#define		AC_CFG_PLEN_8		2		/*  8 bytes	*/ +#define		AC_CFG_PLEN_16		3		/* 16 bytes	*/ +#define	AC_CFG_ALOC(v)		(((v) & 0x1) << 3)	/* Addr/len data is	*/ +							/* explicit in buffers	*/ +#define	AC_CFG_ADDRLEN(v)	(((v) & 0x7) << 0)	/* Bytes per address	*/ +	unsigned char	cfg_byte10; +#define	AC_CFG_BOFMET(v)	(((v) & 0x1) << 7)	/* Use alternate expo.	*/ +							/* backoff method	*/ +#define	AC_CFG_ACR(v)		(((v) & 0x7) << 4)	/* Accelerated cont. res. */ +#define	AC_CFG_LINPRIO(v)	(((v) & 0x7) << 0)	/* Linear priority	*/ +	unsigned char	cfg_ifs;	/* Interframe spacing		*/ +	unsigned char	cfg_slotl;	/* Slot time (low byte)		*/ +	unsigned char	cfg_byte13; +#define	AC_CFG_RETRYNUM(v)	(((v) & 0xF) << 4)	/* Max. collision retry	*/ +#define	AC_CFG_SLTTMHI(v)	(((v) & 0x7) << 0)	/* Slot time (high bits) */ +	unsigned char	cfg_byte14; +#define	AC_CFG_FLGPAD(v)	(((v) & 0x1) << 7)	/* Pad with HDLC flags	*/ +#define	AC_CFG_BTSTF(v)		(((v) & 0x1) << 6)	/* Do HDLC bitstuffing	*/ +#define	AC_CFG_CRC16(v)		(((v) & 0x1) << 5)	/* 16 bit CCITT CRC	*/ +#define	AC_CFG_NCRC(v)		(((v) & 0x1) << 4)	/* Insert no CRC	*/ +#define	AC_CFG_TNCRS(v)		(((v) & 0x1) << 3)	/* Tx even if no carrier */ +#define	AC_CFG_MANCH(v)		(((v) & 0x1) << 2)	/* Manchester coding	*/ +#define	AC_CFG_BCDIS(v)		(((v) & 0x1) << 1)	/* Disable broadcast	*/ +#define	AC_CFG_PRM(v)		(((v) & 0x1) << 0)	/* Promiscuous mode	*/ +	unsigned char	cfg_byte15; +#define	AC_CFG_ICDS(v)		(((v) & 0x1) << 7)	/* Internal collision	*/ +							/* detect source	*/ +#define	AC_CFG_CDTF(v)		(((v) & 0x7) << 4)	/* Collision detect	*/ +							/* filter in bit times	*/ +#define	AC_CFG_ICSS(v)		(((v) & 0x1) << 3)	/* Internal carrier	*/ +							/* sense source		*/ +#define	AC_CFG_CSTF(v)		(((v) & 0x7) << 0)	/* Carrier sense	*/ +							/* filter in bit times	*/ +	unsigned short	cfg_min_frm_len; +#define	AC_CFG_MNFRM(v)		(((v) & 0xFF) << 0)	/* Min. bytes/frame (<= 255) */ +}; + +/* + * The MC-Setup Action Command. + */ +typedef struct ac_mcs_t	ac_mcs_t; +struct ac_mcs_t +{ +	ach_t		mcs_h; +	unsigned short	mcs_cnt;	/* No. of bytes of MC addresses	*/ +#if 0 +	unsigned char	mcs_data[ADDR_LEN]; /* The first MC address ..	*/ +	... +#endif +}; + +#define I82586_MAX_MULTICAST_ADDRESSES	128	/* Hardware hashed filter */ + +/* + * The Transmit Action Command. + */ +typedef struct ac_tx_t	ac_tx_t; +struct ac_tx_t +{ +	ach_t		tx_h; +	unsigned short	tx_tbd_offset;	/* Address of list of buffers.	*/ +#if	0 +Linux packets are passed down with the destination MAC address +and length/type field already prepended to the data, +so we do not need to insert it.  Consistent with this +we must also set the AC_CFG_ALOC(..) flag during the +ac_cfg_t action command. +	unsigned char	tx_addr[ADDR_LEN]; /* The frame dest. address	*/ +	unsigned short	tx_length;	/* The frame length		*/ +#endif	/* 0 */ +}; + +/* + * The Time Domain Reflectometer Action Command. + */ +typedef struct ac_tdr_t	ac_tdr_t; +struct ac_tdr_t +{ +	ach_t		tdr_h; +	unsigned short	tdr_result;	/* Result.	*/ +#define		AC_TDR_LNK_OK	(0x1 << 15)	/* No link problem	*/ +#define		AC_TDR_XCVR_PRB	(0x1 << 14)	/* Txcvr cable problem	*/ +#define		AC_TDR_ET_OPN	(0x1 << 13)	/* Open on the link	*/ +#define		AC_TDR_ET_SRT	(0x1 << 12)	/* Short on the link	*/ +#define		AC_TDR_TIME	(0x7FF << 0)	/* Distance to problem	*/ +						/* site	in transmit	*/ +						/* clock cycles		*/ +}; + +/* + * The Dump Action Command. + */ +typedef struct ac_dmp_t	ac_dmp_t; +struct ac_dmp_t +{ +	ach_t		dmp_h; +	unsigned short	dmp_offset;	/* Result.	*/ +}; + +/* + * Size of the result of the dump command. + */ +#define	DUMPBYTES	170 + +/* + * The Diagnose Action Command. + */ +typedef struct ac_dgn_t	ac_dgn_t; +struct ac_dgn_t +{ +	ach_t		dgn_h; +}; + +/* + * Transmit Buffer Descriptor (TBD). + */ +typedef struct tbd_t	tbd_t; +struct tbd_t +{ +	unsigned short	tbd_status;		/* Written by the CPU	*/ +#define		TBD_STATUS_EOF	(0x1 << 15)	/* This TBD is the	*/ +						/* last for this frame	*/ +#define		TBD_STATUS_ACNT	(0x3FFF << 0)	/* Actual count of data	*/ +						/* bytes in this buffer	*/ +	unsigned short	tbd_next_bd_offset;	/* Next in list		*/ +	unsigned short	tbd_bufl;		/* Buffer address (low)	*/ +	unsigned short	tbd_bufh;		/*    "	     "	(high)	*/ +}; + +/* + * Receive Buffer Descriptor (RBD). + */ +typedef struct rbd_t	rbd_t; +struct rbd_t +{ +	unsigned short	rbd_status;		/* Written by the 82586	*/ +#define		RBD_STATUS_EOF	(0x1 << 15)	/* This RBD is the	*/ +						/* last for this frame	*/ +#define		RBD_STATUS_F	(0x1 << 14)	/* ACNT field is valid	*/ +#define		RBD_STATUS_ACNT	(0x3FFF << 0)	/* Actual no. of data	*/ +						/* bytes in this buffer	*/ +	unsigned short	rbd_next_rbd_offset;	/* Next rbd in list	*/ +	unsigned short	rbd_bufl;		/* Data pointer (low)	*/ +	unsigned short	rbd_bufh;		/*  "	   "    (high)	*/ +	unsigned short	rbd_el_size;		/* EL+Data buf. size	*/ +#define		RBD_EL	(0x1 << 15)		/* This BD is the	*/ +						/* last in the list	*/ +#define		RBD_SIZE (0x3FFF << 0)		/* No. of bytes the	*/ +						/* buffer can hold	*/ +}; + +#define	rbdoff(p,f) 	toff(rbd_t, p, f) + +/* + * Frame Descriptor (FD). + */ +typedef struct fd_t	fd_t; +struct fd_t +{ +	unsigned short	fd_status;		/* Written by the 82586	*/ +#define		FD_STATUS_C	(0x1 << 15)	/* Completed storing frame */ +#define		FD_STATUS_B	(0x1 << 14)	/* FD was consumed by RU */ +#define		FD_STATUS_OK	(0x1 << 13)	/* Frame rxd successfully */ +#define		FD_STATUS_S11	(0x1 << 11)	/* CRC error		*/ +#define		FD_STATUS_S10	(0x1 << 10)	/* Alignment error	*/ +#define		FD_STATUS_S9	(0x1 <<  9)	/* Ran out of resources	*/ +#define		FD_STATUS_S8	(0x1 <<  8)	/* Rx DMA overrun	*/ +#define		FD_STATUS_S7	(0x1 <<  7)	/* Frame too short	*/ +#define		FD_STATUS_S6	(0x1 <<  6)	/* No EOF flag		*/ +	unsigned short	fd_command;		/* Command		*/ +#define		FD_COMMAND_EL	(0x1 << 15)	/* Last FD in list	*/ +#define		FD_COMMAND_S	(0x1 << 14)	/* Suspend RU after rx	*/ +	unsigned short	fd_link_offset;		/* Next FD		*/ +	unsigned short	fd_rbd_offset;		/* First RBD (data)	*/ +						/* Prepared by CPU,	*/ +						/* updated by 82586	*/ +#if	0 +I think the rest is unused since we +have set AC_CFG_ALOC(..).  However, just +in case, we leave the space. +#endif	/* 0 */ +	unsigned char	fd_dest[ADDR_LEN];	/* Destination address	*/ +						/* Written by 82586	*/ +	unsigned char	fd_src[ADDR_LEN];	/* Source address	*/ +						/* Written by 82586	*/ +	unsigned short	fd_length;		/* Frame length or type	*/ +						/* Written by 82586	*/ +}; + +#define	fdoff(p,f) 	toff(fd_t, p, f) + +/* + * This software may only be used and distributed + * according to the terms of the GNU Public License. + * + * For more details, see wavelan.c. + */ diff --git a/linux/src/drivers/net/intel-gige.c b/linux/src/drivers/net/intel-gige.c new file mode 100644 index 0000000..5884ffb --- /dev/null +++ b/linux/src/drivers/net/intel-gige.c @@ -0,0 +1,1450 @@ +/* intel-gige.c: A Linux device driver for Intel Gigabit Ethernet adapters. */ +/* +	Written 2000-2002 by Donald Becker. +	Copyright Scyld Computing Corporation. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	You should have received a copy of the GPL with this file. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	410 Severn Ave., Suite 210 +	Annapolis MD 21403 + +	Support information and updates available at +	http://www.scyld.com/network/ethernet.html +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"intel-gige.c:v0.14 11/17/2002 Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/ethernet.html\n"; + +/* Automatically extracted configuration info: +probe-func: igige_probe +config-in: tristate 'Intel PCI Gigabit Ethernet support' CONFIG_IGIGE + +c-help-name: Intel PCI Gigabit Ethernet support +c-help-symbol: CONFIG_IGIGE +c-help: This driver is for the Intel PCI Gigabit Ethernet +c-help: adapter series. +c-help: More specific information and updates are available from  +c-help: http://www.scyld.com/network/drivers.html +*/ + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   This chip has a 16 element perfect filter, and an unusual 4096 bit +   hash filter based directly on address bits, not the Ethernet CRC. +   It is costly to recalculate a large, frequently changing table. +   However even a large table may useful in some nearly-static environments. +*/ +static int multicast_filter_limit = 15; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   The media type is passed in 'options[]'.  The full_duplex[] table only +   allows the duplex to be forced on, implicitly disabling autonegotiation. +   Setting the entry to zero still allows a link to autonegotiate to full +   duplex. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* The delay before announcing a Rx or Tx has completed. */ +static int rx_intr_holdoff = 0; +static int tx_intr_holdoff = 128; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two to avoid divides. +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority. +   There are no ill effects from too-large receive rings. */ +#if ! defined(final_version)		/* Stress the driver. */ +#define TX_RING_SIZE	8 +#define TX_QUEUE_LEN	5 +#define RX_RING_SIZE	4 +#else +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */ +#define RX_RING_SIZE	32 +#endif + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Intel Gigabit Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, +				 "Non-zero to set forced full duplex (deprecated)."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This driver is for the Intel Gigabit Ethernet adapter. + +II. Board-specific settings + +III. Driver operation + +IIIa. Descriptor Rings + +This driver uses two statically allocated fixed-size descriptor arrays +treated as rings by the hardware. The ring sizes are set at compile time +by RX/TX_RING_SIZE. + +IIIb/c. Transmit/Receive Structure + +This driver uses a zero-copy receive and transmit scheme. +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers.  When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack.  Buffers consumed this way are replaced by newly allocated +skbuffs in a later phase of receives. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets.  When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine.  Copying also preloads the cache, which is +most useful with small frames. + +A subtle aspect of the operation is that the IP header at offset 14 in an +ethernet frame isn't longword aligned for further processing. +When unaligned buffers are permitted by the hardware (and always on copies) +frames are put into the skbuff at an offset of "+2", 16-byte aligning +the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control. +One is the send-packet routine which is single-threaded by the queue +layer.  The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring.  At the +start of a transmit attempt netif_pause_tx_queue(dev) is called.  If the +transmit attempt fills the Tx queue controlled by the chip, the driver +informs the software queue layer by not calling +netif_unpause_tx_queue(dev) on exit. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IIId. SMP semantics + +The following are serialized with respect to each other via the "xmit_lock". +  dev->hard_start_xmit()	Transmit a packet +  dev->tx_timeout()			Transmit watchdog for stuck Tx +  dev->set_multicast_list()	Set the recieve filter. +Note: The Tx timeout watchdog code is implemented by the timer routine in +kernels up to 2.2.*.  In 2.4.* and later the timeout code is part of the +driver interface. + +The following fall under the global kernel lock.  The module will not be +unloaded during the call, unless a call with a potential reschedule e.g. +kmalloc() is called.  No other synchronization assertion is made. +  dev->open() +  dev->do_ioctl() +  dev->get_stats() +Caution: The lock for dev->open() is commonly broken with request_irq() or +kmalloc().  It is best to avoid any lock-breaking call in do_ioctl() and +get_stats(), or additional module locking code must be implemented. + +The following is self-serialized (no simultaneous entry) +  An handler registered with request_irq(). + +IV. Notes + +IVb. References + +Intel has also released a Linux driver for this product, "e1000". + +IVc. Errata + +*/ + + + +static void *igige_probe1(struct pci_dev *pdev, void *init_dev, +						   long ioaddr, int irq, int chip_idx, int find_cnt); +static int netdev_pwr_event(void *dev_instance, int event); +enum chip_capability_flags { CanHaveMII=1, }; +#define PCI_IOTYPE () + +static struct pci_id_info pci_id_tbl[] = { +	{"Intel Gigabit Ethernet adapter", {0x10008086, 0xffffffff, }, +	 PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR0, 0x1ffff, 0}, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info igige_drv_id = { +	"intel-gige", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	igige_probe1, netdev_pwr_event }; + +/* This hardware only has a PCI memory space BAR, not I/O space. */ +#ifdef USE_IO_OPS +#error This driver only works with PCI memory space access. +#endif + +/* Offsets to the device registers. +*/ +enum register_offsets { +	ChipCtrl=0x00, ChipStatus=0x08, EECtrl=0x10, +	FlowCtrlAddrLo=0x028, FlowCtrlAddrHi=0x02c, FlowCtrlType=0x030, +	VLANetherType=0x38, + +	RxAddrCAM=0x040, +	IntrStatus=0x0C0,			/* Interrupt, Clear on Read, AKA ICR */ +	IntrEnable=0x0D0,			/* Set enable mask when '1' AKA IMS */ +	IntrDisable=0x0D8,			/* Clear enable mask when '1' */ + +	RxControl=0x100, +	RxQ0IntrDelay=0x108,		/* Rx list #0 interrupt delay timer. */ +	RxRingPtr=0x110,			/* Rx Desc. list #0 base address, 64bits */ +	RxRingLen=0x118,			/* Num bytes of Rx descriptors in ring.  */ +	RxDescHead=0x120, +	RxDescTail=0x128, + +	RxQ1IntrDelay=0x130,		/* Rx list #1 interrupt delay timer. */ +	RxRing1Ptr=0x138,			/* Rx Desc. list #1 base address, 64bits */ +	RxRing1Len=0x140,			/* Num bytes of Rx descriptors in ring.  */ +	RxDesc1Head=0x148, +	RxDesc1Tail=0x150, + +	FlowCtrlTimer=0x170, FlowCtrlThrshHi=0x160, FlowCtrlThrshLo=0x168,  +	TxConfigReg=0x178, +	RxConfigReg=0x180, +	MulticastArray=0x200, + +	TxControl=0x400, +	TxQState=0x408,				/* 64 bit queue state */ +	TxIPG=0x410,				/* Inter-Packet Gap */ +	TxRingPtr=0x420, TxRingLen=0x428, +	TxDescHead=0x430, TxDescTail=0x438, TxIntrDelay=0x440, + +	RxCRCErrs=0x4000, RxMissed=0x4010, + +	TxStatus=0x408, +	RxStatus=0x180, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrTxDone=0x0001,			/* Tx packet queued */ +	IntrLinkChange=0x0004,		/* Link Status Change */ +	IntrRxSErr=0x0008, 			/* Rx Symbol/Sequence error */ +	IntrRxEmpty=0x0010,			/* Rx queue 0 Empty */ +	IntrRxQ1Empty=0x0020,		/* Rx queue 1 Empty */ +	IntrRxDone=0x0080,			/* Rx Done, Queue 0*/ +	IntrRxDoneQ1=0x0100,		/* Rx Done, Queue 0*/ +	IntrPCIErr=0x0200,			/* PCI Bus Error */ + +	IntrTxEmpty=0x0002,			/* Guess */ +	StatsMax=0x1000,			/* Unknown */ +}; + +/* Bits in the RxFilterMode register. */ +enum rx_mode_bits { +	RxCtrlReset=0x01, RxCtrlEnable=0x02, RxCtrlAllUnicast=0x08, +	RxCtrlAllMulticast=0x10, +	RxCtrlLoopback=0xC0,		/* We never configure loopback */ +	RxCtrlAcceptBroadcast=0x8000,  +	/* Aliased names.*/ +	AcceptAllPhys=0x08,	AcceptAllMulticast=0x10, AcceptBroadcast=0x8000, +	AcceptMyPhys=0, +	AcceptMulticast=0, +}; + +/* The Rx and Tx buffer descriptors. */ +struct rx_desc { +	u32 buf_addr; +	u32 buf_addr_hi; +	u32 csum_length;			/* Checksum and length */ +	u32 status;					/* Errors and status. */ +}; + +struct tx_desc { +	u32 buf_addr; +	u32 buf_addr_hi; +	u32 cmd_length; +	u32 status;					/* And errors */ +}; + +/* Bits in tx_desc.cmd_length */ +enum tx_cmd_bits { +	TxDescEndPacket=0x02000000, TxCmdIntrDelay=0x80000000, +	TxCmdAddCRC=0x02000000, TxCmdDoTx=0x13000000, +}; +enum tx_status_bits { +	TxDescDone=0x0001, TxDescEndPkt=0x0002, +}; + +/* Bits in tx_desc.status */ +enum rx_status_bits { +	RxDescDone=0x0001, RxDescEndPkt=0x0002, +}; + + +#define PRIV_ALIGN	15 	/* Required alignment mask */ +/* Use  __attribute__((aligned (L1_CACHE_BYTES)))  to maintain alignment +   within the structure. */ +struct netdev_private { +	struct net_device *next_module;		/* Link for devices of this type. */ +	void *priv_addr;					/* Unaligned address for kfree */ +	const char *product_name; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for later free(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	/* Keep frequently used values adjacent for cache effect. */ +	int msg_level; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	int max_interrupt_work; +	int intr_enable; +	long in_interrupt;			/* Word-long for SMP locks. */ + +	struct rx_desc *rx_ring; +	struct rx_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	struct tx_desc *tx_ring; +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1;				/* The Tx queue is full. */ + +	unsigned int rx_mode; +	unsigned int tx_config; +	int multicast_filter_limit; +	/* These values track the transceiver/media in use. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +}; + +static int  eeprom_read(long ioaddr, int location); +static int  netdev_open(struct net_device *dev); +static int  change_mtu(struct net_device *dev, int new_mtu); +static void check_duplex(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct net_device *dev); +static void init_ring(struct net_device *dev); +static int  start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int  netdev_rx(struct net_device *dev); +static void netdev_error(struct net_device *dev, int intr_status); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int  netdev_close(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_net_dev = NULL; + +#ifndef MODULE +int igige_probe(struct net_device *dev) +{ +	if (pci_drv_register(&igige_drv_id, dev) < 0) +		return -ENODEV; +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif + +static void *igige_probe1(struct pci_dev *pdev, void *init_dev, +						   long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct netdev_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	printk(KERN_INFO "%s: %s at 0x%lx, ", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr); + +	for (i = 0; i < 3; i++) +		((u16*)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i)); +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	/* Do bogusness checks before this point. +	   We do a request_region() only to register /proc/ioports info. */ +	request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name); + +	/* Reset the chip to erase previous misconfiguration. */ +	writel(0x04000000, ioaddr + ChipCtrl); + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_net_dev; +	root_net_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	if (dev->mem_start) +		option = dev->mem_start; + +	/* The lower four bits are the media type. */ +	if (option > 0) { +		if (option & 0x2220) +			np->full_duplex = 1; +		np->default_port = option & 0x3330; +		if (np->default_port) +			np->medialock = 1; +	} +	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) +		np->duplex_lock = 1; + +#if ! defined(final_version) /* Dump the EEPROM contents during development. */ +	if (np->msg_level & NETIF_MSG_MISC) { +		int sum = 0; +		for (i = 0; i < 0x40; i++) { +			int eeval = eeprom_read(ioaddr, i); +			printk("%4.4x%s", eeval, i % 16 != 15 ? " " : "\n"); +			sum += eeval; +		} +		printk(KERN_DEBUG "%s:  EEPROM checksum %4.4X (expected value 0xBABA).\n", +			   dev->name, sum & 0xffff); +	} +#endif + +	/* The chip-specific entries in the device structure. */ +	dev->open = &netdev_open; +	dev->hard_start_xmit = &start_tx; +	dev->stop = &netdev_close; +	dev->get_stats = &get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; +	dev->change_mtu = &change_mtu; + +	/* Turn off VLAN and clear the VLAN filter. */ +	writel(0x04000000, ioaddr + VLANetherType); +	for (i = 0x600; i < 0x800; i+=4) +		writel(0, ioaddr + i); +	np->tx_config = 0x80000020; +	writel(np->tx_config, ioaddr + TxConfigReg); +	{ +		int eeword10 = eeprom_read(ioaddr, 10); +		writel(((eeword10 & 0x01e0) << 17) | ((eeword10 & 0x0010) << 3), +			   ioaddr + ChipCtrl); +	} + +	return dev; +} + + +/* Read the EEPROM interface with a serial bit streams generated by the +   host processor.  +   The example below is for the common 93c46 EEPROM, 64 16 bit words. */ + +/* Delay between EEPROM clock transitions. +   The effectivly flushes the write cache to prevent quick double-writes. +*/ +#define eeprom_delay(ee_addr)	readl(ee_addr) + +enum EEPROM_Ctrl_Bits { +	EE_ShiftClk=0x01, EE_ChipSelect=0x02, EE_DataIn=0x08, EE_DataOut=0x04, +}; +#define EE_Write0 (EE_ChipSelect) +#define EE_Write1 (EE_ChipSelect | EE_DataOut) + +/* The EEPROM commands include the alway-set leading bit. */ +enum EEPROM_Cmds { EE_WriteCmd=5, EE_ReadCmd=6, EE_EraseCmd=7, }; + +static int eeprom_read(long addr, int location) +{ +	int i; +	int retval = 0; +	long ee_addr = addr + EECtrl; +	int read_cmd = ((EE_ReadCmd<<6) | location) << 16 ; +	int cmd_len = 2+6+16; +	u32 baseval = readl(ee_addr) & ~0x0f; + +	writel(EE_Write0 | baseval, ee_addr); + +	/* Shift the read command bits out. */ +	for (i = cmd_len; i >= 0; i--) { +		int dataval = baseval | +			((read_cmd & (1 << i)) ? EE_Write1 : EE_Write0); +		writel(dataval, ee_addr); +		eeprom_delay(ee_addr); +		writel(dataval | EE_ShiftClk, ee_addr); +		eeprom_delay(ee_addr); +		retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); +	} + +	/* Terminate the EEPROM access. */ +	writel(baseval | EE_Write0, ee_addr); +	writel(baseval & ~EE_ChipSelect, ee_addr); +	return retval; +} + + + +static int netdev_open(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	/* Some chips may need to be reset. */ + +	MOD_INC_USE_COUNT; + +	if (np->tx_ring == 0) +		np->tx_ring = (void *)get_free_page(GFP_KERNEL); +	if (np->tx_ring == 0) +		return -ENOMEM; +	if (np->rx_ring == 0) +		np->rx_ring = (void *)get_free_page(GFP_KERNEL); +	if (np->tx_ring == 0) { +		free_page((long)np->tx_ring); +		return -ENOMEM; +	} + +	/* Note that both request_irq() and init_ring() call kmalloc(), which +	   break the global kernel lock protecting this routine. */ +	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", +			   dev->name, dev->irq); + +	init_ring(dev); + +	writel(0, ioaddr + RxControl); +	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); +#if ADDRLEN == 64 +	writel(virt_to_bus(np->rx_ring) >> 32, ioaddr + RxRingPtr + 4); +#else +	writel(0, ioaddr + RxRingPtr + 4); +#endif + +	writel(RX_RING_SIZE * sizeof(struct rx_desc), ioaddr + RxRingLen); +	writel(0x80000000 | rx_intr_holdoff, ioaddr + RxQ0IntrDelay); +	writel(0, ioaddr + RxDescHead); +	writel(np->dirty_rx + RX_RING_SIZE, ioaddr + RxDescTail); + +	/* Zero the unused Rx ring #1. */ +	writel(0, ioaddr + RxQ1IntrDelay); +	writel(0, ioaddr + RxRing1Ptr); +	writel(0, ioaddr + RxRing1Ptr + 4); +	writel(0, ioaddr + RxRing1Len); +	writel(0, ioaddr + RxDesc1Head); +	writel(0, ioaddr + RxDesc1Tail); + +	/* Use 0x002000FA for half duplex. */ +	writel(0x000400FA, ioaddr + TxControl); + +	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); +#if ADDRLEN == 64 +	writel(virt_to_bus(np->tx_ring) >> 32, ioaddr + TxRingPtr + 4); +#else +	writel(0, ioaddr + TxRingPtr + 4); +#endif + +	writel(TX_RING_SIZE * sizeof(struct tx_desc), ioaddr + TxRingLen); +	writel(0, ioaddr + TxDescHead); +	writel(0, ioaddr + TxDescTail); +	writel(0, ioaddr + TxQState); +	writel(0, ioaddr + TxQState + 4); + +	/* Set IPG register with Ethernet standard values. */ +	writel(0x00A0080A, ioaddr + TxIPG); +	/* The delay before announcing a Tx has completed. */ +	writel(tx_intr_holdoff, ioaddr + TxIntrDelay); + +	writel(((u32*)dev->dev_addr)[0], ioaddr + RxAddrCAM); +	writel(0x80000000 | ((((u32*)dev->dev_addr)[1]) & 0xffff), +		   ioaddr + RxAddrCAM + 4); + +	/* Initialize other registers. */ +	/* Configure the PCI bus bursts and FIFO thresholds. */ + +	if (dev->if_port == 0) +		dev->if_port = np->default_port; + +	np->in_interrupt = 0; + +	np->rx_mode = RxCtrlEnable; +	set_rx_mode(dev); + +	/* Tx mode */ +	np->tx_config = 0x80000020; +	writel(np->tx_config, ioaddr + TxConfigReg); + +	/* Flow control */ +	writel(0x00C28001, ioaddr + FlowCtrlAddrLo); +	writel(0x00000100, ioaddr + FlowCtrlAddrHi); +	writel(0x8808, ioaddr + FlowCtrlType); +	writel(0x0100, ioaddr + FlowCtrlTimer); +	writel(0x8000, ioaddr + FlowCtrlThrshHi); +	writel(0x4000, ioaddr + FlowCtrlThrshLo); + +	netif_start_tx_queue(dev); + +	/* Enable interrupts by setting the interrupt mask. */ +	writel(IntrTxDone | IntrLinkChange | IntrRxDone | IntrPCIErr +		   | IntrRxEmpty | IntrRxSErr, ioaddr + IntrEnable); + +	/*	writel(1, dev->base_addr + RxCmd);*/ + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done netdev_open(), status: %x Rx %x Tx %x.\n", +			   dev->name, (int)readl(ioaddr + ChipStatus), +			   (int)readl(ioaddr + RxStatus), (int)readl(ioaddr + TxStatus)); + +	/* Set the timer to check for link beat. */ +	init_timer(&np->timer); +	np->timer.expires = jiffies + 3*HZ; +	np->timer.data = (unsigned long)dev; +	np->timer.function = &netdev_timer;				/* timer handler */ +	add_timer(&np->timer); + +	return 0; +} + +/* Update for jumbo frames... +   Changing the MTU while active is not allowed. + */ +static int change_mtu(struct net_device *dev, int new_mtu) +{ +	if ((new_mtu < 68) || (new_mtu > 1500)) +		return -EINVAL; +	if (netif_running(dev)) +		return -EBUSY; +	dev->mtu = new_mtu; +	return 0; +} + +static void check_duplex(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int chip_ctrl = readl(ioaddr + ChipCtrl); +	int rx_cfg = readl(ioaddr + RxConfigReg); +	int tx_cfg = readl(ioaddr + TxConfigReg); +#if 0 +	int chip_status = readl(ioaddr + ChipStatus); +#endif + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s:  Link changed status.  Ctrl %x rxcfg %8.8x " +			   "txcfg %8.8x.\n", +			   dev->name, chip_ctrl, rx_cfg, tx_cfg); +	if (np->medialock) { +		if (np->full_duplex) +			; +	} +	/* writew(new_tx_mode, ioaddr + TxMode); */ +} + +static void netdev_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 10*HZ; + +	if (np->msg_level & NETIF_MSG_TIMER) { +		printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x, " +			   "Tx %x Rx %x.\n", +			   dev->name, (int)readl(ioaddr + ChipStatus), +			   (int)readl(ioaddr + TxStatus), (int)readl(ioaddr + RxStatus)); +	} +	/* This will either have a small false-trigger window or will not catch +	   tbusy incorrectly set when the queue is empty. */ +	if ((jiffies - dev->trans_start) > TX_TIMEOUT  && +		(np->cur_tx - np->dirty_tx > 0  || +		 netif_queue_paused(dev)) ) { +		tx_timeout(dev); +	} +	check_duplex(dev); +	np->timer.expires = jiffies + next_tick; +	add_timer(&np->timer); +} + +static void tx_timeout(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," +		   " resetting...\n", dev->name, (int)readl(ioaddr + ChipStatus)); + +#ifndef __alpha__ +	if (np->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk(KERN_DEBUG "  Tx registers: "); +		for (i = 0x400; i < 0x444; i += 8) +			printk(" %8.8x", (int)readl(ioaddr + i)); +		printk("\n"KERN_DEBUG "  Rx ring %p: ", np->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)np->rx_ring[i].status); +		printk("\n"KERN_DEBUG"  Tx ring %p: ", np->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %4.4x", np->tx_ring[i].status); +		printk("\n"); +	} +#endif + +	/* Perhaps we should reinitialize the hardware here. */ +	dev->if_port = 0; +	/* Stop and restart the chip's Tx processes . */ + +	/* Trigger an immediate transmit demand. */ + +	dev->trans_start = jiffies; +	np->stats.tx_errors++; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	np->tx_full = 0; +	np->cur_rx = np->cur_tx = 0; +	np->dirty_rx = np->dirty_tx = 0; + +	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); +	np->rx_head_desc = &np->rx_ring[0]; + +	/* Initialize all Rx descriptors. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_skbuff[i] = 0; +	} + +	/* The number of ring descriptors is set by the ring length register, +	   thus the chip does not use 'next_desc' chains. */ + +	/* Fill in the Rx buffers.  Allocation failures are acceptable. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); +		np->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		skb_reserve(skb, 2);	/* 16 byte align the IP header. */ +		np->rx_ring[i].buf_addr = virt_to_le32desc(skb->tail); +		np->rx_ring[i].buf_addr_hi = 0; +		np->rx_ring[i].status = 0; +	} +	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + +	for (i = 0; i < TX_RING_SIZE; i++) { +		np->tx_skbuff[i] = 0; +		np->tx_ring[i].status = 0; +	} +	return; +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned entry; + +	/* Block a timer-based transmit from overlapping.  This happens when +	   packets are presumed lost, and we use this check the Tx status. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tx_timeout(dev); +		return 1; +	} + +	/* Calculate the next Tx descriptor entry. */ +	entry = np->cur_tx % TX_RING_SIZE; + +	np->tx_skbuff[entry] = skb; + +	/* Note: Descriptors may be uncached.  Write each field only once. */ +	np->tx_ring[entry].buf_addr = virt_to_le32desc(skb->data); +	np->tx_ring[entry].buf_addr_hi = 0; +	np->tx_ring[entry].cmd_length = cpu_to_le32(TxCmdDoTx | skb->len); +	np->tx_ring[entry].status = 0; + +	/* Non-CC architectures: explicitly flush descriptor and packet. +	   cache_flush(np->tx_ring[entry], sizeof np->tx_ring[entry]); +	   cache_flush(skb->data, skb->len); +	*/ + +	np->cur_tx++; +	if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) { +		np->tx_full = 1; +		/* Check for a just-cleared queue. */ +		if (np->cur_tx - (volatile int)np->dirty_tx < TX_QUEUE_LEN - 2) { +			netif_unpause_tx_queue(dev); +			np->tx_full = 0; +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ + +	/* Inform the chip we have another Tx. */ +	if (np->msg_level & NETIF_MSG_TX_QUEUED) +		printk(KERN_DEBUG "%s: Tx queued to slot %d, desc tail now %d " +			   "writing %d.\n", +			   dev->name, entry, (int)readl(dev->base_addr + TxDescTail), +			   np->cur_tx % TX_RING_SIZE); +	writel(np->cur_tx % TX_RING_SIZE, dev->base_addr + TxDescTail); + +	dev->trans_start = jiffies; + +	if (np->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Transmit frame #%d (%x) queued in slot %d.\n", +			   dev->name, np->cur_tx, (int)virt_to_bus(&np->tx_ring[entry]), +			   entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct netdev_private *np; +	long ioaddr; +	int work_limit; + +	ioaddr = dev->base_addr; +	np = (struct netdev_private *)dev->priv; +	work_limit = np->max_interrupt_work; + +#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x020300 +	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */ +	if (test_and_set_bit(0, (void*)&dev->interrupt)) { +		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", +			   dev->name); +		dev->interrupt = 0;	/* Avoid halting machine. */ +		return; +	} +#endif + +	do { +		u32 intr_status = readl(ioaddr + IntrStatus); + +		if (np->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", +				   dev->name, intr_status); + +		if (intr_status == 0 || intr_status == 0xffffffff) +			break; + +		if (intr_status & IntrRxDone) +			netdev_rx(dev); + +		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { +			int entry = np->dirty_tx % TX_RING_SIZE; +			if (np->tx_ring[entry].status == 0) +				break; +			if (np->msg_level & NETIF_MSG_TX_DONE) +				printk(KERN_DEBUG "%s: Transmit done, Tx status %8.8x.\n", +					   dev->name, np->tx_ring[entry].status); +			np->stats.tx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			np->stats.tx_bytes += np->tx_skbuff[entry]->len; +#endif +			/* Free the original skb. */ +			dev_free_skb_irq(np->tx_skbuff[entry]); +			np->tx_skbuff[entry] = 0; +		} +		/* Note the 4 slot hysteresis to mark the queue non-full. */ +		if (np->tx_full  &&  np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { +			/* The ring is no longer full, allow new TX entries. */ +			np->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & (IntrPCIErr | IntrLinkChange | StatsMax)) +			netdev_error(dev, intr_status); + +		if (--work_limit < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", +				   dev->name, intr_status); +			break; +		} +	} while (1); + +	if (np->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); + +#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x020300 +	clear_bit(0, (void*)&dev->interrupt); +#endif +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int entry = np->cur_rx % RX_RING_SIZE; +	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; + +	if (np->msg_level & NETIF_MSG_RX_STATUS) { +		printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", +			   entry, np->rx_ring[entry].status); +	} + +	/* If EOP is set on the next entry, it's a new packet. Send it up. */ +	while (np->rx_head_desc->status & cpu_to_le32(RxDescDone)) { +		struct rx_desc *desc = np->rx_head_desc; +		u32 desc_status = le32_to_cpu(desc->status); +		int data_size = le32_to_cpu(desc->csum_length); + +		if (np->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n", +				   desc_status); +		if (--boguscnt < 0) +			break; +		if ( ! (desc_status & RxDescEndPkt)) { +			printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " +				   "multiple buffers, entry %#x length %d status %4.4x!\n", +				   dev->name, np->cur_rx, data_size, desc_status); +			np->stats.rx_length_errors++; +		} else { +			struct sk_buff *skb; +			/* Reported length should omit the CRC. */ +			int pkt_len = (data_size & 0xffff) - 4; + +#ifndef final_version +			if (np->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d" +					   " of %d, bogus_cnt %d.\n", +					   pkt_len, data_size, boguscnt); +#endif +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < np->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +#if HAS_IP_COPYSUM			/* Call copy + cksum if available. */ +				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +			} else { +				char *temp = skb_put(skb = np->rx_skbuff[entry], pkt_len); +				np->rx_skbuff[entry] = NULL; +#ifndef final_version				/* Remove after testing. */ +				if (le32desc_to_virt(np->rx_ring[entry].buf_addr) != temp) +					printk(KERN_ERR "%s: Internal fault: The skbuff addresses " +						   "do not match in netdev_rx: %p vs. %p / %p.\n", +						   dev->name, +						   le32desc_to_virt(np->rx_ring[entry].buf_addr), +						   skb->head, temp); +#endif +			} +#ifndef final_version				/* Remove after testing. */ +			/* You will want this info for the initial debug. */ +			if (np->msg_level & NETIF_MSG_PKTDATA) +				printk(KERN_DEBUG "  Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:" +					   "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x " +					   "%d.%d.%d.%d.\n", +					   skb->data[0], skb->data[1], skb->data[2], skb->data[3], +					   skb->data[4], skb->data[5], skb->data[6], skb->data[7], +					   skb->data[8], skb->data[9], skb->data[10], +					   skb->data[11], skb->data[12], skb->data[13], +					   skb->data[14], skb->data[15], skb->data[16], +					   skb->data[17]); +#endif +			skb->protocol = eth_type_trans(skb, dev); +			/* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */ +			netif_rx(skb); +			dev->last_rx = jiffies; +			np->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			np->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++np->cur_rx) % RX_RING_SIZE; +		np->rx_head_desc = &np->rx_ring[entry]; +	} + +	/* Refill the Rx ring buffers. */ +	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { +		struct sk_buff *skb; +		entry = np->dirty_rx % RX_RING_SIZE; +		if (np->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(np->rx_buf_sz); +			np->rx_skbuff[entry] = skb; +			if (skb == NULL) +				break;				/* Better luck next round. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			np->rx_ring[entry].buf_addr = virt_to_le32desc(skb->tail); +		} +		np->rx_ring[entry].status = 0; +	} + +	/* Restart Rx engine if stopped. */ +	/* writel(1, dev->base_addr + RxCmd); */ +	return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; + +	if (intr_status & IntrLinkChange) { +		int chip_ctrl = readl(ioaddr + ChipCtrl); +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_ERR "%s: Link changed: Autonegotiation on-going.\n", +				   dev->name); +		if (chip_ctrl & 1) +			netif_link_up(dev); +		else +			netif_link_down(dev); +		check_duplex(dev); +	} +	if (intr_status & StatsMax) { +		get_stats(dev); +	} +	if ((intr_status & ~(IntrLinkChange|StatsMax)) +		&& (np->msg_level & NETIF_MSG_DRV)) +		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", +			   dev->name, intr_status); +	/* Hmmmmm, it's not clear how to recover from PCI faults. */ +	if (intr_status & IntrPCIErr) +		np->stats.tx_fifo_errors++; +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int crc_errs = readl(ioaddr + RxCRCErrs); + +	if (crc_errs != 0xffffffff) { +		/* We need not lock this segment of code for SMP. +		   The non-atomic-add vulnerability is very small +		   and statistics are non-critical. */ +		np->stats.rx_crc_errors	+= readl(ioaddr + RxCRCErrs); +		np->stats.rx_missed_errors	+= readl(ioaddr + RxMissed); +	} + +	return &np->stats; +} + +/* The little-endian AUTODIN II ethernet CRC calculations. +   A big-endian version is also available. +   This is slow but compact code.  Do not use this routine for bulk data, +   use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c. +   Chips may use the upper or lower CRC bits, and may reverse and/or invert +   them.  Select the endian-ness that results in minimal calculations. +*/ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ +	unsigned int crc = 0xffffffff;	/* Initial value. */ +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u32 new_mc_filter[128];			/* Multicast filter table */ +	u32 new_rx_mode = np->rx_mode; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		new_rx_mode |= +			RxCtrlAcceptBroadcast | RxCtrlAllMulticast | RxCtrlAllUnicast; +	} else if ((dev->mc_count > np->multicast_filter_limit) +			   ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to match, or accept all multicasts. */ +		new_rx_mode &= ~RxCtrlAllUnicast; +		new_rx_mode |= RxCtrlAcceptBroadcast | RxCtrlAllMulticast; +	} else { +		struct dev_mc_list *mclist; +		int i; +		memset(new_mc_filter, 0, sizeof(new_mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < 15; +			 i++, mclist = mclist->next) { +			writel(((u32*)mclist->dmi_addr)[0], ioaddr + RxAddrCAM + 8 + i*8); +			writel((((u32*)mclist->dmi_addr)[1] & 0xffff) | 0x80000000, +				   ioaddr + RxAddrCAM + 12 + i*8); +		} +		for (; mclist && i < dev->mc_count; i++, mclist = mclist->next) { +			set_bit(((u32*)mclist->dmi_addr)[1] & 0xfff, +					new_mc_filter); +		} +		new_rx_mode &= ~RxCtrlAllUnicast | RxCtrlAllMulticast; +		new_rx_mode |= RxCtrlAcceptBroadcast; +		if (dev->mc_count > 15) +			for (i = 0; i < 128; i++) +				writel(new_mc_filter[i], ioaddr + MulticastArray + (i<<2)); +	} +	if (np->rx_mode != new_rx_mode) +		writel(np->rx_mode = new_rx_mode, ioaddr + RxControl); +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static int netdev_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x " +			   "Rx %4.4x Int %2.2x.\n", +			   dev->name, (int)readl(ioaddr + TxStatus), +			   (int)readl(ioaddr + RxStatus), (int)readl(ioaddr + IntrStatus)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); +	} + +	/* Disable interrupts by clearing the interrupt mask. */ +	writel(~0, ioaddr + IntrDisable); +	readl(ioaddr + IntrStatus); + +	/* Reset everything. */ +	writel(0x04000000, ioaddr + ChipCtrl); + +	del_timer(&np->timer); + +#ifdef __i386__ +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(np->tx_ring)); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" #%d desc. buf %8.8x, length %8.8x, status %8.8x.\n", +				   i, np->tx_ring[i].buf_addr, np->tx_ring[i].cmd_length, +				   np->tx_ring[i].status); +		printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n", +			   (int)virt_to_bus(np->rx_ring)); +		for (i = 0; i < RX_RING_SIZE; i++) { +			printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", +				   i, np->rx_ring[i].csum_length, +				   np->rx_ring[i].status, np->rx_ring[i].buf_addr); +			if (np->rx_ring[i].buf_addr) { +				if (*(u8*)np->rx_skbuff[i]->tail != 0x69) { +					u16 *pkt_buf = (void *)np->rx_skbuff[i]->tail; +					int j; +					for (j = 0; j < 0x50; j++) +						printk(" %4.4x", pkt_buf[j]); +					printk("\n"); +				} +			} +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].status = 0; +		np->rx_ring[i].buf_addr = 0xBADF00D0; /* An invalid address. */ +		if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			np->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(np->rx_skbuff[i]); +		} +		np->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (np->tx_skbuff[i]) +			dev_free_skb(np->tx_skbuff[i]); +		np->tx_skbuff[i] = 0; +	} + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static int netdev_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		/* Disable interrupts, stop Tx and Rx. */ +		writel(~0, ioaddr + IntrDisable); +		/* writel(2, ioaddr + RxCmd); */ +		/* writew(2, ioaddr + TxCmd); */ +		break; +	case DRV_RESUME: +		/* This is incomplete: the actions are very chip specific. */ +		set_rx_mode(dev); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			/* Some, but not all, kernel versions close automatically. */ +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size); +		iounmap((char *)dev->base_addr); +		for (devp = &root_net_dev; *devp; devp = next) { +			next = &((struct netdev_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	} + +	return 0; +} + + +#ifdef MODULE +int init_module(void) +{ +	/* Emit version even if no cards detected. */ +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return pci_drv_register(&igige_drv_id, NULL); +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&igige_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_net_dev) { +		struct netdev_private *np = (void *)(root_net_dev->priv); +		unregister_netdev(root_net_dev); +		release_region(root_net_dev->base_addr, +					   pci_id_tbl[np->chip_id].io_size); +		iounmap((char *)(root_net_dev->base_addr)); +		next_dev = np->next_module; +		if (np->tx_ring == 0) +			free_page((long)np->tx_ring); +		if (np->rx_ring == 0) +			free_page((long)np->rx_ring); +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_net_dev); +		root_net_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` intel-gige.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c intel-gige.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c intel-gige.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/iow.h b/linux/src/drivers/net/iow.h new file mode 100644 index 0000000..6e15688 --- /dev/null +++ b/linux/src/drivers/net/iow.h @@ -0,0 +1,6 @@ +#ifndef _ASM_IOW_H +#define _ASM_IOW_H + +/* no longer used */ + +#endif diff --git a/linux/src/drivers/net/kern_compat.h b/linux/src/drivers/net/kern_compat.h new file mode 100644 index 0000000..39e1934 --- /dev/null +++ b/linux/src/drivers/net/kern_compat.h @@ -0,0 +1,285 @@ +#ifndef _KERN_COMPAT_H +#define _KERN_COMPAT_H +/* kern_compat.h: Linux PCI network adapter backward compatibility code. */ +/* +	$Revision: 1.1.2.2 $ $Date: 2007/08/04 21:02:21 $ + +	Kernel compatibility defines. +	This file provides macros to mask the difference between kernel versions. +	It is designed primarily to allow device drivers to be written so that +	they work with a range of kernel versions. + +	Written 1999-2003 Donald Becker, Scyld Computing Corporation +	This software may be used and distributed according to the terms +	of the GNU General Public License (GPL), incorporated herein by +	reference.  Drivers interacting with these functions are derivative +	works and thus are covered the GPL.  They must include an explicit +	GPL notice. + +	This code also provides inline scan and activate functions for PCI network +	interfaces.  It has an interface identical to pci-scan.c, but is +	intended as an include file to simplify using updated drivers with older +	kernel versions. +	This code version matches pci-scan.c:v0.05 9/16/99 + +	The author may be reached as becker@scyld.com, or +	Donald Becker +	Penguin Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Other contributers: +	<none> +*/ + +/* We try to use defined values to decide when an interface has changed or +   added features, but we must have the kernel version number for a few. */ +#if ! defined(LINUX_VERSION_CODE)  ||  (LINUX_VERSION_CODE < 0x10000) +#include <linux/version.h> +#endif +/* Older kernel versions didn't include modversions automatically. */ +#if LINUX_VERSION_CODE < 0x20300  &&  defined(MODVERSIONS) +#include <linux/modversions.h> +#endif + +/* There was no support for PCI address space mapping in 2.0, but the +   Alpha needed it.  See the 2.2 documentation. */ +#if LINUX_VERSION_CODE < 0x20100  &&  ! defined(__alpha__) +#define ioremap(a,b)\ +    (((unsigned long)(a) >= 0x100000) ? vremap(a,b) : (void*)(a)) +#define iounmap(v)\ +    do { if ((unsigned long)(v) >= 0x100000) vfree(v);} while (0) +#endif + +/* Support for adding info about the purpose of and parameters for kernel +   modules was added in 2.1. */ +#if LINUX_VERSION_CODE < 0x20115 +#define MODULE_AUTHOR(name)  extern int nonesuch +#define MODULE_DESCRIPTION(string)  extern int nonesuch +#define MODULE_PARM(varname, typestring)  extern int nonesuch +#define MODULE_PARM_DESC(var,desc) extern int nonesuch +#endif +#if !defined(MODULE_LICENSE) +#define MODULE_LICENSE(license) 	\ +static const char __module_license[] __attribute__((section(".modinfo"))) =   \ +"license=" license +#endif +#if !defined(MODULE_PARM_DESC) +#define MODULE_PARM_DESC(var,desc)		\ +const char __module_parm_desc_##var[]		\ +__attribute__((section(".modinfo"))) =		\ +"parm_desc_" __MODULE_STRING(var) "=" desc +#endif + +/* SMP and better multiarchitecture support were added. +   Using an older kernel means we assume a little-endian uniprocessor. +*/ +#if LINUX_VERSION_CODE < 0x20123 +#define hard_smp_processor_id() smp_processor_id() +//#define test_and_set_bit(val, addr) set_bit(val, addr) +#define cpu_to_le16(val) (val) +#define cpu_to_le32(val) (val) +#define le16_to_cpu(val) (val) +#define le16_to_cpus(val)		/* In-place conversion. */ +#define le32_to_cpu(val) (val) +#define cpu_to_be16(val) ((((val) & 0xff) << 8) +  (((val) >> 8) & 0xff)) +#define cpu_to_be32(val) ((cpu_to_be16(val) << 16) + cpu_to_be16((val) >> 16)) +typedef long spinlock_t; +#define SPIN_LOCK_UNLOCKED 0 +#define spin_lock(lock) +#define spin_unlock(lock) +#define spin_lock_irqsave(lock, flags)	do {save_flags(flags); cli();} while(0) +#define spin_unlock_irqrestore(lock, flags) restore_flags(flags) +#endif + +#if LINUX_VERSION_CODE <= 0x20139 +#define	net_device_stats enet_statistics +#else +#define NETSTATS_VER2 +#endif + +/* These are used by the netdrivers to report values from the +   MII (Media Indpendent Interface) management registers. +*/ +#ifndef SIOCGMIIPHY +#define SIOCGMIIPHY (SIOCDEVPRIVATE)		/* Get the PHY in use. */ +#define SIOCGMIIREG (SIOCDEVPRIVATE+1) 		/* Read a PHY register. */ +#define SIOCSMIIREG (SIOCDEVPRIVATE+2) 		/* Write a PHY register. */ +#endif +#ifndef SIOCGPARAMS +#define SIOCGPARAMS (SIOCDEVPRIVATE+3) 		/* Read operational parameters. */ +#define SIOCSPARAMS (SIOCDEVPRIVATE+4) 		/* Set operational parameters. */ +#endif + +#if !defined(HAVE_NETIF_MSG) +enum { +	NETIF_MSG_DRV           = 0x0001, +	NETIF_MSG_PROBE         = 0x0002, +	NETIF_MSG_LINK          = 0x0004, +	NETIF_MSG_TIMER         = 0x0008, +	NETIF_MSG_IFDOWN        = 0x0010, +	NETIF_MSG_IFUP          = 0x0020, +	NETIF_MSG_RX_ERR        = 0x0040, +	NETIF_MSG_TX_ERR        = 0x0080, +	NETIF_MSG_TX_QUEUED     = 0x0100, +	NETIF_MSG_INTR          = 0x0200, +	NETIF_MSG_TX_DONE       = 0x0400, +	NETIF_MSG_RX_STATUS     = 0x0800, +	NETIF_MSG_PKTDATA       = 0x1000, +	/* 2000 is reserved. */ +	NETIF_MSG_WOL           = 0x4000, +	NETIF_MSG_MISC          = 0x8000, +	NETIF_MSG_RXFILTER      = 0x10000, +}; +#define NETIF_MSG_MAX 0x10000 +#endif + +#if !defined(NETIF_MSG_MAX) || NETIF_MSG_MAX < 0x8000 +#define NETIF_MSG_MISC 0x8000 +#endif +#if !defined(NETIF_MSG_MAX) || NETIF_MSG_MAX < 0x10000 +#define NETIF_MSG_RXFILTER 0x10000 +#endif + +#if LINUX_VERSION_CODE < 0x20155 +#include <linux/bios32.h> +#define PCI_SUPPORT_VER1 +/* A minimal version of the 2.2.* PCI support that handles configuration +   space access. +   Drivers that actually use pci_dev fields must do explicit compatibility. +   Note that the struct pci_dev * "pointer" is actually a byte mapped integer! +*/ +#if LINUX_VERSION_CODE < 0x20014 +struct pci_dev { int not_used; }; +#endif + +#define pci_find_slot(bus, devfn) (struct pci_dev*)((bus<<8) | devfn | 0xf0000) +#define bus_number(pci_dev) ((((int)(pci_dev))>>8) & 0xff) +#define devfn_number(pci_dev) (((int)(pci_dev)) & 0xff) +#define pci_bus_number(pci_dev) ((((int)(pci_dev))>>8) & 0xff) +#define pci_devfn(pci_dev) (((int)(pci_dev)) & 0xff) + +#ifndef CONFIG_PCI +extern inline int pci_present(void) { return 0; } +#else +#define pci_present pcibios_present +#endif + +#define pci_read_config_byte(pdev, where, valp)\ +	pcibios_read_config_byte(bus_number(pdev), devfn_number(pdev), where, valp) +#define pci_read_config_word(pdev, where, valp)\ +	pcibios_read_config_word(bus_number(pdev), devfn_number(pdev), where, valp) +#define pci_read_config_dword(pdev, where, valp)\ +	pcibios_read_config_dword(bus_number(pdev), devfn_number(pdev), where, valp) +#define pci_write_config_byte(pdev, where, val)\ +	pcibios_write_config_byte(bus_number(pdev), devfn_number(pdev), where, val) +#define pci_write_config_word(pdev, where, val)\ +	pcibios_write_config_word(bus_number(pdev), devfn_number(pdev), where, val) +#define pci_write_config_dword(pdev, where, val)\ +	pcibios_write_config_dword(bus_number(pdev), devfn_number(pdev), where, val) +#else +#define PCI_SUPPORT_VER2 +#define pci_bus_number(pci_dev) ((pci_dev)->bus->number) +#define pci_devfn(pci_dev) ((pci_dev)->devfn) +#endif + +/* The arg count changed, but function name did not. +   We cover that bad choice by defining a new name. +*/ +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE) +#define dev_free_skb_irq(skb) dev_kfree_skb(skb, FREE_WRITE) +#elif LINUX_VERSION_CODE < 0x20400 +#define dev_free_skb(skb) dev_kfree_skb(skb) +#define dev_free_skb_irq(skb) dev_kfree_skb(skb) +#else +#define dev_free_skb(skb) dev_kfree_skb(skb) +#define dev_free_skb_irq(skb) dev_kfree_skb_irq(skb) +#endif + +/* Added at the suggestion of Jes Sorensen. */ +#if LINUX_VERSION_CODE > 0x20153 +#include <linux/init.h> +#else +#define __init +#define __initdata +#define __initfunc(__arginit) __arginit +#endif + +/* The old 'struct device' used a too-generic name. */ +#if LINUX_VERSION_CODE < 0x2030d +#define net_device device +#endif + +/* More changes for the 2.4 kernel, some in the zillion 2.3.99 releases. */ +#if LINUX_VERSION_CODE < 0x20363 +#define DECLARE_MUTEX(name) struct semaphore (name) = MUTEX; +#define down_write(semaphore_p) down(semaphore_p) +#define down_read(semaphore_p) down(semaphore_p) +#define up_write(semaphore_p) up(semaphore_p) +#define up_read(semaphore_p) up(semaphore_p) +/* Note that the kernel version has a broken time_before()! */ +#define time_after(a,b) ((long)(b) - (long)(a) < 0) +#define time_before(a,b) ((long)(a) - (long)(b) < 0) +#else +#define get_free_page get_zeroed_page +#endif + +/* The 2.2 kernels added the start of capability-based security for operations +   that formerally could only be done by root. +*/ +#if ! defined(CAP_NET_ADMIN) +#define capable(CAP_XXX) (suser()) +#endif + +#if ! defined(HAVE_NETIF_QUEUE) +#define netif_wake_queue(dev)   do { clear_bit( 0, (void*)&(dev)->tbusy); mark_bh(NET_BH); } while (0) +#define netif_start_tx_queue(dev) do { (dev)->tbusy = 0; dev->start = 1; } while (0) +#define netif_stop_tx_queue(dev) do { (dev)->tbusy = 1; dev->start = 0; } while (0) +#define netif_queue_paused(dev) ((dev)->tbusy != 0) +/* Splitting these lines exposes a bug in some preprocessors. */ +#define netif_pause_tx_queue(dev) (test_and_set_bit( 0, (void*)&(dev)->tbusy)) +#define netif_unpause_tx_queue(dev) do { clear_bit( 0, (void*)&(dev)->tbusy); } while (0) +#define netif_resume_tx_queue(dev) do { clear_bit( 0, (void*)&(dev)->tbusy); mark_bh(NET_BH); } while (0) + +#define netif_running(dev) ((dev)->start != 0) +#define netif_device_attach(dev) do {; } while (0) +#define netif_device_detach(dev) do {; } while (0) +#define netif_device_present(dev) (1) +#define netif_set_tx_timeout(dev, func, deltajiffs)   do {; } while (0) +#define netif_link_down(dev)  (dev)->flags &= ~IFF_RUNNING +#define netif_link_up(dev)  (dev)->flags |= IFF_RUNNING + +#else + +#define netif_start_tx_queue(dev) netif_start_queue(dev) +#define netif_stop_tx_queue(dev) netif_stop_queue(dev) +#define netif_queue_paused(dev) netif_queue_stopped(dev) +#define netif_resume_tx_queue(dev) netif_wake_queue(dev) +/* Only used in transmit path.  No function in 2.4. */ +#define netif_pause_tx_queue(dev)  0 +#define netif_unpause_tx_queue(dev) do {; } while (0) + +#ifdef __LINK_STATE_NOCARRIER +#define netif_link_down(dev)  netif_carrier_off(dev) +#define netif_link_up(dev)  netif_carrier_on(dev) +#else +#define netif_link_down(dev)  (dev)->flags &= ~IFF_RUNNING +#define netif_link_up(dev)  (dev)->flags |= IFF_RUNNING +#endif + +#endif +#ifndef PCI_DMA_BUS_IS_PHYS +#define pci_dma_sync_single(pci_dev, base_addr, extent, tofrom) do {; } while (0) +#define pci_map_single(pci_dev, base_addr, extent, dir) virt_to_bus(base_addr) +#define pci_unmap_single(pci_dev, base_addr, extent, dir) do {; } while (0) +#endif + +#endif +/* + * Local variables: + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/lance.c b/linux/src/drivers/net/lance.c new file mode 100644 index 0000000..fe3cf68 --- /dev/null +++ b/linux/src/drivers/net/lance.c @@ -0,0 +1,1293 @@ +/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */ +/* +	Written/copyright 1993-1998 by Donald Becker. + +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency. +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	This driver is for the Allied Telesis AT1500 and HP J2405A, and should work +	with most other LANCE-based bus-master (NE2100/NE2500) ethercards. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	Fixing alignment problem with 1.3.* kernel and some minor changes +	by Andrey V. Savochkin, 1996. + +	Problems or questions may be send to Donald Becker (see above) or to +	Andrey Savochkin -- saw@shade.msu.ru or +		Laboratory of Computation Methods,  +		Department of Mathematics and Mechanics, +		Moscow State University, +		Leninskye Gory, Moscow 119899 + +	But I should to inform you that I'm not an expert in the LANCE card +	and it may occurs that you will receive no answer on your mail +	to Donald Becker. I didn't receive any answer on all my letters +	to him. Who knows why... But may be you are more lucky?  ;-> +                                                          SAW + +	Thomas Bogendoerfer (tsbogend@bigbug.franken.de): +	- added support for Linux/Alpha, but removed most of it, because +        it worked only for the PCI chip.  +      - added hook for the 32bit lance driver +      - added PCnetPCI II (79C970A) to chip table +	Paul Gortmaker (gpg109@rsphy1.anu.edu.au): +	- hopefully fix above so Linux/Alpha can use ISA cards too. +    8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb +    v1.12 10/27/97 Module support -djb +    v1.14  2/3/98 Module support modified, made PCI support optional -djb +*/ + +static const char *version = "lance.c:v1.14 2/3/1998 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; + +#ifdef MODULE +#ifdef MODVERSIONS +#include <linux/modversions.h> +#endif +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +static unsigned int lance_portlist[] = { 0x300, 0x320, 0x340, 0x360, 0}; +int lance_probe(struct device *dev); +int lance_probe1(struct device *dev, int ioaddr, int irq, int options); + +#ifdef HAVE_DEVLIST +struct netdev_entry lance_drv = +{"lance", lance_probe1, LANCE_TOTAL_SIZE, lance_portlist}; +#endif + +#ifdef LANCE_DEBUG +int lance_debug = LANCE_DEBUG; +#else +int lance_debug = 1; +#endif + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the AMD 79C960, the "PCnet-ISA +single-chip ethernet controller for ISA".  This chip is used in a wide +variety of boards from vendors such as Allied Telesis, HP, Kingston, +and Boca.  This driver is also intended to work with older AMD 7990 +designs, such as the NE1500 and NE2100, and newer 79C961.  For convenience, +I use the name LANCE to refer to all of the AMD chips, even though it properly +refers only to the original 7990. + +II. Board-specific settings + +The driver is designed to work the boards that use the faster +bus-master mode, rather than in shared memory mode.	 (Only older designs +have on-board buffer memory needed to support the slower shared memory mode.) + +Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA +channel.  This driver probes the likely base addresses: +{0x300, 0x320, 0x340, 0x360}. +After the board is found it generates a DMA-timeout interrupt and uses +autoIRQ to find the IRQ line.  The DMA channel can be set with the low bits +of the otherwise-unused dev->mem_start value (aka PARAM1).  If unset it is +probed for by enabling each free DMA channel in turn and checking if +initialization succeeds. + +The HP-J2405A board is an exception: with this board it is easy to read the +EEPROM-set values for the base, IRQ, and DMA.  (Of course you must already +_know_ the base address -- that field is for writing the EEPROM.) + +III. Driver operation + +IIIa. Ring buffers +The LANCE uses ring buffers of Tx and Rx descriptors.  Each entry describes +the base and length of the data buffer, along with status bits.	 The length +of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of +the buffer length (rather than being directly the buffer length) for +implementation ease.  The current values are 2 (Tx) and 4 (Rx), which leads to +ring sizes of 4 (Tx) and 16 (Rx).  Increasing the number of ring entries +needlessly uses extra space and reduces the chance that an upper layer will +be able to reorder queued Tx packets based on priority.	 Decreasing the number +of entries makes it more difficult to achieve back-to-back packet transmission +and increases the chance that Rx ring will overflow.  (Consider the worst case +of receiving back-to-back minimum-sized packets.) + +The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver +statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to +avoid the administrative overhead. For the Rx side this avoids dynamically +allocating full-sized buffers "just in case", at the expense of a +memory-to-memory data copy for each packet received.  For most systems this +is a good tradeoff: the Rx buffer will always be in low memory, the copy +is inexpensive, and it primes the cache for later packet processing.  For Tx +the buffers are only used when needed as low-memory bounce buffers. + +IIIB. 16M memory limitations. +For the ISA bus master mode all structures used directly by the LANCE, +the initialization block, Rx and Tx rings, and data buffers, must be +accessible from the ISA bus, i.e. in the lower 16M of real memory. +This is a problem for current Linux kernels on >16M machines. The network +devices are initialized after memory initialization, and the kernel doles out +memory from the top of memory downward.	 The current solution is to have a +special network initialization routine that's called before memory +initialization; this will eventually be generalized for all network devices. +As mentioned before, low-memory "bounce-buffers" are used when needed. + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  (The Tx-done interrupt can't be selectively turned off, so +we can't avoid the interrupt overhead by having the Tx routine reap the Tx +stats.)	 After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero.	 Iff the 'lp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +*/ + +/* Set the number of Tx and Rx buffers, using Log_2(# buffers). +   Reasonable default values are 16 Tx buffers, and 16 Rx buffers. +   That translates to 4 and 4 (16 == 2^^4). +   This is a compile-time option for efficiency. +   */ +#ifndef LANCE_LOG_TX_BUFFERS +#define LANCE_LOG_TX_BUFFERS 4 +#define LANCE_LOG_RX_BUFFERS 4 +#endif + +#define TX_RING_SIZE			(1 << (LANCE_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS		((LANCE_LOG_TX_BUFFERS) << 29) + +#define RX_RING_SIZE			(1 << (LANCE_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS		((LANCE_LOG_RX_BUFFERS) << 29) + +#define PKT_BUF_SZ		1544 + +/* Offsets from base I/O address. */ +#define LANCE_DATA 0x10 +#define LANCE_ADDR 0x12 +#define LANCE_RESET 0x14 +#define LANCE_BUS_IF 0x16 +#define LANCE_TOTAL_SIZE 0x18 + +/* The LANCE Rx and Tx ring descriptors. */ +struct lance_rx_head { +	s32 base; +	s16 buf_length;			/* This length is 2s complement (negative)! */ +	s16 msg_length;			/* This length is "normal". */ +}; + +struct lance_tx_head { +	s32 base; +	s16 length;				/* Length is 2s complement (negative)! */ +	s16 misc; +}; + +/* The LANCE initialization block, described in databook. */ +struct lance_init_block { +	u16 mode;		/* Pre-set mode (reg. 15) */ +	u8  phys_addr[6]; /* Physical ethernet address */ +	u32 filter[2];			/* Multicast filter (unused). */ +	/* Receive and transmit ring base, along with extra bits. */ +	u32  rx_ring;			/* Tx and Rx ring base pointers */ +	u32  tx_ring; +}; + +struct lance_private { +	/* The Tx and Rx ring entries must be aligned on 8-byte boundaries. */ +	struct lance_rx_head rx_ring[RX_RING_SIZE]; +	struct lance_tx_head tx_ring[TX_RING_SIZE]; +	struct lance_init_block	init_block; +	const char *name; +	/* The saved address of a sent-in-place packet/buffer, for skfree(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	unsigned long rx_buffs;		/* Address of Rx and Tx buffers. */ +	/* Tx low-memory "bounce buffer" address. */ +	char (*tx_bounce_buffs)[PKT_BUF_SZ]; +	int cur_rx, cur_tx;			/* The next free ring entry */ +	int dirty_rx, dirty_tx;		/* The ring entries to be free()ed. */ +	int dma; +	struct enet_statistics stats; +	unsigned char chip_version;	/* See lance_chip_type. */ +	char tx_full; +	unsigned long lock; +}; + +#define LANCE_MUST_PAD          0x00000001 +#define LANCE_ENABLE_AUTOSELECT 0x00000002 +#define LANCE_MUST_REINIT_RING  0x00000004 +#define LANCE_MUST_UNRESET      0x00000008 +#define LANCE_HAS_MISSED_FRAME  0x00000010 + +/* A mapping from the chip ID number to the part number and features. +   These are from the datasheets -- in real life the '970 version +   reportedly has the same ID as the '965. */ +static struct lance_chip_type { +	int id_number; +	const char *name; +	int flags; +} chip_table[] = { +	{0x0000, "LANCE 7990",				/* Ancient lance chip.  */ +		LANCE_MUST_PAD + LANCE_MUST_UNRESET}, +	{0x0003, "PCnet/ISA 79C960",		/* 79C960 PCnet/ISA.  */ +		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + +			LANCE_HAS_MISSED_FRAME}, +	{0x2260, "PCnet/ISA+ 79C961",		/* 79C961 PCnet/ISA+, Plug-n-Play.  */ +		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + +			LANCE_HAS_MISSED_FRAME}, +	{0x2420, "PCnet/PCI 79C970",		/* 79C970 or 79C974 PCnet-SCSI, PCI. */ +		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + +			LANCE_HAS_MISSED_FRAME}, +	/* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call +		it the PCnet32. */ +	{0x2430, "PCnet32",					/* 79C965 PCnet for VL bus. */ +		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + +			LANCE_HAS_MISSED_FRAME}, +        {0x2621, "PCnet/PCI-II 79C970A",        /* 79C970A PCInetPCI II. */ +                LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + +                        LANCE_HAS_MISSED_FRAME}, +	{0x0, 	 "PCnet (unknown)", +		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + +			LANCE_HAS_MISSED_FRAME}, +}; + +enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6}; + +/* Non-zero only if the current card is a PCI with BIOS-set IRQ. */ +static unsigned char pci_irq_line = 0; + +/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers. +   Assume yes until we know the memory size. */ +static unsigned char lance_need_isa_bounce_buffers = 1; + +static int lance_open(struct device *dev); +static int lance_open_fail(struct device *dev); +static void lance_init_ring(struct device *dev, int mode); +static int lance_start_xmit(struct sk_buff *skb, struct device *dev); +static int lance_rx(struct device *dev); +static void lance_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int lance_close(struct device *dev); +static struct enet_statistics *lance_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + + + +#ifdef MODULE +#define MAX_CARDS		8	/* Max number of interfaces (cards) per module */ +#define IF_NAMELEN		8	/* # of chars for storing dev->name */ + +static int io[MAX_CARDS] = { 0, }; +static int dma[MAX_CARDS] = { 0, }; +static int irq[MAX_CARDS]  = { 0, }; + +static char ifnames[MAX_CARDS][IF_NAMELEN] = { {0, }, }; +static struct device dev_lance[MAX_CARDS] = +{{ +    0, /* device name is inserted by linux/drivers/net/net_init.c */ +	0, 0, 0, 0, +	0, 0, +	0, 0, 0, NULL, NULL}}; + +int init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { +		struct device *dev = &dev_lance[this_dev]; +		dev->name = ifnames[this_dev]; +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->dma = dma[this_dev]; +		dev->init = lance_probe; +		if (io[this_dev] == 0)  { +			if (this_dev != 0) break; /* only complain once */ +			printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n"); +			return -EPERM; +		} +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "lance.c: No PCnet/LANCE card found (i/o = 0x%x).\n", io[this_dev]); +			if (found != 0) return 0;	/* Got at least one. */ +			return -ENXIO; +		} +		found++; +	} + +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { +		struct device *dev = &dev_lance[this_dev]; +		if (dev->priv != NULL) { +			kfree(dev->priv); +			dev->priv = NULL; +			free_dma(dev->dma); +			release_region(dev->base_addr, LANCE_TOTAL_SIZE); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + +/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other +   board probes now that kmalloc() can allocate ISA DMA-able regions. +   This also allows the LANCE driver to be used as a module. +   */ +int lance_probe(struct device *dev) +{ +	int *port, result; + +	if (high_memory <= 16*1024*1024) +		lance_need_isa_bounce_buffers = 0; + +#if defined(CONFIG_PCI) +    if (pcibios_present()) { +	    int pci_index; +		if (lance_debug > 1) +			printk("lance.c: PCI bios is present, checking for devices...\n"); +		for (pci_index = 0; pci_index < 8; pci_index++) { +			unsigned char pci_bus, pci_device_fn; +			unsigned int pci_ioaddr; +			unsigned short pci_command; + +			if (pcibios_find_device (PCI_VENDOR_ID_AMD, +									 PCI_DEVICE_ID_AMD_LANCE, pci_index, +									 &pci_bus, &pci_device_fn) != 0) +				break; +			pcibios_read_config_byte(pci_bus, pci_device_fn, +									 PCI_INTERRUPT_LINE, &pci_irq_line); +			pcibios_read_config_dword(pci_bus, pci_device_fn, +									  PCI_BASE_ADDRESS_0, &pci_ioaddr); +			/* Remove I/O space marker in bit 0. */ +			pci_ioaddr &= ~3; +			/* PCI Spec 2.1 states that it is either the driver or PCI card's +	 		 * responsibility to set the PCI Master Enable Bit if needed. +			 *	(From Mark Stockton <marks@schooner.sys.hou.compaq.com>) +			 */ +			pcibios_read_config_word(pci_bus, pci_device_fn, +									 PCI_COMMAND, &pci_command); +			if ( ! (pci_command & PCI_COMMAND_MASTER)) { +				printk("PCI Master Bit has not been set. Setting...\n"); +				pci_command |= PCI_COMMAND_MASTER; +				pcibios_write_config_word(pci_bus, pci_device_fn, +										  PCI_COMMAND, pci_command); +			} +			printk("Found PCnet/PCI at %#x, irq %d.\n", +				   pci_ioaddr, pci_irq_line); +			result = lance_probe1(dev, pci_ioaddr, pci_irq_line, 0); +			pci_irq_line = 0; +			if (!result) return 0; +		} +	} +#endif  /* defined(CONFIG_PCI) */ + +	for (port = lance_portlist; *port; port++) { +		int ioaddr = *port; + +		if ( check_region(ioaddr, LANCE_TOTAL_SIZE) == 0) { +			/* Detect "normal" 0x57 0x57 and the NI6510EB 0x52 0x44 +			   signatures w/ minimal I/O reads */ +			char offset15, offset14 = inb(ioaddr + 14); +			 +			if ((offset14 == 0x52 || offset14 == 0x57) && +				((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44)) { +				result = lance_probe1(dev, ioaddr, 0, 0); +				if ( !result ) return 0; +			} +		} +	} +	return -ENODEV; +} + +int lance_probe1(struct device *dev, int ioaddr, int irq, int options) +{ +	struct lance_private *lp; +	short dma_channels;					/* Mark spuriously-busy DMA channels */ +	int i, reset_val, lance_version; +	const char *chipname; +	/* Flags for specific chips or boards. */ +	unsigned char hpJ2405A = 0;			/* HP ISA adaptor */ +	int hp_builtin = 0;					/* HP on-board ethernet. */ +	static int did_version = 0;			/* Already printed version info. */ + +	/* First we look for special cases. +	   Check for HP's on-board ethernet by looking for 'HP' in the BIOS. +	   There are two HP versions, check the BIOS for the configuration port. +	   This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com. +	   */ +	if (readw(0x000f0102) == 0x5048)  { +		static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360}; +		int hp_port = (readl(0x000f00f1) & 1)  ? 0x499 : 0x99; +		/* We can have boards other than the built-in!  Verify this is on-board. */ +		if ((inb(hp_port) & 0xc0) == 0x80 +			&& ioaddr_table[inb(hp_port) & 3] == ioaddr) +			hp_builtin = hp_port; +	} +	/* We also recognize the HP Vectra on-board here, but check below. */ +	hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 +				&& inb(ioaddr+2) == 0x09); + +	/* Reset the LANCE.	 */ +	reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */ + +	/* The Un-Reset needed is only needed for the real NE2100, and will +	   confuse the HP board. */ +	if (!hpJ2405A) +		outw(reset_val, ioaddr+LANCE_RESET); + +	outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */ +	if (inw(ioaddr+LANCE_DATA) != 0x0004) +		return -ENODEV; + +	/* Get the version of the chip. */ +	outw(88, ioaddr+LANCE_ADDR); +	if (inw(ioaddr+LANCE_ADDR) != 88) { +		lance_version = 0; +	} else {							/* Good, it's a newer chip. */ +		int chip_version = inw(ioaddr+LANCE_DATA); +		outw(89, ioaddr+LANCE_ADDR); +		chip_version |= inw(ioaddr+LANCE_DATA) << 16; +		if (lance_debug > 2) +			printk("  LANCE chip version is %#x.\n", chip_version); +		if ((chip_version & 0xfff) != 0x003) +			return -ENODEV; +		chip_version = (chip_version >> 12) & 0xffff; +		for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) { +			if (chip_table[lance_version].id_number == chip_version) +				break; +		} +	} + +	/* We can't use init_etherdev() to allocate dev->priv because it must +	   a ISA DMA-able region. */ +	dev = init_etherdev(dev, 0); +	dev->open = lance_open_fail; +	chipname = chip_table[lance_version].name; +	printk("%s: %s at %#3x,", dev->name, chipname, ioaddr); + +	/* There is a 16 byte station address PROM at the base address. +	   The first six bytes are the station address. */ +	for (i = 0; i < 6; i++) +		printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); + +	dev->base_addr = ioaddr; +	request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name); + +	/* Make certain the data structures used by the LANCE are aligned and DMAble. */ +		 +	lp = (struct lance_private *)(((unsigned long)kmalloc(sizeof(*lp)+7, +										   GFP_DMA | GFP_KERNEL)+7) & ~7); +	if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp); +	memset(lp, 0, sizeof(*lp)); +	dev->priv = lp; +	lp->name = chipname; +	lp->rx_buffs = (unsigned long)kmalloc(PKT_BUF_SZ*RX_RING_SIZE, +										  GFP_DMA | GFP_KERNEL); +	if (lance_need_isa_bounce_buffers) +		lp->tx_bounce_buffs = kmalloc(PKT_BUF_SZ*TX_RING_SIZE, +									  GFP_DMA | GFP_KERNEL); +	else +		lp->tx_bounce_buffs = NULL; + +	lp->chip_version = lance_version; + +	lp->init_block.mode = 0x0003;		/* Disable Rx and Tx. */ +	for (i = 0; i < 6; i++) +		lp->init_block.phys_addr[i] = dev->dev_addr[i]; +	lp->init_block.filter[0] = 0x00000000; +	lp->init_block.filter[1] = 0x00000000; +	lp->init_block.rx_ring = ((u32)virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; +	lp->init_block.tx_ring = ((u32)virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; + +	outw(0x0001, ioaddr+LANCE_ADDR); +	inw(ioaddr+LANCE_ADDR); +	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); +	outw(0x0002, ioaddr+LANCE_ADDR); +	inw(ioaddr+LANCE_ADDR); +	outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); +	outw(0x0000, ioaddr+LANCE_ADDR); +	inw(ioaddr+LANCE_ADDR); + +	if (irq) {					/* Set iff PCI card. */ +		dev->dma = 4;			/* Native bus-master, no DMA channel needed. */ +		dev->irq = irq; +	} else if (hp_builtin) { +		static const char dma_tbl[4] = {3, 5, 6, 0}; +		static const char irq_tbl[4] = {3, 4, 5, 9}; +		unsigned char port_val = inb(hp_builtin); +		dev->dma = dma_tbl[(port_val >> 4) & 3]; +		dev->irq = irq_tbl[(port_val >> 2) & 3]; +		printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma); +	} else if (hpJ2405A) { +		static const char dma_tbl[4] = {3, 5, 6, 7}; +		static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15}; +		short reset_val = inw(ioaddr+LANCE_RESET); +		dev->dma = dma_tbl[(reset_val >> 2) & 3]; +		dev->irq = irq_tbl[(reset_val >> 4) & 7]; +		printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma); +	} else if (lance_version == PCNET_ISAP) {		/* The plug-n-play version. */ +		short bus_info; +		outw(8, ioaddr+LANCE_ADDR); +		bus_info = inw(ioaddr+LANCE_BUS_IF); +		dev->dma = bus_info & 0x07; +		dev->irq = (bus_info >> 4) & 0x0F; +	} else { +		/* The DMA channel may be passed in PARAM1. */ +		if (dev->mem_start & 0x07) +			dev->dma = dev->mem_start & 0x07; +	} + +	if (dev->dma == 0) { +		/* Read the DMA channel status register, so that we can avoid +		   stuck DMA channels in the DMA detection below. */ +		dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) | +			(inb(DMA2_STAT_REG) & 0xf0); +	} +	if (dev->irq >= 2) +		printk(" assigned IRQ %d", dev->irq); +	else if (lance_version != 0)  {	/* 7990 boards need DMA detection first. */ +		/* To auto-IRQ we enable the initialization-done and DMA error +		   interrupts. For ISA boards we get a DMA error, but VLB and PCI +		   boards will work. */ +		autoirq_setup(0); + +		/* Trigger an initialization just for the interrupt. */ +		outw(0x0041, ioaddr+LANCE_DATA); + +		dev->irq = autoirq_report(2); +		if (dev->irq) +			printk(", probed IRQ %d", dev->irq); +		else { +			printk(", failed to detect IRQ line.\n"); +			return -ENODEV; +		} + +		/* Check for the initialization done bit, 0x0100, which means +		   that we don't need a DMA channel. */ +		if (inw(ioaddr+LANCE_DATA) & 0x0100) +			dev->dma = 4; +	} + +	if (dev->dma == 4) { +		printk(", no DMA needed.\n"); +	} else if (dev->dma) { +		if (request_dma(dev->dma, chipname)) { +			printk("DMA %d allocation failed.\n", dev->dma); +			return -ENODEV; +		} else +			printk(", assigned DMA %d.\n", dev->dma); +	} else {			/* OK, we have to auto-DMA. */ +		for (i = 0; i < 4; i++) { +			static const char dmas[] = { 5, 6, 7, 3 }; +			int dma = dmas[i]; +			int boguscnt; + +			/* Don't enable a permanently busy DMA channel, or the machine +			   will hang. */ +			if (test_bit(dma, &dma_channels)) +				continue; +			outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */ +			if (request_dma(dma, chipname)) +				continue; +			set_dma_mode(dma, DMA_MODE_CASCADE); +			enable_dma(dma); + +			/* Trigger an initialization. */ +			outw(0x0001, ioaddr+LANCE_DATA); +			for (boguscnt = 100; boguscnt > 0; --boguscnt) +				if (inw(ioaddr+LANCE_DATA) & 0x0900) +					break; +			if (inw(ioaddr+LANCE_DATA) & 0x0100) { +				dev->dma = dma; +				printk(", DMA %d.\n", dev->dma); +				break; +			} else { +				disable_dma(dma); +				free_dma(dma); +			} +		} +		if (i == 4) {			/* Failure: bail. */ +			printk("DMA detection failed.\n"); +			return -ENODEV; +		} +	} + +	if (lance_version == 0 && dev->irq == 0) { +		/* We may auto-IRQ now that we have a DMA channel. */ +		/* Trigger an initialization just for the interrupt. */ +		autoirq_setup(0); +		outw(0x0041, ioaddr+LANCE_DATA); + +		dev->irq = autoirq_report(4); +		if (dev->irq == 0) { +			printk("  Failed to detect the 7990 IRQ line.\n"); +			return -ENODEV; +		} +		printk("  Auto-IRQ detected IRQ%d.\n", dev->irq); +	} + +	if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { +		/* Turn on auto-select of media (10baseT or BNC) so that the user +		   can watch the LEDs even if the board isn't opened. */ +		outw(0x0002, ioaddr+LANCE_ADDR); +		/* Don't touch 10base2 power bit. */ +		outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); +	} + +	if (lance_debug > 0  &&  did_version++ == 0) +		printk("%s", version); + +	/* The LANCE-specific entries in the device structure. */ +	dev->open = lance_open; +	dev->hard_start_xmit = lance_start_xmit; +	dev->stop = lance_close; +	dev->get_stats = lance_get_stats; +	dev->set_multicast_list = set_multicast_list; + +	return 0; +} + +static int +lance_open_fail(struct device *dev) +{ +	return -ENODEV; +} + + + +static int +lance_open(struct device *dev) +{ +	struct lance_private *lp = (struct lance_private *)dev->priv; +	int ioaddr = dev->base_addr; +	int i; + +	if (dev->irq == 0 || +		request_irq(dev->irq, &lance_interrupt, 0, lp->name, dev)) { +		return -EAGAIN; +	} + +	MOD_INC_USE_COUNT; + +	/* We used to allocate DMA here, but that was silly. +	   DMA lines can't be shared!  We now permanently allocate them. */ + +	/* Reset the LANCE */ +	inw(ioaddr+LANCE_RESET); + +	/* The DMA controller is used as a no-operation slave, "cascade mode". */ +	if (dev->dma != 4) { +		enable_dma(dev->dma); +		set_dma_mode(dev->dma, DMA_MODE_CASCADE); +	} + +	/* Un-Reset the LANCE, needed only for the NE2100. */ +	if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET) +		outw(0, ioaddr+LANCE_RESET); + +	if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { +		/* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */ +		outw(0x0002, ioaddr+LANCE_ADDR); +		/* Only touch autoselect bit. */ +		outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); + 	} + +	if (lance_debug > 1) +		printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n", +			   dev->name, dev->irq, dev->dma, +		           (u32) virt_to_bus(lp->tx_ring), +		           (u32) virt_to_bus(lp->rx_ring), +			   (u32) virt_to_bus(&lp->init_block)); + +	lance_init_ring(dev, GFP_KERNEL); +	/* Re-initialize the LANCE, and start it when done. */ +	outw(0x0001, ioaddr+LANCE_ADDR); +	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); +	outw(0x0002, ioaddr+LANCE_ADDR); +	outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); + +	outw(0x0004, ioaddr+LANCE_ADDR); +	outw(0x0915, ioaddr+LANCE_DATA); + +	outw(0x0000, ioaddr+LANCE_ADDR); +	outw(0x0001, ioaddr+LANCE_DATA); + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; +	i = 0; +	while (i++ < 100) +		if (inw(ioaddr+LANCE_DATA) & 0x0100) +			break; +	/*  +	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton +	 * reports that doing so triggers a bug in the '974. +	 */ + 	outw(0x0042, ioaddr+LANCE_DATA); + +	if (lance_debug > 2) +		printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n", +			   dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA)); + +	return 0;					/* Always succeed */ +} + +/* The LANCE has been halted for one reason or another (busmaster memory +   arbitration error, Tx FIFO underflow, driver stopped it to reconfigure, +   etc.).  Modern LANCE variants always reload their ring-buffer +   configuration when restarted, so we must reinitialize our ring +   context before restarting.  As part of this reinitialization, +   find all packets still on the Tx ring and pretend that they had been +   sent (in effect, drop the packets on the floor) - the higher-level +   protocols will time out and retransmit.  It'd be better to shuffle +   these skbs to a temp list and then actually re-Tx them after +   restarting the chip, but I'm too lazy to do so right now.  dplatt@3do.com +*/ + +static void  +lance_purge_tx_ring(struct device *dev) +{ +	struct lance_private *lp = (struct lance_private *)dev->priv; +	int i; + +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (lp->tx_skbuff[i]) { +			dev_kfree_skb(lp->tx_skbuff[i],FREE_WRITE); +			lp->tx_skbuff[i] = NULL; +		} +	} +} + + +/* Initialize the LANCE Rx and Tx rings. */ +static void +lance_init_ring(struct device *dev, int gfp) +{ +	struct lance_private *lp = (struct lance_private *)dev->priv; +	int i; + +	lp->lock = 0, lp->tx_full = 0; +	lp->cur_rx = lp->cur_tx = 0; +	lp->dirty_rx = lp->dirty_tx = 0; + +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb; +		void *rx_buff; + +		skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp); +		lp->rx_skbuff[i] = skb; +		if (skb) { +			skb->dev = dev; +			rx_buff = skb->tail; +		} else +			rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp); +		if (rx_buff == NULL) +			lp->rx_ring[i].base = 0; +		else +			lp->rx_ring[i].base = (u32)virt_to_bus(rx_buff) | 0x80000000; +		lp->rx_ring[i].buf_length = -PKT_BUF_SZ; +	} +	/* The Tx buffer address is filled in as needed, but we do need to clear +	   the upper ownership bit. */ +	for (i = 0; i < TX_RING_SIZE; i++) { +		lp->tx_skbuff[i] = 0; +		lp->tx_ring[i].base = 0; +	} + +	lp->init_block.mode = 0x0000; +	for (i = 0; i < 6; i++) +		lp->init_block.phys_addr[i] = dev->dev_addr[i]; +	lp->init_block.filter[0] = 0x00000000; +	lp->init_block.filter[1] = 0x00000000; +	lp->init_block.rx_ring = ((u32)virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; +	lp->init_block.tx_ring = ((u32)virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; +} + +static void +lance_restart(struct device *dev, unsigned int csr0_bits, int must_reinit) +{ +	struct lance_private *lp = (struct lance_private *)dev->priv; + +	if (must_reinit || +		(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) { +		lance_purge_tx_ring(dev); +		lance_init_ring(dev, GFP_ATOMIC); +	} +	outw(0x0000,    dev->base_addr + LANCE_ADDR); +	outw(csr0_bits, dev->base_addr + LANCE_DATA); +} + +static int +lance_start_xmit(struct sk_buff *skb, struct device *dev) +{ +	struct lance_private *lp = (struct lance_private *)dev->priv; +	int ioaddr = dev->base_addr; +	int entry; +	unsigned long flags; + +	/* Transmitter timeout, serious problems. */ +	if (dev->tbusy) { +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < 20) +			return 1; +		outw(0, ioaddr+LANCE_ADDR); +		printk("%s: transmit timed out, status %4.4x, resetting.\n", +			   dev->name, inw(ioaddr+LANCE_DATA)); +		outw(0x0004, ioaddr+LANCE_DATA); +		lp->stats.tx_errors++; +#ifndef final_version +		{ +			int i; +			printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", +				   lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "", +				   lp->cur_rx); +			for (i = 0 ; i < RX_RING_SIZE; i++) +				printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", +					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, +					   lp->rx_ring[i].msg_length); +			for (i = 0 ; i < TX_RING_SIZE; i++) +				printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", +					   lp->tx_ring[i].base, -lp->tx_ring[i].length, +					   lp->tx_ring[i].misc); +			printk("\n"); +		} +#endif +		lance_restart(dev, 0x0043, 1); + +		dev->tbusy=0; +		dev->trans_start = jiffies; + +		return 0; +	} + +	if (lance_debug > 3) { +		outw(0x0000, ioaddr+LANCE_ADDR); +		printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name, +			   inw(ioaddr+LANCE_DATA)); +		outw(0x0000, ioaddr+LANCE_DATA); +	} + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) { +		printk("%s: Transmitter access conflict.\n", dev->name); +		return 1; +	} + +	if (set_bit(0, (void*)&lp->lock) != 0) { +		if (lance_debug > 0) +			printk("%s: tx queue lock!.\n", dev->name); +		/* don't clear dev->tbusy flag. */ +		return 1; +	} + +	/* Fill in a Tx ring entry */ + +	/* Mask to ring buffer boundary. */ +	entry = lp->cur_tx & TX_RING_MOD_MASK; + +	/* Caution: the write order is important here, set the base address +	   with the "ownership" bits last. */ + +	/* The old LANCE chips doesn't automatically pad buffers to min. size. */ +	if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) { +		lp->tx_ring[entry].length = +			-(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN); +	} else +		lp->tx_ring[entry].length = -skb->len; + +	lp->tx_ring[entry].misc = 0x0000; + +	/* If any part of this buffer is >16M we must copy it to a low-memory +	   buffer. */ +	if ((u32)virt_to_bus(skb->data) + skb->len > 0x01000000) { +		if (lance_debug > 5) +			printk("%s: bouncing a high-memory packet (%#x).\n", +				   dev->name, (u32)virt_to_bus(skb->data)); +		memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len); +		lp->tx_ring[entry].base = +			((u32)virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000; +		dev_kfree_skb (skb, FREE_WRITE); +	} else { +		lp->tx_skbuff[entry] = skb; +		lp->tx_ring[entry].base = ((u32)virt_to_bus(skb->data) & 0xffffff) | 0x83000000; +	} +	lp->cur_tx++; + +	/* Trigger an immediate send poll. */ +	outw(0x0000, ioaddr+LANCE_ADDR); +	outw(0x0048, ioaddr+LANCE_DATA); + +	dev->trans_start = jiffies; + +	save_flags(flags); +	cli(); +	lp->lock = 0; +	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0) +		dev->tbusy=0; +	else +		lp->tx_full = 1; +	restore_flags(flags); + +	return 0; +} + +/* The LANCE interrupt handler. */ +static void +lance_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +	struct device *dev = (struct device *)dev_id; +	struct lance_private *lp; +	int csr0, ioaddr, boguscnt=10; +	int must_restart; + +	if (dev == NULL) { +		printk ("lance_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} + +	ioaddr = dev->base_addr; +	lp = (struct lance_private *)dev->priv; +	if (dev->interrupt) +		printk("%s: Re-entering the interrupt handler.\n", dev->name); + +	dev->interrupt = 1; + +	outw(0x00, dev->base_addr + LANCE_ADDR); +	while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 +		   && --boguscnt >= 0) { +		/* Acknowledge all of the current interrupt sources ASAP. */ +		outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA); + +		must_restart = 0; + +		if (lance_debug > 5) +			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n", +				   dev->name, csr0, inw(dev->base_addr + LANCE_DATA)); + +		if (csr0 & 0x0400)			/* Rx interrupt */ +			lance_rx(dev); + +		if (csr0 & 0x0200) {		/* Tx-done interrupt */ +			int dirty_tx = lp->dirty_tx; + +			while (dirty_tx < lp->cur_tx) { +				int entry = dirty_tx & TX_RING_MOD_MASK; +				int status = lp->tx_ring[entry].base; +			 +				if (status < 0) +					break;			/* It still hasn't been Txed */ + +				lp->tx_ring[entry].base = 0; + +				if (status & 0x40000000) { +					/* There was an major error, log it. */ +					int err_status = lp->tx_ring[entry].misc; +					lp->stats.tx_errors++; +					if (err_status & 0x0400) lp->stats.tx_aborted_errors++; +					if (err_status & 0x0800) lp->stats.tx_carrier_errors++; +					if (err_status & 0x1000) lp->stats.tx_window_errors++; +					if (err_status & 0x4000) { +						/* Ackk!  On FIFO errors the Tx unit is turned off! */ +						lp->stats.tx_fifo_errors++; +						/* Remove this verbosity later! */ +						printk("%s: Tx FIFO error! Status %4.4x.\n", +							   dev->name, csr0); +						/* Restart the chip. */ +						must_restart = 1; +					} +				} else { +					if (status & 0x18000000) +						lp->stats.collisions++; +					lp->stats.tx_packets++; +				} + +				/* We must free the original skb if it's not a data-only copy +				   in the bounce buffer. */ +				if (lp->tx_skbuff[entry]) { +					dev_kfree_skb(lp->tx_skbuff[entry],FREE_WRITE); +					lp->tx_skbuff[entry] = 0; +				} +				dirty_tx++; +			} + +#ifndef final_version +			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { +				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n", +					   dirty_tx, lp->cur_tx, lp->tx_full); +				dirty_tx += TX_RING_SIZE; +			} +#endif + +			if (lp->tx_full && dev->tbusy +				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { +				/* The ring is no longer full, clear tbusy. */ +				lp->tx_full = 0; +				dev->tbusy = 0; +				mark_bh(NET_BH); +			} + +			lp->dirty_tx = dirty_tx; +		} + +		/* Log misc errors. */ +		if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */ +		if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */ +		if (csr0 & 0x0800) { +			printk("%s: Bus master arbitration failure, status %4.4x.\n", +				   dev->name, csr0); +			/* Restart the chip. */ +			must_restart = 1; +		} + +		if (must_restart) { +			/* stop the chip to clear the error condition, then restart */ +			outw(0x0000, dev->base_addr + LANCE_ADDR); +			outw(0x0004, dev->base_addr + LANCE_DATA); +			lance_restart(dev, 0x0002, 0); +		} +	} + +	/* Clear any other interrupt, and set interrupt enable. */ +	outw(0x0000, dev->base_addr + LANCE_ADDR); +	outw(0x7940, dev->base_addr + LANCE_DATA); + +	if (lance_debug > 4) +		printk("%s: exiting interrupt, csr%d=%#4.4x.\n", +			   dev->name, inw(ioaddr + LANCE_ADDR), +			   inw(dev->base_addr + LANCE_DATA)); + +	dev->interrupt = 0; +	return; +} + +static int +lance_rx(struct device *dev) +{ +	struct lance_private *lp = (struct lance_private *)dev->priv; +	int entry = lp->cur_rx & RX_RING_MOD_MASK; +	int i; +		 +	/* If we own the next entry, it's a new packet. Send it up. */ +	while (lp->rx_ring[entry].base >= 0) { +		int status = lp->rx_ring[entry].base >> 24; + +		if (status != 0x03) {			/* There was an error. */ +			/* There is a tricky error noted by John Murphy, +			   <murf@perftech.com> to Russ Nelson: Even with full-sized +			   buffers it's possible for a jabber packet to use two +			   buffers, with only the last correctly noting the error. */ +			if (status & 0x01)	/* Only count a general error at the */ +				lp->stats.rx_errors++; /* end of a packet.*/ +			if (status & 0x20) lp->stats.rx_frame_errors++; +			if (status & 0x10) lp->stats.rx_over_errors++; +			if (status & 0x08) lp->stats.rx_crc_errors++; +			if (status & 0x04) lp->stats.rx_fifo_errors++; +			lp->rx_ring[entry].base &= 0x03ffffff; +		} +		else  +		{ +			/* Malloc up new buffer, compatible with net3. */ +			short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4; +			struct sk_buff *skb; +			 +			if(pkt_len<60) +			{ +				printk("%s: Runt packet!\n",dev->name); +				lp->stats.rx_errors++; +			} +			else +			{ +				skb = dev_alloc_skb(pkt_len+2); +				if (skb == NULL)  +				{ +					printk("%s: Memory squeeze, deferring packet.\n", dev->name); +					for (i=0; i < RX_RING_SIZE; i++) +						if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0) +							break; + +					if (i > RX_RING_SIZE -2)  +					{ +						lp->stats.rx_dropped++; +						lp->rx_ring[entry].base |= 0x80000000; +						lp->cur_rx++; +					} +					break; +				} +				skb->dev = dev; +				skb_reserve(skb,2);	/* 16 byte align */ +				skb_put(skb,pkt_len);	/* Make room */ +				eth_copy_and_sum(skb, +					(unsigned char *)bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)), +					pkt_len,0); +				skb->protocol=eth_type_trans(skb,dev); +				netif_rx(skb); +				lp->stats.rx_packets++; +			} +		} +		/* The docs say that the buffer length isn't touched, but Andrew Boyd +		   of QNX reports that some revs of the 79C965 clear it. */ +		lp->rx_ring[entry].buf_length = -PKT_BUF_SZ; +		lp->rx_ring[entry].base |= 0x80000000; +		entry = (++lp->cur_rx) & RX_RING_MOD_MASK; +	} + +	/* We should check that at least two ring entries are free.	 If not, +	   we should free one and mark stats->rx_dropped++. */ + +	return 0; +} + +static int +lance_close(struct device *dev) +{ +	int ioaddr = dev->base_addr; +	struct lance_private *lp = (struct lance_private *)dev->priv; +	int i; + +	dev->start = 0; +	dev->tbusy = 1; + +	if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) { +		outw(112, ioaddr+LANCE_ADDR); +		lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); +	} +	outw(0, ioaddr+LANCE_ADDR); + +	if (lance_debug > 1) +		printk("%s: Shutting down ethercard, status was %2.2x.\n", +			   dev->name, inw(ioaddr+LANCE_DATA)); + +	/* We stop the LANCE here -- it occasionally polls +	   memory if we don't. */ +	outw(0x0004, ioaddr+LANCE_DATA); + +	if (dev->dma != 4) +		disable_dma(dev->dma); + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx and Tx queues. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = lp->rx_skbuff[i]; +		lp->rx_skbuff[i] = 0; +		lp->rx_ring[i].base = 0;		/* Not owned by LANCE chip. */ +		if (skb) { +			skb->free = 1; +			dev_kfree_skb(skb, FREE_WRITE); +		} +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (lp->tx_skbuff[i]) +			dev_kfree_skb(lp->tx_skbuff[i], FREE_WRITE); +		lp->tx_skbuff[i] = 0; +	} + +	MOD_DEC_USE_COUNT; +	return 0; +} + +static struct enet_statistics * +lance_get_stats(struct device *dev) +{ +	struct lance_private *lp = (struct lance_private *)dev->priv; +	short ioaddr = dev->base_addr; +	short saved_addr; +	unsigned long flags; + +	if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) { +		save_flags(flags); +		cli(); +		saved_addr = inw(ioaddr+LANCE_ADDR); +		outw(112, ioaddr+LANCE_ADDR); +		lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); +		outw(saved_addr, ioaddr+LANCE_ADDR); +		restore_flags(flags); +	} + +	return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + */ + +static void set_multicast_list(struct device *dev) +{ +	short ioaddr = dev->base_addr; + +	outw(0, ioaddr+LANCE_ADDR); +	outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance.	 */ + +	if (dev->flags&IFF_PROMISC) { +		/* Log any net taps. */ +		printk("%s: Promiscuous mode enabled.\n", dev->name); +		outw(15, ioaddr+LANCE_ADDR); +		outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */ +	} else { +		short multicast_table[4]; +		int i; +		int num_addrs=dev->mc_count; +		if(dev->flags&IFF_ALLMULTI) +			num_addrs=1; +		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */ +		memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table)); +		for (i = 0; i < 4; i++) { +			outw(8 + i, ioaddr+LANCE_ADDR); +			outw(multicast_table[i], ioaddr+LANCE_DATA); +		} +		outw(15, ioaddr+LANCE_ADDR); +		outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */ +	} + +	lance_restart(dev, 0x0142, 0); /*  Resume normal operation */ + +} + + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/myson803.c b/linux/src/drivers/net/myson803.c new file mode 100644 index 0000000..545d124 --- /dev/null +++ b/linux/src/drivers/net/myson803.c @@ -0,0 +1,1650 @@ +/* myson803.c: A Linux device driver for the Myson mtd803 Ethernet chip. */ +/* +	Written 1998-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	410 Severn Ave., Suite 210 +	Annapolis MD 21403 + +	Support information and updates available at +	http://www.scyld.com/network/myson803.html +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"myson803.c:v1.05 3/10/2003 Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/drivers.html\n"; + +/* Automatically extracted configuration info: +probe-func: myson803_probe +config-in: tristate 'Myson MTD803 series Ethernet support' CONFIG_MYSON_ETHER + +c-help-name: Myson MTD803 PCI Ethernet support +c-help-symbol: CONFIG_MYSON_ETHER +c-help: This driver is for the Myson MTD803 Ethernet adapter series. +c-help: More specific information and updates are available from  +c-help: http://www.scyld.com/network/drivers.html +*/ + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 40; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   This chip uses a 64 element hash table based on the Ethernet CRC.  */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   Both 'options[]' and 'full_duplex[]' should exist for driver +   interoperability. +   The media type is usually passed in 'options[]'. +    The default is autonegotation for speed and duplex. +	This should rarely be overridden. +    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +    Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +    Use option values 0x20 and 0x200 for forcing full duplex operation. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority. +   There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit Tx ring entries actually used.  */ +#define RX_RING_SIZE	32 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/unaligned.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +/* Kernels before 2.1.0 cannot map the high addrs assigned by some BIOSes. */ +#if (LINUX_VERSION_CODE < 0x20100)  ||  ! defined(MODULE) +#define USE_IO_OPS +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Myson mtd803 Ethernet driver"); +MODULE_LICENSE("GPL"); +/* List in order of common use. */ +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(full_duplex, "Non-zero to force full duplex, " +				 "non-negotiated link (deprecated)."); +MODULE_PARM_DESC(max_interrupt_work, +				 "Maximum events handled per interrupt"); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This driver is for the Myson mtd803 chip. +It should work with other Myson 800 series chips. + +II. Board-specific settings + +None. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE. +Some chips explicitly use only 2^N sized rings, while others use a +'next descriptor' pointer that the driver forms into rings. + +IIIb/c. Transmit/Receive Structure + +This driver uses a zero-copy receive and transmit scheme. +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers.  When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack.  Buffers consumed this way are replaced by newly allocated +skbuffs in a later phase of receives. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets.  When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine.  Copying also preloads the cache, which is +most useful with small frames. + +A subtle aspect of the operation is that the IP header at offset 14 in an +ethernet frame isn't longword aligned for further processing. +When unaligned buffers are permitted by the hardware (and always on copies) +frames are put into the skbuff at an offset of "+2", 16-byte aligning +the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IIId. SMP semantics + +The following are serialized with respect to each other via the "xmit_lock". +  dev->hard_start_xmit()	Transmit a packet +  dev->tx_timeout()			Transmit watchdog for stuck Tx +  dev->set_multicast_list()	Set the recieve filter. +Note: The Tx timeout watchdog code is implemented by the timer routine in +kernels up to 2.2.*.  In 2.4.* and later the timeout code is part of the +driver interface. + +The following fall under the global kernel lock.  The module will not be +unloaded during the call, unless a call with a potential reschedule e.g. +kmalloc() is called.  No other synchronization assertion is made. +  dev->open() +  dev->do_ioctl() +  dev->get_stats() +Caution: The lock for dev->open() is commonly broken with request_irq() or +kmalloc().  It is best to avoid any lock-breaking call in do_ioctl() and +get_stats(), or additional module locking code must be implemented. + +The following is self-serialized (no simultaneous entry) +  An handler registered with request_irq(). + +IV. Notes + +IVb. References + +http://www.scyld.com/expert/100mbps.html +http://scyld.com/expert/NWay.html +http://www.myson.com.hk/mtd/datasheet/mtd803.pdf +   Myson does not require a NDA to read the datasheet. + +IVc. Errata + +No undocumented errata. +*/ + + + +/* PCI probe routines. */ + +static void *myson_probe1(struct pci_dev *pdev, void *init_dev, +						   long ioaddr, int irq, int chip_idx, int find_cnt); +static int netdev_pwr_event(void *dev_instance, int event); + +/* Chips prior to the 803 have an external MII transceiver. */ +enum chip_capability_flags { HasMIIXcvr=1, HasChipXcvr=2 }; + +#ifdef USE_IO_OPS +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO  | PCI_ADDR0) +#define PCI_IOSIZE	256 +#else +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) +#define PCI_IOSIZE	1024 +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{"Myson mtd803 Fast Ethernet", {0x08031516, 0xffffffff, }, +	 PCI_IOTYPE, PCI_IOSIZE, HasChipXcvr}, +	{"Myson mtd891 Gigabit Ethernet", {0x08911516, 0xffffffff, }, +	 PCI_IOTYPE, PCI_IOSIZE, HasChipXcvr}, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info myson803_drv_id = { +	"myson803", 0, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, myson_probe1, +	netdev_pwr_event }; + +/* This driver was written to use PCI memory space, however x86-oriented +   hardware sometimes works only with I/O space accesses. */ +#ifdef USE_IO_OPS +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +/* Offsets to the various registers. +   Most accesses must be longword aligned. */ +enum register_offsets { +	StationAddr=0x00, MulticastFilter0=0x08, MulticastFilter1=0x0C, +	FlowCtrlAddr=0x10, RxConfig=0x18, TxConfig=0x1a, PCIBusCfg=0x1c, +	TxStartDemand=0x20, RxStartDemand=0x24, +	RxCurrentPtr=0x28, TxRingPtr=0x2c, RxRingPtr=0x30, +	IntrStatus=0x34, IntrEnable=0x38, +	FlowCtrlThreshold=0x3c, +	MIICtrl=0x40, EECtrl=0x40, RxErrCnts=0x44, TxErrCnts=0x48, +	PHYMgmt=0x4c, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrRxErr=0x0002, IntrRxDone=0x0004, IntrTxDone=0x0008, +	IntrTxEmpty=0x0010, IntrRxEmpty=0x0020, StatsMax=0x0040, RxEarly=0x0080, +	TxEarly=0x0100, RxOverflow=0x0200, TxUnderrun=0x0400, +	IntrPCIErr=0x2000, NWayDone=0x4000, LinkChange=0x8000, +}; + +/* Bits in the RxMode (np->txrx_config) register. */ +enum rx_mode_bits { +	RxEnable=0x01, RxFilter=0xfe, +	AcceptErr=0x02, AcceptRunt=0x08, AcceptBroadcast=0x40, +	AcceptMulticast=0x20, AcceptAllPhys=0x80, AcceptMyPhys=0x00, +	RxFlowCtrl=0x2000, +	TxEnable=0x40000, TxModeFDX=0x00100000, TxThreshold=0x00e00000, +}; + +/* Misc. bits. */ +enum misc_bits { +	BCR_Reset=1,				/* PCIBusCfg */ +	TxThresholdInc=0x200000, +}; + +/* The Rx and Tx buffer descriptors. */ +/* Note that using only 32 bit fields simplifies conversion to big-endian +   architectures. */ +struct netdev_desc { +	u32 status; +	u32 ctrl_length; +	u32 buf_addr; +	u32 next_desc; +}; + +/* Bits in network_desc.status */ +enum desc_status_bits { +	DescOwn=0x80000000, +	RxDescStartPacket=0x0800, RxDescEndPacket=0x0400, RxDescWholePkt=0x0c00, +	RxDescErrSum=0x80, RxErrRunt=0x40, RxErrLong=0x20, RxErrFrame=0x10, +	RxErrCRC=0x08, RxErrCode=0x04, +	TxErrAbort=0x2000, TxErrCarrier=0x1000, TxErrLate=0x0800, +	TxErr16Colls=0x0400, TxErrDefer=0x0200, TxErrHeartbeat=0x0100, +	TxColls=0x00ff, +}; +/* Bits in network_desc.ctrl_length */ +enum ctrl_length_bits { +	TxIntrOnDone=0x80000000, TxIntrOnFIFO=0x40000000, +	TxDescEndPacket=0x20000000, TxDescStartPacket=0x10000000, +	TxAppendCRC=0x08000000, TxPadTo64=0x04000000, TxNormalPkt=0x3C000000, +}; + +#define PRIV_ALIGN	15 	/* Required alignment mask */ +/* Use  __attribute__((aligned (L1_CACHE_BYTES)))  to maintain alignment +   within the structure. */ +struct netdev_private { +	/* Descriptor rings first for alignment. */ +	struct netdev_desc rx_ring[RX_RING_SIZE]; +	struct netdev_desc tx_ring[TX_RING_SIZE]; +	struct net_device *next_module;		/* Link for devices of this type. */ +	void *priv_addr;					/* Unaligned address for kfree */ +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for later free(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	/* Frequently used values: keep some adjacent for cache effect. */ +	int msg_level; +	int max_interrupt_work; +	int intr_enable; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; + +	struct netdev_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	unsigned int rx_died:1; +	unsigned int txrx_config; + +	/* These values keep track of the transceiver/media in use. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ + +	unsigned int mcast_filter[2]; +	int multicast_filter_limit; + +	/* MII transceiver section. */ +	int mii_cnt;						/* MII device addresses. */ +	u16 advertising;					/* NWay media advertisement */ +	unsigned char phys[2];				/* MII device addresses. */ +}; + +static int  eeprom_read(long ioaddr, int location); +static int  mdio_read(struct net_device *dev, int phy_id, +					  unsigned int location); +static void mdio_write(struct net_device *dev, int phy_id, +					   unsigned int location, int value); +static int  netdev_open(struct net_device *dev); +static void check_duplex(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct net_device *dev); +static void init_ring(struct net_device *dev); +static int  start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int  netdev_rx(struct net_device *dev); +static void netdev_error(struct net_device *dev, int intr_status); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int  netdev_close(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_net_dev = NULL; + +#ifndef MODULE +int myson803_probe(struct net_device *dev) +{ +	if (pci_drv_register(&myson803_drv_id, dev) < 0) +		return -ENODEV; +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif + +static void *myson_probe1(struct pci_dev *pdev, void *init_dev, +						   long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct netdev_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	printk(KERN_INFO "%s: %s at 0x%lx, ", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr); + +	for (i = 0; i < 3; i++) +		((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i + 8)); +	if (memcmp(dev->dev_addr, "\0\0\0\0\0", 6) == 0) { +		/* Fill a temp addr with the "locally administered" bit set. */ +		memcpy(dev->dev_addr, ">Linux", 6); +	} +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +#if ! defined(final_version) /* Dump the EEPROM contents during development. */ +	if (debug > 4) +		for (i = 0; i < 0x40; i++) +			printk("%4.4x%s", +				   eeprom_read(ioaddr, i), i % 16 != 15 ? " " : "\n"); +#endif + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	/* Do bogusness checks before this point. +	   We do a request_region() only to register /proc/ioports info. */ +#ifdef USE_IO_OPS +	request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name); +#endif + +	/* Reset the chip to erase previous misconfiguration. */ +	writel(BCR_Reset, ioaddr + PCIBusCfg); + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_net_dev; +	root_net_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	if (dev->mem_start) +		option = dev->mem_start; + +	/* The lower four bits are the media type. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 0x3ff; +		if (np->default_port) +			np->medialock = 1; +	} +	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) { +		if (np->msg_level & NETIF_MSG_PROBE) +			printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" +				   " disabled.\n", dev->name); +		np->duplex_lock = 1; +	} + +	/* The chip-specific entries in the device structure. */ +	dev->open = &netdev_open; +	dev->hard_start_xmit = &start_tx; +	dev->stop = &netdev_close; +	dev->get_stats = &get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; + +	if (np->drv_flags & HasMIIXcvr) { +		int phy, phy_idx = 0; +		for (phy = 0; phy < 32 && phy_idx < 4; phy++) { +			int mii_status = mdio_read(dev, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				np->phys[phy_idx++] = phy; +				np->advertising = mdio_read(dev, phy, 4); +				if (np->msg_level & NETIF_MSG_PROBE) +					printk(KERN_INFO "%s: MII PHY found at address %d, status " +						   "0x%4.4x advertising %4.4x.\n", +						   dev->name, phy, mii_status, np->advertising); +			} +		} +		np->mii_cnt = phy_idx; +	} +	if (np->drv_flags & HasChipXcvr) { +		np->phys[np->mii_cnt++] = 32; +		printk(KERN_INFO "%s: Internal PHY status 0x%4.4x" +			   " advertising %4.4x.\n", +			   dev->name, mdio_read(dev, 32, 1), mdio_read(dev, 32, 4)); +	} +	/* Allow forcing the media type. */ +	if (np->default_port & 0x330) { +		np->medialock = 1; +		if (option & 0x220) +			np->full_duplex = 1; +		printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +			   (option & 0x300 ? 100 : 10), +			   (np->full_duplex ? "full" : "half")); +		if (np->mii_cnt) +			mdio_write(dev, np->phys[0], 0, +					   ((option & 0x300) ? 0x2000 : 0) | 	/* 100mbps? */ +					   (np->full_duplex ? 0x0100 : 0)); /* Full duplex? */ +	} + +	return dev; +} + + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.  These are +   often serial bit streams generated by the host processor. +   The example below is for the common 93c46 EEPROM, 64 16 bit words. */ + +/* This "delay" forces out buffered PCI writes. +   The udelay() is unreliable for timing, but some Myson NICs shipped with +   absurdly slow EEPROMs. + */ +#define eeprom_delay(ee_addr)	readl(ee_addr); udelay(2); readl(ee_addr) + +enum EEPROM_Ctrl_Bits { +	EE_ShiftClk=0x04<<16, EE_ChipSelect=0x88<<16, +	EE_DataOut=0x02<<16, EE_DataIn=0x01<<16, +	EE_Write0=0x88<<16, EE_Write1=0x8a<<16, +}; + +/* The EEPROM commands always start with 01.. preamble bits. +   Commands are prepended to the variable-length address. */ +enum EEPROM_Cmds { EE_WriteCmd=5, EE_ReadCmd=6, EE_EraseCmd=7, }; + +static int eeprom_read(long addr, int location) +{ +	int i; +	int retval = 0; +	long ee_addr = addr + EECtrl; +	int read_cmd = location | (EE_ReadCmd<<6); + +	writel(EE_ChipSelect, ee_addr); + +	/* Shift the read command bits out. */ +	for (i = 10; i >= 0; i--) { +		int dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; +		writel(dataval, ee_addr); +		eeprom_delay(ee_addr); +		writel(dataval | EE_ShiftClk, ee_addr); +		eeprom_delay(ee_addr); +	} +	writel(EE_ChipSelect, ee_addr); +	eeprom_delay(ee_addr); + +	for (i = 16; i > 0; i--) { +		writel(EE_ChipSelect | EE_ShiftClk, ee_addr); +		eeprom_delay(ee_addr); +		retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); +		writel(EE_ChipSelect, ee_addr); +		eeprom_delay(ee_addr); +	} + +	/* Terminate the EEPROM access. */ +	writel(EE_ChipSelect, ee_addr); +	writel(0, ee_addr); +	return retval; +} + +/*  MII transceiver control section. +	Read and write the MII registers using software-generated serial +	MDIO protocol.  See the MII specifications or DP83840A data sheet +	for details. + +	The maximum data clock rate is 2.5 Mhz. +	The timing is decoupled from the processor clock by flushing the write +	from the CPU write buffer with a following read, and using PCI +	transaction timing. */ +#define mdio_in(mdio_addr) readl(mdio_addr) +#define mdio_out(value, mdio_addr) writel(value, mdio_addr) +#define mdio_delay(mdio_addr) readl(mdio_addr) + +/* Set iff a MII transceiver on any interface requires mdio preamble. +   This only set with older tranceivers, so the extra +   code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 0; + +enum mii_reg_bits { +	MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004, +}; +#define MDIO_EnbIn  (0) +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and +   a few older transceivers. */ +static void mdio_sync(long mdio_addr) +{ +	int bits = 32; + +	/* Establish sync by sending at least 32 logic ones. */ +	while (--bits >= 0) { +		mdio_out(MDIO_WRITE1, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +} + +static int mdio_read(struct net_device *dev, int phy_id, unsigned int location) +{ +	long ioaddr = dev->base_addr; +	long mdio_addr = ioaddr + MIICtrl; +	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; +	int i, retval = 0; + +	if (location >= 32) +		return 0xffff; +	if (phy_id >= 32) { +		if (location < 6) +			return readw(ioaddr + PHYMgmt + location*2); +		else if (location == 16) +			return readw(ioaddr + PHYMgmt + 6*2); +		else if (location == 17) +			return readw(ioaddr + PHYMgmt + 7*2); +		else if (location == 18) +			return readw(ioaddr + PHYMgmt + 10*2); +		else +			return 0; +	} + +	if (mii_preamble_required) +		mdio_sync(mdio_addr); + +	/* Shift the read command bits out. */ +	for (i = 15; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + +		mdio_out(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(dataval | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Read the two transition, 16 data, and wire-idle bits. */ +	for (i = 19; i > 0; i--) { +		mdio_out(MDIO_EnbIn, mdio_addr); +		mdio_delay(mdio_addr); +		retval = (retval << 1) | ((mdio_in(mdio_addr) & MDIO_Data) ? 1 : 0); +		mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, +					   unsigned int location, int value) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	long mdio_addr = ioaddr + MIICtrl; +	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; +	int i; + +	if (location == 4  &&  phy_id == np->phys[0]) +		np->advertising = value; +	else if (location >= 32) +		return; + +	if (phy_id == 32) { +		if (location < 6) +			writew(value, ioaddr + PHYMgmt + location*2); +		else if (location == 16) +			writew(value, ioaddr + PHYMgmt + 6*2); +		else if (location == 17) +			writew(value, ioaddr + PHYMgmt + 7*2); +		return; +	} + +	if (mii_preamble_required) +		mdio_sync(mdio_addr); + +	/* Shift the command bits out. */ +	for (i = 31; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + +		mdio_out(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(dataval | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Clear out extra bits. */ +	for (i = 2; i > 0; i--) { +		mdio_out(MDIO_EnbIn, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return; +} + + +static int netdev_open(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	/* Some chips may need to be reset. */ + +	MOD_INC_USE_COUNT; + +	writel(~0, ioaddr + IntrStatus); + +	/* Note that both request_irq() and init_ring() call kmalloc(), which +	   break the global kernel lock protecting this routine. */ +	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", +			   dev->name, dev->irq); + +	init_ring(dev); + +	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); +	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); + +	/* Address register must be written as words. */ +	writel(cpu_to_le32(cpu_to_le32(get_unaligned((u32 *)dev->dev_addr))), +					   ioaddr + StationAddr); +	writel(cpu_to_le16(cpu_to_le16(get_unaligned((u16 *)(dev->dev_addr+4)))), +					   ioaddr + StationAddr + 4); +	/* Set the flow control address, 01:80:c2:00:00:01. */ +	writel(0x00c28001, ioaddr + FlowCtrlAddr); +	writel(0x00000100, ioaddr + FlowCtrlAddr + 4); + +	/* Initialize other registers. */ +	/* Configure the PCI bus bursts and FIFO thresholds. */ +	writel(0x01f8, ioaddr + PCIBusCfg); + +	if (dev->if_port == 0) +		dev->if_port = np->default_port; + +	np->txrx_config = TxEnable | RxEnable | RxFlowCtrl | 0x00600000; +	np->mcast_filter[0] = np->mcast_filter[1] = 0; +	np->rx_died = 0; +	set_rx_mode(dev); +	netif_start_tx_queue(dev); + +	/* Enable interrupts by setting the interrupt mask. */ +	np->intr_enable = IntrRxDone | IntrRxErr | IntrRxEmpty | IntrTxDone +		| IntrTxEmpty | StatsMax | RxOverflow | TxUnderrun | IntrPCIErr +		| NWayDone | LinkChange; +	writel(np->intr_enable, ioaddr + IntrEnable); + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done netdev_open(), PHY status: %x %x.\n", +			   dev->name, (int)readw(ioaddr + PHYMgmt), +			   (int)readw(ioaddr + PHYMgmt + 2)); + +	/* Set the timer to check for link beat. */ +	init_timer(&np->timer); +	np->timer.expires = jiffies + 3*HZ; +	np->timer.data = (unsigned long)dev; +	np->timer.function = &netdev_timer;				/* timer handler */ +	add_timer(&np->timer); + +	return 0; +} + +static void check_duplex(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int new_tx_mode = np->txrx_config; + +	if (np->medialock) { +	} else { +		int mii_reg5 = mdio_read(dev, np->phys[0], 5); +		int negotiated = mii_reg5 & np->advertising; +		int duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; +		if (np->duplex_lock  ||  mii_reg5 == 0xffff) +			return; +		if (duplex) +			new_tx_mode |= TxModeFDX; +		if (np->full_duplex != duplex) { +			np->full_duplex = duplex; +			if (np->msg_level & NETIF_MSG_LINK) +				printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d" +					   " negotiated capability %4.4x.\n", dev->name, +					   duplex ? "full" : "half", np->phys[0], negotiated); +		} +	} +	if (np->txrx_config != new_tx_mode) +		writel(new_tx_mode, ioaddr + RxConfig); +} + +static void netdev_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 10*HZ; + +	if (np->msg_level & NETIF_MSG_TIMER) { +		printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x.\n", +			   dev->name, (int)readw(ioaddr + PHYMgmt + 10)); +	} +	/* This will either have a small false-trigger window or will not catch +	   tbusy incorrectly set when the queue is empty. */ +	if (netif_queue_paused(dev)  && +		np->cur_tx - np->dirty_tx > 1  && +		(jiffies - dev->trans_start) > TX_TIMEOUT) { +		tx_timeout(dev); +	} +	/* It's dead Jim, no race condition. */ +	if (np->rx_died) +		netdev_rx(dev); +	check_duplex(dev); +	np->timer.expires = jiffies + next_tick; +	add_timer(&np->timer); +} + +static void tx_timeout(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," +		   " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus)); + +	if (np->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)np->rx_ring[i].status); +		printk("\n"KERN_DEBUG"  Tx ring %p: ", np->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %8.8x", np->tx_ring[i].status); +		printk("\n"); +	} + +	/* Stop and restart the chip's Tx processes . */ +	writel(np->txrx_config & ~TxEnable, ioaddr + RxConfig); +	writel(virt_to_bus(np->tx_ring + (np->dirty_tx%TX_RING_SIZE)), +		   ioaddr + TxRingPtr); +	writel(np->txrx_config, ioaddr + RxConfig); +	/* Trigger an immediate transmit demand. */ +	writel(0, dev->base_addr + TxStartDemand); + +	dev->trans_start = jiffies; +	np->stats.tx_errors++; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	np->tx_full = 0; +	np->cur_rx = np->cur_tx = 0; +	np->dirty_rx = np->dirty_tx = 0; + +	np->rx_buf_sz = (dev->mtu <= 1532 ? PKT_BUF_SZ : dev->mtu + 4); +	np->rx_head_desc = &np->rx_ring[0]; + +	/* Initialize all Rx descriptors. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].ctrl_length = cpu_to_le32(np->rx_buf_sz); +		np->rx_ring[i].status = 0; +		np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]); +		np->rx_skbuff[i] = 0; +	} +	/* Mark the last entry as wrapping the ring. */ +	np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]); + +	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); +		np->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		np->rx_ring[i].buf_addr = virt_to_le32desc(skb->tail); +		np->rx_ring[i].status = cpu_to_le32(DescOwn); +	} +	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + +	for (i = 0; i < TX_RING_SIZE; i++) { +		np->tx_skbuff[i] = 0; +		np->tx_ring[i].status = 0; +		np->tx_ring[i].next_desc = virt_to_le32desc(&np->tx_ring[i+1]); +	} +	np->tx_ring[i-1].next_desc = virt_to_le32desc(&np->tx_ring[0]); +	return; +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned entry; + +	/* Block a timer-based transmit from overlapping.  This happens when +	   packets are presumed lost, and we use this check the Tx status. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tx_timeout(dev); +		return 1; +	} + +	/* Note: Ordering is important here, set the field with the +	   "ownership" bit last, and only then increment cur_tx. */ + +	/* Calculate the next Tx descriptor entry. */ +	entry = np->cur_tx % TX_RING_SIZE; + +	np->tx_skbuff[entry] = skb; + +	np->tx_ring[entry].buf_addr = virt_to_le32desc(skb->data); +	np->tx_ring[entry].ctrl_length = +		cpu_to_le32(TxIntrOnDone | TxNormalPkt | (skb->len << 11) | skb->len); +	np->tx_ring[entry].status = cpu_to_le32(DescOwn); +	np->cur_tx++; + +	/* On some architectures: explicitly flushing cache lines here speeds +	   operation. */ + +	if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) { +		np->tx_full = 1; +		/* Check for a just-cleared queue. */ +		if (np->cur_tx - (volatile unsigned int)np->dirty_tx +			< TX_QUEUE_LEN - 2) { +			np->tx_full = 0; +			netif_unpause_tx_queue(dev); +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ +	/* Wake the potentially-idle transmit channel. */ +	writel(0, dev->base_addr + TxStartDemand); + +	dev->trans_start = jiffies; + +	if (np->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", +			   dev->name, np->cur_tx, entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct netdev_private *np; +	long ioaddr; +	int boguscnt; + +#ifndef final_version			/* Can never occur. */ +	if (dev == NULL) { +		printk (KERN_ERR "Netdev interrupt handler(): IRQ %d for unknown " +				"device.\n", irq); +		return; +	} +#endif + +	ioaddr = dev->base_addr; +	np = (struct netdev_private *)dev->priv; +	boguscnt = np->max_interrupt_work; + +#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x020300 +	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */ +	if (test_and_set_bit(0, (void*)&dev->interrupt)) { +		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", +			   dev->name); +		dev->interrupt = 0;	/* Avoid halting machine. */ +		return; +	} +#endif + +	do { +		u32 intr_status = readl(ioaddr + IntrStatus); + +		/* Acknowledge all of the current interrupt sources ASAP. */ +		writel(intr_status, ioaddr + IntrStatus); + +		if (np->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", +				   dev->name, intr_status); + +		if (intr_status == 0) +			break; + +		if (intr_status & IntrRxDone) +			netdev_rx(dev); + +		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { +			int entry = np->dirty_tx % TX_RING_SIZE; +			int tx_status = le32_to_cpu(np->tx_ring[entry].status); +			if (tx_status & DescOwn) +				break; +			if (np->msg_level & NETIF_MSG_TX_DONE) +				printk(KERN_DEBUG "%s: Transmit done, Tx status %8.8x.\n", +					   dev->name, tx_status); +			if (tx_status & (TxErrAbort | TxErrCarrier | TxErrLate +							 | TxErr16Colls | TxErrHeartbeat)) { +				if (np->msg_level & NETIF_MSG_TX_ERR) +					printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", +						   dev->name, tx_status); +				np->stats.tx_errors++; +				if (tx_status & TxErrCarrier) np->stats.tx_carrier_errors++; +				if (tx_status & TxErrLate) np->stats.tx_window_errors++; +				if (tx_status & TxErrHeartbeat) np->stats.tx_heartbeat_errors++; +#ifdef ETHER_STATS +				if (tx_status & TxErr16Colls) np->stats.collisions16++; +				if (tx_status & TxErrAbort) np->stats.tx_aborted_errors++; +#else +				if (tx_status & (TxErr16Colls|TxErrAbort)) +					np->stats.tx_aborted_errors++; +#endif +			} else { +				np->stats.tx_packets++; +				np->stats.collisions += tx_status & TxColls; +#if LINUX_VERSION_CODE > 0x20127 +				np->stats.tx_bytes += np->tx_skbuff[entry]->len; +#endif +#ifdef ETHER_STATS +				if (tx_status & TxErrDefer) np->stats.tx_deferred++; +#endif +			} +			/* Free the original skb. */ +			dev_free_skb_irq(np->tx_skbuff[entry]); +			np->tx_skbuff[entry] = 0; +		} +		/* Note the 4 slot hysteresis to mark the queue non-full. */ +		if (np->tx_full  &&  np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { +			/* The ring is no longer full, allow new TX entries. */ +			np->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & (IntrRxErr | IntrRxEmpty | StatsMax | RxOverflow +						   | TxUnderrun | IntrPCIErr | NWayDone | LinkChange)) +			netdev_error(dev, intr_status); + +		if (--boguscnt < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", +				   dev->name, intr_status); +			break; +		} +	} while (1); + +	if (np->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); + +#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x020300 +	clear_bit(0, (void*)&dev->interrupt); +#endif +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int entry = np->cur_rx % RX_RING_SIZE; +	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; +	int refilled = 0; + +	if (np->msg_level & NETIF_MSG_RX_STATUS) { +		printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", +			   entry, np->rx_ring[entry].status); +	} + +	/* If EOP is set on the next entry, it's a new packet. Send it up. */ +	while ( ! (np->rx_head_desc->status & cpu_to_le32(DescOwn))) { +		struct netdev_desc *desc = np->rx_head_desc; +		u32 desc_status = le32_to_cpu(desc->status); + +		if (np->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n", +				   desc_status); +		if (--boguscnt < 0) +			break; +		if ((desc_status & RxDescWholePkt) != RxDescWholePkt) { +			printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " +				   "multiple buffers, entry %#x length %d status %4.4x!\n", +				   dev->name, np->cur_rx, desc_status >> 16, desc_status); +			np->stats.rx_length_errors++; +		} else if (desc_status & RxDescErrSum) { +			/* There was a error. */ +			if (np->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n", +					   desc_status); +			np->stats.rx_errors++; +			if (desc_status & (RxErrLong|RxErrRunt)) +				np->stats.rx_length_errors++; +			if (desc_status & (RxErrFrame|RxErrCode)) +				np->stats.rx_frame_errors++; +			if (desc_status & RxErrCRC) +				np->stats.rx_crc_errors++; +		} else { +			struct sk_buff *skb; +			/* Reported length should omit the CRC. */ +			u16 pkt_len = ((desc_status >> 16) & 0xfff) - 4; + +#ifndef final_version +			if (np->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d" +					   " of %d, bogus_cnt %d.\n", +					   pkt_len, pkt_len, boguscnt); +#endif +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < np->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +			} else { +				skb_put(skb = np->rx_skbuff[entry], pkt_len); +				np->rx_skbuff[entry] = NULL; +			} +#ifndef final_version				/* Remove after testing. */ +			/* You will want this info for the initial debug. */ +			if (np->msg_level & NETIF_MSG_PKTDATA) +				printk(KERN_DEBUG "  Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:" +					   "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x " +					   "%d.%d.%d.%d.\n", +					   skb->data[0], skb->data[1], skb->data[2], skb->data[3], +					   skb->data[4], skb->data[5], skb->data[6], skb->data[7], +					   skb->data[8], skb->data[9], skb->data[10], +					   skb->data[11], skb->data[12], skb->data[13], +					   skb->data[14], skb->data[15], skb->data[16], +					   skb->data[17]); +#endif +			skb->mac.raw = skb->data; +			/* Protocol lookup disabled until verified with all kernels. */ +			if (0 && ntohs(skb->mac.ethernet->h_proto) >= 0x0800) { +				struct ethhdr *eth = skb->mac.ethernet; +				skb->protocol = eth->h_proto; +				if (desc_status & 0x1000) { +					if ((dev->flags & IFF_PROMISC) && +						memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN)) +						skb->pkt_type = PACKET_OTHERHOST; +				} else if (desc_status & 0x2000) +					skb->pkt_type = PACKET_BROADCAST; +				else if (desc_status & 0x4000) +					skb->pkt_type = PACKET_MULTICAST; +			} else +				skb->protocol = eth_type_trans(skb, dev); +			netif_rx(skb); +			dev->last_rx = jiffies; +			np->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			np->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++np->cur_rx) % RX_RING_SIZE; +		np->rx_head_desc = &np->rx_ring[entry]; +	} + +	/* Refill the Rx ring buffers. */ +	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { +		struct sk_buff *skb; +		entry = np->dirty_rx % RX_RING_SIZE; +		if (np->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(np->rx_buf_sz); +			np->rx_skbuff[entry] = skb; +			if (skb == NULL) +				break;				/* Better luck next round. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			np->rx_ring[entry].buf_addr = virt_to_le32desc(skb->tail); +		} +		np->rx_ring[entry].ctrl_length = cpu_to_le32(np->rx_buf_sz); +		np->rx_ring[entry].status = cpu_to_le32(DescOwn); +		refilled++; +	} + +	/* Restart Rx engine if stopped. */ +	if (refilled) {				/* Perhaps  "&& np->rx_died" */ +		writel(0, dev->base_addr + RxStartDemand); +		np->rx_died = 0; +	} +	return refilled; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (intr_status & (LinkChange | NWayDone)) { +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_NOTICE "%s: Link changed: Autonegotiation advertising" +				   " %4.4x  partner %4.4x.\n", dev->name, +				   mdio_read(dev, np->phys[0], 4), +				   mdio_read(dev, np->phys[0], 5)); +		/* Clear sticky bit first. */ +		readw(ioaddr + PHYMgmt + 2); +		if (readw(ioaddr + PHYMgmt + 2) & 0x0004) +			netif_link_up(dev); +		else +			netif_link_down(dev); +		check_duplex(dev); +	} +	if ((intr_status & TxUnderrun) +		&& (np->txrx_config & TxThreshold) != TxThreshold) { +		np->txrx_config += TxThresholdInc; +		writel(np->txrx_config, ioaddr + RxConfig); +		np->stats.tx_fifo_errors++; +	} +	if (intr_status & IntrRxEmpty) { +		printk(KERN_WARNING "%s: Out of receive buffers: no free memory.\n", +			   dev->name); +		/* Refill Rx descriptors */ +		np->rx_died = 1; +		netdev_rx(dev); +	} +	if (intr_status & RxOverflow) { +		printk(KERN_WARNING "%s: Receiver overflow.\n", dev->name); +		np->stats.rx_over_errors++; +		netdev_rx(dev);			/* Refill Rx descriptors */ +		get_stats(dev);			/* Empty dropped counter. */ +	} +	if (intr_status & StatsMax) { +		get_stats(dev); +	} +	if ((intr_status & ~(LinkChange|NWayDone|StatsMax|TxUnderrun|RxOverflow +						 |TxEarly|RxEarly|0x001e)) +		&& (np->msg_level & NETIF_MSG_DRV)) +		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", +			   dev->name, intr_status); +	/* Hmmmmm, it's not clear how to recover from PCI faults. */ +	if (intr_status & IntrPCIErr) { +		const char *const pcierr[4] = +		{ "Parity Error", "Master Abort", "Target Abort", "Unknown Error" }; +		if (np->msg_level & NETIF_MSG_DRV) +			printk(KERN_WARNING "%s: PCI Bus %s, %x.\n", +				   dev->name, pcierr[(intr_status>>11) & 3], intr_status); +	} +} + +/* We do not bother to spinlock statistics. +   A window only exists if we have non-atomic adds, the error counts are +   typically zero, and statistics are non-critical. */  +static struct net_device_stats *get_stats(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned int rxerrs = readl(ioaddr + RxErrCnts); +	unsigned int txerrs = readl(ioaddr + TxErrCnts); + +	/* The chip only need report frames silently dropped. */ +	np->stats.rx_crc_errors	+= rxerrs >> 16; +	np->stats.rx_missed_errors	+= rxerrs & 0xffff; + +	/* These stats are required when the descriptor is closed before Tx. */ +	np->stats.tx_aborted_errors += txerrs >> 24; +	np->stats.tx_window_errors += (txerrs >> 16) & 0xff; +	np->stats.collisions += txerrs & 0xffff; + +	return &np->stats; +} + +/* Big-endian AUTODIN II ethernet CRC calculations. +   This is slow but compact code.  Do not use this routine for bulk data, +   use a table-based routine instead. +   This is common code and may be in the kernel with Linux 2.5+. +*/ +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ +	u32 crc = ~0; + +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 0; bit < 8; bit++, current_octet >>= 1) +			crc = (crc << 1) ^ +				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u32 mc_filter[2];			/* Multicast hash filter */ +	u32 rx_mode; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		mc_filter[1] = mc_filter[0] = ~0; +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAllPhys +			| AcceptMyPhys; +	} else if ((dev->mc_count > np->multicast_filter_limit) +			   ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to match, or accept all multicasts. */ +		mc_filter[1] = mc_filter[0] = ~0; +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; +	} else { +		struct dev_mc_list *mclist; +		int i; +		mc_filter[1] = mc_filter[0] = 0; +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			set_bit((ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) & 0x3f, +					mc_filter); +		} +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; +	} +	if (mc_filter[0] != np->mcast_filter[0]  || +		mc_filter[1] != np->mcast_filter[1]) { +		writel(mc_filter[0], ioaddr + MulticastFilter0); +		writel(mc_filter[1], ioaddr + MulticastFilter1); +		np->mcast_filter[0] = mc_filter[0]; +		np->mcast_filter[1] = mc_filter[1]; +	} +	if ((np->txrx_config & RxFilter) != rx_mode) { +		np->txrx_config &= ~RxFilter; +		np->txrx_config |= rx_mode; +		writel(np->txrx_config, ioaddr + RxConfig); +	} +} + +/* +  Handle user-level ioctl() calls. +  We must use two numeric constants as the key because some clueless person +  changed the value for the symbolic name. +*/ +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = np->phys[0]; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(dev, data[0], data[1]); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == np->phys[0]) { +			u16 value = data[2]; +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				np->medialock = (value & 0x9000) ? 0 : 1; +				if (np->medialock) +					np->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: np->advertising = value; break; +			} +			/* Perhaps check_duplex(dev), depending on chip semantics. */ +		} +		mdio_write(dev, data[0], data[1], data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static int netdev_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x.\n", +			   dev->name, (int)readl(ioaddr + RxConfig)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); +	} + +	/* Disable interrupts by clearing the interrupt mask. */ +	writel(0x0000, ioaddr + IntrEnable); + +	/* Stop the chip's Tx and Rx processes. */ +	np->txrx_config = 0; +	writel(0, ioaddr + RxConfig); + +	del_timer(&np->timer); + +#ifdef __i386__ +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(np->tx_ring)); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" #%d desc. %x %x %8.8x.\n", +				   i, np->tx_ring[i].status, np->tx_ring[i].ctrl_length, +				   np->tx_ring[i].buf_addr); +		printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n", +			   (int)virt_to_bus(np->rx_ring)); +		for (i = 0; i < RX_RING_SIZE; i++) { +			printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", +				   i, np->rx_ring[i].status, np->rx_ring[i].ctrl_length, +				   np->rx_ring[i].buf_addr); +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].status = 0; +		np->rx_ring[i].buf_addr = 0xBADF00D0; /* An invalid address. */ +		if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			np->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(np->rx_skbuff[i]); +		} +		np->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (np->tx_skbuff[i]) +			dev_free_skb(np->tx_skbuff[i]); +		np->tx_skbuff[i] = 0; +	} + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static int netdev_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		/* Disable interrupts, stop Tx and Rx. */ +		writel(0, ioaddr + IntrEnable); +		writel(0, ioaddr + RxConfig); +		break; +	case DRV_RESUME: +		/* This is incomplete: the actions are very chip specific. */ +		set_rx_mode(dev); +		writel(np->intr_enable, ioaddr + IntrEnable); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			/* Some, but not all, kernel versions close automatically. */ +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_net_dev; *devp; devp = next) { +			next = &((struct netdev_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	} + +	return 0; +} + + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return pci_drv_register(&myson803_drv_id, NULL); +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&myson803_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_net_dev) { +		struct netdev_private *np = (void *)(root_net_dev->priv); +		unregister_netdev(root_net_dev); +#ifdef USE_IO_OPS +		release_region(root_net_dev->base_addr, +					   pci_id_tbl[np->chip_id].io_size); +#else +		iounmap((char *)(root_net_dev->base_addr)); +#endif +		next_dev = np->next_module; +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_net_dev); +		root_net_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` myson803.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c myson803.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c myson803.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/natsemi.c b/linux/src/drivers/net/natsemi.c new file mode 100644 index 0000000..0d98bea --- /dev/null +++ b/linux/src/drivers/net/natsemi.c @@ -0,0 +1,1448 @@ +/* natsemi.c: A Linux PCI Ethernet driver for the NatSemi DP83810 series. */ +/* +	Written/copyright 1999-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL.  License for under other terms may be +	available.  Contact the original author for details. + +	The original author may be reached as becker@scyld.com, or at +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Support information and updates available at +		http://www.scyld.com/network/natsemi.html +	The information and support mailing lists are based at +		http://www.scyld.com/mailman/listinfo/ +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"natsemi.c:v1.17a 8/09/2003  Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/natsemi.html\n"; +/* Updated to recommendations in pci-skeleton v2.11. */ + +/* Automatically extracted configuration info: +probe-func: natsemi_probe +config-in: tristate 'National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI + +c-help-name: National Semiconductor DP8381x series PCI Ethernet support +c-help-symbol: CONFIG_NATSEMI +c-help: This driver is for the National Semiconductor DP83810 series, +c-help: including the 83815 chip. +c-help: Usage information and updates are available from +c-help: http://www.scyld.com/network/natsemi.html +*/ + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   This chip uses a 512 element hash table based on the Ethernet CRC. +   Some chip versions are reported to have unreliable multicast filter +   circuitry.  To work around an observed problem set this value to '0', +   which will immediately switch to Rx-all-multicast. +*/ +static int multicast_filter_limit = 100; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. +   This chip can only receive into aligned buffers, so architectures such +   as the Alpha AXP might benefit from a copy-align. +*/ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   Both 'options[]' and 'full_duplex[]' should exist for driver +   interoperability, however setting full_duplex[] is deprecated. +   The media type is usually passed in 'options[]'. +	The default is autonegotation for speed and duplex. +	This should rarely be overridden. +	Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +	Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +	Use option values 0x20 and 0x200 for forcing full duplex operation. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. +   Understand the implications before changing these settings! +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority. +   Too-large receive rings waste memory and confound network buffer limits. */ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit ring entries actually used, min 4. */ +#define RX_RING_SIZE	32 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. +   Re-autonegotiation may take up to 3 seconds. + */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("National Semiconductor DP83810 series PCI Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, +				 "Non-zero to force full duplex, non-negotiated link " +				 "(deprecated)."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This driver is designed for National Semiconductor DP83815 PCI Ethernet NIC. +It also works with other chips in in the DP83810 series. +The most common board is the Netgear FA311 using the 83815. + +II. Board-specific settings + +This driver requires the PCI interrupt line to be valid. +It honors the EEPROM-set values. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE. +The NatSemi design uses a 'next descriptor' pointer that the driver forms +into a list, thus rings can be arbitrarily sized.  Before changing the +ring sizes you should understand the flow and cache effects of the +full/available/empty hysteresis. + +IIIb/c. Transmit/Receive Structure + +This driver uses a zero-copy receive and transmit scheme. +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers.  When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack.  Buffers consumed this way are replaced by newly allocated +skbuffs in a later phase of receives. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets.  When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine.  Copying also preloads the cache, which is +most useful with small frames. + +A subtle aspect of the operation is that unaligned buffers are not permitted +by the hardware.  Thus the IP header at offset 14 in an ethernet frame isn't +longword aligned for further processing.  On copies frames are put into the +skbuff at an offset of "+2", 16-byte aligning the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +The older dp83810 chips are so uncommon that support is not relevant. +No NatSemi datasheet was publically available at the initial release date, +but the dp83815 has now been published. + +IVb. References + +http://www.scyld.com/expert/100mbps.html +http://www.scyld.com/expert/NWay.html + + +IVc. Errata + +Qustionable multicast filter implementation. +The EEPROM format is obviously the result of a chip bug. +*/ + + + +static void *natsemi_probe1(struct pci_dev *pdev, void *init_dev, +							long ioaddr, int irq, int chip_idx, int find_cnt); +static int power_event(void *dev_instance, int event); +#ifdef USE_IO_OPS +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO  | PCI_ADDR0) +#else +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{"Netgear FA311 (NatSemi DP83815)", +	 { 0x0020100B, 0xffffffff, 0xf3111385, 0xffffffff, }, +	 PCI_IOTYPE, 256, 0}, +	{"NatSemi DP83815", { 0x0020100B, 0xffffffff }, +	 PCI_IOTYPE, 256, 0}, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info natsemi_drv_id = { +	"natsemi", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	natsemi_probe1, power_event }; + +/* Offsets to the device registers. +   Unlike software-only systems, device drivers interact with complex hardware. +   It's not useful to define symbolic names for every register bit in the +   device.  Please do not change these names without good reason. +*/ +enum register_offsets { +	ChipCmd=0x00, ChipConfig=0x04, EECtrl=0x08, PCIBusCfg=0x0C, +	IntrStatus=0x10, IntrMask=0x14, IntrEnable=0x18, +	TxRingPtr=0x20, TxConfig=0x24, +	RxRingPtr=0x30, RxConfig=0x34, ClkRunCtrl=0x3C, +	WOLCmd=0x40, PauseCmd=0x44, RxFilterAddr=0x48, RxFilterData=0x4C, +	BootRomAddr=0x50, BootRomData=0x54, ChipRevReg=0x58, +	StatsCtrl=0x5C, StatsData=0x60, +	RxPktErrs=0x60, RxMissed=0x68, RxCRCErrs=0x64, +	NS_Xcvr_Mgmt = 0x80, NS_MII_BMCR=0x80, NS_MII_BMSR=0x84, +	NS_MII_Advert=0x90, NS_MIILinkPartner=0x94, +}; + +/* Bits in ChipCmd. */ +enum ChipCmdBits { +	ChipReset=0x100, SoftIntr=0x80, RxReset=0x20, TxReset=0x10, +	RxOff=0x08, RxOn=0x04, TxOff=0x02, TxOn=0x01, +}; + +/* Bits in ChipConfig. */ +enum ChipConfigBits { +	CfgLinkGood=0x80000000, CfgFDX=0x20000000, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrRxDone=0x0001, IntrRxIntr=0x0002, IntrRxErr=0x0004, IntrRxEarly=0x0008, +	IntrRxIdle=0x0010, IntrRxOverrun=0x0020, +	IntrTxDone=0x0040, IntrTxIntr=0x0080, IntrTxErr=0x0100, +	IntrTxIdle=0x0200, IntrTxUnderrun=0x0400, +	StatsMax=0x0800, IntrDrv=0x1000, WOLPkt=0x2000, LinkChange=0x4000, +	RxStatusOverrun=0x10000, +	RxResetDone=0x1000000, TxResetDone=0x2000000, +	IntrPCIErr=0x00f00000, +	IntrNormalSummary=0x0251, IntrAbnormalSummary=0xED20, +}; + +/* Bits in the RxMode register. */ +enum rx_mode_bits { +	AcceptErr=0x20, AcceptRunt=0x10, +	AcceptBroadcast=0xC0000000, +	AcceptMulticast=0x00200000, AcceptAllMulticast=0x20000000, +	AcceptAllPhys=0x10000000, AcceptMyPhys=0x08000000, +}; + +/* The Rx and Tx buffer descriptors. */ +/* Note that using only 32 bit fields simplifies conversion to big-endian +   architectures. */ +struct netdev_desc { +	u32 next_desc; +	s32 cmd_status; +	u32 buf_addr; +	u32 software_use; +}; + +/* Bits in network_desc.status */ +enum desc_status_bits { +	DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000, +	DescNoCRC=0x10000000, +	DescPktOK=0x08000000, RxTooLong=0x00400000, +}; + +#define PRIV_ALIGN	15	/* Required alignment mask */ +struct netdev_private { +	/* Descriptor rings first for alignment. */ +	struct netdev_desc rx_ring[RX_RING_SIZE]; +	struct netdev_desc tx_ring[TX_RING_SIZE]; +	struct net_device *next_module;		/* Link for devices of this type. */ +	void *priv_addr;					/* Unaligned address for kfree */ +	const char *product_name; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for later free(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	/* Frequently used values: keep some adjacent for cache effect. */ +	int msg_level; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	long in_interrupt;			/* Word-long for SMP locks. */ +	int max_interrupt_work; +	int intr_enable; +	unsigned int restore_intr_enable:1;	/* Set if temporarily masked.  */ +	unsigned int rx_q_empty:1;			/* Set out-of-skbuffs.  */ + +	struct netdev_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	/* These values keep track of the transceiver/media in use. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	/* Rx filter. */ +	u32 cur_rx_mode; +	u16 rx_filter[32]; +	int multicast_filter_limit; +	/* FIFO and PCI burst thresholds. */ +	int tx_config, rx_config; +	/* MII transceiver section. */ +	u16 advertising;					/* NWay media advertisement */ +}; + +static int  eeprom_read(long ioaddr, int location); +static int  mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, +					   int value); +static int  netdev_open(struct net_device *dev); +static void check_duplex(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct net_device *dev); +static int  rx_ring_fill(struct net_device *dev); +static void init_ring(struct net_device *dev); +static int  start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int  netdev_rx(struct net_device *dev); +static void netdev_error(struct net_device *dev, int intr_status); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int  netdev_close(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_net_dev = NULL; + +#ifndef MODULE +int natsemi_probe(struct net_device *dev) +{ +	if (pci_drv_register(&natsemi_drv_id, dev) < 0) +		return -ENODEV; +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif + +static void *natsemi_probe1(struct pci_dev *pdev, void *init_dev, +							long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct netdev_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; +	int prev_eedata; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	/* Perhaps NETIF_MSG_PROBE */ +	printk(KERN_INFO "%s: %s at 0x%lx, ", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr); + +	/* Work around the dropped serial bit. */ +	prev_eedata = eeprom_read(ioaddr, 6); +	for (i = 0; i < 3; i++) { +		int eedata = eeprom_read(ioaddr, i + 7); +		dev->dev_addr[i*2] = (eedata << 1) + (prev_eedata >> 15); +		dev->dev_addr[i*2+1] = eedata >> 7; +		prev_eedata = eedata; +	} +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	/* Reset the chip to erase previous misconfiguration. */ +	writel(ChipReset, ioaddr + ChipCmd); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_net_dev; +	root_net_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	if (dev->mem_start) +		option = dev->mem_start; + +	/* 0x10/0x20/0x100/0x200 set forced speed&duplex modes. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 0x3ff; +		if (np->default_port & 0x330) { +			np->medialock = 1; +			if (np->msg_level & NETIF_MSG_PROBE) +				printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +					   (option & 0x300 ? 100 : 10), +					   (np->full_duplex ? "full" : "half")); +			writew(((option & 0x300) ? 0x2000 : 0) |	/* 100mbps? */ +				   (np->full_duplex ? 0x0100 : 0), /* Full duplex? */ +				   ioaddr + NS_MII_BMCR); +		} +	} +	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) { +		if (np->msg_level & NETIF_MSG_PROBE) +			printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" +				   " disabled.\n", dev->name); +		np->duplex_lock = 1; +	} + +	/* The chip-specific entries in the device structure. */ +	dev->open = &netdev_open; +	dev->hard_start_xmit = &start_tx; +	dev->stop = &netdev_close; +	dev->get_stats = &get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; + +	/* Override the PME enable from the EEPROM. */ +	writel(0x8000, ioaddr + ClkRunCtrl); + +	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000) { +		u32 chip_config = readl(ioaddr + ChipConfig); +		if (np->msg_level & NETIF_MSG_PROBE) +			printk(KERN_INFO "%s: Transceiver default autonegotiation %s " +				   "10%s %s duplex.\n", +				   dev->name, chip_config & 0x2000 ? "enabled, advertise" +				   : "disabled, force", chip_config & 0x4000 ? "0" : "", +				   chip_config & 0x8000 ? "full" : "half"); +	} +	if (np->msg_level & NETIF_MSG_PROBE) +		printk(KERN_INFO "%s: Transceiver status 0x%4.4x partner %4.4x.\n", +			   dev->name, (int)readl(ioaddr + NS_MII_BMSR), +			   (int)readl(ioaddr + NS_MIILinkPartner)); + +	return dev; +} + + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. +   The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses. +   Update to the code in other drivers for 8/10 bit addresses. +*/ + +/* Delay between EEPROM clock transitions. +   This "delay" forces out buffered PCI writes, which is sufficient to meet +   the timing requirements of most EEPROMs. +*/ +#define eeprom_delay(ee_addr)	readl(ee_addr) + +enum EEPROM_Ctrl_Bits { +	EE_ShiftClk=0x04, EE_DataIn=0x01, EE_ChipSelect=0x08, EE_DataOut=0x02, +}; +#define EE_Write0 (EE_ChipSelect) +#define EE_Write1 (EE_ChipSelect | EE_DataIn) + +/* The EEPROM commands include the preamble. */ +enum EEPROM_Cmds { +	EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), +}; + +static int eeprom_read(long addr, int location) +{ +	int i; +	int retval = 0; +	long ee_addr = addr + EECtrl; +	int read_cmd = location | EE_ReadCmd; +	writel(EE_Write0, ee_addr); + +	/* Shift the read command bits out. */ +	for (i = 10; i >= 0; i--) { +		short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; +		writel(dataval, ee_addr); +		eeprom_delay(ee_addr); +		writel(dataval | EE_ShiftClk, ee_addr); +		eeprom_delay(ee_addr); +	} +	writel(EE_ChipSelect, ee_addr); +	eeprom_delay(ee_addr); + +	for (i = 0; i < 16; i++) { +		writel(EE_ChipSelect | EE_ShiftClk, ee_addr); +		eeprom_delay(ee_addr); +		retval |= (readl(ee_addr) & EE_DataOut) ? 1 << i : 0; +		writel(EE_ChipSelect, ee_addr); +		eeprom_delay(ee_addr); +	} + +	/* Terminate the EEPROM access. */ +	writel(EE_Write0, ee_addr); +	writel(0, ee_addr); +	return retval; +} + +/*  MII transceiver control section. +	The 83815 series has an internal, directly accessable transceiver. +	We present the management registers as if they were MII connected. */ + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ +	if (phy_id == 1 && location < 32) +		return readw(dev->base_addr + NS_Xcvr_Mgmt + (location<<2)); +	else +		return 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, +					   int value) +{ +	if (phy_id == 1 && location < 32) +		writew(value, dev->base_addr + NS_Xcvr_Mgmt + (location<<2)); +} + + +static int netdev_open(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	/* We do not need to reset the '815 chip. */ + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", +			   dev->name, dev->irq); + +	init_ring(dev); + +	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); +	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); + +	for (i = 0; i < 6; i += 2) { +		writel(i, ioaddr + RxFilterAddr); +		writel(dev->dev_addr[i] + (dev->dev_addr[i+1] << 8), +			   ioaddr + RxFilterData); +	} + +	/* Initialize other registers. */ +	/* See the datasheet for this correction. */ +	if (readl(ioaddr + ChipRevReg) == 0x0203) { +		writew(0x0001, ioaddr + 0xCC); +		writew(0x18C9, ioaddr + 0xE4); +		writew(0x0000, ioaddr + 0xFC); +		writew(0x5040, ioaddr + 0xF4); +		writew(0x008C, ioaddr + 0xF8); +	} + +	/* Configure the PCI bus bursts and FIFO thresholds. */ +	/* Configure for standard, in-spec Ethernet. */ + +	if (readl(ioaddr + ChipConfig) & CfgFDX) {	/* Full duplex */ +		np->tx_config = 0xD0801002; +		np->rx_config = 0x10000020; +	} else { +		np->tx_config = 0x10801002; +		np->rx_config = 0x0020; +	} +	if (dev->mtu > 1500) +		np->rx_config |= 0x08000000; +	writel(np->tx_config, ioaddr + TxConfig); +	writel(np->rx_config, ioaddr + RxConfig); + +	if (dev->if_port == 0) +		dev->if_port = np->default_port; + +	np->in_interrupt = 0; + +	check_duplex(dev); +	set_rx_mode(dev); +	netif_start_tx_queue(dev); + +	/* Enable interrupts by setting the interrupt mask. */ +	np->intr_enable = IntrNormalSummary | IntrAbnormalSummary | 0x1f; +	writel(np->intr_enable, ioaddr + IntrMask); +	writel(1, ioaddr + IntrEnable); + +	writel(RxOn | TxOn, ioaddr + ChipCmd); +	writel(4, ioaddr + StatsCtrl);					/* Clear Stats */ + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done netdev_open(), status: %x.\n", +			   dev->name, (int)readl(ioaddr + ChipCmd)); + +	/* Set the timer to check for link beat. */ +	init_timer(&np->timer); +	np->timer.expires = jiffies + 3*HZ; +	np->timer.data = (unsigned long)dev; +	np->timer.function = &netdev_timer;				/* timer handler */ +	add_timer(&np->timer); + +	return 0; +} + +static void check_duplex(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int duplex; + +	if (np->duplex_lock) +		return; +	duplex = readl(ioaddr + ChipConfig) & 0x20000000 ? 1 : 0; +	if (np->full_duplex != duplex) { +		np->full_duplex = duplex; +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Setting %s-duplex based on negotiated link" +				   " capability.\n", dev->name, +				   duplex ? "full" : "half"); +		if (duplex) { +			np->rx_config |= 0x10000000; +			np->tx_config |= 0xC0000000; +		} else { +			np->rx_config &= ~0x10000000; +			np->tx_config &= ~0xC0000000; +		} +		writel(np->tx_config, ioaddr + TxConfig); +		writel(np->rx_config, ioaddr + RxConfig); +	} +} + +static void netdev_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 10*HZ; + +	if (np->msg_level & NETIF_MSG_TIMER) +		printk(KERN_DEBUG "%s: Driver monitor timer tick, status %8.8x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); +	if (np->rx_q_empty) { +		/* Trigger an interrupt to refill. */ +		writel(SoftIntr, ioaddr + ChipCmd); +	} +	/* This will either have a small false-trigger window or will not catch +	   tbusy incorrectly set when the queue is empty. */ +	if (netif_queue_paused(dev)  && +		np->cur_tx - np->dirty_tx > 1  && +		(jiffies - dev->trans_start) > TX_TIMEOUT) { +		tx_timeout(dev); +	} +	check_duplex(dev); +	np->timer.expires = jiffies + next_tick; +	add_timer(&np->timer); +} + +static void tx_timeout(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," +		   " resetting...\n", dev->name, (int)readl(ioaddr + TxRingPtr)); + +	if (np->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)np->rx_ring[i].cmd_status); +		printk("\n"KERN_DEBUG"  Tx ring %p: ", np->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %4.4x", np->tx_ring[i].cmd_status); +		printk("\n"); +	} + +	/* Reinitialize the hardware here. */ +	/* Stop and restart the chip's Tx processes . */ + +	/* Trigger an immediate transmit demand. */ + +	dev->trans_start = jiffies; +	np->stats.tx_errors++; +	return; +} + +/* Refill the Rx ring buffers, returning non-zero if not full. */ +static int rx_ring_fill(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned int entry; + +	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { +		entry = np->dirty_rx % RX_RING_SIZE; +		if (np->rx_skbuff[entry] == NULL) { +			struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); +			np->rx_skbuff[entry] = skb; +			if (skb == NULL) +				return 1;				/* Better luck next time. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			np->rx_ring[entry].buf_addr = virt_to_le32desc(skb->tail); +		} +		np->rx_ring[entry].cmd_status = cpu_to_le32(DescIntr | np->rx_buf_sz); +	} +	return 0; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	np->tx_full = 0; +	np->cur_rx = np->cur_tx = 0; +	np->dirty_rx = np->dirty_tx = 0; + +	/* MAX(PKT_BUF_SZ, dev->mtu + 8); */ +	/* I know you _want_ to change this without understanding it.  Don't. */ +	np->rx_buf_sz = (dev->mtu <= 1532 ? PKT_BUF_SZ : dev->mtu + 8); +	np->rx_head_desc = &np->rx_ring[0]; + +	/* Initialize all Rx descriptors. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]); +		np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn); +		np->rx_skbuff[i] = 0; +	} +	/* Mark the last entry as wrapping the ring. */ +	np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]); + +	for (i = 0; i < TX_RING_SIZE; i++) { +		np->tx_skbuff[i] = 0; +		np->tx_ring[i].next_desc = virt_to_le32desc(&np->tx_ring[i+1]); +		np->tx_ring[i].cmd_status = 0; +	} +	np->tx_ring[i-1].next_desc = virt_to_le32desc(&np->tx_ring[0]); + +	/* Fill in the Rx buffers. +	   Allocation failure just leaves a "negative" np->dirty_rx. */ +	np->dirty_rx = (unsigned int)(0 - RX_RING_SIZE); +	rx_ring_fill(dev); + +	return; +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned int entry; + +	/* Block a timer-based transmit from overlapping.  This happens when +	   packets are presumed lost, and we use this check the Tx status. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tx_timeout(dev); +		return 1; +	} + +	/* Note: Ordering is important here, set the field with the +	   "ownership" bit last, and only then increment cur_tx. +	   No spinlock is needed for either Tx or Rx. +	*/ + +	/* Calculate the next Tx descriptor entry. */ +	entry = np->cur_tx % TX_RING_SIZE; + +	np->tx_skbuff[entry] = skb; + +	np->tx_ring[entry].buf_addr = virt_to_le32desc(skb->data); +	np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn|DescIntr | skb->len); +	np->cur_tx++; + +	/* For some architectures explicitly flushing np->tx_ring,sizeof(tx_ring) +	   and skb->data,skb->len improves performance. */ + +	if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) { +		np->tx_full = 1; +		/* Check for a just-cleared queue. */ +		if (np->cur_tx - (volatile unsigned int)np->dirty_tx +			< TX_QUEUE_LEN - 4) { +			np->tx_full = 0; +			netif_unpause_tx_queue(dev); +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ +	/* Wake the potentially-idle transmit channel. */ +	writel(TxOn, dev->base_addr + ChipCmd); + +	dev->trans_start = jiffies; + +	if (np->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", +			   dev->name, np->cur_tx, entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct netdev_private *np; +	long ioaddr; +	int boguscnt; + +#ifndef final_version			/* Can never occur. */ +	if (dev == NULL) { +		printk (KERN_ERR "Netdev interrupt handler(): IRQ %d for unknown " +				"device.\n", irq); +		return; +	} +#endif + +	ioaddr = dev->base_addr; +	np = (struct netdev_private *)dev->priv; +	boguscnt = np->max_interrupt_work; + +	do { +		u32 intr_status = readl(ioaddr + IntrStatus); + +		if (intr_status == 0 || intr_status == 0xffffffff) +			break; + +		/* Acknowledge all of the current interrupt sources ASAP. +		   Nominally the read above accomplishes this, but... */ +		writel(intr_status & 0x001ffff, ioaddr + IntrStatus); + +		if (np->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt, status %8.8x.\n", +				   dev->name, intr_status); + +		if (intr_status & (IntrRxDone | IntrRxIntr)) { +			netdev_rx(dev); +			np->rx_q_empty = rx_ring_fill(dev); +		} + +		if (intr_status & (IntrRxIdle | IntrDrv)) { +			unsigned int old_dirty_rx = np->dirty_rx; +			if (rx_ring_fill(dev) == 0) +				np->rx_q_empty = 0; +			/* Restart Rx engine iff we did add a buffer. */ +			if (np->dirty_rx != old_dirty_rx) +				writel(RxOn, dev->base_addr + ChipCmd); +		} + +		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { +			int entry = np->dirty_tx % TX_RING_SIZE; +			int tx_status = le32_to_cpu(np->tx_ring[entry].cmd_status); +			if (tx_status & DescOwn) +				break; +			if (np->msg_level & NETIF_MSG_TX_DONE) +				printk(KERN_DEBUG "%s: Transmit done, Tx status %8.8x.\n", +					   dev->name, tx_status); +			if (tx_status & 0x08000000) { +				np->stats.tx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +				np->stats.tx_bytes += np->tx_skbuff[entry]->len; +#endif +			} else {			/* Various Tx errors */ +				if (np->msg_level & NETIF_MSG_TX_ERR) +					printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", +						   dev->name, tx_status); +				if (tx_status & 0x04010000) np->stats.tx_aborted_errors++; +				if (tx_status & 0x02000000) np->stats.tx_fifo_errors++; +				if (tx_status & 0x01000000) np->stats.tx_carrier_errors++; +				if (tx_status & 0x00200000) np->stats.tx_window_errors++; +				np->stats.tx_errors++; +			} +			/* Free the original skb. */ +			dev_free_skb_irq(np->tx_skbuff[entry]); +			np->tx_skbuff[entry] = 0; +		} +		/* Note the 4 slot hysteresis to mark the queue non-full. */ +		if (np->tx_full +			&& np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { +			/* The ring is no longer full, allow new TX entries. */ +			np->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & IntrAbnormalSummary) +			netdev_error(dev, intr_status); + +		if (--boguscnt < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", +				   dev->name, intr_status); +			np->restore_intr_enable = 1; +			break; +		} +	} while (1); + +	if (np->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); + +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int entry = np->cur_rx % RX_RING_SIZE; +	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; +	s32 desc_status = le32_to_cpu(np->rx_head_desc->cmd_status); + +	/* If the driver owns the next entry it's a new packet. Send it up. */ +	while (desc_status < 0) {		/* e.g. & DescOwn */ +		if (np->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  In netdev_rx() entry %d status was %8.8x.\n", +				   entry, desc_status); +		if (--boguscnt < 0) +			break; +		if ((desc_status & (DescMore|DescPktOK|RxTooLong)) != DescPktOK) { +			if (desc_status & DescMore) { +				printk(KERN_WARNING "%s: Oversized(?) Ethernet frame spanned " +					   "multiple buffers, entry %#x status %x.\n", +					   dev->name, np->cur_rx, desc_status); +				np->stats.rx_length_errors++; +			} else { +				/* There was a error. */ +				if (np->msg_level & NETIF_MSG_RX_ERR) +					printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n", +						   desc_status); +				np->stats.rx_errors++; +				if (desc_status & 0x06000000) np->stats.rx_over_errors++; +				if (desc_status & 0x00600000) np->stats.rx_length_errors++; +				if (desc_status & 0x00140000) np->stats.rx_frame_errors++; +				if (desc_status & 0x00080000) np->stats.rx_crc_errors++; +			} +		} else { +			struct sk_buff *skb; +			int pkt_len = (desc_status & 0x0fff) - 4;	/* Omit CRC size. */ +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < np->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +#if defined(HAS_IP_COPYSUM)  ||  (LINUX_VERSION_CODE >= 0x20100) +				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +			} else { +				skb_put(skb = np->rx_skbuff[entry], pkt_len); +				np->rx_skbuff[entry] = NULL; +			} +			skb->protocol = eth_type_trans(skb, dev); +			/* W/ hardware checksum: skb->ip_summed = CHECKSUM_UNNECESSARY; */ +			netif_rx(skb); +			dev->last_rx = jiffies; +			np->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			np->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++np->cur_rx) % RX_RING_SIZE; +		np->rx_head_desc = &np->rx_ring[entry]; +		desc_status = le32_to_cpu(np->rx_head_desc->cmd_status); +	} + +	/* Refill is now done in the main interrupt loop. */ +	return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (intr_status & LinkChange) { +		int chip_config = readl(ioaddr + ChipConfig); +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_NOTICE "%s: Link changed: Autonegotiation advertising" +				   " %4.4x  partner %4.4x.\n", dev->name, +				   (int)readw(ioaddr + NS_MII_Advert), +				   (int)readw(ioaddr + NS_MIILinkPartner)); +		if (chip_config & CfgLinkGood) +			netif_link_up(dev); +		else +			netif_link_down(dev); +		check_duplex(dev); +	} +	if (intr_status & StatsMax) { +		get_stats(dev); +	} +	if (intr_status & IntrTxUnderrun) { +		/* Increase the Tx threshold, 32 byte units. */ +		if ((np->tx_config & 0x3f) < 62) +			np->tx_config += 2;			/* +64 bytes */ +		writel(np->tx_config, ioaddr + TxConfig); +	} +	if (intr_status & WOLPkt) { +		int wol_status = readl(ioaddr + WOLCmd); +		printk(KERN_NOTICE "%s: Link wake-up event %8.8x", +			   dev->name, wol_status); +	} +	if (intr_status & (RxStatusOverrun | IntrRxOverrun)) { +		if (np->msg_level & NETIF_MSG_DRV) +			printk(KERN_ERR "%s: Rx overflow! ns815 %8.8x.\n", +				   dev->name, intr_status); +		np->stats.rx_fifo_errors++; +	} +	if (intr_status & ~(LinkChange|StatsMax|RxResetDone|TxResetDone| +						RxStatusOverrun|0xA7ff)) { +		if (np->msg_level & NETIF_MSG_DRV) +			printk(KERN_ERR "%s: Something Wicked happened! natsemi %8.8x.\n", +				   dev->name, intr_status); +	} +	/* Hmmmmm, it's not clear how to recover from PCI faults. */ +	if (intr_status & IntrPCIErr) { +		np->stats.tx_fifo_errors++; +		np->stats.rx_fifo_errors++; +	} +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int crc_errs = readl(ioaddr + RxCRCErrs); + +	if (crc_errs != 0xffffffff) { +		/* We need not lock this segment of code for SMP. +		   There is no atomic-add vulnerability for most CPUs, +		   and statistics are non-critical. */ +		/* The chip only need report frame silently dropped. */ +		np->stats.rx_crc_errors	+= crc_errs; +		np->stats.rx_missed_errors += readl(ioaddr + RxMissed); +	} + +	return &np->stats; +} + +/* The big-endian AUTODIN II ethernet CRC calculations. +   See ns820.c for how to fill the table on new chips. + */ +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ +	int crc = -1; + +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 0; bit < 8; bit++, current_octet >>= 1) +			crc = (crc << 1) ^ +				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u8 mc_filter[64];			/* Multicast hash filter */ +	u32 rx_mode; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		rx_mode = AcceptBroadcast | AcceptAllMulticast | AcceptAllPhys +			| AcceptMyPhys; +	} else if ((dev->mc_count > np->multicast_filter_limit) +			   ||  (dev->flags & IFF_ALLMULTI)) { +		rx_mode = AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys; +	} else { +		struct dev_mc_list *mclist; +		int i; +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			int filterbit = ether_crc(ETH_ALEN, mclist->dmi_addr); +			set_bit(filterbit & 0x1ff, mc_filter); +			if (np->msg_level & NETIF_MSG_RXFILTER) +				printk(KERN_INFO "%s: Added filter for %2.2x:%2.2x:%2.2x:" +					   "%2.2x:%2.2x:%2.2x  crc %8.8x bit %d.\n", dev->name, +					   mclist->dmi_addr[0], mclist->dmi_addr[1], +					   mclist->dmi_addr[2], mclist->dmi_addr[3], +					   mclist->dmi_addr[4], mclist->dmi_addr[5], +					   filterbit, filterbit & 0x1ff); +		} +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; +		for (i = 0; i < 64; i += 2) { +			u16 filterword = (mc_filter[i+1]<<8) + mc_filter[i]; +			if (filterword != np->rx_filter[i>>2]) { +				writel(0x200 + i, ioaddr + RxFilterAddr); +				writel(filterword, ioaddr + RxFilterData); +				np->rx_filter[i>>2] = filterword; +			} +		} +	} +	writel(rx_mode, ioaddr + RxFilterAddr); +	np->cur_rx_mode = rx_mode; +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = 1; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == 1) { +			u16 miireg = data[1] & 0x1f; +			u16 value = data[2]; +			mdio_write(dev, 1, miireg, value); +			switch (miireg) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				np->duplex_lock = (value & 0x9000) ? 0 : 1; +				if (np->duplex_lock) +					np->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: np->advertising = value; break; +			} +		} +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static int netdev_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x " +			   "Int %2.2x.\n", +			   dev->name, (int)readl(ioaddr + ChipCmd), +			   (int)readl(ioaddr + IntrStatus)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); +	} + +	/* We don't want the timer to re-start anything. */ +	del_timer(&np->timer); + +	/* Disable interrupts using the mask. */ +	writel(0, ioaddr + IntrMask); +	writel(0, ioaddr + IntrEnable); +	writel(2, ioaddr + StatsCtrl);					/* Freeze Stats */ + +	/* Stop the chip's Tx and Rx processes. */ +	writel(RxOff | TxOff, ioaddr + ChipCmd); + +	get_stats(dev); + +#ifdef __i386__ +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(np->tx_ring)); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" #%d desc. %8.8x %8.8x.\n", +				   i, np->tx_ring[i].cmd_status, (u32)np->tx_ring[i].buf_addr); +		printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n", +			   (int)virt_to_bus(np->rx_ring)); +		for (i = 0; i < RX_RING_SIZE; i++) { +			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x\n", +				   i, np->rx_ring[i].cmd_status, (u32)np->rx_ring[i].buf_addr); +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].cmd_status = 0; +		np->rx_ring[i].buf_addr = 0xBADF00D0; /* An invalid address. */ +		if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			np->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(np->rx_skbuff[i]); +		} +		np->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (np->tx_skbuff[i]) +			dev_free_skb(np->tx_skbuff[i]); +		np->tx_skbuff[i] = 0; +	} + +#if 0 +	writel(0x0200, ioaddr + ChipConfig); /* Power down Xcvr. */ +#endif + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static int power_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		/* Disable interrupts, freeze stats, stop Tx and Rx. */ +		writel(0, ioaddr + IntrEnable); +		writel(2, ioaddr + StatsCtrl); +		writel(RxOff | TxOff, ioaddr + ChipCmd); +		break; +	case DRV_RESUME: +		/* This is incomplete: the open() actions should be repeated. */ +		set_rx_mode(dev); +		writel(np->intr_enable, ioaddr + IntrEnable); +		writel(1, ioaddr + IntrEnable); +		writel(RxOn | TxOn, ioaddr + ChipCmd); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			/* Some, but not all, kernel versions close automatically. */ +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size); +		for (devp = &root_net_dev; *devp; devp = next) { +			next = &((struct netdev_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	} + +	return 0; +} + + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +#ifdef CARDBUS +	register_driver(ðerdev_ops); +	return 0; +#else +	return pci_drv_register(&natsemi_drv_id, NULL); +#endif +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +#ifdef CARDBUS +	unregister_driver(ðerdev_ops); +#else +	pci_drv_unregister(&natsemi_drv_id); +#endif + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_net_dev) { +		struct netdev_private *np = (void *)(root_net_dev->priv); +		unregister_netdev(root_net_dev); +		iounmap((char *)root_net_dev->base_addr); +		next_dev = np->next_module; +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_net_dev); +		root_net_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` natsemi.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c natsemi.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c natsemi.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/ne.c b/linux/src/drivers/net/ne.c new file mode 100644 index 0000000..ea2f929 --- /dev/null +++ b/linux/src/drivers/net/ne.c @@ -0,0 +1,812 @@ +/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */ +/* +    Written 1992-94 by Donald Becker. + +    Copyright 1993 United States Government as represented by the +    Director, National Security Agency. + +    This software may be used and distributed according to the terms +    of the GNU Public License, incorporated herein by reference. + +    The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +    Center of Excellence in Space Data and Information Sciences +        Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +    This driver should work with many programmed-I/O 8390-based ethernet +    boards.  Currently it supports the NE1000, NE2000, many clones, +    and some Cabletron products. + +    Changelog: + +    Paul Gortmaker	: use ENISR_RDC to monitor Tx PIO uploads, made +			  sanity checks and bad clone support optional. +    Paul Gortmaker	: new reset code, reset card after probe at boot. +    Paul Gortmaker	: multiple card support for module users. +    Paul Gortmaker	: Support for PCI ne2k clones, similar to lance.c +    Paul Gortmaker	: Allow users with bad cards to avoid full probe. +    Paul Gortmaker	: PCI probe changes, more PCI cards supported. + +*/ + +/* Routines for the NatSemi-based designs (NE[12]000). */ + +static const char *version = +    "ne.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <asm/system.h> +#include <asm/io.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include "8390.h" + +/* Some defines that people can play with if so inclined. */ + +/* Do we support clones that don't adhere to 14,15 of the SAprom ? */ +#define SUPPORT_NE_BAD_CLONES + +/* Do we perform extra sanity checks on stuff ? */ +/* #define NE_SANITY_CHECK */ + +/* Do we implement the read before write bugfix ? */ +/* #define NE_RW_BUGFIX */ + +/* Do we have a non std. amount of memory? (in units of 256 byte pages) */ +/* #define PACKETBUF_MEMSIZE	0x40 */ + +#if defined(HAVE_DEVLIST) || !defined(MODULE) +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int netcard_portlist[] = +{ 0x300, 0x280, 0x320, 0x340, 0x360, 0}; +#endif /* defined(HAVE_DEVLIST) || !defined(MODULE) */ + +#ifdef CONFIG_PCI +/* Ack! People are making PCI ne2000 clones! Oh the horror, the horror... */ +static struct { unsigned short vendor, dev_id;} +pci_clone_list[] = { +	{PCI_VENDOR_ID_REALTEK,		PCI_DEVICE_ID_REALTEK_8029}, +	{PCI_VENDOR_ID_WINBOND2,	PCI_DEVICE_ID_WINBOND2_89C940}, +	{PCI_VENDOR_ID_COMPEX,		PCI_DEVICE_ID_COMPEX_RL2000}, +	{PCI_VENDOR_ID_KTI,		PCI_DEVICE_ID_KTI_ET32P2}, +	{PCI_VENDOR_ID_NETVIN,		PCI_DEVICE_ID_NETVIN_NV5000SC}, +	{PCI_VENDOR_ID_VIA,		PCI_DEVICE_ID_VIA_82C926}, +	{0,} +}; +#endif + +#ifdef SUPPORT_NE_BAD_CLONES +/* A list of bad clones that we none-the-less recognize. */ +static struct { const char *name8, *name16; unsigned char SAprefix[4];} +bad_clone_list[] = { +    {"DE100", "DE200", {0x00, 0xDE, 0x01,}}, +    {"DE120", "DE220", {0x00, 0x80, 0xc8,}}, +    {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh?  */ +    {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}}, +    {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */ +    {"NN1000", "NN2000",  {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */ +    {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}},  /* Outlaw 4-Dimension cards. */ +    {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */ +    {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */ +    {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */ +    {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */ +    {"RealTek 8029", "RealTek 8029", {0x00, 0x3e, 0x4d}}, /* RealTek PCI cards */ +    {0,} +}; +#endif + +/* ---- No user-serviceable parts below ---- */ + +#define NE_BASE	 (dev->base_addr) +#define NE_CMD	 	0x00 +#define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */ +#define NE_RESET	0x1f	/* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT	0x20 + +#define NE1SM_START_PG	0x20	/* First page of TX buffer */ +#define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */ +#define NESM_START_PG	0x40	/* First page of TX buffer */ +#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */ + +/* Non-zero only if the current card is a PCI with BIOS-set IRQ. */ +static unsigned char pci_irq_line = 0; + +int ne_probe(struct device *dev); +#ifdef CONFIG_PCI +static int ne_probe_pci(struct device *dev); +#endif +static int ne_probe1(struct device *dev, int ioaddr); + +static int ne_open(struct device *dev); +static int ne_close(struct device *dev); + +static void ne_reset_8390(struct device *dev); +static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +			  int ring_page); +static void ne_block_input(struct device *dev, int count, +			  struct sk_buff *skb, int ring_offset); +static void ne_block_output(struct device *dev, const int count, +		const unsigned char *buf, const int start_page); + + +/*  Probe for various non-shared-memory ethercards. + +   NEx000-clone boards have a Station Address PROM (SAPROM) in the packet +   buffer memory space.  NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of +   the SAPROM, while other supposed NE2000 clones must be detected by their +   SA prefix. + +   Reading the SAPROM from a word-wide card with the 8390 set in byte-wide +   mode results in doubled values, which can be detected and compensated for. + +   The probe is also responsible for initializing the card and filling +   in the 'dev' and 'ei_status' structures. + +   We use the minimum memory size for some ethercard product lines, iff we can't +   distinguish models.  You can increase the packet buffer size by setting +   PACKETBUF_MEMSIZE.  Reported Cabletron packet buffer locations are: +	E1010   starts at 0x100 and ends at 0x2000. +	E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory") +	E2010	 starts at 0x100 and ends at 0x4000. +	E2010-x starts at 0x100 and ends at 0xffff.  */ + +#ifdef HAVE_DEVLIST +struct netdev_entry netcard_drv = +{"ne", ne_probe1, NE_IO_EXTENT, netcard_portlist}; +#else + +/*  Note that this probe only picks up one card at a time, even for multiple +    PCI ne2k cards. Use "ether=0,0,eth1" if you have a second PCI ne2k card. +    This keeps things consistent regardless of the bus type of the card. */ + +int ne_probe(struct device *dev) +{ +#ifndef MODULE +    int i; +#endif /* MODULE */ +    int base_addr = dev ? dev->base_addr : 0; + +    /* First check any supplied i/o locations. User knows best. <cough> */ +    if (base_addr > 0x1ff)	/* Check a single specified location. */ +	return ne_probe1(dev, base_addr); +    else if (base_addr != 0)	/* Don't probe at all. */ +	return ENXIO; + +#ifdef CONFIG_PCI +    /* Then look for any installed PCI clones */ +    if (pcibios_present() && (ne_probe_pci(dev) == 0)) +  return 0; +#endif + +#ifndef MODULE +    /* Last resort. The semi-risky ISA auto-probe. */ +    for (i = 0; netcard_portlist[i]; i++) { +	int ioaddr = netcard_portlist[i]; +	if (check_region(ioaddr, NE_IO_EXTENT)) +	    continue; +	if (ne_probe1(dev, ioaddr) == 0) +	    return 0; +    } +#endif + +    return ENODEV; +} +#endif + +#ifdef CONFIG_PCI +static int ne_probe_pci(struct device *dev) +{ +	int i; + +	for (i = 0; pci_clone_list[i].vendor != 0; i++) { +		unsigned char pci_bus, pci_device_fn; +		unsigned int pci_ioaddr; +		u16 pci_command, new_command; +		int pci_index; +		 +		for (pci_index = 0; pci_index < 8; pci_index++) { +			if (pcibios_find_device (pci_clone_list[i].vendor, +					pci_clone_list[i].dev_id, pci_index, +					&pci_bus, &pci_device_fn) != 0) +				break;	/* No more of these type of cards */ +			pcibios_read_config_dword(pci_bus, pci_device_fn, +					PCI_BASE_ADDRESS_0, &pci_ioaddr); +			/* Strip the I/O address out of the returned value */ +			pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK; +			/* Avoid already found cards from previous calls */ +			if (check_region(pci_ioaddr, NE_IO_EXTENT)) +				continue; +			pcibios_read_config_byte(pci_bus, pci_device_fn, +					PCI_INTERRUPT_LINE, &pci_irq_line); +			break;	/* Beauty -- got a valid card. */ +		} +		if (pci_irq_line == 0) continue;	/* Try next PCI ID */ +		printk("ne.c: PCI BIOS reports NE 2000 clone at i/o %#x, irq %d.\n", +				pci_ioaddr, pci_irq_line); + +		pcibios_read_config_word(pci_bus, pci_device_fn,  +					PCI_COMMAND, &pci_command); + +		/* Activate the card: fix for brain-damaged Win98 BIOSes. */ +		new_command = pci_command | PCI_COMMAND_IO; +		if (pci_command != new_command) { +			printk(KERN_INFO "  The PCI BIOS has not enabled this" +				" NE2k clone!  Updating PCI command %4.4x->%4.4x.\n", +					pci_command, new_command); +			pcibios_write_config_word(pci_bus, pci_device_fn, +					PCI_COMMAND, new_command); +		} + +		if (ne_probe1(dev, pci_ioaddr) != 0) {	/* Shouldn't happen. */ +			printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr); +			pci_irq_line = 0; +			return -ENXIO; +		} +		pci_irq_line = 0; +		return 0; +	} +	return -ENODEV; +} +#endif  /* CONFIG_PCI */ + +static int ne_probe1(struct device *dev, int ioaddr) +{ +    int i; +    unsigned char SA_prom[32]; +    int wordlength = 2; +    const char *name = NULL; +    int start_page, stop_page; +    int neX000, ctron, bad_card; +    int reg0 = inb_p(ioaddr); +    static unsigned version_printed = 0; + +    if (reg0 == 0xFF) +	return ENODEV; + +    /* Do a preliminary verification that we have a 8390. */ +    {	int regd; +	outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); +	regd = inb_p(ioaddr + 0x0d); +	outb_p(0xff, ioaddr + 0x0d); +	outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); +	inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ +	if (inb_p(ioaddr + EN0_COUNTER0) != 0) { +	    outb_p(reg0, ioaddr); +	    outb_p(regd, ioaddr + 0x0d);	/* Restore the old values. */ +	    return ENODEV; +	} +    } + +    /* We should have a "dev" from Space.c or the static module table. */ +    if (dev == NULL) { +	printk(KERN_ERR "ne.c: Passed a NULL device.\n"); +	dev = init_etherdev(0, 0); +    } + +    if (ei_debug  &&  version_printed++ == 0) +	printk("%s", version); + +    printk("NE*000 ethercard probe at %#3x:", ioaddr); + +    /* A user with a poor card that fails to ack the reset, or that +       does not have a valid 0x57,0x57 signature can still use this +       without having to recompile. Specifying an i/o address along +       with an otherwise unused dev->mem_end value of "0xBAD" will  +       cause the driver to skip these parts of the probe. */ + +    bad_card = ((dev->base_addr != 0) && (dev->mem_end == 0xbad)); + +    /* Reset card. Who knows what dain-bramaged state it was left in. */ +    {	unsigned long reset_start_time = jiffies; + +	/* DON'T change these to inb_p/outb_p or reset will fail on clones. */ +	outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); + +	while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0) +		if (jiffies - reset_start_time > 2*HZ/100) { +			if (bad_card) { +				printk(" (warning: no reset ack)"); +				break; +			} else { +				printk(" not found (no reset ack).\n"); +				return ENODEV; +			} +		} + +	outb_p(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */ +    } + +    /* Read the 16 bytes of station address PROM. +       We must first initialize registers, similar to NS8390_init(eifdev, 0). +       We can't reliably read the SAPROM address without this. +       (I learned the hard way!). */ +    { +	struct {unsigned char value, offset; } program_seq[] = { +	    {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ +	    {0x48,	EN0_DCFG},	/* Set byte-wide (0x48) access. */ +	    {0x00,	EN0_RCNTLO},	/* Clear the count regs. */ +	    {0x00,	EN0_RCNTHI}, +	    {0x00,	EN0_IMR},	/* Mask completion irq. */ +	    {0xFF,	EN0_ISR}, +	    {E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */ +	    {E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */ +	    {32,	EN0_RCNTLO}, +	    {0x00,	EN0_RCNTHI}, +	    {0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */ +	    {0x00,	EN0_RSARHI}, +	    {E8390_RREAD+E8390_START, E8390_CMD}, +	}; +	for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) +	    outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); + +    } +    for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) { +	SA_prom[i] = inb(ioaddr + NE_DATAPORT); +	SA_prom[i+1] = inb(ioaddr + NE_DATAPORT); +	if (SA_prom[i] != SA_prom[i+1]) +	    wordlength = 1; +    } + +    /*	At this point, wordlength *only* tells us if the SA_prom is doubled +	up or not because some broken PCI cards don't respect the byte-wide +	request in program_seq above, and hence don't have doubled up values.  +	These broken cards would otherwise be detected as an ne1000.  */ + +    if (wordlength == 2) +	for (i = 0; i < 16; i++) +		SA_prom[i] = SA_prom[i+i]; +     +    if (pci_irq_line || ioaddr >= 0x400) +	wordlength = 2;		/* Catch broken PCI cards mentioned above. */ + +    if (wordlength == 2) { +	/* We must set the 8390 for word mode. */ +	outb_p(0x49, ioaddr + EN0_DCFG); +	start_page = NESM_START_PG; +	stop_page = NESM_STOP_PG; +    } else { +	start_page = NE1SM_START_PG; +	stop_page = NE1SM_STOP_PG; +    } + +    neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57); +    ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d); + +    /* Set up the rest of the parameters. */ +    if (neX000 || bad_card) { +	name = (wordlength == 2) ? "NE2000" : "NE1000"; +    } else if (ctron) { +	name = (wordlength == 2) ? "Ctron-8" : "Ctron-16"; +	start_page = 0x01; +	stop_page = (wordlength == 2) ? 0x40 : 0x20; +    } else { +#ifdef SUPPORT_NE_BAD_CLONES +	/* Ack!  Well, there might be a *bad* NE*000 clone there. +	   Check for total bogus addresses. */ +	for (i = 0; bad_clone_list[i].name8; i++) { +	    if (SA_prom[0] == bad_clone_list[i].SAprefix[0] && +		SA_prom[1] == bad_clone_list[i].SAprefix[1] && +		SA_prom[2] == bad_clone_list[i].SAprefix[2]) { +		if (wordlength == 2) { +		    name = bad_clone_list[i].name16; +		} else { +		    name = bad_clone_list[i].name8; +		} +		break; +	    } +	} +	if (bad_clone_list[i].name8 == NULL) { +	    printk(" not found (invalid signature %2.2x %2.2x).\n", +		   SA_prom[14], SA_prom[15]); +	    return ENXIO; +	} +#else +	printk(" not found.\n"); +	return ENXIO; +#endif + +    } + +    if (pci_irq_line) +	dev->irq = pci_irq_line; + +    if (dev->irq < 2) { +	autoirq_setup(0); +	outb_p(0x50, ioaddr + EN0_IMR);	/* Enable one interrupt. */ +	outb_p(0x00, ioaddr + EN0_RCNTLO); +	outb_p(0x00, ioaddr + EN0_RCNTHI); +	outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */ +	outb_p(0x00, ioaddr + EN0_IMR); 		/* Mask it again. */ +	dev->irq = autoirq_report(0); +	if (ei_debug > 2) +	    printk(" autoirq is %d\n", dev->irq); +    } else if (dev->irq == 2) +	/* Fixup for users that don't know that IRQ 2 is really IRQ 9, +	   or don't know which one to set. */ +	dev->irq = 9; + +    if (! dev->irq) { +	printk(" failed to detect IRQ line.\n"); +	return EAGAIN; +    } +     +    /* Snarf the interrupt now.  There's no point in waiting since we cannot +       share (with ISA cards) and the board will usually be enabled. */ +    { +	int irqval = request_irq(dev->irq, ei_interrupt, +			pci_irq_line ? SA_SHIRQ : 0, name, dev); +	if (irqval) { +	    printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval); +	    return EAGAIN; +	} +    } + +    dev->base_addr = ioaddr; + +    /* Allocate dev->priv and fill in 8390 specific dev fields. */ +    if (ethdev_init(dev)) { +	printk (" unable to get memory for dev->priv.\n"); +	free_irq(dev->irq, NULL); +	return -ENOMEM; +    } +  +    request_region(ioaddr, NE_IO_EXTENT, name); + +    for(i = 0; i < ETHER_ADDR_LEN; i++) { +	printk(" %2.2x", SA_prom[i]); +	dev->dev_addr[i] = SA_prom[i]; +    } + +    printk("\n%s: %s found at %#x, using IRQ %d.\n", +	   dev->name, name, ioaddr, dev->irq); + +    ei_status.name = name; +    ei_status.tx_start_page = start_page; +    ei_status.stop_page = stop_page; +    ei_status.word16 = (wordlength == 2); + +    ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE +    /* Allow the packet buffer size to be overridden by know-it-alls. */ +    ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + +    ei_status.reset_8390 = &ne_reset_8390; +    ei_status.block_input = &ne_block_input; +    ei_status.block_output = &ne_block_output; +    ei_status.get_8390_hdr = &ne_get_8390_hdr; +    dev->open = &ne_open; +    dev->stop = &ne_close; +    NS8390_init(dev, 0); +    return 0; +} + +static int +ne_open(struct device *dev) +{ +    ei_open(dev); +    MOD_INC_USE_COUNT; +    return 0; +} + +static int +ne_close(struct device *dev) +{ +    if (ei_debug > 1) +	printk("%s: Shutting down ethercard.\n", dev->name); +    ei_close(dev); +    MOD_DEC_USE_COUNT; +    return 0; +} + +/* Hard reset the card.  This used to pause for the same period that a +   8390 reset command required, but that shouldn't be necessary. */ +static void +ne_reset_8390(struct device *dev) +{ +    unsigned long reset_start_time = jiffies; + +    if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies); + +    /* DON'T change these to inb_p/outb_p or reset will fail on clones. */ +    outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); + +    ei_status.txing = 0; +    ei_status.dmaing = 0; + +    /* This check _should_not_ be necessary, omit eventually. */ +    while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) +	if (jiffies - reset_start_time > 2*HZ/100) { +	    printk("%s: ne_reset_8390() did not complete.\n", dev->name); +	    break; +	} +    outb_p(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but +   we don't need to be concerned with ring wrap as the header will be at +   the start of a page, so we optimize accordingly. */ + +static void +ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + +    int nic_base = dev->base_addr; + +    /* This *shouldn't* happen. If it does, it's the last thing you'll see */ +    if (ei_status.dmaing) { +	printk("%s: DMAing conflict in ne_get_8390_hdr " +	   "[DMAstat:%d][irqlock:%d][intr:%d].\n", +	   dev->name, ei_status.dmaing, ei_status.irqlock, +	   dev->interrupt); +	return; +    } + +    ei_status.dmaing |= 0x01; +    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); +    outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); +    outb_p(0, nic_base + EN0_RCNTHI); +    outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */ +    outb_p(ring_page, nic_base + EN0_RSARHI); +    outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); + +    if (ei_status.word16) +	insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); +    else +	insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)); + +    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ +    ei_status.dmaing &= ~0x01; +} + +/* Block input and output, similar to the Crynwr packet driver.  If you +   are porting to a new ethercard, look at the packet driver source for hints. +   The NEx000 doesn't share the on-board packet memory -- you have to put +   the packet out through the "remote DMA" dataport using outb. */ + +static void +ne_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +#ifdef NE_SANITY_CHECK +    int xfer_count = count; +#endif +    int nic_base = dev->base_addr; +    char *buf = skb->data; + +    /* This *shouldn't* happen. If it does, it's the last thing you'll see */ +    if (ei_status.dmaing) { +	printk("%s: DMAing conflict in ne_block_input " +	   "[DMAstat:%d][irqlock:%d][intr:%d].\n", +	   dev->name, ei_status.dmaing, ei_status.irqlock, +	   dev->interrupt); +	return; +    } +    ei_status.dmaing |= 0x01; +    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); +    outb_p(count & 0xff, nic_base + EN0_RCNTLO); +    outb_p(count >> 8, nic_base + EN0_RCNTHI); +    outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); +    outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); +    outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); +    if (ei_status.word16) { +      insw(NE_BASE + NE_DATAPORT,buf,count>>1); +      if (count & 0x01) { +	buf[count-1] = inb(NE_BASE + NE_DATAPORT); +#ifdef NE_SANITY_CHECK +	xfer_count++; +#endif +      } +    } else { +	insb(NE_BASE + NE_DATAPORT, buf, count); +    } + +#ifdef NE_SANITY_CHECK +    /* This was for the ALPHA version only, but enough people have +       been encountering problems so it is still here.  If you see +       this message you either 1) have a slightly incompatible clone +       or 2) have noise/speed problems with your bus. */ +    if (ei_debug > 1) {		/* DMA termination address check... */ +	int addr, tries = 20; +	do { +	    /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here +	       -- it's broken for Rx on some cards! */ +	    int high = inb_p(nic_base + EN0_RSARHI); +	    int low = inb_p(nic_base + EN0_RSARLO); +	    addr = (high << 8) + low; +	    if (((ring_offset + xfer_count) & 0xff) == low) +		break; +	} while (--tries > 0); +	if (tries <= 0) +	    printk("%s: RX transfer address mismatch," +		   "%#4.4x (expected) vs. %#4.4x (actual).\n", +		   dev->name, ring_offset + xfer_count, addr); +    } +#endif +    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ +    ei_status.dmaing &= ~0x01; +} + +static void +ne_block_output(struct device *dev, int count, +		const unsigned char *buf, const int start_page) +{ +    int nic_base = NE_BASE; +    unsigned long dma_start; +#ifdef NE_SANITY_CHECK +    int retries = 0; +#endif + +    /* Round the count up for word writes.  Do we need to do this? +       What effect will an odd byte count have on the 8390? +       I should check someday. */ +    if (ei_status.word16 && (count & 0x01)) +      count++; + +    /* This *shouldn't* happen. If it does, it's the last thing you'll see */ +    if (ei_status.dmaing) { +	printk("%s: DMAing conflict in ne_block_output." +	   "[DMAstat:%d][irqlock:%d][intr:%d]\n", +	   dev->name, ei_status.dmaing, ei_status.irqlock, +	   dev->interrupt); +	return; +    } +    ei_status.dmaing |= 0x01; +    /* We should already be in page 0, but to be safe... */ +    outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); + +#ifdef NE_SANITY_CHECK + retry: +#endif + +#ifdef NE8390_RW_BUGFIX +    /* Handle the read-before-write bug the same way as the +       Crynwr packet driver -- the NatSemi method doesn't work. +       Actually this doesn't always work either, but if you have +       problems with your NEx000 this is better than nothing! */ +    outb_p(0x42, nic_base + EN0_RCNTLO); +    outb_p(0x00,   nic_base + EN0_RCNTHI); +    outb_p(0x42, nic_base + EN0_RSARLO); +    outb_p(0x00, nic_base + EN0_RSARHI); +    outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); +    /* Make certain that the dummy read has occurred. */ +    SLOW_DOWN_IO; +    SLOW_DOWN_IO; +    SLOW_DOWN_IO; +#endif + +    outb_p(ENISR_RDC, nic_base + EN0_ISR); + +   /* Now the normal output. */ +    outb_p(count & 0xff, nic_base + EN0_RCNTLO); +    outb_p(count >> 8,   nic_base + EN0_RCNTHI); +    outb_p(0x00, nic_base + EN0_RSARLO); +    outb_p(start_page, nic_base + EN0_RSARHI); + +    outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD); +    if (ei_status.word16) { +	outsw(NE_BASE + NE_DATAPORT, buf, count>>1); +    } else { +	outsb(NE_BASE + NE_DATAPORT, buf, count); +    } + +    dma_start = jiffies; + +#ifdef NE_SANITY_CHECK +    /* This was for the ALPHA version only, but enough people have +       been encountering problems so it is still here. */ +    if (ei_debug > 1) {		/* DMA termination address check... */ +	int addr, tries = 20; +	do { +	    int high = inb_p(nic_base + EN0_RSARHI); +	    int low = inb_p(nic_base + EN0_RSARLO); +	    addr = (high << 8) + low; +	    if ((start_page << 8) + count == addr) +		break; +	} while (--tries > 0); +	if (tries <= 0) { +	    printk("%s: Tx packet transfer address mismatch," +		   "%#4.4x (expected) vs. %#4.4x (actual).\n", +		   dev->name, (start_page << 8) + count, addr); +	    if (retries++ == 0) +		goto retry; +	} +    } +#endif + +    while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0) +	if (jiffies - dma_start > 2*HZ/100) {		/* 20ms */ +		printk("%s: timeout waiting for Tx RDC.\n", dev->name); +		ne_reset_8390(dev); +		NS8390_init(dev,1); +		break; +	} + +    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ +    ei_status.dmaing &= ~0x01; +    return; +} + + +#ifdef MODULE +#define MAX_NE_CARDS	4	/* Max number of NE cards per module */ +#define NAMELEN		8	/* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_NE_CARDS] = { 0, }; +static struct device dev_ne[MAX_NE_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int io[MAX_NE_CARDS] = { 0, }; +static int irq[MAX_NE_CARDS]  = { 0, }; +static int bad[MAX_NE_CARDS]  = { 0, }; + +/* This is set up so that no autoprobe takes place. We can't guarantee +that the ne2k probe is the last 8390 based probe to take place (as it +is at boot) and so the probe will get confused by any other 8390 cards. +ISA device autoprobes on a running machine are not recommended anyway. */ + +int +init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { +		struct device *dev = &dev_ne[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->init = ne_probe; +		dev->mem_end = bad[this_dev]; +		if (register_netdev(dev) == 0) { +			found++; +			continue; +		} +		if (found != 0) 	/* Got at least one. */ +			return 0; +		if (io[this_dev] != 0) +			printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]); +		else +			printk(KERN_NOTICE "ne.c: No PCI cards found. Use \"io=0xNNN\" value(s) for ISA cards.\n"); +		return -ENXIO; +	} + +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { +		struct device *dev = &dev_ne[this_dev]; +		if (dev->priv != NULL) { +			kfree(dev->priv); +			dev->priv = NULL; +			free_irq(dev->irq, dev); +			irq2dev_map[dev->irq] = NULL; +			release_region(dev->base_addr, NE_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + +/* + * Local variables: + *  compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c" + *  version-control: t + *  kept-new-versions: 5 + * End: + */ diff --git a/linux/src/drivers/net/ne2k-pci.c b/linux/src/drivers/net/ne2k-pci.c new file mode 100644 index 0000000..2b2b1f4 --- /dev/null +++ b/linux/src/drivers/net/ne2k-pci.c @@ -0,0 +1,647 @@ +/* ne2k-pci.c: A NE2000 clone on PCI bus driver for Linux. */ +/* +	A Linux device driver for PCI NE2000 clones. + +	Authors and other copyright holders: +	1992-2002 by Donald Becker, NE2000 core and various modifications. +	1995-1998 by Paul Gortmaker, core modifications and PCI support. +	Copyright 1993 assigned to the United States Government as represented +	by the Director, National Security Agency. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	410 Severn Ave., Suite 210 +	Annapolis MD 21403 + +	Issues remaining: +	People are making PCI ne2000 clones! Oh the horror, the horror... +	Limited full-duplex support. +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"ne2k-pci.c:v1.05 6/13/2002 D. Becker/P. Gortmaker\n"; +static const char version2[] = +"  http://www.scyld.com/network/ne2k-pci.html\n"; + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */ + +#define MAX_UNITS 8				/* More are supported, limit only on options */ +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS] = {0, }; +static int options[MAX_UNITS] = {0, }; + +/* Force a non std. amount of memory.  Units are 256 byte pages. */ +/* #define PACKETBUF_MEMSIZE	0x40 */ + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#include <linux/module.h> +#if LINUX_VERSION_CODE < 0x20300  &&  defined(MODVERSIONS) +#include <linux/modversions.h> +#endif + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/pci.h> +#if LINUX_VERSION_CODE < 0x20200 +#define lock_8390_module() +#define unlock_8390_module() +#else +#include <linux/init.h> +#endif + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include "8390.h" + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +MODULE_AUTHOR("Donald Becker / Paul Gortmaker"); +MODULE_DESCRIPTION("PCI NE2000 clone driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); + +/* Some defines that people can play with if so inclined. */ + +/* Do #define LOAD_8390_BY_KERNELD to automatically load 8390 support. */ +#ifdef LOAD_8390_BY_KERNELD +#include <linux/kerneld.h> +#endif + +static void *ne2k_pci_probe1(struct pci_dev *pdev, void *dev, +							 long ioaddr, int irq, int chip_idx, int fnd_cnt); +/* Flags.  We rename an existing ei_status field to store flags! */ +/* Thus only the low 8 bits are usable for non-init-time flags. */ +#define ne2k_flags reg0 +enum { +	ONLY_16BIT_IO=8, ONLY_32BIT_IO=4,	/* Chip can do only 16/32-bit xfers. */ +	FORCE_FDX=0x20,						/* User override. */ +	REALTEK_FDX=0x40, HOLTEK_FDX=0x80, +	STOP_PG_0x60=0x100, +}; +#define NE_IO_EXTENT	0x20 +#ifndef USE_MEMORY_OPS +#define PCI_IOTYPE (PCI_USES_IO  | PCI_ADDR0) +#else +#warning When using PCI memory mode the 8390 core must be compiled for memory +#warning operations as well. +#warning Not all PCI NE2000 clones support memory mode access.  +#define PCI_IOTYPE (PCI_USES_MEM | PCI_ADDR1) +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{"RealTek RTL-8029",{ 0x802910ec, 0xffffffff}, PCI_IOTYPE, NE_IO_EXTENT, +	 REALTEK_FDX }, +	{"Winbond 89C940",  { 0x09401050, 0xffffffff}, PCI_IOTYPE, NE_IO_EXTENT, 0}, +	{"Winbond w89c940",	{ 0x5a5a1050, 0xffffffff}, PCI_IOTYPE, NE_IO_EXTENT, 0}, +	{"KTI ET32P2",      { 0x30008e2e, 0xffffffff}, PCI_IOTYPE, NE_IO_EXTENT, 0}, +	{"NetVin NV5000SC", { 0x50004a14, 0xffffffff}, PCI_IOTYPE, NE_IO_EXTENT, 0}, +	{"Via 86C926",		{ 0x09261106, 0xffffffff}, +	 PCI_IOTYPE, NE_IO_EXTENT, ONLY_16BIT_IO}, +	{"SureCom NE34",	{ 0x0e3410bd, 0xffffffff}, PCI_IOTYPE, NE_IO_EXTENT, 0}, +	{"Holtek HT80232",	{ 0x005812c3, 0xffffffff}, +	 PCI_IOTYPE, NE_IO_EXTENT, ONLY_16BIT_IO | HOLTEK_FDX}, +	{"Holtek HT80229",	{ 0x559812c3, 0xffffffff}, +	 PCI_IOTYPE, NE_IO_EXTENT, ONLY_32BIT_IO | HOLTEK_FDX | STOP_PG_0x60}, +	{"Compex RL2000", +	 { 0x140111f6, 0xffffffff}, PCI_IOTYPE, NE_IO_EXTENT, 0}, +	/* A mutant board: Winbond chip with a RTL format EEPROM. */ +	{"Winbond w89c940 (misprogrammed type 0x1980)", { 0x19808c4a, 0xffffffff}, +	 PCI_IOTYPE, NE_IO_EXTENT, 0}, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info ne2k_pci_drv_id = { +	"ne2k-pci", 0, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, ne2k_pci_probe1, +}; + +/* ---- No user-serviceable parts below ---- */ + +#define NE_BASE	 (dev->base_addr) +#define NE_CMD	 	0x00 +#define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */ +#define NE_RESET	0x1f	/* Issue a read to reset, a write to clear. */ + +#define NESM_START_PG	0x40	/* First page of TX buffer */ +#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */ + +int ne2k_pci_probe(struct net_device *dev); + +static int ne2k_pci_open(struct net_device *dev); +static int ne2k_pci_close(struct net_device *dev); + +static void ne2k_pci_reset_8390(struct net_device *dev); +static void ne2k_pci_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, +			  int ring_page); +static void ne2k_pci_block_input(struct net_device *dev, int count, +			  struct sk_buff *skb, int ring_offset); +static void ne2k_pci_block_output(struct net_device *dev, const int count, +		const unsigned char *buf, const int start_page); + + + +/* There is no room in the standard 8390 structure for extra info we need, +   so we build a meta/outer-wrapper structure.. */ +struct ne2k_pci_card { +	struct ne2k_pci_card *next; +	struct net_device *dev; +	struct pci_dev *pci_dev; +}; +/* A list of all installed devices, for removing the driver module. */ +static struct ne2k_pci_card *ne2k_card_list = NULL; + +#ifdef LOAD_8390_BY_KERNELD +static int (*Lethdev_init)(struct net_device *dev); +static void (*LNS8390_init)(struct net_device *dev, int startp); +static int (*Lei_open)(struct net_device *dev); +static int (*Lei_close)(struct net_device *dev); +static void (*Lei_interrupt)(int irq, void *dev_id, struct pt_regs *regs); +#else +#define Lethdev_init ethdev_init +#define LNS8390_init NS8390_init +#define Lei_open ei_open +#define Lei_close ei_close +#define Lei_interrupt ei_interrupt +#endif + +#ifdef MODULE +int init_module(void) +{ +	int found_cnt; + +	if (debug)					/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	found_cnt = pci_drv_register(&ne2k_pci_drv_id, NULL); +	if (found_cnt < 0) { +		printk(KERN_NOTICE "ne2k-pci.c: No useable cards found, driver NOT installed.\n"); +		return -ENODEV; +	} +	lock_8390_module(); +	return 0; +} + +void cleanup_module(void) +{ +	struct net_device *dev; +	struct ne2k_pci_card *this_card; + +	pci_drv_unregister(&ne2k_pci_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (ne2k_card_list) { +		dev = ne2k_card_list->dev; +		unregister_netdev(dev); +		release_region(dev->base_addr, NE_IO_EXTENT); +		kfree(dev); +		this_card = ne2k_card_list; +		ne2k_card_list = ne2k_card_list->next; +		kfree(this_card); +	} + +#ifdef LOAD_8390_BY_KERNELD +	release_module("8390", 0); +#else +	unlock_8390_module(); +#endif +} + +#else + +int ne2k_pci_probe(struct net_device *dev) +{ +	int found_cnt = pci_drv_register(&ne2k_pci_drv_id, NULL); +	if (found_cnt >= 0  &&  debug) +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return found_cnt; +} +#endif  /* MODULE */ + +static void *ne2k_pci_probe1(struct pci_dev *pdev, void *init_dev, +							 long ioaddr, int irq, int chip_idx, int fnd_cnt) +{ +	struct net_device *dev; +	int i; +	unsigned char SA_prom[32]; +	int start_page, stop_page; +	int reg0 = inb(ioaddr); +	int flags = pci_id_tbl[chip_idx].drv_flags; +	struct ne2k_pci_card *ne2k_card; + +	if (reg0 == 0xFF) +		return 0; + +	/* Do a preliminary verification that we have a 8390. */ +	{ +		int regd; +		outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); +		regd = inb(ioaddr + 0x0d); +		outb(0xff, ioaddr + 0x0d); +		outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); +		inb(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ +		if (inb(ioaddr + EN0_COUNTER0) != 0) { +			outb(reg0, ioaddr); +			outb(regd, ioaddr + 0x0d);	/* Restore the old values. */ +			return 0; +		} +	} + +	dev = init_etherdev(init_dev, 0); + +	if (dev == NULL) +		return 0; +	ne2k_card = kmalloc(sizeof(struct ne2k_pci_card), GFP_KERNEL); +	if (ne2k_card == NULL) +		return 0; + +	ne2k_card->next = ne2k_card_list; +	ne2k_card_list = ne2k_card; +	ne2k_card->dev = dev; +	ne2k_card->pci_dev = pdev; + +	/* Reset card. Who knows what dain-bramaged state it was left in. */ +	{ +		unsigned long reset_start_time = jiffies; + +		outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); + +		/* This looks like a horrible timing loop, but it should never take +		   more than a few cycles. +		*/ +		while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0) +			/* Limit wait: '2' avoids jiffy roll-over. */ +			if (jiffies - reset_start_time > 2) { +				printk("ne2k-pci: Card failure (no reset ack).\n"); +				return 0; +			} +		 +		outb(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */ +	} + +#if defined(LOAD_8390_BY_KERNELD) +	/* We are now certain the 8390 module is required. */ +	if (request_module("8390")) { +		printk("ne2k-pci: Failed to load the 8390 core module.\n"); +		return 0; +	} +	if ((Lethdev_init = (void*)get_module_symbol(0, "ethdev_init")) == 0 || +		(LNS8390_init = (void*)get_module_symbol(0, "NS8390_init")) == 0 || +		(Lei_open = (void*)get_module_symbol(0, "ei_open")) == 0 || +		(Lei_close = (void*)get_module_symbol(0, "ei_close")) == 0 || +		(Lei_interrupt = (void*)get_module_symbol(0, "ei_interrupt")) == 0 ) { +		printk("ne2k-pci: Failed to resolve an 8390 symbol.\n"); +		release_module("8390", 0); +		return 0; +	} +#endif + +	/* Read the 16 bytes of station address PROM. +	   We must first initialize registers, similar to NS8390_init(eifdev, 0). +	   We can't reliably read the SAPROM address without this. +	   (I learned the hard way!). */ +	{ +		struct {unsigned char value, offset; } program_seq[] = { +			{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ +			{0x49,	EN0_DCFG},	/* Set word-wide access. */ +			{0x00,	EN0_RCNTLO},	/* Clear the count regs. */ +			{0x00,	EN0_RCNTHI}, +			{0x00,	EN0_IMR},	/* Mask completion irq. */ +			{0xFF,	EN0_ISR}, +			{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */ +			{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */ +			{32,	EN0_RCNTLO}, +			{0x00,	EN0_RCNTHI}, +			{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */ +			{0x00,	EN0_RSARHI}, +			{E8390_RREAD+E8390_START, E8390_CMD}, +		}; +		for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) +			outb(program_seq[i].value, ioaddr + program_seq[i].offset); + +	} + +	/* Note: all PCI cards have at least 16 bit access, so we don't have +	   to check for 8 bit cards.  Most cards permit 32 bit access. */ + +	if (flags & ONLY_32BIT_IO) { +		for (i = 0; i < 8; i++) +			((u32 *)SA_prom)[i] = le32_to_cpu(inl(ioaddr + NE_DATAPORT)); +	} else +		for(i = 0; i < 32 /*sizeof(SA_prom)*/; i++) +			SA_prom[i] = inb(ioaddr + NE_DATAPORT); + +	/* We always set the 8390 registers for word mode. */ +	outb(0x49, ioaddr + EN0_DCFG); +	start_page = NESM_START_PG; + +	stop_page = flags & STOP_PG_0x60 ? 0x60 : NESM_STOP_PG; + +	/* Set up the rest of the parameters. */ +	dev->irq = irq; +	dev->base_addr = ioaddr; + +	/* Allocate dev->priv and fill in 8390 specific dev fields. */ +	if (Lethdev_init(dev)) { +		printk ("%s: unable to get memory for dev->priv.\n", dev->name); +		return 0; +	} + +	request_region(ioaddr, NE_IO_EXTENT, dev->name); + +	printk("%s: %s found at %#lx, IRQ %d, ", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr, dev->irq); +	for(i = 0; i < 6; i++) { +		printk("%2.2X%s", SA_prom[i], i == 5 ? ".\n": ":"); +		dev->dev_addr[i] = SA_prom[i]; +	} + +	ei_status.name = pci_id_tbl[chip_idx].name; +	ei_status.tx_start_page = start_page; +	ei_status.stop_page = stop_page; +	ei_status.word16 = 1; +	ei_status.ne2k_flags = flags; +	if (fnd_cnt < MAX_UNITS) { +		if (full_duplex[fnd_cnt] > 0  ||  (options[fnd_cnt] & FORCE_FDX)) { +			printk("%s:  Full duplex set by user option.\n", dev->name); +			ei_status.ne2k_flags |= FORCE_FDX; +		} +	} + +	ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE +	/* Allow the packet buffer size to be overridden by know-it-alls. */ +	ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + +	ei_status.reset_8390 = &ne2k_pci_reset_8390; +	ei_status.block_input = &ne2k_pci_block_input; +	ei_status.block_output = &ne2k_pci_block_output; +	ei_status.get_8390_hdr = &ne2k_pci_get_8390_hdr; +	dev->open = &ne2k_pci_open; +	dev->stop = &ne2k_pci_close; +	LNS8390_init(dev, 0); +	return dev; +} + +static int ne2k_pci_open(struct net_device *dev) +{ +	MOD_INC_USE_COUNT; +	if (request_irq(dev->irq, Lei_interrupt, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} +	/* Set full duplex for the chips that we know about. */ +	if (ei_status.ne2k_flags & FORCE_FDX) { +		long ioaddr = dev->base_addr; +		if (ei_status.ne2k_flags & REALTEK_FDX) { +			outb(0xC0 + E8390_NODMA, ioaddr + NE_CMD); /* Page 3 */ +			outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20); +		} else if (ei_status.ne2k_flags & HOLTEK_FDX) +			outb(inb(ioaddr + 0x20) | 0x80, ioaddr + 0x20); +	} +	Lei_open(dev); +	return 0; +} + +static int ne2k_pci_close(struct net_device *dev) +{ +	Lei_close(dev); +	free_irq(dev->irq, dev); +	MOD_DEC_USE_COUNT; +	return 0; +} + +/* Hard reset the card.  This used to pause for the same period that a +   8390 reset command required, but that shouldn't be necessary. */ +static void ne2k_pci_reset_8390(struct net_device *dev) +{ +	unsigned long reset_start_time = jiffies; + +	if (debug > 1) printk("%s: Resetting the 8390 t=%ld...", +						  dev->name, jiffies); + +	outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); + +	ei_status.txing = 0; +	ei_status.dmaing = 0; + +	/* This check _should_not_ be necessary, omit eventually. */ +	while ((inb(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) +		if (jiffies - reset_start_time > 2) { +			printk("%s: ne2k_pci_reset_8390() did not complete.\n", dev->name); +			break; +		} +	outb(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but +   we don't need to be concerned with ring wrap as the header will be at +   the start of a page, so we optimize accordingly. */ + +static void ne2k_pci_get_8390_hdr(struct net_device *dev, +								  struct e8390_pkt_hdr *hdr, int ring_page) +{ + +	long nic_base = dev->base_addr; + +	/* This *shouldn't* happen. If it does, it's the last thing you'll see */ +	if (ei_status.dmaing) { +		printk("%s: DMAing conflict in ne2k_pci_get_8390_hdr " +			   "[DMAstat:%d][irqlock:%d][intr:%d].\n", +			   dev->name, ei_status.dmaing, ei_status.irqlock, +			   (int)dev->interrupt); +		return; +	} + +	ei_status.dmaing |= 0x01; +	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); +	outb(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); +	outb(0, nic_base + EN0_RCNTHI); +	outb(0, nic_base + EN0_RSARLO);		/* On page boundary */ +	outb(ring_page, nic_base + EN0_RSARHI); +	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); + +	if (ei_status.ne2k_flags & ONLY_16BIT_IO) { +		insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); +	} else { +		*(u32*)hdr = le32_to_cpu(inl(NE_BASE + NE_DATAPORT)); +		le16_to_cpus(&hdr->count); +	} + +	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ +	ei_status.dmaing &= ~0x01; +} + +/* Block input and output, similar to the Crynwr packet driver.  If you +   are porting to a new ethercard, look at the packet driver source for hints. +   The NEx000 doesn't share the on-board packet memory -- you have to put +   the packet out through the "remote DMA" dataport using outb. */ + +static void ne2k_pci_block_input(struct net_device *dev, int count, +								 struct sk_buff *skb, int ring_offset) +{ +	long nic_base = dev->base_addr; +	char *buf = skb->data; + +	/* This *shouldn't* happen. If it does, it's the last thing you'll see */ +	if (ei_status.dmaing) { +		printk("%s: DMAing conflict in ne2k_pci_block_input " +			   "[DMAstat:%d][irqlock:%d][intr:%d].\n", +			   dev->name, ei_status.dmaing, ei_status.irqlock, +			   (int)dev->interrupt); +		return; +	} +	ei_status.dmaing |= 0x01; +	if (ei_status.ne2k_flags & ONLY_32BIT_IO) +		count = (count + 3) & 0xFFFC; +	outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); +	outb(count & 0xff, nic_base + EN0_RCNTLO); +	outb(count >> 8, nic_base + EN0_RCNTHI); +	outb(ring_offset & 0xff, nic_base + EN0_RSARLO); +	outb(ring_offset >> 8, nic_base + EN0_RSARHI); +	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); + +	if (ei_status.ne2k_flags & ONLY_16BIT_IO) { +		insw(NE_BASE + NE_DATAPORT,buf,count>>1); +		if (count & 0x01) { +			buf[count-1] = inb(NE_BASE + NE_DATAPORT); +		} +	} else { +		insl(NE_BASE + NE_DATAPORT, buf, count>>2); +		if (count & 3) { +			buf += count & ~3; +			if (count & 2) { +				*((u16 *) buf) = le16_to_cpu(inw(NE_BASE + NE_DATAPORT)); +				buf = (void *) buf + sizeof (u16); +			} +			if (count & 1) +				*buf = inb(NE_BASE + NE_DATAPORT); +		} +	} + +	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ +	ei_status.dmaing &= ~0x01; +} + +static void +ne2k_pci_block_output(struct net_device *dev, int count, +		const unsigned char *buf, const int start_page) +{ +	int nic_base = NE_BASE; +	unsigned long dma_start; + +	/* On little-endian it's always safe to round the count up for +	   word writes. */ +	if (ei_status.ne2k_flags & ONLY_32BIT_IO) +		count = (count + 3) & 0xFFFC; +	else +		if (count & 0x01) +			count++; + +	/* This *shouldn't* happen. If it does, it's the last thing you'll see */ +	if (ei_status.dmaing) { +		printk("%s: DMAing conflict in ne2k_pci_block_output." +			   "[DMAstat:%d][irqlock:%d][intr:%d]\n", +			   dev->name, ei_status.dmaing, ei_status.irqlock, +			   (int)dev->interrupt); +		return; +	} +	ei_status.dmaing |= 0x01; +	/* We should already be in page 0, but to be safe... */ +	outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); + +#ifdef NE8390_RW_BUGFIX +	/* Handle the read-before-write bug the same way as the +	   Crynwr packet driver -- the NatSemi method doesn't work. +	   Actually this doesn't always work either, but if you have +	   problems with your NEx000 this is better than nothing! */ +	outb(0x42, nic_base + EN0_RCNTLO); +	outb(0x00, nic_base + EN0_RCNTHI); +	outb(0x42, nic_base + EN0_RSARLO); +	outb(0x00, nic_base + EN0_RSARHI); +	outb(E8390_RREAD+E8390_START, nic_base + NE_CMD); +#endif +	outb(ENISR_RDC, nic_base + EN0_ISR); + +   /* Now the normal output. */ +	outb(count & 0xff, nic_base + EN0_RCNTLO); +	outb(count >> 8,   nic_base + EN0_RCNTHI); +	outb(0x00, nic_base + EN0_RSARLO); +	outb(start_page, nic_base + EN0_RSARHI); +	outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD); +	if (ei_status.ne2k_flags & ONLY_16BIT_IO) { +		outsw(NE_BASE + NE_DATAPORT, buf, count>>1); +	} else { +		outsl(NE_BASE + NE_DATAPORT, buf, count>>2); +		if (count & 3) { +			buf += count & ~3; +			if (count & 2) { +				outw(cpu_to_le16(*((u16 *) buf)), NE_BASE + NE_DATAPORT); +				buf = (void *) buf + sizeof (u16); +			} +		} +	} + +	dma_start = jiffies; + +	while ((inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) +		if (jiffies - dma_start > 2) { 			/* Avoid clock roll-over. */ +			printk("%s: timeout waiting for Tx RDC.\n", dev->name); +			ne2k_pci_reset_8390(dev); +			LNS8390_init(dev,1); +			break; +		} + +	outb(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */ +	ei_status.dmaing &= ~0x01; +	return; +} + + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c ne2k-pci.c  -I/usr/src/linux/drivers/net/" + *  alt-compile-command: "gcc -DMODULE -O6 -c ne2k-pci.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + *  version-control: t + *  kept-new-versions: 5 + * End: + */ diff --git a/linux/src/drivers/net/net_init.c b/linux/src/drivers/net/net_init.c new file mode 100644 index 0000000..3d4c42d --- /dev/null +++ b/linux/src/drivers/net/net_init.c @@ -0,0 +1,439 @@ +/* netdrv_init.c: Initialization for network devices. */ +/* +	Written 1993,1994,1995 by Donald Becker. + +	The author may be reached as becker@cesdis.gsfc.nasa.gov or +	C/O Center of Excellence in Space Data and Information Sciences +		Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	This file contains the initialization for the "pl14+" style ethernet +	drivers.  It should eventually replace most of drivers/net/Space.c. +	It's primary advantage is that it's able to allocate low-memory buffers. +	A secondary advantage is that the dangerous NE*000 netcards can reserve +	their I/O port region before the SCSI probes start. + +	Modifications/additions by Bjorn Ekwall <bj0rn@blox.se>: +		ethdev_index[MAX_ETH_CARDS] +		register_netdev() / unregister_netdev() +		 +	Modifications by Wolfgang Walter +		Use dev_close cleanly so we always shut things down tidily. +		 +	Changed 29/10/95, Alan Cox to pass sockaddr's around for mac addresses. +	 +	14/06/96 - Paul Gortmaker:	Add generic eth_change_mtu() function. + +	August 12, 1996 - Lawrence V. Stefani: Added fddi_change_mtu() and +					  fddi_setup() functions. +	Sept. 10, 1996  - Lawrence V. Stefani: Increased hard_header_len to +					  include 3 pad bytes. +*/ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/malloc.h> +#include <linux/if_ether.h> +#include <linux/string.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/fddidevice.h> +#include <linux/trdevice.h> +#include <linux/if_arp.h> +#ifdef CONFIG_NET_ALIAS +#include <linux/net_alias.h> +#endif + +/* The network devices currently exist only in the socket namespace, so these +   entries are unused.  The only ones that make sense are +    open	start the ethercard +    close	stop  the ethercard +    ioctl	To get statistics, perhaps set the interface port (AUI, BNC, etc.) +   One can also imagine getting raw packets using +    read & write +   but this is probably better handled by a raw packet socket. + +   Given that almost all of these functions are handled in the current +   socket-based scheme, putting ethercard devices in /dev/ seems pointless. +    +   [Removed all support for /dev network devices. When someone adds +    streams then by magic we get them, but otherwise they are un-needed +	and a space waste] +*/ + +/* The list of used and available "eth" slots (for "eth0", "eth1", etc.) */ +#define MAX_ETH_CARDS 16 /* same as the number if irq's in irq2dev[] */ +static struct device *ethdev_index[MAX_ETH_CARDS]; + + +/* Fill in the fields of the device structure with ethernet-generic values. + +   If no device structure is passed, a new one is constructed, complete with +   a SIZEOF_PRIVATE private data area. + +   If an empty string area is passed as dev->name, or a new structure is made, +   a new name string is constructed.  The passed string area should be 8 bytes +   long. + */ + +struct device * +init_etherdev(struct device *dev, int sizeof_priv) +{ +	int new_device = 0; +	int i; + +	/* Use an existing correctly named device in Space.c:dev_base. */ +	if (dev == NULL) { +		int alloc_size = sizeof(struct device) + sizeof("eth%d  ") +			+ sizeof_priv + 3; +		struct device *cur_dev; +		char pname[8];		/* Putative name for the device.  */ + +		for (i = 0; i < MAX_ETH_CARDS; ++i) +			if (ethdev_index[i] == NULL) { +				sprintf(pname, "eth%d", i); +				for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) +					if (strcmp(pname, cur_dev->name) == 0) { +						dev = cur_dev; +						dev->init = NULL; +						sizeof_priv = (sizeof_priv + 3) & ~3; +						dev->priv = sizeof_priv +							  ? kmalloc(sizeof_priv, GFP_KERNEL) +							  :	NULL; +						if (dev->priv) memset(dev->priv, 0, sizeof_priv); +						goto found; +					} +			} + +		alloc_size &= ~3;		/* Round to dword boundary. */ + +		dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL); +		memset(dev, 0, alloc_size); +		if (sizeof_priv) +			dev->priv = (void *) (dev + 1); +		dev->name = sizeof_priv + (char *)(dev + 1); +		new_device = 1; +	} + +	found:						/* From the double loop above. */ + +	if (dev->name && +		((dev->name[0] == '\0') || (dev->name[0] == ' '))) { +		for (i = 0; i < MAX_ETH_CARDS; ++i) +			if (ethdev_index[i] == NULL) { +				sprintf(dev->name, "eth%d", i); +				ethdev_index[i] = dev; +				break; +			} +	} + +	ether_setup(dev); 	/* Hmmm, should this be called here? */ +	 +	if (new_device) { +		/* Append the device to the device queue. */ +		struct device **old_devp = &dev_base; +		while ((*old_devp)->next) +			old_devp = & (*old_devp)->next; +		(*old_devp)->next = dev; +		dev->next = 0; +	} +	return dev; +} + + +static int eth_mac_addr(struct device *dev, void *p) +{ +	struct sockaddr *addr=p; +	if(dev->start) +		return -EBUSY; +	memcpy(dev->dev_addr, addr->sa_data,dev->addr_len); +	return 0; +} + +static int eth_change_mtu(struct device *dev, int new_mtu) +{ +	if ((new_mtu < 68) || (new_mtu > 1500)) +		return -EINVAL; +	dev->mtu = new_mtu; +	return 0; +} + +#ifdef CONFIG_FDDI + +static int fddi_change_mtu(struct device *dev, int new_mtu) +{ +	if ((new_mtu < FDDI_K_SNAP_HLEN) || (new_mtu > FDDI_K_SNAP_DLEN)) +		return(-EINVAL); +	dev->mtu = new_mtu; +	return(0); +} + +#endif + +void ether_setup(struct device *dev) +{ +	int i; +	/* Fill in the fields of the device structure with ethernet-generic values. +	   This should be in a common file instead of per-driver.  */ +	for (i = 0; i < DEV_NUMBUFFS; i++) +		skb_queue_head_init(&dev->buffs[i]); + +	/* register boot-defined "eth" devices */ +	if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) { +		i = simple_strtoul(dev->name + 3, NULL, 0); +		if (ethdev_index[i] == NULL) { +			ethdev_index[i] = dev; +		} +		else if (dev != ethdev_index[i]) { +			/* Really shouldn't happen! */ +			printk("ether_setup: Ouch! Someone else took %s\n", +				dev->name); +		} +	} + +	dev->change_mtu		= eth_change_mtu; +	dev->hard_header	= eth_header; +	dev->rebuild_header 	= eth_rebuild_header; +	dev->set_mac_address 	= eth_mac_addr; +	dev->header_cache_bind 	= eth_header_cache_bind; +	dev->header_cache_update= eth_header_cache_update; + +	dev->type		= ARPHRD_ETHER; +	dev->hard_header_len 	= ETH_HLEN; +	dev->mtu		= 1500; /* eth_mtu */ +	dev->addr_len		= ETH_ALEN; +	dev->tx_queue_len	= 100;	/* Ethernet wants good queues */	 +	 +	memset(dev->broadcast,0xFF, ETH_ALEN); + +	/* New-style flags. */ +	dev->flags		= IFF_BROADCAST|IFF_MULTICAST; +	dev->family		= AF_INET; +	dev->pa_addr	= 0; +	dev->pa_brdaddr = 0; +	dev->pa_mask	= 0; +	dev->pa_alen	= 4; +} + +#ifdef CONFIG_TR + +void tr_setup(struct device *dev) +{ +	int i; +	/* Fill in the fields of the device structure with ethernet-generic values. +	   This should be in a common file instead of per-driver.  */ +	for (i = 0; i < DEV_NUMBUFFS; i++) +		skb_queue_head_init(&dev->buffs[i]); + +	dev->hard_header	= tr_header; +	dev->rebuild_header 	= tr_rebuild_header; + +	dev->type		= ARPHRD_IEEE802; +	dev->hard_header_len 	= TR_HLEN; +	dev->mtu		= 2000; /* bug in fragmenter...*/ +	dev->addr_len		= TR_ALEN; +	dev->tx_queue_len	= 100;	/* Long queues on tr */ +	 +	memset(dev->broadcast,0xFF, TR_ALEN); + +	/* New-style flags. */ +	dev->flags		= IFF_BROADCAST; +	dev->family		= AF_INET; +	dev->pa_addr	= 0; +	dev->pa_brdaddr = 0; +	dev->pa_mask	= 0; +	dev->pa_alen	= 4; +} + +#endif + +#ifdef CONFIG_FDDI + +void fddi_setup(struct device *dev) +	{ +	int i; + +	/* +	 * Fill in the fields of the device structure with FDDI-generic values. +	 * This should be in a common file instead of per-driver. +	 */ +	for (i=0; i < DEV_NUMBUFFS; i++) +		skb_queue_head_init(&dev->buffs[i]); + +	dev->change_mtu			= fddi_change_mtu; +	dev->hard_header		= fddi_header; +	dev->rebuild_header		= fddi_rebuild_header; + +	dev->type				= ARPHRD_FDDI; +	dev->hard_header_len	= FDDI_K_SNAP_HLEN+3;	/* Assume 802.2 SNAP hdr len + 3 pad bytes */ +	dev->mtu				= FDDI_K_SNAP_DLEN;		/* Assume max payload of 802.2 SNAP frame */ +	dev->addr_len			= FDDI_K_ALEN; +	dev->tx_queue_len		= 100;	/* Long queues on FDDI */ +	 +	memset(dev->broadcast, 0xFF, FDDI_K_ALEN); + +	/* New-style flags */ +	dev->flags		= IFF_BROADCAST | IFF_MULTICAST; +	dev->family		= AF_INET; +	dev->pa_addr	= 0; +	dev->pa_brdaddr = 0; +	dev->pa_mask	= 0; +	dev->pa_alen	= 4; +	return; +	} + +#endif + +int ether_config(struct device *dev, struct ifmap *map) +{ +	if (map->mem_start != (u_long)(-1)) +		dev->mem_start = map->mem_start; +	if (map->mem_end != (u_long)(-1)) +		dev->mem_end = map->mem_end; +	if (map->base_addr != (u_short)(-1)) +		dev->base_addr = map->base_addr; +	if (map->irq != (u_char)(-1)) +		dev->irq = map->irq; +	if (map->dma != (u_char)(-1)) +		dev->dma = map->dma; +	if (map->port != (u_char)(-1)) +		dev->if_port = map->port; +	return 0; +} + +int register_netdev(struct device *dev) +{ +	struct device *d = dev_base; +	unsigned long flags; +	int i=MAX_ETH_CARDS; + +	save_flags(flags); +	cli(); + +	if (dev && dev->init) { +		if (dev->name && +			((dev->name[0] == '\0') || (dev->name[0] == ' '))) { +			for (i = 0; i < MAX_ETH_CARDS; ++i) +				if (ethdev_index[i] == NULL) { +					sprintf(dev->name, "eth%d", i); +/*					printk("loading device '%s'...\n", dev->name);*/ +					ethdev_index[i] = dev; +					break; +				} +		} + +		sti();	/* device probes assume interrupts enabled */ +		if (dev->init(dev) != 0) { +		    if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL; +			restore_flags(flags); +			return -EIO; +		} +		cli(); + +		/* Add device to end of chain */ +		if (dev_base) { +			while (d->next) +				d = d->next; +			d->next = dev; +		} +		else +			dev_base = dev; +		dev->next = NULL; +	} +	restore_flags(flags); +	return 0; +} + +void unregister_netdev(struct device *dev) +{ +	struct device *d = dev_base; +	unsigned long flags; +	int i; + +	save_flags(flags); +	cli(); + +	if (dev == NULL)  +	{ +		printk("was NULL\n"); +		restore_flags(flags); +		return; +	} +	/* else */ +	if (dev->start) +		printk("ERROR '%s' busy and not MOD_IN_USE.\n", dev->name); + +	/* +	 * 	must jump over main_device+aliases +	 * 	avoid alias devices unregistration so that only +	 * 	net_alias module manages them +	 */ +#ifdef CONFIG_NET_ALIAS		 +	if (dev_base == dev) +		dev_base = net_alias_nextdev(dev); +	else +	{ +		while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */ +			d = net_alias_nextdev(d); +	   +		if (d && (net_alias_nextdev(d) == dev)) +		{ +			/* +			 * 	Critical: Bypass by consider devices as blocks (maindev+aliases) +			 */ +			net_alias_nextdev_set(d, net_alias_nextdev(dev));  +		} +#else +	if (dev_base == dev) +		dev_base = dev->next; +	else  +	{ +		while (d && (d->next != dev)) +			d = d->next; +		 +		if (d && (d->next == dev))  +		{ +			d->next = dev->next; +		} +#endif +		else  +		{ +			printk("unregister_netdev: '%s' not found\n", dev->name); +			restore_flags(flags); +			return; +		} +	} +	for (i = 0; i < MAX_ETH_CARDS; ++i)  +	{ +		if (ethdev_index[i] == dev)  +		{ +			ethdev_index[i] = NULL; +			break; +		} +	} + +	restore_flags(flags); + +	/* +	 *	You can i.e use a interfaces in a route though it is not up. +	 *	We call close_dev (which is changed: it will down a device even if +	 *	dev->flags==0 (but it will not call dev->stop if IFF_UP +	 *	is not set). +	 *	This will call notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev), +	 *	dev_mc_discard(dev), .... +	 */ +	  +	dev_close(dev); +} + + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c" + *  version-control: t + *  kept-new-versions: 5 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/ni52.c b/linux/src/drivers/net/ni52.c new file mode 100644 index 0000000..6d486e9 --- /dev/null +++ b/linux/src/drivers/net/ni52.c @@ -0,0 +1,1387 @@ +/*  + * net-3-driver for the NI5210 card (i82586 Ethernet chip) + * + * This is an extension to the Linux operating system, and is covered by the + * same Gnu Public License that covers that work. + *  + * Alphacode 0.80 (96/02/19) for Linux 1.3.66 (or later) + * Copyrights (c) 1994,1995,1996 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de) + *    [feel free to mail ....] + * + * when using as module: (no autoprobing!) + *   compile with: gcc -D__KERNEL__ -DMODULE -O2 -c ni52.c + *   run with e.g: insmod ni52.o io=0x360 irq=9 memstart=0xd0000 memend=0xd4000 + * + * CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!. + *  + * If you find a bug, please report me: + *   The kernel panic output and any kmsg from the ni52 driver + *   the ni5210-driver-version and the linux-kernel version  + *   how many shared memory (memsize) on the netcard,  + *   bootprom: yes/no, base_addr, mem_start + *   maybe the ni5210-card revision and the i82586 version + * + * autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340 + *                mem_start: 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000, + *                           0xd8000,0xcc000,0xce000,0xda000,0xdc000 + * + * sources: + *   skeleton.c from Donald Becker + * + * I have also done a look in the following sources: (mail me if you need them) + *   crynwr-packet-driver by Russ Nelson + *   Garret A. Wollman's (fourth) i82586-driver for BSD + *   (before getting an i82596 (yes 596 not 586) manual, the existing drivers helped + *    me a lot to understand this tricky chip.) + * + * Known Problems: + *   The internal sysbus seems to be slow. So we often lose packets because of + *   overruns while receiving from a fast remote host.  + *   This can slow down TCP connections. Maybe the newer ni5210 cards are better. + *   my experience is, that if a machine sends with more then about 500-600K/s + *   the fifo/sysbus overflows. + *  + * IMPORTANT NOTE: + *   On fast networks, it's a (very) good idea to have 16K shared memory. With + *   8K, we can store only 4 receive frames, so it can (easily) happen that a remote  + *   machine 'overruns' our system. + * + * Known i82586/card problems (I'm sure, there are many more!): + *   Running the NOP-mode, the i82586 sometimes seems to forget to report + *   every xmit-interrupt until we restart the CU. + *   Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit  + *   in the RBD-Struct which indicates an end of the RBD queue.  + *   Instead, the RU fetches another (randomly selected and  + *   usually used) RBD and begins to fill it. (Maybe, this happens only if  + *   the last buffer from the previous RFD fits exact into the queue and + *   the next RFD can't fetch an initial RBD. Anyone knows more? ) + * + * results from ftp performance tests with Linux 1.2.5  + *   send and receive about 350-400 KByte/s (peak up to 460 kbytes/s) + *   sending in NOP-mode: peak performance up to 530K/s (but better don't run this mode) + */ + +/* + * 19.Feb.96: more Mcast changes, module support (MH) + * + * 18.Nov.95: Mcast changes (AC). + * + * 23.April.95: fixed(?) receiving problems by configuring a RFD more + *              than the number of RBD's. Can maybe cause other problems.  + * 18.April.95: Added MODULE support (MH) + * 17.April.95: MC related changes in init586() and set_multicast_list(). + *              removed use of 'jiffies' in init586() (MH) + * + * 19.Sep.94: Added Multicast support (not tested yet) (MH) + *  + * 18.Sep.94: Workaround for 'EL-Bug'. Removed flexible RBD-handling.  + *            Now, every RFD has exact one RBD. (MH) + * + * 14.Sep.94: added promiscuous mode, a few cleanups (MH) + * + * 19.Aug.94: changed request_irq() parameter (MH) + *  + * 20.July.94: removed cleanup bugs, removed a 16K-mem-probe-bug (MH) + * + * 19.July.94: lotsa cleanups .. (MH) + * + * 17.July.94: some patches ... verified to run with 1.1.29 (MH) + * + * 4.July.94: patches for Linux 1.1.24  (MH) + * + * 26.March.94: patches for Linux 1.0 and iomem-auto-probe (MH) + * + * 30.Sep.93: Added nop-chain .. driver now runs with only one Xmit-Buff, too (MH) + * + * < 30.Sep.93: first versions  + */ + +static int debuglevel = 0; /* debug-printk 0: off 1: a few 2: more */ +static int automatic_resume = 0; /* experimental .. better should be zero */ +static int rfdadd = 0; /* rfdadd=1 may be better for 8K MEM cards */ +static int fifo=0x8;	/* don't change */ + +/* #define REALLY_SLOW_IO */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "ni52.h" + +#define DEBUG       /* debug on */ +#define SYSBUSVAL 1 /* 8 Bit */ + +#define ni_attn586()  {outb(0,dev->base_addr+NI52_ATTENTION);} +#define ni_reset586() {outb(0,dev->base_addr+NI52_RESET);} +#define ni_disint()   {outb(0,dev->base_addr+NI52_INTDIS);} +#define ni_enaint()   {outb(0,dev->base_addr+NI52_INTENA);} + +#define make32(ptr16) (p->memtop + (short) (ptr16) ) +#define make24(ptr32) ((char *) (ptr32) - p->base) +#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop )) + +/******************* how to calculate the buffers ***************************** + +  * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works +  * --------------- in a different (more stable?) mode. Only in this mode it's +  *                 possible to configure the driver with 'NO_NOPCOMMANDS' + +sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8; +sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT +sizeof(rfd) = 24; sizeof(rbd) = 12;  +sizeof(tbd) = 8; sizeof(transmit_cmd) = 16; +sizeof(nop_cmd) = 8;  + +  * if you don't know the driver, better do not change these values: */ + +#define RECV_BUFF_SIZE 1524 /* slightly oversized */ +#define XMIT_BUFF_SIZE 1524 /* slightly oversized */ +#define NUM_XMIT_BUFFS 1    /* config for both, 8K and 16K shmem */ +#define NUM_RECV_BUFFS_8  4 /* config for 8K shared mem */ +#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */ +#define NO_NOPCOMMANDS      /* only possible with NUM_XMIT_BUFFS=1 */ + +/**************************************************************************/ + +/* different DELAYs */ +#define DELAY(x) __delay((loops_per_sec>>5)*(x));  +#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); } +#define DELAY_18(); { __delay( (loops_per_sec>>18)+1 ); } + +/* wait for command with timeout: */ +#define WAIT_4_SCB_CMD() { int i; \ +  for(i=0;i<16384;i++) { \ +    if(!p->scb->cmd_cuc) break; \ +    DELAY_18(); \ +    if(i == 16383) { \ +      printk("%s: scb_cmd timed out: %04x,%04x .. disabling i82586!!\n",dev->name,p->scb->cmd_cuc,p->scb->cus); \ +       if(!p->reseted) { p->reseted = 1; ni_reset586(); } } } } + +#define WAIT_4_SCB_CMD_RUC() { int i; \ +  for(i=0;i<16384;i++) { \ +    if(!p->scb->cmd_ruc) break; \ +    DELAY_18(); \ +    if(i == 16383) { \ +      printk("%s: scb_cmd (ruc) timed out: %04x,%04x .. disabling i82586!!\n",dev->name,p->scb->cmd_ruc,p->scb->rus); \ +       if(!p->reseted) { p->reseted = 1; ni_reset586(); } } } } + +#define WAIT_4_STAT_COMPL(addr) { int i; \ +   for(i=0;i<32767;i++) { \ +     if((addr)->cmd_status & STAT_COMPL) break; \ +     DELAY_16(); DELAY_16(); } } + +#define NI52_TOTAL_SIZE 16 +#define NI52_ADDR0 0x02 +#define NI52_ADDR1 0x07 +#define NI52_ADDR2 0x01 + +static int     ni52_probe1(struct device *dev,int ioaddr); +static void    ni52_interrupt(int irq,void *dev_id,struct pt_regs *reg_ptr); +static int     ni52_open(struct device *dev); +static int     ni52_close(struct device *dev); +static int     ni52_send_packet(struct sk_buff *,struct device *); +static struct  enet_statistics *ni52_get_stats(struct device *dev); +static void    set_multicast_list(struct device *dev); +#if 0 +static void    ni52_dump(struct device *,void *); +#endif + +/* helper-functions */ +static int     init586(struct device *dev); +static int     check586(struct device *dev,char *where,unsigned size); +static void    alloc586(struct device *dev); +static void    startrecv586(struct device *dev); +static void   *alloc_rfa(struct device *dev,void *ptr); +static void    ni52_rcv_int(struct device *dev); +static void    ni52_xmt_int(struct device *dev); +static void    ni52_rnr_int(struct device *dev); + +struct priv +{ +  struct enet_statistics stats; +  unsigned long base; +  char *memtop; +  int lock,reseted; +  volatile struct rfd_struct  *rfd_last,*rfd_top,*rfd_first; +  volatile struct scp_struct  *scp;  /* volatile is important */ +  volatile struct iscp_struct *iscp; /* volatile is important */ +  volatile struct scb_struct  *scb;  /* volatile is important */ +  volatile struct tbd_struct  *xmit_buffs[NUM_XMIT_BUFFS]; +  volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS]; +#if (NUM_XMIT_BUFFS == 1) +  volatile struct nop_cmd_struct *nop_cmds[2]; +#else +  volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS]; +#endif +  volatile int    nop_point,num_recv_buffs; +  volatile char  *xmit_cbuffs[NUM_XMIT_BUFFS]; +  volatile int    xmit_count,xmit_last; +}; + +/********************************************** + * close device  + */ +static int ni52_close(struct device *dev) +{ +  free_irq(dev->irq, NULL); +  irq2dev_map[dev->irq] = NULL; + +  ni_reset586(); /* the hard way to stop the receiver */ + +  dev->start = 0; +  dev->tbusy = 0; + +  MOD_DEC_USE_COUNT; + +  return 0; +} + +/********************************************** + * open device  + */ +static int ni52_open(struct device *dev) +{ +  ni_disint(); +  alloc586(dev); +  init586(dev);   +  startrecv586(dev); +  ni_enaint(); + +  if(request_irq(dev->irq, &ni52_interrupt,0,"ni5210",NULL))  +  { +    ni_reset586(); +    return -EAGAIN; +  }   +  irq2dev_map[dev->irq] = dev; + +  dev->interrupt = 0; +  dev->tbusy = 0; +  dev->start = 1; + +  MOD_INC_USE_COUNT; + +  return 0; /* most done by init */ +} + +/********************************************** + * Check to see if there's an 82586 out there.  + */ +static int check586(struct device *dev,char *where,unsigned size) +{ +  struct priv pb; +  struct priv *p = /* (struct priv *) dev->priv*/ &pb; +  char *iscp_addrs[2]; +  int i; + +  p->base = (unsigned long) where + size - 0x01000000; +  p->memtop = where + size; +  p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS); +  memset((char *)p->scp,0, sizeof(struct scp_struct)); +  for(i=0;i<sizeof(struct scp_struct);i++) /* memory was writeable? */ +    if(((char *)p->scp)[i]) +      return 0; +  p->scp->sysbus = SYSBUSVAL;        /* 1 = 8Bit-Bus, 0 = 16 Bit */ +  if(p->scp->sysbus != SYSBUSVAL) +    return 0; +   +  iscp_addrs[0] = where; +  iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct); + +  for(i=0;i<2;i++) +  { +    p->iscp = (struct iscp_struct *) iscp_addrs[i]; +    memset((char *)p->iscp,0, sizeof(struct iscp_struct)); + +    p->scp->iscp = make24(p->iscp); +    p->iscp->busy = 1; + +    ni_reset586(); +    ni_attn586(); +    DELAY(1);	/* wait a while... */ + +    if(p->iscp->busy) /* i82586 clears 'busy' after successful init */ +      return 0; +  } +  return 1; +} + +/****************************************************************** + * set iscp at the right place, called by ni52_probe1 and open586.  + */ +void alloc586(struct device *dev) +{ +  struct priv *p =  (struct priv *) dev->priv;  + +  ni_reset586(); +  DELAY(1); + +  p->scp  = (struct scp_struct *)  (p->base + SCP_DEFAULT_ADDRESS); +  p->scb  = (struct scb_struct *)  (dev->mem_start); +  p->iscp = (struct iscp_struct *) ((char *)p->scp - sizeof(struct iscp_struct)); + +  memset((char *) p->iscp,0,sizeof(struct iscp_struct)); +  memset((char *) p->scp ,0,sizeof(struct scp_struct)); + +  p->scp->iscp = make24(p->iscp); +  p->scp->sysbus = SYSBUSVAL; +  p->iscp->scb_offset = make16(p->scb); + +  p->iscp->busy = 1; +  ni_reset586(); +  ni_attn586(); + +  DELAY(1);  + +  if(p->iscp->busy) +    printk("%s: Init-Problems (alloc).\n",dev->name); + +  p->reseted = 0; + +  memset((char *)p->scb,0,sizeof(struct scb_struct)); +} + +/********************************************** + * probe the ni5210-card + */ +int ni52_probe(struct device *dev) +{ +#ifndef MODULE +  int *port; +  static int ports[] = {0x300, 0x280, 0x360 , 0x320 , 0x340, 0}; +#endif +  int base_addr = dev->base_addr; + +  if (base_addr > 0x1ff)		/* Check a single specified location. */ +    if( (inb(base_addr+NI52_MAGIC1) == NI52_MAGICVAL1) && +        (inb(base_addr+NI52_MAGIC2) == NI52_MAGICVAL2)) +      return ni52_probe1(dev, base_addr); +  else if (base_addr > 0)		/* Don't probe at all. */ +    return ENXIO; + +#ifdef MODULE +  printk("%s: no autoprobing allowed for modules.\n",dev->name); +#else +  for (port = ports; *port; port++) { +    int ioaddr = *port; +    if (check_region(ioaddr, NI52_TOTAL_SIZE)) +      continue; +    if( !(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) ||  +        !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2)) +      continue; + +    dev->base_addr = ioaddr; +    if (ni52_probe1(dev, ioaddr) == 0) +      return 0; +  } + +#ifdef FULL_IO_PROBE +  for(dev->base_addr=0x200;dev->base_addr<0x400;dev->base_addr+=8) +  { +    int ioaddr = dev->base_addr; +    if (check_region(ioaddr, NI52_TOTAL_SIZE)) +      continue; +    if( !(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) ||  +        !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2)) +      continue; +    if (ni52_probe1(dev, ioaddr) == 0) +      return 0;     +  } +#endif + +#endif + +  dev->base_addr = base_addr; +  return ENODEV; +} + +static int ni52_probe1(struct device *dev,int ioaddr) +{ +  int i,size; + +  for(i=0;i<ETH_ALEN;i++) +    dev->dev_addr[i] = inb(dev->base_addr+i); + +  if(dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1 +     || dev->dev_addr[2] != NI52_ADDR2) +    return ENODEV; + +  printk("%s: NI5210 found at %#3lx, ",dev->name,dev->base_addr); + +  request_region(ioaddr,NI52_TOTAL_SIZE,"ni5210"); + +  /*  +   * check (or search) IO-Memory, 8K and 16K +   */ +#ifdef MODULE +  size = dev->mem_end - dev->mem_start; +  if(size != 0x2000 && size != 0x4000) +  { +    printk("\n%s: Illegal memory size %d. Allowed is 0x2000 or 0x4000 bytes.\n",dev->name,size); +    return ENODEV; +  } +  if(!check586(dev,(char *) dev->mem_start,size)) +  { +    printk("?memcheck, Can't find memory at 0x%lx with size %d!\n",dev->mem_start,size); +    return ENODEV; +  } +#else +  if(dev->mem_start != 0) /* no auto-mem-probe */ +  { +    size = 0x4000; /* check for 16K mem */ +    if(!check586(dev,(char *) dev->mem_start,size)) { +      size = 0x2000; /* check for 8K mem */ +      if(!check586(dev,(char *) dev->mem_start,size)) { +        printk("?memprobe, Can't find memory at 0x%lx!\n",dev->mem_start); +        return ENODEV; +      } +    } +  } +  else   +  { +   static long memaddrs[] = { 0xc8000,0xca000,0xcc000,0xce000,0xd0000,0xd2000, +                              0xd4000,0xd6000,0xd8000,0xda000,0xdc000, 0 }; +   for(i=0;;i++) +    { +      if(!memaddrs[i]) { +        printk("?memprobe, Can't find io-memory!\n"); +        return ENODEV; +      } +      dev->mem_start = memaddrs[i]; +      size = 0x2000; /* check for 8K mem */ +      if(check586(dev,(char *)dev->mem_start,size)) /* 8K-check */ +        break; +      size = 0x4000; /* check for 16K mem */ +      if(check586(dev,(char *)dev->mem_start,size)) /* 16K-check */ +        break; +    } +  } +  dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */ +#endif + +  dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);  +  if(dev->priv == NULL) +  { +    printk("%s: Ooops .. can't allocate private driver memory.\n",dev->name); +    return -ENOMEM; +  } +                                  /* warning: we don't free it on errors */ +  memset((char *) dev->priv,0,sizeof(struct priv)); +   +  ((struct priv *) (dev->priv))->memtop = (char *) dev->mem_start + size; +  ((struct priv *) (dev->priv))->base =  dev->mem_start + size - 0x01000000; +  alloc586(dev); + +  /* set number of receive-buffs according to memsize */ +  if(size == 0x2000) +    ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_8; +  else +    ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_16; + +  printk("Memaddr: 0x%lx, Memsize: %d, ",dev->mem_start,size); + +  if(dev->irq < 2) +  { +    autoirq_setup(0); +    ni_reset586(); +    ni_attn586(); +    if(!(dev->irq = autoirq_report(2))) +    { +      printk("?autoirq, Failed to detect IRQ line!\n");  +      return 1; +    } +    printk("IRQ %d (autodetected).\n",dev->irq); +  } +  else  { +    if(dev->irq == 2) +      dev->irq = 9; +    printk("IRQ %d (assigned and not checked!).\n",dev->irq); +  } + +  dev->open            = &ni52_open; +  dev->stop            = &ni52_close; +  dev->get_stats       = &ni52_get_stats; +  dev->hard_start_xmit = &ni52_send_packet; +  dev->set_multicast_list = &set_multicast_list; + +  dev->if_port 	       = 0; + +  ether_setup(dev); + +  dev->tbusy = 0; +  dev->interrupt = 0; +  dev->start = 0; +   +  return 0; +} + +/**********************************************  + * init the chip (ni52-interrupt should be disabled?!) + * needs a correct 'allocated' memory + */ + +static int init586(struct device *dev) +{ +  void *ptr; +  int i,result=0; +  struct priv *p = (struct priv *) dev->priv; +  volatile struct configure_cmd_struct  *cfg_cmd; +  volatile struct iasetup_cmd_struct *ias_cmd; +  volatile struct tdr_cmd_struct *tdr_cmd; +  volatile struct mcsetup_cmd_struct *mc_cmd; +  struct dev_mc_list *dmi=dev->mc_list; +  int num_addrs=dev->mc_count; + +  ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct)); + +  cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */ +  cfg_cmd->cmd_status = 0; +  cfg_cmd->cmd_cmd    = CMD_CONFIGURE | CMD_LAST; +  cfg_cmd->cmd_link   = 0xffff; + +  cfg_cmd->byte_cnt   = 0x0a; /* number of cfg bytes */ +  cfg_cmd->fifo       = fifo; /* fifo-limit (8=tx:32/rx:64) */ +  cfg_cmd->sav_bf     = 0x40; /* hold or discard bad recv frames (bit 7) */ +  cfg_cmd->adr_len    = 0x2e; /* addr_len |!src_insert |pre-len |loopback */ +  cfg_cmd->priority   = 0x00; +  cfg_cmd->ifs        = 0x60; +  cfg_cmd->time_low   = 0x00; +  cfg_cmd->time_high  = 0xf2; +  cfg_cmd->promisc    = 0; +  if(dev->flags & IFF_ALLMULTI) { +    int len = ((char *) p->iscp - (char *) ptr - 8) / 6; +    if(num_addrs > len)  { +      printk("%s: switching to promisc. mode\n",dev->name); +      dev->flags|=IFF_PROMISC; +    } +  } +  if(dev->flags&IFF_PROMISC) +  { +       cfg_cmd->promisc=1; +       dev->flags|=IFF_PROMISC; +  } +  cfg_cmd->carr_coll  = 0x00; +  +  p->scb->cbl_offset = make16(cfg_cmd); +  p->scb->cmd_ruc = 0; + +  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */ +  ni_attn586(); + +  WAIT_4_STAT_COMPL(cfg_cmd); + +  if((cfg_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK)) +  { +    printk("%s: configure command failed: %x\n",dev->name,cfg_cmd->cmd_status); +    return 1;  +  } + +    /* +     * individual address setup +     */ +  ias_cmd = (struct iasetup_cmd_struct *)ptr; + +  ias_cmd->cmd_status = 0; +  ias_cmd->cmd_cmd    = CMD_IASETUP | CMD_LAST; +  ias_cmd->cmd_link   = 0xffff; + +  memcpy((char *)&ias_cmd->iaddr,(char *) dev->dev_addr,ETH_ALEN); + +  p->scb->cbl_offset = make16(ias_cmd); + +  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */ +  ni_attn586(); + +  WAIT_4_STAT_COMPL(ias_cmd); + +  if((ias_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) { +    printk("%s (ni52): individual address setup command failed: %04x\n",dev->name,ias_cmd->cmd_status); +    return 1;  +  } + +   /*  +    * TDR, wire check .. e.g. no resistor e.t.c  +    */ +  tdr_cmd = (struct tdr_cmd_struct *)ptr; + +  tdr_cmd->cmd_status  = 0; +  tdr_cmd->cmd_cmd     = CMD_TDR | CMD_LAST; +  tdr_cmd->cmd_link    = 0xffff; +  tdr_cmd->status      = 0; + +  p->scb->cbl_offset = make16(tdr_cmd); +  p->scb->cmd_cuc = CUC_START; /* cmd.-unit start */ +  ni_attn586(); + +  WAIT_4_STAT_COMPL(tdr_cmd); + +  if(!(tdr_cmd->cmd_status & STAT_COMPL)) +  { +    printk("%s: Problems while running the TDR.\n",dev->name); +  } +  else +  { +    DELAY_16(); /* wait for result */ +    result = tdr_cmd->status; + +    p->scb->cmd_cuc = p->scb->cus & STAT_MASK; +    ni_attn586(); /* ack the interrupts */ + +    if(result & TDR_LNK_OK)  +      ; +    else if(result & TDR_XCVR_PRB) +      printk("%s: TDR: Transceiver problem. Check the cable(s)!\n",dev->name); +    else if(result & TDR_ET_OPN) +      printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK); +    else if(result & TDR_ET_SRT)  +    { +      if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */ +        printk("%s: TDR: Detected a short circuit %d clocks away.\n",dev->name,result & TDR_TIMEMASK); +    } +    else +      printk("%s: TDR: Unknown status %04x\n",dev->name,result); +  } + +  /* +   * Multicast setup +   */ +  if(num_addrs && !(dev->flags & IFF_PROMISC) ) +  { +    mc_cmd = (struct mcsetup_cmd_struct *) ptr; +    mc_cmd->cmd_status = 0; +    mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST; +    mc_cmd->cmd_link = 0xffff; +    mc_cmd->mc_cnt = num_addrs * 6; + +    for(i=0;i<num_addrs;i++,dmi=dmi->next) +      memcpy((char *) mc_cmd->mc_list[i], dmi->dmi_addr,6); + +    p->scb->cbl_offset = make16(mc_cmd); +    p->scb->cmd_cuc = CUC_START; +    ni_attn586(); + +    WAIT_4_STAT_COMPL(mc_cmd); + +    if( (mc_cmd->cmd_status & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) ) +      printk("%s: Can't apply multicast-address-list.\n",dev->name); +  } + +   /* +    * alloc nop/xmit-cmds +    */ +#if (NUM_XMIT_BUFFS == 1) +  for(i=0;i<2;i++) +  { +    p->nop_cmds[i] = (struct nop_cmd_struct *)ptr; +    p->nop_cmds[i]->cmd_cmd    = CMD_NOP; +    p->nop_cmds[i]->cmd_status = 0; +    p->nop_cmds[i]->cmd_link   = make16((p->nop_cmds[i])); +    ptr = (char *) ptr + sizeof(struct nop_cmd_struct); +  } +#else +  for(i=0;i<NUM_XMIT_BUFFS;i++) +  { +    p->nop_cmds[i] = (struct nop_cmd_struct *)ptr; +    p->nop_cmds[i]->cmd_cmd    = CMD_NOP; +    p->nop_cmds[i]->cmd_status = 0; +    p->nop_cmds[i]->cmd_link   = make16((p->nop_cmds[i])); +    ptr = (char *) ptr + sizeof(struct nop_cmd_struct); +  } +#endif + +  ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */  + +  /* +   * alloc xmit-buffs / init xmit_cmds +   */ +  for(i=0;i<NUM_XMIT_BUFFS;i++) +  { +    p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/ +    ptr = (char *) ptr + sizeof(struct transmit_cmd_struct); +    p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */ +    ptr = (char *) ptr + XMIT_BUFF_SIZE; +    p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */ +    ptr = (char *) ptr + sizeof(struct tbd_struct); +    if((void *)ptr > (void *)p->iscp)  +    { +      printk("%s: not enough shared-mem for your configuration!\n",dev->name); +      return 1; +    }    +    memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct)); +    memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct)); +    p->xmit_cmds[i]->cmd_link = make16(p->nop_cmds[(i+1)%NUM_XMIT_BUFFS]); +    p->xmit_cmds[i]->cmd_status = STAT_COMPL; +    p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT; +    p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i])); +    p->xmit_buffs[i]->next = 0xffff; +    p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i])); +  } + +  p->xmit_count = 0;  +  p->xmit_last  = 0; +#ifndef NO_NOPCOMMANDS +  p->nop_point  = 0; +#endif + +   /* +    * 'start transmitter' +    */ +#ifndef NO_NOPCOMMANDS +  p->scb->cbl_offset = make16(p->nop_cmds[0]); +  p->scb->cmd_cuc = CUC_START; +  ni_attn586(); +  WAIT_4_SCB_CMD(); +#else +  p->xmit_cmds[0]->cmd_link = make16(p->xmit_cmds[0]); +  p->xmit_cmds[0]->cmd_cmd  = CMD_XMIT | CMD_SUSPEND | CMD_INT; +#endif + +  /* +   * ack. interrupts +   */ +  p->scb->cmd_cuc = p->scb->cus & STAT_MASK; +  ni_attn586(); +  DELAY_16(); + +  ni_enaint(); + +  return 0; +} + +/****************************************************** + * This is a helper routine for ni52_rnr_int() and init586().  + * It sets up the Receive Frame Area (RFA). + */ + +static void *alloc_rfa(struct device *dev,void *ptr)  +{ +  volatile struct rfd_struct *rfd = (struct rfd_struct *)ptr; +  volatile struct rbd_struct *rbd; +  int i; +  struct priv *p = (struct priv *) dev->priv; + +  memset((char *) rfd,0,sizeof(struct rfd_struct)*(p->num_recv_buffs+rfdadd)); +  p->rfd_first = rfd; + +  for(i = 0; i < (p->num_recv_buffs+rfdadd); i++) { +    rfd[i].next = make16(rfd + (i+1) % (p->num_recv_buffs+rfdadd) ); +    rfd[i].rbd_offset = 0xffff; +  } +  rfd[p->num_recv_buffs-1+rfdadd].last = RFD_SUSP;   /* RU suspend */ + +  ptr = (void *) (rfd + (p->num_recv_buffs + rfdadd) ); + +  rbd = (struct rbd_struct *) ptr; +  ptr = (void *) (rbd + p->num_recv_buffs); + +   /* clr descriptors */ +  memset((char *) rbd,0,sizeof(struct rbd_struct)*(p->num_recv_buffs)); + +  for(i=0;i<p->num_recv_buffs;i++) +  { +    rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs)); +    rbd[i].size = RECV_BUFF_SIZE; +    rbd[i].buffer = make24(ptr); +    ptr = (char *) ptr + RECV_BUFF_SIZE; +  } + +  p->rfd_top  = p->rfd_first; +  p->rfd_last = p->rfd_first + (p->num_recv_buffs - 1 + rfdadd); + +  p->scb->rfa_offset		= make16(p->rfd_first); +  p->rfd_first->rbd_offset	= make16(rbd); + +  return ptr; +} + + +/************************************************** + * Interrupt Handler ... + */ + +static void ni52_interrupt(int irq,void *dev_id,struct pt_regs *reg_ptr) +{ +  struct device *dev = (struct device *) irq2dev_map[irq]; +  unsigned short stat; +  int cnt=0; +  struct priv *p; + +  if (!dev) { +    printk ("ni5210-interrupt: irq %d for unknown device.\n",irq); +    return; +  } +  p = (struct priv *) dev->priv; + +  if(debuglevel > 1) +    printk("I"); + +  dev->interrupt = 1; + +  WAIT_4_SCB_CMD(); /* wait for last command  */ + +  while((stat=p->scb->cus & STAT_MASK)) +  { +    p->scb->cmd_cuc = stat; +    ni_attn586(); + +    if(stat & STAT_FR)   /* received a frame */ +      ni52_rcv_int(dev); + +    if(stat & STAT_RNR) /* RU went 'not ready' */ +    { +      printk("(R)"); +      if(p->scb->rus & RU_SUSPEND) /* special case: RU_SUSPEND */ +      { +        WAIT_4_SCB_CMD(); +        p->scb->cmd_ruc = RUC_RESUME; +        ni_attn586(); +        WAIT_4_SCB_CMD_RUC(); +      } +      else +      { +        printk("%s: Receiver-Unit went 'NOT READY': %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->rus); +        ni52_rnr_int(dev);  +      } +    } + +    if(stat & STAT_CX)    /* command with I-bit set complete */ +       ni52_xmt_int(dev); + +#ifndef NO_NOPCOMMANDS +    if(stat & STAT_CNA)  /* CU went 'not ready' */ +    { +      if(dev->start) +        printk("%s: oops! CU has left active state. stat: %04x/%02x.\n",dev->name,(int) stat,(int) p->scb->cus); +    } +#endif + +    if(debuglevel > 1) +      printk("%d",cnt++); + +    WAIT_4_SCB_CMD(); /* wait for ack. (ni52_xmt_int can be faster than ack!!) */ +    if(p->scb->cmd_cuc)   /* timed out? */ +    { +      printk("%s: Acknowledge timed out.\n",dev->name); +      ni_disint(); +      break; +    } +  } + +  if(debuglevel > 1) +    printk("i"); + +  dev->interrupt = 0; +} + +/******************************************************* + * receive-interrupt + */ + +static void ni52_rcv_int(struct device *dev) +{ +  int status,cnt=0; +  unsigned short totlen; +  struct sk_buff *skb; +  struct rbd_struct *rbd; +  struct priv *p = (struct priv *) dev->priv; + +  if(debuglevel > 0) +    printk("R"); + +  for(;(status = p->rfd_top->stat_high) & RFD_COMPL;) +  { +      rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset); + +      if(status & RFD_OK) /* frame received without error? */ +      { +        if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */ +        { +          totlen &= RBD_MASK; /* length of this frame */ +          rbd->status = 0; +          skb = (struct sk_buff *) dev_alloc_skb(totlen+2); +          if(skb != NULL) +          { +            skb->dev = dev; +            skb_reserve(skb,2); +            skb_put(skb,totlen); +            eth_copy_and_sum(skb,(char *) p->base+(unsigned long) rbd->buffer,totlen,0); +            skb->protocol=eth_type_trans(skb,dev); +            netif_rx(skb); +            p->stats.rx_packets++; +          } +          else +            p->stats.rx_dropped++; +        } +        else +        { +          int rstat; +             /* free all RBD's until RBD_LAST is set */ +          totlen = 0; +          while(!((rstat=rbd->status) & RBD_LAST)) +          { +            totlen += rstat & RBD_MASK; +            if(!rstat) +            { +              printk("%s: Whoops .. no end mark in RBD list\n",dev->name); +              break; +            } +            rbd->status = 0; +            rbd = (struct rbd_struct *) make32(rbd->next); +          } +          totlen += rstat & RBD_MASK; +          rbd->status = 0; +          printk("%s: received oversized frame! length: %d\n",dev->name,totlen); +          p->stats.rx_dropped++; +       } +      } +      else /* frame !(ok), only with 'save-bad-frames' */ +      { +        printk("%s: oops! rfd-error-status: %04x\n",dev->name,status); +        p->stats.rx_errors++; +      } +      p->rfd_top->stat_high = 0; +      p->rfd_top->last = RFD_SUSP; /* maybe exchange by RFD_LAST */ +      p->rfd_top->rbd_offset = 0xffff; +      p->rfd_last->last = 0;        /* delete RFD_SUSP  */ +      p->rfd_last = p->rfd_top; +      p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */ +      p->scb->rfa_offset = make16(p->rfd_top); + +      if(debuglevel > 0) +        printk("%d",cnt++); +  } + +  if(automatic_resume) +  { +    WAIT_4_SCB_CMD(); +    p->scb->cmd_ruc = RUC_RESUME; +    ni_attn586(); +    WAIT_4_SCB_CMD_RUC(); +  } + +#ifdef WAIT_4_BUSY +  { +    int i; +    for(i=0;i<1024;i++) +    { +      if(p->rfd_top->status) +        break; +      DELAY_16(); +      if(i == 1023) +        printk("%s: RU hasn't fetched next RFD (not busy/complete)\n",dev->name); +    } +  } +#endif + +#if 0 +  if(!at_least_one) +  {  +    int i; +    volatile struct rfd_struct *rfds=p->rfd_top; +    volatile struct rbd_struct *rbds; +    printk("%s: received a FC intr. without having a frame: %04x %d\n",dev->name,status,old_at_least); +    for(i=0;i< (p->num_recv_buffs+4);i++) +    { +      rbds = (struct rbd_struct *) make32(rfds->rbd_offset); +      printk("%04x:%04x ",rfds->status,rbds->status); +      rfds = (struct rfd_struct *) make32(rfds->next); +    } +    printk("\nerrs: %04x %04x stat: %04x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->status); +    printk("\nerrs: %04x %04x rus: %02x, cus: %02x\n",(int)p->scb->rsc_errs,(int)p->scb->ovrn_errs,(int)p->scb->rus,(int)p->scb->cus);   +  } +  old_at_least = at_least_one; +#endif + +  if(debuglevel > 0) +    printk("r"); +} + +/********************************************************** + * handle 'Receiver went not ready'. + */ + +static void ni52_rnr_int(struct device *dev) +{ +  struct priv *p = (struct priv *) dev->priv; + +  p->stats.rx_errors++; + +  WAIT_4_SCB_CMD();    /* wait for the last cmd, WAIT_4_FULLSTAT?? */ +  p->scb->cmd_ruc = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */ +  ni_attn586();  +  WAIT_4_SCB_CMD_RUC();    /* wait for accept cmd. */ + +  alloc_rfa(dev,(char *)p->rfd_first); +/* maybe add a check here, before restarting the RU */ +  startrecv586(dev); /* restart RU */ + +  printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->rus); + +} + +/********************************************************** + * handle xmit - interrupt + */ + +static void ni52_xmt_int(struct device *dev) +{ +  int status; +  struct priv *p = (struct priv *) dev->priv; + +  if(debuglevel > 0) +    printk("X"); + +  status = p->xmit_cmds[p->xmit_last]->cmd_status; +  if(!(status & STAT_COMPL)) +    printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name); + +  if(status & STAT_OK) +  { +    p->stats.tx_packets++; +    p->stats.collisions += (status & TCMD_MAXCOLLMASK); +  } +  else  +  { +    p->stats.tx_errors++; +    if(status & TCMD_LATECOLL) { +      printk("%s: late collision detected.\n",dev->name); +      p->stats.collisions++; +    }  +    else if(status & TCMD_NOCARRIER) { +      p->stats.tx_carrier_errors++; +      printk("%s: no carrier detected.\n",dev->name); +    }  +    else if(status & TCMD_LOSTCTS)  +      printk("%s: loss of CTS detected.\n",dev->name); +    else if(status & TCMD_UNDERRUN) { +      p->stats.tx_fifo_errors++; +      printk("%s: DMA underrun detected.\n",dev->name); +    } +    else if(status & TCMD_MAXCOLL) { +      printk("%s: Max. collisions exceeded.\n",dev->name); +      p->stats.collisions += 16; +    }  +  } + +#if (NUM_XMIT_BUFFS > 1) +  if( (++p->xmit_last) == NUM_XMIT_BUFFS)  +    p->xmit_last = 0; +#endif + +  dev->tbusy = 0; +  mark_bh(NET_BH); +} + +/*********************************************************** + * (re)start the receiver + */  + +static void startrecv586(struct device *dev) +{ +  struct priv *p = (struct priv *) dev->priv; + +  WAIT_4_SCB_CMD(); +  WAIT_4_SCB_CMD_RUC(); +  p->scb->rfa_offset = make16(p->rfd_first); +  p->scb->cmd_ruc = RUC_START; +  ni_attn586();		/* start cmd. */ +  WAIT_4_SCB_CMD_RUC();	/* wait for accept cmd. (no timeout!!) */ +} + +/****************************************************** + * send frame  + */ + +static int ni52_send_packet(struct sk_buff *skb, struct device *dev) +{ +  int len,i; +#ifndef NO_NOPCOMMANDS +  int next_nop; +#endif +  struct priv *p = (struct priv *) dev->priv; + +  if(dev->tbusy) +  { +    int tickssofar = jiffies - dev->trans_start; +    if (tickssofar < 5) +      return 1; + +#ifndef NO_NOPCOMMANDS +    if(p->scb->cus & CU_ACTIVE) /* COMMAND-UNIT active? */ +    { +      dev->tbusy = 0; +#ifdef DEBUG +      printk("%s: strange ... timeout with CU active?!?\n",dev->name); +      printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point); +#endif +      p->scb->cmd_cuc = CUC_ABORT; +      ni_attn586(); +      WAIT_4_SCB_CMD(); +      p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]); +      p->scb->cmd_cuc = CUC_START; +      ni_attn586(); +      WAIT_4_SCB_CMD(); +      dev->trans_start = jiffies; +      dev_kfree_skb(skb, FREE_WRITE); +      return 0; +    } +    else +#endif +    { +#ifdef DEBUG +      printk("%s: xmitter timed out, try to restart! stat: %02x\n",dev->name,p->scb->cus); +      printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status); +      printk("%s: check, whether you set the right interrupt number!\n",dev->name); +#endif +      ni52_close(dev); +      ni52_open(dev); +    } +    dev->trans_start = jiffies; +    return 0; +  } + +  if(skb == NULL) +  { +    dev_tint(dev); +    return 0; +  } + +  if (skb->len <= 0) +    return 0; +  if(skb->len > XMIT_BUFF_SIZE) +  { +    printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %ld bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len); +    return 0; +  } + +  if (set_bit(0, (void*)&dev->tbusy)) { +    printk("%s: Transmitter access conflict.\n", dev->name); +    return 1; +  } +#if(NUM_XMIT_BUFFS > 1) +  else if(set_bit(0,(void *) &p->lock)) { +    printk("%s: Queue was locked\n",dev->name); +    return 1; +  } +#endif +  else +  { +    memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len); +    len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + +#if (NUM_XMIT_BUFFS == 1) +#  ifdef NO_NOPCOMMANDS + +#ifdef DEBUG +    if(p->scb->cus & CU_ACTIVE) +    { +      printk("%s: Hmmm .. CU is still running and we wanna send a new packet.\n",dev->name); +      printk("%s: stat: %04x %04x\n",dev->name,p->scb->cus,p->xmit_cmds[0]->cmd_status); +    } +#endif + +    p->xmit_buffs[0]->size = TBD_LAST | len; +    for(i=0;i<16;i++) +    { +      p->xmit_cmds[0]->cmd_status = 0; +      WAIT_4_SCB_CMD(); +      if( (p->scb->cus & CU_STATUS) == CU_SUSPEND) +        p->scb->cmd_cuc = CUC_RESUME; +      else +      { +        p->scb->cbl_offset = make16(p->xmit_cmds[0]); +        p->scb->cmd_cuc = CUC_START; +      } + +      ni_attn586(); +      dev->trans_start = jiffies; +      if(!i) +        dev_kfree_skb(skb,FREE_WRITE); +      WAIT_4_SCB_CMD(); +      if( (p->scb->cus & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */ +        break; +      if(p->xmit_cmds[0]->cmd_status) +        break; +      if(i==15) +        printk("%s: Can't start transmit-command.\n",dev->name); +    } +#  else +    next_nop = (p->nop_point + 1) & 0x1; +    p->xmit_buffs[0]->size = TBD_LAST | len; + +    p->xmit_cmds[0]->cmd_link   = p->nop_cmds[next_nop]->cmd_link  +                                = make16((p->nop_cmds[next_nop])); +    p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0; + +    p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0])); +    dev->trans_start = jiffies; +    p->nop_point = next_nop; +    dev_kfree_skb(skb,FREE_WRITE); +#  endif +#else +    p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len; +    if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS )  +      next_nop = 0; + +    p->xmit_cmds[p->xmit_count]->cmd_status  = 0; +	/* linkpointer of xmit-command already points to next nop cmd */ +    p->nop_cmds[next_nop]->cmd_link = make16((p->nop_cmds[next_nop])); +    p->nop_cmds[next_nop]->cmd_status = 0; + +    p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count])); +    dev->trans_start = jiffies; +    p->xmit_count = next_nop; +  +    { +      long flags;  +      save_flags(flags); +      cli(); +      if(p->xmit_count != p->xmit_last) +        dev->tbusy = 0; +      p->lock = 0; +      restore_flags(flags); +    } +    dev_kfree_skb(skb,FREE_WRITE); +#endif +  } +  return 0; +} + +/******************************************* + * Someone wanna have the statistics  + */ + +static struct enet_statistics *ni52_get_stats(struct device *dev) +{ +  struct priv *p = (struct priv *) dev->priv; +  unsigned short crc,aln,rsc,ovrn; + +  crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */ +  p->scb->crc_errs = 0; +  aln = p->scb->aln_errs; +  p->scb->aln_errs = 0; +  rsc = p->scb->rsc_errs; +  p->scb->rsc_errs = 0; +  ovrn = p->scb->ovrn_errs; +  p->scb->ovrn_errs = 0; + +  p->stats.rx_crc_errors += crc; +  p->stats.rx_fifo_errors += ovrn; +  p->stats.rx_frame_errors += aln; +  p->stats.rx_dropped += rsc; + +  return &p->stats; +} + +/******************************************************** + * Set MC list ..   + */ +static void set_multicast_list(struct device *dev) +{ +  if(!dev->start) +  { +    printk("%s: Can't apply promiscuous/multicastmode to a not running interface.\n",dev->name); +    return; +  } + +  dev->start = 0; + +  ni_disint(); +  alloc586(dev); +  init586(dev);   +  startrecv586(dev); +  ni_enaint(); + +  dev->start = 1; +} + +#ifdef MODULE +static struct device dev_ni52 = { +  "        ",  /* "ni5210": device name inserted by net_init.c */ +  0, 0, 0, 0, +  0x300, 9,   /* I/O address, IRQ */ +  0, 0, 0, NULL, ni52_probe }; + +/* set: io,irq,memstart,memend or set it when calling insmod */ +int irq=9; +int io=0x300; +long memstart=0; /* e.g 0xd0000 */ +long memend=0;   /* e.g 0xd4000 */ + +int init_module(void) +{ +  if(io <= 0x0 || !memend || !memstart || irq < 2) { +    printk("ni52: Autoprobing not allowed for modules.\nni52: Set symbols 'io' 'irq' 'memstart' and 'memend'\n"); +    return -ENODEV; +  } +  dev_ni52.irq = irq; +  dev_ni52.base_addr = io; +  dev_ni52.mem_end = memend; +  dev_ni52.mem_start = memstart; +  if (register_netdev(&dev_ni52) != 0) +    return -EIO; +  return 0; +} + +void cleanup_module(void) +{ +  release_region(dev_ni52.base_addr, NI52_TOTAL_SIZE); +  kfree(dev_ni52.priv); +  dev_ni52.priv = NULL; +  unregister_netdev(&dev_ni52); +} +#endif /* MODULE */ + +#if 0 +/* + * DUMP .. we expect a not running CMD unit and enough space + */ +void ni52_dump(struct device *dev,void *ptr) +{ +  struct priv *p = (struct priv *) dev->priv; +  struct dump_cmd_struct *dump_cmd = (struct dump_cmd_struct *) ptr; +  int i; + +  p->scb->cmd_cuc = CUC_ABORT; +  ni_attn586(); +  WAIT_4_SCB_CMD(); +  WAIT_4_SCB_CMD_RUC(); + +  dump_cmd->cmd_status = 0; +  dump_cmd->cmd_cmd = CMD_DUMP | CMD_LAST; +  dump_cmd->dump_offset = make16((dump_cmd + 1)); +  dump_cmd->cmd_link = 0xffff; + +  p->scb->cbl_offset = make16(dump_cmd); +  p->scb->cmd_cuc = CUC_START; +  ni_attn586(); +  WAIT_4_STAT_COMPL(dump_cmd); + +  if( (dump_cmd->cmd_status & (STAT_COMPL|STAT_OK)) != (STAT_COMPL|STAT_OK) ) +        printk("%s: Can't get dump information.\n",dev->name); + +  for(i=0;i<170;i++) { +    printk("%02x ",(int) ((unsigned char *) (dump_cmd + 1))[i]); +    if(i % 24 == 23) +      printk("\n"); +  } +  printk("\n"); +} +#endif + +/* + * END: linux/drivers/net/ni52.c  + */ + + diff --git a/linux/src/drivers/net/ni52.h b/linux/src/drivers/net/ni52.h new file mode 100644 index 0000000..b3dfdd2 --- /dev/null +++ b/linux/src/drivers/net/ni52.h @@ -0,0 +1,310 @@ +/* + * Intel i82586 Ethernet definitions + * + * This is an extension to the Linux operating system, and is covered by the + * same Gnu Public License that covers that work. + * + * copyrights (c) 1994 by Michael Hipp (mhipp@student.uni-tuebingen.de) + * + * I have done a look in the following sources: + *   crynwr-packet-driver by Russ Nelson + *   Garret A. Wollman's i82586-driver for BSD + */ + +  +#define NI52_RESET     0  /* writing to this address, resets the i82586 */ +#define NI52_ATTENTION 1  /* channel attention, kick the 586 */ +#define NI52_TENA      3  /* 2-5 possibly wrong, Xmit enable */ +#define NI52_TDIS      2  /* Xmit disable */ +#define NI52_INTENA    5  /* Interrupt enable */ +#define NI52_INTDIS    4  /* Interrupt disable */ +#define NI52_MAGIC1    6  /* dunno exact function */ +#define NI52_MAGIC2    7  /* dunno exact function */ + +#define NI52_MAGICVAL1 0x00  /* magic-values for ni5210 card */ +#define NI52_MAGICVAL2 0x55 + +/* + * where to find the System Configuration Pointer (SCP) + */ +#define SCP_DEFAULT_ADDRESS 0xfffff4 + + +/* + * System Configuration Pointer Struct + */ + +struct scp_struct +{ +  unsigned short zero_dum0;	/* has to be zero */ +  unsigned char  sysbus;	/* 0=16Bit,1=8Bit */ +  unsigned char  zero_dum1;	/* has to be zero for 586 */ +  unsigned short zero_dum2; +  unsigned short zero_dum3; +  char          *iscp;		/* pointer to the iscp-block */ +}; + + +/* + * Intermediate System Configuration Pointer (ISCP) + */ +struct iscp_struct +{ +  unsigned char  busy;          /* 586 clears after successful init */ +  unsigned char  zero_dummy;    /* has to be zero */ +  unsigned short scb_offset;    /* pointeroffset to the scb_base */ +  char          *scb_base;      /* base-address of all 16-bit offsets */ +}; + +/* + * System Control Block (SCB) + */ +struct scb_struct +{ +  unsigned char rus; +  unsigned char cus; +  unsigned char cmd_ruc;           /* command word: RU part */ +  unsigned char cmd_cuc;           /* command word: CU part & ACK */ +  unsigned short cbl_offset;    /* pointeroffset, command block list */ +  unsigned short rfa_offset;    /* pointeroffset, receive frame area */ +  unsigned short crc_errs;      /* CRC-Error counter */ +  unsigned short aln_errs;      /* allignmenterror counter */ +  unsigned short rsc_errs;      /* Resourceerror counter */ +  unsigned short ovrn_errs;     /* OVerrunerror counter */ +}; + +/* + * possible command values for the command word + */ +#define RUC_MASK	0x0070	/* mask for RU commands */ +#define RUC_NOP		0x0000	/* NOP-command */ +#define RUC_START	0x0010	/* start RU */ +#define RUC_RESUME	0x0020	/* resume RU after suspend */ +#define RUC_SUSPEND	0x0030	/* suspend RU */ +#define RUC_ABORT	0x0040	/* abort receiver operation immediately */ + +#define CUC_MASK        0x07  /* mask for CU command */ +#define CUC_NOP         0x00  /* NOP-command */ +#define CUC_START       0x01  /* start execution of 1. cmd on the CBL */ +#define CUC_RESUME      0x02  /* resume after suspend */ +#define CUC_SUSPEND     0x03  /* Suspend CU */ +#define CUC_ABORT       0x04  /* abort command operation immediately */ + +#define ACK_MASK        0xf0  /* mask for ACK command */ +#define ACK_CX          0x80  /* acknowledges STAT_CX */ +#define ACK_FR          0x40  /* ack. STAT_FR */ +#define ACK_CNA         0x20  /* ack. STAT_CNA */ +#define ACK_RNR         0x10  /* ack. STAT_RNR */ + +/* + * possible status values for the status word + */ +#define STAT_MASK       0xf0  /* mask for cause of interrupt */ +#define STAT_CX         0x80  /* CU finished cmd with its I bit set */ +#define STAT_FR         0x40  /* RU finished receiving a frame */ +#define STAT_CNA        0x20  /* CU left active state */ +#define STAT_RNR        0x10  /* RU left ready state */ + +#define CU_STATUS       0x7   /* CU status, 0=idle */ +#define CU_SUSPEND      0x1   /* CU is suspended */ +#define CU_ACTIVE       0x2   /* CU is active */ + +#define RU_STATUS	0x70	/* RU status, 0=idle */ +#define RU_SUSPEND	0x10	/* RU suspended */ +#define RU_NOSPACE	0x20	/* RU no resources */ +#define RU_READY	0x40	/* RU is ready */ + +/* + * Receive Frame Descriptor (RFD) + */ +struct rfd_struct +{ +  unsigned char  stat_low;	/* status word */ +  unsigned char  stat_high;	/* status word */ +  unsigned char  rfd_sf;	/* 82596 mode only */ +  unsigned char  last;		/* Bit15,Last Frame on List / Bit14,suspend */ +  unsigned short next;		/* linkoffset to next RFD */ +  unsigned short rbd_offset;	/* pointeroffset to RBD-buffer */ +  unsigned char  dest[6];	/* ethernet-address, destination */ +  unsigned char  source[6];	/* ethernet-address, source */ +  unsigned short length;	/* 802.3 frame-length */ +  unsigned short zero_dummy;	/* dummy */ +}; + +#define RFD_LAST     0x80	/* last: last rfd in the list */ +#define RFD_SUSP     0x40	/* last: suspend RU after  */ +#define RFD_COMPL    0x80 +#define RFD_OK       0x20 +#define RFD_BUSY     0x40 +#define RFD_ERR_LEN  0x10     /* Length error (if enabled length-checking */ +#define RFD_ERR_CRC  0x08     /* CRC error */ +#define RFD_ERR_ALGN 0x04     /* Alignment error */ +#define RFD_ERR_RNR  0x02     /* status: receiver out of resources */ +#define RFD_ERR_OVR  0x01     /* DMA Overrun! */ + +#define RFD_ERR_FTS  0x0080	/* Frame to short */ +#define RFD_ERR_NEOP 0x0040	/* No EOP flag (for bitstuffing only) */ +#define RFD_ERR_TRUN 0x0020	/* (82596 only/SF mode) indicates truncated frame */ +#define RFD_MATCHADD 0x0002     /* status: Destinationaddress !matches IA (only 82596) */ +#define RFD_COLLDET  0x0001	/* Detected collision during reception */ + +/* + * Receive Buffer Descriptor (RBD) + */ +struct rbd_struct  +{ +  unsigned short status;	/* status word,number of used bytes in buff */ +  unsigned short next;		/* pointeroffset to next RBD */ +  char          *buffer;	/* receive buffer address pointer */ +  unsigned short size;		/* size of this buffer */ +  unsigned short zero_dummy;    /* dummy */ +}; + +#define RBD_LAST	0x8000	/* last buffer */ +#define RBD_USED	0x4000	/* this buffer has data */ +#define RBD_MASK	0x3fff	/* size-mask for length */ + +/* + * Statusvalues for Commands/RFD + */ +#define STAT_COMPL   0x8000	/* status: frame/command is complete */ +#define STAT_BUSY    0x4000	/* status: frame/command is busy */ +#define STAT_OK      0x2000	/* status: frame/command is ok */ + +/* + * Action-Commands + */ +#define CMD_NOP		0x0000	/* NOP */ +#define CMD_IASETUP	0x0001	/* initial address setup command */ +#define CMD_CONFIGURE	0x0002	/* configure command */ +#define CMD_MCSETUP	0x0003	/* MC setup command */ +#define CMD_XMIT	0x0004	/* transmit command */ +#define CMD_TDR		0x0005	/* time domain reflectometer (TDR) command */ +#define CMD_DUMP	0x0006	/* dump command */ +#define CMD_DIAGNOSE	0x0007	/* diagnose command */ + +/* + * Action command bits + */ +#define CMD_LAST	0x8000	/* indicates last command in the CBL */ +#define CMD_SUSPEND	0x4000	/* suspend CU after this CB */ +#define CMD_INT		0x2000	/* generate interrupt after execution */ + +/* + * NOP - command + */ +struct nop_cmd_struct +{ +  unsigned short cmd_status;	/* status of this command */ +  unsigned short cmd_cmd;       /* the command itself (+bits) */ +  unsigned short cmd_link;      /* offsetpointer to next command */ +}; + +/* + * IA Setup command + */ +struct iasetup_cmd_struct  +{ +  unsigned short cmd_status; +  unsigned short cmd_cmd; +  unsigned short cmd_link; +  unsigned char  iaddr[6]; +}; + +/* + * Configure command  + */ +struct configure_cmd_struct +{ +  unsigned short cmd_status; +  unsigned short cmd_cmd; +  unsigned short cmd_link; +  unsigned char  byte_cnt;   /* size of the config-cmd */ +  unsigned char  fifo;       /* fifo/recv monitor */ +  unsigned char  sav_bf;     /* save bad frames (bit7=1)*/ +  unsigned char  adr_len;    /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/ +  unsigned char  priority;   /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */ +  unsigned char  ifs;        /* inter frame spacing */ +  unsigned char  time_low;   /* slot time low */ +  unsigned char  time_high;  /* slot time high(0-2) and max. retries(4-7) */ +  unsigned char  promisc;    /* promisc-mode(0) , et al (1-7) */ +  unsigned char  carr_coll;  /* carrier(0-3)/collision(4-7) stuff */ +  unsigned char  fram_len;   /* minimal frame len */ +  unsigned char  dummy;	     /* dummy */ +}; + +/* + * Multicast Setup command  + */ +struct mcsetup_cmd_struct  +{ +  unsigned short cmd_status; +  unsigned short cmd_cmd; +  unsigned short cmd_link; +  unsigned short mc_cnt;		/* number of bytes in the MC-List */ +  unsigned char  mc_list[0][6];  	/* pointer to 6 bytes entries */ +}; + +/* + * DUMP command + */ +struct dump_cmd_struct +{ +  unsigned short cmd_status; +  unsigned short cmd_cmd; +  unsigned short cmd_link; +  unsigned short dump_offset;    /* pointeroffset to DUMP space */ +}; + +/* + * transmit command  + */ +struct transmit_cmd_struct  +{ +  unsigned short cmd_status; +  unsigned short cmd_cmd; +  unsigned short cmd_link; +  unsigned short tbd_offset;	/* pointeroffset to TBD */ +  unsigned char  dest[6];       /* destination address of the frame */ +  unsigned short length;	/* user defined: 802.3 length / Ether type */ +}; + +#define TCMD_ERRMASK     0x0fa0 +#define TCMD_MAXCOLLMASK 0x000f +#define TCMD_MAXCOLL     0x0020 +#define TCMD_HEARTBEAT   0x0040 +#define TCMD_DEFERRED    0x0080 +#define TCMD_UNDERRUN    0x0100 +#define TCMD_LOSTCTS     0x0200 +#define TCMD_NOCARRIER   0x0400 +#define TCMD_LATECOLL    0x0800 + +struct tdr_cmd_struct +{ +  unsigned short cmd_status; +  unsigned short cmd_cmd; +  unsigned short cmd_link; +  unsigned short status; +}; + +#define TDR_LNK_OK	0x8000	/* No link problem identified */ +#define TDR_XCVR_PRB	0x4000	/* indicates a transceiver problem */ +#define TDR_ET_OPN	0x2000	/* open, no correct termination */ +#define TDR_ET_SRT	0x1000	/* TDR detected a short circuit */ +#define TDR_TIMEMASK	0x07ff	/* mask for the time field */ + +/* + * Transmit Buffer Descriptor (TBD) + */ +struct tbd_struct +{ +  unsigned short size;		/* size + EOF-Flag(15) */ +  unsigned short next;          /* pointeroffset to next TBD */ +  char          *buffer;        /* pointer to buffer */ +}; + +#define TBD_LAST 0x8000         /* EOF-Flag, indicates last buffer in list */ + + + + diff --git a/linux/src/drivers/net/ni65.c b/linux/src/drivers/net/ni65.c new file mode 100644 index 0000000..75e8914 --- /dev/null +++ b/linux/src/drivers/net/ni65.c @@ -0,0 +1,1228 @@ +/* + * ni6510 (am7990 'lance' chip) driver for Linux-net-3 + * BETAcode v0.71 (96/09/29) for 2.0.0 (or later) + * copyrights (c) 1994,1995,1996 by M.Hipp + *  + * This driver can handle the old ni6510 board and the newer ni6510  + * EtherBlaster. (probably it also works with every full NE2100  + * compatible card) + * + * To compile as module, type: + *     gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ -DMODULE -c ni65.c + * driver probes: io: 0x360,0x300,0x320,0x340 / dma: 3,5,6,7 + * + * This is an extension to the Linux operating system, and is covered by the + * same Gnu Public License that covers the Linux-kernel. + * + * comments/bugs/suggestions can be sent to: + *   Michael Hipp + *   email: Michael.Hipp@student.uni-tuebingen.de + * + * sources: + *   some things are from the 'ni6510-packet-driver for dos by Russ Nelson' + *   and from the original drivers by D.Becker + * + * known problems: + *   - on some PCI boards (including my own) the card/board/ISA-bridge has + *     problems with bus master DMA. This results in lotsa overruns. + *     It may help to '#define RCV_PARANOIA_CHECK' or try to #undef + *     the XMT and RCV_VIA_SKB option .. this reduces driver performance. + *     Or just play with your BIOS options to optimize ISA-DMA access. + *     Maybe you also wanna play with the LOW_PERFORAMCE and MID_PERFORMANCE + *     defines -> please report me your experience then + *   - Harald reported for ASUS SP3G mainboards, that you should use + *     the 'optimal settings' from the user's manual on page 3-12! + * + * credits: + *   thanx to Jason Sullivan for sending me a ni6510 card! + *   lot of debug runs with ASUS SP3G Boards (Intel Saturn) by Harald Koenig + * + * simple performance test: (486DX-33/Ni6510-EB receives from 486DX4-100/Ni6510-EB) + *    average: FTP -> 8384421 bytes received in 8.5 seconds + *           (no RCV_VIA_SKB,no XMT_VIA_SKB,PARANOIA_CHECK,4 XMIT BUFS, 8 RCV_BUFFS) + *    peak: FTP -> 8384421 bytes received in 7.5 seconds  + *           (RCV_VIA_SKB,XMT_VIA_SKB,no PARANOIA_CHECK,1(!) XMIT BUF, 16 RCV BUFFS) + */ + +/* + * 96.Sept.29: virt_to_bus stuff added for new memory modell + * 96.April.29: Added Harald Koenig's Patches (MH) + * 96.April.13: enhanced error handling .. more tests (MH) + * 96.April.5/6: a lot of performance tests. Got it stable now (hopefully) (MH) + * 96.April.1: (no joke ;) .. added EtherBlaster and Module support (MH) + * 96.Feb.19: fixed a few bugs .. cleanups .. tested for 1.3.66 (MH) + *            hopefully no more 16MB limit + * + * 95.Nov.18: multicast tweaked (AC). + * + * 94.Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH) + * + * 94.July.16: fixed bugs in recv_skb and skb-alloc stuff  (MH) + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <linux/version.h> +#include <linux/module.h> + +#include "ni65.h" + +/* + * the current setting allows an acceptable performance + * for 'RCV_PARANOIA_CHECK' read the 'known problems' part in  + * the header of this file + * 'invert' the defines for max. performance. This may cause DMA problems + * on some boards (e.g on my ASUS SP3G) + */ +#undef XMT_VIA_SKB +#undef RCV_VIA_SKB +#define RCV_PARANOIA_CHECK + +#define MID_PERFORMANCE + +#if   defined( LOW_PERFORMANCE ) + static int isa0=7,isa1=7,csr80=0x0c10; +#elif defined( MID_PERFORMANCE ) + static int isa0=5,isa1=5,csr80=0x2810; +#else	/* high performance */ + static int isa0=4,isa1=4,csr80=0x0017; +#endif + +/* + * a few card/vendor specific defines + */ +#define NI65_ID0    0x00 +#define NI65_ID1    0x55 +#define NI65_EB_ID0 0x52 +#define NI65_EB_ID1 0x44 +#define NE2100_ID0  0x57 +#define NE2100_ID1  0x57 + +#define PORT p->cmdr_addr + +/* + * buffer configuration + */ +#if 1 +#define RMDNUM 16 +#define RMDNUMMASK 0x80000000  +#else +#define RMDNUM 8 +#define RMDNUMMASK 0x60000000 /* log2(RMDNUM)<<29 */ +#endif + +#if 0 +#define TMDNUM 1 +#define TMDNUMMASK 0x00000000 +#else +#define TMDNUM 4 +#define TMDNUMMASK 0x40000000 /* log2(TMDNUM)<<29 */ +#endif + +/* slightly oversized */ +#define R_BUF_SIZE 1544 +#define T_BUF_SIZE 1544 + +/* + * lance register defines + */ +#define L_DATAREG 0x00 +#define L_ADDRREG 0x02 +#define L_RESET   0x04 +#define L_CONFIG  0x05 +#define L_BUSIF   0x06 + +/*  + * to access the lance/am7990-regs, you have to write + * reg-number into L_ADDRREG, then you can access it using L_DATAREG + */ +#define CSR0  0x00 +#define CSR1  0x01 +#define CSR2  0x02 +#define CSR3  0x03 + +#define INIT_RING_BEFORE_START	0x1 +#define FULL_RESET_ON_ERROR	0x2 + +#if 0 +#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);inw(PORT+L_ADDRREG); \ +                           outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);} +#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_ADDRREG),\ +                       inw(PORT+L_DATAREG)) +#if 0 +#define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);} +#else +#define writedatareg(val) {  writereg(val,CSR0); } +#endif +#else +#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);outw(val,PORT+L_DATAREG);} +#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_DATAREG)) +#define writedatareg(val) { writereg(val,CSR0); } +#endif + +static unsigned char ni_vendor[] = { 0x02,0x07,0x01 }; + +static struct card { +	unsigned char id0,id1; +	short id_offset; +	short total_size; +	short cmd_offset; +	short addr_offset; +	unsigned char *vendor_id; +	char *cardname; +	unsigned char config; +} cards[] = { +	{ NI65_ID0,NI65_ID1,0x0e,0x10,0x0,0x8,ni_vendor,"ni6510", 0x1 } , +	{ NI65_EB_ID0,NI65_EB_ID1,0x0e,0x18,0x10,0x0,ni_vendor,"ni6510 EtherBlaster", 0x2 } , +	{ NE2100_ID0,NE2100_ID1,0x0e,0x18,0x10,0x0,NULL,"generic NE2100", 0x0 } +}; +#define NUM_CARDS 3 + +struct priv  +{ +  struct rmd rmdhead[RMDNUM]; +  struct tmd tmdhead[TMDNUM]; +  struct init_block ib;  +  int rmdnum; +  int tmdnum,tmdlast; +#ifdef RCV_VIA_SKB +  struct sk_buff *recv_skb[RMDNUM]; +#else +  void *recvbounce[RMDNUM]; +#endif +#ifdef XMT_VIA_SKB +  struct sk_buff *tmd_skb[TMDNUM]; +#endif +  void *tmdbounce[TMDNUM]; +  int tmdbouncenum; +  int lock,xmit_queued; +  struct enet_statistics stats; +  void *self; +  int cmdr_addr; +  int cardno; +  int features; +};  + +static int  ni65_probe1(struct device *dev,int); +static void ni65_interrupt(int irq, void * dev_id, struct pt_regs *regs); +static void ni65_recv_intr(struct device *dev,int); +static void ni65_xmit_intr(struct device *dev,int); +static int  ni65_open(struct device *dev); +static int  ni65_lance_reinit(struct device *dev); +static void ni65_init_lance(struct priv *p,unsigned char*,int,int); +static int  ni65_send_packet(struct sk_buff *skb, struct device *dev); +static int  ni65_close(struct device *dev); +static int  ni65_alloc_buffer(struct device *dev); +static void ni65_free_buffer(struct priv *p); +static struct enet_statistics *ni65_get_stats(struct device *); +static void set_multicast_list(struct device *dev); + +static int irqtab[] = { 9,12,15,5 }; /* irq config-translate */ +static int dmatab[] = { 0,3,5,6,7 }; /* dma config-translate and autodetect */ + +static int debuglevel = 1; + +/* + * set 'performance' registers .. we must STOP lance for that + */ +static void ni65_set_performance(struct priv *p) +{ +  writereg(CSR0_STOP | CSR0_CLRALL,CSR0); /* STOP */ + +  if( !(cards[p->cardno].config & 0x02) ) +    return; +  +  outw(80,PORT+L_ADDRREG); +  if(inw(PORT+L_ADDRREG) != 80) +    return; +  +  writereg( (csr80 & 0x3fff) ,80); /* FIFO watermarks */ +  outw(0,PORT+L_ADDRREG); +  outw((short)isa0,PORT+L_BUSIF); /* write ISA 0: DMA_R : isa0 * 50ns */ +  outw(1,PORT+L_ADDRREG); +  outw((short)isa1,PORT+L_BUSIF); /* write ISA 1: DMA_W : isa1 * 50ns  */ + +  outw(CSR0,PORT+L_ADDRREG);	/* switch back to CSR0 */ +} + +/* + * open interface (up) + */ +static int ni65_open(struct device *dev) +{ +  struct priv *p = (struct priv *) dev->priv; +  int irqval = request_irq(dev->irq, &ni65_interrupt,0, +                        cards[p->cardno].cardname,NULL); +  if (irqval) { +    printk ("%s: unable to get IRQ %d (irqval=%d).\n",  +              dev->name,dev->irq, irqval); +    return -EAGAIN; +  } +  irq2dev_map[dev->irq] = dev; + +  if(ni65_lance_reinit(dev)) +  { +    dev->tbusy     = 0; +    dev->interrupt = 0; +    dev->start     = 1; +    MOD_INC_USE_COUNT; +    return 0; +  } +  else +  { +    irq2dev_map[dev->irq] = NULL; +    free_irq(dev->irq,NULL); +    dev->start = 0; +    return -EAGAIN; +  } +} + +/* + * close interface (down) + */ +static int ni65_close(struct device *dev) +{ +  struct priv *p = (struct priv *) dev->priv; + +  outw(inw(PORT+L_RESET),PORT+L_RESET); /* that's the hard way */ + +#ifdef XMT_VIA_SKB +  { +    int i; +    for(i=0;i<TMDNUM;i++) +    { +      if(p->tmd_skb[i]) { +        dev_kfree_skb(p->tmd_skb[i],FREE_WRITE); +        p->tmd_skb[i] = NULL; +      } +    } +  } +#endif +  irq2dev_map[dev->irq] = NULL; +  free_irq(dev->irq,NULL); +  dev->tbusy = 1; +  dev->start = 0; +  MOD_DEC_USE_COUNT; +  return 0;  +} + +/*  + * Probe The Card (not the lance-chip)  + */  +#ifdef MODULE +static +#endif +int ni65_probe(struct device *dev) +{ +  int *port; +  static int ports[] = {0x360,0x300,0x320,0x340, 0}; + +  if (dev->base_addr > 0x1ff)          /* Check a single specified location. */ +     return ni65_probe1(dev, dev->base_addr); +  else if (dev->base_addr > 0)         /* Don't probe at all. */ +     return -ENXIO; + +  for (port = ports; *port; port++)  +  { +    if (ni65_probe1(dev, *port) == 0) +       return 0; +  } + +  return -ENODEV; +} + +/* + * this is the real card probe ..  + */ +static int ni65_probe1(struct device *dev,int ioaddr) +{ +  int i,j; +  struct priv *p;  + +  for(i=0;i<NUM_CARDS;i++) { +    if(check_region(ioaddr, cards[i].total_size)) +      continue; +    if(cards[i].id_offset >= 0) { +      if(inb(ioaddr+cards[i].id_offset+0) != cards[i].id0 || +         inb(ioaddr+cards[i].id_offset+1) != cards[i].id1) { +         continue; +      } +    } +    if(cards[i].vendor_id) { +      for(j=0;j<3;j++) +        if(inb(ioaddr+cards[i].addr_offset+j) != cards[i].vendor_id[j]) +          continue; +    } +    break; +  } +  if(i == NUM_CARDS) +    return -ENODEV; + +  for(j=0;j<6;j++) +    dev->dev_addr[j] = inb(ioaddr+cards[i].addr_offset+j); + +  if( (j=ni65_alloc_buffer(dev)) < 0) +    return j; +  p = (struct priv *) dev->priv; +  p->cmdr_addr = ioaddr + cards[i].cmd_offset; +  p->cardno = i; + +  printk("%s: %s found at %#3x, ", dev->name, cards[p->cardno].cardname , ioaddr); + +  outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */ +  if( (j=readreg(CSR0)) != 0x4) { +     printk(KERN_ERR "can't RESET card: %04x\n",j); +     ni65_free_buffer(p); +     return -EAGAIN; +  } + +  outw(88,PORT+L_ADDRREG); +  if(inw(PORT+L_ADDRREG) == 88) { +    unsigned long v; +    v = inw(PORT+L_DATAREG); +    v <<= 16; +    outw(89,PORT+L_ADDRREG); +    v |= inw(PORT+L_DATAREG); +    printk("Version %#08lx, ",v); +    p->features = INIT_RING_BEFORE_START; +  } +  else { +    printk("ancient LANCE, "); +    p->features = 0x0; +  } + +  if(test_bit(0,&cards[i].config)) { +    dev->irq = irqtab[(inw(ioaddr+L_CONFIG)>>2)&3]; +    dev->dma = dmatab[inw(ioaddr+L_CONFIG)&3]; +    printk("IRQ %d (from card), DMA %d (from card).\n",dev->irq,dev->dma); +  } +  else { +    if(dev->dma == 0) { +		/* 'stuck test' from lance.c */ +      int dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) | (inb(DMA2_STAT_REG) & 0xf0); +      for(i=1;i<5;i++) { +        int dma = dmatab[i]; +        if(test_bit(dma,&dma_channels) || request_dma(dma,"ni6510")) +          continue; +        disable_dma(dma); +        set_dma_mode(dma,DMA_MODE_CASCADE); +        enable_dma(dma); +        ni65_init_lance(p,dev->dev_addr,0,0); /* trigger memory access */ +        disable_dma(dma); +        free_dma(dma); +        if(readreg(CSR0) & CSR0_IDON) +          break; +      } +      if(i == 5) { +        printk("Can't detect DMA channel!\n"); +        ni65_free_buffer(p); +        return -EAGAIN; +      } +      dev->dma = dmatab[i]; +      printk("DMA %d (autodetected), ",dev->dma); +    } +    else +      printk("DMA %d (assigned), ",dev->dma); + +    if(dev->irq < 2) +    { +      ni65_init_lance(p,dev->dev_addr,0,0); +      autoirq_setup(0); +      writereg(CSR0_INIT|CSR0_INEA,CSR0); /* trigger interrupt */ + +      if(!(dev->irq = autoirq_report(2))) +      { +        printk("Failed to detect IRQ line!\n"); +        ni65_free_buffer(p); +        return -EAGAIN; +      } +      printk("IRQ %d (autodetected).\n",dev->irq); +    } +    else +      printk("IRQ %d (assigned).\n",dev->irq); +  } + +  if(request_dma(dev->dma, cards[p->cardno].cardname ) != 0) +  { +    printk("%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma); +    ni65_free_buffer(p); +    return -EAGAIN; +  } + +  /*  +   * Grab the region so we can find another board.  +   */ +  request_region(ioaddr,cards[p->cardno].total_size,cards[p->cardno].cardname); + +  dev->base_addr = ioaddr; + +  dev->open               = ni65_open; +  dev->stop               = ni65_close; +  dev->hard_start_xmit    = ni65_send_packet; +  dev->get_stats          = ni65_get_stats; +  dev->set_multicast_list = set_multicast_list; + +  ether_setup(dev); + +  dev->interrupt      = 0; +  dev->tbusy          = 0; +  dev->start          = 0; + +  return 0; /* everything is OK */ +} + +/* + * set lance register and trigger init  + */ +static void ni65_init_lance(struct priv *p,unsigned char *daddr,int filter,int mode)  +{ +  int i; +  u32 pib; + +  writereg(CSR0_CLRALL|CSR0_STOP,CSR0); + +  for(i=0;i<6;i++) +    p->ib.eaddr[i] = daddr[i]; + +  for(i=0;i<8;i++) +    p->ib.filter[i] = filter; +  p->ib.mode = mode; + +  p->ib.trp = (u32) virt_to_bus(p->tmdhead) | TMDNUMMASK; +  p->ib.rrp = (u32) virt_to_bus(p->rmdhead) | RMDNUMMASK; +  writereg(0,CSR3);  /* busmaster/no word-swap */ +  pib = (u32) virt_to_bus(&p->ib); +  writereg(pib & 0xffff,CSR1); +  writereg(pib >> 16,CSR2); + +  writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */ + +  for(i=0;i<32;i++) +  { +    udelay(4000); +    if(inw(PORT+L_DATAREG) & (CSR0_IDON | CSR0_MERR) ) +      break; /* init ok ? */ +  } +} + +/* + * allocate memory area and check the 16MB border + */ +static void *ni65_alloc_mem(struct device *dev,char *what,int size,int type) +{ +  struct sk_buff *skb=NULL; +  unsigned char *ptr; +  void *ret; + +  if(type) { +    ret = skb = alloc_skb(2+16+size,GFP_KERNEL|GFP_DMA); +    if(!skb) { +      printk("%s: unable to allocate %s memory.\n",dev->name,what); +      return NULL; +    } +    skb->dev = dev; +    skb_reserve(skb,2+16); +    skb_put(skb,R_BUF_SIZE);   /* grab the whole space .. (not necessary) */ +    ptr = skb->data; +  } +  else { +    ret = ptr = kmalloc(T_BUF_SIZE,GFP_KERNEL | GFP_DMA); +    if(!ret) { +      printk("%s: unable to allocate %s memory.\n",dev->name,what); +      return NULL; +    } +  } +  if( (u32) virt_to_bus(ptr+size) > 0x1000000) { +    printk("%s: unable to allocate %s memory in lower 16MB!\n",dev->name,what); +    if(type) +      kfree_skb(skb,FREE_WRITE); +    else +      kfree(ptr); +    return NULL; +  } +  return ret; +} + +/* + * allocate all memory structures .. send/recv buffers etc ... + */ +static int ni65_alloc_buffer(struct device *dev) +{ +  unsigned char *ptr; +  struct priv *p; +  int i; +  +  /*  +   * we need 8-aligned memory .. +   */ +  ptr = ni65_alloc_mem(dev,"BUFFER",sizeof(struct priv)+8,0); +  if(!ptr) +    return -ENOMEM; + +  p = dev->priv = (struct priv *) (((unsigned long) ptr + 7) & ~0x7); +  memset((char *) dev->priv,0,sizeof(struct priv)); +  p->self = ptr; + +  for(i=0;i<TMDNUM;i++) +  { +#ifdef XMT_VIA_SKB +    p->tmd_skb[i] = NULL; +#endif +    p->tmdbounce[i] = ni65_alloc_mem(dev,"XMIT",T_BUF_SIZE,0); +    if(!p->tmdbounce[i]) { +      ni65_free_buffer(p); +      return -ENOMEM; +    } +  } + +  for(i=0;i<RMDNUM;i++) +  { +#ifdef RCV_VIA_SKB +    p->recv_skb[i] = ni65_alloc_mem(dev,"RECV",R_BUF_SIZE,1); +    if(!p->recv_skb[i]) { +      ni65_free_buffer(p); +      return -ENOMEM; +    } +#else +    p->recvbounce[i] = ni65_alloc_mem(dev,"RECV",R_BUF_SIZE,0); +    if(!p->recvbounce[i]) { +      ni65_free_buffer(p); +      return -ENOMEM; +    } +#endif +  } + +  return 0; /* everything is OK */ +} + +/* + * free buffers and private struct  + */ +static void ni65_free_buffer(struct priv *p) +{ +  int i; + +  if(!p) +    return; + +  for(i=0;i<TMDNUM;i++) { +    if(p->tmdbounce[i]) +      kfree(p->tmdbounce[i]); +#ifdef XMT_VIA_SKB +    if(p->tmd_skb[i]) +      dev_kfree_skb(p->tmd_skb[i],FREE_WRITE); +#endif +  } + +  for(i=0;i<RMDNUM;i++) +  { +#ifdef RCV_VIA_SKB +    if(p->recv_skb[i]) +      dev_kfree_skb(p->recv_skb[i],FREE_WRITE); +#else +    if(p->recvbounce[i]) +      kfree(p->recvbounce[i]); +#endif +  } +  if(p->self) +    kfree(p->self); +} + + +/* + * stop and (re)start lance .. e.g after an error + */ +static void ni65_stop_start(struct device *dev,struct priv *p) +{ +  int csr0 = CSR0_INEA; + +  writedatareg(CSR0_STOP); + +  if(debuglevel > 1) +    printk("ni65_stop_start\n"); + +  if(p->features & INIT_RING_BEFORE_START) { +    int i; +#ifdef XMT_VIA_SKB +    struct sk_buff *skb_save[TMDNUM]; +#endif +    unsigned long buffer[TMDNUM]; +    short blen[TMDNUM]; + +    if(p->xmit_queued) { +      while(1) { +        if((p->tmdhead[p->tmdlast].u.s.status & XMIT_OWN)) +          break; +        p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1); +        if(p->tmdlast == p->tmdnum) +          break; +      } +    } +     +    for(i=0;i<TMDNUM;i++) { +      struct tmd *tmdp = p->tmdhead + i; +#ifdef XMT_VIA_SKB +      skb_save[i] = p->tmd_skb[i]; +#endif +      buffer[i] = (u32) bus_to_virt(tmdp->u.buffer); +      blen[i] = tmdp->blen; +      tmdp->u.s.status = 0x0; +    } + +    for(i=0;i<RMDNUM;i++) { +      struct rmd *rmdp = p->rmdhead + i; +      rmdp->u.s.status = RCV_OWN; +    } +    p->tmdnum = p->xmit_queued = 0; +    writedatareg(CSR0_STRT | csr0); + +    for(i=0;i<TMDNUM;i++) { +      int num = (i + p->tmdlast) & (TMDNUM-1); +      p->tmdhead[i].u.buffer = (u32) virt_to_bus((char *)buffer[num]); /* status is part of buffer field */ +      p->tmdhead[i].blen = blen[num]; +      if(p->tmdhead[i].u.s.status & XMIT_OWN) { +         p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1); +         p->xmit_queued = 1; +	 writedatareg(CSR0_TDMD | CSR0_INEA | csr0); +      } +#ifdef XMT_VIA_SKB +      p->tmd_skb[i] = skb_save[num]; +#endif +    } +    p->rmdnum = p->tmdlast = 0; +    if(!p->lock) +      dev->tbusy = (p->tmdnum || !p->xmit_queued) ? 0 : 1; +    dev->trans_start = jiffies; +  }  +  else +    writedatareg(CSR0_STRT | csr0); +} + +/*  + * init lance (write init-values .. init-buffers) (open-helper) + */ +static int ni65_lance_reinit(struct device *dev) +{ +   int i; +   struct priv *p = (struct priv *) dev->priv; + +   p->lock = 0; +   p->xmit_queued = 0; + +   disable_dma(dev->dma); /* I've never worked with dma, but we do it like the packetdriver */ +   set_dma_mode(dev->dma,DMA_MODE_CASCADE); +   enable_dma(dev->dma);  + +   outw(inw(PORT+L_RESET),PORT+L_RESET); /* first: reset the card */ +   if( (i=readreg(CSR0) ) != 0x4) +   { +     printk(KERN_ERR "%s: can't RESET %s card: %04x\n",dev->name, +              cards[p->cardno].cardname,(int) i); +     disable_dma(dev->dma); +     return 0; +   } + +   p->rmdnum = p->tmdnum = p->tmdlast = p->tmdbouncenum = 0; +   for(i=0;i<TMDNUM;i++) +   { +     struct tmd *tmdp = p->tmdhead + i; +#ifdef XMT_VIA_SKB +     if(p->tmd_skb[i]) { +       dev_kfree_skb(p->tmd_skb[i],FREE_WRITE); +       p->tmd_skb[i] = NULL; +     } +#endif +     tmdp->u.buffer = 0x0; +     tmdp->u.s.status = XMIT_START | XMIT_END; +     tmdp->blen = tmdp->status2 = 0; +   } + +   for(i=0;i<RMDNUM;i++) +   { +     struct rmd *rmdp = p->rmdhead + i; +#ifdef RCV_VIA_SKB +     rmdp->u.buffer = (u32) virt_to_bus(p->recv_skb[i]->data); +#else +     rmdp->u.buffer = (u32) virt_to_bus(p->recvbounce[i]); +#endif +     rmdp->blen = -(R_BUF_SIZE-8); +     rmdp->mlen = 0; +     rmdp->u.s.status = RCV_OWN; +   } +    +   if(dev->flags & IFF_PROMISC) +     ni65_init_lance(p,dev->dev_addr,0x00,M_PROM); +   else if(dev->mc_count || dev->flags & IFF_ALLMULTI) +     ni65_init_lance(p,dev->dev_addr,0xff,0x0); +   else  +     ni65_init_lance(p,dev->dev_addr,0x00,0x00); + +  /* +   * ni65_set_lance_mem() sets L_ADDRREG to CSR0 +   * NOW, WE WILL NEVER CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED  +   */ + +   if(inw(PORT+L_DATAREG) & CSR0_IDON)  { +     ni65_set_performance(p); +           /* init OK: start lance , enable interrupts */ +     writedatareg(CSR0_CLRALL | CSR0_INEA | CSR0_STRT); +     return 1; /* ->OK */ +   } +   printk(KERN_ERR "%s: can't init lance, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG)); +   disable_dma(dev->dma); +   return 0; /* ->Error */ +} +  +/*  + * interrupt handler   + */ +static void ni65_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ +  int csr0; +  struct device *dev = (struct device *) irq2dev_map[irq]; +  struct priv *p; +  int bcnt = 32; + +  if (dev == NULL) { +    printk (KERN_ERR "ni65_interrupt(): irq %d for unknown device.\n", irq); +    return; +  } + +  if(set_bit(0,(int *) &dev->interrupt)) { +    printk("ni65: oops .. interrupt while proceeding interrupt\n"); +    return; +  } +  p = (struct priv *) dev->priv; + +  while(--bcnt) { +    csr0 = inw(PORT+L_DATAREG); + +#if 0 +    writedatareg( (csr0 & CSR0_CLRALL) ); /* ack interrupts, disable int. */ +#else +    writedatareg( (csr0 & CSR0_CLRALL) | CSR0_INEA ); /* ack interrupts, interrupts enabled */ +#endif + +    if(!(csr0 & (CSR0_ERR | CSR0_RINT | CSR0_TINT))) +      break; + +    if(csr0 & CSR0_RINT) /* RECV-int? */ +      ni65_recv_intr(dev,csr0); +    if(csr0 & CSR0_TINT) /* XMIT-int? */ +      ni65_xmit_intr(dev,csr0); + +    if(csr0 & CSR0_ERR) +    { +      struct priv *p = (struct priv *) dev->priv; +      if(debuglevel > 1) +        printk("%s: general error: %04x.\n",dev->name,csr0); +      if(csr0 & CSR0_BABL) +        p->stats.tx_errors++; +      if(csr0 & CSR0_MISS) { +        int i; +        for(i=0;i<RMDNUM;i++) +          printk("%02x ",p->rmdhead[i].u.s.status); +        printk("\n"); +        p->stats.rx_errors++; +      } +      if(csr0 & CSR0_MERR) { +        if(debuglevel > 1) +          printk("%s: Ooops .. memory error: %04x.\n",dev->name,csr0); +        ni65_stop_start(dev,p); +      } +    } +  } + +#ifdef RCV_PARANOIA_CHECK +{ + int j; + for(j=0;j<RMDNUM;j++) + { +  struct priv *p = (struct priv *) dev->priv; +  int i,k,num1,num2; +  for(i=RMDNUM-1;i>0;i--) { +     num2 = (p->rmdnum + i) & (RMDNUM-1); +     if(!(p->rmdhead[num2].u.s.status & RCV_OWN)) +        break; +  } + +  if(i) { +    for(k=0;k<RMDNUM;k++) { +      num1 = (p->rmdnum + k) & (RMDNUM-1); +      if(!(p->rmdhead[num1].u.s.status & RCV_OWN)) +        break; +    } +    if(!k) +      break; + +    if(debuglevel > 0) +    { +      char buf[256],*buf1; +      int k; +      buf1 = buf; +      for(k=0;k<RMDNUM;k++) { +        sprintf(buf1,"%02x ",(p->rmdhead[k].u.s.status)); /* & RCV_OWN) ); */ +        buf1 += 3; +      } +      *buf1 = 0; +      printk(KERN_ERR "%s: Ooops, receive ring corrupted %2d %2d | %s\n",dev->name,p->rmdnum,i,buf); +    } + +    p->rmdnum = num1; +    ni65_recv_intr(dev,csr0); +    if((p->rmdhead[num2].u.s.status & RCV_OWN)) +      break;	/* ok, we are 'in sync' again */ +  } +  else +    break; + } +} +#endif + +  if( (csr0 & (CSR0_RXON | CSR0_TXON)) != (CSR0_RXON | CSR0_TXON) ) { +    printk("%s: RX or TX was offline -> restart\n",dev->name); +    ni65_stop_start(dev,p); +  } +  else +    writedatareg(CSR0_INEA); + +  dev->interrupt = 0; + +  return; +} + +/* + * We have received an Xmit-Interrupt .. + * send a new packet if necessary + */ +static void ni65_xmit_intr(struct device *dev,int csr0) +{ +  struct priv *p = (struct priv *) dev->priv; + +  while(p->xmit_queued) +  { +    struct tmd *tmdp = p->tmdhead + p->tmdlast; +    int tmdstat = tmdp->u.s.status; + +    if(tmdstat & XMIT_OWN) +      break; + +    if(tmdstat & XMIT_ERR) +    { +#if 0 +      if(tmdp->status2 & XMIT_TDRMASK && debuglevel > 3) +        printk(KERN_ERR "%s: tdr-problems (e.g. no resistor)\n",dev->name); +#endif +     /* checking some errors */ +      if(tmdp->status2 & XMIT_RTRY) +        p->stats.tx_aborted_errors++; +      if(tmdp->status2 & XMIT_LCAR) +        p->stats.tx_carrier_errors++; +      if(tmdp->status2 & (XMIT_BUFF | XMIT_UFLO )) { +		/* this stops the xmitter */ +        p->stats.tx_fifo_errors++; +        if(debuglevel > 0) +          printk(KERN_ERR "%s: Xmit FIFO/BUFF error\n",dev->name); +        if(p->features & INIT_RING_BEFORE_START) { +          tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;	/* test: resend this frame */ +          ni65_stop_start(dev,p); +          break;	/* no more Xmit processing .. */ +        } +        else +         ni65_stop_start(dev,p); +      } +      if(debuglevel > 2) +        printk(KERN_ERR "%s: xmit-error: %04x %02x-%04x\n",dev->name,csr0,(int) tmdstat,(int) tmdp->status2); +      if(!(csr0 & CSR0_BABL)) /* don't count errors twice */ +        p->stats.tx_errors++; +      tmdp->status2 = 0; +    } +    else +      p->stats.tx_packets++; + +#ifdef XMT_VIA_SKB +    if(p->tmd_skb[p->tmdlast]) { +       dev_kfree_skb(p->tmd_skb[p->tmdlast],FREE_WRITE); +       p->tmd_skb[p->tmdlast] = NULL; +    } +#endif + +    p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1); +    if(p->tmdlast == p->tmdnum) +      p->xmit_queued = 0; +  } +  dev->tbusy = 0; +  mark_bh(NET_BH); +} + +/* + * We have received a packet + */ +static void ni65_recv_intr(struct device *dev,int csr0) +{ +  struct rmd *rmdp;  +  int rmdstat,len; +  int cnt=0; +  struct priv *p = (struct priv *) dev->priv; + +  rmdp = p->rmdhead + p->rmdnum; +  while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN)) +  { +    cnt++; +    if( (rmdstat & (RCV_START | RCV_END | RCV_ERR)) != (RCV_START | RCV_END) ) /* error or oversized? */  +    { +      if(!(rmdstat & RCV_ERR)) { +        if(rmdstat & RCV_START) +        { +          p->stats.rx_length_errors++; +          printk(KERN_ERR "%s: recv, packet too long: %d\n",dev->name,rmdp->mlen & 0x0fff); +        } +      } +      else { +        if(debuglevel > 2) +          printk(KERN_ERR "%s: receive-error: %04x, lance-status: %04x/%04x\n", +                  dev->name,(int) rmdstat,csr0,(int) inw(PORT+L_DATAREG) ); +        if(rmdstat & RCV_FRAM) +          p->stats.rx_frame_errors++; +        if(rmdstat & RCV_OFLO) +          p->stats.rx_over_errors++; +        if(rmdstat & RCV_CRC)  +          p->stats.rx_crc_errors++; +        if(rmdstat & RCV_BUF_ERR) +          p->stats.rx_fifo_errors++; +      } +      if(!(csr0 & CSR0_MISS)) /* don't count errors twice */ +        p->stats.rx_errors++; +    } +    else if( (len = (rmdp->mlen & 0x0fff) - 4) >= 60) +    { +#ifdef RCV_VIA_SKB +      struct sk_buff *skb = alloc_skb(R_BUF_SIZE+2+16,GFP_ATOMIC); +      if (skb) +        skb_reserve(skb,16); +#else +      struct sk_buff *skb = dev_alloc_skb(len+2); +#endif +      if(skb) +      { +        skb_reserve(skb,2); +	skb->dev = dev; +#ifdef RCV_VIA_SKB +        if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) { +          skb_put(skb,len); +          eth_copy_and_sum(skb, (unsigned char *)(p->recv_skb[p->rmdnum]->data),len,0); +        } +        else { +          struct sk_buff *skb1 = p->recv_skb[p->rmdnum]; +          skb_put(skb,R_BUF_SIZE); +          p->recv_skb[p->rmdnum] = skb; +          rmdp->u.buffer = (u32) virt_to_bus(skb->data); +          skb = skb1; +          skb_trim(skb,len); +        } +#else +        skb_put(skb,len); +        eth_copy_and_sum(skb, (unsigned char *) p->recvbounce[p->rmdnum],len,0); +#endif +        p->stats.rx_packets++; +        skb->protocol=eth_type_trans(skb,dev); +        netif_rx(skb); +      } +      else +      { +        printk(KERN_ERR "%s: can't alloc new sk_buff\n",dev->name); +        p->stats.rx_dropped++; +      } +    } +    else { +      printk(KERN_INFO "%s: received runt packet\n",dev->name); +      p->stats.rx_errors++; +    } +    rmdp->blen = -(R_BUF_SIZE-8); +    rmdp->mlen = 0; +    rmdp->u.s.status = RCV_OWN; /* change owner */ +    p->rmdnum = (p->rmdnum + 1) & (RMDNUM-1); +    rmdp = p->rmdhead + p->rmdnum; +  } +} + +/* + * kick xmitter .. + */ +static int ni65_send_packet(struct sk_buff *skb, struct device *dev) +{ +  struct priv *p = (struct priv *) dev->priv; + +  if(dev->tbusy) +  { +    int tickssofar = jiffies - dev->trans_start; +    if (tickssofar < 50) +      return 1; + +    printk(KERN_ERR "%s: xmitter timed out, try to restart!\n",dev->name); +{ +  int i; +  for(i=0;i<TMDNUM;i++) +    printk("%02x ",p->tmdhead[i].u.s.status); +  printk("\n"); +} +    ni65_lance_reinit(dev); +    dev->tbusy=0; +    dev->trans_start = jiffies; +  } + +  if(skb == NULL) { +    dev_tint(dev); +    return 0; +  } + +  if (skb->len <= 0) +    return 0; + +  if (set_bit(0, (void*)&dev->tbusy) != 0) { +     printk(KERN_ERR "%s: Transmitter access conflict.\n", dev->name); +     return 1; +  } +  if (set_bit(0, (void*)&p->lock)) { +	printk(KERN_ERR "%s: Queue was locked.\n", dev->name); +	return 1; +  } + +  { +    short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +    struct tmd *tmdp; +    long flags; + +#ifdef XMT_VIA_SKB +    if( (unsigned long) (skb->data + skb->len) > 0x1000000) { +#endif + +      memcpy((char *) p->tmdbounce[p->tmdbouncenum] ,(char *)skb->data, +               (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len); +      dev_kfree_skb (skb, FREE_WRITE); + +      save_flags(flags); +      cli(); + +      tmdp = p->tmdhead + p->tmdnum; +      tmdp->u.buffer = (u32) virt_to_bus(p->tmdbounce[p->tmdbouncenum]); +      p->tmdbouncenum = (p->tmdbouncenum + 1) & (TMDNUM - 1); + +#ifdef XMT_VIA_SKB +    } +    else { +      save_flags(flags); +      cli(); +  +      tmdp = p->tmdhead + p->tmdnum; +      tmdp->u.buffer = (u32) virt_to_bus(skb->data); +      p->tmd_skb[p->tmdnum] = skb; +    } +#endif +    tmdp->blen = -len; + +    tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END; +    writedatareg(CSR0_TDMD | CSR0_INEA); /* enable xmit & interrupt */ + +    p->xmit_queued = 1; +    p->tmdnum = (p->tmdnum + 1) & (TMDNUM-1); + +    dev->tbusy = (p->tmdnum == p->tmdlast) ? 1 : 0; +    p->lock = 0; +    dev->trans_start = jiffies; + +    restore_flags(flags); +  } + +  return 0; +} + +static struct enet_statistics *ni65_get_stats(struct device *dev) +{ + +#if 0 + int i; + struct priv *p = (struct priv *) dev->priv; + for(i=0;i<RMDNUM;i++) { +   struct rmd *rmdp = p->rmdhead + ((p->rmdnum + i) & (RMDNUM-1)); +   printk("%02x ",rmdp->u.s.status); + } + printk("\n"); +#endif + +  return &((struct priv *) dev->priv)->stats; +} + +static void set_multicast_list(struct device *dev) +{ +	if(!ni65_lance_reinit(dev)) +		printk(KERN_ERR "%s: Can't switch card into MC mode!\n",dev->name); +	dev->tbusy = 0; +} + +#ifdef MODULE +static struct device dev_ni65 = { +  "        ",  /* "ni6510": device name inserted by net_init.c */ +  0, 0, 0, 0, +  0x360, 9,   /* I/O address, IRQ */ +  0, 0, 0, NULL, ni65_probe }; + +/* set: io,irq,dma or set it when calling insmod */ +static int irq=0; +static int io=0; +static int dma=0; + +int init_module(void) +{ +#if 0 +  if(io <= 0x0 || irq < 2) { +    printk("ni65: Autoprobing not allowed for modules.\n"); +    printk("ni65: Set symbols 'io' 'irq' and 'dma'\n"); +    return -ENODEV; +  } +#endif +  dev_ni65.irq = irq; +  dev_ni65.dma = dma; +  dev_ni65.base_addr = io; +  if (register_netdev(&dev_ni65) != 0) +    return -EIO; +  return 0; +} + +void cleanup_module(void) +{ +  struct priv *p; +  p = (struct priv *) dev_ni65.priv; +  if(!p) { +    printk("Ooops .. no privat struct\n"); +    return; +  } +  disable_dma(dev_ni65.dma); +  free_dma(dev_ni65.dma); +  release_region(dev_ni65.base_addr,cards[p->cardno].total_size); +  ni65_free_buffer(p); +  dev_ni65.priv = NULL; +  unregister_netdev(&dev_ni65); +} +#endif /* MODULE */ + +/* + * END of ni65.c  + */ + + diff --git a/linux/src/drivers/net/ni65.h b/linux/src/drivers/net/ni65.h new file mode 100644 index 0000000..6438095 --- /dev/null +++ b/linux/src/drivers/net/ni65.h @@ -0,0 +1,130 @@ +/* am7990 (lance) definitions + *  + * This is an extension to the Linux operating system, and is covered by + * same Gnu Public License that covers that work. + *  + * Michael Hipp + * email: mhipp@student.uni-tuebingen.de + * + * sources: (mail me or ask archie if you need them)  + *    crynwr-packet-driver + */ + +/* + * 	Control and Status Register 0 (CSR0) bit definitions + * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write) + * + */ + +#define CSR0_ERR	0x8000	/* Error summary (R) */ +#define CSR0_BABL	0x4000	/* Babble transmitter timeout error (RC) */ +#define CSR0_CERR	0x2000	/* Collision Error (RC) */ +#define CSR0_MISS	0x1000	/* Missed packet (RC) */ +#define CSR0_MERR	0x0800	/* Memory Error (RC) */  +#define CSR0_RINT	0x0400	/* Receiver Interrupt (RC) */ +#define CSR0_TINT       0x0200	/* Transmit Interrupt (RC) */  +#define CSR0_IDON	0x0100	/* Initialization Done (RC) */ +#define CSR0_INTR	0x0080	/* Interrupt Flag (R) */ +#define CSR0_INEA	0x0040	/* Interrupt Enable (RW) */ +#define CSR0_RXON	0x0020	/* Receiver on (R) */ +#define CSR0_TXON	0x0010  /* Transmitter on (R) */ +#define CSR0_TDMD	0x0008	/* Transmit Demand (RS) */ +#define CSR0_STOP	0x0004 	/* Stop (RS) */ +#define CSR0_STRT	0x0002	/* Start (RS) */ +#define CSR0_INIT	0x0001	/* Initialize (RS) */ + +#define CSR0_CLRALL    0x7f00  /* mask for all clearable bits */ +/* + *	Initialization Block  Mode operation Bit Definitions. + */ + +#define M_PROM		0x8000	/* Promiscuous Mode */ +#define M_INTL		0x0040  /* Internal Loopback */ +#define M_DRTY		0x0020  /* Disable Retry */  +#define M_COLL		0x0010	/* Force Collision */ +#define M_DTCR		0x0008	/* Disable Transmit CRC) */ +#define M_LOOP		0x0004	/* Loopback */ +#define M_DTX		0x0002	/* Disable the Transmitter */  +#define M_DRX		0x0001  /* Disable the Receiver */ + + +/* + * 	Receive message descriptor bit definitions. + */ + +#define RCV_OWN		0x80	/* owner bit 0 = host, 1 = lance */ +#define RCV_ERR		0x40	/* Error Summary */ +#define RCV_FRAM	0x20	/* Framing Error */ +#define RCV_OFLO	0x10	/* Overflow Error */ +#define RCV_CRC		0x08	/* CRC Error */  +#define RCV_BUF_ERR	0x04	/* Buffer Error */ +#define RCV_START	0x02	/* Start of Packet */ +#define RCV_END		0x01	/* End of Packet */ + + +/* + *	Transmit  message descriptor bit definitions. + */ + +#define XMIT_OWN	0x80	/* owner bit 0 = host, 1 = lance */ +#define XMIT_ERR	0x40    /* Error Summary */ +#define XMIT_RETRY	0x10	/* more the 1 retry needed to Xmit */ +#define XMIT_1_RETRY	0x08	/* one retry needed to Xmit */ +#define XMIT_DEF	0x04	/* Deferred */ +#define XMIT_START	0x02	/* Start of Packet */ +#define XMIT_END	0x01	/* End of Packet */ + +/* + * transmit status (2) (valid if XMIT_ERR == 1) + */ + +#define XMIT_TDRMASK    0x03ff  /* time-domain-reflectometer-value */ +#define XMIT_RTRY 	0x0400  /* Failed after 16 retransmissions  */ +#define XMIT_LCAR 	0x0800  /* Loss of Carrier */ +#define XMIT_LCOL 	0x1000  /* Late collision */ +#define XMIT_RESERV 	0x2000  /* Reserved */ +#define XMIT_UFLO 	0x4000  /* Underflow (late memory) */ +#define XMIT_BUFF 	0x8000  /* Buffering error (no ENP) */ + +struct init_block  +{ +  unsigned short mode; +  unsigned char eaddr[6]; +  unsigned char filter[8]; +  /* bit 29-31: number of rmd's (power of 2) */ +  u32 rrp;   /* receive ring pointer (align 8) */ +  /* bit 29-31: number of tmd's (power of 2) */ +  u32 trp;   /* transmit ring pointer (align 8) */ +}; + +struct rmd /* Receive Message Descriptor */ +{  +  union +  { +    volatile u32 buffer; +    struct  +    { +      volatile unsigned char dummy[3]; +      volatile unsigned char status;  +    } s; +  } u; +  volatile short blen; +  volatile unsigned short mlen; +}; + +struct tmd +{ +  union  +  { +    volatile u32 buffer; +    struct  +    { +      volatile unsigned char dummy[3]; +      volatile unsigned char status; +    } s; +  } u; +  volatile unsigned short blen; +  volatile unsigned short status2; +}; + + diff --git a/linux/src/drivers/net/ns820.c b/linux/src/drivers/net/ns820.c new file mode 100644 index 0000000..968f3ac --- /dev/null +++ b/linux/src/drivers/net/ns820.c @@ -0,0 +1,1547 @@ +/* ns820.c: A Linux Gigabit Ethernet driver for the NatSemi DP83820 series. */ +/* +	Written/copyright 1999-2003 by Donald Becker. +	Copyright 2002-2003 by Scyld Computing Corporation. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL.  License for under other terms may be +	available.  Contact the original author for details. + +	The original author may be reached as becker@scyld.com, or at +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Support information and updates available at +		http://www.scyld.com/network/natsemi.html +	The information and support mailing lists are based at +		http://www.scyld.com/mailman/listinfo/ +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"ns820.c:v1.03a 8/09/2003  Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/natsemi.html\n"; +/* Updated to recommendations in pci-skeleton v2.13. */ + +/* Automatically extracted configuration info: +probe-func: ns820_probe +config-in: tristate 'National Semiconductor DP8382x series PCI Ethernet support' CONFIG_NATSEMI820 + +c-help-name: National Semiconductor DP8382x series PCI Ethernet support +c-help-symbol: CONFIG_NATSEMI820 +c-help: This driver is for the National Semiconductor DP83820 Gigabit Ethernet +c-help: adapter series. +c-help: More specific information and updates are available from +c-help: http://www.scyld.com/network/natsemi.html +*/ + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   This chip uses a 2048 element hash table based on the Ethernet CRC. +   Previous natsemi chips had unreliable multicast filter circuitry. +   To work around an observed problem set this value to '0', +   which will immediately switch to Rx-all-multicast. +  */ +static int multicast_filter_limit = 100; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. +   This chip can only receive into aligned buffers, so architectures such +   as the Alpha AXP might benefit from a copy-align. +*/ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   Both 'options[]' and 'full_duplex[]' should exist for driver +   interoperability, however setting full_duplex[] is deprecated. +   The media type is usually passed in 'options[]'. +    The default is autonegotation for speed and duplex. +	This should rarely be overridden. +    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +    Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +    Use option values 0x20 and 0x200 for forcing full duplex operation. +	Use 0x1000 or 0x2000 for gigabit. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. +   Understand the implications before changing these settings! +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority, confuses the system network buffer limits, +   and wastes memory. +   Too-large receive rings waste memory and confound network buffer limits. +*/ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit ring entries actually used, min 4. */ +#define RX_RING_SIZE	64 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. +   Re-autonegotiation may take up to 3 seconds. + */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("National Semiconductor DP83820 series PCI Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, +				 "Non-zero to force full duplex, non-negotiated link " +				 "(deprecated)."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This driver is designed for National Semiconductor DP83820 10/100/1000 +Ethernet NIC.  It is superficially similar to the 810 series "natsemi.c" +driver, however the register layout, descriptor layout and element +length of the new chip series is different. + +II. Board-specific settings + +This driver requires the PCI interrupt line to be configured. +It honors the EEPROM-set values. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE. +The NatSemi design uses a 'next descriptor' pointer that the driver forms +into a list, thus rings can be arbitrarily sized.  Before changing the +ring sizes you should understand the flow and cache effects of the +full/available/empty hysteresis. + +IIIb/c. Transmit/Receive Structure + +This driver uses a zero-copy receive and transmit scheme. +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers.  When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack.  Buffers consumed this way are replaced by newly allocated +skbuffs in a later phase of receives. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets.  When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine.  Copying also preloads the cache, which is +most useful with small frames. + +A subtle aspect of the operation is that unaligned buffers are not permitted +by the hardware.  Thus the IP header at offset 14 in an ethernet frame isn't +longword aligned for further processing.  On copies frames are put into the +skbuff at an offset of "+2", 16-byte aligning the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +The NatSemi 820 series PCI gigabit chips are very common on low-cost NICs. +The '821 appears to be the same as '820 chip, only with pins for the upper +32 bits marked "N/C". + +IVb. References + +http://www.scyld.com/expert/100mbps.html +http://www.scyld.com/expert/NWay.html +The NatSemi dp83820 datasheet is available: search www.natsemi.com + +IVc. Errata + +None characterised. + +*/ + + + +static void *ns820_probe1(struct pci_dev *pdev, void *init_dev, +							long ioaddr, int irq, int chip_idx, int find_cnt); +static int power_event(void *dev_instance, int event); +enum chip_capability_flags {FDXActiveLow=1, InvertGbXcvrPwr=2, }; +#ifdef USE_IO_OPS +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO  | PCI_ADDR0) +#else +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{ "D-Link DGE-500T (DP83820)", +	  { 0x0022100B, 0xffffffff, 0x49001186, 0xffffffff, }, +	  PCI_IOTYPE, 256, FDXActiveLow}, +	{"NatSemi DP83820", { 0x0022100B, 0xffffffff }, +	 PCI_IOTYPE, 256, 0}, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info ns820_drv_id = { +	"ns820", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	ns820_probe1, power_event }; + +/* Offsets to the device registers. +   Unlike software-only systems, device drivers interact with complex hardware. +   It's not useful to define symbolic names for every register bit in the +   device.  Please do not change these names without good reason. +*/ +enum register_offsets { +	ChipCmd=0x00, ChipConfig=0x04, EECtrl=0x08, PCIBusCfg=0x0C, +	IntrStatus=0x10, IntrMask=0x14, IntrEnable=0x18, IntrHoldoff=0x1C, +	TxRingPtr=0x20, TxRingPtrHi=0x24, TxConfig=0x28, +	RxRingPtr=0x30, RxRingPtrHi=0x34, RxConfig=0x38, +	WOLCmd=0x40, PauseCmd=0x44, RxFilterAddr=0x48, RxFilterData=0x4C, +	BootRomAddr=0x50, BootRomData=0x54, ChipRevReg=0x58, +	StatsCtrl=0x5C, RxPktErrs=0x60, RxMissed=0x68, RxCRCErrs=0x64, +}; + +/* Bits in ChipCmd. */ +enum ChipCmdBits { +	ChipReset=0x100, SoftIntr=0x80, RxReset=0x20, TxReset=0x10, +	RxOff=0x08, RxOn=0x04, TxOff=0x02, TxOn=0x01, +}; + +/* Bits in ChipConfig. */ +enum ChipConfigBits { +	CfgLinkGood=0x80000000, CfgFDX=0x10000000, +	CfgXcrReset=0x0400, CfgXcrOff=0x0200, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrRxDone=0x0001, IntrRxIntr=0x0002, IntrRxErr=0x0004, IntrRxEarly=0x0008, +	IntrRxIdle=0x0010, IntrRxOverrun=0x0020, +	IntrTxDone=0x0040, IntrTxIntr=0x0080, IntrTxErr=0x0100, +	IntrTxIdle=0x0200, IntrTxUnderrun=0x0400, +	StatsMax=0x0800, IntrDrv=0x1000, WOLPkt=0x2000, LinkChange=0x4000, +	RxStatusOverrun=0x10000, +	RxResetDone=0x00200000, TxResetDone=0x00400000, +	IntrPCIErr=0x001E0000, +	IntrNormalSummary=0x0251, IntrAbnormalSummary=0xED20, +}; + +/* Bits in the RxMode register. */ +enum rx_mode_bits { +	AcceptErr=0x20, AcceptRunt=0x10, +	AcceptBroadcast=0xC0000000, +	AcceptMulticast=0x00200000, AcceptAllMulticast=0x20000000, +	AcceptAllPhys=0x10000000, AcceptMyPhys=0x08000000, +}; + +/* The Rx and Tx buffer descriptors. */ +/* Note that using only 32 bit fields simplifies conversion to big-endian +   architectures. */ +struct netdev_desc { +#if ADDRLEN == 64 +	u64 next_desc; +	u64 buf_addr; +#endif +	u32 next_desc; +	u32 buf_addr; +	s32 cmd_status; +	u32 vlan_status; +}; + +/* Bits in network_desc.status */ +enum desc_status_bits { +	DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000, +	DescNoCRC=0x10000000, +	DescPktOK=0x08000000, RxTooLong=0x00400000, +}; + +#define PRIV_ALIGN	15	/* Required alignment mask */ +struct netdev_private { +	/* Descriptor rings first for alignment. */ +	struct netdev_desc rx_ring[RX_RING_SIZE]; +	struct netdev_desc tx_ring[TX_RING_SIZE]; +	struct net_device *next_module;		/* Link for devices of this type. */ +	void *priv_addr;					/* Unaligned address for kfree */ +	const char *product_name; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for later free(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	/* Frequently used values: keep some adjacent for cache effect. */ +	int msg_level; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	long in_interrupt;			/* Word-long for SMP locks. */ +	int max_interrupt_work; +	int intr_enable; +	unsigned int restore_intr_enable:1;	/* Set if temporarily masked.  */ +	unsigned int rx_q_empty:1;			/* Set out-of-skbuffs.  */ + +	struct netdev_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	/* These values keep track of the transceiver/media in use. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	/* Rx filter. */ +	u32 cur_rx_mode; +	u32 rx_filter[16]; +	int multicast_filter_limit; +	/* FIFO and PCI burst thresholds. */ +	int tx_config, rx_config; +	/* MII transceiver section. */ +	u16 advertising;					/* NWay media advertisement */ +}; + +static int  eeprom_read(long ioaddr, int location); +static void mdio_sync(long mdio_addr); +static int  mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static int  netdev_open(struct net_device *dev); +static void check_duplex(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct net_device *dev); +static int  rx_ring_fill(struct net_device *dev); +static void init_ring(struct net_device *dev); +static int  start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int  netdev_rx(struct net_device *dev); +static void netdev_error(struct net_device *dev, int intr_status); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int  netdev_close(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_net_dev = NULL; + +#ifndef MODULE +int ns820_probe(struct net_device *dev) +{ +	if (pci_drv_register(&ns820_drv_id, dev) < 0) +		return -ENODEV; +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif + +static void *ns820_probe1(struct pci_dev *pdev, void *init_dev, +						  long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct netdev_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	/* Perhaps NETIF_MSG_PROBE */ +	printk(KERN_INFO "%s: %s at 0x%lx, ", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr); + +	for (i = 0; i < 3; i++) +		((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, 12 - i)); +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	/* Reset the chip to erase previous misconfiguration. */ +	writel(ChipReset, ioaddr + ChipCmd); +	/* Power up Xcvr. */ +	writel(~CfgXcrOff & readl(ioaddr + ChipConfig), ioaddr + ChipConfig); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_net_dev; +	root_net_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	if (dev->mem_start) +		option = dev->mem_start; + +	/* The lower four bits are the media type. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 0x33ff; +		if (np->default_port & 0x330) +			np->medialock = 1; +	} +	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) { +		if (np->msg_level & NETIF_MSG_PROBE) +			printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" +				   " disabled.\n", dev->name); +		np->duplex_lock = 1; +	} + +	/* The chip-specific entries in the device structure. */ +	dev->open = &netdev_open; +	dev->hard_start_xmit = &start_tx; +	dev->stop = &netdev_close; +	dev->get_stats = &get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; + +	/* Allow forcing the media type. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 0x3ff; +		if (np->default_port & 0x330) { +			np->medialock = 1; +			if (np->msg_level & NETIF_MSG_PROBE) +				printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +					   (option & 0x300 ? 100 : 10), +					   (np->full_duplex ? "full" : "half")); +			mdio_write(dev, 1, 0, +					   ((option & 0x300) ? 0x2000 : 0) | 	/* 100mbps? */ +					   (np->full_duplex ? 0x0100 : 0)); /* Full duplex? */ +		} +	} + +	return dev; +} + + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. +   The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses. +   Update to the code in other drivers for 8/10 bit addresses. +*/ + +/* Delay between EEPROM clock transitions. +   This "delay" forces out buffered PCI writes, which is sufficient to meet +   the timing requirements of most EEPROMs. +*/ +#define eeprom_delay(ee_addr)	readl(ee_addr) + +enum EEPROM_Ctrl_Bits { +	EE_ShiftClk=0x04, EE_DataIn=0x01, EE_ChipSelect=0x08, EE_DataOut=0x02, +}; +#define EE_Write0 (EE_ChipSelect) +#define EE_Write1 (EE_ChipSelect | EE_DataIn) + +/* The EEPROM commands include the 01 preamble. */ +enum EEPROM_Cmds { +	EE_WriteCmd=5, EE_ReadCmd=6, EE_EraseCmd=7, +}; + +static int eeprom_read(long addr, int location) +{ +	long eeprom_addr = addr + EECtrl; +	int read_cmd = (EE_ReadCmd << 6) | location; +	int retval = 0; +	int i; + +	writel(EE_Write0, eeprom_addr); + +	/* Shift the read command bits out. */ +	for (i = 10; i >= 0; i--) { +		int dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; +		writel(dataval, eeprom_addr); +		eeprom_delay(eeprom_addr); +		writel(dataval | EE_ShiftClk, eeprom_addr); +		eeprom_delay(eeprom_addr); +	} +	writel(EE_ChipSelect, eeprom_addr); +	eeprom_delay(eeprom_addr); + +	for (i = 15; i >= 0; i--) { +		writel(EE_ChipSelect | EE_ShiftClk, eeprom_addr); +		eeprom_delay(eeprom_addr); +		retval |= (readl(eeprom_addr) & EE_DataOut) ? 1 << i : 0; +		writel(EE_ChipSelect, eeprom_addr); +		eeprom_delay(eeprom_addr); +	} + +	/* Terminate the EEPROM access. */ +	writel(EE_Write0, eeprom_addr); +	writel(0, eeprom_addr); +	return retval; +} + +/*  MII transceiver control section. +	Read and write MII registers using software-generated serial MDIO +	protocol.  See the MII specifications or DP83840A data sheet for details. + +	The maximum data clock rate is 2.5 Mhz.  To meet minimum timing we +	must flush writes to the PCI bus with a PCI read. */ +#define mdio_delay(mdio_addr) readl(mdio_addr) + +/* Set iff a MII transceiver on any interface requires mdio preamble. +   This only set with older tranceivers, so the extra +   code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 0; + +enum mii_reg_bits { +	MDIO_ShiftClk=0x0040, MDIO_Data=0x0010, MDIO_EnbOutput=0x0020, +}; +#define MDIO_EnbIn  (0) +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and +   a few older transceivers. */ +static void mdio_sync(long mdio_addr) +{ +	int bits = 32; + +	/* Establish sync by sending at least 32 logic ones. */ +	while (--bits >= 0) { +		writel(MDIO_WRITE1, mdio_addr); +		mdio_delay(mdio_addr); +		writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +} + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ +	long mdio_addr = dev->base_addr + EECtrl; +	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; +	int i, retval = 0; + +	if (mii_preamble_required) +		mdio_sync(mdio_addr); + +	/* Shift the read command bits out. */ +	for (i = 15; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + +		writel(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		writel(dataval | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Read the two transition, 16 data, and wire-idle bits. */ +	for (i = 19; i > 0; i--) { +		writel(MDIO_EnbIn, mdio_addr); +		mdio_delay(mdio_addr); +		retval = (retval << 1) | ((readl(mdio_addr) & MDIO_Data) ? 1 : 0); +		writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ +	long mdio_addr = dev->base_addr + EECtrl; +	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; +	int i; + +	if (mii_preamble_required) +		mdio_sync(mdio_addr); + +	/* Shift the command bits out. */ +	for (i = 31; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + +		writel(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		writel(dataval | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Clear out extra bits. */ +	for (i = 2; i > 0; i--) { +		writel(MDIO_EnbIn, mdio_addr); +		mdio_delay(mdio_addr); +		writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return; +} + +static int netdev_open(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; +	u32 intr_status = readl(ioaddr + IntrStatus); + +	/* We have not yet encountered a case where we need to reset the chip. */ + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	/* Power up Xcvr. */ +	writel((~CfgXcrOff & readl(ioaddr + ChipConfig)) | 0x00400000, +		   ioaddr + ChipConfig); +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: netdev_open() irq %d intr_status %8.8x.\n", +			   dev->name, dev->irq, intr_status); + +	init_ring(dev); + +#if defined(ADDR_64BITS) && defined(__alpha__) +	writel(virt_to_bus(np->rx_ring) >> 32, ioaddr + RxRingPtrHi); +	writel(virt_to_bus(np->tx_ring) >> 32, ioaddr + TxRingPtrHi); +#else +	writel(0, ioaddr + RxRingPtrHi); +	writel(0, ioaddr + TxRingPtrHi); +#endif +	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); +	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); + +	for (i = 0; i < 6; i += 2) { +		writel(i, ioaddr + RxFilterAddr); +		writel(dev->dev_addr[i] + (dev->dev_addr[i+1] << 8), +			   ioaddr + RxFilterData); +	} + +	/* Initialize other registers. */ +	/* Configure the PCI bus bursts and FIFO thresholds. */ +	/* Configure for standard, in-spec Ethernet. */ + +	if (np->full_duplex  || +		((readl(ioaddr + ChipConfig) & CfgFDX) == 0) ^ +		((np->drv_flags & FDXActiveLow) != 0)) { +		np->tx_config = 0xD0801002; +		np->rx_config = 0x10000020; +	} else { +		np->tx_config = 0x10801002; +		np->rx_config = 0x0020; +	} +	if (dev->mtu > 1500) +		np->rx_config |= 0x08000000; +	writel(np->tx_config, ioaddr + TxConfig); +	writel(np->rx_config, ioaddr + RxConfig); +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Setting TxConfig to %8.8x.\n", +			   dev->name, (int)readl(ioaddr + TxConfig)); + +	if (dev->if_port == 0) +		dev->if_port = np->default_port; + +	np->in_interrupt = 0; + +	check_duplex(dev); +	set_rx_mode(dev); +	netif_start_tx_queue(dev); + +	/* Enable interrupts by setting the interrupt mask. */ +	np->intr_enable = IntrNormalSummary | IntrAbnormalSummary | 0x1f; +	writel(np->intr_enable, ioaddr + IntrMask); +	writel(1, ioaddr + IntrEnable); + +	writel(RxOn | TxOn, ioaddr + ChipCmd); +	writel(4, ioaddr + StatsCtrl);					/* Clear Stats */ + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done netdev_open(), status: %x.\n", +			   dev->name, (int)readl(ioaddr + ChipCmd)); + +	/* Set the timer to check for link beat. */ +	init_timer(&np->timer); +	np->timer.expires = jiffies + 3*HZ; +	np->timer.data = (unsigned long)dev; +	np->timer.function = &netdev_timer;				/* timer handler */ +	add_timer(&np->timer); + +	return 0; +} + +static void check_duplex(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int duplex; + +	if (np->duplex_lock) +		return; +	duplex = readl(ioaddr + ChipConfig) & CfgFDX ? 1 : 0; +	if (np->full_duplex != duplex) { +		np->full_duplex = duplex; +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Setting %s-duplex based on negotiated link" +				   " capability.\n", dev->name, +				   duplex ? "full" : "half"); +		if (duplex) { +			np->rx_config |= 0x10000000; +			np->tx_config |= 0xC0000000; +		} else { +			np->rx_config &= ~0x10000000; +			np->tx_config &= ~0xC0000000; +		} +		writel(np->tx_config, ioaddr + TxConfig); +		writel(np->rx_config, ioaddr + RxConfig); +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: Setting TxConfig to %8.8x (%8.8x).\n", +				   dev->name, np->tx_config, (int)readl(ioaddr + TxConfig)); +	} +} + +static void netdev_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 10*HZ; + +	if (np->msg_level & NETIF_MSG_TIMER) +		printk(KERN_DEBUG "%s: Driver monitor timer tick, status %8.8x.\n", +			   dev->name, (int)readl(ioaddr + ChipConfig)); +	if (np->rx_q_empty) { +		/* Trigger an interrupt to refill. */ +		writel(SoftIntr, ioaddr + ChipCmd); +	} +	if (netif_queue_paused(dev)  && +		np->cur_tx - np->dirty_tx > 1  && +		(jiffies - dev->trans_start) > TX_TIMEOUT) { +		tx_timeout(dev); +	} +	check_duplex(dev); +	np->timer.expires = jiffies + next_tick; +	add_timer(&np->timer); +} + +static void tx_timeout(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," +		   " resetting...\n", dev->name, (int)readl(ioaddr + TxRingPtr)); + +	if (np->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)np->rx_ring[i].cmd_status); +		printk("\n"KERN_DEBUG"  Tx ring %p: ", np->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %4.4x", np->tx_ring[i].cmd_status); +		printk("\n"); +	} + +	/* Perhaps we should reinitialize the hardware here. */ +	dev->if_port = 0; +	/* Stop and restart the chip's Tx processes . */ + +	/* Trigger an immediate transmit demand. */ + +	dev->trans_start = jiffies; +	np->stats.tx_errors++; +	return; +} + +/* Refill the Rx ring buffers, returning non-zero if not full. */ +static int rx_ring_fill(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned int entry; + +	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { +		entry = np->dirty_rx % RX_RING_SIZE; +		if (np->rx_skbuff[entry] == NULL) { +			struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); +			np->rx_skbuff[entry] = skb; +			if (skb == NULL) +				return 1;				/* Better luck next time. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			np->rx_ring[entry].buf_addr = virt_to_bus(skb->tail); +		} +		np->rx_ring[entry].cmd_status = cpu_to_le32(DescIntr | np->rx_buf_sz); +	} +	return 0; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	np->tx_full = 0; +	np->cur_rx = np->cur_tx = 0; +	np->dirty_rx = np->dirty_tx = 0; + +	/* MAX(PKT_BUF_SZ, dev->mtu + 8); */ +	/* I know you _want_ to change this without understanding it.  Don't. */ +	np->rx_buf_sz = (dev->mtu <= 1532 ? PKT_BUF_SZ : dev->mtu + 8); +	np->rx_head_desc = &np->rx_ring[0]; + +	/* Initialize all Rx descriptors. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]); +		np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn); +		np->rx_skbuff[i] = 0; +	} +	/* Mark the last entry as wrapping the ring. */ +	np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]); + +	for (i = 0; i < TX_RING_SIZE; i++) { +		np->tx_skbuff[i] = 0; +		np->tx_ring[i].next_desc = virt_to_bus(&np->tx_ring[i+1]); +		np->tx_ring[i].cmd_status = 0; +	} +	np->tx_ring[i-1].next_desc = virt_to_bus(&np->tx_ring[0]); + +	/* Fill in the Rx buffers. +	   Allocation failure just leaves a "negative" np->dirty_rx. */ +	np->dirty_rx = (unsigned int)(0 - RX_RING_SIZE); +	rx_ring_fill(dev); + +	return; +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned int entry; + +	/* Block a timer-based transmit from overlapping.  This happens when +	   packets are presumed lost, and we use this check the Tx status. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tx_timeout(dev); +		return 1; +	} + +	/* Note: Ordering is important here, set the field with the +	   "ownership" bit last, and only then increment cur_tx. +	   No spinlock is needed for either Tx or Rx. +	*/ + +	/* Calculate the next Tx descriptor entry. */ +	entry = np->cur_tx % TX_RING_SIZE; + +	np->tx_skbuff[entry] = skb; + +	np->tx_ring[entry].buf_addr = virt_to_bus(skb->data); +	np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn|DescIntr | skb->len); +	np->cur_tx++; + +	/* StrongARM: Explicitly cache flush np->tx_ring and skb->data,skb->len. */ + +	if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) { +		np->tx_full = 1; +		/* Check for a just-cleared queue. */ +		if (np->cur_tx - (volatile unsigned int)np->dirty_tx +			< TX_QUEUE_LEN - 4) { +			np->tx_full = 0; +			netif_unpause_tx_queue(dev); +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ +	/* Wake the potentially-idle transmit channel. */ +	writel(TxOn, dev->base_addr + ChipCmd); + +	dev->trans_start = jiffies; + +	if (np->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", +			   dev->name, np->cur_tx, entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct netdev_private *np; +	long ioaddr; +	int boguscnt; + +#ifndef final_version			/* Can never occur. */ +	if (dev == NULL) { +		printk (KERN_ERR "Netdev interrupt handler(): IRQ %d for unknown " +				"device.\n", irq); +		return; +	} +#endif + +	ioaddr = dev->base_addr; +	np = (struct netdev_private *)dev->priv; +	boguscnt = np->max_interrupt_work; + +#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x020300 +	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */ +	if (test_and_set_bit(0, (void*)&dev->interrupt)) { +		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", +			   dev->name); +		dev->interrupt = 0;	/* Avoid halting machine. */ +		return; +	} +#endif + +	do { +		u32 intr_status = readl(ioaddr + IntrStatus); + +		if (np->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt, status %8.8x.\n", +				   dev->name, intr_status); + +		if (intr_status == 0 || intr_status == 0xffffffff) +			break; + +		/* Acknowledge all of the current interrupt sources ASAP. +		   Nominally the read above accomplishes this, but... */ +		writel(intr_status & 0x001ffff, ioaddr + IntrStatus); + +		if (intr_status & (IntrRxDone | IntrRxIntr)) { +			netdev_rx(dev); +			np->rx_q_empty = rx_ring_fill(dev); +		} + +		if (intr_status & (IntrRxIdle | IntrDrv)) { +			unsigned int old_dirty_rx = np->dirty_rx; +			if (rx_ring_fill(dev) == 0) +				np->rx_q_empty = 0; +			/* Restart Rx engine iff we did add a buffer. */ +			if (np->dirty_rx != old_dirty_rx) +				writel(RxOn, dev->base_addr + ChipCmd); +		} + +		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { +			int entry = np->dirty_tx % TX_RING_SIZE; +			if (np->msg_level & NETIF_MSG_INTR) +				printk(KERN_DEBUG "%s: Tx entry %d @%p status %8.8x.\n", +					   dev->name, entry, &np->tx_ring[entry], +					   np->tx_ring[entry].cmd_status); +			if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescOwn)) +				break; +			if (np->tx_ring[entry].cmd_status & cpu_to_le32(0x08000000)) { +				if (np->msg_level & NETIF_MSG_TX_DONE) +					printk(KERN_DEBUG "%s: Transmit done, Tx status %8.8x.\n", +						   dev->name, np->tx_ring[entry].cmd_status); +				np->stats.tx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +				np->stats.tx_bytes += np->tx_skbuff[entry]->len; +#endif +			} else {			/* Various Tx errors */ +				int tx_status = le32_to_cpu(np->tx_ring[entry].cmd_status); +				if (tx_status & 0x04010000) np->stats.tx_aborted_errors++; +				if (tx_status & 0x02000000) np->stats.tx_fifo_errors++; +				if (tx_status & 0x01000000) np->stats.tx_carrier_errors++; +				if (tx_status & 0x00200000) np->stats.tx_window_errors++; +				if (np->msg_level & NETIF_MSG_TX_ERR) +					printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", +						   dev->name, tx_status); +				np->stats.tx_errors++; +			} +			/* Free the original skb. */ +			dev_free_skb_irq(np->tx_skbuff[entry]); +			np->tx_skbuff[entry] = 0; +		} +		/* Note the 4 slot hysteresis to mark the queue non-full. */ +		if (np->tx_full +			&& np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { +			/* The ring is no longer full, allow new TX entries. */ +			np->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & IntrAbnormalSummary) +			netdev_error(dev, intr_status); + +		if (--boguscnt < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", +				   dev->name, intr_status); +			np->restore_intr_enable = 1; +			break; +		} +	} while (1); + +	if (np->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); + +#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x020300 +	clear_bit(0, (void*)&dev->interrupt); +#endif +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int entry = np->cur_rx % RX_RING_SIZE; +	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; +	s32 desc_status = le32_to_cpu(np->rx_head_desc->cmd_status); + +	/* If the driver owns the next entry it's a new packet. Send it up. */ +	while (desc_status < 0) {        /* e.g. & DescOwn */ +		if (np->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  In netdev_rx() entry %d status was %8.8x.\n", +				   entry, desc_status); +		if (--boguscnt < 0) +			break; +		if ((desc_status & (DescMore|DescPktOK|RxTooLong)) != DescPktOK) { +			if (desc_status & DescMore) { +				printk(KERN_WARNING "%s: Oversized(?) Ethernet frame spanned " +					   "multiple buffers, entry %#x status %x.\n", +					   dev->name, np->cur_rx, desc_status); +				np->stats.rx_length_errors++; +			} else { +				/* There was a error. */ +				if (np->msg_level & NETIF_MSG_RX_ERR) +					printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n", +						   desc_status); +				np->stats.rx_errors++; +				if (desc_status & 0x06000000) np->stats.rx_over_errors++; +				if (desc_status & 0x00600000) np->stats.rx_length_errors++; +				if (desc_status & 0x00140000) np->stats.rx_frame_errors++; +				if (desc_status & 0x00080000) np->stats.rx_crc_errors++; +			} +		} else { +			struct sk_buff *skb; +			int pkt_len = (desc_status & 0x0fff) - 4;	/* Omit CRC size. */ +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < np->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +#if HAS_IP_COPYSUM +				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +			} else { +				skb_put(skb = np->rx_skbuff[entry], pkt_len); +				np->rx_skbuff[entry] = NULL; +			} +#ifndef final_version				/* Remove after testing. */ +			/* You will want this info for the initial debug. */ +			if (np->msg_level & NETIF_MSG_PKTDATA) +				printk(KERN_DEBUG "  Rx data %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:" +					   "%2.2x %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x %2.2x%2.2x " +					   "%d.%d.%d.%d.\n", +					   skb->data[0], skb->data[1], skb->data[2], skb->data[3], +					   skb->data[4], skb->data[5], skb->data[6], skb->data[7], +					   skb->data[8], skb->data[9], skb->data[10], +					   skb->data[11], skb->data[12], skb->data[13], +					   skb->data[14], skb->data[15], skb->data[16], +					   skb->data[17]); +#endif +			skb->protocol = eth_type_trans(skb, dev); +			/* W/ hardware checksum: skb->ip_summed = CHECKSUM_UNNECESSARY; */ +			netif_rx(skb); +			dev->last_rx = jiffies; +			np->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			np->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++np->cur_rx) % RX_RING_SIZE; +		np->rx_head_desc = &np->rx_ring[entry]; +		desc_status = le32_to_cpu(np->rx_head_desc->cmd_status); +	} + +	/* Refill is now done in the main interrupt loop. */ +	return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (intr_status & LinkChange) { +		int chip_config = readl(ioaddr + ChipConfig); +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_NOTICE "%s: Link changed: Autonegotiation advertising" +				   " %4.4x  partner %4.4x.\n", dev->name, +				   (int)readl(ioaddr + 0x90), (int)readl(ioaddr + 0x94)); +		if (chip_config & CfgLinkGood) +			netif_link_up(dev); +		else +			netif_link_down(dev); +		check_duplex(dev); +	} +	if (intr_status & StatsMax) { +		get_stats(dev); +	} +	if (intr_status & IntrTxUnderrun) { +		/* Increase the Tx threshold, 32 byte units. */ +		if ((np->tx_config & 0x3f) < 62) +			np->tx_config += 2;			/* +64 bytes */ +		writel(np->tx_config, ioaddr + TxConfig); +	} +	if (intr_status & WOLPkt) { +		int wol_status = readl(ioaddr + WOLCmd); +		printk(KERN_NOTICE "%s: Link wake-up event %8.8x", +			   dev->name, wol_status); +	} +	if (intr_status & (RxStatusOverrun | IntrRxOverrun)) { +		if (np->msg_level & NETIF_MSG_DRV) +			printk(KERN_ERR "%s: Rx overflow! ns820 %8.8x.\n", +				   dev->name, intr_status); +		np->stats.rx_fifo_errors++; +	} +	if (intr_status & ~(LinkChange|StatsMax|RxResetDone|TxResetDone| +						RxStatusOverrun|0xA7ff)) { +		if (np->msg_level & NETIF_MSG_DRV) +			printk(KERN_ERR "%s: Something Wicked happened! ns820 %8.8x.\n", +				   dev->name, intr_status); +	} +	/* Hmmmmm, it's not clear how to recover from PCI faults. */ +	if (intr_status & IntrPCIErr) { +		np->stats.tx_fifo_errors++; +		np->stats.rx_fifo_errors++; +	} +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int crc_errs = readl(ioaddr + RxCRCErrs); + +	if (crc_errs != 0xffffffff) { +		/* We need not lock this segment of code for SMP. +		   There is no atomic-add vulnerability for most CPUs, +		   and statistics are non-critical. */ +		/* The chip only need report frame silently dropped. */ +		np->stats.rx_crc_errors	+= crc_errs; +		np->stats.rx_missed_errors += readl(ioaddr + RxMissed); +	} + +	return &np->stats; +} + +/* The little-endian AUTODIN II ethernet CRC calculations. +   A big-endian version is also available. +   This is slow but compact code.  Do not use this routine for bulk data, +   use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c. +   Chips may use the upper or lower CRC bits, and may reverse and/or invert +   them.  Select the endian-ness that results in minimal calculations. +*/ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ +	unsigned int crc = 0xffffffff;	/* Initial value. */ +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u8 mc_filter[64];			/* Multicast hash filter */ +	u32 rx_mode; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		rx_mode = AcceptBroadcast | AcceptAllMulticast | AcceptAllPhys +			| AcceptMyPhys; +	} else if ((dev->mc_count > np->multicast_filter_limit) +			   ||  (dev->flags & IFF_ALLMULTI)) { +		rx_mode = AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys; +	} else { +		struct dev_mc_list *mclist; +		int i; +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x7ff, +					mc_filter); +		} +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; +		for (i = 0; i < 64; i += 2) { +			writel(rx_mode + 0x200 + i, ioaddr + RxFilterAddr); +			writel((mc_filter[i+1]<<8) + mc_filter[i], ioaddr + RxFilterData); +		} +	} +	writel(rx_mode, ioaddr + RxFilterAddr); +	np->cur_rx_mode = rx_mode; +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = 1; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == 1) { +			u16 miireg = data[1] & 0x1f; +			u16 value = data[2]; +			switch (miireg) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				np->duplex_lock = (value & 0x9000) ? 0 : 1; +				if (np->duplex_lock) +					np->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: np->advertising = value; break; +			} +		} +		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static int netdev_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x " +			   "Int %2.2x.\n", +			   dev->name, (int)readl(ioaddr + ChipCmd), +			   (int)readl(ioaddr + IntrStatus)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); +	} + +	/* We don't want the timer to re-start anything. */ +	del_timer(&np->timer); + +	/* Disable interrupts using the mask. */ +	writel(0, ioaddr + IntrMask); +	writel(0, ioaddr + IntrEnable); +	writel(2, ioaddr + StatsCtrl);					/* Freeze Stats */ + +	/* Stop the chip's Tx and Rx processes. */ +	writel(RxOff | TxOff, ioaddr + ChipCmd); + +	get_stats(dev); + +#ifdef __i386__ +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(np->tx_ring)); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" #%d desc. %8.8x %8.8x.\n", +				   i, np->tx_ring[i].cmd_status, (u32)np->tx_ring[i].buf_addr); +		printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n", +			   (int)virt_to_bus(np->rx_ring)); +		for (i = 0; i < RX_RING_SIZE; i++) { +			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x\n", +				   i, np->rx_ring[i].cmd_status, (u32)np->rx_ring[i].buf_addr); +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].cmd_status = 0; +		np->rx_ring[i].buf_addr = 0xBADF00D0; /* An invalid address. */ +		if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			np->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(np->rx_skbuff[i]); +		} +		np->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (np->tx_skbuff[i]) +			dev_free_skb(np->tx_skbuff[i]); +		np->tx_skbuff[i] = 0; +	} + +	/* Power down Xcvr. */ +	writel(CfgXcrOff | readl(ioaddr + ChipConfig), ioaddr + ChipConfig); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static int power_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		/* Disable interrupts, freeze stats, stop Tx and Rx. */ +		writel(0, ioaddr + IntrEnable); +		writel(2, ioaddr + StatsCtrl); +		writel(RxOff | TxOff, ioaddr + ChipCmd); +		writel(CfgXcrOff | readl(ioaddr + ChipConfig), ioaddr + ChipConfig); +		break; +	case DRV_RESUME: +		/* This is incomplete: the open() actions should be repeated. */ +		writel(~CfgXcrOff & readl(ioaddr + ChipConfig), ioaddr + ChipConfig); +		set_rx_mode(dev); +		writel(np->intr_enable, ioaddr + IntrEnable); +		writel(1, ioaddr + IntrEnable); +		writel(RxOn | TxOn, ioaddr + ChipCmd); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			/* Some, but not all, kernel versions close automatically. */ +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size); +		for (devp = &root_net_dev; *devp; devp = next) { +			next = &((struct netdev_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	} + +	return 0; +} + + +#ifdef MODULE +int init_module(void) +{ +	/* Emit version even if no cards detected. */ +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +#ifdef CARDBUS +	register_driver(ðerdev_ops); +	return 0; +#else +	return pci_drv_register(&ns820_drv_id, NULL); +#endif +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +#ifdef CARDBUS +	unregister_driver(ðerdev_ops); +#else +	pci_drv_unregister(&ns820_drv_id); +#endif + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_net_dev) { +		struct netdev_private *np = (void *)(root_net_dev->priv); +		unregister_netdev(root_net_dev); +		iounmap((char *)root_net_dev->base_addr); +		next_dev = np->next_module; +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_net_dev); +		root_net_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` ns820.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c ns820.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c ns820.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/pci-scan.c b/linux/src/drivers/net/pci-scan.c new file mode 100644 index 0000000..ffb7b12 --- /dev/null +++ b/linux/src/drivers/net/pci-scan.c @@ -0,0 +1,659 @@ +/* pci-scan.c: Linux PCI network adapter support code. */ +/* +	Originally written 1999-2003 by Donald Becker. + +	This software may be used and distributed according to the terms +	of the GNU General Public License (GPL), incorporated herein by +	reference.  Drivers interacting with these functions are derivative +	works and thus also must be licensed under the GPL and include an explicit +	GPL notice. + +	This code provides common scan and activate functions for PCI network +	interfaces. + +	The author may be reached as becker@scyld.com, or +	Donald Becker +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Other contributers: +*/ +static const char version[] = +"pci-scan.c:v1.12 7/30/2003  Donald Becker <becker@scyld.com>" +" http://www.scyld.com/linux/drivers.html\n"; + +/* A few user-configurable values that may be modified when a module. */ + +static int msg_level = 1;		/* 1 normal messages, 0 quiet .. 7 verbose. */ +static int min_pci_latency = 32; + +#if ! defined(__KERNEL__) +#define __KERNEL__ 1 +#endif +#if !defined(__OPTIMIZE__) && /* Mach glue, we think this is ok now: */ 0 +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with the proper options, including "-O". +#endif + +#if defined(MODULE) && ! defined(EXPORT_SYMTAB) +#define EXPORT_SYMTAB +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#if LINUX_VERSION_CODE < 0x20500  &&  defined(MODVERSIONS) +/* Another interface semantics screw-up. */ +#include <linux/module.h> +#include <linux/modversions.h> +#else +#include <linux/module.h> +#endif + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20300 +/* Bogus change in the middle of a "stable" kernel series. +   Also, in 2.4.7+ slab must come before interrupt.h to avoid breakage. */ +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <asm/io.h> +#include "pci-scan.h" +#include "kern_compat.h" +#if defined(CONFIG_APM)  &&  LINUX_VERSION_CODE < 0x20400  +#include <linux/apm_bios.h> +#endif +#ifdef CONFIG_PM +/* New in 2.4 kernels, pointlessly incompatible with earlier APM. */ +#include <linux/pm.h> +#endif + +#if (LINUX_VERSION_CODE >= 0x20100) && defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif +#if (LINUX_VERSION_CODE < 0x20100) +#define PCI_CAPABILITY_LIST	0x34	/* Offset of first capability list entry */ +#define PCI_STATUS_CAP_LIST	0x10	/* Support Capability List */ +#define PCI_CAP_ID_PM		0x01	/* Power Management */ +#endif + +int (*register_hotswap_hook)(struct drv_id_info *did); +void (*unregister_hotswap_hook)(struct drv_id_info *did); + +#if LINUX_VERSION_CODE > 0x20118  &&  defined(MODULE) +MODULE_LICENSE("GPL"); +MODULE_PARM(msg_level, "i"); +MODULE_PARM(min_pci_latency, "i"); +MODULE_PARM_DESC(msg_level, "Enable additional status messages (0-7)"); +MODULE_PARM_DESC(min_pci_latency, +				 "Minimum value for the PCI Latency Timer settings"); +#if defined(EXPORT_SYMTAB) +EXPORT_SYMBOL_NOVERS(pci_drv_register); +EXPORT_SYMBOL_NOVERS(pci_drv_unregister); +EXPORT_SYMBOL_NOVERS(acpi_wake); +EXPORT_SYMBOL_NOVERS(acpi_set_pwr_state); +EXPORT_SYMBOL_NOVERS(register_hotswap_hook); +EXPORT_SYMBOL_NOVERS(unregister_hotswap_hook); +#endif +#endif + +/* List of registered drivers. */ +static struct drv_id_info *drv_list; +/* List of detected PCI devices, for APM events. */ +static struct dev_info { +	struct dev_info *next; +	void *dev; +	struct drv_id_info *drv_id; +	int flags; +} *dev_list; + +/* +  This code is not intended to support every configuration. +  It is intended to minimize duplicated code by providing the functions +  needed in almost every PCI driver. + +  The "no kitchen sink" policy: +  Additional features and code will be added to this module only if more +  than half of the drivers for common hardware would benefit from the feature. +*/ + +/* +  Ideally we would detect and number all cards of a type (e.g. network) in +  PCI slot order. +  But that does not work with hot-swap card, CardBus cards and added drivers. +  So instead we detect just the each chip table in slot order. + +  This routine takes a PCI ID table, scans the PCI bus, and calls the +  associated attach/probe1 routine with the hardware already activated and +  single I/O or memory address already mapped. + +  This routine will later be supplemented with CardBus and hot-swap PCI +  support using the same table.  Thus the pci_chip_tbl[] should not be +  marked as __initdata. +*/ + +#if LINUX_VERSION_CODE >= 0x20200 +/* Grrrr.. complex abstaction layers with negative benefit. */ +int pci_drv_register(struct drv_id_info *drv_id, void *initial_device) +{ +	int chip_idx, cards_found = 0; +	struct pci_dev *pdev = NULL; +	struct pci_id_info *pci_tbl = drv_id->pci_dev_tbl; +	struct drv_id_info *drv; +	void *newdev; + + +	/* Ignore a double-register attempt. */ +	for (drv = drv_list; drv; drv = drv->next) +		if (drv == drv_id) +			return -EBUSY; + +	while ((pdev = pci_find_class(drv_id->pci_class, pdev)) != 0) { +		u32 pci_id, pci_subsys_id, pci_class_rev; +		u16 pci_command, new_command; +		int pci_flags; +		long pciaddr;			/* Bus address. */ +		long ioaddr;			/* Mapped address for this processor. */ + +		pci_read_config_dword(pdev, PCI_VENDOR_ID, &pci_id); +		/* Offset 0x2c is PCI_SUBSYSTEM_ID aka PCI_SUBSYSTEM_VENDOR_ID. */ +		pci_read_config_dword(pdev, 0x2c, &pci_subsys_id); +		pci_read_config_dword(pdev, PCI_REVISION_ID, &pci_class_rev); + +		if (msg_level > 3) +			printk(KERN_DEBUG "PCI ID %8.8x subsystem ID is %8.8x.\n", +				   pci_id, pci_subsys_id); +		for (chip_idx = 0; pci_tbl[chip_idx].name; chip_idx++) { +			struct pci_id_info *chip = &pci_tbl[chip_idx]; +			if ((pci_id & chip->id.pci_mask) == chip->id.pci +				&& (pci_subsys_id&chip->id.subsystem_mask) == chip->id.subsystem +				&& (pci_class_rev&chip->id.revision_mask) == chip->id.revision) +				break; +		} +		if (pci_tbl[chip_idx].name == 0) 		/* Compiled out! */ +			continue; + +		pci_flags = pci_tbl[chip_idx].pci_flags; +#if LINUX_VERSION_CODE >= 0x2030C +		/* Wow. A oversized, hard-to-use abstraction. Bogus. */ +		pciaddr = pdev->resource[(pci_flags >> 4) & 7].start; +#else +		pciaddr = pdev->base_address[(pci_flags >> 4) & 7]; +#if defined(__alpha__)			/* Really any machine with 64 bit addressing. */ +		if (pci_flags & PCI_ADDR_64BITS) +			pciaddr |= ((long)pdev->base_address[((pci_flags>>4)&7)+ 1]) << 32; +#endif +#endif +		if (msg_level > 2) +			printk(KERN_INFO "Found %s at PCI address %#lx, mapped IRQ %d.\n", +				   pci_tbl[chip_idx].name, pciaddr, pdev->irq); + +		if ( ! (pci_flags & PCI_UNUSED_IRQ)  && +			 (pdev->irq == 0 || pdev->irq == 255)) { +			if (pdev->bus->number == 32) 	/* Broken CardBus activation. */ +				printk(KERN_WARNING "Resources for CardBus device '%s' have" +					   " not been allocated.\n" +					   KERN_WARNING "Activation has been delayed.\n", +					   pci_tbl[chip_idx].name); +			else +				printk(KERN_WARNING "PCI device '%s' was not assigned an " +					   "IRQ.\n" +					   KERN_WARNING "It will not be activated.\n", +				   pci_tbl[chip_idx].name); +			continue; +		} +		if ((pci_flags & PCI_BASE_ADDRESS_SPACE_IO)) { +			ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK; +			if (check_region(ioaddr, pci_tbl[chip_idx].io_size)) +				continue; +		} else if ((ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, +										   pci_tbl[chip_idx].io_size)) == 0) { +			printk(KERN_INFO "Failed to map PCI address %#lx for device " +				   "'%s'.\n", pciaddr, pci_tbl[chip_idx].name); +			continue; +		} +		if ( ! (pci_flags & PCI_NO_ACPI_WAKE)) +			acpi_wake(pdev); +		pci_read_config_word(pdev, PCI_COMMAND, &pci_command); +		new_command = pci_command | (pci_flags & 7); +		if (pci_command != new_command) { +			printk(KERN_INFO "  The PCI BIOS has not enabled the" +				   " device at %d/%d!  Updating PCI command %4.4x->%4.4x.\n", +				   pdev->bus->number, pdev->devfn, pci_command, new_command); +			pci_write_config_word(pdev, PCI_COMMAND, new_command); +		} + +		newdev = drv_id->probe1(pdev, initial_device, +								ioaddr, pdev->irq, chip_idx, cards_found); +		if (newdev == NULL) +			continue; +		initial_device = 0; +		cards_found++; +		if (pci_flags & PCI_COMMAND_MASTER) { +			pci_set_master(pdev); +			if ( ! (pci_flags & PCI_NO_MIN_LATENCY)) { +				u8 pci_latency; +				pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); +				if (pci_latency < min_pci_latency) { +					printk(KERN_INFO "  PCI latency timer (CFLT) is " +						   "unreasonably low at %d.  Setting to %d clocks.\n", +						   pci_latency, min_pci_latency); +					pci_write_config_byte(pdev, PCI_LATENCY_TIMER, +										  min_pci_latency); +				} +			} +		} +		{ +			struct dev_info *devp = +				kmalloc(sizeof(struct dev_info), GFP_KERNEL); +			if (devp == 0) +				continue; +			devp->next = dev_list; +			devp->dev = newdev; +			devp->drv_id = drv_id; +			dev_list = devp; +		} +	} + +	if (((drv_id->flags & PCI_HOTSWAP) +		 && register_hotswap_hook && (*register_hotswap_hook)(drv_id) == 0) +		|| cards_found) { +		MOD_INC_USE_COUNT; +		drv_id->next = drv_list; +		drv_list = drv_id; +		return 0; +	} else +		return -ENODEV; +} +#else +int pci_drv_register(struct drv_id_info *drv_id, void *initial_device) +{ +	int pci_index, cards_found = 0; +	unsigned char pci_bus, pci_device_fn; +	struct pci_dev *pdev; +	struct pci_id_info *pci_tbl = drv_id->pci_dev_tbl; +	void *newdev; + +	if ( ! pcibios_present()) +		return -ENODEV; + +	for (pci_index = 0; pci_index < 0xff; pci_index++) { +		u32 pci_id, subsys_id, pci_class_rev; +		u16 pci_command, new_command; +		int chip_idx, irq, pci_flags; +		long pciaddr; +		long ioaddr; +		u32 pci_busaddr; +		u8 pci_irq_line; + +		if (pcibios_find_class (drv_id->pci_class, pci_index, +								&pci_bus, &pci_device_fn) +			!= PCIBIOS_SUCCESSFUL) +			break; +		pcibios_read_config_dword(pci_bus, pci_device_fn, +								  PCI_VENDOR_ID, &pci_id); +		/* Offset 0x2c is PCI_SUBSYSTEM_ID aka PCI_SUBSYSTEM_VENDOR_ID. */ +		pcibios_read_config_dword(pci_bus, pci_device_fn, 0x2c, &subsys_id); +		pcibios_read_config_dword(pci_bus, pci_device_fn, +								  PCI_REVISION_ID, &pci_class_rev); + +		for (chip_idx = 0; pci_tbl[chip_idx].name; chip_idx++) { +			struct pci_id_info *chip = &pci_tbl[chip_idx]; +			if ((pci_id & chip->id.pci_mask) == chip->id.pci +				&& (subsys_id & chip->id.subsystem_mask) == chip->id.subsystem +				&& (pci_class_rev&chip->id.revision_mask) == chip->id.revision) +				break; +		} +		if (pci_tbl[chip_idx].name == 0) 		/* Compiled out! */ +			continue; + +		pci_flags = pci_tbl[chip_idx].pci_flags; +		pdev = pci_find_slot(pci_bus, pci_device_fn); +		pcibios_read_config_byte(pci_bus, pci_device_fn, +								 PCI_INTERRUPT_LINE, &pci_irq_line); +		irq = pci_irq_line; +		pcibios_read_config_dword(pci_bus, pci_device_fn, +								  ((pci_flags >> 2) & 0x1C) + 0x10, +								  &pci_busaddr); +		pciaddr = pci_busaddr; +#if defined(__alpha__) +		if (pci_flags & PCI_ADDR_64BITS) { +			pcibios_read_config_dword(pci_bus, pci_device_fn, +									  ((pci_flags >> 2) & 0x1C) + 0x14, +									  &pci_busaddr); +			pciaddr |= ((long)pci_busaddr)<<32; +		} +#endif + +		if (msg_level > 2) +			printk(KERN_INFO "Found %s at PCI address %#lx, IRQ %d.\n", +				   pci_tbl[chip_idx].name, pciaddr, irq); + +		if ( ! (pci_flags & PCI_UNUSED_IRQ)  && +			 (irq == 0 || irq >= 16)) { +			if (pci_bus == 32) 	/* Broken CardBus activation. */ +				printk(KERN_WARNING "Resources for CardBus device '%s' have" +					   " not been allocated.\n" +					   KERN_WARNING "It will not be activated.\n", +					   pci_tbl[chip_idx].name); +			else +				printk(KERN_WARNING "PCI device '%s' was not assigned an " +					   "IRQ.\n" +					   KERN_WARNING "It will not be activated.\n", +				   pci_tbl[chip_idx].name); +			continue; +		} + +		if ((pciaddr & PCI_BASE_ADDRESS_SPACE_IO)) { +			ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK; +			if (check_region(ioaddr, pci_tbl[chip_idx].io_size)) +				continue; +		} else if ((ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, +										   pci_tbl[chip_idx].io_size)) == 0) { +			printk(KERN_INFO "Failed to map PCI address %#lx.\n", +				   pciaddr); +			continue; +		} + +		if ( ! (pci_flags & PCI_NO_ACPI_WAKE)) +			acpi_wake(pdev); +		pcibios_read_config_word(pci_bus, pci_device_fn, +								 PCI_COMMAND, &pci_command); +		new_command = pci_command | (pci_flags & 7); +		if (pci_command != new_command) { +			printk(KERN_INFO "  The PCI BIOS has not enabled the" +				   " device at %d/%d!  Updating PCI command %4.4x->%4.4x.\n", +				   pci_bus, pci_device_fn, pci_command, new_command); +			pcibios_write_config_word(pci_bus, pci_device_fn, +									  PCI_COMMAND, new_command); +		} + +		newdev = drv_id->probe1(pdev, initial_device, +							   ioaddr, irq, chip_idx, cards_found); + +		if (newdev  && (pci_flags & PCI_COMMAND_MASTER)  && +			! (pci_flags & PCI_NO_MIN_LATENCY)) { +			u8 pci_latency; +			pcibios_read_config_byte(pci_bus, pci_device_fn, +									 PCI_LATENCY_TIMER, &pci_latency); +			if (pci_latency < min_pci_latency) { +				printk(KERN_INFO "  PCI latency timer (CFLT) is " +					   "unreasonably low at %d.  Setting to %d clocks.\n", +					   pci_latency, min_pci_latency); +				pcibios_write_config_byte(pci_bus, pci_device_fn, +										  PCI_LATENCY_TIMER, min_pci_latency); +			} +		} +		if (newdev) { +			struct dev_info *devp = +				kmalloc(sizeof(struct dev_info), GFP_KERNEL); +			if (devp) { +				devp->next = dev_list; +				devp->dev = newdev; +				devp->drv_id = drv_id; +				dev_list = devp; +			} +		} +		initial_device = 0; +		cards_found++; +	} + +	if (((drv_id->flags & PCI_HOTSWAP) +		 && register_hotswap_hook && (*register_hotswap_hook)(drv_id) == 0) +		|| cards_found) { +		MOD_INC_USE_COUNT; +		drv_id->next = drv_list; +		drv_list = drv_id; +		return 0; +	} else +		return cards_found ? 0 : -ENODEV; +} +#endif + +void pci_drv_unregister(struct drv_id_info *drv_id) +{ +	struct drv_id_info **drvp; +	struct dev_info **devip = &dev_list; + +	if (unregister_hotswap_hook) +		(*unregister_hotswap_hook)(drv_id); + +	for (drvp = &drv_list; *drvp; drvp = &(*drvp)->next) +		if (*drvp == drv_id) { +			*drvp = (*drvp)->next; +			MOD_DEC_USE_COUNT; +			break; +		} +	while (*devip) { +		struct dev_info *thisdevi = *devip; +		if (thisdevi->drv_id == drv_id) { +			*devip = thisdevi->next; +			kfree(thisdevi); +		} else +			devip = &(*devip)->next; +	} + +	return; +} + +#if LINUX_VERSION_CODE < 0x20400 +/* +  Search PCI configuration space for the specified capability registers. +  Return the index, or 0 on failure. +  The 2.4 kernel now includes this function. +*/ +int pci_find_capability(struct pci_dev *pdev, int findtype) +{ +	u16 pci_status, cap_type; +	u8 pci_cap_idx; +	int cap_idx; + +	pci_read_config_word(pdev, PCI_STATUS, &pci_status); +	if ( ! (pci_status & PCI_STATUS_CAP_LIST)) +		return 0; +	pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pci_cap_idx); +	cap_idx = pci_cap_idx; +	for (cap_idx = pci_cap_idx; cap_idx; cap_idx = (cap_type >> 8) & 0xff) { +		pci_read_config_word(pdev, cap_idx, &cap_type); +		if ((cap_type & 0xff) == findtype) +			return cap_idx; +	} +	return 0; +} +#endif + +/* Change a device from D3 (sleep) to D0 (active). +   Return the old power state. +   This is more complicated than you might first expect since most cards +   forget all PCI config info during the transition! */ +int acpi_wake(struct pci_dev *pdev) +{ +	u32 base[5], romaddr; +	u16 pci_command, pwr_command; +	u8  pci_latency, pci_cacheline, irq; +	int i, pwr_cmd_idx = pci_find_capability(pdev, PCI_CAP_ID_PM); + +	if (pwr_cmd_idx == 0) +		return 0; +	pci_read_config_word(pdev, pwr_cmd_idx + 4, &pwr_command); +	if ((pwr_command & 3) == 0) +		return 0; +	pci_read_config_word(pdev, PCI_COMMAND, &pci_command); +	for (i = 0; i < 5; i++) +		pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0 + i*4, +								  &base[i]); +	pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &romaddr); +	pci_read_config_byte( pdev, PCI_LATENCY_TIMER, &pci_latency); +	pci_read_config_byte( pdev, PCI_CACHE_LINE_SIZE, &pci_cacheline); +	pci_read_config_byte( pdev, PCI_INTERRUPT_LINE, &irq); + +	pci_write_config_word(pdev, pwr_cmd_idx + 4, 0x0000); +	for (i = 0; i < 5; i++) +		if (base[i]) +			pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0 + i*4, +									   base[i]); +	pci_write_config_dword(pdev, PCI_ROM_ADDRESS, romaddr); +	pci_write_config_byte( pdev, PCI_INTERRUPT_LINE, irq); +	pci_write_config_byte( pdev, PCI_CACHE_LINE_SIZE, pci_cacheline); +	pci_write_config_byte( pdev, PCI_LATENCY_TIMER, pci_latency); +	pci_write_config_word( pdev, PCI_COMMAND, pci_command | 5); +	return pwr_command & 3; +} + +int acpi_set_pwr_state(struct pci_dev *pdev, enum acpi_pwr_state new_state) +{ +	u16 pwr_command; +	int pwr_cmd_idx = pci_find_capability(pdev, PCI_CAP_ID_PM); + +	if (pwr_cmd_idx == 0) +		return 0; +	pci_read_config_word(pdev, pwr_cmd_idx + 4, &pwr_command); +	if ((pwr_command & 3) == ACPI_D3  &&  new_state != ACPI_D3) +		acpi_wake(pdev);		/* The complicated sequence. */ +	pci_write_config_word(pdev, pwr_cmd_idx + 4, +							  (pwr_command & ~3) | new_state); +	return pwr_command & 3; +} + +#if defined(CONFIG_PM) +static int handle_pm_event(struct pm_dev *dev, int event, void *data) +{ +	static int down = 0; +	struct dev_info *devi; +	int pwr_cmd = -1; + +	if (msg_level > 1) +		printk(KERN_DEBUG "pci-scan: Handling power event %d for driver " +			   "list %s...\n", +			   event, drv_list->name); +	switch (event) { +	case PM_SUSPEND: +		if (down) { +			printk(KERN_DEBUG "pci-scan: Received extra suspend event\n"); +			break; +		} +		down = 1; +		for (devi = dev_list; devi; devi = devi->next) +			if (devi->drv_id->pwr_event) +				devi->drv_id->pwr_event(devi->dev, DRV_SUSPEND); +		break; +	case PM_RESUME: +		if (!down) { +			printk(KERN_DEBUG "pci-scan: Received bogus resume event\n"); +			break; +		} +		for (devi = dev_list; devi; devi = devi->next) { +			if (devi->drv_id->pwr_event) { +				if (msg_level > 3) +					printk(KERN_DEBUG "pci-scan: Calling resume for %s " +						   "device.\n", devi->drv_id->name); +				devi->drv_id->pwr_event(devi->dev, DRV_RESUME); +			} +		} +		down = 0; +		break; +	case PM_SET_WAKEUP: pwr_cmd = DRV_PWR_WakeOn; break; +	case PM_EJECT:		pwr_cmd = DRV_DETACH;	break; +	default: +		printk(KERN_DEBUG "pci-scan: Unknown power management event %d.\n", +			   event); +	} +	if (pwr_cmd >= 0) +		for (devi = dev_list; devi; devi = devi->next) +			if (devi->drv_id->pwr_event) +				devi->drv_id->pwr_event(devi->dev, pwr_cmd); + +	return 0; +} + +#elif defined(CONFIG_APM)  &&  LINUX_VERSION_CODE < 0x20400  +static int handle_apm_event(apm_event_t event) +{ +	static int down = 0; +	struct dev_info *devi; + +	if (msg_level > 1) +		printk(KERN_DEBUG "pci-scan: Handling APM event %d for driver " +			   "list %s...\n", +			   event, drv_list->name); +	return 0; +	switch (event) { +	case APM_SYS_SUSPEND: +	case APM_USER_SUSPEND: +		if (down) { +			printk(KERN_DEBUG "pci-scan: Received extra suspend event\n"); +			break; +		} +		down = 1; +		for (devi = dev_list; devi; devi = devi->next) +			if (devi->drv_id->pwr_event) +				devi->drv_id->pwr_event(devi->dev, DRV_SUSPEND); +		break; +	case APM_NORMAL_RESUME: +	case APM_CRITICAL_RESUME: +		if (!down) { +			printk(KERN_DEBUG "pci-scan: Received bogus resume event\n"); +			break; +		} +		for (devi = dev_list; devi; devi = devi->next) +			if (devi->drv_id->pwr_event) +				devi->drv_id->pwr_event(devi->dev, DRV_RESUME); +		down = 0; +		break; +	} +	return 0; +} +#endif /* CONFIG_APM */ + +#ifdef MODULE +int init_module(void) +{ +	if (msg_level)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s", version); + +#if defined(CONFIG_PM) +	pm_register(PM_PCI_DEV, 0, &handle_pm_event); +#elif defined(CONFIG_APM)  &&  LINUX_VERSION_CODE < 0x20400  +	apm_register_callback(&handle_apm_event); +#endif +	return 0; +} +void cleanup_module(void) +{ +#if defined(CONFIG_PM) +	pm_unregister_all(&handle_pm_event); +#elif defined(CONFIG_APM)  &&  LINUX_VERSION_CODE < 0x20400  +	apm_unregister_callback(&handle_apm_event); +#endif +	if (dev_list != NULL) +		printk(KERN_WARNING "pci-scan: Unfreed device references.\n"); +	return; +} +#endif + + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -D__KERNEL__ -DEXPORT_SYMTAB -Wall -Wstrict-prototypes -O6 -c pci-scan.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/pci-scan.h b/linux/src/drivers/net/pci-scan.h new file mode 100644 index 0000000..649b34b --- /dev/null +++ b/linux/src/drivers/net/pci-scan.h @@ -0,0 +1,90 @@ +#ifndef _PCI_SCAN_H +#define _PCI_SCAN_H +/* +  version 1.02 $Version:$ $Date: 2006/01/22 15:54:41 $ +   Copyright 1999-2001 Donald Becker / Scyld Computing Corporation +   This software is part of the Linux kernel.  It may be used and +   distributed according to the terms of the GNU Public License, +   incorporated herein by reference. +*/ + +/* +  These are the structures in the table that drives the PCI probe routines. +  Note the matching code uses a bitmask: more specific table entries should +  be placed before "catch-all" entries. + +  The table must be zero terminated. +*/ +enum pci_id_flags_bits { +	/* Set PCI command register bits before calling probe1(). */ +	PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, +	/* Read and map the single following PCI BAR. */ +	PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4, +	PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400, +	PCI_UNUSED_IRQ=0x800, +}; + +struct pci_id_info { +	const char *name; +	struct match_info { +		int	pci, pci_mask, subsystem, subsystem_mask; +		int revision, revision_mask; 				/* Only 8 bits. */ +	} id; +	enum pci_id_flags_bits pci_flags; +	int io_size;				/* Needed for I/O region check or ioremap(). */ +	int drv_flags;				/* Driver use, intended as capability flags. */ +}; + +enum drv_id_flags { +	PCI_HOTSWAP=1, /* Leave module loaded for Cardbus-like chips. */ +}; +enum drv_pwr_action { +	DRV_NOOP,			/* No action. */ +	DRV_ATTACH,			/* The driver may expect power ops. */ +	DRV_SUSPEND,		/* Machine suspending, next event RESUME or DETACH. */ +	DRV_RESUME,			/* Resume from previous SUSPEND  */ +	DRV_DETACH,			/* Card will-be/is gone. Valid from SUSPEND! */ +	DRV_PWR_WakeOn,		/* Put device in e.g. Wake-On-LAN mode. */ +	DRV_PWR_DOWN,		/* Go to lowest power mode. */ +	DRV_PWR_UP,			/* Go to normal power mode. */ +}; + +struct drv_id_info { +	const char *name;			/* Single-word driver name. */ +	int flags; +	int pci_class;				/* Typically PCI_CLASS_NETWORK_ETHERNET<<8. */ +	struct pci_id_info *pci_dev_tbl; +	void *(*probe1)(struct pci_dev *pdev, void *dev_ptr, +					long ioaddr, int irq, int table_idx, int fnd_cnt); +	/* Optional, called for suspend, resume and detach. */ +	int (*pwr_event)(void *dev, int event); +	/* Internal values. */ +	struct drv_id_info *next; +	void *cb_ops; +}; + +/*  PCI scan and activate. +	Scan PCI-like hardware, calling probe1(..,dev,..) on devices that match. +	Returns -ENODEV, a negative number, if no cards are found. */ + +extern int pci_drv_register(struct drv_id_info *drv_id, void *initial_device); +extern void pci_drv_unregister(struct drv_id_info *drv_id); + + +/*  ACPI routines. +	Wake (change to ACPI D0 state) or set the ACPI power level of a sleeping +	ACPI device.  Returns the old power state.  */ + +int acpi_wake(struct pci_dev *pdev); +enum  acpi_pwr_state {ACPI_D0, ACPI_D1, ACPI_D2, ACPI_D3}; +int acpi_set_pwr_state(struct pci_dev *pdev, enum acpi_pwr_state state); + + +/* + * Local variables: + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ +#endif diff --git a/linux/src/drivers/net/pcnet32.c b/linux/src/drivers/net/pcnet32.c new file mode 100644 index 0000000..da0e870 --- /dev/null +++ b/linux/src/drivers/net/pcnet32.c @@ -0,0 +1,970 @@ +/* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */ +/* + *  Copyright 1996,97 Thomas Bogendoerfer, 1993-1995,1998 Donald Becker + * 	Copyright 1993 United States Government as represented by the + * 	Director, National Security Agency. + * + * 	Derived from the lance driver written 1993-1995 by Donald Becker. + * + * 	This software may be used and distributed according to the terms + * 	of the GNU Public License, incorporated herein by reference. + * + * 	This driver is for AMD PCnet-PCI based ethercards + */ + +static const char *version = "pcnet32.c:v0.99B 4/4/98 DJBecker/TSBogend.\n"; + +/* A few user-configurable values. */ + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. + * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). + */ +#define PCNET_LOG_TX_BUFFERS 4 +#define PCNET_LOG_RX_BUFFERS 4 + +#ifdef MODULE +#ifdef MODVERSIONS +#include <linux/modversions.h> +#endif +#include <linux/module.h> +#include <linux/version.h> +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +/* Driver verbosity level.  0 = no messages, 7 = wordy death. +   Modify here, or when loading as a module. */ +static int pcnet32_debug = 1; + +/* + * 				Theory of Operation + * + * This driver uses the same software structure as the normal lance + * driver. So look for a verbose description in lance.c. The differences + * to the normal lance driver is the use of the 32bit mode of PCnet32 + * and PCnetPCI chips. Because these chips are 32bit chips, there is no + * 16MB limitation and we don't need bounce buffers. + */ + +/* + * History: + * v0.01:  Initial version + *         only tested on Alpha Noname Board + * v0.02:  changed IRQ handling for new interrupt scheme (dev_id) + *         tested on a ASUS SP3G + * v0.10:  fixed an odd problem with the 79C794 in a Compaq Deskpro XL + *         looks like the 974 doesn't like stopping and restarting in a + *         short period of time; now we do a reinit of the lance; the + *         bug was triggered by doing ifconfig eth0 <ip> broadcast <addr> + *         and hangs the machine (thanks to Klaus Liedl for debugging) + * v0.12:  by suggestion from Donald Becker: Renamed driver to pcnet32, + *         made it standalone (no need for lance.c) + * v0.13:  added additional PCI detecting for special PCI devices (Compaq) + * v0.14:  stripped down additional PCI probe (thanks to David C Niemi + *         and sveneric@xs4all.nl for testing this on their Compaq boxes) + * v0.15:  added 79C965 (VLB) probe + *         added interrupt sharing for PCI chips + * v0.16:  fixed set_multicast_list on Alpha machines + * v0.17:  removed hack from dev.c; now pcnet32 uses ethif_probe in Space.c + * v0.19:  changed setting of autoselect bit + * v0.20:  removed additional Compaq PCI probe; there is now a working one + *	   in arch/i386/bios32.c + * v0.21:  added endian conversion for ppc, from work by cort@cs.nmt.edu + * v0.22:  added printing of status to ring dump + * v0.23:  changed enet_statistics to net_devive_stats + * v0.99: Changes for 2.0.34 final release. -djb + */ + + +#ifndef __powerpc__ +#define le16_to_cpu(val)  (val) +#define le32_to_cpu(val)  (val) +#endif +#if (LINUX_VERSION_CODE < 0x20123) +//#define test_and_set_bit(val, addr) set_bit(val, addr) +#endif + +#define TX_RING_SIZE			(1 << (PCNET_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS		((PCNET_LOG_TX_BUFFERS) << 12) + +#define RX_RING_SIZE			(1 << (PCNET_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS		((PCNET_LOG_RX_BUFFERS) << 4) + +#define PKT_BUF_SZ		1544 + +/* Offsets from base I/O address. */ +enum pcnet_offsets { PCNET32_DATA=0x10, PCNET32_ADDR=0x12, PCNET32_RESET=0x14, +					 PCNET32_BUS_IF=0x16,}; +#define PCNET32_TOTAL_SIZE 0x20 + +/* The PCNET32 Rx and Tx ring descriptors. */ +struct pcnet32_rx_head { +	u32 base; +	s16 buf_length; +	s16 status; +	u32 msg_length; +	u32 reserved; +}; + +struct pcnet32_tx_head { +	u32 base; +	s16 length; +	s16 status; +	u32 misc; +	u32 reserved; +}; + +/* The PCNET32 32-Bit initialization block, described in databook. */ +struct pcnet32_init_block { +	u16 mode; +	u16 tlen_rlen; +	u8  phys_addr[6]; +	u16 reserved; +	u32 filter[2]; +	/* Receive and transmit ring base, along with extra bits. */ +	u32 rx_ring; +	u32 tx_ring; +}; + +struct pcnet32_private { +	/* The Tx and Rx ring entries must be aligned on 16-byte boundaries +	   in 32bit mode. */ +	struct pcnet32_rx_head   rx_ring[RX_RING_SIZE]; +	struct pcnet32_tx_head   tx_ring[TX_RING_SIZE]; +	struct pcnet32_init_block	init_block; +	const char *name; +	struct device *next_module; +	/* The saved address of a sent-in-place packet/buffer, for skfree(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	unsigned long rx_buffs;		/* Address of Rx and Tx buffers. */ +	int cur_rx, cur_tx;			/* The next free ring entry */ +	int dirty_rx, dirty_tx;		/* The ring entries to be free()ed. */ +	struct enet_statistics stats; +	char tx_full; +	unsigned long lock; +}; + +static struct pcnet_chip_type { +	int id_number; +	const char *name; +	int flags; +} chip_table[] = { +	{0x2420, "PCnet/PCI 79C970", 0}, +	{0x2430, "PCnet32", 0}, +	{0x2621, "PCnet/PCI II 79C970A", 0}, +	{0x2623, "PCnet/FAST 79C971", 0}, +	{0x2624, "PCnet/FAST+ 79C972", 0}, +	{0x0, 	 "PCnet32 (unknown)", 0}, +}; + +/* Index of functions. */ +int  pcnet32_probe(struct device *dev); +static int  pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line); +static int  pcnet32_open(struct device *dev); +static void pcnet32_init_ring(struct device *dev); +static int  pcnet32_start_xmit(struct sk_buff *skb, struct device *dev); +static int  pcnet32_rx(struct device *dev); +static void pcnet32_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int  pcnet32_close(struct device *dev); +static struct enet_statistics *pcnet32_get_stats(struct device *dev); +static void pcnet32_set_multicast_list(struct device *dev); + + +/* A list of all installed PCnet32 devices, for removing the driver module. */ +static struct device *root_pcnet32_dev = NULL; + +int pcnet32_probe (struct device *dev) +{ +	static int pci_index = 0;	/* Static, for multiple probe calls. */ +	int cards_found = 0; + +	if ( ! pcibios_present()) +		return ENODEV; + +	for (;pci_index < 0xff; pci_index++) { +		u8 irq_line; +		u16 pci_command, new_command; +		unsigned char pci_bus, pci_device_fn; +		u32 pci_ioaddr; + +		if (pcibios_find_device (PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, +								 pci_index, &pci_bus, &pci_device_fn) +			!= PCIBIOS_SUCCESSFUL) +			break; + +		pcibios_read_config_byte(pci_bus, pci_device_fn, +								 PCI_INTERRUPT_LINE, &irq_line); +		pcibios_read_config_dword(pci_bus, pci_device_fn, +								  PCI_BASE_ADDRESS_0, &pci_ioaddr); +		/* Remove I/O space marker in bit 0. */ +		pci_ioaddr &= ~3; + +		/* Avoid already found cards from previous pcnet32_probe() calls */ +		if (check_region(pci_ioaddr, PCNET32_TOTAL_SIZE)) +			continue; + +		/* Activate the card: fix for brain-damaged Win98 BIOSes. */ +		pcibios_read_config_word(pci_bus, pci_device_fn, +								 PCI_COMMAND, &pci_command); +		new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; +		if (pci_command != new_command) { +			printk(KERN_INFO "  The PCI BIOS has not enabled the AMD Ethernet" +				   " device at %2x-%2x." +				   "  Updating PCI command %4.4x->%4.4x.\n", +				   pci_bus, pci_device_fn, pci_command, new_command); +			pcibios_write_config_word(pci_bus, pci_device_fn, +									  PCI_COMMAND, new_command); +		} + +		if (pcnet32_probe1(dev, pci_ioaddr, irq_line) != 0) { +			/* Should never happen. */ +			printk(KERN_ERR "pcnet32.c: Probe of PCI card at %#x failed.\n", +				   pci_ioaddr); +		} else +			dev = 0; +		cards_found++; +	} + +	return cards_found ? 0 : -ENODEV; +} + + +/* pcnet32_probe1 */ +static int pcnet32_probe1(struct device *dev, unsigned int ioaddr, unsigned char irq_line) +{ +	struct pcnet32_private *lp; +	int i; +	const char *chipname; + +	/* check if there is really a pcnet chip on that ioaddr */ +	if ((inb(ioaddr + 14) != 0x57) || (inb(ioaddr + 15) != 0x57)) +		return ENODEV; + +	inw(ioaddr+PCNET32_RESET); /* Reset the PCNET32 */ + +	outw(0x0000, ioaddr+PCNET32_ADDR); /* Switch to window 0 */ +	if (inw(ioaddr+PCNET32_DATA) != 0x0004) +		return ENODEV; + +	/* Get the version of the chip. */ +	outw(88, ioaddr+PCNET32_ADDR); +	if (inw(ioaddr+PCNET32_ADDR) != 88) { +		/* should never happen */ +		return ENODEV; +	} else {			/* Good, it's a newer chip. */ +		int chip_version = inw(ioaddr+PCNET32_DATA); +		outw(89, ioaddr+PCNET32_ADDR); +		chip_version |= inw(ioaddr+PCNET32_DATA) << 16; +		if (pcnet32_debug > 2) +			printk("  PCnet chip version is %#x.\n", chip_version); +		if ((chip_version & 0xfff) != 0x003) +			return ENODEV; +		chip_version = (chip_version >> 12) & 0xffff; +		for (i = 0; chip_table[i].id_number; i++) +			if (chip_table[i].id_number == chip_version) +				break; +		chipname = chip_table[i].name; +	} + +	dev = init_etherdev(dev, 0); + +	printk("%s: %s at %#3x,", dev->name, chipname, ioaddr); + +	/* There is a 16 byte station address PROM at the base address. +	   The first six bytes are the station address. */ +	for (i = 0; i < 6; i++) +		printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i)); +		 +	printk("\n"); + +	dev->base_addr = ioaddr; +	request_region(ioaddr, PCNET32_TOTAL_SIZE, dev->name); + +	/* Data structures used by the PCnet32 are 16byte aligned and DMAble. */ +    lp = (struct pcnet32_private *) +		(((unsigned long)kmalloc(sizeof(*lp)+15, GFP_DMA | GFP_KERNEL)+15) & ~15); + +    memset(lp, 0, sizeof(*lp)); +	dev->priv = lp; + +	lp->next_module = root_pcnet32_dev; +	root_pcnet32_dev = dev; + +    lp->name = chipname; +    lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL); + +    lp->init_block.mode = le16_to_cpu(0x0003);	/* Disable Rx and Tx. */ +    lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); +    for (i = 0; i < 6; i++) +      lp->init_block.phys_addr[i] = dev->dev_addr[i]; +    lp->init_block.filter[0] = 0x00000000; +    lp->init_block.filter[1] = 0x00000000; +    lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring)); +    lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring)); + +    /* switch pcnet32 to 32bit mode */ +    outw(0x0014, ioaddr+PCNET32_ADDR); +    outw(0x0002, ioaddr+PCNET32_BUS_IF); + +    outw(0x0001, ioaddr+PCNET32_ADDR); +    inw(ioaddr+PCNET32_ADDR); +    outw(virt_to_bus(&lp->init_block) & 0xffff, ioaddr+PCNET32_DATA); +    outw(0x0002, ioaddr+PCNET32_ADDR); +    inw(ioaddr+PCNET32_ADDR); +    outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA); +    outw(0x0000, ioaddr+PCNET32_ADDR); +    inw(ioaddr+PCNET32_ADDR); + +    dev->irq = irq_line; + +    if (pcnet32_debug > 0) +      printk("%s", version); + +    /* The PCNET32-specific entries in the device structure. */ +    dev->open = &pcnet32_open; +    dev->hard_start_xmit = &pcnet32_start_xmit; +    dev->stop = &pcnet32_close; +    dev->get_stats = &pcnet32_get_stats; +    dev->set_multicast_list = &pcnet32_set_multicast_list; + +    /* Fill in the generic fields of the device structure. */ +    ether_setup(dev); +    return 0; +} + + +static int +pcnet32_open(struct device *dev) +{ +	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; +	unsigned int ioaddr = dev->base_addr; +	int i; + +	if (dev->irq == 0 || +		request_irq(dev->irq, &pcnet32_interrupt, SA_SHIRQ, +					dev->name, (void *)dev)) { +		return -EAGAIN; +	} +	MOD_INC_USE_COUNT; + +	/* Reset the PCNET32 */ +	inw(ioaddr+PCNET32_RESET); + +	/* switch pcnet32 to 32bit mode */ +	outw(0x0014, ioaddr+PCNET32_ADDR); +	outw(0x0002, ioaddr+PCNET32_BUS_IF); + +	/* Turn on auto-select of media (AUI, BNC). */ +	outw(0x0002, ioaddr+PCNET32_ADDR); +	/* only touch autoselect bit */ +	outw(inw(ioaddr+PCNET32_BUS_IF) | 0x0002, ioaddr+PCNET32_BUS_IF); + +	if (pcnet32_debug > 1) +		printk("%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n", +			   dev->name, dev->irq, +		           (u32) virt_to_bus(lp->tx_ring), +		           (u32) virt_to_bus(lp->rx_ring), +			   (u32) virt_to_bus(&lp->init_block)); + +	/* check for ATLAS T1/E1 LAW card */ +	if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75) { +		/* select GPSI mode */ +		lp->init_block.mode = 0x0100; +		outw(0x0002, ioaddr+PCNET32_ADDR); +		outw(inw(ioaddr+PCNET32_BUS_IF) & ~2, ioaddr+PCNET32_BUS_IF); +		/* switch full duplex on */ +		outw(0x0009, ioaddr+PCNET32_ADDR); +		outw(inw(ioaddr+PCNET32_BUS_IF) | 1, ioaddr+PCNET32_BUS_IF); +	} else +		lp->init_block.mode = 0x0000; +	lp->init_block.filter[0] = 0x00000000; +	lp->init_block.filter[1] = 0x00000000; +	pcnet32_init_ring(dev); + +	/* Re-initialize the PCNET32, and start it when done. */ +	outw(0x0001, ioaddr+PCNET32_ADDR); +	outw(virt_to_bus(&lp->init_block) &0xffff, ioaddr+PCNET32_DATA); +	outw(0x0002, ioaddr+PCNET32_ADDR); +	outw(virt_to_bus(&lp->init_block) >> 16, ioaddr+PCNET32_DATA); + +	outw(0x0004, ioaddr+PCNET32_ADDR); +	outw(0x0915, ioaddr+PCNET32_DATA); + +	outw(0x0000, ioaddr+PCNET32_ADDR); +	outw(0x0001, ioaddr+PCNET32_DATA); + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; +	i = 0; +	while (i++ < 100) +		if (inw(ioaddr+PCNET32_DATA) & 0x0100) +			break; +	/* +	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton +	 * reports that doing so triggers a bug in the '974. +	 */ + 	outw(0x0042, ioaddr+PCNET32_DATA); + +	if (pcnet32_debug > 2) +		printk("%s: PCNET32 open after %d ticks, init block %#x csr0 %4.4x.\n", +			   dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+PCNET32_DATA)); + +	return 0;					/* Always succeed */ +} + +/* + * The LANCE has been halted for one reason or another (busmaster memory + * arbitration error, Tx FIFO underflow, driver stopped it to reconfigure, + * etc.).  Modern LANCE variants always reload their ring-buffer + * configuration when restarted, so we must reinitialize our ring + * context before restarting.  As part of this reinitialization, + * find all packets still on the Tx ring and pretend that they had been + * sent (in effect, drop the packets on the floor) - the higher-level + * protocols will time out and retransmit.  It'd be better to shuffle + * these skbs to a temp list and then actually re-Tx them after + * restarting the chip, but I'm too lazy to do so right now.  dplatt@3do.com + */ + +static void +pcnet32_purge_tx_ring(struct device *dev) +{ +	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; +	int i; + +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (lp->tx_skbuff[i]) { +			dev_kfree_skb(lp->tx_skbuff[i], FREE_WRITE); +			lp->tx_skbuff[i] = NULL; +		} +	} +} + + +/* Initialize the PCNET32 Rx and Tx rings. */ +static void +pcnet32_init_ring(struct device *dev) +{ +	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; +	int i; + +	lp->lock = 0, lp->tx_full = 0; +	lp->cur_rx = lp->cur_tx = 0; +	lp->dirty_rx = lp->dirty_tx = 0; + +	for (i = 0; i < RX_RING_SIZE; i++) { +		lp->rx_ring[i].base = (u32)le32_to_cpu(virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ)); +		lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); +	        lp->rx_ring[i].status = le16_to_cpu(0x8000); +	} +	/* The Tx buffer address is filled in as needed, but we do need to clear +	   the upper ownership bit. */ +	for (i = 0; i < TX_RING_SIZE; i++) { +	        lp->tx_ring[i].base = 0; +	        lp->tx_ring[i].status = 0; +	} + +	lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS; +	for (i = 0; i < 6; i++) +		lp->init_block.phys_addr[i] = dev->dev_addr[i]; +	lp->init_block.rx_ring = (u32)le32_to_cpu(virt_to_bus(lp->rx_ring)); +	lp->init_block.tx_ring = (u32)le32_to_cpu(virt_to_bus(lp->tx_ring)); +} + +static void +pcnet32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit) +{ +        int i; +	unsigned int ioaddr = dev->base_addr; + +	pcnet32_purge_tx_ring(dev); +	pcnet32_init_ring(dev); + +	outw(0x0000, ioaddr + PCNET32_ADDR); +        /* ReInit Ring */ +        outw(0x0001, ioaddr + PCNET32_DATA); +	i = 0; +	while (i++ < 100) +		if (inw(ioaddr+PCNET32_DATA) & 0x0100) +			break; + +	outw(csr0_bits, ioaddr + PCNET32_DATA); +} + +static int +pcnet32_start_xmit(struct sk_buff *skb, struct device *dev) +{ +	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; +	unsigned int ioaddr = dev->base_addr; +	int entry; +	unsigned long flags; + +	/* Transmitter timeout, serious problems. */ +	if (dev->tbusy) { +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < 20) +			return 1; +		outw(0, ioaddr+PCNET32_ADDR); +		printk("%s: transmit timed out, status %4.4x, resetting.\n", +			   dev->name, inw(ioaddr+PCNET32_DATA)); +		outw(0x0004, ioaddr+PCNET32_DATA); +		lp->stats.tx_errors++; +#ifndef final_version +		{ +			int i; +			printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", +				   lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "", +				   lp->cur_rx); +			for (i = 0 ; i < RX_RING_SIZE; i++) +				printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", +					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, +					   lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status); +			for (i = 0 ; i < TX_RING_SIZE; i++) +				printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", +					   lp->tx_ring[i].base, -lp->tx_ring[i].length, +					   lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status); +			printk("\n"); +		} +#endif +		pcnet32_restart(dev, 0x0042, 1); + +		dev->tbusy = 0; +		dev->trans_start = jiffies; + +		return 0; +	} + +	if (pcnet32_debug > 3) { +		outw(0x0000, ioaddr+PCNET32_ADDR); +		printk("%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name, +			   inw(ioaddr+PCNET32_DATA)); +		outw(0x0000, ioaddr+PCNET32_DATA); +	} + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { +		printk("%s: Transmitter access conflict.\n", dev->name); +		return 1; +	} + +	if (test_and_set_bit(0, (void*)&lp->lock) != 0) { +		if (pcnet32_debug > 0) +			printk("%s: tx queue lock!.\n", dev->name); +		/* don't clear dev->tbusy flag. */ +		return 1; +	} + +	/* Fill in a Tx ring entry */ + +	/* Mask to ring buffer boundary. */ +	entry = lp->cur_tx & TX_RING_MOD_MASK; + +	/* Caution: the write order is important here, set the base address +	   with the "ownership" bits last. */ + +	lp->tx_ring[entry].length = le16_to_cpu(-skb->len); + +	lp->tx_ring[entry].misc = 0x00000000; + +	lp->tx_skbuff[entry] = skb; +	lp->tx_ring[entry].base = (u32)le32_to_cpu(virt_to_bus(skb->data)); +	lp->tx_ring[entry].status = le16_to_cpu(0x8300); + +	lp->cur_tx++; + +	/* Trigger an immediate send poll. */ +	outw(0x0000, ioaddr+PCNET32_ADDR); +	outw(0x0048, ioaddr+PCNET32_DATA); + +	dev->trans_start = jiffies; + +	save_flags(flags); +	cli(); +	lp->lock = 0; +	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0) +		clear_bit(0, (void*)&dev->tbusy); +	else +		lp->tx_full = 1; +	restore_flags(flags); + +	return 0; +} + +/* The PCNET32 interrupt handler. */ +static void +pcnet32_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +	struct device *dev = (struct device *)dev_id; +	struct pcnet32_private *lp; +	unsigned int csr0, ioaddr; +	int boguscnt = max_interrupt_work; +	int must_restart; + +	if (dev == NULL) { +		printk ("pcnet32_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} + +	ioaddr = dev->base_addr; +	lp = (struct pcnet32_private *)dev->priv; +	if (dev->interrupt) +		printk("%s: Re-entering the interrupt handler.\n", dev->name); + +	dev->interrupt = 1; + +	outw(0x00, dev->base_addr + PCNET32_ADDR); +	while ((csr0 = inw(dev->base_addr + PCNET32_DATA)) & 0x8600 +		   && --boguscnt >= 0) { +		/* Acknowledge all of the current interrupt sources ASAP. */ +		outw(csr0 & ~0x004f, dev->base_addr + PCNET32_DATA); + +		must_restart = 0; + +		if (pcnet32_debug > 5) +			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n", +				   dev->name, csr0, inw(dev->base_addr + PCNET32_DATA)); + +		if (csr0 & 0x0400)			/* Rx interrupt */ +			pcnet32_rx(dev); + +		if (csr0 & 0x0200) {		/* Tx-done interrupt */ +			int dirty_tx = lp->dirty_tx; + +			while (dirty_tx < lp->cur_tx) { +				int entry = dirty_tx & TX_RING_MOD_MASK; +				int status = (short)le16_to_cpu(lp->tx_ring[entry].status); + +				if (status < 0) +					break;			/* It still hasn't been Txed */ + +				lp->tx_ring[entry].base = 0; + +				if (status & 0x4000) { +					/* There was an major error, log it. */ +					int err_status = le16_to_cpu(lp->tx_ring[entry].misc); +					lp->stats.tx_errors++; +					if (err_status & 0x04000000) lp->stats.tx_aborted_errors++; +					if (err_status & 0x08000000) lp->stats.tx_carrier_errors++; +					if (err_status & 0x10000000) lp->stats.tx_window_errors++; +					if (err_status & 0x40000000) { +						/* Ackk!  On FIFO errors the Tx unit is turned off! */ +						lp->stats.tx_fifo_errors++; +						/* Remove this verbosity later! */ +						printk("%s: Tx FIFO error! Status %4.4x.\n", +							   dev->name, csr0); +						/* Restart the chip. */ +						must_restart = 1; +					} +				} else { +					if (status & 0x1800) +						lp->stats.collisions++; +					lp->stats.tx_packets++; +				} + +				/* We must free the original skb */ +				if (lp->tx_skbuff[entry]) { +					dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); +					lp->tx_skbuff[entry] = 0; +				} +				dirty_tx++; +			} + +#ifndef final_version +			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { +				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n", +					   dirty_tx, lp->cur_tx, lp->tx_full); +				dirty_tx += TX_RING_SIZE; +			} +#endif + +			if (lp->tx_full && dev->tbusy +				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { +				/* The ring is no longer full, clear tbusy. */ +				lp->tx_full = 0; +				clear_bit(0, (void*)&dev->tbusy); +				mark_bh(NET_BH); +			} + +			lp->dirty_tx = dirty_tx; +		} + +		/* Log misc errors. */ +		if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */ +		if (csr0 & 0x1000) { +			/* +			 * this happens when our receive ring is full. This  +			 * shouldn't be a problem as we will see normal rx  +			 * interrupts for the frames in the receive ring. But  +			 * there are some PCI chipsets (I can reproduce this  +			 * on SP3G with Intel saturn chipset) which have some- +			 * times problems and will fill up the receive ring  +			 * with error descriptors. In this situation we don't  +			 * get a rx interrupt, but a missed frame interrupt  +			 * sooner or later. So we try to clean up our receive  +			 * ring here. +			 */ +			pcnet32_rx(dev); +			lp->stats.rx_errors++; /* Missed a Rx frame. */ +		} +		if (csr0 & 0x0800) { +			printk("%s: Bus master arbitration failure, status %4.4x.\n", +				   dev->name, csr0); +			/* Restart the chip. */ +			must_restart = 1; +		} + +		if (must_restart) { +			/* stop the chip to clear the error condition, then restart */ +			outw(0x0000, dev->base_addr + PCNET32_ADDR); +			outw(0x0004, dev->base_addr + PCNET32_DATA); +			pcnet32_restart(dev, 0x0002, 0); +		} +	} + +	/* Clear any other interrupt, and set interrupt enable. */ +	outw(0x0000, dev->base_addr + PCNET32_ADDR); +	outw(0x7940, dev->base_addr + PCNET32_DATA); + +	if (pcnet32_debug > 4) +		printk("%s: exiting interrupt, csr%d=%#4.4x.\n", +			   dev->name, inw(ioaddr + PCNET32_ADDR), +			   inw(dev->base_addr + PCNET32_DATA)); + +	dev->interrupt = 0; +	return; +} + +static int +pcnet32_rx(struct device *dev) +{ +	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; +	int entry = lp->cur_rx & RX_RING_MOD_MASK; +	int i; + +	/* If we own the next entry, it's a new packet. Send it up. */ +	while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) { +		int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8; + +		if (status != 0x03) {			/* There was an error. */ +			/* There is a tricky error noted by John Murphy, +			   <murf@perftech.com> to Russ Nelson: Even with full-sized +			   buffers it's possible for a jabber packet to use two +			   buffers, with only the last correctly noting the error. */ +			if (status & 0x01)	/* Only count a general error at the */ +				lp->stats.rx_errors++; /* end of a packet.*/ +			if (status & 0x20) lp->stats.rx_frame_errors++; +			if (status & 0x10) lp->stats.rx_over_errors++; +			if (status & 0x08) lp->stats.rx_crc_errors++; +			if (status & 0x04) lp->stats.rx_fifo_errors++; +			lp->rx_ring[entry].status &= le16_to_cpu(0x03ff); +		} +		else +		{ +			/* Malloc up new buffer, compatible with net-2e. */ +			short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4; +			struct sk_buff *skb; + +			if(pkt_len < 60) { +				printk("%s: Runt packet!\n",dev->name); +				lp->stats.rx_errors++; +			} else { +				skb = dev_alloc_skb(pkt_len+2); +				if (skb == NULL) { +					printk("%s: Memory squeeze, deferring packet.\n", +						   dev->name); +					for (i=0; i < RX_RING_SIZE; i++) +						if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0) +							break; + +					if (i > RX_RING_SIZE -2) +					{ +						lp->stats.rx_dropped++; +						lp->rx_ring[entry].status |= le16_to_cpu(0x8000); +						lp->cur_rx++; +					} +					break; +				} +				skb->dev = dev; +				skb_reserve(skb,2);	/* 16 byte align */ +				skb_put(skb,pkt_len);	/* Make room */ +				eth_copy_and_sum(skb, +					(unsigned char *)bus_to_virt(le32_to_cpu(lp->rx_ring[entry].base)), +					pkt_len,0); +				skb->protocol=eth_type_trans(skb,dev); +				netif_rx(skb); +				lp->stats.rx_packets++; +			} +		} +		/* The docs say that the buffer length isn't touched, but Andrew Boyd +		   of QNX reports that some revs of the 79C965 clear it. */ +		lp->rx_ring[entry].buf_length = le16_to_cpu(-PKT_BUF_SZ); +		lp->rx_ring[entry].status |= le16_to_cpu(0x8000); +		entry = (++lp->cur_rx) & RX_RING_MOD_MASK; +	} + +	/* We should check that at least two ring entries are free.	 If not, +	   we should free one and mark stats->rx_dropped++. */ + +	return 0; +} + +static int +pcnet32_close(struct device *dev) +{ +	unsigned int ioaddr = dev->base_addr; +	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + +	dev->start = 0; +	set_bit(0, (void*)&dev->tbusy); + +	outw(112, ioaddr+PCNET32_ADDR); +	lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA); + +	outw(0, ioaddr+PCNET32_ADDR); + +	if (pcnet32_debug > 1) +		printk("%s: Shutting down ethercard, status was %2.2x.\n", +			   dev->name, inw(ioaddr+PCNET32_DATA)); + +	/* We stop the PCNET32 here -- it occasionally polls +	   memory if we don't. */ +	outw(0x0004, ioaddr+PCNET32_DATA); + +	free_irq(dev->irq, dev); +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static struct enet_statistics *pcnet32_get_stats(struct device *dev) +{ +	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; +	unsigned int ioaddr = dev->base_addr; +	unsigned short saved_addr; +	unsigned long flags; + +	save_flags(flags); +	cli(); +	saved_addr = inw(ioaddr+PCNET32_ADDR); +	outw(112, ioaddr+PCNET32_ADDR); +	lp->stats.rx_missed_errors = inw(ioaddr+PCNET32_DATA); +	outw(saved_addr, ioaddr+PCNET32_ADDR); +	restore_flags(flags); + +	return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. + */ + +static void pcnet32_set_multicast_list(struct device *dev) +{ +	unsigned int ioaddr = dev->base_addr; +	struct pcnet32_private *lp = (struct pcnet32_private *)dev->priv; + +	if (dev->flags&IFF_PROMISC) { +		/* Log any net taps. */ +		printk("%s: Promiscuous mode enabled.\n", dev->name); +		lp->init_block.mode |= 0x8000; +	} else { +		int num_addrs=dev->mc_count; +		if(dev->flags&IFF_ALLMULTI) +			num_addrs=1; +		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */ +		memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter)); +		lp->init_block.mode &= ~0x8000; +	} + +	outw(0, ioaddr+PCNET32_ADDR); +	outw(0x0004, ioaddr+PCNET32_DATA); /* Temporarily stop the lance. */ + +	pcnet32_restart(dev, 0x0042, 0); /*  Resume normal operation */ + +} + + +#ifdef MODULE +#if LINUX_VERSION_CODE > 0x20118 +MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>"); +MODULE_DESCRIPTION("AMD PCnet/PCI ethernet driver"); +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif + +/* An additional parameter that may be passed in... */ +static int debug = -1; + +int +init_module(void) +{ +	if (debug >= 0) +		pcnet32_debug = debug; + +#ifdef CARDBUS +	register_driver(&pcnet32_ops); +	return 0; +#else +	return pcnet32_probe(NULL); +#endif +} + +void +cleanup_module(void) +{ +	struct device *next_dev; + +#ifdef CARDBUS +	unregister_driver(&pcnet32_ops); +#endif + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_pcnet32_dev) { +		next_dev = ((struct pcnet32_private *)root_pcnet32_dev->priv)->next_module; +		unregister_netdev(root_pcnet32_dev); +		release_region(root_pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE); +		kfree(root_pcnet32_dev); +		root_pcnet32_dev = next_dev; +	} +} + +#endif  /* MODULE */ + + +/* + * Local variables: + *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c pcnet32.c  `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/rtl8139.c b/linux/src/drivers/net/rtl8139.c new file mode 100644 index 0000000..e97c905 --- /dev/null +++ b/linux/src/drivers/net/rtl8139.c @@ -0,0 +1,1737 @@ +/* rtl8139.c: A RealTek RTL8129/8139 Fast Ethernet driver for Linux. */ +/* +	Written and Copyright 1997-2003 by Donald Becker. +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	This driver is for boards based on the RTL8129 and RTL8139 PCI ethernet +	chips. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	410 Severn Ave., Suite 210 +	Annapolis MD 21403 + +	Support and updates available at +	http://www.scyld.com/network/rtl8139.html + +	Twister-tuning table provided by Kinston <shangh@realtek.com.tw>. +*/ + +/* These identify the driver base version and may not be removed. */ +static const char versionA[] = +"rtl8139.c:v1.23a 8/24/2003 Donald Becker, becker@scyld.com.\n"; +static const char versionB[] = +" http://www.scyld.com/network/rtl8139.html\n"; + +#ifndef USE_MEM_OPS +/* Note: Register access width and timing restrictions apply in MMIO mode. +   This updated driver should nominally work, but I/O mode is better tested. */ +#define USE_IO_OPS +#endif + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). +   The RTL chips use a 64 element hash table based on the Ethernet CRC.  It +   is efficient to update the hardware filter, but recalculating the table +   for a long filter list is painful.  */ +static int multicast_filter_limit = 32; + +/* Used to pass the full-duplex flag, etc. */ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Maximum size of the in-memory receive ring (smaller if no memory). */ +#define RX_BUF_LEN_IDX	2			/* 0==8K, 1==16K, 2==32K, 3==64K */ +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+14+4). */ +#define TX_BUF_SIZE	1536 + +/* PCI Tuning Parameters +   Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256	/* In bytes, rounded down to 32 byte units. */ + +/* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024. */ +#define RX_FIFO_THRESH	4		/* Rx buffer level before first PCI xfer.  */ +#define RX_DMA_BURST	4		/* Maximum PCI burst, '4' is 256 bytes */ +#define TX_DMA_BURST	4		/* Calculate as 16<<val. */ + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with full-sized Ethernet frames. +   This is a cross-driver value that is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> + +#if LINUX_VERSION_CODE >= 0x20300 +#include <linux/spinlock.h> +#elif LINUX_VERSION_CODE >= 0x20200 +#include <asm/spinlock.h> +#endif + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the RealTek RTL8129 series, the RealTek +Fast Ethernet controllers for PCI and CardBus.  This chip is used on many +low-end boards, sometimes with custom chip labels. + + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board.  The system BIOS will assign the +PCI INTA signal to a (preferably otherwise unused) system IRQ line. +Note: Kernel versions earlier than 1.3.73 do not support shared PCI +interrupt lines. + +III. Driver operation + +IIIa. Rx Ring buffers + +The receive unit uses a single linear ring buffer rather than the more +common (and more efficient) descriptor-based architecture.  Incoming frames +are sequentially stored into the Rx region, and the host copies them into +skbuffs. + +Comment: While it is theoretically possible to process many frames in place, +any delay in Rx processing would block the Rx ring and cause us to drop +frames.  It would be difficult to design a protocol stack where the data +buffer could be recalled by the device driver. + +IIIb. Tx operation + +The RTL8129 uses a fixed set of four Tx descriptors in register space.  Tx +frames must be 32 bit aligned.  Linux aligns the IP header on word +boundaries, and 14 byte ethernet header means that almost all frames will +need to be copied to an alignment buffer.  The driver statically allocates +alignment the four alignment buffers at open() time. + +IVb. References + +http://www.realtek.com.tw/cn/cn.html +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html + +IVc. Errata + +*/ + + +static void *rtl8139_probe1(struct pci_dev *pdev, void *init_dev, +							long ioaddr, int irq, int chip_idx, int find_cnt); +static int rtl_pwr_event(void *dev_instance, int event); + +enum chip_capability_flags {HAS_MII_XCVR=0x01, HAS_CHIP_XCVR=0x02, +							HAS_LNK_CHNG=0x04, HAS_DESC=0x08}; +#ifdef USE_IO_OPS +#define RTL8139_IOTYPE  PCI_USES_MASTER|PCI_USES_IO |PCI_ADDR0 +#else +#define RTL8139_IOTYPE  PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR1 +#endif +#define RTL8129_CAPS  HAS_MII_XCVR +#define RTL8139_CAPS  HAS_CHIP_XCVR|HAS_LNK_CHNG +#define RTL8139D_CAPS  HAS_CHIP_XCVR|HAS_LNK_CHNG|HAS_DESC + +/* Note: Update the marked constant in _attach() if the RTL8139B entry moves.*/ +static struct pci_id_info pci_tbl[] = { +	{"RealTek RTL8139C+, 64 bit high performance", +	 { 0x813910ec, 0xffffffff, 0,0, 0x20, 0xff}, +	 RTL8139_IOTYPE, 0x80, RTL8139D_CAPS, }, +	{"RealTek RTL8139C Fast Ethernet", +	 { 0x813910ec, 0xffffffff, 0,0, 0x10, 0xff}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"RealTek RTL8129 Fast Ethernet", { 0x812910ec, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8129_CAPS, }, +	{"RealTek RTL8139 Fast Ethernet", { 0x813910ec, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"RealTek RTL8139B PCI/CardBus",  { 0x813810ec, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"SMC1211TX EZCard 10/100 (RealTek RTL8139)", { 0x12111113, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"Accton MPX5030 (RealTek RTL8139)", { 0x12111113, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"D-Link DFE-530TX+ (RealTek RTL8139C)", +	 { 0x13001186, 0xffffffff, 0x13011186, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x100, RTL8139_CAPS, }, +	{"D-Link DFE-538TX (RealTek RTL8139)", { 0x13001186, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"LevelOne FPC-0106Tx (RealTek RTL8139)", { 0x0106018a, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"Compaq HNE-300 (RealTek RTL8139c)", { 0x8139021b, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"Edimax EP-4103DL CardBus (RealTek RTL8139c)", { 0xab0613d1, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{"Siemens 1012v2 CardBus (RealTek RTL8139c)", { 0x101202ac, 0xffffffff,}, +	 RTL8139_IOTYPE, 0x80, RTL8139_CAPS, }, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info rtl8139_drv_id = { +	"realtek", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_tbl, +	rtl8139_probe1, rtl_pwr_event }; + +#ifndef USE_IO_OPS +#undef inb +#undef inw +#undef inl +#undef outb +#undef outw +#undef outl +#define inb readb +#define inw readw +#define inl readl +#define outb writeb +#define outw writew +#define outl writel +#endif + +/* The rest of these values should never change. */ +#define NUM_TX_DESC	4			/* Number of Tx descriptor registers. */ + +/* Symbolic offsets to registers. */ +enum RTL8129_registers { +	MAC0=0,						/* Ethernet hardware address. */ +	MAR0=8,						/* Multicast filter. */ +	TxStatus0=0x10,				/* Transmit status (Four 32bit registers). */ +	TxAddr0=0x20,				/* Tx descriptors (also four 32bit). */ +	RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36, +	ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A, +	IntrMask=0x3C, IntrStatus=0x3E, +	TxConfig=0x40, RxConfig=0x44, +	Timer=0x48,					/* A general-purpose counter. */ +	RxMissed=0x4C,				/* 24 bits valid, write clears. */ +	Cfg9346=0x50, Config0=0x51, Config1=0x52, +	FlashReg=0x54, GPPinData=0x58, GPPinDir=0x59, MII_SMI=0x5A, HltClk=0x5B, +	MultiIntr=0x5C, TxSummary=0x60, +	MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68, +	NWayExpansion=0x6A, +	/* Undocumented registers, but required for proper operation. */ +	FIFOTMS=0x70,	/* FIFO Control and test. */ +	CSCR=0x74,	/* Chip Status and Configuration Register. */ +	PARA78=0x78, PARA7c=0x7c,	/* Magic transceiver parameter register. */ +}; + +enum ChipCmdBits { +	CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, }; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { +	PCIErr=0x8000, PCSTimeout=0x4000, +	RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10, +	TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01, +}; +enum TxStatusBits { +	TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000, +	TxOutOfWindow=0x20000000, TxAborted=0x40000000, TxCarrierLost=0x80000000, +}; +enum RxStatusBits { +	RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000, +	RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004, +	RxBadAlign=0x0002, RxStatusOK=0x0001, +}; + +/* Twister tuning parameters from RealTek. +   Completely undocumented, but required to tune bad links. */ +enum CSCRBits { +	CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800, +	CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0, +	CSCR_LinkDownCmd=0x0f3c0, +}; +#define PARA78_default	0x78fa8388 +#define PARA7c_default	0xcb38de43 			/* param[0][3] */ +#define PARA7c_xxx		0xcb38de43 +unsigned long param[4][4]={ +	{0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, +	{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, +	{0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, +	{0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} +}; + +#define PRIV_ALIGN	15 	/* Desired alignment mask */ +struct rtl8129_private { +	struct net_device *next_module; +	void *priv_addr;					/* Unaligned address for kfree */ + +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	struct net_device_stats stats; +	struct timer_list timer;	/* Media selection timer. */ +	int msg_level; +	int max_interrupt_work; + +	/* Receive state. */ +	unsigned char *rx_ring; +	unsigned int cur_rx;		/* Index into the Rx buffer of next Rx pkt. */ +	unsigned int rx_buf_len;	/* Size (8K 16K 32K or 64KB) of the Rx ring */ + +	/* Transmit state. */ +	unsigned int cur_tx, dirty_tx, tx_flag; +	unsigned long tx_full;				/* The Tx queue is full. */ +	/* The saved address of a sent-in-place packet/buffer, for skfree(). */ +	struct sk_buff* tx_skbuff[NUM_TX_DESC]; +	unsigned char *tx_buf[NUM_TX_DESC];	/* Tx bounce buffers */ +	unsigned char *tx_bufs;				/* Tx bounce buffer region. */ + +	/* Receive filter state. */ +	unsigned int rx_config; +	u32 mc_filter[2];		 /* Multicast hash filter */ +	int cur_rx_mode; +	int multicast_filter_limit; + +	/* Transceiver state. */ +	char phys[4];						/* MII device addresses. */ +	u16 advertising;					/* NWay media advertisement */ +	char twistie, twist_row, twist_col;	/* Twister tune state. */ +	u8	config1; +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int media2:4;				/* Secondary monitored media port. */ +	unsigned int medialock:1;			/* Don't sense media type. */ +	unsigned int mediasense:1;			/* Media sensing in progress. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +}; + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("RealTek RTL8129/8139 Fast Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(full_duplex, "Non-zero to set forced full duplex."); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); + +static int rtl8129_open(struct net_device *dev); +static void rtl_hw_start(struct net_device *dev); +static int read_eeprom(long ioaddr, int location, int addr_len); +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int val); +static void rtl8129_timer(unsigned long data); +static void rtl8129_tx_timeout(struct net_device *dev); +static void rtl8129_init_ring(struct net_device *dev); +static int rtl8129_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int rtl8129_rx(struct net_device *dev); +static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static void rtl_error(struct net_device *dev, int status, int link_status); +static int rtl8129_close(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *rtl8129_get_stats(struct net_device *dev); +static inline u32 ether_crc(int length, unsigned char *data); +static void set_rx_mode(struct net_device *dev); + + +/* A list of all installed RTL8129 devices, for removing the driver module. */ +static struct net_device *root_rtl8129_dev = NULL; + +#ifndef MODULE +int rtl8139_probe(struct net_device *dev) +{ +	static int did_version = 0;			/* Already printed version info. */ + +	if (debug >= NETIF_MSG_DRV	/* Emit version even if no cards detected. */ +		&&  did_version++ == 0) +		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); +	return pci_drv_register(&rtl8139_drv_id, dev); +} +#endif + +static void *rtl8139_probe1(struct pci_dev *pdev, void *init_dev, +							long ioaddr, int irq, int chip_idx, int found_cnt) +{ +	struct net_device *dev; +	struct rtl8129_private *np; +	void *priv_mem; +	int i, option = found_cnt < MAX_UNITS ? options[found_cnt] : 0; +	int config1; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", +		   dev->name, pci_tbl[chip_idx].name, ioaddr, irq); + +	/* Bring the chip out of low-power mode. */ +	config1 = inb(ioaddr + Config1); +	if (pci_tbl[chip_idx].drv_flags & HAS_MII_XCVR)			/* rtl8129 chip */ +		outb(config1 & ~0x03, ioaddr + Config1); + +	{ +		int addr_len = read_eeprom(ioaddr, 0, 8) == 0x8129 ? 8 : 6; +		for (i = 0; i < 3; i++) +			((u16 *)(dev->dev_addr))[i] = +				le16_to_cpu(read_eeprom(ioaddr, i+7, addr_len)); +	} + +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x.\n", dev->dev_addr[i]); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	/* We do a request_region() to register /proc/ioports info. */ +	request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_rtl8129_dev; +	root_rtl8129_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	np->config1 = config1; + +	/* Find the connected MII xcvrs. +	   Doing this in open() would allow detecting external xcvrs later, but +	   takes too much time. */ +	if (np->drv_flags & HAS_MII_XCVR) { +		int phy, phy_idx = 0; +		for (phy = 0; phy < 32 && phy_idx < sizeof(np->phys); phy++) { +			int mii_status = mdio_read(dev, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				np->phys[phy_idx++] = phy; +				np->advertising = mdio_read(dev, phy, 4); +				printk(KERN_INFO "%s: MII transceiver %d status 0x%4.4x " +					   "advertising %4.4x.\n", +					   dev->name, phy, mii_status, np->advertising); +			} +		} +		if (phy_idx == 0) { +			printk(KERN_INFO "%s: No MII transceivers found!  Assuming SYM " +				   "transceiver.\n", +				   dev->name); +			np->phys[0] = 32; +		} +	} else +		np->phys[0] = 32; + +	/* Put the chip into low-power mode. */ +	outb(0xC0, ioaddr + Cfg9346); +	if (np->drv_flags & HAS_MII_XCVR)			/* rtl8129 chip */ +		outb(0x03, ioaddr + Config1); + +	outb('H', ioaddr + HltClk);		/* 'R' would leave the clock running. */ + +	/* The lower four bits are the media type. */ +	if (option > 0) { +		np->full_duplex = (option & 0x220) ? 1 : 0; +		np->default_port = option & 0x330; +		if (np->default_port) +			np->medialock = 1; +	} + +	if (found_cnt < MAX_UNITS  &&  full_duplex[found_cnt] > 0) +		np->full_duplex = full_duplex[found_cnt]; + +	if (np->full_duplex) { +		printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); +		/* Changing the MII-advertised media might prevent re-connection. */ +		np->duplex_lock = 1; +	} +	if (np->default_port) { +		printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +			   (option & 0x300 ? 100 : 10), +			   (option & 0x220 ? "full" : "half")); +		mdio_write(dev, np->phys[0], 0, +				   ((option & 0x300) ? 0x2000 : 0) | 	/* 100mbps? */ +				   ((option & 0x220) ? 0x0100 : 0)); /* Full duplex? */ +	} + +	/* The rtl81x9-specific entries in the device structure. */ +	dev->open = &rtl8129_open; +	dev->hard_start_xmit = &rtl8129_start_xmit; +	dev->stop = &rtl8129_close; +	dev->get_stats = &rtl8129_get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; + +	return dev; +} + +/* Serial EEPROM section. */ + +/*  EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK	0x04	/* EEPROM shift clock. */ +#define EE_CS			0x08	/* EEPROM chip select. */ +#define EE_DATA_WRITE	0x02	/* EEPROM chip data in. */ +#define EE_WRITE_0		0x00 +#define EE_WRITE_1		0x02 +#define EE_DATA_READ	0x01	/* EEPROM chip data out. */ +#define EE_ENB			(0x80 | EE_CS) + +/* Delay between EEPROM clock transitions. +   No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. + */ + +#define eeprom_delay()	inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD	(5) +#define EE_READ_CMD		(6) +#define EE_ERASE_CMD	(7) + +static int read_eeprom(long ioaddr, int location, int addr_len) +{ +	int i; +	unsigned retval = 0; +	long ee_addr = ioaddr + Cfg9346; +	int read_cmd = location | (EE_READ_CMD << addr_len); + +	outb(EE_ENB & ~EE_CS, ee_addr); +	outb(EE_ENB, ee_addr); + +	/* Shift the read command bits out. */ +	for (i = 4 + addr_len; i >= 0; i--) { +		int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; +		outb(EE_ENB | dataval, ee_addr); +		eeprom_delay(); +		outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); +		eeprom_delay(); +	} +	outb(EE_ENB, ee_addr); +	eeprom_delay(); + +	for (i = 16; i > 0; i--) { +		outb(EE_ENB | EE_SHIFT_CLK, ee_addr); +		eeprom_delay(); +		retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); +		outb(EE_ENB, ee_addr); +		eeprom_delay(); +	} + +	/* Terminate the EEPROM access. */ +	outb(~EE_CS, ee_addr); +	return retval; +} + +/* MII serial management: mostly bogus for now. */ +/* Read and write the MII management registers using software-generated +   serial MDIO protocol. +   The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually +   met by back-to-back PCI I/O cycles, but we insert a delay to avoid +   "overclocking" issues. */ +#define MDIO_DIR		0x80 +#define MDIO_DATA_OUT	0x04 +#define MDIO_DATA_IN	0x02 +#define MDIO_CLK		0x01 +#define MDIO_WRITE0 (MDIO_DIR) +#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) + +#define mdio_delay(mdio_addr)	inb(mdio_addr) + +static char mii_2_8139_map[8] = {MII_BMCR, MII_BMSR, 0, 0, NWayAdvert, +								 NWayLPAR, NWayExpansion, 0 }; + +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_sync(long mdio_addr) +{ +	int i; + +	for (i = 32; i >= 0; i--) { +		outb(MDIO_WRITE1, mdio_addr); +		mdio_delay(mdio_addr); +		outb(MDIO_WRITE1 | MDIO_CLK, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return; +} +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ +	long mdio_addr = dev->base_addr + MII_SMI; +	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; +	int retval = 0; +	int i; + +	if (phy_id > 31) {	/* Really a 8139.  Use internal registers. */ +		return location < 8 && mii_2_8139_map[location] ? +			inw(dev->base_addr + mii_2_8139_map[location]) : 0; +	} +	mdio_sync(mdio_addr); +	/* Shift the read command bits out. */ +	for (i = 15; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; + +		outb(MDIO_DIR | dataval, mdio_addr); +		mdio_delay(mdio_addr); +		outb(MDIO_DIR | dataval | MDIO_CLK, mdio_addr); +		mdio_delay(mdio_addr); +	} + +	/* Read the two transition, 16 data, and wire-idle bits. */ +	for (i = 19; i > 0; i--) { +		outb(0, mdio_addr); +		mdio_delay(mdio_addr); +		retval = (retval << 1) | ((inb(mdio_addr) & MDIO_DATA_IN) ? 1 : 0); +		outb(MDIO_CLK, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, +					   int value) +{ +	long mdio_addr = dev->base_addr + MII_SMI; +	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; +	int i; + +	if (phy_id > 31) {	/* Really a 8139.  Use internal registers. */ +		long ioaddr = dev->base_addr; +		if (location == 0) { +			outb(0xC0, ioaddr + Cfg9346); +			outw(value, ioaddr + MII_BMCR); +			outb(0x00, ioaddr + Cfg9346); +		} else if (location < 8  &&  mii_2_8139_map[location]) +			outw(value, ioaddr + mii_2_8139_map[location]); +		return; +	} +	mdio_sync(mdio_addr); + +	/* Shift the command bits out. */ +	for (i = 31; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; +		outb(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		outb(dataval | MDIO_CLK, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Clear out extra bits. */ +	for (i = 2; i > 0; i--) { +		outb(0, mdio_addr); +		mdio_delay(mdio_addr); +		outb(MDIO_CLK, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return; +} + + +static int rtl8129_open(struct net_device *dev) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int rx_buf_len_idx; + +	MOD_INC_USE_COUNT; + +	/* The Rx ring allocation size is 2^N + delta, which is worst-case for +	   the kernel binary-buddy allocation.  We allocate the Tx bounce buffers +	   at the same time to use some of the otherwise wasted space. +	   The delta of +16 is required for dribble-over because the receiver does +	   not wrap when the packet terminates just beyond the end of the ring. */ +	rx_buf_len_idx = RX_BUF_LEN_IDX; +	do { +		tp->rx_buf_len = 8192 << rx_buf_len_idx; +		tp->rx_ring = kmalloc(tp->rx_buf_len + 16 + +							  (TX_BUF_SIZE * NUM_TX_DESC), GFP_KERNEL); +	} while (tp->rx_ring == NULL  &&  --rx_buf_len_idx >= 0); + +	if (tp->rx_ring == NULL) { +		if (debug > 0) +			printk(KERN_ERR "%s: Couldn't allocate a %d byte receive ring.\n", +				   dev->name, tp->rx_buf_len); +		MOD_DEC_USE_COUNT; +		return -ENOMEM; +	} +	tp->tx_bufs = tp->rx_ring + tp->rx_buf_len + 16; + +	rtl8129_init_ring(dev); +	tp->full_duplex = tp->duplex_lock; +	tp->tx_flag = (TX_FIFO_THRESH<<11) & 0x003f0000; +	tp->rx_config = +		(RX_FIFO_THRESH << 13) | (rx_buf_len_idx << 11) | (RX_DMA_BURST<<8); + +	if (request_irq(dev->irq, &rtl8129_interrupt, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	rtl_hw_start(dev); +	netif_start_tx_queue(dev); + +	if (tp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG"%s: rtl8129_open() ioaddr %#lx IRQ %d" +			   " GP Pins %2.2x %s-duplex.\n", +			   dev->name, ioaddr, dev->irq, inb(ioaddr + GPPinData), +			   tp->full_duplex ? "full" : "half"); + +	/* Set the timer to switch to check for link beat and perhaps switch +	   to an alternate media type. */ +	init_timer(&tp->timer); +	tp->timer.expires = jiffies + 3*HZ; +	tp->timer.data = (unsigned long)dev; +	tp->timer.function = &rtl8129_timer; +	add_timer(&tp->timer); + +	return 0; +} + +/* Start the hardware at open or resume. */ +static void rtl_hw_start(struct net_device *dev) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	/* Soft reset the chip. */ +	outb(CmdReset, ioaddr + ChipCmd); +	/* Check that the chip has finished the reset. */ +	for (i = 1000; i > 0; i--) +		if ((inb(ioaddr + ChipCmd) & CmdReset) == 0) +			break; +	/* Restore our idea of the MAC address. */ +	outb(0xC0, ioaddr + Cfg9346); +	outl(cpu_to_le32(*(u32*)(dev->dev_addr + 0)), ioaddr + MAC0 + 0); +	outl(cpu_to_le32(*(u32*)(dev->dev_addr + 4)), ioaddr + MAC0 + 4); + +	/* Hmmm, do these belong here? */ +	tp->cur_rx = 0; + +	/* Must enable Tx/Rx before setting transfer thresholds! */ +	outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); +	outl(tp->rx_config, ioaddr + RxConfig); +	/* Check this value: the documentation contradicts ifself.  Is the +	   IFG correct with bit 28:27 zero, or with |0x03000000 ? */ +	outl((TX_DMA_BURST<<8), ioaddr + TxConfig); + +	/* This is check_duplex() */ +	if (tp->phys[0] >= 0  ||  (tp->drv_flags & HAS_MII_XCVR)) { +		u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5); +		if (mii_reg5 == 0xffff) +			;					/* Not there */ +		else if ((mii_reg5 & 0x0100) == 0x0100 +				 || (mii_reg5 & 0x00C0) == 0x0040) +			tp->full_duplex = 1; +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO"%s: Setting %s%s-duplex based on" +				   " auto-negotiated partner ability %4.4x.\n", dev->name, +				   mii_reg5 == 0 ? "" : +				   (mii_reg5 & 0x0180) ? "100mbps " : "10mbps ", +				   tp->full_duplex ? "full" : "half", mii_reg5); +	} + +	if (tp->drv_flags & HAS_MII_XCVR)			/* rtl8129 chip */ +		outb(tp->full_duplex ? 0x60 : 0x20, ioaddr + Config1); +	outb(0x00, ioaddr + Cfg9346); + +	outl(virt_to_bus(tp->rx_ring), ioaddr + RxBuf); +	/* Start the chip's Tx and Rx process. */ +	outl(0, ioaddr + RxMissed); +	set_rx_mode(dev); +	outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); +	/* Enable all known interrupts by setting the interrupt mask. */ +	outw(PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver +		 | TxErr | TxOK | RxErr | RxOK, ioaddr + IntrMask); + +} + +static void rtl8129_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct rtl8129_private *np = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 60*HZ; +	int mii_reg5 = mdio_read(dev, np->phys[0], 5); + +	if (! np->duplex_lock  &&  mii_reg5 != 0xffff) { +		int duplex = (mii_reg5&0x0100) || (mii_reg5 & 0x01C0) == 0x0040; +		if (np->full_duplex != duplex) { +			np->full_duplex = duplex; +			printk(KERN_INFO "%s: Using %s-duplex based on MII #%d link" +				   " partner ability of %4.4x.\n", dev->name, +				   np->full_duplex ? "full" : "half", np->phys[0], mii_reg5); +			if (np->drv_flags & HAS_MII_XCVR) { +				outb(0xC0, ioaddr + Cfg9346); +				outb(np->full_duplex ? 0x60 : 0x20, ioaddr + Config1); +				outb(0x00, ioaddr + Cfg9346); +			} +		} +	} +#if LINUX_VERSION_CODE < 0x20300 +	/* Check for bogusness. */ +	if (inw(ioaddr + IntrStatus) & (TxOK | RxOK)) { +		int status = inw(ioaddr + IntrStatus);			/* Double check */ +		if (status & (TxOK | RxOK)  &&  ! dev->interrupt) { +			printk(KERN_ERR "%s: RTL8139 Interrupt line blocked, status %x.\n", +				   dev->name, status); +			rtl8129_interrupt(dev->irq, dev, 0); +		} +	} +	if (dev->tbusy  &&  jiffies - dev->trans_start >= 2*TX_TIMEOUT) +		rtl8129_tx_timeout(dev); +#else +	if (netif_queue_paused(dev)  && +		np->cur_tx - np->dirty_tx > 1  && +		(jiffies - dev->trans_start) > TX_TIMEOUT) { +		rtl8129_tx_timeout(dev); +	} +#endif + +#if defined(RTL_TUNE_TWISTER) +	/* This is a complicated state machine to configure the "twister" for +	   impedance/echos based on the cable length. +	   All of this is magic and undocumented. +	   */ +	if (np->twistie) switch(np->twistie) { +	case 1: { +		if (inw(ioaddr + CSCR) & CSCR_LinkOKBit) { +			/* We have link beat, let us tune the twister. */ +			outw(CSCR_LinkDownOffCmd, ioaddr + CSCR); +			np->twistie = 2;	/* Change to state 2. */ +			next_tick = HZ/10; +		} else { +			/* Just put in some reasonable defaults for when beat returns. */ +			outw(CSCR_LinkDownCmd, ioaddr + CSCR); +			outl(0x20,ioaddr + FIFOTMS);	/* Turn on cable test mode. */ +			outl(PARA78_default ,ioaddr + PARA78); +			outl(PARA7c_default ,ioaddr + PARA7c); +			np->twistie = 0;	/* Bail from future actions. */ +		} +	} break; +	case 2: { +		/* Read how long it took to hear the echo. */ +		int linkcase = inw(ioaddr + CSCR) & CSCR_LinkStatusBits; +		if (linkcase == 0x7000) np->twist_row = 3; +		else if (linkcase == 0x3000) np->twist_row = 2; +		else if (linkcase == 0x1000) np->twist_row = 1; +		else np->twist_row = 0; +		np->twist_col = 0; +		np->twistie = 3;	/* Change to state 2. */ +		next_tick = HZ/10; +	} break; +	case 3: { +		/* Put out four tuning parameters, one per 100msec. */ +		if (np->twist_col == 0) outw(0, ioaddr + FIFOTMS); +		outl(param[(int)np->twist_row][(int)np->twist_col], ioaddr + PARA7c); +		next_tick = HZ/10; +		if (++np->twist_col >= 4) { +			/* For short cables we are done. +			   For long cables (row == 3) check for mistune. */ +			np->twistie = (np->twist_row == 3) ? 4 : 0; +		} +	} break; +	case 4: { +		/* Special case for long cables: check for mistune. */ +		if ((inw(ioaddr + CSCR) & CSCR_LinkStatusBits) == 0x7000) { +			np->twistie = 0; +			break; +		} else { +			outl(0xfb38de03, ioaddr + PARA7c); +			np->twistie = 5; +			next_tick = HZ/10; +		} +	} break; +	case 5: { +		/* Retune for shorter cable (column 2). */ +		outl(0x20,ioaddr + FIFOTMS); +		outl(PARA78_default,  ioaddr + PARA78); +		outl(PARA7c_default,  ioaddr + PARA7c); +		outl(0x00,ioaddr + FIFOTMS); +		np->twist_row = 2; +		np->twist_col = 0; +		np->twistie = 3; +		next_tick = HZ/10; +	} break; +	} +#endif + +	if (np->msg_level & NETIF_MSG_TIMER) { +		if (np->drv_flags & HAS_MII_XCVR) +			printk(KERN_DEBUG"%s: Media selection tick, GP pins %2.2x.\n", +				   dev->name, inb(ioaddr + GPPinData)); +		else +			printk(KERN_DEBUG"%s: Media selection tick, Link partner %4.4x.\n", +				   dev->name, inw(ioaddr + NWayLPAR)); +		printk(KERN_DEBUG"%s:  Other registers are IntMask %4.4x " +			   "IntStatus %4.4x RxStatus %4.4x.\n", +			   dev->name, inw(ioaddr + IntrMask), inw(ioaddr + IntrStatus), +			   (int)inl(ioaddr + RxEarlyStatus)); +		printk(KERN_DEBUG"%s:  Chip config %2.2x %2.2x.\n", +			   dev->name, inb(ioaddr + Config0), inb(ioaddr + Config1)); +	} + +	np->timer.expires = jiffies + next_tick; +	add_timer(&np->timer); +} + +static void rtl8129_tx_timeout(struct net_device *dev) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int status = inw(ioaddr + IntrStatus); +	int mii_reg, i; + +	/* Could be wrapped with if (tp->msg_level & NETIF_MSG_TX_ERR) */ +	printk(KERN_ERR "%s: Transmit timeout, status %2.2x %4.4x " +		   "media %2.2x.\n", +		   dev->name, inb(ioaddr + ChipCmd), status, inb(ioaddr + GPPinData)); + +	if (status & (TxOK | RxOK)) { +		printk(KERN_ERR "%s: RTL8139 Interrupt line blocked, status %x.\n", +			   dev->name, status); +	} + +	/* Disable interrupts by clearing the interrupt mask. */ +	outw(0x0000, ioaddr + IntrMask); +	/* Emit info to figure out what went wrong. */ +	printk(KERN_DEBUG "%s: Tx queue start entry %d  dirty entry %d%s.\n", +		   dev->name, tp->cur_tx, tp->dirty_tx, tp->tx_full ? ", full" : ""); +	for (i = 0; i < NUM_TX_DESC; i++) +		printk(KERN_DEBUG "%s:  Tx descriptor %d is %8.8x.%s\n", +			   dev->name, i, (int)inl(ioaddr + TxStatus0 + i*4), +			   i == tp->dirty_tx % NUM_TX_DESC ? " (queue head)" : ""); +	printk(KERN_DEBUG "%s: MII #%d registers are:", dev->name, tp->phys[0]); +	for (mii_reg = 0; mii_reg < 8; mii_reg++) +		printk(" %4.4x", mdio_read(dev, tp->phys[0], mii_reg)); +	printk(".\n"); + +	/* Stop a shared interrupt from scavenging while we are. */ +	tp->dirty_tx = tp->cur_tx = 0; +	/* Dump the unsent Tx packets. */ +	for (i = 0; i < NUM_TX_DESC; i++) { +		if (tp->tx_skbuff[i]) { +			dev_free_skb(tp->tx_skbuff[i]); +			tp->tx_skbuff[i] = 0; +			tp->stats.tx_dropped++; +		} +	} +	rtl_hw_start(dev); +	netif_unpause_tx_queue(dev); +	tp->tx_full = 0; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +rtl8129_init_ring(struct net_device *dev) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	int i; + +	tp->tx_full = 0; +	tp->dirty_tx = tp->cur_tx = 0; + +	for (i = 0; i < NUM_TX_DESC; i++) { +		tp->tx_skbuff[i] = 0; +		tp->tx_buf[i] = &tp->tx_bufs[i*TX_BUF_SIZE]; +	} +} + +static int +rtl8129_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int entry; + +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			rtl8129_tx_timeout(dev); +		return 1; +	} + +	/* Calculate the next Tx descriptor entry. */ +	entry = tp->cur_tx % NUM_TX_DESC; + +	tp->tx_skbuff[entry] = skb; +	if ((long)skb->data & 3) {			/* Must use alignment buffer. */ +		memcpy(tp->tx_buf[entry], skb->data, skb->len); +		outl(virt_to_bus(tp->tx_buf[entry]), ioaddr + TxAddr0 + entry*4); +	} else +		outl(virt_to_bus(skb->data), ioaddr + TxAddr0 + entry*4); +	/* Note: the chip doesn't have auto-pad! */ +	outl(tp->tx_flag | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN), +		 ioaddr + TxStatus0 + entry*4); + +	/* There is a race condition here -- we might read dirty_tx, take an +	   interrupt that clears the Tx queue, and only then set tx_full. +	   So we do this in two phases. */ +	if (++tp->cur_tx - tp->dirty_tx >= NUM_TX_DESC) { +		set_bit(0, &tp->tx_full); +		if (tp->cur_tx - (volatile unsigned int)tp->dirty_tx < NUM_TX_DESC) { +			clear_bit(0, &tp->tx_full); +			netif_unpause_tx_queue(dev); +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev); + +	dev->trans_start = jiffies; +	if (tp->msg_level & NETIF_MSG_TX_QUEUED) +		printk(KERN_DEBUG"%s: Queued Tx packet at %p size %d to slot %d.\n", +			   dev->name, skb->data, (int)skb->len, entry); + +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void rtl8129_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct rtl8129_private *np = (struct rtl8129_private *)dev->priv; +	struct rtl8129_private *tp = np; +	int boguscnt = np->max_interrupt_work; +	long ioaddr = dev->base_addr; +	int link_changed = 0;		/* Grrr, avoid bogus "uninitialized" warning */ + +#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x20123 +	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */ +	if (test_and_set_bit(0, (void*)&dev->interrupt)) { +		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", +			   dev->name); +		dev->interrupt = 0;	/* Avoid halting machine. */ +		return; +	} +#endif + +	do { +		int status = inw(ioaddr + IntrStatus); +		/* Acknowledge all of the current interrupt sources ASAP, but +		   an first get an additional status bit from CSCR. */ +		if (status & RxUnderrun) +			link_changed = inw(ioaddr+CSCR) & CSCR_LinkChangeBit; +		outw(status, ioaddr + IntrStatus); + +		if (tp->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG"%s: interrupt  status=%#4.4x new intstat=%#4.4x.\n", +				   dev->name, status, inw(ioaddr + IntrStatus)); + +		if ((status & (PCIErr|PCSTimeout|RxUnderrun|RxOverflow|RxFIFOOver +					   |TxErr|TxOK|RxErr|RxOK)) == 0) +			break; + +		if (status & (RxOK|RxUnderrun|RxOverflow|RxFIFOOver))/* Rx interrupt */ +			rtl8129_rx(dev); + +		if (status & (TxOK | TxErr)) { +			unsigned int dirty_tx = tp->dirty_tx; + +			while (tp->cur_tx - dirty_tx > 0) { +				int entry = dirty_tx % NUM_TX_DESC; +				int txstatus = inl(ioaddr + TxStatus0 + entry*4); + +				if ( ! (txstatus & (TxStatOK | TxUnderrun | TxAborted))) +					break;			/* It still hasn't been Txed */ + +				/* Note: TxCarrierLost is always asserted at 100mbps. */ +				if (txstatus & (TxOutOfWindow | TxAborted)) { +					/* There was an major error, log it. */ +					if (tp->msg_level & NETIF_MSG_TX_ERR) +						printk(KERN_NOTICE"%s: Transmit error, Tx status %8.8x.\n", +							   dev->name, txstatus); +					tp->stats.tx_errors++; +					if (txstatus&TxAborted) { +						tp->stats.tx_aborted_errors++; +						outl(TX_DMA_BURST << 8, ioaddr + TxConfig); +					} +					if (txstatus&TxCarrierLost) tp->stats.tx_carrier_errors++; +					if (txstatus&TxOutOfWindow) tp->stats.tx_window_errors++; +#ifdef ETHER_STATS +					if ((txstatus & 0x0f000000) == 0x0f000000) +						tp->stats.collisions16++; +#endif +				} else { +					if (tp->msg_level & NETIF_MSG_TX_DONE) +						printk(KERN_DEBUG "%s: Transmit done, Tx status" +							   " %8.8x.\n", dev->name, txstatus); +					if (txstatus & TxUnderrun) { +						/* Add 64 to the Tx FIFO threshold. */ +						if (tp->tx_flag <  0x00300000) +							tp->tx_flag += 0x00020000; +						tp->stats.tx_fifo_errors++; +					} +					tp->stats.collisions += (txstatus >> 24) & 15; +#if LINUX_VERSION_CODE > 0x20119 +					tp->stats.tx_bytes += txstatus & 0x7ff; +#endif +					tp->stats.tx_packets++; +				} + +				/* Free the original skb. */ +				dev_free_skb_irq(tp->tx_skbuff[entry]); +				tp->tx_skbuff[entry] = 0; +				if (test_bit(0, &tp->tx_full)) { +					/* The ring is no longer full, clear tbusy. */ +					clear_bit(0, &tp->tx_full); +					netif_resume_tx_queue(dev); +				} +				dirty_tx++; +			} + +#ifndef final_version +			if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { +				printk(KERN_ERR"%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", +					   dev->name, dirty_tx, tp->cur_tx, (int)tp->tx_full); +				dirty_tx += NUM_TX_DESC; +			} +#endif +			tp->dirty_tx = dirty_tx; +		} + +		/* Check uncommon events with one test. */ +		if (status & (PCIErr|PCSTimeout |RxUnderrun|RxOverflow|RxFIFOOver +					  |TxErr|RxErr)) { +			if (status == 0xffff) 			/* Missing chip! */ +				break; +			rtl_error(dev, status, link_changed); +		} + +		if (--boguscnt < 0) { +			printk(KERN_WARNING"%s: Too much work at interrupt, " +				   "IntrStatus=0x%4.4x.\n", +				   dev->name, status); +			/* Clear all interrupt sources. */ +			outw(0xffff, ioaddr + IntrStatus); +			break; +		} +	} while (1); + +	if (tp->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG"%s: exiting interrupt, intr_status=%#4.4x.\n", +			   dev->name, inw(ioaddr + IntrStatus)); + +#if defined(__i386__)  &&  LINUX_VERSION_CODE < 0x20123 +	clear_bit(0, (void*)&dev->interrupt); +#endif +	return; +} + +/* The data sheet doesn't describe the Rx ring at all, so I'm guessing at the +   field alignments and semantics. */ +static int rtl8129_rx(struct net_device *dev) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; +	unsigned char *rx_ring = tp->rx_ring; +	u16 cur_rx = tp->cur_rx; + +	if (tp->msg_level & NETIF_MSG_RX_STATUS) +		printk(KERN_DEBUG"%s: In rtl8129_rx(), current %4.4x BufAddr %4.4x," +			   " free to %4.4x, Cmd %2.2x.\n", +			   dev->name, cur_rx, inw(ioaddr + RxBufAddr), +			   inw(ioaddr + RxBufPtr), inb(ioaddr + ChipCmd)); + +	while ((inb(ioaddr + ChipCmd) & RxBufEmpty) == 0) { +		int ring_offset = cur_rx % tp->rx_buf_len; +		u32 rx_status = le32_to_cpu(*(u32*)(rx_ring + ring_offset)); +		int rx_size = rx_status >> 16; 				/* Includes the CRC. */ + +		if (tp->msg_level & NETIF_MSG_RX_STATUS) { +			int i; +			printk(KERN_DEBUG"%s:  rtl8129_rx() status %4.4x, size %4.4x," +				   " cur %4.4x.\n", +				   dev->name, rx_status, rx_size, cur_rx); +			printk(KERN_DEBUG"%s: Frame contents ", dev->name); +			for (i = 0; i < 70; i++) +				printk(" %2.2x", rx_ring[ring_offset + i]); +			printk(".\n"); +		} +		if (rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) { +			if (tp->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG"%s: Ethernet frame had errors," +					   " status %8.8x.\n", dev->name, rx_status); +			if (rx_status == 0xffffffff) { +				printk(KERN_NOTICE"%s: Invalid receive status at ring " +					   "offset %4.4x\n", dev->name, ring_offset); +				rx_status = 0; +			} +			if (rx_status & RxTooLong) { +				if (tp->msg_level & NETIF_MSG_DRV) +					printk(KERN_NOTICE"%s: Oversized Ethernet frame, status" +						   " %4.4x!\n", +						   dev->name, rx_status); +				/* A.C.: The chip hangs here. +				   This should never occur, which means that we are screwed +				   when it does. +				 */ +			} +			tp->stats.rx_errors++; +			if (rx_status & (RxBadSymbol|RxBadAlign)) +				tp->stats.rx_frame_errors++; +			if (rx_status & (RxRunt|RxTooLong)) tp->stats.rx_length_errors++; +			if (rx_status & RxCRCErr) tp->stats.rx_crc_errors++; +			/* Reset the receiver, based on RealTek recommendation. (Bug?) */ +			tp->cur_rx = 0; +			outb(CmdTxEnb, ioaddr + ChipCmd); +			/* A.C.: Reset the multicast list. */ +			set_rx_mode(dev); +			outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); +		} else { +			/* Malloc up new buffer, compatible with net-2e. */ +			/* Omit the four octet CRC from the length. */ +			struct sk_buff *skb; +			int pkt_size = rx_size - 4; + +			/* Allocate a common-sized skbuff if we are close. */ +			skb = dev_alloc_skb(1400 < pkt_size && pkt_size < PKT_BUF_SZ-2 ? +								PKT_BUF_SZ : pkt_size + 2); +			if (skb == NULL) { +				printk(KERN_WARNING"%s: Memory squeeze, deferring packet.\n", +					   dev->name); +				/* We should check that some rx space is free. +				   If not, free one and mark stats->rx_dropped++. */ +				tp->stats.rx_dropped++; +				break; +			} +			skb->dev = dev; +			skb_reserve(skb, 2);	/* 16 byte align the IP fields. */ +			if (ring_offset + rx_size > tp->rx_buf_len) { +				int semi_count = tp->rx_buf_len - ring_offset - 4; +				/* This could presumably use two calls to copy_and_sum()? */ +				memcpy(skb_put(skb, semi_count), &rx_ring[ring_offset + 4], +					   semi_count); +				memcpy(skb_put(skb, pkt_size-semi_count), rx_ring, +					   pkt_size-semi_count); +				if (tp->msg_level & NETIF_MSG_PKTDATA) { +					int i; +					printk(KERN_DEBUG"%s:  Frame wrap @%d", +						   dev->name, semi_count); +					for (i = 0; i < 16; i++) +						printk(" %2.2x", rx_ring[i]); +					printk(".\n"); +					memset(rx_ring, 0xcc, 16); +				} +			} else { +				eth_copy_and_sum(skb, &rx_ring[ring_offset + 4], +								 pkt_size, 0); +				skb_put(skb, pkt_size); +			} +			skb->protocol = eth_type_trans(skb, dev); +			netif_rx(skb); +#if LINUX_VERSION_CODE > 0x20119 +			tp->stats.rx_bytes += pkt_size; +#endif +			tp->stats.rx_packets++; +		} + +		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; +		outw(cur_rx - 16, ioaddr + RxBufPtr); +	} +	if (tp->msg_level & NETIF_MSG_RX_STATUS) +		printk(KERN_DEBUG"%s: Done rtl8129_rx(), current %4.4x BufAddr %4.4x," +			   " free to %4.4x, Cmd %2.2x.\n", +			   dev->name, cur_rx, inw(ioaddr + RxBufAddr), +			   inw(ioaddr + RxBufPtr), inb(ioaddr + ChipCmd)); +	tp->cur_rx = cur_rx; +	return 0; +} + +/* Error and abnormal or uncommon events handlers. */ +static void rtl_error(struct net_device *dev, int status, int link_changed) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (tp->msg_level & NETIF_MSG_LINK) +		printk(KERN_NOTICE"%s: Abnormal interrupt, status %8.8x.\n", +			   dev->name, status); + +	/* Update the error count. */ +	tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); +	outl(0, ioaddr + RxMissed); + +	if (status & RxUnderrun){ +		/* This might actually be a link change event. */ +		if ((tp->drv_flags & HAS_LNK_CHNG)  &&  link_changed) { +			/* Really link-change on new chips. */ +			int lpar = inw(ioaddr + NWayLPAR); +			int duplex = (lpar&0x0100) || (lpar & 0x01C0) == 0x0040 +				|| tp->duplex_lock; +			/* Do not use MII_BMSR as that clears sticky bit. */ +			if (inw(ioaddr + GPPinData) & 0x0004) { +				netif_link_down(dev); +			} else +				netif_link_up(dev); +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_DEBUG "%s: Link changed, link partner " +					   "%4.4x new duplex %d.\n", +					   dev->name, lpar, duplex); +			tp->full_duplex = duplex; +			/* Only count as errors with no link change. */ +			status &= ~RxUnderrun; +		} else { +			/* If this does not work, we will do rtl_hw_start(dev); */ +			outb(CmdTxEnb, ioaddr + ChipCmd); +			set_rx_mode(dev);	/* Reset the multicast list. */ +			outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); + +			tp->stats.rx_errors++; +			tp->stats.rx_fifo_errors++; +		} +	} +	 +	if (status & (RxOverflow | RxErr | RxFIFOOver)) tp->stats.rx_errors++; +	if (status & (PCSTimeout)) tp->stats.rx_length_errors++; +	if (status & RxFIFOOver) tp->stats.rx_fifo_errors++; +	if (status & RxOverflow) { +		tp->stats.rx_over_errors++; +		tp->cur_rx = inw(ioaddr + RxBufAddr) % tp->rx_buf_len; +		outw(tp->cur_rx - 16, ioaddr + RxBufPtr); +	} +	if (status & PCIErr) { +		u32 pci_cmd_status; +		pci_read_config_dword(tp->pci_dev, PCI_COMMAND, &pci_cmd_status); + +		printk(KERN_ERR "%s: PCI Bus error %4.4x.\n", +			   dev->name, pci_cmd_status); +	} +} + +static int +rtl8129_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (tp->msg_level & NETIF_MSG_IFDOWN) +		printk(KERN_DEBUG"%s: Shutting down ethercard, status was 0x%4.4x.\n", +			   dev->name, inw(ioaddr + IntrStatus)); + +	/* Disable interrupts by clearing the interrupt mask. */ +	outw(0x0000, ioaddr + IntrMask); + +	/* Stop the chip's Tx and Rx DMA processes. */ +	outb(0x00, ioaddr + ChipCmd); + +	/* Update the error counts. */ +	tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); +	outl(0, ioaddr + RxMissed); + +	del_timer(&tp->timer); + +	free_irq(dev->irq, dev); + +	for (i = 0; i < NUM_TX_DESC; i++) { +		if (tp->tx_skbuff[i]) +			dev_free_skb(tp->tx_skbuff[i]); +		tp->tx_skbuff[i] = 0; +	} +	kfree(tp->rx_ring); +	tp->rx_ring = 0; + +	/* Green! Put the chip in low-power mode. */ +	outb(0xC0, ioaddr + Cfg9346); +	outb(tp->config1 | 0x03, ioaddr + Config1); +	outb('H', ioaddr + HltClk);		/* 'R' would leave the clock running. */ + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +/* +  Handle user-level ioctl() calls. +  We must use two numeric constants as the key because some clueless person +  changed value for the symbolic name. +*/ +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct rtl8129_private *np = (struct rtl8129_private *)dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = np->phys[0] & 0x3f; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(dev, data[0], data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == np->phys[0]) { +			u16 value = data[2]; +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				np->medialock = (value & 0x9000) ? 0 : 1; +				if (np->medialock) +					np->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: np->advertising = value; break; +			} +		} +		mdio_write(dev, data[0], data[1] & 0x1f, data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = 0;			/* No rx_copybreak, always copy. */ +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static struct net_device_stats * +rtl8129_get_stats(struct net_device *dev) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (netif_running(dev)) { +		tp->stats.rx_missed_errors += inl(ioaddr + RxMissed); +		outl(0, ioaddr + RxMissed); +	} + +	return &tp->stats; +} + +/* Set or clear the multicast filter for this adaptor. +   This routine is not state sensitive and need not be SMP locked. */ + +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ +	int crc = -1; + +	while (--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 0; bit < 8; bit++, current_octet >>= 1) +			crc = (crc << 1) ^ +				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); +	} +	return crc; +} + +/* Bits in RxConfig. */ +enum rx_mode_bits { +	AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08, +	AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01, +}; + +static void set_rx_mode(struct net_device *dev) +{ +	struct rtl8129_private *tp = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u32 mc_filter[2];		 /* Multicast hash filter */ +	int i, rx_mode; + +	if (tp->msg_level & NETIF_MSG_RXFILTER) +		printk(KERN_DEBUG"%s:   set_rx_mode(%4.4x) done -- Rx config %8.8x.\n", +			   dev->name, dev->flags, (int)inl(ioaddr + RxConfig)); + +	/* Note: do not reorder, GCC is clever about common statements. */ +	if (dev->flags & IFF_PROMISC) { +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name); +		rx_mode = AcceptBroadcast|AcceptMulticast|AcceptMyPhys|AcceptAllPhys; +		mc_filter[1] = mc_filter[0] = 0xffffffff; +	} else if ((dev->mc_count > tp->multicast_filter_limit) +			   || (dev->flags & IFF_ALLMULTI)) { +		/* Too many to filter perfectly -- accept all multicasts. */ +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; +		mc_filter[1] = mc_filter[0] = 0xffffffff; +	} else { +		struct dev_mc_list *mclist; +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; +		mc_filter[1] = mc_filter[0] = 0; +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) +			set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, mc_filter); +	} +	/* We can safely update without stopping the chip. */ +	outl(tp->rx_config | rx_mode, ioaddr + RxConfig); +	tp->mc_filter[0] = mc_filter[0]; +	tp->mc_filter[1] = mc_filter[1]; +	outl(mc_filter[0], ioaddr + MAR0 + 0); +	outl(mc_filter[1], ioaddr + MAR0 + 4); +	return; +} + + +static int rtl_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct rtl8129_private *np = (struct rtl8129_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk("%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		netif_device_detach(dev); +		/* Disable interrupts, stop Tx and Rx. */ +		outw(0x0000, ioaddr + IntrMask); +		outb(0x00, ioaddr + ChipCmd); +		/* Update the error counts. */ +		np->stats.rx_missed_errors += inl(ioaddr + RxMissed); +		outl(0, ioaddr + RxMissed); +		break; +	case DRV_RESUME: +		netif_device_attach(dev); +		rtl_hw_start(dev); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_rtl8129_dev; *devp; devp = next) { +			next = &((struct rtl8129_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	} + +	return 0; +} + +#ifdef CARDBUS + +#include <pcmcia/driver_ops.h> + +static dev_node_t *rtl8139_attach(dev_locator_t *loc) +{ +	struct net_device *dev; +	u16 dev_id; +	u32 pciaddr; +	u8 bus, devfn, irq; +	long hostaddr; +	/* Note: the chip index should match the 8139B pci_tbl[] entry. */ +	int chip_idx = 2; + +	if (loc->bus != LOC_PCI) return NULL; +	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; +	printk(KERN_DEBUG "rtl8139_attach(bus %d, function %d)\n", bus, devfn); +#ifdef USE_IO_OPS +	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &pciaddr); +	hostaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK; +#else +	pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_1, &pciaddr); +	hostaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, +							 pci_tbl[chip_idx].io_size); +#endif +	pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq); +	pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); +	if (hostaddr == 0 || irq == 0) { +		printk(KERN_ERR "The %s interface at %d/%d was not assigned an %s.\n" +			   KERN_ERR "  It will not be activated.\n", +			   pci_tbl[chip_idx].name, bus, devfn, +			   hostaddr == 0 ? "address" : "IRQ"); +		return NULL; +	} +	dev = rtl8139_probe1(pci_find_slot(bus, devfn), NULL, +						 hostaddr, irq, chip_idx, 0); +	if (dev) { +		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); +		strcpy(node->dev_name, dev->name); +		node->major = node->minor = 0; +		node->next = NULL; +		MOD_INC_USE_COUNT; +		return node; +	} +	return NULL; +} + +static void rtl8139_detach(dev_node_t *node) +{ +	struct net_device **devp, **next; +	printk(KERN_INFO "rtl8139_detach(%s)\n", node->dev_name); +	for (devp = &root_rtl8129_dev; *devp; devp = next) { +		next = &((struct rtl8129_private *)(*devp)->priv)->next_module; +		if (strcmp((*devp)->name, node->dev_name) == 0) break; +	} +	if (*devp) { +		struct rtl8129_private *np = +			(struct rtl8129_private *)(*devp)->priv; +		unregister_netdev(*devp); +		release_region((*devp)->base_addr, pci_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)(*devp)->base_addr); +#endif +		kfree(*devp); +		if (np->priv_addr) +			kfree(np->priv_addr); +		*devp = *next; +		kfree(node); +		MOD_DEC_USE_COUNT; +	} +} + +struct driver_operations realtek_ops = { +	"realtek_cb", +	rtl8139_attach, /*rtl8139_suspend*/0, /*rtl8139_resume*/0, rtl8139_detach +}; + +#endif  /* Cardbus support */ + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", versionA, versionB); +#ifdef CARDBUS +	register_driver(&realtek_ops); +	return 0; +#else +	return pci_drv_register(&rtl8139_drv_id, NULL); +#endif +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +#ifdef CARDBUS +	unregister_driver(&realtek_ops); +#else +	pci_drv_unregister(&rtl8139_drv_id); +#endif + +	while (root_rtl8129_dev) { +		struct rtl8129_private *np = (void *)(root_rtl8129_dev->priv); +		unregister_netdev(root_rtl8129_dev); +		release_region(root_rtl8129_dev->base_addr, +					   pci_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)(root_rtl8129_dev->base_addr)); +#endif +		next_dev = np->next_module; +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_rtl8129_dev); +		root_rtl8129_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` rtl8139.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c rtl8139.c" + *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -Wall -Wstrict-prototypes -O6 -c rtl8139.c -o realtek_cb.o -I/usr/src/pcmcia/include/" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/seeq8005.c b/linux/src/drivers/net/seeq8005.c new file mode 100644 index 0000000..4adebde --- /dev/null +++ b/linux/src/drivers/net/seeq8005.c @@ -0,0 +1,760 @@ +/* seeq8005.c: A network driver for linux. */ +/* +	Based on skeleton.c, +	Written 1993-94 by Donald Becker. +	See the skeleton.c file for further copyright information. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as hamish@zot.apana.org.au + +	This file is a network device driver for the SEEQ 8005 chipset and +	the Linux operating system. + +*/ + +static const char *version = +	"seeq8005.c:v1.00 8/07/95 Hamish Coleman (hamish@zot.apana.org.au)\n"; + +/* +  Sources: +  	SEEQ 8005 databook +  	 +  Version history: +  	1.00	Public release. cosmetic changes (no warnings now) +  	0.68	Turning per- packet,interrupt debug messages off - testing for release. +  	0.67	timing problems/bad buffer reads seem to be fixed now +  	0.63	*!@$ protocol=eth_type_trans -- now packets flow +  	0.56	Send working +  	0.48	Receive working +*/ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include "seeq8005.h" + +/* First, a few definitions that the brave might change. */ +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int seeq8005_portlist[] = +   { 0x300, 0x320, 0x340, 0x360, 0}; + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 1 +#endif +static unsigned int net_debug = NET_DEBUG; + +/* Information that need to be kept for each board. */ +struct net_local { +	struct enet_statistics stats; +	unsigned short receive_ptr;		/* What address in packet memory do we expect a recv_pkt_header? */ +	long open_time;				/* Useless example local info. */ +}; + +/* The station (ethernet) address prefix, used for IDing the board. */ +#define SA_ADDR0 0x00 +#define SA_ADDR1 0x80 +#define SA_ADDR2 0x4b + +/* Index to functions, as function prototypes. */ + +extern int seeq8005_probe(struct device *dev); + +static int seeq8005_probe1(struct device *dev, int ioaddr); +static int seeq8005_open(struct device *dev); +static int seeq8005_send_packet(struct sk_buff *skb, struct device *dev); +static void seeq8005_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void seeq8005_rx(struct device *dev); +static int seeq8005_close(struct device *dev); +static struct enet_statistics *seeq8005_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); + +/* Example routines you must write ;->. */ +#define tx_done(dev)	(inw(SEEQ_STATUS) & SEEQSTAT_TX_ON) +extern void hardware_send_packet(struct device *dev, char *buf, int length); +extern void seeq8005_init(struct device *dev, int startp); +inline void wait_for_buffer(struct device *dev); + + +/* Check for a network adaptor of this type, and return '0' iff one exists. +   If dev->base_addr == 0, probe all likely locations. +   If dev->base_addr == 1, always return failure. +   If dev->base_addr == 2, allocate space for the device and return success +   (detachable devices only). +   */ +#ifdef HAVE_DEVLIST +/* Support for a alternate probe manager, which will eliminate the +   boilerplate below. */ +struct netdev_entry seeq8005_drv = +{"seeq8005", seeq8005_probe1, SEEQ8005_IO_EXTENT, seeq8005_portlist}; +#else +int +seeq8005_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return seeq8005_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; seeq8005_portlist[i]; i++) { +		int ioaddr = seeq8005_portlist[i]; +		if (check_region(ioaddr, SEEQ8005_IO_EXTENT)) +			continue; +		if (seeq8005_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +/* This is the real probe routine.  Linux has a history of friendly device +   probes on the ISA bus.  A good device probes avoids doing writes, and +   verifies that the correct device exists and functions.  */ + +static int seeq8005_probe1(struct device *dev, int ioaddr) +{ +	static unsigned version_printed = 0; +	int i,j; +	unsigned char SA_prom[32]; +	int old_cfg1; +	int old_cfg2; +	int old_stat; +	int old_dmaar; +	int old_rear; + +	if (net_debug>1) +		printk("seeq8005: probing at 0x%x\n",ioaddr); + +	old_stat = inw(SEEQ_STATUS);					/* read status register */ +	if (old_stat == 0xffff) +		return ENODEV;						/* assume that 0xffff == no device */ +	if ( (old_stat & 0x1800) != 0x1800 ) {				/* assume that unused bits are 1, as my manual says */ +		if (net_debug>1) { +			printk("seeq8005: reserved stat bits != 0x1800\n"); +			printk("          == 0x%04x\n",old_stat); +		} +	 	return ENODEV; +	} + +	old_rear = inw(SEEQ_REA); +	if (old_rear == 0xffff) { +		outw(0,SEEQ_REA); +		if (inw(SEEQ_REA) == 0xffff) {				/* assume that 0xffff == no device */ +			return ENODEV; +		} +	} else if ((old_rear & 0xff00) != 0xff00) {			/* assume that unused bits are 1 */ +		if (net_debug>1) { +			printk("seeq8005: unused rear bits != 0xff00\n"); +			printk("          == 0x%04x\n",old_rear); +		} +		return ENODEV; +	} +	 +	old_cfg2 = inw(SEEQ_CFG2);					/* read CFG2 register */ +	old_cfg1 = inw(SEEQ_CFG1); +	old_dmaar = inw(SEEQ_DMAAR); +	 +	if (net_debug>4) { +		printk("seeq8005: stat = 0x%04x\n",old_stat); +		printk("seeq8005: cfg1 = 0x%04x\n",old_cfg1); +		printk("seeq8005: cfg2 = 0x%04x\n",old_cfg2); +		printk("seeq8005: raer = 0x%04x\n",old_rear); +		printk("seeq8005: dmaar= 0x%04x\n",old_dmaar); +	} +	 +	outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);	/* setup for reading PROM */ +	outw( 0, SEEQ_DMAAR);						/* set starting PROM address */ +	outw( SEEQCFG1_BUFFER_PROM, SEEQ_CFG1);				/* set buffer to look at PROM */ +	 +	 +	j=0; +	for(i=0; i <32; i++) { +		j+= SA_prom[i] = inw(SEEQ_BUFFER) & 0xff; +	} + +#if 0 +	/* untested because I only have the one card */ +	if ( (j&0xff) != 0 ) {						/* checksum appears to be 8bit = 0 */ +		if (net_debug>1) {					/* check this before deciding that we have a card */ +			printk("seeq8005: prom sum error\n"); +		} +		outw( old_stat, SEEQ_STATUS); +		outw( old_dmaar, SEEQ_DMAAR); +		outw( old_cfg1, SEEQ_CFG1); +		return ENODEV; +	} +#endif + +	outw( SEEQCFG2_RESET, SEEQ_CFG2);				/* reset the card */ +	SLOW_DOWN_IO;							/* have to wait 4us after a reset - should be fixed */ +	SLOW_DOWN_IO; +	SLOW_DOWN_IO; +	SLOW_DOWN_IO; +	outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD); +	 +	if (net_debug) { +		printk("seeq8005: prom sum = 0x%08x\n",j); +		for(j=0; j<32; j+=16) { +			printk("seeq8005: prom %02x: ",j); +			for(i=0;i<16;i++) { +				printk("%02x ",SA_prom[j|i]); +			} +			printk(" "); +			for(i=0;i<16;i++) { +				if ((SA_prom[j|i]>31)&&(SA_prom[j|i]<127)) { +					printk("%c", SA_prom[j|i]); +				} else { +					printk(" "); +				} +			} +			printk("\n"); +		} +	} + +#if 0	 +	/*  +	 * testing the packet buffer memory doesn't work yet +	 * but all other buffer accesses do  +	 *			- fixing is not a priority +	 */ +	if (net_debug>1) {					/* test packet buffer memory */ +		printk("seeq8005: testing packet buffer ... "); +		outw( SEEQCFG1_BUFFER_BUFFER, SEEQ_CFG1); +		outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD); +		outw( 0 , SEEQ_DMAAR); +		for(i=0;i<32768;i++) { +			outw(0x5a5a, SEEQ_BUFFER); +		} +		j=jiffies+HZ; +		while ( ((inw(SEEQ_STATUS) & SEEQSTAT_FIFO_EMPTY) != SEEQSTAT_FIFO_EMPTY) && jiffies < j ) +			mb(); +		outw( 0 , SEEQ_DMAAR); +		while ( ((inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && jiffies < j+HZ) +			mb(); +		if ( (inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT) +			outw( SEEQCMD_WINDOW_INT_ACK | (inw(SEEQ_STATUS)& SEEQCMD_INT_MASK), SEEQ_CMD); +		outw( SEEQCMD_FIFO_READ | SEEQCMD_SET_ALL_OFF, SEEQ_CMD); +		j=0; +		for(i=0;i<32768;i++) { +			if (inw(SEEQ_BUFFER) != 0x5a5a) +				j++; +		} +		if (j) { +			printk("%i\n",j); +		} else { +			printk("ok.\n"); +		} +	} +#endif + +	/* Allocate a new 'dev' if needed. */ +	if (dev == NULL) +		dev = init_etherdev(0, sizeof(struct net_local)); + +	if (net_debug  &&  version_printed++ == 0) +		printk("%s", version); + +	printk("%s: %s found at %#3x, ", dev->name, "seeq8005", ioaddr); + +	/* Fill in the 'dev' fields. */ +	dev->base_addr = ioaddr; + +	/* Retrieve and print the ethernet address. */ +	for (i = 0; i < 6; i++) +		printk(" %2.2x", dev->dev_addr[i] = SA_prom[i+6]); + +	if (dev->irq == 0xff) +		;			/* Do nothing: a user-level program will set it. */ +	else if (dev->irq < 2) {	/* "Auto-IRQ" */ +		autoirq_setup(0); +		 +		outw( SEEQCMD_RX_INT_EN | SEEQCMD_SET_RX_ON | SEEQCMD_SET_RX_OFF, SEEQ_CMD ); + +		dev->irq = autoirq_report(0); +		 +		if (net_debug >= 2) +			printk(" autoirq is %d\n", dev->irq); +	} else if (dev->irq == 2) +	  /* Fixup for users that don't know that IRQ 2 is really IRQ 9, +	   * or don't know which one to set.  +	   */ +	  dev->irq = 9; + +#if 0 +	{ +		 int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", NULL); +		 if (irqval) { +			 printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, +					 dev->irq, irqval); +			 return EAGAIN; +		 } +	} +#endif + +	/* Grab the region so we can find another board if autoIRQ fails. */ +	request_region(ioaddr, SEEQ8005_IO_EXTENT,"seeq8005"); + +	/* Initialize the device structure. */ +	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); +	if (dev->priv == NULL) +		return -ENOMEM; +	memset(dev->priv, 0, sizeof(struct net_local)); + +	dev->open		= seeq8005_open; +	dev->stop		= seeq8005_close; +	dev->hard_start_xmit = seeq8005_send_packet; +	dev->get_stats	= seeq8005_get_stats; +	dev->set_multicast_list = &set_multicast_list; + +	/* Fill in the fields of the device structure with ethernet values. */ +	ether_setup(dev); +	 +	dev->flags &= ~IFF_MULTICAST; + +	return 0; +} + + +/* Open/initialize the board.  This is called (in the current kernel) +   sometime after booting when the 'ifconfig' program is run. + +   This routine should set everything up anew at each open, even +   registers that "should" only need to be set once at boot, so that +   there is non-reboot way to recover if something goes wrong. +   */ +static int +seeq8005_open(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; + +	{ +		 int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005", NULL); +		 if (irqval) { +			 printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, +					 dev->irq, irqval); +			 return EAGAIN; +		 } +	} +	irq2dev_map[dev->irq] = dev; + +	/* Reset the hardware here.  Don't forget to set the station address. */ +	seeq8005_init(dev, 1); + +	lp->open_time = jiffies; + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; +	return 0; +} + +static int +seeq8005_send_packet(struct sk_buff *skb, struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if (dev->tbusy) { +		/* If we get here, some higher level has decided we are broken. +		   There should really be a "kick me" function call instead. */ +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < 5) +			return 1; +		printk("%s: transmit timed out, %s?\n", dev->name, +			   tx_done(dev) ? "IRQ conflict" : "network cable problem"); +		/* Try to restart the adaptor. */ +		seeq8005_init(dev, 1); +		dev->tbusy=0; +		dev->trans_start = jiffies; +	} + +	/* If some higher layer thinks we've missed an tx-done interrupt +	   we are passed NULL. Caution: dev_tint() handles the cli()/sti() +	   itself. */ +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) +		printk("%s: Transmitter access conflict.\n", dev->name); +	else { +		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		unsigned char *buf = skb->data; + +		hardware_send_packet(dev, buf, length);  +		dev->trans_start = jiffies; +	} +	dev_kfree_skb (skb, FREE_WRITE); + +	/* You might need to clean up and record Tx statistics here. */ + +	return 0; +} + +/* The typical workload of the driver: +   Handle the network interface interrupts. */ +static void +seeq8005_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +	struct device *dev = (struct device *)(irq2dev_map[irq]); +	struct net_local *lp; +	int ioaddr, status, boguscount = 0; + +	if (dev == NULL) { +		printk ("net_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +	 +	if (dev->interrupt) +		printk ("%s: Re-entering the interrupt handler.\n", dev->name); +	dev->interrupt = 1; + +	ioaddr = dev->base_addr; +	lp = (struct net_local *)dev->priv; + +	status = inw(SEEQ_STATUS); +	do { +		if (net_debug >2) { +			printk("%s: int, status=0x%04x\n",dev->name,status); +		} +		 +		if (status & SEEQSTAT_WINDOW_INT) { +			outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD); +			if (net_debug) { +				printk("%s: window int!\n",dev->name); +			} +		} +		if (status & SEEQSTAT_TX_INT) { +			outw( SEEQCMD_TX_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD); +			lp->stats.tx_packets++; +			dev->tbusy = 0; +			mark_bh(NET_BH);	/* Inform upper layers. */ +		} +		if (status & SEEQSTAT_RX_INT) { +			/* Got a packet(s). */ +			seeq8005_rx(dev); +		} +		status = inw(SEEQ_STATUS); +	} while ( (++boguscount < 10) && (status & SEEQSTAT_ANY_INT)) ; + +	if(net_debug>2) { +		printk("%s: eoi\n",dev->name); +	} +	dev->interrupt = 0; +	return; +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +seeq8005_rx(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int boguscount = 10; +	int pkt_hdr; +	int ioaddr = dev->base_addr; + +	do { +		int next_packet; +		int pkt_len; +		int i; +		int status; + +		status = inw(SEEQ_STATUS); +	  	outw( lp->receive_ptr, SEEQ_DMAAR); +		outw(SEEQCMD_FIFO_READ | SEEQCMD_RX_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD); +	  	wait_for_buffer(dev); +	  	next_packet = ntohs(inw(SEEQ_BUFFER)); +	  	pkt_hdr = inw(SEEQ_BUFFER); +	  	 +		if (net_debug>2) { +			printk("%s: 0x%04x recv next=0x%04x, hdr=0x%04x\n",dev->name,lp->receive_ptr,next_packet,pkt_hdr); +		} +			 +		if ((next_packet == 0) || ((pkt_hdr & SEEQPKTH_CHAIN)==0)) {	/* Read all the frames? */ +			return;							/* Done for now */ +		} +			 +		if ((pkt_hdr & SEEQPKTS_DONE)==0) +			break; +			 +		if (next_packet < lp->receive_ptr) { +			pkt_len = (next_packet + 0x10000 - ((DEFAULT_TEA+1)<<8)) - lp->receive_ptr - 4; +		} else { +			pkt_len = next_packet - lp->receive_ptr - 4; +		} +		 +		if (next_packet < ((DEFAULT_TEA+1)<<8)) {			/* is the next_packet address sane? */ +			printk("%s: recv packet ring corrupt, resetting board\n",dev->name); +			seeq8005_init(dev,1); +			return; +		} +		 +		lp->receive_ptr = next_packet; +		 +		if (net_debug>2) { +			printk("%s: recv len=0x%04x\n",dev->name,pkt_len); +		} + +		if (pkt_hdr & SEEQPKTS_ANY_ERROR) {				/* There was an error. */ +			lp->stats.rx_errors++; +			if (pkt_hdr & SEEQPKTS_SHORT) lp->stats.rx_frame_errors++; +			if (pkt_hdr & SEEQPKTS_DRIB) lp->stats.rx_frame_errors++; +			if (pkt_hdr & SEEQPKTS_OVERSIZE) lp->stats.rx_over_errors++; +			if (pkt_hdr & SEEQPKTS_CRC_ERR) lp->stats.rx_crc_errors++; +			/* skip over this packet */ +			outw( SEEQCMD_FIFO_WRITE | SEEQCMD_DMA_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD); +			outw( (lp->receive_ptr & 0xff00)>>8, SEEQ_REA); +		} else { +			/* Malloc up new buffer. */ +			struct sk_buff *skb; +			unsigned char *buf; + +			skb = dev_alloc_skb(pkt_len); +			if (skb == NULL) { +				printk("%s: Memory squeeze, dropping packet.\n", dev->name); +				lp->stats.rx_dropped++; +				break; +			} +			skb->dev = dev; +			skb_reserve(skb, 2);	/* align data on 16 byte */ +			buf = skb_put(skb,pkt_len); +			 +			insw(SEEQ_BUFFER, buf, (pkt_len + 1) >> 1); +			 +			if (net_debug>2) { +				char * p = buf; +				printk("%s: recv ",dev->name); +				for(i=0;i<14;i++) { +					printk("%02x ",*(p++)&0xff); +				} +				printk("\n"); +			} + +			skb->protocol=eth_type_trans(skb,dev); +			netif_rx(skb); +			lp->stats.rx_packets++; +		} +	} while ((--boguscount) && (pkt_hdr & SEEQPKTH_CHAIN)); + +	/* If any worth-while packets have been received, netif_rx() +	   has done a mark_bh(NET_BH) for us and will work on them +	   when we get to the bottom-half routine. */ +	return; +} + +/* The inverse routine to net_open(). */ +static int +seeq8005_close(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; + +	lp->open_time = 0; + +	dev->tbusy = 1; +	dev->start = 0; + +	/* Flush the Tx and disable Rx here. */ +	outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD); + +	free_irq(dev->irq, NULL); + +	irq2dev_map[dev->irq] = 0; + +	/* Update the statistics here. */ + +	return 0; + +} + +/* Get the current statistics.	This may be called with the card open or +   closed. */ +static struct enet_statistics * +seeq8005_get_stats(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; + +	return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. +   num_addrs == -1	Promiscuous mode, receive all packets +   num_addrs == 0	Normal mode, clear multicast list +   num_addrs > 0	Multicast mode, receive normal and MC packets, and do +			best-effort filtering. + */ +static void +set_multicast_list(struct device *dev) +{ +/* + * I _could_ do up to 6 addresses here, but won't (yet?) + */ + +#if 0 +	int ioaddr = dev->base_addr; +/* + * hmm, not even sure if my matching works _anyway_ - seem to be receiving + * _everything_ . . . + */ +  +	if (num_addrs) {			/* Enable promiscuous mode */ +		outw( (inw(SEEQ_CFG1) & ~SEEQCFG1_MATCH_MASK)| SEEQCFG1_MATCH_ALL,  SEEQ_CFG1); +		dev->flags|=IFF_PROMISC; +	} else {				/* Disable promiscuous mode, use normal mode */ +		outw( (inw(SEEQ_CFG1) & ~SEEQCFG1_MATCH_MASK)| SEEQCFG1_MATCH_BROAD, SEEQ_CFG1); +	} +#endif +} + +void seeq8005_init(struct device *dev, int startp) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int i; +	 +	outw(SEEQCFG2_RESET, SEEQ_CFG2);	/* reset device */ +	SLOW_DOWN_IO;				/* have to wait 4us after a reset - should be fixed */ +	SLOW_DOWN_IO; +	SLOW_DOWN_IO; +	SLOW_DOWN_IO; +	 +	outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD); +	outw( 0, SEEQ_DMAAR);			/* load start address into both low and high byte */ +/*	wait_for_buffer(dev); */		/* I think that you only need a wait for memory buffer */ +	outw( SEEQCFG1_BUFFER_MAC0, SEEQ_CFG1); +	 +	for(i=0;i<6;i++) {			/* set Station address */ +		outb(dev->dev_addr[i], SEEQ_BUFFER); +		SLOW_DOWN_IO; +	} +	 +	outw( SEEQCFG1_BUFFER_TEA, SEEQ_CFG1);	/* set xmit end area pointer to 16K */ +	outb( DEFAULT_TEA, SEEQ_BUFFER);	/* this gives us 16K of send buffer and 48K of recv buffer */ +	 +	lp->receive_ptr = (DEFAULT_TEA+1)<<8;	/* so we can find our packet_header */ +	outw( lp->receive_ptr, SEEQ_RPR);	/* Receive Pointer Register is set to recv buffer memory */ +	 +	outw( 0x00ff, SEEQ_REA);		/* Receive Area End */ + +	if (net_debug>4) { +		printk("%s: SA0 = ",dev->name); + +		outw( SEEQCMD_FIFO_READ | SEEQCMD_SET_ALL_OFF, SEEQ_CMD); +		outw( 0, SEEQ_DMAAR); +		outw( SEEQCFG1_BUFFER_MAC0, SEEQ_CFG1); +		 +		for(i=0;i<6;i++) { +			printk("%02x ",inb(SEEQ_BUFFER)); +		} +		printk("\n"); +	} +	 +	outw( SEEQCFG1_MAC0_EN | SEEQCFG1_MATCH_BROAD | SEEQCFG1_BUFFER_BUFFER, SEEQ_CFG1); +	outw( SEEQCFG2_AUTO_REA | SEEQCFG2_CTRLO, SEEQ_CFG2); +	outw( SEEQCMD_SET_RX_ON | SEEQCMD_TX_INT_EN | SEEQCMD_RX_INT_EN, SEEQ_CMD); + +	if (net_debug>4) { +		int old_cfg1; +		old_cfg1 = inw(SEEQ_CFG1); +		printk("%s: stat = 0x%04x\n",dev->name,inw(SEEQ_STATUS)); +		printk("%s: cfg1 = 0x%04x\n",dev->name,old_cfg1); +		printk("%s: cfg2 = 0x%04x\n",dev->name,inw(SEEQ_CFG2)); +		printk("%s: raer = 0x%04x\n",dev->name,inw(SEEQ_REA)); +		printk("%s: dmaar= 0x%04x\n",dev->name,inw(SEEQ_DMAAR)); +		 +	} +}	 + + +void hardware_send_packet(struct device * dev, char *buf, int length) +{ +	int ioaddr = dev->base_addr; +	int status = inw(SEEQ_STATUS); +	int transmit_ptr = 0; +	int tmp; + +	if (net_debug>4) { +		printk("%s: send 0x%04x\n",dev->name,length); +	} +	 +	/* Set FIFO to writemode and set packet-buffer address */ +	outw( SEEQCMD_FIFO_WRITE | (status & SEEQCMD_INT_MASK), SEEQ_CMD); +	outw( transmit_ptr, SEEQ_DMAAR); +	 +	/* output SEEQ Packet header barfage */ +	outw( htons(length + 4), SEEQ_BUFFER); +	outw( SEEQPKTH_XMIT | SEEQPKTH_DATA_FOLLOWS | SEEQPKTH_XMIT_INT_EN, SEEQ_BUFFER ); +	 +	/* blat the buffer */ +	outsw( SEEQ_BUFFER, buf, (length +1) >> 1); +	/* paranoia !! */ +	outw( 0, SEEQ_BUFFER); +	outw( 0, SEEQ_BUFFER); +	 +	/* set address of start of transmit chain */ +	outw( transmit_ptr, SEEQ_TPR); +	 +	/* drain FIFO */ +	tmp = jiffies; +	while ( (((status=inw(SEEQ_STATUS)) & SEEQSTAT_FIFO_EMPTY) == 0) && (jiffies < tmp + HZ)) +		mb(); +	 +	/* doit ! */ +	outw( SEEQCMD_WINDOW_INT_ACK | SEEQCMD_SET_TX_ON | (status & SEEQCMD_INT_MASK), SEEQ_CMD); +	 +} + + +/* + * wait_for_buffer + * + * This routine waits for the SEEQ chip to assert that the FIFO is ready + * by checking for a window interrupt, and then clearing it + */ +inline void wait_for_buffer(struct device * dev) +{ +	int ioaddr = dev->base_addr; +	int tmp; +	int status; +	 +	tmp = jiffies + HZ; +	while ( ( ((status=inw(SEEQ_STATUS)) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && jiffies < tmp) +		mb(); +		 +	if ( (status & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT) +		outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD); +} +	 + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c" + *  version-control: t + *  kept-new-versions: 5 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/seeq8005.h b/linux/src/drivers/net/seeq8005.h new file mode 100644 index 0000000..809ba6d --- /dev/null +++ b/linux/src/drivers/net/seeq8005.h @@ -0,0 +1,156 @@ +/*  + * defines, etc for the seeq8005 + */ +  +/* + * This file is distributed under GPL. + * + * This style and layout of this file is also copied + * from many of the other linux network device drivers. + */ + +/* The number of low I/O ports used by the ethercard. */ +#define SEEQ8005_IO_EXTENT	16 + +#define SEEQ_B		(ioaddr) + +#define	SEEQ_CMD	(SEEQ_B)		/* Write only */ +#define	SEEQ_STATUS	(SEEQ_B)		/* Read only */ +#define SEEQ_CFG1	(SEEQ_B + 2) +#define SEEQ_CFG2	(SEEQ_B + 4) +#define	SEEQ_REA	(SEEQ_B + 6)		/* Receive End Area Register */ +#define SEEQ_RPR	(SEEQ_B + 10)		/* Receive Pointer Register */ +#define	SEEQ_TPR	(SEEQ_B + 12)		/* Transmit Pointer Register */ +#define	SEEQ_DMAAR	(SEEQ_B + 14)		/* DMA Address Register */ +#define SEEQ_BUFFER	(SEEQ_B + 8)		/* Buffer Window Register */ + +#define	DEFAULT_TEA	(0x3f) + +#define SEEQCMD_DMA_INT_EN	(0x0001)	/* DMA Interrupt Enable */ +#define SEEQCMD_RX_INT_EN	(0x0002)	/* Receive Interrupt Enable */ +#define SEEQCMD_TX_INT_EN	(0x0004)	/* Transmit Interrupt Enable */ +#define SEEQCMD_WINDOW_INT_EN	(0x0008)	/* What the hell is this for?? */ +#define SEEQCMD_INT_MASK	(0x000f) + +#define SEEQCMD_DMA_INT_ACK	(0x0010)	/* DMA ack */ +#define SEEQCMD_RX_INT_ACK	(0x0020) +#define SEEQCMD_TX_INT_ACK	(0x0040) +#define	SEEQCMD_WINDOW_INT_ACK	(0x0080) +#define SEEQCMD_ACK_ALL		(0x00f0) + +#define SEEQCMD_SET_DMA_ON	(0x0100)	/* Enables DMA Request logic */ +#define SEEQCMD_SET_RX_ON	(0x0200)	/* Enables Packet RX */ +#define SEEQCMD_SET_TX_ON	(0x0400)	/* Starts TX run */ +#define SEEQCMD_SET_DMA_OFF	(0x0800) +#define SEEQCMD_SET_RX_OFF	(0x1000) +#define SEEQCMD_SET_TX_OFF	(0x2000) +#define SEEQCMD_SET_ALL_OFF	(0x3800)	/* set all logic off */ + +#define SEEQCMD_FIFO_READ	(0x4000)	/* Set FIFO to read mode (read from Buffer) */ +#define SEEQCMD_FIFO_WRITE	(0x8000)	/* Set FIFO to write mode */ + +#define SEEQSTAT_DMA_INT_EN	(0x0001)	/* Status of interrupt enable */ +#define SEEQSTAT_RX_INT_EN	(0x0002) +#define SEEQSTAT_TX_INT_EN	(0x0004) +#define SEEQSTAT_WINDOW_INT_EN	(0x0008) + +#define	SEEQSTAT_DMA_INT	(0x0010)	/* Interrupt flagged */ +#define SEEQSTAT_RX_INT		(0x0020) +#define SEEQSTAT_TX_INT		(0x0040) +#define	SEEQSTAT_WINDOW_INT	(0x0080) +#define SEEQSTAT_ANY_INT	(0x00f0) + +#define SEEQSTAT_DMA_ON		(0x0100)	/* DMA logic on */ +#define SEEQSTAT_RX_ON		(0x0200)	/* Packet RX on */ +#define SEEQSTAT_TX_ON		(0x0400)	/* TX running */ + +#define SEEQSTAT_FIFO_FULL	(0x2000) +#define SEEQSTAT_FIFO_EMPTY	(0x4000) +#define SEEQSTAT_FIFO_DIR	(0x8000)	/* 1=read, 0=write */ + +#define SEEQCFG1_BUFFER_MASK	(0x000f)	/* define what maps into the BUFFER register */ +#define SEEQCFG1_BUFFER_MAC0	(0x0000)	/* MAC station addresses 0-5 */ +#define SEEQCFG1_BUFFER_MAC1	(0x0001) +#define SEEQCFG1_BUFFER_MAC2	(0x0002) +#define SEEQCFG1_BUFFER_MAC3	(0x0003) +#define SEEQCFG1_BUFFER_MAC4	(0x0004) +#define SEEQCFG1_BUFFER_MAC5	(0x0005) +#define SEEQCFG1_BUFFER_PROM	(0x0006)	/* The Address/CFG PROM */ +#define SEEQCFG1_BUFFER_TEA	(0x0007)	/* Transmit end area */ +#define SEEQCFG1_BUFFER_BUFFER	(0x0008)	/* Packet buffer memory */ +#define SEEQCFG1_BUFFER_INT_VEC	(0x0009)	/* Interrupt Vector */ + +#define SEEQCFG1_DMA_INTVL_MASK	(0x0030) +#define SEEQCFG1_DMA_CONT	(0x0000) +#define SEEQCFG1_DMA_800ns	(0x0010) +#define SEEQCFG1_DMA_1600ns	(0x0020) +#define SEEQCFG1_DMA_3200ns	(0x0030) + +#define SEEQCFG1_DMA_LEN_MASK	(0x00c0) +#define SEEQCFG1_DMA_LEN1	(0x0000) +#define SEEQCFG1_DMA_LEN2	(0x0040) +#define SEEQCFG1_DMA_LEN4	(0x0080) +#define SEEQCFG1_DMA_LEN8	(0x00c0) + +#define SEEQCFG1_MAC_MASK	(0x3f00)	/* Dis/enable bits for MAC addresses */ +#define SEEQCFG1_MAC0_EN	(0x0100) +#define SEEQCFG1_MAC1_EN	(0x0200) +#define SEEQCFG1_MAC2_EN	(0x0400) +#define SEEQCFG1_MAC3_EN	(0x0800) +#define	SEEQCFG1_MAC4_EN	(0x1000) +#define SEEQCFG1_MAC5_EN	(0x2000) + +#define	SEEQCFG1_MATCH_MASK	(0xc000)	/* Packet matching logic cfg bits */ +#define SEEQCFG1_MATCH_SPECIFIC	(0x0000)	/* only matching MAC addresses */ +#define SEEQCFG1_MATCH_BROAD	(0x4000)	/* matching and broadcast addresses */ +#define SEEQCFG1_MATCH_MULTI	(0x8000)	/* matching, broadcast and multicast */ +#define SEEQCFG1_MATCH_ALL	(0xc000)	/* Promiscuous mode */ + +#define SEEQCFG1_DEFAULT	(SEEQCFG1_BUFFER_BUFFER | SEEQCFG1_MAC0_EN | SEEQCFG1_MATCH_BROAD) + +#define SEEQCFG2_BYTE_SWAP	(0x0001)	/* 0=Intel byte-order */ +#define SEEQCFG2_AUTO_REA	(0x0002)	/* if set, Receive End Area will be updated when reading from Buffer */ + +#define SEEQCFG2_CRC_ERR_EN	(0x0008)	/* enables receiving of packets with CRC errors */ +#define SEEQCFG2_DRIBBLE_EN	(0x0010)	/* enables receiving of non-aligned packets */ +#define SEEQCFG2_SHORT_EN	(0x0020)	/* enables receiving of short packets */ + +#define	SEEQCFG2_SLOTSEL	(0x0040)	/* 0= standard IEEE802.3, 1= smaller,faster, non-standard */ +#define SEEQCFG2_NO_PREAM	(0x0080)	/* 1= user supplies Xmit preamble bytes */ +#define SEEQCFG2_ADDR_LEN	(0x0100)	/* 1= 2byte addresses */ +#define SEEQCFG2_REC_CRC	(0x0200)	/* 0= received packets will have CRC stripped from them */ +#define SEEQCFG2_XMIT_NO_CRC	(0x0400)	/* don't xmit CRC with each packet (user supplies it) */ +#define SEEQCFG2_LOOPBACK	(0x0800) +#define SEEQCFG2_CTRLO		(0x1000) +#define SEEQCFG2_RESET		(0x8000)	/* software Hard-reset bit */ + +struct seeq_pkt_hdr { +	unsigned short	next;			/* address of next packet header */ +	unsigned char	babble_int:1,		/* enable int on >1514 byte packet */ +			coll_int:1,		/* enable int on collision */ +			coll_16_int:1,		/* enable int on >15 collision */ +			xmit_int:1,		/* enable int on success (or xmit with <15 collision) */ +			unused:1, +			data_follows:1,		/* if not set, process this as a header and pointer only */ +			chain_cont:1,		/* if set, more headers in chain 		only cmd bit valid in recv header */ +			xmit_recv:1;		/* if set, a xmit packet, else a receive packet.*/ +	unsigned char	status; +}; + +#define SEEQPKTH_BAB_INT_EN	(0x01)		/* xmit only */ +#define SEEQPKTH_COL_INT_EN	(0x02)		/* xmit only */ +#define SEEQPKTH_COL16_INT_EN	(0x04)		/* xmit only */ +#define SEEQPKTH_XMIT_INT_EN	(0x08)		/* xmit only */ +#define SEEQPKTH_DATA_FOLLOWS	(0x20)		/* supposedly in xmit only */ +#define SEEQPKTH_CHAIN		(0x40)		/* more headers follow */ +#define SEEQPKTH_XMIT		(0x80) + +#define SEEQPKTS_BABBLE		(0x0100)	/* xmit only */ +#define SEEQPKTS_OVERSIZE	(0x0100)	/* recv only */ +#define SEEQPKTS_COLLISION	(0x0200)	/* xmit only */ +#define SEEQPKTS_CRC_ERR	(0x0200)	/* recv only */ +#define SEEQPKTS_COLL16		(0x0400)	/* xmit only */ +#define SEEQPKTS_DRIB		(0x0400)	/* recv only */ +#define SEEQPKTS_SHORT		(0x0800)	/* recv only */ +#define SEEQPKTS_DONE		(0x8000) +#define SEEQPKTS_ANY_ERROR	(0x0f00) diff --git a/linux/src/drivers/net/sis900.c b/linux/src/drivers/net/sis900.c new file mode 100644 index 0000000..d9e5f63 --- /dev/null +++ b/linux/src/drivers/net/sis900.c @@ -0,0 +1,1803 @@ +/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux. +   Copyright 1999 Silicon Integrated System Corporation +   Revision:	1.06.11 Apr. 30 2002 + +   Modified from the driver which is originally written by Donald Becker. + +   This software may be used and distributed according to the terms +   of the GNU Public License (GPL), incorporated herein by reference. +   Drivers based on this skeleton fall under the GPL and must retain +   the authorship (implicit copyright) notice. + +   References: +   SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, +   preliminary Rev. 1.0 Jan. 14, 1998 +   SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, +   preliminary Rev. 1.0 Nov. 10, 1998 +   SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, +   preliminary Rev. 1.0 Jan. 18, 1998 +   http://www.sis.com.tw/support/databook.htm + +   Rev 1.06.11 Apr. 25 2002 Mufasa Yang (mufasa@sis.com.tw) added SiS962 support +   Rev 1.06.10 Dec. 18 2001 Hui-Fen Hsu workaround for EDB & RTL8201 PHY +   Rev 1.06.09 Sep. 28 2001 Hui-Fen Hsu update for 630ET & workaround for ICS1893 PHY +   Rev 1.06.08 Mar.  2 2001 Hui-Fen Hsu (hfhsu@sis.com.tw) some bug fix & 635M/B support +   Rev 1.06.07 Jan.  8 2001 Lei-Chun Chang added RTL8201 PHY support +   Rev 1.06.06 Sep.  6 2000 Lei-Chun Chang added ICS1893 PHY support +   Rev 1.06.05 Aug. 22 2000 Lei-Chun Chang (lcchang@sis.com.tw) modified 630E equalier workaroung rule +   Rev 1.06.03 Dec. 23 1999 Ollie Lho Third release +   Rev 1.06.02 Nov. 23 1999 Ollie Lho bug in mac probing fixed +   Rev 1.06.01 Nov. 16 1999 Ollie Lho CRC calculation provide by Joseph Zbiciak (im14u2c@primenet.com) +   Rev 1.06 Nov. 4 1999 Ollie Lho (ollie@sis.com.tw) Second release +   Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx +   Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support +   Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release +*/ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/malloc.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/bios32.h> +#include <linux/compatmac.h> + +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>	/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> +#include <linux/delay.h> +#include <asm/types.h> +#include "sis900.h" + + +#if LINUX_VERSION_CODE < 0x20159 +#define dev_free_skb(skb) dev_kfree_skb (skb, FREE_WRITE); +#else  /* Grrr, incompatible changes should change the name. */ +#define dev_free_skb(skb) dev_kfree_skb(skb); +#endif + +static const char *version = +"sis900.c: modified v1.06.11  4/30/2002"; + +static int max_interrupt_work = 20; +static int multicast_filter_limit = 128; + +#define sis900_debug debug +static int sis900_debug = 0; + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (4*HZ) + +enum pci_flags_bit { +  PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, +  PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3, +}; + +struct mac_chip_info { +  const char *name; +  u16	vendor_id, device_id, flags; +  int	io_size; +  struct device *(*probe) (struct mac_chip_info *mac, long ioaddr, int irq, +			   int pci_index,  unsigned char pci_device_fn, unsigned char pci_bus, struct device * net_dev); +}; +static struct device * sis900_mac_probe (struct mac_chip_info * mac, long ioaddr, int irq, +					 int pci_index,  unsigned char pci_device_fn, +					 unsigned char pci_bus, struct device * net_dev); +static struct mac_chip_info  mac_chip_table[] = { +  { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900, +    PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe}, +  { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016, +    PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE, sis900_mac_probe}, +  {0,},					       /* 0 terminatted list. */ +}; + +static void sis900_read_mode(struct device *net_dev, int *speed, int *duplex); + +static struct mii_chip_info { +  const char * name; +  u16 phy_id0; +  u16 phy_id1; +  u8  phy_types; +#define	HOME 	0x0001 +#define LAN	0x0002 +#define MIX	0x0003 +} mii_chip_table[] = { +  { "SiS 900 Internal MII PHY", 		0x001d, 0x8000, LAN }, +  { "SiS 7014 Physical Layer Solution", 	0x0016, 0xf830, LAN }, +  { "AMD 79C901 10BASE-T PHY",  		0x0000, 0x6B70, LAN }, +  { "AMD 79C901 HomePNA PHY",		0x0000, 0x6B90, HOME}, +  { "ICS LAN PHY",			0x0015, 0xF440, LAN }, +  { "NS  83851 PHY",			0x2000, 0x5C20, MIX }, +  { "Realtek RTL8201 PHY",		0x0000, 0x8200, LAN }, +  {0,}, +}; + +struct mii_phy { +  struct mii_phy * next; +  int phy_addr; +  u16 phy_id0; +  u16 phy_id1; +  u16 status; +  u8  phy_types; +}; + +typedef struct _BufferDesc { +  u32	link; +  u32	cmdsts; +  u32	bufptr; +} BufferDesc; + +struct sis900_private { +  struct device *next_module; +  struct enet_statistics stats; + +  /*	struct pci_dev * pci_dev;*/ +  unsigned char pci_bus; +  unsigned char pci_device_fn; +  int pci_index; + +  struct mac_chip_info * mac; +  struct mii_phy * mii; +  struct mii_phy * first_mii; /* record the first mii structure */ +  unsigned int cur_phy; + +  struct timer_list timer; /* Link status detection timer. */ +  u8     autong_complete; /* 1: auto-negotiate complete  */ + +  unsigned int cur_rx, dirty_rx;	/* producer/comsumer pointers for Tx/Rx ring */ +  unsigned int cur_tx, dirty_tx; + +  /* The saved address of a sent/receive-in-place packet buffer */ +  struct sk_buff *tx_skbuff[NUM_TX_DESC]; +  struct sk_buff *rx_skbuff[NUM_RX_DESC]; +  BufferDesc tx_ring[NUM_TX_DESC]; +  BufferDesc rx_ring[NUM_RX_DESC]; + +  unsigned int tx_full;		/* The Tx queue is full.    */ +  int LinkOn; +}; + +#ifdef MODULE +#if LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("Jim Huang <cmhuang@sis.com.tw>, Ollie Lho <ollie@sis.com.tw>"); +MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); +#endif +#endif + +static int sis900_open(struct device *net_dev); +static int sis900_mii_probe (unsigned char pci_bus, unsigned char pci_device_fn, struct device * net_dev); +static void sis900_init_rxfilter (struct device * net_dev); +static u16 read_eeprom(long ioaddr, int location); +static u16 mdio_read(struct device *net_dev, int phy_id, int location); +static void mdio_write(struct device *net_dev, int phy_id, int location, int val); +static void sis900_timer(unsigned long data); +static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy); +static void sis900_tx_timeout(struct device *net_dev); +static void sis900_init_tx_ring(struct device *net_dev); +static void sis900_init_rx_ring(struct device *net_dev); +static int sis900_start_xmit(struct sk_buff *skb, struct device *net_dev); +static int sis900_rx(struct device *net_dev); +static void sis900_finish_xmit (struct device *net_dev); +static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int sis900_close(struct device *net_dev); +static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd); +static struct enet_statistics *sis900_get_stats(struct device *net_dev); +static u16 sis900_compute_hashtable_index(u8 *addr, u8 revision); +static void set_rx_mode(struct device *net_dev); +static void sis900_reset(struct device *net_dev); +static void sis630_set_eq(struct device *net_dev, u8 revision); +static u16 sis900_default_phy(struct device * net_dev); +static void sis900_set_capability( struct device *net_dev ,struct mii_phy *phy); +static u16 sis900_reset_phy(struct device *net_dev, int phy_addr); +static void sis900_auto_negotiate(struct device *net_dev, int phy_addr); +static void sis900_set_mode (long ioaddr, int speed, int duplex); + +/* A list of all installed SiS900 devices, for removing the driver module. */ +static struct device *root_sis900_dev = NULL; + +#ifdef HAVE_DEVLIST +struct netdev_entry netcard_drv = +  {"sis900", sis900_probe, SIS900_TOTAL_SIZE, NULL}; +#endif + +/* walk through every ethernet PCI devices to see if some of them are matched with our card list*/ +int sis900_probe (struct device * net_dev) +{ +  int found = 0; +  int pci_index = 0; +  unsigned char pci_bus, pci_device_fn; +  long ioaddr; +  int irq; + +  if (!pcibios_present()) +    return -ENODEV; + +  for (; pci_index < 0xff; pci_index++) +    { +      u16 vendor, device, pci_command; +      struct mac_chip_info *mac; + +      if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, +			      &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) +	   break; + +      pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); +      pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); + +      for (mac = mac_chip_table; mac->vendor_id; mac++) +      { +	  if (vendor == mac->vendor_id && device == mac->device_id) break; +       } + +      /* pci_dev does not match any of our cards */ +      if (mac->vendor_id == 0) +	continue; + +      { +	u32 pci_ioaddr; +	u8 pci_irq_line; + +	pcibios_read_config_byte(pci_bus, pci_device_fn, +				 PCI_INTERRUPT_LINE, &pci_irq_line); +	pcibios_read_config_dword(pci_bus, pci_device_fn, +				  PCI_BASE_ADDRESS_0, &pci_ioaddr); +	ioaddr = pci_ioaddr & ~3; +	irq = pci_irq_line; + +	if ((mac->flags & PCI_USES_IO) && +	    check_region (pci_ioaddr, mac->io_size)) +	  continue; +	 +	pcibios_read_config_word(pci_bus, pci_device_fn, +				 PCI_COMMAND, &pci_command); +	 +	{ +	  u8 lat; +	 +	  pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &lat); +	  if (lat < 16) { +	    printk("PCI: Increasing latency timer of device %02x:%02x to 64\n", +		   pci_bus, pci_device_fn); +	    pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, 64); +	  } +	} +	net_dev = mac->probe (mac, ioaddr, irq, pci_index, pci_device_fn, pci_bus, net_dev); +	if (net_dev != NULL) +	  { +	    found++; +	  } +	net_dev = NULL; +      } +    } +  return found ? 0 : -ENODEV; + +} + +/* older SiS900 and friends, use EEPROM to store MAC address */ +static int +sis900_get_mac_addr(long ioaddr, struct device *net_dev) +{ +  u16 signature; +  int i; + +  /* check to see if we have sane EEPROM */ +  signature = (u16) read_eeprom(ioaddr, EEPROMSignature); +  if (signature == 0xffff || signature == 0x0000) { + 	printk (KERN_INFO "%s: Error EERPOM read %x\n", +	 	net_dev->name, signature);	 + 	return 0; +  } + +  /* get MAC address from EEPROM */ +  for (i = 0; i < 3; i++) +	  ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr); +  return 1; +} + +/* SiS630E model, use APC CMOS RAM to store MAC address */ +static int sis630e_get_mac_addr(long ioaddr, int pci_index, struct device *net_dev) +{ +  u8 reg; +  int i; +  u8 pci_bus, pci_dfn; +  int not_found; + +  not_found = pcibios_find_device(0x1039, 0x0008, +				  pci_index, +				  &pci_bus, +				  &pci_dfn); +  if (not_found) { +    printk("%s: Can not find ISA bridge\n", net_dev->name); +    return 0; +  } +  pcibios_read_config_byte(pci_bus, pci_dfn, 0x48, ®); +  pcibios_write_config_byte(pci_bus, pci_dfn, 0x48, reg | 0x40); + +  for (i = 0; i < 6; i++) { +    outb(0x09 + i, 0x70); +    ((u8 *)(net_dev->dev_addr))[i] = inb(0x71); +  } +  pcibios_write_config_byte(pci_bus, pci_dfn, 0x48, reg & ~0x40); + +  return 1; +} + +/* 635 model : set Mac reload bit and get mac address from rfdr */ +static int sis635_get_mac_addr(struct device *net_dev) +{ +  long ioaddr = net_dev->base_addr; +  u32 rfcrSave; +  u32 i; + +  rfcrSave = inl(rfcr + ioaddr); + +  outl(rfcrSave | RELOAD, ioaddr + cr); +  outl(0, ioaddr + cr); + +  /* disable packet filtering before setting filter */ +  outl(rfcrSave & ~RFEN, rfcr + ioaddr); + +  /* load MAC addr to filter data register */ +  for (i = 0 ; i < 3 ; i++) { +    outl((i << RFADDR_shift), ioaddr + rfcr); +    *( ((u16 *)net_dev->dev_addr) + i) = inw(ioaddr + rfdr); +  } + +  /* enable packet filitering */ +  outl(rfcrSave | RFEN, rfcr + ioaddr); + +  return 1; +} + + +/** + *	sis962_get_mac_addr: - Get MAC address for SiS962 model + *	@pci_dev: the sis900 pci device + *	@net_dev: the net device to get address for + * + *	SiS962 model, use EEPROM to store MAC address. And EEPROM is shared by + *	LAN and 1394. When access EEPROM, send EEREQ signal to hardware first + *	and wait for EEGNT. If EEGNT is ON, EEPROM is permitted to be access + *	by LAN, otherwise is not. After MAC address is read from EEPROM, send + *	EEDONE signal to refuse EEPROM access by LAN. + *	MAC address is read into @net_dev->dev_addr. + */ + +static int sis962_get_mac_addr(struct device *net_dev) +{ +  long ioaddr = net_dev->base_addr; +  long ee_addr = ioaddr + mear; +  u32 waittime = 0; +  int i; +	 +  outl(EEREQ, ee_addr); +  while(waittime < 2000) { +    	if(inl(ee_addr) & EEGNT) { +		/* get MAC address from EEPROM */ +                for (i = 0; i < 3; i++) +                        ((u16 *)(net_dev->dev_addr))[i] = read_eeprom(ioaddr, i+EEPROMMACAddr); +                outl(EEDONE, ee_addr); +                return 1; +        } else { +    		udelay(1);	 +    		waittime ++; +    	} +  } +  outl(EEDONE, ee_addr); +  return 0; +} + +struct device * +sis900_mac_probe (struct mac_chip_info *mac, long ioaddr, int irq, int pci_index, +		  unsigned char pci_device_fn, unsigned char pci_bus, struct device * net_dev) +{ +  struct sis900_private *sis_priv; +  static int did_version = 0; + +  u8 revision; +  int i, ret = 0; + +  if (did_version++ == 0) +    printk(KERN_INFO "%s\n", version); + +  if ((net_dev = init_etherdev(net_dev, 0)) == NULL) +    return NULL; + +  if ((net_dev->priv = kmalloc(sizeof(struct sis900_private), GFP_KERNEL)) == NULL) { +    unregister_netdev(net_dev); +    return NULL; +  } + +  sis_priv = net_dev->priv; +  memset(sis_priv, 0, sizeof(struct sis900_private)); + +  /* We do a request_region() to register /proc/ioports info. */ +  request_region(ioaddr, mac->io_size, net_dev->name); +  net_dev->base_addr = ioaddr; +  net_dev->irq = irq; + +  sis_priv->mac = mac; +  sis_priv->pci_bus = pci_bus; +  sis_priv->pci_device_fn = pci_device_fn; +  sis_priv->pci_index = pci_index; + +  pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &revision); + +  if ( revision == SIS630E_900_REV ) +    ret = sis630e_get_mac_addr(ioaddr, pci_index, net_dev); +  else if ((revision > 0x81) && (revision <= 0x90)) +    ret = sis635_get_mac_addr(net_dev); +  else if (revision == SIS962_900_REV) +    ret = sis962_get_mac_addr(net_dev); +  else +    ret = sis900_get_mac_addr(ioaddr, net_dev); + +  if (ret == 0) { +    unregister_netdev(net_dev); +    return NULL; +  } + +  /* print some information about our NIC */ +  printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, mac->name, +	 ioaddr, irq); +  for (i = 0; i < 5; i++) +    printk("%2.2x:", (u8)net_dev->dev_addr[i]); +  printk("%2.2x.\n", net_dev->dev_addr[i]); + +  /* 630ET : set the mii access mode as software-mode */ +  if (revision == SIS630ET_900_REV) +    outl(ACCESSMODE | inl(ioaddr + cr), ioaddr + cr); + +  /* probe for mii transceiver */ +  if (sis900_mii_probe(pci_bus, pci_device_fn, net_dev) == 0) { +    unregister_netdev(net_dev); +    kfree(sis_priv); +    release_region(ioaddr, mac->io_size); +    return NULL; +  } + +  sis_priv->next_module = root_sis900_dev; +  root_sis900_dev = net_dev; + +  /* The SiS900-specific entries in the device structure. */ +  net_dev->open = &sis900_open; +  net_dev->hard_start_xmit = &sis900_start_xmit; +  net_dev->stop = &sis900_close; +  net_dev->get_stats = &sis900_get_stats; +  net_dev->set_multicast_list = &set_rx_mode; +  net_dev->do_ioctl = &mii_ioctl; + +  return net_dev; +} + +/* sis900_mii_probe: - Probe MII PHY for sis900 */ +static int sis900_mii_probe (unsigned char pci_bus, unsigned char pci_device_fn, struct device * net_dev) +{ +  struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv; +  u16 poll_bit = MII_STAT_LINK, status = 0; +  unsigned int timeout = jiffies + 5 * HZ; +  int phy_addr; +  u8 revision; + +  sis_priv->mii = NULL; + +  /* search for total of 32 possible mii phy addresses */ +  for (phy_addr = 0; phy_addr < 32; phy_addr++) {	 +    struct mii_phy * mii_phy = NULL; +    u16 mii_status; +    int i; + +    for(i=0; i<2; i++) +      mii_status = mdio_read(net_dev, phy_addr, MII_STATUS); + +    if (mii_status == 0xffff || mii_status == 0x0000) +      /* the mii is not accessable, try next one */ +      continue; +		 +    if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) == NULL) { +      printk(KERN_INFO "Cannot allocate mem for struct mii_phy\n"); +      return 0; +    } +		 +    mii_phy->phy_id0 = mdio_read(net_dev, phy_addr, MII_PHY_ID0); +    mii_phy->phy_id1 = mdio_read(net_dev, phy_addr, MII_PHY_ID1);		 +    mii_phy->phy_addr = phy_addr; +    mii_phy->status = mii_status; +    mii_phy->next = sis_priv->mii; +    sis_priv->mii = mii_phy; +    sis_priv->first_mii = mii_phy; + +    for (i=0; mii_chip_table[i].phy_id1; i++) +      if ( ( mii_phy->phy_id0 == mii_chip_table[i].phy_id0 ) && +	   ( (mii_phy->phy_id1 & 0xFFF0) == mii_chip_table[i].phy_id1 )){ + +	mii_phy->phy_types = mii_chip_table[i].phy_types; +	if(mii_chip_table[i].phy_types == MIX) +	  mii_phy->phy_types = +	    (mii_status & (MII_STAT_CAN_TX_FDX | MII_STAT_CAN_TX))?LAN:HOME; +	printk(KERN_INFO "%s: %s transceiver found at address %d.\n", +	       net_dev->name, mii_chip_table[i].name, phy_addr); +	break; +      } + +    if( !mii_chip_table[i].phy_id1 ) +      printk(KERN_INFO "%s: Unknown PHY transceiver found at address %d.\n", +	     net_dev->name, phy_addr); +  } +	 +  if (sis_priv->mii == NULL) { +    printk(KERN_INFO "%s: No MII transceivers found!\n", +	   net_dev->name); +    return 0; +  } + +  /* Slect Default PHY to put in sis_priv->mii & sis_priv->cur_phy */ +  sis_priv->mii = NULL; +  sis900_default_phy( net_dev ); + +  /* Reset PHY if default PHY is internal sis900 */ +  if( (sis_priv->mii->phy_id0 == 0x001D) && +      ( (sis_priv->mii->phy_id1&0xFFF0) == 0x8000) ) +    status = sis900_reset_phy( net_dev,  sis_priv->cur_phy ); + +  /* workaround for ICS1893 PHY */ +  if ((sis_priv->mii->phy_id0 == 0x0015) && +      ((sis_priv->mii->phy_id1&0xFFF0) == 0xF440)) +    mdio_write(net_dev, sis_priv->cur_phy, 0x0018, 0xD200); + +  if( status & MII_STAT_LINK ){ +    while (poll_bit) +      { +        poll_bit ^= (mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS) & poll_bit); +        if (jiffies >= timeout) +          { +            printk(KERN_WARNING "%s: reset phy and link down now\n", net_dev->name); +            return -ETIME; +          } +       } +  } +	 +	pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &revision); +	if (revision == SIS630E_900_REV) { +		/* SiS 630E has some bugs on default value of PHY registers */ +		mdio_write(net_dev, sis_priv->cur_phy, MII_ANADV, 0x05e1); +		mdio_write(net_dev, sis_priv->cur_phy, MII_CONFIG1, 0x22); +		mdio_write(net_dev, sis_priv->cur_phy, MII_CONFIG2, 0xff00); +		mdio_write(net_dev, sis_priv->cur_phy, MII_MASK, 0xffc0); +		//mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, 0x1000);	 +	} + +	if (sis_priv->mii->status & MII_STAT_LINK) +		sis_priv->LinkOn = TRUE; +	else +		sis_priv->LinkOn = FALSE; + +	return 1; +} + + +/* sis900_default_phy : Select one default PHY for sis900 mac */ +static u16 sis900_default_phy(struct device * net_dev) +{ +	struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv; + 	struct mii_phy *phy = NULL, *phy_home = NULL, *default_phy = NULL; +	u16 status; + +        for( phy=sis_priv->first_mii; phy; phy=phy->next ){ +		status = mdio_read(net_dev, phy->phy_addr, MII_STATUS); +		status = mdio_read(net_dev, phy->phy_addr, MII_STATUS); + +		/* Link ON & Not select deafalut PHY */ +		 if ( (status & MII_STAT_LINK) && !(default_phy) ) +		 	default_phy = phy; +		 else{ +			status = mdio_read(net_dev, phy->phy_addr, MII_CONTROL); +			mdio_write(net_dev, phy->phy_addr, MII_CONTROL, +				status | MII_CNTL_AUTO | MII_CNTL_ISOLATE); +			if( phy->phy_types == HOME ) +				phy_home = phy; +		 } +	} + +	if( (!default_phy) && phy_home ) +		default_phy = phy_home; +	else if(!default_phy) +		default_phy = sis_priv->first_mii; + +	if( sis_priv->mii != default_phy ){ +		sis_priv->mii = default_phy; +		sis_priv->cur_phy = default_phy->phy_addr; +		printk(KERN_INFO "%s: Using transceiver found at address %d as default\n", net_dev->name,sis_priv->cur_phy); +	} +	 +	status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL); +	status &= (~MII_CNTL_ISOLATE); + +	mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, status);	 +	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); +	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); + +	return status;	 +} + + +/* sis900_set_capability : set the media capability of network adapter */ +static void sis900_set_capability( struct device *net_dev , struct mii_phy *phy ) +{ +	u16 cap; +	u16 status; +	 +	status = mdio_read(net_dev, phy->phy_addr, MII_STATUS); +	status = mdio_read(net_dev, phy->phy_addr, MII_STATUS); +	 +	cap = MII_NWAY_CSMA_CD | +		((phy->status & MII_STAT_CAN_TX_FDX)? MII_NWAY_TX_FDX:0) | +		((phy->status & MII_STAT_CAN_TX)    ? MII_NWAY_TX:0) | +		((phy->status & MII_STAT_CAN_T_FDX) ? MII_NWAY_T_FDX:0)| +		((phy->status & MII_STAT_CAN_T)     ? MII_NWAY_T:0); + +	mdio_write( net_dev, phy->phy_addr, MII_ANADV, cap ); +} + + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay()	inl(ee_addr) + +/* Read Serial EEPROM through EEPROM Access Register, Note that location is +   in word (16 bits) unit */ +static u16 read_eeprom(long ioaddr, int location) +{ +	int i; +	u16 retval = 0; +	long ee_addr = ioaddr + mear; +	u32 read_cmd = location | EEread; + +	outl(0, ee_addr); +	eeprom_delay(); +	outl(EECS, ee_addr); +	eeprom_delay(); + +	/* Shift the read command (9) bits out. */ +	for (i = 8; i >= 0; i--) { +		u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS; +		outl(dataval, ee_addr); +		eeprom_delay(); +		outl(dataval | EECLK, ee_addr); +		eeprom_delay(); +	} +	outb(EECS, ee_addr); +	eeprom_delay(); + +	/* read the 16-bits data in */ +	for (i = 16; i > 0; i--) { +		outl(EECS, ee_addr); +		eeprom_delay(); +		outl(EECS | EECLK, ee_addr); +		eeprom_delay(); +		retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0); +		eeprom_delay(); +	} + +	/* Terminate the EEPROM access. */ +	outl(0, ee_addr); +	eeprom_delay(); +//	outl(EECLK, ee_addr); + +	return (retval); +} + +/* Read and write the MII management registers using software-generated +   serial MDIO protocol. Note that the command bits and data bits are +   send out seperately */ +#define mdio_delay()	inl(mdio_addr) + +static void mdio_idle(long mdio_addr) +{ +	outl(MDIO | MDDIR, mdio_addr); +	mdio_delay(); +	outl(MDIO | MDDIR | MDC, mdio_addr); +} + +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_reset(long mdio_addr) +{ +	int i; + +	for (i = 31; i >= 0; i--) { +		outl(MDDIR | MDIO, mdio_addr); +		mdio_delay(); +		outl(MDDIR | MDIO | MDC, mdio_addr); +		mdio_delay(); +	} +	return; +} + +static u16 mdio_read(struct device *net_dev, int phy_id, int location) +{ +	long mdio_addr = net_dev->base_addr + mear; +	int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift); +	u16 retval = 0; +	int i; + +	mdio_reset(mdio_addr); +	mdio_idle(mdio_addr); + +	for (i = 15; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; +		outl(dataval, mdio_addr); +		mdio_delay(); +		outl(dataval | MDC, mdio_addr); +		mdio_delay(); +	} + +	/* Read the 16 data bits. */ +	for (i = 16; i > 0; i--) { +		outl(0, mdio_addr); +		mdio_delay(); +		retval = (retval << 1) | ((inl(mdio_addr) & MDIO) ? 1 : 0); +		outl(MDC, mdio_addr); +		mdio_delay(); +	} +	outl(0x00, mdio_addr); + +	return retval; +} + +static void mdio_write(struct device *net_dev, int phy_id, int location, int value) +{ +	long mdio_addr = net_dev->base_addr + mear; +	int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift); +	int i; + +	mdio_reset(mdio_addr); +	mdio_idle(mdio_addr); + +	/* Shift the command bits out. */ +	for (i = 15; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDDIR | MDIO : MDDIR; +		outb(dataval, mdio_addr); +		mdio_delay(); +		outb(dataval | MDC, mdio_addr); +		mdio_delay(); +	} +	mdio_delay(); + +	/* Shift the value bits out. */ +	for (i = 15; i >= 0; i--) { +		int dataval = (value & (1 << i)) ? MDDIR | MDIO : MDDIR; +		outl(dataval, mdio_addr); +		mdio_delay(); +		outl(dataval | MDC, mdio_addr); +		mdio_delay(); +	} +	mdio_delay(); + +	/* Clear out extra bits. */ +	for (i = 2; i > 0; i--) { +		outb(0, mdio_addr); +		mdio_delay(); +		outb(MDC, mdio_addr); +		mdio_delay(); +	} +	outl(0x00, mdio_addr); + +	return; +} + +static u16 sis900_reset_phy(struct device *net_dev, int phy_addr) +{ +	int i = 0; +	u16 status; + +	while (i++ < 2) +		status = mdio_read(net_dev, phy_addr, MII_STATUS); + +	mdio_write( net_dev, phy_addr, MII_CONTROL, MII_CNTL_RESET ); +	 +	return status; +} + +static int +sis900_open(struct device *net_dev) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	long ioaddr = net_dev->base_addr; +	u8 revision; + +	/* Soft reset the chip. */ +	sis900_reset(net_dev); +	 +	/* Equalizer workaroung Rule */ +	pcibios_read_config_byte(sis_priv->pci_bus, sis_priv->pci_device_fn, PCI_CLASS_REVISION, &revision); +	sis630_set_eq(net_dev, revision); +	 +	if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) { +		return -EAGAIN; +	} + +	MOD_INC_USE_COUNT; + +	sis900_init_rxfilter(net_dev); + +	sis900_init_tx_ring(net_dev); +	sis900_init_rx_ring(net_dev); + +	set_rx_mode(net_dev); + +	net_dev->tbusy = 0; +	net_dev->interrupt = 0; +	net_dev->start = 1; + +	/* Workaround for EDB */ +	sis900_set_mode(ioaddr, HW_SPEED_10_MBPS, FDX_CAPABLE_HALF_SELECTED); + +	/* Enable all known interrupts by setting the interrupt mask. */ +	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); +	outl(RxENA | inl(ioaddr + cr), ioaddr + cr); +	outl(IE, ioaddr + ier); + +	sis900_check_mode(net_dev, sis_priv->mii); + +	/* Set the timer to switch to check for link beat and perhaps switch +	   to an alternate media type. */ +	init_timer(&sis_priv->timer); +	sis_priv->timer.expires = jiffies + HZ; +	sis_priv->timer.data = (unsigned long)net_dev; +	sis_priv->timer.function = &sis900_timer; +	add_timer(&sis_priv->timer); + +	return 0; +} + +/* set receive filter address to our MAC address */ +static void +sis900_init_rxfilter (struct device * net_dev) +{ +	long ioaddr = net_dev->base_addr; +	u32 rfcrSave; +	u32 i; + +	rfcrSave = inl(rfcr + ioaddr); + +	/* disable packet filtering before setting filter */ +	outl(rfcrSave & ~RFEN, rfcr + ioaddr); + +	/* load MAC addr to filter data register */ +	for (i = 0 ; i < 3 ; i++) { +		u32 w; + +		w = (u32) *((u16 *)(net_dev->dev_addr)+i); +		outl((i << RFADDR_shift), ioaddr + rfcr); +		outl(w, ioaddr + rfdr); + +		if (sis900_debug > 2) { +			printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n", +			       net_dev->name, i, inl(ioaddr + rfdr)); +		} +	} + +	/* enable packet filitering */ +	outl(rfcrSave | RFEN, rfcr + ioaddr); +} + +/* Initialize the Tx ring. */ +static void +sis900_init_tx_ring(struct device *net_dev) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	long ioaddr = net_dev->base_addr; +	int i; + +	sis_priv->tx_full = 0; +	sis_priv->dirty_tx = sis_priv->cur_tx = 0; + +	for (i = 0; i < NUM_TX_DESC; i++) { +		sis_priv->tx_skbuff[i] = NULL; + +		sis_priv->tx_ring[i].link = (u32) virt_to_bus(&sis_priv->tx_ring[i+1]); +		sis_priv->tx_ring[i].cmdsts = 0; +		sis_priv->tx_ring[i].bufptr = 0; +	} +	sis_priv->tx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->tx_ring[0]); + +	/* load Transmit Descriptor Register */ +	outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp); +	if (sis900_debug > 2) +		printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n", +		       net_dev->name, inl(ioaddr + txdp)); +} + +/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */ +static void +sis900_init_rx_ring(struct device *net_dev) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	long ioaddr = net_dev->base_addr; +	int i; + +	sis_priv->cur_rx = 0; +	sis_priv->dirty_rx = 0; + +	/* init RX descriptor */ +	for (i = 0; i < NUM_RX_DESC; i++) { +		sis_priv->rx_skbuff[i] = NULL; + +		sis_priv->rx_ring[i].link = (u32) virt_to_bus(&sis_priv->rx_ring[i+1]); +		sis_priv->rx_ring[i].cmdsts = 0; +		sis_priv->rx_ring[i].bufptr = 0; +	} +	sis_priv->rx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->rx_ring[0]); + +	/* allocate sock buffers */ +	for (i = 0; i < NUM_RX_DESC; i++) { +		struct sk_buff *skb; + +		if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { +			/* not enough memory for skbuff, this makes a "hole" +			   on the buffer ring, it is not clear how the +			   hardware will react to this kind of degenerated +			   buffer */ +			break; +		} +		skb->dev = net_dev; +		sis_priv->rx_skbuff[i] = skb; +		sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE; +		sis_priv->rx_ring[i].bufptr = virt_to_bus(skb->tail); +	} +	sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC); + +	/* load Receive Descriptor Register */ +	outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp); +	if (sis900_debug > 2) +		printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", +		       net_dev->name, inl(ioaddr + rxdp)); +} + +/** + *	sis630_set_eq: - set phy equalizer value for 630 LAN + *	@net_dev: the net device to set equalizer value + *	@revision: 630 LAN revision number + * + *	630E equalizer workaround rule(Cyrus Huang 08/15) + *	PHY register 14h(Test) + *	Bit 14: 0 -- Automatically dectect (default) + *		1 -- Manually set Equalizer filter + *	Bit 13: 0 -- (Default) + *		1 -- Speed up convergence of equalizer setting + *	Bit 9 : 0 -- (Default) + *		1 -- Disable Baseline Wander + *	Bit 3~7   -- Equalizer filter setting + *	Link ON: Set Bit 9, 13 to 1, Bit 14 to 0 + *	Then calculate equalizer value + *	Then set equalizer value, and set Bit 14 to 1, Bit 9 to 0 + *	Link Off:Set Bit 13 to 1, Bit 14 to 0 + *	Calculate Equalizer value: + *	When Link is ON and Bit 14 is 0, SIS900PHY will auto-dectect proper equalizer value. + *	When the equalizer is stable, this value is not a fixed value. It will be within + *	a small range(eg. 7~9). Then we get a minimum and a maximum value(eg. min=7, max=9) + *	0 <= max <= 4  --> set equalizer to max + *	5 <= max <= 14 --> set equalizer to max+1 or set equalizer to max+2 if max == min + *	max >= 15      --> set equalizer to max+5 or set equalizer to max+6 if max == min + */ + +static void sis630_set_eq(struct device *net_dev, u8 revision) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	u16 reg14h, eq_value, max_value=0, min_value=0; +	u8 host_bridge_rev; +	int i, maxcount=10; +	int not_found; +	u8 pci_bus, pci_device_fn; + +	if ( !(revision == SIS630E_900_REV || revision == SIS630EA1_900_REV || +	       revision == SIS630A_900_REV || revision ==  SIS630ET_900_REV) ) +		return; +	not_found = pcibios_find_device(SIS630_VENDOR_ID, SIS630_DEVICE_ID, +					sis_priv->pci_index, +					&pci_bus, +					&pci_device_fn); +	if (not_found) +	    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &host_bridge_rev); + +	if (sis_priv->LinkOn) { +		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); +		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (0x2200 | reg14h) & 0xBFFF); +		for (i=0; i < maxcount; i++) { +			eq_value=(0x00F8 & mdio_read(net_dev, sis_priv->cur_phy, MII_RESV)) >> 3; +			if (i == 0) +				max_value=min_value=eq_value; +			max_value=(eq_value > max_value) ? eq_value : max_value; +			min_value=(eq_value < min_value) ? eq_value : min_value; +		} +		/* 630E rule to determine the equalizer value */ +		if (revision == SIS630E_900_REV || revision == SIS630EA1_900_REV || +		    revision == SIS630ET_900_REV) { +			if (max_value < 5) +				eq_value=max_value; +			else if (max_value >= 5 && max_value < 15) +				eq_value=(max_value == min_value) ? max_value+2 : max_value+1; +			else if (max_value >= 15) +				eq_value=(max_value == min_value) ? max_value+6 : max_value+5; +		} +		/* 630B0&B1 rule to determine the equalizer value */ +		if (revision == SIS630A_900_REV && +		    (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) { +			if (max_value == 0) +				eq_value=3; +			else +				eq_value=(max_value+min_value+1)/2; +		} +		/* write equalizer value and setting */ +		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); +		reg14h=(reg14h & 0xFF07) | ((eq_value << 3) & 0x00F8); +		reg14h=(reg14h | 0x6000) & 0xFDFF; +		mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, reg14h); +	} +	else { +		reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); +		if (revision == SIS630A_900_REV && +		    (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) +			mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2200) & 0xBFFF); +		else +			mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF); +	} +	return; +} + + +/* on each timer ticks we check two things, Link Status (ON/OFF) and +   Link Mode (10/100/Full/Half) +*/ +static void sis900_timer(unsigned long data) +{ +	struct device *net_dev = (struct device *)data; +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	struct mii_phy *mii_phy = sis_priv->mii; +	static int next_tick = 5*HZ; +	u16 status; +	u8 revision; + +	if(!sis_priv->autong_complete){ +		int speed, duplex = 0; + +		sis900_read_mode(net_dev, &speed, &duplex); +		if(duplex){ +			sis900_set_mode(net_dev->base_addr, speed, duplex); +			pcibios_read_config_byte(sis_priv->pci_bus, sis_priv->pci_device_fn, PCI_CLASS_REVISION, &revision); +			sis630_set_eq(net_dev, revision); +		} +		 +		sis_priv->timer.expires = jiffies + HZ; +		add_timer(&sis_priv->timer); +		return; +	} + +	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); +	status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS); + +	/* Link OFF -> ON */ +	if ( !sis_priv->LinkOn ) { +LookForLink: +		/* Search for new PHY */ +		status = sis900_default_phy( net_dev ); +		mii_phy = sis_priv->mii; + +		if( status & MII_STAT_LINK ){ +			sis900_check_mode(net_dev, mii_phy); +			sis_priv->LinkOn = TRUE; +		} +	} +	/* Link ON -> OFF */ +	else{ +                if( !(status & MII_STAT_LINK) ){ +			sis_priv->LinkOn = FALSE; +                	printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); + +                	/* Change mode issue */ +                	if( (mii_phy->phy_id0 == 0x001D) && +                	  ( (mii_phy->phy_id1 & 0xFFF0) == 0x8000 )) +               			sis900_reset_phy( net_dev,  sis_priv->cur_phy ); + +                	pcibios_read_config_byte(sis_priv->pci_bus, sis_priv->pci_device_fn, PCI_CLASS_REVISION, &revision); +			sis630_set_eq(net_dev, revision); + +                	goto LookForLink; +                } +	} + +	sis_priv->timer.expires = jiffies + next_tick; +	add_timer(&sis_priv->timer); +} + +static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	long ioaddr = net_dev->base_addr; +	int speed, duplex; + +	if( mii_phy->phy_types == LAN  ){ +		outl( ~EXD & inl( ioaddr + cfg ), ioaddr + cfg); +		sis900_set_capability(net_dev , mii_phy); +		sis900_auto_negotiate(net_dev, sis_priv->cur_phy); +	}else{ +		outl(EXD | inl( ioaddr + cfg ), ioaddr + cfg); +		speed = HW_SPEED_HOME; +		duplex = FDX_CAPABLE_HALF_SELECTED; +		sis900_set_mode(net_dev->base_addr, speed, duplex); +		sis_priv->autong_complete = 1; +	} +} + +static void sis900_set_mode (long ioaddr, int speed, int duplex) +{ +	u32 tx_flags = 0, rx_flags = 0; + +	if( inl(ioaddr + cfg) & EDB_MASTER_EN ){ +		tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); +		rx_flags = DMA_BURST_64 << RxMXDMA_shift; +	} +	else{ +		tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); +		rx_flags = DMA_BURST_512 << RxMXDMA_shift; +	} + +	if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) { +		rx_flags |= (RxDRNT_10 << RxDRNT_shift); +		tx_flags |= (TxDRNT_10 << TxDRNT_shift); +	} +	else { +		rx_flags |= (RxDRNT_100 << RxDRNT_shift); +		tx_flags |= (TxDRNT_100 << TxDRNT_shift); +	} + +	if (duplex == FDX_CAPABLE_FULL_SELECTED) { +		tx_flags |= (TxCSI | TxHBI); +		rx_flags |= RxATX; +	} + +	outl (tx_flags, ioaddr + txcfg); +	outl (rx_flags, ioaddr + rxcfg); +} + + +static void sis900_auto_negotiate(struct device *net_dev, int phy_addr) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	int i = 0; +	u32 status; +	 +	while (i++ < 2) +		status = mdio_read(net_dev, phy_addr, MII_STATUS); + +	if (!(status & MII_STAT_LINK)){ +		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); +		sis_priv->autong_complete = 1; +		sis_priv->LinkOn = FALSE; +		return; +	} + +	/* (Re)start AutoNegotiate */ +	mdio_write(net_dev, phy_addr, MII_CONTROL, +			MII_CNTL_AUTO | MII_CNTL_RST_AUTO); +	sis_priv->autong_complete = 0; +} + + +static void sis900_read_mode(struct device *net_dev, int *speed, int *duplex) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	struct mii_phy *phy = sis_priv->mii; +	int phy_addr = sis_priv->cur_phy; +	u32 status; +	u16 autoadv, autorec; +	int i = 0; + +	while (i++ < 2) +		status = mdio_read(net_dev, phy_addr, MII_STATUS); + +	if (!(status & MII_STAT_LINK))	return; +	 +	/* AutoNegotiate completed */ +	autoadv = mdio_read(net_dev, phy_addr, MII_ANADV); +	autorec = mdio_read(net_dev, phy_addr, MII_ANLPAR); +	status = autoadv & autorec; + +	*speed = HW_SPEED_10_MBPS; +	*duplex = FDX_CAPABLE_HALF_SELECTED; + +	if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX)) +		*speed = HW_SPEED_100_MBPS; +	if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) +		*duplex = FDX_CAPABLE_FULL_SELECTED; + +	sis_priv->autong_complete = 1; + +	/* Workaround for Realtek RTL8201 PHY issue */ +	if((phy->phy_id0 == 0x0000) && ((phy->phy_id1 & 0xFFF0) == 0x8200)){ +		if(mdio_read(net_dev, phy_addr, MII_CONTROL) & MII_CNTL_FDX) +			*duplex = FDX_CAPABLE_FULL_SELECTED; +		if(mdio_read(net_dev, phy_addr, 0x0019) & 0x01) +			*speed = HW_SPEED_100_MBPS; +	} +			 +	printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", +	       net_dev->name, +	       *speed == HW_SPEED_100_MBPS ? +	       "100mbps" : "10mbps", +	       *duplex == FDX_CAPABLE_FULL_SELECTED ? +	       "full" : "half"); +} + + +static void sis900_tx_timeout(struct device *net_dev) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	long ioaddr = net_dev->base_addr; +	int i; + +	printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n", +	       net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr)); + +	/* Disable interrupts by clearing the interrupt mask. */ +	outl(0x0000, ioaddr + imr); + +	/* discard unsent packets, should this code section be protected by +	   cli(), sti() ?? */ +	sis_priv->dirty_tx = sis_priv->cur_tx = 0; +	for (i = 0; i < NUM_TX_DESC; i++) { +		if (sis_priv->tx_skbuff[i] != NULL) { +			dev_free_skb(sis_priv->tx_skbuff[i]); +			sis_priv->tx_skbuff[i] = 0; +			sis_priv->tx_ring[i].cmdsts = 0; +			sis_priv->tx_ring[i].bufptr = 0; +			sis_priv->stats.tx_dropped++; +		} +	} +	net_dev->trans_start = jiffies; +	net_dev->tbusy = sis_priv->tx_full = 0; + +	/* FIXME: Should we restart the transmission thread here  ?? */ +	outl(TxENA | inl(ioaddr + cr), ioaddr + cr); + +	/* Enable all known interrupts by setting the interrupt mask. */ +	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); +	return; +} + +static int +sis900_start_xmit(struct sk_buff *skb, struct device *net_dev) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	long ioaddr = net_dev->base_addr; +	unsigned int  entry; + +	/* test tbusy to see if we have timeout situation then set it */ +	if (test_and_set_bit(0, (void*)&net_dev->tbusy) != 0) { +		if (jiffies - net_dev->trans_start > TX_TIMEOUT) +			sis900_tx_timeout(net_dev); +		return 1; +	} + +	/* Calculate the next Tx descriptor entry. */ +	entry = sis_priv->cur_tx % NUM_TX_DESC; +	sis_priv->tx_skbuff[entry] = skb; + +	/* set the transmit buffer descriptor and enable Transmit State Machine */ +	sis_priv->tx_ring[entry].bufptr = virt_to_bus(skb->data); +	sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len); +	outl(TxENA | inl(ioaddr + cr), ioaddr + cr); + +	if (++sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC) { +		/* Typical path, clear tbusy to indicate more +		   transmission is possible */ +		clear_bit(0, (void*)&net_dev->tbusy); +	} else { +		/* no more transmit descriptor avaiable, tbusy remain set */ +		sis_priv->tx_full = 1; +	} + +	net_dev->trans_start = jiffies; +	 +	{ +	  int i; +	  for (i = 0; i < 100000; i++); /* GRUIIIIIK */ +	} +	 +	if (sis900_debug > 3) +		printk(KERN_INFO "%s: Queued Tx packet at %p size %d " +		       "to slot %d.\n", +		       net_dev->name, skb->data, (int)skb->len, entry); + +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ +	struct device *net_dev = (struct device *)dev_instance; +	int boguscnt = max_interrupt_work; +	long ioaddr = net_dev->base_addr; +	u32 status; + +#if defined(__i386__) +	/* A lock to prevent simultaneous entry bug on Intel SMP machines. */ +	if (test_and_set_bit(0, (void*)&net_dev->interrupt)) { +		printk(KERN_INFO "%s: SMP simultaneous entry of " +		       "an interrupt handler.\n", net_dev->name); +		net_dev->interrupt = 0;		/* Avoid halting machine. */ +		return; +	} +#else +	if (net_dev->interrupt) { +		printk(KERN_INFO "%s: Re-entering the interrupt handler.\n", +		       net_dev->name); +		return; +	} +	net_dev->interrupt = 1; +#endif + +	do { +		status = inl(ioaddr + isr); + +		if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0) +			/* nothing intresting happened */ +			break; + +		/* why dow't we break after Tx/Rx case ?? keyword: full-duplex */ +		if (status & (RxORN | RxERR | RxOK)) +			/* Rx interrupt */ +			sis900_rx(net_dev); + +		if (status & (TxURN | TxERR | TxIDLE)) +			/* Tx interrupt */ +			sis900_finish_xmit(net_dev); + +		/* something strange happened !!! */ +		if (status & HIBERR) { +			printk(KERN_INFO "%s: Abnormal interrupt," +			       "status %#8.8x.\n", net_dev->name, status); +			break; +		} +		if (--boguscnt < 0) { +			printk(KERN_INFO "%s: Too much work at interrupt, " +			       "interrupt status = %#8.8x.\n", +			       net_dev->name, status); +			break; +		} +	} while (1); + +	if (sis900_debug > 4) +		printk(KERN_INFO "%s: exiting interrupt, " +		       "interrupt status = 0x%#8.8x.\n", +		       net_dev->name, inl(ioaddr + isr)); +	 +#if defined(__i386__) +	clear_bit(0, (void*)&net_dev->interrupt); +#else +	net_dev->interrupt = 0; +#endif +	return; +} + +/* Process receive interrupt events, put buffer to higher layer and refill buffer pool +   Note: This fucntion is called by interrupt handler, don't do "too much" work here */ +static int sis900_rx(struct device *net_dev) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	long ioaddr = net_dev->base_addr; +	unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC; +	u32 rx_status = sis_priv->rx_ring[entry].cmdsts; + +	if (sis900_debug > 4) +		printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d " +		       "status:0x%8.8x\n", +		       sis_priv->cur_rx, sis_priv->dirty_rx, rx_status); + +	while (rx_status & OWN) { +		unsigned int rx_size; + +		rx_size = (rx_status & DSIZE) - CRC_SIZE; + +		if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) { +			/* corrupted packet received */ +			if (sis900_debug > 4) +				printk(KERN_INFO "%s: Corrupted packet " +				       "received, buffer status = 0x%8.8x.\n", +				       net_dev->name, rx_status); +			sis_priv->stats.rx_errors++; +			if (rx_status & OVERRUN) +				sis_priv->stats.rx_over_errors++; +			if (rx_status & (TOOLONG|RUNT)) +				sis_priv->stats.rx_length_errors++; +			if (rx_status & (RXISERR | FAERR)) +				sis_priv->stats.rx_frame_errors++; +			if (rx_status & CRCERR) +				sis_priv->stats.rx_crc_errors++; +			/* reset buffer descriptor state */ +			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; +		} else { +			struct sk_buff * skb; + +			/* This situation should never happen, but due to +			   some unknow bugs, it is possible that +			   we are working on NULL sk_buff :-( */ +			if (sis_priv->rx_skbuff[entry] == NULL) { +				printk(KERN_INFO "%s: NULL pointer " +				       "encountered in Rx ring, skipping\n", +				       net_dev->name); +				break; +			} + +			/* gvie the socket buffer to upper layers */ +			skb = sis_priv->rx_skbuff[entry]; +			skb_put(skb, rx_size); +			skb->protocol = eth_type_trans(skb, net_dev); +			netif_rx(skb); + +			/* some network statistics */ +			if ((rx_status & BCAST) == MCAST) +				sis_priv->stats.multicast++; +			net_dev->last_rx = jiffies; +			/*			sis_priv->stats.rx_bytes += rx_size;*/ +			sis_priv->stats.rx_packets++; + +			/* refill the Rx buffer, what if there is not enought memory for +			   new socket buffer ?? */ +			if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { +				/* not enough memory for skbuff, this makes a "hole" +				   on the buffer ring, it is not clear how the +				   hardware will react to this kind of degenerated +				   buffer */ +				printk(KERN_INFO "%s: Memory squeeze," +				       "deferring packet.\n", +				       net_dev->name); +				sis_priv->rx_skbuff[entry] = NULL; +				/* reset buffer descriptor state */ +				sis_priv->rx_ring[entry].cmdsts = 0; +				sis_priv->rx_ring[entry].bufptr = 0; +				sis_priv->stats.rx_dropped++; +				break; +			} +			skb->dev = net_dev; +			sis_priv->rx_skbuff[entry] = skb; +			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; +			sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail); +			sis_priv->dirty_rx++; +		} +		sis_priv->cur_rx++; +		entry = sis_priv->cur_rx % NUM_RX_DESC; +		rx_status = sis_priv->rx_ring[entry].cmdsts; +	} // while + +	/* refill the Rx buffer, what if the rate of refilling is slower than +	   consuming ?? */ +	for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; sis_priv->dirty_rx++) { +		struct sk_buff *skb; + +		entry = sis_priv->dirty_rx % NUM_RX_DESC; + +		if (sis_priv->rx_skbuff[entry] == NULL) { +			if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { +				/* not enough memory for skbuff, this makes a "hole" +				   on the buffer ring, it is not clear how the +				   hardware will react to this kind of degenerated +				   buffer */ +				printk(KERN_INFO "%s: Memory squeeze," +				       "deferring packet.\n", +				       net_dev->name); +				sis_priv->stats.rx_dropped++; +				break; +			} +			skb->dev = net_dev; +			sis_priv->rx_skbuff[entry] = skb; +			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; +			sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail); +		} +	} + +	/* re-enable the potentially idle receive state matchine */ +	outl(RxENA | inl(ioaddr + cr), ioaddr + cr ); + +	return 0; +} + +/* finish up transmission of packets, check for error condition and free skbuff etc. +   Note: This fucntion is called by interrupt handler, don't do "too much" work here */ +static void sis900_finish_xmit (struct device *net_dev) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + +	for (; sis_priv->dirty_tx < sis_priv->cur_tx; sis_priv->dirty_tx++) { +		unsigned int entry; +		u32 tx_status; + +		entry = sis_priv->dirty_tx % NUM_TX_DESC; +		tx_status = sis_priv->tx_ring[entry].cmdsts; + +		if (tx_status & OWN) { +			/* The packet is not transmitted yet (owned by hardware) ! +			   Note: the interrupt is generated only when Tx Machine +			   is idle, so this is an almost impossible case */ +			break; +		} + +		if (tx_status & (ABORT | UNDERRUN | OWCOLL)) { +			/* packet unsuccessfully transmitted */ +			if (sis900_debug > 4) +				printk(KERN_INFO "%s: Transmit " +				       "error, Tx status %8.8x.\n", +				       net_dev->name, tx_status); +			sis_priv->stats.tx_errors++; +			if (tx_status & UNDERRUN) +				sis_priv->stats.tx_fifo_errors++; +			if (tx_status & ABORT) +				sis_priv->stats.tx_aborted_errors++; +			if (tx_status & NOCARRIER) +				sis_priv->stats.tx_carrier_errors++; +			if (tx_status & OWCOLL) +				sis_priv->stats.tx_window_errors++; +		} else { +			/* packet successfully transmitted */ +			sis_priv->stats.collisions += (tx_status & COLCNT) >> 16; +			/*			sis_priv->stats.tx_bytes += tx_status & DSIZE;*/ +			sis_priv->stats.tx_packets++; +		} +		/* Free the original skb. */ +		dev_free_skb(sis_priv->tx_skbuff[entry]); +		sis_priv->tx_skbuff[entry] = NULL; +		sis_priv->tx_ring[entry].bufptr = 0; +		sis_priv->tx_ring[entry].cmdsts = 0; +	} + +	if (sis_priv->tx_full && net_dev->tbusy && +	    sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) { +		/* The ring is no longer full, clear tbusy, tx_full and +		   schedule more transmission by marking NET_BH */ +		sis_priv->tx_full = 0; +		clear_bit(0, (void *)&net_dev->tbusy); +		mark_bh(NET_BH); +	} +} + +static int +sis900_close(struct device *net_dev) +{ +	long ioaddr = net_dev->base_addr; +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	int i; + +	net_dev->start = 0; +	net_dev->tbusy = 1; + +	/* Disable interrupts by clearing the interrupt mask. */ +	outl(0x0000, ioaddr + imr); +	outl(0x0000, ioaddr + ier); + +	/* Stop the chip's Tx and Rx Status Machine */ +	outl(RxDIS | TxDIS | inl(ioaddr + cr), ioaddr + cr); + +	del_timer(&sis_priv->timer); + +	free_irq(net_dev->irq, net_dev); + +	/* Free Tx and RX skbuff */ +	for (i = 0; i < NUM_RX_DESC; i++) { +		if (sis_priv->rx_skbuff[i] != NULL) +			dev_free_skb(sis_priv->rx_skbuff[i]); +		sis_priv->rx_skbuff[i] = 0; +	} +	for (i = 0; i < NUM_TX_DESC; i++) { +		if (sis_priv->tx_skbuff[i] != NULL) +			dev_free_skb(sis_priv->tx_skbuff[i]); +		sis_priv->tx_skbuff[i] = 0; +	} + +	/* Green! Put the chip in low-power mode. */ + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; + +	switch(cmd) { +	case SIOCDEVPRIVATE:			/* Get the address of the PHY in use. */ +		data[0] = sis_priv->mii->phy_addr; +		/* Fall Through */ +	case SIOCDEVPRIVATE+1:			/* Read the specified MII register. */ +		data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case SIOCDEVPRIVATE+2:			/* Write the specified MII register */ +		if (!suser()) +			return -EPERM; +		mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static struct enet_statistics * +sis900_get_stats(struct device *net_dev) +{ +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; + +	return &sis_priv->stats; +} + + +/* SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast + * hash table, which makes this function a little bit different from other drivers + * SiS 900 B0 & 635 M/B uses the most significat 8 bits to index 256 bits + * multicast hash table. + */ +static u16 sis900_compute_hashtable_index(u8 *addr, u8 revision) +{ + +/* what is the correct value of the POLYNOMIAL ?? +   Donald Becker use 0x04C11DB7U +   Joseph Zbiciak im14u2c@primenet.com gives me the +   correct answer, thank you Joe !! */ +#define POLYNOMIAL 0x04C11DB7L +	u32 crc = 0xffffffff, msb; +	int  i, j; +	u32 byte; + +	for (i = 0; i < 6; i++) { +		byte = *addr++; +		for (j = 0; j < 8; j++) { +			msb = crc >> 31; +			crc <<= 1; +			if (msb ^ (byte & 1)) { +				crc ^= POLYNOMIAL; +			} +			byte >>= 1; +		} +	} + +	/* leave 8 or 7 most siginifant bits */ +	if((revision >= SIS635A_900_REV) || (revision == SIS900B_900_REV)) +		return ((int)(crc >> 24)); +	else +		return ((int)(crc >> 25)); +} + +static void set_rx_mode(struct device *net_dev) +{ +	long ioaddr = net_dev->base_addr; +	struct sis900_private * sis_priv = (struct sis900_private *)net_dev->priv; +	u16 mc_filter[16] = {0};	/* 256/128 bits multicast hash table */ +	int i, table_entries; +	u32 rx_mode; +	u8 revision; + +	/* 635 Hash Table entires = 256(2^16) */ +	pcibios_read_config_byte(sis_priv->pci_bus, sis_priv->pci_device_fn, PCI_CLASS_REVISION, &revision); +	if((revision >= SIS635A_900_REV) || (revision == SIS900B_900_REV)) +		table_entries = 16; +	else +		table_entries = 8; + +	if (net_dev->flags & IFF_PROMISC) { +		/* Accept any kinds of packets */ +		rx_mode = RFPromiscuous; +		for (i = 0; i < table_entries; i++) +			mc_filter[i] = 0xffff; +	} else if ((net_dev->mc_count > multicast_filter_limit) || +		   (net_dev->flags & IFF_ALLMULTI)) { +		/* too many multicast addresses or accept all multicast packets */ +		rx_mode = RFAAB | RFAAM; +		for (i = 0; i < table_entries; i++) +			mc_filter[i] = 0xffff; +	} else { +		/* Accept Broadcast packets, destination addresses match our MAC address, +		   use Receive Filter to reject unwanted MCAST packets */ +		struct dev_mc_list *mclist; +		rx_mode = RFAAB; +		for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count; +		     i++, mclist = mclist->next) +			set_bit(sis900_compute_hashtable_index(mclist->dmi_addr, revision), +				mc_filter); +	} + +	/* update Multicast Hash Table in Receive Filter */ +	for (i = 0; i < table_entries; i++) { +		/* why plus 0x04 ??, That makes the correct value for hash table. */ +		outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr); +		outl(mc_filter[i], ioaddr + rfdr); +	} + +	outl(RFEN | rx_mode, ioaddr + rfcr); + +	/* sis900 is capatable of looping back packet at MAC level for debugging purpose */ +	if (net_dev->flags & IFF_LOOPBACK) { +		u32 cr_saved; +		/* We must disable Tx/Rx before setting loopback mode */ +		cr_saved = inl(ioaddr + cr); +		outl(cr_saved | TxDIS | RxDIS, ioaddr + cr); +		/* enable loopback */ +		outl(inl(ioaddr + txcfg) | TxMLB, ioaddr + txcfg); +		outl(inl(ioaddr + rxcfg) | RxATX, ioaddr + rxcfg); +		/* restore cr */ +		outl(cr_saved, ioaddr + cr); +	}		 + +	return; +} + +static void sis900_reset(struct device *net_dev) +{ +	long ioaddr = net_dev->base_addr; +	struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; +	int i = 0; +	u8 revision; +	u32 status = TxRCMP | RxRCMP; + +	outl(0, ioaddr + ier); +	outl(0, ioaddr + imr); +	outl(0, ioaddr + rfcr); + +	outl(RxRESET | TxRESET | RESET | inl(ioaddr + cr), ioaddr + cr); +	 +	/* Check that the chip has finished the reset. */ +	while (status && (i++ < 1000)) { +		status ^= (inl(isr + ioaddr) & status); +	} + +	pcibios_read_config_byte(sis_priv->pci_bus, sis_priv->pci_device_fn, PCI_CLASS_REVISION, &revision); +	if( (revision >= SIS635A_900_REV) || (revision == SIS900B_900_REV) ) +		outl(PESEL | RND_CNT, ioaddr + cfg); +	else +		outl(PESEL, ioaddr + cfg); +} + +#ifdef MODULE +int init_module(void) +{ +	return sis900_probe(NULL); +} + +void +cleanup_module(void) +{ +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_sis900_dev) { +		struct sis900_private *sis_priv = +			(struct sis900_private *)root_sis900_dev->priv; +		struct device *next_dev = sis_priv->next_module; +		struct mii_phy *phy = NULL; + +		while(sis_priv->first_mii){ +			phy = sis_priv->first_mii; +			sis_priv->first_mii = phy->next; +			kfree(phy); +		} + +		unregister_netdev(root_sis900_dev); +		release_region(root_sis900_dev->base_addr, +			       sis_priv->mac->io_size); +		kfree(sis_priv); +		kfree(root_sis900_dev); + +		root_sis900_dev = next_dev; +	} +} + +#endif	/* MODULE */ diff --git a/linux/src/drivers/net/sis900.h b/linux/src/drivers/net/sis900.h new file mode 100644 index 0000000..2153625 --- /dev/null +++ b/linux/src/drivers/net/sis900.h @@ -0,0 +1,284 @@ +/* sis900.h Definitions for SiS ethernet controllers including 7014/7016 and 900 + * Copyright 1999 Silicon Integrated System Corporation + * References: + *   SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + *	preliminary Rev. 1.0 Jan. 14, 1998 + *   SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + *	preliminary Rev. 1.0 Nov. 10, 1998 + *   SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + *	preliminary Rev. 1.0 Jan. 18, 1998 + *   http://www.sis.com.tw/support/databook.htm + */ + +/* MAC operationl registers of SiS 7016 and SiS 900 ehternet controller */ +/* The I/O extent, SiS 900 needs 256 bytes of io address */ +#define SIS900_TOTAL_SIZE 0x100 + +/* Symbolic offsets to registers. */ +enum sis900_registers { +	cr=0x0,                 //Command Register +	cfg=0x4,                //Configuration Register +	mear=0x8,               //EEPROM Access Register +	ptscr=0xc,              //PCI Test Control Register +	isr=0x10,               //Interrupt Status Register +	imr=0x14,               //Interrupt Mask Register +	ier=0x18,               //Interrupt Enable Register +	epar=0x18,              //Enhanced PHY Access Register +	txdp=0x20,              //Transmit Descriptor Pointer Register +        txcfg=0x24,             //Transmit Configuration Register +        rxdp=0x30,              //Receive Descriptor Pointer Register +        rxcfg=0x34,             //Receive Configuration Register +        flctrl=0x38,            //Flow Control Register +        rxlen=0x3c,             //Receive Packet Length Register +        rfcr=0x48,              //Receive Filter Control Register +        rfdr=0x4C,              //Receive Filter Data Register +        pmctrl=0xB0,            //Power Management Control Register +        pmer=0xB4               //Power Management Wake-up Event Register +}; + +/* Symbolic names for bits in various registers */ +enum sis900_command_register_bits { +	RELOAD  = 0x00000400, ACCESSMODE = 0x00000200,/* ET */ +	RESET   = 0x00000100, SWI = 0x00000080, RxRESET = 0x00000020, +	TxRESET = 0x00000010, RxDIS = 0x00000008, RxENA = 0x00000004, +	TxDIS   = 0x00000002, TxENA = 0x00000001 +}; + +enum sis900_configuration_register_bits { +	DESCRFMT = 0x00000100 /* 7016 specific */, REQALG = 0x00000080, +	SB    = 0x00000040, POW = 0x00000020, EXD = 0x00000010, +	PESEL = 0x00000008, LPM = 0x00000004, BEM = 0x00000001, +	/* 635 & 900B Specific */ +        RND_CNT = 0x00000400, FAIR_BACKOFF = 0x00000200, +        EDB_MASTER_EN = 0x00002000 +}; + +enum sis900_eeprom_access_reigster_bits { +	MDC  = 0x00000040, MDDIR = 0x00000020, MDIO = 0x00000010, /* 7016 specific */ +	EECS = 0x00000008, EECLK = 0x00000004, EEDO = 0x00000002, +	EEDI = 0x00000001 +}; + +enum sis900_interrupt_register_bits { +	WKEVT  = 0x10000000, TxPAUSEEND = 0x08000000, TxPAUSE = 0x04000000, +	TxRCMP = 0x02000000, RxRCMP = 0x01000000, DPERR = 0x00800000, +	SSERR  = 0x00400000, RMABT  = 0x00200000, RTABT = 0x00100000, +	RxSOVR = 0x00010000, HIBERR = 0x00008000, SWINT = 0x00001000, +	MIBINT = 0x00000800, TxURN  = 0x00000400, TxIDLE  = 0x00000200, +	TxERR  = 0x00000100, TxDESC = 0x00000080, TxOK  = 0x00000040, +	RxORN  = 0x00000020, RxIDLE = 0x00000010, RxEARLY = 0x00000008, +	RxERR  = 0x00000004, RxDESC = 0x00000002, RxOK  = 0x00000001 +}; + +enum sis900_interrupt_enable_reigster_bits { +	IE = 0x00000001 +}; + +/* maximum dma burst fro transmission and receive*/ +#define MAX_DMA_RANGE	7	/* actually 0 means MAXIMUM !! */ +#define TxMXDMA_shift   	20 +#define RxMXDMA_shift    	20 + +enum sis900_tx_rx_dma{ +        DMA_BURST_512 = 0,      DMA_BURST_64 = 5 +}; + +/* transmit FIFO threshholds */ +#define TX_FILL_THRESH   16	/* 1/4 FIFO size */ +#define TxFILLT_shift   	8 +#define TxDRNT_shift    	0 +#define TxDRNT_100      	48	/* 3/4 FIFO size */ +#define TxDRNT_10		16 	/* 1/2 FIFO size */ + +enum sis900_transmit_config_register_bits { +	TxCSI = 0x80000000, TxHBI = 0x40000000, TxMLB = 0x20000000, +	TxATP = 0x10000000, TxIFG = 0x0C000000, TxFILLT = 0x00003F00, +	TxDRNT = 0x0000003F +}; + +/* recevie FIFO thresholds */ +#define RxDRNT_shift     1 +#define RxDRNT_100	16	/* 1/2 FIFO size */ +#define RxDRNT_10		24 	/* 3/4 FIFO size */ + +enum sis900_reveive_config_register_bits { +	RxAEP  = 0x80000000, RxARP = 0x40000000, RxATX = 0x10000000, +	RxAJAB = 0x08000000, RxDRNT = 0x0000007F +}; + +#define RFAA_shift      28 +#define RFADDR_shift    16 + +enum sis900_receive_filter_control_register_bits { +	RFEN  = 0x80000000, RFAAB = 0x40000000, RFAAM = 0x20000000, +	RFAAP = 0x10000000, RFPromiscuous = (RFAAB|RFAAM|RFAAP) +}; + +enum sis900_reveive_filter_data_mask { +	RFDAT =  0x0000FFFF +}; + +/* EEPROM Addresses */ +enum sis900_eeprom_address { +	EEPROMSignature = 0x00, EEPROMVendorID = 0x02, EEPROMDeviceID = 0x03, +	EEPROMMACAddr   = 0x08, EEPROMChecksum = 0x0b +}; + +/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */ +enum sis900_eeprom_command { +	EEread     = 0x0180, EEwrite    = 0x0140, EEerase = 0x01C0, +	EEwriteEnable = 0x0130, EEwriteDisable = 0x0100, +	EEeraseAll = 0x0120, EEwriteAll = 0x0110, +	EEaddrMask = 0x013F, EEcmdShift = 16 +}; + +/* For SiS962, request the eeprom software access */ +enum sis962_eeprom_command { +	EEREQ = 0x00000400, EEDONE = 0x00000200, EEGNT = 0x00000100 +}; + +/* Manamgement Data I/O (mdio) frame */ +#define MIIread         0x6000 +#define MIIwrite        0x5002 +#define MIIpmdShift     7 +#define MIIregShift     2 +#define MIIcmdLen       16 +#define MIIcmdShift     16 + +/* Buffer Descriptor Status*/ +enum sis900_buffer_status { +	OWN    = 0x80000000, MORE   = 0x40000000, INTR = 0x20000000, +	SUPCRC = 0x10000000, INCCRC = 0x10000000, +	OK     = 0x08000000, DSIZE  = 0x00000FFF +}; +/* Status for TX Buffers */ +enum sis900_tx_buffer_status { +	ABORT   = 0x04000000, UNDERRUN = 0x02000000, NOCARRIER = 0x01000000, +	DEFERD  = 0x00800000, EXCDEFER = 0x00400000, OWCOLL    = 0x00200000, +	EXCCOLL = 0x00100000, COLCNT   = 0x000F0000 +}; + +enum sis900_rx_bufer_status { +	OVERRUN = 0x02000000, DEST = 0x00800000,     BCAST = 0x01800000, +	MCAST   = 0x01000000, UNIMATCH = 0x00800000, TOOLONG = 0x00400000, +	RUNT    = 0x00200000, RXISERR  = 0x00100000, CRCERR  = 0x00080000, +	FAERR   = 0x00040000, LOOPBK   = 0x00020000, RXCOL   = 0x00010000 +}; + +/* MII register offsets */ +enum mii_registers { +	MII_CONTROL = 0x0000, MII_STATUS = 0x0001, MII_PHY_ID0 = 0x0002, +	MII_PHY_ID1 = 0x0003, MII_ANADV  = 0x0004, MII_ANLPAR  = 0x0005, +	MII_ANEXT   = 0x0006 +}; + +/* mii registers specific to SiS 900 */ +enum sis_mii_registers { +	MII_CONFIG1 = 0x0010, MII_CONFIG2 = 0x0011, MII_STSOUT = 0x0012, +	MII_MASK    = 0x0013, MII_RESV    = 0x0014 +}; + +/* mii registers specific to ICS 1893 */ +enum ics_mii_registers { +	MII_EXTCTRL  = 0x0010, MII_QPDSTS = 0x0011, MII_10BTOP = 0x0012, +	MII_EXTCTRL2 = 0x0013 +}; + +/* mii registers specific to AMD 79C901 */ +enum amd_mii_registers { +	MII_STATUS_SUMMARY = 0x0018 +}; + +/* MII Control register bit definitions. */ +enum mii_control_register_bits { +	MII_CNTL_FDX     = 0x0100, MII_CNTL_RST_AUTO = 0x0200, +	MII_CNTL_ISOLATE = 0x0400, MII_CNTL_PWRDWN   = 0x0800, +	MII_CNTL_AUTO    = 0x1000, MII_CNTL_SPEED    = 0x2000, +	MII_CNTL_LPBK    = 0x4000, MII_CNTL_RESET    = 0x8000 +}; + +/* MII Status register bit  */ +enum mii_status_register_bits { +	MII_STAT_EXT    = 0x0001, MII_STAT_JAB        = 0x0002, +	MII_STAT_LINK   = 0x0004, MII_STAT_CAN_AUTO   = 0x0008, +	MII_STAT_FAULT  = 0x0010, MII_STAT_AUTO_DONE  = 0x0020, +	MII_STAT_CAN_T  = 0x0800, MII_STAT_CAN_T_FDX  = 0x1000, +	MII_STAT_CAN_TX = 0x2000, MII_STAT_CAN_TX_FDX = 0x4000, +	MII_STAT_CAN_T4 = 0x8000 +}; + +#define		MII_ID1_OUI_LO		0xFC00	/* low bits of OUI mask */ +#define		MII_ID1_MODEL		0x03F0	/* model number */ +#define		MII_ID1_REV		0x000F	/* model number */ + +/* MII NWAY Register Bits ... +   valid for the ANAR (Auto-Negotiation Advertisement) and +   ANLPAR (Auto-Negotiation Link Partner) registers */ +enum mii_nway_register_bits { +	MII_NWAY_NODE_SEL = 0x001f, MII_NWAY_CSMA_CD = 0x0001, +	MII_NWAY_T	  = 0x0020, MII_NWAY_T_FDX   = 0x0040, +	MII_NWAY_TX       = 0x0080, MII_NWAY_TX_FDX  = 0x0100, +	MII_NWAY_T4       = 0x0200, MII_NWAY_PAUSE   = 0x0400, +	MII_NWAY_RF       = 0x2000, MII_NWAY_ACK     = 0x4000, +	MII_NWAY_NP       = 0x8000 +}; + +enum mii_stsout_register_bits { +	MII_STSOUT_LINK_FAIL = 0x4000, +	MII_STSOUT_SPD       = 0x0080, MII_STSOUT_DPLX = 0x0040 +}; + +enum mii_stsics_register_bits { +	MII_STSICS_SPD  = 0x8000, MII_STSICS_DPLX = 0x4000, +	MII_STSICS_LINKSTS = 0x0001 +}; + +enum mii_stssum_register_bits { +	MII_STSSUM_LINK = 0x0008, MII_STSSUM_DPLX = 0x0004, +	MII_STSSUM_AUTO = 0x0002, MII_STSSUM_SPD  = 0x0001 +}; + +enum sis900_revision_id { +	SIS630A_900_REV = 0x80, 	SIS630E_900_REV = 0x81, +	SIS630S_900_REV = 0x82,		SIS630EA1_900_REV = 0x83, +	SIS630ET_900_REV = 0x84,        SIS635A_900_REV = 0x90, +	SIS962_900_REV = 0X91,		SIS900B_900_REV = 0x03 +}; + +enum sis630_revision_id { +        SIS630A0    = 0x00, SIS630A1      = 0x01, +        SIS630B0    = 0x10, SIS630B1      = 0x11 +}; + +#define FDX_CAPABLE_DUPLEX_UNKNOWN      0 +#define FDX_CAPABLE_HALF_SELECTED       1 +#define FDX_CAPABLE_FULL_SELECTED       2 + +#define HW_SPEED_UNCONFIG		0 +#define HW_SPEED_HOME		1 +#define HW_SPEED_10_MBPS        	10 +#define HW_SPEED_100_MBPS       	100 +#define HW_SPEED_DEFAULT        	(HW_SPEED_100_MBPS) + +#define CRC_SIZE                4 +#define MAC_HEADER_SIZE         14 + +#define TX_BUF_SIZE     1536 +#define RX_BUF_SIZE     1536 + +#define NUM_TX_DESC     16      	/* Number of Tx descriptor registers. */ +#define NUM_RX_DESC     16       	/* Number of Rx descriptor registers. */ + +#define TRUE            1 +#define FALSE           0 + +/* PCI stuff, should be move to pic.h */ +#define PCI_DEVICE_ID_SI_900	0x900 +#define PCI_DEVICE_ID_SI_7016	0x7016 +#define SIS630_VENDOR_ID        0x1039 +#define SIS630_DEVICE_ID        0x0630 + +/* ioctl for accessing MII transveiver */ +#define SIOCGMIIPHY (SIOCDEVPRIVATE)		/* Get the PHY in use. */ +#define SIOCGMIIREG (SIOCDEVPRIVATE+1)		/* Read a PHY register. */ +#define SIOCSMIIREG (SIOCDEVPRIVATE+2)		/* Write a PHY register */ diff --git a/linux/src/drivers/net/sk_g16.c b/linux/src/drivers/net/sk_g16.c new file mode 100644 index 0000000..13ebb3e --- /dev/null +++ b/linux/src/drivers/net/sk_g16.c @@ -0,0 +1,2110 @@ +/*- + * Copyright (C) 1994 by PJD Weichmann & SWS Bern, Switzerland + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * Module         : sk_g16.c + * + * Version        : $Revision: 1.1 $ + * + * Author         : Patrick J.D. Weichmann + * + * Date Created   : 94/05/26 + * Last Updated   : $Date: 1999/04/26 05:52:37 $ + * + * Description    : Schneider & Koch G16 Ethernet Device Driver for + *                  Linux Kernel >= 1.1.22 + * Update History : + * +-*/ + +static const char *rcsid = "$Id: sk_g16.c,v 1.1 1999/04/26 05:52:37 tb Exp $"; + +/* + * The Schneider & Koch (SK) G16 Network device driver is based + * on the 'ni6510' driver from Michael Hipp which can be found at + * ftp://sunsite.unc.edu/pub/Linux/system/Network/drivers/nidrivers.tar.gz + *  + * Sources: 1) ni6510.c by M. Hipp + *          2) depca.c  by D.C. Davies + *          3) skeleton.c by D. Becker + *          4) Am7990 Local Area Network Controller for Ethernet (LANCE), + *             AMD, Pub. #05698, June 1989 + * + * Many Thanks for helping me to get things working to:  + *                  + *                 A. Cox (A.Cox@swansea.ac.uk) + *                 M. Hipp (mhipp@student.uni-tuebingen.de) + *                 R. Bolz (Schneider & Koch, Germany) + * + * See README.sk_g16 for details about limitations and bugs for the + * current version. + * + * To Do:  + *        - Support of SK_G8 and other SK Network Cards. + *        - Autoset memory mapped RAM. Check for free memory and then + *          configure RAM correctly.  + *        - SK_close should really set card in to initial state. + *        - Test if IRQ 3 is not switched off. Use autoirq() functionality. + *          (as in /drivers/net/skeleton.c) + *        - Implement Multicast addressing. At minimum something like + *          in depca.c.  + *        - Redo the statistics part. + *        - Try to find out if the board is in 8 Bit or 16 Bit slot. + *          If in 8 Bit mode don't use IRQ 11. + *        - (Try to make it slightly faster.)  + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/fcntl.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/malloc.h> +#include <linux/string.h>  +#include <asm/system.h> +#include <asm/io.h> +#include <asm/bitops.h>  +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "sk_g16.h" + +/*  + * Schneider & Koch Card Definitions  + * ================================= + */   + +#define SK_NAME   "SK_G16" + +/* + * SK_G16 Configuration + * -------------------- + */  + +/*  + * Abbreviations + * ------------- + *   + * RAM - used for the 16KB shared memory  + * Boot_ROM, ROM - are used for referencing the BootEPROM + * + * SK_BOOT_ROM and SK_ADDR are symbolic constants used to configure + * the behaviour of the driver and the SK_G16. + * + * ! See sk_g16.install on how to install and configure the driver !    + * + * SK_BOOT_ROM defines if the Boot_ROM should be switched off or not. + * + * SK_ADDR defines the address where the RAM will be mapped into the real + *         host memory. + *         valid addresses are from 0xa0000 to 0xfc000 in 16Kbyte steps. + */   +  +#define SK_BOOT_ROM     1              /* 1=BootROM on 0=off */ + +#define SK_ADDR         0xcc000 + +/*  + * In POS3 are bits A14-A19 of the address bus. These bits can be set + * to choose the RAM address. That's why we only can choose the RAM address + * in 16KB steps. + */ + +#define POS_ADDR       (rom_addr>>14)  /* Do not change this line */ + +/*  + * SK_G16 I/O PORT's + IRQ's + Boot_ROM locations + * ---------------------------------------------- + */ + +/*  + * As nearly every card has also SK_G16 a specified I/O Port region and + * only a few possible IRQ's. + * In the Installation Guide from Schneider & Koch is listed a possible + * Interrupt IRQ2. IRQ2 is always IRQ9 in boards with two cascaded interrupt + * controllers. So we use in SK_IRQS IRQ9. + */ + +/* Don't touch any of the following #defines. */ + +#define SK_IO_PORTS     { 0x100, 0x180, 0x208, 0x220, 0x288, 0x320, 0x328, 0x390, 0 } + +#define SK_IRQS         { 3, 5, 9, 11, 0 } + +#define SK_BOOT_ROM_LOCATIONS { 0xc0000, 0xc4000, 0xc8000, 0xcc000, 0xd0000, 0xd4000, 0xd8000, 0xdc000, 0 } + +#define SK_BOOT_ROM_ID  { 0x55, 0xaa, 0x10, 0x50, 0x06, 0x33 } + +/*  + * SK_G16 POS REGISTERS  + * -------------------- + */ + +/* + * SK_G16 has a Programmable Option Select (POS) Register. + * The POS is composed of 8 separate registers (POS0-7) which  + * are I/O mapped on an address set by the W1 switch.                     + * + */ + +#define SK_POS_SIZE 8           /* 8 I/O Ports are used by SK_G16 */ + +#define SK_POS0     ioaddr      /* Card-ID Low (R) */ +#define SK_POS1     ioaddr+1    /* Card-ID High (R) */ +#define SK_POS2     ioaddr+2    /* Card-Enable, Boot-ROM Disable (RW) */ +#define SK_POS3     ioaddr+3    /* Base address of RAM */ +#define SK_POS4     ioaddr+4    /* IRQ */ + +/* POS5 - POS7 are unused */ + +/*  + * SK_G16 MAC PREFIX  + * ----------------- + */ + +/*  + * Scheider & Koch manufacturer code (00:00:a5). + * This must be checked, that we are sure it is a SK card. + */ + +#define SK_MAC0         0x00 +#define SK_MAC1         0x00 +#define SK_MAC2         0x5a + +/*  + * SK_G16 ID  + * --------- + */  + +/*  + * If POS0,POS1 contain the following ID, then we know + * at which I/O Port Address we are.  + */ + +#define SK_IDLOW  0xfd  +#define SK_IDHIGH 0x6a + + +/*  + * LANCE POS Bit definitions  + * ------------------------- + */ + +#define SK_ROM_RAM_ON  (POS2_CARD) +#define SK_ROM_RAM_OFF (POS2_EPROM) +#define SK_ROM_ON      (inb(SK_POS2) & POS2_CARD) +#define SK_ROM_OFF     (inb(SK_POS2) | POS2_EPROM) +#define SK_RAM_ON      (inb(SK_POS2) | POS2_CARD) +#define SK_RAM_OFF     (inb(SK_POS2) & POS2_EPROM)  + +#define POS2_CARD  0x0001              /* 1 = SK_G16 on      0 = off */ +#define POS2_EPROM 0x0002              /* 1 = Boot EPROM off 0 = on */  + +/*  + * SK_G16 Memory mapped Registers + * ------------------------------ + * + */  + +#define SK_IOREG        (board->ioreg) /* LANCE data registers.     */  +#define SK_PORT         (board->port)  /* Control, Status register  */ +#define SK_IOCOM        (board->iocom) /* I/O Command               */ + +/*  + * SK_G16 Status/Control Register bits + * ----------------------------------- + * + * (C) Controlreg (S) Statusreg  + */ + +/*  + * Register transfer: 0 = no transfer + *                    1 = transferring data between LANCE and I/O reg  + */ +#define SK_IORUN        0x20    + +/*  + * LANCE interrupt: 0 = LANCE interrupt occurred	 + *                  1 = no LANCE interrupt occurred + */ +#define SK_IRQ          0x10    +			 +#define SK_RESET        0x08   /* Reset SK_CARD: 0 = RESET 1 = normal */ +#define SK_RW           0x02   /* 0 = write to 1 = read from */ +#define SK_ADR          0x01   /* 0 = REG DataPort 1 = RAP Reg addr port */ + +   +#define SK_RREG         SK_RW  /* Transferdirection to read from lance */ +#define SK_WREG         0      /* Transferdirection to write to lance */ +#define SK_RAP          SK_ADR /* Destination Register RAP */ +#define SK_RDATA        0      /* Destination Register REG DataPort */ + +/*  + * SK_G16 I/O Command  + * ------------------ + */ + +/*  + * Any bitcombination sets the internal I/O bit (transfer will start)  + * when written to I/O Command + */ + +#define SK_DOIO         0x80   /* Do Transfer */  +  +/*  + * LANCE RAP (Register Address Port).  + * --------------------------------- + */ + +/*    + * The LANCE internal registers are selected through the RAP.  + * The Registers are: + * + * CSR0 - Status and Control flags  + * CSR1 - Low order bits of initialize block (bits 15:00) + * CSR2 - High order bits of initialize block (bits 07:00, 15:08 are reserved) + * CSR3 - Allows redefinition of the Bus Master Interface. + *        This register must be set to 0x0002, which means BSWAP = 0, + *        ACON = 1, BCON = 0; + * + */ +  +#define CSR0            0x00    +#define CSR1            0x01   +#define CSR2            0x02  +#define CSR3            0x03 + +/*  + * General Definitions  + * =================== + */ + +/*  + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * We have 16KB RAM which can be accessed by the LANCE. In the  + * memory are not only the buffers but also the ring descriptors and + * the initialize block.  + * Don't change anything unless you really know what you do. + */ + +#define LC_LOG_TX_BUFFERS 1               /* (2 == 2^^1) 2 Transmit buffers */ +#define LC_LOG_RX_BUFFERS 3               /* (8 == 2^^3) 8 Receive buffers */ + +/* Descriptor ring sizes */ + +#define TMDNUM (1 << (LC_LOG_TX_BUFFERS)) /* 2 Transmit descriptor rings */ +#define RMDNUM (1 << (LC_LOG_RX_BUFFERS)) /* 8 Receive Buffers */ + +/* Define Mask for setting RMD, TMD length in the LANCE init_block */ + +#define TMDNUMMASK (LC_LOG_TX_BUFFERS << 29) +#define RMDNUMMASK (LC_LOG_RX_BUFFERS << 29) + +/* + * Data Buffer size is set to maximum packet length. + */ + +#define PKT_BUF_SZ              1518  + +/*  + * The number of low I/O ports used by the ethercard.  + */ + +#define ETHERCARD_TOTAL_SIZE    SK_POS_SIZE + +/*  + * Portreserve is there to mark the Card I/O Port region as used.  + * Check_region is to check if the region at ioaddr with the size "size"  + * is free or not. + * Snarf_region allocates the I/O Port region. + */ + +#ifndef HAVE_PORTRESERVE + +#define check_region(ioaddr, size)              0 +#define request_region(ioaddr, size,name)       do ; while (0) + +#endif + +/*  + * SK_DEBUG + * + * Here you can choose what level of debugging wanted. + * + * If SK_DEBUG and SK_DEBUG2 are undefined, then only the + *  necessary messages will be printed. + * + * If SK_DEBUG is defined, there will be many debugging prints + *  which can help to find some mistakes in configuration or even + *  in the driver code. + * + * If SK_DEBUG2 is defined, many many messages will be printed  + *  which normally you don't need. I used this to check the interrupt + *  routine.  + * + * (If you define only SK_DEBUG2 then only the messages for  + *  checking interrupts will be printed!) + * + * Normal way of live is:  + * + * For the whole thing get going let both symbolic constants + * undefined. If you face any problems and you know what's going + * on (you know something about the card and you can interpret some + * hex LANCE register output) then define SK_DEBUG + *  + */ + +#undef  SK_DEBUG	/* debugging */ +#undef  SK_DEBUG2	/* debugging with more verbose report */ + +#ifdef SK_DEBUG +#define PRINTK(x) printk x +#else +#define PRINTK(x) /**/ +#endif + +#ifdef SK_DEBUG2 +#define PRINTK2(x) printk x +#else +#define PRINTK2(x) /**/ +#endif + +/*  + * SK_G16 RAM + * + * The components are memory mapped and can be set in a region from + * 0x00000 through 0xfc000 in 16KB steps.  + * + * The Network components are: dual ported RAM, Prom, I/O Reg, Status-, + * Controlregister and I/O Command. + * + * dual ported RAM: This is the only memory region which the LANCE chip + *      has access to. From the Lance it is addressed from 0x0000 to + *      0x3fbf. The host accesses it normally. + * + * PROM: The PROM obtains the ETHERNET-MAC-Address. It is realised as a + *       8-Bit PROM, this means only the 16 even addresses are used of the + *       32 Byte Address region. Access to a odd address results in invalid + *       data. + *  + * LANCE I/O Reg: The I/O Reg is build of 4 single Registers, Low-Byte Write, + *       Hi-Byte Write, Low-Byte Read, Hi-Byte Read. + *       Transfer from or to the LANCE is always in 16Bit so Low and High + *       registers are always relevant. + * + *       The Data from the Readregister is not the data in the Writeregister!! + *        + * Port: Status- and Controlregister.  + *       Two different registers which share the same address, Status is  + *       read-only, Control is write-only. + *     + * I/O Command:  + *       Any bitcombination written in here starts the transmission between + *       Host and LANCE. + */ + +typedef struct +{ +	unsigned char  ram[0x3fc0];   /* 16KB dual ported ram */ +	unsigned char  rom[0x0020];   /* 32Byte PROM containing 6Byte MAC */ +	unsigned char  res1[0x0010];  /* reserved */ +	unsigned volatile short ioreg;/* LANCE I/O Register */ +	unsigned volatile char  port; /* Statusregister and Controlregister */ +	unsigned char  iocom;         /* I/O Command Register */ +} SK_RAM; + +/* struct  */ + +/*  + * This is the structure for the dual ported ram. We + * have exactly 16 320 Bytes. In here there must be: + * + *     - Initialize Block   (starting at a word boundary) + *     - Receive and Transmit Descriptor Rings (quadword boundary) + *     - Data Buffers (arbitrary boundary) + * + * This is because LANCE has on SK_G16 only access to the dual ported + * RAM and nowhere else. + */ + +struct SK_ram +{ +    struct init_block ib; +    struct tmd tmde[TMDNUM]; +    struct rmd rmde[RMDNUM]; +    char tmdbuf[TMDNUM][PKT_BUF_SZ]; +    char rmdbuf[RMDNUM][PKT_BUF_SZ]; +}; + +/*  + * Structure where all necessary information is for ring buffer  + * management and statistics. + */ + +struct priv +{ +    struct SK_ram *ram;  /* dual ported ram structure */ +    struct rmd *rmdhead; /* start of receive ring descriptors */ +    struct tmd *tmdhead; /* start of transmit ring descriptors */ +    int        rmdnum;   /* actual used ring descriptor */ +    int        tmdnum;   /* actual transmit descriptor for transmitting data */ +    int        tmdlast;  /* last sent descriptor used for error handling, etc */ +    void       *rmdbufs[RMDNUM]; /* pointer to the receive buffers */ +    void       *tmdbufs[TMDNUM]; /* pointer to the transmit buffers */ +    struct enet_statistics stats; /* Device driver statistics */ +}; + +/* global variable declaration */ + +/* IRQ map used to reserve a IRQ (see SK_open()) */ + +/* extern void *irq2dev_map[16]; */ /* Declared in <linux/ioport.h> */ + +/* static variables */ + +static SK_RAM *board;  /* pointer to our memory mapped board components */ + +/* Macros */ + + +/* Function Prototypes */ + +/* + * Device Driver functions + * ----------------------- + * See for short explanation of each function its definitions header. + */ + +int          SK_init(struct device *dev); +static int   SK_probe(struct device *dev, short ioaddr); + +static int   SK_open(struct device *dev); +static int   SK_send_packet(struct sk_buff *skb, struct device *dev); +static void  SK_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static void  SK_rxintr(struct device *dev); +static void  SK_txintr(struct device *dev); +static int   SK_close(struct device *dev); + +static struct enet_statistics *SK_get_stats(struct device *dev); + +unsigned int SK_rom_addr(void); + +static void set_multicast_list(struct device *dev); + +/* + * LANCE Functions + * --------------- + */ + +static int SK_lance_init(struct device *dev, unsigned short mode); +void   SK_reset_board(void); +void   SK_set_RAP(int reg_number); +int    SK_read_reg(int reg_number); +int    SK_rread_reg(void); +void   SK_write_reg(int reg_number, int value); + +/*  + * Debugging functions + * ------------------- + */ + +void SK_print_pos(struct device *dev, char *text); +void SK_print_dev(struct device *dev, char *text); +void SK_print_ram(struct device *dev); + + +/*- + * Function       : SK_init + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/26 + * + * Description    : Check for a SK_G16 network adaptor and initialize it. + *                  This function gets called by dev_init which initializes + *                  all Network devices. + * + * Parameters     : I : struct device *dev - structure preconfigured  + *                                           from Space.c + * Return Value   : 0 = Driver Found and initialized  + * Errors         : ENODEV - no device found + *                  ENXIO  - not probed + * Globals        : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +/*  + * Check for a network adaptor of this type, and return '0' if one exists. + * If dev->base_addr == 0, probe all likely locations. + * If dev->base_addr == 1, always return failure. + * If dev->base_addr == 2, allocate space for the device and return success + *                         (detachable devices only). + */ + +int SK_init(struct device *dev) +{ +	int ioaddr         = 0;            /* I/O port address used for POS regs */ +	int *port, ports[] = SK_IO_PORTS;  /* SK_G16 supported ports */ + +	/* get preconfigured base_addr from dev which is done in Space.c */ +	int base_addr = dev->base_addr;  + +        PRINTK(("%s: %s", SK_NAME, rcsid)); +        rcsid = NULL;                 /* We do not want to use this further */ + +	if (base_addr > 0x0ff)        /* Check a single specified address */ +	{ +	    /* Check if on specified address is a SK_G16 */ + +	    if ( (inb(SK_POS0) == SK_IDLOW) || +		 (inb(SK_POS1) == SK_IDHIGH) )   +	    { +		return SK_probe(dev, base_addr);   +	    } + +	    return ENODEV;            /* Sorry, but on specified address NO SK_G16 */ +	} +	else if (base_addr > 0)       /* Don't probe at all */ +	{ +		return ENXIO; +	} + +	/* Autoprobe base_addr */ + +	for (port = &ports[0]; *port; port++)  +	{ +	    ioaddr = *port;           /* we need ioaddr for accessing POS regs */ + +	    /* Check if I/O Port region is used by another board */ + +	    if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE)) +	    { +		continue;             /* Try next Port address */ +	    } + +	    /* Check if at ioaddr is a SK_G16 */ + +	    if ( !(inb(SK_POS0) == SK_IDLOW) || +		 !(inb(SK_POS1) == SK_IDHIGH) ) +	    { +		continue;             /* Try next Port address */ +	    } + +	    dev->base_addr = ioaddr;  /* Set I/O Port Address */ + +	    if (SK_probe(dev, ioaddr) == 0)   +	    { +		return 0; /* Card found and initialized */ +	    } +	} + +	dev->base_addr = base_addr;   /* Write back original base_addr */ + +	return ENODEV;                /* Failed to find or init driver */ + +} /* End of SK_init */ + + +/*- + * Function       : SK_probe + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/26 + * + * Description    : This function is called by SK_init and  + *                  does the main part of initialization. + *                   + * Parameters     : I : struct device *dev - SK_G16 device structure + *                  I : short ioaddr       - I/O Port address where POS is. + * Return Value   : 0 = Initialization done              + * Errors         : ENODEV - No SK_G16 found + *                  -1     - Configuration problem + * Globals        : irq2dev_map - Which device uses which IRQ + *                : board       - pointer to SK_RAM + * Update History : + *     YY/MM/DD  uid  Description + *     94/06/30  pwe  SK_ADDR now checked and at the correct place +-*/ + +int SK_probe(struct device *dev, short ioaddr) +{ +    int i,j;                /* Counters */ +    int sk_addr_flag = 0;   /* SK ADDR correct? 1 - no, 0 - yes */ +    unsigned int rom_addr;  /* used to store RAM address used for POS_ADDR */ + +    struct priv *p;         /* SK_G16 private structure */ + +    if (SK_ADDR & 0x3fff || SK_ADDR < 0xa0000) +    { +       +       sk_addr_flag = 1; + +       /*  +        * Now here we could use a routine which searches for a free +        * place in the ram and set SK_ADDR if found. TODO.  +        */ +    } + +    if (SK_BOOT_ROM)            /* Shall we keep Boot_ROM on ? */ +    { +        PRINTK(("## %s: SK_BOOT_ROM is set.\n", SK_NAME)); + +        rom_addr = SK_rom_addr(); + +	if (rom_addr == 0)      /* No Boot_ROM found */ +	{ +            if (sk_addr_flag)   /* No or Invalid SK_ADDR is defined */  +            { +                printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n", +                       dev->name, SK_ADDR); +                return -1; +            } + +	    rom_addr = SK_ADDR; /* assign predefined address */ + +	    PRINTK(("## %s: NO Bootrom found \n", SK_NAME)); + +	    outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */ +	    outb(POS_ADDR, SK_POS3);       /* Set RAM address */ +	    outb(SK_RAM_ON, SK_POS2);      /* enable RAM */ +	} +	else if (rom_addr == SK_ADDR)  +	{ +            printk("%s: RAM + ROM are set to the same address %#08x\n" +                   "   Check configuration. Now switching off Boot_ROM\n", +                   SK_NAME, rom_addr); + +	    outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off*/ +	    outb(POS_ADDR, SK_POS3);       /* Set RAM address */ +	    outb(SK_RAM_ON, SK_POS2);      /* enable RAM */ +	} +	else +	{ +            PRINTK(("## %s: Found ROM at %#08x\n", SK_NAME, rom_addr)); +	    PRINTK(("## %s: Keeping Boot_ROM on\n", SK_NAME)); + +            if (sk_addr_flag)       /* No or Invalid SK_ADDR is defined */  +            { +                printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n", +                       dev->name, SK_ADDR); +                return -1; +            } + +	    rom_addr = SK_ADDR; + +	    outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */  +	    outb(POS_ADDR, SK_POS3);       /* Set RAM address */ +	    outb(SK_ROM_RAM_ON, SK_POS2);  /* RAM on, BOOT_ROM on */ +	} +    } +    else /* Don't keep Boot_ROM */ +    { +        PRINTK(("## %s: SK_BOOT_ROM is not set.\n", SK_NAME)); + +        if (sk_addr_flag)           /* No or Invalid SK_ADDR is defined */  +        { +            printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n", +                   dev->name, SK_ADDR); +            return -1; +        } + +	rom_addr = SK_rom_addr();          /* Try to find a Boot_ROM */ + +	/* IF we find a Boot_ROM disable it */ + +	outb(SK_ROM_RAM_OFF, SK_POS2);     /* Boot_ROM + RAM off */   + +        /* We found a Boot_ROM and it's gone. Set RAM address on +         * Boot_ROM address.  +         */  + +	if (rom_addr)  +	{ +            printk("%s: We found Boot_ROM at %#08x. Now setting RAM on" +                   "that address\n", SK_NAME, rom_addr); + +	    outb(POS_ADDR, SK_POS3);       /* Set RAM on Boot_ROM address */ +	} +	else /* We did not find a Boot_ROM, use predefined SK_ADDR for ram */ +	{ +            if (sk_addr_flag)       /* No or Invalid SK_ADDR is defined */  +            { +                printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n", +                       dev->name, SK_ADDR); +                return -1; +            } + +	    rom_addr = SK_ADDR; + +	    outb(POS_ADDR, SK_POS3);       /* Set RAM address */  +	} +	outb(SK_RAM_ON, SK_POS2);          /* enable RAM */ +    } + +#ifdef SK_DEBUG +    SK_print_pos(dev, "POS registers after ROM, RAM config"); +#endif + +    board = (SK_RAM *) rom_addr;  + +    /* Read in station address */ +    for (i = 0, j = 0; i < ETH_ALEN; i++, j+=2) +    { +	dev->dev_addr[i] = board->rom[j];           +    } + +    /* Check for manufacturer code */ +    if (!(dev->dev_addr[0] == SK_MAC0 && +	  dev->dev_addr[1] == SK_MAC1 && +	  dev->dev_addr[2] == SK_MAC2) ) +    { +        PRINTK(("## %s: We did not find SK_G16 at RAM location.\n", +                SK_NAME));  +	return ENODEV;                     /* NO SK_G16 found */ +    } + +    printk("%s: %s found at %#3x, HW addr: %#04x:%02x:%02x:%02x:%02x:%02x\n", +	    dev->name, +	    "Schneider & Koch Netcard", +	    (unsigned int) dev->base_addr, +	    dev->dev_addr[0], +	    dev->dev_addr[1], +	    dev->dev_addr[2], +	    dev->dev_addr[3], +	    dev->dev_addr[4], +	    dev->dev_addr[5]); + +    /* Allocate memory for private structure */ +    p = dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL); +    if (p == NULL) { +	   printk("%s: ERROR - no memory for driver data!\n", dev->name); +	   return -ENOMEM; +    } +    memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */ + +    /* Grab the I/O Port region */ +    request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16"); + +    /* Assign our Device Driver functions */ + +    dev->open                   = &SK_open; +    dev->stop                   = &SK_close; +    dev->hard_start_xmit        = &SK_send_packet; +    dev->get_stats              = &SK_get_stats; +    dev->set_multicast_list     = &set_multicast_list; + + +    /* Set the generic fields of the device structure */ + +    ether_setup(dev); +     +    dev->flags &= ~IFF_MULTICAST; + +    /* Initialize private structure */ + +    p->ram = (struct SK_ram *) rom_addr; /* Set dual ported RAM addr */ +    p->tmdhead = &(p->ram)->tmde[0];     /* Set TMD head */ +    p->rmdhead = &(p->ram)->rmde[0];     /* Set RMD head */ + +    /* Initialize buffer pointers */ + +    for (i = 0; i < TMDNUM; i++) +    { +	p->tmdbufs[i] = &(p->ram)->tmdbuf[i]; +    } + +    for (i = 0; i < RMDNUM; i++) +    { +	p->rmdbufs[i] = &(p->ram)->rmdbuf[i];  +    } + +#ifdef SK_DEBUG +    SK_print_pos(dev, "End of SK_probe"); +    SK_print_ram(dev); +#endif  + +    return 0;                            /* Initialization done */ + +} /* End of SK_probe() */ + + +/*-  + * Function       : SK_open + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/26 + * + * Description    : This function is called sometimes after booting  + *                  when ifconfig program is run. + * + *                  This function requests an IRQ, sets the correct + *                  IRQ in the card. Then calls SK_lance_init() to  + *                  init and start the LANCE chip. Then if everything is  + *                  ok returns with 0 (OK), which means SK_G16 is now + *                  opened and operational. + * + *                  (Called by dev_open() /net/inet/dev.c) + * + * Parameters     : I : struct device *dev - SK_G16 device structure + * Return Value   : 0 - Device opened + * Errors         : -EAGAIN - Open failed + * Globals        : irq2dev_map - which device uses which irq + * Side Effects   : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +static int SK_open(struct device *dev) +{ +    int i = 0; +    int irqval = 0; +    int ioaddr = dev->base_addr; + +    int irqtab[] = SK_IRQS;  + +    struct priv *p = (struct priv *)dev->priv; + +    PRINTK(("## %s: At beginning of SK_open(). CSR0: %#06x\n",  +           SK_NAME, SK_read_reg(CSR0))); + +    if (dev->irq == 0) /* Autoirq */ +    { +	i = 0; + +	/*  +         * Check if one IRQ out of SK_IRQS is free and install  +	 * interrupt handler. +	 * Most done by request_irq().  +	 * irqval: 0       - interrupt handler installed for IRQ irqtab[i] +	 *         -EBUSY  - interrupt busy  +         *         -EINVAL - irq > 15 or handler = NULL +	 */ + +	do +	{ +	  irqval = request_irq(irqtab[i], &SK_interrupt, 0, "sk_g16", NULL); +	  i++; +	} while (irqval && irqtab[i]); + +	if (irqval) /* We tried every possible IRQ but no success */ +	{ +	    printk("%s: unable to get an IRQ\n", dev->name); +	    return -EAGAIN; +	} + +	dev->irq = irqtab[--i];  +	 +	outb(i<<2, SK_POS4);           /* Set Card on probed IRQ */ + +    } +    else if (dev->irq == 2) /* IRQ2 is always IRQ9 */ +    { +	if (request_irq(9, &SK_interrupt, 0, "sk_g16", NULL)) +	{ +	    printk("%s: unable to get IRQ 9\n", dev->name); +	    return -EAGAIN; +	}  +	dev->irq = 9; +	 +        /*  +         * Now we set card on IRQ2. +         * This can be confusing, but remember that IRQ2 on the network +         * card is in reality IRQ9 +         */ +	outb(0x08, SK_POS4);           /* set card to IRQ2 */ + +    } +    else  /* Check IRQ as defined in Space.c */ +    { +	int i = 0; + +	/* check if IRQ free and valid. Then install Interrupt handler */ + +	if (request_irq(dev->irq, &SK_interrupt, 0, "sk_g16", NULL)) +	{ +	    printk("%s: unable to get selected IRQ\n", dev->name); +	    return -EAGAIN; +	} + +	switch(dev->irq) +	{ +	    case 3: i = 0; +		    break; +	    case 5: i = 1; +		    break; +	    case 2: i = 2; +		    break; +	    case 11:i = 3; +		    break; +	    default:  +		printk("%s: Preselected IRQ %d is invalid for %s boards", +		       dev->name, +		       dev->irq, +                       SK_NAME); +		return -EAGAIN; +	}       +   +	outb(i<<2, SK_POS4);           /* Set IRQ on card */ +    } + +    irq2dev_map[dev->irq] = dev;       /* Set IRQ as used by us */ +     +    printk("%s: Schneider & Koch G16 at %#3x, IRQ %d, shared mem at %#08x\n", +	    dev->name, (unsigned int)dev->base_addr,  +	    (int) dev->irq, (unsigned int) p->ram); + +    if (!(i = SK_lance_init(dev, 0)))  /* LANCE init OK? */ +    { + + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; + +#ifdef SK_DEBUG + +        /*  +         * This debug block tries to stop LANCE, +         * reinit LANCE with transmitter and receiver disabled, +         * then stop again and reinit with NORMAL_MODE +         */ + +        printk("## %s: After lance init. CSR0: %#06x\n",  +               SK_NAME, SK_read_reg(CSR0)); +        SK_write_reg(CSR0, CSR0_STOP); +        printk("## %s: LANCE stopped. CSR0: %#06x\n",  +               SK_NAME, SK_read_reg(CSR0)); +        SK_lance_init(dev, MODE_DTX | MODE_DRX); +        printk("## %s: Reinit with DTX + DRX off. CSR0: %#06x\n",  +               SK_NAME, SK_read_reg(CSR0)); +        SK_write_reg(CSR0, CSR0_STOP); +        printk("## %s: LANCE stopped. CSR0: %#06x\n",  +               SK_NAME, SK_read_reg(CSR0)); +        SK_lance_init(dev, MODE_NORMAL); +        printk("## %s: LANCE back to normal mode. CSR0: %#06x\n",  +               SK_NAME, SK_read_reg(CSR0)); +        SK_print_pos(dev, "POS regs before returning OK"); + +#endif /* SK_DEBUG */ +        +	return 0;              /* SK_open() is successful */ +    } +    else /* LANCE init failed */ +    { + +	PRINTK(("## %s: LANCE init failed: CSR0: %#06x\n",  +               SK_NAME, SK_read_reg(CSR0))); + +	dev->start = 0;        /* Device not ready */ +	return -EAGAIN; +    } + +} /* End of SK_open() */ + + +/*- + * Function       : SK_lance_init + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/26 + * + * Description    : Reset LANCE chip, fill RMD, TMD structures with + *                  start values and Start LANCE. + * + * Parameters     : I : struct device *dev - SK_G16 device structure + *                  I : int mode - put LANCE into "mode" see data-sheet for + *                                 more info. + * Return Value   : 0  - Init done + * Errors         : -1 - Init failed + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +static int SK_lance_init(struct device *dev, unsigned short mode) +{ +    int i; +    struct priv *p = (struct priv *) dev->priv;  +    struct tmd  *tmdp; +    struct rmd  *rmdp; + +    PRINTK(("## %s: At beginning of LANCE init. CSR0: %#06x\n",  +           SK_NAME, SK_read_reg(CSR0))); + +    /* Reset LANCE */ +    SK_reset_board(); + +    /* Initialize TMD's with start values */ +    p->tmdnum = 0;                   /* First descriptor for transmitting */  +    p->tmdlast = 0;                  /* First descriptor for reading stats */ + +    for (i = 0; i < TMDNUM; i++)     /* Init all TMD's */ +    { +	tmdp = p->tmdhead + i;  +    +	tmdp->u.buffer = (unsigned long) p->tmdbufs[i]; /* assign buffer */ +	 +	/* Mark TMD as start and end of packet */ +	tmdp->u.s.status = TX_STP | TX_ENP; +    } + + +    /* Initialize RMD's with start values */ + +    p->rmdnum = 0;                   /* First RMD which will be used */ +  +    for (i = 0; i < RMDNUM; i++)     /* Init all RMD's */ +    { +	rmdp = p->rmdhead + i; + +	 +	rmdp->u.buffer = (unsigned long) p->rmdbufs[i]; /* assign buffer */ +	 +	/*  +         * LANCE must be owner at beginning so that he can fill in  +	 * receiving packets, set status and release RMD  +	 */ + +	rmdp->u.s.status = RX_OWN;   + +	rmdp->blen = -PKT_BUF_SZ;    /* Buffer Size in a two's complement */ + +	rmdp->mlen = 0;              /* init message length */        +	 +    } + +    /* Fill LANCE Initialize Block */ + +    (p->ram)->ib.mode = mode;        /* Set operation mode */ + +    for (i = 0; i < ETH_ALEN; i++)   /* Set physical address */ +    { +	(p->ram)->ib.paddr[i] = dev->dev_addr[i];  +    } + +    for (i = 0; i < 8; i++)          /* Set multicast, logical address */ +    { +	(p->ram)->ib.laddr[i] = 0;   /* We do not use logical addressing */ +    }  + +    /* Set ring descriptor pointers and set number of descriptors */ + +    (p->ram)->ib.rdrp = (int)  p->rmdhead | RMDNUMMASK; +    (p->ram)->ib.tdrp = (int)  p->tmdhead | TMDNUMMASK; + +    /* Prepare LANCE Control and Status Registers */ + +    cli(); + +    SK_write_reg(CSR3, CSR3_ACON);   /* Ale Control !!!THIS MUST BE SET!!!! */ +  +    /*  +     * LANCE addresses the RAM from 0x0000 to 0x3fbf and has no access to +     * PC Memory locations. +     * +     * In structure SK_ram is defined that the first thing in ram +     * is the initialization block. So his address is for LANCE always +     * 0x0000 +     * +     * CSR1 contains low order bits 15:0 of initialization block address +     * CSR2 is built of:  +     *    7:0  High order bits 23:16 of initialization block address +     *   15:8  reserved, must be 0 +     */ +     +    /* Set initialization block address (must be on word boundary) */ +    SK_write_reg(CSR1, 0);          /* Set low order bits 15:0 */ +    SK_write_reg(CSR2, 0);          /* Set high order bits 23:16 */  +     + +    PRINTK(("## %s: After setting CSR1-3. CSR0: %#06x\n",  +           SK_NAME, SK_read_reg(CSR0))); + +    /* Initialize LANCE */ + +    /*  +     * INIT = Initialize, when set, causes the LANCE to begin the +     * initialization procedure and access the Init Block. +     */ + +    SK_write_reg(CSR0, CSR0_INIT);  + +    sti(); + +    /* Wait until LANCE finished initialization */ +     +    SK_set_RAP(CSR0);              /* Register Address Pointer to CSR0 */ + +    for (i = 0; (i < 100) && !(SK_rread_reg() & CSR0_IDON); i++)  +	; /* Wait until init done or go ahead if problems (i>=100) */ + +    if (i >= 100) /* Something is wrong ! */ +    { +	printk("%s: can't init am7990, status: %04x " +	       "init_block: %#08x\n",  +		dev->name, (int) SK_read_reg(CSR0),  +		(unsigned int) &(p->ram)->ib); + +#ifdef SK_DEBUG +	SK_print_pos(dev, "LANCE INIT failed"); +	SK_print_dev(dev,"Device Structure:"); +#endif + +	return -1;                 /* LANCE init failed */ +    } + +    PRINTK(("## %s: init done after %d ticks\n", SK_NAME, i)); + +    /* Clear Initialize done, enable Interrupts, start LANCE */ + +    SK_write_reg(CSR0, CSR0_IDON | CSR0_INEA | CSR0_STRT); + +    PRINTK(("## %s: LANCE started. CSR0: %#06x\n", SK_NAME,  +            SK_read_reg(CSR0))); + +    return 0;                      /* LANCE is up and running */ + +} /* End of SK_lance_init() */ + + + +/*- + * Function       : SK_send_packet + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/27 + * + * Description    : Writes an socket buffer into a transmit descriptor + *                  and starts transmission. + * + * Parameters     : I : struct sk_buff *skb - packet to transfer + *                  I : struct device *dev  - SK_G16 device structure + * Return Value   : 0 - OK + *                  1 - Could not transmit (dev_queue_xmit will queue it) + *                      and try to sent it later + * Globals        : None + * Side Effects   : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +static int SK_send_packet(struct sk_buff *skb, struct device *dev) +{ +    struct priv *p = (struct priv *) dev->priv; +    struct tmd *tmdp; + +    if (dev->tbusy) +    { +	/* if Transmitter more than 150ms busy -> time_out */ + +	int tickssofar = jiffies - dev->trans_start; +	if (tickssofar < 15) +	{ +	    return 1;                    /* We have to try transmit later */ +	} + +	printk("%s: xmitter timed out, try to restart!\n", dev->name); + +	SK_lance_init(dev, MODE_NORMAL); /* Reinit LANCE */ + +	dev->tbusy = 0;                  /* Clear Transmitter flag */ + +	dev->trans_start = jiffies;      /* Mark Start of transmission */ + +    } + +    /*  +     * If some upper Layer thinks we missed a transmit done interrupt +     * we are passed NULL. +     * (dev_queue_xmit net/inet/dev.c  +     */ + +    if (skb == NULL) +    { +        /*  +         * Dequeue packets from transmit queue and send them. +         */ +	dev_tint(dev);  +                     +	return 0; +    } +     +    PRINTK2(("## %s: SK_send_packet() called, CSR0 %#04x.\n",  +	    SK_NAME, SK_read_reg(CSR0))); + + +    /*  +     * Block a timer-based transmit from overlapping.  +     * This means check if we are already in.  +     */ + +    if (set_bit(0, (void *) &dev->tbusy) != 0) /* dev->tbusy already set ? */  +    { +	printk("%s: Transmitter access conflict.\n", dev->name); +    } +    else +    { +	/* Evaluate Packet length */ +	short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;  +        +	tmdp = p->tmdhead + p->tmdnum; /* Which descriptor for transmitting */ + +	/* Fill in Transmit Message Descriptor */ + +	/* Copy data into dual ported ram */ + +	memcpy((char *) (tmdp->u.buffer & 0x00ffffff), (char *)skb->data, +	       skb->len); + +	tmdp->blen = -len;            /* set length to transmit */ + +	/*  +	 * Packet start and end is always set because we use the maximum +	 * packet length as buffer length. +	 * Relinquish ownership to LANCE +	 */ + +	tmdp->u.s.status = TX_OWN | TX_STP | TX_ENP; +	 +	/* Start Demand Transmission */ +	SK_write_reg(CSR0, CSR0_TDMD | CSR0_INEA); + +	dev->trans_start = jiffies;   /* Mark start of transmission */ + +	/* Set pointer to next transmit buffer */ +	p->tmdnum++;  +	p->tmdnum &= TMDNUM-1;  + +	/* Do we own the next transmit buffer ? */ +	if (! ((p->tmdhead + p->tmdnum)->u.s.status & TX_OWN) ) +	{ +	   /*  +	    * We own next buffer and are ready to transmit, so +	    * clear busy flag +	    */ +	   dev->tbusy = 0; +	} +    } +    dev_kfree_skb(skb, FREE_WRITE); +    return 0;   +} /* End of SK_send_packet */ + + +/*- + * Function       : SK_interrupt + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/27 + * + * Description    : SK_G16 interrupt handler which checks for LANCE + *                  Errors, handles transmit and receive interrupts + * + * Parameters     : I : int irq, void *dev_id, struct pt_regs * regs - + * Return Value   : None + * Errors         : None + * Globals        : None + * Side Effects   : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +static void SK_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +    int csr0; +    struct device *dev = (struct device *) irq2dev_map[irq]; +    struct priv *p = (struct priv *) dev->priv; + + +    PRINTK2(("## %s: SK_interrupt(). status: %#06x\n",  +            SK_NAME, SK_read_reg(CSR0))); + +    if (dev == NULL) +    { +	printk("SK_interrupt(): IRQ %d for unknown device.\n", irq); +    } +     + +    if (dev->interrupt) +    { +	printk("%s: Re-entering the interrupt handler.\n", dev->name); +    } + +    csr0 = SK_read_reg(CSR0);      /* store register for checking */ + +    dev->interrupt = 1;            /* We are handling an interrupt */ + +    /*  +     * Acknowledge all of the current interrupt sources, disable       +     * Interrupts (INEA = 0)  +     */ + +    SK_write_reg(CSR0, csr0 & CSR0_CLRALL);  + +    if (csr0 & CSR0_ERR) /* LANCE Error */ +    { +	printk("%s: error: %04x\n", dev->name, csr0); +       +        if (csr0 & CSR0_MISS)      /* No place to store packet ? */ +        {  +            p->stats.rx_dropped++; +        } +    } + +    if (csr0 & CSR0_RINT)          /* Receive Interrupt (packet arrived) */  +    { +	SK_rxintr(dev);  +    } + +    if (csr0 & CSR0_TINT)          /* Transmit interrupt (packet sent) */ +    { +	SK_txintr(dev); +    } + +    SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */ + +    dev->interrupt = 0;            /* We are out */ +} /* End of SK_interrupt() */  + + +/*- + * Function       : SK_txintr + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/27 + * + * Description    : After sending a packet we check status, update + *                  statistics and relinquish ownership of transmit  + *                  descriptor ring. + * + * Parameters     : I : struct device *dev - SK_G16 device structure + * Return Value   : None + * Errors         : None + * Globals        : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +static void SK_txintr(struct device *dev) +{ +    int tmdstat; +    struct tmd *tmdp; +    struct priv *p = (struct priv *) dev->priv; + + +    PRINTK2(("## %s: SK_txintr() status: %#06x\n",  +            SK_NAME, SK_read_reg(CSR0))); + +    tmdp = p->tmdhead + p->tmdlast;     /* Which buffer we sent at last ? */ + +    /* Set next buffer */ +    p->tmdlast++; +    p->tmdlast &= TMDNUM-1; + +    tmdstat = tmdp->u.s.status & 0xff00; /* filter out status bits 15:08 */ + +    /*  +     * We check status of transmitted packet. +     * see LANCE data-sheet for error explanation +     */ +    if (tmdstat & TX_ERR) /* Error occurred */ +    { +	printk("%s: TX error: %04x %04x\n", dev->name, (int) tmdstat, +		(int) tmdp->status2); + +	if (tmdp->status2 & TX_TDR)    /* TDR problems? */ +	{ +	    printk("%s: tdr-problems \n", dev->name); +	} + +	if (tmdp->status2 & TX_RTRY)   /* Failed in 16 attempts to transmit ? */ +            p->stats.tx_aborted_errors++;    +        if (tmdp->status2 & TX_LCOL)   /* Late collision ? */ +            p->stats.tx_window_errors++;  +	if (tmdp->status2 & TX_LCAR)   /* Loss of Carrier ? */   +            p->stats.tx_carrier_errors++; +        if (tmdp->status2 & TX_UFLO)   /* Underflow error ? */ +        { +            p->stats.tx_fifo_errors++; + +            /*  +             * If UFLO error occurs it will turn transmitter of. +             * So we must reinit LANCE +             */ + +            SK_lance_init(dev, MODE_NORMAL); +        } +	 +	p->stats.tx_errors++; + +	tmdp->status2 = 0;             /* Clear error flags */ +    } +    else if (tmdstat & TX_MORE)        /* Collisions occurred ? */ +    { +        /*  +         * Here I have a problem. +         * I only know that there must be one or up to 15 collisions. +         * That's why TX_MORE is set, because after 16 attempts TX_RTRY +         * will be set which means couldn't send packet aborted transfer. +         * +         * First I did not have this in but then I thought at minimum +         * we see that something was not ok. +         * If anyone knows something better than this to handle this +         * please report it. (see Email addresses in the README file) +         */  + +        p->stats.collisions++;  +    } +    else   /* Packet sent without any problems */ +    { +        p->stats.tx_packets++;  +    } + +    /*  +     * We mark transmitter not busy anymore, because now we have a free +     * transmit descriptor which can be filled by SK_send_packet and +     * afterwards sent by the LANCE +     */ + +    dev->tbusy = 0;  + +    /*  +     * mark_bh(NET_BH); +     * This will cause net_bh() to run after this interrupt handler. +     * +     * The function which do handle slow IRQ parts is do_bottom_half() +     * which runs at normal kernel priority, that means all interrupt are +     * enabled. (see kernel/irq.c) +     *   +     * net_bh does something like this: +     *  - check if already in net_bh +     *  - try to transmit something from the send queue +     *  - if something is in the receive queue send it up to higher  +     *    levels if it is a known protocol +     *  - try to transmit something from the send queue +     */ + +    mark_bh(NET_BH);  + +} /* End of SK_txintr() */ + + +/*- + * Function       : SK_rxintr + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/27 + * + * Description    : Buffer sent, check for errors, relinquish ownership + *                  of the receive message descriptor.  + * + * Parameters     : I : SK_G16 device structure + * Return Value   : None + * Globals        : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +static void SK_rxintr(struct device *dev) +{ + +    struct rmd *rmdp; +    int rmdstat; +    struct priv *p = (struct priv *) dev->priv; + +    PRINTK2(("## %s: SK_rxintr(). CSR0: %#06x\n",  +            SK_NAME, SK_read_reg(CSR0))); + +    rmdp = p->rmdhead + p->rmdnum; + +    /* As long as we own the next entry, check status and send +     * it up to higher layer  +     */ + +    while (!( (rmdstat = rmdp->u.s.status) & RX_OWN)) +    { +	/*  +         * Start and end of packet must be set, because we use  +	 * the ethernet maximum packet length (1518) as buffer size. +	 *  +	 * Because our buffers are at maximum OFLO and BUFF errors are +	 * not to be concerned (see Data sheet) +	 */ + +	if ((rmdstat & (RX_STP | RX_ENP)) != (RX_STP | RX_ENP)) +	{ +	    /* Start of a frame > 1518 Bytes ? */ + +	    if (rmdstat & RX_STP)  +	    { +		p->stats.rx_errors++;        /* bad packet received */ +		p->stats.rx_length_errors++; /* packet too long */ + +		printk("%s: packet too long\n", dev->name); +	    } +	     +	    /*  +             * All other packets will be ignored until a new frame with +	     * start (RX_STP) set follows. +	     *  +	     * What we do is just give descriptor free for new incoming +	     * packets.  +	     */ + +	    rmdp->u.s.status = RX_OWN;      /* Relinquish ownership to LANCE */  + +	} +	else if (rmdstat & RX_ERR)          /* Receive Error ? */ +	{ +	    printk("%s: RX error: %04x\n", dev->name, (int) rmdstat); +	     +	    p->stats.rx_errors++; + +	    if (rmdstat & RX_FRAM) p->stats.rx_frame_errors++; +	    if (rmdstat & RX_CRC)  p->stats.rx_crc_errors++; + +	    rmdp->u.s.status = RX_OWN;      /* Relinquish ownership to LANCE */ + +	} +	else /* We have a packet which can be queued for the upper layers */ +	{ + +	    int len = (rmdp->mlen & 0x0fff);  /* extract message length from receive buffer */ +	    struct sk_buff *skb; + +	    skb = dev_alloc_skb(len+2); /* allocate socket buffer */  + +	    if (skb == NULL)                /* Could not get mem ? */ +	    { +     +		/*  +                 * Couldn't allocate sk_buffer so we give descriptor back +		 * to Lance, update statistics and go ahead. +		 */ + +		rmdp->u.s.status = RX_OWN;  /* Relinquish ownership to LANCE */ +		printk("%s: Couldn't allocate sk_buff, deferring packet.\n", +		       dev->name); +		p->stats.rx_dropped++; + +		break;                      /* Jump out */ +	    } +	     +	    /* Prepare sk_buff to queue for upper layers */ + +	    skb->dev = dev; +	    skb_reserve(skb,2);		/* Align IP header on 16 byte boundary */ +	     +	    /*  +             * Copy data out of our receive descriptor into sk_buff. +	     * +	     * (rmdp->u.buffer & 0x00ffffff) -> get address of buffer and  +	     * ignore status fields)  +	     */ + +	    memcpy(skb_put(skb,len), (unsigned char *) (rmdp->u.buffer & 0x00ffffff), +		   len); + + +	    /*  +             * Notify the upper protocol layers that there is another packet +	     * to handle +	     * +	     * netif_rx() always succeeds. see /net/inet/dev.c for more. +	     */ + +	    skb->protocol=eth_type_trans(skb,dev); +	    netif_rx(skb);                 /* queue packet and mark it for processing */ +	    +	    /*  +             * Packet is queued and marked for processing so we +	     * free our descriptor and update statistics  +	     */ + +	    rmdp->u.s.status = RX_OWN; +	    p->stats.rx_packets++; + + +	    p->rmdnum++; +	    p->rmdnum %= RMDNUM; + +	    rmdp = p->rmdhead + p->rmdnum; +	} +    } +} /* End of SK_rxintr() */ + + +/*- + * Function       : SK_close + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/26 + * + * Description    : close gets called from dev_close() and should + *                  deinstall the card (free_irq, mem etc). + * + * Parameters     : I : struct device *dev - our device structure + * Return Value   : 0 - closed device driver + * Errors         : None + * Globals        : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +/* I have tried to set BOOT_ROM on and RAM off but then, after a 'ifconfig + * down' the system stops. So I don't shut set card to init state. + */ + +static int SK_close(struct device *dev) +{ + +    PRINTK(("## %s: SK_close(). CSR0: %#06x\n",  +           SK_NAME, SK_read_reg(CSR0))); + +    dev->tbusy = 1;                /* Transmitter busy */ +    dev->start = 0;                /* Card down */ + +    printk("%s: Shutting %s down CSR0 %#06x\n", dev->name, SK_NAME,  +           (int) SK_read_reg(CSR0)); + +    SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */ + +    free_irq(dev->irq, NULL);      /* Free IRQ */ +    irq2dev_map[dev->irq] = 0;     /* Mark IRQ as unused */ + +    return 0; /* always succeed */ +     +} /* End of SK_close() */ + + +/*- + * Function       : SK_get_stats + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/26 + * + * Description    : Return current status structure to upper layers. + *                  It is called by sprintf_stats (dev.c). + * + * Parameters     : I : struct device *dev   - our device structure + * Return Value   : struct enet_statistics * - our current statistics + * Errors         : None + * Side Effects   : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +static struct enet_statistics *SK_get_stats(struct device *dev) +{ + +    struct priv *p = (struct priv *) dev->priv; + +    PRINTK(("## %s: SK_get_stats(). CSR0: %#06x\n",  +           SK_NAME, SK_read_reg(CSR0))); + +    return &p->stats;             /* Return Device status */ + +} /* End of SK_get_stats() */ + + +/*- + * Function       : set_multicast_list + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/26 + * + * Description    : This function gets called when a program performs + *                  a SIOCSIFFLAGS call. Ifconfig does this if you call + *                  'ifconfig [-]allmulti' which enables or disables the + *                  Promiscuous mode. + *                  Promiscuous mode is when the Network card accepts all + *                  packets, not only the packets which match our MAC  + *                  Address. It is useful for writing a network monitor, + *                  but it is also a security problem. You have to remember + *                  that all information on the net is not encrypted. + * + * Parameters     : I : struct device *dev - SK_G16 device Structure + * Return Value   : None + * Errors         : None + * Globals        : None + * Update History : + *     YY/MM/DD  uid  Description + *     95/10/18  ACox  New multicast calling scheme +-*/ + + +/* Set or clear the multicast filter for SK_G16. + */ + +static void set_multicast_list(struct device *dev) +{ + +    if (dev->flags&IFF_PROMISC) +    { +	/* Reinitialize LANCE with MODE_PROM set */ +	SK_lance_init(dev, MODE_PROM); +    } +    else if (dev->mc_count==0 && !(dev->flags&IFF_ALLMULTI)) +    { +	/* Reinitialize LANCE without MODE_PROM */ +	SK_lance_init(dev, MODE_NORMAL); +    } +    else +    { +	/* Multicast with logical address filter on */ +	/* Reinitialize LANCE without MODE_PROM */ +	SK_lance_init(dev, MODE_NORMAL); +	 +	/* Not implemented yet. */ +    } +} /* End of set_multicast_list() */ + + + +/*- + * Function       : SK_rom_addr + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/06/01 + * + * Description    : Try to find a Boot_ROM at all possible locations + * + * Parameters     : None + * Return Value   : Address where Boot_ROM is + * Errors         : 0 - Did not find Boot_ROM + * Globals        : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +unsigned int SK_rom_addr(void) +{ +    int i,j; +    int rom_found = 0; +    unsigned int rom_location[] = SK_BOOT_ROM_LOCATIONS; +    unsigned char rom_id[] = SK_BOOT_ROM_ID; +    unsigned char *test_byte; + +    /* Autodetect Boot_ROM */ +    PRINTK(("## %s: Autodetection of Boot_ROM\n", SK_NAME)); + +    for (i = 0; (rom_location[i] != 0) && (rom_found == 0); i++) +    { +	 +	PRINTK(("##   Trying ROM location %#08x", rom_location[i])); +	 +	rom_found = 1;  +	for (j = 0; j < 6; j++) +	{ +	    test_byte = (unsigned char *) (rom_location[i]+j); +	    PRINTK((" %02x ", *test_byte)); + +	    if(!(*test_byte == rom_id[j])) +	    { +		rom_found = 0; +	    }  +	} +	PRINTK(("\n")); +    } + +    if (rom_found == 1) +    { +	PRINTK(("## %s: Boot_ROM found at %#08x\n",  +               SK_NAME, rom_location[(i-1)])); + +	return (rom_location[--i]); +    } +    else +    { +	PRINTK(("%s: No Boot_ROM found\n", SK_NAME)); +	return 0; +    } +} /* End of SK_rom_addr() */ + + + +/* LANCE access functions  + * + * ! CSR1-3 can only be accessed when in CSR0 the STOP bit is set ! + */ + + +/*- + * Function       : SK_reset_board + * + * Author         : Patrick J.D. Weichmann + * + * Date Created   : 94/05/25 + * + * Description    : This function resets SK_G16 and all components, but + *                  POS registers are not changed + * + * Parameters     : None + * Return Value   : None + * Errors         : None + * Globals        : SK_RAM *board - SK_RAM structure pointer + * + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +void SK_reset_board(void) +{ +    int i; + +    SK_PORT = 0x00;           /* Reset active */ +    for (i = 0; i < 10 ; i++) /* Delay min 5ms */ +	; +    SK_PORT = SK_RESET;       /* Set back to normal operation */ + +} /* End of SK_reset_board() */ + + +/*- + * Function       : SK_set_RAP + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/25 + * + * Description    : Set LANCE Register Address Port to register + *                  for later data transfer. + * + * Parameters     : I : reg_number - which CSR to read/write from/to + * Return Value   : None + * Errors         : None + * Globals        : SK_RAM *board - SK_RAM structure pointer + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +void SK_set_RAP(int reg_number) +{ +    SK_IOREG = reg_number; +    SK_PORT  = SK_RESET | SK_RAP | SK_WREG; +    SK_IOCOM = SK_DOIO; + +    while (SK_PORT & SK_IORUN)  +	; +} /* End of SK_set_RAP() */ + + +/*- + * Function       : SK_read_reg + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/25 + * + * Description    : Set RAP and read data from a LANCE CSR register + * + * Parameters     : I : reg_number - which CSR to read from + * Return Value   : Register contents + * Errors         : None + * Globals        : SK_RAM *board - SK_RAM structure pointer + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +int SK_read_reg(int reg_number) +{ +    SK_set_RAP(reg_number); + +    SK_PORT  = SK_RESET | SK_RDATA | SK_RREG; +    SK_IOCOM = SK_DOIO; + +    while (SK_PORT & SK_IORUN) +	; +    return (SK_IOREG); + +} /* End of SK_read_reg() */ + + +/*- + * Function       : SK_rread_reg + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/28 + * + * Description    : Read data from preseted register. + *                  This function requires that you know which + *                  Register is actually set. Be aware that CSR1-3 + *                  can only be accessed when in CSR0 STOP is set. + * + * Return Value   : Register contents + * Errors         : None + * Globals        : SK_RAM *board - SK_RAM structure pointer + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +int SK_rread_reg(void) +{ +    SK_PORT  = SK_RESET | SK_RDATA | SK_RREG; + +    SK_IOCOM = SK_DOIO; + +    while (SK_PORT & SK_IORUN) +	; +    return (SK_IOREG); + +} /* End of SK_rread_reg() */ + + +/*- + * Function       : SK_write_reg + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/25 + * + * Description    : This function sets the RAP then fills in the + *                  LANCE I/O Reg and starts Transfer to LANCE. + *                  It waits until transfer has ended which is max. 7 ms + *                  and then it returns. + * + * Parameters     : I : reg_number - which CSR to write to + *                  I : value      - what value to fill into register  + * Return Value   : None + * Errors         : None + * Globals        : SK_RAM *board - SK_RAM structure pointer + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +void SK_write_reg(int reg_number, int value) +{ +    SK_set_RAP(reg_number); + +    SK_IOREG = value; +    SK_PORT  = SK_RESET | SK_RDATA | SK_WREG; +    SK_IOCOM = SK_DOIO; + +    while (SK_PORT & SK_IORUN) +	; +} /* End of SK_write_reg */ + + + +/*  + * Debugging functions + * ------------------- + */ + +/*- + * Function       : SK_print_pos + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/25 + * + * Description    : This function prints out the 4 POS (Programmable + *                  Option Select) Registers. Used mainly to debug operation. + * + * Parameters     : I : struct device *dev - SK_G16 device structure + *                  I : char * - Text which will be printed as title + * Return Value   : None + * Errors         : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +void SK_print_pos(struct device *dev, char *text) +{ +    int ioaddr = dev->base_addr; + +    unsigned char pos0 = inb(SK_POS0), +		  pos1 = inb(SK_POS1), +		  pos2 = inb(SK_POS2), +		  pos3 = inb(SK_POS3), +		  pos4 = inb(SK_POS4); + + +    printk("## %s: %s.\n" +           "##   pos0=%#4x pos1=%#4x pos2=%#04x pos3=%#08x pos4=%#04x\n", +           SK_NAME, text, pos0, pos1, pos2, (pos3<<14), pos4); + +} /* End of SK_print_pos() */ + + + +/*- + * Function       : SK_print_dev + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/05/25 + * + * Description    : This function simply prints out the important fields + *                  of the device structure. + * + * Parameters     : I : struct device *dev  - SK_G16 device structure + *                  I : char *text - Title for printing + * Return Value   : None + * Errors         : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +void SK_print_dev(struct device *dev, char *text) +{ +    if (dev == NULL) +    { +	printk("## %s: Device Structure. %s\n", SK_NAME, text); +	printk("## DEVICE == NULL\n"); +    } +    else +    { +	printk("## %s: Device Structure. %s\n", SK_NAME, text); +	printk("## Device Name: %s Base Address: %#06lx IRQ: %d\n",  +               dev->name, dev->base_addr, dev->irq); +	        +	printk("##   FLAGS: start: %d tbusy: %ld int: %d\n",  +               dev->start, dev->tbusy, dev->interrupt); + +	printk("## next device: %#08x init function: %#08x\n",  +              (int) dev->next, (int) dev->init); +    } + +} /* End of SK_print_dev() */ + + + +/*- + * Function       : SK_print_ram + * Author         : Patrick J.D. Weichmann + * Date Created   : 94/06/02 + * + * Description    : This function is used to check how are things set up + *                  in the 16KB RAM. Also the pointers to the receive and  + *                  transmit descriptor rings and rx and tx buffers locations. + *                  It contains a minor bug in printing, but has no effect to the values + *                  only newlines are not correct. + * + * Parameters     : I : struct device *dev - SK_G16 device structure + * Return Value   : None + * Errors         : None + * Globals        : None + * Update History : + *     YY/MM/DD  uid  Description +-*/ + +void SK_print_ram(struct device *dev) +{ + +    int i;     +    struct priv *p = (struct priv *) dev->priv; + +    printk("## %s: RAM Details.\n" +           "##   RAM at %#08x tmdhead: %#08x rmdhead: %#08x initblock: %#08x\n", +           SK_NAME,  +           (unsigned int) p->ram, +           (unsigned int) p->tmdhead,  +           (unsigned int) p->rmdhead,  +           (unsigned int) &(p->ram)->ib); +            +    printk("##   "); + +    for(i = 0; i < TMDNUM; i++) +    { +           if (!(i % 3)) /* Every third line do a newline */ +           { +               printk("\n##   "); +           } +        printk("tmdbufs%d: %#08x ", (i+1), (int) p->tmdbufs[i]); +    } +    printk("##   "); + +    for(i = 0; i < RMDNUM; i++) +    { +         if (!(i % 3)) /* Every third line do a newline */ +           { +               printk("\n##   "); +           } +        printk("rmdbufs%d: %#08x ", (i+1), (int) p->rmdbufs[i]); +    }  +    printk("\n"); + +} /* End of SK_print_ram() */ + diff --git a/linux/src/drivers/net/sk_g16.h b/linux/src/drivers/net/sk_g16.h new file mode 100644 index 0000000..31ae19a --- /dev/null +++ b/linux/src/drivers/net/sk_g16.h @@ -0,0 +1,164 @@ +/*- + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * Module         : sk_g16.h + * Version        : $Revision: 1.1.4.1 $   + * + * Author         : M.Hipp (mhipp@student.uni-tuebingen.de) + * changes by     : Patrick J.D. Weichmann + * + * Date Created   : 94/05/25 + * + * Description    : In here are all necessary definitions of   + *                  the am7990 (LANCE) chip used for writing a + *                  network device driver which uses this chip  + * +-*/ + +#ifndef SK_G16_H + +#define SK_G16_H + + +/* + * 	Control and Status Register 0 (CSR0) bit definitions + * + * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write) + * + */ + +#define CSR0_ERR	0x8000	/* Error summary (R) */ +#define CSR0_BABL	0x4000	/* Babble transmitter timeout error (RC) */ +#define CSR0_CERR	0x2000	/* Collision Error (RC) */ +#define CSR0_MISS	0x1000	/* Missed packet (RC) */ +#define CSR0_MERR	0x0800	/* Memory Error  (RC) */  +#define CSR0_RINT	0x0400	/* Receiver Interrupt (RC) */ +#define CSR0_TINT       0x0200	/* Transmit Interrupt (RC) */  +#define CSR0_IDON	0x0100	/* Initialization Done (RC) */ +#define CSR0_INTR	0x0080	/* Interrupt Flag (R) */ +#define CSR0_INEA	0x0040	/* Interrupt Enable (RW) */ +#define CSR0_RXON	0x0020	/* Receiver on (R) */ +#define CSR0_TXON	0x0010  /* Transmitter on (R) */ +#define CSR0_TDMD	0x0008	/* Transmit Demand (RS) */ +#define CSR0_STOP	0x0004 	/* Stop (RS) */ +#define CSR0_STRT	0x0002	/* Start (RS) */ +#define CSR0_INIT	0x0001	/* Initialize (RS) */ + +#define CSR0_CLRALL     0x7f00  /* mask for all clearable bits */ + +/* + *    Control and Status Register 3 (CSR3) bit definitions + * + */ + +#define CSR3_BSWAP	0x0004	/* Byte Swap (RW) */ +#define CSR3_ACON	0x0002  /* ALE Control (RW) */ +#define CSR3_BCON	0x0001	/* Byte Control (RW) */ + +/* + *	Initialization Block Mode operation Bit Definitions. + */ + +#define MODE_PROM	0x8000	/* Promiscuous Mode */ +#define MODE_INTL	0x0040  /* Internal Loopback */ +#define MODE_DRTY	0x0020  /* Disable Retry */  +#define MODE_COLL	0x0010	/* Force Collision */ +#define MODE_DTCR	0x0008	/* Disable Transmit CRC) */ +#define MODE_LOOP	0x0004	/* Loopback */ +#define MODE_DTX	0x0002	/* Disable the Transmitter */  +#define MODE_DRX	0x0001  /* Disable the Receiver */ + +#define MODE_NORMAL 	0x0000  /* Normal operation mode */ + +/* + * 	Receive message descriptor status bit definitions. + */ + +#define RX_OWN		0x80	/* Owner bit 0 = host, 1 = lance */ +#define RX_ERR		0x40	/* Error Summary */ +#define RX_FRAM		0x20	/* Framing Error */ +#define RX_OFLO		0x10	/* Overflow Error */ +#define RX_CRC		0x08	/* CRC Error */  +#define RX_BUFF		0x04	/* Buffer Error */ +#define RX_STP		0x02	/* Start of Packet */ +#define RX_ENP		0x01	/* End of Packet */ + + +/* + *	Transmit message descriptor status bit definitions. + */ + +#define TX_OWN		0x80	/* Owner bit 0 = host, 1 = lance */ +#define TX_ERR		0x40    /* Error Summary */ +#define TX_MORE		0x10	/* More the 1 retry needed to Xmit */ +#define TX_ONE		0x08	/* One retry needed to Xmit */ +#define TX_DEF		0x04	/* Deferred */ +#define TX_STP 		0x02	/* Start of Packet */ +#define TX_ENP		0x01	/* End of Packet */ + +/* + *      Transmit status (2) (valid if TX_ERR == 1) + */ + +#define TX_BUFF 	0x8000  /* Buffering error (no ENP) */ +#define TX_UFLO 	0x4000  /* Underflow (late memory) */ +#define TX_LCOL 	0x1000  /* Late collision */ +#define TX_LCAR 	0x0400  /* Loss of Carrier */ +#define TX_RTRY 	0x0200  /* Failed after 16 retransmissions  */ +#define TX_TDR          0x003f  /* Time-domain-reflectometer-value */ + + +/*  + * Structures used for Communication with the LANCE  + */ + +/* LANCE Initialize Block */ + +struct init_block  +{ +  unsigned short mode;     /* Mode Register */ +  unsigned char  paddr[6]; /* Physical Address (MAC) */ +  unsigned char  laddr[8]; /* Logical Filter Address (not used) */ +  unsigned int   rdrp;     /* Receive Descriptor Ring pointer */ +  unsigned int   tdrp;     /* Transmit Descriptor Ring pointer */ +}; + + +/* Receive Message Descriptor Entry */ + +struct rmd  +{  +  union +  { +    unsigned long buffer;     /* Address of buffer */ +    struct  +    { +      unsigned char unused[3];  +      unsigned volatile char status;   /* Status Bits */ +    } s; +  } u; +  volatile short blen;        /* Buffer Length (two's complement) */ +  unsigned short mlen;        /* Message Byte Count */ +}; + + +/* Transmit Message Descriptor Entry */ + +struct tmd    +{ +  union  +  { +    unsigned long  buffer;    /* Address of buffer */ +    struct  +    { +      unsigned char unused[3]; +      unsigned volatile char status;   /* Status Bits */ +    } s; +  } u; +  unsigned short blen;             /* Buffer Length (two's complement) */ +  unsigned volatile short status2; /* Error Status Bits */ +}; + +#endif /* End of SK_G16_H */ diff --git a/linux/src/drivers/net/smc-ultra.c b/linux/src/drivers/net/smc-ultra.c new file mode 100644 index 0000000..f593aeb --- /dev/null +++ b/linux/src/drivers/net/smc-ultra.c @@ -0,0 +1,496 @@ +/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ +/* +	This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards. + +	Written 1993-1998 by Donald Becker. + +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +		Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	This driver uses the cards in the 8390-compatible mode. +	Most of the run-time complexity is handled by the generic code in +	8390.c.  The code in this file is responsible for + +		ultra_probe()	 	Detecting and initializing the card. +		ultra_probe1()	 + +		ultra_open()		The card-specific details of starting, stopping +		ultra_reset_8390()	and resetting the 8390 NIC core. +		ultra_close() + +		ultra_block_input()		Routines for reading and writing blocks of +		ultra_block_output()	packet buffer memory. +		ultra_pio_input() +		ultra_pio_output() + +	This driver enables the shared memory only when doing the actual data +	transfers to avoid a bug in early version of the card that corrupted +	data transferred by a AHA1542. + +	This driver now supports the programmed-I/O (PIO) data transfer mode of +	the EtherEZ. It does not use the non-8390-compatible "Altego" mode. +	That support (if available) is in smc-ez.c. + +	Changelog: + +	Paul Gortmaker	: multiple card support for module users. +	Donald Becker	: 4/17/96 PIO support, minor potential problems avoided. +	Donald Becker	: 6/6/96 correctly set auto-wrap bit. +*/ + +static const char *version = +	"smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <asm/io.h> +#include <asm/system.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include "8390.h" + +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int ultra_portlist[] = +{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0}; + +int ultra_probe(struct device *dev); +int ultra_probe1(struct device *dev, int ioaddr); + +static int ultra_open(struct device *dev); +static void ultra_reset_8390(struct device *dev); +static void ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +						int ring_page); +static void ultra_block_input(struct device *dev, int count, +						  struct sk_buff *skb, int ring_offset); +static void ultra_block_output(struct device *dev, int count, +							const unsigned char *buf, const int start_page); +static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +						int ring_page); +static void ultra_pio_input(struct device *dev, int count, +						  struct sk_buff *skb, int ring_offset); +static void ultra_pio_output(struct device *dev, int count, +							 const unsigned char *buf, const int start_page); +static int ultra_close_card(struct device *dev); + + +#define START_PG		0x00	/* First page of TX buffer */ + +#define ULTRA_CMDREG	0		/* Offset to ASIC command register. */ +#define	 ULTRA_RESET	0x80	/* Board reset, in ULTRA_CMDREG. */ +#define	 ULTRA_MEMENB	0x40	/* Enable the shared memory. */ +#define IOPD	0x02			/* I/O Pipe Data (16 bits), PIO operation. */ +#define IOPA	0x07			/* I/O Pipe Address for PIO operation. */ +#define ULTRA_NIC_OFFSET  16	/* NIC register offset from the base_addr. */ +#define ULTRA_IO_EXTENT 32 +#define EN0_ERWCNT		0x08	/* Early receive warning count. */ + +/*	Probe for the Ultra.  This looks like a 8013 with the station +	address PROM at I/O ports <base>+8 to <base>+13, with a checksum +	following. +*/ +#ifdef HAVE_DEVLIST +struct netdev_entry ultra_drv = +{"ultra", ultra_probe1, NETCARD_IO_EXTENT, netcard_portlist}; +#else + +int ultra_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return ultra_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; ultra_portlist[i]; i++) { +		int ioaddr = ultra_portlist[i]; +		if (check_region(ioaddr, ULTRA_IO_EXTENT)) +			continue; +		if (ultra_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +int ultra_probe1(struct device *dev, int ioaddr) +{ +	int i; +	int checksum = 0; +	const char *model_name; +	unsigned char eeprom_irq = 0; +	static unsigned version_printed = 0; +	/* Values from various config regs. */ +	unsigned char num_pages, irqreg, addr, piomode; +	unsigned char idreg = inb(ioaddr + 7); +	unsigned char reg4 = inb(ioaddr + 4) & 0x7f; + +	/* Check the ID nibble. */ +	if ((idreg & 0xF0) != 0x20 			/* SMC Ultra */ +		&& (idreg & 0xF0) != 0x40) 		/* SMC EtherEZ */ +		return ENODEV; + +	/* Select the station address register set. */ +	outb(reg4, ioaddr + 4); + +	for (i = 0; i < 8; i++) +		checksum += inb(ioaddr + 8 + i); +	if ((checksum & 0xff) != 0xFF) +		return ENODEV; + +	if (dev == NULL) +		dev = init_etherdev(0, 0); + +	if (ei_debug  &&  version_printed++ == 0) +		printk("%s", version); + +	model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ"; + +	printk("%s: %s at %#3x,", dev->name, model_name, ioaddr); + +	for (i = 0; i < 6; i++) +		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i)); + +	/* Switch from the station address to the alternate register set and +	   read the useful registers there. */ +	outb(0x80 | reg4, ioaddr + 4); + +	/* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ +	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); +	piomode = inb(ioaddr + 0x8); +	addr = inb(ioaddr + 0xb); +	irqreg = inb(ioaddr + 0xd); + +	/* Switch back to the station address register set so that the MS-DOS driver +	   can find the card after a warm boot. */ +	outb(reg4, ioaddr + 4); + +	if (dev->irq < 2) { +		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; +		int irq; + +		/* The IRQ bits are split. */ +		irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)]; + +		if (irq == 0) { +			printk(", failed to detect IRQ line.\n"); +			return -EAGAIN; +		} +		dev->irq = irq; +		eeprom_irq = 1; +	} + +	/* Allocate dev->priv and fill in 8390 specific dev fields. */ +	if (ethdev_init(dev)) { +		printk (", no memory for dev->priv.\n"); +                return -ENOMEM; +        } + +	/* OK, we are certain this is going to work.  Setup the device. */ +	request_region(ioaddr, ULTRA_IO_EXTENT, model_name); + +	/* The 8390 isn't at the base address, so fake the offset */ +	dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; + +	{ +		int addr_tbl[4] = {0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000}; +		short num_pages_tbl[4] = {0x20, 0x40, 0x80, 0xff}; + +		dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ; +		num_pages = num_pages_tbl[(addr >> 4) & 3]; +	} + +	ei_status.name = model_name; +	ei_status.word16 = 1; +	ei_status.tx_start_page = START_PG; +	ei_status.rx_start_page = START_PG + TX_PAGES; +	ei_status.stop_page = num_pages; + +	dev->rmem_start = dev->mem_start + TX_PAGES*256; +	dev->mem_end = dev->rmem_end +		= dev->mem_start + (ei_status.stop_page - START_PG)*256; + +	if (piomode) { +		printk(",%s IRQ %d programmed-I/O mode.\n", +			   eeprom_irq ? "EEPROM" : "assigned ", dev->irq); +		ei_status.block_input = &ultra_pio_input; +		ei_status.block_output = &ultra_pio_output; +		ei_status.get_8390_hdr = &ultra_pio_get_hdr; +	} else { +		printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ", +			   dev->irq, dev->mem_start, dev->mem_end-1); +		ei_status.block_input = &ultra_block_input; +		ei_status.block_output = &ultra_block_output; +		ei_status.get_8390_hdr = &ultra_get_8390_hdr; +	} +	ei_status.reset_8390 = &ultra_reset_8390; +	dev->open = &ultra_open; +	dev->stop = &ultra_close_card; +	NS8390_init(dev, 0); + +	return 0; +} + +static int +ultra_open(struct device *dev) +{ +	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ +	unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40, +							   0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, }; + +	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev)) +		return -EAGAIN; + +	outb(0x00, ioaddr);	/* Disable shared memory for safety. */ +	outb(0x80, ioaddr + 5); +	/* Set the IRQ line. */ +	outb(inb(ioaddr + 4) | 0x80, ioaddr + 4); +	outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13); +	outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4); + +	if (ei_status.block_input == &ultra_pio_input) { +		outb(0x11, ioaddr + 6);		/* Enable interrupts and PIO. */ +		outb(0x01, ioaddr + 0x19);  	/* Enable ring read auto-wrap. */ +	} else +		outb(0x01, ioaddr + 6);		/* Enable interrupts and memory. */ +	/* Set the early receive warning level in window 0 high enough not +	   to receive ERW interrupts. */ +	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); +	outb(0xff, dev->base_addr + EN0_ERWCNT); +	ei_open(dev); +	MOD_INC_USE_COUNT; +	return 0; +} + +static void +ultra_reset_8390(struct device *dev) +{ +	int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */ + +	outb(ULTRA_RESET, cmd_port); +	if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies); +	ei_status.txing = 0; + +	outb(0x00, cmd_port);	/* Disable shared memory for safety. */ +	outb(0x80, cmd_port + 5); +	if (ei_status.block_input == &ultra_pio_input) +		outb(0x11, cmd_port + 6);		/* Enable interrupts and PIO. */ +	else +		outb(0x01, cmd_port + 6);		/* Enable interrupts and memory. */ + +	if (ei_debug > 1) printk("reset done\n"); +	return; +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but +   we don't need to be concerned with ring wrap as the header will be at +   the start of a page, so we optimize accordingly. */ + +static void +ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ +	unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8); + +	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);	/* shmem on */ +#ifdef notdef +	/* Officially this is what we are doing, but the readl() is faster */ +	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); +#else +	((unsigned int*)hdr)[0] = readl(hdr_start); +#endif +	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */ +} + +/* Block input and output are easy on shared memory ethercards, the only +   complication is when the ring buffer wraps. */ + +static void +ultra_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +	unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG<<8); + +	/* Enable shared memory. */ +	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); + +	if (xfer_start + count > dev->rmem_end) { +		/* We must wrap the input move. */ +		int semi_count = dev->rmem_end - xfer_start; +		memcpy_fromio(skb->data, xfer_start, semi_count); +		count -= semi_count; +		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count); +	} else { +		/* Packet is in one chunk -- we can copy + cksum. */ +		eth_io_copy_and_sum(skb, xfer_start, count, 0); +	} + +	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET);	/* Disable memory. */ +} + +static void +ultra_block_output(struct device *dev, int count, const unsigned char *buf, +				int start_page) +{ +	unsigned long shmem = dev->mem_start + ((start_page - START_PG)<<8); + +	/* Enable shared memory. */ +	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); + +	memcpy_toio(shmem, buf, count); + +	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ +} + +/* The identical operations for programmed I/O cards. +   The PIO model is trivial to use: the 16 bit start address is written +   byte-sequentially to IOPA, with no intervening I/O operations, and the +   data is read or written to the IOPD data port. +   The only potential complication is that the address register is shared +   and must be always be rewritten between each read/write direction change. +   This is no problem for us, as the 8390 code ensures that we are single +   threaded. */ +static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +						int ring_page) +{ +	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ +	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */ +	outb(ring_page, ioaddr + IOPA); +	insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1); +} + +static void ultra_pio_input(struct device *dev, int count, +						  struct sk_buff *skb, int ring_offset) +{ +	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ +    char *buf = skb->data; + +	/* For now set the address again, although it should already be correct. */ +	outb(ring_offset, ioaddr + IOPA);	/* Set the address, LSB first. */ +	outb(ring_offset >> 8, ioaddr + IOPA); +	/* We know skbuffs are padded to at least word alignment. */ +	insw(ioaddr + IOPD, buf, (count+1)>>1); +} + +static void ultra_pio_output(struct device *dev, int count, +							const unsigned char *buf, const int start_page) +{ +	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ +	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */ +	outb(start_page, ioaddr + IOPA); +	/* An extra odd byte is OK here as well. */ +	outsw(ioaddr + IOPD, buf, (count+1)>>1); +} + +static int +ultra_close_card(struct device *dev) +{ +	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */ + +	dev->start = 0; +	dev->tbusy = 1; + +	if (ei_debug > 1) +		printk("%s: Shutting down ethercard.\n", dev->name); + +	outb(0x00, ioaddr + 6);		/* Disable interrupts. */ +	free_irq(dev->irq, dev); +	irq2dev_map[dev->irq] = 0; + +	NS8390_init(dev, 0); + +	/* We should someday disable shared memory and change to 8-bit mode +	   "just in case"... */ + +	MOD_DEC_USE_COUNT; + +	return 0; +} + + +#ifdef MODULE +#define MAX_ULTRA_CARDS	4	/* Max number of Ultra cards per module */ +#define NAMELEN		8	/* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, }; +static struct device dev_ultra[MAX_ULTRA_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int io[MAX_ULTRA_CARDS] = { 0, }; +static int irq[MAX_ULTRA_CARDS]  = { 0, }; + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { +		struct device *dev = &dev_ultra[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->init = ultra_probe; +		if (io[this_dev] == 0)  { +			if (this_dev != 0) break; /* only autoprobe 1st one */ +			printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n"); +		} +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); +			if (found != 0) return 0;	/* Got at least one. */ +			return -ENXIO; +		} +		found++; +	} + +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { +		struct device *dev = &dev_ultra[this_dev]; +		if (dev->priv != NULL) { +			/* NB: ultra_close_card() does free_irq + irq2dev */ +			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; +			kfree(dev->priv); +			dev->priv = NULL; +			release_region(ioaddr, ULTRA_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/inet -c smc-ultra.c" + *  version-control: t + *  kept-new-versions: 5 + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/smc-ultra32.c b/linux/src/drivers/net/smc-ultra32.c new file mode 100644 index 0000000..6cde4c2 --- /dev/null +++ b/linux/src/drivers/net/smc-ultra32.c @@ -0,0 +1,413 @@ +/* 	smc-ultra32.c: An SMC Ultra32 EISA ethernet driver for linux. + +Sources: + +	This driver is based on (cloned from) the ISA SMC Ultra driver +	written by Donald Becker. Modifications to support the EISA +	version of the card by Paul Gortmaker and Leonard N. Zubkoff. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +Theory of Operation: + +	The SMC Ultra32C card uses the SMC 83c790 chip which is also +	found on the ISA SMC Ultra cards. It has a shared memory mode of +	operation that makes it similar to the ISA version of the card. +	The main difference is that the EISA card has 32KB of RAM, but +	only an 8KB window into that memory. The EISA card also can be +	set for a bus-mastering mode of operation via the ECU, but that +	is not (and probably will never be) supported by this driver. +	The ECU should be run to enable shared memory and to disable the +	bus-mastering feature for use with linux. + +	By programming the 8390 to use only 8KB RAM, the modifications +	to the ISA driver can be limited to the probe and initialization +	code. This allows easy integration of EISA support into the ISA +	driver. However, the driver development kit from SMC provided the +	register information for sliding the 8KB window, and hence the 8390 +	is programmed to use the full 32KB RAM. + +	Unfortunately this required code changes outside the probe/init +	routines, and thus we decided to separate the EISA driver from +	the ISA one. In this way, ISA users don't end up with a larger +	driver due to the EISA code, and EISA users don't end up with a +	larger driver due to the ISA EtherEZ PIO code. The driver is +	similar to the 3c503/16 driver, in that the window must be set +	back to the 1st 8KB of space for access to the two 8390 Tx slots. + +	In testing, using only 8KB RAM (3 Tx / 5 Rx) didn't appear to +	be a limiting factor, since the EISA bus could get packets off +	the card fast enough, but having the use of lots of RAM as Rx +	space is extra insurance if interrupt latencies become excessive. + +*/ + +static const char *version = "smc-ultra32.c: 06/97 v1.00\n"; + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> + +#include <asm/io.h> +#include <asm/system.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include "8390.h" + +int ultra32_probe(struct device *dev); +int ultra32_probe1(struct device *dev, int ioaddr); +static int ultra32_open(struct device *dev); +static void ultra32_reset_8390(struct device *dev); +static void ultra32_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +				 int ring_page); +static void ultra32_block_input(struct device *dev, int count, +				struct sk_buff *skb, int ring_offset); +static void ultra32_block_output(struct device *dev, int count, +				 const unsigned char *buf, const int start_page); +static int ultra32_close(struct device *dev); + +#define ULTRA32_CMDREG	0	/* Offset to ASIC command register. */ +#define	 ULTRA32_RESET	0x80	/* Board reset, in ULTRA32_CMDREG. */ +#define	 ULTRA32_MEMENB	0x40	/* Enable the shared memory. */ +#define ULTRA32_NIC_OFFSET 16	/* NIC register offset from the base_addr. */ +#define ULTRA32_IO_EXTENT 32 +#define EN0_ERWCNT		0x08	/* Early receive warning count. */ + +/* + * Defines that apply only to the Ultra32 EISA card. Note that + * "smc" = 10011 01101 00011 = 0x4da3, and hence !smc8010.cfg translates + * into an EISA ID of 0x1080A34D + */ +#define ULTRA32_BASE	0xca0 +#define ULTRA32_ID	0x1080a34d +#define ULTRA32_IDPORT	(-0x20)	/* 0xc80 */ +/* Config regs 1->7 from the EISA !SMC8010.CFG file. */ +#define ULTRA32_CFG1	0x04	/* 0xca4 */ +#define ULTRA32_CFG2	0x05	/* 0xca5 */ +#define ULTRA32_CFG3	(-0x18)	/* 0xc88 */ +#define ULTRA32_CFG4	(-0x17)	/* 0xc89 */ +#define ULTRA32_CFG5	(-0x16)	/* 0xc8a */ +#define ULTRA32_CFG6	(-0x15)	/* 0xc8b */ +#define ULTRA32_CFG7	0x0d	/* 0xcad */ + + +/*	Probe for the Ultra32.  This looks like a 8013 with the station +	address PROM at I/O ports <base>+8 to <base>+13, with a checksum +	following. +*/ + +int ultra32_probe(struct device *dev) +{ +	const char *ifmap[] = {"UTP No Link", "", "UTP/AUI", "UTP/BNC"}; +	int ioaddr, edge, media; + +	if (!EISA_bus) return ENODEV; + +	/* EISA spec allows for up to 16 slots, but 8 is typical. */ +	for (ioaddr = 0x1000 + ULTRA32_BASE; ioaddr < 0x9000; ioaddr += 0x1000) +	if (check_region(ioaddr, ULTRA32_IO_EXTENT) == 0 && +	    inb(ioaddr + ULTRA32_IDPORT) != 0xff && +	    inl(ioaddr + ULTRA32_IDPORT) == ULTRA32_ID) { +		media = inb(ioaddr + ULTRA32_CFG7) & 0x03; +		edge = inb(ioaddr + ULTRA32_CFG5) & 0x08; +		printk("SMC Ultra32 in EISA Slot %d, Media: %s, %s IRQs.\n", +		       ioaddr >> 12, ifmap[media], +		       (edge ? "Edge Triggered" : "Level Sensitive")); +		if (ultra32_probe1(dev, ioaddr) == 0) +		  return 0; +	} +	return ENODEV; +} + +int ultra32_probe1(struct device *dev, int ioaddr) +{ +	int i; +	int checksum = 0; +	const char *model_name; +	static unsigned version_printed = 0; +	/* Values from various config regs. */ +	unsigned char idreg = inb(ioaddr + 7); +	unsigned char reg4 = inb(ioaddr + 4) & 0x7f; + +	/* Check the ID nibble. */ +	if ((idreg & 0xf0) != 0x20) 			/* SMC Ultra */ +		return ENODEV; + +	/* Select the station address register set. */ +	outb(reg4, ioaddr + 4); + +	for (i = 0; i < 8; i++) +		checksum += inb(ioaddr + 8 + i); +	if ((checksum & 0xff) != 0xff) +		return ENODEV; + +	/* We should have a "dev" from Space.c or the static module table. */ +	if (dev == NULL) { +		printk("smc-ultra32.c: Passed a NULL device.\n"); +		dev = init_etherdev(0, 0); +	} + +	if (ei_debug  &&  version_printed++ == 0) +		printk("%s", version); + +	model_name = "SMC Ultra32"; + +	printk("%s: %s at 0x%X,", dev->name, model_name, ioaddr); + +	for (i = 0; i < 6; i++) +		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i)); + +	/* Switch from the station address to the alternate register set and +	   read the useful registers there. */ +	outb(0x80 | reg4, ioaddr + 4); + +	/* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ +	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); + +	/* Reset RAM addr. */ +	outb(0x00, ioaddr + 0x0b); + +	/* Switch back to the station address register set so that the +	   MS-DOS driver can find the card after a warm boot. */ +	outb(reg4, ioaddr + 4); + +	if ((inb(ioaddr + ULTRA32_CFG5) & 0x40) == 0) { +		printk("\nsmc-ultra32: Card RAM is disabled!  " +		       "Run EISA config utility.\n"); +		return ENODEV; +	} +	if ((inb(ioaddr + ULTRA32_CFG2) & 0x04) == 0) +		printk("\nsmc-ultra32: Ignoring Bus-Master enable bit.  " +		       "Run EISA config utility.\n"); + +	if (dev->irq < 2) { +		unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; +		int irq = irqmap[inb(ioaddr + ULTRA32_CFG5) & 0x07]; +		if (irq == 0) { +			printk(", failed to detect IRQ line.\n"); +			return -EAGAIN; +		} +		dev->irq = irq; +	} + +	/* Allocate dev->priv and fill in 8390 specific dev fields. */ +	if (ethdev_init(dev)) { +		printk (", no memory for dev->priv.\n"); +                return -ENOMEM; +        } + +	/* OK, we are certain this is going to work.  Setup the device. */ +	request_region(ioaddr, ULTRA32_IO_EXTENT, model_name); + +	/* The 8390 isn't at the base address, so fake the offset */ +	dev->base_addr = ioaddr + ULTRA32_NIC_OFFSET; + +	/* Save RAM address in the unused reg0 to avoid excess inb's. */ +	ei_status.reg0 = inb(ioaddr + ULTRA32_CFG3) & 0xfc; + +	dev->mem_start =  0xc0000 + ((ei_status.reg0 & 0x7c) << 11); + +	ei_status.name = model_name; +	ei_status.word16 = 1; +	ei_status.tx_start_page = 0; +	ei_status.rx_start_page = TX_PAGES; +	/* All Ultra32 cards have 32KB memory with an 8KB window. */ +	ei_status.stop_page = 128; + +	dev->rmem_start = dev->mem_start + TX_PAGES*256; +	dev->mem_end = dev->rmem_end = dev->mem_start + 0x1fff; + +	printk(", IRQ %d, 32KB memory, 8KB window at 0x%lx-0x%lx.\n", +	       dev->irq, dev->mem_start, dev->mem_end); +	ei_status.block_input = &ultra32_block_input; +	ei_status.block_output = &ultra32_block_output; +	ei_status.get_8390_hdr = &ultra32_get_8390_hdr; +	ei_status.reset_8390 = &ultra32_reset_8390; +	dev->open = &ultra32_open; +	dev->stop = &ultra32_close; +	NS8390_init(dev, 0); + +	return 0; +} + +static int ultra32_open(struct device *dev) +{ +	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC addr */ + +	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, dev)) +		return -EAGAIN; + +	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ +	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */ +	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */ +	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */ +	/* Set the early receive warning level in window 0 high enough not +	   to receive ERW interrupts. */ +	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); +	outb(0xff, dev->base_addr + EN0_ERWCNT); +	ei_open(dev); +	MOD_INC_USE_COUNT; +	return 0; +} + +static int ultra32_close(struct device *dev) +{ +	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* CMDREG */ + +	dev->start = 0; +	dev->tbusy = 1; + +	if (ei_debug > 1) +		printk("%s: Shutting down ethercard.\n", dev->name); + +	outb(0x00, ioaddr + ULTRA32_CFG6); /* Disable Interrupts. */ +	outb(0x00, ioaddr + 6);		/* Disable interrupts. */ +	free_irq(dev->irq, dev); +	irq2dev_map[dev->irq] = 0; + +	NS8390_init(dev, 0); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static void ultra32_reset_8390(struct device *dev) +{ +	int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; /* ASIC base addr */ + +	outb(ULTRA32_RESET, ioaddr); +	if (ei_debug > 1) printk("resetting Ultra32, t=%ld...", jiffies); +	ei_status.txing = 0; + +	outb(ULTRA32_MEMENB, ioaddr); /* Enable Shared Memory. */ +	outb(0x80, ioaddr + ULTRA32_CFG6); /* Enable Interrupts. */ +	outb(0x84, ioaddr + 5);	/* Enable MEM16 & Disable Bus Master. */ +	outb(0x01, ioaddr + 6);	/* Enable Interrupts. */ +	if (ei_debug > 1) printk("reset done\n"); +	return; +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but +   we don't need to be concerned with ring wrap as the header will be at +   the start of a page, so we optimize accordingly. */ + +static void ultra32_get_8390_hdr(struct device *dev, +				 struct e8390_pkt_hdr *hdr, +				 int ring_page) +{ +	unsigned long hdr_start = dev->mem_start + ((ring_page & 0x1f) << 8); +	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; + +	/* Select correct 8KB Window. */ +	outb(ei_status.reg0 | ((ring_page & 0x60) >> 5), RamReg); + +#ifdef notdef +	/* Officially this is what we are doing, but the readl() is faster */ +	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); +#else +	((unsigned int*)hdr)[0] = readl(hdr_start); +#endif +} + +/* Block input and output are easy on shared memory ethercards, the only +   complication is when the ring buffer wraps, or in this case, when a +   packet spans an 8KB boundary. Note that the current 8KB segment is +   already set by the get_8390_hdr routine. */ + +static void ultra32_block_input(struct device *dev, +				int count, +				struct sk_buff *skb, +				int ring_offset) +{ +	unsigned long xfer_start = dev->mem_start + (ring_offset & 0x1fff); +	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; + +	if ((ring_offset & ~0x1fff) != ((ring_offset + count - 1) & ~0x1fff)) { +		int semi_count = 8192 - (ring_offset & 0x1FFF); +		memcpy_fromio(skb->data, xfer_start, semi_count); +		count -= semi_count; +		if (ring_offset < 96*256) { +			/* Select next 8KB Window. */ +			ring_offset += semi_count; +			outb(ei_status.reg0 | ((ring_offset & 0x6000) >> 13), RamReg); +			memcpy_fromio(skb->data + semi_count, dev->mem_start, count); +		} else { +			/* Select first 8KB Window. */ +			outb(ei_status.reg0, RamReg); +			memcpy_fromio(skb->data + semi_count, dev->rmem_start, count); +		} +	} else { +		/* Packet is in one chunk -- we can copy + cksum. */ +		eth_io_copy_and_sum(skb, xfer_start, count, 0); +	} +} + +static void ultra32_block_output(struct device *dev, +				 int count, +				 const unsigned char *buf, +				 int start_page) +{ +	unsigned long xfer_start = dev->mem_start + (start_page<<8); +	unsigned int RamReg = dev->base_addr - ULTRA32_NIC_OFFSET + ULTRA32_CFG3; + +	/* Select first 8KB Window. */ +	outb(ei_status.reg0, RamReg); + +	memcpy_toio(xfer_start, buf, count); +} + +#ifdef MODULE +#define MAX_ULTRA32_CARDS   4	/* Max number of Ultra cards per module */ +#define NAMELEN		    8	/* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_ULTRA32_CARDS] = { 0, }; +static struct device dev_ultra[MAX_ULTRA32_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +int init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) { +		struct device *dev = &dev_ultra[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->init = ultra32_probe; +		if (register_netdev(dev) != 0) { +			if (found > 0) return 0; /* Got at least one. */ +			printk(KERN_WARNING "smc-ultra32.c: No SMC Ultra32 found.\n"); +			return -ENXIO; +		} +		found++; +	} + +	return 0; +} + +void cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_ULTRA32_CARDS; this_dev++) { +		struct device *dev = &dev_ultra[this_dev]; +		if (dev->priv != NULL) { +			/* NB: ultra32_close_card() does free_irq + irq2dev */ +			int ioaddr = dev->base_addr - ULTRA32_NIC_OFFSET; +			kfree(dev->priv); +			dev->priv = NULL; +			release_region(ioaddr, ULTRA32_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ diff --git a/linux/src/drivers/net/smc9194.c b/linux/src/drivers/net/smc9194.c new file mode 100644 index 0000000..e3d648d --- /dev/null +++ b/linux/src/drivers/net/smc9194.c @@ -0,0 +1,1779 @@ +/*------------------------------------------------------------------------ + . smc9194.c + . This is a driver for SMC's 9000 series of Ethernet cards.  + . + . Copyright (C) 1996 by Erik Stahlman + . This software may be used and distributed according to the terms + . of the GNU Public License, incorporated herein by reference. + . + . "Features" of the SMC chip:   + .   4608 byte packet memory. ( for the 91C92.  Others have more )  + .   EEPROM for configuration + .   AUI/TP selection  ( mine has 10Base2/10BaseT select ) + . + . Arguments: + . 	io		 = for the base address + .	irq	 = for the IRQ  + .	ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 )    + . + . author:   + . 	Erik Stahlman				( erik@vt.edu ) + .    + . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + . + . Sources: + .    o   SMC databook + .    o   skeleton.c by Donald Becker ( becker@cesdis.gsfc.nasa.gov ) + .    o   ( a LOT of advice from Becker as well ) + . + . History: + .	12/07/95  Erik Stahlman  written, got receive/xmit handled  + . 	01/03/96  Erik Stahlman  worked out some bugs, actually usable!!! :-) + .	01/06/96  Erik Stahlman	 cleaned up some, better testing, etc  + .	01/29/96  Erik Stahlman	 fixed autoirq, added multicast + . 	02/01/96  Erik Stahlman	 1. disabled all interrupts in smc_reset + .		   		 2. got rid of post-decrementing bug -- UGH.   + .	02/13/96  Erik Stahlman  Tried to fix autoirq failure.  Added more + .				 descriptive error messages. + .	02/15/96  Erik Stahlman  Fixed typo that caused detection failure + . 	02/23/96  Erik Stahlman	 Modified it to fit into kernel tree   + .				 Added support to change hardware address + .				 Cleared stats on opens + .	02/26/96  Erik Stahlman	 Trial support for Kernel 1.2.13 + .				 Kludge for automatic IRQ detection + .	03/04/96  Erik Stahlman	 Fixed kernel 1.3.70 +  + .				 Fixed bug reported by Gardner Buchanan in  + .				   smc_enable, with outw instead of outb + .	03/06/96  Erik Stahlman  Added hardware multicast from Peter Cammaert + ----------------------------------------------------------------------------*/ + +static const char *version = +	"smc9194.c:v0.12 03/06/96 by Erik Stahlman (erik@vt.edu)\n";   + +#ifdef MODULE +#include <linux/module.h> +#include <linux/version.h> +#endif  + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <linux/errno.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include "smc9194.h" +/*------------------------------------------------------------------------ + .   + . Configuration options, for the experienced user to change.  + . + -------------------------------------------------------------------------*/ + +/*  + . this is for kernels > 1.2.70  +*/ +#define REALLY_NEW_KERNEL  +#ifndef REALLY_NEW_KERNEL +#define free_irq( x, y ) free_irq( x ) +#define request_irq( x, y, z, u, v ) request_irq( x, y, z, u ) +#endif + +/* + . Do you want to use this with old kernels.   + . WARNING: this is not well tested.   +#define SUPPORT_OLD_KERNEL +*/ + + +/* + . Do you want to use 32 bit xfers?  This should work on all chips, as + . the chipset is designed to accommodate them.    +*/ +#define USE_32_BIT 1 + +/*  + .the SMC9194 can be at any of the following port addresses.  To change, + .for a slightly different card, you can add it to the array.  Keep in  + .mind that the array must end in zero. +*/ +static unsigned int smc_portlist[] = +   { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, +	  0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0}; + +/*  + . Wait time for memory to be free.  This probably shouldn't be  + . tuned that much, as waiting for this means nothing else happens  + . in the system  +*/ +#define MEMORY_WAIT_TIME 16 + +/* + . DEBUGGING LEVELS + .  + . 0 for normal operation + . 1 for slightly more details + . >2 for various levels of increasingly useless information + .    2 for interrupt tracking, status flags + .    3 for packet dumps, etc. +*/ +#define SMC_DEBUG 0 + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(x) printk x  +#else  +#define PRINTK3(x)  +#endif + +#if SMC_DEBUG > 1  +#define PRINTK2(x) printk x  +#else +#define PRINTK2(x) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(x) printk x +#else +#define PRINTK(x) +#endif  + + +/* the older versions of the kernel cannot support autoprobing */ +#ifdef SUPPORT_OLD_KERNEL +#define NO_AUTOPROBE +#endif + + +/*------------------------------------------------------------------------ + . + . The internal workings of the driver.  If you are changing anything  + . here with the SMC stuff, you should have the datasheet and known  + . what you are doing.   + .   + -------------------------------------------------------------------------*/ +#define CARDNAME "SMC9194" + +#ifdef SUPPORT_OLD_KERNEL +char kernel_version[] = UTS_RELEASE; +#endif  + +/* store this information for the driver.. */  +struct smc_local { +	/* + 	   these are things that the kernel wants me to keep, so users +	   can find out semi-useless statistics of how well the card is +	   performing  + 	*/ +	struct enet_statistics stats; +	 +	/*  +	   If I have to wait until memory is available to send +	   a packet, I will store the skbuff here, until I get the +	   desired memory.  Then, I'll send it out and free it.     +	*/ +	struct sk_buff * saved_skb; + +	/* + 	 . This keeps track of how many packets that I have + 	 . sent out.  When an TX_EMPTY interrupt comes, I know +	 . that all of these have been sent. +	*/ +	int	packets_waiting; +}; + + +/*----------------------------------------------------------------- + . + .  The driver can be entered at any of the following entry points. + .  + .------------------------------------------------------------------  */ + +/* + . This is called by  register_netdev().  It is responsible for  + . checking the portlist for the SMC9000 series chipset.  If it finds  + . one, then it will initialize the device, find the hardware information, + . and sets up the appropriate device parameters.    + . NOTE: Interrupts are *OFF* when this procedure is called. + . + . NB:This shouldn't be static since it is referred to externally. +*/ +int smc_init(struct device *dev); + +/* + . The kernel calls this function when someone wants to use the device, + . typically 'ifconfig ethX up'.    +*/ +static int smc_open(struct device *dev); + +/* + . This is called by the kernel to send a packet out into the net.  it's + . responsible for doing a best-effort send, but if it's simply not possible + . to send it, the packet gets dropped.  +*/   +static int smc_send_packet(struct sk_buff *skb, struct device *dev); + +/*  + . This is called by the kernel in response to 'ifconfig ethX down'.  It + . is responsible for cleaning up everything that the open routine  + . does, and maybe putting the card into a powerdown state.  +*/ +static int smc_close(struct device *dev); + +/* + . This routine allows the proc file system to query the driver's  + . statistics.   +*/ +static struct enet_statistics * smc_query_statistics( struct device *dev); + +/* + . Finally, a call to set promiscuous mode ( for TCPDUMP and related  + . programs ) and multicast modes. +*/ +#ifdef SUPPORT_OLD_KERNEL +static void smc_set_multicast_list(struct device *dev, int num_addrs, +				 void *addrs); +#else +static void smc_set_multicast_list(struct device *dev); +#endif  + +/*--------------------------------------------------------------- + .  + . Interrupt level calls..  + . + ----------------------------------------------------------------*/ + +/* + . Handles the actual interrupt  +*/ +#ifdef REALLY_NEW_KERNEL +static void smc_interrupt(int irq, void *, struct pt_regs *regs); +#else +static void smc_interrupt(int irq, struct pt_regs *regs); +#endif  +/* + . This is a separate procedure to handle the receipt of a packet, to + . leave the interrupt code looking slightly cleaner  +*/  +inline static void smc_rcv( struct device *dev ); +/* + . This handles a TX interrupt, which is only called when an error + . relating to a packet is sent.   +*/ +inline static void smc_tx( struct device * dev ); + +/* + ------------------------------------------------------------ + .  + . Internal routines + . + ------------------------------------------------------------ +*/ + +/* + . Test if a given location contains a chip, trying to cause as  + . little damage as possible if it's not a SMC chip. +*/ +static int smc_probe( int ioaddr ); + +/* + . this routine initializes the cards hardware, prints out the configuration + . to the system log as well as the vanity message, and handles the setup + . of a device parameter.  + . It will give an error if it can't initialize the card. +*/ +static int smc_initcard( struct device *, int ioaddr );  + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/  +#if SMC_DEBUG > 2  +static void print_packet( byte *, int ); +#endif   + +#define tx_done(dev) 1 + +/* this is called to actually send the packet to the chip */  +static void smc_hardware_send_packet( struct device * dev ); + +/* Since I am not sure if I will have enough room in the chip's ram + . to store the packet, I call this routine, which either sends it  + . now, or generates an interrupt when the card is ready for the  + . packet */ +static int  smc_wait_to_send_packet( struct sk_buff * skb, struct device *dev ); + +/* this does a soft reset on the device */ +static void smc_reset( int ioaddr ); + +/* Enable Interrupts, Receive, and Transmit */ +static void smc_enable( int ioaddr ); + +/* this puts the device in an inactive state */ +static void smc_shutdown( int ioaddr ); + +#ifndef NO_AUTOPROBE +/* This routine will find the IRQ of the driver if one is not + . specified in the input to the device.  */ +static int smc_findirq( int ioaddr ); +#endif + +/* +  this routine will set the hardware multicast table to the specified  +  values given it by the higher level routines +*/ +#ifndef SUPPORT_OLD_KERNEL +static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list *  ); +static int crc32( char *, int ); +#endif + +#ifdef SUPPORT_OLD_KERNEL +extern struct device *init_etherdev(struct device *dev, int sizeof_private, + 			unsigned long *mem_startp ); +#endif  + +/* + . Function: smc_reset( int ioaddr ) + . Purpose: + .  	This sets the SMC91xx chip to its normal state, hopefully from whatever + . 	mess that any other DOS driver has put it in.    + .  + . Maybe I should reset more registers to defaults in here?  SOFTRESET  should + . do that for me.   + .  + . Method: + .	1.  send a SOFT RESET  + .	2.  wait for it to finish + .	3.  enable autorelease mode + .	4.  reset the memory management unit + .	5.  clear all interrupts + . +*/   +static void smc_reset( int ioaddr )  +{  +	/* This resets the registers mostly to defaults, but doesn't +	   affect EEPROM.  That seems unnecessary */ +	SMC_SELECT_BANK( 0 ); +	outw( RCR_SOFTRESET, ioaddr + RCR );  +	 +	/* this should pause enough for the chip to be happy */ +	SMC_DELAY( ); + +	/* Set the transmit and receive configuration registers to  +	   default values */ +	outw( RCR_CLEAR, ioaddr + RCR ); +	outw( TCR_CLEAR, ioaddr + TCR ); + +	/* set the control register to automatically +	   release successfully transmitted packets, to make the best  +	   use out of our limited memory */ +	SMC_SELECT_BANK( 1 ); +	outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL );	 + +	/* Reset the MMU */ +	SMC_SELECT_BANK( 2 ); +	outw( MC_RESET, ioaddr + MMU_CMD ); + +	/* Note:  It doesn't seem that waiting for the MMU busy is needed here,  +	   but this is a place where future chipsets _COULD_ break.  Be wary + 	   of issuing another MMU command right after this */ + +	outb( 0, ioaddr + INT_MASK ); +} + +/*  + . Function: smc_enable + . Purpose: let the chip talk to the outside work + . Method:  + .	1.  Enable the transmitter + .	2.  Enable the receiver + .	3.  Enable interrupts +*/ +static void smc_enable( int ioaddr )  +{ +	SMC_SELECT_BANK( 0 ); +	/* see the header file for options in TCR/RCR NORMAL*/ +	outw( TCR_NORMAL, ioaddr + TCR ); +	outw( RCR_NORMAL, ioaddr + RCR ); + +	/* now, enable interrupts */ +	SMC_SELECT_BANK( 2 ); +	outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK ); +} 	 +	 +/* + . Function: smc_shutdown + . Purpose:  closes down the SMC91xxx chip. + . Method:    + .	1. zero the interrupt mask + .	2. clear the enable receive flag + .	3. clear the enable xmit flags + . + . TODO:  + .   (1) maybe utilize power down mode. + .	Why not yet?  Because while the chip will go into power down mode, + .	the manual says that it will wake up in response to any I/O requests + .	in the register space.   Empirical results do not show this working. +*/ +static void smc_shutdown( int ioaddr )  +{ +	/* no more interrupts for me */ +	SMC_SELECT_BANK( 2 ); +	outb( 0, ioaddr + INT_MASK ); + +	/* and tell the card to stay away from that nasty outside world */ +	SMC_SELECT_BANK( 0 ); +	outb( RCR_CLEAR, ioaddr + RCR ); +	outb( TCR_CLEAR, ioaddr + TCR ); +#if 0  +	/* finally, shut the chip down */ +	SMC_SELECT_BANK( 1 );  +	outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL  ); +#endif  +} + + +#ifndef SUPPORT_OLD_KERNEL  +/*  + . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) + . Purpose: + .    This sets the internal hardware table to filter out unwanted multicast + .    packets before they take up memory.   + .     + .    The SMC chip uses a hash table where the high 6 bits of the CRC of + .    address are the offset into the table.  If that bit is 1, then the  + .    multicast packet is accepted.  Otherwise, it's dropped silently. + .   + .    To use the 6 bits as an offset into the table, the high 3 bits are the + .    number of the 8 bit register, while the low 3 bits are the bit within + .    that register. + . + . This routine is based very heavily on the one provided by Peter Cammaert.  +*/ + + +static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) { +	int			i; +	unsigned char		multicast_table[ 8 ]; +	struct dev_mc_list	* cur_addr; +	/* table for flipping the order of 3 bits */   +	unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + +	/* start with a table of all zeros: reject all */	 +	memset( multicast_table, 0, sizeof( multicast_table ) ); + +	cur_addr = addrs; +	for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next  ) { +		int position; +		 +		/* do we have a pointer here? */ +		if ( !cur_addr )  +			break; +		/* make sure this is a multicast address - shouldn't this +		   be a given if we have it here ? */	 +		if ( !( *cur_addr->dmi_addr & 1 ) ) 	 +			continue;	 + +		/* only use the low order bits */	 +		position = crc32( cur_addr->dmi_addr, 6 ) & 0x3f; +				 +		/* do some messy swapping to put the bit in the right spot */ +		multicast_table[invert3[position&7]] |=  +					(1<<invert3[(position>>3)&7]); + +	} +	/* now, the table can be loaded into the chipset */ +	SMC_SELECT_BANK( 3 ); +	 +	for ( i = 0; i < 8 ; i++ ) { +		outb( multicast_table[i], ioaddr + MULTICAST1 + i ); +	} +} + +/* +  Finds the CRC32 of a set of bytes. +  Again, from Peter Cammaert's code.  +*/ +static int crc32( char * s, int length ) {  +	/* indices */ +	int perByte; +	int perBit; +	/* crc polynomial for Ethernet */ +	const unsigned long poly = 0xedb88320; +	/* crc value - preinitialized to all 1's */ +	unsigned long crc_value = 0xffffffff;  + +	for ( perByte = 0; perByte < length; perByte ++ ) { +		unsigned char	c; +	 +		c = *(s++); +		for ( perBit = 0; perBit < 8; perBit++ ) { +			crc_value = (crc_value>>1)^ +				(((crc_value^c)&0x01)?poly:0); +			c >>= 1; +		} +	} +	return	crc_value; +}  + +#endif  + + +/*  + . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct device * )  + . Purpose:  + .    Attempt to allocate memory for a packet, if chip-memory is not + .    available, then tell the card to generate an interrupt when it  + .    is available. + . + . Algorithm: + . + . o	if the saved_skb is not currently null, then drop this packet + .	on the floor.  This should never happen, because of TBUSY. + . o	if the saved_skb is null, then replace it with the current packet, + . o	See if I can sending it now.  + . o 	(NO): Enable interrupts and let the interrupt handler deal with it. + . o	(YES):Send it now. +*/ +static int smc_wait_to_send_packet( struct sk_buff * skb, struct device * dev ) +{  +	struct smc_local *lp 	= (struct smc_local *)dev->priv; +	unsigned short ioaddr 	= dev->base_addr; +	word 			length; +	unsigned short 		numPages; +	word			time_out;	 +	 +	if ( lp->saved_skb) { +		/* THIS SHOULD NEVER HAPPEN. */ +		lp->stats.tx_aborted_errors++; +		printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); +		return 1; +	} +	lp->saved_skb = skb; + +	length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		 +	/* +	. the MMU wants the number of pages to be the number of 256 bytes  +    	. 'pages', minus 1 ( since a packet can't ever have 0 pages :) )  +	*/ +	numPages = length / 256; + +	if (numPages > 7 ) { +		printk(CARDNAME": Far too big packet error. \n"); +		/* freeing the packet is a good thing here... but should 		 +		 . any packets of this size get down here?   */ +		dev_kfree_skb (skb, FREE_WRITE); +		lp->saved_skb = NULL; +		/* this IS an error, but, i don't want the skb saved */ +		return 0;  +	} +	/* either way, a packet is waiting now */ +	lp->packets_waiting++; +	  +	/* now, try to allocate the memory */ +	SMC_SELECT_BANK( 2 ); +	outw( MC_ALLOC | numPages, ioaddr + MMU_CMD ); +	/* + 	. Performance Hack +	.   + 	. wait a short amount of time.. if I can send a packet now, I send +	. it now.  Otherwise, I enable an interrupt and wait for one to be +	. available.  +	. +	. I could have handled this a slightly different way, by checking to +	. see if any memory was available in the FREE MEMORY register.  However, +	. either way, I need to generate an allocation, and the allocation works +	. no matter what, so I saw no point in checking free memory.    +	*/  +	time_out = MEMORY_WAIT_TIME; +	do {  +		word	status; + +		status = inb( ioaddr + INTERRUPT ); +		if ( status & IM_ALLOC_INT ) {  +			/* acknowledge the interrupt */ +			outb( IM_ALLOC_INT, ioaddr + INTERRUPT ); +  			break;	 +		} +   	} while ( -- time_out ); + +   	if ( !time_out ) { +		/* oh well, wait until the chip finds memory later */  +		SMC_ENABLE_INT( IM_ALLOC_INT ); +      		PRINTK2((CARDNAME": memory allocation deferred. \n")); +		/* it's deferred, but I'll handle it later */ +      		return 0; +   	} +	/* or YES! I can send the packet now.. */ +	smc_hardware_send_packet(dev); +	 +	return 0; +}	 + +/* + . Function:  smc_hardware_send_packet(struct device * ) + . Purpose:	 + .	This sends the actual packet to the SMC9xxx chip.    + .  + . Algorithm: + . 	First, see if a saved_skb is available.     + .		( this should NOT be called if there is no 'saved_skb' + .	Now, find the packet number that the chip allocated + .	Point the data pointers at it in memory  + .	Set the length word in the chip's memory + .	Dump the packet to chip memory + .	Check if a last byte is needed ( odd length packet ) + .		if so, set the control flag right  + . 	Tell the card to send it  + .	Enable the transmit interrupt, so I know if it failed + . 	Free the kernel data if I actually sent it. +*/ +static void smc_hardware_send_packet( struct device * dev )  +{ +	struct smc_local *lp = (struct smc_local *)dev->priv; +	byte	 		packet_no; +	struct sk_buff * 	skb = lp->saved_skb; +	word			length;	 +	unsigned short		ioaddr; +	byte			* buf; + +	ioaddr = dev->base_addr;	 + +	if ( !skb ) { +		PRINTK((CARDNAME": In XMIT with no packet to send \n")); +		return; +	} +	length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +	buf = skb->data; + +	/* If I get here, I _know_ there is a packet slot waiting for me */ +	packet_no = inb( ioaddr + PNR_ARR + 1 );  +	if ( packet_no & 0x80 ) {  +		/* or isn't there?  BAD CHIP! */ +		printk(KERN_DEBUG CARDNAME": Memory allocation failed. \n"); +		kfree(skb); +		lp->saved_skb = NULL; +		dev->tbusy = 0; +		return; +	} + +	/* we have a packet address, so tell the card to use it */ +	outb( packet_no, ioaddr + PNR_ARR ); + +	/* point to the beginning of the packet */	 +	outw( PTR_AUTOINC , ioaddr + POINTER ); + +   	PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length )); +#if SMC_DEBUG > 2 +	print_packet( buf, length ); +#endif + +	/* send the packet length ( +6 for status, length and ctl byte )  + 	   and the status word ( set to zeros ) */  +#ifdef USE_32_BIT +	outl(  (length +6 ) << 16 , ioaddr + DATA_1 ); +#else +	outw( 0, ioaddr + DATA_1 );	  +	/* send the packet length ( +6 for status words, length, and ctl*/		 +	outb( (length+6) & 0xFF,ioaddr + DATA_1 ); +	outb( (length+6) >> 8 , ioaddr + DATA_1 ); +#endif  + +	/* send the actual data  +	 . I _think_ it's faster to send the longs first, and then  +	 . mop up by sending the last word.  It depends heavily  + 	 . on alignment, at least on the 486.  Maybe it would be  + 	 . a good idea to check which is optimal?  But that could take +	 . almost as much time as is saved?  +	*/	 +#ifdef USE_32_BIT  +	if ( length & 0x2  ) {	 +		outsl(ioaddr + DATA_1, buf,  length >> 2 );  +		outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); +	} +	else +		outsl(ioaddr + DATA_1, buf,  length >> 2 );  +#else +	outsw(ioaddr + DATA_1 , buf, (length ) >> 1); +#endif +	/* Send the last byte, if there is one.   */ +	 +	if ( (length & 1) == 0 ) { +		outw( 0, ioaddr + DATA_1 ); +	} else { +		outb( buf[length -1 ], ioaddr + DATA_1 ); +		outb( 0x20, ioaddr + DATA_1); +	} + +	/* enable the interrupts */ +	SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); + +	/* and let the chipset deal with it */ +	outw( MC_ENQUEUE , ioaddr + MMU_CMD ); + +	PRINTK2((CARDNAME": Sent packet of length %d \n",length));  + +	lp->saved_skb = NULL; +	dev_kfree_skb (skb, FREE_WRITE); + +	dev->trans_start = jiffies; + +	/* we can send another packet */ +	dev->tbusy = 0; + + +	return; +} + +/*------------------------------------------------------------------------- + | + | smc_init( struct device * dev )   + |   Input parameters:  + |	dev->base_addr == 0, try to find all possible locations + |	dev->base_addr == 1, return failure code + |	dev->base_addr == 2, always allocate space,  and return success + |	dev->base_addr == <anything else>   this is the address to check  + | + |   Output:  + |	0 --> there is a device + |	anything else, error  + |  + --------------------------------------------------------------------------- +*/  +int smc_init(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	/*  try a specific location */ +	if (base_addr > 0x1ff)	{		  +		int	error;  +		error = smc_probe(base_addr); +		if ( 0 == error ) { +			return smc_initcard( dev, base_addr ); +		} +		return error;  +	} else { +		if ( 0 != base_addr ) { +			return -ENXIO; +		} +	} +	 +	/* check every ethernet address */ +	for (i = 0; smc_portlist[i]; i++) { +		int ioaddr = smc_portlist[i]; + +		/* check if the area is available */ +		if (check_region( ioaddr , SMC_IO_EXTENT)) +			continue; + +		/* check this specific address */ +		if ( smc_probe( ioaddr ) == 0)  {   +			return smc_initcard( dev, ioaddr  );  +		} +	} + +	/* couldn't find anything */ +	return -ENODEV; +} + +#ifndef NO_AUTOPROBE +/*---------------------------------------------------------------------- + . smc_findirq  + .  + . This routine has a simple purpose -- make the SMC chip generate an  + . interrupt, so an auto-detect routine can detect it, and find the IRQ, + ------------------------------------------------------------------------ +*/ +int smc_findirq( int ioaddr )  +{ +	int	timeout = 20; + + +	/* I have to do a STI() here, because this is called from +	   a routine that does an CLI during this process, making it  +	   rather difficult to get interrupts for auto detection */ +	sti(); + +	autoirq_setup( 0 ); + +	/* +	 * What I try to do here is trigger an ALLOC_INT. This is done +	 * by allocating a small chunk of memory, which will give an interrupt +	 * when done. +	 */ + +	   +	SMC_SELECT_BANK(2);	 +	/* enable ALLOCation interrupts ONLY */ +	outb( IM_ALLOC_INT, ioaddr + INT_MASK );	 + +	/* + 	 . Allocate 512 bytes of memory.  Note that the chip was just  +	 . reset so all the memory is available +	*/ +	outw( MC_ALLOC | 1, ioaddr + MMU_CMD );		 + +	/* +	 . Wait until positive that the interrupt has been generated +	*/ +	while ( timeout ) { +		byte	int_status; + +		int_status = inb( ioaddr + INTERRUPT ); + +		if ( int_status & IM_ALLOC_INT ) 	 +			break;		/* got the interrupt */ +		timeout--; +	} +	/* there is really nothing that I can do here if timeout fails, +	   as autoirq_report will return a 0 anyway, which is what I +	   want in this case.   Plus, the clean up is needed in both +	   cases.  */ + +	/* DELAY HERE! +	   On a fast machine, the status might change before the interrupt +	   is given to the processor.  This means that the interrupt was  +	   never detected, and autoirq_report fails to report anything.   +	   This should fix autoirq_* problems.  +	*/ +	SMC_DELAY(); +	SMC_DELAY();		 +	 +	/* and disable all interrupts again */ +	outb( 0, ioaddr + INT_MASK ); + +	/* clear hardware interrupts again, because that's how it +	   was when I was called... */ +	cli(); + +	/* and return what I found */ +	return autoirq_report( 0 );	 +} +#endif +  +/*---------------------------------------------------------------------- + . Function: smc_probe( int ioaddr ) + .  + . Purpose:   + .	Tests to see if a given ioaddr points to an SMC9xxx chip. + .	Returns a 0 on success 	 + .  + . Algorithm: + .	(1) see if the high byte of BANK_SELECT is 0x33 + . 	(2) compare the ioaddr with the base register's address + .	(3) see if I recognize the chip ID in the appropriate register + .  + .--------------------------------------------------------------------- + */  + +static int smc_probe( int ioaddr )  +{ +	unsigned int	bank; +	word	revision_register;	 +	word  base_address_register; + +	/* First, see if the high byte is 0x33 */ +	bank = inw( ioaddr + BANK_SELECT ); +	if ( (bank & 0xFF00) != 0x3300 ) { +		return -ENODEV; +	} +	/* The above MIGHT indicate a device, but I need to write to further + 	 	test this.  */ +	outw( 0x0, ioaddr + BANK_SELECT ); +	bank = inw( ioaddr + BANK_SELECT ); +	if ( (bank & 0xFF00 ) != 0x3300 ) { +		return -ENODEV; +	} +	/* well, we've already written once, so hopefully another time won't + 	   hurt.  This time, I need to switch the bank register to bank 1,  +	   so I can access the base address register */ +	SMC_SELECT_BANK(1); +	base_address_register = inw( ioaddr + BASE ); +	if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) )  { +		printk(CARDNAME ": IOADDR %x doesn't match configuration (%x)."  +			"Probably not a SMC chip\n", +			ioaddr, base_address_register >> 3 & 0x3E0 ); +		/* well, the base address register didn't match.  Must not have +		   been a SMC chip after all. */ +		return -ENODEV; +	} + +	/*  check if the revision register is something that I recognize.   +	    These might need to be added to later, as future revisions  +	    could be added.  */ +	SMC_SELECT_BANK(3); +	revision_register  = inw( ioaddr + REVISION ); +	if ( !chip_ids[ ( revision_register  >> 4 ) & 0xF  ] ) {  +		/* I don't recognize this chip, so... */ +		printk(CARDNAME ": IO %x: Unrecognized revision register:" +			" %x, Contact author. \n", ioaddr, revision_register ); + +		return -ENODEV; +	} + +	/* at this point I'll assume that the chip is an SMC9xxx.    +	   It might be prudent to check a listing of MAC addresses  +	   against the hardware address, or do some other tests. */ 	 +	return 0; +} + +/*--------------------------------------------------------------- + . Here I do typical initialization tasks.  + .  + . o  Initialize the structure if needed + . o  print out my vanity message if not done so already + . o  print out what type of hardware is detected + . o  print out the ethernet address + . o  find the IRQ  + . o  set up my private data  + . o  configure the dev structure with my subroutines + . o  actually GRAB the irq. + . o  GRAB the region  + .----------------------------------------------------------------- +*/ +static int  smc_initcard(struct device *dev, int ioaddr) +{ +	int i; + +	static unsigned version_printed = 0; + +	/* registers */ +	word	revision_register;	 +	word	configuration_register;	 +	word  	memory_info_register; +	word 	memory_cfg_register; + +	const char *	version_string; +	const char *	if_string; +	int	memory; + +	int   irqval; + +	/* see if I need to initialize the ethernet card structure */ +	if (dev == NULL) { +#ifdef SUPPORT_OLD_KERNEL +#ifndef MODULE +/* note: the old module interface does not support this call */ +		dev = init_etherdev( 0, sizeof( struct smc_local ), 0 ); +#endif  +#else +		dev = init_etherdev(0, 0); +#endif  +		if (dev == NULL) +			return -ENOMEM; +	} + +	if (version_printed++ == 0) +		printk("%s", version); + +	/* fill in some of the fields */ +	dev->base_addr = ioaddr; + +	/* + 	 . Get the MAC address ( bank 1, regs 4 - 9 ) +	*/ +	SMC_SELECT_BANK( 1 ); +	for ( i = 0; i < 6; i += 2 ) {  +		word	address; + +		address = inw( ioaddr + ADDR0 + i  ); +		dev->dev_addr[ i + 1] = address >> 8; +		dev->dev_addr[ i ] = address & 0xFF;	 +	} + +	/* get the memory information */ + +	SMC_SELECT_BANK( 0 ); +	memory_info_register = inw( ioaddr + MIR ); +	memory_cfg_register  = inw( ioaddr + MCR ); +	memory = ( memory_cfg_register >> 9 )  & 0x7;  /* multiplier */ +	memory *= 256 * ( memory_info_register & 0xFF ); + +	/*  +	 Now, I want to find out more about the chip.  This is sort of + 	 redundant, but it's cleaner to have it in both, rather than having + 	 one VERY long probe procedure. +	*/	 +	SMC_SELECT_BANK(3); +	revision_register  = inw( ioaddr + REVISION ); +	version_string = chip_ids[ ( revision_register  >> 4 ) & 0xF  ]; +	if ( !version_string ) { +		/* I shouldn't get here because this call was done before.... */  +		return -ENODEV; +	} + +	/* is it using AUI or 10BaseT ? */ +	if ( dev->if_port == 0 ) { +		SMC_SELECT_BANK(1); +		configuration_register = inw( ioaddr + CONFIG ); +		if ( configuration_register & CFG_AUI_SELECT )   +			dev->if_port = 2; +		else +			dev->if_port = 1; +	} +	if_string = interfaces[ dev->if_port - 1 ]; + +	/* now, reset the chip, and put it into a known state */ +	smc_reset( ioaddr ); + +	/* +	 . If dev->irq is 0, then the device has to be banged on to see +	 . what the IRQ is.  + 	 .  +	 . This banging doesn't always detect the IRQ, for unknown reasons. +	 . a workaround is to reset the chip and try again.   +	 .     +	 . Interestingly, the DOS packet driver *SETS* the IRQ on the card to +	 . be what is requested on the command line.   I don't do that, mostly +	 . because the card that I have uses a non-standard method of accessing +	 . the IRQs, and because this _should_ work in most configurations. +	 . +	 . Specifying an IRQ is done with the assumption that the user knows  +	 . what (s)he is doing.  No checking is done!!!! + 	 . +	*/ +#ifndef NO_AUTOPROBE +	if ( dev->irq < 2 ) { +		int	trials; + +		trials = 3; +		while ( trials-- ) {  +			dev->irq = smc_findirq( ioaddr ); +			if ( dev->irq )  +				break; +			/* kick the card and try again */ +			smc_reset( ioaddr ); +		} +	}  +	if (dev->irq == 0 ) { +		printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n"); +		return -ENODEV; +	} +#else +	if (dev->irq == 0 ) { +		printk(CARDNAME +		": Autoprobing IRQs is not supported for old kernels.\n"); +		return -ENODEV; +	} +#endif +	if (dev->irq == 2) { +		/* Fixup for users that don't know that IRQ 2 is really IRQ 9, +		 * or don't know which one to set. +		 */ +		dev->irq = 9; +	} + +	/* now, print out the card info, in a short format.. */ +	 +	printk(CARDNAME ": %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", +		version_string, revision_register & 0xF, ioaddr, dev->irq,  +		if_string, memory ); +	/* +	 . Print the Ethernet address  +	*/ +	printk("ADDR: "); +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i] ); +	printk("%2.2x \n", dev->dev_addr[5] ); + + +	/* Initialize the private structure. */ +	if (dev->priv == NULL) { +		dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL); +		if (dev->priv == NULL) +			return -ENOMEM; +	} +	/* set the private data to zero by default */ +	memset(dev->priv, 0, sizeof(struct smc_local)); + +	/* Fill in the fields of the device structure with ethernet values. */ +	ether_setup(dev); + +	/* Grab the IRQ */ +      	irqval = request_irq(dev->irq, &smc_interrupt, 0, CARDNAME, NULL); +      	if (irqval) { +       	  printk(CARDNAME": unable to get IRQ %d (irqval=%d).\n", +		dev->irq, irqval); +       	  return -EAGAIN; +      	} +	irq2dev_map[dev->irq] = dev; + +	/* Grab the region so that no one else tries to probe our ioports. */ +	request_region(ioaddr, SMC_IO_EXTENT, CARDNAME); + +	dev->open		        = smc_open; +	dev->stop		        = smc_close; +	dev->hard_start_xmit    	= smc_send_packet; +	dev->get_stats			= smc_query_statistics; +#ifdef	HAVE_MULTICAST +	dev->set_multicast_list 	= &smc_set_multicast_list; +#endif + +	return 0; +} + +#if SMC_DEBUG > 2 +static void print_packet( byte * buf, int length )  +{  +#if 0 +	int i; +	int remainder; +	int lines; +	 +	printk("Packet of length %d \n", length ); +	lines = length / 16; +	remainder = length % 16; + +	for ( i = 0; i < lines ; i ++ ) {  +		int cur; + +		for ( cur = 0; cur < 8; cur ++ ) {  +			byte a, b; + +			a = *(buf ++ ); +			b = *(buf ++ ); +			printk("%02x%02x ", a, b ); +		} +		printk("\n"); +	} +	for ( i = 0; i < remainder/2 ; i++ ) { +		byte a, b; + +		a = *(buf ++ ); +		b = *(buf ++ ); +		printk("%02x%02x ", a, b ); +	} +	printk("\n"); +#endif +} +#endif	 + + +/* + * Open and Initialize the board + *  + * Set up everything, reset the card, etc .. + * + */ +static int smc_open(struct device *dev) +{ +	int	ioaddr = dev->base_addr; + +	int	i;	/* used to set hw ethernet address */  + +	/* clear out all the junk that was put here before... */ +	memset(dev->priv, 0, sizeof(struct smc_local)); + +	dev->tbusy 	= 0; +	dev->interrupt  = 0; +	dev->start 	= 1; +#ifdef MODULE +	MOD_INC_USE_COUNT; +#endif + +	/* reset the hardware */ + +	smc_reset( ioaddr ); +	smc_enable( ioaddr ); + +	/* Select which interface to use */ + 	  +	SMC_SELECT_BANK( 1 ); +	if ( dev->if_port == 1 ) { 	 +		outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT,  +			ioaddr + CONFIG );							 +	}  +	else if ( dev->if_port == 2 ) {  +		outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT,  +			ioaddr + CONFIG );							 +	} + +	/* +  		According to Becker, I have to set the hardware address +		at this point, because the (l)user can set it with an +		ioctl.  Easily done...  +	*/ +	SMC_SELECT_BANK( 1 ); +	for ( i = 0; i < 6; i += 2 ) {  +		word	address; +		 +		address = dev->dev_addr[ i + 1 ] << 8 ; +		address  |= dev->dev_addr[ i ]; +		outw( address, ioaddr + ADDR0 + i ); +	} +	return 0; +} + +/*-------------------------------------------------------- + . Called by the kernel to send a packet out into the void + . of the net.  This routine is largely based on  + . skeleton.c, from Becker.    + .-------------------------------------------------------- +*/ +static int smc_send_packet(struct sk_buff *skb, struct device *dev) +{ +	if (dev->tbusy) { +		/* If we get here, some higher level has decided we are broken. +		   There should really be a "kick me" function call instead. */ +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < 5) +			return 1; +		printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n", +			tx_done(dev) ? "IRQ conflict" :  +			"network cable problem"); +		/* "kick" the adaptor */ +		smc_reset( dev->base_addr ); +		smc_enable( dev->base_addr ); + +		dev->tbusy = 0; +		dev->trans_start = jiffies; +		/* clear anything saved */ +		((struct smc_local *)dev->priv)->saved_skb = NULL; +	} + +	/* If some higher layer thinks we've missed an tx-done interrupt +	   we are passed NULL. Caution: dev_tint() handles the cli()/sti() +	   itself. */ +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) { +		printk(KERN_WARNING CARDNAME": Transmitter access conflict.\n"); +		dev_kfree_skb (skb, FREE_WRITE); +	} else { +		/* Well, I want to send the packet.. but I don't know  +		   if I can send it right now...  */	 +		return smc_wait_to_send_packet( skb, dev ); +	} +	return 0; +} + +/*-------------------------------------------------------------------- + . + . This is the main routine of the driver, to handle the device when + . it needs some attention. + . + . So: + .   first, save state of the chipset + .   branch off into routines to handle each case, and acknowledge  + .	    each to the interrupt register + .   and finally restore state.  + .   + ---------------------------------------------------------------------*/ +#ifdef REALLY_NEW_KERNEL +static void smc_interrupt(int irq, void * dev_id,  struct pt_regs * regs) +#else  +static void smc_interrupt(int irq, struct pt_regs * regs) +#endif  +{ +	struct device *dev 	= (struct device *)(irq2dev_map[irq]); +	int ioaddr 		= dev->base_addr; +	struct smc_local *lp 	= (struct smc_local *)dev->priv; + +	byte	status; +	word	card_stats; +	byte	mask; +	int	timeout; +	/* state registers */ +	word	saved_bank; +	word	saved_pointer; + + +	 +	PRINTK3((CARDNAME": SMC interrupt started \n")); + +	if (dev == NULL) { +		printk(KERN_WARNING  CARDNAME": irq %d for unknown device.\n",  +			irq); +		return; +	} + +/* will Linux let this happen ??  If not, this costs some speed */ +	if ( dev->interrupt ) {  +		printk(KERN_WARNING CARDNAME": interrupt inside interrupt.\n"); +		return; +	} +		 +	dev->interrupt = 1; + +	saved_bank = inw( ioaddr + BANK_SELECT ); + +	SMC_SELECT_BANK(2); +	saved_pointer = inw( ioaddr + POINTER ); + +	mask = inb( ioaddr + INT_MASK ); +	/* clear all interrupts */ +	outb( 0, ioaddr + INT_MASK ); + + +	/* set a timeout value, so I don't stay here forever */ +	timeout = 4; + +	PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x \n", mask )); +	do { 	 +		/* read the status flag, and mask it */ +		status = inb( ioaddr + INTERRUPT ) & mask; +		if (!status ) +			break; + +		PRINTK3((KERN_WARNING CARDNAME +			": Handling interrupt status %x \n", status ));  + +		if (status & IM_RCV_INT) { +			/* Got a packet(s). */ +			PRINTK2((KERN_WARNING CARDNAME +				": Receive Interrupt\n")); +			smc_rcv(dev); +		} else if (status & IM_TX_INT ) { +			PRINTK2((KERN_WARNING CARDNAME +				": TX ERROR handled\n")); +			smc_tx(dev); +			outb(IM_TX_INT, ioaddr + INTERRUPT );  +		} else if (status & IM_TX_EMPTY_INT ) { +			/* update stats */ +			SMC_SELECT_BANK( 0 ); +			card_stats = inw( ioaddr + COUNTER ); +			/* single collisions */ +			lp->stats.collisions += card_stats & 0xF; +			card_stats >>= 4; +			/* multiple collisions */ +			lp->stats.collisions += card_stats & 0xF; + +			/* these are for when linux supports these statistics */ +#if 0  +			card_stats >>= 4; +			/* deferred */ +			card_stats >>= 4; +			/* excess deferred */ +#endif  +			SMC_SELECT_BANK( 2 ); +			PRINTK2((KERN_WARNING CARDNAME  +				": TX_BUFFER_EMPTY handled\n")); +			outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); +			mask &= ~IM_TX_EMPTY_INT; +			lp->stats.tx_packets += lp->packets_waiting; +			lp->packets_waiting = 0; + +		} else if (status & IM_ALLOC_INT ) { +			PRINTK2((KERN_DEBUG CARDNAME +				": Allocation interrupt \n")); +			/* clear this interrupt so it doesn't happen again */ +			mask &= ~IM_ALLOC_INT; +		 +			smc_hardware_send_packet( dev ); +			 +			/* enable xmit interrupts based on this */ +			mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); + +			/* and let the card send more packets to me */ +			mark_bh( NET_BH ); + +			PRINTK2((CARDNAME": Handoff done successfully.\n"));	 +		} else if (status & IM_RX_OVRN_INT ) { +			lp->stats.rx_errors++; +			lp->stats.rx_fifo_errors++;			 +			outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); +		} else if (status & IM_EPH_INT ) { +			PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT \n")); +		} else if (status & IM_ERCV_INT ) { +			PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT \n")); +			outb( IM_ERCV_INT, ioaddr + INTERRUPT ); +		} +	} while ( timeout -- );  + +	 +	/* restore state register */ +	SMC_SELECT_BANK( 2 ); +	outb( mask, ioaddr + INT_MASK ); +	 +	PRINTK3(( KERN_WARNING CARDNAME ": MASK is now %x \n", mask )); +	outw( saved_pointer, ioaddr + POINTER ); + +	SMC_SELECT_BANK( saved_bank ); + +	dev->interrupt = 0; +	PRINTK3((CARDNAME ": Interrupt done\n")); +	return; +} + +/*------------------------------------------------------------- + . + . smc_rcv -  receive a packet from the card + . + . There is ( at least ) a packet waiting to be read from + . chip-memory. + .  + . o Read the status  + . o If an error, record it   + . o otherwise, read in the packet  + -------------------------------------------------------------- +*/ +static void smc_rcv(struct device *dev) +{ +	struct smc_local *lp = (struct smc_local *)dev->priv; +	int 	ioaddr = dev->base_addr; +	int 	packet_number; +	word	status; +	word	packet_length;  +	 +	/* assume bank 2 */ + +	packet_number = inw( ioaddr + FIFO_PORTS ); + +	if ( packet_number & FP_RXEMPTY ) { +		/* we got called , but nothing was on the FIFO */ +		PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO. \n")); +		/* don't need to restore anything */ +		return; +	} +		 +	/*  start reading from the start of the packet */ +	outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); + +	/* First two words are status and packet_length */	 +	status 		= inw( ioaddr + DATA_1 ); +	packet_length 	= inw( ioaddr + DATA_1 ); +	 +	packet_length &= 0x07ff;  /* mask off top bits */ + +	PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length )); +	/*  +	 . the packet length contains 3 extra words :  +	 . status, length, and a extra word with an odd byte . +	*/ +	packet_length -= 6;  +	 +	if ( !(status & RS_ERRORS ) ){			 +		/* do stuff to make a new packet */ +		struct sk_buff  * skb; +		byte		* data; + +		/* read one extra byte */ +		if ( status & RS_ODDFRAME ) 	 +			packet_length++; 	 + +		/* set multicast stats */  +		if ( status & RS_MULTICAST ) +			lp->stats.multicast++; + +#ifdef SUPPORT_OLD_KERNEL +		skb = alloc_skb( packet_length + 5, GFP_ATOMIC ); +#else	 +		skb = dev_alloc_skb( packet_length + 5);  +#endif + +		if ( skb == NULL ) {	 +			printk(KERN_NOTICE CARDNAME +			": Low memory, packet dropped.\n"); +			lp->stats.rx_dropped++; +		} + +		/*  +		 ! This should work without alignment, but it could be +		 ! in the worse case  +		*/ +#ifndef SUPPORT_OLD_KERNEL +		/* TODO: Should I use 32bit alignment here ? */ +		skb_reserve( skb, 2 );   /* 16 bit alignment */ +#endif + +		skb->dev = dev; +#ifdef SUPPORT_OLD_KERNEL +		skb->len = packet_length; +		data = skb->data; +#else +		data = skb_put( skb, packet_length); +#endif +#ifdef USE_32_BIT +		/* QUESTION:  Like in the TX routine, do I want 		 +		   to send the DWORDs or the bytes first, or some +		   mixture.  A mixture might improve already slow PIO +		   performance  */ +		PRINTK3((" Reading %d dwords (and %d bytes) \n",  +			packet_length >> 2, packet_length & 3 )); +		insl(ioaddr + DATA_1 , data, packet_length >> 2 );  +		/* read the left over bytes */ +		insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC),  +			packet_length & 0x3  ); +#else +		PRINTK3((" Reading %d words and %d byte(s) \n",  +			(packet_length >> 1 ), packet_length & 1 ); +		if ( packet_length & 1 )  +			*(data++) = inb( ioaddr + DATA_1 );  +		insw(ioaddr + DATA_1 , data, (packet_length + 1 ) >> 1); +		if ( packet_length & 1 ) { +			data += packet_length & ~1; +			*((data++) = inb( ioaddr + DATA_1 );  +		} +#endif 		 +#if	SMC_DEBUG > 2 	 +			print_packet( data, packet_length ); +#endif + +#ifndef SUPPORT_OLD_KERNEL +		skb->protocol = eth_type_trans(skb, dev );  +#endif +		netif_rx(skb); +		lp->stats.rx_packets++; +	} else { +		/* error ... */ +		lp->stats.rx_errors++; +			 +		if ( status & RS_ALGNERR )  lp->stats.rx_frame_errors++;   +		if ( status & (RS_TOOSHORT | RS_TOOLONG ) )   +			lp->stats.rx_length_errors++; +		if ( status & RS_BADCRC)	lp->stats.rx_crc_errors++; +	} +	/*  error or good, tell the card to get rid of this packet */ +	outw( MC_RELEASE, ioaddr + MMU_CMD ); + + +	return; +} + + +/*************************************************************************  + . smc_tx + .  + . Purpose:  Handle a transmit error message.   This will only be called + .   when an error, because of the AUTO_RELEASE mode.  + .  + . Algorithm: + .	Save pointer and packet no + .	Get the packet no from the top of the queue + .	check if it's valid ( if not, is this an error??? ) + .	read the status word  + .	record the error + .	( resend?  Not really, since we don't want old packets around ) + .	Restore saved values  + ************************************************************************/  +static void smc_tx( struct device * dev )  +{ +	int	ioaddr = dev->base_addr; +	struct smc_local *lp = (struct smc_local *)dev->priv; +	byte saved_packet; +	byte packet_no; +	word tx_status; + + +	/* assume bank 2  */ + +	saved_packet = inb( ioaddr + PNR_ARR ); +	packet_no = inw( ioaddr + FIFO_PORTS ); +	packet_no &= 0x7F; + +	/* select this as the packet to read from */ +	outb( packet_no, ioaddr + PNR_ARR );  +	 +	/* read the first word from this packet */	 +	outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER ); + +	tx_status = inw( ioaddr + DATA_1 ); +	PRINTK3((CARDNAME": TX DONE STATUS: %4x \n", tx_status )); +	 +	lp->stats.tx_errors++; +	if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++; +	if ( tx_status & TS_LATCOL  ) { +		printk(KERN_DEBUG CARDNAME  +			": Late collision occurred on last xmit.\n"); +		lp->stats.tx_window_errors++; +	} +#if 0 +		if ( tx_status & TS_16COL ) { ... } +#endif  + +	if ( tx_status & TS_SUCCESS ) {   +		printk(CARDNAME": Successful packet caused interrupt \n"); +	}  +	/* re-enable transmit */ +	SMC_SELECT_BANK( 0 ); +	outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR ); + +	/* kill the packet */			 +	SMC_SELECT_BANK( 2 ); +	outw( MC_FREEPKT, ioaddr + MMU_CMD ); + +	/* one less packet waiting for me */ +	lp->packets_waiting--; +		 +	outb( saved_packet, ioaddr + PNR_ARR ); +	return; +} + +/*---------------------------------------------------- + . smc_close + .  + . this makes the board clean up everything that it can + . and not talk to the outside world.   Caused by + . an 'ifconfig ethX down' + . + -----------------------------------------------------*/ +static int smc_close(struct device *dev) +{ +	dev->tbusy = 1; +	dev->start = 0; + +	/* clear everything */ +	smc_shutdown( dev->base_addr ); + +	/* Update the statistics here. */ +#ifdef MODULE +	MOD_DEC_USE_COUNT; +#endif + +	return 0; +} + +/*------------------------------------------------------------ + . Get the current statistics.	 + . This may be called with the card open or closed.  + .-------------------------------------------------------------*/ +static struct enet_statistics * smc_query_statistics(struct device *dev) { +	struct smc_local *lp = (struct smc_local *)dev->priv; + +	return &lp->stats; +} + +/*----------------------------------------------------------- + . smc_set_multicast_list + .   + . This routine will, depending on the values passed to it, + . either make it accept multicast packets, go into  + . promiscuous mode ( for TCPDUMP and cousins ) or accept + . a select set of multicast packets   +*/ +#ifdef SUPPORT_OLD_KERNEL +static void smc_set_multicast_list( struct device * dev,  +			int num_addrs, void * addrs ) 	 +#else +static void smc_set_multicast_list(struct device *dev)  +#endif  +{ +	short ioaddr = dev->base_addr; + +	SMC_SELECT_BANK(0); +#ifdef  SUPPORT_OLD_KERNEL +	if ( num_addrs < 0 )   +#else +	if ( dev->flags & IFF_PROMISC )  +#endif  +		outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR ); + +/* BUG?  I never disable promiscuous mode if multicasting was turned on.  +   Now, I turn off promiscuous mode, but I don't do anything to multicasting +   when promiscuous mode is turned on.  +*/ + +	/* Here, I am setting this to accept all multicast packets.   +	   I don't need to zero the multicast table, because the flag is +	   checked before the table is  +	*/ +#ifdef  SUPPORT_OLD_KERNEL  +	else if ( num_addrs > 20 )	/* arbitrary constant */ +#else +	else if (dev->flags & IFF_ALLMULTI)   +#endif  +		outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR );  + +	/* We just get all multicast packets even if we only want them +	 . from one source.  This will be changed at some future +	 . point. */ +#ifdef  SUPPORT_OLD_KERNEL +	else if (num_addrs > 0 ) {  +/* the old kernel support will not have hardware multicast support. It would +   involve more kludges, and make the multicast setting code even worse.   +   Instead, just use the ALMUL method.   This is reasonable, considering that +   it is seldom used +*/ +		outw( inw( ioaddr + RCR ) & ~RCR_PROMISC, ioaddr + RCR ); +		outw( inw( ioadddr + RCR ) | RCR_ALMUL, ioadddr + RCR ); +	} +#else +	else if (dev->mc_count )  {  +		/* support hardware multicasting */ +		 +		/* be sure I get rid of flags I might have set */	 +		outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),  +			ioaddr + RCR ); +		/* NOTE: this has to set the bank, so make sure it is the +		   last thing called.  The bank is set to zero at the top */ +		smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list ); +	} +#endif +	else  { +		outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),  +			ioaddr + RCR ); + +		/*  +		  since I'm disabling all multicast entirely, I need to  +		  clear the multicast list  +		*/ +		SMC_SELECT_BANK( 3 ); +		outw( 0, ioaddr + MULTICAST1 );  +		outw( 0, ioaddr + MULTICAST2 );  +		outw( 0, ioaddr + MULTICAST3 );  +		outw( 0, ioaddr + MULTICAST4 );  +	} +} + +#ifdef MODULE + +static char devicename[9] = { 0, }; +static struct device devSMC9194 = { +	devicename, /* device name is inserted by linux/drivers/net/net_init.c */ +	0, 0, 0, 0, +	0, 0,  /* I/O address, IRQ */ +	0, 0, 0, NULL, smc_init }; + +int io = 0; +int irq = 0; +int ifport = 0; + +int init_module(void) +{ +	int result; + +	if (io == 0) +		printk(KERN_WARNING  +		CARDNAME": You shouldn't use auto-probing with insmod!\n" ); + +	/* copy the parameters from insmod into the device structure */ +	devSMC9194.base_addr = io; +	devSMC9194.irq       = irq; +	devSMC9194.if_port	= ifport; +	if ((result = register_netdev(&devSMC9194)) != 0) +		return result; + +	return 0; +} + +void cleanup_module(void) +{ +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	unregister_netdev(&devSMC9194); + +	free_irq(devSMC9194.irq, NULL ); +	irq2dev_map[devSMC9194.irq] = NULL; +	release_region(devSMC9194.base_addr, SMC_IO_EXTENT); + +	if (devSMC9194.priv) +		kfree_s(devSMC9194.priv, sizeof(struct smc_local)); +} + +#endif /* MODULE */ + diff --git a/linux/src/drivers/net/smc9194.h b/linux/src/drivers/net/smc9194.h new file mode 100644 index 0000000..66f8b8c --- /dev/null +++ b/linux/src/drivers/net/smc9194.h @@ -0,0 +1,240 @@ +/*------------------------------------------------------------------------ + . smc9194.h + . Copyright (C) 1996 by Erik Stahlman  + . + . This software may be used and distributed according to the terms + . of the GNU Public License, incorporated herein by reference. + . + . This file contains register information and access macros for  + . the SMC91xxx chipset.    + .  + . Information contained in this file was obtained from the SMC91C94  + . manual from SMC.  To get a copy, if you really want one, you can find  + . information under www.smc.com in the components division. + . ( this thanks to advice from Donald Becker ). + .  + . Authors + . 	Erik Stahlman				( erik@vt.edu ) + . + . History + . 01/06/96		 Erik Stahlman   moved definitions here from main .c file + . 01/19/96		 Erik Stahlman	  polished this up some, and added better + .										  error handling + . + ---------------------------------------------------------------------------*/ +#ifndef _SMC9194_H_ +#define _SMC9194_H_ + +/* I want some simple types */ + +typedef unsigned char			byte; +typedef unsigned short			word; +typedef unsigned long int 		dword; + + +/* Because of bank switching, the SMC91xxx uses only 16 I/O ports */ + +#define SMC_IO_EXTENT	16 + + +/*--------------------------------------------------------------- + .   + . A description of the SMC registers is probably in order here, + . although for details, the SMC datasheet is invaluable.   + .  + . Basically, the chip has 4 banks of registers ( 0 to 3 ), which + . are accessed by writing a number into the BANK_SELECT register + . ( I also use a SMC_SELECT_BANK macro for this ). + .  + . The banks are configured so that for most purposes, bank 2 is all + . that is needed for simple run time tasks.   + -----------------------------------------------------------------------*/ + +/* + . Bank Select Register:  + . + .		yyyy yyyy 0000 00xx   + .		xx 		= bank number + .		yyyy yyyy	= 0x33, for identification purposes. +*/ +#define	BANK_SELECT		14 + +/* BANK 0  */ + +#define	TCR 		0    	/* transmit control register */ +#define TCR_ENABLE	0x0001	/* if this is 1, we can transmit */  +#define TCR_FDUPLX    	0x0800  /* receive packets sent out */ +#define TCR_STP_SQET	0x1000	/* stop transmitting if Signal quality error */ +#define	TCR_MON_CNS	0x0400	/* monitors the carrier status */ +#define	TCR_PAD_ENABLE	0x0080	/* pads short packets to 64 bytes */ + +#define	TCR_CLEAR	0	/* do NOTHING */ +/* the normal settings for the TCR register : */  +/* QUESTION: do I want to enable padding of short packets ? */ +#define	TCR_NORMAL  	TCR_ENABLE  + + +#define EPH_STATUS	2 +#define ES_LINK_OK	0x4000	/* is the link integrity ok ? */ + +#define	RCR		4 +#define RCR_SOFTRESET	0x8000 	/* resets the chip */	 +#define	RCR_STRIP_CRC	0x200	/* strips CRC */ +#define RCR_ENABLE	0x100	/* IFF this is set, we can receive packets */ +#define RCR_ALMUL	0x4 	/* receive all multicast packets */ +#define	RCR_PROMISC	0x2	/* enable promiscuous mode */ + +/* the normal settings for the RCR register : */ +#define	RCR_NORMAL	(RCR_STRIP_CRC | RCR_ENABLE) +#define RCR_CLEAR	0x0		/* set it to a base state */ + +#define	COUNTER		6 +#define	MIR		8 +#define	MCR		10 +/* 12 is reserved */ + +/* BANK 1 */ +#define CONFIG			0 +#define CFG_AUI_SELECT	 	0x100 +#define	BASE			2 +#define	ADDR0			4 +#define	ADDR1			6 +#define	ADDR2			8 +#define	GENERAL			10 +#define	CONTROL			12 +#define	CTL_POWERDOWN		0x2000 +#define	CTL_LE_ENABLE		0x80 +#define	CTL_CR_ENABLE		0x40 +#define	CTL_TE_ENABLE		0x0020 +#define CTL_AUTO_RELEASE	0x0800 +#define	CTL_EPROM_ACCESS	0x0003 /* high if Eprom is being read */ + +/* BANK 2 */ +#define MMU_CMD		0 +#define MC_BUSY		1	/* only readable bit in the register */ +#define MC_NOP		0 +#define	MC_ALLOC	0x20  	/* or with number of 256 byte packets */ +#define	MC_RESET	0x40	 +#define	MC_REMOVE	0x60  	/* remove the current rx packet */ +#define MC_RELEASE  	0x80  	/* remove and release the current rx packet */ +#define MC_FREEPKT  	0xA0  	/* Release packet in PNR register */ +#define MC_ENQUEUE	0xC0 	/* Enqueue the packet for transmit */ + 	 +#define	PNR_ARR		2 +#define FIFO_PORTS	4 + +#define FP_RXEMPTY  0x8000 +#define FP_TXEMPTY  0x80 + +#define	POINTER		6 +#define PTR_READ	0x2000 +#define	PTR_RCV		0x8000 +#define	PTR_AUTOINC 	0x4000 +#define PTR_AUTO_INC	0x0040 + +#define	DATA_1		8 +#define	DATA_2		10 +#define	INTERRUPT	12 + +#define INT_MASK	13 +#define IM_RCV_INT	0x1 +#define	IM_TX_INT	0x2 +#define	IM_TX_EMPTY_INT	0x4	 +#define	IM_ALLOC_INT	0x8 +#define	IM_RX_OVRN_INT	0x10 +#define	IM_EPH_INT	0x20 +#define	IM_ERCV_INT	0x40 /* not on SMC9192 */		 + +/* BANK 3 */ +#define	MULTICAST1	0 +#define	MULTICAST2	2 +#define	MULTICAST3	4 +#define	MULTICAST4	6 +#define	MGMT		8 +#define	REVISION	10 /* ( hi: chip id   low: rev # ) */ + + +/* this is NOT on SMC9192 */ +#define	ERCV		12 + +#define CHIP_9190	3 +#define CHIP_9194	4 +#define CHIP_9195	5 +#define CHIP_91100	7 + +static const char * chip_ids[ 15 ] =  {  +	NULL, NULL, NULL,  +	/* 3 */ "SMC91C90/91C92", +	/* 4 */ "SMC91C94", +	/* 5 */ "SMC91C95", +	NULL, +	/* 7 */ "SMC91C100",  +	NULL, NULL, NULL, NULL,  +	NULL, NULL, NULL};   + +/*  + . Transmit status bits  +*/ +#define TS_SUCCESS 0x0001 +#define TS_LOSTCAR 0x0400 +#define TS_LATCOL  0x0200 +#define TS_16COL   0x0010 + +/* + . Receive status bits +*/ +#define RS_ALGNERR	0x8000 +#define RS_BADCRC	0x2000 +#define RS_ODDFRAME	0x1000 +#define RS_TOOLONG	0x0800 +#define RS_TOOSHORT	0x0400 +#define RS_MULTICAST	0x0001 +#define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)  + +static const char * interfaces[ 2 ] = { "TP", "AUI" }; + +/*------------------------------------------------------------------------- + .  I define some macros to make it easier to do somewhat common + . or slightly complicated, repeated tasks.  + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3  */ + +#define SMC_SELECT_BANK(x)  { outw( x, ioaddr + BANK_SELECT ); }  + +/* define a small delay for the reset */ +#define SMC_DELAY() { inw( ioaddr + RCR );\ +			inw( ioaddr + RCR );\ +			inw( ioaddr + RCR );  } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(x) {\ +		unsigned char mask;\ +		SMC_SELECT_BANK(2);\ +		mask = inb( ioaddr + INT_MASK );\ +		mask |= (x);\ +		outb( mask, ioaddr + INT_MASK ); \ +} + +/* this disables an interrupt from the interrupt mask register */ + +#define SMC_DISABLE_INT(x) {\ +		unsigned char mask;\ +		SMC_SELECT_BANK(2);\ +		mask = inb( ioaddr + INT_MASK );\ +		mask &= ~(x);\ +		outb( mask, ioaddr + INT_MASK ); \ +} + +/*---------------------------------------------------------------------- + . Define the interrupts that I want to receive from the card + .  + . I want:  + .  IM_EPH_INT, for nasty errors + .  IM_RCV_INT, for happy received packets + .  IM_RX_OVRN_INT, because I have to kick the receiver + --------------------------------------------------------------------------*/ +#define SMC_INTERRUPT_MASK   (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT)  + +#endif  /* _SMC_9194_H_ */ + diff --git a/linux/src/drivers/net/starfire.c b/linux/src/drivers/net/starfire.c new file mode 100644 index 0000000..b8702a0 --- /dev/null +++ b/linux/src/drivers/net/starfire.c @@ -0,0 +1,1535 @@ +/* starfire.c: Linux device driver for the Adaptec Starfire network adapter. */ +/* +	Written/Copyright 1998-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Support information and updates available at +	http://www.scyld.com/network/starfire.html +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"starfire.c:v1.09 7/22/2003  Copyright by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +" Updates and info at http://www.scyld.com/network/starfire.html\n"; + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Used for tuning interrupt latency vs. overhead. */ +static int interrupt_mitigation = 0x0; + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   The Starfire has a 512 element hash table based on the Ethernet CRC.  */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   Both 'options[]' and 'full_duplex[]' exist for driver interoperability, +   however full_duplex[] should never be used in new configurations. +   The media type is usually passed in 'options[]'. +    The default is autonegotation for speed and duplex. +	This should rarely be overridden. +    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +    Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +    Use option values 0x20 and 0x200 for forcing full duplex operation. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Automatically extracted configuration info: +probe-func: starfire_probe +config-in: tristate 'Adaptec DuraLAN ("starfire") series PCI Ethernet support' CONFIG_DURLAN + +c-help-name: Adaptec DuraLAN ("starfire") series PCI Ethernet support +c-help-symbol: CONFIG_DURALAN +c-help: This driver is for the Adaptec DuraLAN series, the 6915, 62022 +c-help: and 62044 boards. +c-help: Design information, usage details and updates are available from +c-help: http://www.scyld.com/network/starfire.html +*/ + +/* Operational parameters that are set at compile time. */ + +/* The "native" ring sizes are either 256 or 2048. +   However in some modes a descriptor may be marked to wrap the ring earlier. +   The driver allocates a single page for each descriptor ring, constraining +   the maximum size in an architecture-dependent way. +*/ +#define RX_RING_SIZE	256 +#define TX_RING_SIZE	32 +/* The completion queues are fixed at 1024 entries i.e. 4K or 8KB. */ +#define DONE_Q_SIZE	1024 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. +   Compatibility defines are in kern_compat.h */ + +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM_DESC(debug, "Driver message enable level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, +				 "Non-zero to set forced full duplex (deprecated)."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This driver is for the Adaptec 6915 DuraLAN "Starfire" 64 bit PCI Ethernet +adapter, and the multiport boards using the same chip. + +II. Board-specific settings + +III. Driver operation + +IIIa. Ring buffers + +The Starfire hardware uses multiple fixed-size descriptor queues/rings.  The +ring sizes are set fixed by the hardware, but may optionally be wrapped +earlier by the END bit in the descriptor. +This driver uses that hardware queue size for the Rx ring, where a large +number of entries has no ill effect beyond increases the potential backlog. +The Tx ring is wrapped with the END bit, since a large hardware Tx queue +disables the queue layer priority ordering and we have no mechanism to +utilize the hardware two-level priority queue.  When modifying the +RX/TX_RING_SIZE pay close attention to page sizes and the ring-empty warning +levels. + +IIIb/c. Transmit/Receive Structure + +See the Adaptec manual for the many possible structures, and options for +each structure.  There are far too many to document here. + +For transmit this driver uses type 1 transmit descriptors, and relies on +automatic minimum-length padding.  It does not use the completion queue +consumer index, but instead checks for non-zero status entries. + +For receive this driver uses type 0 receive descriptors.  The driver +allocates full frame size skbuffs for the Rx ring buffers, so all frames +should fit in a single descriptor.  The driver does not use the completion +queue consumer index, but instead checks for non-zero status entries. + +When an incoming frame is less than RX_COPYBREAK bytes long, a fresh skbuff +is allocated and the frame is copied to the new skbuff.  When the incoming +frame is larger, the skbuff is passed directly up the protocol stack. +Buffers consumed this way are replaced by newly allocated skbuffs in a later +phase of receive. + +A notable aspect of operation is that unaligned buffers are not permitted by +the Starfire hardware.  The IP header at offset 14 in an ethernet frame thus +isn't longword aligned, which may cause problems on some machine +e.g. Alphas.  Copied frames are put into the skbuff at an offset of "+2", +16-byte aligning the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +IVb. References + +The Adaptec Starfire manuals, available only from Adaptec. +http://www.scyld.com/expert/100mbps.html +http://www.scyld.com/expert/NWay.html + +IVc. Errata + +*/ + + + +static void *starfire_probe1(struct pci_dev *pdev, void *init_dev, +							 long ioaddr, int irq, int chip_idx, int find_cnt); +static int starfire_pwr_event(void *dev_instance, int event); +enum chip_capability_flags {CanHaveMII=1, }; +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR0) +/* And maps in 0.5MB(!) -- no I/O mapping here!  */ +#define MEM_ADDR_SZ 0x80000 + +#if 0 && (defined(__x86_64) || defined(__alpha__)) +/* Enable 64 bit address modes. */ +#define STARFIRE_ADDR_64BITS 1 +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{"Adaptec Starfire 6915", { 0x69159004, 0xffffffff, }, +	 PCI_IOTYPE, MEM_ADDR_SZ, CanHaveMII}, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info starfire_drv_id = { +	"starfire", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	starfire_probe1, starfire_pwr_event }; + +/* Offsets to the device registers. +   Unlike software-only systems, device drivers interact with complex hardware. +   It's not useful to define symbolic names for every register bit in the +   device.  The name can only partially document the semantics and make +   the driver longer and more difficult to read. +   In general, only the important configuration values or bits changed +   multiple times should be defined symbolically. +*/ +enum register_offsets { +	PCIDeviceConfig=0x50040, GenCtrl=0x50070, IntrTimerCtrl=0x50074, +	IntrClear=0x50080, IntrStatus=0x50084, IntrEnable=0x50088, +	MIICtrl=0x52000, StationAddr=0x50120, EEPROMCtrl=0x51000, +	TxDescCtrl=0x50090, +	TxRingPtr=0x50098, HiPriTxRingPtr=0x50094, /* Low and High priority. */ +	TxRingHiAddr=0x5009C,		/* 64 bit address extension. */ +	TxProducerIdx=0x500A0, TxConsumerIdx=0x500A4, +	TxThreshold=0x500B0, +	CompletionHiAddr=0x500B4, TxCompletionAddr=0x500B8, +	RxCompletionAddr=0x500BC, RxCompletionQ2Addr=0x500C0, +	CompletionQConsumerIdx=0x500C4, +	RxDescQCtrl=0x500D4, RxDescQHiAddr=0x500DC, RxDescQAddr=0x500E0, +	RxDescQIdx=0x500E8, RxDMAStatus=0x500F0, RxFilterMode=0x500F4, +	TxMode=0x55000, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrNormalSummary=0x8000,	IntrAbnormalSummary=0x02000000, +	IntrRxDone=0x0300, IntrRxEmpty=0x10040, IntrRxPCIErr=0x80000, +	IntrTxDone=0x4000, IntrTxEmpty=0x1000, IntrTxPCIErr=0x80000, +	StatsMax=0x08000000, LinkChange=0xf0000000, +	IntrTxDataLow=0x00040000, +	IntrPCIPin=0x01, +}; + +/* Bits in the RxFilterMode register. */ +enum rx_mode_bits { +	AcceptBroadcast=0x04, AcceptAllMulticast=0x02, AcceptAll=0x01, +	AcceptMulticast=0x10, AcceptMyPhys=0xE040, +}; + +/* Misc. bits.  Symbolic names so that may be searched for. */ +enum misc_bits { +	ChipResetCmd=1,				/* PCIDeviceConfig */ +	PCIIntEnb=0x00800000,		/* PCIDeviceConfig */ +	TxEnable=0x0A, RxEnable=0x05, SoftIntr=0x100, /* GenCtrl */ +}; + +/* The Rx and Tx buffer descriptors. */ +struct starfire_rx_desc { +	u32 rxaddr;					/* Optionally 64 bits. */ +#if defined(STARFIRE_ADDR_64BITS) +	u32 rxaddr_hi;					/* Optionally 64 bits. */ +#endif +}; +enum rx_desc_bits { +	RxDescValid=1, RxDescEndRing=2, +}; + +/* Completion queue entry. +   You must update the page allocation, init_ring and the shift count in rx() +   if using a larger format. */ +struct rx_done_desc { +	u32 status;					/* Low 16 bits is length. */ +#ifdef full_rx_status +	u32 status2; +	u16 vlanid; +	u16 csum; 			/* partial checksum */ +	u32 timestamp; +#endif +}; +enum rx_done_bits { +	RxOK=0x20000000, RxFIFOErr=0x10000000, RxBufQ2=0x08000000, +}; + +/* Type 1 Tx descriptor. */ +struct starfire_tx_desc { +	u32 status;					/* Upper bits are status, lower 16 length. */ +	u32 addr; +}; +enum tx_desc_bits { +	TxDescID=0xB1010000,		/* Also marks single fragment, add CRC.  */ +	TxDescIntr=0x08000000, TxRingWrap=0x04000000, +}; +struct tx_done_report { +	u32 status;					/* timestamp, index. */ +#if 0 +	u32 intrstatus;				/* interrupt status */ +#endif +}; + +#define PRIV_ALIGN	15 	/* Required alignment mask */ +struct netdev_private { +	/* Descriptor rings first for alignment. */ +	struct starfire_rx_desc *rx_ring; +	struct starfire_tx_desc *tx_ring; +	struct net_device *next_module;		/* Link for devices of this type. */ +	void *priv_addr;					/* Unaligned address for kfree */ +	const char *product_name; +	/* The addresses of rx/tx-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	u8 pad0[100];						/* Impact padding */ +	/* Pointers to completion queues (full pages).  Cache line pad.. */ +	struct rx_done_desc *rx_done_q  __attribute__((aligned (L1_CACHE_BYTES))); +	unsigned int rx_done; +	struct tx_done_report *tx_done_q __attribute__((aligned (L1_CACHE_BYTES))); +	unsigned int tx_done; + +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	int msg_level; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	/* Frequently used values: keep some adjacent for cache effect. */ +	int max_interrupt_work; +	int intr_enable; +	unsigned int restore_intr_enable:1;	/* Set if temporarily masked.  */ +	unsigned int polling:1;				/* Erk, IRQ err. */ + +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	/* These values keep track of the transceiver/media in use. */ +	unsigned int full_duplex:1,			/* Full-duplex operation requested. */ +		medialock:1,					/* Xcvr set to fixed speed/duplex. */ +		rx_flowctrl:1, +		tx_flowctrl:1;					/* Use 802.3x flow control. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	u32 tx_mode; +	u8 tx_threshold; +	u32 cur_rx_mode; +	u16 mc_filter[32]; +	int multicast_filter_limit; + +	/* MII transceiver section. */ +	int mii_cnt;						/* MII device addresses. */ +	u16 advertising;					/* NWay media advertisement */ +	unsigned char phys[2];				/* MII device addresses. */ +}; + +static int  mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, +					   int value); +static int  netdev_open(struct net_device *dev); +static int  change_mtu(struct net_device *dev, int new_mtu); +static void check_duplex(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct net_device *dev); +static void init_ring(struct net_device *dev); +static int  start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int  netdev_rx(struct net_device *dev); +static void netdev_error(struct net_device *dev, int intr_status); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int  netdev_close(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_net_dev = NULL; + +#ifndef MODULE +int starfire_probe(struct net_device *dev) +{ +	if (pci_drv_register(&starfire_drv_id, dev) < 0) +		return -ENODEV; +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif + +static void *starfire_probe1(struct pci_dev *pdev, void *init_dev, +							 long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct netdev_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	printk(KERN_INFO "%s: %s at 0x%lx, ", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr); + +	/* Serial EEPROM reads are hidden by the hardware. */ +	for (i = 0; i < 6; i++) +		dev->dev_addr[i] = readb(ioaddr + EEPROMCtrl + 20-i); +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	/* Reset the chip to erase previous misconfiguration. */ +	writel(ChipResetCmd, ioaddr + PCIDeviceConfig); + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_net_dev; +	root_net_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	if (dev->mem_start) +		option = dev->mem_start; + +	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) { +		if (np->msg_level & NETIF_MSG_PROBE) +			printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" +				   " disabled.\n", dev->name); +		np->medialock = 1; +	} + +	/* The chip-specific entries in the device structure. */ +	dev->open = &netdev_open; +	dev->hard_start_xmit = &start_tx; +	dev->stop = &netdev_close; +	dev->get_stats = &get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; +	dev->change_mtu = &change_mtu; + +	if (np->drv_flags & CanHaveMII) { +		int phy, phy_idx = 0; +		for (phy = 0; phy < 32 && phy_idx < 4; phy++) { +			int mii_status = mdio_read(dev, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				np->phys[phy_idx++] = phy; +				np->advertising = mdio_read(dev, phy, 4); +				if (np->msg_level & NETIF_MSG_PROBE) +					printk(KERN_INFO "%s: MII PHY found at address %d, status " +						   "0x%4.4x advertising %4.4x.\n", +						   dev->name, phy, mii_status, np->advertising); +			} +		} +		np->mii_cnt = phy_idx; +	} + +	/* Force the media type after detecting the transceiver. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 0x3ff; +		if (np->default_port & 0x330) { +			np->medialock = 1; +			if (np->msg_level & NETIF_MSG_PROBE) +				printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +					   (option & 0x300 ? 100 : 10), +					   (np->full_duplex ? "full" : "half")); +			mdio_write(dev, np->phys[0], 0, +					   ((option & 0x300) ? 0x2000 : 0) | 	/* 100mbps? */ +					   (np->full_duplex ? 0x0100 : 0)); /* Full duplex? */ +		} +	} + +	return dev; +} + + +/* Read the MII Management Data I/O (MDIO) interfaces. */ + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ +	long mdio_addr = dev->base_addr + MIICtrl + (phy_id<<7) + (location<<2); +	int result, boguscnt=1000; +	/* ??? Should we add a busy-wait here? */ +	do +		result = readl(mdio_addr); +	while ((result & 0xC0000000) != 0x80000000 && --boguscnt >= 0); +	return result & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, int value) +{ +	long mdio_addr = dev->base_addr + MIICtrl + (phy_id<<7) + (location<<2); +	writel(value, mdio_addr); +	/* The busy-wait will occur before a read. */ +	return; +} + + +static int netdev_open(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	/* We have no reports that indicate we need to reset the chip. +	   But to be on the safe side... */ +	/* Disable the Rx and Tx, and reset the chip. */ +	writel(0, ioaddr + GenCtrl); +	writel(ChipResetCmd, ioaddr + PCIDeviceConfig); +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", +			   dev->name, dev->irq); +	/* Allocate the various queues, failing gracefully. */ +	if (np->tx_done_q == 0) +		np->tx_done_q = (struct tx_done_report *)get_free_page(GFP_KERNEL); +	if (np->rx_done_q == 0) +		np->rx_done_q = (struct rx_done_desc *)get_free_page(GFP_KERNEL); +	if (np->tx_ring == 0) +		np->tx_ring = (struct starfire_tx_desc *)get_free_page(GFP_KERNEL); +	if (np->rx_ring == 0) +		np->rx_ring = (struct starfire_rx_desc *)get_free_page(GFP_KERNEL); +	if (np->tx_done_q == 0  ||  np->rx_done_q == 0 +		|| np->rx_ring == 0 ||  np->tx_ring == 0) { +		/* Retain the pages to increase our chances next time. */ +		MOD_DEC_USE_COUNT; +		return -ENOMEM; +	} + +	init_ring(dev); +	/* Set the size of the Rx buffers. */ +	writel((np->rx_buf_sz<<16) | 0xA000, ioaddr + RxDescQCtrl); + +	/* Set Tx descriptor to type 1 and padding to 0 bytes. */ +	writel(0x02000401, ioaddr + TxDescCtrl); + +#if defined(STARFIRE_ADDR_64BITS) +	writel(virt_to_bus(np->rx_ring) >> 32, ioaddr + RxDescQHiAddr); +	writel(virt_to_bus(np->tx_ring) >> 32, ioaddr + TxRingHiAddr); +#else +	writel(0, ioaddr + RxDescQHiAddr); +	writel(0, ioaddr + TxRingHiAddr); +	writel(0, ioaddr + CompletionHiAddr); +#endif +	writel(virt_to_bus(np->rx_ring), ioaddr + RxDescQAddr); +	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); + +	writel(virt_to_bus(np->tx_done_q), ioaddr + TxCompletionAddr); +	writel(virt_to_bus(np->rx_done_q), ioaddr + RxCompletionAddr); + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s:  Filling in the station address.\n", dev->name); + +	/* Fill both the unused Tx SA register and the Rx perfect filter. */ +	for (i = 0; i < 6; i++) +		writeb(dev->dev_addr[i], ioaddr + StationAddr + 5-i); +	for (i = 0; i < 16; i++) { +		u16 *eaddrs = (u16 *)dev->dev_addr; +		long setup_frm = ioaddr + 0x56000 + i*16; +		writew(cpu_to_be16(eaddrs[2]), setup_frm); setup_frm += 4; +		writew(cpu_to_be16(eaddrs[1]), setup_frm); setup_frm += 4; +		writew(cpu_to_be16(eaddrs[0]), setup_frm); setup_frm += 8; +	} + +	/* Initialize other registers. */ +	/* Configure the PCI bus bursts and FIFO thresholds. */ +	np->tx_mode = 0;			/* Initialized when TxMode set. */ +	np->tx_threshold = 4; +	writel(np->tx_threshold, ioaddr + TxThreshold); +	writel(interrupt_mitigation, ioaddr + IntrTimerCtrl); + +	if (dev->if_port == 0) +		dev->if_port = np->default_port; + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s:  Setting the Rx and Tx modes.\n", dev->name); +	set_rx_mode(dev); + +	np->advertising = mdio_read(dev, np->phys[0], 4); +	check_duplex(dev); +	netif_start_tx_queue(dev); + +	/* Set the interrupt mask and enable PCI interrupts. */ +	np->intr_enable = IntrRxDone | IntrRxEmpty | IntrRxPCIErr | +		IntrTxDone | IntrTxEmpty | IntrTxPCIErr | +		StatsMax | LinkChange | IntrNormalSummary | IntrAbnormalSummary +		| 0x0010; +	writel(np->intr_enable, ioaddr + IntrEnable); +	writel(PCIIntEnb | readl(ioaddr + PCIDeviceConfig), +		   ioaddr + PCIDeviceConfig); + +	/* Enable the Rx and Tx units. */ +	writel(TxEnable|RxEnable, ioaddr + GenCtrl); + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done netdev_open().\n", +			   dev->name); + +	/* Set the timer to check for link beat. */ +	init_timer(&np->timer); +	np->timer.expires = jiffies + 3*HZ; +	np->timer.data = (unsigned long)dev; +	np->timer.function = &netdev_timer;				/* timer handler */ +	add_timer(&np->timer); + +	return 0; +} + +/* The starfire can handle frame sizes up to 64KB, but we arbitrarily + * limit the size. + */ +static int change_mtu(struct net_device *dev, int new_mtu) +{ +	if ((new_mtu < 68) || (new_mtu > 17268)) +		return -EINVAL; +	if (netif_running(dev)) +		return -EBUSY; +	dev->mtu = new_mtu; +	return 0; +} + +static void check_duplex(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int new_tx_mode; + +	new_tx_mode = 0x0C04 | (np->tx_flowctrl ? 0x0800:0) +		| (np->rx_flowctrl ? 0x0400:0); +	if (np->medialock) { +		if (np->full_duplex) +			new_tx_mode |= 2; +	} else { +		int mii_reg5 = mdio_read(dev, np->phys[0], 5); +		int negotiated = mii_reg5 & np->advertising; +		int duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; +		if (duplex) +			new_tx_mode |= 2; +		if (np->full_duplex != duplex) { +			np->full_duplex = duplex; +			if (np->msg_level & NETIF_MSG_LINK) +				printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d" +					   " negotiated capability %4.4x.\n", dev->name, +					   duplex ? "full" : "half", np->phys[0], negotiated); +		} +	} +	if (new_tx_mode != np->tx_mode) { +		np->tx_mode = new_tx_mode; +		writel(np->tx_mode | 0x8000, ioaddr + TxMode); +		writel(np->tx_mode, ioaddr + TxMode); +	} +} + +/* Check for duplex changes, but mostly check for failures. */ +static void netdev_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int status = readl(ioaddr + IntrStatus); +	static long last_msg = 0; + +	/* Normally we check only every few seconds. */ +	np->timer.expires = jiffies + 60*HZ; + +	if (np->msg_level & NETIF_MSG_TIMER) { +		printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x.\n", +			   dev->name, status); +	} + +	/* Check for a missing chip or failed interrupt line. +	 * The latter may be falsely triggered, so we check twice. */ +	if (status == 0xffffffff) { +		if (jiffies - last_msg > 10*HZ) { +			last_msg = jiffies; +			printk(KERN_ERR "%s: The Starfire chip is missing!\n", +				   dev->name); +		} +	} else if (np->polling) { +		if (status & IntrPCIPin) { +			intr_handler(dev->irq, dev, 0); +			if (jiffies - last_msg > 10*HZ) { +				printk(KERN_ERR "%s: IRQ %d is still blocked!\n", +					   dev->name, dev->irq); +				last_msg = jiffies; +			} +		} else if (jiffies - last_msg > 10*HZ) +			np->polling = 0; +		np->timer.expires = jiffies + 2; +	} else if (status & IntrPCIPin) { +		int new_status = readl(ioaddr + IntrStatus); +		/* Bogus hardware IRQ mapping: Fake an interrupt handler call. */ +		if (new_status & IntrPCIPin) { +			printk(KERN_ERR "%s: IRQ %d is not raising an interrupt! " +				   "Status %8.8x/%8.8x.  \n", +				   dev->name, dev->irq, status, new_status); +			intr_handler(dev->irq, dev, 0); +			np->timer.expires = jiffies + 2; +			np->polling = 1; +		} +	} else if (netif_queue_paused(dev)  && +			   np->cur_tx - np->dirty_tx > 1  && +			   (jiffies - dev->trans_start) > TX_TIMEOUT) { +		/* This will not catch tbusy incorrectly set when the queue is empty, +		 * but that state should never occur. */ +		tx_timeout(dev); +	} + +	check_duplex(dev); + +	add_timer(&np->timer); +} + +static void tx_timeout(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," +		   " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus)); + +#if defined(__i386__) +	if (np->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk("\n" KERN_DEBUG "  Tx ring %p: ", np->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %4.4x", np->tx_ring[i].status); +		printk("\n" KERN_DEBUG "  Rx ring %p: ", np->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)np->rx_ring[i].rxaddr); +		printk("\n"); +	} +#endif + +	/* If a specific problem is reported, reinitialize the hardware here. */ +	dev->if_port = 0; +	/* Stop and restart the chip's Tx processes . */ +	writel(0, ioaddr + GenCtrl); +	/* Enable the Rx and Tx units. */ +	writel(TxEnable|RxEnable, ioaddr + GenCtrl); + +	dev->trans_start = jiffies; +	np->stats.tx_errors++; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	np->tx_full = 0; +	np->cur_rx = np->cur_tx = 0; +	np->dirty_rx = np->rx_done = np->dirty_tx = np->tx_done = 0; + +	np->rx_buf_sz = (dev->mtu <= 1522 ? PKT_BUF_SZ : +					 (dev->mtu + 14 + 3) & ~3);	/* Round to word. */ + +	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); +		np->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		/* Grrr, we cannot offset to correctly align the IP header. */ +		np->rx_ring[i].rxaddr = +			virt_to_le32desc(skb->tail) | cpu_to_le32(RxDescValid); +	} +	writew(i - 1, dev->base_addr + RxDescQIdx); +	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + +	/* Clear the remainder of the Rx buffer ring. */ +	for (  ; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].rxaddr = 0; +		np->rx_skbuff[i] = 0; +	} +	/* Mark the last entry as wrapping the ring. */ +	np->rx_ring[i-1].rxaddr |= cpu_to_le32(RxDescEndRing); + +	/* Clear the completion rings. */ +	for (i = 0; i < DONE_Q_SIZE; i++) { +		np->rx_done_q[i].status = 0; +		np->tx_done_q[i].status = 0; +	} + +	for (i = 0; i < TX_RING_SIZE; i++) { +		np->tx_skbuff[i] = 0; +		np->tx_ring[i].status = 0; +	} +	return; +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned entry; + +	/* Block a timer-based transmit from overlapping.  This happens when +	   packets are presumed lost, and we use this check the Tx status. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tx_timeout(dev); +		return 1; +	} + +	/* Caution: the write order is important here, set the field +	   with the "ownership" bits last. */ + +	/* Calculate the next Tx descriptor entry. */ +	entry = np->cur_tx % TX_RING_SIZE; + +	np->tx_skbuff[entry] = skb; + +	np->tx_ring[entry].addr = virt_to_le32desc(skb->data); +	/* Add  "| TxDescIntr" to generate Tx-done interrupts. */ +	np->tx_ring[entry].status = cpu_to_le32(skb->len | TxDescID); +#if 1 +	if (entry >= TX_RING_SIZE-1) {		 /* Wrap ring */ +		np->tx_ring[entry].status |= cpu_to_le32(TxRingWrap | TxDescIntr); +		entry = -1; +	} +#endif + +	/* On some architectures better performance results by explicitly +	   flushing cache lines: pci_flush_virt(skb->data, skb->len); */ + +	np->cur_tx++; +	/* Update the producer index. */ +	writel(++entry, dev->base_addr + TxProducerIdx); + +	/* cf. using TX_QUEUE_LEN instead of TX_RING_SIZE here. */ +	if (np->cur_tx - np->dirty_tx >= TX_RING_SIZE - 1) { +		np->tx_full = 1; +		/* Check for the rare case of a just-cleared queue. */ +		if (np->cur_tx - (volatile unsigned int)np->dirty_tx +			< TX_RING_SIZE - 2) { +			np->tx_full = 0; +			netif_unpause_tx_queue(dev); +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ + +	dev->trans_start = jiffies; + +	if (np->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Tx frame #%d slot %d  %8.8x %8.8x.\n", +			   dev->name, np->cur_tx, entry, +			   np->tx_ring[entry].status, np->tx_ring[entry].addr); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct netdev_private *np; +	long ioaddr; +	int boguscnt; + +#ifndef final_version			/* Can never occur. */ +	if (dev == NULL) { +		printk (KERN_ERR "Netdev interrupt handler(): IRQ %d for unknown " +				"device.\n", irq); +		return; +	} +#endif + +	ioaddr = dev->base_addr; +	np = (struct netdev_private *)dev->priv; +	boguscnt = np->max_interrupt_work; + +	do { +		u32 intr_status = readl(ioaddr + IntrClear); + +		if (np->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt status %4.4x.\n", +				   dev->name, intr_status); + +		if (intr_status == 0 || intr_status == 0xffffffff) +			break; + +		if (intr_status & IntrRxDone) +			netdev_rx(dev); + +		/* Scavenge the skbuff list based on the Tx-done queue. +		   There are redundant checks here that may be cleaned up +		   after the driver has proven to be reliable. */ +		{ +			int consumer = readl(ioaddr + TxConsumerIdx); +			int tx_status; +			if (np->msg_level & NETIF_MSG_INTR) +				printk(KERN_DEBUG "%s: Tx Consumer index is %d.\n", +					   dev->name, consumer); +#if 0 +			if (np->tx_done >= 250  || np->tx_done == 0) +				printk(KERN_DEBUG "%s: Tx completion entry %d is %8.8x, " +					   "%d is %8.8x.\n", dev->name, +					   np->tx_done, np->tx_done_q[np->tx_done].status, +					   (np->tx_done+1) & (DONE_Q_SIZE-1), +					   np->tx_done_q[(np->tx_done+1)&(DONE_Q_SIZE-1)].status); +#endif +			while ((tx_status = cpu_to_le32(np->tx_done_q[np->tx_done].status)) +				   != 0) { +				if (np->msg_level & NETIF_MSG_TX_DONE) +					printk(KERN_DEBUG "%s: Tx completion entry %d is %8.8x.\n", +						   dev->name, np->tx_done, tx_status); +				if ((tx_status & 0xe0000000) == 0xa0000000) { +					np->stats.tx_packets++; +				} else if ((tx_status & 0xe0000000) == 0x80000000) { +					u16 entry = tx_status; 		/* Implicit truncate */ +					entry >>= 3; +					/* Scavenge the descriptor. */ +					if (np->tx_skbuff[entry]) { +						dev_free_skb_irq(np->tx_skbuff[entry]); +					} else +						printk(KERN_WARNING "%s: Null skbuff at entry %d!!!\n", +							   dev->name, entry); +					np->tx_skbuff[entry] = 0; +					np->dirty_tx++; +				} +				np->tx_done_q[np->tx_done].status = 0; +				np->tx_done = (np->tx_done+1) & (DONE_Q_SIZE-1); +			} +			writew(np->tx_done, ioaddr + CompletionQConsumerIdx + 2); +		} +		if (np->tx_full && np->cur_tx - np->dirty_tx < TX_RING_SIZE - 4) { +			/* The ring is no longer full, allow new TX entries. */ +			np->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & IntrAbnormalSummary) +			netdev_error(dev, intr_status); + +		if (--boguscnt < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", +				   dev->name, intr_status); +			writel(0x0021, ioaddr + IntrTimerCtrl); +			break; +		} +	} while (1); + +	if (np->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); + +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; +	u32 desc_status; + +	if (np->rx_done_q == 0) { +		printk(KERN_ERR "%s:  rx_done_q is NULL!  rx_done is %d. %p.\n", +			   dev->name, np->rx_done, np->tx_done_q); +		return 0; +	} + +	/* If EOP is set on the next entry, it's a new packet. Send it up. */ +	while ((desc_status = le32_to_cpu(np->rx_done_q[np->rx_done].status)) != 0) { +		if (np->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  netdev_rx() status of %d was %8.8x.\n", +				   np->rx_done, desc_status); +		if (--boguscnt < 0) +			break; +		if ( ! (desc_status & RxOK)) { +			/* There was a error. */ +			if (np->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n", +					   desc_status); +			np->stats.rx_errors++; +			if (desc_status & RxFIFOErr) +				np->stats.rx_fifo_errors++; +		} else { +			struct sk_buff *skb; +			u16 pkt_len = desc_status;			/* Implicitly Truncate */ +			int entry = (desc_status >> 16) & 0x7ff; + +#ifndef final_version +			if (np->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d" +					   ", bogus_cnt %d.\n", +					   pkt_len, boguscnt); +#endif +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +#if HAS_IP_COPYSUM			/* Call copy + cksum if available. */ +				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +			} else { +				char *temp = skb_put(skb = np->rx_skbuff[entry], pkt_len); +				np->rx_skbuff[entry] = NULL; +#ifndef final_version				/* Remove after testing. */ +				if (le32desc_to_virt(np->rx_ring[entry].rxaddr & ~3) != temp) +					printk(KERN_ERR "%s: Internal fault: The skbuff addresses " +						   "do not match in netdev_rx: %p vs. %p / %p.\n", +						   dev->name, +						   le32desc_to_virt(np->rx_ring[entry].rxaddr), +						   skb->head, temp); +#endif +			} +			skb->protocol = eth_type_trans(skb, dev); +#ifdef full_rx_status +			if (np->rx_done_q[np->rx_done].status2 & cpu_to_le32(0x01000000)) +				skb->ip_summed = CHECKSUM_UNNECESSARY; +#endif +			netif_rx(skb); +			dev->last_rx = jiffies; +			np->stats.rx_packets++; +		} +		np->cur_rx++; +		np->rx_done_q[np->rx_done].status = 0; +		np->rx_done = (np->rx_done + 1) & (DONE_Q_SIZE-1); +	} +	writew(np->rx_done, dev->base_addr + CompletionQConsumerIdx); + +	/* Refill the Rx ring buffers. */ +	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { +		struct sk_buff *skb; +		int entry = np->dirty_rx % RX_RING_SIZE; +		if (np->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(np->rx_buf_sz); +			np->rx_skbuff[entry] = skb; +			if (skb == NULL) +				break;				/* Better luck next round. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			np->rx_ring[entry].rxaddr = +				virt_to_le32desc(skb->tail) | cpu_to_le32(RxDescValid); +		} +		if (entry == RX_RING_SIZE - 1) +			np->rx_ring[entry].rxaddr |= cpu_to_le32(RxDescEndRing); +		/* We could defer this until later... */ +		writew(entry, dev->base_addr + RxDescQIdx); +	} + +	if ((np->msg_level & NETIF_MSG_RX_STATUS) +		|| memcmp(np->pad0, np->pad0 + 1, sizeof(np->pad0) -1)) +		printk(KERN_DEBUG "  exiting netdev_rx() status of %d was %8.8x %d.\n", +			   np->rx_done, desc_status, +			   memcmp(np->pad0, np->pad0 + 1, sizeof(np->pad0) -1)); + +	return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; + +	if (intr_status & LinkChange) { +		int phy_num = np->phys[0]; +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_NOTICE "%s: Link changed: Autonegotiation advertising" +				   " %4.4x  partner %4.4x.\n", dev->name, +				   mdio_read(dev, phy_num, 4), +				   mdio_read(dev, phy_num, 5)); +		/* Clear sticky bit. */ +		mdio_read(dev, phy_num, 1); +		/* If link beat has returned... */ +		if (mdio_read(dev, phy_num, 1) & 0x0004) +			netif_link_up(dev); +		else +			netif_link_down(dev); +		check_duplex(dev); +	} +	if (intr_status & StatsMax) { +		get_stats(dev); +	} +	/* Came close to underrunning the Tx FIFO, increase threshold. */ +	if (intr_status & IntrTxDataLow) +		writel(++np->tx_threshold, dev->base_addr + TxThreshold); +	/* Ingore expected normal events, and handled abnormal events. */ +	if ((intr_status & +		 ~(IntrAbnormalSummary|LinkChange|StatsMax|IntrTxDataLow| 0xFF01)) +		&& (np->msg_level & NETIF_MSG_DRV)) +		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", +			   dev->name, intr_status); +	/* Hmmmmm, it's not clear how to recover from PCI faults. */ +	if (intr_status & IntrTxPCIErr) +		np->stats.tx_fifo_errors++; +	if (intr_status & IntrRxPCIErr) +		np->stats.rx_fifo_errors++; +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; + +	/* This adapter architecture needs no SMP locks. */ +#if LINUX_VERSION_CODE > 0x20119 +	np->stats.tx_bytes = readl(ioaddr + 0x57010); +	np->stats.rx_bytes = readl(ioaddr + 0x57044); +#endif +	np->stats.tx_packets = readl(ioaddr + 0x57000); +	np->stats.tx_aborted_errors = +		readl(ioaddr + 0x57024) + readl(ioaddr + 0x57028); +	np->stats.tx_window_errors = readl(ioaddr + 0x57018); +	np->stats.collisions = readl(ioaddr + 0x57004) + readl(ioaddr + 0x57008); + +	/* The chip only need report frame silently dropped. */ +	np->stats.rx_dropped	   += readw(ioaddr + RxDMAStatus); +	writew(0, ioaddr + RxDMAStatus); +	np->stats.rx_crc_errors	   = readl(ioaddr + 0x5703C); +	np->stats.rx_frame_errors = readl(ioaddr + 0x57040); +	np->stats.rx_length_errors = readl(ioaddr + 0x57058); +	np->stats.rx_missed_errors = readl(ioaddr + 0x5707C); + +	return &np->stats; +} + +/* The little-endian AUTODIN II ethernet CRC calculations. +   A big-endian version is also available. +   This is slow but compact code.  Do not use this routine for bulk data, +   use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c. +   Chips may use the upper or lower CRC bits, and may reverse and/or invert +   them.  Select the endian-ness that results in minimal calculations. +*/ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ +	unsigned int crc = ~0;	/* Initial value. */ +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u32 rx_mode; +	struct dev_mc_list *mclist; +	int i; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		rx_mode = AcceptBroadcast|AcceptAllMulticast|AcceptAll|AcceptMyPhys; +	} else if ((dev->mc_count > np->multicast_filter_limit) +			   ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to match, or accept all multicasts. */ +		rx_mode = AcceptBroadcast|AcceptAllMulticast|AcceptMyPhys; +	} else if (dev->mc_count <= 15) { +		/* Use the 16 element perfect filter. */ +		long filter_addr = ioaddr + 0x56000 + 1*16; +		for (i = 1, mclist = dev->mc_list; mclist  &&  i <= dev->mc_count; +			 i++, mclist = mclist->next) { +			u16 *eaddrs = (u16 *)mclist->dmi_addr; +			writew(cpu_to_be16(eaddrs[2]), filter_addr); filter_addr += 4; +			writew(cpu_to_be16(eaddrs[1]), filter_addr); filter_addr += 4; +			writew(cpu_to_be16(eaddrs[0]), filter_addr); filter_addr += 8; +		} +		while (i++ < 16) { +			writew(0xffff, filter_addr); filter_addr += 4; +			writew(0xffff, filter_addr); filter_addr += 4; +			writew(0xffff, filter_addr); filter_addr += 8; +		} +		rx_mode = AcceptBroadcast | AcceptMyPhys; +	} else { +		/* Must use a multicast hash table. */ +		long filter_addr; +		u16 mc_filter[32];			/* Multicast hash filter */ + +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 23, mc_filter); +		} +		/* Clear the perfect filter list. */ +		filter_addr = ioaddr + 0x56000 + 1*16; +		for (i = 1; i < 16; i++) { +			writew(0xffff, filter_addr); filter_addr += 4; +			writew(0xffff, filter_addr); filter_addr += 4; +			writew(0xffff, filter_addr); filter_addr += 8; +		} +		for (filter_addr=ioaddr + 0x56100, i=0; i < 32; filter_addr+= 16, i++){ +			np->mc_filter[i] = mc_filter[i]; +			writew(mc_filter[i], filter_addr); +		} +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; +	} +	writel(rx_mode, ioaddr + RxFilterMode); +} + +/* +  Handle user-level ioctl() calls. +  We must use two numeric constants as the key because some clueless person +  changed the value for the symbolic name. +*/ +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = np->phys[0] & 0x1f; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == np->phys[0]) { +			u16 value = data[2]; +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				np->medialock = (value & 0x9000) ? 0 : 1; +				if (np->medialock) +					np->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: np->advertising = value; break; +			} +			check_duplex(dev); +		} +		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static int netdev_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, Intr status %4.4x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); +	} + +	/* Disable interrupts by clearing the interrupt mask. */ +	writel(0, ioaddr + IntrEnable); + +	/* Stop the chip's Tx and Rx processes. */ +	writel(0, ioaddr + GenCtrl); + +	del_timer(&np->timer); + +#ifdef __i386__ +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(np->tx_ring)); +		for (i = 0; i < 8 /* TX_RING_SIZE is huge! */; i++) +			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x -> %8.8x.\n", +				   i, np->tx_ring[i].status, np->tx_ring[i].addr, +				   np->tx_done_q[i].status); +		printk(KERN_DEBUG "  Rx ring at %8.8x -> %p:\n", +			   (int)virt_to_bus(np->rx_ring), np->rx_done_q); +		if (np->rx_done_q) +			for (i = 0; i < 8 /* RX_RING_SIZE */; i++) { +				printk(KERN_DEBUG " #%d desc. %8.8x -> %8.8x\n", +					   i, np->rx_ring[i].rxaddr, np->rx_done_q[i].status); +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].rxaddr = 0xBADF00D0; /* An invalid address. */ +		if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			np->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(np->rx_skbuff[i]); +		} +		np->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (np->tx_skbuff[i]) +			dev_free_skb(np->tx_skbuff[i]); +		np->tx_skbuff[i] = 0; +	} + +	MOD_DEC_USE_COUNT; + +	return 0; +} + + +static int starfire_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		/* Disable interrupts, stop Tx and Rx. */ +		writel(0x0000, ioaddr + IntrEnable); +		writel(0, ioaddr + GenCtrl); +		break; +	case DRV_RESUME: +		/* This is incomplete: we must factor start_chip() out of open(). */ +		writel(np->tx_threshold, ioaddr + TxThreshold); +		writel(interrupt_mitigation, ioaddr + IntrTimerCtrl); +		set_rx_mode(dev); +		writel(np->intr_enable, ioaddr + IntrEnable); +		writel(TxEnable|RxEnable, ioaddr + GenCtrl); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			/* Some, but not all, kernel versions close automatically. */ +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_net_dev; *devp; devp = next) { +			next = &((struct netdev_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	} + +	return 0; +} + + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	if (pci_drv_register(&starfire_drv_id, NULL)) { +		printk(KERN_INFO " No Starfire adapters detected, driver not loaded.\n"); +		return -ENODEV; +	} +	return 0; +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&starfire_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_net_dev) { +		struct netdev_private *np = (void *)(root_net_dev->priv); +		unregister_netdev(root_net_dev); +		iounmap((char *)(root_net_dev->base_addr)); +		next_dev = np->next_module; +		if (np->tx_done_q) free_page((long)np->tx_done_q); +		if (np->rx_done_q) free_page((long)np->rx_done_q); +		if (np->priv_addr) kfree(np->priv_addr); +		kfree(root_net_dev); +		root_net_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` starfire.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c starfire.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c starfire.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/sundance.c b/linux/src/drivers/net/sundance.c new file mode 100644 index 0000000..3723164 --- /dev/null +++ b/linux/src/drivers/net/sundance.c @@ -0,0 +1,1556 @@ +/* sundance.c: A Linux device driver for the Sundance ST201 "Alta". */ +/* +	Written 1999-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	410 Severn Ave., Suite 210 +	Annapolis MD 21403 + +	Support information and updates available at +	http://www.scyld.com/network/sundance.html +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"sundance.c:v1.11 2/4/2003  Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/sundance.html\n"; +/* Updated to recommendations in pci-skeleton v2.12. */ + +/* Automatically extracted configuration info: +probe-func: sundance_probe +config-in: tristate 'Sundance ST201 "Alta" PCI Ethernet support' CONFIG_SUNDANCE +c-help-name: Sundance ST201 "Alta" PCI Ethernet support +c-help-symbol: CONFIG_SUNDANCE +c-help: This driver is for the Sundance ST201 "Alta" and Kendin KS8723, as +c-help: used on the D-Link DFE-550 and DFE-580. +c-help: Design information, usage details and updates are available from +c-help: http://www.scyld.com/network/sundance.html +*/ + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   The sundance uses a 64 element hash table based on the Ethernet CRC.  */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. +   This chip can receive into any byte alignment buffers, so word-oriented +   archs do not need a copy-align of the IP header. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   Both 'options[]' and 'full_duplex[]' should exist for driver +   interoperability. +   The media type is usually passed in 'options[]'. +    The default is autonegotation for speed and duplex. +	This should rarely be overridden. +    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +    Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +    Use option values 0x20 and 0x200 for forcing full duplex operation. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Ring sizes are a power of two only for compile efficiency. +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   There must be at least five Tx entries for the tx_full hysteresis, and +   more than 31 requires modifying the Tx status handling error recovery. +   Leave a inactive gap in the Tx ring for better cache behavior. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority. +   Large receive rings waste memory and impact buffer accounting. +   The driver need to protect against interrupt latency and the kernel +   not reserving enough available memory. +*/ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */ +#define RX_RING_SIZE	32 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +/* Set iff a MII transceiver on any interface requires mdio preamble. +   This only set with older tranceivers, so the extra +   code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 0; + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#if LINUX_VERSION_CODE >= 0x20300 +#include <linux/spinlock.h> +#elif LINUX_VERSION_CODE >= 0x20200 +#include <asm/spinlock.h> +#endif + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Sundance Alta Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); + +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, +				 "Non-zero to set forced full duplex (deprecated)."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This driver is designed for the Sundance Technologies "Alta" ST201 chip. +The Kendin KS8723 is the same design with an integrated transceiver and +new quirks. + +II. Board-specific settings + +This is an all-in-one chip, so there are no board-specific settings. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE. +Some chips explicitly use only 2^N sized rings, while others use a +'next descriptor' pointer that the driver forms into rings. + +IIIb/c. Transmit/Receive Structure + +This driver uses a zero-copy receive and transmit scheme. +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers.  When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack.  Buffers consumed this way are replaced by newly allocated +skbuffs in a later phase of receives. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets.  When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine.  Copying also preloads the cache, which is +most useful with small frames. + +A subtle aspect of the operation is that the IP header at offset 14 in an +ethernet frame isn't longword aligned for further processing. +Unaligned buffers are permitted by the Sundance hardware, so +frames are received into the skbuff at an offset of "+2", 16-byte aligning +the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +IVb. References + +The Sundance ST201 datasheet, preliminary version. +The Kendin KS8723 datasheet, preliminary version. +http://www.scyld.com/expert/100mbps.html +http://www.scyld.com/expert/NWay.html + +IVc. Errata + +*/ + + + +/* Work-around for Kendin chip bugs.  This will be reversed after tracking + down all of the chip access quirks in memory mode. */ +#ifndef USE_MEM_OPS +#define USE_IO_OPS 1 +#endif + +static void *sundance_probe1(struct pci_dev *pdev, void *init_dev, +							 long ioaddr, int irq, int chip_idx, int find_cnt); +static int sundance_pwr_event(void *dev_instance, int event); + +enum chip_capability_flags {CanHaveMII=1, KendinPktDropBug=2, }; +#ifdef USE_IO_OPS +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO  | PCI_ADDR0) +#else +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{"D-Link DFE-580TX (Kendin/Sundance ST201 Alta)", +	 {0x10021186, 0xffffffff, 0x10121186, 0xffffffff, 0x14, 0xff}, +	 PCI_IOTYPE, 128, CanHaveMII|KendinPktDropBug}, +	{"D-Link DFE-580TX (Sundance ST201)", +	 {0x10021186, 0xffffffff, 0x10121186, 0xffffffff, }, +	 PCI_IOTYPE, 128, CanHaveMII|KendinPktDropBug}, +	{"D-Link DFE-550FX 100baseFx (Sundance ST201)", +	 {0x10031186, 0xffffffff, }, +	 PCI_IOTYPE, 128, CanHaveMII|KendinPktDropBug}, +	{"OEM Sundance Technology ST201", {0x10021186, 0xffffffff, }, +	 PCI_IOTYPE, 128, CanHaveMII}, +	{"Sundance Technology Alta", {0x020113F0, 0xffffffff, }, +	 PCI_IOTYPE, 128, CanHaveMII}, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info sundance_drv_id = { +	"sundance", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	sundance_probe1, sundance_pwr_event }; + +/* This driver was written to use PCI memory space, however x86-oriented +   hardware often uses I/O space accesses. */ +#ifdef USE_IO_OPS +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +/* Offsets to the device registers. +   Unlike software-only systems, device drivers interact with complex hardware. +   It's not useful to define symbolic names for every register bit in the +   device.  The name can only partially document the semantics and make +   the driver longer and more difficult to read. +   In general, only the important configuration values or bits changed +   multiple times should be defined symbolically. +*/ +enum alta_offsets { +	DMACtrl=0x00,     TxListPtr=0x04, TxDMACtrl=0x08, TxDescPoll=0x0a, +	RxDMAStatus=0x0c, RxListPtr=0x10, RxDMACtrl=0x14, RxDescPoll=0x16, +	LEDCtrl=0x1a, ASICCtrl=0x30, +	EEData=0x34, EECtrl=0x36, TxThreshold=0x3c, +	FlashAddr=0x40, FlashData=0x44, WakeEvent=0x45, TxStatus=0x46, +	DownCounter=0x48, IntrClear=0x4a, IntrEnable=0x4c, IntrStatus=0x4e, +	MACCtrl0=0x50, MACCtrl1=0x52, StationAddr=0x54, +	MaxFrameSize=0x5A, RxMode=0x5c, MIICtrl=0x5e, +	MulticastFilter0=0x60, MulticastFilter1=0x64, +	RxOctetsLow=0x68, RxOctetsHigh=0x6a, TxOctetsLow=0x6c, TxOctetsHigh=0x6e, +	TxFramesOK=0x70, RxFramesOK=0x72, StatsCarrierError=0x74, +	StatsLateColl=0x75, StatsMultiColl=0x76, StatsOneColl=0x77, +	StatsTxDefer=0x78, RxMissed=0x79, StatsTxXSDefer=0x7a, StatsTxAbort=0x7b, +	StatsBcastTx=0x7c, StatsBcastRx=0x7d, StatsMcastTx=0x7e, StatsMcastRx=0x7f, +	/* Aliased and bogus values! */ +	RxStatus=0x0c, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrSummary=0x0001, IntrPCIErr=0x0002, IntrMACCtrl=0x0008, +	IntrTxDone=0x0004, IntrRxDone=0x0010, IntrRxStart=0x0020, +	IntrDrvRqst=0x0040, +	StatsMax=0x0080, LinkChange=0x0100, +	IntrTxDMADone=0x0200, IntrRxDMADone=0x0400, +}; + +/* Bits in the RxMode register. */ +enum rx_mode_bits { +	AcceptAllIPMulti=0x20, AcceptMultiHash=0x10, AcceptAll=0x08, +	AcceptBroadcast=0x04, AcceptMulticast=0x02, AcceptMyPhys=0x01, +}; +/* Bits in MACCtrl. */ +enum mac_ctrl0_bits { +	EnbFullDuplex=0x20, EnbRcvLargeFrame=0x40, +	EnbFlowCtrl=0x100, EnbPassRxCRC=0x200, +}; +enum mac_ctrl1_bits { +	StatsEnable=0x0020,	StatsDisable=0x0040, StatsEnabled=0x0080, +	TxEnable=0x0100, TxDisable=0x0200, TxEnabled=0x0400, +	RxEnable=0x0800, RxDisable=0x1000, RxEnabled=0x2000, +}; + +/* The Rx and Tx buffer descriptors. +   Using only 32 bit fields simplifies software endian correction. +   This structure must be aligned, and should avoid spanning cache lines. +*/ +struct netdev_desc { +	u32 next_desc; +	u32 status; +	struct desc_frag { u32 addr, length; } frag[1]; +}; + +/* Bits in netdev_desc.status */ +enum desc_status_bits { +	DescOwn=0x8000, DescEndPacket=0x4000, DescEndRing=0x2000, +	DescTxDMADone=0x10000, +	LastFrag=0x80000000, DescIntrOnTx=0x8000, DescIntrOnDMADone=0x80000000, +}; + +#define PRIV_ALIGN	15 	/* Required alignment mask */ +/* Use  __attribute__((aligned (L1_CACHE_BYTES)))  to maintain alignment +   within the structure. */ +struct netdev_private { +	/* Descriptor rings first for alignment. */ +	struct netdev_desc rx_ring[RX_RING_SIZE]; +	struct netdev_desc tx_ring[TX_RING_SIZE]; +	struct net_device *next_module;		/* Link for devices of this type. */ +	void *priv_addr;					/* Unaligned address for kfree */ +	const char *product_name; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for later free(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	/* Frequently used values: keep some adjacent for cache effect. */ +	int msg_level; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	int max_interrupt_work; + +	/* Note: Group variables for cache line effect. */ +	struct netdev_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	spinlock_t txlock;					/* Group with Tx control cache line. */ +	struct netdev_desc *last_tx;		/* Last Tx descriptor used. */ +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1;				/* The Tx queue is full. */ + +	/* These values keep track of the transceiver/media in use. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	/* Multicast and receive mode. */ +	spinlock_t mcastlock;				/* SMP lock multicast updates. */ +	u16 mcast_filter[4]; +	int multicast_filter_limit; + +	/* MII transceiver section. */ +	int mii_cnt;						/* MII device addresses. */ +	int link_status; +	u16 advertising;					/* NWay media advertisement */ +	unsigned char phys[2];				/* MII device addresses. */ +}; + +/* The station address location in the EEPROM. */ +#define EEPROM_SA_OFFSET	0x10 + +static int  eeprom_read(long ioaddr, int location); +static int  mdio_read(struct net_device *dev, int phy_id, +					  unsigned int location); +static void mdio_write(struct net_device *dev, int phy_id, +					   unsigned int location, int value); +static int  netdev_open(struct net_device *dev); +static void sundance_start(struct net_device *dev); +static int  change_mtu(struct net_device *dev, int new_mtu); +static void check_duplex(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct net_device *dev); +static void init_ring(struct net_device *dev); +static int  start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int  netdev_rx(struct net_device *dev); +static void netdev_error(struct net_device *dev, int intr_status); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int  netdev_close(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_net_dev = NULL; + +#ifndef MODULE +int sundance_probe(struct net_device *dev) +{ +	if (pci_drv_register(&sundance_drv_id, dev) < 0) +		return -ENODEV; +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif + +static void *sundance_probe1(struct pci_dev *pdev, void *init_dev, +							 long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct netdev_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	/* Perhaps NETIF_MSG_PROBE */ +	printk(KERN_INFO "%s: %s at 0x%lx, ", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr); + +	for (i = 0; i < 3; i++) +		((u16 *)dev->dev_addr)[i] = +			le16_to_cpu(eeprom_read(ioaddr, i + EEPROM_SA_OFFSET)); +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	/* All failure checks before this point. +	   We do a request_region() only to register /proc/ioports info. */ +#ifdef USE_IO_OPS +	request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name); +#endif + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_net_dev; +	root_net_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	if (dev->mem_start) +		option = dev->mem_start; + +	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) +		np->medialock = 1; + +	/* The chip-specific entries in the device structure. */ +	dev->open = &netdev_open; +	dev->hard_start_xmit = &start_tx; +	dev->stop = &netdev_close; +	dev->get_stats = &get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; +	dev->change_mtu = &change_mtu; + +	if (1) { +		int phy, phy_idx = 0; +		np->phys[0] = 1;		/* Default setting */ +		mii_preamble_required++; +		for (phy = 1; phy < 32 && phy_idx < 4; phy++) { +			int mii_status = mdio_read(dev, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				np->phys[phy_idx++] = phy; +				np->advertising = mdio_read(dev, phy, 4); +				if ((mii_status & 0x0040) == 0) +					mii_preamble_required++; +				if (np->msg_level & NETIF_MSG_PROBE) +					printk(KERN_INFO "%s: MII PHY found at address %d, status " +						   "0x%4.4x advertising %4.4x.\n", +						   dev->name, phy, mii_status, np->advertising); +			} +		} +		mii_preamble_required--; +		np->mii_cnt = phy_idx; +		if (phy_idx == 0) +			printk(KERN_INFO "%s: No MII transceiver found!, ASIC status %x\n", +				   dev->name, (int)readl(ioaddr + ASICCtrl)); +	} + +	/* Allow forcing the media type. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 0x3ff; +		if (np->default_port & 0x330) { +			np->medialock = 1; +			if (np->msg_level & NETIF_MSG_PROBE) +				printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +					   (option & 0x300 ? 100 : 10), +					   (np->full_duplex ? "full" : "half")); +			if (np->mii_cnt) +				mdio_write(dev, np->phys[0], 0, +						   ((option & 0x300) ? 0x2000 : 0) | 	/* 100mbps? */ +						   (np->full_duplex ? 0x0100 : 0)); /* Full duplex? */ +		} +	} + +	/* Reset the chip to erase previous misconfiguration. */ +	if (np->msg_level & NETIF_MSG_MISC) +		printk("ASIC Control is %x.\n", (int)readl(ioaddr + ASICCtrl)); +	writel(0x007f0000 | readl(ioaddr + ASICCtrl), ioaddr + ASICCtrl); +	if (np->msg_level & NETIF_MSG_MISC) +		printk("ASIC Control is now %x.\n", (int)readl(ioaddr + ASICCtrl)); + +	return dev; +} + + + +static int change_mtu(struct net_device *dev, int new_mtu) +{ +	if ((new_mtu < 68) || (new_mtu > 8191)) /* Limited by RxDMAFrameLen */ +		return -EINVAL; +	if (netif_running(dev)) +		return -EBUSY; +	dev->mtu = new_mtu; +	return 0; +} + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */ +static int eeprom_read(long ioaddr, int location) +{ +	int boguscnt = 2000;		/* Typical 190 ticks. */ +	writew(0x0200 | (location & 0xff), ioaddr + EECtrl); +	do { +		if (! (readw(ioaddr + EECtrl) & 0x8000)) { +			return readw(ioaddr + EEData); +		} +	} while (--boguscnt > 0); +	return 0; +} + +/*  MII transceiver control section. +	Read and write the MII registers using software-generated serial +	MDIO protocol.  See the MII specifications or DP83840A data sheet +	for details. + +	The maximum data clock rate is 2.5 Mhz. +	The timing is decoupled from the processor clock by flushing the write +	from the CPU write buffer with a following read, and using PCI +	transaction time. */ +#define mdio_in(mdio_addr) readb(mdio_addr) +#define mdio_out(value, mdio_addr) writeb(value, mdio_addr) +#define mdio_delay(mdio_addr) readb(mdio_addr) + +enum mii_reg_bits { +	MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004, +}; +#define MDIO_EnbIn  (0) +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and +   a few older transceivers. */ +static void mdio_sync(long mdio_addr) +{ +	int bits = 32; + +	/* Establish sync by sending at least 32 logic ones. */ +	while (--bits >= 0) { +		mdio_out(MDIO_WRITE1, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +} + +static int mdio_read(struct net_device *dev, int phy_id, unsigned int location) +{ +	long mdio_addr = dev->base_addr + MIICtrl; +	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; +	int i, retval = 0; + +	if (mii_preamble_required) +		mdio_sync(mdio_addr); + +	/* Shift the read command bits out. */ +	for (i = 15; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + +		mdio_out(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(dataval | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Read the two transition, 16 data, and wire-idle bits. */ +	for (i = 19; i > 0; i--) { +		mdio_out(MDIO_EnbIn, mdio_addr); +		mdio_delay(mdio_addr); +		retval = (retval << 1) | ((mdio_in(mdio_addr) & MDIO_Data) ? 1 : 0); +		mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, +					   unsigned int location, int value) +{ +	long mdio_addr = dev->base_addr + MIICtrl; +	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; +	int i; + +	if (mii_preamble_required) +		mdio_sync(mdio_addr); + +	/* Shift the command bits out. */ +	for (i = 31; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + +		mdio_out(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(dataval | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Clear out extra bits. */ +	for (i = 2; i > 0; i--) { +		mdio_out(MDIO_EnbIn, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return; +} + + +static int netdev_open(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", +			   dev->name, dev->irq); + +	init_ring(dev); + +	if (dev->if_port == 0) +		dev->if_port = np->default_port; + +	np->full_duplex = np->duplex_lock; +	np->mcastlock = (spinlock_t) SPIN_LOCK_UNLOCKED; + +	sundance_start(dev); +	netif_start_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x " +			   "MAC Control %x, %4.4x %4.4x.\n", +			   dev->name, (int)readl(ioaddr + RxStatus), +			   (int)readw(ioaddr + TxStatus), (int)readl(ioaddr + MACCtrl0), +			   (int)readw(ioaddr + MACCtrl1), (int)readw(ioaddr + MACCtrl0)); + +	/* Set the timer to check for link beat. */ +	init_timer(&np->timer); +	np->timer.expires = jiffies + 3*HZ; +	np->timer.data = (unsigned long)dev; +	np->timer.function = &netdev_timer;				/* timer handler */ +	add_timer(&np->timer); + +	return 0; +} + +static void sundance_start(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	/* No reports have indicated that we need to reset the chip. */ + +	writel(virt_to_bus(&np->rx_ring[np->cur_rx % RX_RING_SIZE]), +		   ioaddr + RxListPtr); +	/* The Tx list pointer is written as packets are queued. */ + +	/* Station address must be written as 16 bit words with the Kendin chip. */ +	for (i = 0; i < 6; i += 2) +		writew((dev->dev_addr[i + 1] << 8) + dev->dev_addr[i], +			   ioaddr + StationAddr + i); + +	np->link_status = readb(ioaddr + MIICtrl) & 0xE0; +	writew((np->full_duplex || (np->link_status & 0x20)) ? 0x120 : 0, +		   ioaddr + MACCtrl0); +	writew(dev->mtu + 14, ioaddr + MaxFrameSize); +	if (dev->mtu > 2047) +		writel(readl(ioaddr + ASICCtrl) | 0x0C, ioaddr + ASICCtrl); + +	set_rx_mode(dev); +	writew(0, ioaddr + DownCounter); +	/* Set the chip to poll every N*320nsec. */ +	writeb(100, ioaddr + RxDescPoll); +	writeb(127, ioaddr + TxDescPoll); +#if 0 +	if (np->drv_flags & KendinPktDropBug) +		writeb(0x01, ioaddr + DebugCtrl1); +#endif + +	/* Enable interrupts by setting the interrupt mask. */ +	writew(IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone +		   | StatsMax | LinkChange, ioaddr + IntrEnable); +	writew(StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1); +} + +static void check_duplex(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int mii_reg5 = mdio_read(dev, np->phys[0], 5); +	int negotiated = mii_reg5 & np->advertising; +	int duplex; + +	if (np->duplex_lock  ||  mii_reg5 == 0xffff) +		return; +	duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; +	if (np->full_duplex != duplex) { +		np->full_duplex = duplex; +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d " +				   "negotiated capability %4.4x.\n", dev->name, +				   duplex ? "full" : "half", np->phys[0], negotiated); +		writew(duplex ? 0x20 : 0, ioaddr + MACCtrl0); +	} +} + +static void netdev_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 10*HZ; + +	if (np->msg_level & NETIF_MSG_TIMER) { +		printk(KERN_DEBUG "%s: Media selection timer tick, intr status %4.4x, " +			   "Tx %x Rx %x.\n", +			   dev->name, (int)readw(ioaddr + IntrEnable), +			   (int)readw(ioaddr + TxStatus), (int)readl(ioaddr + RxStatus)); +	} +	/* Note: This does not catch a 0 or 1 element stuck queue. */ +	if (netif_queue_paused(dev)  && +		np->cur_tx - np->dirty_tx > 1  && +		(jiffies - dev->trans_start) > TX_TIMEOUT) { +		tx_timeout(dev); +	} +	check_duplex(dev); +	np->timer.expires = jiffies + next_tick; +	add_timer(&np->timer); +} + +static void tx_timeout(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Transmit timed out, status %4.4x," +		   " resetting...\n", dev->name, (int)readw(ioaddr + TxStatus)); + +#ifdef __i386__ +	if (np->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)np->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)np->rx_ring[i].status); +		printk("\n"KERN_DEBUG"  Tx ring %8.8x: ", (int)np->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %8.8x", np->tx_ring[i].status); +		printk("\n"); +	} +#endif + +	/* Perhaps we should reinitialize the hardware here. */ +	dev->if_port = 0; +	/* Stop and restart the chip's Tx processes . */ + +	/* Trigger an immediate transmit demand. */ +	writew(IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone +		   | StatsMax | LinkChange, ioaddr + IntrEnable); + +	dev->trans_start = jiffies; +	np->stats.tx_errors++; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	np->tx_full = 0; +	np->cur_rx = np->cur_tx = 0; +	np->dirty_rx = np->dirty_tx = 0; + +	np->rx_buf_sz = dev->mtu + 20; +	if (np->rx_buf_sz < PKT_BUF_SZ) +		np->rx_buf_sz = PKT_BUF_SZ; +	np->rx_head_desc = &np->rx_ring[0]; + +	/* Initialize all Rx descriptors. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]); +		np->rx_ring[i].status = 0; +		np->rx_ring[i].frag[0].length = 0; +		np->rx_skbuff[i] = 0; +	} +	/* Wrap the ring. */ +	np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]); + +	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); +		np->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		skb_reserve(skb, 2);	/* 16 byte align the IP header. */ +		np->rx_ring[i].frag[0].addr = virt_to_le32desc(skb->tail); +		np->rx_ring[i].frag[0].length = cpu_to_le32(np->rx_buf_sz | LastFrag); +	} +	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + +	for (i = 0; i < TX_RING_SIZE; i++) { +		np->tx_skbuff[i] = 0; +		np->tx_ring[i].status = 0; +	} +	return; +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	struct netdev_desc *txdesc; +	unsigned entry; + +	/* Block a timer-based transmit from overlapping. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tx_timeout(dev); +		return 1; +	} + +	/* Note: Ordering is important here, set the field with the +	   "ownership" bit last, and only then increment cur_tx. */ + +	/* Calculate the next Tx descriptor entry. */ +	entry = np->cur_tx % TX_RING_SIZE; +	np->tx_skbuff[entry] = skb; +	txdesc = &np->tx_ring[entry]; + +	txdesc->next_desc = 0; +	/* Note: disable the interrupt generation here before releasing. */ +	txdesc->status = +		cpu_to_le32((entry<<2) | DescIntrOnDMADone | DescIntrOnTx | 1); +	txdesc->frag[0].addr = virt_to_le32desc(skb->data); +	txdesc->frag[0].length = cpu_to_le32(skb->len | LastFrag); +	if (np->last_tx) +		np->last_tx->next_desc = virt_to_le32desc(txdesc); +	np->last_tx = txdesc; +	np->cur_tx++; + +	/* On some architectures: explicitly flush cache lines here. */ + +	if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) { +		np->tx_full = 1; +		/* Check for a just-cleared queue. */ +		if (np->cur_tx - (volatile unsigned int)np->dirty_tx +			< TX_QUEUE_LEN - 2) { +			np->tx_full = 0; +			netif_unpause_tx_queue(dev); +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ + +	/* Side effect: The read wakes the potentially-idle transmit channel. */ +	if (readl(dev->base_addr + TxListPtr) == 0) +		writel(virt_to_bus(&np->tx_ring[entry]), dev->base_addr + TxListPtr); + +	dev->trans_start = jiffies; + +	if (np->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Transmit frame #%d len %ld queued in slot %u.\n", +			   dev->name, np->cur_tx, skb->len, entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct netdev_private *np; +	long ioaddr; +	int boguscnt; + +	ioaddr = dev->base_addr; +	np = (struct netdev_private *)dev->priv; +	boguscnt = np->max_interrupt_work; + +	do { +		int intr_status = readw(ioaddr + IntrStatus); +		if ((intr_status & ~IntrRxDone) == 0 || intr_status == 0xffff) +			break; + +		writew(intr_status & (IntrRxDMADone | IntrPCIErr | +							  IntrDrvRqst |IntrTxDone|IntrTxDMADone | +							  StatsMax | LinkChange), +							  ioaddr + IntrStatus); + +		if (np->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", +				   dev->name, intr_status); + +		if (intr_status & IntrRxDMADone) +			netdev_rx(dev); + +		if (intr_status & IntrTxDone) { +			int txboguscnt = 32; +			int tx_status = readw(ioaddr + TxStatus); +			while (tx_status & 0x80) { +				if (np->msg_level & NETIF_MSG_TX_DONE) +					printk("%s: Transmit status is %4.4x.\n", +						   dev->name, tx_status); +				if (tx_status & 0x1e) { +					if (np->msg_level & NETIF_MSG_TX_ERR) +						printk("%s: Transmit error status %4.4x.\n", +							   dev->name, tx_status); +					np->stats.tx_errors++; +					if (tx_status & 0x10)  np->stats.tx_fifo_errors++; +#ifdef ETHER_STATS +					if (tx_status & 0x08)  np->stats.collisions16++; +#else +					if (tx_status & 0x08)  np->stats.collisions++; +#endif +					if (tx_status & 0x04)  np->stats.tx_fifo_errors++; +					if (tx_status & 0x02)  np->stats.tx_window_errors++; +					/* This reset has not been verified!. */ +					if (tx_status & 0x10) {			/* Reset the Tx. */ +						writel(0x001c0000 | readl(ioaddr + ASICCtrl), +							   ioaddr + ASICCtrl); +#if 0					/* Do we need to reset the Tx pointer here? */ +						writel(virt_to_bus(&np->tx_ring[np->dirty_tx]), +							   dev->base_addr + TxListPtr); +#endif +					} +					if (tx_status & 0x1e) 		/* Restart the Tx. */ +						writew(TxEnable, ioaddr + MACCtrl1); +				} +				/* Yup, this is a documentation bug.  It cost me *hours*. */ +				writew(0, ioaddr + TxStatus); +				if (--txboguscnt < 0) +					break; +				tx_status = readw(ioaddr + TxStatus); +			} +		} +		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { +			int entry = np->dirty_tx % TX_RING_SIZE; +			if ( ! (np->tx_ring[entry].status & cpu_to_le32(DescTxDMADone))) +				break; +			/* Free the original skb. */ +			dev_free_skb_irq(np->tx_skbuff[entry]); +			np->tx_skbuff[entry] = 0; +		} +		if (np->tx_full  &&  np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { +			/* The ring is no longer full, allow new TX entries. */ +			np->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & (IntrDrvRqst | IntrPCIErr | LinkChange | StatsMax)) +			netdev_error(dev, intr_status); + +		if (--boguscnt < 0) { +			int intr_clear = readw(ioaddr + IntrClear); +			get_stats(dev); +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x / 0x%4.4x .. 0x%4.4x.\n", +				   dev->name, intr_status, intr_clear, +				   (int)readw(ioaddr + IntrClear)); +			/* Re-enable us in 3.2msec. */ +			writew(1000, ioaddr + DownCounter); +			writew(IntrDrvRqst, ioaddr + IntrEnable); +			break; +		} +	} while (1); + +	if (np->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readw(ioaddr + IntrStatus)); + +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int entry = np->cur_rx % RX_RING_SIZE; +	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; + +	if (np->msg_level & NETIF_MSG_RX_STATUS) { +		printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", +			   entry, np->rx_ring[entry].status); +	} + +	/* If EOP is set on the next entry, it's a new packet. Send it up. */ +	while (np->rx_head_desc->status & cpu_to_le32(DescOwn)) { +		struct netdev_desc *desc = np->rx_head_desc; +		u32 frame_status = le32_to_cpu(desc->status); +		int pkt_len = frame_status & 0x1fff;		/* Chip omits the CRC. */ + +		if (np->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n", +				   frame_status); +		if (--boguscnt < 0) +			break; +		if (frame_status & 0x001f4000) { +			/* There was a error. */ +			if (np->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n", +					   frame_status); +			np->stats.rx_errors++; +			if (frame_status & 0x00100000) np->stats.rx_length_errors++; +			if (frame_status & 0x00010000) np->stats.rx_fifo_errors++; +			if (frame_status & 0x00060000) np->stats.rx_frame_errors++; +			if (frame_status & 0x00080000) np->stats.rx_crc_errors++; +			if (frame_status & 0x00100000) { +				printk(KERN_WARNING "%s: Oversized Ethernet frame," +					   " status %8.8x.\n", +					   dev->name, frame_status); +			} +		} else { +			struct sk_buff *skb; + +#ifndef final_version +			if (np->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d" +					   ", bogus_cnt %d.\n", +					   pkt_len, boguscnt); +#endif +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < np->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +			} else { +				skb_put(skb = np->rx_skbuff[entry], pkt_len); +				np->rx_skbuff[entry] = NULL; +			} +			skb->protocol = eth_type_trans(skb, dev); +			/* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */ +			netif_rx(skb); +			dev->last_rx = jiffies; +		} +		entry = (++np->cur_rx) % RX_RING_SIZE; +		np->rx_head_desc = &np->rx_ring[entry]; +	} + +	/* Refill the Rx ring buffers. */ +	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { +		struct sk_buff *skb; +		entry = np->dirty_rx % RX_RING_SIZE; +		if (np->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(np->rx_buf_sz); +			np->rx_skbuff[entry] = skb; +			if (skb == NULL) +				break;				/* Better luck next round. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			np->rx_ring[entry].frag[0].addr = virt_to_le32desc(skb->tail); +		} +		/* Perhaps we need not reset this field. */ +		np->rx_ring[entry].frag[0].length = +			cpu_to_le32(np->rx_buf_sz | LastFrag); +		np->rx_ring[entry].status = 0; +	} + +	/* No need to restart Rx engine, it will poll. */ +	return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; + +	if (intr_status & IntrDrvRqst) { +		/* Stop the down counter and turn interrupts back on. */ +		printk(KERN_WARNING "%s: Turning interrupts back on.\n", dev->name); +		writew(0, ioaddr + DownCounter); +		writew(IntrRxDMADone | IntrPCIErr | IntrDrvRqst | +			   IntrTxDone | StatsMax | LinkChange, ioaddr + IntrEnable); +	} +	if (intr_status & LinkChange) { +		int new_status = readb(ioaddr + MIICtrl) & 0xE0; +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_NOTICE "%s: Link changed: Autonegotiation advertising" +				   " %4.4x  partner %4.4x.\n", dev->name, +				   mdio_read(dev, np->phys[0], 4), +				   mdio_read(dev, np->phys[0], 5)); +		if ((np->link_status ^ new_status) & 0x80) { +			if (new_status & 0x80) +				netif_link_up(dev); +			else +				netif_link_down(dev); +		} +		np->link_status = new_status; +		check_duplex(dev); +	} +	if (intr_status & StatsMax) { +		get_stats(dev); +	} +	if (intr_status & IntrPCIErr) { +		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", +			   dev->name, intr_status); +		/* We must do a global reset of DMA to continue. */ +	} +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	if (readw(ioaddr + StationAddr) == 0xffff) +		return &np->stats; + +	/* We do not spinlock statistics. +	   A window only exists if we have non-atomic adds, the error counts +	   are typically zero, and statistics are non-critical. */  +	np->stats.rx_missed_errors	+= readb(ioaddr + RxMissed); +	np->stats.tx_packets += readw(ioaddr + TxFramesOK); +	np->stats.rx_packets += readw(ioaddr + RxFramesOK); +	np->stats.collisions += readb(ioaddr + StatsLateColl); +	np->stats.collisions += readb(ioaddr + StatsMultiColl); +	np->stats.collisions += readb(ioaddr + StatsOneColl); +	readb(ioaddr + StatsCarrierError); +	readb(ioaddr + StatsTxDefer); +	for (i = StatsTxXSDefer; i <= StatsMcastRx; i++) +		readb(ioaddr + i); +#if LINUX_VERSION_CODE > 0x20127 +	np->stats.tx_bytes += readw(ioaddr + TxOctetsLow); +	np->stats.tx_bytes += readw(ioaddr + TxOctetsHigh) << 16; +	np->stats.rx_bytes += readw(ioaddr + RxOctetsLow); +	np->stats.rx_bytes += readw(ioaddr + RxOctetsHigh) << 16; +#else +	readw(ioaddr + TxOctetsLow); +	readw(ioaddr + TxOctetsHigh); +	readw(ioaddr + RxOctetsLow); +	readw(ioaddr + RxOctetsHigh); +#endif + +	return &np->stats; +} + +/* The little-endian AUTODIN II ethernet CRC calculations. +   A big-endian version is also available. +   This is slow but compact code.  Do not use this routine for bulk data, +   use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c. +   Chips may use the upper or lower CRC bits, and may reverse and/or invert +   them.  Select the endian-ness that results in minimal calculations. +*/ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ +	unsigned int crc = ~0;	/* Initial value. */ +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u16 mc_filter[4];			/* Multicast hash filter */ +	u32 rx_mode; +	int i; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		memset(mc_filter, ~0, sizeof(mc_filter)); +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys; +	} else if ((dev->mc_count > np->multicast_filter_limit) +			   ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to match, or accept all multicasts. */ +		memset(mc_filter, 0xff, sizeof(mc_filter)); +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; +	} else if (dev->mc_count) { +		struct dev_mc_list *mclist; +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, +					mc_filter); +		} +		rx_mode = AcceptBroadcast | AcceptMultiHash | AcceptMyPhys; +	} else { +		writeb(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode); +		return; +	} +	for (i = 0; i < 4; i++) +		writew(mc_filter[i], ioaddr + MulticastFilter0 + i*2); +	writeb(rx_mode, ioaddr + RxMode); +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = np->phys[0] & 0x1f; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == np->phys[0]) { +			u16 value = data[2]; +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				np->medialock = (value & 0x9000) ? 0 : 1; +				if (np->medialock) +					np->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: np->advertising = value; break; +			} +			/* Perhaps check_duplex(dev), depending on chip semantics. */ +		} +		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static int sundance_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		/* Disable interrupts, stop Tx and Rx. */ +		writew(0x0000, ioaddr + IntrEnable); +		writew(TxDisable | RxDisable | StatsDisable, ioaddr + MACCtrl1); +		break; +	case DRV_RESUME: +		sundance_start(dev); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			/* Some, but not all, kernel versions close automatically. */ +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_net_dev; *devp; devp = next) { +			next = &((struct netdev_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	case DRV_PWR_WakeOn: +		writeb(readb(ioaddr + WakeEvent) | 2, ioaddr + WakeEvent); +		/* Fall through. */ +	case DRV_PWR_DOWN: +	case DRV_PWR_UP: +		acpi_set_pwr_state(np->pci_dev, event==DRV_PWR_UP ? ACPI_D0:ACPI_D3); +		break; +	default: +		return -1; +	} + +	return 0; +} + +static int netdev_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %2.2x " +			   "Rx %4.4x Int %2.2x.\n", +			   dev->name, (int)readw(ioaddr + TxStatus), +			   (int)readl(ioaddr + RxStatus), (int)readw(ioaddr + IntrStatus)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); +	} + +	/* Disable interrupts by clearing the interrupt mask. */ +	writew(0x0000, ioaddr + IntrEnable); + +	/* Stop the chip's Tx and Rx processes. */ +	writew(TxDisable | RxDisable | StatsDisable, ioaddr + MACCtrl1); + +	del_timer(&np->timer); + +#ifdef __i386__ +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(np->tx_ring)); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" #%d desc. %4.4x %8.8x %8.8x.\n", +				   i, np->tx_ring[i].status, np->tx_ring[i].frag[0].addr, +				   np->tx_ring[i].frag[0].length); +		printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n", +			   (int)virt_to_bus(np->rx_ring)); +		for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) { +			printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", +				   i, np->rx_ring[i].status, np->rx_ring[i].frag[0].addr, +				   np->rx_ring[i].frag[0].length); +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].status = 0; +		np->rx_ring[i].frag[0].addr = 0xBADF00D0; /* An invalid address. */ +		if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			np->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(np->rx_skbuff[i]); +		} +		np->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (np->tx_skbuff[i]) +			dev_free_skb(np->tx_skbuff[i]); +		np->tx_skbuff[i] = 0; +	} + +	MOD_DEC_USE_COUNT; + +	return 0; +} + + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return pci_drv_register(&sundance_drv_id, NULL); +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&sundance_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_net_dev) { +		struct netdev_private *np = (void *)(root_net_dev->priv); +		unregister_netdev(root_net_dev); +#ifdef USE_IO_OPS +		release_region(root_net_dev->base_addr, +					   pci_id_tbl[np->chip_id].io_size); +#else +		iounmap((char *)root_net_dev->base_addr); +#endif +		next_dev = np->next_module; +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_net_dev); +		root_net_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` sundance.o" + *  compile-cmd1: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c sundance.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c sundance.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/tlan.c b/linux/src/drivers/net/tlan.c new file mode 100644 index 0000000..fedc11f --- /dev/null +++ b/linux/src/drivers/net/tlan.c @@ -0,0 +1,2863 @@ +/******************************************************************** + * + *  Linux ThunderLAN Driver + * + *  tlan.c + *  by James Banks + * + *  (C) 1997-1998 Caldera, Inc. + *  (C) 1998 James Banks + * + *  This software may be used and distributed according to the terms + *  of the GNU Public License, incorporated herein by reference. + * + ** This file is best viewed/edited with columns>=132. + * + ** Useful (if not required) reading: + * + *		Texas Instruments, ThunderLAN Programmer's Guide, + *			TI Literature Number SPWU013A + *			available in PDF format from www.ti.com + *		Level One, LXT901 and LXT970 Data Sheets + *			available in PDF format from www.level1.com + *		National Semiconductor, DP83840A Data Sheet + *			available in PDF format from www.national.com + *		Microchip Technology, 24C01A/02A/04A Data Sheet + *			available in PDF format from www.microchip.com + * + ********************************************************************/ + + +#include <linux/module.h> + +#include "tlan.h" + +#include <linux/bios32.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> + + + +typedef u32 (TLanIntVectorFunc)( struct device *, u16 ); + + +#ifdef MODULE + +static	struct device	*TLanDevices = NULL; +static	int		TLanDevicesInstalled = 0; + +#endif + + +static  int		debug = 0; +static	int		aui = 0; +static	int		sa_int = 0; +static	int		bbuf = 0; +static	int		duplex = 0; +static	int		speed = 0; +static	u8		*TLanPadBuffer; +static	char		TLanSignature[] = "TLAN"; +static	int		TLanVersionMajor = 1; +static	int		TLanVersionMinor = 0; + + +static	TLanAdapterEntry TLanAdapterList[] = { +	{ PCI_VENDOR_ID_COMPAQ,  +	  PCI_DEVICE_ID_NETELLIGENT_10, +	  "Compaq Netelligent 10 T PCI UTP", +	  TLAN_ADAPTER_ACTIVITY_LED, +	  0x83 +	}, +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_NETELLIGENT_10_100, +	  "Compaq Netelligent 10/100 TX PCI UTP", +	  TLAN_ADAPTER_ACTIVITY_LED, +	  0x83 +	},  +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED, +	  "Compaq Integrated NetFlex-3/P", +	  TLAN_ADAPTER_NONE, +	  0x83 +	},  +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_NETFLEX_3P, +	  "Compaq NetFlex-3/P", +	  TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY, +	  0x83 +	}, +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_NETFLEX_3P_BNC, +	  "Compaq NetFlex-3/P", +	  TLAN_ADAPTER_NONE, +	  0x83 +	}, +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT, +	  "Compaq Netelligent Integrated 10/100 TX UTP", +	  TLAN_ADAPTER_NONE, +	  0x83 +	}, +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL, +	  "Compaq Netelligent Dual 10/100 TX PCI UTP", +	  TLAN_ADAPTER_NONE, +	  0x83 +	}, +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_DESKPRO_4000_5233MMX, +	  "Compaq Netelligent 10/100 TX Embedded UTP", +	  TLAN_ADAPTER_NONE, +	  0x83 +	}, +	{ PCI_VENDOR_ID_OLICOM, +	  PCI_DEVICE_ID_OLICOM_OC2183, +	  "Olicom OC-2183/2185", +	  TLAN_ADAPTER_USE_INTERN_10, +	  0xF8 +	}, +	{ PCI_VENDOR_ID_OLICOM, +	  PCI_DEVICE_ID_OLICOM_OC2325, +	  "Olicom OC-2325", +	  TLAN_ADAPTER_UNMANAGED_PHY, +	  0xF8 +	}, +	{ PCI_VENDOR_ID_OLICOM, +	  PCI_DEVICE_ID_OLICOM_OC2326, +	  "Olicom OC-2326", +	  TLAN_ADAPTER_USE_INTERN_10, +	  0xF8 +	}, +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100, +	  "Compaq Netelligent 10/100 TX UTP", +	  TLAN_ADAPTER_ACTIVITY_LED, +	  0x83 +	}, +	{ PCI_VENDOR_ID_COMPAQ, +	  PCI_DEVICE_ID_NETELLIGENT_10_T2, +	  "Compaq Netelligent 10 T/2 PCI UTP/Coax", +	  TLAN_ADAPTER_NONE, +	  0x83 +	}, +	{ 0, +	  0, +	  NULL, +	  0, +	  0 +	} /* End of List */ +}; + + +static int	TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * ); +static int	TLan_Init( struct device * ); +static int	TLan_Open(struct device *dev); +static int	TLan_StartTx(struct sk_buff *, struct device *); +static void	TLan_HandleInterrupt(int, void *, struct pt_regs *); +static int	TLan_Close(struct device *); +static struct	net_device_stats *TLan_GetStats( struct device * ); +static void	TLan_SetMulticastList( struct device * ); + +static u32	TLan_HandleInvalid( struct device *, u16 ); +static u32	TLan_HandleTxEOF( struct device *, u16 ); +static u32	TLan_HandleStatOverflow( struct device *, u16 ); +static u32	TLan_HandleRxEOF( struct device *, u16 ); +static u32	TLan_HandleDummy( struct device *, u16 ); +static u32	TLan_HandleTxEOC( struct device *, u16 ); +static u32	TLan_HandleStatusCheck( struct device *, u16 ); +static u32	TLan_HandleRxEOC( struct device *, u16 ); + +static void	TLan_Timer( unsigned long ); + +static void	TLan_ResetLists( struct device * ); +static void	TLan_FreeLists( struct device * ); +static void	TLan_PrintDio( u16 ); +static void	TLan_PrintList( TLanList *, char *, int ); +static void	TLan_ReadAndClearStats( struct device *, int ); +static void	TLan_ResetAdapter( struct device * ); +static void	TLan_FinishReset( struct device * ); +static void	TLan_SetMac( struct device *, int areg, char *mac ); + +static void	TLan_PhyPrint( struct device * ); +static void	TLan_PhyDetect( struct device * ); +static void	TLan_PhyPowerDown( struct device * ); +static void	TLan_PhyPowerUp( struct device * ); +static void	TLan_PhyReset( struct device * ); +static void	TLan_PhyStartLink( struct device * ); +static void	TLan_PhyFinishAutoNeg( struct device * ); +/* +static int	TLan_PhyNop( struct device * ); +static int	TLan_PhyInternalCheck( struct device * ); +static int	TLan_PhyInternalService( struct device * ); +static int	TLan_PhyDp83840aCheck( struct device * ); +*/ + +static int	TLan_MiiReadReg( struct device *, u16, u16, u16 * ); +static void	TLan_MiiSendData( u16, u32, unsigned ); +static void	TLan_MiiSync( u16 ); +static void	TLan_MiiWriteReg( struct device *, u16, u16, u16 ); + +static void	TLan_EeSendStart( u16 ); +static int	TLan_EeSendByte( u16, u8, int ); +static void	TLan_EeReceiveByte( u16, u8 *, int ); +static int	TLan_EeReadByte( struct device *, u8, u8 * ); + + +static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = { +	TLan_HandleInvalid, +	TLan_HandleTxEOF, +	TLan_HandleStatOverflow, +	TLan_HandleRxEOF, +	TLan_HandleDummy, +	TLan_HandleTxEOC, +	TLan_HandleStatusCheck, +	TLan_HandleRxEOC +}; + +static inline void +TLan_SetTimer( struct device *dev, u32 ticks, u32 type ) +{ +	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + +	cli(); +	if ( priv->timer.function != NULL ) { +		return; +	} +	priv->timer.function = &TLan_Timer; +	sti(); + +	priv->timer.data = (unsigned long) dev; +	priv->timer.expires = jiffies + ticks; +	priv->timerSetAt = jiffies; +	priv->timerType = type; +	add_timer( &priv->timer ); + +} /* TLan_SetTimer */ + + +/***************************************************************************** +****************************************************************************** + +	ThunderLAN Driver Primary Functions + +	These functions are more or less common to all Linux network drivers. + +****************************************************************************** +*****************************************************************************/ + + +#ifdef MODULE + +	/*************************************************************** +	 *	init_module +	 * +	 *	Returns: +	 *		0 if module installed ok, non-zero if not. +	 *	Parms: +	 *		None +	 * +	 *	This function begins the setup of the driver creating a +	 *	pad buffer, finding all TLAN devices (matching +	 *	TLanAdapterList entries), and creating and initializing a +	 *	device structure for each adapter. +	 * +	 **************************************************************/ + +extern int init_module(void) +{ +	TLanPrivateInfo	*priv; +	u8		bus; +	struct device	*dev; +	size_t		dev_size; +	u8		dfn; +	u32		index; +	int		failed; +	int		found; +	u32		io_base; +	u8		irq; +	u8		rev; + +	printk( "TLAN driver, v%d.%d, (C) 1997-8 Caldera, Inc.\n", +		TLanVersionMajor, +		TLanVersionMinor +	      ); +	TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE, +					( GFP_KERNEL | GFP_DMA ) +				      ); +	if ( TLanPadBuffer == NULL ) { +		printk( "TLAN:  Could not allocate memory for pad buffer.\n" ); +		return -ENOMEM; +	} + +	memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); + +	dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo); + +	while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ) ) ) { +		dev = (struct device *) kmalloc( dev_size, GFP_KERNEL ); +		if ( dev == NULL ) { +			printk( "TLAN:  Could not allocate memory for device.\n" ); +			continue; +		} +		memset( dev, 0, dev_size ); + +		dev->priv = priv = ( (void *) dev ) + sizeof(struct device); +		dev->name = priv->devName; +		strcpy( priv->devName, "    " ); +		dev->base_addr = io_base; +		dev->irq = irq; +		dev->init = TLan_Init; + +		priv->adapter =    &TLanAdapterList[index]; +		priv->adapterRev = rev; +		priv->aui =        aui; +		if ( ( duplex != 1 ) && ( duplex != 2 ) ) { +			duplex = 0; +		} +		priv->duplex =     duplex; +		if ( ( speed != 10 ) && ( speed != 100 ) ) { +			speed = 0; +		} +		priv->speed =      speed; +		priv->sa_int =     sa_int; +		priv->debug =      debug; + +		ether_setup( dev ); + +		failed = register_netdev( dev ); + +		if ( failed ) { +			printk( "TLAN:  Could not register device.\n" ); +			kfree( dev ); +		} else { +			priv->nextDevice = TLanDevices; +			TLanDevices = dev; +			TLanDevicesInstalled++; +			printk("TLAN:  %s irq=%2d io=%04x, %s, Rev. %d\n", +				dev->name, +				(int) dev->irq, +				(int) dev->base_addr, +				priv->adapter->deviceLabel, +				priv->adapterRev ); +		} +	} +	 +	/* printk( "TLAN:  Found %d device(s).\n", TLanDevicesInstalled ); */ + +    return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV ); + +} /* init_module */ + + + + +	/*************************************************************** +	 *	cleanup_module +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		None +	 * +	 *	Goes through the TLanDevices list and frees the device +	 *	structs and memory associated with each device (lists +	 *	and buffers).  It also ureserves the IO port regions +	 *	associated with this device. +	 * +	 **************************************************************/ + +extern void cleanup_module(void) +{ +	struct device	*dev; +	TLanPrivateInfo	*priv; + +	while ( TLanDevicesInstalled ) { +		dev = TLanDevices; +		priv = (TLanPrivateInfo *) dev->priv; +		if ( priv->dmaStorage ) { +			kfree( priv->dmaStorage ); +		} +		release_region( dev->base_addr, 0x10 ); +		unregister_netdev( dev ); +		TLanDevices = priv->nextDevice; +		kfree( dev ); +		TLanDevicesInstalled--; +	} +	kfree( TLanPadBuffer ); + +} /* cleanup_module */ + + +#else /* MODULE */ + + + + +	/*************************************************************** +	 *	tlan_probe +	 * +	 *	Returns: +	 *		0 on success, error code on error +	 *	Parms: +	 *		dev	device struct to use if adapter is +	 *			found. +	 * +	 *	The name is lower case to fit in with all the rest of +	 *	the netcard_probe names.  This function looks for a/ +	 *	another TLan based adapter, setting it up with the +	 *	provided device struct if one is found. +	 * +	 **************************************************************/ +	  +extern int tlan_probe( struct device *dev ) +{ +	TLanPrivateInfo	*priv; +	static int	pad_allocated = 0; +	int		found; +	u8		bus, dfn, irq, rev; +	u32		io_base, index; + +	found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ); + +	if ( ! found ) { +		return -ENODEV; +	} + +	dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL ); +	 +	if ( dev->priv == NULL ) { +		printk( "TLAN:  Could not allocate memory for device.\n" ); +		return  -ENOMEM; +	} + +	memset( dev->priv, 0, sizeof(TLanPrivateInfo) ); + +	if ( ! pad_allocated ) { +		TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE,  +//						( GFP_KERNEL | GFP_DMA ) +						( GFP_KERNEL ) +					      ); +		if ( TLanPadBuffer == NULL ) { +			printk( "TLAN:  Could not allocate memory for padding.\n" ); +			kfree( dev->priv ); +			return -ENOMEM; +		} else { +			pad_allocated = 1; +			memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE ); +		} +	} + +	priv = (TLanPrivateInfo *) dev->priv; + +	dev->name = priv->devName; +	strcpy( priv->devName, "    " ); + +	dev = init_etherdev( dev, sizeof(TLanPrivateInfo) ); + +	dev->base_addr = io_base; +	dev->irq = irq; + + +	priv->adapter =    &TLanAdapterList[index]; +	priv->adapterRev = rev; +	priv->aui =        dev->mem_start & 0x01; +	priv->duplex =     ( ( dev->mem_start & 0x0C ) == 0x0C ) ? 0 : ( dev->mem_start & 0x0C ) >> 2; +	priv->speed =      ( ( dev->mem_start & 0x30 ) == 0x30 ) ? 0 : ( dev->mem_start & 0x30 ) >> 4; +	if ( priv->speed == 0x1 ) { +		priv->speed = TLAN_SPEED_10; +	} else if ( priv->speed == 0x2 ) { +		priv->speed = TLAN_SPEED_100; +	} +	priv->sa_int =     dev->mem_start & 0x02; +	priv->debug =      dev->mem_end; + + +	printk("TLAN %d.%d:  %s irq=%2d io=%04x, %s, Rev. %d\n", +		TLanVersionMajor, +		TLanVersionMinor, +		dev->name,  +		(int) irq,  +		io_base, +		priv->adapter->deviceLabel, +		priv->adapterRev ); + +	TLan_Init( dev ); +			 +   	return 0; + +} /* tlan_probe */ + + +#endif /* MODULE */ + + + + +	/*************************************************************** +	 *	TLan_PciProbe +	 * +	 *	Returns: +	 *		1 if another TLAN card was found, 0 if not. +	 *	Parms: +	 *		pci_bus		The PCI bus the card was found +	 *				on. +	 *		pci_dfn		The PCI whatever the card was +	 *				found at. +	 *		pci_irq		The IRQ of the found adapter. +	 *		pci_rev		The revision of the adapter. +	 *		pci_io_base	The first IO port used by the +	 *				adapter. +	 *		dl_ix		The index in the device list +	 *				of the adapter. +	 * +	 *	This function searches for an adapter with PCI vendor +	 *	and device IDs matching those in the TLanAdapterList. +	 *	The function 'remembers' the last device it found, +	 *	and so finds a new device (if anymore are to be found) +	 *	each time the function is called.  It then looks up +	 *	pertinent PCI info and returns it to the caller. +	 * +	 **************************************************************/ + +int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix ) +{ +	static int dl_index = 0; +	static int pci_index = 0; + +	int	not_found; +	u8	pci_latency; +	u16	pci_command; +	int	reg; + + +	if ( ! pcibios_present() ) { +		printk( "TLAN:   PCI Bios not present.\n" ); +		return 0; +	} + +	for (; TLanAdapterList[dl_index].vendorId != 0; dl_index++) { + +		not_found = pcibios_find_device( +			TLanAdapterList[dl_index].vendorId, +			TLanAdapterList[dl_index].deviceId, +			pci_index, +			pci_bus, +			pci_dfn +		); + +		if ( ! not_found ) { + +			TLAN_DBG( +				TLAN_DEBUG_GNRL, +				"TLAN:  found: Vendor Id = 0x%hx, Device Id = 0x%hx\n", +				TLanAdapterList[dl_index].vendorId, +				TLanAdapterList[dl_index].deviceId +			); + +			pcibios_read_config_byte ( *pci_bus,  *pci_dfn, PCI_REVISION_ID, pci_rev); +			pcibios_read_config_byte ( *pci_bus,  *pci_dfn, PCI_INTERRUPT_LINE, pci_irq); +			pcibios_read_config_word ( *pci_bus,  *pci_dfn, PCI_COMMAND, &pci_command); +			pcibios_read_config_dword( *pci_bus,  *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base); +			pcibios_read_config_byte ( *pci_bus,  *pci_dfn, PCI_LATENCY_TIMER, &pci_latency); + +			if (pci_latency < 0x10) { +				pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff); +				TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:    Setting latency timer to max.\n"); +			} + +			for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg +=4 ) { +				pcibios_read_config_dword( *pci_bus, *pci_dfn, reg, pci_io_base); +				if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) { +					*pci_io_base &= PCI_BASE_ADDRESS_IO_MASK; +					TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:    IO mapping is available at %x.\n", *pci_io_base); +					break; +				} else { +					*pci_io_base = 0; +				} +			} + +			if ( *pci_io_base == 0 ) +				printk("TLAN:    IO mapping not available, ignoring device.\n"); + +			if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) { +				pcibios_write_config_word ( *pci_bus,  *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER ); +				printk( "TLAN:  Activating PCI bus mastering for this device.\n" ); +			} + +			pci_index++; + +			if ( *pci_io_base ) { +				*dl_ix = dl_index; +				return 1; +			} + +		} else { +			pci_index = 0; +		} +	} + +	return 0; + +} /* TLan_PciProbe */ + + + + +	/*************************************************************** +	 *	TLan_Init +	 * +	 *	Returns: +	 *		0 on success, error code otherwise. +	 *	Parms: +	 *		dev	The structure of the device to be +	 *			init'ed. +	 * +	 *	This function completes the initialization of the +	 *	device structure and driver.  It reserves the IO +	 *	addresses, allocates memory for the lists and bounce +	 *	buffers, retrieves the MAC address from the eeprom +	 *	and assignes the device's methods. +	 *	 +	 **************************************************************/ + +int TLan_Init( struct device *dev ) +{ +	int		dma_size; +	int		err; +	int		i; +	TLanPrivateInfo	*priv; + +	priv = (TLanPrivateInfo *) dev->priv; + +	err = check_region( dev->base_addr, 0x10 ); +	if ( err ) { +		printk( "TLAN:  %s: Io port region 0x%lx size 0x%x in use.\n", +			dev->name, +			dev->base_addr, +			0x10 ); +		return -EIO; +	} +	request_region( dev->base_addr, 0x10, TLanSignature ); + +	if ( bbuf ) { +		dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) +	           * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE ); +	} else { +		dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS ) +	           * ( sizeof(TLanList) ); +	} + +	priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA ); +	if ( priv->dmaStorage == NULL ) { +		printk( "TLAN:  Could not allocate lists and buffers for %s.\n", +			dev->name ); +		return -ENOMEM; +	} +	memset( priv->dmaStorage, 0, dma_size ); +	priv->rxList = (TLanList *)  +		       ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 ); +	priv->txList = priv->rxList + TLAN_NUM_RX_LISTS; + +	if ( bbuf ) { +		priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS ); +		priv->txBuffer = priv->rxBuffer +				 + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE ); +	} + +	err = 0; +	for ( i = 0;  i < 6 ; i++ ) +		err |= TLan_EeReadByte( dev, +					(u8) priv->adapter->addrOfs + i, +					(u8 *) &dev->dev_addr[i] ); +	if ( err ) { +		printk( "TLAN:  %s: Error reading MAC from eeprom: %d\n", +			dev->name, +			err ); +	} + +	dev->addr_len = 6; + +	dev->open = &TLan_Open; +	dev->hard_start_xmit = &TLan_StartTx; +	dev->stop = &TLan_Close; +	dev->get_stats = &TLan_GetStats; +	dev->set_multicast_list = &TLan_SetMulticastList; + + +	return 0; + +} /* TLan_Init */ + + + + +	/*************************************************************** +	 *	TLan_Open +	 * +	 *	Returns: +	 *		0 on success, error code otherwise. +	 *	Parms: +	 *		dev	Structure of device to be opened. +	 * +	 *	This routine puts the driver and TLAN adapter in a +	 *	state where it is ready to send and receive packets. +	 *	It allocates the IRQ, resets and brings the adapter +	 *	out of reset, and allows interrupts.  It also delays +	 *	the startup for autonegotiation or sends a Rx GO +	 *	command to the adapter, as appropriate. +	 * +	 **************************************************************/ + +int TLan_Open( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	int		err; + +	priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION ); +	if ( priv->sa_int ) { +		TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:   Using SA_INTERRUPT\n" );  +		err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev ); +	} else { +		err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev ); +	} +	if ( err ) { +		printk( "TLAN:  Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq ); +		return -EAGAIN; +	} +	 +	MOD_INC_USE_COUNT; + +	dev->tbusy = 0; +	dev->interrupt = 0; +	dev->start = 1; + +	/* NOTE: It might not be necessary to read the stats before a +			 reset if you don't care what the values are. +	*/ +	TLan_ResetLists( dev ); +	TLan_ReadAndClearStats( dev, TLAN_IGNORE ); +	TLan_ResetAdapter( dev ); + +	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Opened.  TLAN Chip Rev: %x\n", dev->name, priv->tlanRev ); + +	return 0; + +} /* TLan_Open */ + + + + +	/*************************************************************** +	 *	TLan_StartTx +	 *   +	 *	Returns: +	 *		0 on success, non-zero on failure. +	 *	Parms: +	 *		skb	A pointer to the sk_buff containing the +	 *			frame to be sent. +	 *		dev	The device to send the data on. +	 * +	 *	This function adds a frame to the Tx list to be sent +	 *	ASAP.  First it	verifies that the adapter is ready and +	 *	there is room in the queue.  Then it sets up the next +	 *	available list, copies the frame to the	corresponding +	 *	buffer.  If the adapter Tx channel is idle, it gives +	 *	the adapter a Tx Go command on the list, otherwise it +	 *	sets the forward address of the previous list to point +	 *	to this one.  Then it frees the sk_buff. +	 * +	 **************************************************************/ + +int TLan_StartTx( struct sk_buff *skb, struct device *dev ) +{ +	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; +	TLanList	*tail_list; +	u8		*tail_buffer; +	int		pad; + +	if ( ! priv->phyOnline ) { +		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s PHY is not ready\n", dev->name ); +		dev_kfree_skb( skb, FREE_WRITE ); +		return 0; +	} + +	tail_list = priv->txList + priv->txTail; + +	if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) { +		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail ); +		dev->tbusy = 1; +		priv->txBusyCount++; +		return 1; +	} + +	tail_list->forward = 0; + +	if ( bbuf ) { +		tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); +		memcpy( tail_buffer, skb->data, skb->len ); +	} else { +		tail_list->buffer[0].address = virt_to_bus( skb->data ); +		tail_list->buffer[9].address = (u32) skb; +	} + +	pad = TLAN_MIN_FRAME_SIZE - skb->len; + +	if ( pad > 0 ) { +		tail_list->frameSize = (u16) skb->len + pad; +		tail_list->buffer[0].count = (u32) skb->len; +		tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad; +		tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer ); +	} else { +		tail_list->frameSize = (u16) skb->len; +		tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len; +		tail_list->buffer[1].count = 0; +		tail_list->buffer[1].address = 0; +	} + +	cli(); +	tail_list->cStat = TLAN_CSTAT_READY; +	if ( ! priv->txInProgress ) { +		priv->txInProgress = 1; +		outw( 0x4, dev->base_addr + TLAN_HOST_INT ); +		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Starting TX on buffer %d\n", priv->txTail ); +		outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM ); +		outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD ); +	} else { +		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Adding buffer %d to TX channel\n", priv->txTail ); +		if ( priv->txTail == 0 ) { +			( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list ); +		} else { +			( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list ); +		} +	} +	sti(); + +	CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS ); + +	if ( bbuf ) { +		dev_kfree_skb( skb, FREE_WRITE ); +	} +		 +	dev->trans_start = jiffies; +	return 0; + +} /* TLan_StartTx */ + + + + +	/*************************************************************** +	 *	TLan_HandleInterrupt +	 *   +	 *	Returns:	 +	 *		Nothing +	 *	Parms: +	 *		irq	The line on which the interrupt +	 *			occurred. +	 *		dev_id	A pointer to the device assigned to +	 *			this irq line. +	 *		regs	??? +	 * +	 *	This function handles an interrupt generated by its +	 *	assigned TLAN adapter.  The function deactivates +	 *	interrupts on its adapter, records the type of +	 *	interrupt, executes the appropriate subhandler, and +	 *	acknowdges the interrupt to the adapter (thus +	 *	re-enabling adapter interrupts. +	 * +	 **************************************************************/ + +void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +	u32		ack; +	struct device	*dev; +	u32		host_cmd; +	u16		host_int; +	int		type; + +	dev = (struct device *) dev_id; + +	cli(); +	if ( dev->interrupt ) { +		printk( "TLAN:   Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt ); +	} +	dev->interrupt++; + +	host_int = inw( dev->base_addr + TLAN_HOST_INT ); +	outw( host_int, dev->base_addr + TLAN_HOST_INT ); + +	type = ( host_int & TLAN_HI_IT_MASK ) >> 2; + +	ack = TLanIntVector[type]( dev, host_int ); + +	if ( ack ) { +		host_cmd = TLAN_HC_ACK | ack | ( type << 18 ); +		outl( host_cmd, dev->base_addr + TLAN_HOST_CMD ); +	} + +	dev->interrupt--; +	sti(); + +} /* TLan_HandleInterrupts */ + + + + +	/*************************************************************** +	 *	TLan_Close +	 *   +	 * 	Returns: +	 *		An error code. +	 *	Parms: +	 *		dev	The device structure of the device to +	 *			close. +	 * +	 *	This function shuts down the adapter.  It records any +	 *	stats, puts the adapter into reset state, deactivates +	 *	its time as needed, and	frees the irq it is using. +	 * +	 **************************************************************/ + +int TLan_Close(struct device *dev) +{ +	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; + +	dev->start = 0; +	dev->tbusy = 1; + +	TLan_ReadAndClearStats( dev, TLAN_RECORD ); +	outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); +	if ( priv->timer.function != NULL ) +		del_timer( &priv->timer ); +	free_irq( dev->irq, dev ); +	TLan_FreeLists( dev ); +	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  Device %s closed.\n", dev->name ); + +	MOD_DEC_USE_COUNT; + +	return 0; + +} /* TLan_Close */ + + + + +	/*************************************************************** +	 *	TLan_GetStats +	 *   +	 *	Returns: +	 *		A pointer to the device's statistics structure. +	 *	Parms: +	 *		dev	The device structure to return the +	 *			stats for. +	 * +	 *	This function updates the devices statistics by reading +	 *	the TLAN chip's onboard registers.  Then it returns the +	 *	address of the statistics structure. +	 * +	 **************************************************************/ + +struct net_device_stats *TLan_GetStats( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	int i; + +	/* Should only read stats if open ? */ +	TLan_ReadAndClearStats( dev, TLAN_RECORD ); + +	TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  %s EOC count = %d\n", dev->name, priv->rxEocCount ); +	TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  %s Busy count = %d\n", dev->name, priv->txBusyCount ); +	if ( debug & TLAN_DEBUG_GNRL ) { +		TLan_PrintDio( dev->base_addr ); +		TLan_PhyPrint( dev ); +	} +	if ( debug & TLAN_DEBUG_LIST ) { +		for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) +			TLan_PrintList( priv->rxList + i, "RX", i ); +		for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) +			TLan_PrintList( priv->txList + i, "TX", i ); +	} +	 +	return ( &( (TLanPrivateInfo *) dev->priv )->stats ); + +} /* TLan_GetStats */ + + + + +	/*************************************************************** +	 *	TLan_SetMulticastList +	 *   +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		dev	The device structure to set the +	 *			multicast list for. +	 * +	 *	This function sets the TLAN adaptor to various receive +	 *	modes.  If the IFF_PROMISC flag is set, promiscuous +	 *	mode is acitviated.  Otherwise,	promiscuous mode is +	 *	turned off.  If the IFF_ALLMULTI flag is set, then +	 *	the hash table is set to receive all group addresses. +	 *	Otherwise, the first three multicast addresses are +	 *	stored in AREG_1-3, and the rest are selected via the +	 *	hash table, as necessary. +	 * +	 **************************************************************/ + +void TLan_SetMulticastList( struct device *dev ) +{	 +	struct dev_mc_list	*dmi = dev->mc_list; +	u32			hash1 = 0; +	u32			hash2 = 0; +	int			i; +	u32			offset; +	u8			tmp; + +	if ( dev->flags & IFF_PROMISC ) { +		tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); +		TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF ); +	} else { +		tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD ); +		TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF ); +		if ( dev->flags & IFF_ALLMULTI ) { +			for ( i = 0; i < 3; i++ )  +				TLan_SetMac( dev, i + 1, NULL ); +			TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF ); +			TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF ); +		} else { +			for ( i = 0; i < dev->mc_count; i++ ) { +				if ( i < 3 ) { +					TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr ); +				} else { +					offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr ); +					if ( offset < 32 )  +						hash1 |= ( 1 << offset ); +					else +						hash2 |= ( 1 << ( offset - 32 ) ); +				} +				dmi = dmi->next; +			} +			for ( ; i < 3; i++ )  +				TLan_SetMac( dev, i + 1, NULL ); +			TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 ); +			TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 ); +		} +	} + +} /* TLan_SetMulticastList */ + + + +/***************************************************************************** +****************************************************************************** + +        ThunderLAN Driver Interrupt Vectors and Table + +	Please see Chap. 4, "Interrupt Handling" of the "ThunderLAN +	Programmer's Guide" for more informations on handling interrupts +	generated by TLAN based adapters.   + +****************************************************************************** +*****************************************************************************/ + + +	/*************************************************************** +	 *	TLan_HandleInvalid +	 * +	 *	Returns: +	 *		0 +	 *	Parms: +	 *		dev		Device assigned the IRQ that was +	 *				raised. +	 *		host_int	The contents of the HOST_INT +	 *				port. +	 * +	 *	This function handles invalid interrupts.  This should +	 *	never happen unless some other adapter is trying to use +	 *	the IRQ line assigned to the device. +	 * +	 **************************************************************/ + +u32 TLan_HandleInvalid( struct device *dev, u16 host_int ) +{ +	host_int = 0; +	/* printk( "TLAN:  Invalid interrupt on %s.\n", dev->name ); */ +	return 0; + +} /* TLan_HandleInvalid */ + + + + +	/*************************************************************** +	 *	TLan_HandleTxEOF +	 * +	 *	Returns: +	 *		1 +	 *	Parms: +	 *		dev		Device assigned the IRQ that was +	 *				raised. +	 *		host_int	The contents of the HOST_INT +	 *				port. +	 * +	 *	This function handles Tx EOF interrupts which are raised +	 *	by the adapter when it has completed sending the +	 *	contents of a buffer.  If detemines which list/buffer +	 *	was completed and resets it.  If the buffer was the last +	 *	in the channel (EOC), then the function checks to see if +	 *	another buffer is ready to send, and if so, sends a Tx +	 *	Go command.  Finally, the driver activates/continues the +	 *	activity LED. +	 * +	 **************************************************************/ + +u32 TLan_HandleTxEOF( struct device *dev, u16 host_int ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	int		eoc = 0; +	TLanList	*head_list; +	u32		ack = 1; + +	TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); +	host_int = 0; +	head_list = priv->txList + priv->txHead; + +	if ( ! bbuf ) { +		dev_kfree_skb( (struct sk_buff *) head_list->buffer[9].address, FREE_WRITE ); +		head_list->buffer[9].address = 0; +	} + +	if ( head_list->cStat & TLAN_CSTAT_EOC ) +		eoc = 1; +	if (!(head_list->cStat & TLAN_CSTAT_FRM_CMP)) { +		printk( "TLAN:  Received interrupt for uncompleted TX frame.\n" ); +	} + +#if LINUX_KERNEL_VERSION > 0x20100 +	priv->stats->tx_bytes += head_list->frameSize; +#endif + +	head_list->cStat = TLAN_CSTAT_UNUSED; +	dev->tbusy = 0; +	CIRC_INC( priv->txHead, TLAN_NUM_TX_LISTS ); +	if ( eoc ) { +		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail ); +		head_list = priv->txList + priv->txHead; +		if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { +			outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); +			ack |= TLAN_HC_GO; +		} else { +			priv->txInProgress = 0; +		} +	} + +	if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) { +		TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); +		if ( priv->timer.function == NULL ) { +			TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY ); +		} else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) { +			priv->timerSetAt = jiffies; +		} +	} + +	return ack; + +} /* TLan_HandleTxEOF */ + + + + +	/*************************************************************** +	 *	TLan_HandleStatOverflow +	 * +	 *	Returns: +	 *		1 +	 *	Parms: +	 *		dev		Device assigned the IRQ that was +	 *				raised. +	 *		host_int	The contents of the HOST_INT +	 *				port. +	 * +	 *	This function handles the Statistics Overflow interrupt +	 *	which means that one or more of the TLAN statistics +	 *	registers has reached 1/2 capacity and needs to be read. +	 * +	 **************************************************************/ + +u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int ) +{ +	host_int = 0; +	TLan_ReadAndClearStats( dev, TLAN_RECORD ); + +	return 1; + +} /* TLan_HandleStatOverflow */ + + + + +	/*************************************************************** +	 *	TLan_HandleRxEOF +	 * +	 *	Returns: +	 *		1 +	 *	Parms: +	 *		dev		Device assigned the IRQ that was +	 *				raised. +	 *		host_int	The contents of the HOST_INT +	 *				port. +	 * +	 *	This function handles the Rx EOF interrupt which +	 *	indicates a frame has been received by the adapter from +	 *	the net and the frame has been transferred to memory. +	 *	The function determines the bounce buffer the frame has +	 *	been loaded into, creates a new sk_buff big enough to +	 *	hold the frame, and sends it to protocol stack.  It +	 *	then resets the used buffer and appends it to the end +	 *	of the list.  If the frame was the last in the Rx +	 *	channel (EOC), the function restarts the receive channel +	 *	by sending an Rx Go command to the adapter.  Then it +	 *	activates/continues the activity LED. +	 * +	 **************************************************************/ + +u32 TLan_HandleRxEOF( struct device *dev, u16 host_int ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u32		ack = 1; +	int		eoc = 0; +	u8		*head_buffer; +	TLanList	*head_list; +	struct sk_buff	*skb; +	TLanList	*tail_list; +	void		*t; + +	TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  Handling RX EOF (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); +	host_int = 0; +	head_list = priv->rxList + priv->rxHead; +	tail_list = priv->rxList + priv->rxTail; + +	if ( head_list->cStat & TLAN_CSTAT_EOC ) { +		eoc = 1; +	} + +	if (!(head_list->cStat & TLAN_CSTAT_FRM_CMP)) { +		printk( "TLAN:  Received interrupt for uncompleted RX frame.\n" ); +	} else if ( bbuf ) { +		skb = dev_alloc_skb( head_list->frameSize + 7 ); +		if ( skb == NULL ) {  +			printk( "TLAN:  Couldn't allocate memory for received data.\n" ); +		} else { +			head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE ); +			skb->dev = dev; +			skb_reserve( skb, 2 ); +			t = (void *) skb_put( skb, head_list->frameSize ); + +#if LINUX_KERNEL_VERSION > 0x20100 +			priv->stats->rx_bytes += head_list->frameSize; +#endif + +			memcpy( t, head_buffer, head_list->frameSize ); +			skb->protocol = eth_type_trans( skb, dev ); +			netif_rx( skb ); +		} +	} else { +		skb = (struct sk_buff *) head_list->buffer[9].address; +		head_list->buffer[9].address = 0; +		skb_trim( skb, head_list->frameSize ); + +#if LINUX_KERNEL_VERSION > 0x20100 +			priv->stats->rx_bytes += head_list->frameSize; +#endif + +		skb->protocol = eth_type_trans( skb, dev ); +		netif_rx( skb ); + +		skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 ); +		if ( skb == NULL ) { +			printk( "TLAN:  Couldn't allocate memory for received data.\n" ); +			/* If this ever happened it would be a problem */ +		} else { +			skb->dev = dev; +			skb_reserve( skb, 2 ); +			t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE ); +			head_list->buffer[0].address = virt_to_bus( t ); +			head_list->buffer[9].address = (u32) skb; +		} +	} + +	head_list->forward = 0; +	head_list->frameSize = TLAN_MAX_FRAME_SIZE; +	head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; +	tail_list->forward = virt_to_bus( head_list ); + +	CIRC_INC( priv->rxHead, TLAN_NUM_RX_LISTS ); +	CIRC_INC( priv->rxTail, TLAN_NUM_RX_LISTS ); + +	if ( eoc ) {  +		TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail ); +		head_list = priv->rxList + priv->rxHead; +		outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); +		ack |= TLAN_HC_GO | TLAN_HC_RT; +		priv->rxEocCount++; +	} + +	if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) { +		TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT ); +		if ( priv->timer.function == NULL )  { +			TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY ); +		} else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) { +			priv->timerSetAt = jiffies; +		} +	} + +	dev->last_rx = jiffies; + +	return ack; + +} /* TLan_HandleRxEOF */ + + + + +	/*************************************************************** +	 *	TLan_HandleDummy +	 * +	 *	Returns: +	 *		1 +	 *	Parms: +	 *		dev		Device assigned the IRQ that was +	 *				raised. +	 *		host_int	The contents of the HOST_INT +	 *				port. +	 * +	 *	This function handles the Dummy interrupt, which is +	 *	raised whenever a test interrupt is generated by setting +	 *	the Req_Int bit of HOST_CMD to 1. +	 * +	 **************************************************************/ + +u32 TLan_HandleDummy( struct device *dev, u16 host_int ) +{ +	host_int = 0; +	printk( "TLAN:  Test interrupt on %s.\n", dev->name ); +	return 1; + +} /* TLan_HandleDummy */ + + + + +	/*************************************************************** +	 *	TLan_HandleTxEOC +	 * +	 *	Returns: +	 *		1 +	 *	Parms: +	 *		dev		Device assigned the IRQ that was +	 *				raised. +	 *		host_int	The contents of the HOST_INT +	 *				port. +	 * +	 *	This driver is structured to determine EOC occurances by +	 *	reading the CSTAT member of the list structure.  Tx EOC +	 *	interrupts are disabled via the DIO INTDIS register. +	 *	However, TLAN chips before revision 3.0 didn't have this +	 *	functionality, so process EOC events if this is the +	 *	case. +	 * +	 **************************************************************/ + +u32 TLan_HandleTxEOC( struct device *dev, u16 host_int ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	TLanList		*head_list; +	u32				ack = 1; + +	host_int = 0; +	if ( priv->tlanRev < 0x30 ) { +		TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT:  Handling TX EOC (Head=%d Tail=%d) -- IRQ\n", priv->txHead, priv->txTail ); +		head_list = priv->txList + priv->txHead; +		if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) { +			outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); +			ack |= TLAN_HC_GO; +		} else { +			priv->txInProgress = 0; +		} +	} + +	return ack; + +} /* TLan_HandleTxEOC */ + + + + +	/*************************************************************** +	 *	TLan_HandleStatusCheck +	 * +	 *	Returns: +	 *		0 if Adapter check, 1 if Network Status check. +	 *	Parms: +	 *		dev		Device assigned the IRQ that was +	 *				raised. +	 *		host_int	The contents of the HOST_INT +	 *				port. +	 * +	 *	This function handles Adapter Check/Network Status +	 *	interrupts generated by the adapter.  It checks the +	 *	vector in the HOST_INT register to determine if it is +	 *	an Adapter Check interrupt.  If so, it resets the +	 *	adapter.  Otherwise it clears the status registers +	 *	and services the PHY. +	 * +	 **************************************************************/ + +u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int ) +{	 +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u32		ack; +	u32		error; +	u8		net_sts; +	u32		phy; +	u16		tlphy_ctl; +	u16		tlphy_sts; +	 +	ack = 1; +	if ( host_int & TLAN_HI_IV_MASK ) { +		error = inl( dev->base_addr + TLAN_CH_PARM ); +		printk( "TLAN:  %s: Adaptor Error = 0x%x\n", dev->name, error ); +		TLan_ReadAndClearStats( dev, TLAN_RECORD ); +		outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD ); +		TLan_FreeLists( dev ); +		TLan_ResetLists( dev ); +		TLan_ResetAdapter( dev ); +		dev->tbusy = 0; +		ack = 0; +	} else { +		TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Status Check\n", dev->name ); +		phy = priv->phy[priv->phyNum]; + +		net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS ); +		if ( net_sts ) { +			TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts ); +			TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s:    Net_Sts = %x\n", dev->name, (unsigned) net_sts ); +		} +		if ( ( net_sts & TLAN_NET_STS_MIRQ ) &&  ( priv->phyNum == 0 ) ) { +			TLan_MiiReadReg( dev, phy, TLAN_TLPHY_STS, &tlphy_sts ); +			TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl ); +        		if ( ! ( tlphy_sts & TLAN_TS_POLOK ) && ! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) { +                		tlphy_ctl |= TLAN_TC_SWAPOL; +                		TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl); +        		} else if ( ( tlphy_sts & TLAN_TS_POLOK ) && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) { +                		tlphy_ctl &= ~TLAN_TC_SWAPOL; +                		TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl); +        		} + +			if (debug) { +				TLan_PhyPrint( dev ); +			} +		} +	} + +	return ack; + +} /* TLan_HandleStatusCheck */ + + + + +	/*************************************************************** +	 *	TLan_HandleRxEOC +	 * +	 *	Returns: +	 *		1 +	 *	Parms: +	 *		dev		Device assigned the IRQ that was +	 *				raised. +	 *		host_int	The contents of the HOST_INT +	 *				port. +	 * +	 *	This driver is structured to determine EOC occurances by +	 *	reading the CSTAT member of the list structure.  Rx EOC +	 *	interrupts are disabled via the DIO INTDIS register. +	 *	However, TLAN chips before revision 3.0 didn't have this +	 *	CSTAT member or a INTDIS register, so if this chip is +	 *	pre-3.0, process EOC interrupts normally. +	 * +	 **************************************************************/ + +u32 TLan_HandleRxEOC( struct device *dev, u16 host_int ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	TLanList	*head_list; +	u32		ack = 1; + +	host_int = 0; +	if (  priv->tlanRev < 0x30 ) { +		TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE:  Handling RX EOC (Head=%d Tail=%d) -- IRQ\n", priv->rxHead, priv->rxTail ); +		head_list = priv->rxList + priv->rxHead; +		outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM ); +		ack |= TLAN_HC_GO | TLAN_HC_RT; +		priv->rxEocCount++; +	} + +	return ack; + +} /* TLan_HandleRxEOC */ + + + + +/***************************************************************************** +****************************************************************************** + +	ThunderLAN Driver Timer Function + +****************************************************************************** +*****************************************************************************/ + + +	/*************************************************************** +	 *	TLan_Timer +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		data	A value given to add timer when +	 *			add_timer was called. +	 * +	 *	This function handles timed functionality for the +	 *	TLAN driver.  The two current timer uses are for +	 *	delaying for autonegotionation and driving the ACT LED. +	 *	-	Autonegotiation requires being allowed about +	 *		2 1/2 seconds before attempting to transmit a +	 *		packet.  It would be a very bad thing to hang +	 *		the kernel this long, so the driver doesn't +	 *		allow transmission 'til after this time, for +	 *		certain PHYs.  It would be much nicer if all +	 *		PHYs were interrupt-capable like the internal +	 *		PHY. +	 *	-	The ACT LED, which shows adapter activity, is +	 *		driven by the driver, and so must be left on +	 *		for a short period to power up the LED so it +	 *		can be seen.  This delay can be changed by +	 *		changing the TLAN_TIMER_ACT_DELAY in tlan.h, +	 *		if desired.  10 jiffies produces a slightly +	 *		sluggish response. +	 * +	 **************************************************************/ + +void TLan_Timer( unsigned long data ) +{ +	struct device	*dev = (struct device *) data; +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u32		elapsed; + +	priv->timer.function = NULL; + +	switch ( priv->timerType ) { +		case TLAN_TIMER_PHY_PDOWN: +			TLan_PhyPowerDown( dev ); +			break; +		case TLAN_TIMER_PHY_PUP: +			TLan_PhyPowerUp( dev ); +			break; +		case TLAN_TIMER_PHY_RESET: +			TLan_PhyReset( dev ); +			break; +		case TLAN_TIMER_PHY_START_LINK: +			TLan_PhyStartLink( dev ); +			break; +		case TLAN_TIMER_PHY_FINISH_AN: +			TLan_PhyFinishAutoNeg( dev ); +			break; +		case TLAN_TIMER_FINISH_RESET: +			TLan_FinishReset( dev ); +			break; +		case TLAN_TIMER_ACTIVITY: +			cli(); +			if ( priv->timer.function == NULL ) { +				elapsed = jiffies - priv->timerSetAt; +				if ( elapsed >= TLAN_TIMER_ACT_DELAY ) { +					TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); +				} else  { +					priv->timer.function = &TLan_Timer; +					priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY; +					sti(); +					add_timer( &priv->timer ); +				} +			} +			sti(); +			break; +		default: +			break; +	} + +} /* TLan_Timer */ + + + + +/***************************************************************************** +****************************************************************************** + +	ThunderLAN Driver Adapter Related Routines + +****************************************************************************** +*****************************************************************************/ + + +	/*************************************************************** +	 *	TLan_ResetLists +	 *   +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		dev	The device structure with the list +	 *			stuctures to be reset. +	 * +	 *	This routine sets the variables associated with managing +	 *	the TLAN lists to their initial values. +	 * +	 **************************************************************/ + +void TLan_ResetLists( struct device *dev ) +{ +	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; +	int		i; +	TLanList	*list; +	struct sk_buff	*skb; +	void		*t = NULL; + +	priv->txHead = 0; +	priv->txTail = 0; +	for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { +		list = priv->txList + i; +		list->cStat = TLAN_CSTAT_UNUSED; +		if ( bbuf ) { +			list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); +		} else { +			list->buffer[0].address = 0; +		} +		list->buffer[2].count = 0; +		list->buffer[2].address = 0; +	} + +	priv->rxHead = 0; +	priv->rxTail = TLAN_NUM_RX_LISTS - 1; +	for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) { +		list = priv->rxList + i; +		list->cStat = TLAN_CSTAT_READY; +		list->frameSize = TLAN_MAX_FRAME_SIZE; +		list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; +		if ( bbuf ) { +			list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) ); +		} else { +			skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 ); +			if ( skb == NULL ) { +				printk( "TLAN:  Couldn't allocate memory for received data.\n" ); +				/* If this ever happened it would be a problem */ +			} else { +				skb->dev = dev; +				skb_reserve( skb, 2 ); +				t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE ); +			} +			list->buffer[0].address = virt_to_bus( t ); +			list->buffer[9].address = (u32) skb; +		} +		list->buffer[1].count = 0; +		list->buffer[1].address = 0; +		if ( i < TLAN_NUM_RX_LISTS - 1 ) +			list->forward = virt_to_bus( list + 1 ); +		else +			list->forward = 0; +	} + +} /* TLan_ResetLists */ + + +void TLan_FreeLists( struct device *dev ) +{ +	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; +	int		i; +	TLanList	*list; +	struct sk_buff	*skb; + +	if ( ! bbuf ) { +		for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) { +			list = priv->txList + i; +			skb = (struct sk_buff *) list->buffer[9].address; +			if ( skb ) { +				dev_kfree_skb( skb, FREE_WRITE ); +				list->buffer[9].address = 0; +			} +		} + +		for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) { +			list = priv->rxList + i; +			skb = (struct sk_buff *) list->buffer[9].address; +			if ( skb ) { +				dev_kfree_skb( skb, FREE_READ ); +				list->buffer[9].address = 0; +			} +		} +	} + +} /* TLan_FreeLists */ + + + + +	/*************************************************************** +	 *	TLan_PrintDio +	 *   +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		io_base		Base IO port of the device of +	 *				which to print DIO registers. +	 * +	 *	This function prints out all the internal (DIO) +	 *	registers of a TLAN chip. +	 * +	 **************************************************************/ + +void TLan_PrintDio( u16 io_base ) +{ +	u32 data0, data1; +	int	i; + +	printk( "TLAN:   Contents of internal registers for io base 0x%04hx.\n", io_base ); +	printk( "TLAN:      Off.  +0         +4\n" ); +	for ( i = 0; i < 0x4C; i+= 8 ) { +		data0 = TLan_DioRead32( io_base, i ); +		data1 = TLan_DioRead32( io_base, i + 0x4 ); +		printk( "TLAN:      0x%02x  0x%08x 0x%08x\n", i, data0, data1 ); +	} + +} /* TLan_PrintDio */ + + + + +	/*************************************************************** +	 *	TLan_PrintList +	 *   +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		list	A pointer to the TLanList structure to +	 *			be printed. +	 *		type	A string to designate type of list, +	 *			"Rx" or "Tx". +	 *		num	The index of the list. +	 * +	 *	This function prints out the contents of the list +	 *	pointed to by the list parameter. +	 * +	 **************************************************************/ + +void TLan_PrintList( TLanList *list, char *type, int num) +{ +	int i; + +	printk( "TLAN:   %s List %d at 0x%08x\n", type, num, (u32) list ); +	printk( "TLAN:      Forward    = 0x%08x\n",  list->forward ); +	printk( "TLAN:      CSTAT      = 0x%04hx\n", list->cStat ); +	printk( "TLAN:      Frame Size = 0x%04hx\n", list->frameSize ); +	/* for ( i = 0; i < 10; i++ ) { */ +	for ( i = 0; i < 2; i++ ) { +		printk( "TLAN:      Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address ); +	} + +} /* TLan_PrintList */ + + + + +	/*************************************************************** +	 *	TLan_ReadAndClearStats +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		dev	Pointer to device structure of adapter +	 *			to which to read stats. +	 *		record	Flag indicating whether to add  +	 * +	 *	This functions reads all the internal status registers +	 *	of the TLAN chip, which clears them as a side effect. +	 *	It then either adds the values to the device's status +	 *	struct, or discards them, depending on whether record +	 *	is TLAN_RECORD (!=0)  or TLAN_IGNORE (==0). +	 * +	 **************************************************************/ + +void TLan_ReadAndClearStats( struct device *dev, int record ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u32		tx_good, tx_under; +	u32		rx_good, rx_over; +	u32		def_tx, crc, code; +	u32		multi_col, single_col; +	u32		excess_col, late_col, loss; + +	outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR ); +	tx_good  = inb( dev->base_addr + TLAN_DIO_DATA ); +	tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; +	tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; +	tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); + +	outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR ); +	rx_good  = inb( dev->base_addr + TLAN_DIO_DATA ); +	rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; +	rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16; +	rx_over  = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); +		 +	outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR ); +	def_tx  = inb( dev->base_addr + TLAN_DIO_DATA ); +	def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; +	crc     = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); +	code    = inb( dev->base_addr + TLAN_DIO_DATA + 3 ); +	 +	outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); +	multi_col   = inb( dev->base_addr + TLAN_DIO_DATA ); +	multi_col  += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8; +	single_col  = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); +	single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8; + +	outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR ); +	excess_col = inb( dev->base_addr + TLAN_DIO_DATA ); +	late_col   = inb( dev->base_addr + TLAN_DIO_DATA + 1 ); +	loss       = inb( dev->base_addr + TLAN_DIO_DATA + 2 ); + +	if ( record ) { +		priv->stats.rx_packets += rx_good; +		priv->stats.rx_errors  += rx_over + crc + code; +		priv->stats.tx_packets += tx_good; +		priv->stats.tx_errors  += tx_under + loss; +		priv->stats.collisions += multi_col + single_col + excess_col + late_col; + +		priv->stats.rx_over_errors    += rx_over; +		priv->stats.rx_crc_errors     += crc; +		priv->stats.rx_frame_errors   += code; + +		priv->stats.tx_aborted_errors += tx_under; +		priv->stats.tx_carrier_errors += loss; +	} +			 +} /* TLan_ReadAndClearStats */ + + + + +	/*************************************************************** +	 *	TLan_Reset +	 * +	 *	Returns: +	 *		0 +	 *	Parms: +	 *		dev	Pointer to device structure of adapter +	 *			to be reset. +	 * +	 *	This function resets the adapter and it's physical +	 *	device.  See Chap. 3, pp. 9-10 of the "ThunderLAN +	 *	Programmer's Guide" for details.  The routine tries to +	 *	implement what is detailed there, though adjustments +	 *	have been made. +	 * +	 **************************************************************/ + +void +TLan_ResetAdapter( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	int		i; +	u32		addr; +	u32		data; +	u8		data8; + +	priv->tlanFullDuplex = FALSE; +/*  1.	Assert reset bit. */ + +	data = inl(dev->base_addr + TLAN_HOST_CMD); +	data |= TLAN_HC_AD_RST; +	outl(data, dev->base_addr + TLAN_HOST_CMD); +	 +	udelay(1000); + +/*  2.	Turn off interrupts. ( Probably isn't necessary ) */ + +	data = inl(dev->base_addr + TLAN_HOST_CMD); +	data |= TLAN_HC_INT_OFF; +	outl(data, dev->base_addr + TLAN_HOST_CMD); + +/*  3.	Clear AREGs and HASHs. */ + + 	for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) { +		TLan_DioWrite32( dev->base_addr, (u16) i, 0 ); +	} + +/*  4.	Setup NetConfig register. */ + +	data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; +	TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data ); + +/*  5.	Load Ld_Tmr and Ld_Thr in HOST_CMD. */ + + 	outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD ); + 	outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD ); + +/*  6.	Unreset the MII by setting NMRST (in NetSio) to 1. */ + +	outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR ); +	addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO; +	TLan_SetBit( TLAN_NET_SIO_NMRST, addr ); + +/*  7.	Setup the remaining registers. */ + +	if ( priv->tlanRev >= 0x30 ) { +		data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC; +		TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 ); +	} +	TLan_PhyDetect( dev ); +	data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN; +	if ( priv->adapter->flags & TLAN_ADAPTER_BIT_RATE_PHY ) { +		data |= TLAN_NET_CFG_BIT; +		if ( priv->aui == 1 ) { +			TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a ); +		} else if ( priv->duplex == TLAN_DUPLEX_FULL ) { +			TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x00 ); +			priv->tlanFullDuplex = TRUE; +		} else { +			TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 ); +		} +	} +	if ( priv->phyNum == 0 ) { +		data |= TLAN_NET_CFG_PHY_EN; +	} +	TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data ); + +	if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) { +		TLan_FinishReset( dev ); +	} else { +		TLan_PhyPowerDown( dev ); +	} + +} /* TLan_ResetAdapter */ + + + + +void +TLan_FinishReset( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u8		data; +	u32		phy; +	u8		sio; +	u16		status; +	u16		tlphy_ctl; + +	phy = priv->phy[priv->phyNum]; + +	data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP; +	if ( priv->tlanFullDuplex ) { +		data |= TLAN_NET_CMD_DUPLEX; +	} +	TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data ); +	data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5;  +	if ( priv->phyNum == 0 ) { +		data |= TLAN_NET_MASK_MASK7;  +	} +	TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data ); +	TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE ); + +	if ( ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) || ( priv->aui ) ) { +		status = MII_GS_LINK; +		printk( "TLAN:  %s: Link forced.\n", dev->name ); +	} else { +		TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); +		udelay( 1000 ); +		TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); +		if ( status & MII_GS_LINK ) { +			printk( "TLAN:  %s: Link active.\n", dev->name ); +			TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK ); +		} +	} + +	if ( priv->phyNum == 0 ) { +        	TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl ); +        	tlphy_ctl |= TLAN_TC_INTEN; +        	TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl ); +        	sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO ); +        	sio |= TLAN_NET_SIO_MINTEN; +        	TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio ); +	} + +	if ( status & MII_GS_LINK ) { +		TLan_SetMac( dev, 0, dev->dev_addr ); +		priv->phyOnline = 1; +		outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); +		if ( debug >= 1 ) { +			outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 ); +		} +		outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM ); +		outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD ); +	} else { +		printk( "TLAN:  %s: Link inactive, will retry in 10 secs...\n", dev->name ); +		TLan_SetTimer( dev, 1000, TLAN_TIMER_FINISH_RESET ); +		return; +	} + +} /* TLan_FinishReset */ + + + + +	/*************************************************************** +	 *	TLan_SetMac +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		dev	Pointer to device structure of adapter +	 *			on which to change the AREG. +	 *		areg	The AREG to set the address in (0 - 3). +	 *		mac	A pointer to an array of chars.  Each +	 *			element stores one byte of the address. +	 *			IE, it isn't in ascii. +	 * +	 *	This function transfers a MAC address to one of the +	 *	TLAN AREGs (address registers).  The TLAN chip locks +	 *	the register on writing to offset 0 and unlocks the +	 *	register after writing to offset 5.  If NULL is passed +	 *	in mac, then the AREG is filled with 0's. +	 * +	 **************************************************************/ + +void TLan_SetMac( struct device *dev, int areg, char *mac ) +{ +	int i; +			 +	areg *= 6; + +	if ( mac != NULL ) { +		for ( i = 0; i < 6; i++ ) +			TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] ); +	} else { +		for ( i = 0; i < 6; i++ ) +			TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 ); +	} + +} /* TLan_SetMac */ + + + + +/***************************************************************************** +****************************************************************************** + +	ThunderLAN Driver PHY Layer Routines + +****************************************************************************** +*****************************************************************************/ + + + +	/********************************************************************* +	 *	TLan_PhyPrint +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		dev	A pointer to the device structure of the +	 *			TLAN device having the PHYs to be detailed. +	 *				 +	 *	This function prints the registers a PHY (aka tranceiver). +	 * +	 ********************************************************************/ + +void TLan_PhyPrint( struct device *dev ) +{ +	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; +	u16 i, data0, data1, data2, data3, phy; + +	phy = priv->phy[priv->phyNum]; + +	if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) { +		printk( "TLAN:   Device %s, Unmanaged PHY.\n", dev->name ); +	} else if ( phy <= TLAN_PHY_MAX_ADDR ) { +		printk( "TLAN:   Device %s, PHY 0x%02x.\n", dev->name, phy ); +		printk( "TLAN:      Off.  +0     +1     +2     +3 \n" ); +                for ( i = 0; i < 0x20; i+= 4 ) { +			printk( "TLAN:      0x%02x", i ); +			TLan_MiiReadReg( dev, phy, i, &data0 ); +			printk( " 0x%04hx", data0 ); +			TLan_MiiReadReg( dev, phy, i + 1, &data1 ); +			printk( " 0x%04hx", data1 ); +			TLan_MiiReadReg( dev, phy, i + 2, &data2 ); +			printk( " 0x%04hx", data2 ); +			TLan_MiiReadReg( dev, phy, i + 3, &data3 ); +			printk( " 0x%04hx\n", data3 ); +		} +	} else { +		printk( "TLAN:   Device %s, Invalid PHY.\n", dev->name ); +	} + +} /* TLan_PhyPrint */ + + + + +	/********************************************************************* +	 *	TLan_PhyDetect +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		dev	A pointer to the device structure of the adapter +	 *			for which the PHY needs determined. +	 * +	 *	So far I've found that adapters which have external PHYs +	 *	may also use the internal PHY for part of the functionality. +	 *	(eg, AUI/Thinnet).  This function finds out if this TLAN +	 *	chip has an internal PHY, and then finds the first external +	 *	PHY (starting from address 0) if it exists). +	 * +	 ********************************************************************/ + +void TLan_PhyDetect( struct device *dev ) +{ +	TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv; +	u16		control; +	u16		hi; +	u16		lo; +	u32		phy; + +	if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) { +		priv->phyNum = 0xFFFF; +		return; +	} + +	TLan_MiiReadReg( dev, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi ); + +	if ( hi != 0xFFFF ) { +		priv->phy[0] = TLAN_PHY_MAX_ADDR; +	} else { +		priv->phy[0] = TLAN_PHY_NONE; +	} + +	priv->phy[1] = TLAN_PHY_NONE; +	for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) { +		TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &control ); +		TLan_MiiReadReg( dev, phy, MII_GEN_ID_HI, &hi ); +		TLan_MiiReadReg( dev, phy, MII_GEN_ID_LO, &lo ); +		if ( ( control != 0xFFFF ) || ( hi != 0xFFFF ) || ( lo != 0xFFFF ) ) { +			TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: PHY found at %02x %04x %04x %04x\n", phy, control, hi, lo ); +			if ( ( priv->phy[1] == TLAN_PHY_NONE ) && ( phy != TLAN_PHY_MAX_ADDR ) ) { +				priv->phy[1] = phy; +			} +		} +	} + +	if ( priv->phy[1] != TLAN_PHY_NONE ) { +		priv->phyNum = 1; +	} else if ( priv->phy[0] != TLAN_PHY_NONE ) { +		priv->phyNum = 0; +	} else { +		printk( "TLAN:  Cannot initialize device, no PHY was found!\n" ); +	} + +} /* TLan_PhyDetect */ + + + + +void TLan_PhyPowerDown( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u16		value; + +	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Powering down PHY(s).\n", dev->name ); +	value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE; +	TLan_MiiSync( dev->base_addr ); +	TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value ); +	if ( ( priv->phyNum == 0 ) && ( priv->phy[1] != TLAN_PHY_NONE ) && ( ! ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) ) ) { +		TLan_MiiSync( dev->base_addr ); +		TLan_MiiWriteReg( dev, priv->phy[1], MII_GEN_CTL, value ); +	} + +	/* Wait for 5 jiffies (50 ms) and powerup +	 * This is abitrary.  It is intended to make sure the +	 * tranceiver settles. +	 */ +	TLan_SetTimer( dev, 5, TLAN_TIMER_PHY_PUP ); + +} /* TLan_PhyPowerDown */ + + + + +void TLan_PhyPowerUp( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u16		value; + +	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Powering up PHY.\n", dev->name ); +	TLan_MiiSync( dev->base_addr ); +	value = MII_GC_LOOPBK; +	TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value ); + +	/* Wait for 50 jiffies (500 ms) and reset the +	 * tranceiver.  The TLAN docs say both 50 ms and +	 * 500 ms, so do the longer, just in case +	 */ +	TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_RESET ); + +} /* TLan_PhyPowerUp */ + + + + +void TLan_PhyReset( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u16		phy; +	u16		value; + +	phy = priv->phy[priv->phyNum]; + +	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Reseting PHY.\n", dev->name ); +	TLan_MiiSync( dev->base_addr ); +	value = MII_GC_LOOPBK | MII_GC_RESET; +	TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, value ); +	TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value ); +	while ( value & MII_GC_RESET ) { +		TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value ); +	} +	TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 ); + +	/* Wait for 50 jiffies (500 ms) and initialize. +	 * I don't remember why I wait this long. +	 */ +	TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_START_LINK ); + +} /* TLan_PhyReset */ + + + + +void TLan_PhyStartLink( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u16		ability; +	u16		control; +	u16		data; +	u16		phy; +	u16		status; +	u16		tctl; + +	phy = priv->phy[priv->phyNum]; + +	TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN:  %s: Trying to activate link.\n", dev->name ); +	TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); +	if ( ( status & MII_GS_AUTONEG ) &&  +	     ( priv->duplex == TLAN_DUPLEX_DEFAULT ) &&  +	     ( priv->speed == TLAN_SPEED_DEFAULT ) && +	     ( ! priv->aui ) ) { +		ability = status >> 11; + +		if ( priv->speed == TLAN_SPEED_10 ) { +			ability &= 0x0003; +		} else if ( priv->speed == TLAN_SPEED_100 ) { +			ability &= 0x001C; +		} + +		if ( priv->duplex == TLAN_DUPLEX_FULL ) { +			ability &= 0x000A; +		} else if ( priv->duplex == TLAN_DUPLEX_HALF ) { +			ability &= 0x0005; +		} + +		TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 ); +       		TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 ); +		TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 ); + +		/* Wait for 400 jiffies (4 sec) for autonegotiation +		 * to complete.  The max spec time is less than this +		 * but the card need additional time to start AN. +		 * .5 sec should be plenty extra. +		 */ +		printk( "TLAN:  %s: Starting autonegotiation.\n", dev->name ); +		TLan_SetTimer( dev, 400, TLAN_TIMER_PHY_FINISH_AN ); +		return; +	} + +	if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) { +		priv->phyNum = 0; +		data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; +		TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data ); +		TLan_SetTimer( dev, 4, TLAN_TIMER_PHY_PDOWN ); +		return; +	} else if ( priv->phyNum == 0 ) { +        	TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl ); +		if ( priv->aui ) { +                	tctl |= TLAN_TC_AUISEL; +		} else { +                	tctl &= ~TLAN_TC_AUISEL; +			control = 0; +			if ( priv->duplex == TLAN_DUPLEX_FULL ) { +				control |= MII_GC_DUPLEX; +				priv->tlanFullDuplex = TRUE; +			} +			if ( priv->speed == TLAN_SPEED_100 ) { +				control |= MII_GC_SPEEDSEL; +			} +       			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, control ); +		} +        	TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl ); +	} + +	/* Wait for 100 jiffies (1 sec) to give the tranceiver time +	 * to establish link. +	 */ +	TLan_SetTimer( dev, 100, TLAN_TIMER_FINISH_RESET ); + +} /* TLan_PhyStartLink */ + + + + +void TLan_PhyFinishAutoNeg( struct device *dev ) +{ +	TLanPrivateInfo	*priv = (TLanPrivateInfo *) dev->priv; +	u16		an_adv; +	u16		an_lpa; +	u16		data; +	u16		mode; +	u16		phy; +	u16		status; +	 +	phy = priv->phy[priv->phyNum]; + +	TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status ); +	if ( ! ( status & MII_GS_AUTOCMPLT ) ) { +		/* Wait for 800 jiffies (8 sec) to give the process +		 * more time.  Perhaps we should fail after a while. +		 */ +		printk( "TLAN:  Giving autonegotiation more time.\n" ); +		TLan_SetTimer( dev, 800, TLAN_TIMER_PHY_FINISH_AN ); +		return; +	} + +	printk( "TLAN:  %s: Autonegotiation complete.\n", dev->name ); +	TLan_MiiReadReg( dev, phy, MII_AN_ADV, &an_adv ); +	TLan_MiiReadReg( dev, phy, MII_AN_LPA, &an_lpa ); +	mode = an_adv & an_lpa & 0x03E0; +	if ( mode & 0x0100 ) { +		priv->tlanFullDuplex = TRUE; +	} else if ( ! ( mode & 0x0080 ) && ( mode & 0x0040 ) ) { +		priv->tlanFullDuplex = TRUE; +	} + +	if ( ( ! ( mode & 0x0180 ) ) && ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) && ( priv->phyNum != 0 ) ) { +		priv->phyNum = 0; +		data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; +		TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data ); +		TLan_SetTimer( dev, 40, TLAN_TIMER_PHY_PDOWN ); +		return; +	} + +	if ( priv->phyNum == 0 ) { +		if ( ( priv->duplex == TLAN_DUPLEX_FULL ) || ( an_adv & an_lpa & 0x0040 ) ) { +			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX ); +			printk( "TLAN:  Starting internal PHY with DUPLEX\n" ); +		} else { +			TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB ); +			printk( "TLAN:  Starting internal PHY with HALF-DUPLEX\n" ); +		} +	} + +	/* Wait for 10 jiffies (100 ms).  No reason in partiticular. +	 */ +	TLan_SetTimer( dev, 10, TLAN_TIMER_FINISH_RESET ); +		 +} /* TLan_PhyFinishAutoNeg */ + + + + +/***************************************************************************** +****************************************************************************** + +	ThunderLAN Driver MII Routines + +	These routines are based on the information in Chap. 2 of the +	"ThunderLAN Programmer's Guide", pp. 15-24. + +****************************************************************************** +*****************************************************************************/ + + +	/*************************************************************** +	 *	TLan_MiiReadReg +	 * +	 *	Returns: +	 *		0	if ack received ok +	 *		1	otherwise. +	 * +	 *	Parms: +	 *		dev		The device structure containing +	 *				The io address and interrupt count +	 *				for this device. +	 *		phy		The address of the PHY to be queried. +	 *		reg		The register whose contents are to be +	 *				retreived. +	 *		val		A pointer to a variable to store the +	 *				retrieved value. +	 * +	 *	This function uses the TLAN's MII bus to retreive the contents +	 *	of a given register on a PHY.  It sends the appropriate info +	 *	and then reads the 16-bit register value from the MII bus via +	 *	the TLAN SIO register. +	 * +	 **************************************************************/ + +int TLan_MiiReadReg( struct device *dev, u16 phy, u16 reg, u16 *val ) +{ +	u8	nack; +	u16	sio, tmp; +	u32	i; +	int	err; +	int	minten; + +	err = FALSE; +	outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR); +	sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO; + +	if ( dev->interrupt == 0 ) +		cli(); +	dev->interrupt++; + +	TLan_MiiSync(dev->base_addr); + +	minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); +	if ( minten ) +		TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); + +	TLan_MiiSendData( dev->base_addr, 0x1, 2 );	/* Start ( 01b ) */ +	TLan_MiiSendData( dev->base_addr, 0x2, 2 );	/* Read  ( 10b ) */ +	TLan_MiiSendData( dev->base_addr, phy, 5 );	/* Device #      */ +	TLan_MiiSendData( dev->base_addr, reg, 5 );	/* Register #    */ + + +	TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio);		/* Change direction */ + +	TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);		/* Clock Idle bit */ +	TLan_SetBit(TLAN_NET_SIO_MCLK, sio); +	TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);		/* Wait 300ns */ + +	nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio);	/* Check for ACK */ +	TLan_SetBit(TLAN_NET_SIO_MCLK, sio);		/* Finish ACK */ +	if (nack) {					/* No ACK, so fake it */ +		for (i = 0; i < 16; i++) { +			TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); +			TLan_SetBit(TLAN_NET_SIO_MCLK, sio); +		} +		tmp = 0xffff; +		err = TRUE; +	} else {					/* ACK, so read data */ +		for (tmp = 0, i = 0x8000; i; i >>= 1) { +			TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); +			if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio)) +				tmp |= i; +			TLan_SetBit(TLAN_NET_SIO_MCLK, sio); +		} +	} + + +	TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);		/* Idle cycle */ +	TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + +	if ( minten ) +		TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); + +	*val = tmp; + +	dev->interrupt--; +	if ( dev->interrupt == 0 ) +		sti(); + +	return err; + +} /* TLan_MiiReadReg */ + + + + +	/*************************************************************** +	 *	TLan_MiiSendData +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		base_port	The base IO port of the adapter	in +	 *				question. +	 *		dev		The address of the PHY to be queried. +	 *		data		The value to be placed on the MII bus. +	 *		num_bits	The number of bits in data that are to +	 *				be placed on the MII bus. +	 * +	 *	This function sends on sequence of bits on the MII +	 *	configuration bus. +	 * +	 **************************************************************/ + +void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits ) +{ +	u16 sio; +	u32 i; + +	if ( num_bits == 0 ) +		return; + +	outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); +	sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; +	TLan_SetBit( TLAN_NET_SIO_MTXEN, sio ); + +	for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) { +		TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); +		TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); +		if ( data & i ) +			TLan_SetBit( TLAN_NET_SIO_MDATA, sio ); +		else +			TLan_ClearBit( TLAN_NET_SIO_MDATA, sio ); +		TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); +		TLan_GetBit( TLAN_NET_SIO_MCLK, sio ); +	} + +} /* TLan_MiiSendData */ + + + + +	/*************************************************************** +	 *	TLan_MiiSync +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		base_port	The base IO port of the adapter in +	 *				question. +	 * +	 *	This functions syncs all PHYs in terms of the MII configuration +	 *	bus. +	 * +	 **************************************************************/ + +void TLan_MiiSync( u16 base_port ) +{ +	int i; +	u16 sio; + +	outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR ); +	sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + +	TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio ); +	for ( i = 0; i < 32; i++ ) { +		TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); +		TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); +	} + +} /* TLan_MiiSync */ + + + + +	/*************************************************************** +	 *	TLan_MiiWriteReg +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		dev		The device structure for the device +	 *				to write to. +	 *		phy		The address of the PHY to be written to. +	 *		reg		The register whose contents are to be +	 *				written. +	 *		val		The value to be written to the register. +	 * +	 *	This function uses the TLAN's MII bus to write the contents of a +	 *	given register on a PHY.  It sends the appropriate info and then +	 *	writes the 16-bit register value from the MII configuration bus +	 *	via the TLAN SIO register. +	 * +	 **************************************************************/ + +void TLan_MiiWriteReg( struct device *dev, u16 phy, u16 reg, u16 val ) +{ +	u16	sio; +	int	minten; + +	outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR); +	sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO; + +	if ( dev->interrupt == 0 ) +		cli(); +	dev->interrupt++; + +	TLan_MiiSync( dev->base_addr ); + +	minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio ); +	if ( minten ) +		TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio ); + +	TLan_MiiSendData( dev->base_addr, 0x1, 2 );	/* Start ( 01b ) */ +	TLan_MiiSendData( dev->base_addr, 0x1, 2 );	/* Write ( 01b ) */ +	TLan_MiiSendData( dev->base_addr, phy, 5 );	/* Device #      */ +	TLan_MiiSendData( dev->base_addr, reg, 5 );	/* Register #    */ + +	TLan_MiiSendData( dev->base_addr, 0x2, 2 );	/* Send ACK */ +	TLan_MiiSendData( dev->base_addr, val, 16 );	/* Send Data */ + +	TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );	/* Idle cycle */ +	TLan_SetBit( TLAN_NET_SIO_MCLK, sio ); + +	if ( minten ) +		TLan_SetBit( TLAN_NET_SIO_MINTEN, sio ); + +	dev->interrupt--; +	if ( dev->interrupt == 0 ) +		sti(); + +} /* TLan_MiiWriteReg */ + + + + +/***************************************************************************** +****************************************************************************** + +	ThunderLAN Driver Eeprom routines + +	The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A +	EEPROM.  These functions are based on information in Microchip's +	data sheet.  I don't know how well this functions will work with +	other EEPROMs. + +****************************************************************************** +*****************************************************************************/ + + +	/*************************************************************** +	 *	TLan_EeSendStart +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms:	 +	 *		io_base		The IO port base address for the +	 *				TLAN device with the EEPROM to +	 *				use. +	 * +	 *	This function sends a start cycle to an EEPROM attached +	 *	to a TLAN chip. +	 * +	 **************************************************************/ + +void TLan_EeSendStart( u16 io_base ) +{ +	u16	sio; + +	outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); +	sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + +	TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); +	TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); +	TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); +	TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); +	TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); + +} /* TLan_EeSendStart */ + + + + +	/*************************************************************** +	 *	TLan_EeSendByte +	 * +	 *	Returns: +	 *		If the correct ack was received, 0, otherwise 1 +	 *	Parms:	io_base		The IO port base address for the +	 *				TLAN device with the EEPROM to +	 *				use. +	 *		data		The 8 bits of information to +	 *				send to the EEPROM. +	 *		stop		If TLAN_EEPROM_STOP is passed, a +	 *				stop cycle is sent after the +	 *				byte is sent after the ack is +	 *				read. +	 * +	 *	This function sends a byte on the serial EEPROM line, +	 *	driving the clock to send each bit. The function then +	 *	reverses transmission direction and reads an acknowledge +	 *	bit. +	 * +	 **************************************************************/ + +int TLan_EeSendByte( u16 io_base, u8 data, int stop ) +{ +	int	err; +	u8	place; +	u16	sio; + +	outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); +	sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + +	/* Assume clock is low, tx is enabled; */ +	for ( place = 0x80; place != 0; place >>= 1 ) { +		if ( place & data ) +			TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); +		else +			TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); +		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); +		TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); +	} +	TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); +	TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); +	err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio ); +	TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); +	TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); + +	if ( ( ! err ) && stop ) { +		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	/* STOP, raise data while clock is high */ +		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); +		TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); +	} + +	return ( err ); + +} /* TLan_EeSendByte */ + + + + +	/*************************************************************** +	 *	TLan_EeReceiveByte +	 * +	 *	Returns: +	 *		Nothing +	 *	Parms: +	 *		io_base		The IO port base address for the +	 *				TLAN device with the EEPROM to +	 *				use. +	 *		data		An address to a char to hold the +	 *				data sent from the EEPROM. +	 *		stop		If TLAN_EEPROM_STOP is passed, a +	 *				stop cycle is sent after the +	 *				byte is received, and no ack is +	 *				sent. +	 * +	 *	This function receives 8 bits of data from the EEPROM +	 *	over the serial link.  It then sends and ack bit, or no +	 *	ack and a stop bit.  This function is used to retrieve +	 *	data after the address of a byte in the EEPROM has been +	 *	sent. +	 * +	 **************************************************************/ + +void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop ) +{ +	u8  place; +	u16 sio; + +	outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR ); +	sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; +	*data = 0; + +	/* Assume clock is low, tx is enabled; */ +	TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio ); +	for ( place = 0x80; place; place >>= 1 ) { +		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); +		if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) ) +			*data |= place; +		TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); +	} + +	TLan_SetBit( TLAN_NET_SIO_ETXEN, sio ); +	if ( ! stop ) { +		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	/* Ack = 0 */ +		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); +		TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); +	} else { +		TLan_SetBit( TLAN_NET_SIO_EDATA, sio );		/* No ack = 1 (?) */ +		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); +		TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio ); +		TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );	/* STOP, raise data while clock is high */ +		TLan_SetBit( TLAN_NET_SIO_ECLOK, sio ); +		TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); +	} + +} /* TLan_EeReceiveByte */ + + + + +	/*************************************************************** +	 *	TLan_EeReadByte +	 * +	 *	Returns: +	 *		No error = 0, else, the stage at which the error +	 *		occured. +	 *	Parms: +	 *		io_base		The IO port base address for the +	 *				TLAN device with the EEPROM to +	 *				use. +	 *		ee_addr		The address of the byte in the +	 *				EEPROM whose contents are to be +	 *				retrieved. +	 *		data		An address to a char to hold the +	 *				data obtained from the EEPROM. +	 * +	 *	This function reads a byte of information from an byte +	 *	cell in the EEPROM. +	 * +	 **************************************************************/ + +int TLan_EeReadByte( struct device *dev, u8 ee_addr, u8 *data ) +{ +	int err; + +	if ( dev->interrupt == 0 ) +		cli(); +	dev->interrupt++; + +	TLan_EeSendStart( dev->base_addr ); +	err = TLan_EeSendByte( dev->base_addr, 0xA0, TLAN_EEPROM_ACK ); +	if (err) +		return 1; +	err = TLan_EeSendByte( dev->base_addr, ee_addr, TLAN_EEPROM_ACK ); +	if (err) +		return 2; +	TLan_EeSendStart( dev->base_addr ); +	err = TLan_EeSendByte( dev->base_addr, 0xA1, TLAN_EEPROM_ACK ); +	if (err) +		return 3; +	TLan_EeReceiveByte( dev->base_addr, data, TLAN_EEPROM_STOP ); + +	dev->interrupt--; +	if ( dev->interrupt == 0 ) +		sti(); + +	return 0; + +} /* TLan_EeReadByte */ + + + + + diff --git a/linux/src/drivers/net/tlan.h b/linux/src/drivers/net/tlan.h new file mode 100644 index 0000000..a66e26c --- /dev/null +++ b/linux/src/drivers/net/tlan.h @@ -0,0 +1,525 @@ +#ifndef TLAN_H +#define TLAN_H +/******************************************************************** + * + *  Linux ThunderLAN Driver + * + *  tlan.h + *  by James Banks + * + *  (C) 1997-1998 Caldera, Inc. + * + *  This software may be used and distributed according to the terms + *  of the GNU Public License, incorporated herein by reference. + * + ** This file is best viewed/edited with tabstop=4, colums>=132 + * + ********************************************************************/ + + +#include <asm/io.h> +#include <asm/types.h> +#include <linux/netdevice.h> + +#if LINUX_VERSION_CODE <= 0x20100 +#define net_device_stats	enet_statistics +#endif + + + + +	/***************************************************************** +	 * TLan Definitions +	 * +	 ****************************************************************/ + +#define FALSE			0 +#define TRUE			1 + +#define TLAN_MIN_FRAME_SIZE	64 +#define TLAN_MAX_FRAME_SIZE	1600 + +#define TLAN_NUM_RX_LISTS	4 +#define TLAN_NUM_TX_LISTS	8 + +#define TLAN_IGNORE		0 +#define TLAN_RECORD		1 + +#define TLAN_DBG(lvl, format, args...)	if (debug&lvl) printk( format, ##args ); +#define TLAN_DEBUG_GNRL		0x0001 +#define TLAN_DEBUG_TX		0x0002 +#define TLAN_DEBUG_RX		0x0004  +#define TLAN_DEBUG_LIST		0x0008 + + + + +	/***************************************************************** +	 * Device Identification Definitions +	 * +	 ****************************************************************/ +		 +#define PCI_DEVICE_ID_NETELLIGENT_10			0xAE34 +#define PCI_DEVICE_ID_NETELLIGENT_10_100		0xAE32 +#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED		0xAE35 +#define PCI_DEVICE_ID_NETFLEX_3P			0xF130 +#define PCI_DEVICE_ID_NETFLEX_3P_BNC			0xF150 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT	0xAE43 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL		0xAE40 +#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX		0xB011 +#define PCI_DEVICE_ID_NETELLIGENT_10_T2			0xB012 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100	0xB030 +#ifndef PCI_DEVICE_ID_OLICOM_OC2183 +#define PCI_DEVICE_ID_OLICOM_OC2183			0x0013 +#endif +#ifndef PCI_DEVICE_ID_OLICOM_OC2325 +#define PCI_DEVICE_ID_OLICOM_OC2325			0x0012 +#endif +#ifndef PCI_DEVICE_ID_OLICOM_OC2326 +#define PCI_DEVICE_ID_OLICOM_OC2326			0x0014 +#endif + +typedef struct tlan_adapter_entry { +	u16	vendorId; +	u16	deviceId; +	char	*deviceLabel; +	u32	flags; +	u16	addrOfs; +} TLanAdapterEntry; + +#define TLAN_ADAPTER_NONE		0x00000000 +#define TLAN_ADAPTER_UNMANAGED_PHY	0x00000001 +#define TLAN_ADAPTER_BIT_RATE_PHY	0x00000002 +#define TLAN_ADAPTER_USE_INTERN_10	0x00000004 +#define TLAN_ADAPTER_ACTIVITY_LED	0x00000008 + +#define TLAN_SPEED_DEFAULT	0 +#define TLAN_SPEED_10		10 +#define TLAN_SPEED_100		100 + +#define TLAN_DUPLEX_DEFAULT	0 +#define TLAN_DUPLEX_HALF	1 +#define TLAN_DUPLEX_FULL	2 + + + + +	/***************************************************************** +	 * Rx/Tx List Definitions +	 * +	 ****************************************************************/ + +#define TLAN_BUFFERS_PER_LIST	10 +#define TLAN_LAST_BUFFER	0x80000000 +#define TLAN_CSTAT_UNUSED	0x8000 +#define TLAN_CSTAT_FRM_CMP	0x4000 +#define TLAN_CSTAT_READY	0x3000 +#define TLAN_CSTAT_EOC		0x0800 +#define TLAN_CSTAT_RX_ERROR	0x0400 +#define TLAN_CSTAT_PASS_CRC	0x0200 +#define TLAN_CSTAT_DP_PR	0x0100 + + +typedef struct tlan_buffer_ref_tag { +	u32	count; +	u32	address; +} TLanBufferRef; + + +typedef struct tlan_list_tag { +	u32		forward; +	u16		cStat; +	u16		frameSize; +	TLanBufferRef	buffer[TLAN_BUFFERS_PER_LIST]; +} TLanList; + + +typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; + + + + +	/***************************************************************** +	 * PHY definitions +	 * +	 ****************************************************************/ + +#define TLAN_PHY_MAX_ADDR	0x1F +#define TLAN_PHY_NONE		0x20 + + + + +	/***************************************************************** +	 * TLAN Private Information Structure +	 * +	 ****************************************************************/ + +typedef struct tlan_private_tag { +	struct device           *nextDevice; +	void			*dmaStorage; +	u8			*padBuffer; +	TLanList                *rxList; +	u8			*rxBuffer; +	u32                     rxHead; +	u32                     rxTail; +	u32			rxEocCount; +	TLanList                *txList; +	u8			*txBuffer; +	u32                     txHead; +	u32                     txInProgress; +	u32                     txTail; +	u32			txBusyCount; +	u32                     phyOnline; +	u32			timerSetAt; +	u32			timerType; +	struct timer_list	timer; +	struct net_device_stats	stats; +	TLanAdapterEntry	*adapter; +	u32			adapterRev; +	u32			aui; +	u32			debug; +	u32			duplex; +	u32			phy[2]; +	u32			phyNum; +	u32			sa_int; +	u32			speed; +	u8			tlanRev; +	u8			tlanFullDuplex; +	char                    devName[8]; +} TLanPrivateInfo; + + + + +	/***************************************************************** +	 * TLan Driver Timer Definitions +	 * +	 ****************************************************************/ + +#define TLAN_TIMER_LINK			1 +#define TLAN_TIMER_ACTIVITY		2 +#define TLAN_TIMER_PHY_PDOWN		3 +#define TLAN_TIMER_PHY_PUP		4 +#define TLAN_TIMER_PHY_RESET		5 +#define TLAN_TIMER_PHY_START_LINK	6 +#define TLAN_TIMER_PHY_FINISH_AN	7 +#define TLAN_TIMER_FINISH_RESET		8 + +#define TLAN_TIMER_ACT_DELAY		10 + + + + +	/***************************************************************** +	 * TLan Driver Eeprom Definitions +	 * +	 ****************************************************************/ + +#define TLAN_EEPROM_ACK		0 +#define TLAN_EEPROM_STOP	1 + + + + +	/***************************************************************** +	 * Host Register Offsets and Contents +	 * +	 ****************************************************************/ + +#define TLAN_HOST_CMD			0x00 +#define 	TLAN_HC_GO		0x80000000 +#define		TLAN_HC_STOP		0x40000000 +#define		TLAN_HC_ACK		0x20000000 +#define		TLAN_HC_CS_MASK		0x1FE00000 +#define		TLAN_HC_EOC		0x00100000 +#define		TLAN_HC_RT		0x00080000 +#define		TLAN_HC_NES		0x00040000 +#define		TLAN_HC_AD_RST		0x00008000 +#define		TLAN_HC_LD_TMR		0x00004000 +#define		TLAN_HC_LD_THR		0x00002000 +#define		TLAN_HC_REQ_INT		0x00001000 +#define		TLAN_HC_INT_OFF		0x00000800 +#define		TLAN_HC_INT_ON		0x00000400 +#define		TLAN_HC_AC_MASK		0x000000FF +#define TLAN_CH_PARM			0x04 +#define TLAN_DIO_ADR			0x08 +#define		TLAN_DA_ADR_INC		0x8000 +#define		TLAN_DA_RAM_ADR		0x4000 +#define TLAN_HOST_INT			0x0A +#define		TLAN_HI_IV_MASK		0x1FE0 +#define		TLAN_HI_IT_MASK		0x001C +#define TLAN_DIO_DATA			0x0C + + +/* ThunderLAN Internal Register DIO Offsets */ + +#define TLAN_NET_CMD			0x00 +#define		TLAN_NET_CMD_NRESET	0x80 +#define		TLAN_NET_CMD_NWRAP	0x40 +#define		TLAN_NET_CMD_CSF	0x20 +#define		TLAN_NET_CMD_CAF	0x10 +#define		TLAN_NET_CMD_NOBRX	0x08 +#define		TLAN_NET_CMD_DUPLEX	0x04 +#define		TLAN_NET_CMD_TRFRAM	0x02 +#define		TLAN_NET_CMD_TXPACE	0x01 +#define TLAN_NET_SIO			0x01 +#define 	TLAN_NET_SIO_MINTEN	0x80 +#define		TLAN_NET_SIO_ECLOK	0x40 +#define		TLAN_NET_SIO_ETXEN	0x20 +#define		TLAN_NET_SIO_EDATA	0x10 +#define		TLAN_NET_SIO_NMRST	0x08 +#define		TLAN_NET_SIO_MCLK	0x04 +#define		TLAN_NET_SIO_MTXEN	0x02 +#define		TLAN_NET_SIO_MDATA	0x01 +#define TLAN_NET_STS			0x02 +#define		TLAN_NET_STS_MIRQ	0x80 +#define		TLAN_NET_STS_HBEAT	0x40 +#define		TLAN_NET_STS_TXSTOP	0x20 +#define		TLAN_NET_STS_RXSTOP	0x10 +#define		TLAN_NET_STS_RSRVD	0x0F +#define TLAN_NET_MASK			0x03 +#define		TLAN_NET_MASK_MASK7	0x80 +#define		TLAN_NET_MASK_MASK6	0x40 +#define		TLAN_NET_MASK_MASK5	0x20 +#define		TLAN_NET_MASK_MASK4	0x10 +#define		TLAN_NET_MASK_RSRVD	0x0F +#define TLAN_NET_CONFIG			0x04 +#define 	TLAN_NET_CFG_RCLK	0x8000 +#define		TLAN_NET_CFG_TCLK	0x4000 +#define		TLAN_NET_CFG_BIT	0x2000 +#define		TLAN_NET_CFG_RXCRC	0x1000 +#define		TLAN_NET_CFG_PEF	0x0800 +#define		TLAN_NET_CFG_1FRAG	0x0400 +#define		TLAN_NET_CFG_1CHAN	0x0200 +#define		TLAN_NET_CFG_MTEST	0x0100 +#define		TLAN_NET_CFG_PHY_EN	0x0080 +#define		TLAN_NET_CFG_MSMASK	0x007F +#define TLAN_MAN_TEST			0x06 +#define TLAN_DEF_VENDOR_ID		0x08 +#define TLAN_DEF_DEVICE_ID		0x0A +#define TLAN_DEF_REVISION		0x0C +#define TLAN_DEF_SUBCLASS		0x0D +#define TLAN_DEF_MIN_LAT		0x0E +#define TLAN_DEF_MAX_LAT		0x0F +#define TLAN_AREG_0			0x10 +#define TLAN_AREG_1			0x16 +#define TLAN_AREG_2			0x1C +#define TLAN_AREG_3			0x22 +#define TLAN_HASH_1			0x28 +#define TLAN_HASH_2			0x2C +#define TLAN_GOOD_TX_FRMS		0x30 +#define TLAN_TX_UNDERUNS		0x33 +#define TLAN_GOOD_RX_FRMS		0x34 +#define TLAN_RX_OVERRUNS		0x37 +#define TLAN_DEFERRED_TX		0x38 +#define TLAN_CRC_ERRORS			0x3A +#define TLAN_CODE_ERRORS		0x3B +#define TLAN_MULTICOL_FRMS		0x3C +#define TLAN_SINGLECOL_FRMS		0x3E +#define TLAN_EXCESSCOL_FRMS		0x40 +#define TLAN_LATE_COLS			0x41 +#define TLAN_CARRIER_LOSS		0x42 +#define TLAN_ACOMMIT			0x43 +#define TLAN_LED_REG			0x44 +#define		TLAN_LED_ACT		0x10 +#define		TLAN_LED_LINK		0x01 +#define TLAN_BSIZE_REG			0x45 +#define TLAN_MAX_RX			0x46 +#define TLAN_INT_DIS			0x48 +#define		TLAN_ID_TX_EOC		0x04 +#define		TLAN_ID_RX_EOF		0x02 +#define		TLAN_ID_RX_EOC		0x01 + + + +/* ThunderLAN Interrupt Codes */ + +#define TLAN_INT_NUMBER_OF_INTS	8 + +#define TLAN_INT_NONE			0x0000 +#define TLAN_INT_TX_EOF			0x0001 +#define TLAN_INT_STAT_OVERFLOW		0x0002 +#define TLAN_INT_RX_EOF			0x0003 +#define TLAN_INT_DUMMY			0x0004 +#define TLAN_INT_TX_EOC			0x0005 +#define TLAN_INT_STATUS_CHECK		0x0006 +#define TLAN_INT_RX_EOC			0x0007 + + + +/* ThunderLAN MII Registers */ + +/* Generic MII/PHY Registers */ + +#define MII_GEN_CTL			0x00 +#define 	MII_GC_RESET		0x8000 +#define		MII_GC_LOOPBK		0x4000 +#define		MII_GC_SPEEDSEL		0x2000 +#define		MII_GC_AUTOENB		0x1000 +#define		MII_GC_PDOWN		0x0800 +#define		MII_GC_ISOLATE		0x0400 +#define		MII_GC_AUTORSRT		0x0200 +#define		MII_GC_DUPLEX		0x0100 +#define		MII_GC_COLTEST		0x0080 +#define		MII_GC_RESERVED		0x007F +#define MII_GEN_STS			0x01 +#define		MII_GS_100BT4		0x8000 +#define		MII_GS_100BTXFD		0x4000 +#define		MII_GS_100BTXHD		0x2000 +#define		MII_GS_10BTFD		0x1000 +#define		MII_GS_10BTHD		0x0800 +#define		MII_GS_RESERVED		0x07C0 +#define		MII_GS_AUTOCMPLT	0x0020 +#define		MII_GS_RFLT		0x0010 +#define		MII_GS_AUTONEG		0x0008 +#define		MII_GS_LINK		0x0004 +#define		MII_GS_JABBER		0x0002 +#define		MII_GS_EXTCAP		0x0001 +#define MII_GEN_ID_HI			0x02 +#define MII_GEN_ID_LO			0x03 +#define 	MII_GIL_OUI		0xFC00 +#define 	MII_GIL_MODEL		0x03F0 +#define 	MII_GIL_REVISION	0x000F +#define MII_AN_ADV			0x04 +#define MII_AN_LPA			0x05 +#define MII_AN_EXP			0x06 + +/* ThunderLAN Specific MII/PHY Registers */ + +#define TLAN_TLPHY_ID			0x10 +#define TLAN_TLPHY_CTL			0x11 +#define 	TLAN_TC_IGLINK		0x8000 +#define		TLAN_TC_SWAPOL		0x4000 +#define		TLAN_TC_AUISEL		0x2000 +#define		TLAN_TC_SQEEN		0x1000 +#define		TLAN_TC_MTEST		0x0800 +#define		TLAN_TC_RESERVED	0x07F8 +#define		TLAN_TC_NFEW		0x0004 +#define		TLAN_TC_INTEN		0x0002 +#define		TLAN_TC_TINT		0x0001 +#define TLAN_TLPHY_STS			0x12 +#define		TLAN_TS_MINT		0x8000 +#define		TLAN_TS_PHOK		0x4000 +#define		TLAN_TS_POLOK		0x2000 +#define		TLAN_TS_TPENERGY	0x1000 +#define		TLAN_TS_RESERVED	0x0FFF + + +#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0 + +/* Routines to access internal registers. */ + +inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr) +{ +	outw(internal_addr, base_addr + TLAN_DIO_ADR); +	return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3))); +	 +} /* TLan_DioRead8 */ + + + + +inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr) +{ +	outw(internal_addr, base_addr + TLAN_DIO_ADR); +	return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2))); + +} /* TLan_DioRead16 */ + + + + +inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr) +{ +	outw(internal_addr, base_addr + TLAN_DIO_ADR); +	return (inl(base_addr + TLAN_DIO_DATA)); + +} /* TLan_DioRead32 */ + + + + +inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data) +{ +	outw(internal_addr, base_addr + TLAN_DIO_ADR); +	outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3)); + +} + + + + +inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data) +{ +	outw(internal_addr, base_addr + TLAN_DIO_ADR); +	outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + + +inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data) +{ +	outw(internal_addr, base_addr + TLAN_DIO_ADR); +	outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + +#if 0 +inline void TLan_ClearBit(u8 bit, u16 port) +{ +	outb_p(inb_p(port) & ~bit, port); +} + + + + +inline int TLan_GetBit(u8 bit, u16 port) +{ +	return ((int) (inb_p(port) & bit)); +} + + + + +inline void TLan_SetBit(u8 bit, u16 port) +{ +	outb_p(inb_p(port) | bit, port); +} +#endif + +#define TLan_ClearBit( bit, port )	outb_p(inb_p(port) & ~bit, port) +#define TLan_GetBit( bit, port )	((int) (inb_p(port) & bit)) +#define TLan_SetBit( bit, port )	outb_p(inb_p(port) | bit, port) + + +inline	u32	xor( u32 a, u32 b ) +{ +	return ( ( a && ! b ) || ( ! a && b ) ); +} +#define XOR8( a, b, c, d, e, f, g, h )	xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) ) +#define DA( a, bit )					( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) ) + +inline u32 TLan_HashFunc( u8 *a ) +{ +	u32	hash; + +	hash  = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) ); +	hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1; +	hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2; +	hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3; +	hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4; +	hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5; + +	return hash; + +}  + + + + +#endif diff --git a/linux/src/drivers/net/tulip.c b/linux/src/drivers/net/tulip.c new file mode 100644 index 0000000..2a20301 --- /dev/null +++ b/linux/src/drivers/net/tulip.c @@ -0,0 +1,3685 @@ +/* tulip.c: A DEC 21040 family ethernet driver for Linux. */ +/* +	Written/copyright 1994-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	This driver is for the Digital "Tulip" Ethernet adapter interface. +	It should work with most DEC 21*4*-based chips/ethercards, as well as +	with work-alike chips from Lite-On (PNIC) and Macronix (MXIC) and ASIX. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Support and updates available at +	http://www.scyld.com/network/tulip.html +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"tulip.c:v0.97 7/22/2003  Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/tulip.html\n"; + +#define SMP_CHECK + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +static int debug = 2;			/* Message enable: 0..31 = no..all messages. */ + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 25; + +#define MAX_UNITS 8 +/* Used to pass the full-duplex flag, etc. */ +static int full_duplex[MAX_UNITS] = {0, }; +static int options[MAX_UNITS] = {0, }; +static int mtu[MAX_UNITS] = {0, };			/* Jumbo MTU for interfaces. */ + +/*  The possible media types that can be set in options[] are: */ +#define MEDIA_MASK 31 +static const char * const medianame[32] = { +	"10baseT", "10base2", "AUI", "100baseTx", +	"10baseT-FDX", "100baseTx-FDX", "100baseT4", "100baseFx", +	"100baseFx-FDX", "MII 10baseT", "MII 10baseT-FDX", "MII", +	"10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FDX", "MII 100baseT4", +	"MII 100baseFx-HDX", "MII 100baseFx-FDX", "Home-PNA 1Mbps", "Invalid-19", +	"","","","", "","","","",  "","","","Transceiver reset", +}; + +/* Set if the PCI BIOS detects the chips on a multiport board backwards. */ +#ifdef REVERSE_PROBE_ORDER +static int reverse_probe = 1; +#else +static int reverse_probe = 0; +#endif + +/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ +#ifdef __alpha__				/* Always copy to aligned IP headers. */ +static int rx_copybreak = 1518; +#else +static int rx_copybreak = 100; +#endif + +/* +  Set the bus performance register. +	Typical: Set 16 longword cache alignment, no burst limit. +	Cache alignment bits 15:14	     Burst length 13:8 +		0000	No alignment  0x00000000 unlimited		0800 8 longwords +		4000	8  longwords		0100 1 longword		1000 16 longwords +		8000	16 longwords		0200 2 longwords	2000 32 longwords +		C000	32  longwords		0400 4 longwords +	Warning: many older 486 systems are broken and require setting 0x00A04800 +	   8 longword cache alignment, 8 longword burst. +	ToDo: Non-Intel setting could be better. +*/ + +#if defined(__alpha__) || defined(__x86_64) || defined(__ia64) +static int csr0 = 0x01A00000 | 0xE000; +#elif defined(__i386__) || defined(__powerpc__) || defined(__sparc__) +/* Do *not* rely on hardware endian correction for big-endian machines! */ +static int csr0 = 0x01A00000 | 0x8000; +#else +#warning Processor architecture undefined! +static int csr0 = 0x00A00000 | 0x4800; +#endif + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   Typical is a 64 element hash table based on the Ethernet CRC. +   This value does not apply to the 512 bit table chips. +*/ +static int multicast_filter_limit = 32; + +/* Operational parameters that are set at compile time. */ + +/* Keep the descriptor ring sizes a power of two for efficiency. +   The Tx queue length limits transmit packets to a portion of the available +   ring entries.  It should be at least one element less to allow multicast +   filter setup frames to be queued.  It must be at least four for hysteresis. +   Making the Tx queue too long decreases the effectiveness of channel +   bonding and packet priority. +   Large receive rings waste memory and confound network buffer limits. +   These values have been carefully studied: changing these might mask a +   problem, it won't fix it. +*/ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10 +#define RX_RING_SIZE	32 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) +/* Preferred skbuff allocation size. */ +#define PKT_BUF_SZ		1536 +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) +   to support a pre-NWay full-duplex signaling mechanism using short frames. +   No one knows what it should be, but if left at its default value some +   10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC	0x6969 + +/* The include file section.  We start by doing checks and fix-ups for +   missing compile flags. */ +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP)  &&  ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(CONFIG_MODVERSIONS) && defined(MODULE) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/unaligned.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(reverse_probe, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(csr0, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +#ifdef MODULE_PARM_DESC +MODULE_PARM_DESC(debug, "Tulip driver message level (0-31)"); +MODULE_PARM_DESC(options, +				 "Tulip: force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Tulip driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, "Tulip: non-zero to set forced full duplex."); +MODULE_PARM_DESC(rx_copybreak, +				 "Tulip breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Tulip breakpoint for switching to Rx-all-multicast"); +MODULE_PARM_DESC(reverse_probe, "Search PCI devices in reverse order to work " +				 "around misordered multiport NICS."); +MODULE_PARM_DESC(csr0, "Special setting for the CSR0 PCI bus parameter " +				 "register."); +#endif + +/* This driver was originally written to use I/O space access, but now +   uses memory space by default. Override this this with -DUSE_IO_OPS. */ +#if (LINUX_VERSION_CODE < 0x20100)  ||  ! defined(MODULE) +#define USE_IO_OPS +#endif +#ifndef USE_IO_OPS +#undef inb +#undef inw +#undef inl +#undef outb +#undef outw +#undef outl +#define inb readb +#define inw readw +#define inl readl +#define outb writeb +#define outw writew +#define outl writel +#endif + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the DECchip "Tulip", Digital's +single-chip ethernet controllers for PCI.  Supported members of the family +are the 21040, 21041, 21140, 21140A, 21142, and 21143.  Similar work-alike +chips from Lite-On, Macronics, ASIX, Compex and other listed below are also +supported. + +These chips are used on at least 140 unique PCI board designs.  The great +number of chips and board designs supported is the reason for the +driver size and complexity.  Almost of the increasing complexity is in the +board configuration and media selection code.  There is very little +increasing in the operational critical path length. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board.  The system BIOS preferably should assign the +PCI INTA signal to an otherwise unused system IRQ line. + +Some boards have EEPROMs tables with default media entry.  The factory default +is usually "autoselect".  This should only be overridden when using +transceiver connections without link beat e.g. 10base2 or AUI, or (rarely!) +for forcing full-duplex when used with old link partners that do not do +autonegotiation. + +III. Driver operation + +IIIa. Ring buffers + +The Tulip can use either ring buffers or lists of Tx and Rx descriptors. +This driver uses statically allocated rings of Rx and Tx descriptors, set at +compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs +for the Rx ring buffers at open() time and passes the skb->data field to the +Tulip as receive data buffers.  When an incoming frame is less than +RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is +copied to the new skbuff.  When the incoming frame is larger, the skbuff is +passed directly up the protocol stack and replaced by a newly allocated +skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information).  For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data.  A subtle aspect of this +choice is that the Tulip only receives into longword aligned buffers, thus +the IP header at offset 14 is not longword aligned for further processing. +Copied frames are put into the new skbuff at an offset of "+2", thus copying +has the beneficial effect of aligning the IP header and preloading the +cache. + +IIIC. Synchronization +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it is queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'tp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  (The Tx-done interrupt can not be selectively turned off, so +we cannot avoid the interrupt overhead by having the Tx routine reap the Tx +stats.)	 After reaping the stats, it marks the queue entry as empty by setting +the 'base' to zero.	 Iff the 'tp->tx_full' flag is set, it clears both the +tx_full and tbusy flags. + +IV. Notes + +Thanks to Duke Kamstra of SMC for long ago providing an EtherPower board. +Greg LaPolla at Linksys provided PNIC and other Linksys boards. +Znyx provided a four-port card for testing. + +IVb. References + +http://scyld.com/expert/NWay.html +http://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM") +http://www.national.com/pf/DP/DP83840A.html +http://www.asix.com.tw/pmac.htm +http://www.admtek.com.tw/ + +IVc. Errata + +The old DEC databooks were light on details. +The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last +register of the set CSR12-15 written.  Hmmm, now how is that possible? + +The DEC SROM format is very badly designed not precisely defined, leading to +part of the media selection junkheap below.  Some boards do not have EEPROM +media tables and need to be patched up.  Worse, other boards use the DEC +design kit media table when it is not correct for their design. + +We cannot use MII interrupts because there is no defined GPIO pin to attach +them.  The MII transceiver status is polled using an kernel timer. + +*/ + +static void *tulip_probe1(struct pci_dev *pdev, void *init_dev, +						  long ioaddr, int irq, int chip_idx, int find_cnt); +static int tulip_pwr_event(void *dev_instance, int event); + +#ifdef USE_IO_OPS +#define TULIP_IOTYPE  PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0 +#define TULIP_SIZE 0x80 +#define TULIP_SIZE1 0x100 +#else +#define TULIP_IOTYPE  PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1 +#define TULIP_SIZE   0x400		/* New PCI v2.1 recommends 4K min mem size. */ +#define TULIP_SIZE1	0x400		/* New PCI v2.1 recommends 4K min mem size. */ +#endif + +/* This much match tulip_tbl[]!  Note 21142 == 21143. */ +enum tulip_chips { +	DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, +	LC82C168, MX98713, MX98715, MX98725, AX88141, AX88140, PNIC2, COMET, +	COMPEX9881, I21145, XIRCOM, CONEXANT, +	/* These flags may be added to the chip type. */ +	HAS_VLAN=0x100, +}; + +static struct pci_id_info pci_id_tbl[] = { +	{ "Digital DC21040 Tulip", { 0x00021011, 0xffffffff }, +	  TULIP_IOTYPE, 0x80, DC21040 }, +	{ "Digital DC21041 Tulip", { 0x00141011, 0xffffffff }, +	  TULIP_IOTYPE, 0x80, DC21041 }, +	{ "Digital DS21140A Tulip", { 0x00091011, 0xffffffff, 0,0, 0x20,0xf0 }, +	  TULIP_IOTYPE, 0x80, DC21140 }, +	{ "Digital DS21140 Tulip", { 0x00091011, 0xffffffff }, +	  TULIP_IOTYPE, 0x80, DC21140 }, +	{ "Digital DS21143-xD Tulip", { 0x00191011, 0xffffffff, 0,0, 0x40,0xf0 }, +	  TULIP_IOTYPE, TULIP_SIZE, DC21142 | HAS_VLAN }, +	{ "Digital DS21143-xC Tulip", { 0x00191011, 0xffffffff, 0,0, 0x30,0xf0 }, +	  TULIP_IOTYPE, TULIP_SIZE, DC21142 }, +	{ "Digital DS21142 Tulip", { 0x00191011, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE, DC21142 }, +	{ "Kingston KNE110tx (PNIC)", +	  { 0x000211AD, 0xffffffff, 0xf0022646, 0xffffffff }, +	  TULIP_IOTYPE, 256, LC82C168 }, +	{ "Linksys LNE100TX (82c168 PNIC)",				/*  w/SYM */ +	  { 0x000211AD, 0xffffffff, 0xffff11ad, 0xffffffff, 17,0xff }, +	  TULIP_IOTYPE, 256, LC82C168 }, +	{ "Linksys LNE100TX (82c169 PNIC)",				/* w/ MII */ +	  { 0x000211AD, 0xffffffff, 0xf00311ad, 0xffffffff, 32,0xff }, +	  TULIP_IOTYPE, 256, LC82C168 }, +	{ "Lite-On 82c168 PNIC", { 0x000211AD, 0xffffffff }, +	  TULIP_IOTYPE, 256, LC82C168 }, +	{ "Macronix 98713 PMAC", { 0x051210d9, 0xffffffff }, +	  TULIP_IOTYPE, 256, MX98713 }, +	{ "Macronix 98715 PMAC", { 0x053110d9, 0xffffffff }, +	  TULIP_IOTYPE, 256, MX98715 }, +	{ "Macronix 98725 PMAC", { 0x053110d9, 0xffffffff }, +	  TULIP_IOTYPE, 256, MX98725 }, +	{ "ASIX AX88141", { 0x1400125B, 0xffffffff, 0,0, 0x10, 0xf0 }, +	  TULIP_IOTYPE, 128, AX88141 }, +	{ "ASIX AX88140", { 0x1400125B, 0xffffffff }, +	  TULIP_IOTYPE, 128, AX88140 }, +	{ "Lite-On LC82C115 PNIC-II", { 0xc11511AD, 0xffffffff }, +	  TULIP_IOTYPE, 256, PNIC2 }, +	{ "ADMtek AN981 Comet", { 0x09811317, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Centaur-P", { 0x09851317, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Centaur-C", { 0x19851317, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "D-Link DFE-680TXD v1.0 (ADMtek Centaur-C)", { 0x15411186, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Centaur-C (Linksys v2)", { 0xab0213d1, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Centaur-C (Linksys)", { 0xab0313d1, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Centaur-C (Linksys)", { 0xab0813d1, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Centaur-C (Linksys PCM200 v3)", { 0xab081737, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Centaur-C (Linksys PCM200 v3)", { 0xab091737, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "STMicro STE10/100 Comet", { 0x0981104a, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "STMicro STE10/100A Comet", { 0x2774104a, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Comet-II", { 0x95111317, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Comet-II (9513)", { 0x95131317, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "SMC1255TX (ADMtek Comet)", +	  { 0x12161113, 0xffffffff, 0x125510b8, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "Accton EN1217/EN2242 (ADMtek Comet)", { 0x12161113, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "SMC1255TX (ADMtek Comet-II)", { 0x125510b8, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "ADMtek Comet-II (model 1020)", { 0x1020111a, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "Allied Telesyn A120 (ADMtek Comet)", { 0xa1201259, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ "Compex RL100-TX", { 0x988111F6, 0xffffffff }, +	  TULIP_IOTYPE, 128, COMPEX9881 }, +	{ "Intel 21145 Tulip", { 0x00398086, 0xffffffff }, +	  TULIP_IOTYPE, 128, I21145 }, +	{ "Xircom Tulip clone", { 0x0003115d, 0xffffffff }, +	  TULIP_IOTYPE, 128, XIRCOM }, +	{ "Davicom DM9102", { 0x91021282, 0xffffffff }, +	  TULIP_IOTYPE, 0x80, DC21140 }, +	{ "Davicom DM9100", { 0x91001282, 0xffffffff }, +	  TULIP_IOTYPE, 0x80, DC21140 }, +	{ "Macronix mxic-98715 (EN1217)", { 0x12171113, 0xffffffff }, +	  TULIP_IOTYPE, 256, MX98715 }, +	{ "Conexant LANfinity", { 0x180314f1, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, CONEXANT }, +	{ "3Com 3cSOHO100B-TX (ADMtek Centaur)", { 0x930010b7, 0xffffffff }, +	  TULIP_IOTYPE, TULIP_SIZE1, COMET }, +	{ 0}, +}; + +struct drv_id_info tulip_drv_id = { +	"tulip", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	tulip_probe1, tulip_pwr_event }; + +/* This table is used during operation for capabilities and media timer. */ + +static void tulip_timer(unsigned long data); +static void nway_timer(unsigned long data); +static void mxic_timer(unsigned long data); +static void pnic_timer(unsigned long data); +static void comet_timer(unsigned long data); + +enum tbl_flag { +	HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8, +	HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */ +	HAS_PNICNWAY=0x80, HAS_NWAY=0x40,	/* Uses internal NWay xcvr. */ +	HAS_INTR_MITIGATION=0x100, IS_ASIX=0x200, HAS_8023X=0x400, +	COMET_MAC_ADDR=0x0800, +}; + +/* Note: this table must match  enum tulip_chips  above. */ +static struct tulip_chip_table { +	char *chip_name; +	int io_size;				/* Unused */ +	int valid_intrs;			/* CSR7 interrupt enable settings */ +	int flags; +	void (*media_timer)(unsigned long data); +} tulip_tbl[] = { +  { "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, +  { "Digital DC21041 Tulip", 128, 0x0001ebff, +	HAS_MEDIA_TABLE | HAS_NWAY, tulip_timer }, +  { "Digital DS21140 Tulip", 128, 0x0001ebef, +	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, +  { "Digital DS21143 Tulip", 128, 0x0801fbff, +	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY +	| HAS_INTR_MITIGATION, nway_timer }, +  { "Lite-On 82c168 PNIC", 256, 0x0001ebef, +	HAS_MII | HAS_PNICNWAY, pnic_timer }, +  { "Macronix 98713 PMAC", 128, 0x0001ebef, +	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, +  { "Macronix 98715 PMAC", 256, 0x0001ebef, +	HAS_MEDIA_TABLE, mxic_timer }, +  { "Macronix 98725 PMAC", 256, 0x0001ebef, +	HAS_MEDIA_TABLE, mxic_timer }, +  { "ASIX AX88140", 128, 0x0001fbff, +	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY | IS_ASIX, tulip_timer }, +  { "ASIX AX88141", 128, 0x0001fbff, +	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY | IS_ASIX, tulip_timer }, +  { "Lite-On PNIC-II", 256, 0x0801fbff, +	HAS_MII | HAS_NWAY | HAS_8023X, nway_timer }, +  { "ADMtek Comet", 256, 0x0001abef, +	HAS_MII | MC_HASH_ONLY | COMET_MAC_ADDR, comet_timer }, +  { "Compex 9881 PMAC", 128, 0x0001ebef, +	HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer }, +  { "Intel DS21145 Tulip", 128, 0x0801fbff, +	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY, +	nway_timer }, +  { "Xircom tulip work-alike", 128, 0x0801fbff, +	HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY, +	nway_timer }, +  { "Conexant LANfinity", 256, 0x0001ebef, +	HAS_MII | HAS_PWRDWN, tulip_timer }, +  {0}, +}; + +/* A full-duplex map for media types. */ +enum MediaIs { +	MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, +	MediaIs100=16}; +static const char media_cap[32] = +{0,0,0,16,  3,19,16,24,  27,4,7,5, 0,20,23,20,  28,31,0,0, }; +static u8 t21040_csr13[] = {2,0x0C,8,4,  4,0,0,0, 0,0,0,0, 4,0,0,0}; + +/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ +static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x6F3F, 0x6F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; +static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, }; +static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +/* Offsets to the Command and Status Registers, "CSRs".  All accesses +   must be longword instructions and quadword aligned. */ +enum tulip_offsets { +	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, +	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, +	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 }; + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { +	TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, +	NormalIntr=0x10000, AbnormalIntr=0x8000, PCIBusError=0x2000, +	RxJabber=0x200, RxStopped=0x100, RxNoBuf=0x80, RxIntr=0x40, +	TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, +}; + +/* The configuration bits in CSR6. */ +enum csr6_mode_bits { +	TxOn=0x2000, RxOn=0x0002, FullDuplex=0x0200, +	AcceptBroadcast=0x0100, AcceptAllMulticast=0x0080, +	AcceptAllPhys=0x0040, AcceptRunt=0x0008, +}; + + +/* The Tulip Rx and Tx buffer descriptors. */ +struct tulip_rx_desc { +	s32 status; +	s32 length; +	u32 buffer1, buffer2; +}; + +struct tulip_tx_desc { +	s32 status; +	s32 length; +	u32 buffer1, buffer2;				/* We use only buffer 1.  */ +}; + +enum desc_status_bits { +	DescOwned=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, +}; + +/* Ring-wrap flag in length field, use for last ring entry. +	0x01000000 means chain on buffer2 address, +	0x02000000 means use the ring start address in CSR2/3. +   Note: Some work-alike chips do not function correctly in chained mode. +   The ASIX chip works only in chained mode. +   Thus we indicates ring mode, but always write the 'next' field for +   chained mode as well. +*/ +#define DESC_RING_WRAP 0x02000000 + +#define EEPROM_SIZE 512		/* support 256*16 EEPROMs */ + +struct medialeaf { +	u8 type; +	u8 media; +	unsigned char *leafdata; +}; + +struct mediatable { +	u16 defaultmedia; +	u8 leafcount, csr12dir;				/* General purpose pin directions. */ +	unsigned has_mii:1, has_nonmii:1, has_reset:6; +	u32 csr15dir, csr15val;				/* 21143 NWay setting. */ +	struct medialeaf mleaf[0]; +}; + +struct mediainfo { +	struct mediainfo *next; +	int info_type; +	int index; +	unsigned char *info; +}; + +#define PRIV_ALIGN	15	/* Required alignment mask */ +struct tulip_private { +	struct tulip_rx_desc rx_ring[RX_RING_SIZE]; +	struct tulip_tx_desc tx_ring[TX_RING_SIZE]; +	/* The saved addresses of Rx/Tx-in-place packet buffers. */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	struct net_device *next_module; +	void *priv_addr;			/* Unaligned address of dev->priv for kfree */ +	/* Multicast filter control. */ +	u16 setup_frame[96];		/* Pseudo-Tx frame to init address table. */ +	u32 mc_filter[2];			/* Multicast hash filter */ +	int multicast_filter_limit; +	struct pci_dev *pci_dev; +	int chip_id, revision; +	int flags; +	int max_interrupt_work; +	int msg_level; +	unsigned int csr0, csr6;			/* Current CSR0, CSR6 settings. */ +	/* Note: cache line pairing and isolation of Rx vs. Tx indicies. */ +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; +	unsigned int rx_dead:1;				/* We have no Rx buffers. */ + +	struct net_device_stats stats; +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_full:1;				/* The Tx queue is full. */ + +	/* Media selection state. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int full_duplex_lock:1; +	unsigned int fake_addr:1;			/* Multiport board faked address. */ +	unsigned int media2:4;				/* Secondary monitored media port. */ +	unsigned int medialock:1;			/* Do not sense media type. */ +	unsigned int mediasense:1;			/* Media sensing in progress. */ +	unsigned int nway:1, nwayset:1;		/* 21143 internal NWay. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	unsigned char eeprom[EEPROM_SIZE];	/* Serial EEPROM contents. */ +	struct timer_list timer;			/* Media selection timer. */ +	void (*link_change)(struct net_device *dev, int csr5); +	u16 lpar;							/* 21143 Link partner ability. */ +	u16 sym_advertise, mii_advertise;	/* NWay to-advertise. */ +	u16 advertising[4];					/* MII advertise, from SROM table. */ +	signed char phys[4], mii_cnt;		/* MII device addresses. */ +	spinlock_t mii_lock; +	struct mediatable *mtable; +	int cur_index;						/* Current media index. */ +	int saved_if_port; +}; + +static void start_link(struct net_device *dev); +static void parse_eeprom(struct net_device *dev); +static int read_eeprom(long ioaddr, int location, int addr_len); +static int mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static int tulip_open(struct net_device *dev); +/* Chip-specific media selection (timer functions prototyped above). */ +static int  check_duplex(struct net_device *dev); +static void select_media(struct net_device *dev, int startup); +static void init_media(struct net_device *dev); +static void nway_lnk_change(struct net_device *dev, int csr5); +static void nway_start(struct net_device *dev); +static void pnic_lnk_change(struct net_device *dev, int csr5); +static void pnic_do_nway(struct net_device *dev); + +static void tulip_tx_timeout(struct net_device *dev); +static void tulip_init_ring(struct net_device *dev); +static int tulip_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int tulip_rx(struct net_device *dev); +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int tulip_close(struct net_device *dev); +static struct net_device_stats *tulip_get_stats(struct net_device *dev); +#ifdef HAVE_PRIVATE_IOCTL +static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#endif +static void set_rx_mode(struct net_device *dev); + + + +/* A list of all installed Tulip devices. */ +static struct net_device *root_tulip_dev = NULL; + +static void *tulip_probe1(struct pci_dev *pdev, void *init_dev, +						  long ioaddr, int irq, int pci_tbl_idx, int find_cnt) +{ +	struct net_device *dev; +	struct tulip_private *tp; +	void *priv_mem; +	/* See note below on the multiport cards. */ +	static unsigned char last_phys_addr[6] = {0x02, 'L', 'i', 'n', 'u', 'x'}; +	static int last_irq = 0; +	static int multiport_cnt = 0;		/* For four-port boards w/one EEPROM */ +	u8 chip_rev; +	int i, chip_idx = pci_id_tbl[pci_tbl_idx].drv_flags & 0xff; +	unsigned short sum; +	u8 ee_data[EEPROM_SIZE]; + +	/* Bring the 21041/21143 out of sleep mode. +	   Caution: Snooze mode does not work with some boards! */ +	if (tulip_tbl[chip_idx].flags & HAS_PWRDWN) +		pci_write_config_dword(pdev, 0x40, 0x00000000); + +	if (inl(ioaddr + CSR5) == 0xffffffff) { +		printk(KERN_ERR "The Tulip chip at %#lx is not functioning.\n", ioaddr); +		return 0; +	} + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	/* Make certain the data structures are quadword aligned. */ +	priv_mem = kmalloc(sizeof(*tp) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; +	dev->priv = tp = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(tp, 0, sizeof(*tp)); +	tp->mii_lock = (spinlock_t) SPIN_LOCK_UNLOCKED; +	tp->priv_addr = priv_mem; + +	tp->next_module = root_tulip_dev; +	root_tulip_dev = dev; + +	pci_read_config_byte(pdev, PCI_REVISION_ID, &chip_rev); + +	printk(KERN_INFO "%s: %s rev %d at %#3lx,", +		   dev->name, pci_id_tbl[pci_tbl_idx].name, chip_rev, ioaddr); + +	/* Stop the Tx and Rx processes. */ +	outl(inl(ioaddr + CSR6) & ~TxOn & ~RxOn, ioaddr + CSR6); +	/* Clear the missed-packet counter. */ +	inl(ioaddr + CSR8); + +	if (chip_idx == DC21041  &&  inl(ioaddr + CSR9) & 0x8000) { +		printk(" 21040 compatible mode,"); +		chip_idx = DC21040; +	} + +	/* The SROM/EEPROM interface varies dramatically. */ +	sum = 0; +	if (chip_idx == DC21040) { +		outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */ +		for (i = 0; i < 6; i++) { +			int value, boguscnt = 100000; +			do +				value = inl(ioaddr + CSR9); +			while (value < 0  && --boguscnt > 0); +			dev->dev_addr[i] = value; +			sum += value & 0xff; +		} +	} else if (chip_idx == LC82C168) { +		for (i = 0; i < 3; i++) { +			int value, boguscnt = 100000; +			outl(0x600 | i, ioaddr + 0x98); +			do +				value = inl(ioaddr + CSR9); +			while (value < 0  && --boguscnt > 0); +			put_unaligned(le16_to_cpu(value), ((u16*)dev->dev_addr) + i); +			sum += value & 0xffff; +		} +	} else if (chip_idx == COMET) { +		/* No need to read the EEPROM. */ +		put_unaligned(le32_to_cpu(inl(ioaddr + 0xA4)), (u32 *)dev->dev_addr); +		put_unaligned(le16_to_cpu(inl(ioaddr + 0xA8)), +					  (u16 *)(dev->dev_addr + 4)); +		for (i = 0; i < 6; i ++) +			sum += dev->dev_addr[i]; +	} else { +		/* A serial EEPROM interface, we read now and sort it out later. */ +		int sa_offset = 0; +		int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; +		int eeprom_word_cnt = 1 << ee_addr_size; + +		for (i = 0; i < eeprom_word_cnt; i++) +			((u16 *)ee_data)[i] = +				le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size)); + +		/* DEC now has a specification (see Notes) but early board makers +		   just put the address in the first EEPROM locations. */ +		/* This does  memcmp(eedata, eedata+16, 8) */ +		for (i = 0; i < 8; i ++) +			if (ee_data[i] != ee_data[16+i]) +				sa_offset = 20; +		if (chip_idx == CONEXANT) { +			/* Check that the tuple type and length is correct. */ +			if (ee_data[0x198] == 0x04  &&  ee_data[0x199] == 6) +				sa_offset = 0x19A; +		} else if (ee_data[0] == 0xff  &&  ee_data[1] == 0xff && +				   ee_data[2] == 0) { +			sa_offset = 2;		/* Grrr, damn Matrox boards. */ +			multiport_cnt = 4; +		} +		for (i = 0; i < 6; i ++) { +			dev->dev_addr[i] = ee_data[i + sa_offset]; +			sum += ee_data[i + sa_offset]; +		} +	} +	/* Lite-On boards have the address byte-swapped. */ +	if ((dev->dev_addr[0] == 0xA0  ||  dev->dev_addr[0] == 0xC0) +		&&  dev->dev_addr[1] == 0x00) +		for (i = 0; i < 6; i+=2) { +			char tmp = dev->dev_addr[i]; +			dev->dev_addr[i] = dev->dev_addr[i+1]; +			dev->dev_addr[i+1] = tmp; +		} +	/* On the Zynx 315 Etherarray and other multiport boards only the +	   first Tulip has an EEPROM. +	   The addresses of the subsequent ports are derived from the first. +	   Many PCI BIOSes also incorrectly report the IRQ line, so we correct +	   that here as well. */ +	if (sum == 0  || sum == 6*0xff) { +		printk(" EEPROM not present,"); +		for (i = 0; i < 5; i++) +			dev->dev_addr[i] = last_phys_addr[i]; +		dev->dev_addr[i] = last_phys_addr[i] + 1; +#if defined(__i386__)		/* Patch up x86 BIOS bug. */ +		if (last_irq) +			irq = last_irq; +#endif +	} + +	for (i = 0; i < 6; i++) +		printk("%c%2.2X", i ? ':' : ' ', last_phys_addr[i] = dev->dev_addr[i]); +	printk(", IRQ %d.\n", irq); +	last_irq = irq; + +#ifdef USE_IO_OPS +	/* We do a request_region() to register /proc/ioports info. */ +	request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name); +#endif + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	tp->pci_dev = pdev; +	tp->msg_level = (1 << debug) - 1; +	tp->chip_id = chip_idx; +	tp->revision = chip_rev; +	tp->flags = tulip_tbl[chip_idx].flags +		| (pci_id_tbl[pci_tbl_idx].drv_flags & 0xffffff00); +	tp->rx_copybreak = rx_copybreak; +	tp->max_interrupt_work = max_interrupt_work; +	tp->multicast_filter_limit = multicast_filter_limit; +	tp->csr0 = csr0; + +	/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. +	   And the ASIX must have a burst limit or horrible things happen. */ +	if (chip_idx == DC21143  &&  chip_rev == 65) +		tp->csr0 &= ~0x01000000; +	else if (tp->flags & IS_ASIX) +		tp->csr0 |= 0x2000; + +	/* We support a zillion ways to set the media type. */ +#ifdef TULIP_FULL_DUPLEX +	tp->full_duplex = 1; +	tp->full_duplex_lock = 1; +#endif +#ifdef TULIP_DEFAULT_MEDIA +	tp->default_port = TULIP_DEFAULT_MEDIA; +#endif +#ifdef TULIP_NO_MEDIA_SWITCH +	tp->medialock = 1; +#endif + +	/* The lower four bits are the media type. */ +	if (find_cnt >= 0  &&  find_cnt < MAX_UNITS) { +		if (options[find_cnt] & 0x1f) +			tp->default_port = options[find_cnt] & 0x1f; +		if ((options[find_cnt] & 0x200) || full_duplex[find_cnt] > 0) +			tp->full_duplex = 1; +		if (mtu[find_cnt] > 0) +			dev->mtu = mtu[find_cnt]; +	} +	if (dev->mem_start) +		tp->default_port = dev->mem_start & 0x1f; +	if (tp->default_port) { +		printk(KERN_INFO "%s: Transceiver selection forced to %s.\n", +			   dev->name, medianame[tp->default_port & MEDIA_MASK]); +		tp->medialock = 1; +		if (media_cap[tp->default_port] & MediaAlwaysFD) +			tp->full_duplex = 1; +	} +	if (tp->full_duplex) +		tp->full_duplex_lock = 1; + +	if (media_cap[tp->default_port] & MediaIsMII) { +		u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; +		tp->mii_advertise = media2advert[tp->default_port - 9]; +		tp->mii_advertise |= (tp->flags & HAS_8023X); /* Matching bits! */ +	} + +	/* This is logically part of probe1(), but too complex to write inline. */ +	if (tp->flags & HAS_MEDIA_TABLE) { +		memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom)); +		parse_eeprom(dev); +	} + +	/* The Tulip-specific entries in the device structure. */ +	dev->open = &tulip_open; +	dev->hard_start_xmit = &tulip_start_xmit; +	dev->stop = &tulip_close; +	dev->get_stats = &tulip_get_stats; +#ifdef HAVE_PRIVATE_IOCTL +	dev->do_ioctl = &private_ioctl; +#endif +#ifdef HAVE_MULTICAST +	dev->set_multicast_list = &set_rx_mode; +#endif + +	if (tp->flags & HAS_NWAY) +		tp->link_change = nway_lnk_change; +	else if (tp->flags & HAS_PNICNWAY) +		tp->link_change = pnic_lnk_change; +	start_link(dev); +	if (chip_idx == COMET) { +		/* Set the Comet LED configuration. */ +		outl(0xf0000000, ioaddr + CSR9); +	} + +	return dev; +} + +/* Start the link, typically called at probe1() time but sometimes later with +   multiport cards. */ +static void start_link(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	if ((tp->flags & ALWAYS_CHECK_MII) || +		(tp->mtable  &&  tp->mtable->has_mii) || +		( ! tp->mtable  &&  (tp->flags & HAS_MII))) { +		int phyn, phy_idx = 0; +		if (tp->mtable  &&  tp->mtable->has_mii) { +			for (i = 0; i < tp->mtable->leafcount; i++) +				if (tp->mtable->mleaf[i].media == 11) { +					tp->cur_index = i; +					tp->saved_if_port = dev->if_port; +					select_media(dev, 2); +					dev->if_port = tp->saved_if_port; +					break; +				} +		} +		/* Find the connected MII xcvrs. +		   Doing this in open() would allow detecting external xcvrs later, +		   but takes much time. */ +		for (phyn = 1; phyn <= 32 && phy_idx < sizeof(tp->phys); phyn++) { +			int phy = phyn & 0x1f; +			int mii_status = mdio_read(dev, phy, 1); +			if ((mii_status & 0x8301) == 0x8001 || +				((mii_status & 0x8000) == 0  && (mii_status & 0x7800) != 0)) { +				int mii_reg0 = mdio_read(dev, phy, 0); +				int mii_advert = mdio_read(dev, phy, 4); +				int to_advert; + +				if (tp->mii_advertise) +					to_advert = tp->mii_advertise; +				else if (tp->advertising[phy_idx]) +					to_advert = tp->advertising[phy_idx]; +				else			/* Leave unchanged. */ +					tp->mii_advertise = to_advert = mii_advert; + +				tp->phys[phy_idx++] = phy; +				printk(KERN_INFO "%s:  MII transceiver #%d " +					   "config %4.4x status %4.4x advertising %4.4x.\n", +					   dev->name, phy, mii_reg0, mii_status, mii_advert); +				/* Fixup for DLink with miswired PHY. */ +				if (mii_advert != to_advert) { +					printk(KERN_DEBUG "%s:  Advertising %4.4x on PHY %d," +						   " previously advertising %4.4x.\n", +						   dev->name, to_advert, phy, mii_advert); +					mdio_write(dev, phy, 4, to_advert); +				} +				/* Enable autonegotiation: some boards default to off. */ +				mdio_write(dev, phy, 0, (mii_reg0 & ~0x3000) | +						   (tp->full_duplex ? 0x0100 : 0x0000) | +						   ((media_cap[tp->default_port] & MediaIs100) ? +							0x2000 : 0x1000)); +			} +		} +		tp->mii_cnt = phy_idx; +		if (tp->mtable  &&  tp->mtable->has_mii  &&  phy_idx == 0) { +			printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", +				   dev->name); +			tp->phys[0] = 1; +		} +	} + +	/* Reset the xcvr interface and turn on heartbeat. */ +	switch (tp->chip_id) { +	case DC21040: +		outl(0x00000000, ioaddr + CSR13); +		outl(0x00000004, ioaddr + CSR13); +		break; +	case DC21041: +		/* This is nway_start(). */ +		if (tp->sym_advertise == 0) +			tp->sym_advertise = 0x0061; +		outl(0x00000000, ioaddr + CSR13); +		outl(0xFFFFFFFF, ioaddr + CSR14); +		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ +		outl(inl(ioaddr + CSR6) | FullDuplex, ioaddr + CSR6); +		outl(0x0000EF01, ioaddr + CSR13); +		break; +	case DC21140: default: +		if (tp->mtable) +			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); +		break; +	case DC21142: +	case PNIC2: +		if (tp->mii_cnt  ||  media_cap[dev->if_port] & MediaIsMII) { +			outl(0x82020000, ioaddr + CSR6); +			outl(0x0000, ioaddr + CSR13); +			outl(0x0000, ioaddr + CSR14); +			outl(0x820E0000, ioaddr + CSR6); +		} else +			nway_start(dev); +		break; +	case LC82C168: +		if ( ! tp->mii_cnt) { +			tp->nway = 1; +			tp->nwayset = 0; +			outl(0x00420000, ioaddr + CSR6); +			outl(0x30, ioaddr + CSR12); +			outl(0x0001F078, ioaddr + 0xB8); +			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ +		} +		break; +	case COMPEX9881: +		outl(0x00000000, ioaddr + CSR6); +		outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ +		outl(0x00000001, ioaddr + CSR13); +		break; +	case MX98713: case MX98715: case MX98725: +		outl(0x01a80000, ioaddr + CSR6); +		outl(0xFFFFFFFF, ioaddr + CSR14); +		outl(0x00001000, ioaddr + CSR12); +		break; +	case COMET: +		break; +	} + +	if (tp->flags & HAS_PWRDWN) +		pci_write_config_dword(tp->pci_dev, 0x40, 0x40000000); +} + + +/* Serial EEPROM section. */ +/* The main routine to parse the very complicated SROM structure. +   Search www.digital.com for "21X4 SROM" to get details. +   This code is very complex, and will require changes to support +   additional cards, so I will be verbose about what is going on. +   */ + +/* Known cards that have old-style EEPROMs. +   Writing this table is described at +   http://www.scyld.com/network/tulip-media.html +*/ +static struct fixups { +  char *name; +  unsigned char addr0, addr1, addr2; +  u16 newtable[32];				/* Max length below. */ +} eeprom_fixups[] = { +  {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, +						  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, +  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f, +							   0x0000, 0x009E, /* 10baseT */ +							   0x0004, 0x009E, /* 10baseT-FD */ +							   0x0903, 0x006D, /* 100baseTx */ +							   0x0905, 0x006D, /* 100baseTx-FD */ }}, +  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f, +								 0x0107, 0x8021, /* 100baseFx */ +								 0x0108, 0x8021, /* 100baseFx-FD */ +								 0x0100, 0x009E, /* 10baseT */ +								 0x0104, 0x009E, /* 10baseT-FD */ +								 0x0103, 0x006D, /* 100baseTx */ +								 0x0105, 0x006D, /* 100baseTx-FD */ }}, +  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513, +							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/ +							   0x0000, 0x009E, /* 10baseT */ +							   0x0004, 0x009E, /* 10baseT-FD */ +							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ +							   0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}}, +  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F, +								  0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */ +								  0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */ +								  0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */ +								  0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ +								  0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */ +   }}, +  {0, 0, 0, 0, {}}}; + +static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", + "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; + +#if defined(__i386__)			/* AKA get_unaligned() */ +#define get_u16(ptr) (*(u16 *)(ptr)) +#else +#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8)) +#endif + +static void parse_eeprom(struct net_device *dev) +{ +	/* The last media info list parsed, for multiport boards.  */ +	static struct mediatable *last_mediatable = NULL; +	static unsigned char *last_ee_data = NULL; +	static int controller_index = 0; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	unsigned char *p, *ee_data = tp->eeprom; +	int new_advertise = 0; +	int i; + +	tp->mtable = 0; +	/* Detect an old-style (SA only) EEPROM layout: +	   memcmp(eedata, eedata+16, 8). */ +	for (i = 0; i < 8; i ++) +		if (ee_data[i] != ee_data[16+i]) +			break; +	if (i >= 8) { +		if (ee_data[0] == 0xff) { +			if (last_mediatable) { +				controller_index++; +				printk(KERN_INFO "%s:  Controller %d of multiport board.\n", +					   dev->name, controller_index); +				tp->mtable = last_mediatable; +				ee_data = last_ee_data; +				goto subsequent_board; +			} else +				printk(KERN_INFO "%s:  Missing EEPROM, this interface may " +					   "not work correctly!\n", +					   dev->name); +			return; +		} +		/* Do a fix-up based on the vendor half of the station address. */ +		for (i = 0; eeprom_fixups[i].name; i++) { +			if (dev->dev_addr[0] == eeprom_fixups[i].addr0 +				&&  dev->dev_addr[1] == eeprom_fixups[i].addr1 +				&&  dev->dev_addr[2] == eeprom_fixups[i].addr2) { +				if (dev->dev_addr[2] == 0xE8  &&  ee_data[0x1a] == 0x55) +					i++;		/* An Accton EN1207, not an outlaw Maxtech. */ +				memcpy(ee_data + 26, eeprom_fixups[i].newtable, +					   sizeof(eeprom_fixups[i].newtable)); +				printk(KERN_INFO "%s: Old format EEPROM on '%s' board.\n" +					   KERN_INFO "%s: Using substitute media control info.\n", +					   dev->name, eeprom_fixups[i].name, dev->name); +				break; +			} +		} +		if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ +			printk(KERN_INFO "%s: Old style EEPROM with no media selection " +				   "information.\n", +				   dev->name); +			return; +		} +	} + +	controller_index = 0; +	if (ee_data[19] > 1) { +		struct net_device *prev_dev; +		struct tulip_private *otp; +		/* This is a multiport board.  The probe order may be "backwards", so +		   we patch up already found devices. */ +		last_ee_data = ee_data; +		for (prev_dev = tp->next_module; prev_dev; prev_dev = otp->next_module) { +			otp = (struct tulip_private *)prev_dev->priv; +			if (otp->eeprom[0] == 0xff  &&  otp->mtable == 0) { +				parse_eeprom(prev_dev); +				start_link(prev_dev); +			} else +				break; +		} +		controller_index = 0; +	} +subsequent_board: + +	p = (void *)ee_data + ee_data[27 + controller_index*3]; +	if (ee_data[27] == 0) {		/* No valid media table. */ +	} else if (tp->chip_id == DC21041) { +		int media = get_u16(p); +		int count = p[2]; +		p += 3; + +		printk(KERN_INFO "%s: 21041 Media table, default media %4.4x (%s).\n", +			   dev->name, media, +			   media & 0x0800 ? "Autosense" : medianame[media & MEDIA_MASK]); +		for (i = 0; i < count; i++) { +			unsigned char media_block = *p++; +			int media_code = media_block & MEDIA_MASK; +			if (media_block & 0x40) +				p += 6; +			switch(media_code) { +			case 0: new_advertise |= 0x0020; break; +			case 4: new_advertise |= 0x0040; break; +			} +			printk(KERN_INFO "%s:  21041 media #%d, %s.\n", +				   dev->name, media_code, medianame[media_code]); +		} +	} else { +		unsigned char csr12dir = 0; +		int count; +		struct mediatable *mtable; +		u16 media = get_u16(p); + +		p += 2; +		if (tp->flags & CSR12_IN_SROM) +			csr12dir = *p++; +		count = *p++; +		mtable = (struct mediatable *) +			kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf), +					GFP_KERNEL); +		if (mtable == NULL) +			return;				/* Horrible, impossible failure. */ +		last_mediatable = tp->mtable = mtable; +		mtable->defaultmedia = media; +		mtable->leafcount = count; +		mtable->csr12dir = csr12dir; +		mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0; +		mtable->csr15dir = mtable->csr15val = 0; + +		printk(KERN_INFO "%s:  EEPROM default media type %s.\n", dev->name, +			   media & 0x0800 ? "Autosense" : medianame[media & MEDIA_MASK]); +		for (i = 0; i < count; i++) { +			struct medialeaf *leaf = &mtable->mleaf[i]; + +			if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ +				leaf->type = 0; +				leaf->media = p[0] & 0x3f; +				leaf->leafdata = p; +				if ((p[2] & 0x61) == 0x01)	/* Bogus, but Znyx boards do it. */ +					mtable->has_mii = 1; +				p += 4; +			} else { +				switch(leaf->type = p[1]) { +				case 5: +					mtable->has_reset = i + 1; /* Assure non-zero */ +					/* Fall through */ +				case 6: +					leaf->media = 31; +					break; +				case 1: case 3: +					mtable->has_mii = 1; +					leaf->media = 11; +					break; +				case 2: +					if ((p[2] & 0x3f) == 0) { +						u32 base15 = (p[2] & 0x40) ? get_u16(p + 7) : 0x0008; +						u16 *p1 = (u16 *)(p + (p[2] & 0x40 ? 9 : 3)); +						mtable->csr15dir = (get_unaligned(p1 + 0)<<16) + base15; +						mtable->csr15val = (get_unaligned(p1 + 1)<<16) + base15; +					} +					/* Fall through. */ +				case 0: case 4: +					mtable->has_nonmii = 1; +					leaf->media = p[2] & MEDIA_MASK; +					switch (leaf->media) { +					case 0: new_advertise |= 0x0020; break; +					case 4: new_advertise |= 0x0040; break; +					case 3: new_advertise |= 0x0080; break; +					case 5: new_advertise |= 0x0100; break; +					case 6: new_advertise |= 0x0200; break; +					} +					break; +				default: +					leaf->media = 19; +				} +				leaf->leafdata = p + 2; +				p += (p[0] & 0x3f) + 1; +			} +			if ((tp->msg_level & NETIF_MSG_LINK) && +				leaf->media == 11) { +				unsigned char *bp = leaf->leafdata; +				printk(KERN_INFO "%s:  MII interface PHY %d, setup/reset " +					   "sequences %d/%d long, capabilities %2.2x %2.2x.\n", +					   dev->name, bp[0], bp[1], bp[2 + bp[1]*2], +					   bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); +			} +			if (tp->msg_level & NETIF_MSG_PROBE) +				printk(KERN_INFO "%s:  Index #%d - Media %s (#%d) described " +					   "by a %s (%d) block.\n", +					   dev->name, i, medianame[leaf->media], leaf->media, +					   leaf->type < 6 ? block_name[leaf->type] : "UNKNOWN", +					   leaf->type); +		} +		if (new_advertise) +			tp->sym_advertise = new_advertise; +	} +} +/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/ + +/*  EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */ +#define EE_CS			0x01	/* EEPROM chip select. */ +#define EE_DATA_WRITE	0x04	/* Data from the Tulip to EEPROM. */ +#define EE_WRITE_0		0x01 +#define EE_WRITE_1		0x05 +#define EE_DATA_READ	0x08	/* Data from the EEPROM chip. */ +#define EE_ENB			(0x4800 | EE_CS) + +/* Delay between EEPROM clock transitions. +   Even at 33Mhz current PCI implementations do not overrun the EEPROM clock. +   We add a bus turn-around to insure that this remains true. */ +#define eeprom_delay()	inl(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_READ_CMD		(6) + +/* Note: this routine returns extra data bits for size detection. */ +static int read_eeprom(long ioaddr, int location, int addr_len) +{ +	int i; +	unsigned retval = 0; +	long ee_addr = ioaddr + CSR9; +	int read_cmd = location | (EE_READ_CMD << addr_len); + +	outl(EE_ENB & ~EE_CS, ee_addr); +	outl(EE_ENB, ee_addr); + +	/* Shift the read command bits out. */ +	for (i = 4 + addr_len; i >= 0; i--) { +		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; +		outl(EE_ENB | dataval, ee_addr); +		eeprom_delay(); +		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); +		eeprom_delay(); +		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); +	} +	outl(EE_ENB, ee_addr); +	eeprom_delay(); + +	for (i = 16; i > 0; i--) { +		outl(EE_ENB | EE_SHIFT_CLK, ee_addr); +		eeprom_delay(); +		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); +		outl(EE_ENB, ee_addr); +		eeprom_delay(); +	} + +	/* Terminate the EEPROM access. */ +	outl(EE_ENB & ~EE_CS, ee_addr); +	return retval; +} + +/* MII transceiver control section. +   Read and write the MII registers using software-generated serial +   MDIO protocol.  See the MII specifications or DP83840A data sheet +   for details. */ + +/* The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually +   met by back-to-back PCI I/O cycles, but we insert a delay to avoid +   "overclocking" issues or future 66Mhz PCI. */ +#define mdio_delay() inl(mdio_addr) + +/* Read and write the MII registers using software-generated serial +   MDIO protocol.  It is just different enough from the EEPROM protocol +   to not share code.  The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK	0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB		0x00000		/* Ignore the 0x02000 databook setting. */ +#define MDIO_ENB_IN		0x40000 +#define MDIO_DATA_READ	0x80000 + +static const unsigned char comet_miireg2offset[32] = { +	0xB4, 0xB8, 0xBC, 0xC0,  0xC4, 0xC8, 0xCC, 0,  0,0,0,0,  0,0,0,0, +	0,0xD0,0,0,  0,0,0,0,  0,0,0,0, 0, 0xD4, 0xD8, 0xDC, }; + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	int i; +	int read_cmd = (0xf6 << 10) | ((phy_id & 0x1f) << 5) | location; +	int retval = 0; +	long ioaddr = dev->base_addr; +	long mdio_addr = ioaddr + CSR9; +	unsigned long flags; + +	if (location & ~0x1f) +		return 0xffff; + +	if (tp->chip_id == COMET  &&  phy_id == 30) { +		if (comet_miireg2offset[location]) +			return inl(ioaddr + comet_miireg2offset[location]); +		return 0xffff; +	} + +	spin_lock_irqsave(&tp->mii_lock, flags); +	if (tp->chip_id == LC82C168) { +		int i = 1000; +		outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); +		inl(ioaddr + 0xA0); +		inl(ioaddr + 0xA0); +		inl(ioaddr + 0xA0); +		inl(ioaddr + 0xA0); +		while (--i > 0) +			if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) +				break; +		spin_unlock_irqrestore(&tp->mii_lock, flags); +		return retval & 0xffff; +	} + +	/* Establish sync by sending at least 32 logic ones. */ +	for (i = 32; i >= 0; i--) { +		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); +		mdio_delay(); +		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	/* Shift the read command bits out. */ +	for (i = 15; i >= 0; i--) { +		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + +		outl(MDIO_ENB | dataval, mdio_addr); +		mdio_delay(); +		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	/* Read the two transition, 16 data, and wire-idle bits. */ +	for (i = 19; i > 0; i--) { +		outl(MDIO_ENB_IN, mdio_addr); +		mdio_delay(); +		retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); +		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	spin_unlock_irqrestore(&tp->mii_lock, flags); +	return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int location, int val) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	int i; +	int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | (val & 0xffff); +	long ioaddr = dev->base_addr; +	long mdio_addr = ioaddr + CSR9; +	unsigned long flags; + +	if (location & ~0x1f) +		return; + +	if (tp->chip_id == COMET  &&  phy_id == 30) { +		if (comet_miireg2offset[location]) +			outl(val, ioaddr + comet_miireg2offset[location]); +		return; +	} + +	spin_lock_irqsave(&tp->mii_lock, flags); +	if (tp->chip_id == LC82C168) { +		int i = 1000; +		outl(cmd, ioaddr + 0xA0); +		do +			if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) +				break; +		while (--i > 0); +		spin_unlock_irqrestore(&tp->mii_lock, flags); +		return; +	} + +	/* Establish sync by sending 32 logic ones. */ +	for (i = 32; i >= 0; i--) { +		outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); +		mdio_delay(); +		outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	/* Shift the command bits out. */ +	for (i = 31; i >= 0; i--) { +		int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; +		outl(MDIO_ENB | dataval, mdio_addr); +		mdio_delay(); +		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	/* Clear out extra bits. */ +	for (i = 2; i > 0; i--) { +		outl(MDIO_ENB_IN, mdio_addr); +		mdio_delay(); +		outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); +		mdio_delay(); +	} +	spin_unlock_irqrestore(&tp->mii_lock, flags); +	return; +} + + +static int +tulip_open(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 3*HZ; + +	/* Wake the chip from sleep/snooze mode. */ +	if (tp->flags & HAS_PWRDWN) +		pci_write_config_dword(tp->pci_dev, 0x40, 0); + +	/* On some chip revs we must set the MII/SYM port before the reset!? */ +	if (tp->mii_cnt  ||  (tp->mtable  &&  tp->mtable->has_mii)) +		outl(0x00040000, ioaddr + CSR6); + +	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ +	outl(0x00000001, ioaddr + CSR0); + +	MOD_INC_USE_COUNT; + +	/* This would be done after interrupts are initialized, but we do not want +	   to frob the transceiver only to fail later. */ +	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	/* Deassert reset. +	   Wait the specified 50 PCI cycles after a reset by initializing +	   Tx and Rx queues and the address filter list. */ +	outl(tp->csr0, ioaddr + CSR0); + +	if (tp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq); + +	tulip_init_ring(dev); + +	if (tp->chip_id == PNIC2) { +		u32 addr_high = (dev->dev_addr[1]<<8) + (dev->dev_addr[0]<<0); +		/* This address setting does not appear to impact chip operation?? */ +		outl((dev->dev_addr[5]<<8) + dev->dev_addr[4] + +			 (dev->dev_addr[3]<<24) + (dev->dev_addr[2]<<16), +			 ioaddr + 0xB0); +		outl(addr_high + (addr_high<<16), ioaddr + 0xB8); +	} +	if (tp->flags & MC_HASH_ONLY) { +		u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr)); +		u32 addr_high = cpu_to_le16(get_unaligned((u16 *)(dev->dev_addr+4))); +		if (tp->flags & IS_ASIX) { +			outl(0, ioaddr + CSR13); +			outl(addr_low,  ioaddr + CSR14); +			outl(1, ioaddr + CSR13); +			outl(addr_high, ioaddr + CSR14); +		} else if (tp->flags & COMET_MAC_ADDR) { +			outl(addr_low,  ioaddr + 0xA4); +			outl(addr_high, ioaddr + 0xA8); +			outl(0, ioaddr + 0xAC); +			outl(0, ioaddr + 0xB0); +		} +	} + +	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); +	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); + +	if ( ! tp->full_duplex_lock) +		tp->full_duplex = 0; +	init_media(dev); +	if (media_cap[dev->if_port] & MediaIsMII) +		check_duplex(dev); +	set_rx_mode(dev); + +	/* Start the Tx to process setup frame. */ +	outl(tp->csr6, ioaddr + CSR6); +	outl(tp->csr6 | TxOn, ioaddr + CSR6); + +	netif_start_tx_queue(dev); + +	/* Enable interrupts by setting the interrupt mask. */ +	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); +	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); +	outl(tp->csr6 | TxOn | RxOn, ioaddr + CSR6); +	outl(0, ioaddr + CSR2);		/* Rx poll demand */ + +	if (tp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 " +			   "%8.8x.\n", dev->name, (int)inl(ioaddr + CSR0), +			   (int)inl(ioaddr + CSR5), (int)inl(ioaddr + CSR6)); + +	/* Set the timer to switch to check for link beat and perhaps switch +	   to an alternate media type. */ +	init_timer(&tp->timer); +	tp->timer.expires = jiffies + next_tick; +	tp->timer.data = (unsigned long)dev; +	tp->timer.function = tulip_tbl[tp->chip_id].media_timer; +	add_timer(&tp->timer); + +	return 0; +} + +static void init_media(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	tp->saved_if_port = dev->if_port; +	if (dev->if_port == 0) +		dev->if_port = tp->default_port; + +	/* Allow selecting a default media. */ +	i = 0; +	if (tp->mtable == NULL) +		goto media_picked; +	if (dev->if_port) { +		int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 : +			(dev->if_port == 12 ? 0 : dev->if_port); +		for (i = 0; i < tp->mtable->leafcount; i++) +			if (tp->mtable->mleaf[i].media == looking_for) { +				printk(KERN_INFO "%s: Using user-specified media %s.\n", +					   dev->name, medianame[dev->if_port]); +				goto media_picked; +			} +	} +	if ((tp->mtable->defaultmedia & 0x0800) == 0) { +		int looking_for = tp->mtable->defaultmedia & MEDIA_MASK; +		for (i = 0; i < tp->mtable->leafcount; i++) +			if (tp->mtable->mleaf[i].media == looking_for) { +				printk(KERN_INFO "%s: Using EEPROM-set media %s.\n", +					   dev->name, medianame[looking_for]); +				goto media_picked; +			} +	} +	/* Start sensing first non-full-duplex media. */ +	for (i = tp->mtable->leafcount - 1; +		 (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) +		; +media_picked: + +	tp->csr6 = 0; +	tp->cur_index = i; +	tp->nwayset = 0; + +	if (dev->if_port) { +		if (tp->chip_id == DC21143  && +			(media_cap[dev->if_port] & MediaIsMII)) { +			/* We must reset the media CSRs when we force-select MII mode. */ +			outl(0x0000, ioaddr + CSR13); +			outl(0x0000, ioaddr + CSR14); +			outl(0x0008, ioaddr + CSR15); +		} +		select_media(dev, 1); +		return; +	} +	switch(tp->chip_id) { +	case DC21041: +		/* tp->nway = 1;*/ +		nway_start(dev); +		break; +	case DC21142: +		if (tp->mii_cnt) { +			select_media(dev, 1); +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_INFO "%s: Using MII transceiver %d, status " +					   "%4.4x.\n", +					   dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1)); +			outl(0x82020000, ioaddr + CSR6); +			tp->csr6 = 0x820E0000; +			dev->if_port = 11; +			outl(0x0000, ioaddr + CSR13); +			outl(0x0000, ioaddr + CSR14); +		} else +			nway_start(dev); +		break; +	case PNIC2: +		nway_start(dev); +		break; +	case LC82C168: +		if (tp->mii_cnt) { +			dev->if_port = 11; +			tp->csr6 = 0x814C0000 | (tp->full_duplex ? FullDuplex : 0); +			outl(0x0001, ioaddr + CSR15); +		} else if (inl(ioaddr + CSR5) & TPLnkPass) +			pnic_do_nway(dev); +		else { +			/* Start with 10mbps to do autonegotiation. */ +			outl(0x32, ioaddr + CSR12); +			tp->csr6 = 0x00420000; +			outl(0x0001B078, ioaddr + 0xB8); +			outl(0x0201B078, ioaddr + 0xB8); +		} +		break; +	case MX98713: case COMPEX9881: +		dev->if_port = 0; +		tp->csr6 = 0x01880000 | (tp->full_duplex ? FullDuplex : 0); +		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); +		break; +	case MX98715: case MX98725: +		/* Provided by BOLO, Macronix - 12/10/1998. */ +		dev->if_port = 0; +		tp->csr6 = 0x01a80000 | FullDuplex; +		outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); +		outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0); +		break; +	case COMET: case CONEXANT: +		/* Enable automatic Tx underrun recovery. */ +		outl(inl(ioaddr + 0x88) | 1, ioaddr + 0x88); +		dev->if_port = tp->mii_cnt ? 11 : 0; +		tp->csr6 = 0x00040000; +		break; +	case AX88140: case AX88141: +		tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100; +		break; +	default: +		select_media(dev, 1); +	} +} + +/* Set up the transceiver control registers for the selected media type. +   STARTUP indicates to reset the transceiver.  It is set to '2' for +   the initial card detection, and '1' during resume or open(). +*/ +static void select_media(struct net_device *dev, int startup) +{ +	long ioaddr = dev->base_addr; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	struct mediatable *mtable = tp->mtable; +	u32 new_csr6; +	int i; + +	if (mtable) { +		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; +		unsigned char *p = mleaf->leafdata; +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s:  Media table type %d.\n", +				   dev->name, mleaf->type); +		switch (mleaf->type) { +		case 0:					/* 21140 non-MII xcvr. */ +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver" +					   " with control setting %2.2x.\n", +					   dev->name, p[1]); +			dev->if_port = p[0]; +			if (startup) +				outl(mtable->csr12dir | 0x100, ioaddr + CSR12); +			outl(p[1], ioaddr + CSR12); +			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); +			break; +		case 2: case 4: { +			u16 setup[5]; +			u32 csr13val, csr14val, csr15dir, csr15val; +			for (i = 0; i < 5; i++) +				setup[i] = get_u16(&p[i*2 + 1]); + +			dev->if_port = p[0] & MEDIA_MASK; +			if (media_cap[dev->if_port] & MediaAlwaysFD) +				tp->full_duplex = 1; + +			if (startup && mtable->has_reset) { +				struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset-1]; +				unsigned char *rst = rleaf->leafdata; +				if (tp->msg_level & NETIF_MSG_LINK) +					printk(KERN_DEBUG "%s: Resetting the transceiver.\n", +						   dev->name); +				for (i = 0; i < rst[0]; i++) +					outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15); +			} +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_DEBUG "%s: 21143 non-MII %s transceiver control " +					   "%4.4x/%4.4x.\n", +					   dev->name, medianame[dev->if_port], setup[0], setup[1]); +			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */ +				csr13val = setup[0]; +				csr14val = setup[1]; +				csr15dir = (setup[3]<<16) | setup[2]; +				csr15val = (setup[4]<<16) | setup[2]; +				outl(0, ioaddr + CSR13); +				outl(csr14val, ioaddr + CSR14); +				outl(csr15dir, ioaddr + CSR15);	/* Direction */ +				outl(csr15val, ioaddr + CSR15);	/* Data */ +				outl(csr13val, ioaddr + CSR13); +			} else { +				csr13val = 1; +				csr14val = 0x0003FFFF; +				csr15dir = (setup[0]<<16) | 0x0008; +				csr15val = (setup[1]<<16) | 0x0008; +				if (dev->if_port <= 4) +					csr14val = t21142_csr14[dev->if_port]; +				if (startup) { +					outl(0, ioaddr + CSR13); +					outl(csr14val, ioaddr + CSR14); +				} +				outl(csr15dir, ioaddr + CSR15);	/* Direction */ +				outl(csr15val, ioaddr + CSR15);	/* Data */ +				if (startup) outl(csr13val, ioaddr + CSR13); +			} +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_DEBUG "%s:  Setting CSR15 to %8.8x/%8.8x.\n", +					   dev->name, csr15dir, csr15val); +			if (mleaf->type == 4) +				new_csr6 = 0x820A0000 | ((setup[2] & 0x71) << 18); +			else +				new_csr6 = 0x82420000; +			break; +		} +		case 1: case 3: { +			int phy_num = p[0]; +			int init_length = p[1]; +			u16 *misc_info; + +			dev->if_port = 11; +			new_csr6 = 0x020E0000; +			if (mleaf->type == 3) {	/* 21142 */ +				u16 *init_sequence = (u16*)(p+2); +				u16 *reset_sequence = &((u16*)(p+3))[init_length]; +				int reset_length = p[2 + init_length*2]; +				misc_info = reset_sequence + reset_length; +				if (startup) +					for (i = 0; i < reset_length; i++) +						outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); +				for (i = 0; i < init_length; i++) +					outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); +			} else { +				u8 *init_sequence = p + 2; +				u8 *reset_sequence = p + 3 + init_length; +				int reset_length = p[2 + init_length]; +				misc_info = (u16*)(reset_sequence + reset_length); +				if (startup) { +					outl(mtable->csr12dir | 0x100, ioaddr + CSR12); +					for (i = 0; i < reset_length; i++) +						outl(reset_sequence[i], ioaddr + CSR12); +				} +				for (i = 0; i < init_length; i++) +					outl(init_sequence[i], ioaddr + CSR12); +			} +			tp->advertising[phy_num] = get_u16(&misc_info[1]) | 1; +			if (startup < 2) { +				if (tp->mii_advertise == 0) +					tp->mii_advertise = tp->advertising[phy_num]; +				if (tp->msg_level & NETIF_MSG_LINK) +					printk(KERN_DEBUG "%s:  Advertising %4.4x on MII %d.\n", +						   dev->name, tp->mii_advertise, tp->phys[phy_num]); +				mdio_write(dev, tp->phys[phy_num], 4, tp->mii_advertise); +			} +			break; +		} +		default: +			printk(KERN_DEBUG "%s:  Invalid media table selection %d.\n", +					   dev->name, mleaf->type); +			new_csr6 = 0x020E0000; +		} +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n", +				   dev->name, medianame[dev->if_port], +				   (int)inl(ioaddr + CSR12) & 0xff); +	} else if (tp->chip_id == DC21041) { +		int port = dev->if_port <= 4 ? dev->if_port : 0; +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", +				   dev->name, medianame[port == 3 ? 12: port], +				   (int)inl(ioaddr + CSR12)); +		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ +		outl(t21041_csr14[port], ioaddr + CSR14); +		outl(t21041_csr15[port], ioaddr + CSR15); +		outl(t21041_csr13[port], ioaddr + CSR13); +		new_csr6 = 0x80020000; +	} else if (tp->chip_id == LC82C168) { +		if (startup && ! tp->medialock) +			dev->if_port = tp->mii_cnt ? 11 : 0; +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, media %s.\n", +				   dev->name, (int)inl(ioaddr + 0xB8), +				   medianame[dev->if_port]); +		if (tp->mii_cnt) { +			new_csr6 = 0x810C0000; +			outl(0x0001, ioaddr + CSR15); +			outl(0x0201B07A, ioaddr + 0xB8); +		} else if (startup) { +			/* Start with 10mbps to do autonegotiation. */ +			outl(0x32, ioaddr + CSR12); +			new_csr6 = 0x00420000; +			outl(0x0001B078, ioaddr + 0xB8); +			outl(0x0201B078, ioaddr + 0xB8); +		} else if (dev->if_port == 3  ||  dev->if_port == 5) { +			outl(0x33, ioaddr + CSR12); +			new_csr6 = 0x01860000; +			/* Trigger autonegotiation. */ +			outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8); +		} else { +			outl(0x32, ioaddr + CSR12); +			new_csr6 = 0x00420000; +			outl(0x1F078, ioaddr + 0xB8); +		} +	} else if (tp->chip_id == DC21040) {					/* 21040 */ +		/* Turn on the xcvr interface. */ +		int csr12 = inl(ioaddr + CSR12); +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: 21040 media type is %s, CSR12 is %2.2x.\n", +				   dev->name, medianame[dev->if_port], csr12); +		if (media_cap[dev->if_port] & MediaAlwaysFD) +			tp->full_duplex = 1; +		new_csr6 = 0x20000; +		/* Set the full duplux match frame. */ +		outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); +		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ +		if (t21040_csr13[dev->if_port] & 8) { +			outl(0x0705, ioaddr + CSR14); +			outl(0x0006, ioaddr + CSR15); +		} else { +			outl(0xffff, ioaddr + CSR14); +			outl(0x0000, ioaddr + CSR15); +		} +		outl(0x8f01 | t21040_csr13[dev->if_port], ioaddr + CSR13); +	} else {					/* Unknown chip type with no media table. */ +		if (tp->default_port == 0) +			dev->if_port = tp->mii_cnt ? 11 : 3; +		if (media_cap[dev->if_port] & MediaIsMII) { +			new_csr6 = 0x020E0000; +		} else if (media_cap[dev->if_port] & MediaIsFx) { +			new_csr6 = 0x02860000; +		} else +			new_csr6 = 0x038E0000; +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: No media description table, assuming " +				   "%s transceiver, CSR12 %2.2x.\n", +				   dev->name, medianame[dev->if_port], +				   (int)inl(ioaddr + CSR12)); +	} + +	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | +		(tp->full_duplex ? FullDuplex : 0); +	return; +} + +/* +  Check the MII negotiated duplex, and change the CSR6 setting if +  required. +  Return 0 if everything is OK. +  Return < 0 if the transceiver is missing or has no link beat. +  */ +static int check_duplex(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	int mii_reg1, mii_reg5, negotiated, duplex; + +	if (tp->full_duplex_lock) +		return 0; +	mii_reg5 = mdio_read(dev, tp->phys[0], 5); +	negotiated = mii_reg5 & tp->mii_advertise; + +	if (tp->msg_level & NETIF_MSG_TIMER) +		printk(KERN_INFO "%s: MII link partner %4.4x, negotiated %4.4x.\n", +			   dev->name, mii_reg5, negotiated); +	if (mii_reg5 == 0xffff) +		return -2; +	if ((mii_reg5 & 0x4000) == 0  &&			/* No negotiation. */ +		((mii_reg1 = mdio_read(dev, tp->phys[0], 1)) & 0x0004) == 0) { +		int new_reg1 = mdio_read(dev, tp->phys[0], 1); +		if ((new_reg1 & 0x0004) == 0) { +			if (tp->msg_level & NETIF_MSG_TIMER) +				printk(KERN_INFO "%s: No link beat on the MII interface," +					   " status %4.4x.\n", dev->name, new_reg1); +			return -1; +		} +	} +	duplex = ((negotiated & 0x0300) == 0x0100 +			  || (negotiated & 0x00C0) == 0x0040); +	/* 100baseTx-FD  or  10T-FD, but not 100-HD */ +	if (tp->full_duplex != duplex) { +		tp->full_duplex = duplex; +		if (negotiated & 0x0380)	/* 100mbps. */ +			tp->csr6 &= ~0x00400000; +		if (tp->full_duplex) tp->csr6 |= FullDuplex; +		else				 tp->csr6 &= ~FullDuplex; +		outl(tp->csr6 | RxOn, ioaddr + CSR6); +		outl(tp->csr6 | TxOn | RxOn, ioaddr + CSR6); +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Setting %s-duplex based on MII " +				   "#%d link partner capability of %4.4x.\n", +				   dev->name, tp->full_duplex ? "full" : "half", +				   tp->phys[0], mii_reg5); +		return 1; +	} +	return 0; +} + +static void tulip_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u32 csr12 = inl(ioaddr + CSR12); +	int next_tick = 2*HZ; + +	if (tp->msg_level & NETIF_MSG_TIMER) +		printk(KERN_DEBUG "%s: Media selection tick, %s, status %8.8x mode" +			   " %8.8x SIA %8.8x %8.8x %8.8x %8.8x.\n", +			   dev->name, medianame[dev->if_port], (int)inl(ioaddr + CSR5), +			   (int)inl(ioaddr + CSR6), csr12, (int)inl(ioaddr + CSR13), +			   (int)inl(ioaddr + CSR14), (int)inl(ioaddr + CSR15)); + +	switch (tp->chip_id) { +	case DC21040: +		if (!tp->medialock  &&  (csr12 & 0x0002)) { /* Network error */ +			if (tp->msg_level & NETIF_MSG_TIMER) +				printk(KERN_INFO "%s: No link beat found.\n", +					   dev->name); +			dev->if_port = (dev->if_port == 2 ? 0 : 2); +			select_media(dev, 0); +			dev->trans_start = jiffies; +		} +		break; +	case DC21041: +		if (tp->msg_level & NETIF_MSG_TIMER) +			printk(KERN_DEBUG "%s: 21041 media tick  CSR12 %8.8x.\n", +				   dev->name, csr12); +		if (tp->medialock) break; +		switch (dev->if_port) { +		case 0: case 3: case 4: +		  if (csr12 & 0x0004) { /*LnkFail */ +			/* 10baseT is dead.  Check for activity on alternate port. */ +			tp->mediasense = 1; +			if (csr12 & 0x0200) +				dev->if_port = 2; +			else +				dev->if_port = 1; +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_INFO "%s: No 21041 10baseT link beat, Media " +					   "switched to %s.\n", +					   dev->name, medianame[dev->if_port]); +			outl(0, ioaddr + CSR13); /* Reset */ +			outl(t21041_csr14[dev->if_port], ioaddr + CSR14); +			outl(t21041_csr15[dev->if_port], ioaddr + CSR15); +			outl(t21041_csr13[dev->if_port], ioaddr + CSR13); +			next_tick = 10*HZ;			/* 2.4 sec. */ +		  } else +			next_tick = 30*HZ; +		  break; +		case 1:					/* 10base2 */ +		case 2:					/* AUI */ +			if (csr12 & 0x0100) { +				next_tick = (30*HZ);			/* 30 sec. */ +				tp->mediasense = 0; +			} else if ((csr12 & 0x0004) == 0) { +				if (tp->msg_level & NETIF_MSG_LINK) +					printk(KERN_INFO "%s: 21041 media switched to 10baseT.\n", +						   dev->name); +				dev->if_port = 0; +				select_media(dev, 0); +				next_tick = (24*HZ)/10;				/* 2.4 sec. */ +			} else if (tp->mediasense || (csr12 & 0x0002)) { +				dev->if_port = 3 - dev->if_port; /* Swap ports. */ +				select_media(dev, 0); +				next_tick = 20*HZ; +			} else { +				next_tick = 20*HZ; +			} +			break; +		} +		break; +	case DC21140:  case DC21142: case MX98713: case COMPEX9881: default: { +		struct medialeaf *mleaf; +		unsigned char *p; +		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */ +			/* Not much that can be done. +			   Assume this a generic MII or SYM transceiver. */ +			next_tick = 60*HZ; +			if (tp->msg_level & NETIF_MSG_TIMER) +				printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x " +					   "CSR12 0x%2.2x.\n", +					   dev->name, (int)inl(ioaddr + CSR6), csr12 & 0xff); +			break; +		} +		mleaf = &tp->mtable->mleaf[tp->cur_index]; +		p = mleaf->leafdata; +		switch (mleaf->type) { +		case 0: case 4: { +			/* Type 0 serial or 4 SYM transceiver.  Check the link beat bit. */ +			int offset = mleaf->type == 4 ? 5 : 2; +			s8 bitnum = p[offset]; +			if (p[offset+1] & 0x80) { +				if (tp->msg_level & NETIF_MSG_TIMER) +					printk(KERN_DEBUG"%s: Transceiver monitor tick " +						   "CSR12=%#2.2x, no media sense.\n", +						   dev->name, csr12); +				if (mleaf->type == 4) { +					if (mleaf->media == 3 && (csr12 & 0x02)) +						goto select_next_media; +				} +				break; +			} +			if (tp->msg_level & NETIF_MSG_TIMER) +				printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x" +					   " bit %d is %d, expecting %d.\n", +					   dev->name, csr12, (bitnum >> 1) & 7, +					   (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, +					   (bitnum >= 0)); +			/* Check that the specified bit has the proper value. */ +			if ((bitnum < 0) != +				((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { +				if (tp->msg_level & NETIF_MSG_LINK) +					printk(KERN_DEBUG "%s: Link beat detected for %s.\n", +						   dev->name, medianame[mleaf->media & MEDIA_MASK]); +				if ((p[2] & 0x61) == 0x01)	/* Bogus Znyx board. */ +					goto actually_mii; +				break; +			} +			if (tp->medialock) +				break; +	  select_next_media: +			if (--tp->cur_index < 0) { +				/* We start again, but should instead look for default. */ +				tp->cur_index = tp->mtable->leafcount - 1; +			} +			dev->if_port = tp->mtable->mleaf[tp->cur_index].media; +			if (media_cap[dev->if_port] & MediaIsFD) +				goto select_next_media; /* Skip FD entries. */ +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_DEBUG "%s: No link beat on media %s," +					   " trying transceiver type %s.\n", +					   dev->name, medianame[mleaf->media & MEDIA_MASK], +					   medianame[tp->mtable->mleaf[tp->cur_index].media]); +			select_media(dev, 0); +			/* Restart the transmit process. */ +			outl(tp->csr6 | RxOn, ioaddr + CSR6); +			outl(tp->csr6 | TxOn | RxOn, ioaddr + CSR6); +			next_tick = (24*HZ)/10; +			break; +		} +		case 1:  case 3:		/* 21140, 21142 MII */ +		actually_mii: +			check_duplex(dev); +			next_tick = 60*HZ; +			break; +		case 2:					/* 21142 serial block has no link beat. */ +		default: +			break; +		} +	} +	break; +	} +	tp->timer.expires = jiffies + next_tick; +	add_timer(&tp->timer); +} + +/* Handle internal NWay transceivers uniquely. +   These exist on the 21041, 21143 (in SYM mode) and the PNIC2. +   */ +static void nway_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int csr12 = inl(ioaddr + CSR12); +	int next_tick = 60*HZ; +	int new_csr6 = 0; + +	if (tp->msg_level & NETIF_MSG_TIMER) +		printk(KERN_INFO"%s: N-Way autonegotiation status %8.8x, %s.\n", +			   dev->name, csr12, medianame[dev->if_port]); +	if (media_cap[dev->if_port] & MediaIsMII) { +		check_duplex(dev); +	} else if (tp->nwayset) { +		/* Do not screw up a negotiated session! */ +		if (tp->msg_level & NETIF_MSG_TIMER) +			printk(KERN_INFO"%s: Using NWay-set %s media, csr12 %8.8x.\n", +				   dev->name, medianame[dev->if_port], csr12); +	} else if (tp->medialock) { +			; +	} else if (dev->if_port == 3) { +		if (csr12 & 2) {	/* No 100mbps link beat, revert to 10mbps. */ +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_INFO"%s: No 21143 100baseTx link beat, %8.8x, " +					   "trying NWay.\n", dev->name, csr12); +			nway_start(dev); +			next_tick = 3*HZ; +		} +	} else if ((csr12 & 0x7000) != 0x5000) { +		/* Negotiation failed.  Search media types. */ +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO"%s: 21143 negotiation failed, status %8.8x.\n", +				   dev->name, csr12); +		if (!(csr12 & 4)) {		/* 10mbps link beat good. */ +			new_csr6 = 0x82420000; +			dev->if_port = 0; +			outl(0, ioaddr + CSR13); +			outl(0x0003FFFF, ioaddr + CSR14); +			outw(t21142_csr15[dev->if_port], ioaddr + CSR15); +			outl(t21142_csr13[dev->if_port], ioaddr + CSR13); +		} else { +			/* Select 100mbps port to check for link beat. */ +			new_csr6 = 0x83860000; +			dev->if_port = 3; +			outl(0, ioaddr + CSR13); +			outl(0x0003FF7F, ioaddr + CSR14); +			outw(8, ioaddr + CSR15); +			outl(1, ioaddr + CSR13); +		} +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO"%s: Testing new 21143 media %s.\n", +				   dev->name, medianame[dev->if_port]); +		if (new_csr6 != (tp->csr6 & ~0x20D7)) { +			tp->csr6 &= 0x20D7; +			tp->csr6 |= new_csr6; +			outl(0x0301, ioaddr + CSR12); +			outl(tp->csr6 | RxOn, ioaddr + CSR6); +			outl(tp->csr6 | TxOn | RxOn, ioaddr + CSR6); +		} +		next_tick = 3*HZ; +	} +	if (tp->cur_tx - tp->dirty_tx > 0  && +		jiffies - dev->trans_start > TX_TIMEOUT) { +		printk(KERN_WARNING "%s: Tx hung, %d vs. %d.\n", +			   dev->name, tp->cur_tx, tp->dirty_tx); +		tulip_tx_timeout(dev); +	} + +	tp->timer.expires = jiffies + next_tick; +	add_timer(&tp->timer); +} + +static void nway_start(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int csr14 = ((tp->sym_advertise & 0x0780) << 9)  | +		((tp->sym_advertise&0x0020)<<1) | 0xffbf; + +	dev->if_port = 0; +	tp->nway = tp->mediasense = 1; +	tp->nwayset = tp->lpar = 0; +	if (tp->chip_id == PNIC2) { +		tp->csr6 = 0x01000000 | (tp->sym_advertise & 0x0040 ? FullDuplex : 0); +		return; +	} +	if (tp->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Restarting internal NWay autonegotiation, " +			   "%8.8x.\n", dev->name, csr14); +	outl(0x0001, ioaddr + CSR13); +	outl(csr14, ioaddr + CSR14); +	tp->csr6 = 0x82420000 | (tp->sym_advertise & 0x0040 ? FullDuplex : 0) +		| (tp->csr6 & 0x20ff); +	outl(tp->csr6, ioaddr + CSR6); +	if (tp->mtable  &&  tp->mtable->csr15dir) { +		outl(tp->mtable->csr15dir, ioaddr + CSR15); +		outl(tp->mtable->csr15val, ioaddr + CSR15); +	} else if (tp->chip_id != PNIC2) +		outw(0x0008, ioaddr + CSR15); +	if (tp->chip_id == DC21041)			/* Trigger NWAY. */ +		outl(0xEF01, ioaddr + CSR12); +	else +		outl(0x1301, ioaddr + CSR12); +} + +static void nway_lnk_change(struct net_device *dev, int csr5) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int csr12 = inl(ioaddr + CSR12); + +	if (tp->chip_id == PNIC2) { +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO"%s: PNIC-2 link status changed, CSR5/12/14 %8.8x" +				   " %8.8x, %8.8x.\n", +				   dev->name, csr12, csr5, (int)inl(ioaddr + CSR14)); +		dev->if_port = 5; +		tp->lpar = csr12 >> 16; +		tp->nwayset = 1; +		tp->csr6 = 0x01000000 | (tp->csr6 & 0xffff); +		outl(tp->csr6, ioaddr + CSR6); +		return; +	} +	if (tp->msg_level & NETIF_MSG_LINK) +		printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, " +			   "%8.8x.\n", dev->name, csr12, csr5, (int)inl(ioaddr + CSR14)); + +	/* If NWay finished and we have a negotiated partner capability. */ +	if (tp->nway  &&  !tp->nwayset  &&  (csr12 & 0x7000) == 0x5000) { +		int setup_done = 0; +		int negotiated = tp->sym_advertise & (csr12 >> 16); +		tp->lpar = csr12 >> 16; +		tp->nwayset = 1; +		if (negotiated & 0x0100)		dev->if_port = 5; +		else if (negotiated & 0x0080)	dev->if_port = 3; +		else if (negotiated & 0x0040)	dev->if_port = 4; +		else if (negotiated & 0x0020)	dev->if_port = 0; +		else { +			tp->nwayset = 0; +			if ((csr12 & 2) == 0  &&  (tp->sym_advertise & 0x0180)) +				dev->if_port = 3; +		} +		tp->full_duplex = (media_cap[dev->if_port] & MediaAlwaysFD) ? 1:0; + +		if (tp->msg_level & NETIF_MSG_LINK) { +			if (tp->nwayset) +				printk(KERN_INFO "%s: Switching to %s based on link " +					   "negotiation %4.4x & %4.4x = %4.4x.\n", +					   dev->name, medianame[dev->if_port], tp->sym_advertise, +					   tp->lpar, negotiated); +			else +				printk(KERN_INFO "%s: Autonegotiation failed, using %s," +					   " link beat status %4.4x.\n", +					   dev->name, medianame[dev->if_port], csr12); +		} + +		if (tp->mtable) { +			int i; +			for (i = 0; i < tp->mtable->leafcount; i++) +				if (tp->mtable->mleaf[i].media == dev->if_port) { +					tp->cur_index = i; +					select_media(dev, 0); +					setup_done = 1; +					break; +				} +		} +		if ( ! setup_done) { +			tp->csr6 = (dev->if_port & 1 ? 0x838E0000 : 0x82420000) +				| (tp->csr6 & 0x20ff); +			if (tp->full_duplex) +				tp->csr6 |= FullDuplex; +			outl(1, ioaddr + CSR13); +		} +#if 0							/* Restart should not be needed. */ +		outl(tp->csr6 | 0x0000, ioaddr + CSR6); +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s:  Restarting Tx and Rx, CSR5 is %8.8x.\n", +				   dev->name, inl(ioaddr + CSR5)); +#endif +		outl(tp->csr6 | TxOn | RxOn, ioaddr + CSR6); +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s:  Setting CSR6 %8.8x/%x CSR12 %8.8x.\n", +				   dev->name, tp->csr6, (int)inl(ioaddr + CSR6), +				   (int)inl(ioaddr + CSR12)); +	} else if ((tp->nwayset  &&  (csr5 & 0x08000000) +				&& (dev->if_port == 3  ||  dev->if_port == 5) +				&& (csr12 & 2) == 2) || +			   (tp->nway && (csr5 & (TPLnkFail)))) { +		/* Link blew? Maybe restart NWay. */ +		del_timer(&tp->timer); +		nway_start(dev); +		tp->timer.expires = jiffies + 3*HZ; +		add_timer(&tp->timer); +	} else if (dev->if_port == 3  ||  dev->if_port == 5) { +		if (tp->msg_level & NETIF_MSG_LINK)	/* TIMER? */ +			printk(KERN_INFO"%s: 21143 %s link beat %s.\n", +				   dev->name, medianame[dev->if_port], +				   (csr12 & 2) ? "failed" : "good"); +		if ((csr12 & 2)  &&  ! tp->medialock) { +			del_timer(&tp->timer); +			nway_start(dev); +			tp->timer.expires = jiffies + 3*HZ; +			add_timer(&tp->timer); +		} else if (dev->if_port == 5) +			outl(inl(ioaddr + CSR14) & ~0x080, ioaddr + CSR14); +	} else if (dev->if_port == 0  ||  dev->if_port == 4) { +		if ((csr12 & 4) == 0) +			printk(KERN_INFO"%s: 21143 10baseT link beat good.\n", +				   dev->name); +	} else if (!(csr12 & 4)) {		/* 10mbps link beat good. */ +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO"%s: 21143 10mbps sensed media.\n", +				   dev->name); +		dev->if_port = 0; +	} else if (tp->nwayset) { +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO"%s: 21143 using NWay-set %s, csr6 %8.8x.\n", +				   dev->name, medianame[dev->if_port], tp->csr6); +	} else {		/* 100mbps link beat good. */ +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO"%s: 21143 100baseTx sensed media.\n", +				   dev->name); +		dev->if_port = 3; +		tp->csr6 = 0x838E0000 | (tp->csr6 & 0x20ff); +		outl(0x0003FF7F, ioaddr + CSR14); +		outl(0x0301, ioaddr + CSR12); +		outl(tp->csr6 | RxOn, ioaddr + CSR6); +		outl(tp->csr6 | RxOn | TxOn, ioaddr + CSR6); +	} +} + +static void mxic_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 60*HZ; + +	if (tp->msg_level & NETIF_MSG_TIMER) { +		printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name, +			   (int)inl(ioaddr + CSR12)); +	} +	tp->timer.expires = jiffies + next_tick; +	add_timer(&tp->timer); +} + +static void pnic_do_nway(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u32 phy_reg = inl(ioaddr + 0xB8); +	u32 new_csr6 = tp->csr6 & ~0x40C40200; + +	if (phy_reg & 0x78000000) { /* Ignore baseT4 */ +		if (phy_reg & 0x20000000)		dev->if_port = 5; +		else if (phy_reg & 0x40000000)	dev->if_port = 3; +		else if (phy_reg & 0x10000000)	dev->if_port = 4; +		else if (phy_reg & 0x08000000)	dev->if_port = 0; +		tp->nwayset = 1; +		new_csr6 = (dev->if_port & 1) ? 0x01860000 : 0x00420000; +		outl(0x32 | (dev->if_port & 1), ioaddr + CSR12); +		if (dev->if_port & 1) +			outl(0x1F868, ioaddr + 0xB8); +		if (phy_reg & 0x30000000) { +			tp->full_duplex = 1; +			new_csr6 |= FullDuplex; +		} +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_DEBUG "%s: PNIC autonegotiated status %8.8x, %s.\n", +				   dev->name, phy_reg, medianame[dev->if_port]); +		if (tp->csr6 != new_csr6) { +			tp->csr6 = new_csr6; +			outl(tp->csr6 | RxOn, ioaddr + CSR6);	/* Restart Tx */ +			outl(tp->csr6 | TxOn | RxOn, ioaddr + CSR6); +			dev->trans_start = jiffies; +		} +	} +} + +static void pnic_lnk_change(struct net_device *dev, int csr5) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int phy_reg = inl(ioaddr + 0xB8); + +	if (tp->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: PNIC link changed state %8.8x, CSR5 %8.8x.\n", +			   dev->name, phy_reg, csr5); +	if (inl(ioaddr + CSR5) & TPLnkFail) { +		outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7); +		if (! tp->nwayset  ||  jiffies - dev->trans_start > 1*HZ) { +			tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff); +			outl(tp->csr6, ioaddr + CSR6); +			outl(0x30, ioaddr + CSR12); +			outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ +			dev->trans_start = jiffies; +		} +	} else if (inl(ioaddr + CSR5) & TPLnkPass) { +		pnic_do_nway(dev); +		outl((inl(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7); +	} +} +static void pnic_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 60*HZ; + +	if (media_cap[dev->if_port] & MediaIsMII) { +		if (check_duplex(dev) > 0) +			next_tick = 3*HZ; +	} else { +		int csr12 = inl(ioaddr + CSR12); +		int new_csr6 = tp->csr6 & ~0x40C40200; +		int phy_reg = inl(ioaddr + 0xB8); +		int csr5 = inl(ioaddr + CSR5); + +		if (tp->msg_level & NETIF_MSG_TIMER) +			printk(KERN_DEBUG "%s: PNIC timer PHY status %8.8x, %s " +				   "CSR5 %8.8x.\n", +				   dev->name, phy_reg, medianame[dev->if_port], csr5); +		if (phy_reg & 0x04000000) {	/* Remote link fault */ +			outl(0x0201F078, ioaddr + 0xB8); +			next_tick = 1*HZ; +			tp->nwayset = 0; +		} else if (phy_reg & 0x78000000) { /* Ignore baseT4 */ +			pnic_do_nway(dev); +			next_tick = 60*HZ; +		} else if (csr5 & TPLnkFail) { /* 100baseTx link beat */ +			if (tp->msg_level & NETIF_MSG_LINK) +				printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " +					   "CSR5 %8.8x, PHY %3.3x.\n", +					   dev->name, medianame[dev->if_port], csr12, +					   (int)inl(ioaddr + CSR5), (int)inl(ioaddr + 0xB8)); +			next_tick = 3*HZ; +			if (tp->medialock) { +			} else if (tp->nwayset  &&  (dev->if_port & 1)) { +				next_tick = 1*HZ; +			} else if (dev->if_port == 0) { +				dev->if_port = 3; +				outl(0x33, ioaddr + CSR12); +				new_csr6 = 0x01860000; +				outl(0x1F868, ioaddr + 0xB8); +			} else { +				dev->if_port = 0; +				outl(0x32, ioaddr + CSR12); +				new_csr6 = 0x00420000; +				outl(0x1F078, ioaddr + 0xB8); +			} +			if (tp->csr6 != new_csr6) { +				tp->csr6 = new_csr6; +				outl(tp->csr6 | RxOn, ioaddr + CSR6);	/* Restart Tx */ +				outl(tp->csr6 | RxOn | TxOn, ioaddr + CSR6); +				dev->trans_start = jiffies; +				if (tp->msg_level & NETIF_MSG_LINK) +					printk(KERN_INFO "%s: Changing PNIC configuration to %s " +						   "%s-duplex, CSR6 %8.8x.\n", +						   dev->name, medianame[dev->if_port], +						   tp->full_duplex ? "full" : "half", new_csr6); +			} +		} +	} +	tp->timer.expires = jiffies + next_tick; +	add_timer(&tp->timer); +} + +static void comet_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	int next_tick = 60*HZ; + +	if (tp->msg_level & NETIF_MSG_TIMER) +		printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability " +			   "%4.4x.\n", +			   dev->name, mdio_read(dev, tp->phys[0], 1), +			   mdio_read(dev, tp->phys[0], 5)); +	check_duplex(dev); +	tp->timer.expires = jiffies + next_tick; +	add_timer(&tp->timer); +} + +static void tulip_tx_timeout(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (media_cap[dev->if_port] & MediaIsMII) { +		/* Do nothing -- the media monitor should handle this. */ +		int mii_bmsr = mdio_read(dev, tp->phys[0], 1); +		if (tp->msg_level & NETIF_MSG_LINK) +			printk(KERN_WARNING "%s: Transmit timeout using MII device," +				   " status %4.4x.\n", +				   dev->name, mii_bmsr); +		if ( ! (mii_bmsr & 0x0004)) {		/* No link beat present */ +			dev->trans_start = jiffies; +			netif_link_down(dev); +			return; +		} +	} else switch (tp->chip_id) { +	case DC21040: +		if ( !tp->medialock  &&  inl(ioaddr + CSR12) & 0x0002) { +			dev->if_port = (dev->if_port == 2 ? 0 : 2); +			printk(KERN_INFO "%s: transmit timed out, switching to " +				   "%s.\n", +				   dev->name, medianame[dev->if_port]); +			select_media(dev, 0); +		} +		dev->trans_start = jiffies; +		return;					/* Note: not break! */ +	case DC21041: { +		int csr12 = inl(ioaddr + CSR12); + +		printk(KERN_WARNING "%s: 21041 transmit timed out, status %8.8x, " +			   "CSR12 %8.8x, CSR13 %8.8x, CSR14 %8.8x, resetting...\n", +			   dev->name, (int)inl(ioaddr + CSR5), csr12, +			   (int)inl(ioaddr + CSR13), (int)inl(ioaddr + CSR14)); +		tp->mediasense = 1; +		if ( ! tp->medialock) { +			if (dev->if_port == 1 || dev->if_port == 2) +				dev->if_port = (csr12 & 0x0004) ? 2 - dev->if_port : 0; +			else +				dev->if_port = 1; +			select_media(dev, 0); +		} +		break; +	} +	case DC21142: +		if (tp->nwayset) { +			printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, " +				   "SIA %8.8x %8.8x %8.8x %8.8x, restarting NWay .\n", +				   dev->name, (int)inl(ioaddr + CSR5), +				   (int)inl(ioaddr + CSR12), (int)inl(ioaddr + CSR13), +				   (int)inl(ioaddr + CSR14), (int)inl(ioaddr + CSR15)); +			nway_start(dev); +			break; +		} +		/* Fall through. */ +	case DC21140: case MX98713: case COMPEX9881: +		printk(KERN_WARNING "%s: %s transmit timed out, status %8.8x, " +			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", +			   dev->name, tulip_tbl[tp->chip_id].chip_name, +			   (int)inl(ioaddr + CSR5), (int)inl(ioaddr + CSR12), +			   (int)inl(ioaddr + CSR13), (int)inl(ioaddr + CSR14), +			   (int)inl(ioaddr + CSR15)); +		if ( ! tp->medialock  &&  tp->mtable) { +			do +				--tp->cur_index; +			while (tp->cur_index >= 0 +				   && (media_cap[tp->mtable->mleaf[tp->cur_index].media] +					   & MediaIsFD)); +			if (tp->cur_index < 0) { +				/* We start again, but should instead look for default. */ +				tp->cur_index = tp->mtable->leafcount - 1; +			} +			select_media(dev, 0); +			printk(KERN_WARNING "%s: transmit timed out, switching to %s " +				   "media.\n", dev->name, medianame[dev->if_port]); +		} +		break; +	case PNIC2: +		printk(KERN_WARNING "%s: PNIC2 transmit timed out, status %8.8x, " +			   "CSR6/7 %8.8x / %8.8x CSR12 %8.8x, resetting...\n", +			   dev->name, (int)inl(ioaddr + CSR5), (int)inl(ioaddr + CSR6), +			   (int)inl(ioaddr + CSR7), (int)inl(ioaddr + CSR12)); +		break; +	default: +		printk(KERN_WARNING "%s: Transmit timed out, status %8.8x, CSR12 " +			   "%8.8x, resetting...\n", +			   dev->name, (int)inl(ioaddr + CSR5), (int)inl(ioaddr + CSR12)); +	} + +#if defined(way_too_many_messages)  &&  defined(__i386__) +	if (tp->msg_level & NETIF_MSG_TXERR) { +		int i; +		for (i = 0; i < RX_RING_SIZE; i++) { +			u8 *buf = (u8 *)(tp->rx_ring[i].buffer1); +			int j; +			printk(KERN_DEBUG "%2d: %8.8x %8.8x %8.8x %8.8x  " +				   "%2.2x %2.2x %2.2x.\n", +				   i, (unsigned int)tp->rx_ring[i].status, +				   (unsigned int)tp->rx_ring[i].length, +				   (unsigned int)tp->rx_ring[i].buffer1, +				   (unsigned int)tp->rx_ring[i].buffer2, +				   buf[0], buf[1], buf[2]); +			for (j = 0; buf[j] != 0xee && j < 1600; j++) +				if (j < 100) printk(" %2.2x", buf[j]); +			printk(" j=%d.\n", j); +		} +		printk(KERN_DEBUG "  Rx ring %8.8x: ", (int)tp->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)tp->rx_ring[i].status); +		printk("\n" KERN_DEBUG "  Tx ring %8.8x: ", (int)tp->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %8.8x", (unsigned int)tp->tx_ring[i].status); +		printk("\n"); +	} +#endif + +	/* Stop and restart the Tx process. +	   The pwr_event approach of empty/init_rings() may be better... */ +	outl(tp->csr6 | RxOn, ioaddr + CSR6); +	outl(tp->csr6 | RxOn | TxOn, ioaddr + CSR6); +	/* Trigger an immediate transmit demand. */ +	outl(0, ioaddr + CSR1); +	outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); + +	dev->trans_start = jiffies; +	tp->stats.tx_errors++; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void tulip_init_ring(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	int i; + +	tp->rx_dead = tp->tx_full = 0; +	tp->cur_rx = tp->cur_tx = 0; +	tp->dirty_rx = tp->dirty_tx = 0; + +	tp->rx_buf_sz = dev->mtu + 18; +	if (tp->rx_buf_sz < PKT_BUF_SZ) +		tp->rx_buf_sz = PKT_BUF_SZ; + +	for (i = 0; i < RX_RING_SIZE; i++) { +		tp->rx_ring[i].status = 0x00000000; +		tp->rx_ring[i].length = cpu_to_le32(tp->rx_buf_sz); +		tp->rx_ring[i].buffer2 = virt_to_le32desc(&tp->rx_ring[i+1]); +		tp->rx_skbuff[i] = NULL; +	} +	/* Mark the last entry as wrapping the ring. */ +	tp->rx_ring[i-1].length |= cpu_to_le32(DESC_RING_WRAP); +	tp->rx_ring[i-1].buffer2 = virt_to_le32desc(&tp->rx_ring[0]); + +	for (i = 0; i < RX_RING_SIZE; i++) { +		/* Note the receive buffer must be longword aligned. +		   dev_alloc_skb() provides 16 byte alignment.  But do *not* +		   use skb_reserve() to align the IP header! */ +		struct sk_buff *skb = dev_alloc_skb(tp->rx_buf_sz); +		tp->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		tp->rx_ring[i].status = cpu_to_le32(DescOwned); +		tp->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail); +	} +	tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + +	/* The Tx buffer descriptor is filled in as needed, but we +	   do need to clear the ownership bit. */ +	for (i = 0; i < TX_RING_SIZE; i++) { +		tp->tx_skbuff[i] = 0; +		tp->tx_ring[i].status = 0x00000000; +		tp->tx_ring[i].buffer2 = virt_to_le32desc(&tp->tx_ring[i+1]); +	} +	tp->tx_ring[i-1].buffer2 = virt_to_le32desc(&tp->tx_ring[0]); +} + +static int +tulip_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	int entry, q_used_cnt; +	u32 flag; + +	/* Block a timer-based transmit from overlapping.  This happens when +	   packets are presumed lost, and we use this check the Tx status. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tulip_tx_timeout(dev); +		return 1; +	} + +	/* Caution: the write order is important here, set the field +	   with the ownership bits last. */ + +	/* Calculate the next Tx descriptor entry. */ +	entry = tp->cur_tx % TX_RING_SIZE; +	q_used_cnt = tp->cur_tx - tp->dirty_tx; + +	tp->tx_skbuff[entry] = skb; +	tp->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data); + +	if (q_used_cnt < TX_QUEUE_LEN/2) {/* Typical path */ +		flag = 0x60000000; /* No interrupt */ +	} else if (q_used_cnt == TX_QUEUE_LEN/2) { +		flag = 0xe0000000; /* Tx-done intr. */ +	} else if (q_used_cnt < TX_QUEUE_LEN) { +		flag = 0x60000000; /* No Tx-done intr. */ +	} else {		/* Leave room for set_rx_mode() to fill entries. */ +		tp->tx_full = 1; +		flag = 0xe0000000; /* Tx-done intr. */ +	} +	if (entry == TX_RING_SIZE-1) +		flag = 0xe0000000 | DESC_RING_WRAP; + +	tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag); +	tp->tx_ring[entry].status = cpu_to_le32(DescOwned); +	tp->cur_tx++; +	if ( ! tp->tx_full) +		netif_unpause_tx_queue(dev); +	else { +		netif_stop_tx_queue(dev); +		/* Check for a just-cleared queue race. +		   Note that this code path differs from other drivers because we +		   set the tx_full flag early. */ +		if ( ! tp->tx_full) +			netif_resume_tx_queue(dev); +	} + +	dev->trans_start = jiffies; +	/* Trigger an immediate transmit demand. */ +	outl(0, dev->base_addr + CSR1); + +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int csr5, work_budget = tp->max_interrupt_work; + +	do { +		csr5 = inl(ioaddr + CSR5); +		if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) +			break; + +		if (tp->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n", +				   dev->name, csr5, (int)inl(dev->base_addr + CSR5)); +		/* Acknowledge all of the current interrupt sources ASAP. */ +		outl(csr5 & 0x0001ffff, ioaddr + CSR5); + +		if (csr5 & (RxIntr | RxNoBuf)) +			work_budget -= tulip_rx(dev); + +		if (csr5 & (TxNoBuf | TxDied | TxIntr)) { +			unsigned int dirty_tx; + +			for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; +				 dirty_tx++) { +				int entry = dirty_tx % TX_RING_SIZE; +				int status = le32_to_cpu(tp->tx_ring[entry].status); + +				if (status < 0) +					break;			/* It still has not been Txed */ +				/* Check for Rx filter setup frames. */ +				if (tp->tx_skbuff[entry] == NULL) +				  continue; + +				if (status & 0x8000) { +					/* There was an major error, log it. */ +					if (tp->msg_level & NETIF_MSG_TX_ERR) +						printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", +							   dev->name, status); +					tp->stats.tx_errors++; +					if (status & 0x4104) tp->stats.tx_aborted_errors++; +					if (status & 0x0C00) tp->stats.tx_carrier_errors++; +					if (status & 0x0200) tp->stats.tx_window_errors++; +					if (status & 0x0002) tp->stats.tx_fifo_errors++; +					if ((status & 0x0080) && tp->full_duplex == 0) +						tp->stats.tx_heartbeat_errors++; +#ifdef ETHER_STATS +					if (status & 0x0100) tp->stats.collisions16++; +#endif +				} else { +					if (tp->msg_level & NETIF_MSG_TX_DONE) +						printk(KERN_DEBUG "%s: Transmit complete, status " +							   "%8.8x.\n", dev->name, status); +#ifdef ETHER_STATS +					if (status & 0x0001) tp->stats.tx_deferred++; +#endif +#if LINUX_VERSION_CODE > 0x20127 +					tp->stats.tx_bytes += tp->tx_skbuff[entry]->len; +#endif +					tp->stats.collisions += (status >> 3) & 15; +					tp->stats.tx_packets++; +				} + +				/* Free the original skb. */ +				dev_free_skb_irq(tp->tx_skbuff[entry]); +				tp->tx_skbuff[entry] = 0; +			} + +#ifndef final_version +			if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { +				printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", +					   dev->name, dirty_tx, tp->cur_tx, tp->tx_full); +				dirty_tx += TX_RING_SIZE; +			} +#endif + +			if (tp->tx_full && tp->cur_tx - dirty_tx  < TX_QUEUE_LEN - 4) { +				/* The ring is no longer full, clear tbusy. */ +				tp->tx_full = 0; +				netif_resume_tx_queue(dev); +			} + +			tp->dirty_tx = dirty_tx; +		} + +		if (tp->rx_dead) { +			tulip_rx(dev); +			if (tp->cur_rx - tp->dirty_rx < RX_RING_SIZE - 3) { +				printk(KERN_ERR "%s: Restarted Rx at %d / %d.\n", +					   dev->name, tp->cur_rx, tp->dirty_rx); +				outl(0, ioaddr + CSR2);		/* Rx poll demand */ +				tp->rx_dead = 0; +			} +		} + +		/* Log errors. */ +		if (csr5 & AbnormalIntr) {	/* Abnormal error summary bit. */ +			if (csr5 == 0xffffffff) +				break; +			if (csr5 & TxJabber) tp->stats.tx_errors++; +			if (csr5 & PCIBusError) { +				printk(KERN_ERR "%s: PCI Fatal Bus Error, %8.8x.\n", +					   dev->name, csr5); +			} +			if (csr5 & TxFIFOUnderflow) { +				if ((tp->csr6 & 0xC000) != 0xC000) +					tp->csr6 += 0x4000;	/* Bump up the Tx threshold */ +				else +					tp->csr6 |= 0x00200000;  /* Store-n-forward. */ +				if (tp->msg_level & NETIF_MSG_TX_ERR) +					printk(KERN_WARNING "%s: Tx threshold increased, " +						   "new CSR6 %x.\n", dev->name, tp->csr6); +			} +			if (csr5 & TxDied) { +				/* This is normal when changing Tx modes. */ +				if (tp->msg_level & NETIF_MSG_LINK) +					printk(KERN_WARNING "%s: The transmitter stopped." +						   "  CSR5 is %x, CSR6 %x, new CSR6 %x.\n", +						   dev->name, csr5, (int)inl(ioaddr + CSR6), tp->csr6); +			} +			if (csr5 & (TxDied | TxFIFOUnderflow | PCIBusError)) { +				/* Restart the transmit process. */ +				outl(tp->csr6 | RxOn, ioaddr + CSR6); +				outl(tp->csr6 | RxOn | TxOn, ioaddr + CSR6); +			} +			if (csr5 & (RxStopped | RxNoBuf)) { +				/* Missed a Rx frame or mode change. */ +				tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; +				if (tp->flags & COMET_MAC_ADDR) { +					outl(tp->mc_filter[0], ioaddr + 0xAC); +					outl(tp->mc_filter[1], ioaddr + 0xB0); +				} +				tulip_rx(dev); +				if (csr5 & RxNoBuf) +					tp->rx_dead = 1; +				outl(tp->csr6 | RxOn | TxOn, ioaddr + CSR6); +			} +			if (csr5 & TimerInt) { +				if (tp->msg_level & NETIF_MSG_INTR) +					printk(KERN_ERR "%s: Re-enabling interrupts, %8.8x.\n", +						   dev->name, csr5); +				outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); +			} +			if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) { +				if (tp->link_change) +					(tp->link_change)(dev, csr5); +			} +			/* Clear all error sources, included undocumented ones! */ +			outl(0x0800f7ba, ioaddr + CSR5); +		} +		if (--work_budget < 0) { +			if (tp->msg_level & NETIF_MSG_DRV) +				printk(KERN_WARNING "%s: Too much work during an interrupt, " +					   "csr5=0x%8.8x.\n", dev->name, csr5); +			/* Acknowledge all interrupt sources. */ +			outl(0x8001ffff, ioaddr + CSR5); +			if (tp->flags & HAS_INTR_MITIGATION) { +				/* Josip Loncaric at ICASE did extensive experimentation +				   to develop a good interrupt mitigation setting.*/ +				outl(0x8b240000, ioaddr + CSR11); +			} else { +				/* Mask all interrupting sources, set timer to re-enable. */ +				outl(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt, +					 ioaddr + CSR7); +				outl(0x0012, ioaddr + CSR11); +			} +			break; +		} +	} while (1); + +	if (tp->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", +			   dev->name, (int)inl(ioaddr + CSR5)); + +	return; +} + +static int tulip_rx(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	int entry = tp->cur_rx % RX_RING_SIZE; +	int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; +	int work_done = 0; + +	if (tp->msg_level & NETIF_MSG_RX_STATUS) +		printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, +			   tp->rx_ring[entry].status); +	/* If we own the next entry, it is a new packet. Send it up. */ +	while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { +		s32 status = le32_to_cpu(tp->rx_ring[entry].status); + +		if (tp->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %8.8x.\n", +				   dev->name, entry, status); +		if (--rx_work_limit < 0) +			break; +		if ((status & 0x38008300) != 0x0300) { +			if ((status & 0x38000300) != 0x0300) { +				/* Ingore earlier buffers. */ +				if ((status & 0xffff) != 0x7fff) { +					if (tp->msg_level & NETIF_MSG_RX_ERR) +						printk(KERN_WARNING "%s: Oversized Ethernet frame " +							   "spanned multiple buffers, status %8.8x!\n", +							   dev->name, status); +					tp->stats.rx_length_errors++; +				} +			} else if (status & RxDescFatalErr) { +				/* There was a fatal error. */ +				if (tp->msg_level & NETIF_MSG_RX_ERR) +					printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", +						   dev->name, status); +				tp->stats.rx_errors++; /* end of a packet.*/ +				if (status & 0x0890) tp->stats.rx_length_errors++; +				if (status & 0x0004) tp->stats.rx_frame_errors++; +				if (status & 0x0002) tp->stats.rx_crc_errors++; +				if (status & 0x0001) tp->stats.rx_fifo_errors++; +			} +		} else { +			/* Omit the four octet CRC from the length. */ +			short pkt_len = ((status >> 16) & 0x7ff) - 4; +			struct sk_buff *skb; + +#ifndef final_version +			if (pkt_len > 1518) { +				printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n", +					   dev->name, pkt_len, pkt_len); +				pkt_len = 1518; +				tp->stats.rx_length_errors++; +			} +#endif +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < tp->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +#if (LINUX_VERSION_CODE >= 0x20100) +				eth_copy_and_sum(skb, tp->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), tp->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +				work_done++; +			} else {	/* Pass up the skb already on the Rx ring. */ +				skb_put(skb = tp->rx_skbuff[entry], pkt_len); +				tp->rx_skbuff[entry] = NULL; +			} +			skb->protocol = eth_type_trans(skb, dev); +			netif_rx(skb); +			dev->last_rx = jiffies; +			tp->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			tp->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++tp->cur_rx) % RX_RING_SIZE; +	} + +	/* Refill the Rx ring buffers. */ +	for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { +		entry = tp->dirty_rx % RX_RING_SIZE; +		if (tp->rx_skbuff[entry] == NULL) { +			struct sk_buff *skb; +			skb = tp->rx_skbuff[entry] = dev_alloc_skb(tp->rx_buf_sz); +			if (skb == NULL) { +				if (tp->cur_rx - tp->dirty_rx == RX_RING_SIZE) +					printk(KERN_ERR "%s: No kernel memory to allocate " +						   "receive buffers.\n", dev->name); +				break; +			} +			skb->dev = dev;			/* Mark as being used by this device. */ +			tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail); +			work_done++; +		} +		tp->rx_ring[entry].status = cpu_to_le32(DescOwned); +	} + +	return work_done; +} + +static void empty_rings(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	int i; + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = tp->rx_skbuff[i]; +		tp->rx_skbuff[i] = 0; +		tp->rx_ring[i].status = 0;		/* Not owned by Tulip chip. */ +		tp->rx_ring[i].length = 0; +		tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */ +		if (skb) { +#if LINUX_VERSION_CODE < 0x20100 +			skb->free = 1; +#endif +			dev_free_skb(skb); +		} +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (tp->tx_skbuff[i]) +			dev_free_skb(tp->tx_skbuff[i]); +		tp->tx_skbuff[i] = 0; +	} +} + +static int tulip_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; + +	netif_stop_tx_queue(dev); + +	if (tp->msg_level & NETIF_MSG_IFDOWN) +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", +			   dev->name, (int)inl(ioaddr + CSR5)); + +	/* Disable interrupts by clearing the interrupt mask. */ +	outl(0x00000000, ioaddr + CSR7); +	/* Stop the Tx and Rx processes. */ +	outl(inl(ioaddr + CSR6) & ~TxOn & ~RxOn, ioaddr + CSR6); +	/* 21040 -- Leave the card in 10baseT state. */ +	if (tp->chip_id == DC21040) +		outl(0x00000004, ioaddr + CSR13); + +	if (inl(ioaddr + CSR6) != 0xffffffff) +		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + +	del_timer(&tp->timer); + +	free_irq(dev->irq, dev); + +	dev->if_port = tp->saved_if_port; + +	empty_rings(dev); +	/* Leave the driver in snooze, not sleep, mode. */ +	if (tp->flags & HAS_PWRDWN) +		pci_write_config_dword(tp->pci_dev, 0x40, 0x40000000); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static struct net_device_stats *tulip_get_stats(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int csr8 = inl(ioaddr + CSR8); + +	if (netif_running(dev)  &&  csr8 != 0xffffffff) +		tp->stats.rx_missed_errors += (u16)csr8; + +	return &tp->stats; +} + +#ifdef HAVE_PRIVATE_IOCTL +/* Provide ioctl() calls to examine the MII xcvr state. +   We emulate a MII management registers for chips without MII. +   The two numeric constants are because some clueless person +   changed value for the symbolic name. + */ +static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; +	unsigned int phy = tp->phys[0]; +	unsigned int regnum = data[1]; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		if (tp->mii_cnt) +			data[0] = phy; +		else if (tp->flags & HAS_NWAY) +			data[0] = 32; +		else if (tp->chip_id == COMET) +			data[0] = 1; +		else +			return -ENODEV; +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		if (data[0] == 32  &&  (tp->flags & HAS_NWAY)) { +			int csr12 = inl(ioaddr + CSR12); +			int csr14 = inl(ioaddr + CSR14); +			switch (regnum) { +			case 0: +				if (((csr14<<5) & 0x1000) || +					(dev->if_port == 5 && tp->nwayset)) +					data[3] = 0x1000; +				else +					data[3] = (media_cap[dev->if_port]&MediaIs100 ? 0x2000 : 0) +						| (media_cap[dev->if_port]&MediaIsFD ? 0x0100 : 0); +				break; +			case 1: +				data[3] = 0x1848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0) +					+ ((csr12&0x06) == 6 ? 0 : 4); +				if (tp->chip_id != DC21041) +					data[3] |= 0x6048; +				break; +			case 4: { +				/* Advertised value, bogus 10baseTx-FD value from CSR6. */ +				data[3] = ((inl(ioaddr + CSR6)>>3)&0x0040)+((csr14>>1)&0x20)+1; +				if (tp->chip_id != DC21041) +					 data[3] |= ((csr14>>9)&0x03C0); +				break; +			} +			case 5: data[3] = tp->lpar; break; +			default: data[3] = 0; break; +			} +		} else { +			data[3] = mdio_read(dev, data[0] & 0x1f, regnum); +		} +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (regnum & ~0x1f) +			return -EINVAL; +		if (data[0] == phy) { +			u16 value = data[2]; +			switch (regnum) { +			case 0: /* Check for autonegotiation on or reset. */ +				tp->full_duplex_lock = (value & 0x9000) ? 0 : 1; +				if (tp->full_duplex_lock) +					tp->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: tp->mii_advertise = data[2]; break; +			} +		} +		if (data[0] == 32  &&  (tp->flags & HAS_NWAY)) { +			u16 value = data[2]; +			if (regnum == 0) { +				if ((value & 0x1200) == 0x1200) +					nway_start(dev); +			} else if (regnum == 4) +				tp->sym_advertise = value; +		} else { +			mdio_write(dev, data[0] & 0x1f, regnum, data[2]); +		} +		return 0; +	case SIOCGPARAMS: +		data32[0] = tp->msg_level; +		data32[1] = tp->multicast_filter_limit; +		data32[2] = tp->max_interrupt_work; +		data32[3] = tp->rx_copybreak; +		data32[4] = inl(ioaddr + CSR11); +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		tp->msg_level = data32[0]; +		tp->multicast_filter_limit = data32[1]; +		tp->max_interrupt_work = data32[2]; +		tp->rx_copybreak = data32[3]; +		if (tp->flags & HAS_INTR_MITIGATION) { +			u32 *d = (u32 *)&rq->ifr_data; +			outl(data32[4], ioaddr + CSR11); +			printk(KERN_NOTICE "%s: Set interrupt mitigate paramters %8.8x.\n", +				   dev->name, d[0]); +		} +		return 0; +	default: +		return -EOPNOTSUPP; +	} + +	return -EOPNOTSUPP; +} +#endif  /* HAVE_PRIVATE_IOCTL */ + +/* Set or clear the multicast filter for this adaptor. +   Note that we only use exclusion around actually queueing the +   new frame, not around filling tp->setup_frame.  This is non-deterministic +   when re-entered but still correct. */ + +/* The little-endian AUTODIN32 ethernet CRC calculation. +   N.B. Do not use for bulk data, use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline u32 ether_crc_le(int length, unsigned char *data) +{ +	u32 crc = 0xffffffff;	/* Initial value. */ +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +	} +	return crc; +} +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ +	int crc = -1; + +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 0; bit < 8; bit++, current_octet >>= 1) +			crc = (crc << 1) ^ +				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int csr6 = inl(ioaddr + CSR6) & ~0x00D5; + +	tp->csr6 &= ~0x00D5; +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		tp->csr6 |= AcceptAllMulticast | AcceptAllPhys; +		csr6 |= AcceptAllMulticast | AcceptAllPhys; +		/* Unconditionally log net taps. */ +		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); +	} else if ((dev->mc_count > tp->multicast_filter_limit)  || +			   (dev->flags & IFF_ALLMULTI)) { +		/* Too many to filter well -- accept all multicasts. */ +		tp->csr6 |= AcceptAllMulticast; +		csr6 |= AcceptAllMulticast; +	} else	if (tp->flags & MC_HASH_ONLY) { +		/* Some work-alikes have only a 64-entry hash filter table. */ +		/* Should verify correctness on big-endian/__powerpc__ */ +		struct dev_mc_list *mclist; +		int i; +		if (dev->mc_count > tp->multicast_filter_limit) { +			tp->csr6 |= AcceptAllMulticast; +			csr6 |= AcceptAllMulticast; +		} else { +			u32 mc_filter[2] = {0, 0};		 /* Multicast hash filter */ +			int filterbit; +			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +				 i++, mclist = mclist->next) { +				if (tp->flags & COMET_MAC_ADDR) +					filterbit = ether_crc_le(ETH_ALEN, mclist->dmi_addr); +				else +					filterbit = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; +				filterbit &= 0x3f; +				set_bit(filterbit, mc_filter); +				if (tp->msg_level & NETIF_MSG_RXFILTER) +					printk(KERN_INFO "%s: Added filter for %2.2x:%2.2x:%2.2x:" +						   "%2.2x:%2.2x:%2.2x  %8.8x bit %d.\n", dev->name, +						   mclist->dmi_addr[0], mclist->dmi_addr[1], +						   mclist->dmi_addr[2], mclist->dmi_addr[3], +						   mclist->dmi_addr[4], mclist->dmi_addr[5], +						   ether_crc(ETH_ALEN, mclist->dmi_addr), filterbit); +			} +			if (mc_filter[0] == tp->mc_filter[0]  && +				mc_filter[1] == tp->mc_filter[1]) +				;				/* No change. */ +			else if (tp->flags & IS_ASIX) { +				outl(2, ioaddr + CSR13); +				outl(mc_filter[0], ioaddr + CSR14); +				outl(3, ioaddr + CSR13); +				outl(mc_filter[1], ioaddr + CSR14); +			} else if (tp->flags & COMET_MAC_ADDR) { +				outl(mc_filter[0], ioaddr + 0xAC); +				outl(mc_filter[1], ioaddr + 0xB0); +			} +			tp->mc_filter[0] = mc_filter[0]; +			tp->mc_filter[1] = mc_filter[1]; +		} +	} else { +		u16 *eaddrs, *setup_frm = tp->setup_frame; +		struct dev_mc_list *mclist; +		u32 tx_flags = 0x08000000 | 192; +		int i; + +		/* Note that only the low-address shortword of setup_frame is valid! +		   The values are doubled for big-endian architectures. */ +		if (dev->mc_count > 14) { /* Must use a multicast hash table. */ +			u16 hash_table[32]; +			tx_flags = 0x08400000 | 192;		/* Use hash filter. */ +			memset(hash_table, 0, sizeof(hash_table)); +			set_bit(255, hash_table);			/* Broadcast entry */ +			/* This should work on big-endian machines as well. */ +			for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +				 i++, mclist = mclist->next) +				set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff, +						hash_table); +			for (i = 0; i < 32; i++) { +				*setup_frm++ = hash_table[i]; +				*setup_frm++ = hash_table[i]; +			} +			setup_frm = &tp->setup_frame[13*6]; +		} else { +			/* We have <= 14 addresses so we can use the wonderful +			   16 address perfect filtering of the Tulip. */ +			for (i = 0, mclist = dev->mc_list; i < dev->mc_count; +				 i++, mclist = mclist->next) { +				eaddrs = (u16 *)mclist->dmi_addr; +				*setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; +				*setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; +				*setup_frm++ = *eaddrs; *setup_frm++ = *eaddrs++; +			} +			/* Fill the unused entries with the broadcast address. */ +			memset(setup_frm, 0xff, (15-i)*12); +			setup_frm = &tp->setup_frame[15*6]; +		} +		/* Fill the final entry with our physical address. */ +		eaddrs = (u16 *)dev->dev_addr; +		*setup_frm++ = eaddrs[0]; *setup_frm++ = eaddrs[0]; +		*setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; +		*setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; +		/* Now add this frame to the Tx list. */ +		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { +			/* Same setup recently queued, we need not add it. */ +		} else { +			unsigned long flags; +			unsigned int entry; + +			spin_lock_irqsave(&tp->mii_lock, flags); +			entry = tp->cur_tx++ % TX_RING_SIZE; + +			if (entry != 0) { +				/* Avoid a chip errata by prefixing a dummy entry. */ +				tp->tx_skbuff[entry] = 0; +				tp->tx_ring[entry].length = +					(entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP):0; +				tp->tx_ring[entry].buffer1 = 0; +				tp->tx_ring[entry].status = cpu_to_le32(DescOwned); +				entry = tp->cur_tx++ % TX_RING_SIZE; +			} + +			tp->tx_skbuff[entry] = 0; +			/* Put the setup frame on the Tx list. */ +			if (entry == TX_RING_SIZE-1) +				tx_flags |= DESC_RING_WRAP;		/* Wrap ring. */ +			tp->tx_ring[entry].length = cpu_to_le32(tx_flags); +			tp->tx_ring[entry].buffer1 = virt_to_le32desc(tp->setup_frame); +			tp->tx_ring[entry].status = cpu_to_le32(DescOwned); +			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { +				netif_stop_tx_queue(dev); +				tp->tx_full = 1; +			} +			spin_unlock_irqrestore(&tp->mii_lock, flags); +			/* Trigger an immediate transmit demand. */ +			outl(0, ioaddr + CSR1); +		} +	} +	outl(csr6, ioaddr + CSR6); +} + + +static int tulip_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct tulip_private *tp = (struct tulip_private *)dev->priv; +	long ioaddr = dev->base_addr; +	if (tp->msg_level & NETIF_MSG_LINK) +		printk("%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: { +		int csr6 = inl(ioaddr + CSR6); +		/* Disable interrupts, stop the chip, gather stats. */ +		if (csr6 != 0xffffffff) { +			int csr8 = inl(ioaddr + CSR8); +			outl(0x00000000, ioaddr + CSR7); +			outl(csr6 & ~TxOn & ~RxOn, ioaddr + CSR6); +			tp->stats.rx_missed_errors += (unsigned short)csr8; +		} +		empty_rings(dev); +		/* Put the 21143 into sleep mode. */ +		if (tp->flags & HAS_PWRDWN) +			pci_write_config_dword(tp->pci_dev, 0x40,0x80000000); +		break; +	} +	case DRV_RESUME: +		if (tp->flags & HAS_PWRDWN) +			pci_write_config_dword(tp->pci_dev, 0x40, 0x0000); +		outl(tp->csr0, ioaddr + CSR0); +		tulip_init_ring(dev); +		outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3); +		outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); +		if (tp->mii_cnt) { +			dev->if_port = 11; +			if (tp->mtable  &&  tp->mtable->has_mii) +				select_media(dev, 1); +			tp->csr6 = 0x820E0000; +			dev->if_port = 11; +			outl(0x0000, ioaddr + CSR13); +			outl(0x0000, ioaddr + CSR14); +		} else if (! tp->medialock) +			nway_start(dev); +		else +			select_media(dev, 1); +		outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); +		outl(tp->csr6 | TxOn | RxOn, ioaddr + CSR6); +		outl(0, ioaddr + CSR2);		/* Rx poll demand */ +		set_rx_mode(dev); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			printk(KERN_ERR "%s: Tulip CardBus interface was detached while " +				   "still active.\n", dev->name); +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		if (tp->msg_level & NETIF_MSG_DRV) +			printk(KERN_DEBUG "%s: Unregistering device.\n", dev->name); +		unregister_netdev(dev); +#ifdef USE_IO_OPS +		release_region(dev->base_addr, pci_id_tbl[tp->chip_id].io_size); +#else +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_tulip_dev; *devp; devp = next) { +			next = &((struct tulip_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (tp->priv_addr) +			kfree(tp->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	default: +		break; +	} + +	return 0; +} + +#ifdef CARDBUS + +#include <pcmcia/driver_ops.h> + +static dev_node_t *tulip_attach(dev_locator_t *loc) +{ +	struct net_device *dev; +	long ioaddr; +	struct pci_dev *pdev; +	u8 bus, devfn, irq; +	u32 dev_id; +	u32 pciaddr; +	int i, chip_id = 4;			/* DC21143 */ + +	if (loc->bus != LOC_PCI) return NULL; +	bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; +	printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn); +	pdev = pci_find_slot(bus, devfn); +#ifdef USE_IO_OPS +	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &pciaddr); +	ioaddr = pciaddr & PCI_BASE_ADDRESS_IO_MASK; +#else +	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, &pciaddr); +	ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, +						   pci_id_tbl[DC21142].io_size); +#endif +	pci_read_config_dword(pdev, 0, &dev_id); +	pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &irq); +	if (ioaddr == 0 || irq == 0) { +		printk(KERN_ERR "The Tulip CardBus Ethernet interface at %d/%d was " +			   "not assigned an %s.\n" +			   KERN_ERR "  It will not be activated.\n", +			   bus, devfn, ioaddr == 0 ? "address" : "IRQ"); +		return NULL; +	} +	for (i = 0; pci_id_tbl[i].id.pci; i++) { +		if (pci_id_tbl[i].id.pci == (dev_id & pci_id_tbl[i].id.pci_mask)) { +			chip_id = i; break; +		} +	} +	dev = tulip_probe1(pdev, NULL, ioaddr, irq, chip_id, 0); +	if (dev) { +		dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); +		strcpy(node->dev_name, dev->name); +		node->major = node->minor = 0; +		node->next = NULL; +		MOD_INC_USE_COUNT; +		return node; +	} +	return NULL; +} + +static void tulip_suspend(dev_node_t *node) +{ +	struct net_device **devp, **next; +	printk(KERN_INFO "tulip_suspend(%s)\n", node->dev_name); +	for (devp = &root_tulip_dev; *devp; devp = next) { +		next = &((struct tulip_private *)(*devp)->priv)->next_module; +		if (strcmp((*devp)->name, node->dev_name) == 0) { +			tulip_pwr_event(*devp, DRV_SUSPEND); +			break; +		} +	} +} + +static void tulip_resume(dev_node_t *node) +{ +	struct net_device **devp, **next; +	printk(KERN_INFO "tulip_resume(%s)\n", node->dev_name); +	for (devp = &root_tulip_dev; *devp; devp = next) { +		next = &((struct tulip_private *)(*devp)->priv)->next_module; +		if (strcmp((*devp)->name, node->dev_name) == 0) { +			tulip_pwr_event(*devp, DRV_RESUME); +			break; +		} +	} +} + +static void tulip_detach(dev_node_t *node) +{ +	struct net_device **devp, **next; +	printk(KERN_INFO "tulip_detach(%s)\n", node->dev_name); +	for (devp = &root_tulip_dev; *devp; devp = next) { +		next = &((struct tulip_private *)(*devp)->priv)->next_module; +		if (strcmp((*devp)->name, node->dev_name) == 0) break; +	} +	if (*devp) { +		struct tulip_private *tp = (struct tulip_private *)(*devp)->priv; +		unregister_netdev(*devp); +#ifdef USE_IO_OPS +		release_region((*devp)->base_addr, pci_id_tbl[DC21142].io_size); +#else +		iounmap((char *)(*devp)->base_addr); +#endif +		kfree(*devp); +		if (tp->priv_addr) +			kfree(tp->priv_addr); +		*devp = *next; +		kfree(node); +		MOD_DEC_USE_COUNT; +	} +} + +struct driver_operations tulip_ops = { +	"tulip_cb", tulip_attach, tulip_suspend, tulip_resume, tulip_detach +}; + +#endif  /* Cardbus support */ + + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +#ifdef CARDBUS +	register_driver(&tulip_ops); +	return 0; +#else +	return pci_drv_register(&tulip_drv_id, NULL); +#endif +	reverse_probe = 0;			/* Not used. */ +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +#ifdef CARDBUS +	unregister_driver(&tulip_ops); +#else +	pci_drv_unregister(&tulip_drv_id); +#endif + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_tulip_dev) { +		struct tulip_private *tp = (struct tulip_private*)root_tulip_dev->priv; +		unregister_netdev(root_tulip_dev); +#ifdef USE_IO_OPS +		release_region(root_tulip_dev->base_addr, +					   pci_id_tbl[tp->chip_id].io_size); +#else +		iounmap((char *)root_tulip_dev->base_addr); +#endif +		next_dev = tp->next_module; +		if (tp->priv_addr) +			kfree(tp->priv_addr); +		kfree(root_tulip_dev); +		root_tulip_dev = next_dev; +	} +} +#else +int tulip_probe(struct net_device *dev) +{ +	if (pci_drv_register(&tulip_drv_id, dev) < 0) +		return -ENODEV; +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +	reverse_probe = 0;			/* Not used. */ +} +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` tulip.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c tulip.c" + *  cardbus-compile-command: "gcc -DCARDBUS -DMODULE -Wall -Wstrict-prototypes -O6 -c tulip.c -o tulip_cb.o -I/usr/src/pcmcia/include/" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/via-rhine.c b/linux/src/drivers/net/via-rhine.c new file mode 100644 index 0000000..4d7fceb --- /dev/null +++ b/linux/src/drivers/net/via-rhine.c @@ -0,0 +1,1427 @@ +/* via-rhine.c: A Linux Ethernet device driver for VIA Rhine family chips. */ +/* +	Written 1998-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	This driver is designed for the VIA VT86c100A Rhine-II PCI Fast Ethernet +	controller.  It also works with the older 3043 Rhine-I chip. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Support information and updates available at +		http://www.scyld.com/network/via-rhine.html +	The information and support mailing lists are based at +		http://www.scyld.com/mailman/listinfo/ +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"via-rhine.c:v1.16 7/22/2003  Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/via-rhine.html\n"; + +/* Automatically extracted configuration info: +probe-func: via_rhine_probe +config-in: tristate 'VIA "Rhine" vt86c100, vt3043, and vt3065 series PCI Ethernet support' CONFIG_VIA_RHINE + +c-help-name: VIA Rhine series PCI Ethernet support +c-help-symbol: CONFIG_VIA_RHINE +c-help: This driver is for the VIA Rhine (v3043) and Rhine-II +c-help: (vt3065 AKA vt86c100) network adapter chip series. +c-help: More specific information and updates are available from  +c-help: http://www.scyld.com/network/via-rhine.html +*/ + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   Both 'options[]' and 'full_duplex[]' should exist for driver +   interoperability. +   The media type is usually passed in 'options[]'. +    The default is autonegotation for speed and duplex. +	This should rarely be overridden. +    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +    Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +    Use option values 0x20 and 0x200 for forcing full duplex operation. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   The Rhine has a 64 element 8390-like hash table.  */ +static const int multicast_filter_limit = 32; + +/* Operational parameters that are set at compile time. */ + +/* Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority. +   There are no ill effects from too-large receive rings. */ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */ +#define RX_RING_SIZE	32 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed bus+endian portability operations. */ +#define virt_to_le32desc(addr)	cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)	bus_to_virt(le32_to_cpu(addr)) + +/* This driver was written to use PCI memory space, however most versions +   of the Rhine only work correctly with I/O space accesses. */ +#if defined(VIA_USE_MEMORY) +#warning Many adapters using the VIA Rhine chip are not configured to work +#warning with PCI memory space accesses. +#else +#define USE_IO_OPS +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("VIA Rhine PCI Fast Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, "Non-zero to set forced full duplex " +				 "(deprecated, use options[] instead)."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet +controller. + +II. Board-specific settings + +Boards with this chip are functional only in a bus-master PCI slot. + +Many operational settings are loaded from the EEPROM to the Config word at +offset 0x78.  This driver assumes that they are correct. +If this driver is compiled to use PCI memory space operations the EEPROM +must be configured to enable memory ops. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE. + +IIIb/c. Transmit/Receive Structure + +This driver attempts to use a zero-copy receive and transmit scheme. + +Alas, all data buffers are required to start on a 32 bit boundary, so +the driver must often copy transmit packets into bounce buffers. + +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers.  When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack.  Buffers consumed this way are replaced by newly allocated +skbuffs in the last phase of netdev_rx(). + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets.  When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine.  Copying also preloads the cache, which is +most useful with small frames. + +Since the VIA chips are only able to transfer data to buffers on 32 bit +boundaries, the the IP header at offset 14 in an ethernet frame isn't +longword aligned for further processing.  Copying these unaligned buffers +has the beneficial effect of 16-byte aligning the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +IVb. References + +This driver was originally written using a preliminary VT86C100A manual +from +  http://www.via.com.tw/  +The usual background material was used: +  http://www.scyld.com/expert/100mbps.html +  http://scyld.com/expert/NWay.html + +Additional information is now available, especially for the newer chips. +   http://www.via.com.tw/en/Networking/DS6105LOM100.pdf + +IVc. Errata + +The VT86C100A manual is not reliable information. +The 3043 chip does not handle unaligned transmit or receive buffers, +resulting in significant performance degradation for bounce buffer +copies on transmit and unaligned IP headers on receive. +The chip does not pad to minimum transmit length. + +There is a bug with the transmit descriptor pointer handling when the +chip encounters a transmit error. + +*/ + + + +static void *via_probe1(struct pci_dev *pdev, void *init_dev, +						long ioaddr, int irq, int chip_idx, int find_cnt); +static int via_pwr_event(void *dev_instance, int event); +enum chip_capability_flags { +	CanHaveMII=1, HasESIPhy=2, HasDavicomPhy=4, HasV1TxStat=8, +	ReqTxAlign=0x10, HasWOL=0x20, HasIPChecksum=0x40, HasVLAN=0x80, +	 +}; + +#if defined(VIA_USE_MEMORY) +#define RHINE_IOTYPE (PCI_USES_MEM | PCI_USES_MASTER | PCI_ADDR1) +#define RHINE_I_IOSIZE 128 +#define RHINEII_IOSIZE 4096 +#else +#define RHINE_IOTYPE (PCI_USES_IO  | PCI_USES_MASTER | PCI_ADDR0) +#define RHINE_I_IOSIZE 128 +#define RHINEII_IOSIZE 256 +#endif + +static struct pci_id_info pci_tbl[] = { +	{ "VIA VT3043 Rhine", { 0x30431106, 0xffffffff,}, +	  RHINE_IOTYPE, RHINE_I_IOSIZE, CanHaveMII | ReqTxAlign | HasV1TxStat }, +	{ "VIA VT86C100A Rhine", { 0x61001106, 0xffffffff,}, +	  RHINE_IOTYPE, RHINE_I_IOSIZE, CanHaveMII | ReqTxAlign | HasV1TxStat }, +	{ "VIA VT6102 Rhine-II", { 0x30651106, 0xffffffff,}, +	  RHINE_IOTYPE, RHINEII_IOSIZE, CanHaveMII | HasWOL }, +	{ "VIA VT6105LOM Rhine-III (3106)", { 0x31061106, 0xffffffff,}, +	  RHINE_IOTYPE, RHINEII_IOSIZE, CanHaveMII | HasWOL }, +	/* Duplicate entry, with 'M' features enabled. */ +	{ "VIA VT6105M Rhine-III (3106)", { 0x31061106, 0xffffffff,}, +	  RHINE_IOTYPE, RHINEII_IOSIZE, CanHaveMII|HasWOL|HasIPChecksum|HasVLAN}, +	{ "VIA VT6105M Rhine-III (3053 prototype)", { 0x30531106, 0xffffffff,}, +	  RHINE_IOTYPE, RHINEII_IOSIZE, CanHaveMII | HasWOL }, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info via_rhine_drv_id = { +	"via-rhine", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_tbl, +	via_probe1, via_pwr_event +}; + +/* Offsets to the device registers. +*/ +enum register_offsets { +	StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08, +	IntrStatus=0x0C, IntrEnable=0x0E, +	MulticastFilter0=0x10, MulticastFilter1=0x14, +	RxRingPtr=0x18, TxRingPtr=0x1C, +	MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E, +	MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, MACRegEEcsr=0x74, +	Config=0x78, ConfigA=0x7A, RxMissed=0x7C, RxCRCErrs=0x7E, +	StickyHW=0x83, WOLcrClr=0xA4, WOLcgClr=0xA7, PwrcsrClr=0xAC, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020, +	IntrTxDone=0x0002, IntrTxAbort=0x0008, IntrTxUnderrun=0x0010, +	IntrPCIErr=0x0040, +	IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200, +	IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000, +	IntrTxAborted=0x2000, IntrLinkChange=0x4000, +	IntrRxWakeUp=0x8000, +	IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260, +}; + +/* The Rx and Tx buffer descriptors. */ +struct rx_desc { +	s32 rx_status; +	u32 desc_length; +	u32 addr; +	u32 next_desc; +}; +struct tx_desc { +	s32 tx_status; +	u32 desc_length; +	u32 addr; +	u32 next_desc; +}; + +/* Bits in *_desc.status */ +enum rx_status_bits { +	RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F}; +enum desc_status_bits { +	DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000, +}; + +/* Bits in rx.desc_length for extended status. */ +enum rx_info_bits { +	RxTypeTag=0x00010000, +	RxTypeUDP=0x00020000, RxTypeTCP=0x00040000, RxTypeIP=0x00080000, +	RxTypeUTChksumOK=0x00100000, RxTypeIPChksumOK=0x00200000, +	/* Summarized. */ +	RxTypeCsumMask=0x003E0000, +	RxTypeUDPSumOK=0x003A0000, RxTypeTCPSumOK=0x003C0000,  +}; + +/* Bits in ChipCmd. */ +enum chip_cmd_bits { +	CmdInit=0x0001, CmdStart=0x0002, CmdStop=0x0004, CmdRxOn=0x0008, +	CmdTxOn=0x0010, CmdTxDemand=0x0020, CmdRxDemand=0x0040, +	CmdEarlyRx=0x0100, CmdEarlyTx=0x0200, CmdFDuplex=0x0400, +	CmdNoTxPoll=0x0800, CmdReset=0x8000, +}; + +#define PRIV_ALIGN	15	/* Required alignment mask */ +/* Use  __attribute__((aligned (L1_CACHE_BYTES)))  to maintain alignment +   within the structure. */ +struct netdev_private { +	/* Descriptor rings first for alignment. */ +	struct rx_desc rx_ring[RX_RING_SIZE]; +	struct tx_desc tx_ring[TX_RING_SIZE]; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for later free(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	unsigned char *tx_buf[TX_RING_SIZE];	/* Tx bounce buffers */ +	unsigned char *tx_bufs;				/* Tx bounce buffer region. */ +	struct net_device *next_module;		/* Link for devices of this type. */ +	void *priv_addr;					/* Unaligned address for kfree */ +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	int msg_level; +	int max_interrupt_work; +	int intr_enable; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; + +	/* Frequently used values: keep some adjacent for cache effect. */ + +	struct rx_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	unsigned int cur_tx, dirty_tx; +	u16 chip_cmd;						/* Current setting for ChipCmd */ +	int multicast_filter_limit; +	u32 mc_filter[2]; +	int rx_mode; +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	/* These values are keep track of the transceiver/media in use. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	u8 tx_thresh, rx_thresh; +	/* MII transceiver section. */ +	int mii_cnt;						/* MII device addresses. */ +	u16 advertising;					/* NWay media advertisement */ +	unsigned char phys[2];				/* MII device addresses. */ +}; + +static int  mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static int  netdev_open(struct net_device *dev); +static void check_duplex(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct net_device *dev); +static void init_ring(struct net_device *dev); +static int  start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static int  netdev_rx(struct net_device *dev); +static void netdev_error(struct net_device *dev, int intr_status); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int  netdev_close(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_net_dev = NULL; + +#ifndef MODULE +int via_rhine_probe(struct net_device *dev) +{ +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return pci_drv_register(&via_rhine_drv_id, dev); +} +#endif + +static void *via_probe1(struct pci_dev *pdev, void *init_dev, +						long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct netdev_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	printk(KERN_INFO "%s: %s at 0x%lx, ", +		   dev->name, pci_tbl[chip_idx].name, ioaddr); + +	/* We would prefer to directly read the EEPROM but access may be locked. */ +	for (i = 0; i < 6; i++) +		dev->dev_addr[i] = readb(ioaddr + StationAddr + i); +	if (memcmp(dev->dev_addr, "\0\0\0\0\0", 6) == 0) { +		/* Reload the station address from the EEPROM. */ +		writeb(0x20, ioaddr + MACRegEEcsr); + 		/* Typically 2 cycles to reload. */ +		for (i = 0; i < 150; i++) +			if (! (readb(ioaddr + MACRegEEcsr) & 0x20)) +				break; +		for (i = 0; i < 6; i++) +			dev->dev_addr[i] = readb(ioaddr + StationAddr + i); +		if (memcmp(dev->dev_addr, "\0\0\0\0\0", 6) == 0) { +			printk(" (MISSING EEPROM ADDRESS)"); +			/* Fill a temp addr with the "locally administered" bit set. */ +			memcpy(dev->dev_addr, ">Linux", 6); +		} +	} + +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	/* Make certain the descriptor lists are cache-aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +#ifdef USE_IO_OPS +	request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name); +#endif + +	/* Reset the chip to erase previous misconfiguration. */ +	writew(CmdReset, ioaddr + ChipCmd); + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_net_dev; +	root_net_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	if (dev->mem_start) +		option = dev->mem_start; + +	/* The lower four bits are the media type. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 15; +		if (np->default_port) +			np->medialock = 1; +	} +	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) { +		printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" +			   " disabled.\n", dev->name); +		np->duplex_lock = 1; +	} + +	/* The chip-specific entries in the device structure. */ +	dev->open = &netdev_open; +	dev->hard_start_xmit = &start_tx; +	dev->stop = &netdev_close; +	dev->get_stats = &get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; + +	if (np->drv_flags & CanHaveMII) { +		int phy, phy_idx = 0; +		np->phys[0] = 1;		/* Standard for this chip. */ +		for (phy = 1; phy < 32 && phy_idx < 4; phy++) { +			int mii_status = mdio_read(dev, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				np->phys[phy_idx++] = phy; +				np->advertising = mdio_read(dev, phy, 4); +				printk(KERN_INFO "%s: MII PHY found at address %d, status " +					   "0x%4.4x advertising %4.4x Link %4.4x.\n", +					   dev->name, phy, mii_status, np->advertising, +					   mdio_read(dev, phy, 5)); +			} +		} +		np->mii_cnt = phy_idx; +	} + +	/* Allow forcing the media type. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 0x3ff; +		if (np->default_port & 0x330) { +			np->medialock = 1; +			printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +				   (option & 0x300 ? 100 : 10), +				   (np->full_duplex ? "full" : "half")); +			if (np->mii_cnt) +				mdio_write(dev, np->phys[0], 0, +						   ((option & 0x300) ? 0x2000 : 0) | 	/* 100mbps? */ +						   (np->full_duplex ? 0x0100 : 0)); /* Full duplex? */ +		} +	} + +	return dev; +} + + +/* Read and write over the MII Management Data I/O (MDIO) interface. */ + +static int mdio_read(struct net_device *dev, int phy_id, int regnum) +{ +	long ioaddr = dev->base_addr; +	int boguscnt = 1024; + +	/* Wait for a previous command to complete. */ +	while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) +		; +	writeb(0x00, ioaddr + MIICmd); +	writeb(phy_id, ioaddr + MIIPhyAddr); +	writeb(regnum, ioaddr + MIIRegAddr); +	writeb(0x40, ioaddr + MIICmd);			/* Trigger read */ +	boguscnt = 1024; +	while ((readb(ioaddr + MIICmd) & 0x40) && --boguscnt > 0) +		; +	return readw(ioaddr + MIIData); +} + +static void mdio_write(struct net_device *dev, int phy_id, int regnum, int value) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int boguscnt = 1024; + +	if (phy_id == np->phys[0]) { +		switch (regnum) { +		case 0:					/* Is user forcing speed/duplex? */ +			if (value & 0x9000)	/* Autonegotiation. */ +				np->duplex_lock = 0; +			else +				np->full_duplex = (value & 0x0100) ? 1 : 0; +			break; +		case 4: np->advertising = value; break; +		} +	} +	/* Wait for a previous command to complete. */ +	while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) +		; +	writeb(0x00, ioaddr + MIICmd); +	writeb(phy_id, ioaddr + MIIPhyAddr); +	writeb(regnum, ioaddr + MIIRegAddr); +	writew(value, ioaddr + MIIData); +	writeb(0x20, ioaddr + MIICmd);			/* Trigger write. */ +	return; +} + + +static int netdev_open(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	/* Reset the chip. */ +	writew(CmdReset, ioaddr + ChipCmd); + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", +			   dev->name, dev->irq); + +	init_ring(dev); + +	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); +	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); + +	for (i = 0; i < 6; i++) +		writeb(dev->dev_addr[i], ioaddr + StationAddr + i); + +	/* Initialize other registers. */ +	writew(0x0006, ioaddr + PCIBusConfig);	/* Tune configuration??? */ +	/* Configure the FIFO thresholds. */ +	writeb(0x20, ioaddr + TxConfig);	/* Initial threshold 32 bytes */ +	np->tx_thresh = 0x20; +	np->rx_thresh = 0x60;				/* Written in set_rx_mode(). */ + +	if (dev->if_port == 0) +		dev->if_port = np->default_port; + +	set_rx_mode(dev); +	netif_start_tx_queue(dev); + +	np->intr_enable = IntrRxDone | IntrRxErr | IntrRxEmpty | +		IntrRxOverflow| IntrRxDropped| IntrTxDone | IntrTxAbort | +		IntrTxUnderrun | IntrPCIErr | IntrStatsMax | IntrLinkChange | +		IntrMIIChange; +	/* Enable interrupts by setting the interrupt mask. */ +	writew(np->intr_enable, ioaddr + IntrEnable); + +	np->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll; +	if (np->duplex_lock) +		np->chip_cmd |= CmdFDuplex; +	writew(np->chip_cmd, ioaddr + ChipCmd); + +	check_duplex(dev); +	/* The LED outputs of various MII xcvrs should be configured.  */ +	/* For NS or Mison phys, turn on bit 1 in register 0x17 */ +	/* For ESI phys, turn on bit 7 in register 0x17. */ +	mdio_write(dev, np->phys[0], 0x17, mdio_read(dev, np->phys[0], 0x17) | +			   (np->drv_flags & HasESIPhy) ? 0x0080 : 0x0001); + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done netdev_open(), status %4.4x " +			   "MII status: %4.4x.\n", +			   dev->name, readw(ioaddr + ChipCmd), +			   mdio_read(dev, np->phys[0], 1)); + +	/* Set the timer to check for link beat. */ +	init_timer(&np->timer); +	np->timer.expires = jiffies + 2; +	np->timer.data = (unsigned long)dev; +	np->timer.function = &netdev_timer;				/* timer handler */ +	add_timer(&np->timer); + +	return 0; +} + +static void check_duplex(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int mii_reg5 = mdio_read(dev, np->phys[0], 5); +	int negotiated = mii_reg5 & np->advertising; +	int duplex; + +	if (np->duplex_lock  ||  mii_reg5 == 0xffff) +		return; +	duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; +	if (np->full_duplex != duplex) { +		np->full_duplex = duplex; +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" +				   " partner capability of %4.4x.\n", dev->name, +				   duplex ? "full" : "half", np->phys[0], mii_reg5); +		if (duplex) +			np->chip_cmd |= CmdFDuplex; +		else +			np->chip_cmd &= ~CmdFDuplex; +		writew(np->chip_cmd, ioaddr + ChipCmd); +	} +} + +static void netdev_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 10*HZ; + +	if (np->msg_level & NETIF_MSG_TIMER) { +		printk(KERN_DEBUG "%s: VIA Rhine monitor tick, status %4.4x.\n", +			   dev->name, readw(ioaddr + IntrStatus)); +	} +	if (netif_queue_paused(dev) +		&& np->cur_tx - np->dirty_tx > 1 +		&& jiffies - dev->trans_start > TX_TIMEOUT) +		tx_timeout(dev); + +	check_duplex(dev); + +	np->timer.expires = jiffies + next_tick; +	add_timer(&np->timer); +} + +static void tx_timeout(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status " +		   "%4.4x, resetting...\n", +		   dev->name, readw(ioaddr + IntrStatus), +		   mdio_read(dev, np->phys[0], 1)); + +	/* Perhaps we should reinitialize the hardware here. */ +	dev->if_port = 0; +	/* Restart the chip's Tx processes . */ +	writel(virt_to_bus(np->tx_ring + (np->dirty_tx % TX_RING_SIZE)), +		   ioaddr + TxRingPtr); +	writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + +	/* Trigger an immediate transmit demand. */ + +	dev->trans_start = jiffies; +	np->stats.tx_errors++; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	np->tx_full = 0; +	np->cur_rx = np->cur_tx = 0; +	np->dirty_rx = np->dirty_tx = 0; + +	/* Use 1518/+18 if the CRC is transferred. */ +	np->rx_buf_sz = dev->mtu + 14; +	if (np->rx_buf_sz < PKT_BUF_SZ) +		np->rx_buf_sz = PKT_BUF_SZ; +	np->rx_head_desc = &np->rx_ring[0]; + +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].rx_status = 0; +		np->rx_ring[i].desc_length = cpu_to_le32(np->rx_buf_sz); +		np->rx_ring[i].next_desc = virt_to_le32desc(&np->rx_ring[i+1]); +		np->rx_skbuff[i] = 0; +	} +	/* Mark the last entry as wrapping the ring. */ +	np->rx_ring[i-1].next_desc = virt_to_le32desc(&np->rx_ring[0]); + +	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); +		np->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		np->rx_ring[i].addr = virt_to_le32desc(skb->tail); +		np->rx_ring[i].rx_status = cpu_to_le32(DescOwn); +	} +	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + +	for (i = 0; i < TX_RING_SIZE; i++) { +		np->tx_skbuff[i] = 0; +		np->tx_ring[i].tx_status = 0; +		np->tx_ring[i].desc_length = cpu_to_le32(0x00e08000); +		np->tx_ring[i].next_desc = virt_to_le32desc(&np->tx_ring[i+1]); +		np->tx_buf[i] = 0;		/* Allocated as/if needed. */ +	} +	np->tx_ring[i-1].next_desc = virt_to_le32desc(&np->tx_ring[0]); + +	return; +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned entry; + +	/* Block a timer-based transmit from overlapping.  This happens when +	   packets are presumed lost, and we use this check the Tx status. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tx_timeout(dev); +		return 1; +	} + +	/* Caution: the write order is important here, set the descriptor word +	   with the "ownership" bit last.  No SMP locking is needed if the +	   cur_tx is incremented after the descriptor is consistent.  */ + +	/* Calculate the next Tx descriptor entry. */ +	entry = np->cur_tx % TX_RING_SIZE; + +	np->tx_skbuff[entry] = skb; + +	if ((np->drv_flags & ReqTxAlign)  && ((long)skb->data & 3)) { +		/* Must use alignment buffer. */ +		if (np->tx_buf[entry] == NULL && +			(np->tx_buf[entry] = kmalloc(PKT_BUF_SZ, GFP_KERNEL)) == NULL) +			return 1; +		memcpy(np->tx_buf[entry], skb->data, skb->len); +		np->tx_ring[entry].addr = virt_to_le32desc(np->tx_buf[entry]); +	} else +		np->tx_ring[entry].addr = virt_to_le32desc(skb->data); +	/* Explicitly flush packet data cache lines here. */ + +	np->tx_ring[entry].desc_length = +		cpu_to_le32(0x00E08000 | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); +	np->tx_ring[entry].tx_status = cpu_to_le32(DescOwn); + +	np->cur_tx++; + +	/* Explicitly flush descriptor cache lines here. */ + +	/* Wake the potentially-idle transmit channel. */ +	writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); + +	if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) { +		np->tx_full = 1; +		/* Check for a just-cleared queue. */ +		if (np->cur_tx - (volatile unsigned int)np->dirty_tx +			< TX_QUEUE_LEN - 2) { +			np->tx_full = 0; +			netif_unpause_tx_queue(dev); +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ + +	dev->trans_start = jiffies; + +	if (np->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", +			   dev->name, np->cur_tx, entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct netdev_private *np = (void *)dev->priv; +	long ioaddr = dev->base_addr; +	int boguscnt = np->max_interrupt_work; + +	do { +		u32 intr_status = readw(ioaddr + IntrStatus); + +		/* Acknowledge all of the current interrupt sources ASAP. */ +		writew(intr_status & 0xffff, ioaddr + IntrStatus); + +		if (np->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", +				   dev->name, intr_status); + +		if (intr_status == 0) +			break; + +		if (intr_status & (IntrRxDone | IntrRxErr | IntrRxDropped | +						   IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf)) +			netdev_rx(dev); + +		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { +			int entry = np->dirty_tx % TX_RING_SIZE; +			int txstatus = le32_to_cpu(np->tx_ring[entry].tx_status); +			if (txstatus & DescOwn) +				break; +			if (np->msg_level & NETIF_MSG_TX_DONE) +				printk(KERN_DEBUG "  Tx scavenge %d status %4.4x.\n", +					   entry, txstatus); +			if (txstatus & 0x8000) { +				if (np->msg_level & NETIF_MSG_TX_ERR) +					printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n", +						   dev->name, txstatus); +				np->stats.tx_errors++; +				if (txstatus & 0x0400) np->stats.tx_carrier_errors++; +				if (txstatus & 0x0200) np->stats.tx_window_errors++; +				if (txstatus & 0x0100) np->stats.tx_aborted_errors++; +				if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++; +				if (txstatus & 0x0002) np->stats.tx_fifo_errors++; +#ifdef ETHER_STATS +				if (txstatus & 0x0100) np->stats.collisions16++; +#endif +				/* Transmitter restarted in 'abnormal' handler. */ +			} else { +#ifdef ETHER_STATS +				if (txstatus & 0x0001) np->stats.tx_deferred++; +#endif +				if (np->drv_flags & HasV1TxStat) +					np->stats.collisions += (txstatus >> 3) & 15; +				else +					np->stats.collisions += txstatus & 15; +#if defined(NETSTATS_VER2) +				np->stats.tx_bytes += np->tx_skbuff[entry]->len; +#endif +				np->stats.tx_packets++; +			} +			/* Free the original skb. */ +			dev_free_skb_irq(np->tx_skbuff[entry]); +			np->tx_skbuff[entry] = 0; +		} +		/* Note the 4 slot hysteresis in mark the queue non-full. */ +		if (np->tx_full  &&  np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { +			/* The ring is no longer full, allow new TX entries. */ +			np->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange | +						   IntrStatsMax | IntrTxAbort | IntrTxUnderrun)) +			netdev_error(dev, intr_status); + +		if (--boguscnt < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", +				   dev->name, intr_status); +			break; +		} +	} while (1); + +	if (np->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readw(ioaddr + IntrStatus)); + +	return; +} + +/* This routine is logically part of the interrupt handler, but isolated +   for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int entry = np->cur_rx % RX_RING_SIZE; +	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx; + +	if (np->msg_level & NETIF_MSG_RX_STATUS) { +		printk(KERN_DEBUG " In netdev_rx(), entry %d status %8.8x.\n", +			   entry, np->rx_head_desc->rx_status); +	} + +	/* If EOP is set on the next entry, it's a new packet. Send it up. */ +	while ( ! (np->rx_head_desc->rx_status & cpu_to_le32(DescOwn))) { +		struct rx_desc *desc = np->rx_head_desc; +		u32 desc_status = le32_to_cpu(desc->rx_status); +		int data_size = desc_status >> 16; + +		if (np->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  netdev_rx() status is %4.4x.\n", +				   desc_status); +		if (--boguscnt < 0) +			break; +		if ( (desc_status & (RxWholePkt | RxErr)) !=  RxWholePkt) { +			if ((desc_status & RxWholePkt) !=  RxWholePkt) { +				printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " +					   "multiple buffers, entry %#x length %d status %4.4x!\n", +					   dev->name, np->cur_rx, data_size, desc_status); +				printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", +					   dev->name, np->rx_head_desc, +					   &np->rx_ring[np->cur_rx % RX_RING_SIZE]); +				np->stats.rx_length_errors++; +			} else if (desc_status & RxErr) { +				/* There was a error. */ +				if (np->msg_level & NETIF_MSG_RX_ERR) +					printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n", +						   desc_status); +				np->stats.rx_errors++; +				if (desc_status & 0x0030) np->stats.rx_length_errors++; +				if (desc_status & 0x0048) np->stats.rx_fifo_errors++; +				if (desc_status & 0x0004) np->stats.rx_frame_errors++; +				if (desc_status & 0x0002) np->stats.rx_crc_errors++; +			} +		} else { +			struct sk_buff *skb; +			/* Length should omit the CRC */ +			int pkt_len = data_size - 4; + +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < np->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +#if HAS_IP_COPYSUM			/* Call copy + cksum if available. */ +				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +			} else { +				skb_put(skb = np->rx_skbuff[entry], pkt_len); +				np->rx_skbuff[entry] = NULL; +			} +			skb->protocol = eth_type_trans(skb, dev); +			{					/* Use hardware checksum info. */ +				int rxtype = le32_to_cpu(desc->desc_length); +				int csum_bits = rxtype & RxTypeCsumMask; +				if (csum_bits == RxTypeUDPSumOK || +					csum_bits == RxTypeTCPSumOK) +					skb->ip_summed = CHECKSUM_UNNECESSARY; +			} +			netif_rx(skb); +			dev->last_rx = jiffies; +#if defined(NETSTATS_VER2) +			np->stats.rx_bytes += pkt_len; +#endif +			np->stats.rx_packets++; +		} +		entry = (++np->cur_rx) % RX_RING_SIZE; +		np->rx_head_desc = &np->rx_ring[entry]; +	} + +	/* Refill the Rx ring buffers. */ +	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { +		struct sk_buff *skb; +		entry = np->dirty_rx % RX_RING_SIZE; +		if (np->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(np->rx_buf_sz); +			np->rx_skbuff[entry] = skb; +			if (skb == NULL) +				break;			/* Better luck next round. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			np->rx_ring[entry].addr = virt_to_le32desc(skb->tail); +		} +		np->rx_ring[entry].rx_status = cpu_to_le32(DescOwn); +	} + +	/* Pre-emptively restart Rx engine. */ +	writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd); +	return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (intr_status & (IntrMIIChange | IntrLinkChange)) { +		if (readb(ioaddr + MIIStatus) & 0x02) { +			/* Link failed, restart autonegotiation. */ +			if (np->drv_flags & HasDavicomPhy) +				mdio_write(dev, np->phys[0], 0, 0x3300); +			netif_link_down(dev); +		} else { +			netif_link_up(dev); +			check_duplex(dev); +		} +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_ERR "%s: MII status changed: Autonegotiation " +				   "advertising %4.4x  partner %4.4x.\n", dev->name, +			   mdio_read(dev, np->phys[0], 4), +			   mdio_read(dev, np->phys[0], 5)); +	} +	if (intr_status & IntrStatsMax) { +		np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs); +		np->stats.rx_missed_errors	+= readw(ioaddr + RxMissed); +		writel(0, ioaddr + RxMissed); +	} +	if (intr_status & IntrTxAbort) { +		/* Stats counted in Tx-done handler, just restart Tx. */ +		writel(virt_to_bus(&np->tx_ring[np->dirty_tx % TX_RING_SIZE]), +			   ioaddr + TxRingPtr); +		writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); +	} +	if (intr_status & IntrTxUnderrun) { +		if (np->tx_thresh < 0xE0) +			writeb(np->tx_thresh += 0x20, ioaddr + TxConfig); +		if (np->msg_level & NETIF_MSG_TX_ERR) +			printk(KERN_INFO "%s: Transmitter underrun, increasing Tx " +				   "threshold setting to %2.2x.\n", dev->name, np->tx_thresh); +	} +	if ((intr_status & ~(IntrLinkChange | IntrMIIChange | IntrStatsMax | +						 IntrTxAbort|IntrTxAborted | IntrNormalSummary)) +		 && (np->msg_level & NETIF_MSG_DRV)) { +		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", +			   dev->name, intr_status); +		/* Recovery for other fault sources not known. */ +		writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); +	} +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	/* Nominally we should lock this segment of code for SMP, although +	   the vulnerability window is very small and statistics are +	   non-critical. */ +	np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs); +	np->stats.rx_missed_errors	+= readw(ioaddr + RxMissed); +	writel(0, ioaddr + RxMissed); + +	return &np->stats; +} + +/* The big-endian AUTODIN II ethernet CRC calculation. +   N.B. Do not use for bulk data, use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ +	int crc = -1; + +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 0; bit < 8; bit++, current_octet >>= 1) { +			crc = (crc << 1) ^ +				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); +		} +	} +	return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u32 mc_filter[2];			/* Multicast hash filter */ +	u8 rx_mode;					/* Note: 0x02=accept runt, 0x01=accept errs */ + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		rx_mode = 0x1C; +	} else if ((dev->mc_count > np->multicast_filter_limit) +			   ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to match, or accept all multicasts. */ +		writel(0xffffffff, ioaddr + MulticastFilter0); +		writel(0xffffffff, ioaddr + MulticastFilter1); +		rx_mode = 0x0C; +	} else { +		struct dev_mc_list *mclist; +		int i; +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26, +					mc_filter); +		} +		writel(mc_filter[0], ioaddr + MulticastFilter0); +		writel(mc_filter[1], ioaddr + MulticastFilter1); +		rx_mode = 0x0C; +	} +	writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig); +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = np->phys[0] & 0x1f; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		/* Note: forced media tracking is done in mdio_write(). */ +		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + +static int netdev_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFDOWN) +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n", +			   dev->name, readw(ioaddr + ChipCmd)); + +	/* Switch to loopback mode to avoid hardware races. */ +	writeb(np->tx_thresh | 0x01, ioaddr + TxConfig); + +	/* Disable interrupts by clearing the interrupt mask. */ +	writew(0x0000, ioaddr + IntrEnable); + +	/* Stop the chip's Tx and Rx processes. */ +	np->chip_cmd = CmdStop; +	writew(CmdStop, ioaddr + ChipCmd); + +	del_timer(&np->timer); + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		np->rx_ring[i].rx_status = 0; +		np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ +		if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			np->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(np->rx_skbuff[i]); +		} +		np->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (np->tx_skbuff[i]) +			dev_free_skb(np->tx_skbuff[i]); +		np->tx_skbuff[i] = 0; +		if (np->tx_buf[i]) { +			kfree(np->tx_buf[i]); +			np->tx_buf[i] = 0; +		} +	} + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static int via_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: +		/* Disable interrupts, stop Tx and Rx. */ +		writew(0x0000, ioaddr + IntrEnable); +		/* Stop the chip's Tx and Rx processes. */ +		writew(CmdStop, ioaddr + ChipCmd); +		break; +	case DRV_RESUME: +		/* This is incomplete: the actions are very chip specific. */ +		set_rx_mode(dev); +		netif_start_tx_queue(dev); +		writew(np->chip_cmd, ioaddr + ChipCmd); +		writew(np->intr_enable, ioaddr + IntrEnable); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			/* Some, but not all, kernel versions close automatically. */ +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_net_dev; *devp; devp = next) { +			next = &((struct netdev_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	} + +	return 0; +} + + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return pci_drv_register(&via_rhine_drv_id, NULL); +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&via_rhine_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_net_dev) { +		struct netdev_private *np = (void *)(root_net_dev->priv); +		unregister_netdev(root_net_dev); +#ifdef USE_IO_OPS +		release_region(root_net_dev->base_addr, pci_tbl[np->chip_id].io_size); +#else +		iounmap((char *)(root_net_dev->base_addr)); +#endif +		next_dev = np->next_module; +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_net_dev); +		root_net_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` via-rhine.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c via-rhine.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c via-rhine.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/wavelan.c b/linux/src/drivers/net/wavelan.c new file mode 100644 index 0000000..dbe8815 --- /dev/null +++ b/linux/src/drivers/net/wavelan.c @@ -0,0 +1,4373 @@ +/* + *	WaveLAN ISA driver + * + *		Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyright follows (also see the end of this file). + * See wavelan.p.h for details. + */ + +/* + * AT&T GIS (nee NCR) WaveLAN card: + *	An Ethernet-like radio transceiver + *	controlled by an Intel 82586 coprocessor. + */ + +#include "wavelan.p.h"		/* Private header */ + +/************************* MISC SUBROUTINES **************************/ +/* + * Subroutines which won't fit in one of the following category + * (WaveLAN modem or i82586) + */ + +/*------------------------------------------------------------------*/ +/* + * Wrapper for disabling interrupts. + */ +static inline unsigned long +wv_splhi(void) +{ +  unsigned long flags; + +  save_flags(flags); +  cli(); + +  return(flags); +} + +/*------------------------------------------------------------------*/ +/* + * Wrapper for re-enabling interrupts. + */ +static inline void +wv_splx(unsigned long	flags) +{ +  restore_flags(flags); +} + +/*------------------------------------------------------------------*/ +/* + * Translate irq number to PSA irq parameter + */ +static u_char +wv_irq_to_psa(int	irq) +{ +  if(irq < 0 || irq >= NELS(irqvals)) +    return 0; + +  return irqvals[irq]; +} + +/*------------------------------------------------------------------*/ +/* + * Translate PSA irq parameter to irq number  + */ +static int +wv_psa_to_irq(u_char	irqval) +{ +  int	irq; + +  for(irq = 0; irq < NELS(irqvals); irq++) +    if(irqvals[irq] == irqval) +      return irq; + +  return -1; +} + +#ifdef STRUCT_CHECK +/*------------------------------------------------------------------*/ +/* + * Sanity routine to verify the sizes of the various WaveLAN interface + * structures. + */ +static char * +wv_struct_check(void) +{ +#define	SC(t,s,n)	if (sizeof(t) != s) return(n); + +  SC(psa_t, PSA_SIZE, "psa_t"); +  SC(mmw_t, MMW_SIZE, "mmw_t"); +  SC(mmr_t, MMR_SIZE, "mmr_t"); +  SC(ha_t, HA_SIZE, "ha_t"); + +#undef	SC + +  return((char *) NULL); +} /* wv_struct_check */ +#endif	/* STRUCT_CHECK */ + +/********************* HOST ADAPTER SUBROUTINES *********************/ +/* + * Useful subroutines to manage the WaveLAN ISA interface + * + * One major difference with the PCMCIA hardware (except the port mapping) + * is that we have to keep the state of the Host Control Register + * because of the interrupt enable & bus size flags. + */ + +/*------------------------------------------------------------------*/ +/* + * Read from card's Host Adaptor Status Register. + */ +static inline u_short +hasr_read(u_long	ioaddr) +{ +  return(inw(HASR(ioaddr))); +} /* hasr_read */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. + */ +static inline void +hacr_write(u_long	ioaddr, +	   u_short	hacr) +{ +  outw(hacr, HACR(ioaddr)); +} /* hacr_write */ + +/*------------------------------------------------------------------*/ +/* + * Write to card's Host Adapter Command Register. Include a delay for + * those times when it is needed. + */ +static inline void +hacr_write_slow(u_long	ioaddr, +		u_short	hacr) +{ +  hacr_write(ioaddr, hacr); +  /* delay might only be needed sometimes */ +  udelay(1000L); +} /* hacr_write_slow */ + +/*------------------------------------------------------------------*/ +/* + * Set the channel attention bit. + */ +static inline void +set_chan_attn(u_long	ioaddr, +	      u_short	hacr) +{ +  hacr_write(ioaddr, hacr | HACR_CA); +} /* set_chan_attn */ + +/*------------------------------------------------------------------*/ +/* + * Reset, and then set host adaptor into default mode. + */ +static inline void +wv_hacr_reset(u_long	ioaddr) +{ +  hacr_write_slow(ioaddr, HACR_RESET); +  hacr_write(ioaddr, HACR_DEFAULT); +} /* wv_hacr_reset */ + +/*------------------------------------------------------------------*/ +/* + * Set the i/o transfer over the ISA bus to 8 bits mode + */ +static inline void +wv_16_off(u_long	ioaddr, +	  u_short	hacr) +{ +  hacr &= ~HACR_16BITS; +  hacr_write(ioaddr, hacr); +} /* wv_16_off */ + +/*------------------------------------------------------------------*/ +/* + * Set the i/o transfer over the ISA bus to 8 bits mode + */ +static inline void +wv_16_on(u_long		ioaddr, +	 u_short	hacr) +{ +  hacr |= HACR_16BITS; +  hacr_write(ioaddr, hacr); +} /* wv_16_on */ + +/*------------------------------------------------------------------*/ +/* + * Disable interrupts on the WaveLAN hardware + */ +static inline void +wv_ints_off(device *	dev) +{ +  net_local *	lp = (net_local *)dev->priv; +  u_long	ioaddr = dev->base_addr; +  u_long	x; + +  x = wv_splhi(); + +  lp->hacr &= ~HACR_INTRON; +  hacr_write(ioaddr, lp->hacr); + +  wv_splx(x); +} /* wv_ints_off */ + +/*------------------------------------------------------------------*/ +/* + * Enable interrupts on the WaveLAN hardware + */ +static inline void +wv_ints_on(device *	dev) +{ +  net_local *	lp = (net_local *)dev->priv; +  u_long	ioaddr = dev->base_addr; +  u_long	x; + +  x = wv_splhi(); + +  lp->hacr |= HACR_INTRON; +  hacr_write(ioaddr, lp->hacr); + +  wv_splx(x); +} /* wv_ints_on */ + +/******************* MODEM MANAGEMENT SUBROUTINES *******************/ +/* + * Useful subroutines to manage the modem of the WaveLAN + */ + +/*------------------------------------------------------------------*/ +/* + * Read the Parameter Storage Area from the WaveLAN card's memory + */ +/* + * Read bytes from the PSA. + */ +static void +psa_read(u_long		ioaddr, +	 u_short	hacr, +	 int		o,	/* offset in PSA */ +	 u_char *	b,	/* buffer to fill */ +	 int		n)	/* size to read */ +{ +  wv_16_off(ioaddr, hacr); + +  while(n-- > 0) +    { +      outw(o, PIOR2(ioaddr)); +      o++; +      *b++ = inb(PIOP2(ioaddr)); +    } + +  wv_16_on(ioaddr, hacr); +} /* psa_read */ + +/*------------------------------------------------------------------*/ +/* + * Write the Paramter Storage Area to the WaveLAN card's memory + */ +static void +psa_write(u_long	ioaddr, +	  u_short	hacr, +	  int		o,	/* Offset in psa */ +	  u_char *	b,	/* Buffer in memory */ +	  int		n)	/* Length of buffer */ +{ +  int	count = 0; + +  wv_16_off(ioaddr, hacr); + +  while(n-- > 0) +    { +      outw(o, PIOR2(ioaddr)); +      o++; + +      outb(*b, PIOP2(ioaddr)); +      b++; + +      /* Wait for the memory to finish its write cycle */ +      count = 0; +      while((count++ < 100) && +	    (hasr_read(ioaddr) & HASR_PSA_BUSY)) +	udelay(1000); +    } + +  wv_16_on(ioaddr, hacr); +} /* psa_write */ + +#ifdef PSA_CRC +/*------------------------------------------------------------------*/ +/* + * Calculate the PSA CRC (not tested yet) + * As the WaveLAN drivers don't use the CRC, I won't use it either. + * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code + * NOTE: By specifying a length including the CRC position the + * returned value should be zero. (i.e. a correct checksum in the PSA) + */ +static u_short +psa_crc(u_short *	psa,	/* The PSA */ +	int		size)	/* Number of short for CRC */ +{ +  int		byte_cnt;	/* Loop on the PSA */ +  u_short	crc_bytes = 0;	/* Data in the PSA */ +  int		bit_cnt;	/* Loop on the bits of the short */ + +  for(byte_cnt = 0; byte_cnt <= size; byte_cnt++ ) +    { +      crc_bytes ^= psa[byte_cnt];	/* Its an xor */ + +      for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ ) +	{ +	  if(crc_bytes & 0x0001) +	    crc_bytes = (crc_bytes >> 1) ^ 0xA001; +	  else +	    crc_bytes >>= 1 ; +        } +    } + +  return crc_bytes; +} /* psa_crc */ +#endif	/* PSA_CRC */ + +/*------------------------------------------------------------------*/ +/* + * Write 1 byte to the MMC. + */ +static inline void +mmc_out(u_long		ioaddr, +	u_short		o, +	u_char		d) +{ +  /* Wait for MMC to go idle */ +  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY) +    ; + +  outw((u_short) (((u_short) d << 8) | (o << 1) | 1), +       MMCR(ioaddr)); +} + +/*------------------------------------------------------------------*/ +/* + * Routine to write bytes to the Modem Management Controller. + * We start by the end because it is the way it should be ! + */ +static inline void +mmc_write(u_long	ioaddr, +	  u_char	o, +	  u_char *	b, +	  int		n) +{ +  o += n; +  b += n; + +  while(n-- > 0 ) +    mmc_out(ioaddr, --o, *(--b)); +} /* mmc_write */ + +/*------------------------------------------------------------------*/ +/* + * Read 1 byte from the MMC. + * Optimised version for 1 byte, avoid using memory... + */ +static inline u_char +mmc_in(u_long	ioaddr, +       u_short	o) +{ +  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY) +    ; +  outw(o << 1, MMCR(ioaddr)); + +  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY) +    ; +  return (u_char) (inw(MMCR(ioaddr)) >> 8); +} + +/*------------------------------------------------------------------*/ +/* + * Routine to read bytes from the Modem Management Controller. + * The implementation is complicated by a lack of address lines, + * which prevents decoding of the low-order bit. + * (code has just been moved in the above function) + * We start by the end because it is the way it should be ! + */ +static inline void +mmc_read(u_long		ioaddr, +	 u_char		o, +	 u_char *	b, +	 int		n) +{ +  o += n; +  b += n; + +  while(n-- > 0) +    *(--b) = mmc_in(ioaddr, --o); +} /* mmc_read */ + +/*------------------------------------------------------------------*/ +/* + * Get the type of encryption available... + */ +static inline int +mmc_encr(u_long		ioaddr)	/* i/o port of the card */ +{ +  int	temp; + +  temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail)); +  if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES)) +    return 0; +  else +    return temp; +} + +/*------------------------------------------------------------------*/ +/* + * Wait for the frequency EEPROM to complete a command... + * I hope this one will be optimally inlined... + */ +static inline void +fee_wait(u_long		ioaddr,	/* i/o port of the card */ +	 int		delay,	/* Base delay to wait for */ +	 int		number)	/* Number of time to wait */ +{ +  int		count = 0;	/* Wait only a limited time */ + +  while((count++ < number) && +	(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY)) +    udelay(delay); +} + +/*------------------------------------------------------------------*/ +/* + * Read bytes from the frequency EEPROM (frequency select cards). + */ +static void +fee_read(u_long		ioaddr,	/* i/o port of the card */ +	 u_short	o,	/* destination offset */ +	 u_short *	b,	/* data buffer */ +	 int		n)	/* number of registers */ +{ +  b += n;		/* Position at the end of the area */ + +  /* Write the address */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); + +  /* Loop on all buffer */ +  while(n-- > 0) +    { +      /* Write the read command */ +      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ); + +      /* Wait until EEPROM is ready (should be quick!) */ +      fee_wait(ioaddr, 10, 100); + +      /* Read the value */ +      *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) | +	      mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); +    } +} + +#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */ + +/*------------------------------------------------------------------*/ +/* + * Write bytes from the Frequency EEPROM (frequency select cards). + * This is a bit complicated, because the frequency EEPROM has to + * be unprotected and the write enabled. + * Jean II + */ +static void +fee_write(u_long	ioaddr,	/* i/o port of the card */ +	  u_short	o,	/* destination offset */ +	  u_short *	b,	/* data buffer */ +	  int		n)	/* number of registers */ +{ +  b += n;		/* Position at the end of the area */ + +#ifdef EEPROM_IS_PROTECTED	/* disabled */ +#ifdef DOESNT_SEEM_TO_WORK	/* disabled */ +  /* Ask to read the protected register */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD); + +  fee_wait(ioaddr, 10, 100); + +  /* Read the protected register */ +  printk("Protected 2 : %02X-%02X\n", +	 mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)), +	 mmc_in(ioaddr, mmroff(0, mmr_fee_data_l))); +#endif	/* DOESNT_SEEM_TO_WORK */ + +  /* Enable protected register */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); +  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN); + +  fee_wait(ioaddr, 10, 100); + +  /* Unprotect area */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n); +  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); +#ifdef DOESNT_SEEM_TO_WORK	/* disabled */ +  /* Or use : */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR); +#endif	/* DOESNT_SEEM_TO_WORK */ + +  fee_wait(ioaddr, 10, 100); +#endif	/* EEPROM_IS_PROTECTED */ + +  /* Write enable */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN); +  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN); + +  fee_wait(ioaddr, 10, 100); + +  /* Write the EEPROM address */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1); + +  /* Loop on all buffer */ +  while(n-- > 0) +    { +      /* Write the value */ +      mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8); +      mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF); + +      /* Write the write command */ +      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE); + +      /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */ +      udelay(10000); +      fee_wait(ioaddr, 10, 100); +    } + +  /* Write disable */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS); +  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS); + +  fee_wait(ioaddr, 10, 100); + +#ifdef EEPROM_IS_PROTECTED	/* disabled */ +  /* Reprotect EEPROM */ +  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00); +  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE); + +  fee_wait(ioaddr, 10, 100); +#endif	/* EEPROM_IS_PROTECTED */ +} +#endif	/* WIRELESS_EXT */ + +/************************ I82586 SUBROUTINES *************************/ +/* + * Usefull subroutines to manage the Ethernet controler + */ + +/*------------------------------------------------------------------*/ +/* + * Read bytes from the on-board RAM. + * Why inlining this function make it fail ??? + */ +static /*inline*/ void +obram_read(u_long	ioaddr, +	   u_short	o, +	   u_char *	b, +	   int		n) +{ +  outw(o, PIOR1(ioaddr)); +  insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); +} + +/*------------------------------------------------------------------*/ +/* + * Write bytes to the on-board RAM. + */ +static inline void +obram_write(u_long	ioaddr, +	    u_short	o, +	    u_char *	b, +	    int		n) +{ +  outw(o, PIOR1(ioaddr)); +  outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1); +} + +/*------------------------------------------------------------------*/ +/* + * Acknowledge the reading of the status issued by the i82586 + */ +static void +wv_ack(device *		dev) +{ +  net_local *	lp = (net_local *)dev->priv; +  u_long	ioaddr = dev->base_addr; +  u_short	scb_cs; +  int		i; + +  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), +	     (unsigned char *) &scb_cs, sizeof(scb_cs)); +  scb_cs &= SCB_ST_INT; + +  if(scb_cs == 0) +    return; + +  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), +	      (unsigned char *) &scb_cs, sizeof(scb_cs)); + +  set_chan_attn(ioaddr, lp->hacr); + +  for(i = 1000; i > 0; i--) +    { +      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs)); +      if(scb_cs == 0) +	break; + +      udelay(10); +    } +  udelay(100); + +#ifdef DEBUG_CONFIG_ERROR +  if(i <= 0) +    printk(KERN_INFO "%s: wv_ack(): board not accepting command.\n", +	   dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Set channel attention bit and busy wait until command has + * completed, then acknowledge the command completion. + */ +static inline int +wv_synchronous_cmd(device *	dev, +		   const char *	str) +{ +  net_local *	lp = (net_local *)dev->priv; +  u_long	ioaddr = dev->base_addr; +  u_short	scb_cmd; +  ach_t		cb; +  int		i; + +  scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO; +  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), +	      (unsigned char *) &scb_cmd, sizeof(scb_cmd)); + +  set_chan_attn(ioaddr, lp->hacr); + +  for (i = 1000; i > 0; i--) +    { +      obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb)); +      if (cb.ac_status & AC_SFLD_C) +	break; + +      udelay(10); +    } +  udelay(100); + +  if(i <= 0 || !(cb.ac_status & AC_SFLD_OK)) +    { +#ifdef DEBUG_CONFIG_ERROR +      printk(KERN_INFO "%s: %s failed; status = 0x%x\n", +	     dev->name, str, cb.ac_status); +#endif +#ifdef DEBUG_I82586_SHOW +      wv_scb_show(ioaddr); +#endif +      return -1; +    } + +  /* Ack the status */ +  wv_ack(dev); + +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Configuration commands completion interrupt. + * Check if done, and if ok... + */ +static inline int +wv_config_complete(device *	dev, +		   u_long	ioaddr, +		   net_local *	lp) +{ +  unsigned short	mcs_addr; +  unsigned short	status; +  int			ret; + +#ifdef DEBUG_INTERRUPT_TRACE +  printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name); +#endif + +  mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t) +    + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t); + +  /* Read the status of the last command (set mc list) */ +  obram_read(ioaddr, acoff(mcs_addr, ac_status), (unsigned char *)&status, sizeof(status)); + +  /* If not completed -> exit */ +  if((status & AC_SFLD_C) == 0) +    ret = 0;		/* Not ready to be scrapped */ +  else +    { +#ifdef DEBUG_CONFIG_ERROR +      unsigned short	cfg_addr; +      unsigned short	ias_addr; + +      /* Check mc_config command */ +      if(status & AC_SFLD_OK != 0) +	printk(KERN_INFO "wv_config_complete(): set_multicast_address failed; status = 0x%x\n", +	       dev->name, str, status); + +      /* check ia-config command */ +      ias_addr = mcs_addr - sizeof(ac_ias_t); +      obram_read(ioaddr, acoff(ias_addr, ac_status), (unsigned char *)&status, sizeof(status)); +      if(status & AC_SFLD_OK != 0) +	printk(KERN_INFO "wv_config_complete(): set_MAC_address; status = 0x%x\n", +	       dev->name, str, status); + +      /* Check config command */ +      cfg_addr = ias_addr - sizeof(ac_cfg_t); +      obram_read(ioaddr, acoff(cfg_addr, ac_status), (unsigned char *)&status, sizeof(status)); +      if(status & AC_SFLD_OK != 0) +	printk(KERN_INFO "wv_config_complete(): configure; status = 0x%x\n", +	       dev->name, str, status); +#endif	/* DEBUG_CONFIG_ERROR */ + +      ret = 1;		/* Ready to be scrapped */ +    } + +#ifdef DEBUG_INTERRUPT_TRACE +  printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name, ret); +#endif +  return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Command completion interrupt. + * Reclaim as many freed tx buffers as we can. + */ +static int +wv_complete(device *	dev, +	    u_long	ioaddr, +	    net_local *	lp) +{ +  int	nreaped = 0; + +#ifdef DEBUG_INTERRUPT_TRACE +  printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name); +#endif + +  /* Loop on all the transmit buffers */ +  while(lp->tx_first_in_use != I82586NULL) +    { +      unsigned short	tx_status; + +      /* Read the first transmit buffer */ +      obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status)); + +      /* Hack for reconfiguration... */ +      if(tx_status == 0xFFFF) +	if(!wv_config_complete(dev, ioaddr, lp)) +	  break;	/* Not completed */ + +      /* If not completed -> exit */ +      if((tx_status & AC_SFLD_C) == 0) +	break; + +      /* We now remove this buffer */ +      nreaped++; +      --lp->tx_n_in_use; + +/* +if (lp->tx_n_in_use > 0) +	printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]); +*/ + +      /* Was it the last one ? */ +      if(lp->tx_n_in_use <= 0) +	lp->tx_first_in_use = I82586NULL; +      else +	{ +	  /* Next one in the chain */ +	  lp->tx_first_in_use += TXBLOCKZ; +	  if(lp->tx_first_in_use >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) +	    lp->tx_first_in_use -= NTXBLOCKS * TXBLOCKZ; +	} + +      /* Hack for reconfiguration... */ +      if(tx_status == 0xFFFF) +	continue; + +      /* Now, check status of the finished command */ +      if(tx_status & AC_SFLD_OK) +	{ +	  int	ncollisions; + +	  lp->stats.tx_packets++; +	  ncollisions = tx_status & AC_SFLD_MAXCOL; +	  lp->stats.collisions += ncollisions; +#ifdef DEBUG_INTERRUPT_INFO +	  if(ncollisions > 0) +	    printk(KERN_DEBUG "%s: wv_complete(): tx completed after %d collisions.\n", +		   dev->name, ncollisions); +#endif +	} +      else +	{ +	  lp->stats.tx_errors++; +#ifndef IGNORE_NORMAL_XMIT_ERRS +	  if(tx_status & AC_SFLD_S10) +	    { +	      lp->stats.tx_carrier_errors++; +#ifdef DEBUG_INTERRUPT_ERROR +	      printk(KERN_INFO "%s: wv_complete(): tx error: no CS.\n", +		     dev->name); +#endif +	    } +#endif	/* IGNORE_NORMAL_XMIT_ERRS */ +	  if(tx_status & AC_SFLD_S9) +	    { +	      lp->stats.tx_carrier_errors++; +#ifdef DEBUG_INTERRUPT_ERROR +	      printk(KERN_INFO "%s: wv_complete(): tx error: lost CTS.\n", +		     dev->name); +#endif +	    } +	  if(tx_status & AC_SFLD_S8) +	    { +	      lp->stats.tx_fifo_errors++; +#ifdef DEBUG_INTERRUPT_ERROR +	      printk(KERN_INFO "%s: wv_complete(): tx error: slow DMA.\n", +		     dev->name); +#endif +	    } +#ifndef IGNORE_NORMAL_XMIT_ERRS +	  if(tx_status & AC_SFLD_S6) +	    { +	      lp->stats.tx_heartbeat_errors++; +#ifdef DEBUG_INTERRUPT_ERROR +	      printk(KERN_INFO "%s: wv_complete(): tx error: heart beat.\n", +		     dev->name); +#endif +	    } +	  if(tx_status & AC_SFLD_S5) +	    { +	      lp->stats.tx_aborted_errors++; +#ifdef DEBUG_INTERRUPT_ERROR +	      printk(KERN_INFO "%s: wv_complete(): tx error: too many collisions.\n", +		     dev->name); +#endif +	    } +#endif	/* IGNORE_NORMAL_XMIT_ERRS */ +	} + +#ifdef DEBUG_INTERRUPT_INFO +      printk(KERN_DEBUG "%s: wv_complete(): tx completed, tx_status 0x%04x\n", +	     dev->name, tx_status); +#endif +    } + +#ifdef DEBUG_INTERRUPT_INFO +  if(nreaped > 1) +    printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n", dev->name, nreaped); +#endif + +  /* +   * Inform upper layers. +   */ +  if(lp->tx_n_in_use < NTXBLOCKS - 1) +    { +      dev->tbusy = 0; +      mark_bh(NET_BH); +    } + +#ifdef DEBUG_INTERRUPT_TRACE +  printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name); +#endif +  return nreaped; +} + +/*------------------------------------------------------------------*/ +/* + * Reconfigure the i82586, or at least ask for it... + * Because wv_82586_config use a transmission buffer, we must do it + * when we are sure that there is one left, so we do it now + * or in wavelan_packet_xmit() (I can't find any better place, + * wavelan_interrupt is not an option...), so you may experience + * some delay sometime... + */ +static inline void +wv_82586_reconfig(device *	dev) +{ +  net_local *	lp = (net_local *)dev->priv; + +  /* Check if we can do it now ! */ +  if(!(dev->start) || (set_bit(0, (void *)&dev->tbusy) != 0)) +    { +      lp->reconfig_82586 = 1; +#ifdef DEBUG_CONFIG_INFO +      printk(KERN_DEBUG "%s: wv_82586_reconfig(): delayed (busy = %ld, start = %d)\n", +	     dev->name, dev->tbusy, dev->start); +#endif +    } +  else +    wv_82586_config(dev); +} + +/********************* DEBUG & INFO SUBROUTINES *********************/ +/* + * This routines are used in the code to show debug informations. + * Most of the time, it dump the content of hardware structures... + */ + +#ifdef DEBUG_PSA_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted contents of the Parameter Storage Area. + */ +static void +wv_psa_show(psa_t *	p) +{ +  printk(KERN_DEBUG "##### WaveLAN psa contents: #####\n"); +  printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n", +	 p->psa_io_base_addr_1, +	 p->psa_io_base_addr_2, +	 p->psa_io_base_addr_3, +	 p->psa_io_base_addr_4); +  printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n", +	 p->psa_rem_boot_addr_1, +	 p->psa_rem_boot_addr_2, +	 p->psa_rem_boot_addr_3); +  printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params); +  printk("psa_int_req_no: %d\n", p->psa_int_req_no); +#ifdef DEBUG_SHOW_UNUSED +  printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n", +	 p->psa_unused0[0], +	 p->psa_unused0[1], +	 p->psa_unused0[2], +	 p->psa_unused0[3], +	 p->psa_unused0[4], +	 p->psa_unused0[5], +	 p->psa_unused0[6]); +#endif	/* DEBUG_SHOW_UNUSED */ +  printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n", +	 p->psa_univ_mac_addr[0], +	 p->psa_univ_mac_addr[1], +	 p->psa_univ_mac_addr[2], +	 p->psa_univ_mac_addr[3], +	 p->psa_univ_mac_addr[4], +	 p->psa_univ_mac_addr[5]); +  printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n", +	 p->psa_local_mac_addr[0], +	 p->psa_local_mac_addr[1], +	 p->psa_local_mac_addr[2], +	 p->psa_local_mac_addr[3], +	 p->psa_local_mac_addr[4], +	 p->psa_local_mac_addr[5]); +  printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel); +  printk("psa_comp_number: %d, ", p->psa_comp_number); +  printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set); +  printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ", +	 p->psa_feature_select); +  printk("psa_subband/decay_update_prm: %d\n", p->psa_subband); +  printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr); +  printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay); +  printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]); +  printk("psa_nwid_select: %d\n", p->psa_nwid_select); +  printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select); +  printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", +	 p->psa_encryption_key[0], +	 p->psa_encryption_key[1], +	 p->psa_encryption_key[2], +	 p->psa_encryption_key[3], +	 p->psa_encryption_key[4], +	 p->psa_encryption_key[5], +	 p->psa_encryption_key[6], +	 p->psa_encryption_key[7]); +  printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width); +  printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ", +	 p->psa_call_code[0]); +  printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", +	 p->psa_call_code[0], +	 p->psa_call_code[1], +	 p->psa_call_code[2], +	 p->psa_call_code[3], +	 p->psa_call_code[4], +	 p->psa_call_code[5], +	 p->psa_call_code[6], +	 p->psa_call_code[7]); +#ifdef DEBUG_SHOW_UNUSED +  printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n", +	 p->psa_reserved[0], +	 p->psa_reserved[1], +	 p->psa_reserved[2], +	 p->psa_reserved[3]); +#endif	/* DEBUG_SHOW_UNUSED */ +  printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status); +  printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]); +  printk("psa_crc_status: 0x%02x\n", p->psa_crc_status); +} /* wv_psa_show */ +#endif	/* DEBUG_PSA_SHOW */ + +#ifdef DEBUG_MMC_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the Modem Management Controller. + * This function need to be completed... + */ +static void +wv_mmc_show(device *	dev) +{ +  u_long	ioaddr = dev->base_addr; +  net_local *	lp = (net_local *)dev->priv; +  mmr_t		m; + +  /* Basic check */ +  if(hasr_read(ioaddr) & HASR_NO_CLK) +    { +      printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n", +	     dev->name); +      return; +    } + +  /* Read the mmc */ +  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); +  mmc_read(ioaddr, 0, (u_char *)&m, sizeof(m)); +  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); + +#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */ +  /* Don't forget to update statistics */ +  lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; +#endif	/* WIRELESS_EXT */ + +  printk(KERN_DEBUG "##### WaveLAN modem status registers: #####\n"); +#ifdef DEBUG_SHOW_UNUSED +  printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", +	 m.mmr_unused0[0], +	 m.mmr_unused0[1], +	 m.mmr_unused0[2], +	 m.mmr_unused0[3], +	 m.mmr_unused0[4], +	 m.mmr_unused0[5], +	 m.mmr_unused0[6], +	 m.mmr_unused0[7]); +#endif	/* DEBUG_SHOW_UNUSED */ +  printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n", +	 m.mmr_des_avail, m.mmr_des_status); +#ifdef DEBUG_SHOW_UNUSED +  printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n", +	 m.mmr_unused1[0], +	 m.mmr_unused1[1], +	 m.mmr_unused1[2], +	 m.mmr_unused1[3], +	 m.mmr_unused1[4]); +#endif	/* DEBUG_SHOW_UNUSED */ +  printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n", +	 m.mmr_dce_status, +	 (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"", +	 (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? +	 "loop test indicated," : "", +	 (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "", +	 (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? +	 "jabber timer expired," : ""); +  printk(KERN_DEBUG "Dsp ID: %02X\n", +	 m.mmr_dsp_id); +#ifdef DEBUG_SHOW_UNUSED +  printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n", +	 m.mmr_unused2[0], +	 m.mmr_unused2[1]); +#endif	/* DEBUG_SHOW_UNUSED */ +  printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n", +	 (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l, +	 (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l); +  printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n", +	 m.mmr_thr_pre_set & MMR_THR_PRE_SET, +	 (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below"); +  printk(KERN_DEBUG "signal_lvl: %d [%s], ", +	 m.mmr_signal_lvl & MMR_SIGNAL_LVL, +	 (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg"); +  printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL, +	 (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update"); +  printk("sgnl_qual: 0x%x [%s]\n", +	 m.mmr_sgnl_qual & MMR_SGNL_QUAL, +	 (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0"); +#ifdef DEBUG_SHOW_UNUSED +  printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l); +#endif	/* DEBUG_SHOW_UNUSED */ +} /* wv_mmc_show */ +#endif	/* DEBUG_MMC_SHOW */ + +#ifdef DEBUG_I82586_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the last block of the i82586 memory + */ +static void +wv_scb_show(u_long	ioaddr) +{ +  scb_t		scb; + +  obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));    + +  printk(KERN_DEBUG "##### WaveLAN system control block: #####\n"); + +  printk(KERN_DEBUG "status: "); +  printk("stat 0x%x[%s%s%s%s] ", +	 (scb.scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | SCB_ST_RNR)) >> 12, +	 (scb.scb_status & SCB_ST_CX) ? "cmd completion interrupt," : "", +	 (scb.scb_status & SCB_ST_FR) ? "frame received," : "", +	 (scb.scb_status & SCB_ST_CNA) ? "cmd unit not active," : "", +	 (scb.scb_status & SCB_ST_RNR) ? "rcv unit not ready," : ""); +  printk("cus 0x%x[%s%s%s] ", +	 (scb.scb_status & SCB_ST_CUS) >> 8, +	 ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_IDLE) ? "idle" : "", +	 ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_SUSP) ? "suspended" : "", +	 ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_ACTV) ? "active" : ""); +  printk("rus 0x%x[%s%s%s%s]\n", +	 (scb.scb_status & SCB_ST_RUS) >> 4, +	 ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_IDLE) ? "idle" : "", +	 ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_SUSP) ? "suspended" : "", +	 ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_NRES) ? "no resources" : "", +	 ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_RDY) ? "ready" : ""); + +  printk(KERN_DEBUG "command: "); +  printk("ack 0x%x[%s%s%s%s] ", +	 (scb.scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12, +	 (scb.scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "", +	 (scb.scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "", +	 (scb.scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "", +	 (scb.scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""); +  printk("cuc 0x%x[%s%s%s%s%s] ", +	 (scb.scb_command & SCB_CMD_CUC) >> 8, +	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_NOP) ? "nop" : "", +	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_GO) ? "start cbl_offset" : "", +	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_RES) ? "resume execution" : "", +	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_SUS) ? "suspend execution" : "", +	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_ABT) ? "abort execution" : ""); +  printk("ruc 0x%x[%s%s%s%s%s]\n", +	 (scb.scb_command & SCB_CMD_RUC) >> 4, +	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_NOP) ? "nop" : "", +	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_GO) ? "start rfa_offset" : "", +	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_RES) ? "resume reception" : "", +	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_SUS) ? "suspend reception" : "", +	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_ABT) ? "abort reception" : ""); + +  printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset); +  printk("rfa_offset 0x%x\n", scb.scb_rfa_offset); + +  printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs); +  printk("alnerrs %d ", scb.scb_alnerrs); +  printk("rscerrs %d ", scb.scb_rscerrs); +  printk("ovrnerrs %d\n", scb.scb_ovrnerrs); +} + +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the i82586's receive unit. + */ +static void +wv_ru_show(device *	dev) +{ +  /* net_local *lp = (net_local *) dev->priv; */ + +  printk(KERN_DEBUG "##### WaveLAN i82586 receiver unit status: #####\n"); +  printk(KERN_DEBUG "ru:"); +  /* +   * Not implemented yet... +   */ +  printk("\n"); +} /* wv_ru_show */ + +/*------------------------------------------------------------------*/ +/* + * Display info about one control block of the i82586 memory + */ +static void +wv_cu_show_one(device *		dev, +	       net_local *	lp, +	       int		i, +	       u_short		p) +{ +  u_long		ioaddr; +  ac_tx_t		actx; + +  ioaddr = dev->base_addr; + +  printk("%d: 0x%x:", i, p); + +  obram_read(ioaddr, p, (unsigned char *)&actx, sizeof(actx)); +  printk(" status=0x%x,", actx.tx_h.ac_status); +  printk(" command=0x%x,", actx.tx_h.ac_command); + +  /* +  { +    tbd_t	tbd; + +    obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd)); +    printk(" tbd_status=0x%x,", tbd.tbd_status); +  } +  */ + +  printk("|"); +} + +/*------------------------------------------------------------------*/ +/* + * Print status of the command unit of the i82586 + */ +static void +wv_cu_show(device *	dev) +{ +  net_local *	lp = (net_local *)dev->priv; +  unsigned int	i; +  u_short	p; + +  printk(KERN_DEBUG "##### WaveLAN i82586 command unit status: #####\n"); + +  printk(KERN_DEBUG); +  for(i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++) +    { +      wv_cu_show_one(dev, lp, i, p); + +      p += TXBLOCKZ; +      if(p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) +	p -= NTXBLOCKS * TXBLOCKZ; +    } +  printk("\n"); +} +#endif	/* DEBUG_I82586_SHOW */ + +#ifdef DEBUG_DEVICE_SHOW +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the WaveLAN PCMCIA device driver. + */ +static void +wv_dev_show(device *	dev) +{ +  printk(KERN_DEBUG "dev:"); +  printk(" start=%d,", dev->start); +  printk(" tbusy=%ld,", dev->tbusy); +  printk(" interrupt=%d,", dev->interrupt); +  printk(" trans_start=%ld,", dev->trans_start); +  printk(" flags=0x%x,", dev->flags); +  printk("\n"); +} /* wv_dev_show */ + +/*------------------------------------------------------------------*/ +/* + * Print the formatted status of the WaveLAN PCMCIA device driver's + * private information. + */ +static void +wv_local_show(device *	dev) +{ +  net_local *lp; + +  lp = (net_local *)dev->priv; + +  printk(KERN_DEBUG "local:"); +  printk(" tx_n_in_use=%d,", lp->tx_n_in_use); +  printk(" hacr=0x%x,", lp->hacr); +  printk(" rx_head=0x%x,", lp->rx_head); +  printk(" rx_last=0x%x,", lp->rx_last); +  printk(" tx_first_free=0x%x,", lp->tx_first_free); +  printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use); +  printk("\n"); +} /* wv_local_show */ +#endif	/* DEBUG_DEVICE_SHOW */ + +#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) +/*------------------------------------------------------------------*/ +/* + * Dump packet header (and content if necessary) on the screen + */ +static inline void +wv_packet_info(u_char *		p,		/* Packet to dump */ +	       int		length,		/* Length of the packet */ +	       char *		msg1,		/* Name of the device */ +	       char *		msg2)		/* Name of the function */ +{ +#ifndef DEBUG_PACKET_DUMP +  printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n", +	 msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length); +  printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n", +	 msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]); + +#else	/* DEBUG_PACKET_DUMP */ +  int		i; +  int		maxi; + +  printk(KERN_DEBUG "%s: %s(): len=%d, data=\"", msg1, msg2, length); + +  if((maxi = length) > DEBUG_PACKET_DUMP) +    maxi = DEBUG_PACKET_DUMP; +  for(i = 0; i < maxi; i++) +    if(p[i] >= ' ' && p[i] <= '~') +      printk(" %c", p[i]); +    else +      printk("%02X", p[i]); +  if(maxi < length) +    printk(".."); +  printk("\"\n"); +  printk(KERN_DEBUG "\n"); +#endif	/* DEBUG_PACKET_DUMP */ +} +#endif	/* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */ + +/*------------------------------------------------------------------*/ +/* + * This is the information which is displayed by the driver at startup + * There  is a lot of flag to configure it at your will... + */ +static inline void +wv_init_info(device *	dev) +{ +  short		ioaddr = dev->base_addr; +  net_local *	lp = (net_local *)dev->priv; +  psa_t		psa; +  int		i; + +  /* Read the parameter storage area */ +  psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa)); + +#ifdef DEBUG_PSA_SHOW +  wv_psa_show(&psa); +#endif +#ifdef DEBUG_MMC_SHOW +  wv_mmc_show(dev); +#endif +#ifdef DEBUG_I82586_SHOW +  wv_cu_show(dev); +#endif + +#ifdef DEBUG_BASIC_SHOW +  /* Now, let's go for the basic stuff */ +  printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr); +  for(i = 0; i < WAVELAN_ADDR_SIZE; i++) +    printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]); +  printk(", IRQ %d", dev->irq); + +  /* Print current network id */ +  if(psa.psa_nwid_select) +    printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]); +  else +    printk(", nwid off"); + +  /* If 2.00 card */ +  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & +       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) +    { +      unsigned short	freq; + +      /* Ask the EEPROM to read the frequency from the first area */ +      fee_read(ioaddr, 0x00 /* 1st area - frequency... */, +	       &freq, 1); + +      /* Print frequency */ +      printk(", 2.00, %ld", (freq >> 6) + 2400L); + +      /* Hack !!! */ +      if(freq & 0x20) +	printk(".5"); +    } +  else +    { +      printk(", PC"); +      switch(psa.psa_comp_number) +	{ +	case PSA_COMP_PC_AT_915: +	case PSA_COMP_PC_AT_2400: +	  printk("-AT"); +	  break; +	case PSA_COMP_PC_MC_915: +	case PSA_COMP_PC_MC_2400: +	  printk("-MC"); +	  break; +	case PSA_COMP_PCMCIA_915: +	  printk("MCIA"); +	  break; +	default: +	  printk("???"); +	} +      printk(", "); +      switch (psa.psa_subband) +	{ +	case PSA_SUBBAND_915: +	  printk("915"); +	  break; +	case PSA_SUBBAND_2425: +	  printk("2425"); +	  break; +	case PSA_SUBBAND_2460: +	  printk("2460"); +	  break; +	case PSA_SUBBAND_2484: +	  printk("2484"); +	  break; +	case PSA_SUBBAND_2430_5: +	  printk("2430.5"); +	  break; +	default: +	  printk("???"); +	} +    } + +  printk(" MHz\n"); +#endif	/* DEBUG_BASIC_SHOW */ + +#ifdef DEBUG_VERSION_SHOW +  /* Print version information */ +  printk(KERN_NOTICE "%s", version); +#endif +} /* wv_init_info */ + +/********************* IOCTL, STATS & RECONFIG *********************/ +/* + * We found here routines that are called by Linux on differents + * occasions after the configuration and not for transmitting data + * These may be called when the user use ifconfig, /proc/net/dev + * or wireless extensions + */ + +/*------------------------------------------------------------------*/ +/* + * Get the current ethernet statistics. This may be called with the + * card open or closed. + * Used when the user read /proc/net/dev + */ +static en_stats	* +wavelan_get_stats(device *	dev) +{ +#ifdef DEBUG_IOCTL_TRACE +  printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name); +#endif + +  return(&((net_local *) dev->priv)->stats); +} + +/*------------------------------------------------------------------*/ +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1	Promiscuous mode, receive all packets + * num_addrs == 0	Normal mode, clear multicast list + * num_addrs > 0	Multicast mode, receive normal and MC packets, + *			and do best-effort filtering. + */ +static void +wavelan_set_multicast_list(device *	dev) +{ +  net_local *	lp = (net_local *) dev->priv; + +#ifdef DEBUG_IOCTL_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name); +#endif + +#ifdef DEBUG_IOCTL_INFO +  printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n", +	 dev->name, dev->flags, dev->mc_count); +#endif + +  /* If we ask for promiscuous mode, +   * or all multicast addresses (we don't have that !) +   * or too much multicast addresses for the hardware filter */ +  if((dev->flags & IFF_PROMISC) || +     (dev->flags & IFF_ALLMULTI) || +     (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES)) +    { +      /* +       * Enable promiscuous mode: receive all packets. +       */ +      if(!lp->promiscuous) +	{ +	  lp->promiscuous = 1; +	  lp->mc_count = 0; + +	  wv_82586_reconfig(dev); + +	  /* Tell the kernel that we are doing a really bad job... */ +	  dev->flags |= IFF_PROMISC; +	} +    } +  else +    /* If there is some multicast addresses to send */ +    if(dev->mc_list != (struct dev_mc_list *) NULL) +      { +	/* +	 * Disable promiscuous mode, but receive all packets +	 * in multicast list +	 */ +#ifdef MULTICAST_AVOID +	if(lp->promiscuous || +	   (dev->mc_count != lp->mc_count)) +#endif +	  { +	    lp->promiscuous = 0; +	    lp->mc_count = dev->mc_count; + +	    wv_82586_reconfig(dev); +	  } +      } +    else +      { +	/* +	 * Switch to normal mode: disable promiscuous mode and  +	 * clear the multicast list. +	 */ +	if(lp->promiscuous || lp->mc_count == 0) +	  { +	    lp->promiscuous = 0; +	    lp->mc_count = 0; + +	    wv_82586_reconfig(dev); +	  } +      } +#ifdef DEBUG_IOCTL_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This function doesn't exist... + */ +static int +wavelan_set_mac_address(device *	dev, +			void *		addr) +{ +  struct sockaddr *	mac = addr; + +  /* Copy the address */ +  memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE); + +  /* Reconfig the beast */ +  wv_82586_reconfig(dev); + +  return 0; +} + +#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */ + +/*------------------------------------------------------------------*/ +/* + * Frequency setting (for hardware able of it) + * It's a bit complicated and you don't really want to look into it... + * (called in wavelan_ioctl) + */ +static inline int +wv_set_frequency(u_long		ioaddr,	/* i/o port of the card */ +		 iw_freq *	frequency) +{ +  const int	BAND_NUM = 10;	/* Number of bands */ +  long		freq = 0L;	/* offset to 2.4 GHz in .5 MHz */ +#ifdef DEBUG_IOCTL_INFO +  int		i; +#endif + +  /* Setting by frequency */ +  /* Theoretically, you may set any frequency between +   * the two limits with a 0.5 MHz precision. In practice, +   * I don't want you to have trouble with local +   * regulations... */ +  if((frequency->e == 1) && +     (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8)) +    { +      freq = ((frequency->m / 10000) - 24000L) / 5; +    } + +  /* Setting by channel (same as wfreqsel) */ +  /* Warning : each channel is 22MHz wide, so some of the channels +   * will interfere... */ +  if((frequency->e == 0) && +     (frequency->m >= 0) && (frequency->m < BAND_NUM)) +    { +      /* frequency in 1/4 of MHz (as read in the offset register) */ +      short	bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, 0xD0, 0xF0, 0xF8, 0x150 }; + +      /* Get frequency offset */ +      freq = bands[frequency->m] >> 1; +    } + +  /* Verify if the frequency is allowed */ +  if(freq != 0L) +    { +      u_short	table[10];	/* Authorized frequency table */ + +      /* Read the frequency table */ +      fee_read(ioaddr, 0x71 /* frequency table */, +	       table, 10); + +#ifdef DEBUG_IOCTL_INFO +      printk(KERN_DEBUG "Frequency table :"); +      for(i = 0; i < 10; i++) +	{ +	  printk(" %04X", +		 table[i]); +	} +      printk("\n"); +#endif + +      /* Look in the table if the frequency is allowed */ +      if(!(table[9 - ((freq - 24) / 16)] & +	   (1 << ((freq - 24) % 16)))) +	return -EINVAL;		/* not allowed */ +    } +  else +    return -EINVAL; + +  /* If we get a usable frequency */ +  if(freq != 0L) +    { +      unsigned short	area[16]; +      unsigned short	dac[2]; +      unsigned short	area_verify[16]; +      unsigned short	dac_verify[2]; +      /* Corresponding gain (in the power adjust value table) +       * see AT&T WaveLAN Data Manual, REF 407-024689/E, page 3-8 +       * & WCIN062D.DOC, page 6.2.9 */ +      unsigned short	power_limit[] = { 40, 80, 120, 160, 0 }; +      int		power_band = 0;		/* Selected band */ +      unsigned short	power_adjust;		/* Correct value */ + +      /* Search for the gain */ +      power_band = 0; +      while((freq > power_limit[power_band]) && +	    (power_limit[++power_band] != 0)) +	; + +      /* Read the first area */ +      fee_read(ioaddr, 0x00, +	       area, 16); + +      /* Read the DAC */ +      fee_read(ioaddr, 0x60, +	       dac, 2); + +      /* Read the new power adjust value */ +      fee_read(ioaddr, 0x6B - (power_band >> 1), +	       &power_adjust, 1); +      if(power_band & 0x1) +	power_adjust >>= 8; +      else +	power_adjust &= 0xFF; + +#ifdef DEBUG_IOCTL_INFO +      printk(KERN_DEBUG "WaveLAN EEPROM Area 1:"); +      for(i = 0; i < 16; i++) +	{ +	  printk(" %04X", +		 area[i]); +	} +      printk("\n"); + +      printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n", +	     dac[0], dac[1]); +#endif + +      /* Frequency offset (for info only) */ +      area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F); + +      /* Receiver Principle main divider coefficient */ +      area[3] = (freq >> 1) + 2400L - 352L; +      area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); + +      /* Transmitter Main divider coefficient */ +      area[13] = (freq >> 1) + 2400L; +      area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF); + +      /* Others part of the area are flags, bit streams or unused... */ + +      /* Set the value in the DAC. */ +      dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80); +      dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF); + +      /* Write the first area. */ +      fee_write(ioaddr, 0x00, +		area, 16); + +      /* Write the DAC. */ +      fee_write(ioaddr, 0x60, +		dac, 2); + +      /* We now should verify here that the EEPROM writing was OK. */ + +      /* Reread the first area. */ +      fee_read(ioaddr, 0x00, +	       area_verify, 16); + +      /* ReRead the DAC */ +      fee_read(ioaddr, 0x60, +	       dac_verify, 2); + +      /* Compare */ +      if(memcmp(area, area_verify, 16 * 2) || +	 memcmp(dac, dac_verify, 2 * 2)) +	{ +#ifdef DEBUG_IOCTL_ERROR +	  printk(KERN_INFO "WaveLAN: wv_set_frequency: unable to write new frequency to EEPROM(?).\n"); +#endif +	  return -EOPNOTSUPP; +	} + +      /* We must download the frequency parameters to the +       * synthesizers (from the EEPROM - area 1) +       * Note: as the EEPROM is automatically decremented, we set the end +       * if the area... */ +      mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F); +      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), +	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + +      /* Wait until the download is finished */ +      fee_wait(ioaddr, 100, 100); + +      /* We must now download the power adjust value (gain) to +       * the synthesizers (from the EEPROM - area 7 - DAC) */ +      mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61); +      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), +	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD); + +      /* Wait until the download is finished */ +      fee_wait(ioaddr, 100, 100); + +#ifdef DEBUG_IOCTL_INFO +      /* Verification of what we have done... */ + +      printk(KERN_DEBUG "WaveLAN EEPROM Area 1:"); +      for(i = 0; i < 16; i++) +	{ +	  printk(" %04X", +		 area_verify[i]); +	} +      printk("\n"); + +      printk(KERN_DEBUG "WaveLAN EEPROM DAC: %04X %04X\n", +	     dac_verify[0], dac_verify[1]); +#endif + +      return 0; +    } +  else +    return -EINVAL;		/* Bah, never get there... */ +} + +/*------------------------------------------------------------------*/ +/* + * Give the list of available frequencies + */ +static inline int +wv_frequency_list(u_long	ioaddr,	/* i/o port of the card */ +		  iw_freq *	list,	/* List of frequency to fill */ +		  int		max)	/* Maximum number of frequencies */ +{ +  u_short	table[10];	/* Authorized frequency table */ +  long		freq = 0L;	/* offset to 2.4 GHz in .5 MHz + 12 MHz */ +  int		i;		/* index in the table */ + +  /* Read the frequency table */ +  fee_read(ioaddr, 0x71 /* frequency table */, +	   table, 10); + +  /* Look all frequencies */ +  i = 0; +  for(freq = 0; freq < 150; freq++) +    /* Look in the table if the frequency is allowed */ +    if(table[9 - (freq / 16)] & (1 << (freq % 16))) +      { +	/* put in the list */ +	list[i].m = (((freq + 24) * 5) + 24000L) * 10000; +	list[i++].e = 1; + +	/* Check number */ +	if(i >= max) +	  return(i); +      } + +  return(i); +} + +#ifdef WIRELESS_SPY +/*------------------------------------------------------------------*/ +/* + * Gather wireless spy statistics : for each packet, compare the source + * address with out list, and if match, get the stats... + * Sorry, but this function really need wireless extensions... + */ +static inline void +wl_spy_gather(device *	dev, +	      u_char *	mac,		/* MAC address */ +	      u_char *	stats)		/* Statistics to gather */ +{ +  net_local *	lp = (net_local *) dev->priv; +  int		i; + +  /* Look all addresses */ +  for(i = 0; i < lp->spy_number; i++) +    /* If match */ +    if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE)) +      { +	/* Update statistics */ +	lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL; +	lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL; +	lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL; +	lp->spy_stat[i].updated = 0x7; +      } +} +#endif	/* WIRELESS_SPY */ + +#ifdef HISTOGRAM +/*------------------------------------------------------------------*/ +/* + * This function calculates an histogram on the signal level. + * As the noise is quite constant, it's like doing it on the SNR. + * We have defined a set of interval (lp->his_range), and each time + * the level goes in that interval, we increment the count (lp->his_sum). + * With this histogram you may detect if one WaveLAN is really weak, + * or you may also calculate the mean and standard deviation of the level. + */ +static inline void +wl_his_gather(device *	dev, +	      u_char *	stats)		/* Statistics to gather */ +{ +  net_local *	lp = (net_local *) dev->priv; +  u_char	level = stats[0] & MMR_SIGNAL_LVL; +  int		i; + +  /* Find the correct interval */ +  i = 0; +  while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++])) +    ; + +  /* Increment interval counter */ +  (lp->his_sum[i])++; +} +#endif	/* HISTOGRAM */ + +/*------------------------------------------------------------------*/ +/* + * Perform ioctl : config & info stuff + * This is here that are treated the wireless extensions (iwconfig) + */ +static int +wavelan_ioctl(struct device *	dev,	/* device on which the ioctl is applied */ +	      struct ifreq *	rq,	/* data passed */ +	      int		cmd)	/* ioctl number */ +{ +  u_long		ioaddr = dev->base_addr; +  net_local *		lp = (net_local *)dev->priv;	/* lp is not unused */ +  struct iwreq *	wrq = (struct iwreq *) rq; +  psa_t			psa; +  mm_t			m; +  unsigned long		x; +  int			ret = 0; + +#ifdef DEBUG_IOCTL_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd); +#endif + +  /* Disable interrupts & save flags */ +  x = wv_splhi(); + +  /* Look what is the request */ +  switch(cmd) +    { +      /* --------------- WIRELESS EXTENSIONS --------------- */ + +    case SIOCGIWNAME: +      strcpy(wrq->u.name, "Wavelan"); +      break; + +    case SIOCSIWNWID: +      /* Set NWID in WaveLAN */ +      if(wrq->u.nwid.on) +	{ +	  /* Set NWID in psa */ +	  psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8; +	  psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF; +	  psa.psa_nwid_select = 0x01; +	  psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa, +		    (unsigned char *)psa.psa_nwid, 3); + +	  /* Set NWID in mmc */ +	  m.w.mmw_netw_id_l = wrq->u.nwid.nwid & 0xFF; +	  m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8; +	  mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m, +		    (unsigned char *)&m.w.mmw_netw_id_l, 2); +	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00); +	} +      else +	{ +	  /* Disable nwid in the psa */ +	  psa.psa_nwid_select = 0x00; +	  psa_write(ioaddr, lp->hacr, +		    (char *)&psa.psa_nwid_select - (char *)&psa, +		    (unsigned char *)&psa.psa_nwid_select, 1); + +	  /* Disable nwid in the mmc (no filtering) */ +	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID); +	} +      break; + +    case SIOCGIWNWID: +      /* Read the NWID */ +      psa_read(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa, +	       (unsigned char *)psa.psa_nwid, 3); +      wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1]; +      wrq->u.nwid.on = psa.psa_nwid_select; +      break; + +    case SIOCSIWFREQ: +      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */ +      if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & +	   (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) +	ret = wv_set_frequency(ioaddr, &(wrq->u.freq)); +      else +	ret = -EOPNOTSUPP; +      break; + +    case SIOCGIWFREQ: +      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) +       * (does it work for everybody ??? - especially old cards...) */ +      if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & +	   (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) +	{ +	  unsigned short	freq; + +	  /* Ask the EEPROM to read the frequency from the first area */ +	  fee_read(ioaddr, 0x00 /* 1st area - frequency... */, +		   &freq, 1); +	  wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000; +	  wrq->u.freq.e = 1; +	} +      else +	{ +	  int	bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 }; + +	  psa_read(ioaddr, lp->hacr, (char *)&psa.psa_subband - (char *)&psa, +		   (unsigned char *)&psa.psa_subband, 1); + +	  if(psa.psa_subband <= 4) +	    { +	      wrq->u.freq.m = bands[psa.psa_subband]; +	      wrq->u.freq.e = (psa.psa_subband != 0); +	    } +	  else +	    ret = -EOPNOTSUPP; +	} +      break; + +    case SIOCSIWSENS: +      /* Set the level threshold */ +      if(!suser()) +	return -EPERM; +      psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F; +      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa, +	       (unsigned char *) &psa.psa_thr_pre_set, 1); +      mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set); +      break; + +    case SIOCGIWSENS: +      /* Read the level threshold */ +      psa_read(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa, +	       (unsigned char *) &psa.psa_thr_pre_set, 1); +      wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F; +      break; + +     case SIOCSIWENCODE: +       /* Set encryption key */ +       if(!mmc_encr(ioaddr)) +	 { +	   ret = -EOPNOTSUPP; +	   break; +	 } + +       if(wrq->u.encoding.method) +	 {	/* enable encryption */ +	   int		i; +	   long long	key = wrq->u.encoding.code; + +	   for(i = 7; i >= 0; i--) +	     { +	       psa.psa_encryption_key[i] = key & 0xFF; +	       key >>= 8; +	     } +           psa.psa_encryption_select = 1; +	   psa_write(ioaddr, lp->hacr, +		     (char *) &psa.psa_encryption_select - (char *) &psa, +		     (unsigned char *) &psa.psa_encryption_select, 8+1); + +           mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), +		   MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE); +           mmc_write(ioaddr, mmwoff(0, mmw_encr_key), +		     (unsigned char *) &psa.psa_encryption_key, 8); +	 } +       else +	 {	/* disable encryption */ +	   psa.psa_encryption_select = 0; +	   psa_write(ioaddr, lp->hacr, +		     (char *) &psa.psa_encryption_select - (char *) &psa, +		     (unsigned char *) &psa.psa_encryption_select, 1); + +	   mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); +	 } +       break; + +     case SIOCGIWENCODE: +       /* Read the encryption key */ +       if(!mmc_encr(ioaddr)) +	 { +	   ret = -EOPNOTSUPP; +	   break; +	 } + +       /* only super-user can see encryption key */ +       if(!suser()) +	 { +	   ret = -EPERM; +	   break; +	 } +       else +	 { +	   int		i; +	   long long	key = 0; + +	   psa_read(ioaddr, lp->hacr, +		    (char *) &psa.psa_encryption_select - (char *) &psa, +		    (unsigned char *) &psa.psa_encryption_select, 1+8); +	   for(i = 0; i < 8; i++) +	     { +	       key <<= 8; +	       key += psa.psa_encryption_key[i]; +	     } +	   wrq->u.encoding.code = key; + +	   /* encryption is enabled */ +	   if(psa.psa_encryption_select) +	     wrq->u.encoding.method = mmc_encr(ioaddr); +	   else +	     wrq->u.encoding.method = 0; +	 } +       break; + +    case SIOCGIWRANGE: +      /* basic checking */ +      if(wrq->u.data.pointer != (caddr_t) 0) +	{ +	  struct iw_range	range; + +	  /* Verify the user buffer */ +	  ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer, +			    sizeof(struct iw_range)); +	  if(ret) +	    break; + +	  /* Set the length (useless : its constant...) */ +	  wrq->u.data.length = sizeof(struct iw_range); + +	  /* Set information in the range struct */ +	  range.throughput = 1.6 * 1024 * 1024;	/* don't argue on this ! */ +	  range.min_nwid = 0x0000; +	  range.max_nwid = 0xFFFF; + +	  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable). */ +	  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & +	       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) +	    { +	      range.num_channels = 10; +	      range.num_frequency = wv_frequency_list(ioaddr, range.freq, +						      IW_MAX_FREQUENCIES); +	    } +	  else +	    range.num_channels = range.num_frequency = 0; + +	  range.sensitivity = 0x3F; +	  range.max_qual.qual = MMR_SGNL_QUAL; +	  range.max_qual.level = MMR_SIGNAL_LVL; +	  range.max_qual.noise = MMR_SILENCE_LVL; + +	  /* Copy structure to the user buffer */ +	  copy_to_user(wrq->u.data.pointer, &range, +		       sizeof(struct iw_range)); +	} +      break; + +    case SIOCGIWPRIV: +      /* Basic checking... */ +      if(wrq->u.data.pointer != (caddr_t) 0) +	{ +	  struct iw_priv_args	priv[] = +	  {	/* cmd,		set_args,	get_args,	name */ +	    { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" }, +	    { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" }, + +	    { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16,	0, "sethisto" }, +	    { SIOCGIPHISTO, 0,	    IW_PRIV_TYPE_INT | 16, "gethisto" }, +	  }; + +	  /* Verify the user buffer */ +	  ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer, +			    sizeof(priv)); +	  if(ret) +	    break; + +	  /* Set the number of ioctl available */ +	  wrq->u.data.length = 4; + +	  /* Copy structure to the user buffer */ +	  copy_to_user(wrq->u.data.pointer, (u_char *) priv, +		       sizeof(priv)); +	} +      break; + +#ifdef WIRELESS_SPY +    case SIOCSIWSPY: +      /* Set the spy list */ + +      /* Check the number of addresses */ +      if(wrq->u.data.length > IW_MAX_SPY) +	{ +	  ret = -E2BIG; +	  break; +	} +      lp->spy_number = wrq->u.data.length; + +      /* If there is some addresses to copy */ +      if(lp->spy_number > 0) +	{ +	  struct sockaddr	address[IW_MAX_SPY]; +	  int			i; + +	  /* Verify where the user has set his addresses */ +	  ret = verify_area(VERIFY_READ, wrq->u.data.pointer, +			    sizeof(struct sockaddr) * lp->spy_number); +	  if(ret) +	    break; +	  /* Copy addresses to the driver */ +	  copy_from_user(address, wrq->u.data.pointer, +			 sizeof(struct sockaddr) * lp->spy_number); + +	  /* Copy addresses to the lp structure */ +	  for(i = 0; i < lp->spy_number; i++) +	    { +	      memcpy(lp->spy_address[i], address[i].sa_data, +		     WAVELAN_ADDR_SIZE); +	    } + +	  /* Reset structure... */ +	  memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY); + +#ifdef DEBUG_IOCTL_INFO +	  printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n"); +	  for(i = 0; i < wrq->u.data.length; i++) +	    printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X \n", +		   lp->spy_address[i][0], +		   lp->spy_address[i][1], +		   lp->spy_address[i][2], +		   lp->spy_address[i][3], +		   lp->spy_address[i][4], +		   lp->spy_address[i][5]); +#endif	/* DEBUG_IOCTL_INFO */ +	} + +      break; + +    case SIOCGIWSPY: +      /* Get the spy list and spy stats */ + +      /* Set the number of addresses */ +      wrq->u.data.length = lp->spy_number; + +      /* If the user want to have the addresses back... */ +      if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0)) +	{ +	  struct sockaddr	address[IW_MAX_SPY]; +	  int			i; + +	  /* Verify the user buffer */ +	  ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer, +			    (sizeof(iw_qual) + sizeof(struct sockaddr)) +			    * IW_MAX_SPY); +	  if(ret) +	    break; + +	  /* Copy addresses from the lp structure */ +	  for(i = 0; i < lp->spy_number; i++) +	    { +	      memcpy(address[i].sa_data, lp->spy_address[i], +		     WAVELAN_ADDR_SIZE); +	      address[i].sa_family = AF_UNIX; +	    } + +	  /* Copy addresses to the user buffer */ +	  copy_to_user(wrq->u.data.pointer, address, +		       sizeof(struct sockaddr) * lp->spy_number); + +	  /* Copy stats to the user buffer (just after) */ +	  copy_to_user(wrq->u.data.pointer + +		       (sizeof(struct sockaddr) * lp->spy_number), +		       lp->spy_stat, sizeof(iw_qual) * lp->spy_number); + +	  /* Reset updated flags */ +	  for(i = 0; i < lp->spy_number; i++) +	    lp->spy_stat[i].updated = 0x0; +	}	/* if(pointer != NULL) */ + +      break; +#endif	/* WIRELESS_SPY */ + +      /* ------------------ PRIVATE IOCTL ------------------ */ + +    case SIOCSIPQTHR: +      if(!suser()) +	return -EPERM; +      psa.psa_quality_thr = *(wrq->u.name) & 0x0F; +      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa, +	       (unsigned char *)&psa.psa_quality_thr, 1); +      mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr); +      break; + +    case SIOCGIPQTHR: +      psa_read(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa, +	       (unsigned char *)&psa.psa_quality_thr, 1); +      *(wrq->u.name) = psa.psa_quality_thr & 0x0F; +      break; + +#ifdef HISTOGRAM +    case SIOCSIPHISTO: +      /* Verif if the user is root */ +      if(!suser()) +	return -EPERM; + +      /* Check the number of intervals */ +      if(wrq->u.data.length > 16) +	{ +	  ret = -E2BIG; +	  break; +	} +      lp->his_number = wrq->u.data.length; + +      /* If there is some addresses to copy */ +      if(lp->his_number > 0) +	{ +	  /* Verify where the user has set his addresses */ +	  ret = verify_area(VERIFY_READ, wrq->u.data.pointer, +			    sizeof(char) * lp->his_number); +	  if(ret) +	    break; +	  /* Copy interval ranges to the driver */ +	  copy_from_user(lp->his_range, wrq->u.data.pointer, +			 sizeof(char) * lp->his_number); + +	  /* Reset structure... */ +	  memset(lp->his_sum, 0x00, sizeof(long) * 16); +	} +      break; + +    case SIOCGIPHISTO: +      /* Set the number of intervals */ +      wrq->u.data.length = lp->his_number; + +      /* Give back the distribution statistics */ +      if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0)) +	{ +	  /* Verify the user buffer */ +	  ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer, +			    sizeof(long) * 16); +	  if(ret) +	    break; + +	  /* Copy data to the user buffer */ +	  copy_to_user(wrq->u.data.pointer, lp->his_sum, +		       sizeof(long) * lp->his_number); +	}	/* if(pointer != NULL) */ +      break; +#endif	/* HISTOGRAM */ + +      /* ------------------- OTHER IOCTL ------------------- */ + +    default: +      ret = -EOPNOTSUPP; +    } + +  /* Enable interrupts, restore flags */ +  wv_splx(x); + +#ifdef DEBUG_IOCTL_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name); +#endif +  return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Get wireless statistics + * Called by /proc/net/wireless + */ +static iw_stats * +wavelan_get_wireless_stats(device *	dev) +{ +  u_long		ioaddr = dev->base_addr; +  net_local *		lp = (net_local *) dev->priv; +  mmr_t			m; +  iw_stats *		wstats; +  unsigned long		x; + +#ifdef DEBUG_IOCTL_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name); +#endif + +  /* Disable interrupts & save flags */ +  x = wv_splhi(); + +  if(lp == (net_local *) NULL) +    return (iw_stats *) NULL; +  wstats = &lp->wstats; + +  /* Get data from the mmc */ +  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); + +  mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1); +  mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2); +  mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4); + +  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); + +  /* Copy data to wireless stuff */ +  wstats->status = m.mmr_dce_status; +  wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL; +  wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL; +  wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL; +  wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) | +			  ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) | +			  ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5)); +  wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l; +  wstats->discard.code = 0L; +  wstats->discard.misc = 0L; + +  /* Enable interrupts & restore flags */ +  wv_splx(x); + +#ifdef DEBUG_IOCTL_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name); +#endif +  return &lp->wstats; +} +#endif	/* WIRELESS_EXT */ + +/************************* PACKET RECEPTION *************************/ +/* + * This part deals with receiving the packets. + * The interrupt handler gets an interrupt when a packet has been + * successfully received and calls this part. + */ + +/*------------------------------------------------------------------*/ +/* + * This routine does the actual copying of data (including the Ethernet + * header structure) from the WaveLAN card to an sk_buff chain that + * will be passed up to the network interface layer. NOTE: we + * currently don't handle trailer protocols (neither does the rest of + * the network interface), so if that is needed, it will (at least in + * part) be added here.  The contents of the receive ring buffer are + * copied to a message chain that is then passed to the kernel. + * + * Note: if any errors occur, the packet is "dropped on the floor" + * (called by wv_packet_rcv()) + */ +static inline void +wv_packet_read(device *		dev, +	       u_short		buf_off, +	       int		sksize) +{ +  net_local *		lp = (net_local *) dev->priv; +  u_long		ioaddr = dev->base_addr; +  struct sk_buff *	skb; + +#ifdef DEBUG_RX_TRACE +  printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", +	 dev->name, fd_p, sksize); +#endif + +  /* Allocate buffer for the data */ +  if((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL) +    { +#ifdef DEBUG_RX_ERROR +      printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n", +	     dev->name, sksize); +#endif +      lp->stats.rx_dropped++; +      return; +    } + +  skb->dev = dev; + +  /* Copy the packet to the buffer */ +  obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize); +  skb->protocol=eth_type_trans(skb, dev); + +#ifdef DEBUG_RX_INFO +  wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read"); +#endif	/* DEBUG_RX_INFO */ + +  /* Statistics gathering & stuff associated. +   * It seem a bit messy with all the define, but it's really simple... */ +#if defined(WIRELESS_SPY) || defined(HISTOGRAM) +  if( +#ifdef WIRELESS_SPY +     (lp->spy_number > 0) || +#endif	/* WIRELESS_SPY */ +#ifdef HISTOGRAM +     (lp->his_number > 0) || +#endif	/* HISTOGRAM */ +     0) +    { +      u_char	stats[3];	/* signal level, noise level, signal quality */ + +      /* read signal level, silence level and signal quality bytes */ +      /* Note: in the PCMCIA hardware, these are part of the frame. It seems +       * that for the ISA hardware, it's nowhere to be found in the frame, +       * so I'm obliged to do this (it has a side effect on /proc/net/wireless). +       * Any ideas? */ +      mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1); +      mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3); +      mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0); + +#ifdef DEBUG_RX_INFO +      printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n", +	     dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F); +#endif + +      /* Spying stuff */ +#ifdef WIRELESS_SPY +      wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats); +#endif	/* WIRELESS_SPY */ +#ifdef HISTOGRAM +      wl_his_gather(dev, stats); +#endif	/* HISTOGRAM */ +    } +#endif	/* defined(WIRELESS_SPY) || defined(HISTOGRAM) */ + +  /* +   * Hand the packet to the Network Module +   */ +  netif_rx(skb); + +  lp->stats.rx_packets++; + +#ifdef DEBUG_RX_TRACE +  printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Transfer as many packets as we can + * from the device RAM. + * Called by the interrupt handler. + */ +static inline void +wv_receive(device *	dev) +{ +  u_long	ioaddr = dev->base_addr; +  net_local *	lp = (net_local *)dev->priv; +  int		nreaped = 0; + +#ifdef DEBUG_RX_TRACE +  printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name); +#endif + +  /* Loop on each received packet */ +  for(;;) +    { +      fd_t		fd; +      rbd_t		rbd; +      ushort		pkt_len; + +      obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd, sizeof(fd)); + +      /* If the current frame is not complete, we have reach the end... */ +      if((fd.fd_status & FD_STATUS_C) != FD_STATUS_C) +	break;		/* This is how we exit the loop */ + +      nreaped++; + +      /* Check if frame correctly received */ +      if((fd.fd_status & (FD_STATUS_B | FD_STATUS_OK)) != +	 (FD_STATUS_B | FD_STATUS_OK)) +	{ +	  /* +	   * Not sure about this one -- it does not seem +	   * to be an error so we will keep quiet about it. +	   */ +#ifndef IGNORE_NORMAL_XMIT_ERRS +#ifdef DEBUG_RX_ERROR +	  if((fd.fd_status & FD_STATUS_B) != FD_STATUS_B) +	    printk(KERN_INFO "%s: wv_receive(): frame not consumed by RU.\n", +		   dev->name); +#endif +#endif	/* IGNORE_NORMAL_XMIT_ERRS */ + +#ifdef DEBUG_RX_ERROR +	  if((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK) +	    printk(KERN_INFO "%s: wv_receive(): frame not received successfully.\n", +		   dev->name); +#endif +	} + +      /* Were there problems in processing the frame?  Let's check. */ +      if((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 | +			  FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11)) +	 != 0) +	{ +	  lp->stats.rx_errors++; + +#ifdef DEBUG_RX_ERROR +	  if((fd.fd_status & FD_STATUS_S6) != 0) +	    printk(KERN_INFO "%s: wv_receive(): no EOF flag.\n", dev->name); +#endif + +	  if((fd.fd_status & FD_STATUS_S7) != 0) +	    { +	      lp->stats.rx_length_errors++; +#ifdef DEBUG_RX_ERROR +	      printk(KERN_INFO "%s: wv_receive(): frame too short.\n", +		     dev->name); +#endif +	    } + +	  if((fd.fd_status & FD_STATUS_S8) != 0) +	    { +	      lp->stats.rx_over_errors++; +#ifdef DEBUG_RX_ERROR +	      printk(KERN_INFO "%s: wv_receive(): rx DMA overrun.\n", +		     dev->name); +#endif +	    } + +	  if((fd.fd_status & FD_STATUS_S9) != 0) +	    { +	      lp->stats.rx_fifo_errors++; +#ifdef DEBUG_RX_ERROR +	      printk(KERN_INFO "%s: wv_receive(): ran out of resources.\n", +		     dev->name); +#endif +	    } + +	  if((fd.fd_status & FD_STATUS_S10) != 0) +	    { +	      lp->stats.rx_frame_errors++; +#ifdef DEBUG_RX_ERROR +	      printk(KERN_INFO "%s: wv_receive(): alignment error.\n", +		     dev->name); +#endif +	    } + +	  if((fd.fd_status & FD_STATUS_S11) != 0) +	    { +	      lp->stats.rx_crc_errors++; +#ifdef DEBUG_RX_ERROR +	      printk(KERN_INFO "%s: wv_receive(): CRC error.\n", dev->name); +#endif +	    } +	} + +      /* Does the frame contain a pointer to the data?  Let's check. */ +      if(fd.fd_rbd_offset == I82586NULL) +#ifdef DEBUG_RX_ERROR +	printk(KERN_INFO "%s: wv_receive(): frame has no data.\n", dev->name); +#endif +      else +	{ +	  obram_read(ioaddr, fd.fd_rbd_offset, +		     (unsigned char *) &rbd, sizeof(rbd)); + +#ifdef DEBUG_RX_ERROR +	  if((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF) +	    printk(KERN_INFO "%s: wv_receive(): missing EOF flag.\n", +		   dev->name); + +	  if((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F) +	    printk(KERN_INFO "%s: wv_receive(): missing F flag.\n", +		   dev->name); +#endif + +	  pkt_len = rbd.rbd_status & RBD_STATUS_ACNT; + +	  /* Read the packet and transmit to Linux */ +	  wv_packet_read(dev, rbd.rbd_bufl, pkt_len); +	}	/* if frame has data */ + +      fd.fd_status = 0; +      obram_write(ioaddr, fdoff(lp->rx_head, fd_status), +		  (unsigned char *) &fd.fd_status, sizeof(fd.fd_status)); + +      fd.fd_command = FD_COMMAND_EL; +      obram_write(ioaddr, fdoff(lp->rx_head, fd_command), +		  (unsigned char *) &fd.fd_command, sizeof(fd.fd_command)); + +      fd.fd_command = 0; +      obram_write(ioaddr, fdoff(lp->rx_last, fd_command), +		  (unsigned char *) &fd.fd_command, sizeof(fd.fd_command)); + +      lp->rx_last = lp->rx_head; +      lp->rx_head = fd.fd_link_offset; +    }	/* for(;;) -> loop on all frames */ + +#ifdef DEBUG_RX_INFO +  if(nreaped > 1) +    printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n", dev->name, nreaped); +#endif +#ifdef DEBUG_RX_TRACE +  printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name); +#endif +} + +/*********************** PACKET TRANSMISSION ***********************/ +/* + * This part deals with sending packet through the WaveLAN + * + */ + +/*------------------------------------------------------------------*/ +/* + * This routine fills in the appropriate registers and memory + * locations on the WaveLAN card and starts the card off on + * the transmit. + * + * The principle : + * Each block contain a transmit command, a nop command, + * a transmit block descriptor and a buffer. + * The CU read the transmit block which point to the tbd, + * read the tbd and the content of the buffer. + * When it has finished with it, it goes to the next command + * which in our case is the nop. The nop point on itself, + * so the CU stop here. + * When we add the next block, we modify the previous nop + * to make it point on the new tx command. + * Simple, isn't it ? + * + * (called in wavelan_packet_xmit()) + */ +static inline void +wv_packet_write(device *	dev, +		void *	buf, +		short	length) +{ +  net_local *		lp = (net_local *) dev->priv; +  u_long		ioaddr = dev->base_addr; +  unsigned short	txblock; +  unsigned short	txpred; +  unsigned short	tx_addr; +  unsigned short	nop_addr; +  unsigned short	tbd_addr; +  unsigned short	buf_addr; +  ac_tx_t		tx; +  ac_nop_t		nop; +  tbd_t			tbd; +  int			clen = length; +  unsigned long		x; + +#ifdef DEBUG_TX_TRACE +  printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length); +#endif + +  /* Check if we need some padding */ +  if(clen < ETH_ZLEN) +    clen = ETH_ZLEN; + +  x = wv_splhi(); + +  /* Calculate addresses of next block and previous block */ +  txblock = lp->tx_first_free; +  txpred = txblock - TXBLOCKZ; +  if(txpred < OFFSET_CU) +    txpred += NTXBLOCKS * TXBLOCKZ; +  lp->tx_first_free += TXBLOCKZ; +  if(lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) +    lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; + +/* +if (lp->tx_n_in_use > 0) +	printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]); +*/ + +  lp->tx_n_in_use++; + +  /* Calculate addresses of the differents part of the block */ +  tx_addr = txblock; +  nop_addr = tx_addr + sizeof(tx); +  tbd_addr = nop_addr + sizeof(nop); +  buf_addr = tbd_addr + sizeof(tbd); + +  /* +   * Transmit command. +   */ +  tx.tx_h.ac_status = 0; +  obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), +	      (unsigned char *) &tx.tx_h.ac_status, +	      sizeof(tx.tx_h.ac_status)); + +  /* +   * NOP command. +   */ +  nop.nop_h.ac_status = 0; +  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), +	      (unsigned char *) &nop.nop_h.ac_status, +	      sizeof(nop.nop_h.ac_status)); +  nop.nop_h.ac_link = nop_addr; +  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), +	      (unsigned char *) &nop.nop_h.ac_link, +	      sizeof(nop.nop_h.ac_link)); + +  /* +   * Transmit buffer descriptor +   */ +  tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen); +  tbd.tbd_next_bd_offset = I82586NULL; +  tbd.tbd_bufl = buf_addr; +  tbd.tbd_bufh = 0; +  obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd)); + +  /* +   * Data +   */ +  obram_write(ioaddr, buf_addr, buf, clen); + +  /* +   * Overwrite the predecessor NOP link +   * so that it points to this txblock. +   */ +  nop_addr = txpred + sizeof(tx); +  nop.nop_h.ac_status = 0; +  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), +	      (unsigned char *)&nop.nop_h.ac_status, +	      sizeof(nop.nop_h.ac_status)); +  nop.nop_h.ac_link = txblock; +  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), +	      (unsigned char *) &nop.nop_h.ac_link, +	      sizeof(nop.nop_h.ac_link)); + +  /* If watchdog not already active, activate it... */ +  if(lp->watchdog.prev == (timer_list *) NULL) +    { +      /* set timer to expire in WATCHDOG_JIFFIES */ +      lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES; +      add_timer(&lp->watchdog); +    } + +  if(lp->tx_first_in_use == I82586NULL) +    lp->tx_first_in_use = txblock; + +  if(lp->tx_n_in_use < NTXBLOCKS - 1) +    dev->tbusy = 0; + +  wv_splx(x); + +#ifdef DEBUG_TX_INFO +  wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write"); +#endif	/* DEBUG_TX_INFO */ + +#ifdef DEBUG_TX_TRACE +  printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This routine is called when we want to send a packet (NET3 callback) + * In this routine, we check if the hardware is ready to accept + * the packet. We also prevent reentrance. Then, we call the function + * to send the packet... + */ +static int +wavelan_packet_xmit(struct sk_buff *	skb, +		    device *		dev) +{ +  net_local *	lp = (net_local *)dev->priv; + +#ifdef DEBUG_TX_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, +	 (unsigned) skb); +#endif + +  /* This flag indicate that the hardware can't perform a transmission. +   * Theoretically, NET3 checks it before sending a packet to the driver, +   * but in fact it never does that and pool continuously. +   * As the watchdog will abort overly long transmissions, we are quite safe. +   */ +  if(dev->tbusy) +    return 1; + +  /* +   * If some higher layer thinks we've missed +   * a tx-done interrupt we are passed NULL. +   * Caution: dev_tint() handles the cli()/sti() itself. +   */ +  if(skb == (struct sk_buff *)0) +    { +#ifdef DEBUG_TX_ERROR +      printk(KERN_INFO "%s: wavelan_packet_xmit(): skb == NULL\n", dev->name); +#endif +      dev_tint(dev); +      return 0; +    } + +  /* +   * Block a timer-based transmit from overlapping. +   * In other words, prevent reentering this routine. +   */ +  if(set_bit(0, (void *)&dev->tbusy) != 0) +#ifdef DEBUG_TX_ERROR +    printk(KERN_INFO "%s: Transmitter access conflict.\n", dev->name); +#endif +  else +    { +      /* If somebody has asked to reconfigure the controller,  +       * we can do it now. +       */ +      if(lp->reconfig_82586) +	{ +	  wv_82586_config(dev); +	  if(dev->tbusy) +	    return 1; +	} + +#ifdef DEBUG_TX_ERROR +      if(skb->next) +	printk(KERN_INFO "skb has next\n"); +#endif + +      wv_packet_write(dev, skb->data, skb->len); +    } + +  dev_kfree_skb(skb, FREE_WRITE); + +#ifdef DEBUG_TX_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name); +#endif +  return 0; +} + +/*********************** HARDWARE CONFIGURATION ***********************/ +/* + * This part does the real job of starting and configuring the hardware. + */ + +/*--------------------------------------------------------------------*/ +/* + * Routine to initialize the Modem Management Controller. + * (called by wv_hw_reset()) + */ +static inline int +wv_mmc_init(device *	dev) +{ +  u_long	ioaddr = dev->base_addr; +  net_local *	lp = (net_local *)dev->priv; +  psa_t		psa; +  mmw_t		m; +  int		configured; + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name); +#endif + +  /* Read the parameter storage area */ +  psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa)); + +#ifdef USE_PSA_CONFIG +  configured = psa.psa_conf_status & 1; +#else +  configured = 0; +#endif + +  /* Is the PSA is not configured */ +  if(!configured) +    { +      /* User will be able to configure NWID after (with iwconfig) */ +      psa.psa_nwid[0] = 0; +      psa.psa_nwid[1] = 0; + +      /* no NWID checking since NWID is not set */ +      psa.psa_nwid_select = 0; + +      /* Disable encryption */ +      psa.psa_encryption_select = 0; + +      /* Set to standard values +       * 0x04 for AT, +       * 0x01 for MCA, +       * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document) +       */ +      if (psa.psa_comp_number & 1) +	psa.psa_thr_pre_set = 0x01; +      else +	psa.psa_thr_pre_set = 0x04; +      psa.psa_quality_thr = 0x03; + +      /* It is configured */ +      psa.psa_conf_status |= 1; + +#ifdef USE_PSA_CONFIG +      /* Write the psa */ +      psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa, +		(unsigned char *)psa.psa_nwid, 4); +      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa, +		(unsigned char *)&psa.psa_thr_pre_set, 1); +      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa, +		(unsigned char *)&psa.psa_quality_thr, 1); +      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_conf_status - (char *)&psa, +		(unsigned char *)&psa.psa_conf_status, 1); +#endif +    } + +  /* Zero the mmc structure */ +  memset(&m, 0x00, sizeof(m)); + +  /* Copy PSA info to the mmc */ +  m.mmw_netw_id_l = psa.psa_nwid[1]; +  m.mmw_netw_id_h = psa.psa_nwid[0]; +   +  if(psa.psa_nwid_select & 1) +    m.mmw_loopt_sel = 0x00; +  else +    m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID; + +  memcpy(&m.mmw_encr_key, &psa.psa_encryption_key,  +	 sizeof(m.mmw_encr_key)); + +  if(psa.psa_encryption_select) +    m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE; +  else +    m.mmw_encr_enable = 0; + +  m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F; +  m.mmw_quality_thr = psa.psa_quality_thr & 0x0F; + +  /* Missing: encryption stuff... */ + +  /* +   * Set default modem control parameters. +   * See NCR document 407-0024326 Rev. A. +   */ +  m.mmw_jabber_enable = 0x01; +  m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN; +  m.mmw_ifs = 0x20; +  m.mmw_mod_delay = 0x04; +  m.mmw_jam_time = 0x38; + +  m.mmw_encr_enable = 0; +  m.mmw_des_io_invert = 0; +  m.mmw_freeze = 0; +  m.mmw_decay_prm = 0; +  m.mmw_decay_updat_prm = 0; + +  /* Write all info to MMC */ +  mmc_write(ioaddr, 0, (u_char *)&m, sizeof(m)); + +  /* The following code starts the modem of the 2.00 frequency +   * selectable cards at power on. It's not strictly needed for the +   * following boots. +   * The original patch was by Joe Finney for the PCMCIA driver, but +   * I've cleaned it up a bit and added documentation. +   * Thanks to Loeke Brederveld from Lucent for the info. +   */ + +  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) +   * (does it work for everybody? -- especially old cards?) */ +  /* Note: WFREQSEL verifies that it is able to read a sensible +   * frequency from EEPROM (address 0x00) and that MMR_FEE_STATUS_ID +   * is 0xA (Xilinx version) or 0xB (Ariadne version). +   * My test is more crude but does work. */ +  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & +       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY))) +    { +      /* We must download the frequency parameters to the +       * synthesizers (from the EEPROM - area 1) +       * Note : as the EEPROM is auto decremented, we set the end +       * if the area... */ +      m.mmw_fee_addr = 0x0F; +      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; +      mmc_write(ioaddr, (char *)&m.mmw_fee_ctrl - (char *)&m, +		(unsigned char *)&m.mmw_fee_ctrl, 2); + +      /* Wait until the download is finished */ +      fee_wait(ioaddr, 100, 100); + +#ifdef DEBUG_CONFIG_INFO +      /* The frequency was in the last word downloaded. */ +      mmc_read(ioaddr, (char *)&m.mmw_fee_data_l - (char *)&m, +	       (unsigned char *)&m.mmw_fee_data_l, 2); + +      /* Print some info for the user. */ +      printk(KERN_DEBUG "%s: WaveLAN 2.00 recognised (frequency select) : Current frequency = %ld\n", +	     dev->name, +	     ((m.mmw_fee_data_h << 4) | +	      (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L); +#endif + +      /* We must now download the power adjust value (gain) to +       * the synthesizers (from the EEPROM - area 7 - DAC) */ +      m.mmw_fee_addr = 0x61; +      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD; +      mmc_write(ioaddr, (char *)&m.mmw_fee_ctrl - (char *)&m, +		(unsigned char *)&m.mmw_fee_ctrl, 2); + +      /* Wait until the download is finished */ +    }	/* if 2.00 card */ + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name); +#endif +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Construct the fd and rbd structures. + * Start the receive unit. + * (called by wv_hw_reset()) + */ +static inline int +wv_ru_start(device *	dev) +{ +  net_local *	lp = (net_local *) dev->priv; +  u_long	ioaddr = dev->base_addr; +  u_short	scb_cs; +  fd_t		fd; +  rbd_t		rbd; +  u_short	rx; +  u_short	rx_next; +  int		i; + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name); +#endif + +  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs)); +  if((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY) +    return 0; + +  lp->rx_head = OFFSET_RU; + +  for(i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next) +    { +      rx_next = (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ; + +      fd.fd_status = 0; +      fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0; +      fd.fd_link_offset = rx_next; +      fd.fd_rbd_offset = rx + sizeof(fd); +      obram_write(ioaddr, rx, (unsigned char *)&fd, sizeof(fd)); + +      rbd.rbd_status = 0; +      rbd.rbd_next_rbd_offset = I82586NULL; +      rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd); +      rbd.rbd_bufh = 0; +      rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ); +      obram_write(ioaddr, rx + sizeof(fd), +		  (unsigned char *) &rbd, sizeof(rbd)); + +      lp->rx_last = rx; +    } + +  obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), +	      (unsigned char *) &lp->rx_head, sizeof(lp->rx_head)); + +  scb_cs = SCB_CMD_RUC_GO; +  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), +	      (unsigned char *) &scb_cs, sizeof(scb_cs)); + +  set_chan_attn(ioaddr, lp->hacr); + +  for(i = 1000; i > 0; i--) +    { +      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), +		 (unsigned char *) &scb_cs, sizeof(scb_cs)); +      if (scb_cs == 0) +	break; + +      udelay(10); +    } + +  if(i <= 0) +    { +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_INFO "%s: wavelan_ru_start(): board not accepting command.\n", +	     dev->name); +#endif +      return -1; +    } + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name); +#endif +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Initialise the transmit blocks. + * Start the command unit executing the NOP + * self-loop of the first transmit block. + * + * Here, we create the list of send buffers used to transmit packets + * between the PC and the command unit. For each buffer, we create a + * buffer descriptor (pointing on the buffer), a transmit command + * (pointing to the buffer descriptor) and a NOP command. + * The transmit command is linked to the NOP, and the NOP to itself. + * When we will have finished executing the transmit command, we will + * then loop on the NOP. By releasing the NOP link to a new command, + * we may send another buffer. + * + * (called by wv_hw_reset()) + */ +static inline int +wv_cu_start(device *	dev) +{ +  net_local *	lp = (net_local *) dev->priv; +  u_long	ioaddr = dev->base_addr; +  int		i; +  u_short	txblock; +  u_short	first_nop; +  u_short	scb_cs; + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name); +#endif + +  lp->tx_first_free = OFFSET_CU; +  lp->tx_first_in_use = I82586NULL; + +  for(i = 0, txblock = OFFSET_CU; +      i < NTXBLOCKS; +      i++, txblock += TXBLOCKZ) +    { +      ac_tx_t		tx; +      ac_nop_t		nop; +      tbd_t		tbd; +      unsigned short	tx_addr; +      unsigned short	nop_addr; +      unsigned short	tbd_addr; +      unsigned short	buf_addr; + +      tx_addr = txblock; +      nop_addr = tx_addr + sizeof(tx); +      tbd_addr = nop_addr + sizeof(nop); +      buf_addr = tbd_addr + sizeof(tbd); + +      tx.tx_h.ac_status = 0; +      tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I; +      tx.tx_h.ac_link = nop_addr; +      tx.tx_tbd_offset = tbd_addr; +      obram_write(ioaddr, tx_addr, (unsigned char *) &tx, sizeof(tx)); + +      nop.nop_h.ac_status = 0; +      nop.nop_h.ac_command = acmd_nop; +      nop.nop_h.ac_link = nop_addr; +      obram_write(ioaddr, nop_addr, (unsigned char *) &nop, sizeof(nop)); + +      tbd.tbd_status = TBD_STATUS_EOF; +      tbd.tbd_next_bd_offset = I82586NULL; +      tbd.tbd_bufl = buf_addr; +      tbd.tbd_bufh = 0; +      obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd)); +    } + +  first_nop = OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t); +  obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), +	      (unsigned char *) &first_nop, sizeof(first_nop)); + +  scb_cs = SCB_CMD_CUC_GO; +  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), +	      (unsigned char *) &scb_cs, sizeof(scb_cs)); + +  set_chan_attn(ioaddr, lp->hacr); + +  for(i = 1000; i > 0; i--) +    { +      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), +		 (unsigned char *) &scb_cs, sizeof(scb_cs)); +      if (scb_cs == 0) +	break; + +      udelay(10); +    } + +  if(i <= 0) +    { +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_INFO "%s: wavelan_cu_start(): board not accepting command.\n", +	     dev->name); +#endif +      return -1; +    } + +  lp->tx_n_in_use = 0; +  dev->tbusy = 0; + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name); +#endif +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This routine does a standard config of the WaveLAN controler (i82586). + * + * It initialises the scp, iscp and scb structure + * The first two are just pointers to the next. + * The last one is used for basic configuration and for basic + * communication (interrupt status). + * + * (called by wv_hw_reset()) + */ +static inline int +wv_82586_start(device *	dev) +{ +  net_local *	lp = (net_local *) dev->priv; +  u_long	ioaddr = dev->base_addr; +  scp_t		scp;		/* system configuration pointer */ +  iscp_t	iscp;		/* intermediate scp */ +  scb_t		scb;		/* system control block */ +  ach_t		cb;		/* Action command header */ +  u_char	zeroes[512]; +  int		i; + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name); +#endif + +  /* +   * Clear the onboard RAM. +   */ +  memset(&zeroes[0], 0x00, sizeof(zeroes)); +  for(i = 0; i < I82586_MEMZ; i += sizeof(zeroes)) +    obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes)); + +  /* +   * Construct the command unit structures: +   * scp, iscp, scb, cb. +   */ +  memset(&scp, 0x00, sizeof(scp)); +  scp.scp_sysbus = SCP_SY_16BBUS; +  scp.scp_iscpl = OFFSET_ISCP; +  obram_write(ioaddr, OFFSET_SCP, (unsigned char *)&scp, sizeof(scp)); + +  memset(&iscp, 0x00, sizeof(iscp)); +  iscp.iscp_busy = 1; +  iscp.iscp_offset = OFFSET_SCB; +  obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp)); + +  /* Our first command is to reset the i82586. */ +  memset(&scb, 0x00, sizeof(scb)); +  scb.scb_command = SCB_CMD_RESET; +  scb.scb_cbl_offset = OFFSET_CU; +  scb.scb_rfa_offset = OFFSET_RU; +  obram_write(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb)); + +  set_chan_attn(ioaddr, lp->hacr); + +  /* Wait for command to finish. */ +  for(i = 1000; i > 0; i--) +    { +      obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, sizeof(iscp)); + +      if(iscp.iscp_busy == (unsigned short) 0) +	break; + +      udelay(10); +    } + +  if(i <= 0) +    { +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_INFO "%s: wv_82586_start(): iscp_busy timeout.\n", +	     dev->name); +#endif +      return -1; +    } + +  /* Check command completion */ +  for(i = 15; i > 0; i--) +    { +      obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, sizeof(scb)); + +      if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA)) +	break; + +      udelay(10); +    } + +  if (i <= 0) +    { +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_INFO "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n", +	     dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status); +#endif +      return -1; +    } + +  wv_ack(dev); + +  /* Set the action command header. */ +  memset(&cb, 0x00, sizeof(cb)); +  cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose); +  cb.ac_link = OFFSET_CU; +  obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb)); + +  if(wv_synchronous_cmd(dev, "diag()") == -1) +    return -1; + +  obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb)); +  if(cb.ac_status & AC_SFLD_FAIL) +    { +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_INFO "%s: wv_82586_start(): i82586 Self Test failed.\n", +	     dev->name); +#endif +      return -1; +    } + +#ifdef DEBUG_I82586_SHOW +  wv_scb_show(ioaddr); +#endif + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name); +#endif +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * This routine does a standard configuration of the WaveLAN controller + * (i82586). + * + * This routine is a violent hack. We use the first free transmit block + * to make our configuration. In the buffer area, we create the three + * configuration commands (linked). We make the previous NOP point to + * the beginning of the buffer instead of the tx command. After, we go + * as usual to the NOP command. + * Note that only the last command (mc_set) will generate an interrupt. + * + * (called by wv_hw_reset(), wv_82586_reconfig()) + */ +static void +wv_82586_config(device *	dev) +{ +  net_local *		lp = (net_local *) dev->priv; +  u_long		ioaddr = dev->base_addr; +  unsigned short	txblock; +  unsigned short	txpred; +  unsigned short	tx_addr; +  unsigned short	nop_addr; +  unsigned short	tbd_addr; +  unsigned short	cfg_addr; +  unsigned short	ias_addr; +  unsigned short	mcs_addr; +  ac_tx_t		tx; +  ac_nop_t		nop; +  ac_cfg_t		cfg;		/* Configure action */ +  ac_ias_t		ias;		/* IA-setup action */ +  ac_mcs_t		mcs;		/* Multicast setup */ +  struct dev_mc_list *	dmi; +  unsigned long		x; + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name); +#endif + +  x = wv_splhi(); + +  /* Calculate addresses of next block and previous block */ +  txblock = lp->tx_first_free; +  txpred = txblock - TXBLOCKZ; +  if(txpred < OFFSET_CU) +    txpred += NTXBLOCKS * TXBLOCKZ; +  lp->tx_first_free += TXBLOCKZ; +  if(lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ) +    lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ; + +  lp->tx_n_in_use++; + +  /* Calculate addresses of the different parts of the block. */ +  tx_addr = txblock; +  nop_addr = tx_addr + sizeof(tx); +  tbd_addr = nop_addr + sizeof(nop); +  cfg_addr = tbd_addr + sizeof(tbd_t);	/* beginning of the buffer */ +  ias_addr = cfg_addr + sizeof(cfg); +  mcs_addr = ias_addr + sizeof(ias); + +  /* +   * Transmit command +   */ +  tx.tx_h.ac_status = 0xFFFF;	/* Fake completion value */ +  obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), +	      (unsigned char *) &tx.tx_h.ac_status, +	      sizeof(tx.tx_h.ac_status)); + +  /* +   * NOP command +   */ +  nop.nop_h.ac_status = 0; +  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), +	      (unsigned char *) &nop.nop_h.ac_status, +	      sizeof(nop.nop_h.ac_status)); +  nop.nop_h.ac_link = nop_addr; +  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), +	      (unsigned char *) &nop.nop_h.ac_link, +	      sizeof(nop.nop_h.ac_link)); + +  /* Create a configure action */ +  memset(&cfg, 0x00, sizeof(cfg)); + +#if	0 +  /* +   * The default board configuration +   */ +  cfg.fifolim_bytecnt 	= 0x080c; +  cfg.addrlen_mode  	= 0x2600; +  cfg.linprio_interframe	= 0x7820;	/* IFS=120, ACS=2 */ +  cfg.slot_time      	= 0xf00c;	/* slottime=12    */ +  cfg.hardware	     	= 0x0008;	/* tx even without CD */ +  cfg.min_frame_len   	= 0x0040; +#endif	/* 0 */ + +  /* +   * For Linux we invert AC_CFG_ALOC(..) so as to conform +   * to the way that net packets reach us from above. +   * (See also ac_tx_t.) +   */ +  cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t)); +  cfg.cfg_fifolim = AC_CFG_FIFOLIM(8); +  cfg.cfg_byte8 = AC_CFG_SAV_BF(0) | +		  AC_CFG_SRDY(0); +  cfg.cfg_byte9 = AC_CFG_ELPBCK(0) | +		  AC_CFG_ILPBCK(0) | +		  AC_CFG_PRELEN(AC_CFG_PLEN_2) | +		  AC_CFG_ALOC(1) | +		  AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE); +  cfg.cfg_byte10 = AC_CFG_BOFMET(0) | +		   AC_CFG_ACR(0) | +		   AC_CFG_LINPRIO(0); +  cfg.cfg_ifs = 32; +  cfg.cfg_slotl = 0; +  cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) | +		   AC_CFG_SLTTMHI(2); +  cfg.cfg_byte14 = AC_CFG_FLGPAD(0) | +		   AC_CFG_BTSTF(0) | +		   AC_CFG_CRC16(0) | +		   AC_CFG_NCRC(0) | +		   AC_CFG_TNCRS(1) | +		   AC_CFG_MANCH(0) | +		   AC_CFG_BCDIS(0) | +		   AC_CFG_PRM(lp->promiscuous); +  cfg.cfg_byte15 = AC_CFG_ICDS(0) | +		   AC_CFG_CDTF(0) | +		   AC_CFG_ICSS(0) | +		   AC_CFG_CSTF(0); +/* +  cfg.cfg_min_frm_len = AC_CFG_MNFRM(64); +*/ +  cfg.cfg_min_frm_len = AC_CFG_MNFRM(8); + +  cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure); +  cfg.cfg_h.ac_link = ias_addr; +  obram_write(ioaddr, cfg_addr, (unsigned char *)&cfg, sizeof(cfg)); + +  /* Setup the MAC address */ +  memset(&ias, 0x00, sizeof(ias)); +  ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup); +  ias.ias_h.ac_link = mcs_addr; +  memcpy(&ias.ias_addr[0], (unsigned char *)&dev->dev_addr[0], sizeof(ias.ias_addr)); +  obram_write(ioaddr, ias_addr, (unsigned char *)&ias, sizeof(ias)); + +  /* Initialize adapter's ethernet multicast addresses */ +  memset(&mcs, 0x00, sizeof(mcs)); +  mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup); +  mcs.mcs_h.ac_link = nop_addr; +  mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count; +  obram_write(ioaddr, mcs_addr, (unsigned char *)&mcs, sizeof(mcs)); + +  /* If any address to set */ +  if(lp->mc_count) +    { +      for(dmi=dev->mc_list; dmi; dmi=dmi->next) +	outsw(PIOP1(ioaddr), (u_short *) dmi->dmi_addr, +	      WAVELAN_ADDR_SIZE >> 1); + +#ifdef DEBUG_CONFIG_INFO +      printk(KERN_DEBUG "%s: wv_82586_config(): set %d multicast addresses:\n", +	     dev->name, lp->mc_count); +      for(dmi=dev->mc_list; dmi; dmi=dmi->next) +	printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n", +	       dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2], +	       dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] ); +#endif +    } + +  /* +   * Overwrite the predecessor NOP link +   * so that it points to the configure action. +   */ +  nop_addr = txpred + sizeof(tx); +  nop.nop_h.ac_status = 0; +  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), +	      (unsigned char *)&nop.nop_h.ac_status, +	      sizeof(nop.nop_h.ac_status)); +  nop.nop_h.ac_link = cfg_addr; +  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), +	      (unsigned char *) &nop.nop_h.ac_link, +	      sizeof(nop.nop_h.ac_link)); + +  /* If watchdog not already active, activate it... */ +  if(lp->watchdog.prev == (timer_list *) NULL) +    { +      /* set timer to expire in WATCHDOG_JIFFIES */ +      lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES; +      add_timer(&lp->watchdog); +    } + +  lp->reconfig_82586 = 0; + +  if(lp->tx_first_in_use == I82586NULL) +    lp->tx_first_in_use = txblock; + +  if(lp->tx_n_in_use < NTXBLOCKS - 1) +    dev->tbusy = 0; + +  wv_splx(x); + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * This routine, called by wavelan_close(), gracefully stops the  + * WaveLAN controller (i82586). + */ +static inline void +wv_82586_stop(device *	dev) +{ +  net_local *	lp = (net_local *) dev->priv; +  u_long	ioaddr = dev->base_addr; +  u_short	scb_cmd; + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name); +#endif + +  /* Suspend both command unit and receive unit. */ +  scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS); +  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), +	      (unsigned char *)&scb_cmd, sizeof(scb_cmd)); +  set_chan_attn(ioaddr, lp->hacr); + +  /* No more interrupts */ +  wv_ints_off(dev); + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Totally reset the WaveLAN and restart it. + * Performs the following actions: + *	1. A power reset (reset DMA) + *	2. Initialize the radio modem (using wv_mmc_init) + *	3. Reset & Configure LAN controller (using wv_82586_start) + *	4. Start the LAN controller's command unit + *	5. Start the LAN controller's receive unit + */ +static int +wv_hw_reset(device *	dev) +{ +  net_local *	lp = (net_local *)dev->priv; +  u_long	ioaddr = dev->base_addr; + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name, +	 (unsigned int)dev); +#endif + +  /* If watchdog was activated, kill it! */ +  if(lp->watchdog.prev != (timer_list *) NULL) +    del_timer(&lp->watchdog); + +  /* Increase the number of resets done */ +  lp->nresets++; + +  wv_hacr_reset(ioaddr); +  lp->hacr = HACR_DEFAULT; + +  if((wv_mmc_init(dev) < 0) || +     (wv_82586_start(dev) < 0)) +    return -1; + +  /* Enable the card to send interrupts */ +  wv_ints_on(dev); + +  /* Start card functions */ +  if((wv_ru_start(dev) < 0) || +     (wv_cu_start(dev) < 0)) +    return -1; + +  /* Finish configuration */ +  wv_82586_config(dev); + +#ifdef DEBUG_CONFIG_TRACE +  printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); +#endif +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Check if there is a WaveLAN at the specific base address. + * As a side effect, this reads the MAC address. + * (called in wavelan_probe() and init_module()) + */ +static int +wv_check_ioaddr(u_long		ioaddr, +		u_char *	mac) +{ +  int		i;		/* Loop counter */ + +  /* Check if the base address if available */ +  if(check_region(ioaddr, sizeof(ha_t))) +    return  EADDRINUSE;		/* ioaddr already used... */ + +  /* Reset host interface */ +  wv_hacr_reset(ioaddr); + +  /* Read the MAC address from the parameter storage area */ +  psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr), +	   mac, 6); + +  /* +   * Check the first three octets of the address for the manufacturer's code. +   * Note: If this can't find your WaveLAN card, you've got a +   * non-NCR/AT&T/Lucent ISA card.  See wavelan.p.h for details on +   * how to configure your card. +   */ +  for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++) +    if((mac[0] == MAC_ADDRESSES[i][0]) && +       (mac[1] == MAC_ADDRESSES[i][1]) && +       (mac[2] == MAC_ADDRESSES[i][2])) +      return 0; + +#ifdef DEBUG_CONFIG_INFO +  printk(KERN_WARNING "WaveLAN (0x%3X): your MAC address might be: %02X:%02X:%02X.\n", +	 ioaddr, mac[0], mac[1], mac[2]); +#endif +    return ENODEV; +} + +/************************ INTERRUPT HANDLING ************************/ + +/* + * This function is the interrupt handler for the WaveLAN card. This + * routine will be called whenever:  + */ +static void +wavelan_interrupt(int			irq, +		  void *		dev_id, +		  struct pt_regs *	regs) +{ +  device *	dev; +  u_long	ioaddr; +  net_local *	lp; +  u_short	hasr; +  u_short	status; +  u_short	ack_cmd; + +  if((dev = (device *) (irq2dev_map[irq])) == (device *) NULL) +    { +#ifdef DEBUG_INTERRUPT_ERROR +      printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n", +	     irq); +#endif +      return; +    } + +#ifdef DEBUG_INTERRUPT_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); +#endif + +  lp = (net_local *) dev->priv; +  ioaddr = dev->base_addr; + +  /* Prevent reentrance. What should we do here? */ +#ifdef DEBUG_INTERRUPT_ERROR +  if(dev->interrupt) +    printk(KERN_INFO "%s: wavelan_interrupt(): Re-entering the interrupt handler.\n", +	   dev->name); +#endif +  dev->interrupt = 1; + +  if((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR) +    { +      u_char	dce_status; + +      /* +       * Interrupt from the modem management controller. +       * This will clear it -- ignored for now. +       */ +      mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status)); +#ifdef DEBUG_INTERRUPT_ERROR +      printk(KERN_INFO "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", +	     dev->name, dce_status); +#endif +    } + +  if((hasr & HASR_82586_INTR) == 0) +    { +      dev->interrupt = 0; +#ifdef DEBUG_INTERRUPT_ERROR +      printk(KERN_INFO "%s: wavelan_interrupt(): interrupt not coming from i82586\n", +	     dev->name); +#endif +      return; +    } + +  /* Read interrupt data. */ +  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), +	     (unsigned char *) &status, sizeof(status)); + +  /* +   * Acknowledge the interrupt(s). +   */ +  ack_cmd = status & SCB_ST_INT; +  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), +	      (unsigned char *) &ack_cmd, sizeof(ack_cmd)); +  set_chan_attn(ioaddr, lp->hacr); + +#ifdef DEBUG_INTERRUPT_INFO +  printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n", +	 dev->name, status); +#endif + +  /* Command completed. */ +  if((status & SCB_ST_CX) == SCB_ST_CX) +    { +#ifdef DEBUG_INTERRUPT_INFO +      printk(KERN_DEBUG "%s: wavelan_interrupt(): command completed.\n", +	     dev->name); +#endif +      wv_complete(dev, ioaddr, lp); + +      /* If watchdog was activated, kill it ! */ +      if(lp->watchdog.prev != (timer_list *) NULL) +	del_timer(&lp->watchdog); +      if(lp->tx_n_in_use > 0) +	{ +	  /* set timer to expire in WATCHDOG_JIFFIES */ +	  lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES; +	  add_timer(&lp->watchdog); +	} +    } + +  /* Frame received. */ +  if((status & SCB_ST_FR) == SCB_ST_FR) +    { +#ifdef DEBUG_INTERRUPT_INFO +      printk(KERN_DEBUG "%s: wavelan_interrupt(): received packet.\n", +	     dev->name); +#endif +      wv_receive(dev); +    } + +  /* Check the state of the command unit. */ +  if(((status & SCB_ST_CNA) == SCB_ST_CNA) || +     (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start)) +    { +#ifdef DEBUG_INTERRUPT_ERROR +      printk(KERN_INFO "%s: wavelan_interrupt(): CU inactive -- restarting\n", +	     dev->name); +#endif +      wv_hw_reset(dev); +    } + +  /* Check the state of the command unit. */ +  if(((status & SCB_ST_RNR) == SCB_ST_RNR) || +     (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start)) +    { +#ifdef DEBUG_INTERRUPT_ERROR +      printk(KERN_INFO "%s: wavelan_interrupt(): RU not ready -- restarting\n", +	     dev->name); +#endif +      wv_hw_reset(dev); +    } + +  dev->interrupt = 0; + +#ifdef DEBUG_INTERRUPT_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name); +#endif +} + +/*------------------------------------------------------------------*/ +/* + * Watchdog: when we start a transmission, we set a timer in the + * kernel.  If the transmission completes, this timer is disabled. If + * the timer expires, we try to unlock the hardware. + * + * Note: this watchdog doesn't work on the same principle as the + * watchdog in the previous version of the ISA driver. I made it this + * way because the overhead of add_timer() and del_timer() is nothing + * and because it avoids calling the watchdog, saving some CPU time. + */ +static void +wavelan_watchdog(u_long		a) +{ +  device *		dev; +  net_local *		lp; +  u_long		ioaddr; +  unsigned long		x; +  unsigned int		nreaped; + +  dev = (device *) a; +  ioaddr = dev->base_addr; +  lp = (net_local *) dev->priv; + +#ifdef DEBUG_INTERRUPT_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name); +#endif + +#ifdef DEBUG_INTERRUPT_ERROR +  printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n", +	 dev->name); +#endif + +  x = wv_splhi(); + +  dev = (device *) a; +  ioaddr = dev->base_addr; +  lp = (net_local *) dev->priv; + +  if(lp->tx_n_in_use <= 0) +    { +      wv_splx(x); +      return; +    } + +  nreaped = wv_complete(dev, ioaddr, lp); + +#ifdef DEBUG_INTERRUPT_INFO +  printk(KERN_DEBUG "%s: wavelan_watchdog(): %d reaped, %d remain.\n", +	 dev->name, nreaped, lp->tx_n_in_use); +#endif + +#ifdef DEBUG_PSA_SHOW +  { +    psa_t		psa; +    psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa)); +    wv_psa_show(&psa); +  } +#endif +#ifdef DEBUG_MMC_SHOW +  wv_mmc_show(dev); +#endif +#ifdef DEBUG_I82586_SHOW +  wv_cu_show(dev); +#endif + +  /* If no buffer has been freed */ +  if(nreaped == 0) +    { +#ifdef DEBUG_INTERRUPT_ERROR +      printk(KERN_INFO "%s: wavelan_watchdog(): cleanup failed, trying reset\n", +	     dev->name); +#endif +      wv_hw_reset(dev); +    } +  else +    /* Reset watchdog for next transmission. */ +    if(lp->tx_n_in_use > 0) +      { +	/* set timer to expire in WATCHDOG_JIFFIES */ +	lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES; +	add_timer(&lp->watchdog); +      } + +  wv_splx(x); + +#ifdef DEBUG_INTERRUPT_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name); +#endif +} + +/********************* CONFIGURATION CALLBACKS *********************/ +/* + * Here are the functions called by the Linux networking code (NET3) + * for initialization, configuration and deinstallations of the  + * WaveLAN ISA hardware. + */ + +/*------------------------------------------------------------------*/ +/* + * Configure and start up the WaveLAN PCMCIA adaptor. + * Called by NET3 when it "open" the device. + */ +static int +wavelan_open(device *	dev) +{ +  u_long	x; + +#ifdef DEBUG_CALLBACK_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name, +	 (unsigned int) dev); +#endif + +  /* Check irq */ +  if(dev->irq == 0) +    { +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n", dev->name); +#endif +      return -ENXIO; +    } + +  if((irq2dev_map[dev->irq] != (device *) NULL) || +     /* This is always true, but avoid the false IRQ. */ +     ((irq2dev_map[dev->irq] = dev) == (device *) NULL) || +     (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", NULL) != 0)) +    { +      irq2dev_map[dev->irq] = (device *) NULL; +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n", dev->name); +#endif +      return -EAGAIN; +    } + +  x = wv_splhi(); +  if(wv_hw_reset(dev) != -1) +    { +      dev->interrupt = 0; +      dev->start = 1; +    } +  else +    { +      free_irq(dev->irq, NULL); +      irq2dev_map[dev->irq] = (device *) NULL; +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_INFO "%s: wavelan_open(): impossible to start the card\n", +	     dev->name); +#endif +      return -EAGAIN; +    } +  wv_splx(x); + +  MOD_INC_USE_COUNT; + +#ifdef DEBUG_CALLBACK_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name); +#endif +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Shut down the WaveLAN ISA card. + * Called by NET3 when it "closes" the device. + */ +static int +wavelan_close(device *	dev) +{ +  net_local *	lp = (net_local *)dev->priv; + +#ifdef DEBUG_CALLBACK_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name, +	 (unsigned int) dev); +#endif + +  /* Not do the job twice. */ +  if(dev->start == 0) +    return 0; + +  dev->tbusy = 1; +  dev->start = 0; + +  /* If watchdog was activated, kill it! */ +  if(lp->watchdog.prev != (timer_list *) NULL) +    del_timer(&lp->watchdog); + +  /* +   * Flush the Tx and disable Rx. +   */ +  wv_82586_stop(dev); + +  free_irq(dev->irq, NULL); +  irq2dev_map[dev->irq] = (device *) NULL; + +  MOD_DEC_USE_COUNT; + +#ifdef DEBUG_CALLBACK_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name); +#endif +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Probe an I/O address, and if the WaveLAN is there configure the + * device structure + * (called by wavelan_probe() & via init_module()) + */ +static int +wavelan_config(device *	dev) +{ +  u_long	ioaddr = dev->base_addr; +  u_char	irq_mask; +  int		irq; +  net_local *	lp; + +#ifdef DEBUG_CALLBACK_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%x)\n", dev->name, +	 (unsigned int)dev, ioaddr); +#endif + +  /* Check irq arg on command line */ +  if(dev->irq != 0) +    { +      irq_mask = wv_irq_to_psa(dev->irq); + +      if(irq_mask == 0) +	{ +#ifdef DEBUG_CONFIG_ERROR +	  printk(KERN_WARNING "%s: wavelan_config(): invalid irq %d -- ignored.\n", +		 dev->name, dev->irq); +#endif +	  dev->irq = 0; +	} +      else +	{ +#ifdef DEBUG_CONFIG_INFO +	  printk(KERN_DEBUG "%s: wavelan_config(): changing irq to %d\n", +		 dev->name, dev->irq); +#endif +	  psa_write(ioaddr, HACR_DEFAULT, +		    psaoff(0, psa_int_req_no), &irq_mask, 1); +	  wv_hacr_reset(ioaddr); +	} +    } + +  psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), &irq_mask, 1); +  if((irq = wv_psa_to_irq(irq_mask)) == -1) +    { +#ifdef DEBUG_CONFIG_ERROR +      printk(KERN_INFO "%s: wavelan_config(): could not wavelan_map_irq(%d).\n", +	     dev->name, irq_mask); +#endif +      return EAGAIN; +    } + +  dev->irq = irq; + +  request_region(ioaddr, sizeof(ha_t), "wavelan"); + +  dev->mem_start = 0x0000; +  dev->mem_end = 0x0000; +  dev->if_port = 0; + +  /* Initialize device structures */ +  dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL); +  if(dev->priv == NULL) +    return -ENOMEM; +  memset(dev->priv, 0x00, sizeof(net_local)); +  lp = (net_local *)dev->priv; + +  /* Back link to the device structure. */ +  lp->dev = dev; +  /* Add the device at the beginning of the linked list. */ +  lp->next = wavelan_list; +  wavelan_list = lp; + +  lp->hacr = HACR_DEFAULT; + +  lp->watchdog.function = wavelan_watchdog; +  lp->watchdog.data = (unsigned long) dev; +  lp->promiscuous = 0; +  lp->mc_count = 0; + +  /* +   * Fill in the fields of the device structure +   * with Ethernet-generic values. +   */ +  ether_setup(dev); + +  dev->open = wavelan_open; +  dev->stop = wavelan_close; +  dev->hard_start_xmit = wavelan_packet_xmit; +  dev->get_stats = wavelan_get_stats; +  dev->set_multicast_list = &wavelan_set_multicast_list; +  dev->set_mac_address = &wavelan_set_mac_address; + +#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */ +  dev->do_ioctl = wavelan_ioctl; +  dev->get_wireless_stats = wavelan_get_wireless_stats; +#endif + +  dev->mtu = WAVELAN_MTU; + +  /* Display nice info */ +  wv_init_info(dev); + +#ifdef DEBUG_CALLBACK_TRACE +  printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name); +#endif +  return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Check for a network adaptor of this type.  Return '0' iff one  + * exists.  (There seem to be different interpretations of + * the initial value of dev->base_addr. + * We follow the example in drivers/net/ne.c.) + * (called in "Space.c") + * As this function is called outside the wavelan module, it should be + * declared extern, but it seem to cause troubles... + */ +/* extern */ int +wavelan_probe(device *	dev) +{ +  short		base_addr; +  mac_addr	mac;		/* MAC address (check WaveLAN existence) */ +  int		i; +  int		r; + +#ifdef DEBUG_CALLBACK_TRACE +  printk(KERN_DEBUG "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n", +	 dev->name, (unsigned int)dev, (unsigned int)dev->base_addr); +#endif + +#ifdef	STRUCT_CHECK +  if (wv_struct_check() != (char *) NULL) +    { +      printk(KERN_WARNING "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n", +	     dev->name, wv_struct_check()); +      return ENODEV; +    } +#endif	/* STRUCT_CHECK */ + +  /* Check the value of the command line parameter for base address */ +  base_addr = dev->base_addr; + +  /* Don't probe at all. */ +  if(base_addr < 0) +    { +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_WARNING "%s: wavelan_probe(): invalid base address\n", +	     dev->name); +#endif +      return ENXIO; +    } + +  /* Check a single specified location. */ +  if(base_addr > 0x100) +    { +      /* Check if the is something at this base address */ +      if((r = wv_check_ioaddr(base_addr, mac)) == 0) +	{ +	  memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address */ +	  r = wavelan_config(dev); +	} + +#ifdef DEBUG_CONFIG_INFO +      if(r != 0) +	printk(KERN_DEBUG "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n", +	       dev->name, base_addr); +#endif + +#ifdef DEBUG_CALLBACK_TRACE +      printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name); +#endif +      return r; +    } + +  /* Scan all possible addresses of the WaveLAN hardware */ +  for(i = 0; i < NELS(iobase); i++) +    { +      /* Check whether there is something at this base address */ +      if(wv_check_ioaddr(iobase[i], mac) == 0) +	{ +	  dev->base_addr = iobase[i];		/* Copy base address. */ +	  memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address. */ +	  if(wavelan_config(dev) == 0) +	    { +#ifdef DEBUG_CALLBACK_TRACE +	      printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name); +#endif +	      return 0; +	    } +	} +    } + +  /* We may have touch base_addr: another driver may not like it. */ +  dev->base_addr = base_addr; + +#ifdef DEBUG_CONFIG_INFO +  printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n", +	 dev->name); +#endif + +  return ENODEV; +} + +/****************************** MODULE ******************************/ +/* + * Module entry point: insertion & removal + */ + +#ifdef	MODULE +/*------------------------------------------------------------------*/ +/* + * Insertion of the module. + * I'm now quite proud of the multi-device support. + */ +int +init_module(void) +{ +  mac_addr	mac;		/* MAC address (check WaveLAN existence) */ +  int		ret = 0; +  int		i; + +#ifdef DEBUG_MODULE_TRACE +  printk(KERN_DEBUG "-> init_module()\n"); +#endif + +  /* If probing is asked */ +  if(io[0] == 0) +    { +#ifdef DEBUG_CONFIG_ERRORS +      printk(KERN_WARNING "WaveLAN init_module(): doing device probing (bad !)\n"); +      printk(KERN_WARNING "Specify base addresses while loading module to correct the problem\n"); +#endif + +      /* Copy the basic set of address to be probed. */ +      for(i = 0; i < NELS(iobase); i++) +	io[i] = iobase[i]; +    } + + +  /* Loop on all possible base addresses */ +  i = -1; +  while((io[++i] != 0) && (i < NELS(io))) +    { +      /* Check if there is something at this base address. */ +      if(wv_check_ioaddr(io[i], mac) == 0) +	{ +	  device *	dev; + +	  /* Create device and set basics args */ +	  dev = kmalloc(sizeof(struct device), GFP_KERNEL); +	  memset(dev, 0x00, sizeof(struct device)); +	  dev->name = name[i]; +	  dev->base_addr = io[i]; +	  dev->irq = irq[i]; +	  dev->init = &wavelan_config; +	  memcpy(dev->dev_addr, mac, 6);	/* Copy MAC address */ + +	  /* Try to create the device */ +	  if(register_netdev(dev) != 0) +	    { +	      /* DeAllocate everything */ +	      /* Note : if dev->priv is mallocated, there is no way to fail */ +	      kfree_s(dev, sizeof(struct device)); +	      ret = -EIO; +	    } +	}	/* if there is something at the address */ +    }		/* Loop on all addresses. */ + +#ifdef DEBUG_CONFIG_ERRORS +  if(wavelan_list == (net_local *) NULL) +    printk(KERN_WARNING "WaveLAN init_module(): no device found\n"); +#endif + +#ifdef DEBUG_MODULE_TRACE +  printk(KERN_DEBUG "<- init_module()\n"); +#endif +  return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Removal of the module + */ +void +cleanup_module(void) +{ +#ifdef DEBUG_MODULE_TRACE +  printk(KERN_DEBUG "-> cleanup_module()\n"); +#endif + +  /* Loop on all devices and release them. */ +  while(wavelan_list != (net_local *) NULL) +    { +      device *	dev = wavelan_list->dev; + +#ifdef DEBUG_CONFIG_INFO +      printk(KERN_DEBUG "%s: cleanup_module(): removing device at 0x%x\n", +	     dev->name, (unsigned int) dev); +#endif + +      /* Release the ioport-region. */ +      release_region(dev->base_addr, sizeof(ha_t)); + +      /* Definitely remove the device. */ +      unregister_netdev(dev); + +      /* Unlink the device. */ +      wavelan_list = wavelan_list->next; + +      /* Free pieces. */ +      kfree_s(dev->priv, sizeof(struct net_local)); +      kfree_s(dev, sizeof(struct device)); +    } + +#ifdef DEBUG_MODULE_TRACE +  printk(KERN_DEBUG "<- cleanup_module()\n"); +#endif +} +#endif	/* MODULE */ + +/* + * This software may only be used and distributed + * according to the terms of the GNU Public License. + * + * This software was developed as a component of the + * Linux operating system. + * It is based on other device drivers and information + * either written or supplied by: + *	Ajay Bakre (bakre@paul.rutgers.edu), + *	Donald Becker (becker@cesdis.gsfc.nasa.gov), + *	Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com), + *	Anders Klemets (klemets@it.kth.se), + *	Vladimir V. Kolpakov (w@stier.koenig.ru), + *	Marc Meertens (Marc.Meertens@Utrecht.NCR.com), + *	Pauline Middelink (middelin@polyware.iaf.nl), + *	Robert Morris (rtm@das.harvard.edu), + *	Jean Tourrilhes (jt@hplb.hpl.hp.com), + *	Girish Welling (welling@paul.rutgers.edu), + * + * Thanks go also to: + *	James Ashton (jaa101@syseng.anu.edu.au), + *	Alan Cox (iialan@iiit.swan.ac.uk), + *	Allan Creighton (allanc@cs.usyd.edu.au), + *	Matthew Geier (matthew@cs.usyd.edu.au), + *	Remo di Giovanni (remo@cs.usyd.edu.au), + *	Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de), + *	Vipul Gupta (vgupta@cs.binghamton.edu), + *	Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), + *	Tim Nicholson (tim@cs.usyd.edu.au), + *	Ian Parkin (ian@cs.usyd.edu.au), + *	John Rosenberg (johnr@cs.usyd.edu.au), + *	George Rossi (george@phm.gov.au), + *	Arthur Scott (arthur@cs.usyd.edu.au), + *	Peter Storey, + * for their assistance and advice. + * + * Please send bug reports, updates, comments to: + * + * Bruce Janson                                    Email:  bruce@cs.usyd.edu.au + * Basser Department of Computer Science           Phone:  +61-2-9351-3423 + * University of Sydney, N.S.W., 2006, AUSTRALIA   Fax:    +61-2-9351-3838 + */ diff --git a/linux/src/drivers/net/wavelan.h b/linux/src/drivers/net/wavelan.h new file mode 100644 index 0000000..2e92c79 --- /dev/null +++ b/linux/src/drivers/net/wavelan.h @@ -0,0 +1,346 @@ +/* + *	Wavelan ISA driver + * + *		Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * Original copyrigth follow. See wavelan.p.h for details. + * + * This file contain the declarations of the Wavelan hardware. Note that + * the Wavelan ISA include a i82586 controler (see definitions in + * file i82586.h). + * + * The main difference between the ISA hardware and the pcmcia one is + * the Ethernet Controler (i82586 instead of i82593). + * The i82586 allow multiple transmit buffers. The PSA need to be accessed + * through the host interface. + */ + +#ifndef _WAVELAN_H +#define	_WAVELAN_H + +/* The detection of the wavelan card is made by reading the MAC + * address from the card and checking it. If you have a non AT&T + * product (OEM, like DEC RoamAbout, or Digital Ocean, Epson, ...), + * you might need to modify this part to accomodate your hardware... + */ +const char	MAC_ADDRESSES[][3] = +{ +  { 0x08, 0x00, 0x0E },		/* AT&T Wavelan (standard) & DEC RoamAbout */ +  { 0x08, 0x00, 0x6A },		/* AT&T Wavelan (alternate) */ +  /* Add your card here and send me the patch ! */ +}; + +#define WAVELAN_ADDR_SIZE	6	/* Size of a MAC address */ + +#define WAVELAN_MTU		1500	/* Maximum size of WaveLAN packet */ + +#define	MAXDATAZ		(WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU) + +/*************************** PC INTERFACE ****************************/ + +/* + * Host Adaptor structure. + * (base is board port address). + */ +typedef union hacs_u	hacs_u; +union hacs_u +{ +	unsigned short	hu_command;		/* Command register */ +#define		HACR_RESET		0x0001	/* Reset board */ +#define		HACR_CA			0x0002	/* Set Channel Attention for 82586 */ +#define		HACR_16BITS		0x0004	/* 16 bits operation (0 => 8bits) */ +#define		HACR_OUT0		0x0008	/* General purpose output pin 0 */ +						/* not used - must be 1 */ +#define		HACR_OUT1		0x0010	/* General purpose output pin 1 */ +						/* not used - must be 1 */ +#define		HACR_82586_INT_ENABLE	0x0020	/* Enable 82586 interrupts */ +#define		HACR_MMC_INT_ENABLE	0x0040	/* Enable MMC interrupts */ +#define		HACR_INTR_CLR_ENABLE	0x0080	/* Enable interrupt status read/clear */ +	unsigned short	hu_status;		/* Status Register */ +#define		HASR_82586_INTR		0x0001	/* Interrupt request from 82586 */ +#define		HASR_MMC_INTR		0x0002	/* Interrupt request from MMC */ +#define		HASR_MMC_BUSY		0x0004	/* MMC busy indication */ +#define		HASR_PSA_BUSY		0x0008	/* LAN parameter storage area busy */ +}; + +typedef struct ha_t	ha_t; +struct ha_t +{ +	hacs_u		ha_cs;		/* Command and status registers */ +#define 		ha_command	ha_cs.hu_command +#define 		ha_status	ha_cs.hu_status +	unsigned short	ha_mmcr;	/* Modem Management Ctrl Register */ +	unsigned short	ha_pior0;	/* Program I/O Address Register Port 0 */ +	unsigned short	ha_piop0;	/* Program I/O Port 0 */ +	unsigned short	ha_pior1;	/* Program I/O Address Register Port 1 */ +	unsigned short	ha_piop1;	/* Program I/O Port 1 */ +	unsigned short	ha_pior2;	/* Program I/O Address Register Port 2 */ +	unsigned short	ha_piop2;	/* Program I/O Port 2 */ +}; + +#define HA_SIZE		16 + +#define	hoff(p,f) 	(unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0) +#define	HACR(p)		hoff(p, ha_command) +#define	HASR(p)		hoff(p, ha_status) +#define	MMCR(p)		hoff(p, ha_mmcr) +#define	PIOR0(p)	hoff(p, ha_pior0) +#define	PIOP0(p)	hoff(p, ha_piop0) +#define	PIOR1(p)	hoff(p, ha_pior1) +#define	PIOP1(p)	hoff(p, ha_piop1) +#define	PIOR2(p)	hoff(p, ha_pior2) +#define	PIOP2(p)	hoff(p, ha_piop2) + +/* + * Program I/O Mode Register values. + */ +#define STATIC_PIO		0	/* Mode 1: static mode */ +					/* RAM access ??? */ +#define AUTOINCR_PIO		1	/* Mode 2: auto increment mode */ +					/* RAM access ??? */ +#define AUTODECR_PIO		2	/* Mode 3: auto decrement mode */ +					/* RAM access ??? */ +#define PARAM_ACCESS_PIO	3	/* Mode 4: LAN parameter access mode */ +					/* Parameter access. */ +#define PIO_MASK		3	/* register mask */ +#define PIOM(cmd,piono)		((u_short)cmd << 10 << (piono * 2)) + +#define	HACR_DEFAULT		(HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2)) +#define	HACR_INTRON		(HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE) + +/************************** MEMORY LAYOUT **************************/ + +/* + * Onboard 64k RAM layout. + * (Offsets from 0x0000.) + */ +#define OFFSET_RU		0x0000		/* 75 % memory */ +#define OFFSET_CU		0xC000		/* 25 % memory */ +#define OFFSET_SCB		(OFFSET_ISCP - sizeof(scb_t)) +#define OFFSET_ISCP		(OFFSET_SCP - sizeof(iscp_t)) +#define OFFSET_SCP		I82586_SCP_ADDR + +#define	RXBLOCKZ		(sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ) +#define	TXBLOCKZ		(sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ) + +#define	NRXBLOCKS		((OFFSET_CU - OFFSET_RU) / RXBLOCKZ) +#define	NTXBLOCKS		((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ) + +/********************** PARAMETER STORAGE AREA **********************/ + +/* + * Parameter Storage Area (PSA). + */ +typedef struct psa_t	psa_t; +struct psa_t +{ +  unsigned char	psa_io_base_addr_1;	/* [0x00] Base address 1 ??? */ +  unsigned char	psa_io_base_addr_2;	/* [0x01] Base address 2 */ +  unsigned char	psa_io_base_addr_3;	/* [0x02] Base address 3 */ +  unsigned char	psa_io_base_addr_4;	/* [0x03] Base address 4 */ +  unsigned char	psa_rem_boot_addr_1;	/* [0x04] Remote Boot Address 1 */ +  unsigned char	psa_rem_boot_addr_2;	/* [0x05] Remote Boot Address 2 */ +  unsigned char	psa_rem_boot_addr_3;	/* [0x06] Remote Boot Address 3 */ +  unsigned char	psa_holi_params;	/* [0x07] HOst Lan Interface (HOLI) Parameters */ +  unsigned char	psa_int_req_no;		/* [0x08] Interrupt Request Line */ +  unsigned char	psa_unused0[7];		/* [0x09-0x0F] unused */ + +  unsigned char	psa_univ_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x10-0x15] Universal (factory) MAC Address */ +  unsigned char	psa_local_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x16-1B] Local MAC Address */ +  unsigned char	psa_univ_local_sel;	/* [0x1C] Universal Local Selection */ +#define		PSA_UNIVERSAL	0		/* Universal (factory) */ +#define		PSA_LOCAL	1		/* Local */ +  unsigned char	psa_comp_number;	/* [0x1D] Compatability Number: */ +#define		PSA_COMP_PC_AT_915	0 	/* PC-AT 915 MHz	*/ +#define		PSA_COMP_PC_MC_915	1 	/* PC-MC 915 MHz	*/ +#define		PSA_COMP_PC_AT_2400	2 	/* PC-AT 2.4 GHz	*/ +#define		PSA_COMP_PC_MC_2400	3 	/* PC-MC 2.4 GHz	*/ +#define		PSA_COMP_PCMCIA_915	4 	/* PCMCIA 915 MHz or 2.0 */ +  unsigned char	psa_thr_pre_set;	/* [0x1E] Modem Threshold Preset */ +  unsigned char	psa_feature_select;	/* [0x1F] Call code required (1=on) */ +#define		PSA_FEATURE_CALL_CODE	0x01 	/* Call code required (Japan) */ +  unsigned char	psa_subband;		/* [0x20] Subband	*/ +#define		PSA_SUBBAND_915		0	/* 915 MHz or 2.0 */ +#define		PSA_SUBBAND_2425	1	/* 2425 MHz	*/ +#define		PSA_SUBBAND_2460	2	/* 2460 MHz	*/ +#define		PSA_SUBBAND_2484	3	/* 2484 MHz	*/ +#define		PSA_SUBBAND_2430_5	4	/* 2430.5 MHz	*/ +  unsigned char	psa_quality_thr;	/* [0x21] Modem Quality Threshold */ +  unsigned char	psa_mod_delay;		/* [0x22] Modem Delay ??? (reserved) */ +  unsigned char	psa_nwid[2];		/* [0x23-0x24] Network ID */ +  unsigned char	psa_nwid_select;	/* [0x25] Network ID Select On Off */ +  unsigned char	psa_encryption_select;	/* [0x26] Encryption On Off */ +  unsigned char	psa_encryption_key[8];	/* [0x27-0x2E] Encryption Key */ +  unsigned char	psa_databus_width;	/* [0x2F] AT bus width select 8/16 */ +  unsigned char	psa_call_code[8];	/* [0x30-0x37] (Japan) Call Code */ +  unsigned char	psa_nwid_prefix[2];	/* [0x38-0x39] Roaming domain */ +  unsigned char	psa_reserved[2];	/* [0x3A-0x3B] Reserved - fixed 00 */ +  unsigned char	psa_conf_status;	/* [0x3C] Conf Status, bit 0=1:config*/ +  unsigned char	psa_crc[2];		/* [0x3D] CRC-16 over PSA */ +  unsigned char	psa_crc_status;		/* [0x3F] CRC Valid Flag */ +}; + +#define	PSA_SIZE	64 + +/* Calculate offset of a field in the above structure + * Warning : only even addresses are used */ +#define	psaoff(p,f) 	((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL)) + +/******************** MODEM MANAGEMENT INTERFACE ********************/ + +/* + * Modem Management Controller (MMC) write structure. + */ +typedef struct mmw_t	mmw_t; +struct mmw_t +{ +  unsigned char	mmw_encr_key[8];	/* encryption key */ +  unsigned char	mmw_encr_enable;	/* enable/disable encryption */ +#define	MMW_ENCR_ENABLE_MODE	0x02	/* Mode of security option */ +#define	MMW_ENCR_ENABLE_EN	0x01	/* Enable security option */ +  unsigned char	mmw_unused0[1];		/* unused */ +  unsigned char	mmw_des_io_invert;	/* Encryption option */ +#define	MMW_DES_IO_INVERT_RES	0x0F	/* Reserved */ +#define	MMW_DES_IO_INVERT_CTRL	0xF0	/* Control ??? (set to 0) */ +  unsigned char	mmw_unused1[5];		/* unused */ +  unsigned char	mmw_loopt_sel;		/* looptest selection */ +#define	MMW_LOOPT_SEL_DIS_NWID	0x40	/* disable NWID filtering */ +#define	MMW_LOOPT_SEL_INT	0x20	/* activate Attention Request */ +#define	MMW_LOOPT_SEL_LS	0x10	/* looptest w/o collision avoidance */ +#define MMW_LOOPT_SEL_LT3A	0x08	/* looptest 3a */ +#define	MMW_LOOPT_SEL_LT3B	0x04	/* looptest 3b */ +#define	MMW_LOOPT_SEL_LT3C	0x02	/* looptest 3c */ +#define	MMW_LOOPT_SEL_LT3D	0x01	/* looptest 3d */ +  unsigned char	mmw_jabber_enable;	/* jabber timer enable */ +  /* Abort transmissions > 200 ms */ +  unsigned char	mmw_freeze;		/* freeze / unfreeeze signal level */ +  /* 0 : signal level & qual updated for every new message, 1 : frozen */ +  unsigned char	mmw_anten_sel;		/* antenna selection */ +#define MMW_ANTEN_SEL_SEL	0x01	/* direct antenna selection */ +#define	MMW_ANTEN_SEL_ALG_EN	0x02	/* antenna selection algo. enable */ +  unsigned char	mmw_ifs;		/* inter frame spacing */ +  /* min time between transmission in bit periods (.5 us) - bit 0 ignored */ +  unsigned char	mmw_mod_delay;	 	/* modem delay (synchro) */ +  unsigned char	mmw_jam_time;		/* jamming time (after collision) */ +  unsigned char	mmw_unused2[1];		/* unused */ +  unsigned char	mmw_thr_pre_set;	/* level threshold preset */ +  /* Discard all packet with signal < this value (4) */ +  unsigned char	mmw_decay_prm;		/* decay parameters */ +  unsigned char	mmw_decay_updat_prm;	/* decay update parameterz */ +  unsigned char	mmw_quality_thr;	/* quality (z-quotient) threshold */ +  /* Discard all packet with quality < this value (3) */ +  unsigned char	mmw_netw_id_l;		/* NWID low order byte */ +  unsigned char	mmw_netw_id_h;		/* NWID high order byte */ +  /* Network ID or Domain : create virtual net on the air */ + +  /* 2.0 Hardware extension - frequency selection support */ +  unsigned char	mmw_mode_select;	/* for analog tests (set to 0) */ +  unsigned char	mmw_unused3[1];		/* unused */ +  unsigned char	mmw_fee_ctrl;		/* frequency eeprom control */ +#define	MMW_FEE_CTRL_PRE	0x10	/* Enable protected instructions */ +#define	MMW_FEE_CTRL_DWLD	0x08	/* Download eeprom to mmc */ +#define	MMW_FEE_CTRL_CMD	0x07	/* EEprom commands : */ +#define	MMW_FEE_CTRL_READ	0x06	/* Read */ +#define	MMW_FEE_CTRL_WREN	0x04	/* Write enable */ +#define	MMW_FEE_CTRL_WRITE	0x05	/* Write data to address */ +#define	MMW_FEE_CTRL_WRALL	0x04	/* Write data to all addresses */ +#define	MMW_FEE_CTRL_WDS	0x04	/* Write disable */ +#define	MMW_FEE_CTRL_PRREAD	0x16	/* Read addr from protect register */ +#define	MMW_FEE_CTRL_PREN	0x14	/* Protect register enable */ +#define	MMW_FEE_CTRL_PRCLEAR	0x17	/* Unprotect all registers */ +#define	MMW_FEE_CTRL_PRWRITE	0x15	/* Write addr in protect register */ +#define	MMW_FEE_CTRL_PRDS	0x14	/* Protect register disable */ +  /* Never issue this command (PRDS) : it's irreversible !!! */ + +  unsigned char	mmw_fee_addr;		/* EEprom address */ +#define	MMW_FEE_ADDR_CHANNEL	0xF0	/* Select the channel */ +#define	MMW_FEE_ADDR_OFFSET	0x0F	/* Offset in channel data */ +#define	MMW_FEE_ADDR_EN		0xC0	/* FEE_CTRL enable operations */ +#define	MMW_FEE_ADDR_DS		0x00	/* FEE_CTRL disable operations */ +#define	MMW_FEE_ADDR_ALL	0x40	/* FEE_CTRL all operations */ +#define	MMW_FEE_ADDR_CLEAR	0xFF	/* FEE_CTRL clear operations */ + +  unsigned char	mmw_fee_data_l;		/* Write data to EEprom */ +  unsigned char	mmw_fee_data_h;		/* high octet */ +  unsigned char	mmw_ext_ant;		/* Setting for external antenna */ +#define	MMW_EXT_ANT_EXTANT	0x01	/* Select external antenna */ +#define	MMW_EXT_ANT_POL		0x02	/* Polarity of the antenna */ +#define	MMW_EXT_ANT_INTERNAL	0x00	/* Internal antenna */ +#define	MMW_EXT_ANT_EXTERNAL	0x03	/* External antenna */ +#define	MMW_EXT_ANT_IQ_TEST	0x1C	/* IQ test pattern (set to 0) */ +}; + +#define	MMW_SIZE	37 + +#define	mmwoff(p,f) 	(unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0) + +/* + * Modem Management Controller (MMC) read structure. + */ +typedef struct mmr_t	mmr_t; +struct mmr_t +{ +  unsigned char	mmr_unused0[8];		/* unused */ +  unsigned char	mmr_des_status;		/* encryption status */ +  unsigned char	mmr_des_avail;		/* encryption available (0x55 read) */ +#define	MMR_DES_AVAIL_DES	0x55		/* DES available */ +#define	MMR_DES_AVAIL_AES	0x33		/* AES (AT&T) available */ +  unsigned char	mmr_des_io_invert;	/* des I/O invert register */ +  unsigned char	mmr_unused1[5];		/* unused */ +  unsigned char	mmr_dce_status;		/* DCE status */ +#define	MMR_DCE_STATUS_RX_BUSY		0x01	/* receiver busy */ +#define	MMR_DCE_STATUS_LOOPT_IND	0x02	/* loop test indicated */ +#define	MMR_DCE_STATUS_TX_BUSY		0x04	/* transmitter on */ +#define	MMR_DCE_STATUS_JBR_EXPIRED	0x08	/* jabber timer expired */ +  unsigned char	mmr_dsp_id;		/* DSP id (AA = Daedalus rev A) */ +  unsigned char	mmr_unused2[2];		/* unused */ +  unsigned char	mmr_correct_nwid_l;	/* # of correct NWID's rxd (low) */ +  unsigned char	mmr_correct_nwid_h;	/* # of correct NWID's rxd (high) */ +  /* Warning : Read high order octet first !!! */ +  unsigned char	mmr_wrong_nwid_l;	/* # of wrong NWID's rxd (low) */ +  unsigned char	mmr_wrong_nwid_h;	/* # of wrong NWID's rxd (high) */ +  unsigned char	mmr_thr_pre_set;	/* level threshold preset */ +#define	MMR_THR_PRE_SET		0x3F		/* level threshold preset */ +#define	MMR_THR_PRE_SET_CUR	0x80		/* Current signal above it */ +  unsigned char	mmr_signal_lvl;		/* signal level */ +#define	MMR_SIGNAL_LVL		0x3F		/* signal level */ +#define	MMR_SIGNAL_LVL_VALID	0x80		/* Updated since last read */ +  unsigned char	mmr_silence_lvl;	/* silence level (noise) */ +#define	MMR_SILENCE_LVL		0x3F		/* silence level */ +#define	MMR_SILENCE_LVL_VALID	0x80		/* Updated since last read */ +  unsigned char	mmr_sgnl_qual;		/* signal quality */ +#define	MMR_SGNL_QUAL		0x0F		/* signal quality */ +#define	MMR_SGNL_QUAL_ANT	0x80		/* current antenna used */ +  unsigned char	mmr_netw_id_l;		/* NWID low order byte ??? */ +  unsigned char	mmr_unused3[3];		/* unused */ + +  /* 2.0 Hardware extension - frequency selection support */ +  unsigned char	mmr_fee_status;		/* Status of frequency eeprom */ +#define	MMR_FEE_STATUS_ID	0xF0		/* Modem revision id */ +#define	MMR_FEE_STATUS_DWLD	0x08		/* Download in progress */ +#define	MMR_FEE_STATUS_BUSY	0x04		/* EEprom busy */ +  unsigned char	mmr_unused4[1];		/* unused */ +  unsigned char	mmr_fee_data_l;		/* Read data from eeprom (low) */ +  unsigned char	mmr_fee_data_h;		/* Read data from eeprom (high) */ +}; + +#define	MMR_SIZE	36 + +#define	mmroff(p,f) 	(unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0) + +/* Make the two above structures one */ +typedef union mm_t +{ +  struct mmw_t	w;	/* Write to the mmc */ +  struct mmr_t	r;	/* Read from the mmc */ +} mm_t; + +#endif /* _WAVELAN_H */ + +/* + * This software may only be used and distributed + * according to the terms of the GNU Public License. + * + * For more details, see wavelan.c. + */ diff --git a/linux/src/drivers/net/wavelan.p.h b/linux/src/drivers/net/wavelan.p.h new file mode 100644 index 0000000..3a6124e --- /dev/null +++ b/linux/src/drivers/net/wavelan.p.h @@ -0,0 +1,635 @@ +/* + *	Wavelan ISA driver + * + *		Jean II - HPLB '96 + * + * Reorganisation and extension of the driver. + * + * This file contain all definition and declarations necessary for the + * wavelan isa driver. This file is a private header, so it should + * be included only on wavelan.c !!! + */ + +#ifndef WAVELAN_P_H +#define WAVELAN_P_H + +/************************** DOCUMENTATION **************************/ +/* + * This driver provide a Linux interface to the Wavelan ISA hardware + * The Wavelan is a product of Lucent ("http://wavelan.netland.nl/"). + * This division was formerly part of NCR and then AT&T. + * Wavelan are also distributed by DEC (RoamAbout), Digital Ocean and + * Aironet (Arlan). If you have one of those product, you will need to + * make some changes below... + * + * This driver is still a beta software. A lot of bugs have been corrected, + * a lot of functionalities are implemented, the whole appear pretty stable, + * but there is still some area of improvement (encryption, performance...). + * + * To know how to use this driver, read the NET3 HOWTO. + * If you want to exploit the many other fonctionalities, look comments + * in the code... + * + * This driver is the result of the effort of many peoples (see below). + */ + +/* ------------------------ SPECIFIC NOTES ------------------------ */ +/* + * wavelan.o is darn too big + * ------------------------- + *	That's true ! There is a very simple way to reduce the driver + *	object by 33% (yes !). Comment out the following line : + *		#include <linux/wireless.h> + * + * MAC address and hardware detection : + * ---------------------------------- + *	The detection code of the wavelan chech that the first 3 + *	octets of the MAC address fit the company code. This type of + *	detection work well for AT&T cards (because the AT&T code is + *	hardcoded in wavelan.h), but of course will fail for other + *	manufacturer. + * + *	If you are sure that your card is derived from the wavelan, + *	here is the way to configure it : + *	1) Get your MAC address + *		a) With your card utilities (wfreqsel, instconf, ...) + *		b) With the driver : + *			o compile the kernel with DEBUG_CONFIG_INFO enabled + *			o Boot and look the card messages + *	2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h) + *	3) Compile & verify + *	4) Send me the MAC code - I will include it in the next version... + * + * "CU Inactive" message at boot up : + * ----------------------------------- + *	It seem that there is some weird timings problems with the + *	Intel microcontroler. In fact, this message is triggered by a + *	bad reading of the on board ram the first time we read the + *	control block. If you ignore this message, all is ok (but in + *	fact, currently, it reset the wavelan hardware). + * + *	To get rid of that problem, there is two solution. The first + *	is to add a dummy read of the scb at the end of + *	wv_82586_config. The second is to add the timers + *	wv_synchronous_cmd and wv_ack (the udelay just after the + *	waiting loops - seem that the controler is not totally ready + *	when it say it is !). + * + *	In the current code, I use the second solution (to be + *	consistent with the original solution of Bruce Janson). + */ + +/* --------------------- WIRELESS EXTENSIONS --------------------- */ +/* + * This driver is the first one to support "wireless extensions". + * This set of extensions provide you some way to control the wireless + * caracteristics of the hardware in a standard way and support for + * applications for taking advantage of it (like Mobile IP). + * + * You will need to enable the CONFIG_NET_RADIO define in the kernel + * configuration to enable the wireless extensions (this is the one + * giving access to the radio network device choice). + * + * It might also be a good idea as well to fetch the wireless tools to + * configure the device and play a bit. + */ + +/* ---------------------------- FILES ---------------------------- */ +/* + * wavelan.c :		The actual code for the driver - C functions + * + * wavelan.p.h :	Private header : local types / vars for the driver + * + * wavelan.h :		Description of the hardware interface & structs + * + * i82586.h :		Description if the Ethernet controler + */ + +/* --------------------------- HISTORY --------------------------- */ +/* + * (Made with information in drivers headers. It may not be accurate, + * and I garantee nothing except my best effort...) + * + * The history of the Wavelan drivers is as complicated as history of + * the Wavelan itself (NCR -> AT&T -> Lucent). + * + * All started with Anders Klemets <klemets@paul.rutgers.edu>, + * writting a Wavelan ISA driver for the MACH microkernel. Girish + * Welling <welling@paul.rutgers.edu> had also worked on it. + * Keith Moore modify this for the Pcmcia hardware. + *  + * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI + * and add specific Pcmcia support (there is currently no equivalent + * of the PCMCIA package under BSD...). + * + * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to freeBSD. + * + * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux. + * + * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver + * (with help of the BSDI PCMCIA driver) for PCMCIA. + * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work. + * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start + * correctly 2.00 cards (2.4 GHz with frequency selection). + * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his + * Pcmcia package (+ bug corrections). + * + * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some + * patchs to the Pcmcia driver. After, I added code in the ISA driver + * for Wireless Extensions and full support of frequency selection + * cards. Then, I've done the same to the Pcmcia driver + some + * reorganisation. Finally, I came back to the ISA driver to + * upgrade it at the same level as the Pcmcia one and reorganise + * the code + * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me + * much needed informations on the Wavelan hardware. + */ + +/* The original copyrights and litteratures mention others names and + * credits. I don't know what there part in this development was... + */ + +/* By the way : for the copyright & legal stuff : + * Almost everybody wrote code under GNU or BSD license (or alike), + * and want that their original copyright remain somewhere in the + * code (for myself, I go with the GPL). + * Nobody want to take responsibility for anything, except the fame... + */ + +/* --------------------------- CREDITS --------------------------- */ +/* + * This software was developed as a component of the + * Linux operating system. + * It is based on other device drivers and information + * either written or supplied by: + *	Ajay Bakre (bakre@paul.rutgers.edu), + *	Donald Becker (becker@cesdis.gsfc.nasa.gov), + *	Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com), + *	Brent Elphick <belphick@uwaterloo.ca>, + *	Anders Klemets (klemets@it.kth.se), + *	Vladimir V. Kolpakov (w@stier.koenig.ru), + *	Marc Meertens (Marc.Meertens@Utrecht.NCR.com), + *	Pauline Middelink (middelin@polyware.iaf.nl), + *	Robert Morris (rtm@das.harvard.edu), + *	Jean Tourrilhes (jt@hplb.hpl.hp.com), + *	Girish Welling (welling@paul.rutgers.edu), + *	Clark Woodworth <clark@hiway1.exit109.com> + *	Yongguang Zhang <ygz@isl.hrl.hac.com>... + * + * Thanks go also to: + *	James Ashton (jaa101@syseng.anu.edu.au), + *	Alan Cox (iialan@iiit.swan.ac.uk), + *	Allan Creighton (allanc@cs.usyd.edu.au), + *	Matthew Geier (matthew@cs.usyd.edu.au), + *	Remo di Giovanni (remo@cs.usyd.edu.au), + *	Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de), + *	Vipul Gupta (vgupta@cs.binghamton.edu), + *	Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM), + *	Tim Nicholson (tim@cs.usyd.edu.au), + *	Ian Parkin (ian@cs.usyd.edu.au), + *	John Rosenberg (johnr@cs.usyd.edu.au), + *	George Rossi (george@phm.gov.au), + *	Arthur Scott (arthur@cs.usyd.edu.au), + *	Stanislav Sinyagin <stas@isf.ru> + *	Peter Storey, + * for their assistance and advice. + * + * Additional Credits: + * + * My developpement has been done under Linux 2.0.x (Debian 1.1) with + *	an HP Vectra XP/60. + * + */ + +/* ------------------------- IMPROVEMENTS ------------------------- */ +/* + * I proudly present : + * + * Changes mades in first pre-release : + * ---------------------------------- + *	- Reorganisation of the code, function name change + *	- Creation of private header (wavelan.p.h) + *	- Reorganised debug messages + *	- More comments, history, ... + *	- mmc_init : configure the PSA if not done + *	- mmc_init : correct default value of level threshold for pcmcia + *	- mmc_init : 2.00 detection better code for 2.00 init + *	- better info at startup + *	- irq setting (note : this setting is permanent...) + *	- Watchdog : change strategy (+ solve module removal problems) + *	- add wireless extensions (ioctl & get_wireless_stats) + *	  get/set nwid/frequency on fly, info for /proc/net/wireless + *	- More wireless extension : SETSPY and GETSPY + *	- Make wireless extensions optional + *	- Private ioctl to set/get quality & level threshold, histogram + *	- Remove /proc/net/wavelan + *	- Supress useless stuff from lp (net_local) + *	- kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs) + *	- Add message level (debug stuff in /var/adm/debug & errors not + *	  displayed at console and still in /var/adm/messages) + *	- multi device support + *	- Start fixing the probe (init code) + *	- More inlines + *	- man page + *	- Lot of others minor details & cleanups + * + * Changes made in second pre-release : + * ---------------------------------- + *	- Cleanup init code (probe & module init) + *	- Better multi device support (module) + *	- name assignement (module) + * + * Changes made in third pre-release : + * --------------------------------- + *	- Be more conservative on timers + *	- Preliminary support for multicast (I still lack some details...) + * + * Changes made in fourth pre-release : + * ---------------------------------- + *	- multicast (revisited and finished) + *	- Avoid reset in set_multicast_list (a really big hack) + *	  if somebody could apply this code for other i82586 based driver... + *	- Share on board memory 75% RU / 25% CU (instead of 50/50) + * + * Changes made for release in 2.1.15 : + * ---------------------------------- + *	- Change the detection code for multi manufacturer code support + * + * Changes made for release in 2.1.17 : + * ---------------------------------- + *	- Update to wireless extensions changes + *	- Silly bug in card initial configuration (psa_conf_status) + * + * Changes made for release in 2.1.27 & 2.0.30 : + * ------------------------------------------- + *	- Small bug in debug code (probably not the last one...) + *	- Remove extern kerword for wavelan_probe() + *	- Level threshold is now a standard wireless extension (version 4 !) + * + * Changes made for release in 2.1.36 : + * ---------------------------------- + *	- Encryption setting from Brent Elphick (thanks a lot !) + *	- 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin) + * + * Wishes & dreams : + * --------------- + *	- Roaming + */ + +/***************************** INCLUDES *****************************/ + +#include	<linux/module.h> + +#include	<linux/kernel.h> +#include	<linux/sched.h> +#include	<linux/types.h> +#include	<linux/fcntl.h> +#include	<linux/interrupt.h> +#include	<linux/stat.h> +#include	<linux/ptrace.h> +#include	<linux/ioport.h> +#include	<linux/in.h> +#include	<linux/string.h> +#include	<linux/delay.h> +#include	<asm/system.h> +#include	<asm/bitops.h> +#include	<asm/io.h> +#include	<asm/dma.h> +#include	<linux/errno.h> +#include	<linux/netdevice.h> +#include	<linux/etherdevice.h> +#include	<linux/skbuff.h> +#include	<linux/malloc.h> +#include	<linux/timer.h> + +#include <linux/wireless.h>		/* Wireless extensions */ + +/* Wavelan declarations */ +#include	"i82586.h" +#include	"wavelan.h" + +/****************************** DEBUG ******************************/ + +#undef DEBUG_MODULE_TRACE	/* Module insertion/removal */ +#undef DEBUG_CALLBACK_TRACE	/* Calls made by Linux */ +#undef DEBUG_INTERRUPT_TRACE	/* Calls to handler */ +#undef DEBUG_INTERRUPT_INFO	/* type of interrupt & so on */ +#define DEBUG_INTERRUPT_ERROR	/* problems */ +#undef DEBUG_CONFIG_TRACE	/* Trace the config functions */ +#undef DEBUG_CONFIG_INFO	/* What's going on... */ +#define DEBUG_CONFIG_ERRORS	/* Errors on configuration */ +#undef DEBUG_TX_TRACE		/* Transmission calls */ +#undef DEBUG_TX_INFO		/* Header of the transmited packet */ +#define DEBUG_TX_ERROR		/* unexpected conditions */ +#undef DEBUG_RX_TRACE		/* Transmission calls */ +#undef DEBUG_RX_INFO		/* Header of the transmited packet */ +#define DEBUG_RX_ERROR		/* unexpected conditions */ +#undef DEBUG_PACKET_DUMP	16	/* Dump packet on the screen */ +#undef DEBUG_IOCTL_TRACE	/* Misc call by Linux */ +#undef DEBUG_IOCTL_INFO		/* Various debug info */ +#define DEBUG_IOCTL_ERROR	/* What's going wrong */ +#define DEBUG_BASIC_SHOW	/* Show basic startup info */ +#undef DEBUG_VERSION_SHOW	/* Print version info */ +#undef DEBUG_PSA_SHOW		/* Dump psa to screen */ +#undef DEBUG_MMC_SHOW		/* Dump mmc to screen */ +#undef DEBUG_SHOW_UNUSED	/* Show also unused fields */ +#undef DEBUG_I82586_SHOW	/* Show i82586 status */ +#undef DEBUG_DEVICE_SHOW	/* Show device parameters */ + +/* Options : */ +#define USE_PSA_CONFIG		/* Use info from the PSA */ +#define IGNORE_NORMAL_XMIT_ERRS	/* Don't bother with normal conditions */ +#undef STRUCT_CHECK		/* Verify padding of structures */ +#undef PSA_CRC			/* Check CRC in PSA */ +#undef OLDIES			/* Old code (to redo) */ +#undef RECORD_SNR		/* To redo */ +#undef EEPROM_IS_PROTECTED	/* Doesn't seem to be necessary */ +#define MULTICAST_AVOID		/* Avoid extra multicast (I'm sceptical) */ + +#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */ +/* Warning : these stuff will slow down the driver... */ +#define WIRELESS_SPY		/* Enable spying addresses */ +#undef HISTOGRAM		/* Enable histogram of sig level... */ +#endif + +/************************ CONSTANTS & MACROS ************************/ + +#ifdef DEBUG_VERSION_SHOW +static const char	*version	= "wavelan.c : v16 (wireless extensions) 17/4/97\n"; +#endif + +/* Watchdog temporisation */ +#define	WATCHDOG_JIFFIES	32	/* TODO: express in HZ. */ + +/* Macro to get the number of elements in an array */ +#define	NELS(a)				(sizeof(a) / sizeof(a[0])) + +/* ------------------------ PRIVATE IOCTL ------------------------ */ + +#define SIOCSIPQTHR	SIOCDEVPRIVATE		/* Set quality threshold */ +#define SIOCGIPQTHR	SIOCDEVPRIVATE + 1	/* Get quality threshold */ +#define SIOCSIPLTHR	SIOCDEVPRIVATE + 2	/* Set level threshold */ +#define SIOCGIPLTHR	SIOCDEVPRIVATE + 3	/* Get level threshold */ + +#define SIOCSIPHISTO	SIOCDEVPRIVATE + 6	/* Set histogram ranges */ +#define SIOCGIPHISTO	SIOCDEVPRIVATE + 7	/* Get histogram values */ + +/* ----------------------- VERSION SUPPORT ----------------------- */ + +/* This ugly patch is needed to cope with old version of the kernel */ +#ifndef copy_from_user +#define copy_from_user	memcpy_fromfs +#define copy_to_user	memcpy_tofs +#endif + +/****************************** TYPES ******************************/ + +/* Shortcuts */ +typedef struct device		device; +typedef struct enet_statistics	en_stats; +typedef struct iw_statistics	iw_stats; +typedef struct iw_quality	iw_qual; +typedef struct iw_freq		iw_freq; +typedef struct net_local	net_local; +typedef struct timer_list	timer_list; + +/* Basic types */ +typedef u_char		mac_addr[WAVELAN_ADDR_SIZE];	/* Hardware address */ + +/* + * Static specific data for the interface. + * + * For each network interface, Linux keep data in two structure. "device" + * keep the generic data (same format for everybody) and "net_local" keep + * the additional specific data. + * Note that some of this specific data is in fact generic (en_stats, for + * example). + */ +struct net_local +{ +  net_local *	next;		/* Linked list of the devices */ +  device *	dev;		/* Reverse link... */ +  en_stats	stats;		/* Ethernet interface statistics */ +  int		nresets;	/* Number of hw resets */ +  u_char	reconfig_82586;	/* Need to reconfigure the controler */ +  u_char	promiscuous;	/* Promiscuous mode */ +  int		mc_count;	/* Number of multicast addresses */ +  timer_list	watchdog;	/* To avoid blocking state */ +  u_short	hacr;		/* Current host interface state */ + +  int		tx_n_in_use; +  u_short	rx_head; +  u_short	rx_last; +  u_short	tx_first_free; +  u_short	tx_first_in_use; + +#ifdef WIRELESS_EXT +  iw_stats	wstats;		/* Wireless specific stats */ +#endif + +#ifdef WIRELESS_SPY +  int		spy_number;		/* Number of addresses to spy */ +  mac_addr	spy_address[IW_MAX_SPY];	/* The addresses to spy */ +  iw_qual	spy_stat[IW_MAX_SPY];		/* Statistics gathered */ +#endif	/* WIRELESS_SPY */ +#ifdef HISTOGRAM +  int		his_number;		/* Number of intervals */ +  u_char	his_range[16];		/* Boundaries of interval ]n-1; n] */ +  u_long	his_sum[16];		/* Sum in interval */ +#endif	/* HISTOGRAM */ +}; + +/**************************** PROTOTYPES ****************************/ + +/* ----------------------- MISC SUBROUTINES ------------------------ */ +static inline unsigned long	/* flags */ +	wv_splhi(void);		/* Disable interrupts */ +static inline void +	wv_splx(unsigned long);	/* ReEnable interrupts : flags */ +static u_char +	wv_irq_to_psa(int); +static int +	wv_psa_to_irq(u_char); +/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */ +static inline u_short		/* data */ +	hasr_read(u_long);	/* Read the host interface : base address */ +static inline void +	hacr_write(u_long,	/* Write to host interface : base address */ +		   u_short),	/* data */ +	hacr_write_slow(u_long, +		   u_short), +	set_chan_attn(u_long,	/* ioaddr */ +		      u_short),	/* hacr */ +	wv_hacr_reset(u_long),	/* ioaddr */ +	wv_16_off(u_long,	/* ioaddr */ +		  u_short),	/* hacr */ +	wv_16_on(u_long,	/* ioaddr */ +		 u_short),	/* hacr */ +	wv_ints_off(device *), +	wv_ints_on(device *); +/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */ +static void +	psa_read(u_long,	/* Read the Parameter Storage Area */ +		 u_short,	/* hacr */ +		 int,		/* offset in PSA */ +		 u_char *,	/* buffer to fill */ +		 int),		/* size to read */ +	psa_write(u_long, 	/* Write to the PSA */ +		  u_short,	/* hacr */ +		  int,		/* Offset in psa */ +		  u_char *,	/* Buffer in memory */ +		  int);		/* Length of buffer */ +static inline void +	mmc_out(u_long,		/* Write 1 byte to the Modem Manag Control */ +		u_short, +		u_char), +	mmc_write(u_long,	/* Write n bytes to the MMC */ +		  u_char, +		  u_char *, +		  int); +static inline u_char		/* Read 1 byte from the MMC */ +	mmc_in(u_long, +	       u_short); +static inline void +	mmc_read(u_long,	/* Read n bytes from the MMC */ +		 u_char, +		 u_char *, +		 int), +	fee_wait(u_long,	/* Wait for frequency EEprom : base address */ +		 int,		/* Base delay to wait for */ +		 int);		/* Number of time to wait */ +static void +	fee_read(u_long,	/* Read the frequency EEprom : base address */ +		 u_short,	/* destination offset */ +		 u_short *,	/* data buffer */ +		 int);		/* number of registers */ +/* ---------------------- I82586 SUBROUTINES ----------------------- */ +static /*inline*/ void +	obram_read(u_long,	/* ioaddr */ +		   u_short,	/* o */ +		   u_char *,	/* b */ +		   int);	/* n */ +static inline void +	obram_write(u_long,	/* ioaddr */ +		    u_short,	/* o */ +		    u_char *,	/* b */ +		    int);	/* n */ +static void +	wv_ack(device *); +static inline int +	wv_synchronous_cmd(device *, +			   const char *), +	wv_config_complete(device *, +			   u_long, +			   net_local *); +static int +	wv_complete(device *, +		    u_long, +		    net_local *); +static inline void +	wv_82586_reconfig(device *); +/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */ +#ifdef DEBUG_I82586_SHOW +static void +	wv_scb_show(unsigned short); +#endif +static inline void +	wv_init_info(device *);	/* display startup info */ +/* ------------------- IOCTL, STATS & RECONFIG ------------------- */ +static en_stats	* +	wavelan_get_stats(device *);	/* Give stats /proc/net/dev */ +static void +	wavelan_set_multicast_list(device *); +/* ----------------------- PACKET RECEPTION ----------------------- */ +static inline void +	wv_packet_read(device *,	/* Read a packet from a frame */ +		       u_short, +		       int), +	wv_receive(device *);	/* Read all packets waiting */ +/* --------------------- PACKET TRANSMISSION --------------------- */ +static inline void +	wv_packet_write(device *,	/* Write a packet to the Tx buffer */ +			void *, +			short); +static int +	wavelan_packet_xmit(struct sk_buff *,	/* Send a packet */ +			    device *); +/* -------------------- HARDWARE CONFIGURATION -------------------- */ +static inline int +	wv_mmc_init(device *),		/* Initialize the modem */ +	wv_ru_start(device *),		/* Start the i82586 receiver unit */ +	wv_cu_start(device *),		/* Start the i82586 command unit */ +	wv_82586_start(device *);	/* Start the i82586 */ +static void +	wv_82586_config(device *);	/* Configure the i82586 */ +static inline void +	wv_82586_stop(device *); +static int +	wv_hw_reset(device *),		/* Reset the wavelan hardware */ +	wv_check_ioaddr(u_long,		/* ioaddr */ +			u_char *);	/* mac address (read) */ +/* ---------------------- INTERRUPT HANDLING ---------------------- */ +static void +	wavelan_interrupt(int,		/* Interrupt handler */ +			  void *, +			  struct pt_regs *); +static void +	wavelan_watchdog(u_long);	/* Transmission watchdog */ +/* ------------------- CONFIGURATION CALLBACKS ------------------- */ +static int +	wavelan_open(device *),		/* Open the device */ +	wavelan_close(device *),	/* Close the device */ +	wavelan_config(device *);	/* Configure one device */ +extern int +	wavelan_probe(device *);	/* See Space.c */ + +/**************************** VARIABLES ****************************/ + +/* + * This is the root of the linked list of wavelan drivers + * It is use to verify that we don't reuse the same base address + * for two differents drivers and to make the cleanup when + * removing the module. + */ +static net_local *	wavelan_list	= (net_local *) NULL; + +/* + * This table is used to translate the psa value to irq number + * and vice versa... + */ +static u_char	irqvals[]	= +{ +	   0,    0,    0, 0x01, +	0x02, 0x04,    0, 0x08, +	   0,    0, 0x10, 0x20, +	0x40,    0,    0, 0x80, +}; + +/* + * Table of the available i/o address (base address) for wavelan + */ +static unsigned short	iobase[]	= +{ +#if	0 +  /* Leave out 0x3C0 for now -- seems to clash with some video +   * controllers. +   * Leave out the others too -- we will always use 0x390 and leave +   * 0x300 for the Ethernet device. +   * Jean II : 0x3E0 is really fine as well... +   */ +  0x300, 0x390, 0x3E0, 0x3C0 +#endif	/* 0 */ +  0x390, 0x3E0 +}; + +#ifdef	MODULE +/* Name of the devices (memory allocation) */ +static char	devname[4][IFNAMSIZ] = { "", "", "", "" }; + +/* Parameters set by insmod */ +static int	io[4]	= { 0, 0, 0, 0 }; +static int	irq[4]	= { 0, 0, 0, 0 }; +static char *	name[4] = { devname[0], devname[1], devname[2], devname[3] }; +#endif	/* MODULE */ + +#endif	/* WAVELAN_P_H */ diff --git a/linux/src/drivers/net/wd.c b/linux/src/drivers/net/wd.c new file mode 100644 index 0000000..dd87902 --- /dev/null +++ b/linux/src/drivers/net/wd.c @@ -0,0 +1,513 @@ +/* wd.c: A WD80x3 ethernet driver for linux. */ +/* +	Written 1993-94 by Donald Becker. + +	Copyright 1993 United States Government as represented by the +	Director, National Security Agency. + +	This software may be used and distributed according to the terms +	of the GNU Public License, incorporated herein by reference. + +	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O +	Center of Excellence in Space Data and Information Sciences +	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 + +	This is a driver for WD8003 and WD8013 "compatible" ethercards. + +	Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013. + +	Changelog: + +	Paul Gortmaker	: multiple card support for module users, support +			  for non-standard memory sizes. +			  + +*/ + +static const char *version = +	"wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <asm/io.h> +#include <asm/system.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include "8390.h" + +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int wd_portlist[] = +{0x300, 0x280, 0x380, 0x240, 0}; + +int wd_probe(struct device *dev); +int wd_probe1(struct device *dev, int ioaddr); + +static int wd_open(struct device *dev); +static void wd_reset_8390(struct device *dev); +static void wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, +						int ring_page); +static void wd_block_input(struct device *dev, int count, +						  struct sk_buff *skb, int ring_offset); +static void wd_block_output(struct device *dev, int count, +							const unsigned char *buf, const int start_page); +static int wd_close_card(struct device *dev); + + +#define WD_START_PG		0x00	/* First page of TX buffer */ +#define WD03_STOP_PG	0x20	/* Last page +1 of RX ring */ +#define WD13_STOP_PG	0x40	/* Last page +1 of RX ring */ + +#define WD_CMDREG		0		/* Offset to ASIC command register. */ +#define	 WD_RESET		0x80	/* Board reset, in WD_CMDREG. */ +#define	 WD_MEMENB		0x40	/* Enable the shared memory. */ +#define WD_CMDREG5		5		/* Offset to 16-bit-only ASIC register 5. */ +#define	 ISA16			0x80	/* Enable 16 bit access from the ISA bus. */ +#define	 NIC16			0x40	/* Enable 16 bit access from the 8390. */ +#define WD_NIC_OFFSET	16		/* Offset to the 8390 from the base_addr. */ +#define WD_IO_EXTENT	32 + + +/*	Probe for the WD8003 and WD8013.  These cards have the station +	address PROM at I/O ports <base>+8 to <base>+13, with a checksum +	following. A Soundblaster can have the same checksum as an WDethercard, +	so we have an extra exclusionary check for it. + +	The wd_probe1() routine initializes the card and fills the +	station address field. */ + +#ifdef HAVE_DEVLIST +struct netdev_entry wd_drv = +{"wd", wd_probe1, WD_IO_EXTENT, wd_portlist}; +#else + +int wd_probe(struct device *dev) +{ +	int i; +	int base_addr = dev ? dev->base_addr : 0; + +	if (base_addr > 0x1ff)		/* Check a single specified location. */ +		return wd_probe1(dev, base_addr); +	else if (base_addr != 0)	/* Don't probe at all. */ +		return ENXIO; + +	for (i = 0; wd_portlist[i]; i++) { +		int ioaddr = wd_portlist[i]; +		if (check_region(ioaddr, WD_IO_EXTENT)) +			continue; +		if (wd_probe1(dev, ioaddr) == 0) +			return 0; +	} + +	return ENODEV; +} +#endif + +int wd_probe1(struct device *dev, int ioaddr) +{ +	int i; +	int checksum = 0; +	int ancient = 0;			/* An old card without config registers. */ +	int word16 = 0;				/* 0 = 8 bit, 1 = 16 bit */ +	const char *model_name; +	static unsigned version_printed = 0; + +	for (i = 0; i < 8; i++) +		checksum += inb(ioaddr + 8 + i); +	if (inb(ioaddr + 8) == 0xff 	/* Extra check to avoid soundcard. */ +		|| inb(ioaddr + 9) == 0xff +		|| (checksum & 0xff) != 0xFF) +		return ENODEV; + +	/* We should have a "dev" from Space.c or the static module table. */ +	if (dev == NULL) { +		printk("wd.c: Passed a NULL device.\n"); +		dev = init_etherdev(0, 0); +	} + +	/* Check for semi-valid mem_start/end values if supplied. */ +	if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) { +		printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n"); +		dev->mem_start = 0; +		dev->mem_end = 0; +	} + +	if (ei_debug  &&  version_printed++ == 0) +		printk("%s", version); + +	printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr); +	for (i = 0; i < 6; i++) +		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i)); + +	/* The following PureData probe code was contributed by +	   Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software +	   configuration differently from others so we have to check for them. +	   This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card. +	   */ +	if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { +		unsigned char reg5 = inb(ioaddr+5); + +		switch (inb(ioaddr+2)) { +		case 0x03: word16 = 0; model_name = "PDI8023-8";	break; +		case 0x05: word16 = 0; model_name = "PDUC8023";	break; +		case 0x0a: word16 = 1; model_name = "PDI8023-16"; break; +			/* Either 0x01 (dumb) or they've released a new version. */ +		default:	 word16 = 0; model_name = "PDI8023";	break; +		} +		dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12; +		dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; +	} else {								/* End of PureData probe */ +		/* This method of checking for a 16-bit board is borrowed from the +		   we.c driver.  A simpler method is just to look in ASIC reg. 0x03. +		   I'm comparing the two method in alpha test to make certain they +		   return the same result. */ +		/* Check for the old 8 bit board - it has register 0/8 aliasing. +		   Do NOT check i>=6 here -- it hangs the old 8003 boards! */ +		for (i = 0; i < 6; i++) +			if (inb(ioaddr+i) != inb(ioaddr+8+i)) +				break; +		if (i >= 6) { +			ancient = 1; +			model_name = "WD8003-old"; +			word16 = 0; +		} else { +			int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ +			outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ +			if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ +				&& (tmp & 0x01) == 0x01	) {				/* In a 16 slot. */ +				int asic_reg5 = inb(ioaddr+WD_CMDREG5); +				/* Magic to set ASIC to word-wide mode. */ +				outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); +				outb(tmp, ioaddr+1); +				model_name = "WD8013"; +				word16 = 1;		/* We have a 16bit board here! */ +			} else { +				model_name = "WD8003"; +				word16 = 0; +			} +			outb(tmp, ioaddr+1);			/* Restore original reg1 value. */ +		} +#ifndef final_version +		if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01)) +			printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", +				   word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8); +#endif +	} + +#if defined(WD_SHMEM) && WD_SHMEM > 0x80000 +	/* Allow a compile-time override.	 */ +	dev->mem_start = WD_SHMEM; +#else +	if (dev->mem_start == 0) { +		/* Sanity and old 8003 check */ +		int reg0 = inb(ioaddr); +		if (reg0 == 0xff || reg0 == 0) { +			/* Future plan: this could check a few likely locations first. */ +			dev->mem_start = 0xd0000; +			printk(" assigning address %#lx", dev->mem_start); +		} else { +			int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; +			/* Some boards don't have the register 5 -- it returns 0xff. */ +			if (high_addr_bits == 0x1f || word16 == 0) +				high_addr_bits = 0x01; +			dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19); +		} +	} +#endif + +	/* The 8390 isn't at the base address -- the ASIC regs are there! */ +	dev->base_addr = ioaddr+WD_NIC_OFFSET; + +	if (dev->irq < 2) { +		int irqmap[] = {9,3,5,7,10,11,15,4}; +		int reg1 = inb(ioaddr+1); +		int reg4 = inb(ioaddr+4); +		if (ancient || reg1 == 0xff) {	/* Ack!! No way to read the IRQ! */ +			short nic_addr = ioaddr+WD_NIC_OFFSET; + +			/* We have an old-style ethercard that doesn't report its IRQ +			   line.  Do autoirq to find the IRQ line. Note that this IS NOT +			   a reliable way to trigger an interrupt. */ +			outb_p(E8390_NODMA + E8390_STOP, nic_addr); +			outb(0x00, nic_addr+EN0_IMR);	/* Disable all intrs. */ +			autoirq_setup(0); +			outb_p(0xff, nic_addr + EN0_IMR);	/* Enable all interrupts. */ +			outb_p(0x00, nic_addr + EN0_RCNTLO); +			outb_p(0x00, nic_addr + EN0_RCNTHI); +			outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */ +			dev->irq = autoirq_report(2); +			outb_p(0x00, nic_addr+EN0_IMR);	/* Mask all intrs. again. */ + +			if (ei_debug > 2) +				printk(" autoirq is %d", dev->irq); +			if (dev->irq < 2) +				dev->irq = word16 ? 10 : 5; +		} else +			dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; +	} else if (dev->irq == 2)		/* Fixup bogosity: IRQ2 is really IRQ9 */ +		dev->irq = 9; + +	/* Snarf the interrupt now.  There's no point in waiting since we cannot +	   share and the board will usually be enabled. */ +	if (request_irq(dev->irq, ei_interrupt, 0, model_name, NULL)) { +		printk (" unable to get IRQ %d.\n", dev->irq); +		return EAGAIN; +	} + +	/* Allocate dev->priv and fill in 8390 specific dev fields. */ +	if (ethdev_init(dev)) {	 +		printk (" unable to get memory for dev->priv.\n"); +		free_irq(dev->irq, NULL); +		return -ENOMEM; +	} + +	/* OK, were are certain this is going to work.  Setup the device. */ +	request_region(ioaddr, WD_IO_EXTENT, model_name); + +	ei_status.name = model_name; +	ei_status.word16 = word16; +	ei_status.tx_start_page = WD_START_PG; +	ei_status.rx_start_page = WD_START_PG + TX_PAGES; + +	/* Don't map in the shared memory until the board is actually opened. */ +	dev->rmem_start = dev->mem_start + TX_PAGES*256; + +	/* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ +	if (dev->mem_end != 0) { +		ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; +	} else { +		ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; +		dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; +	} +	dev->rmem_end = dev->mem_end; + +	printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", +		   model_name, dev->irq, dev->mem_start, dev->mem_end-1); + +	ei_status.reset_8390 = &wd_reset_8390; +	ei_status.block_input = &wd_block_input; +	ei_status.block_output = &wd_block_output; +	ei_status.get_8390_hdr = &wd_get_8390_hdr; +	dev->open = &wd_open; +	dev->stop = &wd_close_card; +	NS8390_init(dev, 0); + +#if 1 +	/* Enable interrupt generation on softconfig cards -- M.U */ +	/* .. but possibly potentially unsafe - Donald */ +	if (inb(ioaddr+14) & 0x20) +		outb(inb(ioaddr+4)|0x80, ioaddr+4); +#endif + +	return 0; +} + +static int +wd_open(struct device *dev) +{ +  int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + +  /* Map in the shared memory. Always set register 0 last to remain +	 compatible with very old boards. */ +  ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB; +  ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16; + +  if (ei_status.word16) +	  outb(ei_status.reg5, ioaddr+WD_CMDREG5); +  outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ + +  ei_open(dev); +  MOD_INC_USE_COUNT; +  return 0; +} + +static void +wd_reset_8390(struct device *dev) +{ +	int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + +	outb(WD_RESET, wd_cmd_port); +	if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies); +	ei_status.txing = 0; + +	/* Set up the ASIC registers, just in case something changed them. */ +	outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); +	if (ei_status.word16) +		outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5); + +	if (ei_debug > 1) printk("reset done\n"); +	return; +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but +   we don't need to be concerned with ring wrap as the header will be at +   the start of a page, so we optimize accordingly. */ + +static void +wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + +	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ +	unsigned long hdr_start = dev->mem_start + ((ring_page - WD_START_PG)<<8); + +	/* We'll always get a 4 byte header read followed by a packet read, so +	   we enable 16 bit mode before the header, and disable after the body. */ +	if (ei_status.word16) +		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); + +#ifdef notdef +	/* Officially this is what we are doing, but the readl() is faster */ +	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); +#else +	((unsigned int*)hdr)[0] = readl(hdr_start); +#endif +} + +/* Block input and output are easy on shared memory ethercards, and trivial +   on the Western digital card where there is no choice of how to do it. +   The only complications are that the ring buffer wraps, and need to map +   switch between 8- and 16-bit modes. */ + +static void +wd_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ +	unsigned long xfer_start = dev->mem_start + ring_offset - (WD_START_PG<<8); + +	if (xfer_start + count > dev->rmem_end) { +		/* We must wrap the input move. */ +		int semi_count = dev->rmem_end - xfer_start; +		memcpy_fromio(skb->data, xfer_start, semi_count); +		count -= semi_count; +		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count); +	} else { +		/* Packet is in one chunk -- we can copy + cksum. */ +		eth_io_copy_and_sum(skb, xfer_start, count, 0); +	} + +	/* Turn off 16 bit access so that reboot works.	 ISA brain-damage */ +	if (ei_status.word16) +		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); +} + +static void +wd_block_output(struct device *dev, int count, const unsigned char *buf, +				int start_page) +{ +	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ +	long shmem = dev->mem_start + ((start_page - WD_START_PG)<<8); + + +	if (ei_status.word16) { +		/* Turn on and off 16 bit access so that reboot works. */ +		outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); +		memcpy_toio(shmem, buf, count); +		outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); +	} else +		memcpy_toio(shmem, buf, count); +} + + +static int +wd_close_card(struct device *dev) +{ +	int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ + +	if (ei_debug > 1) +		printk("%s: Shutting down ethercard.\n", dev->name); +	ei_close(dev); + +	/* Change from 16-bit to 8-bit shared memory so reboot works. */ +	if (ei_status.word16) +		outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); + +	/* And disable the shared memory. */ +	outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + + +#ifdef MODULE +#define MAX_WD_CARDS	4	/* Max number of wd cards per module */ +#define NAMELEN		8	/* # of chars for storing dev->name */ +static char namelist[NAMELEN * MAX_WD_CARDS] = { 0, }; +static struct device dev_wd[MAX_WD_CARDS] = { +	{ +		NULL,		/* assign a chunk of namelist[] below */ +		0, 0, 0, 0, +		0, 0, +		0, 0, 0, NULL, NULL +	}, +}; + +static int io[MAX_WD_CARDS] = { 0, }; +static int irq[MAX_WD_CARDS]  = { 0, }; +static int mem[MAX_WD_CARDS] = { 0, }; +static int mem_end[MAX_WD_CARDS] = { 0, };	/* for non std. mem size */ + +/* This is set up so that only a single autoprobe takes place per call. +ISA device autoprobes on a running machine are not recommended. */ +int +init_module(void) +{ +	int this_dev, found = 0; + +	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { +		struct device *dev = &dev_wd[this_dev]; +		dev->name = namelist+(NAMELEN*this_dev); +		dev->irq = irq[this_dev]; +		dev->base_addr = io[this_dev]; +		dev->mem_start = mem[this_dev]; +		dev->mem_end = mem_end[this_dev]; +		dev->init = wd_probe; +		if (io[this_dev] == 0)  { +			if (this_dev != 0) break; /* only autoprobe 1st one */ +			printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n"); +		} +		if (register_netdev(dev) != 0) { +			printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); +			if (found != 0) return 0;	/* Got at least one. */ +			return -ENXIO; +		} +		found++; +	} + +	return 0; +} + +void +cleanup_module(void) +{ +	int this_dev; + +	for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { +		struct device *dev = &dev_wd[this_dev]; +		if (dev->priv != NULL) { +			int ioaddr = dev->base_addr - WD_NIC_OFFSET; +			kfree(dev->priv); +			dev->priv = NULL; +			free_irq(dev->irq, NULL); +			irq2dev_map[dev->irq] = NULL; +			release_region(ioaddr, WD_IO_EXTENT); +			unregister_netdev(dev); +		} +	} +} +#endif /* MODULE */ + + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c wd.c" + *  version-control: t + *  tab-width: 4 + *  kept-new-versions: 5 + * End: + */ diff --git a/linux/src/drivers/net/winbond-840.c b/linux/src/drivers/net/winbond-840.c new file mode 100644 index 0000000..556d8ad --- /dev/null +++ b/linux/src/drivers/net/winbond-840.c @@ -0,0 +1,1558 @@ +/* winbond-840.c: A Linux network device driver for the Winbond W89c840. */ +/* +	Written 1998-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Support information and updates available at +		http://www.scyld.com/network/drivers.html +	The information and support mailing lists are based at +		http://www.scyld.com/mailman/listinfo/ + +	Do not remove the copyright infomation. +	Do not change the version information unless an improvement has been made. +	Merely removing my name, as Compex has done in the past, does not count +	as an improvement. +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"winbond-840.c:v1.10 7/22/2003  Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/drivers.html\n"; + +/* Automatically extracted configuration info: +probe-func: winbond840_probe +config-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840 + +c-help-name: Winbond W89c840 PCI Ethernet support +c-help-symbol: CONFIG_WINBOND_840 +c-help: The winbond-840.c driver is for the Winbond W89c840 chip. +c-help: This chip is named TX9882 on the Compex RL100-ATX board. +c-help: More specific information and updates are available from +c-help: http://www.scyld.com/network/drivers.html +*/ + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). +   The '840 uses a 64 element hash table based on the Ethernet CRC.  */ +static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   Both 'options[]' and 'full_duplex[]' should exist for driver +   interoperability, however setting full_duplex[] is deprecated. +   The media type is usually passed in 'options[]'. +    The default is autonegotation for speed and duplex. +	This should rarely be overridden. +    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps. +    Use option values 0x10 and 0x100 for forcing half duplex fixed speed. +    Use option values 0x20 and 0x200 for forcing full duplex operation. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. +   The compiler will convert <unsigned>'%'<2^N> into a bit mask. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority, confuses the system network buffer limits, +   and wastes memory. +   Larger receive rings merely waste memory. +*/ +#define TX_RING_SIZE	16 +#define TX_QUEUE_LEN	10		/* Limit ring entries actually used, min 4. */ +#define RX_RING_SIZE	32 + +/* The presumed FIFO size for working around the Tx-FIFO-overflow bug. +   To avoid overflowing we don't queue again until we have room for a +   full-size packet. + */ +#define TX_FIFO_SIZE (2048) +#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16) + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. +   Re-autonegotiation may take up to 3 seconds. + */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +/* Include files, designed to support most kernel versions 2.0.0 and later. */ +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Configure the PCI bus bursts and FIFO thresholds. +	   486: Set 8 longword cache alignment, 8 longword burst. +	   586: Set 16 longword cache alignment, no burst limit. +	   Cache alignment bits 15:14	     Burst length 13:8 +		0000	<not allowed> 		0000 align to cache	0800 8 longwords +		4000	8  longwords		0100 1 longword		1000 16 longwords +		8000	16 longwords		0200 2 longwords	2000 32 longwords +		C000	32  longwords		0400 4 longwords +	Wait the specified 50 PCI cycles after a reset by initializing +	Tx and Rx queues and the address filter list. */ +#define TX_DESC_SIZE	16 +#if defined(__powerpc__) || defined(__sparc__)		/* Big endian */ +static int csr0 = 0x00100000 | 0xE000 | TX_DESC_SIZE; +#elif defined(__alpha__) || defined(__x86_64) || defined(__ia64) +static int csr0 = 0xE000 | TX_DESC_SIZE; +#elif defined(__i386__) +static int csr0 = 0xE000 | TX_DESC_SIZE; +#else +static int csr0 = 0xE000 | TX_DESC_SIZE; +#warning Processor architecture unknown! +#endif + + + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Winbond W89c840 Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM_DESC(debug, "Driver message level (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(full_duplex, "Non-zero to set forced full duplex."); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); + +/* +				Theory of Operation + +I. Board Compatibility + +This driver is for the Winbond w89c840 chip. + +II. Board-specific settings + +None. + +III. Driver operation + +This chip is very similar to the Digital 21*4* "Tulip" family.  The first +twelve registers and the descriptor format are nearly identical.  Read a +Tulip manual for operational details. + +A significant difference is that the multicast filter and station address are +stored in registers rather than loaded through a pseudo-transmit packet. + +Unlike the Tulip, transmit buffers are limited to 1KB.  To transmit a +full-sized packet we must use both data buffers in a descriptor.  Thus the +driver uses ring mode where descriptors are implicitly sequential in memory, +rather than using the second descriptor address as a chain pointer to +subsequent descriptors. + +IV. Notes + +If you are going to almost clone a Tulip, why not go all the way and avoid +the need for a new driver? + +IVb. References + +http://www.scyld.com/expert/100mbps.html +http://www.scyld.com/expert/NWay.html +http://www.winbond.com.tw/ + +IVc. Errata + +A horrible bug exists in the transmit FIFO.  Apparently the chip doesn't +correctly detect a full FIFO, and queuing more than 2048 bytes may result in +silent data corruption. + +*/ + + + +/* +  PCI probe table. +*/ +static void *w840_probe1(struct pci_dev *pdev, void *init_dev, +						 long ioaddr, int irq, int chip_idx, int find_cnt); +static int winbond_pwr_event(void *dev_instance, int event); +enum chip_capability_flags { +	CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,}; +#ifdef USE_IO_OPS +#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER) +#else +#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER) +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{"Winbond W89c840",			/* Sometime a Level-One switch card. */ +	 { 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 }, +	 W840_FLAGS, 128, CanHaveMII | HasBrokenTx | FDXOnNoMII}, +	{"Winbond W89c840", { 0x08401050, 0xffffffff, }, +	 W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, +	{"Compex RL100-ATX", { 0x201111F6, 0xffffffff,}, +	 W840_FLAGS, 128, CanHaveMII | HasBrokenTx}, +	{0,},						/* 0 terminated list. */ +}; + +struct drv_id_info winbond840_drv_id = { +	"winbond-840", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	w840_probe1, winbond_pwr_event }; + +/* This driver was written to use PCI memory space, however some x86 systems +   work only with I/O space accesses.  Pass -DUSE_IO_OPS to use PCI I/O space +   accesses instead of memory space. */ + +#ifdef USE_IO_OPS +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +/* Offsets to the Command and Status Registers, "CSRs". +   While similar to the Tulip, these registers are longword aligned. +   Note: It's not useful to define symbolic names for every register bit in +   the device.  The name can only partially document the semantics and make +   the driver longer and more difficult to read. +*/ +enum w840_offsets { +	PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08, +	RxRingPtr=0x0C, TxRingPtr=0x10, +	IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C, +	RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C, +	CurRxDescAddr=0x30, CurRxBufAddr=0x34,			/* Debug use */ +	MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40, +	CurTxDescAddr=0x4C, CurTxBufAddr=0x50, +}; + +/* Bits in the interrupt status/enable registers. */ +/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ +enum intr_status_bits { +	NormalIntr=0x10000, AbnormalIntr=0x8000, +	IntrPCIErr=0x2000, TimerInt=0x800, +	IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40, +	TxFIFOUnderflow=0x20, RxErrIntr=0x10, +	TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01, +}; + +/* Bits in the NetworkConfig register. */ +enum rx_mode_bits { +	TxOn=0x2000, RxOn=0x0002, FullDuplex=0x0200, +	AcceptErr=0x80, AcceptRunt=0x40, 		/* Not used */ +	AcceptBroadcast=0x20, AcceptMulticast=0x10, AcceptAllPhys=0x08, +}; + +enum mii_reg_bits { +	MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000, +	MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000, +}; + +/* The Tulip-like Rx and Tx buffer descriptors. */ +struct w840_rx_desc { +	s32 status; +	s32 length; +	u32 buffer1; +	u32 next_desc; +}; + +struct w840_tx_desc { +	s32 status; +	s32 length; +	u32 buffer1, buffer2;				/* We use only buffer 1.  */ +	char pad[TX_DESC_SIZE - 16]; +}; + +/* Bits in network_desc.status */ +enum desc_status_bits { +	DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000, +	DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000, +	DescIntr=0x80000000, +}; + +#define PRIV_ALIGN	15 	/* Required alignment mask */ +struct netdev_private { +	/* Descriptor rings first for alignment. */ +	struct w840_rx_desc rx_ring[RX_RING_SIZE]; +	struct w840_tx_desc tx_ring[TX_RING_SIZE]; +	struct net_device *next_module;		/* Link for devices of this type. */ +	void *priv_addr;					/* Unaligned address for kfree */ +	const char *product_name; +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for later free(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct net_device_stats stats; +	struct timer_list timer;	/* Media monitoring timer. */ +	/* Frequently used values: keep some adjacent for cache effect. */ +	int msg_level; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	int csr0, csr6; +	unsigned int polling;				/* Switched to polling mode. */ +	int max_interrupt_work; + +	struct w840_rx_desc *rx_head_desc; +	unsigned int rx_ring_size; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	unsigned int tx_ring_size; +	unsigned int cur_tx, dirty_tx; +	unsigned int tx_q_bytes, tx_unq_bytes; +	unsigned int tx_full:1;				/* The Tx queue is full. */ + +	/* These values track of the transceiver/media in use. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	/* Rx filter. */ +	u32 cur_rx_mode; +	u32 rx_filter[2]; +	int multicast_filter_limit; + +	/* MII transceiver section. */ +	int mii_cnt;						/* MII device addresses. */ +	u16 advertising;					/* NWay media advertisement */ +	unsigned char phys[2];				/* MII device addresses. */ +}; + +static int  eeprom_read(long ioaddr, int location); +static int  mdio_read(struct net_device *dev, int phy_id, int location); +static void mdio_write(struct net_device *dev, int phy_id, int location, int value); +static int  netdev_open(struct net_device *dev); +static void check_duplex(struct net_device *dev); +static void netdev_timer(unsigned long data); +static void tx_timeout(struct net_device *dev); +static void init_ring(struct net_device *dev); +static int  start_tx(struct sk_buff *skb, struct net_device *dev); +static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); +static void netdev_error(struct net_device *dev, int intr_status); +static int  netdev_rx(struct net_device *dev); +static void netdev_error(struct net_device *dev, int intr_status); +static inline unsigned ether_crc(int length, unsigned char *data); +static void set_rx_mode(struct net_device *dev); +static struct net_device_stats *get_stats(struct net_device *dev); +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int  netdev_close(struct net_device *dev); + + + +/* A list of our installed devices, for removing the driver module. */ +static struct net_device *root_net_dev = NULL; + +static void *w840_probe1(struct pci_dev *pdev, void *init_dev, +						 long ioaddr, int irq, int chip_idx, int card_idx) +{ +	struct net_device *dev; +	struct netdev_private *np; +	void *priv_mem; +	int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +#if LINUX_VERSION_CODE < 0x20155 +	printk(KERN_INFO "%s: %s at 0x%lx, %2.2x:%2.2x", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr, +		   pci_bus_number(pdev), pci_devfn(pdev)>>3); +#else +	printk(KERN_INFO "%s: %s at 0x%lx, %2.2x:%2.2x", +		   dev->name, pci_id_tbl[chip_idx].name, ioaddr, +		   pdev->bus->number, pdev->devfn>>3); +#endif + +	/* Warning: validate for big-endian machines. */ +	for (i = 0; i < 3; i++) +		((u16 *)dev->dev_addr)[i] = le16_to_cpu(eeprom_read(ioaddr, i)); + +	for (i = 0; i < 5; i++) +		printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Out of memory is very unlikely. */ +	if (priv_mem == NULL) +		return NULL; + +#ifdef USE_IO_OPS +	request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name); +#endif + +	/* Reset the chip to erase previous misconfiguration. +	   No hold time required! */ +	writel(0x00000001, ioaddr + PCIBusCfg); + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	/* The descriptor lists must be aligned. */ +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_net_dev; +	root_net_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = pci_id_tbl[chip_idx].drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; +	np->tx_ring_size = TX_RING_SIZE; +	np->rx_ring_size = RX_RING_SIZE; + +	if (dev->mem_start) +		option = dev->mem_start; + +	if ((card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0) +		|| (np->drv_flags & AlwaysFDX)) +		np->full_duplex = 1; + +	/* The chip-specific entries in the device structure. */ +	dev->open = &netdev_open; +	dev->hard_start_xmit = &start_tx; +	dev->stop = &netdev_close; +	dev->get_stats = &get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; + +	if (np->drv_flags & CanHaveMII) { +		int phy, phy_idx = 0; +		for (phy = 1; phy < 32 && phy_idx < 4; phy++) { +			int mii_status = mdio_read(dev, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				np->phys[phy_idx++] = phy; +				np->advertising = mdio_read(dev, phy, 4); +				printk(KERN_INFO "%s: MII PHY found at address %d, status " +					   "0x%4.4x advertising %4.4x.\n", +					   dev->name, phy, mii_status, np->advertising); +			} +		} +		np->mii_cnt = phy_idx; +		if (phy_idx == 0) { +			printk(KERN_WARNING "%s: MII PHY not found -- this device may " +				   "not operate correctly.\n" +				   KERN_WARNING "%s:  If this is a switch card, explicitly " +				   "force full duplex on this interface.\n", +				   dev->name, dev->name); +			if (np->drv_flags & FDXOnNoMII) { +				printk(KERN_INFO "%s:  Assuming a switch card, forcing full " +					   "duplex.\n", dev->name); +				np->full_duplex = np->duplex_lock = 1; +			} +		} +	} +	/* Allow forcing the media type. */ +	if (np->full_duplex) { +		printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" +			   " disabled.\n", dev->name); +		np->duplex_lock = 1; +	} +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 0x3ff; +		if (np->default_port & 0x330) { +			np->medialock = 1; +			printk(KERN_INFO "  Forcing %dMbs %s-duplex operation.\n", +				   (option & 0x300 ? 100 : 10), +				   (np->full_duplex ? "full" : "half")); +			if (np->mii_cnt) +				mdio_write(dev, np->phys[0], 0, +						   ((option & 0x300) ? 0x2000 : 0) | 	/* 100mbps? */ +						   (np->full_duplex ? 0x0100 : 0)); /* Full duplex? */ +		} +	} + +	return dev; +} + + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. +   The Winbond NIC uses serial bit streams generated by the host processor. */ + +/* Delay between EEPROM clock transitions. +   This "delay" is to force out buffered PCI writes. */ +#define eeprom_delay(ee_addr)	readl(ee_addr) + +enum EEPROM_Ctrl_Bits { +	EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805, +	EE_ChipSelect=0x801, EE_DataIn=0x08, +}; + +/* The EEPROM commands always start with 01.. preamble bits. +   Commands are prepended to the variable-length address. */ +enum EEPROM_Cmds { +	EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), +}; + +static int eeprom_read(long addr, int location) +{ +	int i; +	int retval = 0; +	long ee_addr = addr + EECtrl; +	int read_cmd = location | EE_ReadCmd; + +	writel(EE_ChipSelect, ee_addr); +	/* Shift the read command bits out. */ +	for (i = 10; i >= 0; i--) { +		short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; +		writel(dataval, ee_addr); +		eeprom_delay(ee_addr); +		writel(dataval | EE_ShiftClk, ee_addr); +		eeprom_delay(ee_addr); +	} +	writel(EE_ChipSelect, ee_addr); +	eeprom_delay(ee_addr); + +	for (i = 16; i > 0; i--) { +		writel(EE_ChipSelect | EE_ShiftClk, ee_addr); +		eeprom_delay(ee_addr); +		retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); +		writel(EE_ChipSelect, ee_addr); +		eeprom_delay(ee_addr); +	} + +	/* Terminate the EEPROM access. */ +	writel(0, ee_addr); +	return retval; +} + +/*  MII transceiver control section. +	Read and write the MII registers using software-generated serial +	MDIO protocol.  See the MII specifications or DP83840A data sheet +	for details. + +	The maximum data clock rate is 2.5 Mhz. +	The timing is decoupled from the processor clock by flushing the write +	from the CPU write buffer with a following read, and using PCI +	transaction time. */ +#define mdio_in(mdio_addr) readl(mdio_addr) +#define mdio_out(value, mdio_addr) writel(value, mdio_addr) +#define mdio_delay(mdio_addr) readl(mdio_addr) + +/* Set iff a MII transceiver on any interface requires mdio preamble. +   This only set with older tranceivers, so the extra +   code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 1; + +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and +   a few older transceivers. */ +static void mdio_sync(long mdio_addr) +{ +	int bits = 32; + +	/* Establish sync by sending at least 32 logic ones. */ +	while (--bits >= 0) { +		mdio_out(MDIO_WRITE1, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +} + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ +	long mdio_addr = dev->base_addr + MIICtrl; +	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; +	int i, retval = 0; + +	if (mii_preamble_required) +		mdio_sync(mdio_addr); + +	/* Shift the read command bits out. */ +	for (i = 15; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + +		mdio_out(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(dataval | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Read the two transition, 16 data, and wire-idle bits. */ +	for (i = 20; i > 0; i--) { +		mdio_out(MDIO_EnbIn, mdio_addr); +		mdio_delay(mdio_addr); +		retval = (retval << 1) | ((mdio_in(mdio_addr) & MDIO_DataIn) ? 1 : 0); +		mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return (retval>>1) & 0xffff; +} + +static void mdio_write(struct net_device *dev, int phy_id, int reg, int value) +{ +	long mdio_addr = dev->base_addr + MIICtrl; +	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (reg<<18) | value; +	int i; + +	if (mii_preamble_required) +		mdio_sync(mdio_addr); + +	/* Shift the command bits out. */ +	for (i = 31; i >= 0; i--) { +		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + +		mdio_out(dataval, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(dataval | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	/* Clear out extra bits. */ +	for (i = 2; i > 0; i--) { +		mdio_out(MDIO_EnbIn, mdio_addr); +		mdio_delay(mdio_addr); +		mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); +		mdio_delay(mdio_addr); +	} +	return; +} + + +static int netdev_open(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	writel(0x00000001, ioaddr + PCIBusCfg);		/* Reset */ + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: w89c840_open() irq %d.\n", +			   dev->name, dev->irq); + +	init_ring(dev); + +	writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); +	writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); + +	for (i = 0; i < 6; i++) +		writeb(dev->dev_addr[i], ioaddr + StationAddr + i); + +	/* Initialize other registers. */ +	np->csr0 = csr0; +	writel(np->csr0, ioaddr + PCIBusCfg); + +	if (dev->if_port == 0) +		dev->if_port = np->default_port; + +	writel(0, ioaddr + RxStartDemand); +	np->csr6 = np->full_duplex ? 0x20022202 : 0x20022002; +	check_duplex(dev); +	set_rx_mode(dev); + +	netif_start_tx_queue(dev); + +	/* Clear and Enable interrupts by setting the interrupt mask. +	   See enum intr_status_bits above for bit guide. +	   We omit: TimerInt, IntrRxDied, IntrTxStopped +	*/ +	writel(0x1A0F5, ioaddr + IntrStatus); +	writel(0x1A0F5, ioaddr + IntrEnable); + +	if (np->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); + +	/* Set the timer to check for link beat. */ +	init_timer(&np->timer); +	np->timer.expires = jiffies + 3*HZ; +	np->timer.data = (unsigned long)dev; +	np->timer.function = &netdev_timer;				/* timer handler */ +	add_timer(&np->timer); + +	return 0; +} + +static void check_duplex(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int mii_reg5 = mdio_read(dev, np->phys[0], 5); +	int negotiated =  mii_reg5 & np->advertising; +	int duplex; + +	if (np->duplex_lock  ||  mii_reg5 == 0xffff) +		return; +	duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; +	if (np->full_duplex != duplex) { +		np->full_duplex = duplex; +		if (np->msg_level & NETIF_MSG_LINK) +			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d " +				   "negotiated capability %4.4x.\n", dev->name, +				   duplex ? "full" : "half", np->phys[0], negotiated); +		np->csr6 &= ~0x200; +		np->csr6 |= duplex ? 0x200 : 0; +	} +} + +static void netdev_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 10*HZ; +	int old_csr6 = np->csr6; +	u32 intr_status = readl(ioaddr + IntrStatus); + +	if (np->msg_level & NETIF_MSG_TIMER) +		printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " +			   "config %8.8x.\n", +			   dev->name, intr_status, (int)readl(ioaddr + NetworkConfig)); +	/* Check for blocked interrupts. */ +	if (np->polling) { +		if (intr_status & 0x1ffff) { +			intr_handler(dev->irq, dev, 0); +			next_tick = 1; +			np->polling = 1; +		} else if (++np->polling > 10*HZ) +			np->polling = 0; +		else +			next_tick = 2; +	} else if ((intr_status & 0x1ffff)) { +		np->polling = 1; +	} + +	if (netif_queue_paused(dev)  && +		np->cur_tx - np->dirty_tx > 1  && +		(jiffies - dev->trans_start) > TX_TIMEOUT) { +		tx_timeout(dev); +	} +	check_duplex(dev); +	if (np->csr6 != old_csr6) { +		writel(np->csr6 & ~0x0002, ioaddr + NetworkConfig); +		writel(np->csr6 | 0x2002, ioaddr + NetworkConfig); +	} +	np->timer.expires = jiffies + next_tick; +	add_timer(&np->timer); +} + +static void tx_timeout(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," +		   " resetting...\n", dev->name, (int)readl(ioaddr + IntrStatus)); + +#ifndef __alpha__ +	if (np->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring); +		for (i = 0; i < np->rx_ring_size; i++) +			printk(" %8.8x", (unsigned int)np->rx_ring[i].status); +		printk("\n"KERN_DEBUG"  Tx ring %p: ", np->tx_ring); +		for (i = 0; i < np->tx_ring_size; i++) +			printk(" %8.8x", np->tx_ring[i].status); +		printk("\n"); +	} +#endif + +	/* Perhaps we should reinitialize the hardware here.  Just trigger a +	   Tx demand for now. */ +	writel(0, ioaddr + TxStartDemand); +	dev->if_port = 0; +	/* Stop and restart the chip's Tx processes . */ + +	dev->trans_start = jiffies; +	np->stats.tx_errors++; +	return; +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int i; + +	np->tx_full = 0; +	np->cur_tx = np->dirty_tx = 0; +	np->tx_q_bytes = np->tx_unq_bytes = 0; + +	np->cur_rx = np->dirty_rx = 0; +	np->rx_buf_sz = (dev->mtu <= 1522 ? PKT_BUF_SZ : dev->mtu + 14); +	np->rx_head_desc = &np->rx_ring[0]; + +	/* Initialize all Rx descriptors. */ +	for (i = 0; i < np->rx_ring_size; i++) { +		np->rx_ring[i].length = np->rx_buf_sz; +		np->rx_ring[i].status = 0; +		np->rx_ring[i].next_desc = virt_to_bus(&np->rx_ring[i+1]); +		np->rx_skbuff[i] = 0; +	} +	/* Mark the last entry as wrapping the ring. */ +	np->rx_ring[i-1].length |= DescEndRing; +	np->rx_ring[i-1].next_desc = virt_to_bus(&np->rx_ring[0]); + +	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */ +	for (i = 0; i < np->rx_ring_size; i++) { +		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz); +		np->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		np->rx_ring[i].buffer1 = virt_to_bus(skb->tail); +		np->rx_ring[i].status = DescOwn | DescIntr; +	} +	np->dirty_rx = (unsigned int)(i - np->rx_ring_size); + +	for (i = 0; i < np->tx_ring_size; i++) { +		np->tx_skbuff[i] = 0; +		np->tx_ring[i].status = 0; +	} +	return; +} + +static int start_tx(struct sk_buff *skb, struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	unsigned entry; + +	/* Block a timer-based transmit from overlapping. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			tx_timeout(dev); +		return 1; +	} + +	/* Note: Ordering is important here, set the field with the +	   "ownership" bit last, and only then increment cur_tx. */ + +	/* Calculate the next Tx descriptor entry. */ +	entry = np->cur_tx % np->tx_ring_size; + +	np->tx_skbuff[entry] = skb; +	np->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + +#define one_buffer +#define BPT 1022 +#if defined(one_buffer) +	np->tx_ring[entry].length = DescWholePkt | skb->len; +	if (entry >= np->tx_ring_size-1)		 /* Wrap ring */ +		np->tx_ring[entry].length |= DescIntr | DescEndRing; +	np->tx_ring[entry].status = DescOwn; +	np->cur_tx++; +#elif defined(two_buffer) +	if (skb->len > BPT) { +		unsigned int entry1 = ++np->cur_tx % np->tx_ring_size; +		np->tx_ring[entry].length = DescStartPkt | BPT; +		np->tx_ring[entry1].length = DescEndPkt | (skb->len - BPT); +		np->tx_ring[entry1].buffer1 = virt_to_bus((skb->data) + BPT); +		np->tx_ring[entry1].status = DescOwn; +		np->tx_ring[entry].status = DescOwn; +		if (entry >= np->tx_ring_size-1) +			np->tx_ring[entry].length |= DescIntr|DescEndRing; +		else if (entry1 >= np->tx_ring_size-1) +			np->tx_ring[entry1].length |= DescIntr|DescEndRing; +		np->cur_tx++; +	} else { +		np->tx_ring[entry].length = DescWholePkt | skb->len; +		if (entry >= np->tx_ring_size-1)		 /* Wrap ring */ +			np->tx_ring[entry].length |= DescIntr | DescEndRing; +		np->tx_ring[entry].status = DescOwn; +		np->cur_tx++; +	} +#elif defined(split_buffer) +	{ +		/* Work around the Tx-FIFO-full bug by splitting our transmit packet +		   into two pieces, the first which may be loaded without overflowing +		   the FIFO, and the second which contains the remainder of the +		   packet.  When we get a Tx-done interrupt that frees enough room +		   in the FIFO we mark the remainder of the packet as loadable. + +		   This has the problem that the Tx descriptors are written both +		   here and in the interrupt handler. +		*/ + +		int buf1size = TX_FIFO_SIZE - (np->tx_q_bytes - np->tx_unq_bytes); +		int buf2size = skb->len - buf1size; + +		if (buf2size <= 0) {		/* We fit into one descriptor. */ +			np->tx_ring[entry].length = DescWholePkt | skb->len; +		} else {				/* We must use two descriptors. */ +			unsigned int entry2; +			np->tx_ring[entry].length = DescIntr | DescStartPkt | buf1size; +			if (entry >= np->tx_ring_size-1) {		 /* Wrap ring */ +				np->tx_ring[entry].length |= DescEndRing; +				entry2 = 0; +			} else +				entry2 = entry + 1; +			np->cur_tx++; +			np->tx_ring[entry2].buffer1 = +				virt_to_bus(skb->data + buf1size); +			np->tx_ring[entry2].length = DescEndPkt | buf2size; +			if (entry2 >= np->tx_ring_size-1)		 /* Wrap ring */ +				np->tx_ring[entry2].length |= DescEndRing; +		} +		np->tx_ring[entry].status = DescOwn; +		np->cur_tx++; +	} +#endif +	np->tx_q_bytes += skb->len; +	writel(0, dev->base_addr + TxStartDemand); + +	/* Work around horrible bug in the chip by marking the queue as full +	   when we do not have FIFO room for a maximum sized packet. */ +	if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN) { +		np->tx_full = 1; +		netif_stop_tx_queue(dev); +	} else if ((np->drv_flags & HasBrokenTx) +			   && np->tx_q_bytes - np->tx_unq_bytes > TX_BUG_FIFO_LIMIT) { +		np->tx_full = 1; +		netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ + +	dev->trans_start = jiffies; + +	if (np->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", +			   dev->name, np->cur_tx, entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int work_limit = np->max_interrupt_work; + +	do { +		u32 intr_status = readl(ioaddr + IntrStatus); + +		/* Acknowledge all of the current interrupt sources ASAP. */ +		writel(intr_status & 0x0001ffff, ioaddr + IntrStatus); + +		if (np->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", +				   dev->name, intr_status); + +		if ((intr_status & (NormalIntr|AbnormalIntr)) == 0 +			|| intr_status == 0xffffffff) +			break; + +		if (intr_status & (IntrRxDone | RxNoBuf)) +			netdev_rx(dev); + +		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { +			int entry = np->dirty_tx % np->tx_ring_size; +			int tx_status = np->tx_ring[entry].status; + +			if (tx_status < 0) +				break; +			if (np->msg_level & NETIF_MSG_TX_DONE) +				printk(KERN_DEBUG "%s: Transmit done, Tx status %8.8x.\n", +					   dev->name, tx_status); +			if (tx_status & 0x8000) { 		/* There was an error, log it. */ +				if (np->msg_level & NETIF_MSG_TX_ERR) +					printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", +						   dev->name, tx_status); +				np->stats.tx_errors++; +				if (tx_status & 0x0104) np->stats.tx_aborted_errors++; +				if (tx_status & 0x0C80) np->stats.tx_carrier_errors++; +				if (tx_status & 0x0200) np->stats.tx_window_errors++; +				if (tx_status & 0x0002) np->stats.tx_fifo_errors++; +				if ((tx_status & 0x0080) && np->full_duplex == 0) +					np->stats.tx_heartbeat_errors++; +#ifdef ETHER_STATS +				if (tx_status & 0x0100) np->stats.collisions16++; +#endif +			} else { +#ifdef ETHER_STATS +				if (tx_status & 0x0001) np->stats.tx_deferred++; +#endif +#if LINUX_VERSION_CODE > 0x20127 +				np->stats.tx_bytes += np->tx_skbuff[entry]->len; +#endif +				np->stats.collisions += (tx_status >> 3) & 15; +				np->stats.tx_packets++; +			} +			/* Free the original skb. */ +			np->tx_unq_bytes += np->tx_skbuff[entry]->len; +			dev_free_skb_irq(np->tx_skbuff[entry]); +			np->tx_skbuff[entry] = 0; +		} +		if (np->tx_full && +			np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4 +			&&  np->tx_q_bytes - np->tx_unq_bytes < TX_BUG_FIFO_LIMIT) { +			/* The ring is no longer full, allow new TX entries. */ +			np->tx_full = 0; +			netif_resume_tx_queue(dev); +		} + +		/* Abnormal error summary/uncommon events handlers. */ +		if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr | +						   TimerInt | IntrTxStopped)) +			netdev_error(dev, intr_status); + +		if (--work_limit < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", dev->name, intr_status); +			/* Set the timer to re-enable the other interrupts after +			   10*82usec ticks. */ +			writel(AbnormalIntr | TimerInt, ioaddr + IntrEnable); +			writel(10, ioaddr + GPTimer); +			break; +		} +	} while (1); + +	if (np->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, (int)readl(ioaddr + IntrStatus)); + +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int netdev_rx(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	int entry = np->cur_rx % np->rx_ring_size; +	int work_limit = np->dirty_rx + np->rx_ring_size - np->cur_rx; + +	if (np->msg_level & NETIF_MSG_RX_STATUS) { +		printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n", +			   entry, np->rx_ring[entry].status); +	} + +	/* If EOP is set on the next entry, it's a new packet. Send it up. */ +	while (--work_limit >= 0) { +		struct w840_rx_desc *desc = np->rx_head_desc; +		s32 status = desc->status; + +		if (np->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n", +				   status); +		if (status < 0) +			break; +		if ((status & 0x38008300) != 0x0300) { +			if ((status & 0x38000300) != 0x0300) { +				/* Ingore earlier buffers. */ +				if ((status & 0xffff) != 0x7fff) { +					printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " +						   "multiple buffers, entry %#x status %4.4x!\n", +						   dev->name, np->cur_rx, status); +					np->stats.rx_length_errors++; +				} +			} else if (status & 0x8000) { +				/* There was a fatal error. */ +				if (np->msg_level & NETIF_MSG_RX_ERR) +					printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", +						   dev->name, status); +				np->stats.rx_errors++; /* end of a packet.*/ +				if (status & 0x0890) np->stats.rx_length_errors++; +				if (status & 0x004C) np->stats.rx_frame_errors++; +				if (status & 0x0002) np->stats.rx_crc_errors++; +			} +		} else { +			struct sk_buff *skb; +			/* Omit the four octet CRC from the length. */ +			int pkt_len = ((status >> 16) & 0x7ff) - 4; + +			if (np->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d" +					   " status %x.\n", pkt_len, status); +			/* Check if the packet is long enough to accept without copying +			   to a minimally-sized skbuff. */ +			if (pkt_len < np->rx_copybreak +				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +				/* Call copy + cksum if available. */ +#if HAS_IP_COPYSUM +				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +			} else { +				char *temp = skb_put(skb = np->rx_skbuff[entry], pkt_len); +				np->rx_skbuff[entry] = NULL; +#ifndef final_version				/* Remove after testing. */ +				if (bus_to_virt(desc->buffer1) != temp) +					printk(KERN_ERR "%s: Internal fault: The skbuff addresses " +						   "do not match in netdev_rx: %p vs. %p / %p.\n", +						   dev->name, bus_to_virt(desc->buffer1), +						   skb->head, temp); +#endif +			} +			skb->protocol = eth_type_trans(skb, dev); +			netif_rx(skb); +			dev->last_rx = jiffies; +			np->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			np->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++np->cur_rx) % np->rx_ring_size; +		np->rx_head_desc = &np->rx_ring[entry]; +	} + +	/* Refill the Rx ring buffers. */ +	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) { +		struct sk_buff *skb; +		entry = np->dirty_rx % np->rx_ring_size; +		if (np->rx_skbuff[entry] == NULL) { +			skb = dev_alloc_skb(np->rx_buf_sz); +			np->rx_skbuff[entry] = skb; +			if (skb == NULL) +				break;				/* Better luck next round. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			np->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); +		} +		np->rx_ring[entry].status = DescOwn; +	} + +	return 0; +} + +static void netdev_error(struct net_device *dev, int intr_status) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; + +	if (np->msg_level & NETIF_MSG_MISC) +		printk(KERN_DEBUG "%s: Abnormal event, %8.8x.\n", +			   dev->name, intr_status); +	if (intr_status == 0xffffffff) +		return; +	if (intr_status & TxFIFOUnderflow) { +		np->csr6 += 0x4000;	/* Bump up the Tx threshold */ +		if (np->msg_level & NETIF_MSG_TX_ERR) +			printk(KERN_DEBUG "%s: Tx underflow, increasing threshold to " +				   "%8.8x.\n", dev->name, np->csr6); +		writel(np->csr6, ioaddr + NetworkConfig); +	} +	if (intr_status & IntrRxDied) {		/* Missed a Rx frame. */ +		np->stats.rx_errors++; +	} +	if (intr_status & TimerInt) { +		/* Re-enable other interrupts. */ +		writel(0x1A0F5, ioaddr + IntrEnable); +	} +	np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; +	writel(0, ioaddr + RxStartDemand); +} + +static struct net_device_stats *get_stats(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; + +	/* The chip only need report frame silently dropped. */ +	if (netif_running(dev)) +		np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + +	return &np->stats; +} + +static unsigned const ethernet_polynomial = 0x04c11db7U; +static inline u32 ether_crc(int length, unsigned char *data) +{ +    int crc = -1; + +    while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 0; bit < 8; bit++, current_octet >>= 1) { +			crc = (crc << 1) ^ +				((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0); +		} +    } +    return crc; +} + +static void set_rx_mode(struct net_device *dev) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; +	u32 mc_filter[2];			/* Multicast hash filter */ +	u32 rx_mode; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		memset(mc_filter, ~0, sizeof(mc_filter)); +		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAllPhys; +	} else if ((dev->mc_count > np->multicast_filter_limit) +			   ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to match, or accept all multicasts. */ +		memset(mc_filter, 0xff, sizeof(mc_filter)); +		rx_mode = AcceptBroadcast | AcceptMulticast; +	} else { +		struct dev_mc_list *mclist; +		int i; +		memset(mc_filter, 0, sizeof(mc_filter)); +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			set_bit((ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F, +					mc_filter); +		} +		rx_mode = AcceptBroadcast | AcceptMulticast; +	} +	writel(mc_filter[0], ioaddr + MulticastFilter0); +	writel(mc_filter[1], ioaddr + MulticastFilter1); +	np->csr6 &= ~0x00F8; +	np->csr6 |= rx_mode; +	writel(np->csr6, ioaddr + NetworkConfig); +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = np->phys[0] & 0x1f; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == np->phys[0]) { +			u16 value = data[2]; +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				np->medialock = (value & 0x9000) ? 0 : 1; +				if (np->medialock) +					np->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: np->advertising = value; break; +			} +			/* Perhaps check_duplex(dev), depending on chip semantics. */ +		} +		mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + + +static void empty_rings(struct net_device *dev) +{ +	struct netdev_private *np = (void *)dev->priv; +	int i; + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < np->rx_ring_size; i++) { +		np->rx_ring[i].status = 0; +		if (np->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			np->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(np->rx_skbuff[i]); +		} +		np->rx_skbuff[i] = 0; +	} +	for (i = 0; i < np->tx_ring_size; i++) { +		if (np->tx_skbuff[i]) +			dev_free_skb(np->tx_skbuff[i]); +		np->tx_skbuff[i] = 0; +	} +} + +static int netdev_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct netdev_private *np = (struct netdev_private *)dev->priv; + +	netif_stop_tx_queue(dev); + +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %8.8x " +			   "Config %8.8x.\n", dev->name, (int)readl(ioaddr + IntrStatus), +			   (int)readl(ioaddr + NetworkConfig)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); +	} + +	/* Disable interrupts by clearing the interrupt mask. */ +	writel(0x0000, ioaddr + IntrEnable); + +	/* Stop the chip's Tx and Rx processes. */ +	writel(np->csr6 &= ~0x20FA, ioaddr + NetworkConfig); + +	del_timer(&np->timer); +	if (readl(ioaddr + NetworkConfig) != 0xffffffff) +		np->stats.rx_missed_errors += readl(ioaddr + RxMissed) & 0xffff; + +#ifdef __i386__ +	if (np->msg_level & NETIF_MSG_IFDOWN) { +		int i; +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(np->tx_ring)); +		for (i = 0; i < np->tx_ring_size; i++) +			printk(KERN_DEBUG " #%d desc. %4.4x %8.8x %8.8x.\n", +				   i, np->tx_ring[i].length, +				   np->tx_ring[i].status, np->tx_ring[i].buffer1); +		printk(KERN_DEBUG "\n" KERN_DEBUG "  Rx ring %8.8x:\n", +			   (int)virt_to_bus(np->rx_ring)); +		for (i = 0; i < np->rx_ring_size; i++) { +			printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", +				   i, np->rx_ring[i].length, +				   np->rx_ring[i].status, np->rx_ring[i].buffer1); +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); +	empty_rings(dev); + +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static int winbond_pwr_event(void *dev_instance, int event) +{ +	struct net_device *dev = dev_instance; +	struct netdev_private *np = (struct netdev_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	if (np->msg_level & NETIF_MSG_LINK) +		printk(KERN_DEBUG "%s: Handling power event %d.\n", dev->name, event); +	switch(event) { +	case DRV_ATTACH: +		MOD_INC_USE_COUNT; +		break; +	case DRV_SUSPEND: { +		int csr6 = readl(ioaddr + NetworkConfig); +		/* Disable interrupts, stop the chip, gather stats. */ +		if (csr6 != 0xffffffff) { +			int csr8 = readl(ioaddr + RxMissed); +			writel(0x00000000, ioaddr + IntrEnable); +			writel(csr6 & ~TxOn & ~RxOn, ioaddr + NetworkConfig); +			np->stats.rx_missed_errors += (unsigned short)csr8; +		} +		empty_rings(dev); +		break; +	} +	case DRV_RESUME: +		writel(np->csr0, ioaddr + PCIBusCfg); +		init_ring(dev); +		writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); +		writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); +		writel(0x1A0F5, ioaddr + IntrStatus); +		writel(0x1A0F5, ioaddr + IntrEnable); +		writel(np->csr6 | TxOn | RxOn, ioaddr + NetworkConfig); +		writel(0, ioaddr + RxStartDemand);		/* Rx poll demand */ +		set_rx_mode(dev); +		break; +	case DRV_DETACH: { +		struct net_device **devp, **next; +		if (dev->flags & IFF_UP) { +			printk(KERN_ERR "%s: Winbond-840 NIC removed while still " +				   "active.\n", dev->name); +			dev_close(dev); +			dev->flags &= ~(IFF_UP|IFF_RUNNING); +		} +		unregister_netdev(dev); +		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size); +#ifndef USE_IO_OPS +		iounmap((char *)dev->base_addr); +#endif +		for (devp = &root_net_dev; *devp; devp = next) { +			next = &((struct netdev_private *)(*devp)->priv)->next_module; +			if (*devp == dev) { +				*devp = *next; +				break; +			} +		} +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(dev); +		MOD_DEC_USE_COUNT; +		break; +	} +	default: +		break; +	} + +	return 0; +} + + +#ifdef MODULE +int init_module(void) +{ +	if (debug >= NETIF_MSG_DRV)	/* Emit version even if no cards detected. */ +		printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return pci_drv_register(&winbond840_drv_id, NULL); +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&winbond840_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_net_dev) { +		struct netdev_private *np = (void *)(root_net_dev->priv); +		unregister_netdev(root_net_dev); +#ifdef USE_IO_OPS +		release_region(root_net_dev->base_addr, +					   pci_id_tbl[np->chip_id].io_size); +#else +		iounmap((char *)(root_net_dev->base_addr)); +#endif +		next_dev = np->next_module; +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_net_dev); +		root_net_dev = next_dev; +	} +} +#else +int winbond840_probe(struct net_device *dev) +{ +	if (pci_drv_register(&winbond840_drv_id, dev) < 0) +		return -ENODEV; +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif  /* MODULE */ + + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` winbond-840.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c winbond-840.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/yellowfin.c b/linux/src/drivers/net/yellowfin.c new file mode 100644 index 0000000..9d7ace8 --- /dev/null +++ b/linux/src/drivers/net/yellowfin.c @@ -0,0 +1,1482 @@ +/* yellowfin.c: A Packet Engines G-NIC ethernet driver for linux. */ +/* +	Written 1997-2003 by Donald Becker. + +	This software may be used and distributed according to the terms of +	the GNU General Public License (GPL), incorporated herein by reference. +	Drivers based on or derived from this code fall under the GPL and must +	retain the authorship, copyright and license notice.  This file is not +	a complete program and may only be used when the entire operating +	system is licensed under the GPL. + +	This driver is for the Packet Engines G-NIC PCI Gigabit Ethernet adapter. +	It also supports the Symbios Logic version of the same chip core. + +	The author may be reached as becker@scyld.com, or C/O +	Scyld Computing Corporation +	914 Bay Ridge Road, Suite 220 +	Annapolis MD 21403 + +	Support information and updates available at +		http://www.scyld.com/network/yellowfin.html +	The information and support mailing lists are based at +		http://www.scyld.com/mailman/listinfo/ +*/ + +/* These identify the driver base version and may not be removed. */ +static const char version1[] = +"yellowfin.c:v1.10 7/22/2003  Written by Donald Becker <becker@scyld.com>\n"; +static const char version2[] = +"  http://www.scyld.com/network/yellowfin.html\n"; + +/* The user-configurable values. +   These may be modified when a driver module is loaded.*/ + +/* Message enable level: 0..31 = no..all messages.  See NETIF_MSG docs. */ +static int debug = 2; + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 20; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). +   Typical is a 64 element hash table based on the Ethernet CRC.  */ +static int multicast_filter_limit = 64; + +#ifdef YF_PROTOTYPE			/* Support for prototype hardware errata. */ +/* System-wide count of bogus-rx frames. */ +static int bogus_rx = 0; +static int dma_ctrl = 0x004A0263; 			/* Constrained by errata */ +static int fifo_cfg = 0x0020;				/* Bypass external Tx FIFO. */ +#elif YF_NEW					/* A future perfect board :->.  */ +static int dma_ctrl = 0x00CAC277;			/* Override when loading module! */ +static int fifo_cfg = 0x0028; +#else +static int dma_ctrl = 0x004A0263; 			/* Constrained by errata */ +static int fifo_cfg = 0x0020;				/* Bypass external Tx FIFO. */ +#endif + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. +   Setting to > 1518 effectively disables this feature. */ +static int rx_copybreak = 0; + +/* Used to pass the media type, etc. +   No media types are currently defined.  These options exist only for +   compatibility with other drivers. +*/ +#define MAX_UNITS 8		/* More are supported, limit only on options */ +static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Do ugly workaround for GX server chipset errata. */ +static int gx_fix = 0; + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for efficiency. +   Making the Tx ring too large decreases the effectiveness of channel +   bonding and packet priority, confuses the system network buffer limits, +   and wastes memory. +   Too-large receive rings waste memory and confound network buffer limits. +*/ +#define TX_RING_SIZE	16 +#define TX_QUEUE_SIZE	12		/* Must be > 4 && <= TX_RING_SIZE */ +#define RX_RING_SIZE	64 + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT  (6*HZ) + +/* Allocation size of Rx buffers with normal sized Ethernet frames. +   Do not change this value without good reason.  This is not a limit, +   but a way to keep a consistent allocation size among drivers. + */ +#define PKT_BUF_SZ		1536 + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif +#if !defined(__OPTIMIZE__) +#warning  You must compile this file with the correct options! +#warning  See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +#include <linux/config.h> +#if defined(CONFIG_SMP) && ! defined(__SMP__) +#define __SMP__ +#endif +#if defined(MODULE) && defined(CONFIG_MODVERSIONS) && ! defined(MODVERSIONS) +#define MODVERSIONS +#endif + +#include <linux/version.h> +#if defined(MODVERSIONS) +#include <linux/modversions.h> +#endif +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#if LINUX_VERSION_CODE >= 0x20400 +#include <linux/slab.h> +#else +#include <linux/malloc.h> +#endif +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <asm/processor.h>		/* Processor type for cache alignment. */ +#include <asm/unaligned.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#ifdef INLINE_PCISCAN +#include "k_compat.h" +#else +#include "pci-scan.h" +#include "kern_compat.h" +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr)) + +#if (LINUX_VERSION_CODE >= 0x20100)  &&  defined(MODULE) +char kernel_version[] = UTS_RELEASE; +#endif + +MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); +MODULE_DESCRIPTION("Packet Engines Yellowfin G-NIC Gigabit Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug, "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(multicast_filter_limit, "i"); +MODULE_PARM(max_interrupt_work, "i"); +MODULE_PARM(gx_fix, "i"); +MODULE_PARM_DESC(debug, "Driver message level enable (0-31)"); +MODULE_PARM_DESC(options, "Force transceiver type or fixed speed+duplex"); +MODULE_PARM_DESC(rx_copybreak, +				 "Breakpoint in bytes for copy-only-tiny-frames"); +MODULE_PARM_DESC(full_duplex, +				 "Non-zero to force full duplex, non-negotiated link " +				 "(deprecated)."); +MODULE_PARM_DESC(max_interrupt_work, +				 "Driver maximum events handled per interrupt"); +MODULE_PARM_DESC(multicast_filter_limit, +				 "Multicast addresses before switching to Rx-all-multicast"); +MODULE_PARM_DESC(gx_fix, "Set to work around old GX chipset errata"); + +/* +				Theory of Operation + +I. Board Compatibility + +This device driver is designed for the Packet Engines "Yellowfin" Gigabit +Ethernet adapter.  The only PCA currently supported is the G-NIC 64-bit +PCI card. + +II. Board-specific settings + +PCI bus devices are configured by the system at boot time, so no jumpers +need to be set on the board.  The system BIOS preferably should assign the +PCI INTA signal to an otherwise unused system IRQ line. +Note: Kernel versions earlier than 1.3.73 do not support shared PCI +interrupt lines. + +III. Driver operation + +IIIa. Ring buffers + +The Yellowfin uses the Descriptor Based DMA Architecture specified by Apple. +This is a descriptor list scheme similar to that used by the EEPro100 and +Tulip.  This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE. + +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the Yellowfin as receive data +buffers.  When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack and replaced by a newly allocated skbuff. + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames.  For small frames the copying cost is negligible (esp. considering +that we are pre-loading the cache with immediately useful header +information).  For large frames the copying cost is non-trivial, and the +larger copy might flush the cache of useful data. + +IIIC. Synchronization + +The driver runs as two independent, single-threaded flows of control.  One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag.  The other thread is the interrupt handler, which is single +threaded by the hardware and other software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'yp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring.  After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'yp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +Thanks to Kim Stearns of Packet Engines for providing a pair of G-NIC boards. +Thanks to Bruce Faust of Digitalscape for providing both their SYM53C885 board +and an AlphaStation to verifty the Alpha port! + +IVb. References + +Yellowfin Engineering Design Specification, 4/23/97 Preliminary/Confidential +Symbios SYM53C885 PCI-SCSI/Fast Ethernet Multifunction Controller Preliminary +   Data Manual v3.0 +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html +http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html + +IVc. Errata + +See Packet Engines confidential appendix (prototype chips only). +*/ + + + +static void *yellowfin_probe1(struct pci_dev *pdev, void *init_dev, +							  long ioaddr, int irq, int chip_idx, int fnd_cnt); +enum capability_flags { +	HasMII=1, FullTxStatus=2, IsGigabit=4, HasMulticastBug=8, FullRxStatus=16, +	HasMACAddrBug=32,			/* Only on early revs.  */ +}; +/* The PCI I/O space extent. */ +#define YELLOWFIN_SIZE 0x100 +#ifdef USE_IO_OPS +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO  | PCI_ADDR0) +#else +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1) +#endif + +static struct pci_id_info pci_id_tbl[] = { +	{"Yellowfin G-NIC Gigabit Ethernet", { 0x07021000, 0xffffffff}, +	 PCI_IOTYPE, YELLOWFIN_SIZE, +	 FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug}, +	{"Symbios SYM83C885", { 0x07011000, 0xffffffff}, +	 PCI_IOTYPE, YELLOWFIN_SIZE, HasMII }, +	{0,}, +}; + +struct drv_id_info yellowfin_drv_id = { +	"yellowfin", PCI_HOTSWAP, PCI_CLASS_NETWORK_ETHERNET<<8, pci_id_tbl, +	yellowfin_probe1, }; + +/* Offsets to the Yellowfin registers.  Various sizes and alignments. */ +enum yellowfin_offsets { +	TxCtrl=0x00, TxStatus=0x04, TxPtr=0x0C, +	TxIntrSel=0x10, TxBranchSel=0x14, TxWaitSel=0x18, +	RxCtrl=0x40, RxStatus=0x44, RxPtr=0x4C, +	RxIntrSel=0x50, RxBranchSel=0x54, RxWaitSel=0x58, +	EventStatus=0x80, IntrEnb=0x82, IntrClear=0x84, IntrStatus=0x86, +	ChipRev=0x8C, DMACtrl=0x90, TxThreshold=0x94, +	Cnfg=0xA0, FrameGap0=0xA2, FrameGap1=0xA4, +	MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC, +	MII_Status=0xAE, +	RxDepth=0xB8, FlowCtrl=0xBC, +	AddrMode=0xD0, StnAddr=0xD2, HashTbl=0xD8, FIFOcfg=0xF8, +	EEStatus=0xF0, EECtrl=0xF1, EEAddr=0xF2, EERead=0xF3, EEWrite=0xF4, +	EEFeature=0xF5, +}; + +/* The Yellowfin Rx and Tx buffer descriptors. +   Elements are written as 32 bit for endian portability. */ +struct yellowfin_desc { +	u32 dbdma_cmd; +	u32 addr; +	u32 branch_addr; +	u32 result_status; +}; + +struct tx_status_words { +#if defined(__powerpc__) +	u16 tx_errs; +	u16 tx_cnt; +	u16 paused; +	u16 total_tx_cnt; +#else  /* Little endian chips. */ +	u16 tx_cnt; +	u16 tx_errs; +	u16 total_tx_cnt; +	u16 paused; +#endif +}; + +/* Bits in yellowfin_desc.cmd */ +enum desc_cmd_bits { +	CMD_TX_PKT=0x10000000, CMD_RX_BUF=0x20000000, CMD_TXSTATUS=0x30000000, +	CMD_NOP=0x60000000, CMD_STOP=0x70000000, +	BRANCH_ALWAYS=0x0C0000, INTR_ALWAYS=0x300000, WAIT_ALWAYS=0x030000, +	BRANCH_IFTRUE=0x040000, +}; + +/* Bits in yellowfin_desc.status */ +enum desc_status_bits { RX_EOP=0x0040, }; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { +	IntrRxDone=0x01, IntrRxInvalid=0x02, IntrRxPCIFault=0x04,IntrRxPCIErr=0x08, +	IntrTxDone=0x10, IntrTxInvalid=0x20, IntrTxPCIFault=0x40,IntrTxPCIErr=0x80, +	IntrEarlyRx=0x100, IntrWakeup=0x200, }; + +#define PRIV_ALIGN	31 	/* Required alignment mask */ +struct yellowfin_private { +	/* Descriptor rings first for alignment. +	   Tx requires a second descriptor for status. */ +	struct yellowfin_desc rx_ring[RX_RING_SIZE]; +	struct yellowfin_desc tx_ring[TX_RING_SIZE*2]; +	struct net_device *next_module; +	void *priv_addr;					/* Unaligned address for kfree */ +	/* The addresses of receive-in-place skbuffs. */ +	struct sk_buff* rx_skbuff[RX_RING_SIZE]; +	/* The saved address of a sent-in-place packet/buffer, for later free(). */ +	struct sk_buff* tx_skbuff[TX_RING_SIZE]; +	struct tx_status_words tx_status[TX_RING_SIZE]; +	struct timer_list timer;	/* Media selection timer. */ +	struct net_device_stats stats; +	/* Frequently used and paired value: keep adjacent for cache effect. */ +	int msg_level; +	int chip_id, drv_flags; +	struct pci_dev *pci_dev; +	long in_interrupt; +	int max_interrupt_work; + +	struct yellowfin_desc *rx_head_desc; +	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */ +	unsigned int rx_buf_sz;				/* Based on MTU+slack. */ +	int rx_copybreak; + +	struct tx_status_words *tx_tail_desc; +	unsigned int cur_tx, dirty_tx; +	int tx_threshold; +	unsigned int tx_full:1;				/* The Tx queue is full. */ +	unsigned int full_duplex:1;			/* Full-duplex operation requested. */ +	unsigned int duplex_lock:1; +	unsigned int medialock:1;			/* Do not sense media. */ +	unsigned int default_port;			/* Last dev->if_port value. */ +	/* MII transceiver section. */ +	int mii_cnt;						/* MII device addresses. */ +	u16 advertising;					/* NWay media advertisement */ +	unsigned char phys[2];				/* MII device addresses. */ +	/* Rx multicast filter. */ +	u16 mc_filter[4]; +	int rx_mode; +	int multicast_filter_limit; +}; + +static int read_eeprom(long ioaddr, int location); +static int mdio_read(long ioaddr, int phy_id, int location); +static void mdio_write(long ioaddr, int phy_id, int location, int value); +#ifdef HAVE_PRIVATE_IOCTL +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#endif +static int yellowfin_open(struct net_device *dev); +static void yellowfin_timer(unsigned long data); +static void yellowfin_tx_timeout(struct net_device *dev); +static void yellowfin_init_ring(struct net_device *dev); +static int yellowfin_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void yellowfin_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int yellowfin_rx(struct net_device *dev); +static void yellowfin_error(struct net_device *dev, int intr_status); +static int yellowfin_close(struct net_device *dev); +static struct net_device_stats *yellowfin_get_stats(struct net_device *dev); +static void set_rx_mode(struct net_device *dev); + + + +/* A list of installed Yellowfin devices, for removing the driver module. */ +static struct net_device *root_yellowfin_dev = NULL; + +#ifndef MODULE +int yellowfin_probe(struct net_device *dev) +{ +	if (pci_drv_register(&yellowfin_drv_id, dev) < 0) +		return -ENODEV; +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return 0; +} +#endif + +static void *yellowfin_probe1(struct pci_dev *pdev, void *init_dev, +							  long ioaddr, int irq, int chip_idx, int find_cnt) +{ +	struct net_device *dev; +	struct yellowfin_private *np; +	void *priv_mem; +	int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; +	int drv_flags = pci_id_tbl[chip_idx].drv_flags; + +	dev = init_etherdev(init_dev, 0); +	if (!dev) +		return NULL; + +	printk(KERN_INFO "%s: %s type %8x at 0x%lx, ", +		   dev->name, pci_id_tbl[chip_idx].name, (int)inl(ioaddr + ChipRev), +		   ioaddr); + +	if (drv_flags & IsGigabit) +		for (i = 0; i < 6; i++) +			dev->dev_addr[i] = inb(ioaddr + StnAddr + i); +	else { +		int ee_offset = (read_eeprom(ioaddr, 6) == 0xff ? 0x100 : 0); +		for (i = 0; i < 6; i++) +			dev->dev_addr[i] = read_eeprom(ioaddr, ee_offset + i); +	} +	for (i = 0; i < 5; i++) +			printk("%2.2x:", dev->dev_addr[i]); +	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); + +	/* Reset the chip. */ +	outl(0x80000000, ioaddr + DMACtrl); + +	/* Make certain elements e.g. descriptor lists are aligned. */ +	priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); +	/* Check for the very unlikely case of no memory. */ +	if (priv_mem == NULL) +		return NULL; + +	/* We do a request_region() only to register /proc/ioports info. */ +	request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name); + +	dev->base_addr = ioaddr; +	dev->irq = irq; + +	dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); +	memset(np, 0, sizeof(*np)); +	np->priv_addr = priv_mem; + +	np->next_module = root_yellowfin_dev; +	root_yellowfin_dev = dev; + +	np->pci_dev = pdev; +	np->chip_id = chip_idx; +	np->drv_flags = drv_flags; +	np->msg_level = (1 << debug) - 1; +	np->rx_copybreak = rx_copybreak; +	np->max_interrupt_work = max_interrupt_work; +	np->multicast_filter_limit = multicast_filter_limit; + +	if (dev->mem_start) +		option = dev->mem_start; + +	/* The lower four bits are the media type. */ +	if (option > 0) { +		if (option & 0x220) +			np->full_duplex = 1; +		np->default_port = option & 15; +		if (np->default_port) +			np->medialock = 1; +	} +	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0) +		np->full_duplex = 1; + +	if (np->full_duplex) +		np->duplex_lock = 1; + +	/* The Yellowfin-specific entries in the device structure. */ +	dev->open = &yellowfin_open; +	dev->hard_start_xmit = &yellowfin_start_xmit; +	dev->stop = &yellowfin_close; +	dev->get_stats = &yellowfin_get_stats; +	dev->set_multicast_list = &set_rx_mode; +	dev->do_ioctl = &mii_ioctl; + +	if (np->drv_flags & HasMII) { +		int phy, phy_idx = 0; +		for (phy = 0; phy < 32 && phy_idx < 4; phy++) { +			int mii_status = mdio_read(ioaddr, phy, 1); +			if (mii_status != 0xffff  &&  mii_status != 0x0000) { +				np->phys[phy_idx++] = phy; +				np->advertising = mdio_read(ioaddr, phy, 4); +				printk(KERN_INFO "%s: MII PHY found at address %d, status " +					   "0x%4.4x advertising %4.4x.\n", +					   dev->name, phy, mii_status, np->advertising); +			} +		} +		np->mii_cnt = phy_idx; +	} + +	return dev; +} + +static int read_eeprom(long ioaddr, int location) +{ +	int bogus_cnt = 10000;		/* Typical 33Mhz: 1050 ticks */ + +	outb(location, ioaddr + EEAddr); +	outb(0x30 | ((location >> 8) & 7), ioaddr + EECtrl); +	while ((inb(ioaddr + EEStatus) & 0x80)  &&  --bogus_cnt > 0) +		; +	return inb(ioaddr + EERead); +} + +/* MII Managemen Data I/O accesses. +   These routines assume the MDIO controller is idle, and do not exit until +   the command is finished. */ + +static int mdio_read(long ioaddr, int phy_id, int location) +{ +	int i; + +	outw((phy_id<<8) + location, ioaddr + MII_Addr); +	outw(1, ioaddr + MII_Cmd); +	for (i = 10000; i >= 0; i--) +		if ((inw(ioaddr + MII_Status) & 1) == 0) +			break; +	return inw(ioaddr + MII_Rd_Data); +} + +static void mdio_write(long ioaddr, int phy_id, int location, int value) +{ +	int i; + +	outw((phy_id<<8) + location, ioaddr + MII_Addr); +	outw(value, ioaddr + MII_Wr_Data); + +	/* Wait for the command to finish. */ +	for (i = 10000; i >= 0; i--) +		if ((inw(ioaddr + MII_Status) & 1) == 0) +			break; +	return; +} + + +static int yellowfin_open(struct net_device *dev) +{ +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int i; + +	/* Reset the chip. */ +	outl(0x80000000, ioaddr + DMACtrl); + +	MOD_INC_USE_COUNT; + +	if (request_irq(dev->irq, &yellowfin_interrupt, SA_SHIRQ, dev->name, +					dev)) { +		MOD_DEC_USE_COUNT; +		return -EAGAIN; +	} + +	if (yp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: yellowfin_open() irq %d.\n", +			   dev->name, dev->irq); + +	yellowfin_init_ring(dev); + +	outl(virt_to_bus(yp->rx_ring), ioaddr + RxPtr); +	outl(virt_to_bus(yp->tx_ring), ioaddr + TxPtr); + +	for (i = 0; i < 6; i++) +		outb(dev->dev_addr[i], ioaddr + StnAddr + i); + +	/* Set up various condition 'select' registers. +	   There are no options here. */ +	outl(0x00800080, ioaddr + TxIntrSel); 	/* Interrupt on Tx abort */ +	outl(0x00800080, ioaddr + TxBranchSel);	/* Branch on Tx abort */ +	outl(0x00400040, ioaddr + TxWaitSel); 	/* Wait on Tx status */ +	outl(0x00400040, ioaddr + RxIntrSel);	/* Interrupt on Rx done */ +	outl(0x00400040, ioaddr + RxBranchSel);	/* Branch on Rx error */ +	outl(0x00400040, ioaddr + RxWaitSel);	/* Wait on Rx done */ + +	/* Initialize other registers: with so many this eventually this will +	   converted to an offset/value list. */ +	outl(dma_ctrl, ioaddr + DMACtrl); +	outw(fifo_cfg, ioaddr + FIFOcfg); +	/* Enable automatic generation of flow control frames, period 0xffff. */ +	outl(0x0030FFFF, ioaddr + FlowCtrl); + +	yp->tx_threshold = 32; +	outl(yp->tx_threshold, ioaddr + TxThreshold); + +	if (dev->if_port == 0) +		dev->if_port = yp->default_port; + +	yp->in_interrupt = 0; + +	/* Setting the Rx mode will start the Rx process. */ +	if (yp->drv_flags & IsGigabit) { +		/* We are always in full-duplex mode with gigabit! */ +		yp->full_duplex = 1; +		outw(0x01CF, ioaddr + Cnfg); +	} else { +		outw(0x0018, ioaddr + FrameGap0); /* 0060/4060 for non-MII 10baseT */ +		outw(0x1018, ioaddr + FrameGap1); +		outw(0x101C | (yp->full_duplex ? 2 : 0), ioaddr + Cnfg); +	} +	yp->rx_mode = 0; +	set_rx_mode(dev); +	netif_start_tx_queue(dev); + +	/* Enable interrupts by setting the interrupt mask. */ +	outw(0x81ff, ioaddr + IntrEnb);			/* See enum intr_status_bits */ +	outw(0x0000, ioaddr + EventStatus);		/* Clear non-interrupting events */ +	outl(0x80008000, ioaddr + RxCtrl);		/* Start Rx and Tx channels. */ +	outl(0x80008000, ioaddr + TxCtrl); + +	if (yp->msg_level & NETIF_MSG_IFUP) +		printk(KERN_DEBUG "%s: Done yellowfin_open().\n", +			   dev->name); + +	/* Set the timer to check for link beat. */ +	init_timer(&yp->timer); +	yp->timer.expires = jiffies + 3*HZ; +	yp->timer.data = (unsigned long)dev; +	yp->timer.function = &yellowfin_timer;				/* timer handler */ +	add_timer(&yp->timer); + +	return 0; +} + +static void yellowfin_timer(unsigned long data) +{ +	struct net_device *dev = (struct net_device *)data; +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	long ioaddr = dev->base_addr; +	int next_tick = 60*HZ; + +	if (yp->msg_level & NETIF_MSG_TIMER) +		printk(KERN_DEBUG "%s: Yellowfin timer tick, status %8.8x.\n", +			   dev->name, inw(ioaddr + IntrStatus)); + +	if (jiffies - dev->trans_start > TX_TIMEOUT +		&& yp->cur_tx - yp->dirty_tx > 1 +		&& netif_queue_paused(dev)) +		yellowfin_tx_timeout(dev); + +	if (yp->mii_cnt) { +		int mii_reg1 = mdio_read(ioaddr, yp->phys[0], 1); +		int mii_reg5 = mdio_read(ioaddr, yp->phys[0], 5); +		int negotiated = mii_reg5 & yp->advertising; +		if (yp->msg_level & NETIF_MSG_TIMER) +			printk(KERN_DEBUG "%s: MII #%d status register is %4.4x, " +				   "link partner capability %4.4x.\n", +				   dev->name, yp->phys[0], mii_reg1, mii_reg5); + +		if ( ! yp->duplex_lock && +			 ((negotiated & 0x0300) == 0x0100 +			  || (negotiated & 0x00C0) == 0x0040)) { +			yp->full_duplex = 1; +		} +		outw(0x101C | (yp->full_duplex ? 2 : 0), ioaddr + Cnfg); + +		if (mii_reg1 & 0x0004) +			next_tick = 60*HZ; +		else +			next_tick = 3*HZ; +	} + +	yp->timer.expires = jiffies + next_tick; +	add_timer(&yp->timer); +} + +static void yellowfin_tx_timeout(struct net_device *dev) +{ +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	long ioaddr = dev->base_addr; + +	printk(KERN_WARNING "%s: Yellowfin transmit timed out at %d/%d Tx " +		   "status %4.4x, Rx status %4.4x, resetting...\n", +		   dev->name, yp->cur_tx, yp->dirty_tx, +		   (int)inl(ioaddr + TxStatus), (int)inl(ioaddr + RxStatus)); + +	/* Note: these should be KERN_DEBUG. */ +	if (yp->msg_level & NETIF_MSG_TX_ERR) { +		int i; +		printk(KERN_DEBUG "  Rx ring %p: ", yp->rx_ring); +		for (i = 0; i < RX_RING_SIZE; i++) +			printk(" %8.8x", yp->rx_ring[i].result_status); +		printk("\n"KERN_DEBUG"  Tx ring %p: ", yp->tx_ring); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(" %4.4x /%8.8x", yp->tx_status[i].tx_errs, +				   yp->tx_ring[i].result_status); +		printk("\n"); +	} + +	/* If the hardware is found to hang regularly, we will update the code +	   to reinitialize the chip here. */ +	dev->if_port = 0; + +	/* Wake the potentially-idle transmit channel. */ +	outl(0x10001000, dev->base_addr + TxCtrl); +	if (yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE) +		netif_unpause_tx_queue(dev); + +	dev->trans_start = jiffies; +	yp->stats.tx_errors++; +	return; +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void yellowfin_init_ring(struct net_device *dev) +{ +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	int i; + +	yp->tx_full = 0; +	yp->cur_rx = yp->cur_tx = 0; +	yp->dirty_tx = 0; + +	yp->rx_buf_sz = dev->mtu + 18 + 15; +	/* Match other driver's allocation size when possible. */ +	if (yp->rx_buf_sz < PKT_BUF_SZ) +		yp->rx_buf_sz = PKT_BUF_SZ; +	yp->rx_head_desc = &yp->rx_ring[0]; + +	for (i = 0; i < RX_RING_SIZE; i++) { +		yp->rx_ring[i].dbdma_cmd = +			cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz); +		yp->rx_ring[i].branch_addr = virt_to_le32desc(&yp->rx_ring[i+1]); +	} +	/* Mark the last entry as wrapping the ring. */ +	yp->rx_ring[i-1].branch_addr = virt_to_le32desc(&yp->rx_ring[0]); + +	for (i = 0; i < RX_RING_SIZE; i++) { +		struct sk_buff *skb = dev_alloc_skb(yp->rx_buf_sz); +		yp->rx_skbuff[i] = skb; +		if (skb == NULL) +			break; +		skb->dev = dev;			/* Mark as being used by this device. */ +		skb_reserve(skb, 2);	/* 16 byte align the IP header. */ +		yp->rx_ring[i].addr = virt_to_le32desc(skb->tail); +	} +	yp->rx_ring[i-1].dbdma_cmd = cpu_to_le32(CMD_STOP); +	yp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + +#define NO_TXSTATS +#ifdef NO_TXSTATS +	/* In this mode the Tx ring needs only a single descriptor. */ +	for (i = 0; i < TX_RING_SIZE; i++) { +		yp->tx_skbuff[i] = 0; +		yp->tx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP); +		yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[i+1]); +	} +	/* Wrap ring */ +	yp->tx_ring[--i].dbdma_cmd = cpu_to_le32(CMD_STOP | BRANCH_ALWAYS); +	yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[0]); +#else +	/* Tx ring needs a pair of descriptors, the second for the status. */ +	for (i = 0; i < TX_RING_SIZE*2; i++) { +		yp->tx_skbuff[i/2] = 0; +		/* Branch on Tx error. */ +		yp->tx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP); +		yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[i+1]); +		i++; +		if (yp->flags & FullTxStatus) { +			yp->tx_ring[i].dbdma_cmd = +				cpu_to_le32(CMD_TXSTATUS | sizeof(yp->tx_status[i])); +			yp->tx_ring[i].request_cnt = sizeof(yp->tx_status[i]); +			yp->tx_ring[i].addr = virt_to_le32desc(&yp->tx_status[i/2]); +		} else {				/* Symbios chips write only tx_errs word. */ +			yp->tx_ring[i].dbdma_cmd = +				cpu_to_le32(CMD_TXSTATUS | INTR_ALWAYS | 2); +			yp->tx_ring[i].request_cnt = 2; +			yp->tx_ring[i].addr = virt_to_le32desc(&yp->tx_status[i/2].tx_errs); +		} +		yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[i+1]); +	} +	/* Wrap ring */ +	yp->tx_ring[--i].dbdma_cmd |= cpu_to_le32(BRANCH_ALWAYS | INTR_ALWAYS); +	yp->tx_ring[i].branch_addr = virt_to_le32desc(&yp->tx_ring[0]); +#endif +	yp->tx_tail_desc = &yp->tx_status[0]; +	return; +} + +static int yellowfin_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	unsigned entry; + +#if LINUX_VERSION_CODE < 0x20323 +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (netif_pause_tx_queue(dev) != 0) { +		/* This watchdog code is redundant with the media monitor timer. */ +		if (jiffies - dev->trans_start > TX_TIMEOUT) +			yellowfin_tx_timeout(dev); +		return 1; +	} +#endif + +	/* Note: Ordering is important here, set the field with the +	   "ownership" bit last, and only then increment cur_tx. */ + +	/* Calculate the next Tx descriptor entry. */ +	entry = yp->cur_tx % TX_RING_SIZE; + +	yp->tx_skbuff[entry] = skb; + +	if (gx_fix) {		/* Note: only works for paddable protocols e.g. IP. */ +		int cacheline_end = (virt_to_bus(skb->data) + skb->len) % 32; +		/* Fix GX chipset errata. */ +		if (cacheline_end > 24  || cacheline_end == 0) +			skb->len += 32 - cacheline_end + 1; +	} +#ifdef NO_TXSTATS +	yp->tx_ring[entry].addr = virt_to_le32desc(skb->data); +	yp->tx_ring[entry].result_status = 0; +	if (entry >= TX_RING_SIZE-1) { +		/* New stop command. */ +		yp->tx_ring[0].dbdma_cmd = cpu_to_le32(CMD_STOP); +		yp->tx_ring[TX_RING_SIZE-1].dbdma_cmd = +			cpu_to_le32(CMD_TX_PKT|BRANCH_ALWAYS | skb->len); +	} else { +		yp->tx_ring[entry+1].dbdma_cmd = cpu_to_le32(CMD_STOP); +		yp->tx_ring[entry].dbdma_cmd = +			cpu_to_le32(CMD_TX_PKT | BRANCH_IFTRUE | skb->len); +	} +	yp->cur_tx++; +#else +	yp->tx_ring[entry<<1].request_cnt = skb->len; +	yp->tx_ring[entry<<1].addr = virt_to_le32desc(skb->data); +	/* The input_last (status-write) command is constant, but we must rewrite +	   the subsequent 'stop' command. */ + +	yp->cur_tx++; +	{ +		unsigned next_entry = yp->cur_tx % TX_RING_SIZE; +		yp->tx_ring[next_entry<<1].dbdma_cmd = cpu_to_le32(CMD_STOP); +	} +	/* Final step -- overwrite the old 'stop' command. */ + +	yp->tx_ring[entry<<1].dbdma_cmd = +		cpu_to_le32( ((entry % 6) == 0 ? CMD_TX_PKT|INTR_ALWAYS|BRANCH_IFTRUE : +					  CMD_TX_PKT | BRANCH_IFTRUE) | skb->len); +#endif + +	/* Non-x86 Todo: explicitly flush cache lines here. */ + +	/* Wake the potentially-idle transmit channel. */ +	outl(0x10001000, dev->base_addr + TxCtrl); + +	if (yp->cur_tx - yp->dirty_tx >= TX_QUEUE_SIZE) { +		netif_stop_tx_queue(dev); +		yp->tx_full = 1; +		if (yp->cur_tx - (volatile int)yp->dirty_tx < TX_QUEUE_SIZE) { +			netif_unpause_tx_queue(dev); +			yp->tx_full = 0; +		} else +			netif_stop_tx_queue(dev); +	} else +		netif_unpause_tx_queue(dev);		/* Typical path */ +	dev->trans_start = jiffies; + +	if (yp->msg_level & NETIF_MSG_TX_QUEUED) { +		printk(KERN_DEBUG "%s: Yellowfin transmit frame #%d queued in slot %d.\n", +			   dev->name, yp->cur_tx, entry); +	} +	return 0; +} + +/* The interrupt handler does all of the Rx thread work and cleans up +   after the Tx thread. */ +static void yellowfin_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ +	struct net_device *dev = (struct net_device *)dev_instance; +	struct yellowfin_private *yp; +	long ioaddr; +	int boguscnt = max_interrupt_work; + +#ifndef final_version			/* Can never occur. */ +	if (dev == NULL) { +		printk (KERN_ERR "yellowfin_interrupt(): irq %d for unknown device.\n", irq); +		return; +	} +#endif + +	ioaddr = dev->base_addr; +	yp = (struct yellowfin_private *)dev->priv; +	if (test_and_set_bit(0, (void*)&yp->in_interrupt)) { +		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); +		return; +	} + +	do { +		u16 intr_status = inw(ioaddr + IntrClear); + +		if (yp->msg_level & NETIF_MSG_INTR) +			printk(KERN_DEBUG "%s: Yellowfin interrupt, status %4.4x.\n", +				   dev->name, intr_status); + +		if (intr_status == 0) +			break; + +		if (intr_status & (IntrRxDone | IntrEarlyRx)) { +			yellowfin_rx(dev); +			outl(0x10001000, ioaddr + RxCtrl);		/* Wake Rx engine. */ +		} + +#ifdef NO_TXSTATS +		for (; yp->cur_tx - yp->dirty_tx > 0; yp->dirty_tx++) { +			int entry = yp->dirty_tx % TX_RING_SIZE; +			if (yp->tx_ring[entry].result_status == 0) +				break; +			yp->stats.tx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			yp->stats.tx_bytes += yp->tx_skbuff[entry]->len; +#endif +			/* Free the original skb. */ +			dev_free_skb_irq(yp->tx_skbuff[entry]); +			yp->tx_skbuff[entry] = 0; +		} +		if (yp->tx_full +			&& yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE - 4) { +			/* The ring is no longer full, clear tbusy. */ +			yp->tx_full = 0; +			netif_resume_tx_queue(dev); +		} +#else +		if (intr_status & IntrTxDone +			|| yp->tx_tail_desc->tx_errs) { +			unsigned dirty_tx = yp->dirty_tx; + +			for (dirty_tx = yp->dirty_tx; yp->cur_tx - dirty_tx > 0; +				 dirty_tx++) { +				/* Todo: optimize this. */ +				int entry = dirty_tx % TX_RING_SIZE; +				u16 tx_errs = yp->tx_status[entry].tx_errs; + +#ifndef final_version +				if (yp->msg_level & NETIF_MSG_INTR) +					printk(KERN_DEBUG "%s: Tx queue %d check, Tx status " +						   "%4.4x %4.4x %4.4x %4.4x.\n", +						   dev->name, entry, +						   yp->tx_status[entry].tx_cnt, +						   yp->tx_status[entry].tx_errs, +						   yp->tx_status[entry].total_tx_cnt, +						   yp->tx_status[entry].paused); +#endif +				if (tx_errs == 0) +					break;			/* It still hasn't been Txed */ +				if (tx_errs & 0xF810) { +					/* There was an major error, log it. */ +#ifndef final_version +					if (yp->msg_level & NETIF_MSG_TX_ERR) +						printk(KERN_DEBUG "%s: Transmit error, Tx status %4.4x.\n", +							   dev->name, tx_errs); +#endif +					yp->stats.tx_errors++; +					if (tx_errs & 0xF800) yp->stats.tx_aborted_errors++; +					if (tx_errs & 0x0800) yp->stats.tx_carrier_errors++; +					if (tx_errs & 0x2000) yp->stats.tx_window_errors++; +					if (tx_errs & 0x8000) yp->stats.tx_fifo_errors++; +#ifdef ETHER_STATS +					if (tx_errs & 0x1000) yp->stats.collisions16++; +#endif +				} else { +#ifndef final_version +					if (yp->msg_level & NETIF_MSG_TX_DONE) +						printk(KERN_DEBUG "%s: Normal transmit, Tx status %4.4x.\n", +							   dev->name, tx_errs); +#endif +#ifdef ETHER_STATS +					if (tx_errs & 0x0400) yp->stats.tx_deferred++; +#endif +#if LINUX_VERSION_CODE > 0x20127 +					yp->stats.tx_bytes += yp->tx_skbuff[entry]->len; +#endif +					yp->stats.collisions += tx_errs & 15; +					yp->stats.tx_packets++; +				} +				/* Free the original skb. */ +				dev_free_skb_irq(yp->tx_skbuff[entry]); +				yp->tx_skbuff[entry] = 0; +				/* Mark status as empty. */ +				yp->tx_status[entry].tx_errs = 0; +			} + +#ifndef final_version +			if (yp->cur_tx - dirty_tx > TX_RING_SIZE) { +				printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", +					   dev->name, dirty_tx, yp->cur_tx, yp->tx_full); +				dirty_tx += TX_RING_SIZE; +			} +#endif + +			if (yp->tx_full +				&& yp->cur_tx - dirty_tx < TX_QUEUE_SIZE - 2) { +				/* The ring is no longer full, clear tbusy. */ +				yp->tx_full = 0; +				netif_resume_tx_queue(dev); +			} + +			yp->dirty_tx = dirty_tx; +			yp->tx_tail_desc = &yp->tx_status[dirty_tx % TX_RING_SIZE]; +		} +#endif + +		/* Log errors and other uncommon events. */ +		if (intr_status & 0x2ee)	/* Abnormal error summary. */ +			yellowfin_error(dev, intr_status); + +		if (--boguscnt < 0) { +			printk(KERN_WARNING "%s: Too much work at interrupt, " +				   "status=0x%4.4x.\n", +				   dev->name, intr_status); +			break; +		} +	} while (1); + +	if (yp->msg_level & NETIF_MSG_INTR) +		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", +			   dev->name, inw(ioaddr + IntrStatus)); + +	clear_bit(0, (void*)&yp->in_interrupt); +	return; +} + +/* This routine is logically part of the interrupt handler, but separated +   for clarity and better register allocation. */ +static int yellowfin_rx(struct net_device *dev) +{ +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	int entry = yp->cur_rx % RX_RING_SIZE; +	int boguscnt = yp->dirty_rx + RX_RING_SIZE - yp->cur_rx; + +	if (yp->msg_level & NETIF_MSG_RX_STATUS) { +		printk(KERN_DEBUG " In yellowfin_rx(), entry %d status %8.8x.\n", +			   entry, yp->rx_ring[entry].result_status); +		printk(KERN_DEBUG "   #%d desc. %8.8x %8.8x %8.8x.\n", +			   entry, yp->rx_ring[entry].dbdma_cmd, yp->rx_ring[entry].addr, +			   yp->rx_ring[entry].result_status); +	} + +	/* If EOP is set on the next entry, it's a new packet. Send it up. */ +	while (yp->rx_head_desc->result_status) { +		struct yellowfin_desc *desc = yp->rx_head_desc; +		u16 desc_status = le32_to_cpu(desc->result_status) >> 16; +		int data_size = +			(le32_to_cpu(desc->dbdma_cmd) - le32_to_cpu(desc->result_status)) +			& 0xffff; +		u8 *buf_addr = le32desc_to_virt(desc->addr); +		s16 frame_status = get_unaligned((s16*)&(buf_addr[data_size - 2])); + +		if (yp->msg_level & NETIF_MSG_RX_STATUS) +			printk(KERN_DEBUG "  yellowfin_rx() status was %4.4x.\n", +				   frame_status); +		if (--boguscnt < 0) +			break; +		if ( ! (desc_status & RX_EOP)) { +			printk(KERN_WARNING "%s: Oversized Ethernet frame spanned multiple buffers," +				   " status %4.4x!\n", dev->name, desc_status); +			yp->stats.rx_length_errors++; +		} else if ((yp->drv_flags & IsGigabit)  &&  (frame_status & 0x0038)) { +			/* There was a error. */ +			if (yp->msg_level & NETIF_MSG_RX_ERR) +				printk(KERN_DEBUG "  yellowfin_rx() Rx error was %4.4x.\n", +					   frame_status); +			yp->stats.rx_errors++; +			if (frame_status & 0x0060) yp->stats.rx_length_errors++; +			if (frame_status & 0x0008) yp->stats.rx_frame_errors++; +			if (frame_status & 0x0010) yp->stats.rx_crc_errors++; +			if (frame_status < 0) yp->stats.rx_dropped++; +		} else if ( !(yp->drv_flags & IsGigabit)  && +				   ((buf_addr[data_size-1] & 0x85) || buf_addr[data_size-2] & 0xC0)) { +			u8 status1 = buf_addr[data_size-2]; +			u8 status2 = buf_addr[data_size-1]; +			yp->stats.rx_errors++; +			if (status1 & 0xC0) yp->stats.rx_length_errors++; +			if (status2 & 0x03) yp->stats.rx_frame_errors++; +			if (status2 & 0x04) yp->stats.rx_crc_errors++; +			if (status2 & 0x80) yp->stats.rx_dropped++; +#ifdef YF_PROTOTYPE			/* Support for prototype hardware errata. */ +		} else if ((yp->flags & HasMACAddrBug)  && +				   memcmp(le32desc_to_virt(yp->rx_ring[entry].addr), +						  dev->dev_addr, 6) != 0 +				   && memcmp(le32desc_to_virt(yp->rx_ring[entry].addr), +							 "\377\377\377\377\377\377", 6) != 0) { +			if (bogus_rx++ == 0) +				printk(KERN_WARNING "%s: Bad frame to %2.2x:%2.2x:%2.2x:%2.2x:" +					   "%2.2x:%2.2x.\n", +					   dev->name, buf_addr[0], buf_addr[1], buf_addr[2], +					   buf_addr[3], buf_addr[4], buf_addr[5]); +#endif +		} else { +			struct sk_buff *skb; +			int pkt_len = data_size - +				(yp->chip_id ? 7 : 8 + buf_addr[data_size - 8]); +			/* To verify: Yellowfin Length should omit the CRC! */ + +#ifndef final_version +			if (yp->msg_level & NETIF_MSG_RX_STATUS) +				printk(KERN_DEBUG "  yellowfin_rx() normal Rx pkt length %d" +					   " of %d, bogus_cnt %d.\n", +					   pkt_len, data_size, boguscnt); +#endif +			/* Check if the packet is long enough to just pass up the skbuff +			   without copying to a properly sized skbuff. */ +			if (pkt_len > yp->rx_copybreak) { +				char *temp = skb_put(skb = yp->rx_skbuff[entry], pkt_len); +				yp->rx_skbuff[entry] = NULL; +#ifndef final_version				/* Remove after testing. */ +				if (le32desc_to_virt(yp->rx_ring[entry].addr) != temp) +					printk(KERN_ERR "%s: Internal fault: The skbuff addresses " +						   "do not match in yellowfin_rx: %p vs. %p / %p.\n", +						   dev->name, +						   le32desc_to_virt(yp->rx_ring[entry].addr), +						   skb->head, temp); +#endif +			} else { +				skb = dev_alloc_skb(pkt_len + 2); +				if (skb == NULL) +					break; +				skb->dev = dev; +				skb_reserve(skb, 2);	/* 16 byte align the IP header */ +#if HAS_IP_COPYSUM +				eth_copy_and_sum(skb, yp->rx_skbuff[entry]->tail, pkt_len, 0); +				skb_put(skb, pkt_len); +#else +				memcpy(skb_put(skb, pkt_len), yp->rx_skbuff[entry]->tail, +					   pkt_len); +#endif +			} +			skb->protocol = eth_type_trans(skb, dev); +			netif_rx(skb); +			dev->last_rx = jiffies; +			yp->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 +			yp->stats.rx_bytes += pkt_len; +#endif +		} +		entry = (++yp->cur_rx) % RX_RING_SIZE; +		yp->rx_head_desc = &yp->rx_ring[entry]; +	} + +	/* Refill the Rx ring buffers. */ +	for (; yp->cur_rx - yp->dirty_rx > 0; yp->dirty_rx++) { +		entry = yp->dirty_rx % RX_RING_SIZE; +		if (yp->rx_skbuff[entry] == NULL) { +			struct sk_buff *skb = dev_alloc_skb(yp->rx_buf_sz); +			yp->rx_skbuff[entry] = skb; +			if (skb == NULL) +				break;				/* Better luck next round. */ +			skb->dev = dev;			/* Mark as being used by this device. */ +			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */ +			yp->rx_ring[entry].addr = virt_to_le32desc(skb->tail); +		} +		yp->rx_ring[entry].dbdma_cmd = cpu_to_le32(CMD_STOP); +		yp->rx_ring[entry].result_status = 0;	/* Clear complete bit. */ +		if (entry != 0) +			yp->rx_ring[entry - 1].dbdma_cmd = +				cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz); +		else +			yp->rx_ring[RX_RING_SIZE - 1].dbdma_cmd = +				cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS +							| yp->rx_buf_sz); +	} + +	return 0; +} + +static void yellowfin_error(struct net_device *dev, int intr_status) +{ +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; + +	printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", +		   dev->name, intr_status); +	/* Hmmmmm, it's not clear what to do here. */ +	if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) +		yp->stats.tx_errors++; +	if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) +		yp->stats.rx_errors++; +} + +static int yellowfin_close(struct net_device *dev) +{ +	long ioaddr = dev->base_addr; +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	int i; + +	netif_stop_tx_queue(dev); + +	if (yp->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x " +			   "Rx %4.4x Int %2.2x.\n", +			   dev->name, inw(ioaddr + TxStatus), +			   inw(ioaddr + RxStatus), inw(ioaddr + IntrStatus)); +		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n", +			   dev->name, yp->cur_tx, yp->dirty_tx, yp->cur_rx, yp->dirty_rx); +	} + +	/* Disable interrupts by clearing the interrupt mask. */ +	outw(0x0000, ioaddr + IntrEnb); + +	/* Stop the chip's Tx and Rx processes. */ +	outl(0x80000000, ioaddr + RxCtrl); +	outl(0x80000000, ioaddr + TxCtrl); + +	del_timer(&yp->timer); + +#if defined(__i386__) +	if (yp->msg_level & NETIF_MSG_IFDOWN) { +		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n", +			   (int)virt_to_bus(yp->tx_ring)); +		for (i = 0; i < TX_RING_SIZE*2; i++) +			printk(" %c #%d desc. %8.8x %8.8x %8.8x %8.8x.\n", +				   inl(ioaddr + TxPtr) == (long)&yp->tx_ring[i] ? '>' : ' ', +				   i, yp->tx_ring[i].dbdma_cmd, yp->tx_ring[i].addr, +				   yp->tx_ring[i].branch_addr, yp->tx_ring[i].result_status); +		printk(KERN_DEBUG "  Tx status %p:\n", yp->tx_status); +		for (i = 0; i < TX_RING_SIZE; i++) +			printk(KERN_DEBUG "   #%d status %4.4x %4.4x %4.4x %4.4x.\n", +				   i, yp->tx_status[i].tx_cnt, yp->tx_status[i].tx_errs, +				   yp->tx_status[i].total_tx_cnt, yp->tx_status[i].paused); + +		printk("\n"KERN_DEBUG "  Rx ring %8.8x:\n", +			   (int)virt_to_bus(yp->rx_ring)); +		for (i = 0; i < RX_RING_SIZE; i++) { +			printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x %8.8x\n", +				   inl(ioaddr + RxPtr) == (long)&yp->rx_ring[i] ? '>' : ' ', +				   i, yp->rx_ring[i].dbdma_cmd, yp->rx_ring[i].addr, +				   yp->rx_ring[i].result_status); +			if (yp->msg_level & NETIF_MSG_PKTDATA) { +				if (get_unaligned((u8*)yp->rx_ring[i].addr) != 0x69) { +					int j; +					for (j = 0; j < 0x50; j++) +						printk(" %4.4x", +							   get_unaligned(((u16*)yp->rx_ring[i].addr) + j)); +					printk("\n"); +				} +			} +		} +	} +#endif /* __i386__ debugging only */ + +	free_irq(dev->irq, dev); + +	/* Free all the skbuffs in the Rx queue. */ +	for (i = 0; i < RX_RING_SIZE; i++) { +		yp->rx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP); +		yp->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */ +		if (yp->rx_skbuff[i]) { +#if LINUX_VERSION_CODE < 0x20100 +			yp->rx_skbuff[i]->free = 1; +#endif +			dev_free_skb(yp->rx_skbuff[i]); +		} +		yp->rx_skbuff[i] = 0; +	} +	for (i = 0; i < TX_RING_SIZE; i++) { +		if (yp->tx_skbuff[i]) +			dev_free_skb(yp->tx_skbuff[i]); +		yp->tx_skbuff[i] = 0; +	} + +#ifdef YF_PROTOTYPE			/* Support for prototype hardware errata. */ +	if (yp->msg_level & NETIF_MSG_IFDOWN) { +		printk(KERN_DEBUG "%s: Received %d frames that we should not have.\n", +			   dev->name, bogus_rx); +	} +#endif +	MOD_DEC_USE_COUNT; + +	return 0; +} + +static struct net_device_stats *yellowfin_get_stats(struct net_device *dev) +{ +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	return &yp->stats; +} + +/* Set or clear the multicast filter for this adaptor. */ + +/* The little-endian AUTODIN32 ethernet CRC calculation. +   N.B. Do not use for bulk data, use a table-based routine instead. +   This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; + +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ +	unsigned int crc = 0xffffffff;	/* Initial value. */ +	while(--length >= 0) { +		unsigned char current_octet = *data++; +		int bit; +		for (bit = 8; --bit >= 0; current_octet >>= 1) { +			if ((crc ^ current_octet) & 1) { +				crc >>= 1; +				crc ^= ethernet_polynomial_le; +			} else +				crc >>= 1; +		} +	} +	return crc; +} + + +static void set_rx_mode(struct net_device *dev) +{ +	struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; +	u16 hash_table[4] = {0, 0, 0, 0}; +	int mc_change = 0; +	int new_rx_mode, i; + +	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */ +		/* Unconditionally log net taps. */ +		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); +		new_rx_mode = 0x000F; +	} else if (dev->mc_count > yp->multicast_filter_limit +			   ||  (dev->flags & IFF_ALLMULTI)) { +		/* Too many to filter well, or accept all multicasts. */ +		new_rx_mode = 0x000B; +	} else if (dev->mc_count > 0) { /* Must use the multicast hash table. */ +		struct dev_mc_list *mclist; + +		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; +			 i++, mclist = mclist->next) { +			/* Due to a bug in the early chip versions, multiple filter +			   slots must be set for each address. */ +			if (yp->drv_flags & HasMulticastBug) { +				set_bit((ether_crc_le(3, mclist->dmi_addr) >> 3) & 0x3f, +						hash_table); +				set_bit((ether_crc_le(4, mclist->dmi_addr) >> 3) & 0x3f, +						hash_table); +				set_bit((ether_crc_le(5, mclist->dmi_addr) >> 3) & 0x3f, +						hash_table); +			} +			set_bit((ether_crc_le(6, mclist->dmi_addr) >> 3) & 0x3f, +					hash_table); +		} +		if (memcmp(hash_table, yp->mc_filter, sizeof hash_table) != 0) +			mc_change = 1; +		new_rx_mode = 0x0003; +	} else {					/* Normal, unicast/broadcast-only mode. */ +		new_rx_mode = 0x0001; +	} + +	/* Stop the Rx process to change any value. */ +	if (yp->rx_mode != new_rx_mode || mc_change) { +		long ioaddr = dev->base_addr; +		u16 cfg_value = inw(ioaddr + Cnfg); + +		outw(cfg_value & ~0x1000, ioaddr + Cnfg); + +		yp->rx_mode = new_rx_mode; +		outw(new_rx_mode, ioaddr + AddrMode); +		memcpy(yp->mc_filter, hash_table, sizeof hash_table); +		/* Copy the hash table to the chip. */ +		for (i = 0; i < 4; i++) +			outw(hash_table[i], ioaddr + HashTbl + i*2); + +		/* Restart the Rx process. */ +		outw(cfg_value | 0x1000, ioaddr + Cnfg); +	} +} + +static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ +	struct yellowfin_private *np = (void *)dev->priv; +	long ioaddr = dev->base_addr; +	u16 *data = (u16 *)&rq->ifr_data; +	u32 *data32 = (void *)&rq->ifr_data; + +	switch(cmd) { +	case 0x8947: case 0x89F0: +		/* SIOCGMIIPHY: Get the address of the PHY in use. */ +		data[0] = np->phys[0] & 0x1f; +		/* Fall Through */ +	case 0x8948: case 0x89F1: +		/* SIOCGMIIREG: Read the specified MII register. */ +		data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f); +		return 0; +	case 0x8949: case 0x89F2: +		/* SIOCSMIIREG: Write the specified MII register */ +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		if (data[0] == np->phys[0]) { +			u16 value = data[2]; +			switch (data[1]) { +			case 0: +				/* Check for autonegotiation on or reset. */ +				np->medialock = (value & 0x9000) ? 0 : 1; +				if (np->medialock) +					np->full_duplex = (value & 0x0100) ? 1 : 0; +				break; +			case 4: np->advertising = value; break; +			} +			/* Perhaps check_duplex(dev), depending on chip semantics. */ +		} +		mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]); +		return 0; +	case SIOCGPARAMS: +		data32[0] = np->msg_level; +		data32[1] = np->multicast_filter_limit; +		data32[2] = np->max_interrupt_work; +		data32[3] = np->rx_copybreak; +		return 0; +	case SIOCSPARAMS: +		if (!capable(CAP_NET_ADMIN)) +			return -EPERM; +		np->msg_level = data32[0]; +		np->multicast_filter_limit = data32[1]; +		np->max_interrupt_work = data32[2]; +		np->rx_copybreak = data32[3]; +		return 0; +	default: +		return -EOPNOTSUPP; +	} +} + + +#ifdef MODULE +int init_module(void) +{ +	/* Emit version even if no cards detected. */ +	printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); +	return pci_drv_register(&yellowfin_drv_id, NULL); +} + +void cleanup_module(void) +{ +	struct net_device *next_dev; + +	pci_drv_unregister(&yellowfin_drv_id); + +	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */ +	while (root_yellowfin_dev) { +		struct yellowfin_private *np = (void *)(root_yellowfin_dev->priv); +		unregister_netdev(root_yellowfin_dev); +#ifdef USE_IO_OPS +		release_region(root_yellowfin_dev->base_addr, +					   pci_id_tbl[np->chip_id].io_size); +#else +		iounmap((char *)root_yellowfin_dev->base_addr); +#endif +		next_dev = np->next_module; +		if (np->priv_addr) +			kfree(np->priv_addr); +		kfree(root_yellowfin_dev); +		root_yellowfin_dev = next_dev; +	} +} + +#endif  /* MODULE */ + +/* + * Local variables: + *  compile-command: "make KERNVER=`uname -r` yellowfin.o" + *  compile-cmd: "gcc -DMODULE -Wall -Wstrict-prototypes -O6 -c yellowfin.c" + *  simple-compile-command: "gcc -DMODULE -O6 -c yellowfin.c" + *  c-indent-level: 4 + *  c-basic-offset: 4 + *  tab-width: 4 + * End: + */ diff --git a/linux/src/drivers/net/znet.c b/linux/src/drivers/net/znet.c new file mode 100644 index 0000000..a9996fd --- /dev/null +++ b/linux/src/drivers/net/znet.c @@ -0,0 +1,746 @@ +/* znet.c: An Zenith Z-Note ethernet driver for linux. */ + +static const char *version = "znet.c:v1.02 9/23/94 becker@cesdis.gsfc.nasa.gov\n"; + +/* +	Written by Donald Becker. + +	The author may be reached as becker@cesdis.gsfc.nasa.gov. +	This driver is based on the Linux skeleton driver.  The copyright of the +	skeleton driver is held by the United States Government, as represented +	by DIRNSA, and it is released under the GPL. + +	Thanks to Mike Hollick for alpha testing and suggestions. + +  References: +	   The Crynwr packet driver. + +	  "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992 +	  Intel Microcommunications Databook, Vol. 1, 1990. +    As usual with Intel, the documentation is incomplete and inaccurate. +	I had to read the Crynwr packet driver to figure out how to actually +	use the i82593, and guess at what register bits matched the loosely +	related i82586. + +					Theory of Operation + +	The i82593 used in the Zenith Z-Note series operates using two(!) slave +	DMA	channels, one interrupt, and one 8-bit I/O port. + +	While there	several ways to configure '593 DMA system, I chose the one +	that seemed commensurate with the highest system performance in the face +	of moderate interrupt latency: Both DMA channels are configured as +	recirculating ring buffers, with one channel (#0) dedicated to Rx and +	the other channel (#1) to Tx and configuration.  (Note that this is +	different than the Crynwr driver, where the Tx DMA channel is initialized +	before each operation.  That approach simplifies operation and Tx error +	recovery, but requires additional I/O in normal operation and precludes +	transmit buffer	chaining.) + +	Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE.  This provides +	a reasonable ring size for Rx, while simplifying DMA buffer allocation -- +	DMA buffers must not cross a 128K boundary.  (In truth the size selection +	was influenced by my lack of '593 documentation.  I thus was constrained +	to use the Crynwr '593 initialization table, which sets the Rx ring size +	to 8K.) + +	Despite my usual low opinion about Intel-designed parts, I must admit +	that the bulk data handling of the i82593 is a good design for +	an integrated system, like a laptop, where using two slave DMA channels +	doesn't pose a problem.  I still take issue with using only a single I/O +	port.  In the same controlled environment there are essentially no +	limitations on I/O space, and using multiple locations would eliminate +	the	need for multiple operations when looking at status registers, +	setting the Rx ring boundary, or switching to promiscuous mode. + +	I also question Zenith's selection of the '593: one of the advertised +	advantages of earlier Intel parts was that if you figured out the magic +	initialization incantation you could use the same part on many different +	network types.  Zenith's use of the "FriendlyNet" (sic) connector rather +	than an	on-board transceiver leads me to believe that they were planning +	to take advantage of this.  But, uhmmm, the '593 omits all but ethernet +	functionality from the serial subsystem. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> + +#ifndef ZNET_DEBUG +#define ZNET_DEBUG 1 +#endif +static unsigned int znet_debug = ZNET_DEBUG; + +/* The DMA modes we need aren't in <dma.h>. */ +#define DMA_RX_MODE		0x14	/* Auto init, I/O to mem, ++, demand. */ +#define DMA_TX_MODE		0x18	/* Auto init, Mem to I/O, ++, demand. */ +#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17) +#define DMA_BUF_SIZE 8192 +#define RX_BUF_SIZE 8192 +#define TX_BUF_SIZE 8192 + +/* Commands to the i82593 channel 0. */ +#define CMD0_CHNL_0			0x00 +#define CMD0_CHNL_1			0x10		/* Switch to channel 1. */ +#define CMD0_NOP (CMD0_CHNL_0) +#define CMD0_PORT_1	CMD0_CHNL_1 +#define CMD1_PORT_0	1 +#define CMD0_IA_SETUP		1 +#define CMD0_CONFIGURE		2 +#define CMD0_MULTICAST_LIST 3 +#define CMD0_TRANSMIT		4 +#define CMD0_DUMP			6 +#define CMD0_DIAGNOSE		7 +#define CMD0_Rx_ENABLE		8 +#define CMD0_Rx_DISABLE		10 +#define CMD0_Rx_STOP		11 +#define CMD0_RETRANSMIT		12 +#define CMD0_ABORT			13 +#define CMD0_RESET			14 + +#define CMD0_ACK 0x80 + +#define CMD0_STAT0 (0 << 5) +#define CMD0_STAT1 (1 << 5) +#define CMD0_STAT2 (2 << 5) +#define CMD0_STAT3 (3 << 5) + +#define net_local znet_private +struct znet_private { +	int rx_dma, tx_dma; +	struct enet_statistics stats; +	/* The starting, current, and end pointers for the packet buffers. */ +	ushort *rx_start, *rx_cur, *rx_end; +	ushort *tx_start, *tx_cur, *tx_end; +	ushort tx_buf_len;			/* Tx buffer length, in words. */ +}; + +/* Only one can be built-in;-> */ +static struct znet_private zn; +static ushort dma_buffer1[DMA_BUF_SIZE/2]; +static ushort dma_buffer2[DMA_BUF_SIZE/2]; +static ushort dma_buffer3[DMA_BUF_SIZE/2 + 8]; + +/* The configuration block.  What an undocumented nightmare.  The first +   set of values are those suggested (without explanation) for ethernet +   in the Intel 82586 databook.	 The rest appear to be completely undocumented, +   except for cryptic notes in the Crynwr packet driver.  This driver uses +   the Crynwr values verbatim. */ + +static unsigned char i593_init[] = { +  0xAA,					/* 0: 16-byte input & 80-byte output FIFO. */ +						/*	  threshold, 96-byte FIFO, 82593 mode. */ +  0x88,					/* 1: Continuous w/interrupts, 128-clock DMA.*/ +  0x2E,					/* 2: 8-byte preamble, NO address insertion, */ +						/*	  6-byte Ethernet address, loopback off.*/ +  0x00,					/* 3: Default priorities & backoff methods. */ +  0x60,					/* 4: 96-bit interframe spacing. */ +  0x00,					/* 5: 512-bit slot time (low-order). */ +  0xF2,					/* 6: Slot time (high-order), 15 COLL retries. */ +  0x00,					/* 7: Promisc-off, broadcast-on, default CRC. */ +  0x00,					/* 8: Default carrier-sense, collision-detect. */ +  0x40,					/* 9: 64-byte minimum frame length. */ +  0x5F,					/* A: Type/length checks OFF, no CRC input, +						   "jabber" termination, etc. */ +  0x00,					/* B: Full-duplex disabled. */ +  0x3F,					/* C: Default multicast addresses & backoff. */ +  0x07,					/* D: Default IFS retriggering. */ +  0x31,					/* E: Internal retransmit, drop "runt" packets, +						   synchr. DRQ deassertion, 6 status bytes. */ +  0x22,					/* F: Receive ring-buffer size (8K),  +						   receive-stop register enable. */ +}; + +struct netidblk { +	char magic[8];		/* The magic number (string) "NETIDBLK" */ +	unsigned char netid[8]; /* The physical station address */ +	char nettype, globalopt; +	char vendor[8];		/* The machine vendor and product name. */ +	char product[8]; +	char irq1, irq2;		/* Interrupts, only one is currently used.	*/ +	char dma1, dma2; +	short dma_mem_misc[8];		/* DMA buffer locations (unused in Linux). */ +	short iobase1, iosize1; +	short iobase2, iosize2;		/* Second iobase unused. */ +	char driver_options;			/* Misc. bits */ +	char pad; +}; + +int znet_probe(struct device *dev); +static int	znet_open(struct device *dev); +static int	znet_send_packet(struct sk_buff *skb, struct device *dev); +static void	znet_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void	znet_rx(struct device *dev); +static int	znet_close(struct device *dev); +static struct enet_statistics *net_get_stats(struct device *dev); +static void set_multicast_list(struct device *dev); +static void hardware_init(struct device *dev); +static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset); + +#ifdef notdef +static struct sigaction znet_sigaction = { &znet_interrupt, 0, 0, NULL, }; +#endif + + +/* The Z-Note probe is pretty easy.  The NETIDBLK exists in the safe-to-probe +   BIOS area.  We just scan for the signature, and pull the vital parameters +   out of the structure. */ + +int znet_probe(struct device *dev) +{ +	int i; +	struct netidblk *netinfo; +	char *p; + +	/* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */ +	for(p = (char *)0xf0000; p < (char *)0x100000; p++) +		if (*p == 'N'  &&  strncmp(p, "NETIDBLK", 8) == 0) +			break; + +	if (p >= (char *)0x100000) { +		if (znet_debug > 1) +			printk(KERN_INFO "No Z-Note ethernet adaptor found.\n"); +		return ENODEV; +	} +	netinfo = (struct netidblk *)p; +	dev->base_addr = netinfo->iobase1; +	dev->irq = netinfo->irq1; + +	printk(KERN_INFO "%s: ZNET at %#3lx,", dev->name, dev->base_addr); + +	/* The station address is in the "netidblk" at 0x0f0000. */ +	for (i = 0; i < 6; i++) +		printk(" %2.2x", dev->dev_addr[i] = netinfo->netid[i]); + +	printk(", using IRQ %d DMA %d and %d.\n", dev->irq, netinfo->dma1, +		netinfo->dma2); + +	if (znet_debug > 1) { +		printk(KERN_INFO "%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n", +			   dev->name, netinfo->vendor, +			   netinfo->irq1, netinfo->irq2, +			   netinfo->dma1, netinfo->dma2); +		printk(KERN_INFO "%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n", +			   dev->name, netinfo->iobase1, netinfo->iosize1, +			   netinfo->iobase2, netinfo->iosize2, netinfo->nettype); +	} + +	if (znet_debug > 0) +		printk("%s%s", KERN_INFO, version); + +	dev->priv = (void *) &zn; +	zn.rx_dma = netinfo->dma1; +	zn.tx_dma = netinfo->dma2; + +	/* These should never fail.  You can't add devices to a sealed box! */ +	if (request_irq(dev->irq, &znet_interrupt, 0, "ZNet", NULL) +		|| request_dma(zn.rx_dma,"ZNet rx") +		|| request_dma(zn.tx_dma,"ZNet tx")) { +		printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name); +		return EBUSY; +	} +	irq2dev_map[dev->irq] = dev; + +	/* Allocate buffer memory.	We can cross a 128K boundary, so we +	   must be careful about the allocation.  It's easiest to waste 8K. */ +	if (dma_page_eq(dma_buffer1, &dma_buffer1[RX_BUF_SIZE/2-1])) +	  zn.rx_start = dma_buffer1; +	else  +	  zn.rx_start = dma_buffer2; + +	if (dma_page_eq(dma_buffer3, &dma_buffer3[RX_BUF_SIZE/2-1])) +	  zn.tx_start = dma_buffer3; +	else +	  zn.tx_start = dma_buffer2; +	zn.rx_end = zn.rx_start + RX_BUF_SIZE/2; +	zn.tx_buf_len = TX_BUF_SIZE/2; +	zn.tx_end = zn.tx_start + zn.tx_buf_len; + +	/* The ZNET-specific entries in the device structure. */ +	dev->open = &znet_open; +	dev->hard_start_xmit = &znet_send_packet; +	dev->stop = &znet_close; +	dev->get_stats	= net_get_stats; +	dev->set_multicast_list = &set_multicast_list; + +	/* Fill in the 'dev' with ethernet-generic values. */ +	ether_setup(dev); + +	return 0; +} + + +static int znet_open(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if (znet_debug > 2) +		printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name); + +	/* Turn on the 82501 SIA, using zenith-specific magic. */ +	outb(0x10, 0xe6);					/* Select LAN control register */ +	outb(inb(0xe7) | 0x84, 0xe7);		/* Turn on LAN power (bit 2). */ +	/* According to the Crynwr driver we should wait 50 msec. for the +	   LAN clock to stabilize.  My experiments indicates that the '593 can +	   be initialized immediately.  The delay is probably needed for the +	   DC-to-DC converter to come up to full voltage, and for the oscillator +	   to be spot-on at 20Mhz before transmitting. +	   Until this proves to be a problem we rely on the higher layers for the +	   delay and save allocating a timer entry. */ + +	/* This follows the packet driver's lead, and checks for success. */ +	if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00) +		printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n", +			   dev->name); + +	dev->tbusy = 0; +	dev->interrupt = 0; +	hardware_init(dev); +	dev->start = 1; + +	return 0; +} + +static int znet_send_packet(struct sk_buff *skb, struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	if (znet_debug > 4) +		printk(KERN_DEBUG "%s: ZNet_send_packet(%ld).\n", dev->name, dev->tbusy); + +	/* Transmitter timeout, likely just recovery after suspending the machine. */ +	if (dev->tbusy) { +		ushort event, tx_status, rx_offset, state; +		int tickssofar = jiffies - dev->trans_start; +		if (tickssofar < 10) +			return 1; +		outb(CMD0_STAT0, ioaddr); event = inb(ioaddr); +		outb(CMD0_STAT1, ioaddr); tx_status = inw(ioaddr); +		outb(CMD0_STAT2, ioaddr); rx_offset = inw(ioaddr); +		outb(CMD0_STAT3, ioaddr); state = inb(ioaddr); +		printk(KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x," +			   " resetting.\n", dev->name, event, tx_status, rx_offset, state); +		if (tx_status == 0x0400) +		  printk(KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n", +				 dev->name); +		outb(CMD0_RESET, ioaddr); +		hardware_init(dev); +	} + +	if (skb == NULL) { +		dev_tint(dev); +		return 0; +	} + +	/* Check that the part hasn't reset itself, probably from suspend. */ +	outb(CMD0_STAT0, ioaddr); +	if (inw(ioaddr) == 0x0010 +		&& inw(ioaddr) == 0x0000 +		&& inw(ioaddr) == 0x0010) +	  hardware_init(dev); + +	/* Block a timer-based transmit from overlapping.  This could better be +	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ +	if (set_bit(0, (void*)&dev->tbusy) != 0) +		printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); +	else { +		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; +		unsigned char *buf = (void *)skb->data; +		ushort *tx_link = zn.tx_cur - 1; +		ushort rnd_len = (length + 1)>>1; + +		{ +			short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE; +			unsigned addr = inb(dma_port); +			addr |= inb(dma_port) << 8; +			addr <<= 1; +			if (((int)zn.tx_cur & 0x1ffff) != addr) +			  printk(KERN_WARNING "Address mismatch at Tx: %#x vs %#x.\n", +					 (int)zn.tx_cur & 0xffff, addr); +			zn.tx_cur = (ushort *)(((int)zn.tx_cur & 0xfe0000) | addr); +		} + +		if (zn.tx_cur >= zn.tx_end) +		  zn.tx_cur = zn.tx_start; +		*zn.tx_cur++ = length; +		if (zn.tx_cur + rnd_len + 1 > zn.tx_end) { +			int semi_cnt = (zn.tx_end - zn.tx_cur)<<1; /* Cvrt to byte cnt. */ +			memcpy(zn.tx_cur, buf, semi_cnt); +			rnd_len -= semi_cnt>>1; +			memcpy(zn.tx_start, buf + semi_cnt, length - semi_cnt); +			zn.tx_cur = zn.tx_start + rnd_len; +		} else { +			memcpy(zn.tx_cur, buf, skb->len); +			zn.tx_cur += rnd_len; +		} +		*zn.tx_cur++ = 0; +		cli(); { +			*tx_link = CMD0_TRANSMIT + CMD0_CHNL_1; +			/* Is this always safe to do? */ +			outb(CMD0_TRANSMIT + CMD0_CHNL_1,ioaddr); +		} sti(); + +		dev->trans_start = jiffies; +		if (znet_debug > 4) +		  printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length); +	} +	dev_kfree_skb(skb, FREE_WRITE);  +	return 0; +} + +/* The ZNET interrupt handler. */ +static void	znet_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ +	struct device *dev = irq2dev_map[irq]; +	int ioaddr; +	int boguscnt = 20; + +	if (dev == NULL) { +		printk(KERN_WARNING "znet_interrupt(): IRQ %d for unknown device.\n", irq); +		return; +	} + +	dev->interrupt = 1; +	ioaddr = dev->base_addr; + +	outb(CMD0_STAT0, ioaddr); +	do { +		ushort status = inb(ioaddr); +		if (znet_debug > 5) { +			ushort result, rx_ptr, running; +			outb(CMD0_STAT1, ioaddr); +			result = inw(ioaddr); +			outb(CMD0_STAT2, ioaddr); +			rx_ptr = inw(ioaddr); +			outb(CMD0_STAT3, ioaddr); +			running = inb(ioaddr); +			printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n", +				 dev->name, status, result, rx_ptr, running, boguscnt); +		} +		if ((status & 0x80) == 0) +			break; + +		if ((status & 0x0F) == 4) {	/* Transmit done. */ +			struct net_local *lp = (struct net_local *)dev->priv; +			int tx_status; +			outb(CMD0_STAT1, ioaddr); +			tx_status = inw(ioaddr); +			/* It's undocumented, but tx_status seems to match the i82586. */ +			if (tx_status & 0x2000) { +				lp->stats.tx_packets++; +				lp->stats.collisions += tx_status & 0xf; +			} else { +				if (tx_status & 0x0600)  lp->stats.tx_carrier_errors++; +				if (tx_status & 0x0100)  lp->stats.tx_fifo_errors++; +				if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++; +				if (tx_status & 0x0020)  lp->stats.tx_aborted_errors++; +				/* ...and the catch-all. */ +				if ((tx_status | 0x0760) != 0x0760) +				  lp->stats.tx_errors++; +			} +			dev->tbusy = 0; +			mark_bh(NET_BH);	/* Inform upper layers. */ +		} + +		if ((status & 0x40) +			|| (status & 0x0f) == 11) { +			znet_rx(dev); +		} +		/* Clear the interrupts we've handled. */ +		outb(CMD0_ACK,ioaddr); +	} while (boguscnt--); + +	dev->interrupt = 0; +	return; +} + +static void znet_rx(struct device *dev) +{ +	struct net_local *lp = (struct net_local *)dev->priv; +	int ioaddr = dev->base_addr; +	int boguscount = 1; +	short next_frame_end_offset = 0; 		/* Offset of next frame start. */ +	short *cur_frame_end; +	short cur_frame_end_offset; + +	outb(CMD0_STAT2, ioaddr); +	cur_frame_end_offset = inw(ioaddr); + +	if (cur_frame_end_offset == zn.rx_cur - zn.rx_start) { +		printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n", +			   dev->name, cur_frame_end_offset); +		return; +	} + +	/* Use same method as the Crynwr driver: construct a forward list in +	   the same area of the backwards links we now have.  This allows us to +	   pass packets to the upper layers in the order they were received -- +	   important for fast-path sequential operations. */ +	 while (zn.rx_start + cur_frame_end_offset != zn.rx_cur +			&& ++boguscount < 5) { +		unsigned short hi_cnt, lo_cnt, hi_status, lo_status; +		int count, status; + +		if (cur_frame_end_offset < 4) { +			/* Oh no, we have a special case: the frame trailer wraps around +			   the end of the ring buffer.  We've saved space at the end of +			   the ring buffer for just this problem. */ +			memcpy(zn.rx_end, zn.rx_start, 8); +			cur_frame_end_offset += (RX_BUF_SIZE/2); +		} +		cur_frame_end = zn.rx_start + cur_frame_end_offset - 4; + +		lo_status = *cur_frame_end++; +		hi_status = *cur_frame_end++; +		status = ((hi_status & 0xff) << 8) + (lo_status & 0xff); +		lo_cnt = *cur_frame_end++; +		hi_cnt = *cur_frame_end++; +		count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff); + +		if (znet_debug > 5) +		  printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x" +				 " count %#x status %04x.\n", +				 cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt, +				 count, status); +		cur_frame_end[-4] = status; +		cur_frame_end[-3] = next_frame_end_offset; +		cur_frame_end[-2] = count; +		next_frame_end_offset = cur_frame_end_offset; +		cur_frame_end_offset -= ((count + 1)>>1) + 3; +		if (cur_frame_end_offset < 0) +		  cur_frame_end_offset += RX_BUF_SIZE/2; +	}; + +	/* Now step  forward through the list. */ +	do { +		ushort *this_rfp_ptr = zn.rx_start + next_frame_end_offset; +		int status = this_rfp_ptr[-4]; +		int pkt_len = this_rfp_ptr[-2]; +	   +		if (znet_debug > 5) +		  printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x" +				 " next %04x.\n", next_frame_end_offset<<1, status, pkt_len, +				 this_rfp_ptr[-3]<<1); +		/* Once again we must assume that the i82586 docs apply. */ +		if ( ! (status & 0x2000)) {				/* There was an error. */ +			lp->stats.rx_errors++; +			if (status & 0x0800) lp->stats.rx_crc_errors++; +			if (status & 0x0400) lp->stats.rx_frame_errors++; +			if (status & 0x0200) lp->stats.rx_over_errors++; /* Wrong. */ +			if (status & 0x0100) lp->stats.rx_fifo_errors++; +			if (status & 0x0080) lp->stats.rx_length_errors++; +		} else if (pkt_len > 1536) { +			lp->stats.rx_length_errors++; +		} else { +			/* Malloc up new buffer. */ +			struct sk_buff *skb; + +			skb = dev_alloc_skb(pkt_len); +			if (skb == NULL) { +				if (znet_debug) +				  printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); +				lp->stats.rx_dropped++; +				break; +			} +			skb->dev = dev; + +			if (&zn.rx_cur[(pkt_len+1)>>1] > zn.rx_end) { +				int semi_cnt = (zn.rx_end - zn.rx_cur)<<1; +				memcpy(skb_put(skb,semi_cnt), zn.rx_cur, semi_cnt); +				memcpy(skb_put(skb,pkt_len-semi_cnt), zn.rx_start, +					   pkt_len - semi_cnt); +			} else { +				memcpy(skb_put(skb,pkt_len), zn.rx_cur, pkt_len); +				if (znet_debug > 6) { +					unsigned int *packet = (unsigned int *) skb->data; +					printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0], +						   packet[1], packet[2], packet[3]); +				} +		  } +		  skb->protocol=eth_type_trans(skb,dev); +		  netif_rx(skb); +		  lp->stats.rx_packets++; +		} +		zn.rx_cur = this_rfp_ptr; +		if (zn.rx_cur >= zn.rx_end) +			zn.rx_cur -= RX_BUF_SIZE/2; +		update_stop_hit(ioaddr, (zn.rx_cur - zn.rx_start)<<1); +		next_frame_end_offset = this_rfp_ptr[-3]; +		if (next_frame_end_offset == 0)		/* Read all the frames? */ +			break;			/* Done for now */ +		this_rfp_ptr = zn.rx_start + next_frame_end_offset; +	} while (--boguscount); + +	/* If any worth-while packets have been received, dev_rint() +	   has done a mark_bh(INET_BH) for us and will work on them +	   when we get to the bottom-half routine. */ +	return; +} + +/* The inverse routine to znet_open(). */ +static int znet_close(struct device *dev) +{ +	int ioaddr = dev->base_addr; + +	dev->tbusy = 1; +	dev->start = 0; + +	outb(CMD0_RESET, ioaddr);			/* CMD0_RESET */ + +	disable_dma(zn.rx_dma); +	disable_dma(zn.tx_dma); + +	free_irq(dev->irq, NULL); + +	if (znet_debug > 1) +		printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); +	/* Turn off transceiver power. */ +	outb(0x10, 0xe6);					/* Select LAN control register */ +	outb(inb(0xe7) & ~0x84, 0xe7);		/* Turn on LAN power (bit 2). */ + +	return 0; +} + +/* Get the current statistics.	This may be called with the card open or +   closed. */ +static struct enet_statistics *net_get_stats(struct device *dev) +{ +		struct net_local *lp = (struct net_local *)dev->priv; + +		return &lp->stats; +} + +/* Set or clear the multicast filter for this adaptor. +   As a side effect this routine must also initialize the device parameters. +   This is taken advantage of in open(). + +   N.B. that we change i593_init[] in place.  This (properly) makes the +   mode change persistent, but must be changed if this code is moved to +   a multiple adaptor environment. + */ +static void set_multicast_list(struct device *dev) +{ +	short ioaddr = dev->base_addr; + +	if (dev->flags&IFF_PROMISC) { +		/* Enable promiscuous mode */ +		i593_init[7] &= ~3;		i593_init[7] |= 1; +		i593_init[13] &= ~8;	i593_init[13] |= 8; +	} else if (dev->mc_list || (dev->flags&IFF_ALLMULTI)) { +		/* Enable accept-all-multicast mode */ +		i593_init[7] &= ~3;		i593_init[7] |= 0; +		i593_init[13] &= ~8;	i593_init[13] |= 8; +	} else {					/* Enable normal mode. */ +		i593_init[7] &= ~3;		i593_init[7] |= 0; +		i593_init[13] &= ~8;	i593_init[13] |= 0; +	} +	*zn.tx_cur++ = sizeof(i593_init); +	memcpy(zn.tx_cur, i593_init, sizeof(i593_init)); +	zn.tx_cur += sizeof(i593_init)/2; +	outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr); +#ifdef not_tested +	if (num_addrs > 0) { +		int addrs_len = 6*num_addrs; +		*zn.tx_cur++ = addrs_len; +		memcpy(zn.tx_cur, addrs, addrs_len); +		outb(CMD0_MULTICAST_LIST+CMD0_CHNL_1, ioaddr); +		zn.tx_cur += addrs_len>>1; +	} +#endif +} + +void show_dma(void) +{ +	short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE; +	unsigned addr = inb(dma_port); +	addr |= inb(dma_port) << 8; +	printk("Addr: %04x cnt:%3x...", addr<<1, get_dma_residue(zn.tx_dma)); +} + +/* Initialize the hardware.  We have to do this when the board is open()ed +   or when we come out of suspend mode. */ +static void hardware_init(struct device *dev) +{ +	short ioaddr = dev->base_addr; + +	zn.rx_cur = zn.rx_start; +	zn.tx_cur = zn.tx_start; + +	/* Reset the chip, and start it up. */ +	outb(CMD0_RESET, ioaddr); + +	cli(); {							/* Protect against a DMA flip-flop */ +		disable_dma(zn.rx_dma); 		/* reset by an interrupting task. */ +		clear_dma_ff(zn.rx_dma); +		set_dma_mode(zn.rx_dma, DMA_RX_MODE); +		set_dma_addr(zn.rx_dma, (unsigned int) zn.rx_start); +		set_dma_count(zn.rx_dma, RX_BUF_SIZE); +		enable_dma(zn.rx_dma); +		/* Now set up the Tx channel. */ +		disable_dma(zn.tx_dma); +		clear_dma_ff(zn.tx_dma); +		set_dma_mode(zn.tx_dma, DMA_TX_MODE); +		set_dma_addr(zn.tx_dma, (unsigned int) zn.tx_start); +		set_dma_count(zn.tx_dma, zn.tx_buf_len<<1); +		enable_dma(zn.tx_dma); +	} sti(); + +	if (znet_debug > 1) +	  printk(KERN_DEBUG "%s: Initializing the i82593, tx buf %p... ", dev->name, +			 zn.tx_start); +	/* Do an empty configure command, just like the Crynwr driver.  This +	   resets to chip to its default values. */ +	*zn.tx_cur++ = 0; +	*zn.tx_cur++ = 0; +	printk("stat:%02x ", inb(ioaddr)); show_dma(); +	outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr); +	*zn.tx_cur++ = sizeof(i593_init); +	memcpy(zn.tx_cur, i593_init, sizeof(i593_init)); +	zn.tx_cur += sizeof(i593_init)/2; +	printk("stat:%02x ", inb(ioaddr)); show_dma(); +	outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr); +	*zn.tx_cur++ = 6; +	memcpy(zn.tx_cur, dev->dev_addr, 6); +	zn.tx_cur += 3; +	printk("stat:%02x ", inb(ioaddr)); show_dma(); +	outb(CMD0_IA_SETUP + CMD0_CHNL_1, ioaddr); +	printk("stat:%02x ", inb(ioaddr)); show_dma(); + +	update_stop_hit(ioaddr, 8192); +	if (znet_debug > 1)  printk("enabling Rx.\n"); +	outb(CMD0_Rx_ENABLE+CMD0_CHNL_0, ioaddr); +	dev->tbusy = 0; +} + +static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset) +{ +	outb(CMD0_PORT_1, ioaddr); +	if (znet_debug > 5) +	  printk(KERN_DEBUG "Updating stop hit with value %02x.\n", +			 (rx_stop_offset >> 6) | 0x80); +	outb((rx_stop_offset >> 6) | 0x80, ioaddr); +	outb(CMD1_PORT_0, ioaddr); +} + +/* + * Local variables: + *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c znet.c" + *  version-control: t + *  kept-new-versions: 5 + *  c-indent-level: 4 + *  tab-width: 4 + * End: + */  | 
