patch-2.0.21-2.1.0 linux/drivers/net/eexpress.c
Next file: linux/drivers/net/eexpress.h
Previous file: linux/drivers/net/dgrs_driver.c
Back to the patch index
Back to the overall index
- Lines: 1599
- Date:
Sat Sep 28 22:06:12 1996
- Orig file:
lx2.0/v2.0.21/linux/drivers/net/eexpress.c
- Orig date:
Fri Jun 7 14:21:04 1996
diff -u --recursive --new-file lx2.0/v2.0.21/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
@@ -1,93 +1,72 @@
-/* $Id: eexpress.c,v 1.13 1996/05/19 15:59:51 phil Exp $
+/* Intel EtherExpress 16 device driver for Linux
*
- * Intel EtherExpress device driver for Linux
+ * Written by John Sullivan, 1995
+ * based on original code by Donald Becker, with changes by
+ * Alan Cox and Pauline Middelink.
*
- * 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>
+ * Many modifications, and currently maintained, by
+ * Philip Blundell <Philip.Blundell@pobox.com>
*/
-/*
- * 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.
+/* The EtherExpress 16 is a fairly simple card, based on a shared-memory
+ * design using the i82586 Ethernet coprocessor. It bears no relationship,
+ * as far as I know, to the similarly-named "EtherExpress Pro" range.
*
- * 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.
+ * Historically, Linux support for these cards has been very bad. However,
+ * things seem to be getting better slowly.
+ */
+
+/* It would be nice to seperate out all the 82586-specific code, so that it
+ * could be shared between drivers (as with 8390.c). But this would be quite
+ * a messy job. The main motivation for doing this would be to bring 3c507
+ * support back up to scratch.
+ */
+
+/* If your card is confused about what sort of interface it has (eg it
+ * persistently reports "10baseT" when none is fitted), running 'SOFTSET /BART'
+ * or 'SOFTSET /LISA' from DOS seems to help.
+ */
+
+/* Here's the scoop on memory mapping.
*
- * 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.
+ * There are three ways to access EtherExpress card memory: either using the
+ * shared-memory mapping, or using PIO through the dataport, or using PIO
+ * through the "shadow memory" ports.
*
- * 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).
+ * The shadow memory system works by having the card map some of its memory
+ * as follows:
*
- * John Sullivan
+ * (the low five bits of the SMPTR are ignored)
+ *
+ * base+0x4000..400f memory at SMPTR+0..15
+ * base+0x8000..800f memory at SMPTR+16..31
+ * base+0xc000..c007 dubious stuff (memory at SMPTR+16..23 apparently)
+ * base+0xc008..c00f memory at 0x0008..0x000f
*
- * 18/5/95:
+ * This last set (the one at c008) is particularly handy because the SCB
+ * lives at 0x0008. So that set of ports gives us easy random access to data
+ * in the SCB without having to mess around setting up pointers and the like.
+ * We always use this method to access the SCB (via the scb_xx() functions).
*
- * 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.
+ * Dataport access works by aiming the appropriate (read or write) pointer
+ * at the first address you're interested in, and then reading or writing from
+ * the dataport. The pointers auto-increment after each transfer. We use
+ * this for data transfer.
*
- * This bug makes switching in and out of promiscuous mode a risky
- * business, since we must do a 586 reset each time.
+ * We don't use the shared-memory system because it allegedly doesn't work on
+ * all cards, and because it's a bit more prone to go wrong (it's one more
+ * thing to configure...).
*/
-/*
- * 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.
+/* Known bugs:
*
+ * - 8-bit mode is not supported, and makes things go wrong.
+ * - Multicast and promiscuous modes are not supported.
+ * - The card seems to want to give us two interrupts every time something
+ * happens, where just one would be better.
+ * - The statistics may not be getting reported properly.
*/
-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";
-
#include <linux/module.h>
#include <linux/kernel.h>
@@ -102,7 +81,6 @@
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
-#include <asm/dma.h>
#include <linux/delay.h>
#include <linux/errno.h>
@@ -111,31 +89,12 @@
#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
+#define NET_DEBUG 4
#endif
-static unsigned int net_debug = NET_DEBUG;
-
-#undef F_DEB
-#include "eth82586.h"
+#include "eexpress.h"
-#define PRIV(x) ((struct net_local *)(x)->priv)
#define EEXP_IO_EXTENT 16
/*
@@ -145,14 +104,15 @@
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 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;
@@ -161,45 +121,64 @@
unsigned short num_rx_bufs;
};
-unsigned short start_code[] = {
- 0x0000, /* SCP: set bus to 16 bits */
- 0x0000,0x0000, /* junk */
+/* This is the code and data that is downloaded to the EtherExpress card's
+ * memory at boot time.
+ */
+
+static unsigned short start_code[] = {
+/* 0xfff6 */
+ 0x0000, /* set bus to 16 bits */
+ 0x0000,0x0000,
0x0000,0x0000, /* address of ISCP (lo,hi) */
+/* 0x0000 */
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, /* 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 */
+/* 0x0020 -- start of 82586 CU program */
+#define CONF_LINK 0x0020
+ 0x0000,Cmd_Config,
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 */
+ 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,0x0000,0x0000, /* hardware address placed here */
+
+ 0x0000,Cmd_TDR,0x0048,
+/* 0x0044 -- TDR result placed here */
+ 0x0000, 0x0000,
+
+/* Eventually, a set-multicast will go in here */
+
0x0000,Cmd_END|Cmd_Nop, /* end of configure sequence */
- 0x003e,
+ 0x0048,
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 };
@@ -207,32 +186,79 @@
* 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);
+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 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);
+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 void eexp_hw_rx_pio(struct device *dev);
+static void eexp_hw_tx_pio(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_txrestart(struct device *dev);
+
+static void eexp_hw_txinit (struct device *dev);
+static void eexp_hw_rxinit (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);
+
+/*
+ * Primitive hardware access functions.
+ */
+
+static inline unsigned short scb_status(struct device *dev)
+{
+ return inw(dev->base_addr + 0xc008);
+}
+
+static inline unsigned short scb_rdcmd(struct device *dev)
+{
+ return inw(dev->base_addr + 0xc00a);
+}
+
+static inline void scb_command(struct device *dev, unsigned short cmd)
+{
+ outw(cmd, dev->base_addr + 0xc00a);
+}
+
+static inline void scb_wrcbl(struct device *dev, unsigned short val)
+{
+ outw(val, dev->base_addr + 0xc00c);
+}
+
+static inline void scb_wrrfa(struct device *dev, unsigned short val)
+{
+ outw(val, dev->base_addr + 0xc00e);
+}
+
+static inline void set_loopback(struct device *dev)
+{
+ outb(inb(dev->base_addr + Config) | 2, dev->base_addr + Config);
+}
+
+static inline void clear_loopback(struct device *dev)
+{
+ outb(inb(dev->base_addr + Config) & ~2, dev->base_addr + Config);
+}
-static void eexp_hw_init586 (struct device *dev);
-static void eexp_hw_ASICrst (struct device *dev);
+static inline short int SHADOW(short int addr)
+{
+ addr &= 0x1f;
+ if (addr > 0xf) addr += 0x3ff0;
+ return addr + 0x4000;
+}
/*
* Linux interface
@@ -252,7 +278,7 @@
else if (ioaddr)
return ENXIO;
- for ( port=&ports[0] ; *port ; port++ )
+ for (port=&ports[0] ; *port ; port++ )
{
unsigned short sum = 0;
int i;
@@ -285,14 +311,14 @@
return -ENXIO;
if (irq2dev_map[irq] ||
- /* more consistent, surely? */
((irq2dev_map[irq]=dev),0) ||
- request_irq(irq,&eexp_irq,0,"eexpress",NULL))
+ request_irq(irq,&eexp_irq,0,"EtherExpress",NULL))
return -EAGAIN;
- request_region(ioaddr, EEXP_IO_EXTENT, "eexpress");
+ request_region(ioaddr, EEXP_IO_EXTENT, "EtherExpress");
dev->tbusy = 0;
dev->interrupt = 0;
+
eexp_hw_init586(dev);
dev->start = 1;
MOD_INC_USE_COUNT;
@@ -303,25 +329,27 @@
}
/*
- * close and disable the interface, leaving
- * the 586 in reset
+ * close and disable the interface, leaving the 586 in reset.
*/
static int eexp_close(struct device *dev)
{
unsigned short ioaddr = dev->base_addr;
+ struct net_local *lp = dev->priv;
+
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);
+ lp->started = 0;
+ scb_command(dev, SCB_CUsuspend|SCB_RUsuspend);
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;
}
@@ -334,109 +362,117 @@
{
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.
+/*
+ * This gets called when a higher level thinks we are broken. Check that
+ * nothing has become jammed in the CU.
*/
-static int eexp_xmit(struct sk_buff *buf, struct device *dev)
+static void unstick_cu(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)
+ if (lp->started)
{
- /* 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)>50)
{
- if ((jiffies - dev->trans_start)>5)
+ if (lp->tx_link==lp->last_tx_restart)
{
- 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, scb_status(dev));
+ eexp_hw_txinit(dev);
+ lp->last_tx_restart = 0;
+ scb_wrcbl(dev, lp->tx_link);
+ scb_command(dev, SCB_CUstart);
+ outb(0,ioaddr+SIGNAL_CA);
+ while (!SCB_complete(rsst=scb_status(dev)))
{
- 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)
{
- 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);
- }
+ boguscount=200;
+ printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n",
+ dev->name,rsst);
+ scb_wrcbl(dev, lp->tx_link);
+ scb_command(dev, SCB_CUstart);
+ outb(0,ioaddr+SIGNAL_CA);
}
- dev->tbusy = 0;
- mark_bh(NET_BH);
+ }
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ else
+ {
+ unsigned short status = scb_status(dev);
+ 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 status = inw(ioaddr+SCB_STATUS);
- if (SCB_CUdead(status))
+ unsigned short txstatus = eexp_hw_lasttxstat(dev);
+ if (dev->tbusy && !txstatus)
{
- 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);
+ 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
{
- 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);
- }
+ printk(KERN_WARNING "%s: transmit timed out\n", dev->name);
}
}
}
}
- else
+ }
+ else
+ {
+ if ((jiffies-lp->init_time)>10)
{
- 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);
- }
+ unsigned short status = scb_status(dev);
+ 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);
}
}
+}
+
+/*
+ * 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;
+
+#if NET_DEBUG > 6
+ printk(KERN_DEBUG "%s: eexp_xmit()\n", dev->name);
+#endif
+
+ outb(SIRQ_dis|irqrmap[dev->irq],dev->base_addr+SET_IRQ);
+
+ /* If dev->tbusy is set, all our tx buffers are full but the kernel
+ * is calling us anyway. Check that nothing bad is happening.
+ */
+ if (dev->tbusy)
+ unstick_cu(dev);
if (buf==NULL)
{
- unsigned short status = inw(ioaddr+SCB_STATUS);
+ /* Some higher layer thinks we might have missed a
+ * tx-done interrupt. Does this ever actually happen?
+ */
+ unsigned short status = scb_status(dev);
unsigned short txstatus = eexp_hw_lasttxstat(dev);
if (SCB_CUdead(status))
{
@@ -446,7 +482,7 @@
eexp_hw_txrestart(dev);
}
dev_tint(dev);
- outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
+ outb(SIRQ_en|irqrmap[dev->irq],dev->base_addr+SET_IRQ);
return 0;
}
@@ -456,15 +492,14 @@
}
else
{
- unsigned short length = (ETH_ZLEN < buf->len) ? buf->len : ETH_ZLEN;
+ 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);
+ eexp_hw_tx_pio(dev,data,length);
}
dev_kfree_skb(buf, FREE_WRITE);
- outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
+ outb(SIRQ_en|irqrmap[dev->irq],dev->base_addr+SET_IRQ);
return 0;
}
@@ -480,72 +515,92 @@
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);
+ printk(KERN_WARNING "eexpress: irq %d for unknown device\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);
+ outb(SIRQ_dis|irqrmap[irq],ioaddr+SET_IRQ);
+
+ dev->interrupt = 1;
+
+ status = scb_status(dev);
+
+#if NET_DEBUG > 4
+ printk(KERN_DEBUG "%s: interrupt (status %x)\n", dev->name, status);
+#endif
+
ack_cmd = SCB_ack(status);
- if (PRIV(dev)->started==0 && SCB_complete(status))
+ if (lp->started==0 && SCB_complete(status))
{
+ while (SCB_CUstat(status)==2)
+ status = scb_status(dev);
#if NET_DEBUG > 4
- printk(KERN_DEBUG "%s: SCBcomplete event received\n", dev->name);
+ printk(KERN_DEBUG "%s: CU went non-active (status = %08x)\n",
+ dev->name, status);
#endif
- while (SCB_CUstat(status)==2)
- status = inw_p(ioaddr+SCB_STATUS);
+
+ /* now get the TDR status */
+ {
+ short tdr_status;
+ outw(0x40, dev->base_addr + SM_PTR);
+ tdr_status = inw(dev->base_addr + 0x8004);
+ if (tdr_status & TDR_SHORT) {
+ printk(KERN_WARNING "%s: TDR reports cable short at %d tick%s\n", dev->name, tdr_status & TDR_TIME, ((tdr_status & TDR_TIME) != 1) ? "s" : "");
+ }
+ else if (tdr_status & TDR_OPEN) {
+ printk(KERN_WARNING "%s: TDR reports cable broken at %d tick%s\n", dev->name, tdr_status & TDR_TIME, ((tdr_status & TDR_TIME) != 1) ? "s" : "");
+ }
+ else if (tdr_status & TDR_XCVRPROBLEM) {
+ printk(KERN_WARNING "%s: TDR reports transceiver problem\n", dev->name);
+ }
#if NET_DEBUG > 4
- printk(KERN_DEBUG "%s: CU went non-active (status = %08x)\n", dev->name, status);
+ else if (tdr_status & TDR_LINKOK) {
+ printk(KERN_DEBUG "%s: TDR reports link OK\n", dev->name);
+ }
#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;
+ }
+
+ lp->started=1;
+ scb_wrcbl(dev, lp->tx_link);
+ scb_wrrfa(dev, lp->rx_buf_start);
+ ack_cmd |= SCB_CUstart | SCB_RUstart | 0x2000;
}
- else if (PRIV(dev)->started)
+ else if (lp->started)
{
unsigned short txstatus;
txstatus = eexp_hw_lasttxstat(dev);
}
-
+
if (SCB_rxdframe(status))
{
- eexp_hw_rx(dev);
+ eexp_hw_rx_pio(dev);
}
- if ((PRIV(dev)->started&2)!=0 && SCB_RUstat(status)!=4)
+ if ((lp->started&2)!=0 && SCB_RUstat(status)!=4)
{
- printk(KERN_WARNING "%s: RU stopped status %04x, restarting...\n",
+ printk(KERN_WARNING "%s: RU stopped: status %04x\n",
dev->name,status);
lp->stats.rx_errors++;
eexp_hw_rxinit(dev);
- outw(PRIV(dev)->rx_buf_start,ioaddr+SCB_RFA);
+ scb_wrrfa(dev, lp->rx_buf_start);
ack_cmd |= SCB_RUstart;
}
- else if (PRIV(dev)->started==1 && SCB_RUstat(status)==4)
- PRIV(dev)->started|=2;
+ else if (lp->started==1 && SCB_RUstat(status)==4)
+ lp->started|=2;
- outw(ack_cmd,ioaddr+SCB_CMD);
+ scb_command(dev, ack_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);
+
+ 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);
@@ -560,42 +615,44 @@
/*
* Check all the receive buffers, and hand any received packets
* to the upper levels. Basic sanity check on each frame
- * descriptor
+ * descriptor, though we don't bother trying to fix broken ones.
*/
-static void eexp_hw_rx(struct device *dev)
+static void eexp_hw_rx_pio(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;
+ unsigned short ioaddr = dev->base_addr;
#if NET_DEBUG > 6
printk(KERN_DEBUG "%s: eexp_hw_rx()\n", dev->name);
#endif
- while (outw(rx_block,ioaddr+READ_PTR),boguscount--)
+ while (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;
+ unsigned short status, rfd_cmd, rx_next, pbuf, pkt_len;
+
+ outw(rx_block, ioaddr + READ_PTR);
+ status = inw(ioaddr + DATAPORT);
+ rfd_cmd = inw(ioaddr + DATAPORT);
+ rx_next = inw(ioaddr + DATAPORT);
+ pbuf = inw(ioaddr + DATAPORT);
if (FD_Done(status))
{
- outw(pbuf,ioaddr+READ_PTR);
- pkt_len = inw(ioaddr);
+ outw(pbuf, ioaddr + READ_PTR);
+ pkt_len = inw(ioaddr + DATAPORT);
if (rfd_cmd!=0x0000 || pbuf!=rx_block+0x16
|| (pkt_len & 0xc000)!=0xc000)
{
+ /* This should never happen. If it does,
+ * we almost certainly have a driver bug.
+ */
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))
@@ -625,50 +682,54 @@
}
skb->dev = dev;
skb_reserve(skb, 2);
- outw(pbuf+10,ioaddr+READ_PTR);
- insw(ioaddr,skb_put(skb,pkt_len),(pkt_len+1)>>1);
+ outw(pbuf+10, ioaddr+READ_PTR);
+ 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++;
}
- outw(rx_block,ioaddr+WRITE_PTR);
- outw(0x0000,ioaddr);
- outw(0x0000,ioaddr);
+ outw(rx_block, ioaddr+WRITE_PTR);
+ outw(0, ioaddr+DATAPORT);
+ outw(0, ioaddr+DATAPORT);
}
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
+ * buffer region.
*/
-static void eexp_hw_tx(struct device *dev, unsigned short *buf, unsigned short len)
+static void eexp_hw_tx_pio(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);
+ outw(lp->tx_head, ioaddr + WRITE_PTR);
+
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT);
+ outw(lp->tx_head+0x08, ioaddr + DATAPORT);
+ outw(lp->tx_head+0x0e, ioaddr + DATAPORT);
+
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(lp->tx_head+0x08, ioaddr + DATAPORT);
+
+ outw(0x8000|len, ioaddr + DATAPORT);
+ outw(-1, ioaddr + DATAPORT);
+ outw(lp->tx_head+0x16, ioaddr + DATAPORT);
+ outw(0, ioaddr + DATAPORT);
+
+ outsw(ioaddr + DATAPORT, buf, (len+1)>>1);
+
+ outw(lp->tx_tail+0xc, ioaddr + WRITE_PTR);
+ outw(lp->tx_head, ioaddr + DATAPORT);
+
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))
@@ -677,24 +738,31 @@
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!)
+ * Read hardware address, reset card, size memory and initialize buffer
+ * memory pointers. These are held in dev->priv, in case someone has more
+ * than one card in a machine.
*/
static int eexp_hw_probe(struct device *dev, unsigned short ioaddr)
{
unsigned short hw_addr[3];
+ unsigned int memory_size;
+ static char *ifmap[]={"AUI", "BNC", "RJ45"};
+ enum iftype {AUI=0, BNC=1, TP=2};
int i;
- unsigned char *chw_addr = (unsigned char *)hw_addr;
+ unsigned short xsum = 0;
+ struct net_local *lp;
+
+ printk("%s: EtherExpress 16 at %#x",dev->name,ioaddr);
- printk("%s: EtherExpress at %#x, ",dev->name,ioaddr);
+ outb(ASIC_RST, ioaddr+EEPROM_Ctrl);
+ outb(0, ioaddr+EEPROM_Ctrl);
+ udelay(500);
+ outb(i586_RST, ioaddr+EEPROM_Ctrl);
hw_addr[0] = eexp_hw_readeeprom(ioaddr,2);
hw_addr[1] = eexp_hw_readeeprom(ioaddr,3);
@@ -702,90 +770,86 @@
if (hw_addr[2]!=0x00aa || ((hw_addr[1] & 0xff00)!=0x0000))
{
- printk("rejected: invalid address %04x%04x%04x\n",
+ printk(" rejected: invalid address %04x%04x%04x\n",
hw_addr[2],hw_addr[1],hw_addr[0]);
return ENODEV;
}
+ /* Calculate the EEPROM checksum. Carry on anyway if it's bad,
+ * though.
+ */
+ for (i = 0; i < 64; i++)
+ xsum += eexp_hw_readeeprom(ioaddr, i);
+ if (xsum != 0xbaba)
+ printk(" (bad EEPROM xsum 0x%02x)", xsum);
+
dev->base_addr = ioaddr;
for ( i=0 ; i<6 ; i++ )
- dev->dev_addr[i] = chw_addr[5-i];
+ dev->dev_addr[i] = ((unsigned char *)hw_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];
+ /* Use the IRQ from EEPROM if none was given */
+ if (!dev->irq)
+ 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);
+ dev->priv = lp = kmalloc(sizeof(struct net_local), GFP_KERNEL);
if (!dev->priv)
- return -ENOMEM;
+ return ENOMEM;
memset(dev->priv, 0, sizeof(struct net_local));
+
+ printk("; using IRQ %d, %s connector", dev->irq,ifmap[dev->if_port]);
- eexp_hw_ASICrst(dev);
+ /* Find out how much RAM we have on the card */
+ outw(0, dev->base_addr + WRITE_PTR);
+ for (i = 0; i < 32768; i++)
+ outw(0, dev->base_addr + DATAPORT);
+
+ for (memory_size = 0; memory_size < 64; memory_size++)
+ {
+ outw(memory_size<<10, dev->base_addr + WRITE_PTR);
+ outw(memory_size<<10, dev->base_addr + READ_PTR);
+ if (inw(dev->base_addr+DATAPORT))
+ break;
+ outw(memory_size | 0x5000, dev->base_addr+DATAPORT);
+ outw(memory_size<<10, dev->base_addr + READ_PTR);
+ if (inw(dev->base_addr+DATAPORT) != (memory_size | 0x5000))
+ break;
+ }
- {
- 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);
+ /* Sort out the number of buffers. We may have 16, 32, 48 or 64k
+ * of RAM to play with.
+ */
+ lp->num_tx_bufs = 4;
+ lp->rx_buf_end = 0x3ff6;
+ switch (memory_size)
+ {
+ case 64:
+ lp->rx_buf_end += 0x4000;
+ case 48:
+ lp->num_tx_bufs += 4;
+ lp->rx_buf_end += 0x4000;
+ case 32:
+ lp->rx_buf_end += 0x4000;
+ case 16:
+ printk(", %dk RAM.\n", memory_size);
+ break;
+ default:
+ printk("; bad memory size (%dk).\n", memory_size);
+ kfree(dev->priv);
+ return ENODEV;
+ break;
+ }
- 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;
- }
+ lp->rx_buf_start = TX_BUF_START + (lp->num_tx_bufs*TX_BUF_SIZE);
- 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(version);
dev->open = eexp_open;
dev->stop = eexp_close;
dev->hard_start_xmit = eexp_xmit;
@@ -796,16 +860,18 @@
}
/*
- * Read a word from eeprom location (0-63?)
+ * Read a word from the EtherExpress on-board serial EEPROM.
+ * The EEPROM contains 64 words of 16 bits.
*/
-static unsigned short eexp_hw_readeeprom(unsigned short ioaddr, unsigned char location)
+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 )
+ for (i=0x100 ; i ; i>>=1 )
{
if (cmd&i)
wval |= EC_Wr;
@@ -820,7 +886,7 @@
}
wval &= ~EC_Wr;
outb(wval,ioaddr+EEPROM_Ctrl);
- for ( i=0x8000 ; i ; i>>=1 )
+ for (i=0x8000 ; i ; i>>=1 )
{
outb(wval|EC_Clk,ioaddr+EEPROM_Ctrl);
eeprom_delay();
@@ -850,24 +916,19 @@
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)
+ if ((!dev->tbusy) && lp->tx_head==lp->tx_reap)
return 0x0000;
do
{
- outw(tx_block,ioaddr+READ_PTR);
- status = inw(ioaddr);
+ outw(tx_block, dev->base_addr + SM_PTR);
+ status = inw(SHADOW(tx_block));
if (!Stat_Done(status))
{
lp->tx_link = tx_block;
- outw(old_rp,ioaddr+READ_PTR);
- outw(old_wp,ioaddr+WRITE_PTR);
return status;
}
else
@@ -896,17 +957,14 @@
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.
+ * This should never happen. It is called when some higher routine detects
+ * that 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)
@@ -915,24 +973,21 @@
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);
+ scb_wrcbl(dev, lp->tx_link);
+ scb_command(dev, SCB_CUstart);
outb(0,ioaddr+SIGNAL_CA);
{
unsigned short boguscount=50,failcount=5;
- while (!inw(ioaddr+SCB_STATUS))
+ while (!scb_status(dev))
{
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);
+ printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n", dev->name, scb_status(dev), scb_rdcmd(dev));
+ scb_wrcbl(dev, lp->tx_link);
+ scb_command(dev, SCB_CUstart);
outb(0,ioaddr+SIGNAL_CA);
boguscount = 100;
}
@@ -950,40 +1005,39 @@
}
/*
- * 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!)
+ * Writes down the list of transmit buffers into card memory. Each
+ * entry consists of an 82586 transmit command, followed by a jump
+ * pointing to itself. When we want to transmit a packet, we write
+ * the data into the appropriate transmit buffer and then modify the
+ * preceding jump to point at the new transmit command. This means that
+ * the 586 command unit is continuously active.
*/
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;
+ unsigned short ioaddr = dev->base_addr;
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);
+ outw(tx_block, ioaddr + WRITE_PTR);
+
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(Cmd_INT|Cmd_Xmit, ioaddr + DATAPORT);
+ outw(tx_block+0x08, ioaddr + DATAPORT);
+ outw(tx_block+0x0e, ioaddr + DATAPORT);
+
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(0x0000, ioaddr + DATAPORT);
+ outw(tx_block+0x08, ioaddr + DATAPORT);
+
+ outw(0x8000, ioaddr + DATAPORT);
+ outw(-1, ioaddr + DATAPORT);
+ outw(tx_block+0x16, ioaddr + DATAPORT);
+ outw(0x0000, ioaddr + DATAPORT);
+
tx_block += TX_BUF_SIZE;
}
lp->tx_head = TX_BUF_START;
@@ -991,116 +1045,123 @@
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.
+ * Write the circular list of receive buffer descriptors to card memory.
+ * The end of the list isn't marked, which means that the 82586 receive
+ * unit will loop until buffers become available (this avoids it giving us
+ * "out of resources" messages).
*/
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;
+ unsigned short ioaddr = dev->base_addr;
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);
+
+ outw(rx_block, ioaddr + WRITE_PTR);
+
+ outw(0, ioaddr + DATAPORT); outw(0, ioaddr+DATAPORT);
+ outw(rx_block + RX_BUF_SIZE, ioaddr+DATAPORT);
+ outw(rx_block + 0x16, ioaddr+DATAPORT);
+
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+ outw(0xdead, ioaddr+DATAPORT);
+
+ outw(0x8000, ioaddr+DATAPORT);
+ outw(0xffff, ioaddr+DATAPORT);
+ outw(rx_block + 0x20, ioaddr+DATAPORT);
+ outw(0, ioaddr+DATAPORT);
+ outw(0x8000 | (RX_BUF_SIZE-0x20), ioaddr+DATAPORT);
+
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(lp->rx_last + 4, ioaddr+WRITE_PTR);
+ outw(lp->rx_first, ioaddr+DATAPORT);
- 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
+ * Un-reset the 586, 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;
+ int i;
#if NET_DEBUG > 6
- printk("%s: eexp_hw_init586()\n", dev->name);
+ 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));
- }
- }
- }
+ set_loopback(dev);
- outw_p(lp->rx_buf_end,ioaddr+WRITE_PTR);
- start_code[28] = (dev->flags & IFF_PROMISC)?(start_code[28] | 1):(start_code[28] & ~1);
+ /* Bash the startup code a bit */
+ 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);
+ memcpy(&start_code[33], &dev->dev_addr[0], 6);
+
+ outb(SIRQ_dis|irqrmap[dev->irq],ioaddr+SET_IRQ);
+
+ /* Download the startup code */
+ outw(lp->rx_buf_end & ~31, ioaddr + SM_PTR);
+ outw(start_code[0], ioaddr + 0x8006);
+ outw(start_code[1], ioaddr + 0x8008);
+ outw(start_code[2], ioaddr + 0x800a);
+ outw(start_code[3], ioaddr + 0x800c);
+ outw(start_code[4], ioaddr + 0x800e);
+ for (i = 10; i < (sizeof(start_code)); i+=32) {
+ int j;
+ outw(i-10, ioaddr + SM_PTR);
+ for (j = 0; j < 16; j+=2)
+ outw(start_code[(i+j)/2],
+ ioaddr+0x4000+j);
+ for (j = 0; j < 16; j+=2)
+ outw(start_code[(i+j+16)/2],
+ ioaddr+0x8000+j);
+ }
+
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);
+ udelay(5000);
+
+ scb_command(dev, 0xf000);
outb(0,ioaddr+SIGNAL_CA);
+
+ outw(0, ioaddr+SM_PTR);
+
{
unsigned short rboguscount=50,rfailcount=5;
- while (outw(0,ioaddr+READ_PTR),inw(ioaddr))
+ while (inw(ioaddr+0x4000))
{
if (!--rboguscount)
{
printk(KERN_WARNING "%s: i82586 reset timed out, kicking...\n",
dev->name);
- outw(0,ioaddr+SCB_CMD);
+ scb_command(dev, 0);
outb(0,ioaddr+SIGNAL_CA);
rboguscount = 100;
if (!--rfailcount)
@@ -1113,23 +1174,22 @@
}
}
- outw(CONF_LINK,ioaddr+SCB_CBL);
- outw(0,ioaddr+SCB_STATUS);
- outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD);
+ scb_wrcbl(dev, CONF_LINK);
+ scb_command(dev, 0xf000|SCB_CUstart);
outb(0,ioaddr+SIGNAL_CA);
+
{
unsigned short iboguscount=50,ifailcount=5;
- while (!inw(ioaddr+SCB_STATUS))
+ while (!scb_status(dev))
{
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);
+ dev->name, scb_status(dev), scb_rdcmd(dev));
+ scb_wrcbl(dev, CONF_LINK);
+ scb_command(dev, 0xf000|SCB_CUstart);
outb(0,ioaddr+SIGNAL_CA);
iboguscount = 100;
}
@@ -1142,8 +1202,9 @@
}
}
- outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
- clear_loopback;
+ clear_loopback(dev);
+ outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
+
lp->init_time = jiffies;
#if NET_DEBUG > 6
printk("%s: leaving eexp_hw_init586()\n", dev->name);
@@ -1151,57 +1212,13 @@
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);
}
@@ -1221,8 +1238,8 @@
0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, express_probe },
};
-int irq[EEXP_MAX_CARDS] = {0, };
-int io[EEXP_MAX_CARDS] = {0, };
+static int irq[EEXP_MAX_CARDS] = {0, };
+static 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
@@ -1271,6 +1288,5 @@
* 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:
*/
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov