[4463] in linux-net channel archive
EtherExpress
daemon@ATHENA.MIT.EDU (Philip Blundell)
Wed Sep 18 23:02:14 1996
To: Matthew.Wilcox@sandoz.com, daz@sanger.ac.uk, linux-net@vger.rutgers.edu
Date: Wed, 18 Sep 1996 22:19:55 +0100
From: Philip Blundell <Philip.Blundell@pobox.com>
This patch (against a clean 2.0.20) represents my continuing efforts to clean
up the EtherExpress 16 driver. If you have this card, please try it out and
tell me what happens.
For this to work, you _must_ have your card set up to use shared memory. PIO
access is not supported at the moment; if you can't use shared memory for
some reason, don't apply this patch. PIO support will come back at some
point.
Daz: this patch adds a simple memory test to check that the detected memory
region is right. I'm hoping this will turn out to be the problem on your 486.
Matthew: I forget what problems you were having exactly. You'll have to
remind me.
The patch is quite big, because there are a lot of trivial (formatting etc)
changes. Sorry about that.
phil
--
--- clean-linux/linux/drivers/net/eexpress.c Wed Jun 12 23:50:28 1996
+++ linux/drivers/net/eexpress.c Wed Sep 18 22:07:04 1996
@@ -1,92 +1,30 @@
/* $Id: eexpress.c,v 1.13 1996/05/19 15:59:51 phil Exp $
*
- * Intel EtherExpress device driver for Linux
+ * Intel EtherExpress 16 device driver for Linux
*
- * Original version written 1993 by Donald Becker
- * Modularized by Pauline Middelink <middelin@polyware.iaf.nl>
- * Changed to support io= irq= by Alan Cox <Alan.Cox@linux.org>
- * Reworked 1995 by John Sullivan <js10039@cam.ac.uk>
- * More fixes by Philip Blundell <pjb27@cam.ac.uk>
+ * Written by John Sullivan, 1995
+ * based on original code by Donald Becker, with changes by
+ * Alan Cox and Pauline Middelink.
+ *
+ * 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.
- *
- * It's still not quite there, but self-corrects a lot more problems.
- * the 'CU wedged, resetting...' message shouldn't happen at all, but
- * at least we recover. It still locks occasionally, any ideas welcome.
- *
- * The original startup delay experienced by some people was due to the
- * first ARP request for the address of the default router getting lost.
- * (mostly the reply we were getting back was arriving before our
- * hardware address was set up, or before the configuration sequence
- * had told the card NOT to strip of the frame header). If you a long
- * startup delay, you may have lost this ARP request/reply, although
- * the original cause has been fixed. However, it is more likely that
- * you've just locked under this version.
- *
- * The main changes are in the 586 initialization procedure (which was
- * just broken before - the EExp is a strange beasty and needs careful
- * handling) the receive buffer handling (we now use a non-terminating
- * circular list of buffers, which stops the card giving us out-of-
- * resources errors), and the transmit code. The driver is also more
- * structured, and I have tried to keep the kernel interface separate
- * from the hardware interface (although some routines naturally want
- * to do both).
- *
- * John Sullivan
- *
- * 18/5/95:
- *
- * The lock-ups seem to happen when you access card memory after a 586
- * reset. This happens only 1 in 12 resets, on a random basis, and
- * completely locks the machine. As far as I can see there is no
- * workaround possible - the only thing to be done is make sure we
- * never reset the card *after* booting the kernel - once at probe time
- * must be sufficient, and we'll just have to put up with that failing
- * occasionally (or buy a new NIC). By the way, this looks like a
- * definite card bug, since Intel's own driver for DOS does exactly the
- * same.
- *
- * This bug makes switching in and out of promiscuous mode a risky
- * business, since we must do a 586 reset each time.
+/* 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. On
+ * the other hand, it apparently _is_ quite similar to 3Com's 3c507.
*/
-/*
- * 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.
- *
+/* Historically, Linux support for these cards has been very bad. However,
+ * things seem to be getting better slowly.
*/
-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";
+/* This version of the driver relies on shared memory, which I'm told doesn't
+ * work on all cards. But when it _does_ work, it seems to be a lot better
+ * than the old code. PIO mode will have to come back for those cards that
+ * can't do/aren't configured for shared memory, though.
+ */
#include <linux/module.h>
@@ -111,31 +49,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
#endif
-static unsigned int net_debug = NET_DEBUG;
-
-#undef F_DEB
#include "eth82586.h"
-#define PRIV(x) ((struct net_local *)(x)->priv)
#define EEXP_IO_EXTENT 16
/*
@@ -145,61 +64,83 @@
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;
unsigned short rx_buf_end;
unsigned short num_tx_bufs;
unsigned short num_rx_bufs;
+ volatile struct scb *scb;
};
+/* This is the code and data that is downloaded to the EtherExpress
+ * shared memory at boot time.
+ */
+
unsigned short start_code[] = {
- 0x0000, /* SCP: set bus to 16 bits */
- 0x0000,0x0000, /* junk */
+/* 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 */
+#define CONF_TDRdata 0x44
+ 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 +148,33 @@
* 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(struct device *dev);
+static void eexp_hw_tx(struct device *dev, unsigned short *buf,
+ unsigned short len);
+static int eexp_hw_probe(struct device *dev,unsigned short ioaddr);
+static unsigned short eexp_hw_readeeprom(unsigned short ioaddr,
+ unsigned char location);
static unsigned short eexp_hw_lasttxstat(struct device *dev);
-static void eexp_hw_txrestart (struct device *dev);
+static void eexp_hw_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);
-static void eexp_hw_ASICrst (struct device *dev);
+static void eexp_hw_init586 (struct device *dev);
/*
* Linux interface
@@ -285,14 +227,16 @@
return -ENXIO;
if (irq2dev_map[irq] ||
- /* more consistent, surely? */
+ /* more consistent, surely? */
+ /* broken, surely? --pjb */
((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 +247,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;
+ lp->scb->cmd = 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,12 +280,8 @@
{
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)
- */
+ /* !!! ought to update these from the card's stats
+ -- pjb */
return &lp->stats;
}
@@ -347,7 +289,6 @@
* 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;
@@ -364,31 +305,32 @@
* tbusy==0. If it happens too much, we probably ought
* to think about unwedging ourselves...
*/
- if (test_bit(0,(void *)&PRIV(dev)->started))
+
+ /* !!! This is probably broken
+ -- pjb */
+ if (lp->started)
{
- if ((jiffies - dev->trans_start)>5)
+ if ((jiffies - dev->trans_start)>50)
{
if (lp->tx_link==lp->last_tx_restart)
{
unsigned short boguscount=200,rsst;
printk(KERN_WARNING "%s: Retransmit timed out, status %04x,
resetting...\n",
- dev->name,inw(ioaddr+SCB_STATUS));
+ dev->name,lp->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);
+ lp->scb->cbl = lp->tx_link;
+ lp->scb->cmd = SCB_CUstart;
outb(0,ioaddr+SIGNAL_CA);
- while (!SCB_complete(rsst=inw(ioaddr+SCB_STATUS)))
+ while (!SCB_complete(rsst=lp->scb->status))
{
if (!--boguscount)
{
boguscount=200;
printk(KERN_WARNING "%s: Reset timed out status %04x, retrying...\n",
dev->name,rsst);
- outw(lp->tx_link,ioaddr+SCB_CBL);
- outw(0,ioaddr+SCB_STATUS);
- outw(SCB_CUstart,ioaddr+SCB_CMD);
+ lp->scb->cbl = lp->tx_link;
+ lp->scb->cmd = SCB_CUstart;
outb(0,ioaddr+SIGNAL_CA);
}
}
@@ -397,7 +339,7 @@
}
else
{
- unsigned short status = inw(ioaddr+SCB_STATUS);
+ unsigned short status = lp->scb->status;
if (SCB_CUdead(status))
{
unsigned short txstatus = eexp_hw_lasttxstat(dev);
@@ -424,7 +366,7 @@
{
if ((jiffies-lp->init_time)>10)
{
- unsigned short status = inw(ioaddr+SCB_STATUS);
+ unsigned short status = lp->scb->status;
printk(KERN_WARNING "%s: i82586 startup timed out, status %04x,
resetting...\n",
dev->name, status);
eexp_hw_init586(dev);
@@ -436,7 +378,7 @@
if (buf==NULL)
{
- unsigned short status = inw(ioaddr+SCB_STATUS);
+ unsigned short status = lp->scb->status;
unsigned short txstatus = eexp_hw_lasttxstat(dev);
if (SCB_CUdead(status))
{
@@ -480,72 +422,90 @@
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);
+
+ dev->interrupt = 1;
+
+ status = lp->scb->status;
+
+#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 = lp->scb->status;
#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 = readw(dev->mem_start+CONF_TDRdata);
+ 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;
+ lp->scb->cbl = lp->tx_link;
+ lp->scb->rfa = 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);
}
- 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);
+ lp->scb->rfa = 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);
+ lp->scb->cmd = 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);
@@ -566,9 +526,6 @@
static void eexp_hw_rx(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- unsigned short ioaddr = dev->base_addr;
- unsigned short old_wp = inw(ioaddr+WRITE_PTR);
- unsigned short old_rp = inw(ioaddr+READ_PTR);
unsigned short rx_block = lp->rx_first;
unsigned short boguscount = lp->num_rx_bufs;
@@ -576,26 +533,31 @@
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);
+ volatile struct rfd_header *rfd =
+ (struct rfd_header *)(rx_block + dev->mem_start);
+ unsigned short status = rfd->flags & 0xffff;
+ unsigned short rfd_cmd = rfd->flags >> 16;
+ unsigned short rx_next = rfd->link;
+ unsigned short pbuf = rfd->rbd_offset;
unsigned short pkt_len;
+ unsigned short size;
if (FD_Done(status))
{
- outw(pbuf,ioaddr+READ_PTR);
- pkt_len = inw(ioaddr);
+ pkt_len = readw(dev->mem_start + pbuf);
+ size = readw(dev->mem_start + pbuf + 4);
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++;
+ "next %04x, pbuf %04x, len %04x, size %04x\n",dev->name,rx_block,
+ status,rfd_cmd,rx_next,pbuf,pkt_len,size);
continue;
}
else if (!FD_OK(status))
@@ -625,20 +587,16 @@
}
skb->dev = dev;
skb_reserve(skb, 2);
- outw(pbuf+10,ioaddr+READ_PTR);
- insw(ioaddr,skb_put(skb,pkt_len),(pkt_len+1)>>1);
+ memcpy_fromio(skb_put(skb,pkt_len),(void *)(dev->
mem_start+pbuf+10),(pkt_len+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);
+ writew(0x0000,dev->mem_start+rx_block);
+ writew(0x0000,dev->mem_start+rx_block+2);
}
rx_block = rx_next;
}
- outw(old_rp,ioaddr+READ_PTR);
- outw(old_wp,ioaddr+WRITE_PTR);
}
/*
@@ -648,27 +606,28 @@
* buffer region
*/
-static void eexp_hw_tx(struct device *dev, unsigned short *buf, unsigned
short len)
+static void eexp_hw_tx(struct device *dev, unsigned short *buf,
+ unsigned short len)
{
struct net_local *lp = (struct net_local *)dev->priv;
- unsigned short ioaddr = dev->base_addr;
- unsigned short old_wp = inw(ioaddr+WRITE_PTR);
+ unsigned short *ptr = (unsigned short *)(dev->mem_start + lp->tx_head);
+
+ writew(0x0000,ptr++);
+ writew(Cmd_INT|Cmd_Xmit,ptr++);
+ writew(lp->tx_head+0x08,ptr++);
+ writew(lp->tx_head+0x0e,ptr++);
+ writew(0x0000,ptr++);
+ writew(0x0000,ptr++);
+ writew(lp->tx_head+0x08,ptr++);
+ writew(0x8000|len,ptr++);
+ writew(-1,ptr++);
+ writew(lp->tx_head+0x16,ptr++);
+ writew(0,ptr++);
+
+ memcpy_toio(ptr, buf, len);
+
+ writew(lp->tx_head, dev->mem_start+lp->tx_tail+0x0c);
- outw(lp->tx_head,ioaddr+WRITE_PTR);
- outw(0x0000,ioaddr);
- outw(Cmd_INT|Cmd_Xmit,ioaddr);
- outw(lp->tx_head+0x08,ioaddr);
- outw(lp->tx_head+0x0e,ioaddr);
- outw(0x0000,ioaddr);
- outw(0x0000,ioaddr);
- outw(lp->tx_head+0x08,ioaddr);
- outw(0x8000|len,ioaddr);
- outw(-1,ioaddr);
- outw(lp->tx_head+0x16,ioaddr);
- outw(0,ioaddr);
- outsw(ioaddr,buf,(len+1)>>1);
- outw(lp->tx_tail+0x0c,ioaddr+WRITE_PTR);
- outw(lp->tx_head,ioaddr);
dev->trans_start = jiffies;
lp->tx_tail = lp->tx_head;
if (lp->tx_head==TX_BUF_START+((lp->num_tx_bufs-1)*TX_BUF_SIZE))
@@ -677,24 +636,66 @@
lp->tx_head += TX_BUF_SIZE;
if (lp->tx_head != lp->tx_reap)
dev->tbusy = 0;
- outw(old_wp,ioaddr+WRITE_PTR);
+}
+
+/*
+ * Do a basic check on the card's memory. This isn't supposed to be a
+ * sophisticated memory tester - we trust Intel, however misguidedly, to have
+ * got the board design right - but it _is_ supposed to catch the cases where
+ * we have mis-detected the memory address and/or size, or there is a
conflict.
+ * It will catch stuck data lines too, but not problems with addressing.
+ *
+ * return 0 for pass, 1 for fail.
+ */
+
+static int eexpress_memory_test(struct device *dev)
+{
+ unsigned long int i;
+
+ for (i = dev->mem_start; i < dev->mem_end; i+=2)
+ writew(0x5555, i);
+ for (i = dev->mem_start; i < dev->mem_end; i+=2) {
+ if (readw(i) != 0x5555) {
+ printk(KERN_NOTICE "%s: memory test fails at %lx\n",
+ dev->name, i);
+ return 1;
+ }
+ }
+
+ for (i = dev->mem_start; i < dev->mem_end; i+=2)
+ writew(0xaaaa, i);
+ for (i = dev->mem_start; i < dev->mem_end; i+=2) {
+ if (readw(i) != 0xaaaa) {
+ printk(KERN_NOTICE "%s: memory test fails at %lx\n",
+ dev->name, i);
+ return 1;
+ }
+ }
+ return 0;
}
/*
* 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;
+ char *ifmap[]={"AUI", "BNC", "RJ45"};
+ enum iftype {AUI=0, BNC=1, TP=2};
int i;
- unsigned char *chw_addr = (unsigned char *)hw_addr;
+ struct net_local *lp;
- printk("%s: EtherExpress at %#x, ",dev->name,ioaddr);
+ printk("%s: DeathExpress 16 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 +703,131 @@
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;
}
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];
+
+ /* This is quite dubious. My card currently claims to be
+ * using the RJ45 connector, and it doesn't even _have_ one.
+ * However, SOFTSET says the same thing, so I think this code
+ * is correct.
+ */
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));
- eexp_hw_ASICrst(dev);
-
{
- unsigned short i586mso = 0x023e;
- unsigned short old_wp,old_rp,old_a0,old_a1;
- unsigned short a0_0,a1_0,a0_1,a1_1;
-
- old_wp = inw(ioaddr+WRITE_PTR);
- old_rp = inw(ioaddr+READ_PTR);
- outw(0x8000+i586mso,ioaddr+READ_PTR);
- old_a1 = inw(ioaddr);
- outw(i586mso,ioaddr+READ_PTR);
- old_a0 = inw(ioaddr);
- outw(i586mso,ioaddr+WRITE_PTR);
- outw(0x55aa,ioaddr);
- outw(i586mso,ioaddr+READ_PTR);
- a0_0 = inw(ioaddr);
- outw(0x8000+i586mso,ioaddr+WRITE_PTR);
- outw(0x5a5a,ioaddr);
- outw(0x8000+i586mso,ioaddr+READ_PTR);
- a1_0 = inw(ioaddr);
- outw(i586mso,ioaddr+READ_PTR);
- a0_1 = inw(ioaddr);
- outw(i586mso,ioaddr+WRITE_PTR);
- outw(0x1234,ioaddr);
- outw(0x8000+i586mso,ioaddr+READ_PTR);
- a1_1 = inw(ioaddr);
+ unsigned char memory_decode, memory_edecode, memory_adjust;
+ unsigned int memory_page;
- 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;
+ memory_decode = eexp_hw_readeeprom(ioaddr, 6) & 0xff;
+ memory_edecode = eexp_hw_readeeprom(ioaddr, 6) >> 8;
+ memory_adjust = eexp_hw_readeeprom(ioaddr, 1) & 0xff;
+
+ outb(memory_decode, ioaddr + MEM_Dec);
+ outb(memory_adjust, ioaddr + MEM_Ctrl);
+ outb(~memory_decode, ioaddr + MEM_Page_Ctrl);
+ outb(memory_edecode, ioaddr + MEM_ECtrl);
+
+#if 0
+ printk("%s: memory control %02x %02x %02x\n", dev->name,
+ memory_decode, memory_edecode, memory_adjust);
+#endif
+
+ memory_page = 0;
+ memory_size = 0;
+
+ if (!memory_decode) {
+ printk(KERN_NOTICE "%s: no shared memory found\n",
+ dev->name);
+ kfree(dev->priv);
+ return ENODEV;
}
- 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;
+
+ while (!(memory_decode & 1)) {
+ memory_decode = memory_decode >> 1;
+ memory_page++;
+ }
+ while (memory_decode & 1) {
+ memory_size++;
+ memory_decode = memory_decode >> 1;
}
- 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 0
+ printk("%s: memory size = %d, page = %d\n", dev->name,
+ memory_size, memory_page);
+#endif
+ dev->mem_start = 0xc0000 + (0x4000 * memory_page);
+ memory_size = memory_size << 4;
}
-
- if (net_debug)
- printk(version);
+
+ /* 32k cards can map either 16 or 32k of RAM as shared memory.
+ * 64k cards can map 48k or 64k as well.
+ */
+ switch (memory_size)
+ {
+ case 64:
+ lp->num_tx_bufs = 8;
+ lp->rx_buf_end = 0xfff6;
+ break;
+ case 48:
+ lp->num_tx_bufs = 8;
+ lp->rx_buf_end = 0xbff6;
+ break;
+ case 32:
+ lp->rx_buf_end = 0x7ff6;
+ lp->num_tx_bufs = 4;
+ break;
+ case 16:
+ lp->rx_buf_end = 0x3ff6;
+ lp->num_tx_bufs = 4;
+ break;
+ default:
+ printk("; bad memory size (%dk)\n", memory_size);
+ kfree(dev->priv);
+ return ENODEV;
+ break;
+ }
+
+ printk("; using IRQ %d, %s connector, %dk shared memory.\n", dev->irq,
+ ifmap[dev->if_port], memory_size);
+
+ lp->rx_buf_start = TX_BUF_START + (lp->num_tx_bufs*TX_BUF_SIZE);
+
+ dev->mem_end = dev->mem_start + (memory_size << 10);
+
+ lp->scb = (struct scb *)(dev->mem_start + 0x8);
+
+ for (i = 0; i < 32; i += 2)
+ writew(0, dev->mem_start+i);
+
+ if (eexpress_memory_test(dev)) {
+ kfree(dev->priv);
+ return ENODEV;
+ }
+
+ for (i = 0; i < (memory_size << 10); i += 2)
+ writew(0, dev->mem_start+i);
+
dev->open = eexp_open;
dev->stop = eexp_close;
dev->hard_start_xmit = eexp_xmit;
@@ -796,9 +838,11 @@
}
/*
- * 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;
@@ -850,24 +894,18 @@
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);
+ status = readw(dev->mem_start + 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 +934,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 +950,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);
+ lp->scb->cbl = lp->tx_link;
+ lp->scb->cmd = SCB_CUstart;
outb(0,ioaddr+SIGNAL_CA);
{
unsigned short boguscount=50,failcount=5;
- while (!inw(ioaddr+SCB_STATUS))
+ while (!lp->scb->status)
{
if (!--boguscount)
{
if (--failcount)
{
- printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n",
- dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD));
- outw(lp->tx_link,ioaddr+SCB_CBL);
- outw(0,ioaddr+SCB_STATUS);
- outw(SCB_CUstart,ioaddr+SCB_CMD);
+ printk(KERN_WARNING "%s: CU start timed out, status %04x, cmd %04x\n",
dev->name, lp->scb->status, lp->scb->cmd);
+ lp->scb->cbl = lp->tx_link;
+ lp->scb->cmd = SCB_CUstart;
outb(0,ioaddr+SIGNAL_CA);
boguscount = 100;
}
@@ -950,40 +982,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 *ptr;
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);
+ ptr = (unsigned short *)(dev->mem_start + tx_block);
+
+ writew(0x0000,ptr++);
+ writew(Cmd_INT|Cmd_Xmit,ptr++);
+ writew(tx_block+0x08,ptr++);
+ writew(tx_block+0x0e,ptr++);
+
+ writew(0x0000,ptr++);
+ writew(0x0000,ptr++);
+ writew(tx_block+0x08,ptr++);
+
+ writew(0x8000,ptr++);
+ writew(-1,ptr++);
+ writew(tx_block+0x16,ptr++);
+ writew(0x0000,ptr++);
+
tx_block += TX_BUF_SIZE;
}
lp->tx_head = TX_BUF_START;
@@ -991,66 +1022,61 @@
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).
*/
+/* Is this really a good idea? --pjb */
+
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;
+ volatile struct rfd_header *rfd;
lp->num_rx_bufs = 0;
lp->rx_first = rx_block;
do
{
+ rfd = (struct rfd_header *)(rx_block + dev->mem_start);
+
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);
+
+ rfd->flags = 0;
+ rfd->link = rx_block + RX_BUF_SIZE;
+ rfd->rbd_offset = rx_block + 0x16;
+ rfd->dstaddr1 = 0xdead;
+ rfd->dstaddr2 = 0xdead;
+ rfd->dstaddr3 = 0xdead;
+ rfd->srcaddr1 = 0xdead;
+ rfd->srcaddr2 = 0xdead;
+ rfd->srcaddr3 = 0xdead;
+ rfd->length = 0xdead;
+
+ rfd->actual_count = 0x8000;
+ rfd->next_rbd = 0xffff;
+ rfd->buf_addr1 = rx_block + 0x20;
+ rfd->buf_addr2 = 0;
+ rfd->size = 0x8000 | (RX_BUF_SIZE-0x20);
+
lp->rx_last = rx_block;
rx_block += RX_BUF_SIZE;
} while (rx_block <= lp->rx_buf_end-RX_BUF_SIZE);
- outw(lp->rx_last+4,ioaddr+WRITE_PTR);
- outw(lp->rx_first,ioaddr);
-
- outw(old_wp,ioaddr+WRITE_PTR);
+ rfd = (struct rfd_header *)(lp->rx_last + dev->mem_start);
+ rfd->link = lp->rx_first;
}
/*
- * 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)
{
@@ -1058,49 +1084,46 @@
unsigned short ioaddr = dev->base_addr;
#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));
- }
- }
- }
+ /* Loopback seems to do bad things */
+ /* set_loopback; */
- 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 */
+ memcpy_toio((void *)(dev->mem_start+lp->rx_buf_end), start_code, 10);
+ memcpy_toio((void *)dev->mem_start,
+ (unsigned char *)((unsigned long int)start_code+10),
+ sizeof(start_code)-10);
+
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);
+
+ lp->scb->cmd = 0xf000;
outb(0,ioaddr+SIGNAL_CA);
+
{
unsigned short rboguscount=50,rfailcount=5;
- while (outw(0,ioaddr+READ_PTR),inw(ioaddr))
+ while (readw(dev->mem_start))
{
if (!--rboguscount)
{
printk(KERN_WARNING "%s: i82586 reset timed out, kicking...\n",
dev->name);
- outw(0,ioaddr+SCB_CMD);
+ lp->scb->cmd = 0;
outb(0,ioaddr+SIGNAL_CA);
rboguscount = 100;
if (!--rfailcount)
@@ -1113,23 +1136,22 @@
}
}
- outw(CONF_LINK,ioaddr+SCB_CBL);
- outw(0,ioaddr+SCB_STATUS);
- outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD);
+ lp->scb->cbl = CONF_LINK;
+ lp->scb->cmd = 0xf000|SCB_CUstart;
outb(0,ioaddr+SIGNAL_CA);
+
{
unsigned short iboguscount=50,ifailcount=5;
- while (!inw(ioaddr+SCB_STATUS))
+ while (!lp->scb->status)
{
if (!--iboguscount)
{
if (--ifailcount)
{
printk(KERN_WARNING "%s: i82586 initialization timed out, status %04x,
cmd %04x\n",
- dev->name, inw(ioaddr+SCB_STATUS), inw(ioaddr+SCB_CMD));
- outw(CONF_LINK,ioaddr+SCB_CBL);
- outw(0,ioaddr+SCB_STATUS);
- outw(0xf000|SCB_CUstart,ioaddr+SCB_CMD);
+ dev->name, lp->scb->status, lp->scb->cmd);
+ lp->scb->cbl = CONF_LINK;
+ lp->scb->cmd = 0xf000|SCB_CUstart;
outb(0,ioaddr+SIGNAL_CA);
iboguscount = 100;
}
@@ -1142,8 +1164,10 @@
}
}
- outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
- clear_loopback;
+/* clear_loopback; */
+
+ 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 +1175,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);
}
@@ -1224,6 +1204,10 @@
int irq[EEXP_MAX_CARDS] = {0, };
int io[EEXP_MAX_CARDS] = {0, };
+/* These not yet used */
+unsigned long int membase[EEXP_MAX_CARDS] = { 0, };
+int memsize[EEXP_MAX_CARDS] = { 0, };
+
/* Ideally the user would give us io=, irq= for every card. If any parameters
* are specified, we verify and then use them. If no parameters are given, we
* autoprobe for one card only.
@@ -1271,6 +1255,5 @@
* Local Variables:
* c-file-style: "linux"
* tab-width: 8
- * compile-command: "gcc -D__KERNEL__ -I/discs/bibble/src/linux-1.3.69/includ
e -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce
-pipe -m486 -DCPU=486 -DMODULE -c 3c505.c"
* End:
*/
--- clean-linux/linux/drivers/net/eth82586.h Mon May 13 23:56:30 1996
+++ linux/drivers/net/eth82586.h Sat Aug 31 19:38:27 1996
@@ -1,9 +1,10 @@
/*
* eth82586.h: Intel EtherExpress defines
*
- * Written 1995 by John Sullivan
- * See eexpress.c for further details
- * documentation and usage to do.
+ * Most of this file is a waste of space. Really we ought to share the
+ * 82586 stuff with the Wavelan i82586.h, and put all the EtherExpress bits
+ * in a new eexpress.h file.
+ * --pjb
*/
/*
@@ -17,26 +18,13 @@
#define SIGNAL_CA 0x0006
#define SET_IRQ 0x0007
#define SM_PTR 0x0008
+#define MEM_Dec 0x000a
#define MEM_Ctrl 0x000b
#define MEM_Page_Ctrl 0x000c
#define Config 0x000d
#define EEPROM_Ctrl 0x000e
#define ID_PORT 0x000f
-
-/*
- * offset to shadowed memory, 0 <= x <= 31. We don't use this yet,
- * but may in the future. Is shadow memory access any faster than
- * dataport access?
- */
-#define SM_ADDR(x) (0x4000+((x&0x10)<<10)+(x&0xf))
-
-/* Always mirrors eexp-memory at 0x0008-0x000f */
-#define SCB_STATUS 0xc008
-#define SCB_CMD 0xc00a
-#define SCB_CBL 0xc00c
-#define SCB_RFA 0xc00e
-
-
+#define MEM_ECtrl 0x000f
/*
* card register defines
@@ -46,10 +34,6 @@
#define SIRQ_en 0x08
#define SIRQ_dis 0x00
-/* Config */
-#define set_loopback outb(inb(ioaddr+Config)|0x02,ioaddr+Config)
-#define clear_loopback outb(inb(ioaddr+Config)&0xfd,ioaddr+Config)
-
/* EEPROM_Ctrl */
#define EC_Clk 0x01
#define EC_CS 0x02
@@ -67,28 +51,40 @@
/* (System Configuration Pointer) System start up block, read after 586_RST */
#define SCP_START 0xfff6
-
/* Intermediate System Configuration Pointer */
#define ISCP_START 0x0000
+
/* System Command Block */
#define SCB_START 0x0008
-/*
- * Start of buffer region. If we have 64k memory, eexp_hw_probe() may raise
- * NUM_TX_BUFS. RX_BUF_END is set to the end of memory, and all space between
- * the transmit buffer region and end of memory used for as many receive
buffers
- * as we can fit. See eexp_hw_[(rx)(tx)]init().
+/* Start of buffer region. Everything before this is used for control
+ * structures and the CU configuration program. The memory layout is
+ * determined in eexp_hw_probe(), once we know how much memory is
+ * available on the card.
*/
+
#define TX_BUF_START 0x0100
+
#define TX_BUF_SIZE ((24+ETH_FRAME_LEN+31)&~0x1f)
#define RX_BUF_SIZE ((32+ETH_FRAME_LEN+31)&~0x1f)
-
-
/*
- * SCB defines
+ * SCB defines
*/
+struct scb {
+ volatile unsigned short status;
+ volatile unsigned short cmd;
+ volatile unsigned short cbl;
+ volatile unsigned short rfa;
+ struct {
+ volatile unsigned short crc; /* bad CRC in correctly-aligned frame */
+ volatile unsigned short align; /* bad CRC in misaligned frame */
+ volatile unsigned short resource; /* no resources */
+ volatile unsigned short overrun; /* rx overrun */
+ } errors;
+};
+
/* these functions take the SCB status word and test the relevant status bit
*/
#define SCB_complete(s) ((s&0x8000)!=0)
#define SCB_rxdframe(s) ((s&0x4000)!=0)
@@ -108,8 +104,6 @@
#define SCB_CUresume 0x0200
#define SCB_CUsuspend 0x0300
#define SCB_CUabort 0x0400
-
-/* ? */
#define SCB_resetchip 0x0080
#define SCB_RUnop 0x0000
@@ -118,9 +112,8 @@
#define SCB_RUsuspend 0x0030
#define SCB_RUabort 0x0040
-
/*
- * Command block defines
+ * Command block defines
*/
#define Stat_Done(s) ((s&0x8000)!=0)
@@ -142,6 +135,7 @@
* Cmd_INT on the last command in the sequence, followed by a
* dummy Cmd_Nop with Cmd_END after this.
*/
+
#define Cmd_END 0x8000
#define Cmd_SUS 0x4000
#define Cmd_INT 0x2000
@@ -170,3 +164,34 @@
#define FD_DMA(s) ((s&0x0100)!=0)
#define FD_Short(s) ((s&0x0080)!=0)
#define FD_NoEOF(s) ((s&0x0040)!=0)
+
+struct rfd_header {
+ volatile unsigned long flags;
+ volatile unsigned short link;
+ volatile unsigned short rbd_offset;
+ volatile unsigned short dstaddr1;
+ volatile unsigned short dstaddr2;
+ volatile unsigned short dstaddr3;
+ volatile unsigned short srcaddr1;
+ volatile unsigned short srcaddr2;
+ volatile unsigned short srcaddr3;
+ volatile unsigned short length;
+
+ /* This is actually a Receive Buffer Descriptor. The way we
+ * arrange memory means that an RBD always follows the RFD that
+ * points to it, so they might as well be in the same structure.
+ */
+ volatile unsigned short actual_count;
+ volatile unsigned short next_rbd;
+ volatile unsigned short buf_addr1;
+ volatile unsigned short buf_addr2;
+ volatile unsigned short size;
+};
+
+/* Returned data from the Time Domain Reflectometer */
+
+#define TDR_LINKOK (1<<15)
+#define TDR_XCVRPROBLEM (1<<14)
+#define TDR_OPEN (1<<13)
+#define TDR_SHORT (1<<12)
+#define TDR_TIME 0x7ff