 f9dcbcc9e3
			
		
	
	
	f9dcbcc9e3
	
	
	
		
			
			removed some needless checks and also corrected bug in lp486e (dmi was passed instead of dmi->dmi_addr) Signed-off-by: Jiri Pirko <jpirko@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
		
			
				
	
	
		
			1341 lines
		
	
	
	
		
			32 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1341 lines
		
	
	
	
		
			32 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Intel Professional Workstation/panther ethernet driver */
 | |
| /* lp486e.c: A panther 82596 ethernet driver for linux. */
 | |
| /*
 | |
|     History and copyrights:
 | |
| 
 | |
|     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 General Public License
 | |
| 	as modified by SRC, incorporated herein by reference.
 | |
| 
 | |
|         The author may be reached as becker@scyld.com, or C/O
 | |
| 	Scyld Computing Corporation
 | |
| 	410 Severn Ave., Suite 210
 | |
| 	Annapolis MD 21403
 | |
| 
 | |
|     Apricot
 | |
|         Written 1994 by Mark Evans.
 | |
|         This driver is for the Apricot 82596 bus-master interface
 | |
| 
 | |
|         Modularised 12/94 Mark Evans
 | |
| 
 | |
|     Professional Workstation
 | |
| 	Derived from apricot.c by Ard van Breemen
 | |
| 	<ard@murphy.nl>|<ard@cstmel.hobby.nl>|<ard@cstmel.nl.eu.org>
 | |
| 
 | |
| 	Credits:
 | |
| 	Thanks to Murphy Software BV for letting me write this in their time.
 | |
| 	Well, actually, I get payed doing this...
 | |
| 	(Also: see http://www.murphy.nl for murphy, and my homepage ~ard for
 | |
| 	more information on the Professional Workstation)
 | |
| 
 | |
|     Present version
 | |
| 	aeb@cwi.nl
 | |
| */
 | |
| /*
 | |
|     There are currently two motherboards that I know of in the
 | |
|     professional workstation. The only one that I know is the
 | |
|     intel panther motherboard. -- ard
 | |
| */
 | |
| /*
 | |
| The pws is equipped with an intel 82596. This is a very intelligent controller
 | |
| which runs its own micro-code. Communication with the hostprocessor is done
 | |
| through linked lists of commands and buffers in the hostprocessors memory.
 | |
| A complete description of the 82596 is available from intel. Search for
 | |
| a file called "29021806.pdf". It is a complete description of the chip itself.
 | |
| To use it for the pws some additions are needed regarding generation of
 | |
| the PORT and CA signal, and the interrupt glue needed for a pc.
 | |
| I/O map:
 | |
| PORT  SIZE ACTION MEANING
 | |
| 0xCB0    2 WRITE  Lower 16 bits for PORT command
 | |
| 0xCB2    2 WRITE  Upper 16 bits for PORT command, and issue of PORT command
 | |
| 0xCB4    1 WRITE  Generation of CA signal
 | |
| 0xCB8    1 WRITE  Clear interrupt glue
 | |
| All other communication is through memory!
 | |
| */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/string.h>
 | |
| #include <linux/errno.h>
 | |
| #include <linux/ioport.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/interrupt.h>
 | |
| #include <linux/netdevice.h>
 | |
| #include <linux/etherdevice.h>
 | |
| #include <linux/skbuff.h>
 | |
| #include <linux/bitops.h>
 | |
| 
 | |
| #include <asm/io.h>
 | |
| #include <asm/dma.h>
 | |
| 
 | |
| #define DRV_NAME "lp486e"
 | |
| 
 | |
| /* debug print flags */
 | |
| #define LOG_SRCDST    0x80000000
 | |
| #define LOG_STATINT   0x40000000
 | |
| #define LOG_STARTINT  0x20000000
 | |
| 
 | |
| #define i596_debug debug
 | |
| 
 | |
| static int i596_debug = 0;
 | |
| 
 | |
| static const char * const medianame[] = {
 | |
| 	"10baseT", "AUI",
 | |
| 	"10baseT-FD", "AUI-FD",
 | |
| };
 | |
| 
 | |
| #define LP486E_TOTAL_SIZE 16
 | |
| 
 | |
| #define I596_NULL (0xffffffff)
 | |
| 
 | |
| #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,
 | |
| 	CmdIASetup = 1,
 | |
| 	CmdConfigure = 2,
 | |
| 	CmdMulticastList = 3,
 | |
| 	CmdTx = 4,
 | |
| 	CmdTDR = 5,
 | |
| 	CmdDump = 6,
 | |
| 	CmdDiagnose = 7
 | |
| };
 | |
| 
 | |
| #if 0
 | |
| static const char *CUcmdnames[8] = { "NOP", "IASetup", "Configure", "MulticastList",
 | |
| 				     "Tx", "TDR", "Dump", "Diagnose" };
 | |
| #endif
 | |
| 
 | |
| /* Status word bits */
 | |
| #define	STAT_CX		0x8000	/* The CU finished executing a command
 | |
| 				   with the Interrupt bit set */
 | |
| #define	STAT_FR		0x4000	/* The RU finished receiving a frame */
 | |
| #define	STAT_CNA	0x2000	/* The CU left the active state */
 | |
| #define	STAT_RNR	0x1000	/* The RU left the active state */
 | |
| #define STAT_ACK	(STAT_CX | STAT_FR | STAT_CNA | STAT_RNR)
 | |
| #define	STAT_CUS	0x0700	/* Status of CU: 0: idle, 1: suspended,
 | |
| 				   2: active, 3-7: unused */
 | |
| #define STAT_RUS	0x00f0	/* Status of RU: 0: idle, 1: suspended,
 | |
| 				   2: no resources, 4: ready,
 | |
| 				   10: no resources due to no more RBDs,
 | |
| 				   12: no more RBDs, other: unused */
 | |
| #define	STAT_T		0x0008	/* Bus throttle timers loaded */
 | |
| #define	STAT_ZERO	0x0807	/* Always zero */
 | |
| 
 | |
| #if 0
 | |
| static char *CUstates[8] = {
 | |
| 	"idle", "suspended", "active", 0, 0, 0, 0, 0
 | |
| };
 | |
| static char *RUstates[16] = {
 | |
| 	"idle", "suspended", "no resources", 0, "ready", 0, 0, 0,
 | |
| 	0, 0, "no RBDs", 0, "out of RBDs", 0, 0, 0
 | |
| };
 | |
| 
 | |
| static void
 | |
