Date: Thu, 09 Jul 1998 18:53:38 +0200
From: Alain Malek <Alain.Malek@dumbo.cryogen.ch>
To: linux68k <linux-m68k@phil.uni-sb.de>
Subject: L68K: pcmcia ne2000 driver
Sender: owner-linux-m68k@phil.uni-sb.de

Hello,

Here is a new version of the apne driver. Now it uses the existing
8390 driver to avoid code duplication.
I had to change some small things in the 8390 files:

1) changed the type of the reg_offset table from unsigned char to int.
   (offset between odd and even register for pcmcia io is 0x10000)
2) added a 'ei_local' variable in the NS8390_trigger_send function to
   solve a problem with the E8930_CMD macro.

This patch should be applied to kernel 2.1.106.

Bye,

Alain

diff -ruN linux-2.1.106/arch/m68k/amiga/Makefile linux/arch/m68k/amiga/Makefile
--- linux-2.1.106/arch/m68k/amiga/Makefile	Thu Jun  4 20:25:05 1998
+++ linux/arch/m68k/amiga/Makefile	Wed Jul  8 15:00:49 1998
@@ -11,4 +11,8 @@
 O_OBJS   := config.o amiints.o cia.o chipram.o amisound.o
 OX_OBJS  := amiga_ksyms.o
 
+ifdef CONFIG_AMIGA_PCMCIA
+O_OBJS := $(O_OBJS) pcmcia.o
+endif
+
 include $(TOPDIR)/Rules.make
diff -ruN linux-2.1.106/arch/m68k/amiga/amiga_ksyms.c linux/arch/m68k/amiga/amiga_ksyms.c
--- linux-2.1.106/arch/m68k/amiga/amiga_ksyms.c	Thu Jun 18 14:06:44 1998
+++ linux/arch/m68k/amiga/amiga_ksyms.c	Wed Jul  8 15:07:47 1998
@@ -2,6 +2,7 @@
 #include <linux/types.h>
 #include <linux/zorro.h>
 #include <asm/amigahw.h>
+#include <asm/amipcmcia.h>
 
 extern volatile u_short amiga_audio_min_period;
 extern u_short amiga_audio_period;
@@ -19,3 +20,12 @@
 EXPORT_SYMBOL(amiga_chip_size);
 EXPORT_SYMBOL(amiga_audio_period);
 EXPORT_SYMBOL(amiga_audio_min_period);
+
+#ifdef CONFIG_AMIGA_PCMCIA
+  EXPORT_SYMBOL(pcmcia_reset);
+  EXPORT_SYMBOL(pcmcia_copy_tuple);
+  EXPORT_SYMBOL(pcmcia_program_voltage);
+  EXPORT_SYMBOL(pcmcia_access_speed);
+  EXPORT_SYMBOL(pcmcia_write_enable);
+  EXPORT_SYMBOL(pcmcia_write_disable);
+#endif
diff -ruN linux-2.1.106/arch/m68k/amiga/amiints.c linux/arch/m68k/amiga/amiints.c
--- linux-2.1.106/arch/m68k/amiga/amiints.c	Sat Jan 24 20:09:30 1998
+++ linux/arch/m68k/amiga/amiints.c	Wed Jul  8 15:00:49 1998
@@ -32,6 +32,7 @@
 #include <asm/traps.h>
 #include <asm/amigahw.h>
 #include <asm/amigaints.h>
+#include <asm/amipcmcia.h>
 
 extern int cia_request_irq(struct ciabase *base,int irq,
                            void (*handler)(int, void *, struct pt_regs *),
@@ -88,6 +89,10 @@
 	}
 	for (i = 0; i < AMI_IRQS; i++)
 		ami_ablecount[i] = 0;
+
+	/* turn off PCMCIA interrupts */
+	if (AMIGAHW_PRESENT(PCMCIA))
+		pcmcia_disable_irq();
 
 	/* turn off all interrupts and enable the master interrupt bit */
 	custom.intena = 0x7fff;
diff -ruN linux-2.1.106/arch/m68k/amiga/pcmcia.c linux/arch/m68k/amiga/pcmcia.c
--- linux-2.1.106/arch/m68k/amiga/pcmcia.c	Thu Jan  1 01:00:00 1970
+++ linux/arch/m68k/amiga/pcmcia.c	Wed Jul  8 15:00:49 1998
@@ -0,0 +1,114 @@
+
+/*
+** asm-m68k/pcmcia.h -- Amiga Linux PCMCIA support
+**                      most information was found by disassembling card.resource
+**                      I'm still looking for 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: 12/10/97 by Alain Malek
+*/
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <asm/amigayle.h>
+#include <asm/amipcmcia.h>
+
+/* gayle config byte for program voltage and access speed */
+static u_char cfg_byte = GAYLE_CFG_0V|GAYLE_CFG_150NS;
+
+void pcmcia_reset(void)
+{
+	unsigned long reset_start_time = jiffies;
+	unsigned char b;
+
+	gayle_reset = 0x00;
+	while (jiffies - reset_start_time < 1*HZ/100);
+	b = gayle_reset;
+}
+
+
+/* copy a tuple, including tuple header. return nb bytes copied */
+/* be carefull as this may trigger a GAYLE_IRQ_WR interrupt ! */
+
+int pcmcia_copy_tuple(unsigned char tuple_id, void *tuple, int max_len)
+{
+	unsigned char id, *dest;
+	int cnt, pos, len;
+
+	dest = tuple;
+	pos = 0;
+
+	id = gayle_attribute[pos];
+
+	while((id != CISTPL_END) && (pos < 0x10000)) {
+		len = (int)gayle_attribute[pos+2] + 2;
+		if (id == tuple_id) {
+			len = (len > max_len)?max_len:len;
+			for (cnt = 0; cnt < len; cnt++) {
+				*dest++ = gayle_attribute[pos+(cnt<<1)];
+			}
+
+			return len;
+		}
+		pos += len<<1;
+		id = gayle_attribute[pos];
+	}
+
+	return 0;
+}
+
+void pcmcia_program_voltage(int voltage)
+{
+	u_char v;
+
+	switch (voltage) {
+	case PCMCIA_0V:
+		v = GAYLE_CFG_0V;
+		break;
+	case PCMCIA_5V:
+		v = GAYLE_CFG_5V;
+		break;
+	case PCMCIA_12V:
+		v = GAYLE_CFG_12V;
+		break;
+	default:
+		v = GAYLE_CFG_0V;
+	}
+
+	cfg_byte = (cfg_byte & 0xfc) | v;
+	gayle.config = cfg_byte;
+
+}
+
+void pcmcia_access_speed(int speed)
+{
+	u_char s;
+
+	if (speed <= PCMCIA_SPEED_100NS)
+		s = GAYLE_CFG_100NS;
+	else if (speed <= PCMCIA_SPEED_150NS)
+		s = GAYLE_CFG_150NS;
+	else if (speed <= PCMCIA_SPEED_250NS)
+		s = GAYLE_CFG_250NS;
+	else
+		s = GAYLE_CFG_720NS;
+
+	cfg_byte = (cfg_byte & 0xf3) | s;
+	gayle.config = cfg_byte;
+}
+
+void pcmcia_write_enable(void)
+{
+	gayle.cardstatus = GAYLE_CS_WR|GAYLE_CS_DA;
+}
+
+void pcmcia_write_disable(void)
+{
+	gayle.cardstatus = 0;
+}
diff -ruN linux-2.1.106/arch/m68k/config.in linux/arch/m68k/config.in
--- linux-2.1.106/arch/m68k/config.in	Thu Jun 18 16:06:58 1998
+++ linux/arch/m68k/config.in	Wed Jul  8 15:11:06 1998
@@ -82,6 +82,9 @@
 
 if [ "$CONFIG_AMIGA" = "y" ]; then
   bool 'Amiga Zorro (AutoConfig) bus support' CONFIG_ZORRO
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool 'Amiga 1200/600 PCMCIA support' CONFIG_AMIGA_PCMCIA
+  fi
 fi
 if [ "$CONFIG_ATARI" = "y" ]; then
   bool 'Support for ST-RAM as swap space' CONFIG_STRAM_SWAP
@@ -206,6 +209,9 @@
   tristate 'Ariadne support' CONFIG_ARIADNE
   tristate 'A2065 support' CONFIG_A2065
   tristate 'Hydra support' CONFIG_HYDRA
+  if [ "$CONFIG_AMIGA_PCMCIA" = "y" ]; then
+    tristate 'PCMCIA NE2000 support' CONFIG_APNE
+  fi
 fi
 if [ "$CONFIG_APOLLO" = "y" ] ; then
   tristate 'Apollo 3c505 support' CONFIG_APOLLO_ELPLUS
