Date: Mon, 8 Dec 1997 09:46:10 +0100 (CET)
From: Geert Uytterhoeven <Geert.Uytterhoeven@cs.kuleuven.ac.be>
To: Jes Sorensen <Jes.Sorensen@cern.ch>
Cc: Alain.Malek@dumbo.cryogen.ch, linux-m68k@phil.uni-sb.de
Subject: Re: L68K: CNET40 PCMCIA driver
In-Reply-To: <d3btysz2uw.fsf@cern.ch>
Sender: owner-linux-m68k@phil.uni-sb.de

On 8 Dec 1997, Jes Sorensen wrote:
> >>>>> "Alain" == Alain Malek <Alain.Malek@dumbo.cryogen.ch> writes:
> Alain>  Here is my driver for the PCMCIA CNET40 network card.  (I hope
> Alain> that it supports also some more NE2000 compatible pcmcia cards)
> Alain> This patch must be applied to a 2.0.29 m68k kernel.  As it's
> Alain> one of my first attempt to code something for the Linux kernel
> Alain> I may have made some mistakes. So it would be cool if someone
> Alain> could have a look at it. At least it runs fine and reliable on
> Alain> my machine.  Actually the pcmcia specific code is included in
> Alain> the driver code.  I think it wouldn't be a bad idea to seperate
> Alain> this code and create an arch/m68k/amiga/pcmcia.c file.
> 
> Great, could you please repost it in a sane format (ie. uuencoded), I
> don't take MIME.

The non-MIME capable people can use the patch below...

> You probably want to do some generic Amiga PCMCIA code as you also
> mention yourself.
> 
> If people don't report problems with it I would like to put it in the
> official 2.0.32 release.

I see one possible problem: amiints fiddles with the Gayle bits without
checking for the presence of Gayle, which may cause a crash. I haven't tested
it, and I have an A4000 which has Gayle anyway (without PCMCIA though).

If someone wants to port the driver to 2.1.x: amiga_hw_present has a PCMCIA bit
in 2.1.x.

Greetings,

						Geert

diff -ruN linux-2.0.29/arch/m68k/amiga/amiints.c linux/arch/m68k/amiga/amiints.c
--- linux-2.0.29/arch/m68k/amiga/amiints.c	Sat Oct  5 15:23:52 1996
+++ linux/arch/m68k/amiga/amiints.c	Sun Dec  7 11:01:05 1997
@@ -31,6 +31,8 @@
 #include <asm/traps.h>
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
+#include <asm/io.h>
+#include <asm/amigayle.h>
 
 extern int cia_request_irq(struct ciabase *base,int irq,
                            void (*handler)(int, void *, struct pt_regs *),
@@ -92,6 +94,9 @@
 	custom.intena = 0x7fff;
 	custom.intreq = 0x7fff;
 	custom.intena = IF_SETCLR | IF_INTEN;
+
+	/* turn off PCMCIA interrupts */
+	outb(GAYLE_CS_MASTER|GAYLE_CS_CCDET, GAYLE_IRQEN);
 
 	cia_init_IRQ(&ciaa_base);
 	cia_init_IRQ(&ciab_base);
diff -ruN linux-2.0.29/arch/m68k/config.in linux/arch/m68k/config.in
--- linux-2.0.29/arch/m68k/config.in	Thu Feb 27 17:45:46 1997
+++ linux/arch/m68k/config.in	Wed Dec  3 12:59:54 1997
@@ -175,6 +175,7 @@
 if [ "$CONFIG_AMIGA" = "y" ]; then
   tristate 'Ariadne support' CONFIG_ARIADNE
   tristate 'A2065 support' CONFIG_A2065
+  tristate 'APNE support' CONFIG_APNE
   tristate 'Hydra support' CONFIG_HYDRA
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
diff -ruN linux-2.0.29/arch/m68k/defconfig linux/arch/m68k/defconfig
--- linux-2.0.29/arch/m68k/defconfig	Thu Feb 27 17:53:47 1997
+++ linux/arch/m68k/defconfig	Wed Dec  3 12:55:26 1997
@@ -137,6 +137,7 @@
 # CONFIG_PPP is not set
 # CONFIG_ARIADNE is not set
 # CONFIG_A2065 is not set
+# CONFIG_APNE is not set
 # CONFIG_HYDRA is not set
 # CONFIG_ATARILANCE is not set
 # CONFIG_ATARI_BIONET is not set
diff -ruN linux-2.0.29/drivers/net/Makefile linux/drivers/net/Makefile
--- linux-2.0.29/drivers/net/Makefile	Sun Nov  3 21:15:56 1996
+++ linux/drivers/net/Makefile	Wed Dec  3 12:54:16 1997
@@ -490,6 +490,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_APNE),y)
+L_OBJS += apne.o
+else
+  ifeq ($(CONFIG_APNE),m)
+  M_OBJS += apne.o
+  endif
+endif
+
 ifeq ($(CONFIG_HYDRA),y)
 L_OBJS += hydra.o
 else
diff -ruN linux-2.0.29/drivers/net/Space.c linux/drivers/net/Space.c
--- linux-2.0.29/drivers/net/Space.c	Sat Nov  9 15:07:41 1996
+++ linux/drivers/net/Space.c	Fri Dec  5 14:40:32 1997
@@ -76,6 +76,7 @@
 extern int sparc_lance_probe(struct device *);
 extern int atarilance_probe(struct device *);
 extern int a2065_probe(struct device *);
+extern int apne_probe(struct device *);
 extern int ariadne_probe(struct device *);
 extern int hydra_probe(struct device *);
 
@@ -203,6 +204,9 @@
 #endif
 #ifdef CONFIG_A2065		/* Commodore/Ameristar A2065 Ethernet Board */
 	&& a2065_probe(dev)
+#endif
+#ifdef CONFIG_APNE		/* Amiga 1200 PCMCIA NEx000 Ethernet Board */
+	&& apne_probe(dev)
 #endif
 #ifdef CONFIG_ARIADNE		/* Village Tronic Ariadne Ethernet Board */
 	&& ariadne_probe(dev)