| i596_out_status(int status) {
 | |
| 	int bad = 0;
 | |
| 	char *s;
 | |
| 
 | |
| 	printk("status %4.4x:", status);
 | |
| 	if (status == 0xffff)
 | |
| 		printk(" strange..\n");
 | |
| 	else {
 | |
| 		if (status & STAT_CX)
 | |
| 			printk("  CU done");
 | |
| 		if (status & STAT_CNA)
 | |
| 			printk("  CU stopped");
 | |
| 		if (status & STAT_FR)
 | |
| 			printk("  got a frame");
 | |
| 		if (status & STAT_RNR)
 | |
| 			printk("  RU stopped");
 | |
| 		if (status & STAT_T)
 | |
| 			printk("  throttled");
 | |
| 		if (status & STAT_ZERO)
 | |
| 			bad = 1;
 | |
| 		s = CUstates[(status & STAT_CUS) >> 8];
 | |
| 		if (!s)
 | |
| 			bad = 1;
 | |
| 		else
 | |
| 			printk("  CU(%s)", s);
 | |
| 		s = RUstates[(status & STAT_RUS) >> 4];
 | |
| 		if (!s)
 | |
| 			bad = 1;
 | |
| 		else
 | |
| 			printk("  RU(%s)", s);
 | |
| 		if (bad)
 | |
| 			printk("  bad status");
 | |
| 		printk("\n");
 | |
| 	}
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* Command word bits */
 | |
| #define ACK_CX		0x8000
 | |
| #define ACK_FR		0x4000
 | |
| #define ACK_CNA		0x2000
 | |
| #define ACK_RNR		0x1000
 | |
| 
 | |
| #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
 | |
| 
 | |
| typedef u32 phys_addr;
 | |
| 
 | |
| static inline phys_addr
 | |
| va_to_pa(void *x) {
 | |
| 	return x ? virt_to_bus(x) : I596_NULL;
 | |
| }
 | |
| 
 | |
| static inline void *
 | |
| pa_to_va(phys_addr x) {
 | |
| 	return (x == I596_NULL) ? NULL : bus_to_virt(x);
 | |
| }
 | |
| 
 | |
| /* status bits for cmd */
 | |
| #define CMD_STAT_C	0x8000	/* CU command complete */
 | |
| #define CMD_STAT_B	0x4000	/* CU command in progress */
 | |
| #define CMD_STAT_OK	0x2000	/* CU command completed without errors */
 | |
| #define CMD_STAT_A	0x1000	/* CU command abnormally terminated */
 | |
| 
 | |
| struct i596_cmd {		/* 8 bytes */
 | |
| 	unsigned short status;
 | |
| 	unsigned short command;
 | |
| 	phys_addr pa_next;	/* va_to_pa(struct i596_cmd *next) */
 | |
| };
 | |
| 
 | |
| #define EOF		0x8000
 | |
| #define SIZE_MASK	0x3fff
 | |
| 
 | |
| struct i596_tbd {
 | |
| 	unsigned short size;
 | |
| 	unsigned short pad;
 | |
| 	phys_addr pa_next;	/* va_to_pa(struct i596_tbd *next) */
 | |
| 	phys_addr pa_data;	/* va_to_pa(char *data) */
 | |
| 	struct sk_buff *skb;
 | |
| };
 | |
| 
 | |
| struct tx_cmd {
 | |
| 	struct i596_cmd cmd;
 | |
| 	phys_addr pa_tbd;	/* va_to_pa(struct i596_tbd *tbd) */
 | |
| 	unsigned short size;
 | |
| 	unsigned short pad;
 | |
| };
 | |
| 
 | |
| /* status bits for rfd */
 | |
| #define RFD_STAT_C	0x8000	/* Frame reception complete */
 | |
| #define RFD_STAT_B	0x4000	/* Frame reception in progress */
 | |
| #define RFD_STAT_OK	0x2000	/* Frame received without errors */
 | |
| #define RFD_STATUS	0x1fff
 | |
| #define RFD_LENGTH_ERR	0x1000
 | |
| #define RFD_CRC_ERR	0x0800
 | |
| #define RFD_ALIGN_ERR	0x0400
 | |
| #define RFD_NOBUFS_ERR	0x0200
 | |
| #define RFD_DMA_ERR	0x0100	/* DMA overrun failure to acquire system bus */
 | |
| #define RFD_SHORT_FRAME_ERR	0x0080
 | |
| #define RFD_NOEOP_ERR	0x0040
 | |
| #define RFD_TRUNC_ERR	0x0020
 | |
| #define RFD_MULTICAST  0x0002	/* 0: destination had our address
 | |
| 				   1: destination was broadcast/multicast */
 | |
| #define RFD_COLLISION  0x0001
 | |
| 
 | |
| /* receive frame descriptor */
 | |
| struct i596_rfd {
 | |
| 	unsigned short stat;
 | |
| 	unsigned short cmd;
 | |
| 	phys_addr pa_next;	/* va_to_pa(struct i596_rfd *next) */
 | |
| 	phys_addr pa_rbd;	/* va_to_pa(struct i596_rbd *rbd) */
 | |
| 	unsigned short count;
 | |
| 	unsigned short size;
 | |
| 	char data[1532];
 | |
| };
 | |
| 
 | |
| #define RBD_EL		0x8000
 | |
| #define RBD_P		0x4000
 | |
| #define RBD_SIZEMASK	0x3fff
 | |
| #define RBD_EOF		0x8000
 | |
| #define RBD_F		0x4000
 | |
| 
 | |
| /* receive buffer descriptor */
 | |
| struct i596_rbd {
 | |
| 	unsigned short size;
 | |
| 	unsigned short pad;
 | |
| 	phys_addr pa_next;	/* va_to_pa(struct i596_tbd *next) */
 | |
| 	phys_addr pa_data;	/* va_to_pa(char *data) */
 | |
| 	phys_addr pa_prev;	/* va_to_pa(struct i596_tbd *prev) */
 | |
| 
 | |
| 	/* Driver private part */
 | |
| 	struct sk_buff *skb;
 | |
| };
 | |
| 
 | |
| #define RX_RING_SIZE 64
 | |
| #define RX_SKBSIZE (ETH_FRAME_LEN+10)
 | |
| #define RX_RBD_SIZE 32
 | |
| 
 | |
| /* System Control Block - 40 bytes */
 | |
| struct i596_scb {
 | |
| 	u16 status;		/* 0 */
 | |
| 	u16 command;		/* 2 */
 | |
| 	phys_addr pa_cmd;	/* 4 - va_to_pa(struct i596_cmd *cmd) */
 | |
| 	phys_addr pa_rfd;	/* 8 - va_to_pa(struct i596_rfd *rfd) */
 | |
| 	u32 crc_err;		/* 12 */
 | |
| 	u32 align_err;		/* 16 */
 | |
| 	u32 resource_err;	/* 20 */
 | |
| 	u32 over_err;		/* 24 */
 | |
| 	u32 rcvdt_err;		/* 28 */
 | |
| 	u32 short_err;		/* 32 */
 | |
| 	u16 t_on;		/* 36 */
 | |
| 	u16 t_off;		/* 38 */
 | |
| };
 | |
| 
 | |
| /* Intermediate System Configuration Pointer - 8 bytes */
 | |
| struct i596_iscp {
 | |
| 	u32 busy;		/* 0 */
 | |
| 	phys_addr pa_scb;	/* 4 - va_to_pa(struct i596_scb *scb) */
 | |
| };
 | |
| 
 | |
| /* System Configuration Pointer - 12 bytes */
 | |
| struct i596_scp {
 | |
| 	u32 sysbus;		/* 0 */
 | |
| 	u32 pad;		/* 4 */
 | |
| 	phys_addr pa_iscp;	/* 8 - va_to_pa(struct i596_iscp *iscp) */
 | |
| };
 | |
| 
 | |
| /* Selftest and dump results - needs 16-byte alignment */
 | |
| /*
 | |
|  * The size of the dump area is 304 bytes. When the dump is executed
 | |
|  * by the Port command an extra word will be appended to the dump area.
 | |
|  * The extra word is a copy of the Dump status word (containing the
 | |
|  * C, B, OK bits). [I find 0xa006, with a0 for C+OK and 6 for dump]
 | |
|  */
 | |
| struct i596_dump {
 | |
| 	u16 dump[153];		/* (304 = 130h) + 2 bytes */
 | |
| };
 | |
| 
 | |
| struct i596_private {		/* aligned to a 16-byte boundary */
 | |
| 	struct i596_scp scp;	/* 0 - needs 16-byte alignment */
 | |
| 	struct i596_iscp iscp;	/* 12 */
 | |
| 	struct i596_scb scb;	/* 20 */
 | |
| 	u32 dummy;		/* 60 */
 | |
| 	struct i596_dump dump;	/* 64 - needs 16-byte alignment */
 | |
| 
 | |
| 	struct i596_cmd set_add;
 | |
| 	char eth_addr[8];	/* directly follows set_add */
 | |
| 
 | |
| 	struct i596_cmd set_conf;
 | |
| 	char i596_config[16];	/* directly follows set_conf */
 | |
| 
 | |
| 	struct i596_cmd tdr;
 | |
| 	unsigned long tdr_stat;	/* directly follows tdr */
 | |
| 
 | |
| 	int last_restart;
 | |
| 	struct i596_rbd *rbd_list;
 | |
| 	struct i596_rbd *rbd_tail;
 | |
| 	struct i596_rfd *rx_tail;
 | |
| 	struct i596_cmd *cmd_tail;
 | |
| 	struct i596_cmd *cmd_head;
 | |
| 	int cmd_backlog;
 | |
| 	unsigned long last_cmd;
 | |
| 	spinlock_t cmd_lock;
 | |
| };
 | |
| 
 | |
| static char init_setup[14] = {
 | |
| 	0x8E,	/* length 14 bytes, prefetch on */
 | |
| 	0xC8,	/* default: fifo to 8, monitor off */
 | |
| 	0x40,	/* default: don't save bad frames (apricot.c had 0x80) */
 | |
| 	0x2E,	/* (default is 0x26)
 | |
| 		   No source address insertion, 8 byte preamble */
 | |
| 	0x00,	/* default priority and backoff */
 | |
| 	0x60,	/* default interframe spacing */
 | |
| 	0x00,	/* default slot time LSB */
 | |
| 	0xf2,	/* default slot time and nr of retries */
 | |
| 	0x00,	/* default various bits
 | |
| 		   (0: promiscuous mode, 1: broadcast disable,
 | |
| 		    2: encoding mode, 3: transmit on no CRS,
 | |
| 		    4: no CRC insertion, 5: CRC type,
 | |
| 		    6: bit stuffing, 7: padding) */
 | |
| 	0x00,	/* default carrier sense and collision detect */
 | |
| 	0x40,	/* default minimum frame length */
 | |
| 	0xff,	/* (default is 0xff, and that is what apricot.c has;
 | |
| 		   elp486.c has 0xfb: Enable crc append in memory.) */
 | |
| 	0x00,	/* default: not full duplex */
 | |
| 	0x7f	/* (default is 0x3f) multi IA */
 | |
| };
 | |
| 
 | |
| static int i596_open(struct net_device *dev);
 | |
| static netdev_tx_t i596_start_xmit(struct sk_buff *skb, struct net_device *dev);
 | |
| static irqreturn_t i596_interrupt(int irq, void *dev_id);
 | |
| static int i596_close(struct net_device *dev);
 | |
| static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd);
 | |