diff -ruN linux-2.1.106/arch/m68k/defconfig linux/arch/m68k/defconfig
--- linux-2.1.106/arch/m68k/defconfig	Fri May  1 19:15:40 1998
+++ linux/arch/m68k/defconfig	Wed Jul  8 15:00:49 1998
@@ -45,6 +45,7 @@
 # CONFIG_AMIGA_GSP is not set
 # CONFIG_GSP_RESOLVER is not set
 # CONFIG_GSP_A2410 is not set
+# CONFIG_AMIGA_PCMCIA is not set
 # CONFIG_HEARTBEAT is not set
 CONFIG_PROC_HARDWARE=y
 
@@ -148,6 +149,7 @@
 # CONFIG_ARIADNE is not set
 # CONFIG_A2065 is not set
 # CONFIG_HYDRA is not set
+# CONFIG_APNE is not set
 # CONFIG_ATARILANCE is not set
 # CONFIG_ATARI_BIONET is not set
 # CONFIG_ATARI_PAMSNET is not set
diff -ruN linux-2.1.106/drivers/net/8390.c linux/drivers/net/8390.c
--- linux-2.1.106/drivers/net/8390.c	Thu Jun 18 14:07:38 1998
+++ linux/drivers/net/8390.c	Thu Jul  9 18:15:18 1998
@@ -950,7 +950,8 @@
 								int start_page)
 {
 	int e8390_base = dev->base_addr;
-    
+	struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
 	outb_p(E8390_NODMA+E8390_PAGE0, e8390_base+E8390_CMD);
     
 	if (inb_p(e8390_base) & E8390_TRANS) 
diff -ruN linux-2.1.106/drivers/net/8390.h linux/drivers/net/8390.h
--- linux-2.1.106/drivers/net/8390.h	Mon Jun  8 15:39:22 1998
+++ linux/drivers/net/8390.h	Thu Jul  9 18:14:06 1998
@@ -177,7 +177,7 @@
   unsigned char saved_irq;	/* Original dev->irq value. */
   /* The new statistics table. */
   struct net_device_stats stat;
-  unsigned char *reg_offset;    /* Register mapping table */
+  int *reg_offset;              /* Register mapping table */
 };
 
 /* The maximum number of 8390 interrupt service routines called per IRQ. */
@@ -207,11 +207,10 @@
 #define E8390_PAGE1	0x40	/* using the two high-order bits */
 #define E8390_PAGE2	0x80	/* Page 3 is invalid. */
 
-
-#ifndef CONFIG_MAC
-#define EI_SHIFT(x)	(x)
-#else
+#if defined(CONFIG_MAC) || defined(CONFIG_AMIGA_PCMCIA)
 #define EI_SHIFT(x)	(ei_local->reg_offset[x])
+#else
+#define EI_SHIFT(x)	(x)
 #endif
 
 #define E8390_CMD	EI_SHIFT(0x00)  /* The command register (for all pages) */
diff -ruN linux-2.1.106/drivers/net/Makefile linux/drivers/net/Makefile
--- linux-2.1.106/drivers/net/Makefile	Mon Jun  8 16:54:13 1998
+++ linux/drivers/net/Makefile	Thu Jul  9 10:02:05 1998
@@ -74,6 +74,17 @@
 CONFIG_8390_BUILTIN = y
 endif
 
+ifeq ($(CONFIG_APNE),y)
+L_OBJS += apne.o
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_APNE),m)
+  M_OBJS += apne.o
+  CONFIG_8390_MODULE = y
+  endif
+endif
+
+
 ifeq ($(CONFIG_SHAPER),y)
 L_OBJS += shaper.o
 else
@@ -748,6 +759,7 @@
   M_OBJS += hydra.o
   endif
 endif
+
 
 ifeq ($(CONFIG_SDLA),y)
 L_OBJS += sdla.o
diff -ruN linux-2.1.106/drivers/net/Space.c linux/drivers/net/Space.c
--- linux-2.1.106/drivers/net/Space.c	Thu Jun 18 14:07:40 1998
+++ linux/drivers/net/Space.c	Wed Jul  8 15:00:49 1998
@@ -91,6 +91,7 @@
 extern int atarilance_probe(struct device *);
 extern int a2065_probe(struct device *);
 extern int ariadne_probe(struct device *);
