| /* | 
 |  *	Digi RightSwitch SE-X loadable device driver for Linux | 
 |  * | 
 |  *	The RightSwitch is a 4 (EISA) or 6 (PCI) port etherswitch and | 
 |  *	a NIC on an internal board. | 
 |  * | 
 |  *	Author: Rick Richardson, rick@remotepoint.com | 
 |  *	Derived from the SVR4.2 (UnixWare) driver for the same card. | 
 |  * | 
 |  *	Copyright 1995-1996 Digi International Inc. | 
 |  * | 
 |  *	This software may be used and distributed according to the terms | 
 |  *	of the GNU General Public License, incorporated herein by reference. | 
 |  * | 
 |  *	For information on purchasing a RightSwitch SE-4 or SE-6 | 
 |  *	board, please contact Digi's sales department at 1-612-912-3444 | 
 |  *	or 1-800-DIGIBRD.  Outside the U.S., please check our Web page | 
 |  *	at http://www.dgii.com for sales offices worldwide. | 
 |  * | 
 |  *	OPERATION: | 
 |  *	When compiled as a loadable module, this driver can operate | 
 |  *	the board as either a 4/6 port switch with a 5th or 7th port | 
 |  *	that is a conventional NIC interface as far as the host is | 
 |  *	concerned, OR as 4/6 independent NICs.  To select multi-NIC | 
 |  *	mode, add "nicmode=1" on the insmod load line for the driver. | 
 |  * | 
 |  *	This driver uses the "dev" common ethernet device structure | 
 |  *	and a private "priv" (dev->priv) structure that contains | 
 |  *	mostly DGRS-specific information and statistics.  To keep | 
 |  *	the code for both the switch mode and the multi-NIC mode | 
 |  *	as similar as possible, I have introduced the concept of | 
 |  *	"dev0"/"priv0" and "devN"/"privN"  pointer pairs in subroutines | 
 |  *	where needed.  The first pair of pointers points to the | 
 |  *	"dev" and "priv" structures of the zeroth (0th) device | 
 |  *	interface associated with a board.  The second pair of | 
 |  *	pointers points to the current (Nth) device interface | 
 |  *	for the board: the one for which we are processing data. | 
 |  * | 
 |  *	In switch mode, the pairs of pointers are always the same, | 
 |  *	that is, dev0 == devN and priv0 == privN.  This is just | 
 |  *	like previous releases of this driver which did not support | 
 |  *	NIC mode. | 
 |  * | 
 |  *	In multi-NIC mode, the pairs of pointers may be different. | 
 |  *	We use the devN and privN pointers to reference just the | 
 |  *	name, port number, and statistics for the current interface. | 
 |  *	We use the dev0 and priv0 pointers to access the variables | 
 |  *	that control access to the board, such as board address | 
 |  *	and simulated 82596 variables.  This is because there is | 
 |  *	only one "fake" 82596 that serves as the interface to | 
 |  *	the board.  We do not want to try to keep the variables | 
 |  *	associated with this 82596 in sync across all devices. | 
 |  * | 
 |  *	This scheme works well.  As you will see, except for | 
 |  *	initialization, there is very little difference between | 
 |  *	the two modes as far as this driver is concerned.  On the | 
 |  *	receive side in NIC mode, the interrupt *always* comes in on | 
 |  *	the 0th interface (dev0/priv0).  We then figure out which | 
 |  *	real 82596 port it came in on from looking at the "chan" | 
 |  *	member that the board firmware adds at the end of each | 
 |  *	RBD (a.k.a. TBD). We get the channel number like this: | 
 |  *		int chan = ((I596_RBD *) S2H(cbp->xmit.tbdp))->chan; | 
 |  * | 
 |  *	On the transmit side in multi-NIC mode, we specify the | 
 |  *	output 82596 port by setting the new "dstchan" structure | 
 |  *	member that is at the end of the RFD, like this: | 
 |  *		priv0->rfdp->dstchan = privN->chan; | 
 |  * | 
 |  *	TODO: | 
 |  *	- Multi-NIC mode is not yet supported when the driver is linked | 
 |  *	  into the kernel. | 
 |  *	- Better handling of multicast addresses. | 
 |  * | 
 |  *	Fixes: | 
 |  *	Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001 | 
 |  *	- fix dgrs_found_device wrt checking kmalloc return and | 
 |  *	rollbacking the partial steps of the whole process when | 
 |  *	one of the devices can't be allocated. Fix SET_MODULE_OWNER | 
 |  *	on the loop to use devN instead of repeated calls to dev. | 
 |  * | 
 |  *	davej <davej@suse.de> - 9/2/2001 | 
 |  *	- Enable PCI device before reading ioaddr/irq | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/module.h> | 
 | #include <linux/eisa.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/string.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/errno.h> | 
 | #include <linux/ioport.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/interrupt.h> | 
 | #include <linux/pci.h> | 
 | #include <linux/init.h> | 
 | #include <linux/netdevice.h> | 
 | #include <linux/etherdevice.h> | 
 | #include <linux/skbuff.h> | 
 | #include <linux/bitops.h> | 
 |  | 
 | #include <asm/io.h> | 
 | #include <asm/byteorder.h> | 
 | #include <asm/uaccess.h> | 
 |  | 
 | static char version[] __initdata = | 
 | 	"$Id: dgrs.c,v 1.13 2000/06/06 04:07:00 rick Exp $"; | 
 |  | 
 | /* | 
 |  *	DGRS include files | 
 |  */ | 
 | typedef unsigned char uchar; | 
 | typedef unsigned int bool; | 
 | #define vol volatile | 
 |  | 
 | #include "dgrs.h" | 
 | #include "dgrs_es4h.h" | 
 | #include "dgrs_plx9060.h" | 
 | #include "dgrs_i82596.h" | 
 | #include "dgrs_ether.h" | 
 | #include "dgrs_asstruct.h" | 
 | #include "dgrs_bcomm.h" | 
 |  | 
 | #ifdef CONFIG_PCI | 
 | static struct pci_device_id dgrs_pci_tbl[] = { | 
 | 	{ SE6_PCI_VENDOR_ID, SE6_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, }, | 
 | 	{ }			/* Terminating entry */ | 
 | }; | 
 | MODULE_DEVICE_TABLE(pci, dgrs_pci_tbl); | 
 | #endif | 
 |  | 
 | #ifdef CONFIG_EISA | 
 | static struct eisa_device_id dgrs_eisa_tbl[] = { | 
 | 	{ "DBI0A01" }, | 
 | 	{ } | 
 | }; | 
 | MODULE_DEVICE_TABLE(eisa, dgrs_eisa_tbl); | 
 | #endif | 
 |  | 
 | MODULE_LICENSE("GPL"); | 
 |  | 
 |  | 
 | /* | 
 |  *	Firmware.  Compiled separately for local compilation, | 
 |  *	but #included for Linux distribution. | 
 |  */ | 
 | #ifndef NOFW | 
 | 	#include "dgrs_firmware.c" | 
 | #else | 
 | 	extern int	dgrs_firmnum; | 
 | 	extern char	dgrs_firmver[]; | 
 | 	extern char	dgrs_firmdate[]; | 
 | 	extern uchar	dgrs_code[]; | 
 | 	extern int	dgrs_ncode; | 
 | #endif | 
 |  | 
 | /* | 
 |  *	Linux out*() is backwards from all other operating systems | 
 |  */ | 
 | #define	OUTB(ADDR, VAL)	outb(VAL, ADDR) | 
 | #define	OUTW(ADDR, VAL)	outw(VAL, ADDR) | 
 | #define	OUTL(ADDR, VAL)	outl(VAL, ADDR) | 
 |  | 
 | /* | 
 |  *	Macros to convert switch to host and host to switch addresses | 
 |  *	(assumes a local variable priv points to board dependent struct) | 
 |  */ | 
 | #define	S2H(A)	( ((unsigned long)(A)&0x00ffffff) + priv0->vmem ) | 
 | #define	S2HN(A)	( ((unsigned long)(A)&0x00ffffff) + privN->vmem ) | 
 | #define	H2S(A)	( ((char *) (A) - priv0->vmem) + 0xA3000000 ) | 
 |  | 
 | /* | 
 |  *	Convert a switch address to a "safe" address for use with the | 
 |  *	PLX 9060 DMA registers and the associated HW kludge that allows | 
 |  *	for host access of the DMA registers. | 
 |  */ | 
 | #define	S2DMA(A)	( (unsigned long)(A) & 0x00ffffff) | 
 |  | 
 | /* | 
 |  *	"Space.c" variables, now settable from module interface | 
 |  *	Use the name below, minus the "dgrs_" prefix.  See init_module(). | 
 |  */ | 
 | static int	dgrs_debug = 1; | 
 | static int	dgrs_dma = 1; | 
 | static int	dgrs_spantree = -1; | 
 | static int	dgrs_hashexpire = -1; | 
 | static uchar	dgrs_ipaddr[4] = { 0xff, 0xff, 0xff, 0xff}; | 
 | static uchar	dgrs_iptrap[4] = { 0xff, 0xff, 0xff, 0xff}; | 
 | static __u32	dgrs_ipxnet = -1; | 
 | static int	dgrs_nicmode; | 
 |  | 
 | /* | 
 |  *	Private per-board data structure (dev->priv) | 
 |  */ | 
 | typedef struct | 
 | { | 
 | 	/* | 
 | 	 *	Stuff for generic ethercard I/F | 
 | 	 */ | 
 | 	struct net_device_stats	stats; | 
 |  | 
 | 	/* | 
 | 	 *	DGRS specific data | 
 | 	 */ | 
 | 	char		*vmem; | 
 |  | 
 |         struct bios_comm *bcomm;        /* Firmware BIOS comm structure */ | 
 |         PORT            *port;          /* Ptr to PORT[0] struct in VM */ | 
 |         I596_SCB        *scbp;          /* Ptr to SCB struct in VM */ | 
 |         I596_RFD        *rfdp;          /* Current RFD list */ | 
 |         I596_RBD        *rbdp;          /* Current RBD list */ | 
 |  | 
 |         volatile int    intrcnt;        /* Count of interrupts */ | 
 |  | 
 |         /* | 
 |          *      SE-4 (EISA) board variables | 
 |          */ | 
 |         uchar		is_reg;		/* EISA: Value for ES4H_IS reg */ | 
 |  | 
 |         /* | 
 |          *      SE-6 (PCI) board variables | 
 |          * | 
 |          *      The PLX "expansion rom" space is used for DMA register | 
 |          *      access from the host on the SE-6.  These are the physical | 
 |          *      and virtual addresses of that space. | 
 |          */ | 
 |         ulong		plxreg;		/* Phys address of PLX chip */ | 
 |         char            *vplxreg;	/* Virtual address of PLX chip */ | 
 |         ulong		plxdma;		/* Phys addr of PLX "expansion rom" */ | 
 |         ulong volatile  *vplxdma;	/* Virtual addr of "expansion rom" */ | 
 |         int             use_dma;        /* Flag: use DMA */ | 
 | 	DMACHAIN	*dmadesc_s;	/* area for DMA chains (SW addr.) */ | 
 | 	DMACHAIN	*dmadesc_h;	/* area for DMA chains (Host Virtual) */ | 
 |  | 
 | 	/* | 
 | 	 *	Multi-NIC mode variables | 
 | 	 * | 
 | 	 *	All entries of the devtbl[] array are valid for the 0th | 
 | 	 *	device (i.e. eth0, but not eth1...eth5).  devtbl[0] is | 
 | 	 *	valid for all devices (i.e. eth0, eth1, ..., eth5). | 
 | 	 */ | 
 | 	int		nports;		/* Number of physical ports (4 or 6) */ | 
 | 	int		chan;		/* Channel # (1-6) for this device */ | 
 | 	struct net_device	*devtbl[6];	/* Ptrs to N device structs */ | 
 |  | 
 | } DGRS_PRIV; | 
 |  | 
 |  | 
 | /* | 
 |  *	reset or un-reset the IDT processor | 
 |  */ | 
 | static void | 
 | proc_reset(struct net_device *dev0, int reset) | 
 | { | 
 | 	DGRS_PRIV	*priv0 = (DGRS_PRIV *) dev0->priv; | 
 |  | 
 | 	if (priv0->plxreg) | 
 | 	{ | 
 | 		ulong		val; | 
 | 		val = inl(dev0->base_addr + PLX_MISC_CSR); | 
 | 		if (reset) | 
 | 			val |= SE6_RESET; | 
 | 		else | 
 | 			val &= ~SE6_RESET; | 
 | 		OUTL(dev0->base_addr + PLX_MISC_CSR, val); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		OUTB(dev0->base_addr + ES4H_PC, reset ? ES4H_PC_RESET : 0); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *	See if the board supports bus master DMA | 
 |  */ | 
 | static int | 
 | check_board_dma(struct net_device *dev0) | 
 | { | 
 | 	DGRS_PRIV	*priv0 = (DGRS_PRIV *) dev0->priv; | 
 | 	ulong	x; | 
 |  | 
 | 	/* | 
 | 	 *	If Space.c says not to use DMA, or if it's not a PLX based | 
 | 	 *	PCI board, or if the expansion ROM space is not PCI | 
 | 	 *	configured, then return false. | 
 | 	 */ | 
 | 	if (!dgrs_dma || !priv0->plxreg || !priv0->plxdma) | 
 | 		return (0); | 
 |  | 
 | 	/* | 
 | 	 *	Set the local address remap register of the "expansion rom" | 
 | 	 *	area to 0x80000000 so that we can use it to access the DMA | 
 | 	 *	registers from the host side. | 
 | 	 */ | 
 | 	OUTL(dev0->base_addr + PLX_ROM_BASE_ADDR, 0x80000000); | 
 |  | 
 | 	/* | 
 | 	 * Set the PCI region descriptor to: | 
 | 	 *      Space 0: | 
 | 	 *              disable read-prefetch | 
 | 	 *              enable READY | 
 | 	 *              enable BURST | 
 | 	 *              0 internal wait states | 
 | 	 *      Expansion ROM: (used for host DMA register access) | 
 | 	 *              disable read-prefetch | 
 | 	 *              enable READY | 
 | 	 *              disable BURST | 
 | 	 *              0 internal wait states | 
 | 	 */ | 
 | 	OUTL(dev0->base_addr + PLX_BUS_REGION, 0x49430343); | 
 |  | 
 | 	/* | 
 | 	 *	Now map the DMA registers into our virtual space | 
 | 	 */ | 
 | 	priv0->vplxdma = (ulong *) ioremap (priv0->plxdma, 256); | 
 | 	if (!priv0->vplxdma) | 
 | 	{ | 
 | 		printk("%s: can't *remap() the DMA regs\n", dev0->name); | 
 | 		return (0); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	Now test to see if we can access the DMA registers | 
 | 	 *	If we write -1 and get back 1FFF, then we accessed the | 
 | 	 *	DMA register.  Otherwise, we probably have an old board | 
 | 	 *	and wrote into regular RAM. | 
 | 	 */ | 
 | 	priv0->vplxdma[PLX_DMA0_MODE/4] = 0xFFFFFFFF; | 
 | 	x = priv0->vplxdma[PLX_DMA0_MODE/4]; | 
 | 	if (x != 0x00001FFF) { | 
 | 		iounmap((void *)priv0->vplxdma); | 
 | 		return (0); | 
 | 	} | 
 |  | 
 | 	return (1); | 
 | } | 
 |  | 
 | /* | 
 |  *	Initiate DMA using PLX part on PCI board.  Spin the | 
 |  *	processor until completed.  All addresses are physical! | 
 |  * | 
 |  *	If pciaddr is NULL, then it's a chaining DMA, and lcladdr is | 
 |  *	the address of the first DMA descriptor in the chain. | 
 |  * | 
 |  *	If pciaddr is not NULL, then it's a single DMA. | 
 |  * | 
 |  *	In either case, "lcladdr" must have been fixed up to make | 
 |  *	sure the MSB isn't set using the S2DMA macro before passing | 
 |  *	the address to this routine. | 
 |  */ | 
 | static int | 
 | do_plx_dma( | 
 | 	struct net_device *dev, | 
 | 	ulong pciaddr, | 
 | 	ulong lcladdr, | 
 | 	int len, | 
 | 	int to_host | 
 | ) | 
 | { | 
 |         int     	i; | 
 |         ulong   	csr = 0; | 
 | 	DGRS_PRIV	*priv = (DGRS_PRIV *) dev->priv; | 
 |  | 
 | 	if (pciaddr) | 
 | 	{ | 
 | 		/* | 
 | 		 *	Do a single, non-chain DMA | 
 | 		 */ | 
 | 		priv->vplxdma[PLX_DMA0_PCI_ADDR/4] = pciaddr; | 
 | 		priv->vplxdma[PLX_DMA0_LCL_ADDR/4] = lcladdr; | 
 | 		priv->vplxdma[PLX_DMA0_SIZE/4] = len; | 
 | 		priv->vplxdma[PLX_DMA0_DESCRIPTOR/4] = to_host | 
 | 					? PLX_DMA_DESC_TO_HOST | 
 | 					: PLX_DMA_DESC_TO_BOARD; | 
 | 		priv->vplxdma[PLX_DMA0_MODE/4] = | 
 | 					  PLX_DMA_MODE_WIDTH32 | 
 | 					| PLX_DMA_MODE_WAITSTATES(0) | 
 | 					| PLX_DMA_MODE_READY | 
 | 					| PLX_DMA_MODE_NOBTERM | 
 | 					| PLX_DMA_MODE_BURST | 
 | 					| PLX_DMA_MODE_NOCHAIN; | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		/* | 
 | 		 *	Do a chaining DMA | 
 | 		 */ | 
 | 		priv->vplxdma[PLX_DMA0_MODE/4] = | 
 | 					  PLX_DMA_MODE_WIDTH32 | 
 | 					| PLX_DMA_MODE_WAITSTATES(0) | 
 | 					| PLX_DMA_MODE_READY | 
 | 					| PLX_DMA_MODE_NOBTERM | 
 | 					| PLX_DMA_MODE_BURST | 
 | 					| PLX_DMA_MODE_CHAIN; | 
 | 		priv->vplxdma[PLX_DMA0_DESCRIPTOR/4] = lcladdr; | 
 | 	} | 
 |  | 
 | 	priv->vplxdma[PLX_DMA_CSR/4] = | 
 | 				PLX_DMA_CSR_0_ENABLE | PLX_DMA_CSR_0_START; | 
 |  | 
 |         /* | 
 | 	 *	Wait for DMA to complete | 
 | 	 */ | 
 |         for (i = 0; i < 1000000; ++i) | 
 |         { | 
 | 		/* | 
 | 		 *	Spin the host CPU for 1 usec, so we don't thrash | 
 | 		 *	the PCI bus while the PLX 9060 is doing DMA. | 
 | 		 */ | 
 | 		udelay(1); | 
 |  | 
 | 		csr = (volatile unsigned long) priv->vplxdma[PLX_DMA_CSR/4]; | 
 |  | 
 |                 if (csr & PLX_DMA_CSR_0_DONE) | 
 |                         break; | 
 |         } | 
 |  | 
 |         if ( ! (csr & PLX_DMA_CSR_0_DONE) ) | 
 |         { | 
 | 		printk("%s: DMA done never occurred. DMA disabled.\n", | 
 | 			dev->name); | 
 | 		priv->use_dma = 0; | 
 |                 return 1; | 
 |         } | 
 |         return 0; | 
 | } | 
 |  | 
 | /* | 
 |  *	dgrs_rcv_frame() | 
 |  * | 
 |  *	Process a received frame.  This is called from the interrupt | 
 |  *	routine, and works for both switch mode and multi-NIC mode. | 
 |  * | 
 |  *	Note that when in multi-NIC mode, we want to always access the | 
 |  *	hardware using the dev and priv structures of the first port, | 
 |  *	so that we are using only one set of variables to maintain | 
 |  *	the board interface status, but we want to use the Nth port | 
 |  *	dev and priv structures to maintain statistics and to pass | 
 |  *	the packet up. | 
 |  * | 
 |  *	Only the first device structure is attached to the interrupt. | 
 |  *	We use the special "chan" variable at the end of the first RBD | 
 |  *	to select the Nth device in multi-NIC mode. | 
 |  * | 
 |  *	We currently do chained DMA on a per-packet basis when the | 
 |  *	packet is "long", and we spin the CPU a short time polling | 
 |  *	for DMA completion.  This avoids a second interrupt overhead, | 
 |  *	and gives the best performance for light traffic to the host. | 
 |  * | 
 |  *	However, a better scheme that could be implemented would be | 
 |  *	to see how many packets are outstanding for the host, and if | 
 |  *	the number is "large", create a long chain to DMA several | 
 |  *	packets into the host in one go.  In this case, we would set | 
 |  *	up some state variables to let the host CPU continue doing | 
 |  *	other things until a DMA completion interrupt comes along. | 
 |  */ | 
 | static void | 
 | dgrs_rcv_frame( | 
 | 	struct net_device	*dev0, | 
 | 	DGRS_PRIV	*priv0, | 
 | 	I596_CB		*cbp | 
 | ) | 
 | { | 
 | 	int		len; | 
 | 	I596_TBD	*tbdp; | 
 | 	struct sk_buff	*skb; | 
 | 	uchar		*putp; | 
 | 	uchar		*p; | 
 | 	struct net_device	*devN; | 
 | 	DGRS_PRIV	*privN; | 
 |  | 
 | 	/* | 
 | 	 *	Determine Nth priv and dev structure pointers | 
 | 	 */ | 
 | 	if (dgrs_nicmode) | 
 | 	{	/* Multi-NIC mode */ | 
 | 		int chan = ((I596_RBD *) S2H(cbp->xmit.tbdp))->chan; | 
 |  | 
 | 		devN = priv0->devtbl[chan-1]; | 
 | 		/* | 
 | 		 * If devN is null, we got an interrupt before the I/F | 
 | 		 * has been initialized.  Pitch the packet. | 
 | 		 */ | 
 | 		if (devN == NULL) | 
 | 			goto out; | 
 | 		privN = (DGRS_PRIV *) devN->priv; | 
 | 	} | 
 | 	else | 
 | 	{	/* Switch mode */ | 
 | 		devN = dev0; | 
 | 		privN = priv0; | 
 | 	} | 
 |  | 
 | 	if (0) printk("%s: rcv len=%ld\n", devN->name, cbp->xmit.count); | 
 |  | 
 | 	/* | 
 | 	 *	Allocate a message block big enough to hold the whole frame | 
 | 	 */ | 
 | 	len = cbp->xmit.count; | 
 | 	if ((skb = dev_alloc_skb(len+5)) == NULL) | 
 | 	{ | 
 | 		printk("%s: dev_alloc_skb failed for rcv buffer\n", devN->name); | 
 | 		++privN->stats.rx_dropped; | 
 | 		/* discarding the frame */ | 
 | 		goto out; | 
 | 	} | 
 | 	skb->dev = devN; | 
 | 	skb_reserve(skb, 2);	/* Align IP header */ | 
 |  | 
 | again: | 
 | 	putp = p = skb_put(skb, len); | 
 |  | 
 | 	/* | 
 | 	 *	There are three modes here for doing the packet copy. | 
 | 	 *	If we have DMA, and the packet is "long", we use the | 
 | 	 *	chaining mode of DMA.  If it's shorter, we use single | 
 | 	 *	DMA's.  Otherwise, we use memcpy(). | 
 | 	 */ | 
 | 	if (priv0->use_dma && priv0->dmadesc_h && len > 64) | 
 | 	{ | 
 | 		/* | 
 | 		 *	If we can use DMA and it's a long frame, copy it using | 
 | 		 *	DMA chaining. | 
 | 		 */ | 
 | 		DMACHAIN	*ddp_h;	/* Host virtual DMA desc. pointer */ | 
 | 		DMACHAIN	*ddp_s;	/* Switch physical DMA desc. pointer */ | 
 | 		uchar		*phys_p; | 
 |  | 
 | 		/* | 
 | 		 *	Get the physical address of the STREAMS buffer. | 
 | 		 *	NOTE: allocb() guarantees that the whole buffer | 
 | 		 *	is in a single page if the length < 4096. | 
 | 		 */ | 
 | 		phys_p = (uchar *) virt_to_phys(putp); | 
 |  | 
 | 		ddp_h = priv0->dmadesc_h; | 
 | 		ddp_s = priv0->dmadesc_s; | 
 | 		tbdp = (I596_TBD *) S2H(cbp->xmit.tbdp); | 
 | 		for (;;) | 
 | 		{ | 
 | 			int	count; | 
 | 			int	amt; | 
 |  | 
 | 			count = tbdp->count; | 
 | 			amt = count & 0x3fff; | 
 | 			if (amt == 0) | 
 | 				break; /* For safety */ | 
 | 			if ( (p-putp) >= len) | 
 | 			{ | 
 | 				printk("%s: cbp = %lx\n", devN->name, (long) H2S(cbp)); | 
 | 				proc_reset(dev0, 1);	/* Freeze IDT */ | 
 | 				break; /* For Safety */ | 
 | 			} | 
 |  | 
 | 			ddp_h->pciaddr = (ulong) phys_p; | 
 | 			ddp_h->lcladdr = S2DMA(tbdp->buf); | 
 | 			ddp_h->len = amt; | 
 |  | 
 | 			phys_p += amt; | 
 | 			p += amt; | 
 |  | 
 | 			if (count & I596_TBD_EOF) | 
 | 			{ | 
 | 				ddp_h->next = PLX_DMA_DESC_TO_HOST | 
 | 						| PLX_DMA_DESC_EOC; | 
 | 				++ddp_h; | 
 | 				break; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				++ddp_s; | 
 | 				ddp_h->next = PLX_DMA_DESC_TO_HOST | 
 | 						| (ulong) ddp_s; | 
 | 				tbdp = (I596_TBD *) S2H(tbdp->next); | 
 | 				++ddp_h; | 
 | 			} | 
 | 		} | 
 | 		if (ddp_h - priv0->dmadesc_h) | 
 | 		{ | 
 | 			int	rc; | 
 |  | 
 | 			rc = do_plx_dma(dev0, | 
 | 				0, (ulong) priv0->dmadesc_s, len, 0); | 
 | 			if (rc) | 
 | 			{ | 
 | 				printk("%s: Chained DMA failure\n", devN->name); | 
 | 				goto again; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	else if (priv0->use_dma) | 
 | 	{ | 
 | 		/* | 
 | 		 *	If we can use DMA and it's a shorter frame, copy it | 
 | 		 *	using single DMA transfers. | 
 | 		 */ | 
 | 		uchar		*phys_p; | 
 |  | 
 | 		/* | 
 | 		 *	Get the physical address of the STREAMS buffer. | 
 | 		 *	NOTE: allocb() guarantees that the whole buffer | 
 | 		 *	is in a single page if the length < 4096. | 
 | 		 */ | 
 | 		phys_p = (uchar *) virt_to_phys(putp); | 
 |  | 
 | 		tbdp = (I596_TBD *) S2H(cbp->xmit.tbdp); | 
 | 		for (;;) | 
 | 		{ | 
 | 			int	count; | 
 | 			int	amt; | 
 | 			int	rc; | 
 |  | 
 | 			count = tbdp->count; | 
 | 			amt = count & 0x3fff; | 
 | 			if (amt == 0) | 
 | 				break; /* For safety */ | 
 | 			if ( (p-putp) >= len) | 
 | 			{ | 
 | 				printk("%s: cbp = %lx\n", devN->name, (long) H2S(cbp)); | 
 | 				proc_reset(dev0, 1);	/* Freeze IDT */ | 
 | 				break; /* For Safety */ | 
 | 			} | 
 | 			rc = do_plx_dma(dev0, (ulong) phys_p, | 
 | 						S2DMA(tbdp->buf), amt, 1); | 
 | 			if (rc) | 
 | 			{ | 
 | 				memcpy(p, S2H(tbdp->buf), amt); | 
 | 				printk("%s: Single DMA failed\n", devN->name); | 
 | 			} | 
 | 			phys_p += amt; | 
 | 			p += amt; | 
 | 			if (count & I596_TBD_EOF) | 
 | 				break; | 
 | 			tbdp = (I596_TBD *) S2H(tbdp->next); | 
 | 		} | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		/* | 
 | 		 *	Otherwise, copy it piece by piece using memcpy() | 
 | 		 */ | 
 | 		tbdp = (I596_TBD *) S2H(cbp->xmit.tbdp); | 
 | 		for (;;) | 
 | 		{ | 
 | 			int	count; | 
 | 			int	amt; | 
 |  | 
 | 			count = tbdp->count; | 
 | 			amt = count & 0x3fff; | 
 | 			if (amt == 0) | 
 | 				break; /* For safety */ | 
 | 			if ( (p-putp) >= len) | 
 | 			{ | 
 | 				printk("%s: cbp = %lx\n", devN->name, (long) H2S(cbp)); | 
 | 				proc_reset(dev0, 1);	/* Freeze IDT */ | 
 | 				break; /* For Safety */ | 
 | 			} | 
 | 			memcpy(p, S2H(tbdp->buf), amt); | 
 | 			p += amt; | 
 | 			if (count & I596_TBD_EOF) | 
 | 				break; | 
 | 			tbdp = (I596_TBD *) S2H(tbdp->next); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	Pass the frame to upper half | 
 | 	 */ | 
 | 	skb->protocol = eth_type_trans(skb, devN); | 
 | 	netif_rx(skb); | 
 | 	devN->last_rx = jiffies; | 
 | 	++privN->stats.rx_packets; | 
 | 	privN->stats.rx_bytes += len; | 
 |  | 
 | out: | 
 | 	cbp->xmit.status = I596_CB_STATUS_C | I596_CB_STATUS_OK; | 
 | } | 
 |  | 
 | /* | 
 |  *	Start transmission of a frame | 
 |  * | 
 |  *	The interface to the board is simple: we pretend that we are | 
 |  *	a fifth 82596 ethernet controller 'receiving' data, and copy the | 
 |  *	data into the same structures that a real 82596 would.  This way, | 
 |  *	the board firmware handles the host 'port' the same as any other. | 
 |  * | 
 |  *	NOTE: we do not use Bus master DMA for this routine.  Turns out | 
 |  *	that it is not needed.  Slave writes over the PCI bus are about | 
 |  *	as fast as DMA, due to the fact that the PLX part can do burst | 
 |  *	writes.  The same is not true for data being read from the board. | 
 |  * | 
 |  *	For multi-NIC mode, we tell the firmware the desired 82596 | 
 |  *	output port by setting the special "dstchan" member at the | 
 |  *	end of the traditional 82596 RFD structure. | 
 |  */ | 
 |  | 
 | static int dgrs_start_xmit(struct sk_buff *skb, struct net_device *devN) | 
 | { | 
 | 	DGRS_PRIV	*privN = (DGRS_PRIV *) devN->priv; | 
 | 	struct net_device	*dev0; | 
 | 	DGRS_PRIV	*priv0; | 
 | 	I596_RBD	*rbdp; | 
 | 	int		count; | 
 | 	int		i, len, amt; | 
 |  | 
 | 	/* | 
 | 	 *	Determine 0th priv and dev structure pointers | 
 | 	 */ | 
 | 	if (dgrs_nicmode) | 
 | 	{ | 
 | 		dev0 = privN->devtbl[0]; | 
 | 		priv0 = (DGRS_PRIV *) dev0->priv; | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		dev0 = devN; | 
 | 		priv0 = privN; | 
 | 	} | 
 |  | 
 | 	if (dgrs_debug > 1) | 
 | 		printk("%s: xmit len=%d\n", devN->name, (int) skb->len); | 
 |  | 
 | 	devN->trans_start = jiffies; | 
 | 	netif_start_queue(devN); | 
 |  | 
 | 	if (priv0->rfdp->cmd & I596_RFD_EL) | 
 | 	{	/* Out of RFD's */ | 
 | 		if (0) printk("%s: NO RFD's\n", devN->name); | 
 | 		goto no_resources; | 
 | 	} | 
 |  | 
 | 	rbdp = priv0->rbdp; | 
 | 	count = 0; | 
 | 	priv0->rfdp->rbdp = (I596_RBD *) H2S(rbdp); | 
 |  | 
 | 	i = 0; len = skb->len; | 
 | 	for (;;) | 
 | 	{ | 
 | 		if (rbdp->size & I596_RBD_EL) | 
 | 		{	/* Out of RBD's */ | 
 | 			if (0) printk("%s: NO RBD's\n", devN->name); | 
 | 			goto no_resources; | 
 | 		} | 
 |  | 
 | 		amt = min_t(unsigned int, len, rbdp->size - count); | 
 | 		memcpy( (char *) S2H(rbdp->buf) + count, skb->data + i, amt); | 
 | 		i += amt; | 
 | 		count += amt; | 
 | 		len -= amt; | 
 | 		if (len == 0) | 
 | 		{ | 
 | 			if (skb->len < 60) | 
 | 				rbdp->count = 60 | I596_RBD_EOF; | 
 | 			else | 
 | 				rbdp->count = count | I596_RBD_EOF; | 
 | 			rbdp = (I596_RBD *) S2H(rbdp->next); | 
 | 			goto frame_done; | 
 | 		} | 
 | 		else if (count < 32) | 
 | 		{ | 
 | 			/* More data to come, but we used less than 32 | 
 | 			 * bytes of this RBD.  Keep filling this RBD. | 
 | 			 */ | 
 | 			{}	/* Yes, we do nothing here */ | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			rbdp->count = count; | 
 | 			rbdp = (I596_RBD *) S2H(rbdp->next); | 
 | 			count = 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | frame_done: | 
 | 	priv0->rbdp = rbdp; | 
 | 	if (dgrs_nicmode) | 
 | 		priv0->rfdp->dstchan = privN->chan; | 
 | 	priv0->rfdp->status = I596_RFD_C | I596_RFD_OK; | 
 | 	priv0->rfdp = (I596_RFD *) S2H(priv0->rfdp->next); | 
 |  | 
 | 	++privN->stats.tx_packets; | 
 |  | 
 | 	dev_kfree_skb (skb); | 
 | 	return (0); | 
 |  | 
 | no_resources: | 
 | 	priv0->scbp->status |= I596_SCB_RNR;	/* simulate I82596 */ | 
 | 	return (-EAGAIN); | 
 | } | 
 |  | 
 | /* | 
 |  *	Open the interface | 
 |  */ | 
 | static int | 
 | dgrs_open( struct net_device *dev ) | 
 | { | 
 | 	netif_start_queue(dev); | 
 | 	return (0); | 
 | } | 
 |  | 
 | /* | 
 |  *	Close the interface | 
 |  */ | 
 | static int dgrs_close( struct net_device *dev ) | 
 | { | 
 | 	netif_stop_queue(dev); | 
 | 	return (0); | 
 | } | 
 |  | 
 | /* | 
 |  *	Get statistics | 
 |  */ | 
 | static struct net_device_stats *dgrs_get_stats( struct net_device *dev ) | 
 | { | 
 | 	DGRS_PRIV	*priv = (DGRS_PRIV *) dev->priv; | 
 |  | 
 | 	return (&priv->stats); | 
 | } | 
 |  | 
 | /* | 
 |  *	Set multicast list and/or promiscuous mode | 
 |  */ | 
 |  | 
 | static void dgrs_set_multicast_list( struct net_device *dev) | 
 | { | 
 | 	DGRS_PRIV	*priv = (DGRS_PRIV *) dev->priv; | 
 |  | 
 | 	priv->port->is_promisc = (dev->flags & IFF_PROMISC) ? 1 : 0; | 
 | } | 
 |  | 
 | /* | 
 |  *	Unique ioctl's | 
 |  */ | 
 | static int dgrs_ioctl(struct net_device *devN, struct ifreq *ifr, int cmd) | 
 | { | 
 | 	DGRS_PRIV	*privN = (DGRS_PRIV *) devN->priv; | 
 | 	DGRS_IOCTL	ioc; | 
 | 	int		i; | 
 |  | 
 | 	if (cmd != DGRSIOCTL) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if(copy_from_user(&ioc, ifr->ifr_data, sizeof(DGRS_IOCTL))) | 
 | 		return -EFAULT; | 
 |  | 
 | 	switch (ioc.cmd) | 
 | 	{ | 
 | 		case DGRS_GETMEM: | 
 | 			if (ioc.len != sizeof(ulong)) | 
 | 				return -EINVAL; | 
 | 			if(copy_to_user(ioc.data, &devN->mem_start, ioc.len)) | 
 | 				return -EFAULT; | 
 | 			return (0); | 
 | 		case DGRS_SETFILTER: | 
 | 			if (!capable(CAP_NET_ADMIN)) | 
 | 				return -EPERM; | 
 | 			if (ioc.port > privN->bcomm->bc_nports) | 
 | 				return -EINVAL; | 
 | 			if (ioc.filter >= NFILTERS) | 
 | 				return -EINVAL; | 
 | 			if (ioc.len > privN->bcomm->bc_filter_area_len) | 
 | 				return -EINVAL; | 
 |  | 
 | 			/* Wait for old command to finish */ | 
 | 			for (i = 0; i < 1000; ++i) | 
 | 			{ | 
 | 				if ( (volatile long) privN->bcomm->bc_filter_cmd <= 0 ) | 
 | 					break; | 
 | 				udelay(1); | 
 | 			} | 
 | 			if (i >= 1000) | 
 | 				return -EIO; | 
 |  | 
 | 			privN->bcomm->bc_filter_port = ioc.port; | 
 | 			privN->bcomm->bc_filter_num = ioc.filter; | 
 | 			privN->bcomm->bc_filter_len = ioc.len; | 
 | 	 | 
 | 			if (ioc.len) | 
 | 			{ | 
 | 				if(copy_from_user(S2HN(privN->bcomm->bc_filter_area), | 
 | 					ioc.data, ioc.len)) | 
 | 					return -EFAULT; | 
 | 				privN->bcomm->bc_filter_cmd = BC_FILTER_SET; | 
 | 			} | 
 | 			else | 
 | 				privN->bcomm->bc_filter_cmd = BC_FILTER_CLR; | 
 | 			return(0); | 
 | 		default: | 
 | 			return -EOPNOTSUPP; | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *	Process interrupts | 
 |  * | 
 |  *	dev, priv will always refer to the 0th device in Multi-NIC mode. | 
 |  */ | 
 |  | 
 | static irqreturn_t dgrs_intr(int irq, void *dev_id, struct pt_regs *regs) | 
 | { | 
 | 	struct net_device	*dev0 = (struct net_device *) dev_id; | 
 | 	DGRS_PRIV	*priv0 = (DGRS_PRIV *) dev0->priv; | 
 | 	I596_CB		*cbp; | 
 | 	int		cmd; | 
 | 	int		i; | 
 |  | 
 | 	++priv0->intrcnt; | 
 | 	if (1) ++priv0->bcomm->bc_cnt[4]; | 
 | 	if (0) | 
 | 	{ | 
 | 		static int cnt = 100; | 
 | 		if (--cnt > 0) | 
 | 		printk("%s: interrupt: irq %d\n", dev0->name, irq); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	Get 596 command | 
 | 	 */ | 
 | 	cmd = priv0->scbp->cmd; | 
 |  | 
 | 	/* | 
 | 	 *	See if RU has been restarted | 
 | 	 */ | 
 | 	if ( (cmd & I596_SCB_RUC) == I596_SCB_RUC_START) | 
 | 	{ | 
 | 		if (0) printk("%s: RUC start\n", dev0->name); | 
 | 		priv0->rfdp = (I596_RFD *) S2H(priv0->scbp->rfdp); | 
 | 		priv0->rbdp = (I596_RBD *) S2H(priv0->rfdp->rbdp); | 
 | 		priv0->scbp->status &= ~(I596_SCB_RNR|I596_SCB_RUS); | 
 | 		/* | 
 | 		 * Tell upper half (halves) | 
 | 		 */ | 
 | 		if (dgrs_nicmode) | 
 | 		{ | 
 | 			for (i = 0; i < priv0->nports; ++i) | 
 | 				netif_wake_queue (priv0->devtbl[i]); | 
 | 		} | 
 | 		else | 
 | 			netif_wake_queue (dev0); | 
 | 		/* if (bd->flags & TX_QUEUED) | 
 | 			DL_sched(bd, bdd); */ | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	See if any CU commands to process | 
 | 	 */ | 
 | 	if ( (cmd & I596_SCB_CUC) != I596_SCB_CUC_START) | 
 | 	{ | 
 | 		priv0->scbp->cmd = 0;	/* Ignore all other commands */ | 
 | 		goto ack_intr; | 
 | 	} | 
 | 	priv0->scbp->status &= ~(I596_SCB_CNA|I596_SCB_CUS); | 
 |  | 
 | 	/* | 
 | 	 *	Process a command | 
 | 	 */ | 
 | 	cbp = (I596_CB *) S2H(priv0->scbp->cbp); | 
 | 	priv0->scbp->cmd = 0;	/* Safe to clear the command */ | 
 | 	for (;;) | 
 | 	{ | 
 | 		switch (cbp->nop.cmd & I596_CB_CMD) | 
 | 		{ | 
 | 		case I596_CB_CMD_XMIT: | 
 | 			dgrs_rcv_frame(dev0, priv0, cbp); | 
 | 			break; | 
 | 		default: | 
 | 			cbp->nop.status = I596_CB_STATUS_C | I596_CB_STATUS_OK; | 
 | 			break; | 
 | 		} | 
 | 		if (cbp->nop.cmd & I596_CB_CMD_EL) | 
 | 			break; | 
 | 		cbp = (I596_CB *) S2H(cbp->nop.next); | 
 | 	} | 
 | 	priv0->scbp->status |= I596_SCB_CNA; | 
 |  | 
 | 	/* | 
 | 	 * Ack the interrupt | 
 | 	 */ | 
 | ack_intr: | 
 | 	if (priv0->plxreg) | 
 | 		OUTL(dev0->base_addr + PLX_LCL2PCI_DOORBELL, 1); | 
 |  | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | /* | 
 |  *	Download the board firmware | 
 |  */ | 
 | static int __init  | 
 | dgrs_download(struct net_device *dev0) | 
 | { | 
 | 	DGRS_PRIV	*priv0 = (DGRS_PRIV *) dev0->priv; | 
 | 	int		is; | 
 | 	unsigned long	i; | 
 |  | 
 | 	static int	iv2is[16] = { | 
 | 				0, 0, 0, ES4H_IS_INT3, | 
 | 				0, ES4H_IS_INT5, 0, ES4H_IS_INT7, | 
 | 				0, 0, ES4H_IS_INT10, ES4H_IS_INT11, | 
 | 				ES4H_IS_INT12, 0, 0, ES4H_IS_INT15 }; | 
 |  | 
 | 	/* | 
 | 	 * Map in the dual port memory | 
 | 	 */ | 
 | 	priv0->vmem = ioremap(dev0->mem_start, 2048*1024); | 
 | 	if (!priv0->vmem) | 
 | 	{ | 
 | 		printk("%s: cannot map in board memory\n", dev0->name); | 
 | 		return -ENXIO; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	Hold the processor and configure the board addresses | 
 | 	 */ | 
 | 	if (priv0->plxreg) | 
 | 	{	/* PCI bus */ | 
 | 		proc_reset(dev0, 1); | 
 | 	} | 
 | 	else | 
 | 	{	/* EISA bus */ | 
 | 		is = iv2is[dev0->irq & 0x0f]; | 
 | 		if (!is) | 
 | 		{ | 
 | 			printk("%s: Illegal IRQ %d\n", dev0->name, dev0->irq); | 
 | 			iounmap(priv0->vmem); | 
 | 			priv0->vmem = NULL; | 
 | 			return -ENXIO; | 
 | 		} | 
 | 		OUTB(dev0->base_addr + ES4H_AS_31_24, | 
 | 			(uchar) (dev0->mem_start >> 24) ); | 
 | 		OUTB(dev0->base_addr + ES4H_AS_23_16, | 
 | 			(uchar) (dev0->mem_start >> 16) ); | 
 | 		priv0->is_reg = ES4H_IS_LINEAR | is | | 
 | 			((uchar) (dev0->mem_start >> 8) & ES4H_IS_AS15); | 
 | 		OUTB(dev0->base_addr + ES4H_IS, priv0->is_reg); | 
 | 		OUTB(dev0->base_addr + ES4H_EC, ES4H_EC_ENABLE); | 
 | 		OUTB(dev0->base_addr + ES4H_PC, ES4H_PC_RESET); | 
 | 		OUTB(dev0->base_addr + ES4H_MW, ES4H_MW_ENABLE | 0x00); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	See if we can do DMA on the SE-6 | 
 | 	 */ | 
 | 	priv0->use_dma = check_board_dma(dev0); | 
 | 	if (priv0->use_dma) | 
 | 		printk("%s: Bus Master DMA is enabled.\n", dev0->name); | 
 |  | 
 | 	/* | 
 | 	 * Load and verify the code at the desired address | 
 | 	 */ | 
 | 	memcpy(priv0->vmem, dgrs_code, dgrs_ncode);	/* Load code */ | 
 | 	if (memcmp(priv0->vmem, dgrs_code, dgrs_ncode)) | 
 | 	{ | 
 | 		iounmap(priv0->vmem); | 
 | 		priv0->vmem = NULL; | 
 | 		printk("%s: download compare failed\n", dev0->name); | 
 | 		return -ENXIO; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Configurables | 
 | 	 */ | 
 | 	priv0->bcomm = (struct bios_comm *) (priv0->vmem + 0x0100); | 
 | 	priv0->bcomm->bc_nowait = 1;	/* Tell board to make printf not wait */ | 
 | 	priv0->bcomm->bc_squelch = 0;	/* Flag from Space.c */ | 
 | 	priv0->bcomm->bc_150ohm = 0;	/* Flag from Space.c */ | 
 |  | 
 | 	priv0->bcomm->bc_spew = 0;	/* Debug flag from Space.c */ | 
 | 	priv0->bcomm->bc_maxrfd = 0;	/* Debug flag from Space.c */ | 
 | 	priv0->bcomm->bc_maxrbd = 0;	/* Debug flag from Space.c */ | 
 |  | 
 | 	/* | 
 | 	 * Tell board we are operating in switch mode (1) or in | 
 | 	 * multi-NIC mode (2). | 
 | 	 */ | 
 | 	priv0->bcomm->bc_host = dgrs_nicmode ? BC_MULTINIC : BC_SWITCH; | 
 |  | 
 | 	/* | 
 | 	 * Request memory space on board for DMA chains | 
 | 	 */ | 
 | 	if (priv0->use_dma) | 
 | 		priv0->bcomm->bc_hostarea_len = (2048/64) * 16; | 
 |  | 
 | 	/* | 
 | 	 * NVRAM configurables from Space.c | 
 | 	 */ | 
 | 	priv0->bcomm->bc_spantree = dgrs_spantree; | 
 | 	priv0->bcomm->bc_hashexpire = dgrs_hashexpire; | 
 | 	memcpy(priv0->bcomm->bc_ipaddr, dgrs_ipaddr, 4); | 
 | 	memcpy(priv0->bcomm->bc_iptrap, dgrs_iptrap, 4); | 
 | 	memcpy(priv0->bcomm->bc_ipxnet, &dgrs_ipxnet, 4); | 
 |  | 
 | 	/* | 
 | 	 * Release processor, wait 8 seconds for board to initialize | 
 | 	 */ | 
 | 	proc_reset(dev0, 0); | 
 |  | 
 | 	for (i = jiffies + 8 * HZ; time_after(i, jiffies); ) | 
 | 	{ | 
 | 		barrier();		/* Gcc 2.95 needs this */ | 
 | 		if (priv0->bcomm->bc_status >= BC_RUN) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	if (priv0->bcomm->bc_status < BC_RUN) | 
 | 	{ | 
 | 		printk("%s: board not operating\n", dev0->name); | 
 | 		iounmap(priv0->vmem); | 
 | 		priv0->vmem = NULL; | 
 | 		return -ENXIO; | 
 | 	} | 
 |  | 
 | 	priv0->port = (PORT *) S2H(priv0->bcomm->bc_port); | 
 | 	priv0->scbp = (I596_SCB *) S2H(priv0->port->scbp); | 
 | 	priv0->rfdp = (I596_RFD *) S2H(priv0->scbp->rfdp); | 
 | 	priv0->rbdp = (I596_RBD *) S2H(priv0->rfdp->rbdp); | 
 |  | 
 | 	priv0->scbp->status = I596_SCB_CNA;	/* CU is idle */ | 
 |  | 
 | 	/* | 
 | 	 *	Get switch physical and host virtual pointers to DMA | 
 | 	 *	chaining area.  NOTE: the MSB of the switch physical | 
 | 	 *	address *must* be turned off.  Otherwise, the HW kludge | 
 | 	 *	that allows host access of the PLX DMA registers will | 
 | 	 *	erroneously select the PLX registers. | 
 | 	 */ | 
 | 	priv0->dmadesc_s = (DMACHAIN *) S2DMA(priv0->bcomm->bc_hostarea); | 
 | 	if (priv0->dmadesc_s) | 
 | 		priv0->dmadesc_h = (DMACHAIN *) S2H(priv0->dmadesc_s); | 
 | 	else | 
 | 		priv0->dmadesc_h = NULL; | 
 |  | 
 | 	/* | 
 | 	 *	Enable board interrupts | 
 | 	 */ | 
 | 	if (priv0->plxreg) | 
 | 	{	/* PCI bus */ | 
 | 		OUTL(dev0->base_addr + PLX_INT_CSR, | 
 | 			inl(dev0->base_addr + PLX_INT_CSR) | 
 | 			| PLX_PCI_DOORBELL_IE);	/* Enable intr to host */ | 
 | 		OUTL(dev0->base_addr + PLX_LCL2PCI_DOORBELL, 1); | 
 | 	} | 
 | 	else | 
 | 	{	/* EISA bus */ | 
 | 	} | 
 |  | 
 | 	return (0); | 
 | } | 
 |  | 
 | /* | 
 |  *	Probe (init) a board | 
 |  */ | 
 | static int __init  | 
 | dgrs_probe1(struct net_device *dev) | 
 | { | 
 | 	DGRS_PRIV	*priv = (DGRS_PRIV *) dev->priv; | 
 | 	unsigned long	i; | 
 | 	int		rc; | 
 |  | 
 | 	printk("%s: Digi RightSwitch io=%lx mem=%lx irq=%d plx=%lx dma=%lx\n", | 
 | 		dev->name, dev->base_addr, dev->mem_start, dev->irq, | 
 | 		priv->plxreg, priv->plxdma); | 
 |  | 
 | 	/* | 
 | 	 *	Download the firmware and light the processor | 
 | 	 */ | 
 | 	rc = dgrs_download(dev); | 
 | 	if (rc) | 
 | 		goto err_out; | 
 |  | 
 | 	/* | 
 | 	 * Get ether address of board | 
 | 	 */ | 
 | 	printk("%s: Ethernet address", dev->name); | 
 | 	memcpy(dev->dev_addr, priv->port->ethaddr, 6); | 
 | 	for (i = 0; i < 6; ++i) | 
 | 		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); | 
 | 	printk("\n"); | 
 |  | 
 | 	if (dev->dev_addr[0] & 1) | 
 | 	{ | 
 | 		printk("%s: Illegal Ethernet Address\n", dev->name); | 
 | 		rc = -ENXIO; | 
 | 		goto err_out; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	ACK outstanding interrupts, hook the interrupt, | 
 | 	 *	and verify that we are getting interrupts from the board. | 
 | 	 */ | 
 | 	if (priv->plxreg) | 
 | 		OUTL(dev->base_addr + PLX_LCL2PCI_DOORBELL, 1); | 
 | 	 | 
 | 	rc = request_irq(dev->irq, &dgrs_intr, SA_SHIRQ, "RightSwitch", dev); | 
 | 	if (rc) | 
 | 		goto err_out; | 
 |  | 
 | 	priv->intrcnt = 0; | 
 | 	for (i = jiffies + 2*HZ + HZ/2; time_after(i, jiffies); ) | 
 | 	{ | 
 | 		cpu_relax(); | 
 | 		if (priv->intrcnt >= 2) | 
 | 			break; | 
 | 	} | 
 | 	if (priv->intrcnt < 2) | 
 | 	{ | 
 | 		printk(KERN_ERR "%s: Not interrupting on IRQ %d (%d)\n", | 
 | 				dev->name, dev->irq, priv->intrcnt); | 
 | 		rc = -ENXIO; | 
 | 		goto err_free_irq; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	Entry points... | 
 | 	 */ | 
 | 	dev->open = &dgrs_open; | 
 | 	dev->stop = &dgrs_close; | 
 | 	dev->get_stats = &dgrs_get_stats; | 
 | 	dev->hard_start_xmit = &dgrs_start_xmit; | 
 | 	dev->set_multicast_list = &dgrs_set_multicast_list; | 
 | 	dev->do_ioctl = &dgrs_ioctl; | 
 |  | 
 | 	return rc; | 
 |  | 
 | err_free_irq: | 
 | 	free_irq(dev->irq, dev); | 
 | err_out: | 
 |        	return rc; | 
 | } | 
 |  | 
 | static int __init  | 
 | dgrs_initclone(struct net_device *dev) | 
 | { | 
 | 	DGRS_PRIV	*priv = (DGRS_PRIV *) dev->priv; | 
 | 	int		i; | 
 |  | 
 | 	printk("%s: Digi RightSwitch port %d ", | 
 | 		dev->name, priv->chan); | 
 | 	for (i = 0; i < 6; ++i) | 
 | 		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]); | 
 | 	printk("\n"); | 
 |  | 
 | 	return (0); | 
 | } | 
 |  | 
 | static struct net_device * __init  | 
 | dgrs_found_device( | 
 | 	int		io, | 
 | 	ulong		mem, | 
 | 	int		irq, | 
 | 	ulong		plxreg, | 
 | 	ulong		plxdma, | 
 | 	struct device   *pdev | 
 | ) | 
 | { | 
 | 	DGRS_PRIV *priv; | 
 | 	struct net_device *dev; | 
 | 	int i, ret = -ENOMEM; | 
 |  | 
 | 	dev = alloc_etherdev(sizeof(DGRS_PRIV)); | 
 | 	if (!dev) | 
 | 		goto err0; | 
 |  | 
 | 	priv = (DGRS_PRIV *)dev->priv; | 
 |  | 
 | 	dev->base_addr = io; | 
 | 	dev->mem_start = mem; | 
 | 	dev->mem_end = mem + 2048 * 1024 - 1; | 
 | 	dev->irq = irq; | 
 | 	priv->plxreg = plxreg; | 
 | 	priv->plxdma = plxdma; | 
 | 	priv->vplxdma = NULL; | 
 |  | 
 | 	priv->chan = 1; | 
 | 	priv->devtbl[0] = dev; | 
 |  | 
 | 	SET_MODULE_OWNER(dev); | 
 | 	SET_NETDEV_DEV(dev, pdev); | 
 | 	 | 
 | 	ret = dgrs_probe1(dev); | 
 | 	if (ret)  | 
 | 		goto err1; | 
 |  | 
 | 	ret = register_netdev(dev); | 
 | 	if (ret) | 
 | 		goto err2; | 
 |  | 
 | 	if ( !dgrs_nicmode ) | 
 | 		return dev;	/* Switch mode, we are done */ | 
 |  | 
 | 	/* | 
 | 	 * Operating card as N separate NICs | 
 | 	 */ | 
 |  | 
 | 	priv->nports = priv->bcomm->bc_nports; | 
 |  | 
 | 	for (i = 1; i < priv->nports; ++i) | 
 | 	{ | 
 | 		struct net_device	*devN; | 
 | 		DGRS_PRIV	*privN; | 
 | 			/* Allocate new dev and priv structures */ | 
 | 		devN = alloc_etherdev(sizeof(DGRS_PRIV)); | 
 | 		ret = -ENOMEM; | 
 | 		if (!devN)  | 
 | 			goto fail; | 
 |  | 
 | 		/* Don't copy the network device structure! */ | 
 |  | 
 | 		/* copy the priv structure of dev[0] */ | 
 | 		privN = (DGRS_PRIV *)devN->priv; | 
 | 		*privN = *priv; | 
 |  | 
 | 			/* ... and zero out VM areas */ | 
 | 		privN->vmem = NULL; | 
 | 		privN->vplxdma = NULL; | 
 | 			/* ... and zero out IRQ */ | 
 | 		devN->irq = 0; | 
 | 			/* ... and base MAC address off address of 1st port */ | 
 | 		devN->dev_addr[5] += i; | 
 |  | 
 | 		ret = dgrs_initclone(devN); | 
 | 		if (ret) | 
 | 			goto fail; | 
 |  | 
 | 		SET_MODULE_OWNER(devN); | 
 | 		SET_NETDEV_DEV(dev, pdev); | 
 |  | 
 | 		ret = register_netdev(devN); | 
 | 		if (ret) { | 
 | 			free_netdev(devN); | 
 | 			goto fail; | 
 | 		} | 
 | 		privN->chan = i+1; | 
 | 		priv->devtbl[i] = devN; | 
 | 	} | 
 | 	return dev; | 
 |  | 
 |  fail:	 | 
 | 	while (i >= 0) { | 
 | 		struct net_device *d = priv->devtbl[i--]; | 
 | 		unregister_netdev(d); | 
 | 		free_netdev(d); | 
 | 	} | 
 |  | 
 |  err2: | 
 | 	free_irq(dev->irq, dev); | 
 |  err1: | 
 | 	free_netdev(dev); | 
 |  err0: | 
 | 	return ERR_PTR(ret); | 
 | } | 
 |  | 
 | static void __devexit dgrs_remove(struct net_device *dev) | 
 | { | 
 | 	DGRS_PRIV *priv = dev->priv; | 
 | 	int i; | 
 |  | 
 | 	unregister_netdev(dev); | 
 |  | 
 | 	for (i = 1; i < priv->nports; ++i) { | 
 | 		struct net_device *d = priv->devtbl[i]; | 
 | 		if (d) { | 
 | 			unregister_netdev(d); | 
 | 			free_netdev(d); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	proc_reset(priv->devtbl[0], 1); | 
 |  | 
 | 	if (priv->vmem) | 
 | 		iounmap(priv->vmem); | 
 | 	if (priv->vplxdma) | 
 | 		iounmap((uchar *) priv->vplxdma); | 
 |  | 
 | 	if (dev->irq) | 
 | 		free_irq(dev->irq, dev); | 
 |  | 
 | 	for (i = 1; i < priv->nports; ++i) { | 
 | 		if (priv->devtbl[i]) | 
 | 			unregister_netdev(priv->devtbl[i]); | 
 | 	} | 
 | } | 
 |  | 
 | #ifdef CONFIG_PCI | 
 | static int __init dgrs_pci_probe(struct pci_dev *pdev, | 
 | 				 const struct pci_device_id *ent) | 
 | { | 
 | 	struct net_device *dev; | 
 | 	int err; | 
 | 	uint	io; | 
 | 	uint	mem; | 
 | 	uint	irq; | 
 | 	uint	plxreg; | 
 | 	uint	plxdma; | 
 |  | 
 | 	/* | 
 | 	 * Get and check the bus-master and latency values. | 
 | 	 * Some PCI BIOSes fail to set the master-enable bit, | 
 | 	 * and the latency timer must be set to the maximum | 
 | 	 * value to avoid data corruption that occurs when the | 
 | 	 * timer expires during a transfer.  Yes, it's a bug. | 
 | 	 */ | 
 | 	err = pci_enable_device(pdev); | 
 | 	if (err) | 
 | 		return err; | 
 | 	err = pci_request_regions(pdev, "RightSwitch"); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	pci_set_master(pdev); | 
 |  | 
 | 	plxreg = pci_resource_start (pdev, 0); | 
 | 	io = pci_resource_start (pdev, 1); | 
 | 	mem = pci_resource_start (pdev, 2); | 
 | 	pci_read_config_dword(pdev, 0x30, &plxdma); | 
 | 	irq = pdev->irq; | 
 | 	plxdma &= ~15; | 
 |  | 
 | 	/* | 
 | 	 * On some BIOSES, the PLX "expansion rom" (used for DMA) | 
 | 	 * address comes up as "0".  This is probably because | 
 | 	 * the BIOS doesn't see a valid 55 AA ROM signature at | 
 | 	 * the "ROM" start and zeroes the address.  To get | 
 | 	 * around this problem the SE-6 is configured to ask | 
 | 	 * for 4 MB of space for the dual port memory.  We then | 
 | 	 * must set its range back to 2 MB, and use the upper | 
 | 	 * half for DMA register access | 
 | 	 */ | 
 | 	OUTL(io + PLX_SPACE0_RANGE, 0xFFE00000L); | 
 | 	if (plxdma == 0) | 
 | 		plxdma = mem + (2048L * 1024L); | 
 | 	pci_write_config_dword(pdev, 0x30, plxdma + 1); | 
 | 	pci_read_config_dword(pdev, 0x30, &plxdma); | 
 | 	plxdma &= ~15; | 
 |  | 
 | 	dev = dgrs_found_device(io, mem, irq, plxreg, plxdma, &pdev->dev); | 
 | 	if (IS_ERR(dev)) { | 
 | 		pci_release_regions(pdev); | 
 | 		return PTR_ERR(dev); | 
 | 	} | 
 |  | 
 | 	pci_set_drvdata(pdev, dev); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void __devexit dgrs_pci_remove(struct pci_dev *pdev) | 
 | { | 
 | 	struct net_device *dev = pci_get_drvdata(pdev); | 
 |  | 
 | 	dgrs_remove(dev); | 
 | 	pci_release_regions(pdev); | 
 | 	free_netdev(dev); | 
 | } | 
 |  | 
 | static struct pci_driver dgrs_pci_driver = { | 
 | 	.name = "dgrs", | 
 | 	.id_table = dgrs_pci_tbl, | 
 | 	.probe = dgrs_pci_probe, | 
 | 	.remove = __devexit_p(dgrs_pci_remove), | 
 | }; | 
 | #endif | 
 |  | 
 |  | 
 | #ifdef CONFIG_EISA | 
 | static int is2iv[8] __initdata = { 0, 3, 5, 7, 10, 11, 12, 15 }; | 
 |  | 
 | static int __init dgrs_eisa_probe (struct device *gendev) | 
 | { | 
 | 	struct net_device *dev; | 
 | 	struct eisa_device *edev = to_eisa_device(gendev); | 
 | 	uint	io = edev->base_addr; | 
 | 	uint	mem; | 
 | 	uint	irq; | 
 | 	int 	rc = -ENODEV; /* Not EISA configured */ | 
 |  | 
 | 	if (!request_region(io, 256, "RightSwitch")) { | 
 | 		printk(KERN_ERR "dgrs: eisa io 0x%x, which is busy.\n", io); | 
 | 		return -EBUSY; | 
 | 	} | 
 |  | 
 | 	if ( ! (inb(io+ES4H_EC) & ES4H_EC_ENABLE) )  | 
 | 		goto err_out; | 
 |  | 
 | 	mem = (inb(io+ES4H_AS_31_24) << 24) | 
 | 		+ (inb(io+ES4H_AS_23_16) << 16); | 
 |  | 
 | 	irq = is2iv[ inb(io+ES4H_IS) & ES4H_IS_INTMASK ]; | 
 |  | 
 | 	dev = dgrs_found_device(io, mem, irq, 0L, 0L, gendev); | 
 | 	if (IS_ERR(dev)) { | 
 | 		rc = PTR_ERR(dev); | 
 | 		goto err_out; | 
 | 	} | 
 |  | 
 | 	gendev->driver_data = dev; | 
 | 	return 0; | 
 |  err_out: | 
 | 	release_region(io, 256); | 
 | 	return rc; | 
 | } | 
 |  | 
 | static int __devexit dgrs_eisa_remove(struct device *gendev) | 
 | { | 
 | 	struct net_device *dev = gendev->driver_data; | 
 | 	 | 
 | 	dgrs_remove(dev); | 
 |  | 
 | 	release_region(dev->base_addr, 256); | 
 | 		 | 
 | 	free_netdev(dev); | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | static struct eisa_driver dgrs_eisa_driver = { | 
 | 	.id_table = dgrs_eisa_tbl, | 
 | 	.driver = { | 
 | 		.name = "dgrs", | 
 | 		.probe = dgrs_eisa_probe, | 
 | 		.remove = __devexit_p(dgrs_eisa_remove), | 
 | 	} | 
 | }; | 
 | #endif | 
 |  | 
 | /* | 
 |  *	Variables that can be overriden from module command line | 
 |  */ | 
 | static int	debug = -1; | 
 | static int	dma = -1; | 
 | static int	hashexpire = -1; | 
 | static int	spantree = -1; | 
 | static int	ipaddr[4] = { -1 }; | 
 | static int	iptrap[4] = { -1 }; | 
 | static __u32	ipxnet = -1; | 
 | static int	nicmode = -1; | 
 |  | 
 | module_param(debug, int, 0); | 
 | module_param(dma, int, 0); | 
 | module_param(hashexpire, int, 0); | 
 | module_param(spantree, int, 0); | 
 | module_param_array(ipaddr, int, NULL, 0); | 
 | module_param_array(iptrap, int, NULL, 0); | 
 | module_param(ipxnet, int, 0); | 
 | module_param(nicmode, int, 0); | 
 | MODULE_PARM_DESC(debug, "Digi RightSwitch enable debugging (0-1)"); | 
 | MODULE_PARM_DESC(dma, "Digi RightSwitch enable BM DMA (0-1)"); | 
 | MODULE_PARM_DESC(nicmode, "Digi RightSwitch operating mode (1: switch, 2: multi-NIC)"); | 
 |  | 
 | static int __init dgrs_init_module (void) | 
 | { | 
 | 	int	i; | 
 | 	int eisacount = 0, pcicount = 0; | 
 |  | 
 | 	/* | 
 | 	 *	Command line variable overrides | 
 | 	 *		debug=NNN | 
 | 	 *		dma=0/1 | 
 | 	 *		spantree=0/1 | 
 | 	 *		hashexpire=NNN | 
 | 	 *		ipaddr=A,B,C,D | 
 | 	 *		iptrap=A,B,C,D | 
 | 	 *		ipxnet=NNN | 
 | 	 *		nicmode=NNN | 
 | 	 */ | 
 | 	if (debug >= 0) | 
 | 		dgrs_debug = debug; | 
 | 	if (dma >= 0) | 
 | 		dgrs_dma = dma; | 
 | 	if (nicmode >= 0) | 
 | 		dgrs_nicmode = nicmode; | 
 | 	if (hashexpire >= 0) | 
 | 		dgrs_hashexpire = hashexpire; | 
 | 	if (spantree >= 0) | 
 | 		dgrs_spantree = spantree; | 
 | 	if (ipaddr[0] != -1) | 
 | 		for (i = 0; i < 4; ++i) | 
 | 			dgrs_ipaddr[i] = ipaddr[i]; | 
 | 	if (iptrap[0] != -1) | 
 | 		for (i = 0; i < 4; ++i) | 
 | 			dgrs_iptrap[i] = iptrap[i]; | 
 | 	if (ipxnet != -1) | 
 | 		dgrs_ipxnet = htonl( ipxnet ); | 
 |  | 
 | 	if (dgrs_debug) | 
 | 	{ | 
 | 		printk(KERN_INFO "dgrs: SW=%s FW=Build %d %s\nFW Version=%s\n", | 
 | 		       version, dgrs_firmnum, dgrs_firmdate, dgrs_firmver); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *	Find and configure all the cards | 
 | 	 */ | 
 | #ifdef CONFIG_EISA | 
 | 	eisacount = eisa_driver_register(&dgrs_eisa_driver); | 
 | 	if (eisacount < 0) | 
 | 		return eisacount; | 
 | #endif | 
 | #ifdef CONFIG_PCI | 
 | 	pcicount = pci_register_driver(&dgrs_pci_driver); | 
 | 	if (pcicount) | 
 | 		return pcicount; | 
 | #endif | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void __exit dgrs_cleanup_module (void) | 
 | { | 
 | #ifdef CONFIG_EISA | 
 | 	eisa_driver_unregister (&dgrs_eisa_driver); | 
 | #endif | 
 | #ifdef CONFIG_PCI | 
 | 	pci_unregister_driver (&dgrs_pci_driver); | 
 | #endif | 
 | } | 
 |  | 
 | module_init(dgrs_init_module); | 
 | module_exit(dgrs_cleanup_module); |