diff -ruN linux-2.0.29/drivers/net/apne.c linux/drivers/net/apne.c
--- linux-2.0.29/drivers/net/apne.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/net/apne.c	Sun Dec  7 11:16:32 1997
@@ -0,0 +1,1375 @@
+/*
+ * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200
+ *
+ * (C) Copyright 1997 Alain Malek
+ *                    (Alain.Malek@cryogen.com)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * ne.c:       A general non-shared-memory NS8390 ethernet driver for linux
+ *             Written 1992-94 by Donald Becker.
+ *
+ * 8390.c:     A general NS8390 ethernet driver core for linux.
+ *             Written 1992-94 by Donald Becker.
+ *
+ * cnetdevice: A Sana-II ethernet driver for AmigaOS
+ *             Written by Bruce Abbott (bhabbott@inhb.co.nz)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/zorro.h>
+#include <asm/amigatypes.h>
+#include <asm/amigayle.h>
+#include "apne.h"
+
+int apne_probe(struct device *dev);
+static int apne_probe1(struct device *dev, int ioaddr);
+
+static int apne_open(struct device *dev);
+static int apne_close(struct device *dev);
+
+static void apne_reset_8390(struct device *dev);
+static void apne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+			  int ring_page);
+static void apne_block_input(struct device *dev, int count,
+			  struct sk_buff *skb, int ring_offset);
+static void apne_block_output(struct device *dev, const int count,
+		const unsigned char *buf, const int start_page);
+
+
+static int ethdev_init(struct device *dev);
+
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+#define ei_get_8390_hdr (ei_local->get_8390_hdr)
+
+static int ei_open(struct device *dev);
+static int ei_close(struct device *dev);
+extern void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void ei_tx_intr(struct device *dev);
+static void ei_tx_err(struct device *dev);
+static void ei_receive(struct device *dev);
+static void ei_rx_overrun(struct device *dev);
+
+static void NS8390_trigger_send(struct device *dev, unsigned int length,
+								int start_page);
+
+static void pcmcia_reset(void);
+static int pcmcia_init(void);
+static int pcmcia_readtuple(unsigned char tuple_id, void *tuple, int max_len);
+
+/* use 0 for production, 1 for verification, >2 for debug */
+/*#define EI_DEBUG 4*/
+
+#ifdef EI_DEBUG
+int ei_debug = EI_DEBUG;
+#else
+int ei_debug = 1;
+#endif
+
+#define WORDSWAP(a) ( (((a)>>8)&0xff) | ((a)<<8) )
+
+static const char *version =
+    "apne.c:v1.0 11/29/97 Alain Malek (Alain.Malek@cryogen.com)\n";
+
+/*-*************************************************************************-*/
+
+int apne_probe(struct device *dev)
+{
+	char tuple[8];
+
+	if ( !(MACH_IS_AMIGA && (boot_info.bi_un.bi_ami.model == AMI_1200)) )
+		return (ENODEV);
+
+	printk("Looking for PCMCIA ethernet card : ");
+
+	/* check if a card is inserted */
+	if (!(inb(GAYLE_CARDSTATUS) & GAYLE_CS_CCDET)) {
+		printk("NO PCMCIA card inserted\n");
+		return (ENODEV);
+	}
+
+	/* disable pcmcia irq for readtuple */
+	outb(GAYLE_CS_MASTER|GAYLE_CS_CCDET, GAYLE_IRQEN);
+
+	if ((pcmcia_readtuple(CISTPL_FUNC_ID, tuple, 8) < 3) ||
+	     (tuple[2] != FUNC_ID_NETIF)) {
+		printk("not an ethernet card\n");
+		return (ENODEV);
+	}
+
+	printk("ethernet PCMCIA card inserted\n");
+
+	if (pcmcia_init())
+		return apne_probe1(dev, GAYLE_IO);
+	else
+		return (ENODEV);
+
+}
+
+/*-*************************************************************************-*/
+static int apne_open(struct device *dev)
+{
+    ei_open(dev);
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int apne_close(struct device *dev)
+{
+    if (ei_debug > 1)
+	printk("%s: Shutting down ethercard.\n", dev->name);
+    ei_close(dev);
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+/*-*************************************************************************-*/
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+
+static void apNS8390_init(struct device *dev, int startp)
+{
+    int e8390_base = dev->base_addr;
+    struct ei_device *ei_local = (struct ei_device *) dev->priv;
+    int i;
+    int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+    unsigned long flags;
+    
+    /* Follow National Semi's recommendations for initing the DP83902. */
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
+    outb_p(endcfg, e8390_base + EN0_DCFG);	/* 0x48 or 0x49 */
+    /* Clear the remote byte count registers. */
+    outb_p(0x00,  e8390_base + EN0_RCNTLO);
+    outb_p(0x00,  e8390_base + EN0_RCNTHI);
+    /* Set to monitor and loopback mode -- this is vital!. */
+    outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
+    outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+    /* Set the transmit page and receive ring. */
+    outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+    ei_local->tx1 = ei_local->tx2 = 0;
+    outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+    outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
+    ei_local->current_page = ei_local->rx_start_page;		/* assert boundary+1 */
+    outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+    /* Clear the pending interrupts and mask. */
+    outb_p(0xFF, e8390_base + EN0_ISR);
+    outb_p(0x00,  e8390_base + EN0_IMR);
+    
+    /* Copy the station address into the DS8390 registers,
+       and set the multicast hash bitmap to receive all multicasts. */
+    save_flags(flags);
+    cli();
+    outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
+
+    {
+	unsigned long en1_phys[6] = {EN1_PHYS0, EN1_PHYS1, EN1_PHYS2,
+				     EN1_PHYS3, EN1_PHYS4, EN1_PHYS5 };
+        for(i = 0; i < 6; i++) {
+		    outb_p(dev->dev_addr[i], e8390_base + en1_phys[i]);
+        }
+    }
+
+    /* Initialize the multicast list to accept-all.  If we enable multicast
+       the higher levels can do the filtering. */
+    {
+	unsigned long en1_mult[8] = {EN1_MULT0, EN1_MULT1, EN1_MULT2, EN1_MULT3,
+				     EN1_MULT4, EN1_MULT5, EN1_MULT6, EN1_MULT7 };
+        for(i = 0; i < 8; i++) {
+            outb_p(0xff, e8390_base + en1_mult[i]);
+        }
+    }
+
+    outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
+    restore_flags(flags);
+    dev->tbusy = 0;
+    dev->interrupt = 0;
+    ei_local->tx1 = ei_local->tx2 = 0;
+    ei_local->txing = 0;
+    if (startp) {
+		outb_p(0xff, e8390_base + EN0_ISR);
+		outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
+		outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+		/* 3c503 TechMan says rxconfig only after the NIC is started. */
+		outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on,  */
+		dev->set_multicast_list(dev);		/* Get the multicast status right if this
+							   was a reset. */
+    }
+    return;
+}
+
+/*-*************************************************************************-*/
+
+static int apne_probe1(struct device *dev, int ioaddr)
+{
+    int i;
+    unsigned char SA_prom[32];
+    int wordlength = 2;
+    const char *name = NULL;
+    int start_page, stop_page;
+    int neX000, ctron;
+    static unsigned version_printed = 0;
+
+    /* We should have a "dev" from Space.c or the static module table. */
+    if (dev == NULL) {
+	printk(KERN_ERR "apne.c: Passed a NULL device.\n");
+	dev = init_etherdev(0, 0);
+    }
+
+    if (ei_debug  &&  version_printed++ == 0)
+	printk(version);
+
+    printk("PCMCIA NE*000 ethercard probe");
+
+    /* Reset card. Who knows what dain-bramaged state it was left in. */
+    {	unsigned long reset_start_time = jiffies;
+
+	outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+	while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+		if (jiffies - reset_start_time > 2*HZ/100) {
+			printk(" not found (no reset ack).\n");
+			return ENODEV;
+		}
+
+	outb_p(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
+    }
+
+    /* Read the 16 bytes of station address PROM.
+       We must first initialize registers, similar to apNS8390_init(eifdev, 0).
+       We can't reliably read the SAPROM address without this.
+       (I learned the hard way!). */
+    {
+	struct {unsigned long value, offset; } program_seq[] = {
+	    {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+	    {0x48,	EN0_DCFG},	/* Set byte-wide (0x48) access. */
+	    {0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+	    {0x00,	EN0_RCNTHI},
+	    {0x00,	EN0_IMR},	/* Mask completion irq. */
+	    {0xFF,	EN0_ISR},
+	    {E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+	    {E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+	    {32,	EN0_RCNTLO},
+	    {0x00,	EN0_RCNTHI},
+	    {0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
+	    {0x00,	EN0_RSARHI},
+	    {E8390_RREAD+E8390_START, E8390_CMD},
+	};
+	for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) {
+	    outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+	}
+
+    }
+    for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+	SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+	SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+	if (SA_prom[i] != SA_prom[i+1])
+	    wordlength = 1;
+    }
+
+    /*	At this point, wordlength *only* tells us if the SA_prom is doubled
+	up or not because some broken PCI cards don't respect the byte-wide
+	request in program_seq above, and hence don't have doubled up values. 
+	These broken cards would otherwise be detected as an ne1000.  */
+
+    if (wordlength == 2)
+	for (i = 0; i < 16; i++)
+		SA_prom[i] = SA_prom[i+i];
+    
+    if (wordlength == 2) {
+	/* We must set the 8390 for word mode. */
+	outb_p(0x49, ioaddr + EN0_DCFG);
+	start_page = NESM_START_PG;
+	stop_page = NESM_STOP_PG;
+    } else {
+	start_page = NE1SM_START_PG;
+	stop_page = NE1SM_STOP_PG;
+    }
+
+    neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57);
+    ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+
+    /* Set up the rest of the parameters. */
+    if (neX000) {
+	name = (wordlength == 2) ? "NE2000" : "NE1000";
+    } else if (ctron) {
+	name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
+	start_page = 0x01;
+	stop_page = (wordlength == 2) ? 0x40 : 0x20;
+    } else {
+	printk(" not found.\n");
+	return ENXIO;
+
+    }
+
+    dev->base_addr = ioaddr;
+
+    /* Install the Interrupt handler */
+    if (request_irq(IRQ_AMIGA_PORTS, ei_interrupt, 0, "apne Ethernet", dev))
+        return -EAGAIN;
+
+
+    /* Allocate dev->priv and fill in 8390 specific dev fields. */
+    if (ethdev_init(dev)) {
+	printk (" unable to get memory for dev->priv.\n");
+	return -ENOMEM;
+    }
+
+    for(i = 0; i < ETHER_ADDR_LEN; i++) {
+	printk(" %2.2x", SA_prom[i]);
+	dev->dev_addr[i] = SA_prom[i];
+    }
+
+    printk("\n%s: %s found.\n",
+	   dev->name, name);
+
+    ei_status.name = name;
+    ei_status.tx_start_page = start_page;
+    ei_status.stop_page = stop_page;
+    ei_status.word16 = (wordlength == 2);
+
+    ei_status.rx_start_page = start_page + TX_PAGES;
+
+    ei_status.reset_8390 = &apne_reset_8390;
+    ei_status.block_input = &apne_block_input;
+    ei_status.block_output = &apne_block_output;
+    ei_status.get_8390_hdr = &apne_get_8390_hdr;
+    dev->open = &apne_open;
+    dev->stop = &apne_close;
+    apNS8390_init(dev, 0);
+
+    outb((inb(GAYLE_IRQREQ) ^ 0x2c) | (0xc0), GAYLE_IRQREQ); /* ack PCMCIA int req */
+    /* enable PCMCIA interrupt */
+    outb(GAYLE_CS_MASTER|GAYLE_CS_CCDET|GAYLE_CS_IRQ, GAYLE_IRQEN);
+
+    return 0;
+}
+
+/*-*************************************************************************-*/
+
+/* Hard reset the card.  This used to pause for the same period that a
+   8390 reset command required, but that shouldn't be necessary. */
+static void
+apne_reset_8390(struct device *dev)
+{
+    unsigned long reset_start_time = jiffies;
+
+    if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies);
+
+    outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+    ei_status.txing = 0;
+    ei_status.dmaing = 0;
+
+    /* This check _should_not_ be necessary, omit eventually. */
+    while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+	if (jiffies - reset_start_time > 2*HZ/100) {
+	    printk("%s: ne_reset_8390() did not complete.\n", dev->name);
+	    break;
+	}
+    outb_p(ENISR_RESET, NE_BASE + EN0_ISR);	/* Ack intr. */
+}
+
+/*-*************************************************************************-*/
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void
+apne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+    int nic_base = dev->base_addr;
+    int cnt;
+    char *ptrc;
+    short *ptrs;
+
+    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+    if (ei_status.dmaing) {
+	printk("%s: DMAing conflict in ne_get_8390_hdr "
+	   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+	   dev->name, ei_status.dmaing, ei_status.irqlock,
+	   dev->interrupt);
+	return;
+    }
+
+    ei_status.dmaing |= 0x01;
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);
+    outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+    outb_p(0, nic_base + EN0_RCNTHI);
+    outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */
+    outb_p(ring_page, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+    if (ei_status.word16) {
+        ptrs = (short*)hdr;
+        for(cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr)>>1); cnt++)
+            *ptrs++ = inw(NE_BASE + NE_DATAPORT);
+    } else {
+        ptrc = (char*)hdr;
+        for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++)
+            *ptrc++ = inb(NE_BASE + NE_DATAPORT);
+    }
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+
+    hdr->count = WORDSWAP(hdr->count);
+
+    ei_status.dmaing &= ~0x01;
+}
+
+/*-*************************************************************************-*/
+/* Block input and output, similar to the Crynwr packet driver.  If you
+   are porting to a new ethercard, look at the packet driver source for hints.
+   The NEx000 doesn't share the on-board packet memory -- you have to put
+   the packet out through the "remote DMA" dataport using outb. */
+
+static void
+apne_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+    int nic_base = dev->base_addr;
+    char *buf = skb->data;
+    char *ptrc;
+    short *ptrs;
+    int cnt;
+
+    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+    if (ei_status.dmaing) {
+	printk("%s: DMAing conflict in ne_block_input "
+	   "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+	   dev->name, ei_status.dmaing, ei_status.irqlock,
+	   dev->interrupt);
+	return;
+    }
+    ei_status.dmaing |= 0x01;
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);
+    outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+    outb_p(count >> 8, nic_base + EN0_RCNTHI);
+    outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+    outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+    if (ei_status.word16) {
+      ptrs = (short*)buf;
+      for (cnt = 0; cnt < (count>>1); cnt++)
+        *ptrs++ = inw(NE_BASE + NE_DATAPORT);
+      if (count & 0x01) {
+	buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+      }
+    } else {
+      ptrc = (char*)buf;
+      for (cnt = 0; cnt < count; cnt++)
+        *ptrc++ = inb(NE_BASE + NE_DATAPORT);
+    }
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+}
+
+/*-*************************************************************************-*/
+
+static void
+apne_block_output(struct device *dev, int count,
+		const unsigned char *buf, const int start_page)
+{
+    int nic_base = NE_BASE;
+    unsigned long dma_start;
+    char *ptrc;
+    short *ptrs;
+    int cnt;
+
+    /* Round the count up for word writes.  Do we need to do this?
+       What effect will an odd byte count have on the 8390?
+       I should check someday. */
+    if (ei_status.word16 && (count & 0x01))
+      count++;
+
+    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+    if (ei_status.dmaing) {
+	printk("%s: DMAing conflict in ne_block_output."
+	   "[DMAstat:%d][irqlock:%d][intr:%d]\n",
+	   dev->name, ei_status.dmaing, ei_status.irqlock,
+	   dev->interrupt);
+	return;
+    }
+    ei_status.dmaing |= 0x01;
+    /* We should already be in page 0, but to be safe... */
+    outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+   /* Now the normal output. */
+    outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+    outb_p(count >> 8,   nic_base + EN0_RCNTHI);
+    outb_p(0x00, nic_base + EN0_RSARLO);
+    outb_p(start_page, nic_base + EN0_RSARHI);
+
+    outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+    if (ei_status.word16) {
+        ptrs = (short*)buf;
+        for (cnt = 0; cnt < count>>1; cnt++)
+            outw(*ptrs++, NE_BASE+NE_DATAPORT);
+    } else {
+        ptrc = (char*)buf;
+        for (cnt = 0; cnt < count; cnt++)
+	    outb(*ptrc++, NE_BASE + NE_DATAPORT);
+    }
+
+    dma_start = jiffies;
+
+    while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+	if (jiffies - dma_start > 2*HZ/100) {		/* 20ms */
+		printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+		apne_reset_8390(dev);
+		apNS8390_init(dev,1);
+		break;
+	}
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+    return;
+}
+
+/*-*************************************************************************-*/
+/* Open/initialize the board.  This routine goes all-out, setting everything
+   up anew at each open, even though many of these registers should only
+   need to be set once at boot.
+   */
+int ei_open(struct device *dev)
+{
+    struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+    /* This can't happen unless somebody forgot to call ethdev_init(). */
+    if (ei_local == NULL) {
+	printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name);
+	return -ENXIO;
+    }
+
+    apNS8390_init(dev, 1);
+    dev->start = 1;
+    ei_local->irqlock = 0;
+    return 0;
+}
+
+/* Opposite of above. Only used when "ifconfig <devname> down" is done. */
+int ei_close(struct device *dev)
+{
+    apNS8390_init(dev, 0);
+    dev->start = 0;
+    return 0;
+}
+
+/*-*************************************************************************-*/
+
+static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+    int e8390_base = dev->base_addr;
+    struct ei_device *ei_local = (struct ei_device *) dev->priv;
+    int length, send_length, output_page;
+
+/*
+ *  We normally shouldn't be called if dev->tbusy is set, but the
+ *  existing code does anyway. If it has been too long since the
+ *  last Tx, we assume the board has died and kick it.
+ */
+ 
+    if (dev->tbusy) {	/* Do timeouts, just like the 8003 driver. */
+		int txsr = inb(e8390_base+EN0_TSR), isr;
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < TX_TIMEOUT ||	(tickssofar < (TX_TIMEOUT+5) && ! (txsr & ENTSR_PTX))) {
+			return 1;
+		}
+		isr = inb(e8390_base+EN0_ISR);
+		if (dev->start == 0) {
+			printk("%s: xmit on stopped card\n", dev->name);
+			return 1;
+		}
+
+		/*
+		 * Note that if the Tx posted a TX_ERR interrupt, then the
+		 * error will have been handled from the interrupt handler.
+		 * and not here.
+		 */
+
+		printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
+		   dev->name, (txsr & ENTSR_ABT) ? "excess collisions." :
+		   (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar);
+
+		if (!isr && !ei_local->stat.tx_packets) {
+		   /* The 8390 probably hasn't gotten on the cable yet. */
+		   ei_local->interface_num ^= 1;   /* Try a different xcvr.  */
+		}
+
+		/* Try to restart the card.  Perhaps the user has fixed something. */
+		ei_reset_8390(dev);
+		apNS8390_init(dev, 1);
+		dev->trans_start = jiffies;
+    }
+
+    /* Sending a NULL skb means some higher layer thinks we've missed an
+       tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
+       itself. */
+    if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+    }
+
+    length = skb->len;
+    if (skb->len <= 0)
+		return 0;
+
+    /* Mask interrupts from the ethercard. */
+    outb_p(0x00, e8390_base + EN0_IMR);
+    /* disable PCMCIA interrupt */
+    outb(GAYLE_CS_MASTER|GAYLE_CS_CCDET, GAYLE_IRQEN);
+    if (dev->interrupt) {
+	printk("%s: Tx request while isr active.\n",dev->name);
+	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+	return 1;
+    }
+    ei_local->irqlock = 1;
+
+    send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
+
+#ifdef EI_PINGPONG
+
+    /*
+     * We have two Tx slots available for use. Find the first free
+     * slot, and then perform some sanity checks. With two Tx bufs,
+     * you get very close to transmitting back-to-back packets. With
+     * only one Tx buf, the transmitter sits idle while you reload the
+     * card, leaving a substantial gap between each transmitted packet.
+     */
+
+    if (ei_local->tx1 == 0) {
+	output_page = ei_local->tx_start_page;
+	ei_local->tx1 = send_length;
+	if (ei_debug  &&  ei_local->tx2 > 0)
+		printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
+			dev->name, ei_local->tx2, ei_local->lasttx, ei_local->txing);
+    } else if (ei_local->tx2 == 0) {
+	output_page = ei_local->tx_start_page + TX_1X_PAGES;
+	ei_local->tx2 = send_length;
+	if (ei_debug  &&  ei_local->tx1 > 0)
+		printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
+			dev->name, ei_local->tx1, ei_local->lasttx, ei_local->txing);
+    } else {	/* We should never get here. */
+	if (ei_debug)
+		printk("%s: No Tx buffers free! irq=%d tx1=%d tx2=%d last=%d\n",
+			dev->name, dev->interrupt, ei_local->tx1, ei_local->tx2, ei_local->lasttx);
+	ei_local->irqlock = 0;
+	dev->tbusy = 1;
+	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+        /* enable PCMCIA interrupt */
+        outb(GAYLE_CS_MASTER|GAYLE_CS_CCDET|GAYLE_CS_IRQ, GAYLE_IRQEN);
+	return 1;
+    }
+
+    /*
+     * Okay, now upload the packet and trigger a send if the transmitter
+     * isn't already sending. If it is busy, the interrupt handler will
+     * trigger the send later, upon receiving a Tx done interrupt.
+     */
+
+    ei_block_output(dev, length, skb->data, output_page);
+    if (! ei_local->txing) {
+	ei_local->txing = 1;
+	NS8390_trigger_send(dev, send_length, output_page);
+	dev->trans_start = jiffies;
+	if (output_page == ei_local->tx_start_page) {
+		ei_local->tx1 = -1;
+		ei_local->lasttx = -1;
+	} else {
+		ei_local->tx2 = -1;
+		ei_local->lasttx = -2;
+	}
+    } else
+	ei_local->txqueue++;
+
+    dev->tbusy = (ei_local->tx1  &&  ei_local->tx2);
+
+#else	/* EI_PINGPONG */
+
+    /*
+     * Only one Tx buffer in use. You need two Tx bufs to come close to
+     * back-to-back transmits. Expect a 20 -> 25% performance hit on
+     * reasonable hardware if you only use one Tx buffer.
+     */
+
+    ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
+    ei_local->txing = 1;
+    NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
+    dev->trans_start = jiffies;
+    dev->tbusy = 1;
+
+#endif	/* EI_PINGPONG */
+
+    /* Turn 8390 interrupts back on. */
+    ei_local->irqlock = 0;
+    outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+    /* enable PCMCIA interrupt */
+    outb(GAYLE_CS_MASTER|GAYLE_CS_CCDET|GAYLE_CS_IRQ, GAYLE_IRQEN);
+
+    dev_kfree_skb (skb, FREE_WRITE);
+    
+    return 0;
+}
+
+/*-*************************************************************************-*/
+
+static struct enet_statistics *get_stats(struct device *dev)
+{
+    int ioaddr = dev->base_addr;
+    struct ei_device *ei_local = (struct ei_device *) dev->priv;
+    
+    /* If the card is stopped, just return the present stats. */
+    if (dev->start == 0) return &ei_local->stat;
+
+    /* Read the counter registers, assuming we are in page 0. */
+    ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
+    ei_local->stat.rx_crc_errors   += inb_p(ioaddr + EN0_COUNTER1);
+    ei_local->stat.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
+
+    return &ei_local->stat;
+}
+
+/*-*************************************************************************-*/
+/*
+ * A transmitter error has happened. Most likely excess collisions (which
+ * is a fairly normal condition). If the error is one where the Tx will
+ * have been aborted, we try and send another one right away, instead of
+ * letting the failed packet sit and collect dust in the Tx buffer. This
+ * is a much better solution as it avoids kernel based Tx timeouts, and
+ * an unnecessary card reset.
+ */
+
+static void ei_tx_err(struct device *dev)
+{
+    int e8390_base = dev->base_addr;
+    unsigned char txsr = inb_p(e8390_base+EN0_TSR);
+    unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+    struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+#ifdef VERBOSE_ERROR_DUMP
+    printk(KERN_DEBUG "%s: transmitter error (%#2x): ", dev->name, txsr);
+    if (txsr & ENTSR_ABT)
+		printk("excess-collisions ");
+    if (txsr & ENTSR_ND)
+		printk("non-deferral ");
+    if (txsr & ENTSR_CRS)
+		printk("lost-carrier ");
+    if (txsr & ENTSR_FU)
+		printk("FIFO-underrun ");
+    if (txsr & ENTSR_CDH)
+		printk("lost-heartbeat ");
+    printk("\n");
+#endif
+
+    outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+
+    if (tx_was_aborted)
+		ei_tx_intr(dev);
+
+    /*
+     * Note: NCR reads zero on 16 collisions so we add them
+     * in by hand. Somebody might care...
+     */
+    if (txsr & ENTSR_ABT)
+	ei_local->stat.collisions += 16;
+	
+}
+
+/*-*************************************************************************-*/
+/* We have finished a transmit: check for errors and then trigger the next
+   packet to be sent. */
+static void ei_tx_intr(struct device *dev)
+{
+    int e8390_base = dev->base_addr;
+    int status = inb(e8390_base + EN0_TSR);
+    struct ei_device *ei_local = (struct ei_device *) dev->priv;
+    
+    outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
+
+#ifdef EI_PINGPONG
+
+    /*
+     * There are two Tx buffers, see which one finished, and trigger
+     * the send of another one if it exists.
+     */
+    ei_local->txqueue--;
+    if (ei_local->tx1 < 0) {
+	if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+		printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
+			   ei_local->name, ei_local->lasttx, ei_local->tx1);
+	ei_local->tx1 = 0;
+	dev->tbusy = 0;
+	if (ei_local->tx2 > 0) {
+		ei_local->txing = 1;
+		NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+		dev->trans_start = jiffies;
+		ei_local->tx2 = -1,
+		ei_local->lasttx = 2;
+	} else
+		ei_local->lasttx = 20, ei_local->txing = 0;
+    } else if (ei_local->tx2 < 0) {
+	if (ei_local->lasttx != 2  &&  ei_local->lasttx != -2)
+		printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
+			   ei_local->name, ei_local->lasttx, ei_local->tx2);
+	ei_local->tx2 = 0;
+	dev->tbusy = 0;
+	if (ei_local->tx1 > 0) {
+		ei_local->txing = 1;
+		NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+		dev->trans_start = jiffies;
+		ei_local->tx1 = -1;
+		ei_local->lasttx = 1;
+	} else
+		ei_local->lasttx = 10, ei_local->txing = 0;
+    } else
+	printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
+		   dev->name, ei_local->lasttx);
+
+#else	/* EI_PINGPONG */
+    /*
+     *  Single Tx buffer: mark it free so another packet can be loaded.
+     */
+    ei_local->txing = 0;
+    dev->tbusy = 0;
+#endif
+
+    /* Minimize Tx latency: update the statistics after we restart TXing. */
+    if (status & ENTSR_COL)
+	ei_local->stat.collisions++;
+    if (status & ENTSR_PTX)
+	ei_local->stat.tx_packets++;
+    else {
+	ei_local->stat.tx_errors++;
+	if (status & ENTSR_ABT) ei_local->stat.tx_aborted_errors++;
+	if (status & ENTSR_CRS) ei_local->stat.tx_carrier_errors++;
+	if (status & ENTSR_FU)  ei_local->stat.tx_fifo_errors++;
+	if (status & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++;
+	if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++;
+    }
+
+    mark_bh (NET_BH);
+}
+
+/*-*************************************************************************-*/
+/* We have a good packet(s), get it/them out of the buffers. */
+
+static void ei_receive(struct device *dev)
+{
+    int e8390_base = dev->base_addr;
+    struct ei_device *ei_local = (struct ei_device *) dev->priv;
+    unsigned char rxing_page, this_frame, next_frame;
+    unsigned short current_offset;
+    int rx_pkt_count = 0;
+    struct e8390_pkt_hdr rx_frame;
+    int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
+
+    while (++rx_pkt_count < 10) {
+		int pkt_len;
+
+		/* Get the rx page (incoming packet pointer). */
+		outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
+		rxing_page = inb_p(e8390_base + EN1_CURPAG);
+		outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+		
+		/* Remove one frame from the ring.  Boundary is always a page behind. */
+		this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
+		if (this_frame >= ei_local->stop_page)
+			this_frame = ei_local->rx_start_page;
+		
+		/* Someday we'll omit the previous, iff we never get this message.
+		   (There is at least one clone claimed to have a problem.)  */
+		if (ei_debug > 0  &&  this_frame != ei_local->current_page)
+			printk("%s: mismatched read page pointers %2x vs %2x.\n",
+				   dev->name, this_frame, ei_local->current_page);
+		
+		if (this_frame == rxing_page)	/* Read all the frames? */
+			break;				/* Done for now */
+		
+		current_offset = this_frame << 8;
+		ei_get_8390_hdr(dev, &rx_frame, this_frame);
+		
+		pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
+		
+		next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+		
+		/* Check for bogosity warned by 3c503 book: the status byte is never
+		   written.  This happened a lot during testing! This code should be
+		   cleaned up someday. */
+		if (rx_frame.next != next_frame
+			&& rx_frame.next != next_frame + 1
+			&& rx_frame.next != next_frame - num_rx_pages
+			&& rx_frame.next != next_frame + 1 - num_rx_pages) {
+			ei_local->current_page = rxing_page;
+			outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
+			ei_local->stat.rx_errors++;
+			continue;
+		}
+
+		if (pkt_len < 60  ||  pkt_len > 1518) {
+			if (ei_debug)
+				printk("%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
+					   dev->name, rx_frame.count, rx_frame.status,
+					   rx_frame.next);
+			ei_local->stat.rx_errors++;
+		} else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
+			struct sk_buff *skb;
+			
+			skb = dev_alloc_skb(pkt_len+2);
+			if (skb == NULL) {
+				if (ei_debug > 1)
+					printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+						   dev->name, pkt_len);
+				ei_local->stat.rx_dropped++;
+				break;
+			} else {
+				skb_reserve(skb,2);	/* IP headers on 16 byte boundaries */
+				skb->dev = dev;
+				skb_put(skb, pkt_len);	/* Make room */
+				ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
+				skb->protocol=eth_type_trans(skb,dev);
+				netif_rx(skb);
+				ei_local->stat.rx_packets++;
+			}
+		} else {
+			int errs = rx_frame.status;
+			if (ei_debug)
+				printk("%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+					   dev->name, rx_frame.status, rx_frame.next,
+					   rx_frame.count);
+			if (errs & ENRSR_FO)
+				ei_local->stat.rx_fifo_errors++;
+		}
+		next_frame = rx_frame.next;
+		
+		/* This _should_ never happen: it's here for avoiding bad clones. */
+		if (next_frame >= ei_local->stop_page) {
+			printk("%s: next frame inconsistency, %#2x\n", dev->name,
+				   next_frame);
+			next_frame = ei_local->rx_start_page;
+		}
+		ei_local->current_page = next_frame;
+		outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+    }
+
+    /* We used to also ack ENISR_OVER here, but that would sometimes mask
+    a real overrun, leaving the 8390 in a stopped state with rec'vr off. */
+    outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR);
+    return;
+}
+
+/*-*************************************************************************-*/
+/* 
+ * We have a receiver overrun: we have to kick the 8390 to get it started
+ * again. Problem is that you have to kick it exactly as NS prescribes in
+ * the updated datasheets, or "the NIC may act in an unpredictable manner."
+ * This includes causing "the NIC to defer indefinitely when it is stopped
+ * on a busy network."  Ugh.
+ */
+static void ei_rx_overrun(struct device *dev)
+{
+    int e8390_base = dev->base_addr;
+    unsigned long wait_start_time;
+    unsigned char was_txing, must_resend = 0;
+    struct ei_device *ei_local = (struct ei_device *) dev->priv;
+    
+    /*
+     * Record whether a Tx was in progress and then issue the
+     * stop command.
+     */
+    was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+    
+    if (ei_debug > 1)
+	printk("%s: Receiver overrun.\n", dev->name);
+    ei_local->stat.rx_over_errors++;
+    
+    /* 
+     * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
+     * Early datasheets said to poll the reset bit, but now they say that
+     * it "is not a reliable indicator and subsequently should be ignored."
+     * We wait at least 10ms.
+     */
+    wait_start_time = jiffies;
+    while (jiffies - wait_start_time <= 1*HZ/100)
+	barrier();
+
+    /*
+     * Reset RBCR[01] back to zero as per magic incantation.
+     */
+    outb_p(0x00, e8390_base+EN0_RCNTLO);
+    outb_p(0x00, e8390_base+EN0_RCNTHI);
+
+    /*
+     * See if any Tx was interrupted or not. According to NS, this
+     * step is vital, and skipping it will cause no end of havoc.
+     */
+    if (was_txing) { 
+	unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
+	if (!tx_completed) must_resend = 1;
+    }
+
+    /*
+     * Have to enter loopback mode and then restart the NIC before
+     * you are allowed to slurp packets up off the ring.
+     */
+    outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
+    outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+
+    /*
+     * Clear the Rx ring of all the debris, and ack the interrupt.
+     */
+    ei_receive(dev);
+    outb_p(ENISR_OVER, e8390_base+EN0_ISR);
+
+    /*
+     * Leave loopback mode, and resend any packet that got stopped.
+     */
+    outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); 
+    if (must_resend)
+    	outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+	
+}
+
+/*-*************************************************************************-*/
+/*
+ *	Set or clear the multicast filter for this adaptor.
+ */
+ 
+static void set_multicast_list(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+    
+	if(dev->flags&IFF_PROMISC)
+	{
+		outb_p(E8390_RXCONFIG | 0x18, ioaddr + EN0_RXCR);
+	}
+	else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
+	{
+		/* The multicast-accept list is initialized to accept-all, and we
+		   rely on higher-level filtering for now. */
+		outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR);
+	} 
+	else
+		outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
+}
+
+/*-*************************************************************************-*/
+
+/* Initialize the rest of the 8390 device structure. */
+int ethdev_init(struct device *dev)
+{
+    if (ei_debug > 1)
+		printk(version);
+    
+    if (dev->priv == NULL) {
+		struct ei_device *ei_local;
+		
+		dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
+		if (dev->priv == NULL)
+			return -ENOMEM;
+		memset(dev->priv, 0, sizeof(struct ei_device));
+		ei_local = (struct ei_device *)dev->priv;
+    }
+    
+    dev->hard_start_xmit = &ei_start_xmit;
+    dev->get_stats	= get_stats;
+    dev->set_multicast_list = &set_multicast_list;
+
+    ether_setup(dev);
+        
+    return 0;
+}
+
+/*-*************************************************************************-*/
+
+/* Trigger a transmit start, assuming the length is valid. */
+static void NS8390_trigger_send(struct device *dev, unsigned int length,
+								int start_page)
+{
+    int e8390_base = dev->base_addr;
+    
+    outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
+    
+    if (inb_p(e8390_base) & E8390_TRANS) {
+		printk("%s: trigger_send() called with the transmitter busy.\n",
+			   dev->name);
+		return;
+    }
+    outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+    outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+    outb_p(start_page, e8390_base + EN0_TPSR);
+    outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
+    return;
+}
+
+/*-*************************************************************************-*/
+/* The typical workload of the driver:
+   Handle the ether interface interrupts. */
+
+void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+    struct device *dev;
+    int e8390_base;
+    int interrupts, nr_serviced = 0;
+    struct ei_device *ei_local;
+    unsigned char pcmcia_intreq;
+
+    dev = (struct device *) dev_id;
+
+    if (dev == NULL) {
+		printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+    }
+
+    pcmcia_intreq = inb(GAYLE_IRQREQ);
+
+
+    if (!(pcmcia_intreq & GAYLE_CS_IRQ)) {
+        if (pcmcia_intreq & 0x2c)
+	    outb((pcmcia_intreq ^ 0x2c) | (0xc0), GAYLE_IRQREQ);  /* clear PCMCIA int req */
+    	return;
+    }
+    if (ei_debug > 3)
+        printk("pcmcia intreq = %x\n", pcmcia_intreq);
+
+    e8390_base = dev->base_addr;
+    ei_local = (struct ei_device *) dev->priv;
+    if (dev->interrupt || ei_local->irqlock) {
+		/* The "irqlock" check is only for testing. */
+		printk(ei_local->irqlock
+			   ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
+			   : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
+			   dev->name, inb_p(e8390_base + EN0_ISR),
+			   inb_p(e8390_base + EN0_IMR));
+		return;
+    }
+
+    dev->interrupt = 1;
+
+    /* Change to page 0 and read the intr status reg. */
+    outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+    if (ei_debug > 3)
+		printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
+			   inb_p(e8390_base + EN0_ISR));
+
+    /* !!Assumption!! -- we stay in page 0.	 Don't break this. */
+    while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
+		   && ++nr_serviced < MAX_SERVICE) {
+		if (dev->start == 0) {
+			printk("%s: interrupt from stopped card\n", dev->name);
+			/*interrupts = 0;*/
+			break;
+		}
+		if (interrupts & ENISR_OVER) {
+			ei_rx_overrun(dev);
+		} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
+			/* Got a good (?) packet. */
+			ei_receive(dev);
+		}
+		/* Push the next to-transmit packet through. */
+		if (interrupts & ENISR_TX) {
+			ei_tx_intr(dev);
+		} else if (interrupts & ENISR_TX_ERR) {
+			ei_tx_err(dev);
+		}
+
+		if (interrupts & ENISR_COUNTERS) {
+			ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
+			ei_local->stat.rx_crc_errors   += inb_p(e8390_base + EN0_COUNTER1);
+			ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
+			outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
+		}
+		
+		/* Ignore any RDC interrupts that make it back to here. */
+		if (interrupts & ENISR_RDC) {
+			outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+		}
+
+		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+
+		pcmcia_intreq = inb(GAYLE_IRQREQ);
+		outb((pcmcia_intreq ^ 0x2c) | (0xc0), GAYLE_IRQREQ); /* clear PCMCIA int req */
+    }
+    
+    if (interrupts && ei_debug) {
+		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+		if (nr_serviced >= MAX_SERVICE) {
+			printk("%s: Too much work at interrupt, status %#2.2x\n",
+				   dev->name, interrupts);
+			outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+		} else {
+			printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
+			outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+		}
+		pcmcia_intreq = inb(GAYLE_IRQREQ);
+		outb((pcmcia_intreq ^ 0x2c) | (0xc0), GAYLE_IRQREQ); /* clear PCMCIA int req */
+    }
+    dev->interrupt = 0;
+    return;
+}
+
+/*-*************************************************************************-*/
+
+#ifdef MODULE
+static char devicename[9] = {0, };
+
+static struct device apne_dev =
+{
+	devicename,
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, apne_probe,
+};
+
+int init_module(void)
+{
+	int err;
+	if ((err = register_netdev(&apne_dev))) {
+		if (err == -EIO)
+			printk("No PCMCIA NEx000 ethernet card found.\n");
+		return (err);
+	}
+	return (0);
+}
+
+void cleanup_module(void)
+{
+	unregister_netdev(&apne_dev);
+
+	/* disable irq */
+	outb(GAYLE_CS_MASTER|GAYLE_CS_CCDET, GAYLE_IRQEN);
+
+	free_irq(IRQ_AMIGA_PORTS, &apne_dev);
+
+	pcmcia_reset();
+
+}
+
+#endif
+
+/*-*************************************************************************-*/
+
+static void pcmcia_reset(void)
+{
+	unsigned long reset_start_time = jiffies;
+
+	outb(0x00, GAYLE_RESET);
+	while (jiffies - reset_start_time < 1*HZ/100);
+	inb(GAYLE_RESET);
+}
+
+/*-*************************************************************************-*/
+
+static int pcmcia_init(void)
+{
+	unsigned char magic;
+	unsigned char tuple[32];
+	unsigned short offset;
+
+	pcmcia_reset();
+
+	/* init config */
+	outb(GAYLE_CFG_0V|GAYLE_CFG_250NS, GAYLE_CONFIG);
+
+	/* disable write protect */
+	outb(GAYLE_CS_WR|GAYLE_CS_DIGAUDIO, GAYLE_CARDSTATUS);
+
+
+	/* get and write magic byte to enable IO ports */
+
+	if (pcmcia_readtuple(CISTPL_CONFIG, tuple, 32) < 3)
+		return 0;
+
+	magic = tuple[2] & 0x3f;
+
+	if (pcmcia_readtuple(CISTPL_CONF_MAP, tuple, 32) < 6)
+		return 0;
+
+	offset = (tuple[5] << 8) | tuple[4];
+
+	outb(magic, GAYLE_ATTRIBUTE+offset);
+
+	return 1;
+}
+
+/*-*************************************************************************-*/
+/* read a tuple, including tuple header. return nb bytes read */
+/* be carefull as this may trigger a GAYLE_CS_WR interrupt ! */
+
+static int pcmcia_readtuple(unsigned char tuple_id, void *tuple, int max_len)
+{
+	unsigned char b, *dest;
+	int cnt, pos, len;
+
+	dest = tuple;
+	pos = 0;
+
+	if (ei_debug > 1)
+		printk("search tuple %x\n", tuple_id);
+
+	b = inb(GAYLE_ATTRIBUTE+pos);
+
+	while((b != 0xff) && (pos < 0x10000)) {
+		len = (int)inb(GAYLE_ATTRIBUTE+pos+2) + 2;
+		if (ei_debug > 1)
+			printk("tupleid = %x, len = %x\n", b, len);
+		if (b == tuple_id) {
+			len = (len > max_len)?max_len:len;
+			for (cnt = 0; cnt < len; cnt++) {
+				*dest++ = inb(GAYLE_ATTRIBUTE+pos+(cnt<<1));
+			}
+
+			if (ei_debug > 1)
+				printk("readtuple done\n");
+			return len;
+		}
+		pos += len<<1;
+		b = inb(GAYLE_ATTRIBUTE+pos);
+	}
+
+	if (ei_debug > 1)
+		printk("readtuple failed\n");
+	return 0;
+}
+
+/*-*************************************************************************-*/
diff -ruN linux-2.0.29/drivers/net/apne.h linux/drivers/net/apne.h
--- linux-2.0.29/drivers/net/apne.h	Thu Jan  1 01:00:00 1970
+++ linux/drivers/net/apne.h	Sun Dec  7 11:14:26 1997
@@ -0,0 +1,221 @@
+
+/*
+ * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200
+ *
+ * (C) Copyright 1997 Alain Malek
+ *                    (Alain.Malek@cryogen.com)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is based on
+ *
+ * ne.c:       A general non-shared-memory NS8390 ethernet driver for linux
+ *             Written 1992-94 by Donald Becker.
+ *
+ * 8390.c:     A general NS8390 ethernet driver core for linux.
+ *             Written 1992-94 by Donald Becker.
+ *
+ * cnetdevice: A Sana-II ethernet driver for AmigaOS
+ *             Written by Bruce Abbott (bhabbott@inhb.co.nz)
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of the Linux
+ * distribution for more details.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+#ifndef _apne_h
+#define _apne_h
+
+#include <linux/if_ether.h>
+#include <linux/ioport.h>
+#include <linux/skbuff.h>
+#include <asm/amigayle.h>
+
+
+/* nex000 registers */
+
+#define NE_BASE	 (dev->base_addr)
+#define NE_CMD	 	0x00
+#define NE_DATAPORT	0x10		/* NatSemi-defined port window offset. */
+#define NE_RESET	0x1f+GAYLE_ODD 	/* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT	0x20
+
+#define NE1SM_START_PG	0x20	/* First page of TX buffer */
+#define NE1SM_STOP_PG 	0x40	/* Last page +1 of RX ring */
+#define NESM_START_PG	0x40	/* First page of TX buffer */
+#define NESM_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+/* 8390 registers */
+
+/* Some generic ethernet register configurations. */
+#define E8390_TX_IRQ_MASK 0xa		/* For register EN0_ISR */
+#define E8390_RX_IRQ_MASK  0x5
+#define E8390_RXCONFIG 0x4		/* EN0_RXCR: broadcasts, no multicast,errors */
+#define E8390_RXOFF 0x20		/* EN0_RXCR: Accept no packets */
+#define E8390_TXCONFIG 0x00		/* EN0_TXCR: Normal transmit mode */
+#define E8390_TXOFF 0x02		/* EN0_TXCR: Transmitter off */
+
+/*  Register accessed at EN_CMD, the 8390 base addr.  */
+#define E8390_STOP	0x01		/* Stop and reset the chip */
+#define E8390_START	0x02		/* Start the chip, clear reset */
+#define E8390_TRANS	0x04		/* Transmit a frame */
+#define E8390_RREAD	0x08		/* Remote read */
+#define E8390_RWRITE	0x10		/* Remote write  */
+#define E8390_NODMA	0x20		/* Remote DMA */
+#define E8390_PAGE0	0x00		/* Select page chip registers */
+#define E8390_PAGE1	0x40		/* using the two high-order bits */
+#define E8390_PAGE2	0x80		/* Page 3 is invalid. */
+
+#define E8390_CMD	0x00		/* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO	0x01+GAYLE_ODD	/* Low byte of current local dma addr  RD */
+#define EN0_STARTPG	0x01+GAYLE_ODD	/* Starting page of ring bfr WR */
+#define EN0_CLDAHI	0x02		/* High byte of current local dma addr  RD */
+#define EN0_STOPPG	0x02		/* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY	0x03+GAYLE_ODD	/* Boundary page of ring bfr RD WR */
+#define EN0_TSR		0x04		/* Transmit status reg RD */
+#define EN0_TPSR	0x04		/* Transmit starting page WR */
+#define EN0_NCR		0x05+GAYLE_ODD	/* Number of collision reg RD */
+#define EN0_TCNTLO	0x05+GAYLE_ODD	/* Low  byte of tx byte count WR */
+#define EN0_FIFO	0x06		/* FIFO RD */
+#define EN0_TCNTHI	0x06		/* High byte of tx byte count WR */
+#define EN0_ISR		0x07+GAYLE_ODD	/* Interrupt status reg RD WR */
+#define EN0_CRDALO	0x08		/* low byte of current remote dma address RD */
+#define EN0_RSARLO	0x08		/* Remote start address reg 0 */
+#define EN0_CRDAHI	0x09+GAYLE_ODD	/* high byte, current remote dma address RD */
+#define EN0_RSARHI	0x09+GAYLE_ODD	/* Remote start address reg 1 */
+#define EN0_RCNTLO	0x0a		/* Remote byte count reg WR */
+#define EN0_RCNTHI	0x0b+GAYLE_ODD	/* Remote byte count reg WR */
+#define EN0_RSR		0x0c		/* rx status reg RD */
+#define EN0_RXCR	0x0c		/* RX configuration reg WR */
+#define EN0_TXCR	0x0d+GAYLE_ODD	/* TX configuration reg WR */
+#define EN0_COUNTER0	0x0d+GAYLE_ODD	/* Rcv alignment error counter RD */
+#define EN0_DCFG	0x0e		/* Data configuration reg WR */
+#define EN0_COUNTER1	0x0e		/* Rcv CRC error counter RD */
+#define EN0_IMR		0x0f+GAYLE_ODD	/* Interrupt mask reg WR */
+#define EN0_COUNTER2	0x0f+GAYLE_ODD	/* Rcv missed frame error counter RD */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX	0x01		/* Receiver, no error */
+#define ENISR_TX	0x02		/* Transmitter, no error */
+#define ENISR_RX_ERR	0x04		/* Receiver, with error */
+#define ENISR_TX_ERR	0x08		/* Transmitter, with error */
+#define ENISR_OVER	0x10		/* Receiver overwrote the ring */
+#define ENISR_COUNTERS	0x20		/* Counters need emptying */
+#define ENISR_RDC	0x40		/* remote dma complete */
+#define ENISR_RESET	0x80		/* Reset completed */
+#define ENISR_ALL	0x3f		/* Interrupts we will enable */
+
+/* Bits in EN0_DCFG - Data config register */
+#define ENDCFG_WTS	0x01		/* word transfer mode selection */
+
+/* Page 1 register offsets. */
+#define EN1_PHYS0	0x01+GAYLE_ODD	/* This board's physical enet addr RD WR */
+#define EN1_PHYS1	0x02		/* This board's physical enet addr RD WR */
+#define EN1_PHYS2	0x03+GAYLE_ODD	/* This board's physical enet addr RD WR */
+#define EN1_PHYS3	0x04		/* This board's physical enet addr RD WR */
+#define EN1_PHYS4	0x05+GAYLE_ODD	/* This board's physical enet addr RD WR */
+#define EN1_PHYS5	0x06		/* This board's physical enet addr RD WR */
+#define EN1_CURPAG	0x07+GAYLE_ODD	/* Current memory page RD WR */
+#define EN1_MULT0	0x08		/* Multicast filter mask array RD WR */
+#define EN1_MULT1	0x09+GAYLE_ODD	/* Multicast filter mask array RD WR */
+#define EN1_MULT2	0x0a		/* Multicast filter mask array RD WR */
+#define EN1_MULT3	0x0b+GAYLE_ODD	/* Multicast filter mask array RD WR */
+#define EN1_MULT4	0x0c		/* Multicast filter mask array RD WR */
+#define EN1_MULT5	0x0d+GAYLE_ODD	/* Multicast filter mask array RD WR */
+#define EN1_MULT6	0x0e		/* Multicast filter mask array RD WR */
+#define EN1_MULT7	0x0f+GAYLE_ODD	/* Multicast filter mask array RD WR */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK	0x01		/* Received a good packet */
+#define ENRSR_CRC	0x02		/* CRC error */
+#define ENRSR_FAE	0x04		/* frame alignment error */
+#define ENRSR_FO	0x08		/* FIFO overrun */
+#define ENRSR_MPA	0x10		/* missed pkt */
+#define ENRSR_PHY	0x20		/* physical/multicase address */
+#define ENRSR_DIS	0x40		/* receiver disable. set in monitor mode */
+#define ENRSR_DEF	0x80		/* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01			/* Packet transmitted without error */
+#define ENTSR_ND  0x02			/* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04			/* The transmit collided at least once. */
+#define ENTSR_ABT 0x08  		/* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10			/* The carrier sense was lost. */
+#define ENTSR_FU  0x20  		/* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40			/* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80  		/* There was an out-of-window collision. */
+
+
+#define TX_2X_PAGES 12
+#define TX_1X_PAGES 6
+
+/* Should always use two Tx slots to get back-to-back transmits. */
+#define EI_PINGPONG
+
+#ifdef EI_PINGPONG
+#define TX_PAGES TX_2X_PAGES
+#else
+#define TX_PAGES TX_1X_PAGES
+#endif
+
+#define ETHER_ADDR_LEN 6
+
+/* The 8390 specific per-packet-header format. */
+struct e8390_pkt_hdr {
+  unsigned char status; /* status */
+  unsigned char next;   /* pointer to next packet. */
+  unsigned short count; /* header + packet length in bytes */
+};
+
+/* Most of these entries should be in 'struct device' (or most of the
+   things in there should be here!) */
+/* You have one of these per-board */
+struct ei_device {
+  const char *name;
+  void (*reset_8390)(struct device *);
+  void (*get_8390_hdr)(struct device *, struct e8390_pkt_hdr *, int);
+  void (*block_output)(struct device *, int, const unsigned char *, int);
+  void (*block_input)(struct device *, int, struct sk_buff *, int);
+  unsigned open:1;
+  unsigned word16:1;  /* We have the 16-bit (vs 8-bit) version of the card. */
+  unsigned txing:1;		/* Transmit Active */
+  unsigned irqlock:1;		/* 8390's intrs disabled when '1'. */
+  unsigned dmaing:1;		/* Remote DMA Active */
+  unsigned char tx_start_page, rx_start_page, stop_page;
+  unsigned char current_page;	/* Read pointer in buffer  */
+  unsigned char interface_num;	/* Net port (AUI, 10bT.) to use. */
+  unsigned char txqueue;	/* Tx Packet buffer queue length. */
+  short tx1, tx2;		/* Packet lengths for ping-pong tx. */
+  short lasttx;			/* Alpha version consistency check. */
+  unsigned char reg0;		/* Register '0' in a WD8013 */
+  unsigned char reg5;		/* Register '5' in a WD8013 */
+  unsigned char saved_irq;	/* Original dev->irq value. */
+  /* The new statistics table. */
+  struct enet_statistics stat;
+};
+
+/* The maximum number of 8390 interrupt service routines called per IRQ. */
+#define MAX_SERVICE 12
+
+/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */
+#define TX_TIMEOUT (20*HZ/100)
+
+#define ei_status (*(struct ei_device *)(dev->priv))
+
+/* PCMCIA Tuple codes */
+
+#define CISTPL_DEVICE	0x01
+#define	CISTPL_CONF_MAP	0x1a
+#define	CISTPL_CONFIG	0x1b
+#define	CISTPL_MANUF_ID	0x20
+#define	CISTPL_FUNC_ID	0x21
+
+#define FUNC_ID_NETIF	0x06
+
+#endif
diff -ruN linux-2.0.29/include/asm-m68k/amigayle.h linux/include/asm-m68k/amigayle.h
--- linux-2.0.29/include/asm-m68k/amigayle.h	Thu Jan  1 01:00:00 1970
+++ linux/include/asm-m68k/amigayle.h	Fri Dec  5 22:19:35 1997
@@ -0,0 +1,72 @@
+
+/*
+** asm-m68k/amigayle.h -- This header defines the registers of the gayle chip
+**                        found on the Amiga 1200
+**                        This information was found by disassembling card.resource,
+**                        so the definitions may not be 100% correct
+**                        anyone has an official doc ?
+**
+** Copyright 1997 by Alain Malek
+**
+** This file is subject to the terms and conditions of the GNU General Public
+** License.  See the file COPYING in the main directory of this archive
+** for more details.
+**
+** Created: 11/28/97 by Alain Malek
+*/
+
+#ifndef _ASMm68k_AMIGAYLE_H_
+#define _ASMm68k_AMIGAYLE_H_
+
+#include <asm/amigahw.h>
+
+/* memory layout */
+
+#define GAYLE_RAM		(0x600000+zTwoBase)
+#define GAYLE_RAMSIZE		(0x400000+zTwoBase)
+#define GAYLE_ATTRIBUTE		(0xa00000+zTwoBase)
+#define GAYLE_ATTRIBUTESIZE	(0x020000+zTwoBase)
+#define GAYLE_IO		(0xa20000+zTwoBase)	/* 16bit and even 8bit registers */
+#define GAYLE_IOSIZE		(0x010000+zTwoBase)
+#define GAYLE_IO_8BITODD	(0xa30000+zTwoBase)	/* odd 8bit registers */
+
+/* offset for accessing odd IO registers */
+#define GAYLE_ODD		(GAYLE_IO_8BITODD-GAYLE_IO-1)
+
+/* GAYLE registers location */
+
+#define GAYLE_CARDSTATUS	(0xda8000+zTwoBase)
+#define GAYLE_IRQREQ		(0xda9000+zTwoBase)
+#define GAYLE_IRQEN		(0xdaa000+zTwoBase)
+#define GAYLE_CONFIG		(0xdab000+zTwoBase)
+#define GAYLE_RESET		(0xa40000+zTwoBase)	/* write 0x00 to start reset,
+                                                         read 1 byte to stop reset */
+
+/* GAYLE_CARDSTATUS and GAYLE_IRQ bit def */
+
+#define GAYLE_CS_MASTER		0x80	/* not sure of this, also used to signal IDE ints */
+#define GAYLE_CS_CCDET		0x40
+#define GAYLE_CS_BVD1		0x20
+#define GAYLE_CS_SC		0x20
+#define GAYLE_CS_BVD2		0x10
+#define GAYLE_CS_DA		0x10
+#define GAYLE_CS_WR		0x08
+#define GAYLE_CS_BSY		0x04
+#define GAYLE_CS_IRQ		0x04
+#define GAYLE_CS_DIGAUDIO	0x02	/* bit 1 and 0 are also used to ack IDE ints */
+#define GAYLE_CS_IDE		0x01
+
+/* GAYLE_CONFIG bit def
+   (bit 0-1 for program voltage, bit 2-3 for access speed */
+
+#define GAYLE_CFG_0V		0x00
+#define GAYLE_CFG_5V		0x01
+#define GAYLE_CFG_12V		0x02
+
+#define GAYLE_CFG_100NS		0x08
+#define GAYLE_CFG_150NS		0x04
+#define GAYLE_CFG_250NS		0x00
+#define GAYLE_CFG_720NS		0x0c
+
+#endif /* asm-m68k/amigayle.h */
+
diff -ruN linux-2.0.29/include/asm-m68k/io.h linux/include/asm-m68k/io.h
--- linux-2.0.29/include/asm-m68k/io.h	Sat Oct  5 15:26:18 1996
+++ linux/include/asm-m68k/io.h	Thu Dec  4 17:22:10 1997
@@ -21,6 +21,29 @@
 #define outb_p(x,addr) put_user_byte_io((x),(char *)(addr))
 #define outb(x,addr) put_user_byte_io((x),(char *)(addr))
 
+/* word io */
+
+static inline unsigned short get_user_word_io(const short * addr)
+{
+	register unsigned short _v;
+
+	__asm__ __volatile__ ("movew %1,%0":"=dm" (_v):"m" (*addr));
+	return _v;
+}
+#define inw_p(addr) get_user_word_io((short *)(addr))
+#define inw(addr) get_user_word_io((short *)(addr))
+
+static inline void put_user_word_io(short val,short *addr)
+{
+	__asm__ __volatile__ ("movew %0,%1"
+			      : /* no outputs */
+			      :"idm" (val),"m" (*addr)
+			      : "memory");
+}
+#define outw_p(x,addr) put_user_word_io((x),(short *)(addr))
+#define outw(x,addr) put_user_word_io((x),(short *)(addr))
+
+
 /*
  * Change virtual addresses to physical addresses and vv.
  * These are trivial on the 1:1 Linux/i386 mapping (but if we ever

--
Geert Uytterhoeven                     Geert.Uytterhoeven@cs.kuleuven.ac.be
Wavelets, Linux/{m68k~Amiga,PPC~CHRP}  http://www.cs.kuleuven.ac.be/~geert/
Department of Computer Science -- Katholieke Universiteit Leuven -- Belgium