+extern int apne_probe(struct device *);
 extern int hydra_probe(struct device *);
 extern int bionet_probe(struct device *);
 extern int pamsnet_probe(struct device *);
@@ -277,6 +278,9 @@
 #endif
 #ifdef CONFIG_ARIADNE		/* Village Tronic Ariadne Ethernet Board */
 	&& ariadne_probe(dev)
+#endif
+#ifdef CONFIG_APNE		/* Amiga PCMCIA NE2000 Ethernet Board */
+	&& apne_probe(dev)
 #endif
 #ifdef CONFIG_HYDRA		/* Hydra Systems Amiganet Ethernet board */
 	&& hydra_probe(dev)
diff -ruN linux-2.1.106/drivers/net/apne.c linux/drivers/net/apne.c
--- linux-2.1.106/drivers/net/apne.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/net/apne.c	Thu Jul  9 15:41:46 1998
@@ -0,0 +1,634 @@
+/*
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/amigayle.h>
+#include <asm/amipcmcia.h>
+
+#include "8390.h"
+
+/* ---- No user-serviceable parts below ---- */
+
+#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 NE_EN0_ISR		0x07+GAYLE_ODD
+#define NE_EN0_DCFG		0x0e
+
+#define NE_EN0_RSARLO	0x08
+#define NE_EN0_RSARHI	0x09+GAYLE_ODD
+#define NE_EN0_RCNTLO	0x0a
+#define NE_EN0_RXCR		0x0c
+#define NE_EN0_TXCR		0x0d+GAYLE_ODD
+#define NE_EN0_RCNTHI	0x0b+GAYLE_ODD
+#define NE_EN0_IMR		0x0f+GAYLE_ODD
+
+#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 */
+
+
+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 void apne_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static int init_pcmcia(void);
+
+/* IO base address used for nic */
+
+#define IOBASE 0x300
+
+/*
+   use MANUAL_CONFIG and MANUAL_OFFSET for enabling IO by hand
+   you can find the values to use by looking at the cnet.device
+   config file example (the default values are for the CNET40BC card)
+*/
+
+/*
+#define MANUAL_CONFIG 0x20
+#define MANUAL_OFFSET 0x3f8
+
+#define MANUAL_HWADDR0 0x00
+#define MANUAL_HWADDR1 0x12
+#define MANUAL_HWADDR2 0x34
+#define MANUAL_HWADDR3 0x56
+#define MANUAL_HWADDR4 0x78
+#define MANUAL_HWADDR5 0x9a
+*/
+
+#define WORDSWAP(a) ( (((a)>>8)&0xff) | ((a)<<8) )
+
+
+static const char *version =
+    "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
+
+
+__initfunc(int apne_probe(struct device *dev))
+{
+#ifndef MANUAL_CONFIG
+	char tuple[8];
+#endif
+        
+	if ( !(AMIGAHW_PRESENT(PCMCIA)) )
+		return (ENODEV);
+                                
+	printk("Looking for PCMCIA ethernet card : ");
+                                        
+	/* check if a card is inserted */
+	if (!(PCMCIA_INSERTED)) {
+		printk("NO PCMCIA card inserted\n");
+		return (ENODEV);
+	}
+                                                                                                
+	/* disable pcmcia irq for readtuple */
+	pcmcia_disable_irq();
+
+#ifndef MANUAL_CONFIG
+	if ((pcmcia_copy_tuple(CISTPL_FUNCID, tuple, 8) < 3) ||
+		(tuple[2] != CISTPL_FUNCID_NETWORK)) {
+		printk("not an ethernet card\n");
+		return (ENODEV);
+	}
+#endif
+
+	printk("ethernet PCMCIA card inserted\n");
+
+	if (init_pcmcia())
+		return apne_probe1(dev, IOBASE+GAYLE_IO);
+	else
+		return (ENODEV);
+
+}
+
+
+__initfunc(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;
+#ifndef MANUAL_HWADDR0
+    int neX000, ctron;
+#endif
+    static unsigned version_printed = 0;
+    static int pcmcia_offsets[16]={
+                0,   1+GAYLE_ODD,   2,   3+GAYLE_ODD,
+                4,   5+GAYLE_ODD,   6,   7+GAYLE_ODD,
+                8,   9+GAYLE_ODD, 0xa, 0xb+GAYLE_ODD,
+              0xc, 0xd+GAYLE_ODD, 0xe, 0xf+GAYLE_ODD };
+
+    if (load_8390_module("apne.c"))
+        return -ENOSYS;
+
+    /* 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;
+
+	writeb(readb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+	while ((readb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
+		if (jiffies - reset_start_time > 2*HZ/100) {
+			printk(" not found (no reset ack).\n");
+			return ENODEV;
+		}
+
+	writeb(0xff, ioaddr + NE_EN0_ISR);		/* Ack all intr. */
+    }
+
+#ifndef MANUAL_HWADDR0
+
+    /* Read the 16 bytes of station address PROM.
+       We must first initialize registers, similar to NS8390_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, NE_CMD}, /* Select page 0*/
+	    {0x48,	NE_EN0_DCFG},	/* Set byte-wide (0x48) access. */
+	    {0x00,	NE_EN0_RCNTLO},	/* Clear the count regs. */
+	    {0x00,	NE_EN0_RCNTHI},
+	    {0x00,	NE_EN0_IMR},	/* Mask completion irq. */
+	    {0xFF,	NE_EN0_ISR},
+	    {E8390_RXOFF, NE_EN0_RXCR},	/* 0x20  Set to monitor */
+	    {E8390_TXOFF, NE_EN0_TXCR},	/* 0x02  and loopback mode. */
+	    {32,	NE_EN0_RCNTLO},
+	    {0x00,	NE_EN0_RCNTHI},
+	    {0x00,	NE_EN0_RSARLO},	/* DMA starting at 0x0000. */
+	    {0x00,	NE_EN0_RSARHI},
+	    {E8390_RREAD+E8390_START, NE_CMD},
+	};
+	for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) {
+	    writeb(program_seq[i].value, ioaddr + program_seq[i].offset);
+	}
+
+    }
+    for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+	SA_prom[i] = readb(ioaddr + NE_DATAPORT);
+	SA_prom[i+1] = readb(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. */
+	writeb(0x49, ioaddr + NE_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;
+
+    }
+
+#else
+    wordlength = 2;
+    /* We must set the 8390 for word mode. */
+    writeb(0x49, ioaddr + NE_EN0_DCFG);
+    start_page = NESM_START_PG;
+    stop_page = NESM_STOP_PG;
+
+    SA_prom[0] = MANUAL_HWADDR0;
+    SA_prom[1] = MANUAL_HWADDR1;
+    SA_prom[2] = MANUAL_HWADDR2;
+    SA_prom[3] = MANUAL_HWADDR3;
+    SA_prom[4] = MANUAL_HWADDR4;
+    SA_prom[5] = MANUAL_HWADDR5;
+    name = "NE2000";
+#endif
+
+    dev->base_addr = ioaddr;
+
+    /* Install the Interrupt handler */
+    if (request_irq(IRQ_AMIGA_PORTS, apne_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;
+    ei_status.reg_offset = pcmcia_offsets;
+    dev->open = &apne_open;
+    dev->stop = &apne_close;
+    NS8390_init(dev, 0);
+
+    pcmcia_ack_int(pcmcia_get_intreq());		/* ack PCMCIA int req */
+    pcmcia_enable_irq();
+
+    return 0;
+}
+
+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;
+}
+
+/* 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;
+
+    init_pcmcia();
+
+    if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies);
+
+    writeb(readb(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 ((readb(NE_BASE+NE_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;
+	}
+    writeb(ENISR_RESET, NE_BASE + NE_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;
+    writeb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+    writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+    writeb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
+    writeb(0, nic_base + NE_EN0_RCNTHI);
+    writeb(0, nic_base + NE_EN0_RSARLO);		/* On page boundary */
+    writeb(ring_page, nic_base + NE_EN0_RSARHI);
+    writeb(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++ = readw(NE_BASE + NE_DATAPORT);
+    } else {
+        ptrc = (char*)hdr;
+        for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++)
+            *ptrc++ = readb(NE_BASE + NE_DATAPORT);
+    }
+
+    writeb(ENISR_RDC, nic_base + NE_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 writeb. */
+
+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;
+    writeb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+    writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+    writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+    writeb(count >> 8, nic_base + NE_EN0_RCNTHI);
+    writeb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
+    writeb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
+    writeb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+    if (ei_status.word16) {
+      ptrs = (short*)buf;
+      for (cnt = 0; cnt < (count>>1); cnt++)
+        *ptrs++ = readw(NE_BASE + NE_DATAPORT);
+      if (count & 0x01) {
+	buf[count-1] = readb(NE_BASE + NE_DATAPORT);
+      }
+    } else {
+      ptrc = (char*)buf;
+      for (cnt = 0; cnt < count; cnt++)
+        *ptrc++ = readb(NE_BASE + NE_DATAPORT);
+    }
+
+    writeb(ENISR_RDC, nic_base + NE_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... */
+    writeb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+    writeb(ENISR_RDC, nic_base + NE_EN0_ISR);
+
+   /* Now the normal output. */
+    writeb(count & 0xff, nic_base + NE_EN0_RCNTLO);
+    writeb(count >> 8,   nic_base + NE_EN0_RCNTHI);
+    writeb(0x00, nic_base + NE_EN0_RSARLO);
+    writeb(start_page, nic_base + NE_EN0_RSARHI);
+
+    writeb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+    if (ei_status.word16) {
+        ptrs = (short*)buf;
+        for (cnt = 0; cnt < count>>1; cnt++)
+            writew(*ptrs++, NE_BASE+NE_DATAPORT);
+    } else {
+        ptrc = (char*)buf;
+        for (cnt = 0; cnt < count; cnt++)
+	    writeb(*ptrc++, NE_BASE + NE_DATAPORT);
+    }
+
+    dma_start = jiffies;
+
+    while ((readb(NE_BASE + NE_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);
+		NS8390_init(dev,1);
+		break;
+	}
+
+    writeb(ENISR_RDC, nic_base + NE_EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+    return;
+}
+
+static void apne_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    unsigned char pcmcia_intreq;
+
+    if (!(gayle.inten & GAYLE_IRQ_IRQ))
+        return;
+
+    pcmcia_intreq = pcmcia_get_intreq();
+
+    if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) {
+        pcmcia_ack_int(pcmcia_intreq);
+        return;
+    }
+    if (ei_debug > 3)
+        printk("pcmcia intreq = %x\n", pcmcia_intreq);
+    pcmcia_disable_irq();			/* to get rid of the sti() within ei_interrupt */
+    ei_interrupt(irq, dev_id, regs);
+    pcmcia_ack_int(pcmcia_get_intreq());
+    pcmcia_enable_irq();
+}
+
+#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);
+	}
+	lock_8390_module();
+	return (0);
+}
+
+void cleanup_module(void)
+{
+	unregister_netdev(&apne_dev);
+
+	pcmcia_disable_irq();
+
+	free_irq(IRQ_AMIGA_PORTS, &apne_dev);
+
+	pcmcia_reset();
+
+	unlock_8390_module();
+}
+
+#endif
+
+static int init_pcmcia(void)
+{
+	u_char config;
+#ifndef MANUAL_CONFIG
+	u_char tuple[32];
+	int offset_len;
+#endif
+	u_long offset;
+
+	pcmcia_reset();
+	pcmcia_program_voltage(PCMCIA_0V);
+	pcmcia_access_speed(PCMCIA_SPEED_250NS);
+	pcmcia_write_enable();
+
+#ifdef MANUAL_CONFIG
+	config = MANUAL_CONFIG;
+#else
+	/* get and write config byte to enable IO port */
+
+	if (pcmcia_copy_tuple(CISTPL_CFTABLE_ENTRY, tuple, 32) < 3)
+		return 0;
+
+	config = tuple[2] & 0x3f;
+#endif
+#ifdef MANUAL_OFFSET
+	offset = MANUAL_OFFSET;
+#else
+	if (pcmcia_copy_tuple(CISTPL_CONFIG, tuple, 32) < 6)
+		return 0;
+
+	offset_len = (tuple[2] & 0x3) + 1;
+	offset = 0;
+	while(offset_len--) {
+		offset = (offset << 8) | tuple[4+offset_len];
+	}
+#endif
+
+	writeb(config, GAYLE_ATTRIBUTE+offset);
+
+	return 1;
+}
diff -ruN linux-2.1.106/drivers/net/daynaport.c linux/drivers/net/daynaport.c
--- linux-2.1.106/drivers/net/daynaport.c	Mon Jun  8 16:44:35 1998
+++ linux/drivers/net/daynaport.c	Thu Jul  9 09:35:40 1998
@@ -281,13 +281,13 @@
 {
 	static unsigned version_printed = 0;
 
-	static unsigned char fwrd4_offsets[16]={
+	static int fwrd4_offsets[16]={
 		0,      4,      8,      12,
 		16,     20,     24,     28,
 		32,     36,     40,     44,
 		48,     52,     56,     60
 	};
-	static unsigned char back4_offsets[16]={
+	static int back4_offsets[16]={
 		60,     56,     52,     48,
 		44,     40,     36,     32,
 		28,     24,     20,     16,
diff -ruN linux-2.1.106/include/asm-m68k/amigayle.h linux/include/asm-m68k/amigayle.h
--- linux-2.1.106/include/asm-m68k/amigayle.h	Thu Jan  1 01:00:00 1970
+++ linux/include/asm-m68k/amigayle.h	Wed Jul  8 15:31:17 1998
@@ -0,0 +1,107 @@
+
+/*
+** 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>
+#include <asm/io.h>
+
+/* memory layout */
+
+#define GAYLE_RAM		(0x600000+zTwoBase)
+#define GAYLE_RAMSIZE		(0x400000)
+#define GAYLE_ATTRIBUTE		(0xa00000+zTwoBase)
+#define GAYLE_ATTRIBUTESIZE	(0x020000)
+#define GAYLE_IO		(0xa20000+zTwoBase)	/* 16bit and even 8bit registers */
+#define GAYLE_IOSIZE		(0x010000)
+#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 */
+
+struct GAYLE {
+	u_char cardstatus;
+	u_char pad0[0x1000-1];
+
+	u_char intreq;
+	u_char pad1[0x1000-1];
+
+	u_char inten;
+	u_char pad2[0x1000-1];
+
+	u_char config;
+	u_char pad3[0x1000-1];
+};
+
+#define GAYLE_ADDRESS	(0xda8000)	/* gayle main registers base address */
+
+#define GAYLE_RESET	(0xa40000)	/* write 0x00 to start reset,
+                                           read 1 byte to stop reset */
+
+#define gayle (*(volatile struct GAYLE *)(zTwoBase+GAYLE_ADDRESS))
+#define gayle_reset (*(volatile u_char *)(zTwoBase+GAYLE_RESET))
+
+#define gayle_attribute ((volatile u_char *)(GAYLE_ATTRIBUTE))
+
+#define gayle_inb(a) readb( GAYLE_IO+(a)+(((a)&1)*GAYLE_ODD) )
+#define gayle_outb(v,a) writeb( v, GAYLE_IO+(a)+(((a)&1)*GAYLE_ODD) )
+
+#define gayle_inw(a) readw( GAYLE_IO+(a) )
+#define gayle_outw(v,a) writew( v, GAYLE_IO+(a) )
+
+/* GAYLE_CARDSTATUS bit def */
+
+#define GAYLE_CS_CCDET		0x40	/* credit card detect */
+#define GAYLE_CS_BVD1		0x20	/* battery voltage detect 1 */
+#define GAYLE_CS_SC		0x20	/* credit card status change */
+#define GAYLE_CS_BVD2		0x10	/* battery voltage detect 2 */
+#define GAYLE_CS_DA		0x10	/* digital audio */
+#define GAYLE_CS_WR		0x08	/* write enable (1 == enabled) */
+#define GAYLE_CS_BSY		0x04	/* credit card busy */
+#define GAYLE_CS_IRQ		0x04	/* interrupt request */
+
+/* GAYLE_IRQ bit def */
+
+#define GAYLE_IRQ_IDE		0x80
+#define GAYLE_IRQ_CCDET		0x40
+#define GAYLE_IRQ_BVD1		0x20
+#define GAYLE_IRQ_SC		0x20
+#define GAYLE_IRQ_BVD2		0x10
+#define GAYLE_IRQ_DA		0x10
+#define GAYLE_IRQ_WR		0x08
+#define GAYLE_IRQ_BSY		0x04
+#define GAYLE_IRQ_IRQ		0x04
+#define GAYLE_IRQ_IDEACK1	0x02
+#define GAYLE_IRQ_IDEACK0	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.1.106/include/asm-m68k/amipcmcia.h linux/include/asm-m68k/amipcmcia.h
--- linux-2.1.106/include/asm-m68k/amipcmcia.h	Thu Jan  1 01:00:00 1970
+++ linux/include/asm-m68k/amipcmcia.h	Wed Jul  8 15:31:17 1998
@@ -0,0 +1,111 @@
+
+/*
+** asm-m68k/pcmcia.h -- Amiga Linux PCMCIA Definitions
+**
+** 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: 12/10/97 by Alain Malek
+*/
+
+#ifndef __AMIGA_PCMCIA_H__
+#define __AMIGA_PCMCIA_H__
+
+#include <asm/amigayle.h>
+
+/* prototypes */
+
+void pcmcia_reset(void);
+int pcmcia_copy_tuple(unsigned char tuple_id, void *tuple, int max_len);
+void pcmcia_program_voltage(int voltage);
+void pcmcia_access_speed(int speed);
+void pcmcia_write_enable(void);
+void pcmcia_write_disable(void);
+
+static inline u_char pcmcia_read_status(void)
+{
+	return (gayle.cardstatus & 0x7c);
+}
+
+static inline u_char pcmcia_get_intreq(void)
+{
+	return (gayle.intreq);
+}
+
+static inline void pcmcia_ack_int(u_char intreq)
+{
+	gayle.intreq = ((intreq & 0x2c) ^ 0x2c) | 0xc0;
+}
+
+static inline void pcmcia_enable_irq(void)
+{
+	gayle.inten = GAYLE_IRQ_IDE|GAYLE_IRQ_IRQ;
+}
+
+static inline void pcmcia_disable_irq(void)
+{
+	gayle.inten = GAYLE_IRQ_IDE;
+}
+
+#define PCMCIA_INSERTED	(gayle.cardstatus & GAYLE_CS_CCDET)
+
+/* valid voltages for pcmcia_ProgramVoltage */
+
+#define PCMCIA_0V	0
+#define PCMCIA_5V	5
+#define PCMCIA_12V	12
+
+/* valid speeds for pcmcia_AccessSpeed */
+
+#define PCMCIA_SPEED_100NS	100
+#define PCMCIA_SPEED_150NS	150
+#define PCMCIA_SPEED_250NS	250
+#define PCMCIA_SPEED_720NS	720
+
+/* PCMCIA Tuple codes */
+
+#define CISTPL_NULL		0x00
+#define CISTPL_DEVICE		0x01
+#define CISTPL_LONGLINK_CB	0x02
+#define CISTPL_CONFIG_CB	0x04
+#define CISTPL_CFTABLE_ENTRY_CB	0x05
+#define CISTPL_LONGLINK_MFC	0x06
+#define CISTPL_BAR		0x07
+#define CISTPL_CHECKSUM		0x10
+#define CISTPL_LONGLINK_A	0x11
+#define CISTPL_LONGLINK_C	0x12
+#define CISTPL_LINKTARGET	0x13
+#define CISTPL_NO_LINK		0x14
+#define CISTPL_VERS_1		0x15
+#define CISTPL_ALTSTR		0x16
+#define CISTPL_DEVICE_A		0x17
+#define CISTPL_JEDEC_C		0x18
+#define CISTPL_JEDEC_A		0x19
+#define CISTPL_CONFIG		0x1a
+#define CISTPL_CFTABLE_ENTRY	0x1b
+#define CISTPL_DEVICE_OC	0x1c
+#define CISTPL_DEVICE_OA	0x1d
+#define CISTPL_DEVICE_GEO	0x1e
+#define CISTPL_DEVICE_GEO_A	0x1f
+#define CISTPL_MANFID		0x20
+#define CISTPL_FUNCID		0x21
+#define CISTPL_FUNCE		0x22
+#define CISTPL_SWIL		0x23
+#define CISTPL_END		0xff
+
+/* FUNCID */
+
+#define CISTPL_FUNCID_MULTI	0x00
+#define CISTPL_FUNCID_MEMORY	0x01
+#define CISTPL_FUNCID_SERIAL	0x02
+#define CISTPL_FUNCID_PARALLEL	0x03
+#define CISTPL_FUNCID_FIXED	0x04
+#define CISTPL_FUNCID_VIDEO	0x05
+#define CISTPL_FUNCID_NETWORK	0x06
+#define CISTPL_FUNCID_AIMS	0x07
+#define CISTPL_FUNCID_SCSI	0x08
+
+#endif