| static void print_eth(char *);
 | |
| static void set_multicast_list(struct net_device *dev);
 | |
| static void i596_tx_timeout(struct net_device *dev);
 | |
| 
 | |
| static int
 | |
| i596_timeout(struct net_device *dev, char *msg, int ct) {
 | |
| 	struct i596_private *lp;
 | |
| 	int boguscnt = ct;
 | |
| 
 | |
| 	lp = netdev_priv(dev);
 | |
| 	while (lp->scb.command) {
 | |
| 		if (--boguscnt == 0) {
 | |
| 			printk("%s: %s timed out - stat %4.4x, cmd %4.4x\n",
 | |
| 			       dev->name, msg,
 | |
| 			       lp->scb.status, lp->scb.command);
 | |
| 			return 1;
 | |
| 		}
 | |
| 		udelay(5);
 | |
| 		barrier();
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static inline int
 | |
| init_rx_bufs(struct net_device *dev, int num) {
 | |
| 	struct i596_private *lp;
 | |
| 	struct i596_rfd *rfd;
 | |
| 	int i;
 | |
| 	// struct i596_rbd *rbd;
 | |
| 
 | |
| 	lp = netdev_priv(dev);
 | |
| 	lp->scb.pa_rfd = I596_NULL;
 | |
| 
 | |
| 	for (i = 0; i < num; i++) {
 | |
| 		rfd = kmalloc(sizeof(struct i596_rfd), GFP_KERNEL);
 | |
| 		if (rfd == NULL)
 | |
| 			break;
 | |
| 
 | |
| 		rfd->stat = 0;
 | |
| 		rfd->pa_rbd = I596_NULL;
 | |
| 		rfd->count = 0;
 | |
| 		rfd->size = 1532;
 | |
| 		if (i == 0) {
 | |
| 			rfd->cmd = CMD_EOL;
 | |
| 			lp->rx_tail = rfd;
 | |
| 		} else {
 | |
| 			rfd->cmd = 0;
 | |
| 		}
 | |
| 		rfd->pa_next = lp->scb.pa_rfd;
 | |
| 		lp->scb.pa_rfd = va_to_pa(rfd);
 | |
| 		lp->rx_tail->pa_next = lp->scb.pa_rfd;
 | |
| 	}
 | |
| 
 | |
| #if 0
 | |
| 	for (i = 0; i<RX_RBD_SIZE; i++) {
 | |
| 		rbd = kmalloc(sizeof(struct i596_rbd), GFP_KERNEL);
 | |
| 		if (rbd) {
 | |
| 			rbd->pad = 0;
 | |
| 			rbd->count = 0;
 | |
| 			rbd->skb = dev_alloc_skb(RX_SKBSIZE);
 | |
| 			if (!rbd->skb) {
 | |
| 				printk("dev_alloc_skb failed");
 | |
| 			}
 | |
| 			rbd->next = rfd->rbd;
 | |
| 			if (i) {
 | |
| 				rfd->rbd->prev = rbd;
 | |
| 				rbd->size = RX_SKBSIZE;
 | |
| 			} else {
 | |
| 				rbd->size = (RX_SKBSIZE | RBD_EL);
 | |
| 				lp->rbd_tail = rbd;
 | |
| 			}
 | |
| 
 | |
| 			rfd->rbd = rbd;
 | |
| 		} else {
 | |
| 			printk("Could not kmalloc rbd\n");
 | |
| 		}
 | |
| 	}
 | |
| 	lp->rbd_tail->next = rfd->rbd;
 | |
| #endif
 | |
| 	return (i);
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| remove_rx_bufs(struct net_device *dev) {
 | |
| 	struct i596_private *lp;
 | |
| 	struct i596_rfd *rfd;
 | |
| 
 | |
| 	lp = netdev_priv(dev);
 | |
| 	lp->rx_tail->pa_next = I596_NULL;
 | |
| 
 | |
| 	do {
 | |
| 		rfd = pa_to_va(lp->scb.pa_rfd);
 | |
| 		lp->scb.pa_rfd = rfd->pa_next;
 | |
| 		kfree(rfd);
 | |
| 	} while (rfd != lp->rx_tail);
 | |
| 
 | |
| 	lp->rx_tail = NULL;
 | |
| 
 | |
| #if 0
 | |
| 	for (lp->rbd_list) {
 | |
| 	}
 | |
| #endif
 | |
| }
 | |
| 
 | |
| #define PORT_RESET              0x00    /* reset 82596 */
 | |
| #define PORT_SELFTEST           0x01    /* selftest */
 | |
| #define PORT_ALTSCP             0x02    /* alternate SCB address */
 | |
| #define PORT_DUMP               0x03    /* dump */
 | |
| 
 | |
| #define IOADDR	0xcb0		/* real constant */
 | |
| #define IRQ	10		/* default IRQ - can be changed by ECU */
 | |
| 
 | |
| /* The 82596 requires two 16-bit write cycles for a port command */
 | |
| static inline void
 | |
| PORT(phys_addr a, unsigned int cmd) {
 | |
| 	if (a & 0xf)
 | |
| 		printk("lp486e.c: PORT: address not aligned\n");
 | |
| 	outw(((a & 0xffff) | cmd), IOADDR);
 | |
| 	outw(((a>>16) & 0xffff), IOADDR+2);
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| CA(void) {
 | |
| 	outb(0, IOADDR+4);
 | |
| 	udelay(8);
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| CLEAR_INT(void) {
 | |
| 	outb(0, IOADDR+8);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| /* selftest or dump */
 | |
| static void
 | |
| i596_port_do(struct net_device *dev, int portcmd, char *cmdname) {
 | |
| 	struct i596_private *lp = netdev_priv(dev);
 | |
| 	u16 *outp;
 | |
| 	int i, m;
 | |
| 
 | |
| 	memset((void *)&(lp->dump), 0, sizeof(struct i596_dump));
 | |
| 	outp = &(lp->dump.dump[0]);
 | |
| 
 | |
| 	PORT(va_to_pa(outp), portcmd);
 | |
| 	mdelay(30);             /* random, unmotivated */
 | |
| 
 | |
| 	printk("lp486e i82596 %s result:\n", cmdname);
 | |
| 	for (m = ARRAY_SIZE(lp->dump.dump); m && lp->dump.dump[m-1] == 0; m--)
 | |
| 		;
 | |
| 	for (i = 0; i < m; i++) {
 | |
| 		printk(" %04x", lp->dump.dump[i]);
 | |
| 		if (i%8 == 7)
 | |
| 			printk("\n");
 | |
| 	}
 | |
| 	printk("\n");
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| i596_scp_setup(struct net_device *dev) {
 | |
| 	struct i596_private *lp = netdev_priv(dev);
 | |
| 	int boguscnt;
 | |
| 
 | |
| 	/* Setup SCP, ISCP, SCB */
 | |
| 	/*
 | |
| 	 * sysbus bits:
 | |
| 	 *  only a single byte is significant - here 0x44
 | |
| 	 *  0x80: big endian mode (details depend on stepping)
 | |
| 	 *  0x40: 1
 | |
| 	 *  0x20: interrupt pin is active low
 | |
| 	 *  0x10: lock function disabled
 | |
| 	 *  0x08: external triggering of bus throttle timers
 | |
| 	 *  0x06: 00: 82586 compat mode, 01: segmented mode, 10: linear mode
 | |
| 	 *  0x01: unused
 | |
| 	 */
 | |
| 	lp->scp.sysbus = 0x00440000; 		/* linear mode */
 | |
| 	lp->scp.pad = 0;			/* must be zero */
 | |
| 	lp->scp.pa_iscp = va_to_pa(&(lp->iscp));
 | |
| 
 | |
| 	/*
 | |
| 	 * The CPU sets the ISCP to 1 before it gives the first CA()
 | |
| 	 */
 | |
| 	lp->iscp.busy = 0x0001;
 | |
| 	lp->iscp.pa_scb = va_to_pa(&(lp->scb));
 | |
| 
 | |
| 	lp->scb.command = 0;
 | |
| 	lp->scb.status = 0;
 | |
| 	lp->scb.pa_cmd = I596_NULL;
 | |
| 	/* lp->scb.pa_rfd has been initialised already */
 | |
| 
 | |
| 	lp->last_cmd = jiffies;
 | |
| 	lp->cmd_backlog = 0;
 | |
| 	lp->cmd_head = NULL;
 | |
| 
 | |
| 	/*
 | |
| 	 * Reset the 82596.
 | |
| 	 * We need to wait 10 systemclock cycles, and
 | |
| 	 * 5 serial clock cycles.
 | |
| 	 */
 | |
| 	PORT(0, PORT_RESET);	/* address part ignored */
 | |
| 	udelay(100);
 | |
| 
 | |
| 	/*
 | |
| 	 * Before the CA signal is asserted, the default SCP address
 | |
| 	 * (0x00fffff4) can be changed to a 16-byte aligned value
 | |
| 	 */
 | |
| 	PORT(va_to_pa(&lp->scp), PORT_ALTSCP);	/* change the scp address */
 | |
| 
 | |
| 	/*
 | |
| 	 * The initialization procedure begins when a
 | |
| 	 * Channel Attention signal is asserted after a reset.
 | |
| 	 */
 | |
| 
 | |
| 	CA();
 | |
| 
 | |
| 	/*
 | |
| 	 * The ISCP busy is cleared by the 82596 after the SCB address is read.
 | |
| 	 */
 | |
| 	boguscnt = 100;
 | |
| 	while (lp->iscp.busy) {
 | |
| 		if (--boguscnt == 0) {
 | |
| 			/* No i82596 present? */
 | |
| 			printk("%s: i82596 initialization timed out\n",
 | |
| 			       dev->name);
 | |
| 			return 1;
 | |
| 		}
 | |
| 		udelay(5);
 | |
| 		barrier();
 | |
| 	}
 | |
| 	/* I find here boguscnt==100, so no delay was required. */
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| init_i596(struct net_device *dev) {
 | |
| 	struct i596_private *lp;
 | |
| 
 | |
| 	if (i596_scp_setup(dev))
 | |
| 		return 1;
 | |
| 
 | |
| 	lp = netdev_priv(dev);
 | |
| 	lp->scb.command = 0;
 | |
| 
 | |
| 	memcpy ((void *)lp->i596_config, init_setup, 14);
 | |
| 	lp->set_conf.command = CmdConfigure;
 | |
| 	i596_add_cmd(dev, (void *)&lp->set_conf);
 | |
| 
 | |
| 	memcpy ((void *)lp->eth_addr, dev->dev_addr, 6);
 | |
| 	lp->set_add.command = CmdIASetup;
 | |
| 	i596_add_cmd(dev, (struct i596_cmd *)&lp->set_add);
 | |
| 
 | |
| 	lp->tdr.command = CmdTDR;
 | |
| 	i596_add_cmd(dev, (struct i596_cmd *)&lp->tdr);
 | |
| 
 | |
| 	if (lp->scb.command && i596_timeout(dev, "i82596 init", 200))
 | |
| 		return 1;
 | |
| 
 | |
| 	lp->scb.command = RX_START;
 | |
| 	CA();
 | |
| 
 | |
| 	barrier();
 | |
| 
 | |
| 	if (lp->scb.command && i596_timeout(dev, "Receive Unit start", 100))
 | |
| 		return 1;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Receive a single frame */
 | |
| static inline int
 | |
| i596_rx_one(struct net_device *dev, struct i596_private *lp,
 | |
| 	    struct i596_rfd *rfd, int *frames) {
 | |
| 
 | |
| 	if (rfd->stat & RFD_STAT_OK) {
 | |
| 		/* a good frame */
 | |
| 		int pkt_len = (rfd->count & 0x3fff);
 | |
| 		struct sk_buff *skb = dev_alloc_skb(pkt_len);
 | |
| 
 | |
| 		(*frames)++;
 | |
| 
 | |
| 		if (rfd->cmd & CMD_EOL)
 | |
| 			printk("Received on EOL\n");
 | |
| 
 | |
| 		if (skb == NULL) {
 | |
| 			printk ("%s: i596_rx Memory squeeze, "
 | |
| 				"dropping packet.\n", dev->name);
 | |
| 			dev->stats.rx_dropped++;
 | |
| 			return 1;
 | |
| 		}
 | |
| 
 | |
| 		memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len);
 | |
| 
 | |
| 		skb->protocol = eth_type_trans(skb,dev);
 | |
| 		netif_rx(skb);
 | |
| 		dev->stats.rx_packets++;
 | |
| 	} else {
 | |
| #if 0
 | |
| 		printk("Frame reception error status %04x\n",
 | |
| 		       rfd->stat);
 | |
| #endif
 | |
| 		dev->stats.rx_errors++;
 | |
| 		if (rfd->stat & RFD_COLLISION)
 | |
| 			dev->stats.collisions++;
 | |
| 		if (rfd->stat & RFD_SHORT_FRAME_ERR)
 | |
| 			dev->stats.rx_length_errors++;
 | |
| 		if (rfd->stat & RFD_DMA_ERR)
 | |
| 			dev->stats.rx_over_errors++;
 | |
| 		if (rfd->stat & RFD_NOBUFS_ERR)
 | |
| 			dev->stats.rx_fifo_errors++;
 | |
| 		if (rfd->stat & RFD_ALIGN_ERR)
 | |
| 			dev->stats.rx_frame_errors++;
 | |
| 		if (rfd->stat & RFD_CRC_ERR)
 | |
| 			dev->stats.rx_crc_errors++;
 | |
| 		if (rfd->stat & RFD_LENGTH_ERR)
 | |
| 			dev->stats.rx_length_errors++;
 | |
| 	}
 | |
| 	rfd->stat = rfd->count = 0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| i596_rx(struct net_device *dev) {
 | |
| 	struct i596_private *lp = netdev_priv(dev);
 | |
| 	struct i596_rfd *rfd;
 | |
| 	int frames = 0;
 | |
| 
 | |
| 	while (1) {
 | |
| 		rfd = pa_to_va(lp->scb.pa_rfd);
 | |
| 		if (!rfd) {
 | |
| 			printk(KERN_ERR "i596_rx: NULL rfd?\n");
 | |
| 			return 0;
 | |
| 		}
 | |
| #if 1
 | |
| 		if (rfd->stat && !(rfd->stat & (RFD_STAT_C | RFD_STAT_B)))
 | |
| 			printk("SF:%p-%04x\n", rfd, rfd->stat);
 | |
| #endif
 | |
| 		if (!(rfd->stat & RFD_STAT_C))
 | |
| 			break;		/* next one not ready */
 | |
| 		if (i596_rx_one(dev, lp, rfd, &frames))
 | |
| 			break;		/* out of memory */
 | |
| 		rfd->cmd = CMD_EOL;
 | |
| 		lp->rx_tail->cmd = 0;
 | |
| 		lp->rx_tail = rfd;
 | |
| 		lp->scb.pa_rfd = rfd->pa_next;
 | |
| 		barrier();
 | |
| 	}
 | |
| 
 | |
| 	return frames;
 | |
| }
 | |
| 
 | |
| static void
 | |
| i596_cleanup_cmd(struct net_device *dev) {
 | |
| 	struct i596_private *lp;
 | |
| 	struct i596_cmd *cmd;
 | |
| 
 | |
| 	lp = netdev_priv(dev);
 | |
| 	while (lp->cmd_head) {
 | |
| 		cmd = (struct i596_cmd *)lp->cmd_head;
 | |
| 
 | |
| 		lp->cmd_head = pa_to_va(lp->cmd_head->pa_next);
 | |
| 		lp->cmd_backlog--;
 | |
| 
 | |
| 		switch ((cmd->command) & 0x7) {
 | |
| 			case CmdTx: {
 | |
| 				struct tx_cmd *tx_cmd = (struct tx_cmd *) cmd;
 | |
| 				struct i596_tbd * tx_cmd_tbd;
 | |
| 				tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd);
 | |
| 
 | |
| 				dev_kfree_skb_any(tx_cmd_tbd->skb);
 | |
| 
 | |
| 				dev->stats.tx_errors++;
 | |
| 				dev->stats.tx_aborted_errors++;
 | |
| 
 | |
| 				cmd->pa_next = I596_NULL;
 | |
| 				kfree((unsigned char *)tx_cmd);
 | |
| 				netif_wake_queue(dev);
 | |
| 				break;
 | |
| 			}
 | |
| 			case CmdMulticastList: {
 | |
| 				// unsigned short count = *((unsigned short *) (ptr + 1));
 | |
| 
 | |
| 				cmd->pa_next = I596_NULL;
 | |
| 				kfree((unsigned char *)cmd);
 | |
| 				break;
 | |
| 			}
 | |
| 			default: {
 | |
| 				cmd->pa_next = I596_NULL;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		barrier();
 | |
| 	}
 | |
| 
 | |
| 	if (lp->scb.command && i596_timeout(dev, "i596_cleanup_cmd", 100))
 | |
| 		;
 | |
| 
 | |
| 	lp->scb.pa_cmd = va_to_pa(lp->cmd_head);
 | |
| }
 | |
| 
 | |
| static void i596_reset(struct net_device *dev, struct i596_private *lp, int ioaddr) {
 | |
| 
 | |
| 	if (lp->scb.command && i596_timeout(dev, "i596_reset", 100))
 | |
| 		;
 | |
| 
 | |
| 	netif_stop_queue(dev);
 | |
| 
 | |
| 	lp->scb.command = CUC_ABORT | RX_ABORT;
 | |
| 	CA();
 | |
| 	barrier();
 | |
| 
 | |
| 	/* wait for shutdown */
 | |
| 	if (lp->scb.command && i596_timeout(dev, "i596_reset(2)", 400))
 | |
| 		;
 | |
| 
 | |
| 	i596_cleanup_cmd(dev);
 | |
| 	i596_rx(dev);
 | |
| 
 | |
| 	netif_start_queue(dev);
 | |
| 	/*dev_kfree_skb(skb, FREE_WRITE);*/
 | |
| 	init_i596(dev);
 | |
| }
 | |
| 
 | |
| static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) {
 | |
| 	struct i596_private *lp = netdev_priv(dev);
 | |
| 	int ioaddr = dev->base_addr;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	cmd->status = 0;
 | |
| 	cmd->command |= (CMD_EOL | CMD_INTR);
 | |
| 	cmd->pa_next = I596_NULL;
 | |
| 
 | |
| 	spin_lock_irqsave(&lp->cmd_lock, flags);
 | |
| 
 | |
| 	if (lp->cmd_head) {
 | |
| 		lp->cmd_tail->pa_next = va_to_pa(cmd);
 | |
| 	} else {
 | |
| 		lp->cmd_head = cmd;
 | |
| 		if (lp->scb.command && i596_timeout(dev, "i596_add_cmd", 100))
 | |
| 			;
 | |
| 		lp->scb.pa_cmd = va_to_pa(cmd);
 | |
| 		lp->scb.command = CUC_START;
 | |
| 		CA();
 | |
| 	}
 | |
| 	lp->cmd_tail = cmd;
 | |
| 	lp->cmd_backlog++;
 | |
| 
 | |
| 	lp->cmd_head = pa_to_va(lp->scb.pa_cmd);
 | |
| 	spin_unlock_irqrestore(&lp->cmd_lock, flags);
 | |
| 
 | |
| 	if (lp->cmd_backlog > 16) {
 | |
| 		int tickssofar = jiffies - lp->last_cmd;
 | |
| 		if (tickssofar < HZ/4)
 | |
| 			return;
 | |
| 
 | |
| 		printk(KERN_WARNING "%s: command unit timed out, status resetting.\n", dev->name);
 | |
| 		i596_reset(dev, lp, ioaddr);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int i596_open(struct net_device *dev)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	i = request_irq(dev->irq, i596_interrupt, IRQF_SHARED, dev->name, dev);
 | |
| 	if (i) {
 | |
| 		printk(KERN_ERR "%s: IRQ %d not free\n", dev->name, dev->irq);
 | |
| 		return i;
 | |
| 	}
 | |
| 
 | |
| 	if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
 | |
| 		printk(KERN_ERR "%s: only able to allocate %d receive buffers\n", dev->name, i);
 | |
| 
 | |
| 	if (i < 4) {
 | |
| 		free_irq(dev->irq, dev);
 | |
| 		return -EAGAIN;
 | |
| 	}
 | |
| 	netif_start_queue(dev);
 | |
| 	init_i596(dev);
 | |
| 	return 0;			/* Always succeed */
 | |
| }
 | |
| 
 | |
| static netdev_tx_t i596_start_xmit (struct sk_buff *skb, struct net_device *dev) {
 | |
| 	struct tx_cmd *tx_cmd;
 | |
| 	short length;
 | |
| 
 | |
| 	length = skb->len;
 | |
| 
 | |
| 	if (length < ETH_ZLEN) {
 | |
| 		if (skb_padto(skb, ETH_ZLEN))
 | |
| 			return NETDEV_TX_OK;
 | |
| 		length = ETH_ZLEN;
 | |
| 	}
 | |
| 
 | |
| 	dev->trans_start = jiffies;
 | |
| 
 | |
| 	tx_cmd = kmalloc((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC);
 | |
| 	if (tx_cmd == NULL) {
 | |
| 		printk(KERN_WARNING "%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name);
 | |
| 		dev->stats.tx_dropped++;
 | |
| 		dev_kfree_skb (skb);
 | |
| 	} else {
 | |
| 		struct i596_tbd *tx_cmd_tbd;
 | |
| 		tx_cmd_tbd = (struct i596_tbd *) (tx_cmd + 1);
 | |
| 		tx_cmd->pa_tbd = va_to_pa (tx_cmd_tbd);
 | |
| 		tx_cmd_tbd->pa_next = 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->pa_data = va_to_pa (skb->data);
 | |
| 		tx_cmd_tbd->skb = skb;
 | |
| 
 | |
| 		if (i596_debug & LOG_SRCDST)
 | |
| 			print_eth (skb->data);
 | |
| 
 | |
| 		i596_add_cmd (dev, (struct i596_cmd *) tx_cmd);
 | |
| 
 | |
| 		dev->stats.tx_packets++;
 | |
| 	}
 | |
| 
 | |
| 	return NETDEV_TX_OK;
 | |
| }
 | |
| 
 | |
| static void
 | |
| i596_tx_timeout (struct net_device *dev) {
 | |
| 	struct i596_private *lp = netdev_priv(dev);
 | |
| 	int ioaddr = dev->base_addr;
 | |
| 
 | |
| 	/* Transmitter timeout, serious problems. */
 | |
| 	printk(KERN_WARNING "%s: transmit timed out, status resetting.\n", dev->name);
 | |
| 	dev->stats.tx_errors++;
 | |
| 
 | |
| 	/* Try to restart the adaptor */
 | |
| 	if (lp->last_restart == dev->stats.tx_packets) {
 | |
| 		printk ("Resetting board.\n");
 | |
| 
 | |
| 		/* Shutdown and restart */
 | |
| 		i596_reset (dev, lp, ioaddr);
 | |
| 	} else {
 | |
| 		/* Issue a channel attention signal */
 | |
| 		printk ("Kicking board.\n");
 | |
| 		lp->scb.command = (CUC_START | RX_START);
 | |
| 		CA();
 | |
| 		lp->last_restart = dev->stats.tx_packets;
 | |
| 	}
 | |
| 	netif_wake_queue(dev);
 | |
| }
 | |
| 
 | |
| static void print_eth(char *add)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	printk ("Dest  ");
 | |
| 	for (i = 0; i < 6; i++)
 | |
| 		printk(" %2.2X", (unsigned char) add[i]);
 | |
| 	printk ("\n");
 | |
| 
 | |
| 	printk ("Source");
 | |
| 	for (i = 0; i < 6; i++)
 | |
| 		printk(" %2.2X", (unsigned char) add[i+6]);
 | |
| 	printk ("\n");
 | |
| 
 | |
| 	printk ("type %2.2X%2.2X\n",
 | |
| 		(unsigned char) add[12], (unsigned char) add[13]);
 | |
| }
 | |
| 
 | |
| static const struct net_device_ops i596_netdev_ops = {
 | |
| 	.ndo_open		= i596_open,
 | |
| 	.ndo_stop		= i596_close,
 | |
| 	.ndo_start_xmit		= i596_start_xmit,
 | |
| 	.ndo_set_multicast_list = set_multicast_list,
 | |
| 	.ndo_tx_timeout		= i596_tx_timeout,
 | |
| 	.ndo_change_mtu		= eth_change_mtu,
 | |
| 	.ndo_set_mac_address 	= eth_mac_addr,
 | |
| 	.ndo_validate_addr	= eth_validate_addr,
 | |
| };
 | |
| 
 | |
| static int __init lp486e_probe(struct net_device *dev) {
 | |
| 	struct i596_private *lp;
 | |
| 	unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 };
 | |
| 	unsigned char *bios;
 | |
| 	int i, j;
 | |
| 	int ret = -ENOMEM;
 | |
| 	static int probed;
 | |
| 
 | |
| 	if (probed)
 | |
| 		return -ENODEV;
 | |
| 	probed++;
 | |
| 
 | |
| 	if (!request_region(IOADDR, LP486E_TOTAL_SIZE, DRV_NAME)) {
 | |
| 		printk(KERN_ERR "lp486e: IO address 0x%x in use\n", IOADDR);
 | |
| 		return -EBUSY;
 | |
| 	}
 | |
| 
 | |
| 	lp = netdev_priv(dev);
 | |
| 	spin_lock_init(&lp->cmd_lock);
 | |
| 
 | |
| 	/*
 | |
| 	 * Do we really have this thing?
 | |
| 	 */
 | |
| 	if (i596_scp_setup(dev)) {
 | |
| 		ret = -ENODEV;
 | |
| 		goto err_out_kfree;
 | |
| 	}
 | |
| 
 | |
| 	dev->base_addr = IOADDR;
 | |
| 	dev->irq = IRQ;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * How do we find the ethernet address? I don't know.
 | |
| 	 * One possibility is to look at the EISA configuration area
 | |
| 	 * [0xe8000-0xe9fff]. This contains the ethernet address
 | |
| 	 * but not at a fixed address - things depend on setup options.
 | |
| 	 *
 | |
| 	 * If we find no address, or the wrong address, use
 | |
| 	 *   ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6
 | |
| 	 * with the value found in the BIOS setup.
 | |
| 	 */
 | |
| 	bios = bus_to_virt(0xe8000);
 | |
| 	for (j = 0; j < 0x2000; j++) {
 | |
| 		if (bios[j] == 0 && bios[j+1] == 0xaa && bios[j+2] == 0) {
 | |
| 			printk("%s: maybe address at BIOS 0x%x:",
 | |
| 			       dev->name, 0xe8000+j);
 | |
| 			for (i = 0; i < 6; i++) {
 | |
| 				eth_addr[i] = bios[i+j];
 | |
| 				printk(" %2.2X", eth_addr[i]);
 | |
| 			}
 | |
| 			printk("\n");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	printk("%s: lp486e 82596 at %#3lx, IRQ %d,",
 | |
| 	       dev->name, dev->base_addr, dev->irq);
 | |
| 	for (i = 0; i < 6; i++)
 | |
| 		printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]);
 | |
| 	printk("\n");
 | |
| 
 | |
| 	/* The LP486E-specific entries in the device structure. */
 | |
| 	dev->netdev_ops = &i596_netdev_ops;
 | |
| 	dev->watchdog_timeo = 5*HZ;
 | |
| 
 | |
| #if 0
 | |
| 	/* selftest reports 0x320925ae - don't know what that means */
 | |
| 	i596_port_do(dev, PORT_SELFTEST, "selftest");
 | |
| 	i596_port_do(dev, PORT_DUMP, "dump");
 | |
| #endif
 | |
| 	return 0;
 | |
| 
 | |
| err_out_kfree:
 | |
| 	release_region(IOADDR, LP486E_TOTAL_SIZE);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| i596_handle_CU_completion(struct net_device *dev,
 | |
| 			  struct i596_private *lp,
 | |
| 			  unsigned short status,
 | |
| 			  unsigned short *ack_cmdp) {
 | |
| 	struct i596_cmd *cmd;
 | |
| 	int frames_out = 0;
 | |
| 	int commands_done = 0;
 | |
| 	int cmd_val;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	spin_lock_irqsave(&lp->cmd_lock, flags);
 | |
| 	cmd = lp->cmd_head;
 | |
| 
 | |
| 	while (lp->cmd_head && (lp->cmd_head->status & CMD_STAT_C)) {
 | |
| 		cmd = lp->cmd_head;
 | |
| 
 | |
| 		lp->cmd_head = pa_to_va(lp->cmd_head->pa_next);
 | |
| 		lp->cmd_backlog--;
 | |
| 
 | |
| 		commands_done++;
 | |
| 		cmd_val = cmd->command & 0x7;
 | |
| #if 0
 | |
| 		printk("finished CU %s command (%d)\n",
 | |
| 		       CUcmdnames[cmd_val], cmd_val);
 | |
| #endif
 | |
| 		switch (cmd_val) {
 | |
| 		case CmdTx:
 | |
| 		{
 | |
| 			struct tx_cmd *tx_cmd;
 | |
| 			struct i596_tbd *tx_cmd_tbd;
 | |
| 
 | |
| 			tx_cmd = (struct tx_cmd *) cmd;
 | |
| 			tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd);
 | |
| 
 | |
| 			frames_out++;
 | |
| 			if (cmd->status & CMD_STAT_OK) {
 | |
| 				if (i596_debug)
 | |
| 					print_eth(pa_to_va(tx_cmd_tbd->pa_data));
 | |
| 			} else {
 | |
| 				dev->stats.tx_errors++;
 | |
| 				if (i596_debug)
 | |
| 					printk("transmission failure:%04x\n",
 | |
| 					       cmd->status);
 | |
| 				if (cmd->status & 0x0020)
 | |
| 					dev->stats.collisions++;
 | |
| 				if (!(cmd->status & 0x0040))
 | |
| 					dev->stats.tx_heartbeat_errors++;
 | |
| 				if (cmd->status & 0x0400)
 | |
| 					dev->stats.tx_carrier_errors++;
 | |
| 				if (cmd->status & 0x0800)
 | |
| 					dev->stats.collisions++;
 | |
| 				if (cmd->status & 0x1000)
 | |
| 					dev->stats.tx_aborted_errors++;
 | |
| 			}
 | |
| 			dev_kfree_skb_irq(tx_cmd_tbd->skb);
 | |
| 
 | |
| 			cmd->pa_next = I596_NULL;
 | |
| 			kfree((unsigned char *)tx_cmd);
 | |
| 			netif_wake_queue(dev);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		case CmdMulticastList:
 | |
| 			cmd->pa_next = I596_NULL;
 | |
| 			kfree((unsigned char *)cmd);
 | |
| 			break;
 | |
| 
 | |
| 		case CmdTDR:
 | |
| 		{
 | |
| 			unsigned long status = *((unsigned long *) (cmd + 1));
 | |
| 			if (status & 0x8000) {
 | |
| 				if (i596_debug)
 | |
| 					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:
 | |
| 			cmd->pa_next = I596_NULL;
 | |
| 			lp->last_cmd = jiffies;
 | |
| 
 | |
| 		}
 | |
| 		barrier();
 | |
| 	}
 | |
| 
 | |
| 	cmd = lp->cmd_head;
 | |
| 	while (cmd && (cmd != lp->cmd_tail)) {
 | |
| 		cmd->command &= 0x1fff;
 | |
| 		cmd = pa_to_va(cmd->pa_next);
 | |
| 		barrier();
 | |
| 	}
 | |
| 
 | |
| 	if (lp->cmd_head)
 | |
| 		*ack_cmdp |= CUC_START;
 | |
| 	lp->scb.pa_cmd = va_to_pa(lp->cmd_head);
 | |
| 	spin_unlock_irqrestore(&lp->cmd_lock, flags);
 | |
| }
 | |
| 
 | |
| static irqreturn_t
 | |
| i596_interrupt(int irq, void *dev_instance)
 | |
| {
 | |
| 	struct net_device *dev = dev_instance;
 | |
| 	struct i596_private *lp = netdev_priv(dev);
 | |
| 	unsigned short status, ack_cmd = 0;
 | |
| 	int frames_in = 0;
 | |
| 
 | |
| 	/*
 | |
| 	 * The 82596 examines the command, performs the required action,
 | |
| 	 * and then clears the SCB command word.
 | |
| 	 */
 | |
| 	if (lp->scb.command && i596_timeout(dev, "interrupt", 40))
 | |
| 		;
 | |
| 
 | |
| 	/*
 | |
| 	 * The status word indicates the status of the 82596.
 | |
| 	 * It is modified only by the 82596.
 | |
| 	 *
 | |
| 	 * [So, we must not clear it. I find often status 0xffff,
 | |
| 	 *  which is not one of the values allowed by the docs.]
 | |
| 	 */
 | |
| 	status = lp->scb.status;
 | |
| #if 0
 | |
| 	if (i596_debug) {
 | |
| 		printk("%s: i596 interrupt, ", dev->name);
 | |
| 		i596_out_status(status);
 | |
| 	}
 | |
| #endif
 | |
| 	/* Impossible, but it happens - perhaps when we get
 | |
| 	   a receive interrupt but scb.pa_rfd is I596_NULL. */
 | |
| 	if (status == 0xffff) {
 | |
| 		printk("%s: i596_interrupt: got status 0xffff\n", dev->name);
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	ack_cmd = (status & STAT_ACK);
 | |
| 
 | |
| 	if (status & (STAT_CX | STAT_CNA))
 | |
| 		i596_handle_CU_completion(dev, lp, status, &ack_cmd);
 | |
| 
 | |
| 	if (status & (STAT_FR | STAT_RNR)) {
 | |
| 		/* Restart the receive unit when it got inactive somehow */
 | |
| 		if ((status & STAT_RNR) && netif_running(dev))
 | |
| 			ack_cmd |= RX_START;
 | |
| 
 | |
| 		if (status & STAT_FR) {
 | |
| 			frames_in = i596_rx(dev);
 | |
| 			if (!frames_in)
 | |
| 				printk("receive frame reported, but no frames\n");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* acknowledge the interrupt */
 | |
| 	/*
 | |
| 	if ((lp->scb.pa_cmd != I596_NULL) && netif_running(dev))
 | |
| 		ack_cmd |= CUC_START;
 | |
| 	*/
 | |
| 
 | |
| 	if (lp->scb.command && i596_timeout(dev, "i596 interrupt", 100))
 | |
| 		;
 | |
| 
 | |
| 	lp->scb.command = ack_cmd;
 | |
| 
 | |
| 	CLEAR_INT();
 | |
| 	CA();
 | |
| 
 | |
|  out:
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| 
 | |
| static int i596_close(struct net_device *dev) {
 | |
| 	struct i596_private *lp = netdev_priv(dev);
 | |
| 
 | |
| 	netif_stop_queue(dev);
 | |
| 
 | |
| 	if (i596_debug)
 | |
| 		printk("%s: Shutting down ethercard, status was %4.4x.\n",
 | |
| 		       dev->name, lp->scb.status);
 | |
| 
 | |
| 	lp->scb.command = (CUC_ABORT | RX_ABORT);
 | |
| 	CA();
 | |
| 
 | |
| 	i596_cleanup_cmd(dev);
 | |
| 
 | |
| 	if (lp->scb.command && i596_timeout(dev, "i596_close", 200))
 | |
| 		;
 | |
| 
 | |
| 	free_irq(dev->irq, dev);
 | |
| 	remove_rx_bufs(dev);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
| *	Set or clear the multicast filter for this adaptor.
 | |
| */
 | |
| 
 | |
| static void set_multicast_list(struct net_device *dev) {
 | |
| 	struct i596_private *lp = netdev_priv(dev);
 | |
| 	struct i596_cmd *cmd;
 | |
| 
 | |
| 	if (i596_debug > 1)
 | |
| 		printk ("%s: set multicast list %d\n",
 | |
| 			dev->name, netdev_mc_count(dev));
 | |
| 
 | |
| 	if (!netdev_mc_empty(dev)) {
 | |
| 		struct dev_mc_list *dmi;
 | |
| 		char *cp;
 | |
| 		cmd = kmalloc(sizeof(struct i596_cmd) + 2 +
 | |
| 			      netdev_mc_count(dev) * 6, GFP_ATOMIC);
 | |
| 		if (cmd == NULL) {
 | |
| 			printk (KERN_ERR "%s: set_multicast Memory squeeze.\n", dev->name);
 | |
| 			return;
 | |
| 		}
 | |
| 		cmd->command = CmdMulticastList;
 | |
| 		*((unsigned short *) (cmd + 1)) = netdev_mc_count(dev) * 6;
 | |
| 		cp = ((char *)(cmd + 1))+2;
 | |
| 		netdev_for_each_mc_addr(dmi, dev) {
 | |
| 			memcpy(cp, dmi->dmi_addr, 6);
 | |
| 			cp += 6;
 | |
| 		}
 | |
| 		if (i596_debug & LOG_SRCDST)
 | |
| 			print_eth (((char *)(cmd + 1)) + 2);
 | |
| 		i596_add_cmd(dev, cmd);
 | |
| 	} else {
 | |
| 		if (lp->set_conf.pa_next != I596_NULL) {
 | |
| 			return;
 | |
| 		}
 | |
| 		if (netdev_mc_empty(dev) &&
 | |
| 		    !(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) {
 | |
| 			lp->i596_config[8] &= ~0x01;
 | |
| 		} else {
 | |
| 			lp->i596_config[8] |= 0x01;
 | |
| 		}
 | |
| 
 | |
| 		i596_add_cmd(dev, (struct i596_cmd *) &lp->set_conf);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| MODULE_AUTHOR("Ard van Breemen <ard@cstmel.nl.eu.org>");
 | |
| MODULE_DESCRIPTION("Intel Panther onboard i82596 driver");
 | |
| MODULE_LICENSE("GPL");
 | |
| 
 | |
| static struct net_device *dev_lp486e;
 | |
| static int full_duplex;
 | |
| static int options;
 | |
| static int io = IOADDR;
 | |
| static int irq = IRQ;
 | |
| 
 | |
| module_param(debug, int, 0);
 | |
| //module_param(max_interrupt_work, int, 0);
 | |
| //module_param(reverse_probe, int, 0);
 | |
| //module_param(rx_copybreak, int, 0);
 | |
| module_param(options, int, 0);
 | |
| module_param(full_duplex, int, 0);
 | |
| 
 | |
| static int __init lp486e_init_module(void) {
 | |
| 	int err;
 | |
| 	struct net_device *dev = alloc_etherdev(sizeof(struct i596_private));
 | |
| 	if (!dev)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	dev->irq = irq;
 | |
| 	dev->base_addr = io;
 | |
| 	err = lp486e_probe(dev);
 | |
| 	if (err) {
 | |
| 		free_netdev(dev);
 | |
| 		return err;
 | |
| 	}
 | |
| 	err = register_netdev(dev);
 | |
| 	if (err) {
 | |
| 		release_region(dev->base_addr, LP486E_TOTAL_SIZE);
 | |
| 		free_netdev(dev);
 | |
| 		return err;
 | |
| 	}
 | |
| 	dev_lp486e = dev;
 | |
| 	full_duplex = 0;
 | |
| 	options = 0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void __exit lp486e_cleanup_module(void) {
 | |
| 	unregister_netdev(dev_lp486e);
 | |
| 	release_region(dev_lp486e->base_addr, LP486E_TOTAL_SIZE);
 | |
| 	free_netdev(dev_lp486e);
 | |
| }
 | |
| 
 | |
| module_init(lp486e_init_module);
 | |
| module_exit(lp486e_cleanup_module);
 |